diff --git a/DEPS b/DEPS
index 57520e6..c62eb8c 100644
--- a/DEPS
+++ b/DEPS
@@ -181,11 +181,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'c21316a73b543f0cba5d115d5bed81852d432190',
+  'skia_revision': '2df621e68d8d044a4ef5ab3eb698685f7af31c1f',
   # 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': '2fff8d9182bd6ac3bace3e691ee404a192281242',
+  'v8_revision': '79c8de4b3cbcd3a3b7fead5131f7d16283627c07',
   # 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.
@@ -193,11 +193,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '7dfc99e5cf6bd1781b1206ff198e1e9b2ee21f69',
+  'angle_revision': 'cbbb3aafab002deec45b821008dee5e1286f3d9a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '068dd89cab31091e2290c99bd8b6c721e1108bda',
+  'swiftshader_revision': '43e33165a8f03a9b85b782f3a87bf49fbd90b25b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -244,7 +244,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': '296680f097d19afdeb742119e1d26d97392d78d9',
+  'catapult_revision': '69f98e5ab0fec1c30f067a81040d7e99da927e83',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -252,7 +252,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': 'e2db893b1dead2e0b4e55c49269b3abafd8537d8',
+  'devtools_frontend_revision': '57b82d61dd2e7f0a0f25dbee2b3a1fa3b5ca8a97',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -332,7 +332,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libgifcodec
   # and whatever else without interference from each other.
-  'libgifcodec_revision': '3815a0321b8e99a9eb35309c80334a43c2c49ff2',
+  'libgifcodec_revision': '38d9c73f49b861bb4a9829371ac311544b120023',
 
   # TODO(crbug.com/941824): The values below need to be kept in sync
   # between //DEPS and //buildtools/DEPS, so if you're updating one,
@@ -879,7 +879,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '024c350baa0372ea748033c91f8098bf3d45ee49',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '9230b0169cb173c7432c905f8b95e692e07dbb14',
       'condition': 'checkout_linux',
   },
 
@@ -963,7 +963,7 @@
   },
 
   'src/third_party/glslang/src':
-    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + '40801e31ed0cd8501c5daa0fe56e4e825165cc9e',
+    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + '1642ca11282a9d96a42dfedea6065ae25c4c137d',
 
   'src/third_party/google_toolbox_for_mac/src': {
       'url': Var('chromium_git') + '/external/github.com/google/google-toolbox-for-mac.git' + '@' + Var('google_toolbox_for_mac_revision'),
@@ -1297,7 +1297,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '804ad6f18ad8e45dd7f268ec1a9a58580dbc9521',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '30bf2ef2116a48bfd6f9bbe4e3a84409793ef684',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1498,7 +1498,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '53655df4cde60b121fc530842ba9a6d5dfec1ae1',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'c33e4910c5dba2ba51eb95789d885f3eeb7e3d70',
+    Var('webrtc_git') + '/src.git' + '@' + '382cc6d8a6c3e1887de28e948d4dd57922fcfe01',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1568,7 +1568,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@a8b0b9cbdf854530bcc2f4491cb90ff16c0c823c',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@c5147e51f2268b6c9b3c6423413b9d2b80d7e753',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/WATCHLISTS b/WATCHLISTS
index 6679fef..75b4b35 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -1630,10 +1630,6 @@
     'settings_os_settings': {
       'filepath': 'chrome/browser/resources/settings/chromeos/',
     },
-    'settings_reset_prompt': {
-      'filepath': 'chrome/browser/safe_browsing/settings_reset_prompt/'\
-                  '|chrome/browser/ui/views/settings_reset_prompt',
-    },
     'sharing': {
       'filepath': 'chrome/browser/sharing/|'\
                   'chrome/browser/ui/views/sharing/',
@@ -2669,7 +2665,6 @@
                  'maybelle@chromium.org'],
     'settings_os_settings': [
                  'jhawkins+watch@chromium.org'],
-    'settings_reset_prompt': ['alito+watch@chromium.org'],
     'sharing': ['peter@chromium.org',
                 'unido-reviews@chromium.org'],
     'site_engagement': ['dominickn+watch-engagement@chromium.org'],
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn
index 89d02e3..06f3b94 100644
--- a/android_webview/BUILD.gn
+++ b/android_webview/BUILD.gn
@@ -483,7 +483,7 @@
 
   srcjar_deps = [
     "//android_webview/browser:browser_enums",
-    "//components/safe_browsing/db:sb_threat_values",
+    "//components/safe_browsing/core/db:sb_threat_values",
   ]
 
   android_manifest_for_lint = system_webview_android_manifest
diff --git a/android_webview/browser/BUILD.gn b/android_webview/browser/BUILD.gn
index 643e623..b75a88c 100644
--- a/android_webview/browser/BUILD.gn
+++ b/android_webview/browser/BUILD.gn
@@ -215,18 +215,19 @@
     "//components/printing/browser",
     "//components/printing/common",
     "//components/printing/common:mojo_interfaces",
-    "//components/safe_browsing",
-    "//components/safe_browsing:features",
-    "//components/safe_browsing:ping_manager",
     "//components/safe_browsing/android:remote_database_manager",
-    "//components/safe_browsing/browser",
-    "//components/safe_browsing/browser:network_context",
-    "//components/safe_browsing/common",
-    "//components/safe_browsing/db:database_manager",
-    "//components/safe_browsing/db:safebrowsing_proto",
-    "//components/safe_browsing/triggers",
-    "//components/safe_browsing/web_ui",
-    "//components/safe_browsing/web_ui:constants",
+    "//components/safe_browsing/content",
+    "//components/safe_browsing/content/browser",
+    "//components/safe_browsing/content/web_ui",
+    "//components/safe_browsing/core:features",
+    "//components/safe_browsing/core:ping_manager",
+    "//components/safe_browsing/core/browser",
+    "//components/safe_browsing/core/browser:network_context",
+    "//components/safe_browsing/core/common",
+    "//components/safe_browsing/core/db:database_manager",
+    "//components/safe_browsing/core/db:safebrowsing_proto",
+    "//components/safe_browsing/core/triggers",
+    "//components/safe_browsing/core/web_ui:constants",
     "//components/security_interstitials/content:security_interstitial_page",
     "//components/security_interstitials/core",
     "//components/services/heap_profiling/public/cpp",
diff --git a/android_webview/browser/aw_browser_context.cc b/android_webview/browser/aw_browser_context.cc
index bd669cb3..79f1e1ab 100644
--- a/android_webview/browser/aw_browser_context.cc
+++ b/android_webview/browser/aw_browser_context.cc
@@ -43,7 +43,7 @@
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/pref_service_factory.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/url_formatter/url_fixer.h"
 #include "components/user_prefs/user_prefs.h"
 #include "components/variations/net/variations_http_headers.h"
diff --git a/android_webview/browser/aw_browser_process.h b/android_webview/browser/aw_browser_process.h
index ee4ece45..2a3e321 100644
--- a/android_webview/browser/aw_browser_process.h
+++ b/android_webview/browser/aw_browser_process.h
@@ -15,7 +15,7 @@
 #include "components/prefs/pref_change_registrar.h"
 #include "components/prefs/pref_service.h"
 #include "components/safe_browsing/android/remote_database_manager.h"
-#include "components/safe_browsing/triggers/trigger_manager.h"
+#include "components/safe_browsing/core/triggers/trigger_manager.h"
 #include "content/public/browser/network_service_instance.h"
 #include "net/log/net_log.h"
 #include "services/network/network_service.h"
diff --git a/android_webview/browser/aw_content_browser_client.cc b/android_webview/browser/aw_content_browser_client.cc
index 3574496..98f8fe8c 100644
--- a/android_webview/browser/aw_content_browser_client.cc
+++ b/android_webview/browser/aw_content_browser_client.cc
@@ -62,9 +62,9 @@
 #include "components/policy/content/policy_blacklist_navigation_throttle.h"
 #include "components/policy/core/browser/browser_policy_connector_base.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/browser/browser_url_loader_throttle.h"
-#include "components/safe_browsing/browser/mojo_safe_browsing_impl.h"
-#include "components/safe_browsing/features.h"
+#include "components/safe_browsing/content/browser/browser_url_loader_throttle.h"
+#include "components/safe_browsing/content/browser/mojo_safe_browsing_impl.h"
+#include "components/safe_browsing/core/features.h"
 #include "components/spellcheck/spellcheck_buildflags.h"
 #include "content/public/browser/browser_message_filter.h"
 #include "content/public/browser/browser_task_traits.h"
@@ -765,11 +765,11 @@
 
   result.push_back(safe_browsing::BrowserURLLoaderThrottle::Create(
       base::BindOnce(
-          [](AwContentBrowserClient* client, content::ResourceContext*) {
+          [](AwContentBrowserClient* client) {
             return client->GetSafeBrowsingUrlCheckerDelegate();
           },
           base::Unretained(this)),
-      wc_getter, frame_tree_node_id, browser_context->GetResourceContext(),
+      wc_getter, frame_tree_node_id,
       // TODO(crbug.com/1033760): cache manager is used to perform real time url
       // check, which is gated by UKM opted in. Since AW currently doesn't
       // support UKM, this feature is not enabled.
diff --git a/android_webview/browser/aw_content_browser_overlay_manifest.cc b/android_webview/browser/aw_content_browser_overlay_manifest.cc
index b0aed6a7..a7a8e9b4 100644
--- a/android_webview/browser/aw_content_browser_overlay_manifest.cc
+++ b/android_webview/browser/aw_content_browser_overlay_manifest.cc
@@ -5,7 +5,7 @@
 #include "android_webview/browser/aw_content_browser_overlay_manifest.h"
 
 #include "base/no_destructor.h"
-#include "components/safe_browsing/common/safe_browsing.mojom.h"
+#include "components/safe_browsing/core/common/safe_browsing.mojom.h"
 #include "components/spellcheck/common/spellcheck.mojom.h"
 #include "content/public/common/service_names.mojom.h"
 #include "services/service_manager/public/cpp/manifest_builder.h"
diff --git a/android_webview/browser/aw_web_ui_controller_factory.cc b/android_webview/browser/aw_web_ui_controller_factory.cc
index 2d2c855..dedec357 100644
--- a/android_webview/browser/aw_web_ui_controller_factory.cc
+++ b/android_webview/browser/aw_web_ui_controller_factory.cc
@@ -5,8 +5,8 @@
 #include "android_webview/browser/aw_web_ui_controller_factory.h"
 
 #include "base/memory/ptr_util.h"
-#include "components/safe_browsing/web_ui/constants.h"
-#include "components/safe_browsing/web_ui/safe_browsing_ui.h"
+#include "components/safe_browsing/content/web_ui/safe_browsing_ui.h"
+#include "components/safe_browsing/core/web_ui/constants.h"
 #include "content/public/browser/web_ui.h"
 #include "url/gurl.h"
 
diff --git a/android_webview/browser/network_service/aw_proxying_url_loader_factory.cc b/android_webview/browser/network_service/aw_proxying_url_loader_factory.cc
index ab7fe07..e47421b8 100644
--- a/android_webview/browser/network_service/aw_proxying_url_loader_factory.cc
+++ b/android_webview/browser/network_service/aw_proxying_url_loader_factory.cc
@@ -26,7 +26,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/task/post_task.h"
-#include "components/safe_browsing/common/safebrowsing_constants.h"
+#include "components/safe_browsing/core/common/safebrowsing_constants.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/global_request_id.h"
diff --git a/android_webview/browser/safe_browsing/README.md b/android_webview/browser/safe_browsing/README.md
index c3796796..edcf821 100644
--- a/android_webview/browser/safe_browsing/README.md
+++ b/android_webview/browser/safe_browsing/README.md
@@ -53,7 +53,7 @@
 
 WebView supports Safe Browsing checks (for testing purposes) on hard-coded WebUI
 URLs defined in
-[`//components/safe_browsing/web_ui/constants.cc`](/components/safe_browsing/web_ui/constants.cc)
+[`//components/safe_browsing/core/web_ui/constants.cc`](/components/safe_browsing/core/web_ui/constants.cc)
 (ex. `chrome://safe-browsing/match?type=malware`).
 
 These URLs don't show meaningful content, but will trigger an interstitial when
diff --git a/android_webview/browser/safe_browsing/aw_safe_browsing_blocking_page.cc b/android_webview/browser/safe_browsing/aw_safe_browsing_blocking_page.cc
index 0bfbaa8d..0a86df23 100644
--- a/android_webview/browser/safe_browsing/aw_safe_browsing_blocking_page.cc
+++ b/android_webview/browser/safe_browsing/aw_safe_browsing_blocking_page.cc
@@ -13,9 +13,9 @@
 #include "base/feature_list.h"
 #include "base/metrics/histogram_macros.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/browser/threat_details.h"
-#include "components/safe_browsing/features.h"
-#include "components/safe_browsing/triggers/trigger_manager.h"
+#include "components/safe_browsing/content/browser/threat_details.h"
+#include "components/safe_browsing/core/features.h"
+#include "components/safe_browsing/core/triggers/trigger_manager.h"
 #include "components/security_interstitials/content/security_interstitial_controller_client.h"
 #include "components/security_interstitials/content/unsafe_resource.h"
 #include "components/security_interstitials/core/base_safe_browsing_error_ui.h"
diff --git a/android_webview/browser/safe_browsing/aw_safe_browsing_blocking_page.h b/android_webview/browser/safe_browsing/aw_safe_browsing_blocking_page.h
index 8fa033d1..8b85543 100644
--- a/android_webview/browser/safe_browsing/aw_safe_browsing_blocking_page.h
+++ b/android_webview/browser/safe_browsing/aw_safe_browsing_blocking_page.h
@@ -7,7 +7,7 @@
 
 #include <memory>
 
-#include "components/safe_browsing/base_blocking_page.h"
+#include "components/safe_browsing/content/base_blocking_page.h"
 #include "components/security_interstitials/core/base_safe_browsing_error_ui.h"
 
 namespace security_interstitials {
diff --git a/android_webview/browser/safe_browsing/aw_safe_browsing_ui_manager.cc b/android_webview/browser/safe_browsing/aw_safe_browsing_ui_manager.cc
index 2cf158c..aed4457b 100644
--- a/android_webview/browser/safe_browsing/aw_safe_browsing_ui_manager.cc
+++ b/android_webview/browser/safe_browsing/aw_safe_browsing_ui_manager.cc
@@ -18,11 +18,11 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/path_service.h"
 #include "base/task/post_task.h"
-#include "components/safe_browsing/base_ui_manager.h"
-#include "components/safe_browsing/browser/safe_browsing_network_context.h"
-#include "components/safe_browsing/common/safebrowsing_constants.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
-#include "components/safe_browsing/ping_manager.h"
+#include "components/safe_browsing/content/base_ui_manager.h"
+#include "components/safe_browsing/core/browser/safe_browsing_network_context.h"
+#include "components/safe_browsing/core/common/safebrowsing_constants.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/core/ping_manager.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_controller.h"
diff --git a/android_webview/browser/safe_browsing/aw_safe_browsing_ui_manager.h b/android_webview/browser/safe_browsing/aw_safe_browsing_ui_manager.h
index dcaccdb..e09c15e 100644
--- a/android_webview/browser/safe_browsing/aw_safe_browsing_ui_manager.h
+++ b/android_webview/browser/safe_browsing/aw_safe_browsing_ui_manager.h
@@ -8,7 +8,7 @@
 #include <memory>
 #include <string>
 
-#include "components/safe_browsing/base_ui_manager.h"
+#include "components/safe_browsing/content/base_ui_manager.h"
 #include "components/security_interstitials/content/unsafe_resource.h"
 #include "content/public/browser/web_contents.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
diff --git a/android_webview/browser/safe_browsing/aw_url_checker_delegate_impl.cc b/android_webview/browser/safe_browsing/aw_url_checker_delegate_impl.cc
index 9f6c044e..5e31ffb 100644
--- a/android_webview/browser/safe_browsing/aw_url_checker_delegate_impl.cc
+++ b/android_webview/browser/safe_browsing/aw_url_checker_delegate_impl.cc
@@ -19,9 +19,9 @@
 #include "base/android/jni_android.h"
 #include "base/bind.h"
 #include "base/task/post_task.h"
-#include "components/safe_browsing/db/database_manager.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
-#include "components/safe_browsing/web_ui/constants.h"
+#include "components/safe_browsing/core/db/database_manager.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/core/web_ui/constants.h"
 #include "components/security_interstitials/content/unsafe_resource.h"
 #include "components/security_interstitials/core/urls.h"
 #include "content/public/browser/browser_task_traits.h"
@@ -70,7 +70,6 @@
 }
 
 bool AwUrlCheckerDelegateImpl::ShouldSkipRequestCheck(
-    content::ResourceContext* resource_context,
     const GURL& original_url,
     int frame_tree_node_id,
     int render_process_id,
diff --git a/android_webview/browser/safe_browsing/aw_url_checker_delegate_impl.h b/android_webview/browser/safe_browsing/aw_url_checker_delegate_impl.h
index f49fda9..231e895 100644
--- a/android_webview/browser/safe_browsing/aw_url_checker_delegate_impl.h
+++ b/android_webview/browser/safe_browsing/aw_url_checker_delegate_impl.h
@@ -9,7 +9,7 @@
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
-#include "components/safe_browsing/browser/url_checker_delegate.h"
+#include "components/safe_browsing/core/browser/url_checker_delegate.h"
 #include "content/public/browser/web_contents.h"
 
 namespace android_webview {
@@ -46,8 +46,7 @@
       bool is_main_frame,
       bool has_user_gesture) override;
   bool IsUrlWhitelisted(const GURL& url) override;
-  bool ShouldSkipRequestCheck(content::ResourceContext* resource_context,
-                              const GURL& original_url,
+  bool ShouldSkipRequestCheck(const GURL& original_url,
                               int frame_tree_node_id,
                               int render_process_id,
                               int render_frame_id,
diff --git a/android_webview/renderer/BUILD.gn b/android_webview/renderer/BUILD.gn
index 6b0d011f..c88f68f 100644
--- a/android_webview/renderer/BUILD.gn
+++ b/android_webview/renderer/BUILD.gn
@@ -47,9 +47,9 @@
     "//components/page_load_metrics/renderer",
     "//components/printing/common",
     "//components/printing/renderer",
-    "//components/safe_browsing:features",
-    "//components/safe_browsing/common",
-    "//components/safe_browsing/renderer:throttles",
+    "//components/safe_browsing/content/renderer:throttles",
+    "//components/safe_browsing/core:features",
+    "//components/safe_browsing/core/common",
     "//components/security_interstitials/content/renderer:security_interstitial_page_controller",
     "//components/security_interstitials/core",
     "//components/security_interstitials/core/common/mojom",
diff --git a/android_webview/renderer/DEPS b/android_webview/renderer/DEPS
index 2fa77a78..79803c2 100644
--- a/android_webview/renderer/DEPS
+++ b/android_webview/renderer/DEPS
@@ -9,9 +9,9 @@
   "+components/page_load_metrics/renderer",
   "+components/printing/common",
   "+components/printing/renderer",
-  "+components/safe_browsing/common",
-  "+components/safe_browsing/features.h",
-  "+components/safe_browsing/renderer",
+  "+components/safe_browsing/core/common",
+  "+components/safe_browsing/core/features.h",
+  "+components/safe_browsing/content/renderer",
   "+components/security_interstitials/core",
   "+components/security_interstitials/content/renderer",
   "+components/spellcheck/renderer",
diff --git a/android_webview/renderer/aw_url_loader_throttle_provider.cc b/android_webview/renderer/aw_url_loader_throttle_provider.cc
index a0934a6..a2605dc 100644
--- a/android_webview/renderer/aw_url_loader_throttle_provider.cc
+++ b/android_webview/renderer/aw_url_loader_throttle_provider.cc
@@ -7,8 +7,8 @@
 #include <memory>
 
 #include "base/feature_list.h"
-#include "components/safe_browsing/features.h"
-#include "components/safe_browsing/renderer/renderer_url_loader_throttle.h"
+#include "components/safe_browsing/content/renderer/renderer_url_loader_throttle.h"
+#include "components/safe_browsing/core/features.h"
 #include "content/public/common/content_features.h"
 #include "content/public/renderer/render_thread.h"
 
diff --git a/android_webview/renderer/aw_url_loader_throttle_provider.h b/android_webview/renderer/aw_url_loader_throttle_provider.h
index 48d7025..57b87d8 100644
--- a/android_webview/renderer/aw_url_loader_throttle_provider.h
+++ b/android_webview/renderer/aw_url_loader_throttle_provider.h
@@ -6,7 +6,7 @@
 #define ANDROID_WEBVIEW_RENDERER_AW_URL_LOADER_THROTTLE_PROVIDER_H_
 
 #include "base/threading/thread_checker.h"
-#include "components/safe_browsing/common/safe_browsing.mojom.h"
+#include "components/safe_browsing/core/common/safe_browsing.mojom.h"
 #include "content/public/renderer/url_loader_throttle_provider.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/remote.h"
diff --git a/android_webview/renderer/aw_websocket_handshake_throttle_provider.cc b/android_webview/renderer/aw_websocket_handshake_throttle_provider.cc
index 7ac7065..9fae185 100644
--- a/android_webview/renderer/aw_websocket_handshake_throttle_provider.cc
+++ b/android_webview/renderer/aw_websocket_handshake_throttle_provider.cc
@@ -7,8 +7,8 @@
 #include <utility>
 
 #include "base/feature_list.h"
-#include "components/safe_browsing/features.h"
-#include "components/safe_browsing/renderer/websocket_sb_handshake_throttle.h"
+#include "components/safe_browsing/content/renderer/websocket_sb_handshake_throttle.h"
+#include "components/safe_browsing/core/features.h"
 #include "content/public/common/content_features.h"
 #include "content/public/renderer/render_thread.h"
 #include "third_party/blink/public/platform/websocket_handshake_throttle.h"
diff --git a/android_webview/renderer/aw_websocket_handshake_throttle_provider.h b/android_webview/renderer/aw_websocket_handshake_throttle_provider.h
index 5a89f8e..ffbcf2b 100644
--- a/android_webview/renderer/aw_websocket_handshake_throttle_provider.h
+++ b/android_webview/renderer/aw_websocket_handshake_throttle_provider.h
@@ -9,7 +9,7 @@
 
 #include "base/macros.h"
 #include "base/threading/thread_checker.h"
-#include "components/safe_browsing/common/safe_browsing.mojom.h"
+#include "components/safe_browsing/core/common/safe_browsing.mojom.h"
 #include "content/public/renderer/websocket_handshake_throttle_provider.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/remote.h"
diff --git a/android_webview/tools/system_webview_shell/test/data/webexposed/not-webview-exposed.txt b/android_webview/tools/system_webview_shell/test/data/webexposed/not-webview-exposed.txt
index 6f0a395..cf64c9c4 100644
--- a/android_webview/tools/system_webview_shell/test/data/webexposed/not-webview-exposed.txt
+++ b/android_webview/tools/system_webview_shell/test/data/webexposed/not-webview-exposed.txt
@@ -39,6 +39,13 @@
 # remoteplayback api not supported in webview crbug.com/521319
 interface RemotePlayback : EventTarget
 
+# translation service is not implemented in webview which
+# hrefTranslate is gated on. If it does eventually expose it
+# it should call WebRuntimeFeatures.EnableTranslateService(...)
+interface HTMLAnchorElement : HTMLElement
+    getter hrefTranslate
+    setter hrefTranslate
+
 # web payments api not enabled in webview, crbug.com/667069
 interface HTMLIFrameElement : HTMLElement
     getter allowPaymentRequest
diff --git a/ash/public/cpp/window_properties.cc b/ash/public/cpp/window_properties.cc
index dfc8a7e3..d4117a6 100644
--- a/ash/public/cpp/window_properties.cc
+++ b/ash/public/cpp/window_properties.cc
@@ -46,6 +46,7 @@
 DEFINE_UI_CLASS_PROPERTY_KEY(WindowStateType,
                              kPrePipWindowStateTypeKey,
                              WindowStateType::kDefault)
+DEFINE_UI_CLASS_PROPERTY_KEY(bool, kPipOriginalWindowKey, false)
 DEFINE_UI_CLASS_PROPERTY_KEY(bool, kRenderTitleAreaProperty, false)
 DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(gfx::Rect,
                                    kRestoreBoundsOverrideKey,
diff --git a/ash/public/cpp/window_properties.h b/ash/public/cpp/window_properties.h
index 27bf037..f038f21 100644
--- a/ash/public/cpp/window_properties.h
+++ b/ash/public/cpp/window_properties.h
@@ -125,6 +125,20 @@
 ASH_PUBLIC_EXPORT extern const aura::WindowProperty<WindowStateType>* const
     kPrePipWindowStateTypeKey;
 
+// If true, the current PIP window is spawned from this window.
+// Android PIP has two types of behavior depending on how many activities the
+// original task has before entering PIP.
+// SAPIP(Single-activity PIP): If the original task has only one activity, PIP
+// can be handled as window state change of the target window. In this case, the
+// PIP original window is this exact PIP window.
+// MAPIP(Multi-activity PIP): If the original task has more than one activities,
+// a new window is created for PIP, which is a completely different one from
+// the existing window. This existing window is the original window of the
+// current PIP window in this case. This property is used, for example, to
+// calculated the position of the PIP window in the Alt-Tab window cycler.
+ASH_PUBLIC_EXPORT extern const aura::WindowProperty<bool>* const
+    kPipOriginalWindowKey;
+
 // Maps to ws::mojom::WindowManager::kRenderParentTitleArea_Property.
 ASH_PUBLIC_EXPORT extern const aura::WindowProperty<bool>* const
     kRenderTitleAreaProperty;
diff --git a/ash/system/status_area_widget_test_helper.cc b/ash/system/status_area_widget_test_helper.cc
index fbda12b..1f6c0d9 100644
--- a/ash/system/status_area_widget_test_helper.cc
+++ b/ash/system/status_area_widget_test_helper.cc
@@ -8,9 +8,42 @@
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
 #include "ash/system/status_area_widget.h"
+#include "base/run_loop.h"
+#include "ui/compositor/layer_animation_observer.h"
 
 namespace ash {
 
+// An observer that quits a run loop when the animation finishes.
+class AnimationEndObserver : public ui::LayerAnimationObserver {
+ public:
+  explicit AnimationEndObserver(ui::LayerAnimator* animator)
+      : animator_(animator) {
+    animator_->AddObserver(this);
+  }
+  ~AnimationEndObserver() override { animator_->RemoveObserver(this); }
+
+  void WaitForAnimationEnd() {
+    if (!animator_->is_animating())
+      return;
+    // This will return immediately if |Quit| was already called.
+    run_loop_.Run();
+  }
+
+  // ui::LayerAnimationObserver:
+  void OnLayerAnimationEnded(ui::LayerAnimationSequence* sequence) override {
+    run_loop_.Quit();
+  }
+
+  void OnLayerAnimationAborted(ui::LayerAnimationSequence* sequence) override {}
+
+  void OnLayerAnimationScheduled(
+      ui::LayerAnimationSequence* sequence) override {}
+
+ private:
+  ui::LayerAnimator* animator_;
+  base::RunLoop run_loop_;
+};
+
 LoginStatus StatusAreaWidgetTestHelper::GetUserLoginStatus() {
   return Shell::Get()->session_controller()->login_status();
 }
@@ -32,4 +65,10 @@
   return nullptr;
 }
 
+void StatusAreaWidgetTestHelper::WaitForAnimationEnd(
+    StatusAreaWidget* status_area_widget) {
+  AnimationEndObserver observer(status_area_widget->GetLayer()->GetAnimator());
+  observer.WaitForAnimationEnd();
+}
+
 }  // namespace ash
diff --git a/ash/system/status_area_widget_test_helper.h b/ash/system/status_area_widget_test_helper.h
index 2669b60..a9ccd58 100644
--- a/ash/system/status_area_widget_test_helper.h
+++ b/ash/system/status_area_widget_test_helper.h
@@ -22,6 +22,9 @@
   // Returns the StatusAreaWidget that appears on the secondary display.
   static StatusAreaWidget* GetSecondaryStatusAreaWidget();
 
+  // Waits until status area animations are over.
+  static void WaitForAnimationEnd(StatusAreaWidget* status_area_widget);
+
  private:
   DISALLOW_IMPLICIT_CONSTRUCTORS(StatusAreaWidgetTestHelper);
 };
diff --git a/ash/wm/mru_window_tracker.cc b/ash/wm/mru_window_tracker.cc
index b7699461..26057623 100644
--- a/ash/wm/mru_window_tracker.cc
+++ b/ash/wm/mru_window_tracker.cc
@@ -84,10 +84,15 @@
 }
 
 // A predicate that determines whether |window| can be included in the list
-// built for alt-tab cycling, including Android PIP windows.
+// built for alt-tab cycling, including one of the windows for Android PIP apps.
+// For single-activity PIP, the PIP window is included in the list. (in the case
+// of single-activity PIP, the PIP window is the same as the original window.)
+// For multi-activity PIP, the non-PIP activity is included in the list.
+// See the comment for |kPipOriginalWindowKey| for more detail.
 bool CanIncludeWindowInCycleWithPipList(aura::Window* window) {
   return CanIncludeWindowInCycleList(window) ||
-         window_util::IsArcPipWindow(window);
+         (window_util::IsArcPipWindow(window) &&
+          window->GetProperty(ash::kPipOriginalWindowKey));
 }
 
 // Returns a list of windows ordered by their stacking order such that the most
diff --git a/ash/wm/overview/overview_session.cc b/ash/wm/overview/overview_session.cc
index 570c514..ea61644 100644
--- a/ash/wm/overview/overview_session.cc
+++ b/ash/wm/overview/overview_session.cc
@@ -316,7 +316,10 @@
     }
   }
   item->EnsureVisible();
-
+  if (window->GetProperty(kPipOriginalWindowKey)) {
+    window_util::ExpandArcPipWindow();
+    return;
+  }
   // If the selected window is a minimized window, un-minimize it first before
   // activating it so that the window can use the scale-up animation instead of
   // un-minimizing animation. If minimized, the activation of the window will
diff --git a/ash/wm/window_cycle_list.cc b/ash/wm/window_cycle_list.cc
index 8a24cc8f..35a384b 100644
--- a/ash/wm/window_cycle_list.cc
+++ b/ash/wm/window_cycle_list.cc
@@ -10,6 +10,7 @@
 #include "ash/app_list/app_list_controller_impl.h"
 #include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/shell_window_ids.h"
+#include "ash/public/cpp/window_properties.h"
 #include "ash/shell.h"
 #include "ash/style/ash_color_provider.h"
 #include "ash/wm/mru_window_tracker.h"
@@ -621,12 +622,12 @@
   if (window_selected_)
     return;
 
-  window->Show();
-  auto* window_state = WindowState::Get(window);
-  if (window_util::IsArcPipWindow(window))
-    window_state->Restore();
-  else
-    window_state->Activate();
+  if (window->GetProperty(kPipOriginalWindowKey)) {
+    window_util::ExpandArcPipWindow();
+  } else {
+    window->Show();
+    WindowState::Get(window)->Activate();
+  }
 
   window_selected_ = true;
 }
diff --git a/ash/wm/window_util.cc b/ash/wm/window_util.cc
index 771a912..5c790877 100644
--- a/ash/wm/window_util.cc
+++ b/ash/wm/window_util.cc
@@ -310,5 +310,21 @@
   return IsArcWindow(window) && WindowState::Get(window)->IsPip();
 }
 
+void ExpandArcPipWindow() {
+  auto* pip_container = Shell::GetContainer(Shell::GetPrimaryRootWindow(),
+                                            kShellWindowId_PipContainer);
+  if (!pip_container)
+    return;
+
+  auto pip_window_iter = std::find_if(pip_container->children().begin(),
+                                      pip_container->children().end(),
+                                      window_util::IsArcPipWindow);
+  if (pip_window_iter == pip_container->children().end())
+    return;
+
+  auto* window_state = WindowState::Get(*pip_window_iter);
+  window_state->Restore();
+}
+
 }  // namespace window_util
 }  // namespace ash
diff --git a/ash/wm/window_util.h b/ash/wm/window_util.h
index 603b801f..3d4961574 100644
--- a/ash/wm/window_util.h
+++ b/ash/wm/window_util.h
@@ -112,6 +112,9 @@
 // Returns true if |window| is an ARC PIP window.
 ASH_EXPORT bool IsArcPipWindow(const aura::Window* window);
 
+// Expands the Android PIP window.
+ASH_EXPORT void ExpandArcPipWindow();
+
 }  // namespace window_util
 }  // namespace ash
 
diff --git a/base/profiler/chrome_unwinder_android.cc b/base/profiler/chrome_unwinder_android.cc
index c02cb665..9e5363f 100644
--- a/base/profiler/chrome_unwinder_android.cc
+++ b/base/profiler/chrome_unwinder_android.cc
@@ -31,7 +31,7 @@
     base::debug::ElfBuildIdBuffer build_id;
     size_t build_id_length =
         base::debug::ReadElfBuildId(&__executable_start, true, build_id);
-    DCHECK_GT(build_id_length, 0);
+    DCHECK_GT(build_id_length, 0u);
     // Append 0 for the age value.
     return std::string(build_id, build_id_length) + "0";
 #else
diff --git a/base/task/post_task.cc b/base/task/post_task.cc
index dce2f96..244e5f6 100644
--- a/base/task/post_task.cc
+++ b/base/task/post_task.cc
@@ -146,17 +146,4 @@
 }
 #endif  // defined(OS_WIN)
 
-const scoped_refptr<SequencedTaskRunner>& GetContinuationTaskRunner() {
-  TaskExecutor* executor = GetTaskExecutorForCurrentThread();
-  DCHECK(executor) << "Couldn't find a TaskExecutor for this thread. Note "
-                      "you can't use base::GetContinuationTaskRunner in "
-                      "a one-off base::ThreadPool task.";
-  const auto& task_runner = executor->GetContinuationTaskRunner();
-  DCHECK(task_runner)
-      << "The current execution context lacks a continuation task runner. "
-         "Note: you can't use base::GetContinuationTaskRunner() from a native "
-         "system event or any other context outside of a Chrome task.";
-  return task_runner;
-}
-
 }  // namespace base
diff --git a/base/task/post_task.h b/base/task/post_task.h
index 3de95ec..4579cbff 100644
--- a/base/task/post_task.h
+++ b/base/task/post_task.h
@@ -51,13 +51,6 @@
 //     task_runner->PostTask(FROM_HERE, BindOnce(...));
 //     task_runner->PostTask(FROM_HERE, BindOnce(...));
 //
-// To post a task on the current thread or sequence but with an explicit
-// priority:
-//     PostTask(FROM_HERE,
-//              {CurrentThread(), TaskPriority::BEST_EFFORT},
-//              BindOnce(...));
-//
-//
 // The default traits apply to tasks that:
 //     (1) don't block (ref. MayBlock() and WithBaseSyncPrimitives()),
 //     (2) prefer inheriting the current priority to specifying their own, and
@@ -239,15 +232,6 @@
         SingleThreadTaskRunnerThreadMode::SHARED);
 #endif  // defined(OS_WIN)
 
-// Returns: The SequencedTaskRunner for the currently executing task, if any.
-// Otherwise it returns a null scoped_refptr. On threads where there's no
-// TaskExecutor registered this will DCHECK e.g. in a one-off ThreadPool task.
-//
-// Experimental: Further discussions are in progress for this API. Please
-// continue using SequencedTaskRunnerHandle::Get() in the mean time.
-BASE_EXPORT const scoped_refptr<SequencedTaskRunner>&
-GetContinuationTaskRunner();
-
 // Helpers to send a Delete/ReleaseSoon to a new SequencedTaskRunner created
 // from |traits|. The semantics match base::PostTask in that the deletion is
 // guaranteed to be scheduled in order with other tasks using the same |traits|.
diff --git a/base/task/post_task_unittest.cc b/base/task/post_task_unittest.cc
index c1c9ac90..2130a8f 100644
--- a/base/task/post_task_unittest.cc
+++ b/base/task/post_task_unittest.cc
@@ -73,9 +73,6 @@
                    SingleThreadTaskRunnerThreadMode thread_mode));
 #endif  // defined(OS_WIN)
 
-  MOCK_METHOD0(GetContinuationTaskRunner,
-               const scoped_refptr<SequencedTaskRunner>&());
-
   TestSimpleTaskRunner* runner() const { return runner_.get(); }
 
  private:
@@ -282,49 +279,6 @@
   run_loop.Run();
 }
 
-TEST_F(PostTaskTestWithExecutor, TaskRunnerTaskGetContinuationTaskRunner) {
-  auto task_runner = CreateTaskRunner({ThreadPool()});
-  RunLoop run_loop;
-
-  EXPECT_TRUE(task_runner->PostTask(FROM_HERE, BindLambdaForTesting([&]() {
-                                      // GetContinuationTaskRunner is
-                                      // meaningless in this context.
-                                      EXPECT_DCHECK_DEATH(
-                                          GetContinuationTaskRunner());
-                                      run_loop.Quit();
-                                    })));
-
-  run_loop.Run();
-}
-
-TEST_F(PostTaskTestWithExecutor,
-       SequencedTaskRunnerTaskGetContinuationTaskRunner) {
-  auto sequenced_task_runner = CreateSequencedTaskRunner({ThreadPool()});
-  RunLoop run_loop;
-
-  EXPECT_TRUE(sequenced_task_runner->PostTask(
-      FROM_HERE, BindLambdaForTesting([&]() {
-        EXPECT_EQ(GetContinuationTaskRunner(), sequenced_task_runner);
-        run_loop.Quit();
-      })));
-
-  run_loop.Run();
-}
-
-TEST_F(PostTaskTestWithExecutor,
-       SingleThreadTaskRunnerTaskGetContinuationTaskRunner) {
-  auto single_thread_task_runner = CreateSingleThreadTaskRunner({ThreadPool()});
-  RunLoop run_loop;
-
-  EXPECT_TRUE(single_thread_task_runner->PostTask(
-      FROM_HERE, BindLambdaForTesting([&]() {
-        EXPECT_EQ(GetContinuationTaskRunner(), single_thread_task_runner);
-        run_loop.Quit();
-      })));
-
-  run_loop.Run();
-}
-
 TEST_F(PostTaskTestWithExecutor, ThreadPoolCurrentThreadChangePriority) {
   auto single_thread_task_runner =
       CreateSingleThreadTaskRunner({ThreadPool(), TaskPriority::USER_BLOCKING});
diff --git a/base/task/sequence_manager/sequence_manager.h b/base/task/sequence_manager/sequence_manager.h
index 13cebbb..9710bf8f 100644
--- a/base/task/sequence_manager/sequence_manager.h
+++ b/base/task/sequence_manager/sequence_manager.h
@@ -144,8 +144,7 @@
 
   // Returns the task runner the current task was posted on. Returns null if no
   // task is currently running. Must be called on the bound thread.
-  virtual const scoped_refptr<SequencedTaskRunner>&
-  GetTaskRunnerForCurrentTask() = 0;
+  virtual scoped_refptr<SequencedTaskRunner> GetTaskRunnerForCurrentTask() = 0;
 
   // Finishes the initialization for a SequenceManager created via
   // CreateUnboundSequenceManager(). Must not be called in any other
diff --git a/base/task/sequence_manager/sequence_manager_impl.cc b/base/task/sequence_manager/sequence_manager_impl.cc
index 616944f..fcdb8d1 100644
--- a/base/task/sequence_manager/sequence_manager_impl.cc
+++ b/base/task/sequence_manager/sequence_manager_impl.cc
@@ -42,12 +42,6 @@
   return lazy_tls_ptr.get();
 }
 
-const scoped_refptr<SequencedTaskRunner>& GetNullTaskRunner() {
-  static const base::NoDestructor<scoped_refptr<SequencedTaskRunner>>
-      null_task_runner;
-  return *null_task_runner;
-}
-
 }  // namespace
 
 // This controls how big the the initial for
@@ -340,11 +334,11 @@
   BindToMessagePump(std::move(pump));
 }
 
-const scoped_refptr<SequencedTaskRunner>&
+scoped_refptr<SequencedTaskRunner>
 SequenceManagerImpl::GetTaskRunnerForCurrentTask() {
   DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
   if (main_thread_only().task_execution_stack.empty())
-    return GetNullTaskRunner();
+    return nullptr;
   return main_thread_only()
       .task_execution_stack.back()
       .pending_task.task_runner;
diff --git a/base/task/sequence_manager/sequence_manager_impl.h b/base/task/sequence_manager/sequence_manager_impl.h
index bd3babc..37141b7 100644
--- a/base/task/sequence_manager/sequence_manager_impl.h
+++ b/base/task/sequence_manager/sequence_manager_impl.h
@@ -102,8 +102,7 @@
 
   // SequenceManager implementation:
   void BindToCurrentThread() override;
-  const scoped_refptr<SequencedTaskRunner>& GetTaskRunnerForCurrentTask()
-      override;
+  scoped_refptr<SequencedTaskRunner> GetTaskRunnerForCurrentTask() override;
   void BindToMessagePump(std::unique_ptr<MessagePump> message_pump) override;
   void SetObserver(Observer* observer) override;
   void AddTaskTimeObserver(TaskTimeObserver* task_time_observer) override;
diff --git a/base/task/simple_task_executor.cc b/base/task/simple_task_executor.cc
index c4d7d2f..ffccf52 100644
--- a/base/task/simple_task_executor.cc
+++ b/base/task/simple_task_executor.cc
@@ -4,16 +4,11 @@
 
 #include "base/task/simple_task_executor.h"
 
-#include "base/task/sequence_manager/sequence_manager.h"
-
 namespace base {
 
 SimpleTaskExecutor::SimpleTaskExecutor(
-    sequence_manager::SequenceManager* sequence_manager,
     scoped_refptr<SingleThreadTaskRunner> task_queue)
-    : sequence_manager_(sequence_manager),
-      sequenced_task_queue_(task_queue),
-      task_queue_(std::move(task_queue)),
+    : task_queue_(std::move(task_queue)),
       previous_task_executor_(GetTaskExecutorForCurrentThread()) {
   DCHECK(task_queue_);
   // The TaskExecutor API does not expect nesting, but this can happen in tests
@@ -43,7 +38,7 @@
 
 scoped_refptr<SequencedTaskRunner>
 SimpleTaskExecutor::CreateSequencedTaskRunner(const TaskTraits& traits) {
-  return sequenced_task_queue_;
+  return task_queue_;
 }
 
 scoped_refptr<SingleThreadTaskRunner>
@@ -64,11 +59,4 @@
 }
 #endif  // defined(OS_WIN)
 
-const scoped_refptr<SequencedTaskRunner>&
-SimpleTaskExecutor::GetContinuationTaskRunner() {
-  if (sequence_manager_)
-    return sequence_manager_->GetTaskRunnerForCurrentTask();
-  return sequenced_task_queue_;
-}
-
 }  // namespace base
diff --git a/base/task/simple_task_executor.h b/base/task/simple_task_executor.h
index be6531f..7d9a74dc 100644
--- a/base/task/simple_task_executor.h
+++ b/base/task/simple_task_executor.h
@@ -9,19 +9,13 @@
 #include "build/build_config.h"
 
 namespace base {
-namespace sequence_manager {
-class SequenceManager;
-}  // namespace sequence_manager
 
 // A simple TaskExecutor with exactly one SingleThreadTaskRunner.
 // Must be instantiated and destroyed on the thread that runs tasks for the
 // SingleThreadTaskRunner.
 class BASE_EXPORT SimpleTaskExecutor : public TaskExecutor {
  public:
-  // If |sequence_manager| is null, GetContinuationTaskRunner will always return
-  // |task_queue| even if no task is running.
-  SimpleTaskExecutor(sequence_manager::SequenceManager* sequence_manager,
-                     scoped_refptr<SingleThreadTaskRunner> task_queue);
+  explicit SimpleTaskExecutor(scoped_refptr<SingleThreadTaskRunner> task_queue);
 
   ~SimpleTaskExecutor() override;
 
@@ -45,12 +39,7 @@
       SingleThreadTaskRunnerThreadMode thread_mode) override;
 #endif  // defined(OS_WIN)
 
-  const scoped_refptr<SequencedTaskRunner>& GetContinuationTaskRunner()
-      override;
-
- protected:
-  sequence_manager::SequenceManager* const sequence_manager_;
-  const scoped_refptr<SequencedTaskRunner> sequenced_task_queue_;
+ private:
   const scoped_refptr<SingleThreadTaskRunner> task_queue_;
 
   // In tests there may already be a TaskExecutor registered for the thread, we
diff --git a/base/task/single_thread_task_executor.cc b/base/task/single_thread_task_executor.cc
index c88435a..3014bde 100644
--- a/base/task/single_thread_task_executor.cc
+++ b/base/task/single_thread_task_executor.cc
@@ -31,7 +31,7 @@
       default_task_queue_(sequence_manager_->CreateTaskQueue(
           sequence_manager::TaskQueue::Spec("default_tq"))),
       type_(type),
-      simple_task_executor_(sequence_manager_.get(), task_runner()) {
+      simple_task_executor_(task_runner()) {
   sequence_manager_->SetDefaultTaskRunner(default_task_queue_->task_runner());
   sequence_manager_->BindToMessagePump(std::move(pump));
 }
diff --git a/base/task/single_thread_task_executor_unittest.cc b/base/task/single_thread_task_executor_unittest.cc
index b69bbf3cb..b64294f 100644
--- a/base/task/single_thread_task_executor_unittest.cc
+++ b/base/task/single_thread_task_executor_unittest.cc
@@ -7,7 +7,6 @@
 #include "base/run_loop.h"
 #include "base/task/post_task.h"
 #include "base/test/bind_test_util.h"
-#include "base/test/gtest_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -56,24 +55,4 @@
                 {base::CurrentThread(), base::TaskPriority::BEST_EFFORT}));
 }
 
-TEST(SingleThreadTaskExecutorTest, GetContinuationTaskRunner) {
-  SingleThreadTaskExecutor single_thread_task_executor;
-  RunLoop run_loop;
-
-  auto task_runner = CreateSingleThreadTaskRunner({CurrentThread()});
-
-  task_runner->PostTask(FROM_HERE, BindLambdaForTesting([&]() {
-                          EXPECT_EQ(task_runner, GetContinuationTaskRunner());
-                          run_loop.Quit();
-                        }));
-
-  run_loop.Run();
-}
-
-TEST(SingleThreadTaskExecutorTest, GetCurrentTaskWithNoTaskRunning) {
-  SingleThreadTaskExecutor single_thread_task_executor;
-
-  EXPECT_DCHECK_DEATH(GetContinuationTaskRunner());
-}
-
 }  // namespace base
diff --git a/base/task/task_executor.h b/base/task/task_executor.h
index 77debc9..a062fdc 100644
--- a/base/task/task_executor.h
+++ b/base/task/task_executor.h
@@ -63,11 +63,6 @@
       const TaskTraits& traits,
       SingleThreadTaskRunnerThreadMode thread_mode) = 0;
 #endif  // defined(OS_WIN)
-
-  // Returns the sequence the current task was posted on, if any, or null
-  // otherwise (e.g. for parallel tasks).
-  virtual const scoped_refptr<SequencedTaskRunner>&
-  GetContinuationTaskRunner() = 0;
 };
 
 // Register a TaskExecutor with the //base/task/post_task.h API in the current
diff --git a/base/task/thread_pool/task_tracker.cc b/base/task/thread_pool/task_tracker.cc
index a324d21..ea1a9c8 100644
--- a/base/task/thread_pool/task_tracker.cc
+++ b/base/task/thread_pool/task_tracker.cc
@@ -138,20 +138,18 @@
              switches::kLogBestEffortTasks);
 }
 
-// Needed for GetContinuationTaskRunner and CurrentThread. This executor lives
-// for the duration of a threadpool task invocation.
+// Needed for PostTaskHere and CurrentThread. This executor lives for the
+// duration of a threadpool task invocation.
 class EphemeralTaskExecutor : public TaskExecutor {
  public:
   // |sequenced_task_runner| and |single_thread_task_runner| must outlive this
-  // EphemeralTaskExecutor. Note |single_thread_task_runner| may be null.
-  EphemeralTaskExecutor(
-      scoped_refptr<SequencedTaskRunner> sequenced_task_runner,
-      SingleThreadTaskRunner* single_thread_task_runner,
-      const TaskTraits* sequence_traits)
-      : sequenced_task_runner_(std::move(sequenced_task_runner)),
+  // EphemeralTaskExecutor.
+  EphemeralTaskExecutor(SequencedTaskRunner* sequenced_task_runner,
+                        SingleThreadTaskRunner* single_thread_task_runner,
+                        const TaskTraits* sequence_traits)
+      : sequenced_task_runner_(sequenced_task_runner),
         single_thread_task_runner_(single_thread_task_runner),
         sequence_traits_(sequence_traits) {
-    DCHECK(sequenced_task_runner_);
     SetTaskExecutorForCurrentThread(this);
   }
 
@@ -197,11 +195,6 @@
   }
 #endif  // defined(OS_WIN)
 
-  const scoped_refptr<SequencedTaskRunner>& GetContinuationTaskRunner()
-      override {
-    return sequenced_task_runner_;
-  }
-
  private:
   // Currently ignores |traits.priority()|.
   void CheckTraitsCompatibleWithSequenceTraits(const TaskTraits& traits) {
@@ -218,7 +211,7 @@
                sequence_traits_->with_base_sync_primitives());
   }
 
-  const scoped_refptr<SequencedTaskRunner> sequenced_task_runner_;
+  SequencedTaskRunner* const sequenced_task_runner_;
   SingleThreadTaskRunner* const single_thread_task_runner_;
   const TaskTraits* const sequence_traits_;
 };
diff --git a/base/task/thread_pool/thread_pool_impl.cc b/base/task/thread_pool/thread_pool_impl.cc
index 7e49ee8..a56552a 100644
--- a/base/task/thread_pool/thread_pool_impl.cc
+++ b/base/task/thread_pool/thread_pool_impl.cc
@@ -61,12 +61,6 @@
              switches::kDisableBestEffortTasks);
 }
 
-const scoped_refptr<SequencedTaskRunner>& GetNullTaskRunner() {
-  static const NoDestructor<scoped_refptr<SequencedTaskRunner>>
-      null_task_runner;
-  return *null_task_runner;
-}
-
 }  // namespace
 
 ThreadPoolImpl::ThreadPoolImpl(StringPiece histogram_label)
@@ -279,14 +273,6 @@
   return MakeRefCounted<PooledSequencedTaskRunner>(new_traits, this);
 }
 
-const scoped_refptr<SequencedTaskRunner>&
-ThreadPoolImpl::GetContinuationTaskRunner() {
-  // Default to null for parallel tasks; see task_tracker.cc's
-  // EphemeralTaskExecutor for how sequenced contexts handle this.
-  NOTREACHED();
-  return GetNullTaskRunner();
-}
-
 Optional<TimeTicks> ThreadPoolImpl::NextScheduledRunTimeForTesting() const {
   if (task_tracker_->HasIncompleteTaskSourcesForTesting())
     return TimeTicks::Now();
diff --git a/base/task/thread_pool/thread_pool_impl.h b/base/task/thread_pool/thread_pool_impl.h
index b4cfc49ac..82c76a2 100644
--- a/base/task/thread_pool/thread_pool_impl.h
+++ b/base/task/thread_pool/thread_pool_impl.h
@@ -100,8 +100,6 @@
       const TaskTraits& traits,
       SingleThreadTaskRunnerThreadMode thread_mode) override;
 #endif  // defined(OS_WIN)
-  const scoped_refptr<SequencedTaskRunner>& GetContinuationTaskRunner()
-      override;
   scoped_refptr<UpdateableSequencedTaskRunner>
   CreateUpdateableSequencedTaskRunner(const TaskTraits& traits);
 
diff --git a/base/test/task_environment.cc b/base/test/task_environment.cc
index 80a85e2..2f730e6 100644
--- a/base/test/task_environment.cc
+++ b/base/test/task_environment.cc
@@ -403,8 +403,7 @@
             .SetTimeDomain(mock_time_domain_.get()));
     task_runner_ = task_queue_->task_runner();
     sequence_manager_->SetDefaultTaskRunner(task_runner_);
-    simple_task_executor_ = std::make_unique<SimpleTaskExecutor>(
-        sequence_manager_.get(), task_runner_);
+    simple_task_executor_ = std::make_unique<SimpleTaskExecutor>(task_runner_);
     CHECK(base::ThreadTaskRunnerHandle::IsSet())
         << "ThreadTaskRunnerHandle should've been set now.";
     CompleteInitialization();
diff --git a/base/test/task_environment.h b/base/test/task_environment.h
index daae7d6..01d19d9 100644
--- a/base/test/task_environment.h
+++ b/base/test/task_environment.h
@@ -36,7 +36,7 @@
 // This header exposes SingleThreadTaskEnvironment and TaskEnvironment.
 //
 // SingleThreadTaskEnvironment enables the following APIs within its scope:
-//  - ThreadTaskRunnerHandle & GetContinuationTaskRunner on the main thread
+//  - (Thread|Sequenced)TaskRunnerHandle on the main thread
 //  - RunLoop on the main thread
 //
 // TaskEnvironment additionally enables:
@@ -47,7 +47,7 @@
 // Tests should prefer SingleThreadTaskEnvironment over TaskEnvironment when the
 // former is sufficient.
 //
-// Tasks posted to the APIs listed above run synchronously when
+// Tasks posted to the (Thread|Sequenced)TaskRunnerHandle run synchronously when
 // RunLoop::Run(UntilIdle) or TaskEnvironment::RunUntilIdle is called on the
 // main thread.
 //
@@ -185,7 +185,7 @@
             trait_helpers::NotATraitTag()) {}
 
   // Waits until no undelayed ThreadPool tasks remain. Then, unregisters the
-  // ThreadPoolInstance and the APIs listed in the top level comment.
+  // ThreadPoolInstance and the (Thread|Sequenced)TaskRunnerHandle.
   virtual ~TaskEnvironment();
 
   // Returns a TaskRunner that schedules tasks on the main thread.
@@ -195,11 +195,11 @@
   // always return true if called right after RunUntilIdle.
   bool MainThreadIsIdle() const;
 
-  // Runs tasks until both the APIs listed in the top level comment and the
-  // ThreadPool's non-delayed queues are empty. While RunUntilIdle() is quite
-  // practical and sometimes even necessary -- for example, to flush all tasks
-  // bound to Unretained() state before destroying test members -- it should be
-  // used with caution per the following warnings:
+  // Runs tasks until both the (Thread|Sequenced)TaskRunnerHandle and the
+  // ThreadPool's non-delayed queues are empty.
+  // While RunUntilIdle() is quite practical and sometimes even necessary -- for
+  // example, to flush all tasks bound to Unretained() state before destroying
+  // test members -- it should be used with caution per the following warnings:
   //
   // WARNING #1: This may run long (flakily timeout) and even never return! Do
   //             not use this when repeating tasks such as animated web pages
diff --git a/base/test/task_environment_unittest.cc b/base/test/task_environment_unittest.cc
index 03b35465..e6a929e 100644
--- a/base/test/task_environment_unittest.cc
+++ b/base/test/task_environment_unittest.cc
@@ -1199,23 +1199,5 @@
   run_loop.Run();
 }
 
-TEST_F(TaskEnvironmentTest, GetContinuationTaskRunner) {
-  SingleThreadTaskEnvironment task_environment;
-  RunLoop run_loop;
-  auto task_runner = CreateSingleThreadTaskRunner({CurrentThread()});
-
-  task_runner->PostTask(FROM_HERE, BindLambdaForTesting([&]() {
-                          EXPECT_EQ(task_runner, GetContinuationTaskRunner());
-                          run_loop.Quit();
-                        }));
-
-  run_loop.Run();
-}
-
-TEST_F(TaskEnvironmentTest, GetContinuationTaskRunnerWithNoTaskRunning) {
-  SingleThreadTaskEnvironment task_environment;
-  EXPECT_DCHECK_DEATH(GetContinuationTaskRunner());
-}
-
 }  // namespace test
 }  // namespace base
diff --git a/base/threading/thread.cc b/base/threading/thread.cc
index 093d8842..eecd3491 100644
--- a/base/threading/thread.cc
+++ b/base/threading/thread.cc
@@ -87,8 +87,7 @@
     sequence_manager_->BindToMessagePump(
         std::move(message_pump_factory_).Run());
     sequence_manager_->SetTimerSlack(timer_slack);
-    simple_task_executor_.emplace(sequence_manager_.get(),
-                                  GetDefaultTaskRunner());
+    simple_task_executor_.emplace(GetDefaultTaskRunner());
   }
 
  private:
diff --git a/build/.style.yapf b/build/.style.yapf
new file mode 100644
index 0000000..b4ebbe2
--- /dev/null
+++ b/build/.style.yapf
@@ -0,0 +1,6 @@
+[style]
+based_on_style = pep8
+
+# New directories should use a .style.yapf that does not include the following:
+column_limit = 80
+indent_width = 2
diff --git a/build/android/.style.yapf b/build/android/.style.yapf
deleted file mode 100644
index ef24bfc6..0000000
--- a/build/android/.style.yapf
+++ /dev/null
@@ -1,6 +0,0 @@
-[style]
-based_on_style = pep8
-column_limit = 80
-blank_line_before_nested_class_or_def = true
-blank_line_before_module_docstring = true
-indent_width = 2
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index 2354f1b9..3117a2e 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -1545,6 +1545,9 @@
           cflags += [
             # TODO(https://crbug.com/1031169): Clean up and enable.
             "-Wno-misleading-indentation",
+
+            # TODO(https://crbug.com/1039697): Evaluate and possibly enable.
+            "-Wno-range-loop-analysis",
           ]
         }
       }
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 92175fd8..af5795d 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-8892005059433364560
\ No newline at end of file
+8891914193744321072
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index a9329ac..fd9468f 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-8892005057771346480
\ No newline at end of file
+8891914193383482512
\ No newline at end of file
diff --git a/cc/input/input_handler.h b/cc/input/input_handler.h
index b1a0476..8e9a06b 100644
--- a/cc/input/input_handler.h
+++ b/cc/input/input_handler.h
@@ -172,10 +172,6 @@
   virtual ScrollStatus RootScrollBegin(ScrollState* scroll_state,
                                        ScrollInputType type) = 0;
 
-  // Returns SCROLL_ON_IMPL_THREAD if a layer is actively being scrolled or
-  // a subsequent call to ScrollAnimated can begin on the impl thread.
-  virtual ScrollStatus ScrollAnimatedBegin(ScrollState* scroll_state) = 0;
-
   // |delayed_by| is the delay that is taken into account when determining
   // the duration of the animation. TODO(bokan): Should eventually be merged
   // into ScrollBy. https://crbug.com/1016229.
diff --git a/cc/layers/picture_layer_impl.cc b/cc/layers/picture_layer_impl.cc
index e4c8eb9..7c535bd 100644
--- a/cc/layers/picture_layer_impl.cc
+++ b/cc/layers/picture_layer_impl.cc
@@ -1127,12 +1127,12 @@
 
     float min_scale = MinimumContentsScale();
     float max_scale = std::max(1.f, MinimumContentsScale());
-    float clamped_ideal_source_scale_ =
+    float clamped_ideal_source_scale =
         base::ClampToRange(ideal_source_scale_, min_scale, max_scale);
 
-    while (raster_source_scale_ < clamped_ideal_source_scale_)
+    while (raster_source_scale_ < clamped_ideal_source_scale)
       raster_source_scale_ *= 2.f;
-    while (raster_source_scale_ > 4 * clamped_ideal_source_scale_)
+    while (raster_source_scale_ > 4 * clamped_ideal_source_scale)
       raster_source_scale_ /= 2.f;
 
     raster_source_scale_ =
diff --git a/cc/layers/viewport.cc b/cc/layers/viewport.cc
index 635d999..ee1048a5 100644
--- a/cc/layers/viewport.cc
+++ b/cc/layers/viewport.cc
@@ -73,7 +73,7 @@
                          const ScrollState& scroll_state) const {
   DCHECK(ShouldScroll(node));
 
-  bool result = host_impl_->CanConsumeDelta(*InnerScrollNode(), scroll_state);
+  bool result = host_impl_->CanConsumeDelta(scroll_state, *InnerScrollNode());
 
   // If the passed in node is the inner viewport, we're not interested in the
   // scrollability of the outer viewport. See LTHI::GetNodeToScroll for how the
@@ -81,7 +81,7 @@
   if (node.scrolls_inner_viewport)
     return result;
 
-  result |= host_impl_->CanConsumeDelta(*OuterScrollNode(), scroll_state);
+  result |= host_impl_->CanConsumeDelta(scroll_state, *OuterScrollNode());
   return result;
 }
 
diff --git a/cc/paint/paint_canvas.h b/cc/paint/paint_canvas.h
index 31a736d..ea019b0f 100644
--- a/cc/paint/paint_canvas.h
+++ b/cc/paint/paint_canvas.h
@@ -180,7 +180,7 @@
   virtual void drawPicture(sk_sp<const PaintRecord> record) = 0;
 
   virtual bool isClipEmpty() const = 0;
-  virtual const SkMatrix& getTotalMatrix() const = 0;
+  virtual SkMatrix getTotalMatrix() const = 0;
 
   // Used for printing
   enum class AnnotationType {
diff --git a/cc/paint/record_paint_canvas.cc b/cc/paint/record_paint_canvas.cc
index b6c58cb..fee7f29 100644
--- a/cc/paint/record_paint_canvas.cc
+++ b/cc/paint/record_paint_canvas.cc
@@ -296,7 +296,7 @@
   return GetCanvas()->isClipEmpty();
 }
 
-const SkMatrix& RecordPaintCanvas::getTotalMatrix() const {
+SkMatrix RecordPaintCanvas::getTotalMatrix() const {
   return GetCanvas()->getTotalMatrix();
 }
 
diff --git a/cc/paint/record_paint_canvas.h b/cc/paint/record_paint_canvas.h
index 0a33a9c..ecc3860 100644
--- a/cc/paint/record_paint_canvas.h
+++ b/cc/paint/record_paint_canvas.h
@@ -102,7 +102,7 @@
   void drawPicture(sk_sp<const PaintRecord> record) override;
 
   bool isClipEmpty() const override;
-  const SkMatrix& getTotalMatrix() const override;
+  SkMatrix getTotalMatrix() const override;
 
   void Annotate(AnnotationType type,
                 const SkRect& rect,
diff --git a/cc/paint/skia_paint_canvas.cc b/cc/paint/skia_paint_canvas.cc
index 72d34e9..f143d9e 100644
--- a/cc/paint/skia_paint_canvas.cc
+++ b/cc/paint/skia_paint_canvas.cc
@@ -328,7 +328,7 @@
   return canvas_->isClipEmpty();
 }
 
-const SkMatrix& SkiaPaintCanvas::getTotalMatrix() const {
+SkMatrix SkiaPaintCanvas::getTotalMatrix() const {
   return canvas_->getTotalMatrix();
 }
 
diff --git a/cc/paint/skia_paint_canvas.h b/cc/paint/skia_paint_canvas.h
index 3dc12517..e76805b 100644
--- a/cc/paint/skia_paint_canvas.h
+++ b/cc/paint/skia_paint_canvas.h
@@ -126,7 +126,7 @@
   void drawPicture(sk_sp<const PaintRecord> record) override;
 
   bool isClipEmpty() const override;
-  const SkMatrix& getTotalMatrix() const override;
+  SkMatrix getTotalMatrix() const override;
 
   void Annotate(AnnotationType type,
                 const SkRect& rect,
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 7c2a68b..7eb7eba3 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -3637,7 +3637,6 @@
 }
 
 InputHandler::ScrollStatus LayerTreeHostImpl::TryScroll(
-    const gfx::PointF& screen_space_point,
     const ScrollTree& scroll_tree,
     ScrollNode* scroll_node) const {
   InputHandler::ScrollStatus scroll_status;
@@ -3771,8 +3770,7 @@
          scroll_node = scroll_tree.parent(scroll_node)) {
       // The content layer can also block attempts to scroll outside the main
       // thread.
-      ScrollStatus status =
-          TryScroll(device_viewport_point, scroll_tree, scroll_node);
+      ScrollStatus status = TryScroll(scroll_tree, scroll_node);
       if (IsMainThreadScrolling(status, scroll_node)) {
         *scroll_on_main_thread = true;
         *main_thread_scrolling_reasons = status.main_thread_scrolling_reasons;
@@ -3806,8 +3804,7 @@
   impl_scroll_node = GetNodeToScroll(impl_scroll_node);
 
   // Ensure that final scroll node scrolls on impl thread (crbug.com/625100)
-  ScrollStatus status =
-      TryScroll(device_viewport_point, scroll_tree, impl_scroll_node);
+  ScrollStatus status = TryScroll(scroll_tree, impl_scroll_node);
   if (IsMainThreadScrolling(status, impl_scroll_node)) {
     *scroll_on_main_thread = true;
     *main_thread_scrolling_reasons = status.main_thread_scrolling_reasons;
@@ -3820,73 +3817,6 @@
   return impl_scroll_node;
 }
 
-InputHandler::ScrollStatus LayerTreeHostImpl::ScrollBeginImpl(
-    ScrollState* scroll_state,
-    ScrollNode* scrolling_node,
-    InputHandler::ScrollInputType type) {
-  DCHECK(scroll_state);
-  DCHECK(scroll_state->delta_x() == 0 && scroll_state->delta_y() == 0);
-
-  InputHandler::ScrollStatus scroll_status;
-  scroll_status.main_thread_scrolling_reasons =
-      MainThreadScrollingReason::kNotScrollingOnMain;
-  if (!scrolling_node) {
-    if (settings_.is_layer_tree_for_subframe) {
-      TRACE_EVENT_INSTANT0("cc", "Ignored - No ScrollNode (OOPIF)",
-                           TRACE_EVENT_SCOPE_THREAD);
-      scroll_status.thread = SCROLL_UNKNOWN;
-    } else {
-      TRACE_EVENT_INSTANT0("cc", "Ignroed - No ScrollNode",
-                           TRACE_EVENT_SCOPE_THREAD);
-      scroll_status.thread = SCROLL_IGNORED;
-    }
-    scroll_status.main_thread_scrolling_reasons =
-        MainThreadScrollingReason::kNoScrollingLayer;
-    return scroll_status;
-  }
-  scroll_status.thread = SCROLL_ON_IMPL_THREAD;
-  mutator_host_->ScrollAnimationAbort();
-  scroll_animating_snap_target_ids_ = TargetSnapAreaElementIds();
-
-  browser_controls_offset_manager_->ScrollBegin();
-
-  wheel_scrolling_ = type == InputHandler::WHEEL;
-  middle_click_autoscrolling_ = type == InputHandler::AUTOSCROLL;
-
-  LatchToScroller(scroll_state, scrolling_node);
-
-  // If the CurrentlyScrollingNode doesn't exist after distributing scroll
-  // delta, no scroller can scroll in the given delta hint direction(s).
-  if (!active_tree_->CurrentlyScrollingNode()) {
-    TRACE_EVENT_INSTANT0("cc", "Ignored - Didnt Scroll",
-                         TRACE_EVENT_SCOPE_THREAD);
-    scroll_status.thread = InputHandler::SCROLL_IGNORED;
-    scroll_status.main_thread_scrolling_reasons =
-        MainThreadScrollingReason::kNotScrollingOnMain;
-    return scroll_status;
-  }
-
-  // If the viewport is scrolling and it cannot consume any delta hints, the
-  // scroll event will need to get bubbled if the viewport is for a guest or
-  // oopif.
-  if (ScrollNode* node = active_tree_->CurrentlyScrollingNode()) {
-    if (viewport().ShouldScroll(*node) &&
-        !viewport().CanScroll(*node, *scroll_state)) {
-      scroll_status.bubble = true;
-    }
-  }
-
-  frame_trackers_.StartSequence(wheel_scrolling_
-                                    ? FrameSequenceTrackerType::kWheelScroll
-                                    : FrameSequenceTrackerType::kTouchScroll);
-  client_->RenewTreePriority();
-  RecordCompositorSlowScrollMetric(type, CC_THREAD);
-
-  UpdateScrollSourceInfo(type, scroll_state);
-
-  return scroll_status;
-}
-
 InputHandler::ScrollStatus LayerTreeHostImpl::RootScrollBegin(
     ScrollState* scroll_state,
     InputHandler::ScrollInputType type) {
@@ -3907,28 +3837,61 @@
 InputHandler::ScrollStatus LayerTreeHostImpl::ScrollBegin(
     ScrollState* scroll_state,
     InputHandler::ScrollInputType type) {
+  DCHECK(scroll_state);
+  DCHECK(scroll_state->delta_x() == 0 && scroll_state->delta_y() == 0);
+
   ScrollStatus scroll_status;
   scroll_status.main_thread_scrolling_reasons =
       MainThreadScrollingReason::kNotScrollingOnMain;
   TRACE_EVENT0("cc", "LayerTreeHostImpl::ScrollBegin");
 
+  // If this ScrollBegin is non-animated then ensure we cancel any ongoing
+  // animated scrolls.
+  // TODO(bokan): This preserves existing behavior when we had diverging
+  // paths for animated and non-animated scrolls but we should probably
+  // decide when it best makes sense to cancel a scroll animation (maybe
+  // ScrollBy is a better place to do it).
+  if (scroll_state->delta_granularity() ==
+      static_cast<double>(
+          ui::input_types::ScrollGranularity::kScrollByPrecisePixel)) {
+    mutator_host_->ScrollAnimationAbort();
+    scroll_animating_snap_target_ids_ = TargetSnapAreaElementIds();
+  }
+
+  if (CurrentlyScrollingNode()) {
+    // It's possible we haven't yet cleared the CurrentlyScrollingNode if we
+    // received a GSE but we're still animating the last scroll. If that's the
+    // case, we'll simply un-defer the GSE and continue latching to the same
+    // node.
+    DCHECK(deferred_scroll_end_);
+    deferred_scroll_end_ = false;
+    return scroll_status;
+  }
+
   ScrollNode* scrolling_node = nullptr;
   bool scroll_on_main_thread = false;
 
-  // If this element is specified, the caller already knows which scroller they
-  // want to target so skip all the hit testing bits.
-  ElementId current_native_scrolling_element =
-      scroll_state->data()->current_native_scrolling_element();
-  if (current_native_scrolling_element) {
-    auto& scroll_tree = active_tree_->property_trees()->scroll_tree;
-    scrolling_node =
-        scroll_tree.FindNodeFromElementId(current_native_scrolling_element);
-  } else if (!scrolling_node) {
-    // TODO(bokan): ClearCurrentlyScrollingNode shouldn't happen in
-    // ScrollBegin, this should only happen in ScrollEnd. We should DCHECK here
-    // that the state is cleared instead. https://crbug.com/1016229
-    ClearCurrentlyScrollingNode();
+  // TODO(bokan): ClearCurrentlyScrollingNode shouldn't happen in ScrollBegin,
+  // this should only happen in ScrollEnd. We should DCHECK here that the state
+  // is cleared instead. https://crbug.com/1016229
+  ClearCurrentlyScrollingNode();
 
+  if (auto specified_element_id =
+          scroll_state->data()->current_native_scrolling_element()) {
+    // If the caller passed in an element_id we can skip all the hit-testing
+    // bits and provide a node straight-away.
+    auto& scroll_tree = active_tree_->property_trees()->scroll_tree;
+    scrolling_node = scroll_tree.FindNodeFromElementId(specified_element_id);
+
+    // We still need to confirm the targeted node exists and can scroll on the
+    // compositor.
+    if (scrolling_node) {
+      scroll_status = TryScroll(active_tree_->property_trees()->scroll_tree,
+                                scrolling_node);
+      if (IsMainThreadScrolling(scroll_status, scrolling_node))
+        scroll_on_main_thread = true;
+    }
+  } else {
     gfx::Point viewport_point(scroll_state->position_x(),
                               scroll_state->position_y());
 
@@ -3961,21 +3924,52 @@
       }
     }
 
-    scrolling_node = FindScrollNodeForDeviceViewportPoint(
+    ScrollNode* starting_node = FindScrollNodeForDeviceViewportPoint(
         device_viewport_point, layer_impl, &scroll_on_main_thread,
         &scroll_status.main_thread_scrolling_reasons);
+
+    // The above finds the ScrollNode that's hit by the given point but we
+    // still need to walk up the scroll tree looking for the first node that
+    // can consume delta from the scroll state.
+    scrolling_node = FindNodeToLatch(scroll_state, starting_node, type);
   }
 
   if (scroll_on_main_thread) {
     RecordCompositorSlowScrollMetric(type, MAIN_THREAD);
-
     scroll_status.thread = SCROLL_ON_MAIN_THREAD;
     return scroll_status;
-  } else if (scrolling_node) {
-    scroll_affects_scroll_handler_ = active_tree_->have_scroll_event_handlers();
+  } else if (!scrolling_node) {
+    scroll_status.main_thread_scrolling_reasons =
+        MainThreadScrollingReason::kNoScrollingLayer;
+    if (settings_.is_layer_tree_for_subframe) {
+      TRACE_EVENT_INSTANT0("cc", "Ignored - No ScrollNode (OOPIF)",
+                           TRACE_EVENT_SCOPE_THREAD);
+      scroll_status.thread = SCROLL_UNKNOWN;
+    } else {
+      TRACE_EVENT_INSTANT0("cc", "Ignroed - No ScrollNode",
+                           TRACE_EVENT_SCOPE_THREAD);
+      scroll_status.thread = SCROLL_IGNORED;
+    }
+    return scroll_status;
   }
 
-  return ScrollBeginImpl(scroll_state, scrolling_node, type);
+  DCHECK_EQ(scroll_status.main_thread_scrolling_reasons,
+            MainThreadScrollingReason::kNotScrollingOnMain);
+  DCHECK_EQ(scroll_status.thread, SCROLL_ON_IMPL_THREAD);
+
+  active_tree_->SetCurrentlyScrollingNode(scrolling_node);
+
+  DidLatchToScroller(*scroll_state, type);
+
+  // If the viewport is scrolling and it cannot consume any delta hints, the
+  // scroll event will need to get bubbled if the viewport is for a guest or
+  // oopif.
+  if (viewport().ShouldScroll(*CurrentlyScrollingNode()) &&
+      !viewport().CanScroll(*CurrentlyScrollingNode(), *scroll_state)) {
+    scroll_status.bubble = true;
+  }
+
+  return scroll_status;
 }
 
 // Requires falling back to main thread scrolling when it hit tests in scrollbar
@@ -4074,32 +4068,6 @@
   return false;
 }
 
-InputHandler::ScrollStatus LayerTreeHostImpl::ScrollAnimatedBegin(
-    ScrollState* scroll_state) {
-  TRACE_EVENT0("cc", "LayerTreeHostImpl::ScrollAnimatedBegin");
-
-  // It's possible we haven't yet cleared the CurrentlyScrollingNode if we
-  // received a GSE but we're still animating the last scroll. If that's the
-  // case, we'll simply un-defer the GSE and continue latching to the same
-  // node.
-  DCHECK(!CurrentlyScrollingNode() || deferred_scroll_end_);
-
-  InputHandler::ScrollStatus scroll_status;
-  scroll_status.main_thread_scrolling_reasons =
-      MainThreadScrollingReason::kNotScrollingOnMain;
-  deferred_scroll_end_ = false;
-
-  if (CurrentlyScrollingNode())
-    return scroll_status;
-
-  // ScrollAnimated is used for animated wheel scrolls. We find the first layer
-  // that can scroll and set up an animation of its scroll offset. Note that
-  // this does not currently go through the scroll customization machinery
-  // that ScrollBy uses for non-animated wheel scrolls.
-  scroll_status = ScrollBegin(scroll_state, WHEEL);
-  return scroll_status;
-}
-
 gfx::Vector2dF LayerTreeHostImpl::ComputeScrollDelta(
     const ScrollNode& scroll_node,
     const gfx::Vector2dF& delta) {
@@ -4505,67 +4473,76 @@
   scroll_state->ConsumeDelta(applied_delta.x(), applied_delta.y());
 }
 
-void LayerTreeHostImpl::LatchToScroller(ScrollState* scroll_state,
-                                        ScrollNode* starting_node) {
-  std::list<ScrollNode*> current_scroll_chain;
+ScrollNode* LayerTreeHostImpl::FindNodeToLatch(
+    ScrollState* scroll_state,
+    ScrollNode* starting_node,
+    InputHandler::ScrollInputType type) {
   ScrollTree& scroll_tree = active_tree_->property_trees()->scroll_tree;
   ScrollNode* scroll_node = nullptr;
-  if (scroll_state->data()->current_native_scrolling_element()) {
-    DCHECK(starting_node);
-    DCHECK_EQ(starting_node->element_id,
-              scroll_state->data()->current_native_scrolling_element());
+  for (ScrollNode* cur_node = starting_node; cur_node;
+       cur_node = scroll_tree.parent(cur_node)) {
+    if (viewport().ShouldScroll(*cur_node)) {
+      // Don't chain scrolls past a viewport node. Once we reach that, we
+      // should scroll using the appropriate viewport node which may not be
+      // |cur_node|.
+      scroll_node = GetNodeToScroll(cur_node);
+      break;
+    }
 
-    // Needed for non-animated scrolls.
-    scroll_node = starting_node;
-  } else {
-    for (ScrollNode* cur_node = starting_node; cur_node;
-         cur_node = scroll_tree.parent(cur_node)) {
-      if (viewport().ShouldScroll(*cur_node)) {
-        // Don't chain scrolls past a viewport node. Once we reach that, we
-        // should scroll using the appropriate viewport node which may not be
-        // |cur_node|.
-        scroll_node = GetNodeToScroll(cur_node);
-        break;
-      }
+    if (!cur_node->scrollable)
+      continue;
 
-      if (!cur_node->scrollable)
-        continue;
+    // For UX reasons, autoscrolling should always latch to the top-most
+    // scroller, even if it can't scroll in the initial direction.
+    if (type == InputHandler::AUTOSCROLL ||
+        CanConsumeDelta(*scroll_state, *cur_node)) {
+      scroll_node = cur_node;
+      break;
+    }
 
-      if (middle_click_autoscrolling_ ||
-          CanConsumeDelta(*cur_node, *scroll_state)) {
-        scroll_node = cur_node;
-        break;
-      }
+    float delta_x = scroll_state->is_beginning() ? scroll_state->delta_x_hint()
+                                                 : scroll_state->delta_x();
+    float delta_y = scroll_state->is_beginning() ? scroll_state->delta_y_hint()
+                                                 : scroll_state->delta_y();
 
-      float delta_x = scroll_state->is_beginning()
-                          ? scroll_state->delta_x_hint()
-                          : scroll_state->delta_x();
-      float delta_y = scroll_state->is_beginning()
-                          ? scroll_state->delta_y_hint()
-                          : scroll_state->delta_y();
-
-      if (!CanPropagate(cur_node, delta_x, delta_y)) {
-        // If we reach a node with non-auto overscroll-behavior and we still
-        // haven't latched, we must latch to it. Consider a fully scrolled node
-        // with non-auto overscroll-behavior: we are not allowed to further
-        // chain scroll delta passed to it in the current direction but if we
-        // reverse direction we should scroll it so we must be latched to it.
-        scroll_node = cur_node;
-        scroll_state->set_is_scroll_chain_cut(true);
-        break;
-      }
+    if (!CanPropagate(cur_node, delta_x, delta_y)) {
+      // If we reach a node with non-auto overscroll-behavior and we still
+      // haven't latched, we must latch to it. Consider a fully scrolled node
+      // with non-auto overscroll-behavior: we are not allowed to further
+      // chain scroll delta passed to it in the current direction but if we
+      // reverse direction we should scroll it so we must be latched to it.
+      scroll_node = cur_node;
+      scroll_state->set_is_scroll_chain_cut(true);
+      break;
     }
   }
 
-  TRACE_EVENT_INSTANT1("cc", "SetCurrentlyScrollingNode LatchToScroller",
-                       TRACE_EVENT_SCOPE_THREAD, "isNull",
-                       scroll_node ? false : true);
-  active_tree_->SetCurrentlyScrollingNode(scroll_node);
-  last_latched_scroller_ = scroll_node ? scroll_node->element_id : ElementId();
+  return scroll_node;
 }
 
-bool LayerTreeHostImpl::CanConsumeDelta(const ScrollNode& scroll_node,
-                                        const ScrollState& scroll_state) {
+void LayerTreeHostImpl::DidLatchToScroller(const ScrollState& scroll_state,
+                                           InputHandler::ScrollInputType type) {
+  DCHECK(CurrentlyScrollingNode());
+  deferred_scroll_end_ = false;
+  browser_controls_offset_manager_->ScrollBegin();
+  mutator_host_->ScrollAnimationAbort();
+
+  scroll_affects_scroll_handler_ = active_tree_->have_scroll_event_handlers();
+  scroll_animating_snap_target_ids_ = TargetSnapAreaElementIds();
+  last_latched_scroller_ = CurrentlyScrollingNode()->element_id;
+  wheel_scrolling_ = type == InputHandler::WHEEL;
+
+  frame_trackers_.StartSequence(wheel_scrolling_
+                                    ? FrameSequenceTrackerType::kWheelScroll
+                                    : FrameSequenceTrackerType::kTouchScroll);
+  client_->RenewTreePriority();
+  RecordCompositorSlowScrollMetric(type, CC_THREAD);
+
+  UpdateScrollSourceInfo(scroll_state, type);
+}
+
+bool LayerTreeHostImpl::CanConsumeDelta(const ScrollState& scroll_state,
+                                        const ScrollNode& scroll_node) {
   gfx::Vector2dF delta_to_scroll;
   if (scroll_state.is_beginning()) {
     delta_to_scroll = gfx::Vector2dF(scroll_state.delta_x_hint(),
@@ -6065,10 +6042,10 @@
 }
 
 void LayerTreeHostImpl::UpdateScrollSourceInfo(
-    InputHandler::ScrollInputType type,
-    ScrollState* scroll_state) {
+    const ScrollState& scroll_state,
+    InputHandler::ScrollInputType type) {
   if (type == InputHandler::WHEEL &&
-      scroll_state->delta_granularity() ==
+      scroll_state.delta_granularity() ==
           static_cast<double>(
               ui::input_types::ScrollGranularity::kScrollByPrecisePixel)) {
     has_scrolled_by_precisiontouchpad_ = true;
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index 947a3cce..a91f207b 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -266,7 +266,6 @@
   InputHandler::ScrollStatus RootScrollBegin(
       ScrollState* scroll_state,
       InputHandler::ScrollInputType type) override;
-  ScrollStatus ScrollAnimatedBegin(ScrollState* scroll_state) override;
   void ScrollAnimated(const gfx::Point& viewport_point,
                       const gfx::Vector2dF& scroll_delta,
                       base::TimeDelta delayed_by = base::TimeDelta()) override;
@@ -740,8 +739,13 @@
                : task_runner_provider_->MainThreadTaskRunner();
   }
 
-  InputHandler::ScrollStatus TryScroll(const gfx::PointF& screen_space_point,
-                                       const ScrollTree& scroll_tree,
+  // Determines whether the given scroll node can scroll on the compositor
+  // thread or if there are any reasons it must be scrolled on the main thread
+  // or not at all. Note: in general, this is not sufficient to determine if a
+  // scroll can occur on the compositor thread. If hit testing to a scroll
+  // node, the caller must also check whether the hit point intersects a
+  // non-fast-scrolling-region of any ancestor scrolling layers.
+  InputHandler::ScrollStatus TryScroll(const ScrollTree& scroll_tree,
                                        ScrollNode* scroll_node) const;
 
   // Return all ScrollNode indices that have an associated layer with a non-fast
@@ -771,8 +775,8 @@
 
   void ClearCaches();
 
-  bool CanConsumeDelta(const ScrollNode& scroll_node,
-                       const ScrollState& scroll_state);
+  bool CanConsumeDelta(const ScrollState& scroll_state,
+                       const ScrollNode& scroll_node);
 
   void UpdateImageDecodingHints(
       base::flat_map<PaintImage::Id, PaintImage::DecodingMode>
@@ -895,17 +899,30 @@
 
   Viewport& viewport() const { return *viewport_.get(); }
 
-  InputHandler::ScrollStatus ScrollBeginImpl(
-      ScrollState* scroll_state,
-      ScrollNode* scrolling_node,
-      InputHandler::ScrollInputType type);
   bool IsTouchDraggingScrollbar(
       LayerImpl* first_scrolling_layer_or_drawn_scrollbar,
       InputHandler::ScrollInputType type);
   bool IsInitialScrollHitTestReliable(
       LayerImpl* layer,
       LayerImpl* first_scrolling_layer_or_drawn_scrollbar);
-  void LatchToScroller(ScrollState* scroll_state, ScrollNode* starting_node);
+
+  // Given a starting node (determined by hit-test), walks up the scroll tree
+  // looking for the first node that can consume scroll from the given
+  // scroll_state and returns the first such node. If none is found, or if
+  // starting_node is nullptr, returns nullptr;
+  ScrollNode* FindNodeToLatch(ScrollState* scroll_state,
+                              ScrollNode* starting_node,
+                              InputHandler::ScrollInputType type);
+
+  // Called during ScrollBegin once a scroller was successfully latched to
+  // (i.e.  it can and will consume scroll delta on the compositor thread). The
+  // latched scroller is now available in CurrentlyScrollingNode().
+  // TODO(bokan): There's some debate about the name of this method. We should
+  // get consensus on terminology to use and apply it consistently.
+  // https://crrev.com/c/1981336/9/cc/trees/layer_tree_host_impl.cc#4520
+  void DidLatchToScroller(const ScrollState& scroll_state,
+                          InputHandler::ScrollInputType type);
+
   void ScrollLatchedScroller(ScrollState* scroll_state);
 
   bool AnimatePageScale(base::TimeTicks monotonic_time);
@@ -972,8 +989,8 @@
   // thread side to keep track of the frequency of scrolling with different
   // sources per page load. TODO(crbug.com/691886): Use GRC API to plumb the
   // scroll source info for Use Counters.
-  void UpdateScrollSourceInfo(InputHandler::ScrollInputType type,
-                              ScrollState* scroll_state);
+  void UpdateScrollSourceInfo(const ScrollState& scroll_state,
+                              InputHandler::ScrollInputType type);
 
   bool IsScrolledBy(LayerImpl* child, ScrollNode* ancestor);
   void ShowScrollbarsForImplScroll(ElementId element_id);
@@ -1063,8 +1080,14 @@
 
   InputHandlerClient* input_handler_client_ = nullptr;
   bool wheel_scrolling_ = false;
-  bool middle_click_autoscrolling_ = false;
+
+  // This is used to tell the scheduler there are active scroll handlers on the
+  // page so we should prioritize latency during a scroll to try to keep
+  // scroll-linked effects up to data.
+  // TODO(bokan): This is quite old and scheduling has become much more
+  // sophisticated since so it's not clear how much value it's still providing.
   bool scroll_affects_scroll_handler_ = false;
+
   ElementId scroll_element_id_mouse_currently_over_;
   ElementId scroll_element_id_mouse_currently_captured_;
 
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index 4e8d395..66ebe3417 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -1075,6 +1075,43 @@
             status.main_thread_scrolling_reasons);
 }
 
+// Test that specifying a scroller to ScrollBegin (i.e. avoid hit testing)
+// returns the correct status if the scroller cannot be scrolled on the
+// compositor thread.
+TEST_F(LayerTreeHostImplTest, TargetMainThreadScroller) {
+  SetupViewportLayersOuterScrolls(gfx::Size(100, 100), gfx::Size(1000, 1000));
+
+  ScrollStateData scroll_state_data;
+  scroll_state_data.set_current_native_scrolling_element(
+      host_impl_->OuterViewportScrollNode()->element_id);
+  std::unique_ptr<ScrollState> scroll_state(new ScrollState(scroll_state_data));
+
+  // Try on a node that should scroll on the compositor. Confirm it works.
+  {
+    InputHandler::ScrollStatus status =
+        host_impl_->ScrollBegin(scroll_state.get(), InputHandler::WHEEL);
+    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+    EXPECT_EQ(host_impl_->OuterViewportScrollNode(),
+              host_impl_->CurrentlyScrollingNode());
+    host_impl_->ScrollEnd();
+  }
+
+  // Now add a MainThreadScrollingReason. Trying to target the node this time
+  // should result in a failed ScrollBegin with the appropriate return value.
+  host_impl_->OuterViewportScrollNode()->main_thread_scrolling_reasons =
+      MainThreadScrollingReason::kThreadedScrollingDisabled;
+
+  {
+    InputHandler::ScrollStatus status =
+        host_impl_->ScrollBegin(scroll_state.get(), InputHandler::WHEEL);
+    EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+    EXPECT_EQ(
+        host_impl_->OuterViewportScrollNode()->main_thread_scrolling_reasons,
+        status.main_thread_scrolling_reasons);
+    EXPECT_FALSE(host_impl_->CurrentlyScrollingNode());
+  }
+}
+
 TEST_F(LayerTreeHostImplTest, ScrollRootCallsCommitAndRedraw) {
   SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
   DrawFrame();
@@ -1734,8 +1771,9 @@
   EXPECT_EQ(
       InputHandler::SCROLL_ON_IMPL_THREAD,
       host_impl_
-          ->ScrollAnimatedBegin(
-              BeginState(pointer_position, delta, InputHandler::WHEEL).get())
+          ->ScrollBegin(
+              BeginState(pointer_position, delta, InputHandler::WHEEL).get(),
+              InputHandler::WHEEL)
           .thread);
   host_impl_->ScrollAnimated(pointer_position, delta);
   host_impl_->ScrollEnd();
@@ -1820,13 +1858,12 @@
   EXPECT_EQ(0, current_offset.y());
 
   // Interrupt the snap animation with ScrollBegin.
+  auto begin_state = BeginState(pointer_position, x_delta, InputHandler::WHEEL);
+  begin_state->data()->delta_granularity = static_cast<double>(
+      ui::input_types::ScrollGranularity::kScrollByPrecisePixel);
   EXPECT_EQ(
       InputHandler::SCROLL_ON_IMPL_THREAD,
-      host_impl_
-          ->ScrollBegin(
-              BeginState(pointer_position, x_delta, InputHandler::WHEEL).get(),
-              InputHandler::WHEEL)
-          .thread);
+      host_impl_->ScrollBegin(begin_state.get(), InputHandler::WHEEL).thread);
   EXPECT_FALSE(host_impl_->IsAnimatingForSnap());
   EXPECT_EQ(TargetSnapAreaElementIds(),
             GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
@@ -4277,10 +4314,10 @@
   ResetScrollbars();
 
   // Scroll on child should flash all scrollbars.
-  host_impl_->ScrollAnimatedBegin(BeginState(gfx::Point(70, 70),
-                                             gfx::Vector2dF(0, 100),
-                                             InputHandler::WHEEL)
-                                      .get());
+  host_impl_->ScrollBegin(BeginState(gfx::Point(70, 70), gfx::Vector2dF(0, 100),
+                                     InputHandler::WHEEL)
+                              .get(),
+                          InputHandler::WHEEL);
   host_impl_->ScrollAnimated(gfx::Point(70, 70), gfx::Vector2d(0, 100));
   host_impl_->ScrollEnd();
 
@@ -6907,13 +6944,13 @@
   viz::BeginFrameArgs begin_frame_args =
       viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
 
-  EXPECT_EQ(
-      InputHandler::SCROLL_ON_IMPL_THREAD,
-      host_impl_
-          ->ScrollAnimatedBegin(BeginState(gfx::Point(), gfx::Vector2d(0, -100),
-                                           InputHandler::WHEEL)
-                                    .get())
-          .thread);
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+            host_impl_
+                ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(0, -100),
+                                         InputHandler::WHEEL)
+                                  .get(),
+                              InputHandler::WHEEL)
+                .thread);
   host_impl_->ScrollAnimated(gfx::Point(), gfx::Vector2d(0, -100));
 
   begin_frame_args.frame_time = start_time;
@@ -8699,6 +8736,8 @@
                         InputHandler::WHEEL)
           .thread);
 
+  host_impl_->ScrollEnd();
+
   // Overscroll initiated inside layers will be handled by the impl thread.
   EXPECT_NE(nullptr, host_impl_->active_tree()->FindLayerThatIsHitByPoint(
                          gfx::PointF(0, 0)));
@@ -11489,13 +11528,13 @@
         new SimpleSwapPromiseMonitor(nullptr, host_impl_.get(),
                                      &set_needs_commit_count,
                                      &set_needs_redraw_count));
-    EXPECT_EQ(
-        InputHandler::SCROLL_ON_IMPL_THREAD,
-        host_impl_
-            ->ScrollAnimatedBegin(BeginState(gfx::Point(), gfx::Vector2d(0, 50),
-                                             InputHandler::WHEEL)
-                                      .get())
-            .thread);
+    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+              host_impl_
+                  ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(0, 50),
+                                           InputHandler::WHEEL)
+                                    .get(),
+                                InputHandler::WHEEL)
+                  .thread);
     host_impl_->ScrollAnimated(gfx::Point(), gfx::Vector2d(0, 50));
 
     EXPECT_EQ(0, set_needs_commit_count);
@@ -11593,7 +11632,7 @@
     gfx::Point position(40, 40);
     auto begin_state =
         BeginState(position, gfx::Vector2d(0, 50), InputHandler::WHEEL);
-    host_impl_->ScrollAnimatedBegin(begin_state.get());
+    host_impl_->ScrollBegin(begin_state.get(), InputHandler::WHEEL);
     host_impl_->ScrollAnimated(position, gfx::Vector2d(0, 50));
     EXPECT_EQ(outer_scroll->scroll_tree_index(),
               host_impl_->CurrentlyScrollingNode()->id);
@@ -11638,7 +11677,7 @@
     gfx::Point position(10, 10);
     auto begin_state =
         BeginState(position, gfx::Vector2d(0, 50), InputHandler::WHEEL);
-    host_impl_->ScrollAnimatedBegin(begin_state.get());
+    host_impl_->ScrollBegin(begin_state.get(), InputHandler::WHEEL);
     EXPECT_EQ(outer_scroll->scroll_tree_index(),
               host_impl_->CurrentlyScrollingNode()->id);
   }
@@ -11692,13 +11731,14 @@
   // afterwards. This will ensure we test both the starting animation and
   // animation update code.
   {
-    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
-              host_impl_
-                  ->ScrollAnimatedBegin(BeginState(gfx::Point(10, 10),
-                                                   gfx::Vector2d(0, 10),
-                                                   InputHandler::WHEEL)
-                                            .get())
-                  .thread);
+    EXPECT_EQ(
+        InputHandler::SCROLL_ON_IMPL_THREAD,
+        host_impl_
+            ->ScrollBegin(BeginState(gfx::Point(10, 10), gfx::Vector2d(0, 10),
+                                     InputHandler::WHEEL)
+                              .get(),
+                          InputHandler::WHEEL)
+            .thread);
     host_impl_->ScrollAnimated(gfx::Point(10, 10), gfx::Vector2d(0, 10));
     host_impl_->ScrollAnimated(gfx::Point(10, 10), gfx::Vector2d(0, 20));
 
@@ -11832,24 +11872,24 @@
   const gfx::Size viewport_size(50, 100);
   SetupViewportLayersOuterScrolls(viewport_size, content_size);
 
-  EXPECT_EQ(
-      InputHandler::SCROLL_ON_IMPL_THREAD,
-      host_impl_
-          ->ScrollAnimatedBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10),
-                                           InputHandler::WHEEL)
-                                    .get())
-          .thread);
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+            host_impl_
+                ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10),
+                                         InputHandler::WHEEL)
+                                  .get(),
+                              InputHandler::WHEEL)
+                .thread);
 
   host_impl_->ScrollEnd();
 
-  // The second ScrollAnimatedBegin should not get ignored.
-  EXPECT_EQ(
-      InputHandler::SCROLL_ON_IMPL_THREAD,
-      host_impl_
-          ->ScrollAnimatedBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10),
-                                           InputHandler::WHEEL)
-                                    .get())
-          .thread);
+  // The second ScrollBegin should not get ignored.
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+            host_impl_
+                ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10),
+                                         InputHandler::WHEEL)
+                                  .get(),
+                              InputHandler::WHEEL)
+                .thread);
 }
 
 // Verfify that a smooth scroll animation doesn't jump when UpdateTarget gets
@@ -11872,13 +11912,13 @@
   host_impl_->UpdateAnimationState(true);
   host_impl_->DidFinishImplFrame();
 
-  EXPECT_EQ(
-      InputHandler::SCROLL_ON_IMPL_THREAD,
-      host_impl_
-          ->ScrollAnimatedBegin(BeginState(gfx::Point(), gfx::Vector2d(0, 50),
-                                           InputHandler::WHEEL)
-                                    .get())
-          .thread);
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+            host_impl_
+                ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(0, 50),
+                                         InputHandler::WHEEL)
+                                  .get(),
+                              InputHandler::WHEEL)
+                .thread);
   host_impl_->ScrollAnimated(gfx::Point(), gfx::Vector2d(0, 50));
   // This will call ScrollOffsetAnimationCurve::UpdateTarget while the animation
   // created above is in state ANIMATION::WAITING_FOR_TARGET_AVAILABILITY and
@@ -11923,13 +11963,13 @@
       viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
 
   // Create animation with a 100ms delay.
-  EXPECT_EQ(
-      InputHandler::SCROLL_ON_IMPL_THREAD,
-      host_impl_
-          ->ScrollAnimatedBegin(BeginState(gfx::Point(), gfx::Vector2d(0, 100),
-                                           InputHandler::WHEEL)
-                                    .get())
-          .thread);
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+            host_impl_
+                ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(0, 100),
+                                         InputHandler::WHEEL)
+                                  .get(),
+                              InputHandler::WHEEL)
+                .thread);
   host_impl_->ScrollAnimated(gfx::Point(), gfx::Vector2d(0, 100),
                              base::TimeDelta::FromMilliseconds(100));
 
@@ -11987,13 +12027,13 @@
       viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
 
   // Perform animated scroll.
-  EXPECT_EQ(
-      InputHandler::SCROLL_ON_IMPL_THREAD,
-      host_impl_
-          ->ScrollAnimatedBegin(BeginState(gfx::Point(), gfx::Vector2d(0, 50),
-                                           InputHandler::WHEEL)
-                                    .get())
-          .thread);
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+            host_impl_
+                ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(0, 50),
+                                         InputHandler::WHEEL)
+                                  .get(),
+                              InputHandler::WHEEL)
+                .thread);
   host_impl_->ScrollAnimated(gfx::Point(), gfx::Vector2d(0, 50));
 
   LayerImpl* scrolling_layer = OuterViewportScrollLayer();
@@ -12022,19 +12062,24 @@
   float y = scrolling_layer->CurrentScrollOffset().y();
   EXPECT_TRUE(y > 1 && y < 49);
 
+  host_impl_->ScrollEnd();
+
   // Perform instant scroll.
+  // Use "precise pixel" granularity to avoid animating.
+  auto begin_state =
+      BeginState(gfx::Point(0, y), gfx::Vector2dF(0, 50), InputHandler::WHEEL);
+  begin_state->data()->delta_granularity = static_cast<double>(
+      ui::input_types::ScrollGranularity::kScrollByPrecisePixel);
   EXPECT_EQ(
       InputHandler::SCROLL_ON_IMPL_THREAD,
-      host_impl_
-          ->ScrollBegin(BeginState(gfx::Point(0, y), gfx::Vector2dF(0, 50),
-                                   InputHandler::WHEEL)
-                            .get(),
-                        InputHandler::WHEEL)
-          .thread);
+      host_impl_->ScrollBegin(begin_state.get(), InputHandler::WHEEL).thread);
   EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(0, y)));
-  host_impl_->ScrollBy(
-      UpdateState(gfx::Point(0, y), gfx::Vector2d(0, 50), InputHandler::WHEEL)
-          .get());
+  auto update_state =
+      UpdateState(gfx::Point(0, y), gfx::Vector2d(0, 50), InputHandler::WHEEL);
+  // Use "precise pixel" granularity to avoid animating.
+  update_state->data()->delta_granularity = static_cast<double>(
+      ui::input_types::ScrollGranularity::kScrollByPrecisePixel);
+  host_impl_->ScrollBy(update_state.get());
   EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(0, y + 50)));
   host_impl_->ScrollEnd();
   EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point()));
@@ -12064,13 +12109,13 @@
   viz::BeginFrameArgs begin_frame_args =
       viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
 
-  EXPECT_EQ(
-      InputHandler::SCROLL_ON_IMPL_THREAD,
-      host_impl_
-          ->ScrollAnimatedBegin(BeginState(gfx::Point(), gfx::Vector2d(0, 50),
-                                           InputHandler::WHEEL)
-                                    .get())
-          .thread);
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+            host_impl_
+                ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(0, 50),
+                                         InputHandler::WHEEL)
+                                  .get(),
+                              InputHandler::WHEEL)
+                .thread);
   host_impl_->ScrollAnimated(gfx::Point(), gfx::Vector2d(0, 50));
 
   LayerImpl* scrolling_layer = OuterViewportScrollLayer();
@@ -12152,13 +12197,13 @@
       base::TimeTicks() + base::TimeDelta::FromMilliseconds(250);
   viz::BeginFrameArgs begin_frame_args =
       viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
-  EXPECT_EQ(
-      InputHandler::SCROLL_ON_IMPL_THREAD,
-      host_impl_
-          ->ScrollAnimatedBegin(BeginState(gfx::Point(), gfx::Vector2d(10, 20),
-                                           InputHandler::WHEEL)
-                                    .get())
-          .thread);
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+            host_impl_
+                ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(10, 20),
+                                         InputHandler::WHEEL)
+                                  .get(),
+                              InputHandler::WHEEL)
+                .thread);
   host_impl_->ScrollAnimated(gfx::Point(), gfx::Vector2d(10, 20));
   host_impl_->Animate();
   host_impl_->UpdateAnimationState(true);
@@ -12244,13 +12289,13 @@
       base::TimeTicks() + base::TimeDelta::FromMilliseconds(50);
   viz::BeginFrameArgs begin_frame_args =
       viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
-  EXPECT_EQ(
-      InputHandler::SCROLL_ON_IMPL_THREAD,
-      host_impl_
-          ->ScrollAnimatedBegin(BeginState(gfx::Point(), gfx::Vector2d(90, 90),
-                                           InputHandler::WHEEL)
-                                    .get())
-          .thread);
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+            host_impl_
+                ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(90, 90),
+                                         InputHandler::WHEEL)
+                                  .get(),
+                              InputHandler::WHEEL)
+                .thread);
   host_impl_->ScrollAnimated(gfx::Point(), gfx::Vector2d(90, 90));
   host_impl_->Animate();
   host_impl_->UpdateAnimationState(true);
@@ -12306,13 +12351,13 @@
   viz::BeginFrameArgs begin_frame_args =
       viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
 
-  EXPECT_EQ(
-      InputHandler::SCROLL_ON_IMPL_THREAD,
-      host_impl_
-          ->ScrollAnimatedBegin(BeginState(gfx::Point(), gfx::Vector2d(50, 50),
-                                           InputHandler::WHEEL)
-                                    .get())
-          .thread);
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+            host_impl_
+                ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(50, 50),
+                                         InputHandler::WHEEL)
+                                  .get(),
+                              InputHandler::WHEEL)
+                .thread);
   host_impl_->ScrollAnimated(gfx::Point(), gfx::Vector2d(50, 50));
 
   EXPECT_EQ(scrolling_layer->scroll_tree_index(),
@@ -12391,9 +12436,10 @@
   viz::BeginFrameArgs begin_frame_args =
       viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
 
-  host_impl_->ScrollAnimatedBegin(
+  host_impl_->ScrollBegin(
       BeginState(gfx::Point(), gfx::Vector2d(500, 500), InputHandler::WHEEL)
-          .get());
+          .get(),
+      InputHandler::WHEEL);
   host_impl_->ScrollAnimated(gfx::Point(), gfx::Vector2d(500, 500));
 
   EXPECT_EQ(scrolling_layer->scroll_tree_index(),
diff --git a/cc/trees/layer_tree_host_unittest_scroll.cc b/cc/trees/layer_tree_host_unittest_scroll.cc
index 364f789b..0d3020e2 100644
--- a/cc/trees/layer_tree_host_unittest_scroll.cc
+++ b/cc/trees/layer_tree_host_unittest_scroll.cc
@@ -1453,7 +1453,7 @@
         impl->active_tree()->property_trees()->scroll_tree;
     ScrollNode* scroll_node = scroll_tree.Node(scroller_->scroll_tree_index());
     InputHandler::ScrollStatus status =
-        impl->TryScroll(gfx::PointF(0.0f, 1.0f), scroll_tree, scroll_node);
+        impl->TryScroll(scroll_tree, scroll_node);
     switch (impl->active_tree()->source_frame_number()) {
       case 0:
         EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread)
@@ -1557,13 +1557,12 @@
         scroll_tree.Node(outer_scroll_layer->scroll_tree_index());
 
     InputHandler::ScrollStatus status =
-        impl->TryScroll(gfx::PointF(1.f, 1.f), scroll_tree, inner_scroll_node);
+        impl->TryScroll(scroll_tree, inner_scroll_node);
     EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
     EXPECT_EQ(MainThreadScrollingReason::kScrollbarScrolling,
               status.main_thread_scrolling_reasons);
 
-    status =
-        impl->TryScroll(gfx::PointF(1.f, 1.f), scroll_tree, outer_scroll_node);
+    status = impl->TryScroll(scroll_tree, outer_scroll_node);
     EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
     EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
               status.main_thread_scrolling_reasons);
@@ -2534,6 +2533,9 @@
   void BeginTest() override { PostSetNeedsCommitToMainThread(); }
 
   void CommitCompleteOnThread(LayerTreeHostImpl* impl) override {
+    if (TestEnded())
+      return;
+
     // The top-left hit should immediately hit the top layer's non-fast region
     // which forces main-thread scrolling.
     auto top_left_status = impl->ScrollBegin(
@@ -2549,6 +2551,7 @@
     EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, top_right_status.thread);
     EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
               top_right_status.main_thread_scrolling_reasons);
+    impl->ScrollEnd();
 
     // The bottom-right should hit the bottom layer's non-fast region. Though
     // the middle layer is a composited scroller and is hit first, we cannot do
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 4d21d27..7e008164 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -315,6 +315,7 @@
     "//components/navigation_interception/android:navigation_interception_java",
     "//components/offline_items_collection/core:core_java",
     "//components/omnibox/browser:browser_java",
+    "//components/paint_preview/browser/android:java",
     "//components/payments/content/android:java",
     "//components/payments/mojom:mojom_java",
     "//components/policy/android:policy_java",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 47e5553..2332317 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -1316,7 +1316,6 @@
   "java/src/org/chromium/chrome/browser/photo_picker/PickerBitmapViewHolder.java",
   "java/src/org/chromium/chrome/browser/photo_picker/PickerCategoryView.java",
   "java/src/org/chromium/chrome/browser/policy/PolicyAuditor.java",
-  "java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java",
   "java/src/org/chromium/chrome/browser/prerender/ChromePrerenderService.java",
   "java/src/org/chromium/chrome/browser/prerender/ExternalPrerenderHandler.java",
   "java/src/org/chromium/chrome/browser/previews/PreviewsAndroidBridge.java",
@@ -1536,6 +1535,7 @@
   "java/src/org/chromium/chrome/browser/signin/SigninHelper.java",
   "java/src/org/chromium/chrome/browser/signin/SigninInvestigator.java",
   "java/src/org/chromium/chrome/browser/signin/SigninManager.java",
+  "java/src/org/chromium/chrome/browser/signin/SigninPreferencesManager.java",
   "java/src/org/chromium/chrome/browser/signin/SigninPromoController.java",
   "java/src/org/chromium/chrome/browser/signin/SigninPromoUtil.java",
   "java/src/org/chromium/chrome/browser/signin/SigninScrollView.java",
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/additional_sections/AssistantTextInputSection.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/additional_sections/AssistantTextInputSection.java
index 76a0e587..9dc3f10 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/additional_sections/AssistantTextInputSection.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/additional_sections/AssistantTextInputSection.java
@@ -10,12 +10,12 @@
 import android.support.annotation.Nullable;
 import android.support.v4.util.Pair;
 import android.text.Editable;
+import android.text.TextUtils;
 import android.text.TextWatcher;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.LinearLayout;
-import android.widget.Space;
 import android.widget.TextView;
 
 import org.chromium.base.ApiCompatibilityUtils;
@@ -26,6 +26,7 @@
 import org.chromium.chrome.browser.autofill_assistant.AssistantTextUtils;
 import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantVerticalExpander;
 
+import java.util.ArrayList;
 import java.util.List;
 
 /** A section which displays one or multiple text inputs for the user to type in. */
@@ -36,6 +37,8 @@
     private Delegate mDelegate;
     private int mTopPadding;
     private int mBottomPadding;
+    private final List<TextView> mSummaryViews = new ArrayList<>();
+    private final int mTitleToContentPadding;
 
     /** Factory for a single text input field. */
     public static class TextInputFactory {
@@ -104,6 +107,8 @@
             List<TextInputFactory> inputs, @Nullable Delegate delegate) {
         mContext = context;
         mDelegate = delegate;
+        mTitleToContentPadding = context.getResources().getDimensionPixelSize(
+                R.dimen.autofill_assistant_payment_request_title_padding);
 
         LayoutInflater inflater = LayoutInflater.from(context);
         mSectionExpander = new AssistantVerticalExpander(context, null);
@@ -114,23 +119,34 @@
         AssistantTextUtils.applyVisualAppearanceTags(titleView, title, null);
 
         mInputContainer = createInputContainer();
+        LinearLayout summaryViews = new LinearLayout(context);
+        summaryViews.setOrientation(LinearLayout.VERTICAL);
         int horizontalMargin = context.getResources().getDimensionPixelSize(
                 R.dimen.autofill_assistant_bottombar_horizontal_spacing);
         for (TextInputFactory input : inputs) {
+            TextView summaryView = new TextView(context);
+            ApiCompatibilityUtils.setTextAppearance(summaryView, R.style.TextAppearance_BlackBody);
             View inputView = input.createView(context, result -> {
                 if (mDelegate == null) {
                     return;
                 }
+                summaryView.setText(result.second);
+                summaryView.setVisibility(
+                        TextUtils.isEmpty(result.second) ? View.GONE : View.VISIBLE);
                 mDelegate.onValueChanged(result.first, result.second);
             });
             mInputContainer.addView(inputView);
+            mSummaryViews.add(summaryView);
+            summaryViews.addView(summaryView);
+            setHorizontalMargins(summaryView, horizontalMargin, horizontalMargin);
             setHorizontalMargins(inputView, horizontalMargin, horizontalMargin);
         }
         mSectionExpander.setExpandedView(mInputContainer,
                 new LinearLayout.LayoutParams(
                         ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
-        mSectionExpander.setCollapsedView(
-                new Space(context, null), new ViewGroup.LayoutParams(0, 0));
+        mSectionExpander.setCollapsedView(summaryViews,
+                new LinearLayout.LayoutParams(
+                        ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
         mSectionExpander.setTitleView(sectionTitle,
                 new LinearLayout.LayoutParams(
                         ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
@@ -161,6 +177,15 @@
         mDelegate = delegate;
     }
 
+    private boolean isEmpty() {
+        for (int i = 0; i < mSummaryViews.size(); i++) {
+            if (mSummaryViews.get(i).length() > 0) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     private void setHorizontalMargins(View view, int marginStart, int marginEnd) {
         ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
         lp.setMarginStart(marginStart);
@@ -170,17 +195,31 @@
 
     private void updatePaddings() {
         View titleView = mSectionExpander.getTitleView();
-        if (mSectionExpander.isExpanded()) {
+        View chevronButton = mSectionExpander.getChevronButton();
+        if (isEmpty()) {
+            // Section is empty, i.e., the title is the bottom-most widget.
+            setTopAndBottomPadding(titleView, mTopPadding, mBottomPadding);
+            setTopAndBottomPadding(chevronButton, mTopPadding, mBottomPadding);
+            setTopAndBottomPadding(mSectionExpander.getCollapsedView(),
+                    mSectionExpander.getCollapsedView().getPaddingTop(), 0);
+        } else if (mSectionExpander.isExpanded()) {
             // Section is expanded, i.e., the expanded widget is the bottom-most widget.
-            titleView.setPadding(titleView.getPaddingLeft(), mTopPadding,
-                    titleView.getPaddingRight(), titleView.getPaddingBottom());
+            setTopAndBottomPadding(titleView, mTopPadding, mTitleToContentPadding);
+            setTopAndBottomPadding(chevronButton, mTopPadding, mTitleToContentPadding);
+            // No need to set additional bottom padding, expanded sections have enough already.
         } else {
-            // Section is collapsed -> title is both top-most and bottom-most widget.
-            titleView.setPadding(titleView.getPaddingLeft(), mTopPadding,
-                    titleView.getPaddingRight(), mBottomPadding);
+            // Section is non-empty and collapsed -> collapsed widget is the bottom-most widget.
+            setTopAndBottomPadding(titleView, mTopPadding, mTitleToContentPadding);
+            setTopAndBottomPadding(chevronButton, mTopPadding, mBottomPadding);
+            setTopAndBottomPadding(mSectionExpander.getCollapsedView(),
+                    mSectionExpander.getCollapsedView().getPaddingTop(), mBottomPadding);
         }
     }
 
+    private void setTopAndBottomPadding(View view, int topPadding, int bottomPadding) {
+        view.setPadding(view.getPaddingLeft(), topPadding, view.getPaddingRight(), bottomPadding);
+    }
+
     private ViewGroup createInputContainer() {
         LinearLayout inputContainer = new LinearLayout(mContext, null);
         inputContainer.setOrientation(LinearLayout.VERTICAL);
diff --git a/chrome/android/java/DEPS b/chrome/android/java/DEPS
index f17f75c..9b033ad 100644
--- a/chrome/android/java/DEPS
+++ b/chrome/android/java/DEPS
@@ -31,6 +31,7 @@
   "+components/navigation_interception",
   "+components/offline_items_collection/core/android/java",
   "+components/omnibox/browser/android/java",
+  "+components/paint_preview/browser/android/java/src/org/chromium/components/paintpreview",
   "+components/payments/content/android/java/src/org/chromium/components/payments",
   "+components/search_engines/android/java/src/org/chromium/components/search_engines",
   "+components/sync/android/java/src/org/chromium/components/sync",
diff --git a/chrome/android/java/res/menu/main_menu.xml b/chrome/android/java/res/menu/main_menu.xml
index c062a2f..f2c7c73 100644
--- a/chrome/android/java/res/menu/main_menu.xml
+++ b/chrome/android/java/res/menu/main_menu.xml
@@ -60,6 +60,8 @@
                 android:title="@null" />
           </menu>
         </item>
+        <item android:id="@+id/paint_preview_capture_id"
+            android:title="@string/menu_paint_preview_capture" />
         <item android:id="@+id/find_in_page_id"
             android:title="@string/menu_find_in_page" />
         <item android:id="@+id/add_to_homescreen_id"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
index 8671875..ea92e59 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
@@ -164,6 +164,8 @@
             "AutofillCreditCardAuthentication";
     public static final String AUTOFILL_ENABLE_COMPANY_NAME = "AutofillEnableCompanyName";
     public static final String ADJUST_WEBAPK_INSTALLATION_SPACE = "AdjustWebApkInstallationSpace";
+    public static final String ANDROID_BLOCK_INTENT_NON_SAFELISTED_HEADERS =
+            "AndroidBlockIntentNonSafelistedHeaders";
     public static final String ANDROID_NIGHT_MODE = "AndroidNightMode";
     public static final String ANDROID_NIGHT_MODE_CCT = "AndroidNightModeCCT";
     public static final String ANDROID_NIGHT_MODE_FOR_Q = "AndroidNightModeForQ";
@@ -297,6 +299,7 @@
     public static final String OMNIBOX_SEARCH_ENGINE_LOGO = "OmniboxSearchEngineLogo";
     public static final String OVERLAY_NEW_LAYOUT = "OverlayNewLayout";
     public static final String OVERSCROLL_HISTORY_NAVIGATION = "OverscrollHistoryNavigation";
+    public static final String PAINT_PREVIEW_TEST = "PaintPreviewTest";
     public static final String PASSWORD_EDITING_ANDROID = "PasswordEditingAndroid";
     public static final String PASSWORD_LEAK_DETECTION = "PasswordLeakDetection";
     public static final String PASSWORD_MANAGER_ONBOARDING_ANDROID =
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java
index ecba1049..ef4b4507 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java
@@ -776,6 +776,11 @@
 
         // We do some logging to determine what kinds of headers developers are inserting.
         IntentHeadersRecorder recorder = shouldLogHeaders ? new IntentHeadersRecorder() : null;
+        boolean shouldBlockNonSafelistedHeaders = ChromeFeatureList.isEnabled(
+                ChromeFeatureList.ANDROID_BLOCK_INTENT_NON_SAFELISTED_HEADERS);
+        IntentHeadersRecorder.HeaderClassifier headerClassifier = shouldBlockNonSafelistedHeaders
+                ? new IntentHeadersRecorder.HeaderClassifier()
+                : null;
         boolean firstParty = shouldLogHeaders
                 ? IntentHandler.notSecureIsIntentChromeOrFirstParty(intent)
                 : false;
@@ -790,6 +795,11 @@
 
             if (shouldLogHeaders) recorder.recordHeader(key, value, firstParty);
 
+            if (shouldBlockNonSafelistedHeaders
+                    && !headerClassifier.isCorsSafelistedHeader(key, value, firstParty)) {
+                continue;
+            }
+
             if (extraHeaders.length() != 0) extraHeaders.append("\n");
             extraHeaders.append(key);
             extraHeaders.append(": ");
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegateImpl.java
index 82e8971..7dc32dd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegateImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegateImpl.java
@@ -248,13 +248,17 @@
                             && hasMoreThanOneTab);
 
             // Don't allow either "chrome://" pages or interstitial pages to be shared.
-            menu.findItem(R.id.share_row_menu_id)
-                    .setVisible(
-                            !isChromeScheme && !((TabImpl) currentTab).isShowingInterstitialPage());
+            boolean isChromeOrInterstitialPage =
+                    isChromeScheme || ((TabImpl) currentTab).isShowingInterstitialPage();
+            menu.findItem(R.id.share_row_menu_id).setVisible(!isChromeOrInterstitialPage);
 
             ShareHelper.configureDirectShareMenuItem(
                     mContext, menu.findItem(R.id.direct_share_menu_id));
 
+            menu.findItem(R.id.paint_preview_capture_id)
+                    .setVisible(FeatureUtilities.isPaintPreviewTestEnabled()
+                            && !isChromeOrInterstitialPage && !isIncognito);
+
             // Disable find in page on the native NTP.
             menu.findItem(R.id.find_in_page_id)
                     .setVisible(!currentTab.isNativePage() && currentTab.getWebContents() != null);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/flags/FeatureUtilities.java b/chrome/android/java/src/org/chromium/chrome/browser/flags/FeatureUtilities.java
index 012624c..cd63daa 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/flags/FeatureUtilities.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/flags/FeatureUtilities.java
@@ -128,6 +128,7 @@
         cacheStartSurfaceEnabled();
         cacheNativeTabSwitcherUiFlags();
         cacheHomepageLocationPolicyEnabled();
+        cachePaintPreviewTestEnabled();
 
         // Propagate REACHED_CODE_PROFILER feature value to LibraryLoader. This can't be done in
         // LibraryLoader itself because it lives in //base and can't depend on ChromeFeatureList.
@@ -417,6 +418,19 @@
         return isFlagEnabled(ChromePreferenceKeys.FLAGS_CACHED_START_SURFACE_ENABLED, false);
     }
 
+    private static void cachePaintPreviewTestEnabled() {
+        cacheFlag(ChromePreferenceKeys.FLAGS_CACHED_PAINT_PREVIEW_TEST_ENABLED_KEY,
+                ChromeFeatureList.PAINT_PREVIEW_TEST);
+    }
+
+    /**
+     * @return Whether the Paint Preview tapture test is enabled
+     */
+    public static boolean isPaintPreviewTestEnabled() {
+        return isFlagEnabled(
+                ChromePreferenceKeys.FLAGS_CACHED_PAINT_PREVIEW_TEST_ENABLED_KEY, false);
+    }
+
     @VisibleForTesting
     static void cacheNativeTabSwitcherUiFlags() {
         if (isEligibleForTabUiExperiments()) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SignInPromo.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SignInPromo.java
index 205c81b..60760d98 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SignInPromo.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SignInPromo.java
@@ -14,12 +14,12 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
-import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
 import org.chromium.chrome.browser.signin.ProfileDataCache;
 import org.chromium.chrome.browser.signin.SigninManager;
 import org.chromium.chrome.browser.signin.SigninManager.SignInAllowedObserver;
 import org.chromium.chrome.browser.signin.SigninManager.SignInStateObserver;
+import org.chromium.chrome.browser.signin.SigninPreferencesManager;
 import org.chromium.chrome.browser.signin.SigninPromoController;
 import org.chromium.components.signin.AccountManagerFacade;
 import org.chromium.components.signin.AccountsChangeObserver;
@@ -102,7 +102,7 @@
      * will not affect promos that were created before this call.
      */
     public static void temporarilySuppressPromos() {
-        ChromePreferenceManager.getInstance().setNewTabPageSigninPromoSuppressionPeriodStart(
+        SigninPreferencesManager.getInstance().setNewTabPageSigninPromoSuppressionPeriodStart(
                 System.currentTimeMillis());
     }
 
@@ -115,7 +115,7 @@
     }
 
     private static boolean getSuppressionStatus() {
-        long suppressedFrom = ChromePreferenceManager.getInstance()
+        long suppressedFrom = SigninPreferencesManager.getInstance()
                                       .getNewTabPageSigninPromoSuppressionPeriodStart();
         if (suppressedFrom == 0) return false;
         long currentTime = System.currentTimeMillis();
@@ -123,7 +123,7 @@
         if (suppressedFrom <= currentTime && currentTime < suppressedTo) {
             return true;
         }
-        ChromePreferenceManager.getInstance().clearNewTabPageSigninPromoSuppressionPeriodStart();
+        SigninPreferencesManager.getInstance().clearNewTabPageSigninPromoSuppressionPeriodStart();
         return false;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java
deleted file mode 100644
index dc5ff7f..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java
+++ /dev/null
@@ -1,98 +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.
-
-package org.chromium.chrome.browser.preferences;
-
-import java.util.Set;
-
-/**
- * ChromePreferenceManager stores and retrieves values in Android shared preferences for specific
- * features.
- *
- * TODO(crbug.com/1022102): Finish moving feature-specific methods out of this class and delete it.
- */
-public class ChromePreferenceManager {
-    // For new int values with a default of 0, just document the key and its usage, and call
-    // #readInt and #writeInt directly.
-    // For new boolean values, document the key and its usage, call #readBoolean and #writeBoolean
-    // directly. While calling #readBoolean, default value is required.
-
-    private static class LazyHolder {
-        static final ChromePreferenceManager INSTANCE = new ChromePreferenceManager();
-    }
-
-    private final SharedPreferencesManager mManager;
-
-    private ChromePreferenceManager() {
-        mManager = SharedPreferencesManager.getInstance();
-    }
-
-    /**
-     * Get the static instance of ChromePreferenceManager if exists else create it.
-     * @return the ChromePreferenceManager singleton
-     */
-    public static ChromePreferenceManager getInstance() {
-        return LazyHolder.INSTANCE;
-    }
-
-    /**
-     * Returns Chrome major version number when signin promo was last shown, or 0 if version number
-     * isn't known.
-     */
-    public int getSigninPromoLastShownVersion() {
-        return mManager.readInt(ChromePreferenceKeys.SIGNIN_PROMO_LAST_SHOWN_MAJOR_VERSION);
-    }
-
-    /**
-     * Sets Chrome major version number when signin promo was last shown.
-     */
-    public void setSigninPromoLastShownVersion(int majorVersion) {
-        mManager.writeInt(ChromePreferenceKeys.SIGNIN_PROMO_LAST_SHOWN_MAJOR_VERSION, majorVersion);
-    }
-
-    /**
-     * Returns a set of account names on the device when signin promo was last shown,
-     * or null if promo hasn't been shown yet.
-     */
-    public Set<String> getSigninPromoLastAccountNames() {
-        return mManager.readStringSet(
-                ChromePreferenceKeys.SIGNIN_PROMO_LAST_SHOWN_ACCOUNT_NAMES, null);
-    }
-
-    /**
-     * Stores a set of account names on the device when signin promo is shown.
-     */
-    public void setSigninPromoLastAccountNames(Set<String> accountNames) {
-        mManager.writeStringSet(
-                ChromePreferenceKeys.SIGNIN_PROMO_LAST_SHOWN_ACCOUNT_NAMES, accountNames);
-    }
-
-    /**
-     * Returns timestamp of the suppression period start if signin promos in the New Tab Page are
-     * temporarily suppressed; zero otherwise.
-     * @return the epoch time in milliseconds (see {@link System#currentTimeMillis()}).
-     */
-    public long getNewTabPageSigninPromoSuppressionPeriodStart() {
-        return mManager.readLong(
-                ChromePreferenceKeys.SIGNIN_PROMO_NTP_PROMO_SUPPRESSION_PERIOD_START);
-    }
-
-    /**
-     * Sets timestamp of the suppression period start if signin promos in the New Tab Page are
-     * temporarily suppressed.
-     * @param timeMillis the epoch time in milliseconds (see {@link System#currentTimeMillis()}).
-     */
-    public void setNewTabPageSigninPromoSuppressionPeriodStart(long timeMillis) {
-        mManager.writeLong(
-                ChromePreferenceKeys.SIGNIN_PROMO_NTP_PROMO_SUPPRESSION_PERIOD_START, timeMillis);
-    }
-
-    /**
-     * Removes the stored timestamp of the suppression period start when signin promos in the New
-     * Tab Page are no longer suppressed.
-     */
-    public void clearNewTabPageSigninPromoSuppressionPeriodStart() {
-        mManager.removeKey(ChromePreferenceKeys.SIGNIN_PROMO_NTP_PROMO_SUPPRESSION_PERIOD_START);
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/OWNERS b/chrome/android/java/src/org/chromium/chrome/browser/preferences/OWNERS
deleted file mode 100644
index e53de1b..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-file://chrome/browser/preferences/OWNERS
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninPreferencesManager.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninPreferencesManager.java
new file mode 100644
index 0000000..6a3a349
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninPreferencesManager.java
@@ -0,0 +1,98 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.signin;
+
+import android.support.annotation.Nullable;
+
+import androidx.annotation.VisibleForTesting;
+
+import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
+import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
+
+import java.util.Set;
+
+/**
+ * SigninPreferencesManager stores the state of the SignInPromo.
+ */
+public class SigninPreferencesManager {
+    static final SigninPreferencesManager INSTANCE = new SigninPreferencesManager();
+
+    private final SharedPreferencesManager mManager;
+
+    private SigninPreferencesManager() {
+        mManager = SharedPreferencesManager.getInstance();
+    }
+
+    /**
+     * @return the SignInPromoStore singleton
+     */
+    public static SigninPreferencesManager getInstance() {
+        return INSTANCE;
+    }
+
+    /**
+     * Returns Chrome major version number when signin promo was last shown, or 0 if version number
+     * isn't known.
+     */
+    int getSigninPromoLastShownVersion() {
+        return mManager.readInt(ChromePreferenceKeys.SIGNIN_PROMO_LAST_SHOWN_MAJOR_VERSION);
+    }
+
+    /**
+     * Sets Chrome major version number when signin promo was last shown.
+     */
+    void setSigninPromoLastShownVersion(int majorVersion) {
+        mManager.writeInt(ChromePreferenceKeys.SIGNIN_PROMO_LAST_SHOWN_MAJOR_VERSION, majorVersion);
+    }
+
+    /**
+     * Returns a set of account names on the device when signin promo was last shown,
+     * or null if promo hasn't been shown yet.
+     */
+    @Nullable
+    Set<String> getSigninPromoLastAccountNames() {
+        return mManager.readStringSet(
+                ChromePreferenceKeys.SIGNIN_PROMO_LAST_SHOWN_ACCOUNT_NAMES, null);
+    }
+
+    /**
+     * Stores a set of account names on the device when signin promo is shown.
+     */
+    void setSigninPromoLastAccountNames(Set<String> accountNames) {
+        mManager.writeStringSet(
+                ChromePreferenceKeys.SIGNIN_PROMO_LAST_SHOWN_ACCOUNT_NAMES, accountNames);
+    }
+
+    /**
+     * Returns timestamp of the suppression period start if signin promos in the New Tab Page are
+     * temporarily suppressed; zero otherwise.
+     * @return the epoch time in milliseconds (see {@link System#currentTimeMillis()}).
+     */
+    @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+    public long getNewTabPageSigninPromoSuppressionPeriodStart() {
+        return mManager.readLong(
+                ChromePreferenceKeys.SIGNIN_PROMO_NTP_PROMO_SUPPRESSION_PERIOD_START);
+    }
+
+    /**
+     * Sets timestamp of the suppression period start if signin promos in the New Tab Page are
+     * temporarily suppressed.
+     * @param timeMillis the epoch time in milliseconds (see {@link System#currentTimeMillis()}).
+     */
+    @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+    public void setNewTabPageSigninPromoSuppressionPeriodStart(long timeMillis) {
+        mManager.writeLong(
+                ChromePreferenceKeys.SIGNIN_PROMO_NTP_PROMO_SUPPRESSION_PERIOD_START, timeMillis);
+    }
+
+    /**
+     * Removes the stored timestamp of the suppression period start when signin promos in the New
+     * Tab Page are no longer suppressed.
+     */
+    @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+    public void clearNewTabPageSigninPromoSuppressionPeriodStart() {
+        mManager.removeKey(ChromePreferenceKeys.SIGNIN_PROMO_NTP_PROMO_SUPPRESSION_PERIOD_START);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninPromoUtil.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninPromoUtil.java
index 4c00bd9..f35747d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninPromoUtil.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninPromoUtil.java
@@ -14,7 +14,6 @@
 import org.chromium.base.Supplier;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.chrome.browser.ChromeVersionInfo;
-import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
 import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
 import org.chromium.components.signin.AccountManagerFacade;
@@ -38,28 +37,28 @@
      * @return Whether the signin promo is shown.
      */
     public static boolean launchSigninPromoIfNeeded(final Activity activity) {
-        ChromePreferenceManager preferenceManager = ChromePreferenceManager.getInstance();
+        SigninPreferencesManager preferencesManager = SigninPreferencesManager.getInstance();
         int currentMajorVersion = ChromeVersionInfo.getProductMajorVersion();
         boolean wasSignedIn = TextUtils.isEmpty(
                 PrefServiceBridge.getInstance().getString(Pref.SYNC_LAST_ACCOUNT_NAME));
 
         Supplier<Set<String>> accountNamesSupplier =
                 () -> new ArraySet<>(AccountManagerFacade.get().tryGetGoogleAccountNames());
-        if (!shouldLaunchSigninPromo(preferenceManager, currentMajorVersion,
+        if (!shouldLaunchSigninPromo(preferencesManager, currentMajorVersion,
                     ChromeSigninController.get().isSignedIn(), wasSignedIn, accountNamesSupplier)) {
             return false;
         }
 
         SigninUtils.startSigninActivityIfAllowed(activity, SigninAccessPoint.SIGNIN_PROMO);
-        preferenceManager.setSigninPromoLastShownVersion(currentMajorVersion);
-        preferenceManager.setSigninPromoLastAccountNames(
+        preferencesManager.setSigninPromoLastShownVersion(currentMajorVersion);
+        preferencesManager.setSigninPromoLastAccountNames(
                 new ArraySet<>(AccountManagerFacade.get().tryGetGoogleAccountNames()));
         return true;
     }
 
     /**
      * Launches the signin promo if it needs to be displayed.
-     * @param preferenceManager the preference manager to get preferences
+     * @param preferencesManager the preferences manager to persist data
      * @param currentMajorVersion the current major version of Chrome
      * @param isSignedIn is user currently signed in
      * @param wasSignedIn has used manually signed out
@@ -69,12 +68,12 @@
      * @return Whether the signin promo should be shown.
      */
     @VisibleForTesting
-    static boolean shouldLaunchSigninPromo(ChromePreferenceManager preferenceManager,
+    static boolean shouldLaunchSigninPromo(SigninPreferencesManager preferencesManager,
             int currentMajorVersion, boolean isSignedIn, boolean wasSignedIn,
             Supplier<Set<String>> accountNamesSupplier) {
-        int lastPromoMajorVersion = preferenceManager.getSigninPromoLastShownVersion();
+        int lastPromoMajorVersion = preferencesManager.getSigninPromoLastShownVersion();
         if (lastPromoMajorVersion == 0) {
-            preferenceManager.setSigninPromoLastShownVersion(currentMajorVersion);
+            preferencesManager.setSigninPromoLastShownVersion(currentMajorVersion);
             return false;
         }
 
@@ -93,7 +92,7 @@
         if (accountNames.isEmpty()) return false;
 
         // Don't show if no new accounts have been added after the last time promo was shown.
-        Set<String> previousAccountNames = preferenceManager.getSigninPromoLastAccountNames();
+        Set<String> previousAccountNames = preferencesManager.getSigninPromoLastAccountNames();
         return previousAccountNames == null || !previousAccountNames.containsAll(accountNames);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/HomeButton.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/HomeButton.java
index 198be0a..589acf5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/HomeButton.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/HomeButton.java
@@ -21,6 +21,9 @@
 import org.chromium.chrome.browser.ActivityTabProvider.ActivityTabTabObserver;
 import org.chromium.chrome.browser.ThemeColorProvider;
 import org.chromium.chrome.browser.ThemeColorProvider.TintObserver;
+import org.chromium.chrome.browser.compositor.layouts.EmptyOverviewModeObserver;
+import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior;
+import org.chromium.chrome.browser.compositor.layouts.OverviewModeState;
 import org.chromium.chrome.browser.flags.FeatureUtilities;
 import org.chromium.chrome.browser.homepage.HomepagePolicyManager;
 import org.chromium.chrome.browser.ntp.NewTabPage;
@@ -45,6 +48,12 @@
     /** The {@link ActivityTabProvider} used to know if the active tab is on the NTP. */
     private ActivityTabProvider mActivityTabProvider;
 
+    /** The {@link OverviewModeBehavior} used to observe overview state changes.  */
+    private OverviewModeBehavior mOverviewModeBehavior;
+
+    /** The {@link OvervieModeObserver} observing the OverviewModeBehavior  */
+    private OverviewModeBehavior.OverviewModeObserver mOverviewModeObserver;
+
     public HomeButton(Context context, AttributeSet attrs) {
         super(context, attrs);
 
@@ -52,6 +61,18 @@
         setImageDrawable(ContextCompat.getDrawable(context, homeButtonIcon));
         HomepageManager.getInstance().addListener(this);
         updateContextMenuListener();
+
+        mOverviewModeObserver = new EmptyOverviewModeObserver() {
+            @Override
+            public void onOverviewModeStateChanged(
+                    @OverviewModeState int overviewModeState, boolean showTabSwitcherToolbar) {
+                if (overviewModeState == OverviewModeState.SHOWN_HOMEPAGE) {
+                    updateButtonEnabledState(false);
+                } else {
+                    updateButtonEnabledState(null);
+                }
+            }
+        };
     }
 
     public void destroy() {
@@ -66,6 +87,11 @@
         }
 
         HomepageManager.getInstance().removeListener(this);
+
+        if (mOverviewModeBehavior != null) {
+            mOverviewModeBehavior.removeOverviewModeObserver(mOverviewModeObserver);
+            mOverviewModeObserver = null;
+        }
     }
 
     public void setThemeColorProvider(ThemeColorProvider themeColorProvider) {
@@ -73,6 +99,12 @@
         mThemeColorProvider.addTintObserver(this);
     }
 
+    public void setOverviewModeBehavior(OverviewModeBehavior overviewModeBehavior) {
+        assert overviewModeBehavior != null;
+        mOverviewModeBehavior = overviewModeBehavior;
+        mOverviewModeBehavior.addOverviewModeObserver(mOverviewModeObserver);
+    }
+
     @Override
     public void onTintChanged(ColorStateList tint, boolean useLight) {
         ApiCompatibilityUtils.setImageTintList(this, tint);
@@ -135,6 +167,10 @@
             // view, or from one tab to another tab.
             isEnabled = !isTabNTP(tab);
         }
+        updateButtonEnabledState(isEnabled);
+    }
+
+    private void updateButtonEnabledState(boolean isEnabled) {
         setEnabled(isEnabled);
         updateContextMenuListener();
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonCoordinator.java
index 2895e983..b7f6eae 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonCoordinator.java
@@ -11,6 +11,9 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ThemeColorProvider;
 import org.chromium.chrome.browser.ThemeColorProvider.TintObserver;
+import org.chromium.chrome.browser.compositor.layouts.EmptyOverviewModeObserver;
+import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior;
+import org.chromium.chrome.browser.compositor.layouts.OverviewModeState;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabModelObserver;
@@ -41,6 +44,12 @@
     private TabCountProvider mTabCountProvider;
     private TabCountObserver mTabCountObserver;
 
+    /** The {@link OverviewModeBehavior} used to observe overview state changes.  */
+    private OverviewModeBehavior mOverviewModeBehavior;
+
+    /** The {@link OvervieModeObserver} observing the OverviewModeBehavior  */
+    private OverviewModeBehavior.OverviewModeObserver mOverviewModeObserver;
+
     /**
      * Build the controller that manages the tab switcher button.
      * @param root The root {@link ViewGroup} for locating the view to inflate.
@@ -49,6 +58,14 @@
         final TabSwitcherButtonView view = root.findViewById(R.id.tab_switcher_button);
         PropertyModelChangeProcessor.create(
                 mTabSwitcherButtonModel, view, new TabSwitcherButtonViewBinder());
+        mOverviewModeObserver = new EmptyOverviewModeObserver() {
+            @Override
+            public void onOverviewModeStateChanged(
+                    @OverviewModeState int overviewModeState, boolean showTabSwitcherToolbar) {
+                mTabSwitcherButtonModel.set(TabSwitcherButtonProperties.IS_ENABLED,
+                        (overviewModeState == OverviewModeState.SHOWN_TABSWITCHER ? false : true));
+            }
+        };
     }
 
     /**
@@ -81,6 +98,12 @@
         mTabCountProvider.addObserver(mTabCountObserver);
     }
 
+    public void setOverviewModeBehavior(OverviewModeBehavior overviewModeBehavior) {
+        assert overviewModeBehavior != null;
+        mOverviewModeBehavior = overviewModeBehavior;
+        mOverviewModeBehavior.addOverviewModeObserver(mOverviewModeObserver);
+    }
+
     public void destroy() {
         if (mThemeColorProvider != null) {
             mThemeColorProvider.removeTintObserver(mTintObserver);
@@ -90,5 +113,9 @@
             mTabCountProvider.removeObserver(mTabCountObserver);
             mTabCountProvider = null;
         }
+        if (mOverviewModeBehavior != null) {
+            mOverviewModeBehavior.removeOverviewModeObserver(mOverviewModeObserver);
+            mOverviewModeObserver = null;
+        }
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonProperties.java
index 4f140ed..249dce2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonProperties.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonProperties.java
@@ -9,6 +9,7 @@
 import android.view.View.OnLongClickListener;
 
 import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableBooleanPropertyKey;
 import org.chromium.ui.modelutil.PropertyModel.WritableIntPropertyKey;
 import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
 
@@ -31,6 +32,9 @@
     public static final WritableObjectPropertyKey<ColorStateList> TINT =
             new WritableObjectPropertyKey<>();
 
-    public static final PropertyKey[] ALL_KEYS =
-            new PropertyKey[] {NUMBER_OF_TABS, ON_CLICK_LISTENER, ON_LONG_CLICK_LISTENER, TINT};
+    /** Whether the button is enabled. */
+    public static final WritableBooleanPropertyKey IS_ENABLED = new WritableBooleanPropertyKey();
+
+    public static final PropertyKey[] ALL_KEYS = new PropertyKey[] {
+            NUMBER_OF_TABS, ON_CLICK_LISTENER, ON_LONG_CLICK_LISTENER, TINT, IS_ENABLED};
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonViewBinder.java
index d7c91e08..c017fe9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonViewBinder.java
@@ -34,6 +34,8 @@
                     model.get(TabSwitcherButtonProperties.ON_LONG_CLICK_LISTENER));
         } else if (TabSwitcherButtonProperties.TINT == propertyKey) {
             view.setTint(model.get(TabSwitcherButtonProperties.TINT));
+        } else if (TabSwitcherButtonProperties.IS_ENABLED == propertyKey) {
+            view.setEnabled(model.get(TabSwitcherButtonProperties.IS_ENABLED));
         } else {
             assert false : "Unhandled property detected in TabSwitcherViewBinder!";
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarCoordinator.java
index 26ce4af..4f5bc0e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarCoordinator.java
@@ -91,7 +91,8 @@
             TabCountProvider tabCountProvider, IncognitoStateProvider incognitoStateProvider,
             ViewGroup topToolbarRoot) {
         mBrowsingModeCoordinator.initializeWithNative(tabSwitcherListener, menuButtonHelper,
-                tabCountProvider, mThemeColorProvider, incognitoStateProvider);
+                tabCountProvider, mThemeColorProvider, incognitoStateProvider,
+                overviewModeBehavior);
         mTabSwitcherModeCoordinator = new TabSwitcherBottomToolbarCoordinator(mTabSwitcherModeStub,
                 topToolbarRoot, incognitoStateProvider, mThemeColorProvider, newTabClickListener,
                 closeTabsClickListener, menuButtonHelper, tabCountProvider);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarCoordinator.java
index a55fcdd..794d6db 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarCoordinator.java
@@ -14,9 +14,11 @@
 import org.chromium.chrome.browser.ActivityTabProvider;
 import org.chromium.chrome.browser.ActivityTabProvider.HintlessActivityTabObserver;
 import org.chromium.chrome.browser.ThemeColorProvider;
+import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior;
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabImpl;
+import org.chromium.chrome.browser.tasks.ReturnToChromeExperimentsUtil;
 import org.chromium.chrome.browser.toolbar.HomeButton;
 import org.chromium.chrome.browser.toolbar.IncognitoStateProvider;
 import org.chromium.chrome.browser.toolbar.MenuButton;
@@ -169,10 +171,12 @@
      * @param tabCountProvider Updates the tab count number in the tab switcher button.
      * @param themeColorProvider Notifies components when theme color changes.
      * @param incognitoStateProvider Notifies components when incognito state changes.
+     * @param overviewModeBehavior Notifies components when overview mode changes.
      */
     void initializeWithNative(OnClickListener tabSwitcherListener,
             AppMenuButtonHelper menuButtonHelper, TabCountProvider tabCountProvider,
-            ThemeColorProvider themeColorProvider, IncognitoStateProvider incognitoStateProvider) {
+            ThemeColorProvider themeColorProvider, IncognitoStateProvider incognitoStateProvider,
+            OverviewModeBehavior overviewModeBehavior) {
         mMediator.setThemeColorProvider(themeColorProvider);
 
         mHomeButton.setThemeColorProvider(themeColorProvider);
@@ -191,6 +195,15 @@
         assert menuButtonHelper != null;
         mMenuButton.setAppMenuButtonHelper(menuButtonHelper);
         mMenuButton.setThemeColorProvider(themeColorProvider);
+
+        // If StartSurface is HomePage, BrowsingModeBottomToolbar is shown in browsing mode and in
+        // overview mode. We need to pass the OverviewModeBehavior to the buttons so they are
+        // disabled based on the moverview state.
+        if (ReturnToChromeExperimentsUtil.shouldShowStartSurfaceAsTheHomePage()) {
+            mShareButton.setOverviewModeBehavior(overviewModeBehavior);
+            mTabSwitcherButtonCoordinator.setOverviewModeBehavior(overviewModeBehavior);
+            mHomeButton.setOverviewModeBehavior(overviewModeBehavior);
+        }
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/ShareButton.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/ShareButton.java
index 2f8da3d..0d9bd287 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/ShareButton.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/ShareButton.java
@@ -13,6 +13,8 @@
 import org.chromium.chrome.browser.ActivityTabProvider.ActivityTabTabObserver;
 import org.chromium.chrome.browser.ThemeColorProvider;
 import org.chromium.chrome.browser.ThemeColorProvider.TintObserver;
+import org.chromium.chrome.browser.compositor.layouts.EmptyOverviewModeObserver;
+import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabImpl;
 import org.chromium.chrome.browser.util.UrlConstants;
@@ -28,8 +30,21 @@
     /** The {@link ActivityTabTabObserver} used to know when the active page changed. */
     private ActivityTabTabObserver mActivityTabTabObserver;
 
+    /** The {@link OverviewModeBehavior} used to observe overview state changes.  */
+    private OverviewModeBehavior mOverviewModeBehavior;
+
+    /** The {@link OvervieModeObserver} observing the OverviewModeBehavior  */
+    private OverviewModeBehavior.OverviewModeObserver mOverviewModeObserver;
+
     public ShareButton(Context context, AttributeSet attrs) {
         super(context, attrs);
+
+        mOverviewModeObserver = new EmptyOverviewModeObserver() {
+            @Override
+            public void onOverviewModeStartedShowing(boolean showTabSwitcherToolbar) {
+                setEnabled(false);
+            }
+        };
     }
 
     void setThemeColorProvider(ThemeColorProvider themeColorProvider) {
@@ -53,6 +68,12 @@
         };
     }
 
+    public void setOverviewModeBehavior(OverviewModeBehavior overviewModeBehavior) {
+        assert overviewModeBehavior != null;
+        mOverviewModeBehavior = overviewModeBehavior;
+        mOverviewModeBehavior.addOverviewModeObserver(mOverviewModeObserver);
+    }
+
     void destroy() {
         if (mThemeColorProvider != null) {
             mThemeColorProvider.removeTintObserver(this);
@@ -62,6 +83,11 @@
             mActivityTabTabObserver.destroy();
             mActivityTabTabObserver = null;
         }
+
+        if (mOverviewModeBehavior != null) {
+            mOverviewModeBehavior.removeOverviewModeObserver(mOverviewModeObserver);
+            mOverviewModeObserver = null;
+        }
     }
 
     private void updateButtonEnabledState(Tab tab) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
index 707480e..d1bc8ba 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
@@ -48,6 +48,7 @@
 import org.chromium.chrome.browser.vr.VrModuleProvider;
 import org.chromium.chrome.browser.widget.ScrimView;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController;
+import org.chromium.components.paintpreview.browser.PaintPreviewUtils;
 import org.chromium.ui.base.DeviceFormFactor;
 import org.chromium.ui.modaldialog.ModalDialogManager.ModalDialogManagerObserver;
 import org.chromium.ui.modelutil.PropertyModel;
@@ -251,6 +252,8 @@
         } else if (id == R.id.share_menu_id || id == R.id.direct_share_menu_id) {
             onShareMenuItemSelected(id == R.id.direct_share_menu_id,
                     mActivity.getTabModelSelector().isIncognitoSelected());
+        } else if (id == R.id.paint_preview_capture_id) {
+            PaintPreviewUtils.capturePaintPreview(mActivity.getCurrentWebContents());
         }
 
         return false;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java
index ced48cc..90cc6fd1 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java
@@ -70,11 +70,11 @@
 import org.chromium.chrome.browser.ntp.snippets.SnippetArticle;
 import org.chromium.chrome.browser.offlinepages.OfflinePageBridge;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
-import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
 import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
 import org.chromium.chrome.browser.signin.SigninManager;
+import org.chromium.chrome.browser.signin.SigninPreferencesManager;
 import org.chromium.chrome.browser.suggestions.ContentSuggestionsAdditionalAction;
 import org.chromium.chrome.browser.suggestions.DestructionObserver;
 import org.chromium.chrome.browser.suggestions.SuggestionsEventReporter;
@@ -341,7 +341,7 @@
         CardsVariationParameters.setTestVariationParams(null);
         SharedPreferencesManager.getInstance().writeBoolean(
                 ChromePreferenceKeys.SIGNIN_PROMO_NTP_PROMO_DISMISSED, false);
-        ChromePreferenceManager.getInstance().clearNewTabPageSigninPromoSuppressionPeriodStart();
+        SigninPreferencesManager.getInstance().clearNewTabPageSigninPromoSuppressionPeriodStart();
         PrefServiceBridge.setInstanceForTesting(null);
         ShadowPostTask.reset();
         ShadowChromeFeatureList.reset();
@@ -1003,7 +1003,7 @@
         useArticleCategory();
 
         // Suppress promo.
-        ChromePreferenceManager.getInstance().setNewTabPageSigninPromoSuppressionPeriodStart(
+        SigninPreferencesManager.getInstance().setNewTabPageSigninPromoSuppressionPeriodStart(
                 System.currentTimeMillis());
 
         resetUiDelegate();
@@ -1019,8 +1019,8 @@
         useArticleCategory();
 
         // Suppress promo.
-        ChromePreferenceManager preferenceManager = ChromePreferenceManager.getInstance();
-        preferenceManager.setNewTabPageSigninPromoSuppressionPeriodStart(
+        SigninPreferencesManager preferencesManager = SigninPreferencesManager.getInstance();
+        preferencesManager.setNewTabPageSigninPromoSuppressionPeriodStart(
                 System.currentTimeMillis() - SignInPromo.SUPPRESSION_PERIOD_MS);
 
         resetUiDelegate();
@@ -1028,7 +1028,7 @@
         assertTrue(isSignInPromoVisible());
 
         // SignInPromo should clear shared preference when suppression period ends.
-        assertEquals(0, preferenceManager.getNewTabPageSigninPromoSuppressionPeriodStart());
+        assertEquals(0, preferencesManager.getNewTabPageSigninPromoSuppressionPeriodStart());
     }
 
     @Test
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/signin/SigninPromoUtilTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/signin/SigninPromoUtilTest.java
index 67f318d..f26c1b16 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/signin/SigninPromoUtilTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/signin/SigninPromoUtilTest.java
@@ -23,7 +23,6 @@
 
 import org.chromium.base.Supplier;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
 
 import java.util.Arrays;
 import java.util.Set;
@@ -32,18 +31,18 @@
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
 public class SigninPromoUtilTest {
-    private ChromePreferenceManager mPreferenceManager;
+    private SigninPreferencesManager mPreferencesManager;
 
     @Before
     public void setUp() {
-        mPreferenceManager = Mockito.mock(ChromePreferenceManager.class);
+        mPreferencesManager = Mockito.mock(SigninPreferencesManager.class);
         // Return default value if last account names are requested
-        when(mPreferenceManager.getSigninPromoLastAccountNames()).thenReturn(null);
+        when(mPreferencesManager.getSigninPromoLastAccountNames()).thenReturn(null);
     }
 
     @After
     public void tearDown() {
-        verifyNoMoreInteractions(ignoreStubs(mPreferenceManager));
+        verifyNoMoreInteractions(ignoreStubs(mPreferencesManager));
     }
 
     /**
@@ -67,73 +66,73 @@
 
     @Test
     public void whenNoLastShownVersionShouldReturnFalseAndSaveVersion() {
-        when(mPreferenceManager.getSigninPromoLastShownVersion()).thenReturn(0);
+        when(mPreferencesManager.getSigninPromoLastShownVersion()).thenReturn(0);
         Assert.assertFalse(SigninPromoUtil.shouldLaunchSigninPromo(
-                mPreferenceManager, 42, false, false, shouldNotTryToGetAccounts()));
-        verify(mPreferenceManager).setSigninPromoLastShownVersion(42);
+                mPreferencesManager, 42, false, false, shouldNotTryToGetAccounts()));
+        verify(mPreferencesManager).setSigninPromoLastShownVersion(42);
     }
 
     @Test
     public void whenSignedInShouldReturnFalse() {
-        when(mPreferenceManager.getSigninPromoLastShownVersion()).thenReturn(38);
+        when(mPreferencesManager.getSigninPromoLastShownVersion()).thenReturn(38);
         Assert.assertFalse(SigninPromoUtil.shouldLaunchSigninPromo(
-                mPreferenceManager, 42, true, false, shouldNotTryToGetAccounts()));
+                mPreferencesManager, 42, true, false, shouldNotTryToGetAccounts()));
     }
 
     @Test
     public void whenWasSignedInShouldReturnFalse() {
-        when(mPreferenceManager.getSigninPromoLastShownVersion()).thenReturn(38);
+        when(mPreferencesManager.getSigninPromoLastShownVersion()).thenReturn(38);
         Assert.assertFalse(SigninPromoUtil.shouldLaunchSigninPromo(
-                mPreferenceManager, 42, false, true, shouldNotTryToGetAccounts()));
+                mPreferencesManager, 42, false, true, shouldNotTryToGetAccounts()));
     }
 
     @Test
     public void whenVersionDifferenceTooSmallShouldReturnFalse() {
-        when(mPreferenceManager.getSigninPromoLastShownVersion()).thenReturn(41);
+        when(mPreferencesManager.getSigninPromoLastShownVersion()).thenReturn(41);
         Assert.assertFalse(SigninPromoUtil.shouldLaunchSigninPromo(
-                mPreferenceManager, 42, false, false, shouldNotTryToGetAccounts()));
+                mPreferencesManager, 42, false, false, shouldNotTryToGetAccounts()));
     }
 
     @Test
     public void whenNoAccountsShouldReturnFalse() {
-        when(mPreferenceManager.getSigninPromoLastShownVersion()).thenReturn(38);
+        when(mPreferencesManager.getSigninPromoLastShownVersion()).thenReturn(38);
         Assert.assertFalse(SigninPromoUtil.shouldLaunchSigninPromo(
-                mPreferenceManager, 42, false, false, accountsSupplier()));
+                mPreferencesManager, 42, false, false, accountsSupplier()));
     }
 
     @Test
     public void whenNoAccountListStoredShouldReturnTrue() {
-        when(mPreferenceManager.getSigninPromoLastShownVersion()).thenReturn(40);
+        when(mPreferencesManager.getSigninPromoLastShownVersion()).thenReturn(40);
         // Old implementation hasn't been storing account list
-        when(mPreferenceManager.getSigninPromoLastAccountNames()).thenReturn(null);
+        when(mPreferencesManager.getSigninPromoLastAccountNames()).thenReturn(null);
         Assert.assertTrue(SigninPromoUtil.shouldLaunchSigninPromo(
-                mPreferenceManager, 42, false, false, accountsSupplier("test@gmail.com")));
+                mPreferencesManager, 42, false, false, accountsSupplier("test@gmail.com")));
     }
 
     @Test
     public void whenHasNewAccountShouldReturnTrue() {
-        when(mPreferenceManager.getSigninPromoLastShownVersion()).thenReturn(40);
-        when(mPreferenceManager.getSigninPromoLastAccountNames())
+        when(mPreferencesManager.getSigninPromoLastShownVersion()).thenReturn(40);
+        when(mPreferencesManager.getSigninPromoLastAccountNames())
                 .thenReturn(ImmutableSet.of("test@gmail.com"));
-        Assert.assertTrue(SigninPromoUtil.shouldLaunchSigninPromo(mPreferenceManager, 42, false,
+        Assert.assertTrue(SigninPromoUtil.shouldLaunchSigninPromo(mPreferencesManager, 42, false,
                 false, accountsSupplier("test@gmail.com", "test2@gmail.com")));
     }
 
     @Test
     public void whenAccountListUnchangedShouldReturnFalse() {
-        when(mPreferenceManager.getSigninPromoLastShownVersion()).thenReturn(40);
-        when(mPreferenceManager.getSigninPromoLastAccountNames())
+        when(mPreferencesManager.getSigninPromoLastShownVersion()).thenReturn(40);
+        when(mPreferencesManager.getSigninPromoLastAccountNames())
                 .thenReturn(ImmutableSet.of("test@gmail.com"));
         Assert.assertFalse(SigninPromoUtil.shouldLaunchSigninPromo(
-                mPreferenceManager, 42, false, false, accountsSupplier("test@gmail.com")));
+                mPreferencesManager, 42, false, false, accountsSupplier("test@gmail.com")));
     }
 
     @Test
     public void whenNoNewAccountsShouldReturnFalse() {
-        when(mPreferenceManager.getSigninPromoLastShownVersion()).thenReturn(40);
-        when(mPreferenceManager.getSigninPromoLastAccountNames())
+        when(mPreferencesManager.getSigninPromoLastShownVersion()).thenReturn(40);
+        when(mPreferencesManager.getSigninPromoLastAccountNames())
                 .thenReturn(ImmutableSet.of("test@gmail.com", "test2@gmail.com"));
         Assert.assertFalse(SigninPromoUtil.shouldLaunchSigninPromo(
-                mPreferenceManager, 42, false, false, accountsSupplier("test2@gmail.com")));
+                mPreferencesManager, 42, false, false, accountsSupplier("test2@gmail.com")));
     }
 }
diff --git a/chrome/app/BUILD.gn b/chrome/app/BUILD.gn
index f109df1..8dc7f15 100644
--- a/chrome/app/BUILD.gn
+++ b/chrome/app/BUILD.gn
@@ -220,7 +220,7 @@
     "//components/metrics/public/mojom:call_stack_mojo_bindings",
     "//components/page_load_metrics/common:page_load_metrics_mojom",
     "//components/rappor/public/mojom",
-    "//components/safe_browsing/common:interfaces",
+    "//components/safe_browsing/core/common:interfaces",
     "//components/services/heap_profiling/public/mojom",
     "//components/translate/content/common",
     "//extensions/buildflags",
diff --git a/chrome/app/chrome_content_browser_overlay_manifest.cc b/chrome/app/chrome_content_browser_overlay_manifest.cc
index 631630f..744ad94 100644
--- a/chrome/app/chrome_content_browser_overlay_manifest.cc
+++ b/chrome/app/chrome_content_browser_overlay_manifest.cc
@@ -15,7 +15,7 @@
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy.mojom.h"
 #include "components/metrics/public/mojom/call_stack_profile_collector.mojom.h"
 #include "components/rappor/public/mojom/rappor_recorder.mojom.h"
-#include "components/safe_browsing/common/safe_browsing.mojom.h"
+#include "components/safe_browsing/core/common/safe_browsing.mojom.h"
 #include "extensions/buildflags/buildflags.h"
 #include "services/service_manager/public/cpp/manifest_builder.h"
 
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index cc453704..40e1c72 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -4,6 +4,7 @@
 
 import("//base/allocator/allocator.gni")
 import("//build/buildflag_header.gni")
+import("//build/config/buildflags_paint_preview.gni")
 import("//build/config/chrome_build.gni")
 import("//build/config/crypto.gni")
 import("//build/config/features.gni")
@@ -878,6 +879,8 @@
     "navigation_predictor/navigation_predictor_keyed_service_factory.h",
     "navigation_predictor/navigation_predictor_preconnect_client.cc",
     "navigation_predictor/navigation_predictor_preconnect_client.h",
+    "navigation_predictor/search_engine_preconnector.cc",
+    "navigation_predictor/search_engine_preconnector.h",
     "net/chrome_cookie_notification_details.h",
     "net/chrome_mojo_proxy_resolver_factory.cc",
     "net/chrome_mojo_proxy_resolver_factory.h",
@@ -1719,8 +1722,6 @@
     "ssl/chrome_ssl_host_state_delegate.h",
     "ssl/chrome_ssl_host_state_delegate_factory.cc",
     "ssl/chrome_ssl_host_state_delegate_factory.h",
-    "ssl/common_name_mismatch_handler.cc",
-    "ssl/common_name_mismatch_handler.h",
     "ssl/connection_help_tab_helper.cc",
     "ssl/connection_help_tab_helper.h",
     "ssl/insecure_sensitive_input_driver.cc",
@@ -2075,6 +2076,8 @@
     "//components/omnibox/browser",
     "//components/optimization_guide",
     "//components/os_crypt",
+    "//components/paint_preview/buildflags",
+    "//components/paint_preview/features",
     "//components/password_manager/content/browser",
     "//components/password_manager/core/browser",
     "//components/password_manager/core/common",
@@ -2094,7 +2097,7 @@
     "//components/rappor:rappor_recorder",
     "//components/renderer_context_menu",
     "//components/resources",
-    "//components/safe_browsing:public",
+    "//components/safe_browsing/core:public",
     "//components/safe_search_api",
     "//components/safe_search_api:safe_search_client",
     "//components/search",
@@ -2993,6 +2996,8 @@
       "//components/module_installer/android:native",
       "//components/omnibox/browser",
       "//components/page_load_metrics/browser",
+      "//components/paint_preview/browser/android",
+      "//components/paint_preview/browser/android:jni_headers",
       "//components/payments/content/android",
       "//components/resources:components_resources",
       "//components/send_tab_to_self",
@@ -4437,6 +4442,13 @@
     }
   }
 
+  if (enable_paint_preview) {
+    deps += [
+      "//components/paint_preview/browser",
+      "//components/paint_preview/common",
+    ]
+  }
+
   if (enable_captive_portal_detection) {
     sources += [
       "captive_portal/captive_portal_login_detector.cc",
@@ -5688,7 +5700,7 @@
     "//components/password_manager/core/browser:test_support",
     "//components/policy/core/browser:test_support",
     "//components/prefs:test_support",
-    "//components/safe_browsing:csd_proto",
+    "//components/safe_browsing/core:csd_proto",
     "//components/search_engines:test_support",
     "//components/services/unzip/content",
     "//components/sessions:test_support",
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS
index b7e9e6c07..f7a859f2 100644
--- a/chrome/browser/DEPS
+++ b/chrome/browser/DEPS
@@ -166,6 +166,9 @@
   "+components/ownership",
   "+components/page_load_metrics/browser",
   "+components/page_load_metrics/common",
+  "+components/paint_preview/buildflags",
+  "+components/paint_preview/features",
+  "+components/paint_preview/browser",
   "+components/password_manager/content/browser",
   "+components/password_manager/core/browser",
   "+components/password_manager/core/common",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 24265cfd..d756610 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -96,13 +96,15 @@
 #include "components/offline_pages/core/offline_page_feature.h"
 #include "components/omnibox/browser/omnibox_field_trial.h"
 #include "components/omnibox/common/omnibox_features.h"
+#include "components/paint_preview/buildflags/buildflags.h"
+#include "components/paint_preview/features/features.h"
 #include "components/password_manager/core/common/password_manager_features.h"
 #include "components/payments/core/features.h"
 #include "components/policy/core/common/features.h"
 #include "components/previews/core/previews_features.h"
 #include "components/previews/core/previews_switches.h"
 #include "components/printing/browser/features.h"
-#include "components/safe_browsing/features.h"
+#include "components/safe_browsing/core/features.h"
 #include "components/security_interstitials/core/features.h"
 #include "components/security_state/core/features.h"
 #include "components/security_state/core/security_state.h"
@@ -4809,6 +4811,12 @@
      FEATURE_VALUE_TYPE(base::kSlowDCTimerInterruptsWin)},
 #endif  // OS_WIN
 
+#if BUILDFLAG(ENABLE_PAINT_PREVIEW) && defined(OS_ANDROID)
+    {"paint-preview-test", flag_descriptions::kPaintPreviewTestName,
+     flag_descriptions::kPaintPreviewTestDescription, kOsAndroid,
+     FEATURE_VALUE_TYPE(paint_preview::kPaintPreviewTest)},
+#endif  // ENABLE_PAINT_PREVIEW && defined(OS_ANDROID)
+
     // NOTE: Adding a new flag requires adding a corresponding entry to enum
     // "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag
     // Histograms" in tools/metrics/histograms/README.md (run the
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc
index ef143fbf..79cd0d3c 100644
--- a/chrome/browser/android/chrome_feature_list.cc
+++ b/chrome/browser/android/chrome_feature_list.cc
@@ -31,10 +31,11 @@
 #include "components/ntp_tiles/features.h"
 #include "components/offline_pages/core/offline_page_feature.h"
 #include "components/omnibox/common/omnibox_features.h"
+#include "components/paint_preview/features/features.h"
 #include "components/password_manager/core/common/password_manager_features.h"
 #include "components/payments/core/features.h"
 #include "components/previews/core/previews_features.h"
-#include "components/safe_browsing/features.h"
+#include "components/safe_browsing/core/features.h"
 #include "components/security_state/core/features.h"
 #include "components/signin/public/base/account_consistency_method.h"
 #include "components/subresource_filter/core/browser/subresource_filter_features.h"
@@ -93,6 +94,7 @@
     &kAdjustWebApkInstallationSpace,
     &kAllowNewIncognitoTabIntents,
     &kAllowRemoteContextForNotifications,
+    &kAndroidBlockIntentNonSafelistedHeaders,
     &kAndroidNightMode,
     &kAndroidNightModeCCT,
     &kAndroidNightModeForQ,
@@ -203,6 +205,7 @@
     &kWebApkAdaptiveIcon,
     &net::features::kSameSiteByDefaultCookies,
     &net::features::kCookiesWithoutSameSiteMustBeSecure,
+    &paint_preview::kPaintPreviewTest,
     &payments::features::kAlwaysAllowJustInTimePaymentApp,
     &payments::features::kPaymentRequestSkipToGPay,
     &payments::features::kPaymentRequestSkipToGPayIfNoCard,
@@ -259,6 +262,10 @@
 const base::Feature kAdjustWebApkInstallationSpace = {
     "AdjustWebApkInstallationSpace", base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kAndroidBlockIntentNonSafelistedHeaders{
+    "AndroidBlockIntentNonSafelistedHeaders",
+    base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kAndroidNightMode{"AndroidNightMode",
                                       base::FEATURE_ENABLED_BY_DEFAULT};
 
diff --git a/chrome/browser/android/chrome_feature_list.h b/chrome/browser/android/chrome_feature_list.h
index 94f1dd2..0b4ec90 100644
--- a/chrome/browser/android/chrome_feature_list.h
+++ b/chrome/browser/android/chrome_feature_list.h
@@ -15,6 +15,7 @@
 extern const base::Feature kAdjustWebApkInstallationSpace;
 extern const base::Feature kAllowNewIncognitoTabIntents;
 extern const base::Feature kAllowRemoteContextForNotifications;
+extern const base::Feature kAndroidBlockIntentNonSafelistedHeaders;
 extern const base::Feature kAndroidNightMode;
 extern const base::Feature kAndroidNightModeCCT;
 extern const base::Feature kAndroidNightModeForQ;
diff --git a/chrome/browser/android/preferences/prefs.h b/chrome/browser/android/preferences/prefs.h
index 47fdd12a..899fe61 100644
--- a/chrome/browser/android/preferences/prefs.h
+++ b/chrome/browser/android/preferences/prefs.h
@@ -20,7 +20,7 @@
 #include "components/offline_pages/core/prefetch/prefetch_prefs.h"
 #include "components/password_manager/core/common/password_manager_pref_names.h"
 #include "components/payments/core/payment_prefs.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/signin/public/base/signin_pref_names.h"
 #include "components/translate/core/browser/translate_pref_names.h"
 
diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc
index a716174..ada82d56 100644
--- a/chrome/browser/browser_process_impl.cc
+++ b/chrome/browser/browser_process_impl.cc
@@ -104,7 +104,7 @@
 #include "components/prefs/pref_service.h"
 #include "components/rappor/public/rappor_utils.h"
 #include "components/rappor/rappor_service_impl.h"
-#include "components/safe_browsing/safe_browsing_service_interface.h"
+#include "components/safe_browsing/core/safe_browsing_service_interface.h"
 #include "components/sessions/core/session_id_generator.h"
 #include "components/subresource_filter/content/browser/ruleset_service.h"
 #include "components/subresource_filter/core/browser/subresource_filter_constants.h"
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
index b1d3ccd50..230c0ba 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
@@ -89,7 +89,7 @@
 #include "components/password_manager/core/browser/password_store_consumer.h"
 #include "components/password_manager/core/common/password_manager_features.h"
 #include "components/prefs/testing_pref_service.h"
-#include "components/safe_browsing/verdict_cache_manager.h"
+#include "components/safe_browsing/core/verdict_cache_manager.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browsing_data_filter_builder.h"
 #include "content/public/browser/browsing_data_remover.h"
diff --git a/chrome/browser/captive_portal/captive_portal_browsertest.cc b/chrome/browser/captive_portal/captive_portal_browsertest.cc
index d6b5fef..a487cef 100644
--- a/chrome/browser/captive_portal/captive_portal_browsertest.cc
+++ b/chrome/browser/captive_portal/captive_portal_browsertest.cc
@@ -71,7 +71,6 @@
 #include "content/public/test/url_loader_interceptor.h"
 #include "net/base/net_errors.h"
 #include "net/cert/x509_certificate.h"
-#include "net/dns/mock_host_resolver.h"
 #include "net/http/transport_security_state.h"
 #include "net/test/cert_test_util.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
@@ -911,9 +910,6 @@
       std::make_unique<content::URLLoaderInterceptor>(base::Bind(
           &CaptivePortalBrowserTest::OnIntercept, base::Unretained(this)));
 
-  // Do not introduce DNS errors.
-  host_resolver()->AddRule("*", "127.0.0.1");
-
   // Double-check that the captive portal service isn't enabled by default for
   // browser tests.
   EXPECT_EQ(CaptivePortalService::DISABLED_FOR_TESTING,
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index f53b622..2d4e39a4 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -228,15 +228,15 @@
 #include "components/previews/core/previews_switches.h"
 #include "components/rappor/public/rappor_utils.h"
 #include "components/rappor/rappor_service_impl.h"
-#include "components/safe_browsing/browser/browser_url_loader_throttle.h"
-#include "components/safe_browsing/browser/url_checker_delegate.h"
 #include "components/safe_browsing/buildflags.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/db/database_manager.h"
-#include "components/safe_browsing/features.h"
-#include "components/safe_browsing/password_protection/password_protection_navigation_throttle.h"
-#include "components/safe_browsing/realtime/policy_engine.h"
-#include "components/safe_browsing/verdict_cache_manager.h"
+#include "components/safe_browsing/content/browser/browser_url_loader_throttle.h"
+#include "components/safe_browsing/content/password_protection/password_protection_navigation_throttle.h"
+#include "components/safe_browsing/core/browser/url_checker_delegate.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/db/database_manager.h"
+#include "components/safe_browsing/core/features.h"
+#include "components/safe_browsing/core/realtime/policy_engine.h"
+#include "components/safe_browsing/core/verdict_cache_manager.h"
 #include "components/security_interstitials/content/origin_policy_ui.h"
 #include "components/security_interstitials/content/ssl_cert_reporter.h"
 #include "components/security_interstitials/content/ssl_error_navigation_throttle.h"
@@ -4240,9 +4240,9 @@
     result.push_back(safe_browsing::BrowserURLLoaderThrottle::Create(
         base::BindOnce(
             &ChromeContentBrowserClient::GetSafeBrowsingUrlCheckerDelegate,
-            base::Unretained(this)),
-        wc_getter, frame_tree_node_id, profile->GetResourceContext(),
-        cache_manager));
+            base::Unretained(this),
+            profile->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled)),
+        wc_getter, frame_tree_node_id, cache_manager));
   }
 #endif
 
@@ -4898,11 +4898,10 @@
 
 scoped_refptr<safe_browsing::UrlCheckerDelegate>
 ChromeContentBrowserClient::GetSafeBrowsingUrlCheckerDelegate(
-    content::ResourceContext* resource_context) {
+    bool safe_browsing_enabled_for_profile) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
-  ProfileIOData* io_data = ProfileIOData::FromResourceContext(resource_context);
-  if (!io_data->safe_browsing_enabled()->GetValue())
+  if (!safe_browsing_enabled_for_profile)
     return nullptr;
 
   // |safe_browsing_service_| may be unavailable in tests.
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index 2ff5032..2ff70c40 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -686,7 +686,7 @@
 #endif
 
   scoped_refptr<safe_browsing::UrlCheckerDelegate>
-  GetSafeBrowsingUrlCheckerDelegate(content::ResourceContext* resource_context);
+  GetSafeBrowsingUrlCheckerDelegate(bool safe_browsing_enabled_for_profile);
 
 #if BUILDFLAG(ENABLE_PLUGINS)
   // Set of origins that can use TCP/UDP private APIs from NaCl.
diff --git a/chrome/browser/chrome_content_browser_client_receiver_bindings.cc b/chrome/browser/chrome_content_browser_client_receiver_bindings.cc
index 020bf814..aed1cbb 100644
--- a/chrome/browser/chrome_content_browser_client_receiver_bindings.cc
+++ b/chrome/browser/chrome_content_browser_client_receiver_bindings.cc
@@ -29,12 +29,13 @@
 #include "components/rappor/public/rappor_utils.h"
 #include "components/rappor/rappor_recorder_impl.h"
 #include "components/rappor/rappor_service_impl.h"
-#include "components/safe_browsing/browser/mojo_safe_browsing_impl.h"
 #include "components/safe_browsing/buildflags.h"
+#include "components/safe_browsing/content/browser/mojo_safe_browsing_impl.h"
 #include "components/spellcheck/spellcheck_buildflags.h"
 #include "components/startup_metric_utils/browser/startup_metric_host_impl.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/render_process_host.h"
+#include "content/public/browser/resource_context.h"
 #include "media/mojo/buildflags.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
 #include "third_party/widevine/cdm/buildflags.h"
@@ -89,6 +90,36 @@
   drp_settings->data_reduction_proxy_service()->Clone(std::move(receiver));
 }
 
+// Helper method for ExposeInterfacesToRenderer() that checks the latest
+// SafeBrowsing pref value on the UI thread before hopping over to the IO
+// thread.
+void MaybeCreateSafeBrowsingForRenderer(
+    int process_id,
+    content::ResourceContext* resource_context,
+    base::RepeatingCallback<scoped_refptr<safe_browsing::UrlCheckerDelegate>(
+        bool safe_browsing_enabled)> get_checker_delegate,
+    mojo::PendingReceiver<safe_browsing::mojom::SafeBrowsing> receiver) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  content::RenderProcessHost* render_process_host =
+      content::RenderProcessHost::FromID(process_id);
+  if (!render_process_host)
+    return;
+
+  bool safe_browsing_enabled =
+      Profile::FromBrowserContext(render_process_host->GetBrowserContext())
+          ->GetPrefs()
+          ->GetBoolean(prefs::kSafeBrowsingEnabled);
+  base::CreateSingleThreadTaskRunner({content::BrowserThread::IO})
+      ->PostTask(
+          FROM_HERE,
+          base::BindOnce(
+              &safe_browsing::MojoSafeBrowsingImpl::MaybeCreate, process_id,
+              resource_context,
+              base::BindRepeating(get_checker_delegate, safe_browsing_enabled),
+              std::move(receiver)));
+}
+
 }  // namespace
 
 void ChromeContentBrowserClient::ExposeInterfacesToRenderer(
@@ -129,13 +160,13 @@
     content::ResourceContext* resource_context =
         render_process_host->GetBrowserContext()->GetResourceContext();
     registry->AddInterface(
-        base::Bind(
-            &safe_browsing::MojoSafeBrowsingImpl::MaybeCreate,
-            render_process_host->GetID(), resource_context,
-            base::Bind(
+        base::BindRepeating(
+            &MaybeCreateSafeBrowsingForRenderer, render_process_host->GetID(),
+            resource_context,
+            base::BindRepeating(
                 &ChromeContentBrowserClient::GetSafeBrowsingUrlCheckerDelegate,
-                base::Unretained(this), resource_context)),
-        base::CreateSingleThreadTaskRunner({content::BrowserThread::IO}));
+                base::Unretained(this))),
+        ui_task_runner);
   }
 #endif
 
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 4950abb..cbf6bbf 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -213,8 +213,8 @@
     "//components/rappor",
     "//components/renderer_context_menu",
     "//components/rlz",
-    "//components/safe_browsing:csd_proto",
-    "//components/safe_browsing/db:metadata_proto",
+    "//components/safe_browsing/core:csd_proto",
+    "//components/safe_browsing/core/db:metadata_proto",
     "//components/session_manager/core",
     "//components/signin/public/identity_manager",
     "//components/signin/public/webdata",
diff --git a/chrome/browser/chromeos/lock_screen_apps/lock_screen_profile_creator_impl.cc b/chrome/browser/chromeos/lock_screen_apps/lock_screen_profile_creator_impl.cc
index edff84a..89e499b 100644
--- a/chrome/browser/chromeos/lock_screen_apps/lock_screen_profile_creator_impl.cc
+++ b/chrome/browser/chromeos/lock_screen_apps/lock_screen_profile_creator_impl.cc
@@ -18,7 +18,7 @@
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "extensions/browser/extension_system.h"
 
 namespace lock_screen_apps {
diff --git a/chrome/browser/chromeos/lock_screen_apps/lock_screen_profile_creator_impl_unittest.cc b/chrome/browser/chromeos/lock_screen_apps/lock_screen_profile_creator_impl_unittest.cc
index be57aba2..0e6971af4a 100644
--- a/chrome/browser/chromeos/lock_screen_apps/lock_screen_profile_creator_impl_unittest.cc
+++ b/chrome/browser/chromeos/lock_screen_apps/lock_screen_profile_creator_impl_unittest.cc
@@ -33,7 +33,7 @@
 #include "components/arc/session/arc_session.h"
 #include "components/crx_file/id_util.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "content/public/test/browser_task_environment.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_builder.h"
diff --git a/chrome/browser/download/download_browsertest.cc b/chrome/browser/download/download_browsertest.cc
index 5dce9e7e9..3778714 100644
--- a/chrome/browser/download/download_browsertest.cc
+++ b/chrome/browser/download/download_browsertest.cc
@@ -97,9 +97,9 @@
 #include "components/infobars/core/infobar.h"
 #include "components/prefs/pref_service.h"
 #include "components/safe_browsing/buildflags.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/proto/csd.pb.h"
-#include "components/safe_browsing/safe_browsing_service_interface.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
+#include "components/safe_browsing/core/safe_browsing_service_interface.h"
 #include "components/security_state/core/features.h"
 #include "components/security_state/core/security_state.h"
 #include "components/services/quarantine/test_support.h"
diff --git a/chrome/browser/download/download_danger_prompt.h b/chrome/browser/download/download_danger_prompt.h
index 5adc920..d457d97 100644
--- a/chrome/browser/download/download_danger_prompt.h
+++ b/chrome/browser/download/download_danger_prompt.h
@@ -6,7 +6,7 @@
 #define CHROME_BROWSER_DOWNLOAD_DOWNLOAD_DANGER_PROMPT_H_
 
 #include "base/callback_forward.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 
 namespace content {
 class WebContents;
diff --git a/chrome/browser/download/download_danger_prompt_browsertest.cc b/chrome/browser/download/download_danger_prompt_browsertest.cc
index d3895af..2f30761 100644
--- a/chrome/browser/download/download_danger_prompt_browsertest.cc
+++ b/chrome/browser/download/download_danger_prompt_browsertest.cc
@@ -19,8 +19,8 @@
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/download/public/common/mock_download_item.h"
-#include "components/safe_browsing/db/database_manager.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/db/database_manager.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "content/public/browser/download_item_utils.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index cc5da64..a617df4 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -764,9 +764,9 @@
     "//chrome/browser/extensions/api:api_registration",
     "//chrome/common",
     "//chrome/common/extensions/api",
-    "//components/safe_browsing:csd_proto",
-    "//components/safe_browsing:webprotect_proto",
-    "//components/safe_browsing/db:util",
+    "//components/safe_browsing/core:csd_proto",
+    "//components/safe_browsing/core:webprotect_proto",
+    "//components/safe_browsing/core/db:util",
     "//components/signin/core/browser",
     "//content/public/browser",
     "//mojo/public/cpp/bindings",
@@ -836,11 +836,11 @@
     "//components/rappor",
     "//components/resources",
     "//components/safe_browsing:buildflags",
-    "//components/safe_browsing:csd_proto",
-    "//components/safe_browsing:features",
-    "//components/safe_browsing/common:safe_browsing_prefs",
-    "//components/safe_browsing/db:database_manager",
-    "//components/safe_browsing/web_ui:web_ui",
+    "//components/safe_browsing/content/web_ui:web_ui",
+    "//components/safe_browsing/core:csd_proto",
+    "//components/safe_browsing/core:features",
+    "//components/safe_browsing/core/common:safe_browsing_prefs",
+    "//components/safe_browsing/core/db:database_manager",
     "//components/search_engines",
     "//components/services/patch/content",
     "//components/services/unzip/content",
@@ -920,6 +920,10 @@
     "//url",
   ]
 
+  if (enable_autofill_assistant_api) {
+    deps += [ "//components/autofill_assistant/browser" ]
+  }
+
   if (is_chromeos) {
     sources += [
       "api/certificate_provider/certificate_provider_api.cc",
diff --git a/chrome/browser/extensions/api/preference/preference_api.cc b/chrome/browser/extensions/api/preference/preference_api.cc
index c7a9349..8cb5261 100644
--- a/chrome/browser/extensions/api/preference/preference_api.cc
+++ b/chrome/browser/extensions/api/preference/preference_api.cc
@@ -30,7 +30,7 @@
 #include "components/password_manager/core/common/password_manager_pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "components/proxy_config/proxy_config_pref_names.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/spellcheck/browser/pref_names.h"
 #include "components/translate/core/browser/translate_pref_names.h"
 #include "extensions/browser/extension_pref_value_map.h"
diff --git a/chrome/browser/extensions/api/preference/preference_apitest.cc b/chrome/browser/extensions/api/preference/preference_apitest.cc
index a347e5d..7d60e626 100644
--- a/chrome/browser/extensions/api/preference/preference_apitest.cc
+++ b/chrome/browser/extensions/api/preference/preference_apitest.cc
@@ -26,7 +26,7 @@
 #include "components/keep_alive_registry/scoped_keep_alive.h"
 #include "components/password_manager/core/common/password_manager_pref_names.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/translate/core/browser/translate_pref_names.h"
 #include "content/public/browser/notification_service.h"
 #include "extensions/browser/extension_registry.h"
diff --git a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc
index 172b282..4bcae9a 100644
--- a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc
+++ b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc
@@ -17,7 +17,7 @@
 #include "base/time/time.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_content_browser_client.h"
-#include "components/safe_browsing/web_ui/safe_browsing_ui.h"
+#include "components/safe_browsing/content/web_ui/safe_browsing_ui.h"
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/browser_process_platform_part_chromeos.h"
 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
@@ -44,8 +44,8 @@
 #include "components/policy/core/common/cloud/machine_level_user_cloud_policy_manager.h"
 #include "components/policy/core/common/cloud/realtime_reporting_job_configuration.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/proto/webprotect.pb.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/proto/webprotect.pb.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #if defined(OS_CHROMEOS)
 #include "components/user_manager/user.h"
diff --git a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_unittest.cc b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_unittest.cc
index 714f6e2..99f5483 100644
--- a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_unittest.cc
+++ b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_unittest.cc
@@ -21,7 +21,7 @@
 #include "chrome/test/base/testing_profile_manager.h"
 #include "components/policy/core/common/cloud/mock_cloud_policy_client.h"
 #include "components/policy/core/common/cloud/realtime_reporting_job_configuration.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "content/public/test/browser_task_environment.h"
 #include "extensions/browser/test_event_router.h"
 #include "testing/gmock/include/gmock/gmock.h"
diff --git a/chrome/browser/extensions/api/settings_private/prefs_util.cc b/chrome/browser/extensions/api/settings_private/prefs_util.cc
index 6f0fe968..ed66802 100644
--- a/chrome/browser/extensions/api/settings_private/prefs_util.cc
+++ b/chrome/browser/extensions/api/settings_private/prefs_util.cc
@@ -28,7 +28,7 @@
 #include "components/payments/core/payment_prefs.h"
 #include "components/prefs/pref_service.h"
 #include "components/proxy_config/proxy_config_pref_names.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/search_engines/default_search_manager.h"
 #include "components/spellcheck/browser/pref_names.h"
 #include "components/translate/core/browser/translate_pref_names.h"
diff --git a/chrome/browser/extensions/blacklist.cc b/chrome/browser/extensions/blacklist.cc
index 45d386d..35a76ce 100644
--- a/chrome/browser/extensions/blacklist.cc
+++ b/chrome/browser/extensions/blacklist.cc
@@ -22,7 +22,7 @@
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "components/prefs/pref_service.h"
 #include "components/safe_browsing/buildflags.h"
-#include "components/safe_browsing/db/util.h"
+#include "components/safe_browsing/core/db/util.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "extensions/browser/extension_prefs.h"
diff --git a/chrome/browser/extensions/blacklist.h b/chrome/browser/extensions/blacklist.h
index 56da9a3..cbcb5d10 100644
--- a/chrome/browser/extensions/blacklist.h
+++ b/chrome/browser/extensions/blacklist.h
@@ -18,7 +18,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "components/keyed_service/core/keyed_service.h"
-#include "components/safe_browsing/db/database_manager.h"
+#include "components/safe_browsing/core/db/database_manager.h"
 #include "extensions/browser/blacklist_state.h"
 
 namespace content {
diff --git a/chrome/browser/extensions/blacklist_state_fetcher.cc b/chrome/browser/extensions/blacklist_state_fetcher.cc
index 54e99857..568a8f7 100644
--- a/chrome/browser/extensions/blacklist_state_fetcher.cc
+++ b/chrome/browser/extensions/blacklist_state_fetcher.cc
@@ -10,7 +10,7 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "chrome/common/safe_browsing/crx_info.pb.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
 #include "content/public/browser/browser_thread.h"
 #include "net/base/escape.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
diff --git a/chrome/browser/extensions/blacklist_state_fetcher.h b/chrome/browser/extensions/blacklist_state_fetcher.h
index 6816ad26..d21e2ba 100644
--- a/chrome/browser/extensions/blacklist_state_fetcher.h
+++ b/chrome/browser/extensions/blacklist_state_fetcher.h
@@ -13,7 +13,7 @@
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "components/safe_browsing/db/util.h"
+#include "components/safe_browsing/core/db/util.h"
 #include "extensions/browser/blacklist_state.h"
 
 namespace network {
diff --git a/chrome/browser/extensions/fake_safe_browsing_database_manager.cc b/chrome/browser/extensions/fake_safe_browsing_database_manager.cc
index 4cc4f84..03bcd6e 100644
--- a/chrome/browser/extensions/fake_safe_browsing_database_manager.cc
+++ b/chrome/browser/extensions/fake_safe_browsing_database_manager.cc
@@ -16,8 +16,8 @@
 #include "base/run_loop.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
-#include "components/safe_browsing/db/util.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/core/db/util.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
 
 namespace extensions {
 
diff --git a/chrome/browser/extensions/fake_safe_browsing_database_manager.h b/chrome/browser/extensions/fake_safe_browsing_database_manager.h
index f71c291..565c22b 100644
--- a/chrome/browser/extensions/fake_safe_browsing_database_manager.h
+++ b/chrome/browser/extensions/fake_safe_browsing_database_manager.h
@@ -8,7 +8,7 @@
 #include <set>
 #include <string>
 
-#include "components/safe_browsing/db/test_database_manager.h"
+#include "components/safe_browsing/core/db/test_database_manager.h"
 
 namespace extensions {
 
diff --git a/chrome/browser/extensions/test_blacklist_state_fetcher.cc b/chrome/browser/extensions/test_blacklist_state_fetcher.cc
index 13dae1d..0e0201f 100644
--- a/chrome/browser/extensions/test_blacklist_state_fetcher.cc
+++ b/chrome/browser/extensions/test_blacklist_state_fetcher.cc
@@ -6,7 +6,7 @@
 
 #include "base/stl_util.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "components/safe_browsing/db/v4_test_util.h"
+#include "components/safe_browsing/core/db/v4_test_util.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
diff --git a/chrome/browser/extensions/webstore_data_fetcher.cc b/chrome/browser/extensions/webstore_data_fetcher.cc
index 0596ac0..b3a71c3d 100644
--- a/chrome/browser/extensions/webstore_data_fetcher.cc
+++ b/chrome/browser/extensions/webstore_data_fetcher.cc
@@ -10,7 +10,7 @@
 #include "base/metrics/field_trial_params.h"
 #include "base/values.h"
 #include "chrome/browser/extensions/webstore_data_fetcher_delegate.h"
-#include "components/safe_browsing/features.h"
+#include "components/safe_browsing/core/features.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/storage_partition.h"
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 2bea577e..0861faa 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -2988,6 +2988,11 @@
      "expiry_milestone": 80
   },
   {
+    "name": "paint-preview-test",
+    "owners": [ "ckitagawa", "mahmoudi", "vollick" ],
+    "expiry_milestone": 83
+  },
+  {
     "name": "passive-event-listeners-due-to-fling",
     "owners": [ "sahel", "input-dev" ],
     "expiry_milestone": 76
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 1fb54cc7..613db21 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -4015,6 +4015,13 @@
     "installed from the Chrome Web Store.";
 #endif  // ENABLE_NACL
 
+#if BUILDFLAG(ENABLE_PAINT_PREVIEW) && defined(OS_ANDROID)
+const char kPaintPreviewTestName[] = "Paint Preview Test";
+const char kPaintPreviewTestDescription[] =
+    "If enabled a menu item is added to the Android main menu to test paint "
+    "previews.";
+#endif  // ENABLE_PAINT_PREVIEW && defined(OS_ANDROID)
+
 #if BUILDFLAG(ENABLE_PLUGINS)
 
 #if defined(OS_CHROMEOS)
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 416c70c..164fa82b 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -11,6 +11,7 @@
 #include "build/buildflag.h"
 #include "chrome/common/buildflags.h"
 #include "components/nacl/common/buildflags.h"
+#include "components/paint_preview/buildflags/buildflags.h"
 #include "components/spellcheck/spellcheck_buildflags.h"
 #include "device/vr/buildflags/buildflags.h"
 #include "media/media_buildflags.h"
@@ -2415,6 +2416,11 @@
 extern const char kNaclDescription[];
 #endif  // ENABLE_NACL
 
+#if BUILDFLAG(ENABLE_PAINT_PREVIEW) && defined(OS_ANDROID)
+extern const char kPaintPreviewTestName[];
+extern const char kPaintPreviewTestDescription[];
+#endif  // ENABLE_PAINT_PREVIEW && defined(OS_ANDROID)
+
 #if BUILDFLAG(ENABLE_PLUGINS)
 
 #if defined(OS_CHROMEOS)
diff --git a/chrome/browser/interstitials/enterprise_util.h b/chrome/browser/interstitials/enterprise_util.h
index 69aedfd2..731f697 100644
--- a/chrome/browser/interstitials/enterprise_util.h
+++ b/chrome/browser/interstitials/enterprise_util.h
@@ -5,7 +5,7 @@
 #ifndef CHROME_BROWSER_INTERSTITIALS_ENTERPRISE_UTIL_H_
 #define CHROME_BROWSER_INTERSTITIALS_ENTERPRISE_UTIL_H_
 
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
 
 namespace content {
 class WebContents;
diff --git a/chrome/browser/media/webrtc/webrtc_event_log_manager.cc b/chrome/browser/media/webrtc/webrtc_event_log_manager.cc
index cf63098..1245ca8 100644
--- a/chrome/browser/media/webrtc/webrtc_event_log_manager.cc
+++ b/chrome/browser/media/webrtc/webrtc_event_log_manager.cc
@@ -11,11 +11,9 @@
 #include "base/task/post_task.h"
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/policy/profile_policy_connector.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
-#include "components/policy/core/common/policy_service.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_task_traits.h"
@@ -23,14 +21,6 @@
 #include "content/public/browser/network_service_instance.h"
 #include "content/public/browser/render_process_host.h"
 
-#if defined OS_CHROMEOS
-#include "chrome/browser/chromeos/profiles/profile_helper.h"
-#endif
-
-#if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
-#include "chrome/browser/policy/chrome_browser_policy_connector.h"
-#endif
-
 namespace webrtc_event_logging {
 
 namespace {
@@ -104,40 +94,6 @@
   return enabled;
 }
 
-// Checks whether the Profile is considered managed. Used to
-// determine the default value for the policy controlling event logging.
-bool IsBrowserManagedForProfile(const Profile* profile) {
-// For Chrome OS, exclude the signin profile and ephemeral profiles.
-#if defined(OS_CHROMEOS)
-  if (chromeos::ProfileHelper::IsSigninProfile(profile) ||
-      chromeos::ProfileHelper::IsEphemeralUserProfile(profile)) {
-    return false;
-  }
-#endif
-
-  // Child accounts should not have a logging default of true so
-  // we do not consider them as being managed here.
-  if (profile->IsChild()) {
-    return false;
-  }
-
-  if (profile->GetProfilePolicyConnector()
-          ->policy_service()
-          ->IsInitializationComplete(policy::POLICY_DOMAIN_CHROME) &&
-      profile->GetProfilePolicyConnector()->IsManaged()) {
-    return true;
-  }
-
-  // For desktop, machine level policies (Windows, Linux, Mac OS) can affect
-  // user profiles, so we consider these profiles managed.
-#if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
-  return g_browser_process->browser_policy_connector()
-      ->HasMachineLevelPolicies();
-#else
-  return false;
-#endif
-}
-
 BrowserContext* GetBrowserContext(int render_process_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   RenderProcessHost* const host = RenderProcessHost::FromID(render_process_id);
@@ -561,15 +517,9 @@
   if (webrtc_event_log_collection_allowed_pref->IsDefaultValue()) {
     // The pref has not been set. GetBoolean would only return the default
     // value. However, there is no single default value,
-    // because it depends on whether Chrome is managed,
-    // so we check whether Chrome is managed.
-    // TODO(https://crbug.com/980132): use generalized policy default
-    // mechanism when it is available.
-    const bool managed = IsBrowserManagedForProfile(profile);
-    constexpr bool kCollectionAllowedDefaultManaged = true;
-    constexpr bool kCollectionAllowedDefaultUnManaged = false;
-    return managed ? kCollectionAllowedDefaultManaged
-                   : kCollectionAllowedDefaultUnManaged;
+    // because it depends on whether the profile receives cloud-based
+    // enterprise policies.
+    return DoesProfileDefaultToLoggingEnabled(profile);
   }
 
   // There is a non-default value set, so this value is authoritative.
diff --git a/chrome/browser/media/webrtc/webrtc_event_log_manager_common.cc b/chrome/browser/media/webrtc/webrtc_event_log_manager_common.cc
index 59ad7a2..12e0764 100644
--- a/chrome/browser/media/webrtc/webrtc_event_log_manager_common.cc
+++ b/chrome/browser/media/webrtc/webrtc_event_log_manager_common.cc
@@ -17,11 +17,18 @@
 #include "base/strings/stringprintf.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/unguessable_token.h"
+#include "chrome/browser/policy/profile_policy_connector.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/policy/core/common/policy_service.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_process_host.h"
 #include "third_party/zlib/zlib.h"
 
+#if defined OS_CHROMEOS
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#endif
+
 namespace webrtc_event_logging {
 
 using BrowserContextId = WebRtcEventLogPeerConnectionKey::BrowserContextId;
@@ -1010,4 +1017,41 @@
   return ExtractWebAppId(id_str);
 }
 
+bool DoesProfileDefaultToLoggingEnabled(const Profile* const profile) {
+// For Chrome OS, exclude special profiles and users.
+#if defined(OS_CHROMEOS)
+  const user_manager::User* user =
+      chromeos::ProfileHelper::Get()->GetUserByProfile(profile);
+  // We do not log an error here since this can happen in several cases,
+  // e.g. for signin profiles or lock screen app profiles.
+  if (!user) {
+    return false;
+  }
+  const user_manager::UserType user_type = user->GetType();
+  if (user_type != user_manager::USER_TYPE_REGULAR) {
+    return false;
+  }
+  if (chromeos::ProfileHelper::IsEphemeralUserProfile(profile)) {
+    return false;
+  }
+#endif
+
+  // We only want a default of true for regular (i.e. logged-in) profiles
+  // receiving cloud-based user-level enterprise policies. Supervised (child)
+  // profiles are considered regular and can also receive cloud policies in some
+  // cases (e.g. on Chrome OS). Although currently this should be covered by the
+  // other checks, let's explicitly check to anticipate edge cases and make the
+  // requirement explicit.
+  if (!profile->IsRegularProfile() || profile->IsSupervised()) {
+    return false;
+  }
+
+  const policy::ProfilePolicyConnector* const policy_connector =
+      profile->GetProfilePolicyConnector();
+
+  return policy_connector->policy_service()->IsInitializationComplete(
+             policy::POLICY_DOMAIN_CHROME) &&
+         policy_connector->IsManaged();
+}
+
 }  // namespace webrtc_event_logging
diff --git a/chrome/browser/media/webrtc/webrtc_event_log_manager_common.h b/chrome/browser/media/webrtc/webrtc_event_log_manager_common.h
index c6479729..7dd0747 100644
--- a/chrome/browser/media/webrtc/webrtc_event_log_manager_common.h
+++ b/chrome/browser/media/webrtc/webrtc_event_log_manager_common.h
@@ -13,6 +13,8 @@
 #include "base/time/time.h"
 #include "build/build_config.h"
 
+class Profile;
+
 namespace content {
 class BrowserContext;
 }  // namespace content
@@ -538,6 +540,9 @@
 size_t ExtractRemoteBoundWebRtcEventLogWebAppIdFromPath(
     const base::FilePath& path);
 
+// Used to determine the default value for the policy controlling event logging.
+bool DoesProfileDefaultToLoggingEnabled(const Profile* const profile);
+
 }  // namespace webrtc_event_logging
 
 #endif  // CHROME_BROWSER_MEDIA_WEBRTC_WEBRTC_EVENT_LOG_MANAGER_COMMON_H_
diff --git a/chrome/browser/media/webrtc/webrtc_event_log_manager_common_unittest.cc b/chrome/browser/media/webrtc/webrtc_event_log_manager_common_unittest.cc
index 400d439..23afb746 100644
--- a/chrome/browser/media/webrtc/webrtc_event_log_manager_common_unittest.cc
+++ b/chrome/browser/media/webrtc/webrtc_event_log_manager_common_unittest.cc
@@ -19,6 +19,16 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/zlib/google/compression_utils.h"
 
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/account_id/account_id.h"
+#include "components/user_manager/scoped_user_manager.h"
+#include "content/public/test/browser_task_environment.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#endif
+
 namespace webrtc_event_logging {
 
 namespace {
@@ -652,4 +662,90 @@
   EXPECT_FALSE(base::PathExists(path_));  // Errored files deleted by Close().
 }
 
+#if defined(OS_CHROMEOS)
+
+struct DoesProfileDefaultToLoggingEnabledForUserTypeTestCase {
+  user_manager::UserType user_type;
+  bool defaults_to_logging_enabled;
+};
+
+class DoesProfileDefaultToLoggingEnabledForUserTypeParametrizedTest
+    : public ::testing::TestWithParam<
+          DoesProfileDefaultToLoggingEnabledForUserTypeTestCase> {
+ protected:
+  content::BrowserTaskEnvironment task_environment_;
+};
+
+TEST_P(DoesProfileDefaultToLoggingEnabledForUserTypeParametrizedTest,
+       WebRtcPolicyDefaultTest) {
+  DoesProfileDefaultToLoggingEnabledForUserTypeTestCase test_case = GetParam();
+
+  TestingProfile::Builder profile_builder;
+  profile_builder.OverridePolicyConnectorIsManagedForTesting(true);
+  std::unique_ptr<TestingProfile> testing_profile = profile_builder.Build();
+  std::unique_ptr<testing::NiceMock<chromeos::FakeChromeUserManager>>
+      fake_user_manager_ = std::make_unique<
+          testing::NiceMock<chromeos::FakeChromeUserManager>>();
+  // We use a standard Gaia account by default:
+  AccountId account_id = AccountId::FromUserEmailGaiaId("name", "id");
+
+  switch (test_case.user_type) {
+    case user_manager::USER_TYPE_REGULAR:
+      fake_user_manager_->AddUserWithAffiliationAndTypeAndProfile(
+          account_id, false, test_case.user_type, testing_profile.get());
+      break;
+    case user_manager::USER_TYPE_GUEST:
+      account_id = fake_user_manager_->GetGuestAccountId();
+      fake_user_manager_->AddGuestUser();
+      break;
+    case user_manager::USER_TYPE_PUBLIC_ACCOUNT:
+      fake_user_manager_->AddPublicAccountUser(account_id);
+      break;
+    case user_manager::USER_TYPE_SUPERVISED:
+      fake_user_manager_->AddSupervisedUser(account_id);
+      break;
+    case user_manager::USER_TYPE_KIOSK_APP:
+      fake_user_manager_->AddKioskAppUser(account_id);
+      break;
+    case user_manager::USER_TYPE_CHILD:
+      fake_user_manager_->AddChildUser(account_id);
+      break;
+    case user_manager::USER_TYPE_ARC_KIOSK_APP:
+      fake_user_manager_->AddArcKioskAppUser(account_id);
+      break;
+    case user_manager::USER_TYPE_ACTIVE_DIRECTORY:
+      account_id = AccountId::AdFromObjGuid("guid");
+      fake_user_manager_->AddUserWithAffiliationAndTypeAndProfile(
+          account_id, false, test_case.user_type, testing_profile.get());
+      break;
+    default:
+      FAIL() << "Invalid test setup. Unexpected user type.";
+      break;
+  }
+
+  fake_user_manager_->LoginUser(account_id);
+  std::unique_ptr<user_manager::ScopedUserManager> scoped_user_manager_ =
+      std::make_unique<user_manager::ScopedUserManager>(
+          std::move(fake_user_manager_));
+
+  EXPECT_EQ(DoesProfileDefaultToLoggingEnabled(testing_profile.get()),
+            test_case.defaults_to_logging_enabled);
+}
+
+INSTANTIATE_TEST_CASE_P(
+    WebRtcPolicyDefaultTests,
+    DoesProfileDefaultToLoggingEnabledForUserTypeParametrizedTest,
+    testing::ValuesIn(
+        std::vector<DoesProfileDefaultToLoggingEnabledForUserTypeTestCase>{
+            {user_manager::USER_TYPE_REGULAR, true},
+            {user_manager::USER_TYPE_GUEST, false},
+            {user_manager::USER_TYPE_PUBLIC_ACCOUNT, false},
+            {user_manager::USER_TYPE_SUPERVISED, false},
+            {user_manager::USER_TYPE_KIOSK_APP, false},
+            {user_manager::USER_TYPE_CHILD, false},
+            {user_manager::USER_TYPE_ARC_KIOSK_APP, false},
+            {user_manager::USER_TYPE_ACTIVE_DIRECTORY, false}}));
+
+#endif  // defined(OS_CHROMEOS)
+
 }  // namespace webrtc_event_logging
diff --git a/chrome/browser/media/webrtc/webrtc_event_log_manager_unittest.cc b/chrome/browser/media/webrtc/webrtc_event_log_manager_unittest.cc
index b46470fa..87acd7e 100644
--- a/chrome/browser/media/webrtc/webrtc_event_log_manager_unittest.cc
+++ b/chrome/browser/media/webrtc/webrtc_event_log_manager_unittest.cc
@@ -65,6 +65,14 @@
 #include "components/policy/core/common/policy_types.h"
 #endif
 
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/account_id/account_id.h"
+#include "components/user_manager/scoped_user_manager.h"
+#endif
+
 namespace webrtc_event_logging {
 
 #if defined(OS_WIN)
@@ -612,6 +620,17 @@
       bool is_managed_profile,
       bool has_device_level_policies,
       base::Optional<bool> policy_allows_remote_logging) {
+    return CreateBrowserContextWithCustomSupervision(
+        profile_name, is_managed_profile, has_device_level_policies,
+        false /* is_supervised */, policy_allows_remote_logging);
+  }
+  virtual std::unique_ptr<TestingProfile>
+  CreateBrowserContextWithCustomSupervision(
+      std::string profile_name,
+      bool is_managed_profile,
+      bool has_device_level_policies,
+      bool is_supervised,
+      base::Optional<bool> policy_allows_remote_logging) {
     // If profile name not specified, select a unique name.
     if (profile_name.empty()) {
       static size_t index = 0;
@@ -655,9 +674,8 @@
     provider_.UpdateChromePolicy(policy_map);
 #else
     if (has_device_level_policies) {
-      // This should never happen.
-      // Device level policies cannot be set on Chrome OS and Android.
-      EXPECT_TRUE(false);
+      ADD_FAILURE() << "Invalid test setup. Chrome platform policies cannot be "
+                       "set on Chrome OS and Android.";
     }
 #endif
 
@@ -668,6 +686,9 @@
     profile_builder.SetPrefService(base::WrapUnique(regular_prefs));
     profile_builder.OverridePolicyConnectorIsManagedForTesting(
         is_managed_profile);
+    if (is_supervised) {
+      profile_builder.SetSupervisedUserId("id");
+    }
     std::unique_ptr<TestingProfile> profile = profile_builder.Build();
 
     // Blocks on the unit under test's task runner, so that we won't proceed
@@ -1030,6 +1051,11 @@
     WebRtcEventLogManagerTestBase::SetUp();
   }
 
+#if defined(OS_CHROMEOS)
+  std::unique_ptr<user_manager::ScopedUserManager> GetScopedUserManager(
+      user_manager::UserType user_type);
+#endif
+
   void TestManagedProfileAfterBeingExplicitlySet(bool explicitly_set_value);
 };
 
@@ -3958,11 +3984,37 @@
   EXPECT_EQ(StartRemoteLogging(key), allow_remote_logging);
 }
 
+#if defined(OS_CHROMEOS)
+std::unique_ptr<user_manager::ScopedUserManager>
+WebRtcEventLogManagerTestPolicy::GetScopedUserManager(
+    user_manager::UserType user_type) {
+  const AccountId kAccountId = AccountId::FromUserEmailGaiaId("name", "id");
+  auto mock_user_manager =
+      std::make_unique<testing::NiceMock<chromeos::FakeChromeUserManager>>();
+  // On Chrome OS, there are different user types, some of which can be
+  // affiliated with the device if the device is enterprise-enrolled, i.e. the
+  // logged in account belongs to the org that owns the device. For our
+  // purposes here, affiliation does not matter for the determination of the
+  // policy default, so we can set it to false here. We do not need a user
+  // to profile mapping either, so profile can be a nullptr.
+  mock_user_manager->AddUserWithAffiliationAndTypeAndProfile(
+      kAccountId, /*is_affiliated*/ false, user_type, /*profile*/ nullptr);
+  return std::make_unique<user_manager::ScopedUserManager>(
+      std::move(mock_user_manager));
+}
+#endif
+
 TEST_F(WebRtcEventLogManagerTestPolicy,
        ManagedProfileAllowsRemoteLoggingByDefault) {
   SetUp(true);  // Feature generally enabled (kill-switch not engaged).
 
   const bool allow_remote_logging = true;
+
+#if defined(OS_CHROMEOS)
+  std::unique_ptr<user_manager::ScopedUserManager> scoped_user_manager =
+      GetScopedUserManager(user_manager::USER_TYPE_REGULAR);
+#endif
+
   auto browser_context = CreateBrowserContext(
       "name", true /* is_managed_profile */,
       false /* has_device_level_policies */, base::nullopt);
@@ -3975,12 +4027,44 @@
   EXPECT_EQ(StartRemoteLogging(key), allow_remote_logging);
 }
 
-#if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
+// Currently we only test the case of supervised child profiles for Chrome OS
+// here. Other user types for Chrome OS are tested in the unit test for
+// ProfileDefaultsToLoggingEnabledTestCase in
+// webrtc_event_log_manager_common_unittest because the test setup in this
+// class currently does not seem to allow for an easy setup of some user types.
+// TODO(crbug.com/1035829): Figure out whether this can be resolved by tweaking
+// the test setup or whether the Active Directory services need to be adapted
+// for easy testing.
 TEST_F(WebRtcEventLogManagerTestPolicy,
-       ManagedByPlatformPoliciesAllowsRemoteLoggingByDefault) {
+       ManagedProfileDoesNotAllowRemoteLoggingForSupervisedProfiles) {
   SetUp(true);  // Feature generally enabled (kill-switch not engaged).
 
-  const bool allow_remote_logging = true;
+  const bool allow_remote_logging = false;
+
+#if defined(OS_CHROMEOS)
+  std::unique_ptr<user_manager::ScopedUserManager> scoped_user_manager =
+      GetScopedUserManager(user_manager::USER_TYPE_CHILD);
+#endif
+
+  auto browser_context = CreateBrowserContextWithCustomSupervision(
+      "name", true /* is_managed_profile */,
+      false /* has_device_level_policies */, true /* is_supervised */,
+      base::nullopt);
+
+  auto rph = std::make_unique<MockRenderProcessHost>(browser_context.get());
+  const auto key = GetPeerConnectionKey(rph.get(), kLid);
+
+  ASSERT_TRUE(PeerConnectionAdded(key));
+  ASSERT_TRUE(PeerConnectionSessionIdSet(key));
+  EXPECT_EQ(StartRemoteLogging(key), allow_remote_logging);
+}
+
+#if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
+TEST_F(WebRtcEventLogManagerTestPolicy,
+       OnlyManagedByPlatformPoliciesDoesNotAllowRemoteLoggingByDefault) {
+  SetUp(true);  // Feature generally enabled (kill-switch not engaged).
+
+  const bool allow_remote_logging = false;
   auto browser_context =
       CreateBrowserContext("name", false /* is_managed_profile */,
                            true /* has_device_level_policies */, base::nullopt);
diff --git a/chrome/browser/navigation_predictor/navigation_predictor_keyed_service.cc b/chrome/browser/navigation_predictor/navigation_predictor_keyed_service.cc
index 61ef5570..b624def 100644
--- a/chrome/browser/navigation_predictor/navigation_predictor_keyed_service.cc
+++ b/chrome/browser/navigation_predictor/navigation_predictor_keyed_service.cc
@@ -38,9 +38,13 @@
 }
 
 NavigationPredictorKeyedService::NavigationPredictorKeyedService(
-    content::BrowserContext* browser_context) {
+    content::BrowserContext* browser_context)
+    : search_engine_preconnector_(browser_context) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   DCHECK(!browser_context->IsOffTheRecord());
+
+  // Start preconnecting to the search engine.
+  search_engine_preconnector_.StartPreconnecting(/*with_startup_delay=*/true);
 }
 
 NavigationPredictorKeyedService::~NavigationPredictorKeyedService() {
@@ -71,3 +75,8 @@
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   observer_list_.RemoveObserver(observer);
 }
+
+SearchEnginePreconnector*
+NavigationPredictorKeyedService::SearchEnginePreconnectorForTesting() {
+  return &search_engine_preconnector_;
+}
diff --git a/chrome/browser/navigation_predictor/navigation_predictor_keyed_service.h b/chrome/browser/navigation_predictor/navigation_predictor_keyed_service.h
index 538b381a..9de8c5e 100644
--- a/chrome/browser/navigation_predictor/navigation_predictor_keyed_service.h
+++ b/chrome/browser/navigation_predictor/navigation_predictor_keyed_service.h
@@ -11,6 +11,7 @@
 #include "base/observer_list.h"
 #include "base/optional.h"
 #include "base/single_thread_task_runner.h"
+#include "chrome/browser/navigation_predictor/search_engine_preconnector.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "url/gurl.h"
 
@@ -68,6 +69,8 @@
       content::BrowserContext* browser_context);
   ~NavigationPredictorKeyedService() override;
 
+  SearchEnginePreconnector* SearchEnginePreconnectorForTesting();
+
   // |document_url| may be invalid. Called by navigation predictor.
   void OnPredictionUpdated(const content::RenderFrameHost* render_frame_host,
                            const GURL& document_url,
@@ -89,6 +92,9 @@
   // Last known prediction.
   base::Optional<Prediction> last_prediction_;
 
+  // Manages preconnecting to the user's default search engine.
+  SearchEnginePreconnector search_engine_preconnector_;
+
   DISALLOW_COPY_AND_ASSIGN(NavigationPredictorKeyedService);
 };
 
diff --git a/chrome/browser/navigation_predictor/navigation_predictor_preconnect_client.cc b/chrome/browser/navigation_predictor/navigation_predictor_preconnect_client.cc
index 0143534..a5af6fca 100644
--- a/chrome/browser/navigation_predictor/navigation_predictor_preconnect_client.cc
+++ b/chrome/browser/navigation_predictor/navigation_predictor_preconnect_client.cc
@@ -11,6 +11,7 @@
 #include "base/metrics/field_trial_params.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
+#include "chrome/browser/navigation_predictor/search_engine_preconnector.h"
 #include "chrome/browser/predictors/loading_predictor.h"
 #include "chrome/browser/predictors/loading_predictor_factory.h"
 #include "chrome/browser/profiles/profile.h"
@@ -123,7 +124,9 @@
   // On search engine results page, next navigation is likely to be a different
   // origin. Currently, the preconnect is only allowed for same origins. Hence,
   // preconnect is currently disabled on search engine results page.
-  if (IsSearchEnginePage())
+  // If preconnect to DSE is enabled, skip this check.
+  if (!base::FeatureList::IsEnabled(features::kPreconnectToSearch) &&
+      IsSearchEnginePage())
     return;
 
   url::Origin preconnect_origin =
diff --git a/chrome/browser/navigation_predictor/navigation_predictor_preconnect_client_browsertest.cc b/chrome/browser/navigation_predictor/navigation_predictor_preconnect_client_browsertest.cc
index f2719f4..eabd8ff 100644
--- a/chrome/browser/navigation_predictor/navigation_predictor_preconnect_client_browsertest.cc
+++ b/chrome/browser/navigation_predictor/navigation_predictor_preconnect_client_browsertest.cc
@@ -265,4 +265,54 @@
   EXPECT_EQ(3, preresolve_done_count_);
 }
 
+namespace {
+// Feature to control preconnect to search.
+const base::Feature kPreconnectToSearchTest{"PreconnectToSearch",
+                                            base::FEATURE_DISABLED_BY_DEFAULT};
+}  // namespace
+
+class NavigationPredictorPreconnectClientBrowserTestWithSearch
+    : public NavigationPredictorPreconnectClientBrowserTest {
+ public:
+  NavigationPredictorPreconnectClientBrowserTestWithSearch()
+      : NavigationPredictorPreconnectClientBrowserTest() {
+    feature_list_.InitAndEnableFeature(kPreconnectToSearchTest);
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(NavigationPredictorPreconnectClientBrowserTestWithSearch,
+                       PreconnectSearchWithFeature) {
+  static const char kShortName[] = "test";
+  static const char kSearchURL[] =
+      "/anchors_different_area.html?q={searchTerms}";
+  TemplateURLService* model =
+      TemplateURLServiceFactory::GetForProfile(browser()->profile());
+  ASSERT_TRUE(model);
+  search_test_utils::WaitForTemplateURLServiceToLoad(model);
+  ASSERT_TRUE(model->loaded());
+
+  TemplateURLData data;
+  data.SetShortName(base::ASCIIToUTF16(kShortName));
+  data.SetKeyword(data.short_name());
+  data.SetURL(GetTestURL(kSearchURL).spec());
+
+  TemplateURL* template_url = model->Add(std::make_unique<TemplateURL>(data));
+  ASSERT_TRUE(template_url);
+  model->SetUserSelectedDefaultSearchProvider(template_url);
+  const GURL& url = GetTestURL("/anchors_different_area.html?q=cats");
+
+  // There should be 2 DSE preconnects (2 NIKs).
+  WaitForPreresolveCount(2);
+  EXPECT_EQ(2, preresolve_done_count_);
+
+  ui_test_utils::NavigateToURL(browser(), url);
+  // Now there should be an onload preconnect as well as a navigation
+  // preconnect.
+  WaitForPreresolveCount(4);
+  EXPECT_EQ(4, preresolve_done_count_);
+}
+
 }  // namespace
diff --git a/chrome/browser/navigation_predictor/search_engine_preconnector.cc b/chrome/browser/navigation_predictor/search_engine_preconnector.cc
new file mode 100644
index 0000000..63edf82
--- /dev/null
+++ b/chrome/browser/navigation_predictor/search_engine_preconnector.cc
@@ -0,0 +1,159 @@
+// Copyright 2019 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/navigation_predictor/search_engine_preconnector.h"
+
+#include "base/bind.h"
+#include "base/metrics/field_trial_params.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "chrome/browser/predictors/loading_predictor.h"
+#include "chrome/browser/predictors/loading_predictor_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/search_engines/template_url_service_factory.h"
+#include "components/search_engines/template_url_service.h"
+#include "content/public/browser/browser_context.h"
+#include "net/base/features.h"
+
+namespace features {
+// Feature to control preconnect to search.
+const base::Feature kPreconnectToSearch{"PreconnectToSearch",
+                                        base::FEATURE_DISABLED_BY_DEFAULT};
+}  // namespace features
+
+SearchEnginePreconnector::SearchEnginePreconnector(
+    content::BrowserContext* browser_context)
+    :
+#if defined(OS_ANDROID)
+      application_status_listener_(
+          base::android::ApplicationStatusListener::New(base::BindRepeating(
+              &SearchEnginePreconnector::OnApplicationStateChange,
+              // It's safe to use base::Unretained here since the application
+              // state listener is owned by |this|. So, no callbacks can
+              // arrive after |this| has been destroyed.
+              base::Unretained(this)))),
+#endif  // defined(OS_ANDROID)
+      browser_context_(browser_context),
+      currently_in_foreground_(true) {
+  DCHECK(!browser_context_->IsOffTheRecord());
+
+#if defined(OS_ANDROID)
+  auto application_state = base::android::ApplicationStatusListener::GetState();
+
+  currently_in_foreground_ =
+      application_state ==
+          base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES ||
+      application_state ==
+          base::android::APPLICATION_STATE_HAS_PAUSED_ACTIVITIES;
+#endif  // defined(OS_ANDROID)
+}
+
+SearchEnginePreconnector::~SearchEnginePreconnector() = default;
+
+#if defined(OS_ANDROID)
+void SearchEnginePreconnector::OnApplicationStateChange(
+    base::android::ApplicationState application_state) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (!application_status_listener_)
+    return;
+
+  OnAppStateChanged(
+      application_state ==
+          base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES ||
+      application_state ==
+          base::android::APPLICATION_STATE_HAS_PAUSED_ACTIVITIES);
+}
+#endif  // defined(OS_ANDROID)
+
+void SearchEnginePreconnector::OnAppStateChangedForTesting(bool in_foreground) {
+  OnAppStateChanged(in_foreground);
+}
+
+void SearchEnginePreconnector::OnAppStateChanged(bool in_foreground) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (currently_in_foreground_ == in_foreground)
+    return;
+
+  currently_in_foreground_ = in_foreground;
+
+  if (!currently_in_foreground_) {
+    // Stop any future preconnects while in background.
+    timer_.Stop();
+    return;
+  }
+
+  StartPreconnecting(/*with_startup_delay=*/false);
+}
+
+void SearchEnginePreconnector::StartPreconnecting(bool with_startup_delay) {
+  if (!currently_in_foreground_)
+    return;
+
+  if (with_startup_delay) {
+    timer_.Start(
+        FROM_HERE,
+        base::TimeDelta::FromMilliseconds(
+            base::GetFieldTrialParamByFeatureAsInt(
+                features::kPreconnectToSearch, "startup_delay_ms", 1000)),
+        base::BindOnce(&SearchEnginePreconnector::PreconnectDSE,
+                       base::Unretained(this)));
+    return;
+  }
+
+  PreconnectDSE();
+}
+
+void SearchEnginePreconnector::PreconnectDSE() {
+  DCHECK(!browser_context_->IsOffTheRecord());
+  DCHECK(currently_in_foreground_);
+  DCHECK(!timer_.IsRunning());
+
+  if (!base::FeatureList::IsEnabled(features::kPreconnectToSearch))
+    return;
+
+  GURL preconnect_url = GetDefaultSearchEngineOriginURL();
+  if (preconnect_url.scheme() != url::kHttpScheme &&
+      preconnect_url.scheme() != url::kHttpsScheme) {
+    return;
+  }
+
+  auto* loading_predictor = predictors::LoadingPredictorFactory::GetForProfile(
+      Profile::FromBrowserContext(browser_context_));
+
+  loading_predictor->preconnect_manager()->StartPreconnectUrl(
+      preconnect_url, true /* allow_credentials */,
+      net::NetworkIsolationKey(url::Origin::Create(preconnect_url),
+                               url::Origin::Create(preconnect_url)));
+
+  loading_predictor->preconnect_manager()->StartPreconnectUrl(
+      preconnect_url, false /* allow_credentials */,
+      net::NetworkIsolationKey());
+
+  // The delay beyond the idle socket timeout that net uses when
+  // re-preconnecting. If negative, no retries occur.
+  constexpr base::TimeDelta kRelayDelay = base::TimeDelta::FromMilliseconds(50);
+
+  // Set/Reset the timer to fire after the preconnect times out. Add an extra
+  // delay to make sure the preconnect has expired if it wasn't used.
+  timer_.Start(
+      FROM_HERE,
+      base::TimeDelta::FromSeconds(base::GetFieldTrialParamByFeatureAsInt(
+          net::features::kNetUnusedIdleSocketTimeout,
+          "unused_idle_socket_timeout_seconds", 60)) +
+          kRelayDelay,
+      base::BindOnce(&SearchEnginePreconnector::PreconnectDSE,
+                     base::Unretained(this)));
+}
+
+GURL SearchEnginePreconnector::GetDefaultSearchEngineOriginURL() const {
+  auto* template_service = TemplateURLServiceFactory::GetForProfile(
+      Profile::FromBrowserContext(browser_context_));
+  if (!template_service)
+    return GURL();
+  return template_service->GetDefaultSearchProvider()
+      ->GenerateSearchURL({})
+      .GetOrigin();
+}
diff --git a/chrome/browser/navigation_predictor/search_engine_preconnector.h b/chrome/browser/navigation_predictor/search_engine_preconnector.h
new file mode 100644
index 0000000..fed5ecd
--- /dev/null
+++ b/chrome/browser/navigation_predictor/search_engine_preconnector.h
@@ -0,0 +1,79 @@
+// Copyright 2019 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_NAVIGATION_PREDICTOR_SEARCH_ENGINE_PRECONNECTOR_H_
+#define CHROME_BROWSER_NAVIGATION_PREDICTOR_SEARCH_ENGINE_PRECONNECTOR_H_
+
+#include "base/feature_list.h"
+#include "base/sequence_checker.h"
+#include "base/timer/timer.h"
+#include "build/build_config.h"
+#include "url/origin.h"
+
+#if defined(OS_ANDROID)
+#include "base/android/application_status_listener.h"
+#endif
+
+namespace content {
+class BrowserContext;
+}  // namespace content
+
+namespace features {
+extern const base::Feature kPreconnectToSearch;
+}  // namespace features
+
+// Class to preconnect to the user's default search engine at regular intervals.
+class SearchEnginePreconnector {
+ public:
+  explicit SearchEnginePreconnector(content::BrowserContext* browser_context);
+  ~SearchEnginePreconnector();
+
+  SearchEnginePreconnector(const SearchEnginePreconnector&) = delete;
+  SearchEnginePreconnector& operator=(const SearchEnginePreconnector&) = delete;
+
+  // Start the process of preconnecting to the default search engine.
+  // |with_startup_delay| adds a delay to the preconnect, and should be true
+  // only during app start up.
+  void StartPreconnecting(bool with_startup_delay);
+
+  void OnAppStateChangedForTesting(bool in_foreground);
+
+ private:
+  // Preconnects to the default search engine synchronously. Preconnects in
+  // credentialed and uncredentialed mode.
+  void PreconnectDSE();
+
+  // Queries template service for the current DSE URL.
+  GURL GetDefaultSearchEngineOriginURL() const;
+
+  // Called on Android when the app moves to or from the background/foreground.
+  // |in_foreground| is whether it is moving into the foreground or not.
+  void OnAppStateChanged(bool in_foreground);
+
+#if defined(OS_ANDROID)
+  // Called when application state changes. e.g., application is brought to the
+  // background or the foreground.
+  void OnApplicationStateChange(
+      base::android::ApplicationState application_state);
+
+  // Used to listen to the changes in the application state changes. e.g., when
+  // the application is brought to the background or the foreground.
+  std::unique_ptr<base::android::ApplicationStatusListener>
+      application_status_listener_;
+#endif  // defined(OS_ANDROID)
+
+  // Used to get keyed services.
+  content::BrowserContext* const browser_context_;
+
+  // Used to preconnect regularly.
+  base::OneShotTimer timer_;
+
+  // Always true on desktop, on Android only true when the app is the foreground
+  // app.
+  bool currently_in_foreground_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+};
+
+#endif  // CHROME_BROWSER_NAVIGATION_PREDICTOR_SEARCH_ENGINE_PRECONNECTOR_H_
diff --git a/chrome/browser/navigation_predictor/search_engine_preconnector_browsertest.cc b/chrome/browser/navigation_predictor/search_engine_preconnector_browsertest.cc
new file mode 100644
index 0000000..855db82b7
--- /dev/null
+++ b/chrome/browser/navigation_predictor/search_engine_preconnector_browsertest.cc
@@ -0,0 +1,331 @@
+// Copyright 2019 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/macros.h"
+#include "base/run_loop.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/navigation_predictor/navigation_predictor_keyed_service.h"
+#include "chrome/browser/navigation_predictor/navigation_predictor_keyed_service_factory.h"
+#include "chrome/browser/predictors/loading_predictor.h"
+#include "chrome/browser/predictors/loading_predictor_factory.h"
+#include "chrome/browser/predictors/preconnect_manager.h"
+#include "chrome/browser/search_engines/template_url_service_factory.h"
+#include "chrome/browser/subresource_filter/subresource_filter_browser_test_harness.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/search_test_utils.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/search_engines/template_url_service.h"
+#include "components/ukm/test_ukm_recorder.h"
+#include "content/public/test/browser_test_utils.h"
+#include "net/base/features.h"
+#include "net/dns/mock_host_resolver.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "services/metrics/public/cpp/ukm_builders.h"
+#include "url/url_constants.h"
+
+namespace {
+
+// Feature to control preconnect to search.
+const base::Feature kPreconnectToSearchTest{"PreconnectToSearch",
+                                            base::FEATURE_DISABLED_BY_DEFAULT};
+
+GURL fake_search("https://www.fakesearch.com/");
+GURL google_search("https://www.google.com/");
+
+class SearchEnginePreconnectorBrowserTest
+    : public subresource_filter::SubresourceFilterBrowserTest,
+      public predictors::PreconnectManager::Observer {
+ public:
+  SearchEnginePreconnectorBrowserTest()
+      : subresource_filter::SubresourceFilterBrowserTest() {}
+
+  ~SearchEnginePreconnectorBrowserTest() override = default;
+
+  void SetUp() override {
+    https_server_ = std::make_unique<net::EmbeddedTestServer>(
+        net::EmbeddedTestServer::TYPE_HTTPS);
+    https_server_->ServeFilesFromSourceDirectory(
+        "chrome/test/data/navigation_predictor");
+    ASSERT_TRUE(https_server_->Start());
+
+    preresolve_counts_[GetTestURL("/").GetOrigin()] = 0;
+    preresolve_counts_[google_search] = 0;
+    preresolve_counts_[fake_search] = 0;
+
+    subresource_filter::SubresourceFilterBrowserTest::SetUp();
+  }
+
+  void SetUpOnMainThread() override {
+    subresource_filter::SubresourceFilterBrowserTest::SetUpOnMainThread();
+    host_resolver()->ClearRules();
+
+    auto* loading_predictor =
+        predictors::LoadingPredictorFactory::GetForProfile(
+            browser()->profile());
+    ASSERT_TRUE(loading_predictor);
+    loading_predictor->preconnect_manager()->SetObserverForTesting(this);
+  }
+
+  const GURL GetTestURL(const char* file) const {
+    return https_server_->GetURL(file);
+  }
+
+  void OnPreresolveFinished(
+      const GURL& url,
+      const net::NetworkIsolationKey& network_isolation_key,
+      bool success) override {
+    if (!base::Contains(preresolve_counts_, url.GetOrigin())) {
+      return;
+    }
+
+    // Only the test URL should successfully preconnect.
+    EXPECT_EQ(url.GetOrigin() == GetTestURL("/").GetOrigin(), success);
+
+    preresolve_counts_[url.GetOrigin()]++;
+    if (run_loops_[url.GetOrigin()])
+      run_loops_[url.GetOrigin()]->Quit();
+  }
+
+  void WaitForPreresolveCountForURL(const GURL url, int expected_count) {
+    EXPECT_TRUE(base::Contains(preresolve_counts_, url.GetOrigin()));
+    while (preresolve_counts_[url.GetOrigin()] < expected_count) {
+      run_loops_[url.GetOrigin()] = std::make_unique<base::RunLoop>();
+      run_loops_[url.GetOrigin()]->Run();
+      run_loops_[url.GetOrigin()].reset();
+    }
+  }
+
+  void WaitForDelay(base::TimeDelta delay) {
+    base::RunLoop run_loop;
+    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+        FROM_HERE, run_loop.QuitClosure(), delay);
+    run_loop.Run();
+  }
+
+ protected:
+  std::map<GURL, int> preresolve_counts_;
+  base::test::ScopedFeatureList feature_list_;
+
+ private:
+  std::unique_ptr<net::EmbeddedTestServer> https_server_;
+  std::map<GURL, std::unique_ptr<base::RunLoop>> run_loops_;
+
+  DISALLOW_COPY_AND_ASSIGN(SearchEnginePreconnectorBrowserTest);
+};
+
+class SearchEnginePreconnectorNoDelaysBrowserTest
+    : public SearchEnginePreconnectorBrowserTest {
+ public:
+  SearchEnginePreconnectorNoDelaysBrowserTest() {
+    {
+      feature_list_.InitWithFeaturesAndParameters(
+          {{kPreconnectToSearchTest, {{"startup_delay_ms", "0"}}},
+           {net::features::kNetUnusedIdleSocketTimeout,
+            {{"unused_idle_socket_timeout_seconds", "0"}}}},
+          {});
+    }
+  }
+
+  ~SearchEnginePreconnectorNoDelaysBrowserTest() override = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SearchEnginePreconnectorNoDelaysBrowserTest);
+};
+
+IN_PROC_BROWSER_TEST_F(SearchEnginePreconnectorNoDelaysBrowserTest,
+                       PreconnectSearch) {
+  // Verifies that the default search is preconnected.
+  static const char kShortName[] = "test";
+  static const char kSearchURL[] =
+      "/anchors_different_area.html?q={searchTerms}";
+  TemplateURLService* model =
+      TemplateURLServiceFactory::GetForProfile(browser()->profile());
+  ASSERT_TRUE(model);
+  search_test_utils::WaitForTemplateURLServiceToLoad(model);
+  ASSERT_TRUE(model->loaded());
+
+  // Check default URL is being preconnected and test URL is not.
+  WaitForPreresolveCountForURL(google_search, 1);
+  EXPECT_EQ(1, preresolve_counts_[google_search.GetOrigin()]);
+  EXPECT_EQ(0, preresolve_counts_[GetTestURL("/").GetOrigin()]);
+
+  TemplateURLData data;
+  data.SetShortName(base::ASCIIToUTF16(kShortName));
+  data.SetKeyword(data.short_name());
+  data.SetURL(GetTestURL(kSearchURL).spec());
+
+  TemplateURL* template_url = model->Add(std::make_unique<TemplateURL>(data));
+  ASSERT_TRUE(template_url);
+  model->SetUserSelectedDefaultSearchProvider(template_url);
+
+  // After switching search providers, the test URL should now start being
+  // preconnected.
+  WaitForPreresolveCountForURL(GetTestURL("/"), 2);
+  // Preconnect should occur for DSE.
+  EXPECT_EQ(2, preresolve_counts_[GetTestURL("/").GetOrigin()]);
+
+  WaitForPreresolveCountForURL(GetTestURL("/"), 4);
+  // Preconnect should occur again for DSE.
+  EXPECT_EQ(4, preresolve_counts_[GetTestURL("/").GetOrigin()]);
+}
+
+IN_PROC_BROWSER_TEST_F(SearchEnginePreconnectorNoDelaysBrowserTest,
+                       PreconnectOnlyInForeground) {
+  // Verifies that the default search is preconnected only on app foreground.
+  NavigationPredictorKeyedServiceFactory::GetForProfile(
+      Profile::FromBrowserContext(browser()->profile()))
+      ->SearchEnginePreconnectorForTesting()
+      ->OnAppStateChangedForTesting(false /* in_foreground */);
+
+  static const char kShortName[] = "test";
+  static const char kSearchURL[] =
+      "/anchors_different_area.html?q={searchTerms}";
+  TemplateURLService* model =
+      TemplateURLServiceFactory::GetForProfile(browser()->profile());
+  ASSERT_TRUE(model);
+  search_test_utils::WaitForTemplateURLServiceToLoad(model);
+  ASSERT_TRUE(model->loaded());
+
+  TemplateURLData data;
+  data.SetShortName(base::ASCIIToUTF16(kShortName));
+  data.SetKeyword(data.short_name());
+  data.SetURL(GetTestURL(kSearchURL).spec());
+
+  // Set the DSE to the test URL.
+  TemplateURL* template_url = model->Add(std::make_unique<TemplateURL>(data));
+  ASSERT_TRUE(template_url);
+  model->SetUserSelectedDefaultSearchProvider(template_url);
+
+  // Ensure that we wait long enough to trigger preconnects.
+  WaitForDelay(base::TimeDelta::FromMilliseconds(200));
+
+  TemplateURLData data_fake_search;
+  data_fake_search.SetShortName(base::ASCIIToUTF16(kShortName));
+  data_fake_search.SetKeyword(data.short_name());
+  data_fake_search.SetURL(fake_search.spec());
+
+  template_url = model->Add(std::make_unique<TemplateURL>(data_fake_search));
+  ASSERT_TRUE(template_url);
+  model->SetUserSelectedDefaultSearchProvider(template_url);
+
+  // Put the fake search URL to be preconnected in foreground.
+  NavigationPredictorKeyedServiceFactory::GetForProfile(
+      Profile::FromBrowserContext(browser()->profile()))
+      ->SearchEnginePreconnectorForTesting()
+      ->OnAppStateChangedForTesting(true /* in_foreground */);
+
+  WaitForPreresolveCountForURL(fake_search, 2);
+
+  // Preconnect should occur for fake search (2 since there are 2 NIKs).
+  EXPECT_EQ(2, preresolve_counts_[fake_search]);
+
+  // No preconnects should have been issued for the test URL.
+  EXPECT_EQ(0, preresolve_counts_[GetTestURL("/").GetOrigin()]);
+}
+
+class SearchEnginePreconnectorKeepSocketBrowserTest
+    : public SearchEnginePreconnectorBrowserTest {
+ public:
+  SearchEnginePreconnectorKeepSocketBrowserTest() {
+    {
+      feature_list_.InitWithFeaturesAndParameters(
+          {{kPreconnectToSearchTest, {{"startup_delay_ms", "0"}}},
+           {net::features::kNetUnusedIdleSocketTimeout,
+            {{"unused_idle_socket_timeout_seconds", "60"}}}},
+          {});
+    }
+  }
+
+  ~SearchEnginePreconnectorKeepSocketBrowserTest() override = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SearchEnginePreconnectorKeepSocketBrowserTest);
+};
+
+IN_PROC_BROWSER_TEST_F(SearchEnginePreconnectorKeepSocketBrowserTest,
+                       SocketWarmForSearch) {
+  // Verifies that a navigation to search will use a warm socket.
+  NavigationPredictorKeyedServiceFactory::GetForProfile(
+      Profile::FromBrowserContext(browser()->profile()))
+      ->SearchEnginePreconnectorForTesting()
+      ->OnAppStateChangedForTesting(false /* in_foreground */);
+
+  static const char kShortName[] = "test";
+  static const char kSearchURL[] =
+      "/anchors_different_area.html?q={searchTerms}";
+  static const char kSearchURLWithQuery[] =
+      "/anchors_different_area.html?q=porgs";
+
+  TemplateURLService* model =
+      TemplateURLServiceFactory::GetForProfile(browser()->profile());
+  ASSERT_TRUE(model);
+  search_test_utils::WaitForTemplateURLServiceToLoad(model);
+  ASSERT_TRUE(model->loaded());
+
+  TemplateURLData data;
+  data.SetShortName(base::ASCIIToUTF16(kShortName));
+  data.SetKeyword(data.short_name());
+  data.SetURL(GetTestURL(kSearchURL).spec());
+
+  // Set the DSE to the test URL.
+  TemplateURL* template_url = model->Add(std::make_unique<TemplateURL>(data));
+  ASSERT_TRUE(template_url);
+  model->SetUserSelectedDefaultSearchProvider(template_url);
+
+  // Put the fake search URL to be preconnected in foreground.
+  NavigationPredictorKeyedServiceFactory::GetForProfile(
+      Profile::FromBrowserContext(browser()->profile()))
+      ->SearchEnginePreconnectorForTesting()
+      ->OnAppStateChangedForTesting(true /* in_foreground */);
+
+  WaitForPreresolveCountForURL(GetTestURL(kSearchURL), 1);
+
+  ui_test_utils::NavigateToURL(browser(), GetTestURL(kSearchURLWithQuery));
+
+  auto ukm_recorder = std::make_unique<ukm::TestAutoSetUkmRecorder>();
+
+  ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
+
+  const auto& entries =
+      ukm_recorder->GetMergedEntriesByName(ukm::builders::PageLoad::kEntryName);
+  EXPECT_EQ(1u, entries.size());
+
+  for (const auto& kv : entries) {
+    EXPECT_TRUE(ukm_recorder->EntryHasMetric(
+        kv.second.get(),
+        ukm::builders::PageLoad::kMainFrameResource_SocketReusedName));
+  }
+}
+
+IN_PROC_BROWSER_TEST_F(SearchEnginePreconnectorKeepSocketBrowserTest,
+                       SocketColdForNonSearch) {
+  // Verifies that a navigation to non search will not use a warm socket.
+  static const char kSearchURLWithQuery[] =
+      "/anchors_different_area.html?q=porgs";
+
+  ui_test_utils::NavigateToURL(browser(), GetTestURL(kSearchURLWithQuery));
+
+  auto ukm_recorder = std::make_unique<ukm::TestAutoSetUkmRecorder>();
+
+  ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
+
+  const auto& entries =
+      ukm_recorder->GetMergedEntriesByName(ukm::builders::PageLoad::kEntryName);
+  EXPECT_EQ(1u, entries.size());
+
+  for (const auto& kv : entries) {
+    EXPECT_TRUE(ukm_recorder->EntryHasMetric(
+        kv.second.get(),
+        ukm::builders::PageLoad::kMainFrameResource_SocketReusedName));
+
+    EXPECT_EQ(
+        0, *(ukm_recorder->GetEntryMetric(
+               kv.second.get(),
+               ukm::builders::PageLoad::kMainFrameResource_SocketReusedName)));
+  }
+}
+
+}  // namespace
diff --git a/chrome/browser/net/trial_comparison_cert_verifier_browsertest.cc b/chrome/browser/net/trial_comparison_cert_verifier_browsertest.cc
index 05873ef..6d67683 100644
--- a/chrome/browser/net/trial_comparison_cert_verifier_browsertest.cc
+++ b/chrome/browser/net/trial_comparison_cert_verifier_browsertest.cc
@@ -12,7 +12,7 @@
 #include "chrome/common/chrome_features.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "net/base/features.h"
 #include "net/cert/trial_comparison_cert_verifier.h"
 #include "net/net_buildflags.h"
diff --git a/chrome/browser/net/trial_comparison_cert_verifier_controller.cc b/chrome/browser/net/trial_comparison_cert_verifier_controller.cc
index e86dc48c..b957fe7 100644
--- a/chrome/browser/net/trial_comparison_cert_verifier_controller.cc
+++ b/chrome/browser/net/trial_comparison_cert_verifier_controller.cc
@@ -20,7 +20,7 @@
 #include "chrome/browser/safe_browsing/certificate_reporting_service_factory.h"
 #include "chrome/common/channel_info.h"
 #include "chrome/common/chrome_features.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/security_interstitials/content/certificate_error_report.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
diff --git a/chrome/browser/net/trial_comparison_cert_verifier_controller_unittest.cc b/chrome/browser/net/trial_comparison_cert_verifier_controller_unittest.cc
index eea8557..0e46f45 100644
--- a/chrome/browser/net/trial_comparison_cert_verifier_controller_unittest.cc
+++ b/chrome/browser/net/trial_comparison_cert_verifier_controller_unittest.cc
@@ -21,7 +21,7 @@
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/security_interstitials/content/cert_logger.pb.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "content/public/browser/browser_task_traits.h"
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.cc
index eb5ba98..b5836d1 100644
--- a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.cc
@@ -196,7 +196,6 @@
     ukm::SourceId source_id) {
   DCHECK(ad_frames_data_.empty());
 
-  committed_ = true;
   page_load_is_reload_ =
       navigation_handle->GetReloadType() != content::ReloadType::NONE;
 
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.h
index 73ab25f..22a5fd4 100644
--- a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.h
@@ -237,8 +237,6 @@
   // Total ad bytes loaded by the page since it was observed to be interactive.
   size_t page_ad_bytes_at_interactive_ = 0u;
 
-  bool committed_ = false;
-
   ScopedObserver<subresource_filter::SubresourceFilterObserverManager,
                  subresource_filter::SubresourceFilterObserver>
       subresource_observer_;
diff --git a/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc b/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
index 5e9a410..adaf7014 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
@@ -72,7 +72,7 @@
 #endif
 
 #if BUILDFLAG(FULL_SAFE_BROWSING)
-#include "components/safe_browsing/password_protection/mock_password_protection_service.h"
+#include "components/safe_browsing/content/password_protection/mock_password_protection_service.h"
 #endif
 
 #if defined(OS_ANDROID)
diff --git a/chrome/browser/permissions/contextual_notification_permission_ui_selector.cc b/chrome/browser/permissions/contextual_notification_permission_ui_selector.cc
index 9be273c..9534605 100644
--- a/chrome/browser/permissions/contextual_notification_permission_ui_selector.cc
+++ b/chrome/browser/permissions/contextual_notification_permission_ui_selector.cc
@@ -19,7 +19,7 @@
 #include "chrome/browser/permissions/quiet_notification_permission_ui_state.h"
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "chrome/common/chrome_features.h"
-#include "components/safe_browsing/db/database_manager.h"
+#include "components/safe_browsing/core/db/database_manager.h"
 
 namespace {
 
diff --git a/chrome/browser/permissions/crowd_deny_safe_browsing_request.cc b/chrome/browser/permissions/crowd_deny_safe_browsing_request.cc
index ee4d341..07122cd 100644
--- a/chrome/browser/permissions/crowd_deny_safe_browsing_request.cc
+++ b/chrome/browser/permissions/crowd_deny_safe_browsing_request.cc
@@ -15,7 +15,7 @@
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/time/clock.h"
 #include "base/timer/timer.h"
-#include "components/safe_browsing/db/database_manager.h"
+#include "components/safe_browsing/core/db/database_manager.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "url/origin.h"
 
diff --git a/chrome/browser/permissions/crowd_deny_safe_browsing_request_unittest.cc b/chrome/browser/permissions/crowd_deny_safe_browsing_request_unittest.cc
index 0c73eb2..c133577b 100644
--- a/chrome/browser/permissions/crowd_deny_safe_browsing_request_unittest.cc
+++ b/chrome/browser/permissions/crowd_deny_safe_browsing_request_unittest.cc
@@ -10,8 +10,8 @@
 #include "base/task/post_task.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/mock_callback.h"
-#include "components/safe_browsing/db/database_manager.h"
-#include "components/safe_browsing/db/test_database_manager.h"
+#include "components/safe_browsing/core/db/database_manager.h"
+#include "components/safe_browsing/core/db/test_database_manager.h"
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index d471deb..2012f20 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -68,7 +68,7 @@
 #include "components/policy/core/common/policy_pref_names.h"
 #include "components/policy/core/common/schema.h"
 #include "components/policy/policy_constants.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/search_engines/default_search_policy_handler.h"
 #include "components/security_state/core/security_state_pref_names.h"
 #include "components/signin/public/base/signin_pref_names.h"
diff --git a/chrome/browser/policy/safe_browsing_policy_browsertest.cc b/chrome/browser/policy/safe_browsing_policy_browsertest.cc
index fdced17..6e1a5f5bb 100644
--- a/chrome/browser/policy/safe_browsing_policy_browsertest.cc
+++ b/chrome/browser/policy/safe_browsing_policy_browsertest.cc
@@ -15,7 +15,7 @@
 #include "components/policy/core/common/policy_map.h"
 #include "components/policy/policy_constants.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/features.h"
+#include "components/safe_browsing/core/features.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "url/gurl.h"
 
diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
index 68dca20..a3313ee 100644
--- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
+++ b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
@@ -323,6 +323,12 @@
     public static final String FLAGS_CACHED_NIGHT_MODE_DEFAULT_TO_LIGHT =
             "night_mode_default_to_light";
     /**
+     * Whether the Paint Preview Capture menu item is enabled.
+     * Default value is false.
+     */
+    public static final String FLAGS_CACHED_PAINT_PREVIEW_TEST_ENABLED_KEY =
+            "Chrome.Flags.PaintPreviewTestEnabled";
+    /**
      * Whether or not bootstrap tasks should be prioritized (i.e. bootstrap task prioritization
      * experiment is enabled). Default value is true.
      */
@@ -556,6 +562,7 @@
                 FLAGS_CACHED_NIGHT_MODE_AVAILABLE,
                 FLAGS_CACHED_NIGHT_MODE_CCT_AVAILABLE,
                 FLAGS_CACHED_NIGHT_MODE_DEFAULT_TO_LIGHT,
+                FLAGS_CACHED_PAINT_PREVIEW_TEST_ENABLED_KEY,
                 FLAGS_CACHED_PRIORITIZE_BOOTSTRAP_TASKS,
                 FLAGS_CACHED_SERVICE_MANAGER_FOR_BACKGROUND_PREFETCH,
                 FLAGS_CACHED_SERVICE_MANAGER_FOR_DOWNLOAD_RESUMPTION,
@@ -707,6 +714,7 @@
                 FLAGS_CACHED_NIGHT_MODE_AVAILABLE,
                 FLAGS_CACHED_NIGHT_MODE_CCT_AVAILABLE,
                 FLAGS_CACHED_NIGHT_MODE_DEFAULT_TO_LIGHT,
+                FLAGS_CACHED_PAINT_PREVIEW_TEST_ENABLED_KEY,
                 FLAGS_CACHED_PRIORITIZE_BOOTSTRAP_TASKS,
                 FLAGS_CACHED_SERVICE_MANAGER_FOR_BACKGROUND_PREFETCH,
                 FLAGS_CACHED_SERVICE_MANAGER_FOR_DOWNLOAD_RESUMPTION,
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index c436f130..c3e9874 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -126,7 +126,7 @@
 #include "components/prefs/pref_service.h"
 #include "components/proxy_config/pref_proxy_config_tracker_impl.h"
 #include "components/rappor/rappor_service_impl.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/search_engines/template_url_prepopulate_data.h"
 #include "components/security_state/core/security_state.h"
 #include "components/sessions/core/session_id_generator.h"
diff --git a/chrome/browser/prefs/chrome_pref_service_factory.cc b/chrome/browser/prefs/chrome_pref_service_factory.cc
index ce49583f..6186da7 100644
--- a/chrome/browser/prefs/chrome_pref_service_factory.cc
+++ b/chrome/browser/prefs/chrome_pref_service_factory.cc
@@ -47,7 +47,7 @@
 #include "components/prefs/pref_service.h"
 #include "components/prefs/pref_store.h"
 #include "components/prefs/pref_value_store.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/search_engines/default_search_manager.h"
 #include "components/search_engines/search_engines_pref_names.h"
 #include "components/signin/public/base/signin_pref_names.h"
diff --git a/chrome/browser/prefs/pref_functional_browsertest.cc b/chrome/browser/prefs/pref_functional_browsertest.cc
index 29a9b4e..a66a403 100644
--- a/chrome/browser/prefs/pref_functional_browsertest.cc
+++ b/chrome/browser/prefs/pref_functional_browsertest.cc
@@ -22,7 +22,7 @@
 #include "components/content_settings/core/browser/website_settings_registry.h"
 #include "components/content_settings/core/common/content_settings_types.h"
 #include "components/content_settings/core/common/pref_names.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/sync_preferences/pref_service_syncable.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test_utils.h"
diff --git a/chrome/browser/prerender/prerender_browsertest.cc b/chrome/browser/prerender/prerender_browsertest.cc
index d7afecc..5eb9f91 100644
--- a/chrome/browser/prerender/prerender_browsertest.cc
+++ b/chrome/browser/prerender/prerender_browsertest.cc
@@ -85,8 +85,8 @@
 #include "components/password_manager/core/browser/password_bubble_experiment.h"
 #include "components/password_manager/core/browser/password_manager_test_utils.h"
 #include "components/password_manager/core/browser/test_password_store.h"
-#include "components/safe_browsing/db/database_manager.h"
-#include "components/safe_browsing/db/util.h"
+#include "components/safe_browsing/core/db/database_manager.h"
+#include "components/safe_browsing/core/db/util.h"
 #include "components/variations/entropy_provider.h"
 #include "components/variations/variations_associated_data.h"
 #include "content/public/browser/browser_message_filter.h"
diff --git a/chrome/browser/prerender/prerender_test_utils.cc b/chrome/browser/prerender/prerender_test_utils.cc
index 81784ce0..57c671f 100644
--- a/chrome/browser/prerender/prerender_test_utils.cc
+++ b/chrome/browser/prerender/prerender_test_utils.cc
@@ -26,7 +26,7 @@
 #include "chrome/grit/generated_resources.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/render_widget_host.h"
diff --git a/chrome/browser/prerender/prerender_test_utils.h b/chrome/browser/prerender/prerender_test_utils.h
index 42a00c85..be6ecbf 100644
--- a/chrome/browser/prerender/prerender_test_utils.h
+++ b/chrome/browser/prerender/prerender_test_utils.h
@@ -24,7 +24,7 @@
 #include "chrome/browser/prerender/prerender_manager.h"
 #include "chrome/browser/safe_browsing/test_safe_browsing_service.h"
 #include "chrome/test/base/in_process_browser_test.h"
-#include "components/safe_browsing/db/test_database_manager.h"
+#include "components/safe_browsing/core/db/test_database_manager.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_widget_host_observer.h"
 #include "url/gurl.h"
diff --git a/chrome/browser/profiles/off_the_record_profile_io_data.cc b/chrome/browser/profiles/off_the_record_profile_io_data.cc
index dd6e80c7..7935c67 100644
--- a/chrome/browser/profiles/off_the_record_profile_io_data.cc
+++ b/chrome/browser/profiles/off_the_record_profile_io_data.cc
@@ -21,11 +21,8 @@
 #include "chrome/browser/net/profile_network_context_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_switches.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
 #include "components/net_log/chrome_net_log.h"
-#include "components/prefs/pref_service.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/cookie_store_factory.h"
@@ -83,10 +80,6 @@
   // Set initialized_ to true at the beginning in case any of the objects
   // below try to get the ResourceContext pointer.
   initialized_ = true;
-  io_data_->safe_browsing_enabled()->Init(prefs::kSafeBrowsingEnabled,
-      profile_->GetPrefs());
-  io_data_->safe_browsing_enabled()->MoveToSequence(
-      base::CreateSingleThreadTaskRunner({BrowserThread::IO}));
   io_data_->InitializeOnUIThread(profile_);
 }
 
diff --git a/chrome/browser/profiles/profile.cc b/chrome/browser/profiles/profile.cc
index 171a3b1..cf93dac 100644
--- a/chrome/browser/profiles/profile.cc
+++ b/chrome/browser/profiles/profile.cc
@@ -22,7 +22,7 @@
 #include "components/language/core/browser/pref_names.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/sync/base/sync_prefs.h"
 #include "components/sync/driver/sync_driver_switches.h"
 #include "components/sync/driver/sync_service.h"
diff --git a/chrome/browser/profiles/profile_impl_io_data.cc b/chrome/browser/profiles/profile_impl_io_data.cc
index 2701fdee..b74817a 100644
--- a/chrome/browser/profiles/profile_impl_io_data.cc
+++ b/chrome/browser/profiles/profile_impl_io_data.cc
@@ -34,16 +34,10 @@
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_switches.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
 #include "components/cookie_config/cookie_store_util.h"
 #include "components/net_log/chrome_net_log.h"
 #include "components/network_session_configurator/browser/network_session_configurator.h"
-#include "components/prefs/json_pref_store.h"
-#include "components/prefs/pref_filter.h"
-#include "components/prefs/pref_member.h"
-#include "components/prefs/pref_service.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
@@ -115,11 +109,6 @@
   // Set initialized_ to true at the beginning in case any of the objects
   // below try to get the ResourceContext pointer.
   initialized_ = true;
-  PrefService* pref_service = profile_->GetPrefs();
-  io_data_->safe_browsing_enabled()->Init(prefs::kSafeBrowsingEnabled,
-      pref_service);
-  io_data_->safe_browsing_enabled()->MoveToSequence(
-      base::CreateSingleThreadTaskRunner({BrowserThread::IO}));
   io_data_->InitializeOnUIThread(profile_);
 }
 
diff --git a/chrome/browser/profiles/profile_io_data.cc b/chrome/browser/profiles/profile_io_data.cc
index 9a6f5cd..d0b305d 100644
--- a/chrome/browser/profiles/profile_io_data.cc
+++ b/chrome/browser/profiles/profile_io_data.cc
@@ -407,8 +407,6 @@
 void ProfileIOData::ShutdownOnUIThread() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  safe_browsing_enabled_.Destroy();
-
   bool posted = base::DeleteSoon(FROM_HERE, {BrowserThread::IO}, this);
   if (!posted)
     delete this;
diff --git a/chrome/browser/profiles/profile_io_data.h b/chrome/browser/profiles/profile_io_data.h
index 92721687..39eb5630 100644
--- a/chrome/browser/profiles/profile_io_data.h
+++ b/chrome/browser/profiles/profile_io_data.h
@@ -72,10 +72,6 @@
   content_settings::CookieSettings* GetCookieSettings() const;
   HostContentSettingsMap* GetHostContentSettingsMap() const;
 
-  BooleanPrefMember* safe_browsing_enabled() const {
-    return &safe_browsing_enabled_;
-  }
-
 #if defined(OS_CHROMEOS)
   std::string username_hash() const {
     return username_hash_;
@@ -154,9 +150,6 @@
   // Deleted after lazy initialization.
   mutable std::unique_ptr<ProfileParams> profile_params_;
 
-  // Member variables which are pointed to by the various context objects.
-  mutable BooleanPrefMember safe_browsing_enabled_;
-
   // Pointed to by URLRequestContext.
 #if BUILDFLAG(ENABLE_EXTENSIONS)
   mutable scoped_refptr<extensions::InfoMap> extension_info_map_;
diff --git a/chrome/browser/profiles/profile_manager.cc b/chrome/browser/profiles/profile_manager.cc
index c2b6fd06..3c43cc7 100644
--- a/chrome/browser/profiles/profile_manager.cc
+++ b/chrome/browser/profiles/profile_manager.cc
@@ -42,6 +42,7 @@
 #include "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings_factory.h"
 #include "chrome/browser/download/download_core_service.h"
 #include "chrome/browser/download/download_core_service_factory.h"
+#include "chrome/browser/navigation_predictor/navigation_predictor_keyed_service_factory.h"
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h"
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h"
 #include "chrome/browser/password_manager/password_store_factory.h"
@@ -401,13 +402,13 @@
   Profile* profile = GetLastUsedProfile();
   if (!profile)
     return nullptr;
-  if (IncognitoModeForced(profile))
+  if (IsOffTheRecordModeForced(profile))
     return profile->GetOffTheRecordProfile();
   return profile;
 }
 
 // static
-bool ProfileManager::IncognitoModeForced(Profile* profile) {
+bool ProfileManager::IsOffTheRecordModeForced(Profile* profile) {
   return profile->IsGuestSession() ||
          profile->IsSystemProfile() ||
          IncognitoModePrefs::GetAvailability(profile->GetPrefs()) ==
@@ -1281,6 +1282,9 @@
       base::CreateSingleThreadTaskRunner({BrowserThread::UI}),
       profile->GetPath());
 
+  // Ensure NavigationPredictorKeyedService is started.
+  NavigationPredictorKeyedServiceFactory::GetForProfile(profile);
+
   IdentityManagerFactory::GetForProfile(profile)->OnNetworkInitialized();
   AccountReconcilorFactory::GetForProfile(profile);
 
diff --git a/chrome/browser/profiles/profile_manager.h b/chrome/browser/profiles/profile_manager.h
index 7d02af7..f4c3c9e2 100644
--- a/chrome/browser/profiles/profile_manager.h
+++ b/chrome/browser/profiles/profile_manager.h
@@ -54,6 +54,8 @@
   // Same as instance method but provides the default user_data_dir as well.
   // If the Profile is going to be used to open a new window then consider using
   // GetLastUsedProfileAllowedByPolicy() instead.
+  // Except in ChromeOS guest sessions, the returned profile is always a regular
+  // profile (non-OffTheRecord).
   static Profile* GetLastUsedProfile();
 
   // Same as GetLastUsedProfile() but returns the incognito Profile if
@@ -61,9 +63,9 @@
   // will be used to open new browser windows.
   static Profile* GetLastUsedProfileAllowedByPolicy();
 
-  // Helper function that returns true if incognito mode is forced for |profile|
-  // (normal mode is not available for browsing).
-  static bool IncognitoModeForced(Profile* profile);
+  // Helper function that returns true if OffTheRecord mode is forced for
+  // |profile| (normal mode is not available for browsing).
+  static bool IsOffTheRecordModeForced(Profile* profile);
 
   // Same as instance method but provides the default user_data_dir as well.
   static std::vector<Profile*> GetLastOpenedProfiles();
diff --git a/chrome/browser/reputation/reputation_service.cc b/chrome/browser/reputation/reputation_service.cc
index 5fd3557..9bd1a98 100644
--- a/chrome/browser/reputation/reputation_service.cc
+++ b/chrome/browser/reputation/reputation_service.cc
@@ -21,7 +21,7 @@
 #include "chrome/browser/reputation/safety_tips_config.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
 #include "components/security_state/core/security_state.h"
 #include "components/url_formatter/spoof_checks/top_domains/top500_domains.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
diff --git a/chrome/browser/resources/ssl/ssl_error_assistant/BUILD.gn b/chrome/browser/resources/ssl/ssl_error_assistant/BUILD.gn
index 1867e4c..1d1d09f 100644
--- a/chrome/browser/resources/ssl/ssl_error_assistant/BUILD.gn
+++ b/chrome/browser/resources/ssl/ssl_error_assistant/BUILD.gn
@@ -13,14 +13,15 @@
   output_dir = target_gen_dir
   output_basename = "ssl_error_assistant.pb"
   python_path_root = "$root_out_dir/pyproto"
-  python_path_ssl = "$python_path_root/chrome/browser/ssl/"
+  python_path_ssl =
+      "$python_path_root/components/security_interstitials/content/"
 
   inputs = [
     input_filename,
   ]
 
   deps = [
-    "//chrome/browser/ssl:proto",
+    "//components/security_interstitials/content:proto",
     "//third_party/protobuf:py_proto",
   ]
 
diff --git a/chrome/browser/safe_browsing/BUILD.gn b/chrome/browser/safe_browsing/BUILD.gn
index daa4366..91df512 100644
--- a/chrome/browser/safe_browsing/BUILD.gn
+++ b/chrome/browser/safe_browsing/BUILD.gn
@@ -29,13 +29,13 @@
     "//components/pref_registry",
     "//components/resources:components_resources_grit",
     "//components/safe_browsing:buildflags",
-    "//components/safe_browsing:features",
-    "//components/safe_browsing:ping_manager",
-    "//components/safe_browsing/browser:referrer_chain_provider",
-    "//components/safe_browsing/common:interfaces",
-    "//components/safe_browsing/db:database_manager",
-    "//components/safe_browsing/db:v4_local_database_manager",
-    "//components/safe_browsing/web_ui",
+    "//components/safe_browsing/content/web_ui",
+    "//components/safe_browsing/core:features",
+    "//components/safe_browsing/core:ping_manager",
+    "//components/safe_browsing/core/browser:referrer_chain_provider",
+    "//components/safe_browsing/core/common:interfaces",
+    "//components/safe_browsing/core/db:database_manager",
+    "//components/safe_browsing/core/db:v4_local_database_manager",
     "//components/search_engines",
     "//components/sessions",
     "//components/signin/public/identity_manager",
@@ -101,26 +101,27 @@
     deps += [
       "//chrome/browser/engagement:mojo_bindings",
       "//chrome/common/safe_browsing:proto",
-      "//components/safe_browsing:csd_proto",
-      "//components/safe_browsing:safe_browsing",
+      "//components/safe_browsing/content",
+      "//components/safe_browsing/core:csd_proto",
 
       # TODO(crbug/996380): This is needed because the DownloadProtectionService
       # is being built on Android. Since we don't actually use any
       # DownloadProtectionService features on Android, we should fix that
       # dependency and move this to the 'safe_browsing_mode == 1' section.
-      "//components/safe_browsing:webprotect_proto",
-      "//components/safe_browsing/browser",
-      "//components/safe_browsing/common",
-      "//components/safe_browsing/common:safe_browsing_prefs",
-      "//components/safe_browsing/db:allowlist_checker_client",
-      "//components/safe_browsing/db:metadata_proto",
-      "//components/safe_browsing/password_protection",
-      "//components/safe_browsing/triggers",
-      "//components/safe_browsing/triggers:ad_popup_trigger",
-      "//components/safe_browsing/triggers:ad_redirect_trigger",
-      "//components/safe_browsing/triggers:ad_sampler_trigger",
-      "//components/safe_browsing/triggers:suspicious_site_trigger",
-      "//components/safe_browsing/triggers:trigger_throttler",
+      "//components/safe_browsing/content/browser",
+      "//components/safe_browsing/content/password_protection",
+      "//components/safe_browsing/content/triggers:ad_popup_trigger",
+      "//components/safe_browsing/content/triggers:ad_redirect_trigger",
+      "//components/safe_browsing/content/triggers:ad_sampler_trigger",
+      "//components/safe_browsing/content/triggers:suspicious_site_trigger",
+      "//components/safe_browsing/core:webprotect_proto",
+      "//components/safe_browsing/core/browser",
+      "//components/safe_browsing/core/common",
+      "//components/safe_browsing/core/common:safe_browsing_prefs",
+      "//components/safe_browsing/core/db:allowlist_checker_client",
+      "//components/safe_browsing/core/db:metadata_proto",
+      "//components/safe_browsing/core/triggers",
+      "//components/safe_browsing/core/triggers:trigger_throttler",
     ]
     if (safe_browsing_mode == 1) {
       # "Safe Browsing Full" files in addition to the "basic" ones to use for
@@ -242,7 +243,7 @@
         "//components/content_settings/core/browser",
         "//components/language/core/common",
         "//components/prefs",
-        "//components/safe_browsing/db",
+        "//components/safe_browsing/core/db",
         "//components/security_interstitials/content:security_interstitial_page",
         "//content/public/browser",
         "//net",
@@ -278,8 +279,8 @@
   deps = [
     "//components/keyed_service/content",
     "//components/prefs",
-    "//components/safe_browsing/common",
-    "//components/safe_browsing/common:safe_browsing_prefs",
+    "//components/safe_browsing/core/common",
+    "//components/safe_browsing/core/common:safe_browsing_prefs",
     "//components/signin/public/identity_manager",
     "//content/public/browser",
   ]
@@ -296,9 +297,9 @@
       ":safe_browsing",
       "//chrome/common/safe_browsing:proto",
       "//components/safe_browsing:buildflags",
-      "//components/safe_browsing/db:database_manager",
-      "//components/safe_browsing/db:test_database_manager",
-      "//components/safe_browsing/db:v4_protocol_manager_util",
+      "//components/safe_browsing/core/db:database_manager",
+      "//components/safe_browsing/core/db:test_database_manager",
+      "//components/safe_browsing/core/db:v4_protocol_manager_util",
       "//content/public/browser",
     ]
   }
diff --git a/chrome/browser/safe_browsing/ad_redirect_trigger_browsertest.cc b/chrome/browser/safe_browsing/ad_redirect_trigger_browsertest.cc
index b0ec1a1..1bad51c 100644
--- a/chrome/browser/safe_browsing/ad_redirect_trigger_browsertest.cc
+++ b/chrome/browser/safe_browsing/ad_redirect_trigger_browsertest.cc
@@ -14,10 +14,10 @@
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/prefs/testing_pref_service.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/features.h"
-#include "components/safe_browsing/triggers/ad_redirect_trigger.h"
-#include "components/safe_browsing/triggers/mock_trigger_manager.h"
+#include "components/safe_browsing/content/triggers/ad_redirect_trigger.h"
+#include "components/safe_browsing/content/triggers/mock_trigger_manager.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/features.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test_utils.h"
diff --git a/chrome/browser/safe_browsing/advanced_protection_status_manager.cc b/chrome/browser/safe_browsing/advanced_protection_status_manager.cc
index 1390bc78..b1154e5 100644
--- a/chrome/browser/safe_browsing/advanced_protection_status_manager.cc
+++ b/chrome/browser/safe_browsing/advanced_protection_status_manager.cc
@@ -11,8 +11,8 @@
 #include "chrome/browser/safe_browsing/advanced_protection_status_manager_factory.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/features.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/features.h"
 #include "components/signin/public/identity_manager/accounts_mutator.h"
 #include "components/signin/public/identity_manager/primary_account_access_token_fetcher.h"
 #include "content/public/browser/browser_context.h"
diff --git a/chrome/browser/safe_browsing/advanced_protection_status_manager_unittest.cc b/chrome/browser/safe_browsing/advanced_protection_status_manager_unittest.cc
index bf73739..4bdc2ea 100644
--- a/chrome/browser/safe_browsing/advanced_protection_status_manager_unittest.cc
+++ b/chrome/browser/safe_browsing/advanced_protection_status_manager_unittest.cc
@@ -9,7 +9,7 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/signin/public/identity_manager/accounts_mutator.h"
 #include "components/signin/public/identity_manager/identity_test_environment.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
diff --git a/chrome/browser/safe_browsing/android/safe_browsing_bridge.cc b/chrome/browser/safe_browsing/android/safe_browsing_bridge.cc
index 11c62ee..ab1505e 100644
--- a/chrome/browser/safe_browsing/android/safe_browsing_bridge.cc
+++ b/chrome/browser/safe_browsing/android/safe_browsing_bridge.cc
@@ -8,7 +8,7 @@
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/common/safe_browsing/file_type_policies.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 
 using base::android::JavaParamRef;
 
diff --git a/chrome/browser/safe_browsing/android/services_delegate_android.h b/chrome/browser/safe_browsing/android/services_delegate_android.h
index 42181ce..709454e 100644
--- a/chrome/browser/safe_browsing/android/services_delegate_android.h
+++ b/chrome/browser/safe_browsing/android/services_delegate_android.h
@@ -7,7 +7,7 @@
 
 #include "base/macros.h"
 #include "chrome/browser/safe_browsing/services_delegate.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/safe_browsing/browser_feature_extractor.cc b/chrome/browser/safe_browsing/browser_feature_extractor.cc
index b6c0b17..5451295 100644
--- a/chrome/browser/safe_browsing/browser_feature_extractor.cc
+++ b/chrome/browser/safe_browsing/browser_feature_extractor.cc
@@ -25,7 +25,7 @@
 #include "chrome/browser/safe_browsing/client_side_detection_host.h"
 #include "components/history/core/browser/history_service.h"
 #include "components/history/core/browser/history_types.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_controller.h"
diff --git a/chrome/browser/safe_browsing/browser_feature_extractor_unittest.cc b/chrome/browser/safe_browsing/browser_feature_extractor_unittest.cc
index 9dd06cd..f4637a73 100644
--- a/chrome/browser/safe_browsing/browser_feature_extractor_unittest.cc
+++ b/chrome/browser/safe_browsing/browser_feature_extractor_unittest.cc
@@ -24,7 +24,7 @@
 #include "chrome/test/base/testing_profile.h"
 #include "components/history/core/browser/history_backend.h"
 #include "components/history/core/browser/history_service.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/web_contents.h"
diff --git a/chrome/browser/safe_browsing/certificate_reporting_service.cc b/chrome/browser/safe_browsing/certificate_reporting_service.cc
index f0f7219..becf1b2 100644
--- a/chrome/browser/safe_browsing/certificate_reporting_service.cc
+++ b/chrome/browser/safe_browsing/certificate_reporting_service.cc
@@ -12,7 +12,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/security_interstitials/content/certificate_error_report.h"
 #include "content/public/browser/browser_thread.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
diff --git a/chrome/browser/safe_browsing/certificate_reporting_service_browsertest.cc b/chrome/browser/safe_browsing/certificate_reporting_service_browsertest.cc
index c21b158..ed6c49b 100644
--- a/chrome/browser/safe_browsing/certificate_reporting_service_browsertest.cc
+++ b/chrome/browser/safe_browsing/certificate_reporting_service_browsertest.cc
@@ -26,7 +26,7 @@
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/security_interstitials/content/cert_report_helper.h"
 #include "components/security_interstitials/content/certificate_error_report.h"
 #include "components/variations/variations_params_manager.h"
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/BUILD.gn b/chrome/browser/safe_browsing/chrome_cleaner/BUILD.gn
index 668bbdd..5cb868b 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/BUILD.gn
+++ b/chrome/browser/safe_browsing/chrome_cleaner/BUILD.gn
@@ -67,7 +67,7 @@
     "//components/crx_file",
     "//components/pref_registry",
     "//components/prefs",
-    "//components/safe_browsing/common:safe_browsing_prefs",
+    "//components/safe_browsing/core/common:safe_browsing_prefs",
     "//content/public/browser",
     "//extensions/browser",
     "//third_party/protobuf:protobuf_lite",
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_impl_win.cc b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_impl_win.cc
index dd3c09b7..5c83c2a 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_impl_win.cc
+++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_impl_win.cc
@@ -42,7 +42,7 @@
 #include "components/component_updater/component_updater_service.h"
 #include "components/component_updater/pref_names.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "extensions/browser/extension_registry.h"
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/reporter_runner_browsertest_win.cc b/chrome/browser/safe_browsing/chrome_cleaner/reporter_runner_browsertest_win.cc
index 8b52bc3c..8dd5472 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/reporter_runner_browsertest_win.cc
+++ b/chrome/browser/safe_browsing/chrome_cleaner/reporter_runner_browsertest_win.cc
@@ -44,7 +44,7 @@
 #include "components/policy/core/common/mock_configuration_policy_provider.h"
 #include "components/policy/policy_constants.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/srt_client_info_win.cc b/chrome/browser/safe_browsing/chrome_cleaner/srt_client_info_win.cc
index 596c288..7e7dcac0 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/srt_client_info_win.cc
+++ b/chrome/browser/safe_browsing/chrome_cleaner/srt_client_info_win.cc
@@ -12,7 +12,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/common/channel_info.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/version_info/version_info.h"
 
 namespace safe_browsing {
diff --git a/chrome/browser/safe_browsing/chrome_controller_client.cc b/chrome/browser/safe_browsing/chrome_controller_client.cc
index eb3516e..52d814e 100644
--- a/chrome/browser/safe_browsing/chrome_controller_client.cc
+++ b/chrome/browser/safe_browsing/chrome_controller_client.cc
@@ -5,7 +5,7 @@
 #include "chrome/browser/safe_browsing/chrome_controller_client.h"
 
 #include "base/feature_list.h"
-#include "components/safe_browsing/features.h"
+#include "components/safe_browsing/core/features.h"
 #include "components/security_interstitials/core/metrics_helper.h"
 #include "extensions/buildflags/buildflags.h"
 
diff --git a/chrome/browser/safe_browsing/chrome_controller_client.h b/chrome/browser/safe_browsing/chrome_controller_client.h
index 57e778a..c0f20f7 100644
--- a/chrome/browser/safe_browsing/chrome_controller_client.h
+++ b/chrome/browser/safe_browsing/chrome_controller_client.h
@@ -6,7 +6,7 @@
 #define CHROME_BROWSER_SAFE_BROWSING_CHROME_CONTROLLER_CLIENT_H_
 
 #include "base/macros.h"
-#include "components/safe_browsing/safe_browsing_controller_client.h"
+#include "components/safe_browsing/content/safe_browsing_controller_client.h"
 
 namespace content {
 class WebContents;
diff --git a/chrome/browser/safe_browsing/chrome_password_protection_service.cc b/chrome/browser/safe_browsing/chrome_password_protection_service.cc
index d415f6d9..a970f873 100644
--- a/chrome/browser/safe_browsing/chrome_password_protection_service.cc
+++ b/chrome/browser/safe_browsing/chrome_password_protection_service.cc
@@ -37,16 +37,16 @@
 #include "components/prefs/pref_change_registrar.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/common/utils.h"
-#include "components/safe_browsing/db/database_manager.h"
-#include "components/safe_browsing/features.h"
-#include "components/safe_browsing/password_protection/password_protection_navigation_throttle.h"
-#include "components/safe_browsing/password_protection/password_protection_request.h"
-#include "components/safe_browsing/proto/csd.pb.h"
-#include "components/safe_browsing/triggers/trigger_throttler.h"
-#include "components/safe_browsing/verdict_cache_manager.h"
-#include "components/safe_browsing/web_ui/safe_browsing_ui.h"
+#include "components/safe_browsing/content/password_protection/password_protection_navigation_throttle.h"
+#include "components/safe_browsing/content/password_protection/password_protection_request.h"
+#include "components/safe_browsing/content/web_ui/safe_browsing_ui.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/utils.h"
+#include "components/safe_browsing/core/db/database_manager.h"
+#include "components/safe_browsing/core/features.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
+#include "components/safe_browsing/core/triggers/trigger_throttler.h"
+#include "components/safe_browsing/core/verdict_cache_manager.h"
 #include "components/signin/public/identity_manager/account_info.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "components/strings/grit/components_strings.h"
diff --git a/chrome/browser/safe_browsing/chrome_password_protection_service.h b/chrome/browser/safe_browsing/chrome_password_protection_service.h
index 092cdec2..04f8beb 100644
--- a/chrome/browser/safe_browsing/chrome_password_protection_service.h
+++ b/chrome/browser/safe_browsing/chrome_password_protection_service.h
@@ -20,8 +20,8 @@
 #include "components/password_manager/core/browser/password_store.h"
 #include "components/password_manager/core/common/password_manager_pref_names.h"
 #include "components/safe_browsing/buildflags.h"
-#include "components/safe_browsing/password_protection/password_protection_service.h"
-#include "components/safe_browsing/triggers/trigger_manager.h"
+#include "components/safe_browsing/content/password_protection/password_protection_service.h"
+#include "components/safe_browsing/core/triggers/trigger_manager.h"
 #include "components/sessions/core/session_id.h"
 #include "components/sync/protocol/gaia_password_reuse.pb.h"
 #include "components/sync/protocol/user_event_specifics.pb.h"
diff --git a/chrome/browser/safe_browsing/chrome_password_protection_service_browsertest.cc b/chrome/browser/safe_browsing/chrome_password_protection_service_browsertest.cc
index 197467b6..5631ff8c 100644
--- a/chrome/browser/safe_browsing/chrome_password_protection_service_browsertest.cc
+++ b/chrome/browser/safe_browsing/chrome_password_protection_service_browsertest.cc
@@ -25,10 +25,10 @@
 #include "components/password_manager/core/common/password_manager_pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/features.h"
-#include "components/safe_browsing/password_protection/metrics_util.h"
-#include "components/safe_browsing/password_protection/password_protection_request.h"
+#include "components/safe_browsing/content/password_protection/metrics_util.h"
+#include "components/safe_browsing/content/password_protection/password_protection_request.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/features.h"
 #include "components/security_state/core/security_state.h"
 #include "components/signin/public/identity_manager/account_info.h"
 #include "components/signin/public/identity_manager/identity_test_environment.h"
diff --git a/chrome/browser/safe_browsing/chrome_password_protection_service_sync_browsertest.cc b/chrome/browser/safe_browsing/chrome_password_protection_service_sync_browsertest.cc
index 59029a52..02cd062 100644
--- a/chrome/browser/safe_browsing/chrome_password_protection_service_sync_browsertest.cc
+++ b/chrome/browser/safe_browsing/chrome_password_protection_service_sync_browsertest.cc
@@ -27,8 +27,8 @@
 #include "components/password_manager/core/common/password_manager_pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/password_protection/password_protection_request.h"
+#include "components/safe_browsing/content/password_protection/password_protection_request.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/security_state/core/security_state.h"
 #include "components/signin/public/base/signin_pref_names.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
diff --git a/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc b/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc
index 0632655..5ff5fb7 100644
--- a/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc
+++ b/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc
@@ -32,12 +32,12 @@
 #include "components/password_manager/core/browser/password_manager_test_utils.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
-#include "components/safe_browsing/common/utils.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
-#include "components/safe_browsing/features.h"
-#include "components/safe_browsing/password_protection/password_protection_navigation_throttle.h"
-#include "components/safe_browsing/password_protection/password_protection_request.h"
-#include "components/safe_browsing/verdict_cache_manager.h"
+#include "components/safe_browsing/content/password_protection/password_protection_navigation_throttle.h"
+#include "components/safe_browsing/content/password_protection/password_protection_request.h"
+#include "components/safe_browsing/core/common/utils.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/core/features.h"
+#include "components/safe_browsing/core/verdict_cache_manager.h"
 #include "components/signin/public/identity_manager/account_info.h"
 #include "components/strings/grit/components_strings.h"
 #include "components/sync/model/model_type_controller_delegate.h"
diff --git a/chrome/browser/safe_browsing/client_side_detection_host.cc b/chrome/browser/safe_browsing/client_side_detection_host.cc
index b20d070f..baebb7f 100644
--- a/chrome/browser/safe_browsing/client_side_detection_host.cc
+++ b/chrome/browser/safe_browsing/client_side_detection_host.cc
@@ -23,12 +23,12 @@
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/common/safe_browsing.mojom-shared.h"
-#include "components/safe_browsing/common/safe_browsing.mojom.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/db/allowlist_checker_client.h"
-#include "components/safe_browsing/db/database_manager.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/common/safe_browsing.mojom-shared.h"
+#include "components/safe_browsing/core/common/safe_browsing.mojom.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/db/allowlist_checker_client.h"
+#include "components/safe_browsing/core/db/database_manager.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_controller.h"
diff --git a/chrome/browser/safe_browsing/client_side_detection_host.h b/chrome/browser/safe_browsing/client_side_detection_host.h
index fe007adb..1da3b53 100644
--- a/chrome/browser/safe_browsing/client_side_detection_host.h
+++ b/chrome/browser/safe_browsing/client_side_detection_host.h
@@ -15,9 +15,9 @@
 #include "base/memory/ref_counted.h"
 #include "chrome/browser/safe_browsing/browser_feature_extractor.h"
 #include "chrome/browser/safe_browsing/ui_manager.h"
-#include "components/safe_browsing/common/safe_browsing.mojom-shared.h"
-#include "components/safe_browsing/common/safe_browsing.mojom.h"
-#include "components/safe_browsing/db/database_manager.h"
+#include "components/safe_browsing/core/common/safe_browsing.mojom-shared.h"
+#include "components/safe_browsing/core/common/safe_browsing.mojom.h"
+#include "components/safe_browsing/core/db/database_manager.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
diff --git a/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc b/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc
index adb8de27..3f083a7 100644
--- a/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc
+++ b/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc
@@ -26,10 +26,10 @@
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/prefs/scoped_user_pref_update.h"
-#include "components/safe_browsing/common/safe_browsing.mojom-shared.h"
-#include "components/safe_browsing/db/database_manager.h"
-#include "components/safe_browsing/db/test_database_manager.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/common/safe_browsing.mojom-shared.h"
+#include "components/safe_browsing/core/db/database_manager.h"
+#include "components/safe_browsing/core/db/test_database_manager.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/render_frame_host.h"
diff --git a/chrome/browser/safe_browsing/client_side_detection_service.cc b/chrome/browser/safe_browsing/client_side_detection_service.cc
index 14d5a42f..8c5e3b68 100644
--- a/chrome/browser/safe_browsing/client_side_detection_service.cc
+++ b/chrome/browser/safe_browsing/client_side_detection_service.cc
@@ -24,10 +24,10 @@
 #include "chrome/common/pref_names.h"
 #include "chrome/common/safe_browsing/client_model.pb.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/common/safe_browsing.mojom.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/common/utils.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/common/safe_browsing.mojom.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/utils.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_types.h"
diff --git a/chrome/browser/safe_browsing/client_side_detection_service_unittest.cc b/chrome/browser/safe_browsing/client_side_detection_service_unittest.cc
index 82bf6e0..9238110c 100644
--- a/chrome/browser/safe_browsing/client_side_detection_service_unittest.cc
+++ b/chrome/browser/safe_browsing/client_side_detection_service_unittest.cc
@@ -20,7 +20,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/time/time.h"
 #include "chrome/common/safe_browsing/client_model.pb.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "components/variations/variations_associated_data.h"
 #include "content/public/test/browser_task_environment.h"
 #include "crypto/sha2.h"
diff --git a/chrome/browser/safe_browsing/client_side_model_loader.cc b/chrome/browser/safe_browsing/client_side_model_loader.cc
index cca0f7c..c024043a 100644
--- a/chrome/browser/safe_browsing/client_side_model_loader.cc
+++ b/chrome/browser/safe_browsing/client_side_model_loader.cc
@@ -16,8 +16,8 @@
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/time/time.h"
 #include "chrome/common/safe_browsing/client_model.pb.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "components/variations/variations_associated_data.h"
 #include "net/base/load_flags.h"
 #include "net/http/http_response_headers.h"
diff --git a/chrome/browser/safe_browsing/client_side_model_loader_unittest.cc b/chrome/browser/safe_browsing/client_side_model_loader_unittest.cc
index 455845d..daac24c 100644
--- a/chrome/browser/safe_browsing/client_side_model_loader_unittest.cc
+++ b/chrome/browser/safe_browsing/client_side_model_loader_unittest.cc
@@ -18,7 +18,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/time/time.h"
 #include "chrome/common/safe_browsing/client_model.pb.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "components/variations/variations_associated_data.h"
 #include "content/public/test/browser_task_environment.h"
 #include "net/url_request/url_request_status.h"
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/binary_fcm_service.cc b/chrome/browser/safe_browsing/cloud_content_scanning/binary_fcm_service.cc
index 0773720..913673bc 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/binary_fcm_service.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/binary_fcm_service.cc
@@ -17,7 +17,7 @@
 #include "components/gcm_driver/instance_id/instance_id.h"
 #include "components/gcm_driver/instance_id/instance_id_driver.h"
 #include "components/gcm_driver/instance_id/instance_id_profile_service.h"
-#include "components/safe_browsing/proto/webprotect.pb.h"
+#include "components/safe_browsing/core/proto/webprotect.pb.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/binary_fcm_service.h b/chrome/browser/safe_browsing/cloud_content_scanning/binary_fcm_service.h
index 46ef4e2..e538a868 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/binary_fcm_service.h
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/binary_fcm_service.h
@@ -8,7 +8,7 @@
 #include "base/memory/weak_ptr.h"
 #include "components/gcm_driver/gcm_app_handler.h"
 #include "components/gcm_driver/instance_id/instance_id.h"
-#include "components/safe_browsing/proto/webprotect.pb.h"
+#include "components/safe_browsing/core/proto/webprotect.pb.h"
 
 class Profile;
 
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/binary_fcm_service_unittest.cc b/chrome/browser/safe_browsing/cloud_content_scanning/binary_fcm_service_unittest.cc
index e74990f..4b87eeb 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/binary_fcm_service_unittest.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/binary_fcm_service_unittest.cc
@@ -12,7 +12,7 @@
 #include "chrome/test/base/testing_profile.h"
 #include "components/gcm_driver/common/gcm_message.h"
 #include "components/gcm_driver/fake_gcm_profile_service.h"
-#include "components/safe_browsing/proto/webprotect.pb.h"
+#include "components/safe_browsing/core/proto/webprotect.pb.h"
 #include "content/public/test/browser_task_environment.h"
 #include "content/public/test/test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc b/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc
index d7a7c831..bc86e96 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc
@@ -23,8 +23,8 @@
 #include "chrome/browser/safe_browsing/cloud_content_scanning/multipart_uploader.h"
 #include "chrome/browser/safe_browsing/dm_token_utils.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/proto/webprotect.pb.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/proto/webprotect.pb.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "net/http/http_status_code.h"
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h b/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h
index c49c9d4..8428846 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h
@@ -19,7 +19,7 @@
 #include "base/timer/timer.h"
 #include "chrome/browser/safe_browsing/cloud_content_scanning/binary_fcm_service.h"
 #include "chrome/browser/safe_browsing/cloud_content_scanning/multipart_uploader.h"
-#include "components/safe_browsing/proto/webprotect.pb.h"
+#include "components/safe_browsing/core/proto/webprotect.pb.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
 class Profile;
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service_unittest.cc b/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service_unittest.cc
index 2aa39e8..57fdb2e 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service_unittest.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service_unittest.cc
@@ -12,7 +12,7 @@
 #include "base/time/time.h"
 #include "chrome/browser/safe_browsing/cloud_content_scanning/binary_fcm_service.h"
 #include "chrome/browser/safe_browsing/cloud_content_scanning/multipart_uploader.h"
-#include "components/safe_browsing/proto/webprotect.pb.h"
+#include "components/safe_browsing/core/proto/webprotect.pb.h"
 #include "content/public/test/browser_task_environment.h"
 #include "content/public/test/test_utils.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.cc b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.cc
index b357e1b..12e3c4a 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.cc
@@ -28,9 +28,9 @@
 #include "components/policy/core/browser/url_blacklist_manager.h"
 #include "components/policy/core/browser/url_util.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/features.h"
-#include "components/safe_browsing/proto/webprotect.pb.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/features.h"
+#include "components/safe_browsing/core/proto/webprotect.pb.h"
 #include "components/url_matcher/url_matcher.h"
 #include "content/public/browser/web_contents.h"
 #include "crypto/sha2.h"
@@ -166,24 +166,24 @@
 // Keep sorted for efficient access.
 constexpr const std::array<const base::FilePath::CharType*, 36>
     kSupportedDLPFileTypes = {
-        FILE_PATH_LITERAL(".bzip"),    FILE_PATH_LITERAL(".cab"),
-        FILE_PATH_LITERAL(".doc"),     FILE_PATH_LITERAL(".docx"),
-        FILE_PATH_LITERAL(".eps"),     FILE_PATH_LITERAL(".gzip"),
-        FILE_PATH_LITERAL(".hwp"),     FILE_PATH_LITERAL(".img_for_ocr"),
-        FILE_PATH_LITERAL(".kml"),     FILE_PATH_LITERAL(".kmz"),
-        FILE_PATH_LITERAL(".odp"),     FILE_PATH_LITERAL(".ods"),
-        FILE_PATH_LITERAL(".odt"),     FILE_PATH_LITERAL(".pdf"),
-        FILE_PATH_LITERAL(".ppt"),     FILE_PATH_LITERAL(".pptx"),
-        FILE_PATH_LITERAL(".ps"),      FILE_PATH_LITERAL(".rar"),
-        FILE_PATH_LITERAL(".rtf"),     FILE_PATH_LITERAL(".sdc"),
-        FILE_PATH_LITERAL(".sdd"),     FILE_PATH_LITERAL(".sdw"),
-        FILE_PATH_LITERAL(".seven_z"), FILE_PATH_LITERAL(".sxc"),
-        FILE_PATH_LITERAL(".sxi"),     FILE_PATH_LITERAL(".sxw"),
-        FILE_PATH_LITERAL(".tar"),     FILE_PATH_LITERAL(".ttf"),
-        FILE_PATH_LITERAL(".txt"),     FILE_PATH_LITERAL(".wml"),
-        FILE_PATH_LITERAL(".wpd"),     FILE_PATH_LITERAL(".xls"),
-        FILE_PATH_LITERAL(".xlsx"),    FILE_PATH_LITERAL(".xml"),
-        FILE_PATH_LITERAL(".xps"),     FILE_PATH_LITERAL(".zip")};
+        FILE_PATH_LITERAL(".7z"),          FILE_PATH_LITERAL(".bzip"),
+        FILE_PATH_LITERAL(".cab"),         FILE_PATH_LITERAL(".doc"),
+        FILE_PATH_LITERAL(".docx"),        FILE_PATH_LITERAL(".eps"),
+        FILE_PATH_LITERAL(".gzip"),        FILE_PATH_LITERAL(".hwp"),
+        FILE_PATH_LITERAL(".img_for_ocr"), FILE_PATH_LITERAL(".kml"),
+        FILE_PATH_LITERAL(".kmz"),         FILE_PATH_LITERAL(".odp"),
+        FILE_PATH_LITERAL(".ods"),         FILE_PATH_LITERAL(".odt"),
+        FILE_PATH_LITERAL(".pdf"),         FILE_PATH_LITERAL(".ppt"),
+        FILE_PATH_LITERAL(".pptx"),        FILE_PATH_LITERAL(".ps"),
+        FILE_PATH_LITERAL(".rar"),         FILE_PATH_LITERAL(".rtf"),
+        FILE_PATH_LITERAL(".sdc"),         FILE_PATH_LITERAL(".sdd"),
+        FILE_PATH_LITERAL(".sdw"),         FILE_PATH_LITERAL(".sxc"),
+        FILE_PATH_LITERAL(".sxi"),         FILE_PATH_LITERAL(".sxw"),
+        FILE_PATH_LITERAL(".tar"),         FILE_PATH_LITERAL(".ttf"),
+        FILE_PATH_LITERAL(".txt"),         FILE_PATH_LITERAL(".wml"),
+        FILE_PATH_LITERAL(".wpd"),         FILE_PATH_LITERAL(".xls"),
+        FILE_PATH_LITERAL(".xlsx"),        FILE_PATH_LITERAL(".xml"),
+        FILE_PATH_LITERAL(".xps"),         FILE_PATH_LITERAL(".zip")};
 
 }  // namespace
 
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.h b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.h
index 9393714..a435bbeb 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.h
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.h
@@ -21,7 +21,7 @@
 #include "chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.h"
 #include "chrome/browser/ui/tab_modal_confirm_dialog.h"
 #include "chrome/browser/ui/tab_modal_confirm_dialog_delegate.h"
-#include "components/safe_browsing/proto/webprotect.pb.h"
+#include "components/safe_browsing/core/proto/webprotect.pb.h"
 #include "content/public/browser/web_contents_view_delegate.h"
 #include "url/gurl.h"
 
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate_unittest.cc b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate_unittest.cc
index abe885d5..9091458 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate_unittest.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate_unittest.cc
@@ -20,8 +20,8 @@
 #include "chrome/test/base/testing_profile_manager.h"
 #include "components/prefs/scoped_user_pref_update.h"
 #include "components/prefs/testing_pref_service.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/features.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/features.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -1149,7 +1149,7 @@
   data.paths.emplace_back(FILE_PATH_LITERAL("/tmp/foo.sdc"));
   data.paths.emplace_back(FILE_PATH_LITERAL("/tmp/foo.sdd"));
   data.paths.emplace_back(FILE_PATH_LITERAL("/tmp/foo.sdw"));
-  data.paths.emplace_back(FILE_PATH_LITERAL("/tmp/foo.seven_z"));
+  data.paths.emplace_back(FILE_PATH_LITERAL("/tmp/foo.7z"));
   data.paths.emplace_back(FILE_PATH_LITERAL("/tmp/foo.sxc"));
   data.paths.emplace_back(FILE_PATH_LITERAL("/tmp/foo.sxi"));
   data.paths.emplace_back(FILE_PATH_LITERAL("/tmp/foo.sxw"));
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/fake_deep_scanning_dialog_delegate.h b/chrome/browser/safe_browsing/cloud_content_scanning/fake_deep_scanning_dialog_delegate.h
index 4256bfc..f1c2fb4 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/fake_deep_scanning_dialog_delegate.h
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/fake_deep_scanning_dialog_delegate.h
@@ -11,7 +11,7 @@
 #include "base/files/file_path.h"
 #include "chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h"
 #include "chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.h"
-#include "components/safe_browsing/proto/webprotect.pb.h"
+#include "components/safe_browsing/core/proto/webprotect.pb.h"
 
 namespace content {
 class WebContents;
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/test_binary_upload_service.h b/chrome/browser/safe_browsing/cloud_content_scanning/test_binary_upload_service.h
index d24e6a999..716b681 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/test_binary_upload_service.h
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/test_binary_upload_service.h
@@ -9,7 +9,7 @@
 
 #include "chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h"
 #include "chrome/browser/safe_browsing/services_delegate.h"
-#include "components/safe_browsing/proto/webprotect.pb.h"
+#include "components/safe_browsing/core/proto/webprotect.pb.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc b/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc
index 04e3b41..c8906f6 100644
--- a/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc
+++ b/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc
@@ -35,11 +35,11 @@
 #include "components/policy/core/browser/url_util.h"
 #include "components/policy/core/common/policy_pref_names.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/common/utils.h"
-#include "components/safe_browsing/features.h"
-#include "components/safe_browsing/proto/csd.pb.h"
-#include "components/safe_browsing/proto/webprotect.pb.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/utils.h"
+#include "components/safe_browsing/core/features.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/webprotect.pb.h"
 #include "components/url_matcher/url_matcher.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/download_item_utils.h"
diff --git a/chrome/browser/safe_browsing/download_protection/check_client_download_request.h b/chrome/browser/safe_browsing/download_protection/check_client_download_request.h
index 952b473..44848af 100644
--- a/chrome/browser/safe_browsing/download_protection/check_client_download_request.h
+++ b/chrome/browser/safe_browsing/download_protection/check_client_download_request.h
@@ -20,7 +20,7 @@
 #include "chrome/browser/safe_browsing/download_protection/check_client_download_request_base.h"
 #include "chrome/browser/safe_browsing/download_protection/download_protection_util.h"
 #include "components/download/public/common/download_item.h"
-#include "components/safe_browsing/proto/webprotect.pb.h"
+#include "components/safe_browsing/core/proto/webprotect.pb.h"
 #include "content/public/browser/browser_thread.h"
 #include "url/gurl.h"
 
diff --git a/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.cc b/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.cc
index fb08a8b..2d7fdf9 100644
--- a/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.cc
+++ b/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.cc
@@ -24,9 +24,9 @@
 #include "chrome/browser/safe_browsing/download_protection/ppapi_download_request.h"
 #include "chrome/common/safe_browsing/file_type_policies.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/common/utils.h"
-#include "components/safe_browsing/web_ui/safe_browsing_ui.h"
+#include "components/safe_browsing/content/web_ui/safe_browsing_ui.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/utils.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
diff --git a/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.h b/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.h
index eb1ffb8..c501e8a 100644
--- a/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.h
+++ b/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.h
@@ -25,7 +25,7 @@
 #include "chrome/services/file_util/public/cpp/sandboxed_rar_analyzer.h"
 #include "chrome/services/file_util/public/cpp/sandboxed_zip_analyzer.h"
 #include "components/history/core/browser/history_service.h"
-#include "components/safe_browsing/db/database_manager.h"
+#include "components/safe_browsing/core/db/database_manager.h"
 #include "content/public/browser/browser_thread.h"
 #include "url/gurl.h"
 
diff --git a/chrome/browser/safe_browsing/download_protection/check_native_file_system_write_request.cc b/chrome/browser/safe_browsing/download_protection/check_native_file_system_write_request.cc
index a1f3c44..4136a88b 100644
--- a/chrome/browser/safe_browsing/download_protection/check_native_file_system_write_request.cc
+++ b/chrome/browser/safe_browsing/download_protection/check_native_file_system_write_request.cc
@@ -17,9 +17,9 @@
 #include "chrome/browser/sessions/session_tab_helper.h"
 #include "chrome/common/safe_browsing/download_type_util.h"
 #include "chrome/common/safe_browsing/file_type_policies.h"
-#include "components/safe_browsing/common/utils.h"
-#include "components/safe_browsing/features.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/common/utils.h"
+#include "components/safe_browsing/core/features.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/download_item_utils.h"
 #include "content/public/browser/navigation_entry.h"
diff --git a/chrome/browser/safe_browsing/download_protection/download_feedback.cc b/chrome/browser/safe_browsing/download_protection/download_feedback.cc
index 7d8a7b060..ce9c1cbe 100644
--- a/chrome/browser/safe_browsing/download_protection/download_feedback.cc
+++ b/chrome/browser/safe_browsing/download_protection/download_feedback.cc
@@ -12,7 +12,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/task_runner.h"
 #include "chrome/browser/safe_browsing/download_protection/two_phase_uploader.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "content/public/browser/browser_thread.h"
 #include "net/base/net_errors.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
diff --git a/chrome/browser/safe_browsing/download_protection/download_feedback_unittest.cc b/chrome/browser/safe_browsing/download_protection/download_feedback_unittest.cc
index 7b32495..3f47fb4 100644
--- a/chrome/browser/safe_browsing/download_protection/download_feedback_unittest.cc
+++ b/chrome/browser/safe_browsing/download_protection/download_feedback_unittest.cc
@@ -15,7 +15,7 @@
 #include "base/single_thread_task_runner.h"
 #include "base/task/post_task.h"
 #include "chrome/browser/safe_browsing/download_protection/two_phase_uploader.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/test/browser_task_environment.h"
diff --git a/chrome/browser/safe_browsing/download_protection/download_protection_service.cc b/chrome/browser/safe_browsing/download_protection/download_protection_service.cc
index 475b5ec..d15df60 100644
--- a/chrome/browser/safe_browsing/download_protection/download_protection_service.cc
+++ b/chrome/browser/safe_browsing/download_protection/download_protection_service.cc
@@ -27,7 +27,7 @@
 #include "chrome/common/safe_browsing/binary_feature_extractor.h"
 #include "chrome/common/url_constants.h"
 #include "components/google/core/common/google_util.h"
-#include "components/safe_browsing/common/safebrowsing_switches.h"
+#include "components/safe_browsing/core/common/safebrowsing_switches.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
diff --git a/chrome/browser/safe_browsing/download_protection/download_protection_service.h b/chrome/browser/safe_browsing/download_protection/download_protection_service.h
index b3cc83b..5070c35 100644
--- a/chrome/browser/safe_browsing/download_protection/download_protection_service.h
+++ b/chrome/browser/safe_browsing/download_protection/download_protection_service.h
@@ -30,7 +30,7 @@
 #include "chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.h"
 #include "chrome/browser/safe_browsing/services_delegate.h"
 #include "chrome/browser/safe_browsing/ui_manager.h"
-#include "components/safe_browsing/db/database_manager.h"
+#include "components/safe_browsing/core/db/database_manager.h"
 #include "components/sessions/core/session_id.h"
 #include "url/gurl.h"
 
diff --git a/chrome/browser/safe_browsing/download_protection/download_protection_service_browsertest.cc b/chrome/browser/safe_browsing/download_protection/download_protection_service_browsertest.cc
index 3ed9f1e4..80f2b25 100644
--- a/chrome/browser/safe_browsing/download_protection/download_protection_service_browsertest.cc
+++ b/chrome/browser/safe_browsing/download_protection/download_protection_service_browsertest.cc
@@ -11,8 +11,8 @@
 #include "chrome/common/chrome_paths.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
-#include "components/safe_browsing/proto/csd.pb.h"
-#include "components/safe_browsing/web_ui/safe_browsing_ui.h"
+#include "components/safe_browsing/content/web_ui/safe_browsing_ui.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/download_manager.h"
 #include "content/public/browser/site_instance.h"
diff --git a/chrome/browser/safe_browsing/download_protection/download_protection_service_unittest.cc b/chrome/browser/safe_browsing/download_protection/download_protection_service_unittest.cc
index bb8871db3..e824a1a 100644
--- a/chrome/browser/safe_browsing/download_protection/download_protection_service_unittest.cc
+++ b/chrome/browser/safe_browsing/download_protection/download_protection_service_unittest.cc
@@ -66,14 +66,14 @@
 #include "components/password_manager/core/browser/test_password_store.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/common/safebrowsing_switches.h"
-#include "components/safe_browsing/db/database_manager.h"
-#include "components/safe_browsing/db/test_database_manager.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
-#include "components/safe_browsing/features.h"
-#include "components/safe_browsing/proto/csd.pb.h"
-#include "components/safe_browsing/proto/webprotect.pb.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safebrowsing_switches.h"
+#include "components/safe_browsing/core/db/database_manager.h"
+#include "components/safe_browsing/core/db/test_database_manager.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/core/features.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/webprotect.pb.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/download_item_utils.h"
 #include "content/public/browser/page_navigator.h"
diff --git a/chrome/browser/safe_browsing/download_protection/download_protection_util.h b/chrome/browser/safe_browsing/download_protection/download_protection_util.h
index d8bf67ea..de4be79d 100644
--- a/chrome/browser/safe_browsing/download_protection/download_protection_util.h
+++ b/chrome/browser/safe_browsing/download_protection/download_protection_util.h
@@ -9,7 +9,7 @@
 
 #include "base/callback_list.h"
 #include "components/download/public/common/download_item.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "net/cert/x509_certificate.h"
 
 namespace safe_browsing {
diff --git a/chrome/browser/safe_browsing/download_protection/download_url_sb_client.h b/chrome/browser/safe_browsing/download_protection/download_url_sb_client.h
index 2f7b385..60fd5c9 100644
--- a/chrome/browser/safe_browsing/download_protection/download_url_sb_client.h
+++ b/chrome/browser/safe_browsing/download_protection/download_url_sb_client.h
@@ -11,7 +11,7 @@
 #include "base/scoped_observer.h"
 #include "chrome/browser/safe_browsing/download_protection/download_protection_util.h"
 #include "components/download/public/common/download_item.h"
-#include "components/safe_browsing/db/database_manager.h"
+#include "components/safe_browsing/core/db/database_manager.h"
 #include "content/public/browser/browser_thread.h"
 
 using content::BrowserThread;
diff --git a/chrome/browser/safe_browsing/download_protection/file_analyzer.cc b/chrome/browser/safe_browsing/download_protection/file_analyzer.cc
index 92ed54bbc..f224036 100644
--- a/chrome/browser/safe_browsing/download_protection/file_analyzer.cc
+++ b/chrome/browser/safe_browsing/download_protection/file_analyzer.cc
@@ -14,7 +14,7 @@
 #include "chrome/common/safe_browsing/archive_analyzer_results.h"
 #include "chrome/common/safe_browsing/download_type_util.h"
 #include "chrome/common/safe_browsing/file_type_policies.h"
-#include "components/safe_browsing/features.h"
+#include "components/safe_browsing/core/features.h"
 #include "content/public/browser/browser_thread.h"
 
 namespace safe_browsing {
diff --git a/chrome/browser/safe_browsing/download_protection/file_analyzer.h b/chrome/browser/safe_browsing/download_protection/file_analyzer.h
index c66100b4..d8c6f1ce 100644
--- a/chrome/browser/safe_browsing/download_protection/file_analyzer.h
+++ b/chrome/browser/safe_browsing/download_protection/file_analyzer.h
@@ -13,7 +13,7 @@
 #include "chrome/common/safe_browsing/binary_feature_extractor.h"
 #include "chrome/services/file_util/public/cpp/sandboxed_rar_analyzer.h"
 #include "chrome/services/file_util/public/cpp/sandboxed_zip_analyzer.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "third_party/protobuf/src/google/protobuf/repeated_field.h"
 
 #if defined(OS_MACOSX)
diff --git a/chrome/browser/safe_browsing/download_protection/file_analyzer_unittest.cc b/chrome/browser/safe_browsing/download_protection/file_analyzer_unittest.cc
index f52e1482..659e767 100644
--- a/chrome/browser/safe_browsing/download_protection/file_analyzer_unittest.cc
+++ b/chrome/browser/safe_browsing/download_protection/file_analyzer_unittest.cc
@@ -14,7 +14,7 @@
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/safe_browsing/file_type_policies_test_util.h"
 #include "chrome/common/safe_browsing/mock_binary_feature_extractor.h"
-#include "components/safe_browsing/features.h"
+#include "components/safe_browsing/core/features.h"
 #include "content/public/test/browser_task_environment.h"
 #include "content/public/test/test_utils.h"
 #include "testing/gmock/include/gmock/gmock.h"
diff --git a/chrome/browser/safe_browsing/download_protection/ppapi_download_request.cc b/chrome/browser/safe_browsing/download_protection/ppapi_download_request.cc
index 8076255..80d23c1 100644
--- a/chrome/browser/safe_browsing/download_protection/ppapi_download_request.cc
+++ b/chrome/browser/safe_browsing/download_protection/ppapi_download_request.cc
@@ -17,8 +17,8 @@
 #include "chrome/browser/safe_browsing/download_protection/download_protection_util.h"
 #include "chrome/browser/sessions/session_tab_helper.h"
 #include "chrome/common/safe_browsing/file_type_policies.h"
-#include "components/safe_browsing/common/utils.h"
-#include "components/safe_browsing/db/database_manager.h"
+#include "components/safe_browsing/core/common/utils.h"
+#include "components/safe_browsing/core/db/database_manager.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/web_contents.h"
diff --git a/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer.cc b/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer.cc
index 20075863..4444b505 100644
--- a/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer.cc
@@ -19,7 +19,7 @@
 #include "chrome/browser/safe_browsing/incident_reporting/binary_integrity_incident.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident_receiver.h"
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_mac.cc b/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_mac.cc
index 66d7528..bd8b4d9 100644
--- a/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_mac.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_mac.cc
@@ -14,7 +14,7 @@
 #include "chrome/browser/safe_browsing/incident_reporting/binary_integrity_incident.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident_receiver.h"
 #include "chrome/browser/safe_browsing/signature_evaluator_mac.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_mac_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_mac_unittest.cc
index c134711..58076f3 100644
--- a/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_mac_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_mac_unittest.cc
@@ -16,7 +16,7 @@
 #include "chrome/browser/safe_browsing/incident_reporting/incident.h"
 #include "chrome/browser/safe_browsing/incident_reporting/mock_incident_receiver.h"
 #include "chrome/common/chrome_paths.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_win.cc b/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_win.cc
index 6c8b632..32a8990 100644
--- a/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_win.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_win.cc
@@ -18,7 +18,7 @@
 #include "chrome/browser/safe_browsing/incident_reporting/incident_receiver.h"
 #include "chrome/common/chrome_version.h"
 #include "chrome/common/safe_browsing/binary_feature_extractor.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_win_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_win_unittest.cc
index 7824dd1..b4f89df 100644
--- a/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_win_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_win_unittest.cc
@@ -16,7 +16,7 @@
 #include "chrome/browser/safe_browsing/incident_reporting/mock_incident_receiver.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_version.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/binary_integrity_incident.cc b/chrome/browser/safe_browsing/incident_reporting/binary_integrity_incident.cc
index a20a8e4..d6f9b88 100644
--- a/chrome/browser/safe_browsing/incident_reporting/binary_integrity_incident.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/binary_integrity_incident.cc
@@ -6,7 +6,7 @@
 
 #include "base/logging.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident_handler_util.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/binary_integrity_incident_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/binary_integrity_incident_unittest.cc
index 6cd05754..d0b5aac7 100644
--- a/chrome/browser/safe_browsing/incident_reporting/binary_integrity_incident_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/binary_integrity_incident_unittest.cc
@@ -10,7 +10,7 @@
 #include <utility>
 
 #include "base/stl_util.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace safe_browsing {
diff --git a/chrome/browser/safe_browsing/incident_reporting/download_metadata_manager.cc b/chrome/browser/safe_browsing/incident_reporting/download_metadata_manager.cc
index 9d844b8..a21730c 100644
--- a/chrome/browser/safe_browsing/incident_reporting/download_metadata_manager.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/download_metadata_manager.cc
@@ -23,7 +23,7 @@
 #include "base/task/post_task.h"
 #include "base/task/task_traits.h"
 #include "components/download/public/common/download_item.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/download_item_utils.h"
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/download_metadata_manager_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/download_metadata_manager_unittest.cc
index 6600c9a..9131a731 100644
--- a/chrome/browser/safe_browsing/incident_reporting/download_metadata_manager_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/download_metadata_manager_unittest.cc
@@ -17,7 +17,7 @@
 #include "base/sequenced_task_runner.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/download/public/common/mock_download_item.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/download_item_utils.h"
 #include "content/public/browser/download_manager.h"
diff --git a/chrome/browser/safe_browsing/incident_reporting/environment_data_collection.cc b/chrome/browser/safe_browsing/incident_reporting/environment_data_collection.cc
index a994a9c..86d05ff7 100644
--- a/chrome/browser/safe_browsing/incident_reporting/environment_data_collection.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/environment_data_collection.cc
@@ -11,7 +11,7 @@
 #include "base/threading/scoped_blocking_call.h"
 #include "build/build_config.h"
 #include "chrome/common/channel_info.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "components/version_info/version_info.h"
 
 namespace safe_browsing {
diff --git a/chrome/browser/safe_browsing/incident_reporting/environment_data_collection_win.cc b/chrome/browser/safe_browsing/incident_reporting/environment_data_collection_win.cc
index 136f0ed..f5264adc 100644
--- a/chrome/browser/safe_browsing/incident_reporting/environment_data_collection_win.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/environment_data_collection_win.cc
@@ -25,7 +25,7 @@
 #include "chrome/browser/safe_browsing/download_protection/path_sanitizer.h"
 #include "chrome/browser/safe_browsing/incident_reporting/module_integrity_verifier_win.h"
 #include "chrome/common/safe_browsing/binary_feature_extractor.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "components/variations/variations_associated_data.h"
 
 namespace safe_browsing {
diff --git a/chrome/browser/safe_browsing/incident_reporting/environment_data_collection_win_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/environment_data_collection_win_unittest.cc
index 4a1c979..a200f71 100644
--- a/chrome/browser/safe_browsing/incident_reporting/environment_data_collection_win_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/environment_data_collection_win_unittest.cc
@@ -20,7 +20,7 @@
 #include "chrome/browser/safe_browsing/download_protection/path_sanitizer.h"
 #include "chrome/browser/safe_browsing/incident_reporting/module_integrity_unittest_util_win.h"
 #include "chrome/browser/safe_browsing/incident_reporting/module_integrity_verifier_win.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "net/base/winsock_init.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/extension_data_collection.cc b/chrome/browser/safe_browsing/incident_reporting/extension_data_collection.cc
index c35c0f0..8f2b93fc 100644
--- a/chrome/browser/safe_browsing/incident_reporting/extension_data_collection.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/extension_data_collection.cc
@@ -12,7 +12,7 @@
 #include "chrome/browser/extensions/install_signer.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident_reporting_service.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_prefs_factory.h"
 #include "extensions/browser/extension_registry.h"
diff --git a/chrome/browser/safe_browsing/incident_reporting/extension_data_collection_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/extension_data_collection_unittest.cc
index 8480f4a..cc5edd5 100644
--- a/chrome/browser/safe_browsing/incident_reporting/extension_data_collection_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/extension_data_collection_unittest.cc
@@ -20,8 +20,8 @@
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "content/public/test/browser_task_environment.h"
 #include "content/public/test/test_utils.h"
diff --git a/chrome/browser/safe_browsing/incident_reporting/incident.cc b/chrome/browser/safe_browsing/incident_reporting/incident.cc
index 6c9bfc1..004b7a6 100644
--- a/chrome/browser/safe_browsing/incident_reporting/incident.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/incident.cc
@@ -8,7 +8,7 @@
 
 #include "base/logging.h"
 #include "base/time/time.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/incident_report_uploader_impl.cc b/chrome/browser/safe_browsing/incident_reporting/incident_report_uploader_impl.cc
index 9940414..7481ba1 100644
--- a/chrome/browser/safe_browsing/incident_reporting/incident_report_uploader_impl.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/incident_report_uploader_impl.cc
@@ -8,7 +8,7 @@
 
 #include "base/bind.h"
 #include "base/metrics/histogram_macros.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "google_apis/google_api_keys.h"
 #include "net/base/escape.h"
 #include "net/base/load_flags.h"
diff --git a/chrome/browser/safe_browsing/incident_reporting/incident_report_uploader_impl_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/incident_report_uploader_impl_unittest.cc
index 1c1d253..309bfafd 100644
--- a/chrome/browser/safe_browsing/incident_reporting/incident_report_uploader_impl_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/incident_report_uploader_impl_unittest.cc
@@ -10,7 +10,7 @@
 #include "base/bind.h"
 #include "base/test/test_simple_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "net/base/load_flags.h"
 #include "net/http/http_status_code.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
diff --git a/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service.cc b/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service.cc
index d4aac24..a3431e9 100644
--- a/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service.cc
@@ -38,8 +38,8 @@
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/download_item_utils.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
diff --git a/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service_unittest.cc
index bd3f715..35896ea 100644
--- a/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service_unittest.cc
@@ -33,8 +33,8 @@
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "content/public/test/browser_task_environment.h"
 #include "extensions/browser/quota_service.h"
diff --git a/chrome/browser/safe_browsing/incident_reporting/last_download_finder.cc b/chrome/browser/safe_browsing/incident_reporting/last_download_finder.cc
index 6749fd5..664ed34 100644
--- a/chrome/browser/safe_browsing/incident_reporting/last_download_finder.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/last_download_finder.cc
@@ -28,7 +28,7 @@
 #include "components/language/core/browser/pref_names.h"
 #include "components/language/core/common/locale_util.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "crypto/sha2.h"
 #include "extensions/buildflags/buildflags.h"
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/last_download_finder_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/last_download_finder_unittest.cc
index 95a27f5d..08aa37d 100644
--- a/chrome/browser/safe_browsing/incident_reporting/last_download_finder_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/last_download_finder_unittest.cc
@@ -42,8 +42,8 @@
 #include "components/history/core/browser/history_constants.h"
 #include "components/history/core/browser/history_database_params.h"
 #include "components/history/core/browser/history_service.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "content/public/test/browser_task_environment.h"
 #include "content/public/test/test_utils.h"
diff --git a/chrome/browser/safe_browsing/incident_reporting/module_integrity_verifier_win.cc b/chrome/browser/safe_browsing/incident_reporting/module_integrity_verifier_win.cc
index 5b4e11c..efa6986 100644
--- a/chrome/browser/safe_browsing/incident_reporting/module_integrity_verifier_win.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/module_integrity_verifier_win.cc
@@ -17,7 +17,7 @@
 #include "base/stl_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/win/pe_image.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/module_integrity_verifier_win_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/module_integrity_verifier_win_unittest.cc
index fdff5f9a..0596b2c5 100644
--- a/chrome/browser/safe_browsing/incident_reporting/module_integrity_verifier_win_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/module_integrity_verifier_win_unittest.cc
@@ -22,7 +22,7 @@
 #include "base/win/pe_image.h"
 #include "build/build_config.h"
 #include "chrome/browser/safe_browsing/incident_reporting/module_integrity_unittest_util_win.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace safe_browsing {
diff --git a/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate.cc b/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate.cc
index b6fbe66..1dd6052 100644
--- a/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate.cc
@@ -11,7 +11,7 @@
 #include "base/json/json_writer.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident_receiver.h"
 #include "chrome/browser/safe_browsing/incident_reporting/tracked_preference_incident.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "services/preferences/public/mojom/tracked_preference_validation_delegate.mojom.h"
 
 namespace safe_browsing {
diff --git a/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate_unittest.cc
index 1509f61..c4347ac 100644
--- a/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate_unittest.cc
@@ -17,7 +17,7 @@
 #include "base/values.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident.h"
 #include "chrome/browser/safe_browsing/incident_reporting/mock_incident_receiver.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/resource_request_detector.cc b/chrome/browser/safe_browsing/incident_reporting/resource_request_detector.cc
index 02266c30..e725f89 100644
--- a/chrome/browser/safe_browsing/incident_reporting/resource_request_detector.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/resource_request_detector.cc
@@ -11,7 +11,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident_receiver.h"
 #include "chrome/browser/safe_browsing/incident_reporting/resource_request_incident.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_frame_host.h"
diff --git a/chrome/browser/safe_browsing/incident_reporting/resource_request_detector.h b/chrome/browser/safe_browsing/incident_reporting/resource_request_detector.h
index 9a27556..947e4c8 100644
--- a/chrome/browser/safe_browsing/incident_reporting/resource_request_detector.h
+++ b/chrome/browser/safe_browsing/incident_reporting/resource_request_detector.h
@@ -10,7 +10,7 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident_receiver.h"
-#include "components/safe_browsing/db/database_manager.h"
+#include "components/safe_browsing/core/db/database_manager.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/resource_request_detector_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/resource_request_detector_unittest.cc
index 11d2b4a..4c19111 100644
--- a/chrome/browser/safe_browsing/incident_reporting/resource_request_detector_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/resource_request_detector_unittest.cc
@@ -12,8 +12,8 @@
 #include "chrome/browser/safe_browsing/incident_reporting/incident.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident_receiver.h"
 #include "chrome/browser/safe_browsing/incident_reporting/mock_incident_receiver.h"
-#include "components/safe_browsing/db/test_database_manager.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/db/test_database_manager.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "content/public/common/previews_state.h"
 #include "content/public/common/resource_type.h"
 #include "content/public/test/browser_task_environment.h"
diff --git a/chrome/browser/safe_browsing/incident_reporting/resource_request_incident.cc b/chrome/browser/safe_browsing/incident_reporting/resource_request_incident.cc
index a08df8f..b31a3f4d 100644
--- a/chrome/browser/safe_browsing/incident_reporting/resource_request_incident.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/resource_request_incident.cc
@@ -6,7 +6,7 @@
 
 #include "base/logging.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident_handler_util.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/state_store.cc b/chrome/browser/safe_browsing/incident_reporting/state_store.cc
index a18b1ec1..62466bc 100644
--- a/chrome/browser/safe_browsing/incident_reporting/state_store.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/state_store.cc
@@ -13,7 +13,7 @@
 #include "chrome/browser/safe_browsing/incident_reporting/incident.h"
 #include "chrome/browser/safe_browsing/incident_reporting/platform_state_store.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/state_store_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/state_store_unittest.cc
index 02cd335..560ea0f 100644
--- a/chrome/browser/safe_browsing/incident_reporting/state_store_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/state_store_unittest.cc
@@ -21,7 +21,7 @@
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
 #include "components/pref_registry/pref_registry_syncable.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/sync_preferences/pref_service_syncable.h"
 #include "components/sync_preferences/pref_service_syncable_factory.h"
 #include "content/public/test/browser_task_environment.h"
diff --git a/chrome/browser/safe_browsing/incident_reporting/tracked_preference_incident.cc b/chrome/browser/safe_browsing/incident_reporting/tracked_preference_incident.cc
index 63ad14c..1ff340e 100644
--- a/chrome/browser/safe_browsing/incident_reporting/tracked_preference_incident.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/tracked_preference_incident.cc
@@ -6,7 +6,7 @@
 
 #include "base/logging.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident_handler_util.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/tracked_preference_incident_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/tracked_preference_incident_unittest.cc
index f31aaf4..9de3dbe 100644
--- a/chrome/browser/safe_browsing/incident_reporting/tracked_preference_incident_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/tracked_preference_incident_unittest.cc
@@ -7,7 +7,7 @@
 #include <memory>
 #include <utility>
 
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace safe_browsing {
diff --git a/chrome/browser/safe_browsing/safe_browsing_blocking_page.cc b/chrome/browser/safe_browsing/safe_browsing_blocking_page.cc
index 803806e..0cbb9da 100644
--- a/chrome/browser/safe_browsing/safe_browsing_blocking_page.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_blocking_page.cc
@@ -20,10 +20,10 @@
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/browser/threat_details.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/features.h"
-#include "components/safe_browsing/triggers/trigger_manager.h"
+#include "components/safe_browsing/content/browser/threat_details.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/features.h"
+#include "components/safe_browsing/core/triggers/trigger_manager.h"
 #include "components/security_interstitials/content/security_interstitial_controller_client.h"
 #include "components/security_interstitials/core/controller_client.h"
 #include "content/public/browser/browser_thread.h"
diff --git a/chrome/browser/safe_browsing/safe_browsing_blocking_page.h b/chrome/browser/safe_browsing/safe_browsing_blocking_page.h
index de875ef6..7359bc8 100644
--- a/chrome/browser/safe_browsing/safe_browsing_blocking_page.h
+++ b/chrome/browser/safe_browsing/safe_browsing_blocking_page.h
@@ -34,8 +34,8 @@
 
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
-#include "components/safe_browsing/base_blocking_page.h"
-#include "components/safe_browsing/base_ui_manager.h"
+#include "components/safe_browsing/content/base_blocking_page.h"
+#include "components/safe_browsing/content/base_ui_manager.h"
 
 namespace network {
 class SharedURLLoaderFactory;
diff --git a/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc b/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc
index 12f09ac..a85d4aa 100644
--- a/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc
@@ -42,16 +42,16 @@
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/google/core/common/google_util.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/browser/threat_details.h"
-#include "components/safe_browsing/common/safe_browsing.mojom.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/db/database_manager.h"
-#include "components/safe_browsing/db/test_database_manager.h"
-#include "components/safe_browsing/db/util.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
-#include "components/safe_browsing/features.h"
-#include "components/safe_browsing/renderer/threat_dom_details.h"
-#include "components/safe_browsing/web_ui/constants.h"
+#include "components/safe_browsing/content/browser/threat_details.h"
+#include "components/safe_browsing/content/renderer/threat_dom_details.h"
+#include "components/safe_browsing/core/common/safe_browsing.mojom.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/db/database_manager.h"
+#include "components/safe_browsing/core/db/test_database_manager.h"
+#include "components/safe_browsing/core/db/util.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/core/features.h"
+#include "components/safe_browsing/core/web_ui/constants.h"
 #include "components/security_interstitials/content/security_interstitial_controller_client.h"
 #include "components/security_interstitials/content/security_interstitial_tab_helper.h"
 #include "components/security_interstitials/content/ssl_blocking_page.h"
diff --git a/chrome/browser/safe_browsing/safe_browsing_blocking_page_unittest.cc b/chrome/browser/safe_browsing/safe_browsing_blocking_page_unittest.cc
index f4db379e..3613228 100644
--- a/chrome/browser/safe_browsing/safe_browsing_blocking_page_unittest.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_blocking_page_unittest.cc
@@ -20,9 +20,9 @@
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/browser/threat_details.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/features.h"
+#include "components/safe_browsing/content/browser/threat_details.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/features.h"
 #include "components/security_interstitials/content/security_interstitial_controller_client.h"
 #include "components/security_interstitials/core/safe_browsing_quiet_error_ui.h"
 #include "components/strings/grit/components_strings.h"
diff --git a/chrome/browser/safe_browsing/safe_browsing_navigation_observer.h b/chrome/browser/safe_browsing/safe_browsing_navigation_observer.h
index 45f3542..bde7c48 100644
--- a/chrome/browser/safe_browsing/safe_browsing_navigation_observer.h
+++ b/chrome/browser/safe_browsing/safe_browsing_navigation_observer.h
@@ -10,7 +10,7 @@
 #include "components/content_settings/core/browser/content_settings_observer.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "components/sessions/core/session_id.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "url/gurl.h"
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 6e82c9c..7c59fce 100644
--- a/chrome/browser/safe_browsing/safe_browsing_navigation_observer_browsertest.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_navigation_observer_browsertest.cc
@@ -20,7 +20,7 @@
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/download/public/common/download_item.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/features.h"
+#include "components/safe_browsing/core/features.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/download_item_utils.h"
 #include "content/public/browser/download_manager.h"
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 ab14da0..33bef94 100644
--- a/chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.cc
@@ -20,9 +20,9 @@
 #include "chrome/browser/sessions/session_tab_helper.h"
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/common/utils.h"
-#include "components/safe_browsing/features.h"
-#include "components/safe_browsing/web_ui/safe_browsing_ui.h"
+#include "components/safe_browsing/content/web_ui/safe_browsing_ui.h"
+#include "components/safe_browsing/core/common/utils.h"
+#include "components/safe_browsing/core/features.h"
 #include "content/public/browser/navigation_details.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
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 6a7b225..a964f6b 100644
--- a/chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.h
+++ b/chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.h
@@ -9,8 +9,8 @@
 #include "base/feature_list.h"
 #include "base/supports_user_data.h"
 #include "base/timer/timer.h"
-#include "components/safe_browsing/browser/referrer_chain_provider.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/browser/referrer_chain_provider.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "components/sessions/core/session_id.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "third_party/protobuf/src/google/protobuf/repeated_field.h"
diff --git a/chrome/browser/safe_browsing/safe_browsing_service.cc b/chrome/browser/safe_browsing/safe_browsing_service.cc
index 3e54daf..cdd68c86 100644
--- a/chrome/browser/safe_browsing/safe_browsing_service.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_service.cc
@@ -37,15 +37,15 @@
 #include "chrome/common/safe_browsing/file_type_policies.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/browser/safe_browsing_network_context.h"
 #include "components/safe_browsing/buildflags.h"
-#include "components/safe_browsing/common/safebrowsing_constants.h"
-#include "components/safe_browsing/db/database_manager.h"
-#include "components/safe_browsing/ping_manager.h"
-#include "components/safe_browsing/realtime/policy_engine.h"
-#include "components/safe_browsing/triggers/trigger_manager.h"
-#include "components/safe_browsing/verdict_cache_manager.h"
-#include "components/safe_browsing/web_ui/safe_browsing_ui.h"
+#include "components/safe_browsing/content/web_ui/safe_browsing_ui.h"
+#include "components/safe_browsing/core/browser/safe_browsing_network_context.h"
+#include "components/safe_browsing/core/common/safebrowsing_constants.h"
+#include "components/safe_browsing/core/db/database_manager.h"
+#include "components/safe_browsing/core/ping_manager.h"
+#include "components/safe_browsing/core/realtime/policy_engine.h"
+#include "components/safe_browsing/core/triggers/trigger_manager.h"
+#include "components/safe_browsing/core/verdict_cache_manager.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "services/network/public/cpp/cross_thread_pending_shared_url_loader_factory.h"
@@ -62,7 +62,7 @@
 #include "chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident_reporting_service.h"
 #include "chrome/browser/safe_browsing/incident_reporting/resource_request_detector.h"
-#include "components/safe_browsing/password_protection/password_protection_service.h"
+#include "components/safe_browsing/content/password_protection/password_protection_service.h"
 #endif
 
 using content::BrowserThread;
diff --git a/chrome/browser/safe_browsing/safe_browsing_service.h b/chrome/browser/safe_browsing/safe_browsing_service.h
index 00082df..5f137a8 100644
--- a/chrome/browser/safe_browsing/safe_browsing_service.h
+++ b/chrome/browser/safe_browsing/safe_browsing_service.h
@@ -25,9 +25,9 @@
 #include "chrome/browser/profiles/profile_observer.h"
 #include "chrome/browser/safe_browsing/services_delegate.h"
 #include "components/safe_browsing/buildflags.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/db/util.h"
-#include "components/safe_browsing/safe_browsing_service_interface.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/db/util.h"
+#include "components/safe_browsing/core/safe_browsing_service_interface.h"
 #include "content/public/browser/browser_thread.h"
 #include "services/network/public/mojom/network_context.mojom-forward.h"
 
diff --git a/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc b/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc
index bdd462f..0cff7df 100644
--- a/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc
@@ -67,15 +67,15 @@
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/prefs/pref_service.h"
 #include "components/safe_browsing/buildflags.h"
-#include "components/safe_browsing/db/database_manager.h"
-#include "components/safe_browsing/db/metadata.pb.h"
-#include "components/safe_browsing/db/test_database_manager.h"
-#include "components/safe_browsing/db/util.h"
-#include "components/safe_browsing/db/v4_database.h"
-#include "components/safe_browsing/db/v4_get_hash_protocol_manager.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
-#include "components/safe_browsing/db/v4_test_util.h"
-#include "components/safe_browsing/features.h"
+#include "components/safe_browsing/core/db/database_manager.h"
+#include "components/safe_browsing/core/db/metadata.pb.h"
+#include "components/safe_browsing/core/db/test_database_manager.h"
+#include "components/safe_browsing/core/db/util.h"
+#include "components/safe_browsing/core/db/v4_database.h"
+#include "components/safe_browsing/core/db/v4_get_hash_protocol_manager.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/core/db/v4_test_util.h"
+#include "components/safe_browsing/core/features.h"
 #include "components/security_interstitials/content/security_interstitial_tab_helper.h"
 #include "components/security_interstitials/core/controller_client.h"
 #include "content/public/browser/browser_task_traits.h"
diff --git a/chrome/browser/safe_browsing/services_delegate.cc b/chrome/browser/safe_browsing/services_delegate.cc
index 34fbcef..9ad9224 100644
--- a/chrome/browser/safe_browsing/services_delegate.cc
+++ b/chrome/browser/safe_browsing/services_delegate.cc
@@ -17,8 +17,8 @@
 #include "chrome/common/chrome_switches.h"
 #include "components/keyed_service/core/service_access_type.h"
 #include "components/safe_browsing/buildflags.h"
-#include "components/safe_browsing/db/v4_local_database_manager.h"
-#include "components/safe_browsing/verdict_cache_manager.h"
+#include "components/safe_browsing/core/db/v4_local_database_manager.h"
+#include "components/safe_browsing/core/verdict_cache_manager.h"
 #include "content/public/browser/browser_thread.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/preferences/public/mojom/tracked_preference_validation_delegate.mojom.h"
diff --git a/chrome/browser/safe_browsing/services_delegate.h b/chrome/browser/safe_browsing/services_delegate.h
index 1d505f301..3693ea4 100644
--- a/chrome/browser/safe_browsing/services_delegate.h
+++ b/chrome/browser/safe_browsing/services_delegate.h
@@ -11,7 +11,7 @@
 #include "base/memory/ref_counted.h"
 #include "chrome/browser/safe_browsing/chrome_password_protection_service.h"
 #include "chrome/browser/safe_browsing/incident_reporting/delayed_analysis_callback.h"
-#include "components/safe_browsing/password_protection/password_protection_service.h"
+#include "components/safe_browsing/content/password_protection/password_protection_service.h"
 
 class Profile;
 
diff --git a/chrome/browser/safe_browsing/services_delegate_desktop.cc b/chrome/browser/safe_browsing/services_delegate_desktop.cc
index c6b2ae2c..110bac2 100644
--- a/chrome/browser/safe_browsing/services_delegate_desktop.cc
+++ b/chrome/browser/safe_browsing/services_delegate_desktop.cc
@@ -18,8 +18,8 @@
 #include "chrome/common/chrome_switches.h"
 #include "components/keyed_service/core/service_access_type.h"
 #include "components/safe_browsing/buildflags.h"
-#include "components/safe_browsing/db/v4_local_database_manager.h"
-#include "components/safe_browsing/verdict_cache_manager.h"
+#include "components/safe_browsing/core/db/v4_local_database_manager.h"
+#include "components/safe_browsing/core/verdict_cache_manager.h"
 #include "content/public/browser/browser_thread.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/preferences/public/mojom/tracked_preference_validation_delegate.mojom.h"
diff --git a/chrome/browser/safe_browsing/signature_evaluator_mac.mm b/chrome/browser/safe_browsing/signature_evaluator_mac.mm
index a3d3968..ad8dc7c5 100644
--- a/chrome/browser/safe_browsing/signature_evaluator_mac.mm
+++ b/chrome/browser/safe_browsing/signature_evaluator_mac.mm
@@ -19,7 +19,7 @@
 #include "base/strings/sys_string_conversions.h"
 #include "chrome/common/safe_browsing/binary_feature_extractor.h"
 #include "chrome/common/safe_browsing/mach_o_image_reader_mac.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/safe_browsing/signature_evaluator_mac_unittest.cc b/chrome/browser/safe_browsing/signature_evaluator_mac_unittest.cc
index c8a56c0..f72e185 100644
--- a/chrome/browser/safe_browsing/signature_evaluator_mac_unittest.cc
+++ b/chrome/browser/safe_browsing/signature_evaluator_mac_unittest.cc
@@ -22,7 +22,7 @@
 #include "chrome/browser/safe_browsing/incident_reporting/incident.h"
 #include "chrome/browser/safe_browsing/incident_reporting/mock_incident_receiver.h"
 #include "chrome/common/chrome_paths.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "testing/gmock/include/gmock/gmock-matchers.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/safe_browsing/telemetry/android/android_telemetry_service.cc b/chrome/browser/safe_browsing/telemetry/android/android_telemetry_service.cc
index 88bde8ec..16bc286 100644
--- a/chrome/browser/safe_browsing/telemetry/android/android_telemetry_service.cc
+++ b/chrome/browser/safe_browsing/telemetry/android/android_telemetry_service.cc
@@ -21,11 +21,11 @@
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "components/keyed_service/core/service_access_type.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/db/database_manager.h"
-#include "components/safe_browsing/features.h"
-#include "components/safe_browsing/ping_manager.h"
-#include "components/safe_browsing/web_ui/safe_browsing_ui.h"
+#include "components/safe_browsing/content/web_ui/safe_browsing_ui.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/db/database_manager.h"
+#include "components/safe_browsing/core/features.h"
+#include "components/safe_browsing/core/ping_manager.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/download_item_utils.h"
diff --git a/chrome/browser/safe_browsing/telemetry/android/android_telemetry_service.h b/chrome/browser/safe_browsing/telemetry/android/android_telemetry_service.h
index 604fe99e..ac4659786 100644
--- a/chrome/browser/safe_browsing/telemetry/android/android_telemetry_service.h
+++ b/chrome/browser/safe_browsing/telemetry/android/android_telemetry_service.h
@@ -12,7 +12,7 @@
 #include "chrome/browser/safe_browsing/telemetry/telemetry_service.h"
 #include "components/download/public/common/download_item.h"
 #include "components/download/public/common/simple_download_manager_coordinator.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "content/public/browser/download_manager.h"
 
 class Profile;
diff --git a/chrome/browser/safe_browsing/telemetry/android/android_telemetry_service_unittest.cc b/chrome/browser/safe_browsing/telemetry/android/android_telemetry_service_unittest.cc
index b1c1c0c..ffbfc09 100644
--- a/chrome/browser/safe_browsing/telemetry/android/android_telemetry_service_unittest.cc
+++ b/chrome/browser/safe_browsing/telemetry/android/android_telemetry_service_unittest.cc
@@ -16,8 +16,8 @@
 #include "chrome/test/base/testing_profile.h"
 #include "components/download/public/common/mock_download_item.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/features.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/features.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/download_item_utils.h"
diff --git a/chrome/browser/safe_browsing/test_safe_browsing_blocking_page_quiet.h b/chrome/browser/safe_browsing/test_safe_browsing_blocking_page_quiet.h
index dac8296..3b8fce9 100644
--- a/chrome/browser/safe_browsing/test_safe_browsing_blocking_page_quiet.h
+++ b/chrome/browser/safe_browsing/test_safe_browsing_blocking_page_quiet.h
@@ -5,8 +5,8 @@
 #ifndef CHROME_BROWSER_SAFE_BROWSING_TEST_SAFE_BROWSING_BLOCKING_PAGE_QUIET_H_
 #define CHROME_BROWSER_SAFE_BROWSING_TEST_SAFE_BROWSING_BLOCKING_PAGE_QUIET_H_
 
-#include "components/safe_browsing/base_blocking_page.h"
-#include "components/safe_browsing/base_ui_manager.h"
+#include "components/safe_browsing/content/base_blocking_page.h"
+#include "components/safe_browsing/content/base_ui_manager.h"
 #include "components/security_interstitials/core/base_safe_browsing_error_ui.h"
 #include "components/security_interstitials/core/safe_browsing_quiet_error_ui.h"
 
diff --git a/chrome/browser/safe_browsing/test_safe_browsing_database_helper.cc b/chrome/browser/safe_browsing/test_safe_browsing_database_helper.cc
index 01ca698..1f91eb7 100644
--- a/chrome/browser/safe_browsing/test_safe_browsing_database_helper.cc
+++ b/chrome/browser/safe_browsing/test_safe_browsing_database_helper.cc
@@ -14,9 +14,9 @@
 #include "base/strings/stringprintf.h"
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "chrome/browser/safe_browsing/test_safe_browsing_service.h"
-#include "components/safe_browsing/db/v4_database.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
-#include "components/safe_browsing/db/v4_test_util.h"
+#include "components/safe_browsing/core/db/v4_database.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/core/db/v4_test_util.h"
 #include "components/security_interstitials/content/unsafe_resource.h"
 
 namespace {
diff --git a/chrome/browser/safe_browsing/test_safe_browsing_database_helper.h b/chrome/browser/safe_browsing/test_safe_browsing_database_helper.h
index 7092ebb..d653605d 100644
--- a/chrome/browser/safe_browsing/test_safe_browsing_database_helper.h
+++ b/chrome/browser/safe_browsing/test_safe_browsing_database_helper.h
@@ -9,7 +9,7 @@
 #include <vector>
 
 #include "base/macros.h"
-#include "components/safe_browsing/db/util.h"
+#include "components/safe_browsing/core/db/util.h"
 
 namespace safe_browsing {
 class ListIdentifier;
diff --git a/chrome/browser/safe_browsing/test_safe_browsing_service.cc b/chrome/browser/safe_browsing/test_safe_browsing_service.cc
index d9ac41d..13b7b95 100644
--- a/chrome/browser/safe_browsing/test_safe_browsing_service.cc
+++ b/chrome/browser/safe_browsing/test_safe_browsing_service.cc
@@ -11,8 +11,8 @@
 #include "chrome/browser/safe_browsing/services_delegate.h"
 #include "chrome/browser/safe_browsing/ui_manager.h"
 #include "components/safe_browsing/buildflags.h"
-#include "components/safe_browsing/db/database_manager.h"
-#include "components/safe_browsing/db/test_database_manager.h"
+#include "components/safe_browsing/core/db/database_manager.h"
+#include "components/safe_browsing/core/db/test_database_manager.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/safe_browsing/test_safe_browsing_service.h b/chrome/browser/safe_browsing/test_safe_browsing_service.h
index e38776e..f535ad8 100644
--- a/chrome/browser/safe_browsing/test_safe_browsing_service.h
+++ b/chrome/browser/safe_browsing/test_safe_browsing_service.h
@@ -9,7 +9,7 @@
 
 #include "chrome/browser/safe_browsing/services_delegate.h"
 #include "chrome/browser/safe_browsing/ui_manager.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
 
 namespace safe_browsing {
 class SafeBrowsingDatabaseManager;
diff --git a/chrome/browser/safe_browsing/threat_details_unittest.cc b/chrome/browser/safe_browsing/threat_details_unittest.cc
index 0ebb2fcb..89e2337 100644
--- a/chrome/browser/safe_browsing/threat_details_unittest.cc
+++ b/chrome/browser/safe_browsing/threat_details_unittest.cc
@@ -20,11 +20,11 @@
 #include "chrome/test/base/testing_profile.h"
 #include "components/history/core/browser/history_backend.h"
 #include "components/history/core/browser/history_service.h"
-#include "components/safe_browsing/browser/referrer_chain_provider.h"
-#include "components/safe_browsing/browser/threat_details.h"
-#include "components/safe_browsing/browser/threat_details_history.h"
-#include "components/safe_browsing/common/safe_browsing.mojom.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/content/browser/threat_details.h"
+#include "components/safe_browsing/content/browser/threat_details_history.h"
+#include "components/safe_browsing/core/browser/referrer_chain_provider.h"
+#include "components/safe_browsing/core/common/safe_browsing.mojom.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
diff --git a/chrome/browser/safe_browsing/trigger_creator.cc b/chrome/browser/safe_browsing/trigger_creator.cc
index c494757..52a75b7 100644
--- a/chrome/browser/safe_browsing/trigger_creator.cc
+++ b/chrome/browser/safe_browsing/trigger_creator.cc
@@ -9,13 +9,13 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/features.h"
-#include "components/safe_browsing/triggers/ad_popup_trigger.h"
-#include "components/safe_browsing/triggers/ad_redirect_trigger.h"
-#include "components/safe_browsing/triggers/ad_sampler_trigger.h"
-#include "components/safe_browsing/triggers/suspicious_site_trigger.h"
-#include "components/safe_browsing/triggers/trigger_manager.h"
-#include "components/safe_browsing/triggers/trigger_throttler.h"
+#include "components/safe_browsing/content/triggers/ad_popup_trigger.h"
+#include "components/safe_browsing/content/triggers/ad_redirect_trigger.h"
+#include "components/safe_browsing/content/triggers/ad_sampler_trigger.h"
+#include "components/safe_browsing/content/triggers/suspicious_site_trigger.h"
+#include "components/safe_browsing/core/features.h"
+#include "components/safe_browsing/core/triggers/trigger_manager.h"
+#include "components/safe_browsing/core/triggers/trigger_throttler.h"
 #include "components/security_interstitials/core/base_safe_browsing_error_ui.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
diff --git a/chrome/browser/safe_browsing/ui_manager.cc b/chrome/browser/safe_browsing/ui_manager.cc
index 89a6e83..3cca858 100644
--- a/chrome/browser/safe_browsing/ui_manager.cc
+++ b/chrome/browser/safe_browsing/ui_manager.cc
@@ -22,9 +22,9 @@
 #include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/browser/threat_details.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/ping_manager.h"
+#include "components/safe_browsing/content/browser/threat_details.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/ping_manager.h"
 #include "components/security_interstitials/content/security_interstitial_tab_helper.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_entry.h"
diff --git a/chrome/browser/safe_browsing/ui_manager.h b/chrome/browser/safe_browsing/ui_manager.h
index 7549526..d62dbb8 100644
--- a/chrome/browser/safe_browsing/ui_manager.h
+++ b/chrome/browser/safe_browsing/ui_manager.h
@@ -16,7 +16,7 @@
 #include "base/observer_list.h"
 #include "base/time/time.h"
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
-#include "components/safe_browsing/base_ui_manager.h"
+#include "components/safe_browsing/content/base_ui_manager.h"
 #include "components/security_interstitials/content/unsafe_resource.h"
 
 class GURL;
diff --git a/chrome/browser/safe_browsing/ui_manager_unittest.cc b/chrome/browser/safe_browsing/ui_manager_unittest.cc
index e37dac8..0ae8bfb 100644
--- a/chrome/browser/safe_browsing/ui_manager_unittest.cc
+++ b/chrome/browser/safe_browsing/ui_manager_unittest.cc
@@ -15,8 +15,8 @@
 #include "chrome/test/base/scoped_testing_local_state.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/db/util.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/db/util.h"
 #include "components/security_interstitials/core/base_safe_browsing_error_ui.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/navigation_entry.h"
diff --git a/chrome/browser/safe_browsing/url_checker_delegate_impl.cc b/chrome/browser/safe_browsing/url_checker_delegate_impl.cc
index 2059363..bb270c6 100644
--- a/chrome/browser/safe_browsing/url_checker_delegate_impl.cc
+++ b/chrome/browser/safe_browsing/url_checker_delegate_impl.cc
@@ -14,11 +14,11 @@
 #include "chrome/browser/profiles/profile_io_data.h"
 #include "chrome/browser/safe_browsing/ui_manager.h"
 #include "components/safe_browsing/buildflags.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/db/database_manager.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
-#include "components/safe_browsing/features.h"
-#include "components/safe_browsing/triggers/suspicious_site_trigger.h"
+#include "components/safe_browsing/content/triggers/suspicious_site_trigger.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/db/database_manager.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/core/features.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_entry.h"
@@ -121,7 +121,6 @@
 }
 
 bool UrlCheckerDelegateImpl::ShouldSkipRequestCheck(
-    content::ResourceContext* resource_context,
     const GURL& original_url,
     int frame_tree_node_id,
     int render_process_id,
diff --git a/chrome/browser/safe_browsing/url_checker_delegate_impl.h b/chrome/browser/safe_browsing/url_checker_delegate_impl.h
index 6eb39cbf2..0ab1467 100644
--- a/chrome/browser/safe_browsing/url_checker_delegate_impl.h
+++ b/chrome/browser/safe_browsing/url_checker_delegate_impl.h
@@ -7,7 +7,7 @@
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
-#include "components/safe_browsing/browser/url_checker_delegate.h"
+#include "components/safe_browsing/core/browser/url_checker_delegate.h"
 #include "content/public/browser/web_contents.h"
 
 namespace safe_browsing {
@@ -34,8 +34,7 @@
       bool is_main_frame,
       bool has_user_gesture) override;
   bool IsUrlWhitelisted(const GURL& url) override;
-  bool ShouldSkipRequestCheck(content::ResourceContext* resource_context,
-                              const GURL& original_url,
+  bool ShouldSkipRequestCheck(const GURL& original_url,
                               int frame_tree_node_id,
                               int render_process_id,
                               int render_frame_id,
diff --git a/chrome/browser/safe_browsing/v4_embedded_test_server_browsertest.cc b/chrome/browser/safe_browsing/v4_embedded_test_server_browsertest.cc
index 72ff2fa..c8a201e3 100644
--- a/chrome/browser/safe_browsing/v4_embedded_test_server_browsertest.cc
+++ b/chrome/browser/safe_browsing/v4_embedded_test_server_browsertest.cc
@@ -11,11 +11,11 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
-#include "components/safe_browsing/db/safebrowsing.pb.h"
-#include "components/safe_browsing/db/util.h"
-#include "components/safe_browsing/db/v4_embedded_test_server_util.h"
-#include "components/safe_browsing/db/v4_test_util.h"
-#include "components/safe_browsing/features.h"
+#include "components/safe_browsing/core/db/safebrowsing.pb.h"
+#include "components/safe_browsing/core/db/util.h"
+#include "components/safe_browsing/core/db/v4_embedded_test_server_util.h"
+#include "components/safe_browsing/core/db/v4_test_util.h"
+#include "components/safe_browsing/core/features.h"
 #include "components/security_interstitials/content/security_interstitial_tab_helper.h"
 #include "content/public/browser/interstitial_page.h"
 #include "content/public/browser/web_contents.h"
diff --git a/chrome/browser/ssl/BUILD.gn b/chrome/browser/ssl/BUILD.gn
index 43e523b2..d5e76eb 100644
--- a/chrome/browser/ssl/BUILD.gn
+++ b/chrome/browser/ssl/BUILD.gn
@@ -6,7 +6,6 @@
 
 proto_library("proto") {
   sources = [
-    "ssl_error_assistant.proto",
     "tls_deprecation_config.proto",
   ]
 }
diff --git a/chrome/browser/ssl/certificate_reporting_test_utils.cc b/chrome/browser/ssl/certificate_reporting_test_utils.cc
index 33acae52..d03b01b 100644
--- a/chrome/browser/ssl/certificate_reporting_test_utils.cc
+++ b/chrome/browser/ssl/certificate_reporting_test_utils.cc
@@ -15,7 +15,7 @@
 #include "build/build_config.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/security_interstitials/content/cert_report_helper.h"
 #include "components/security_interstitials/content/certificate_error_report.h"
 #include "components/variations/variations_associated_data.h"
diff --git a/chrome/browser/ssl/common_name_mismatch_handler.cc b/chrome/browser/ssl/common_name_mismatch_handler.cc
deleted file mode 100644
index 17feb9d..0000000
--- a/chrome/browser/ssl/common_name_mismatch_handler.cc
+++ /dev/null
@@ -1,171 +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/ssl/common_name_mismatch_handler.h"
-
-#include <utility>
-
-#include "base/bind.h"
-#include "base/logging.h"
-#include "base/strings/string_number_conversions.h"
-#include "components/ssl_errors/error_classification.h"
-#include "net/base/load_flags.h"
-#include "net/http/http_response_headers.h"
-#include "net/http/http_util.h"
-#include "net/traffic_annotation/network_traffic_annotation.h"
-#include "net/url_request/redirect_info.h"
-#include "services/network/public/cpp/resource_request.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "services/network/public/cpp/simple_url_loader.h"
-#include "services/network/public/mojom/url_response_head.mojom.h"
-
-CommonNameMismatchHandler::CommonNameMismatchHandler(
-    const GURL& request_url,
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
-    : request_url_(request_url),
-      url_loader_factory_(std::move(url_loader_factory)) {}
-
-CommonNameMismatchHandler::~CommonNameMismatchHandler() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-}
-
-// static
-CommonNameMismatchHandler::TestingState
-    CommonNameMismatchHandler::testing_state_ = NOT_TESTING;
-
-void CommonNameMismatchHandler::CheckSuggestedUrl(
-    const GURL& url,
-    const CheckUrlCallback& callback) {
-  // Should be used only in tests.
-  if (testing_state_ == IGNORE_REQUESTS_FOR_TESTING)
-    return;
-
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(!IsCheckingSuggestedUrl());
-  DCHECK(check_url_callback_.is_null());
-
-  check_url_ = url;
-  check_url_callback_ = callback;
-
-  // Create traffic annotation tag.
-  net::NetworkTrafficAnnotationTag traffic_annotation =
-      net::DefineNetworkTrafficAnnotation("ssl_name_mismatch_lookup", R"(
-        semantics {
-          sender: "SSL Name Mismatch Handler"
-          description:
-            "If Chromium cannot make a secure connection to a site, this can "
-            "be because the site is misconfigured. The site may be serving a "
-            "security certificate intended for another site. If the SSL Common "
-            "Name Mismatch Handling feature is enabled, Chromium will try to "
-            "detect if one of the domains listed in the site's certificate is "
-            "available by issuing requests to those domains. If the response "
-            "indicates that an alternative site for which the certificate is "
-            "valid is available, Chromium will automatically redirect the user "
-            "to the alternative site."
-          trigger: "Resource load."
-          data: "An HTTP HEAD request to the alternative site."
-          destination: WEBSITE
-        }
-        policy {
-          cookies_allowed: NO
-          setting:
-            "Users can disable this feature by command line flag "
-            "'--disable-feature=SSLCommonNameMismatchHandling'."
-          policy_exception_justification:
-            "Not implemented."
-        })");
-
-  auto resource_request = std::make_unique<network::ResourceRequest>();
-  // Can't safely use net::LOAD_DISABLE_CERT_NETWORK_FETCHES here,
-  // since then the connection may be reused without checking the cert.
-  resource_request->url = check_url_;
-  resource_request->method = "HEAD";
-  resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
-
-  simple_url_loader_ = network::SimpleURLLoader::Create(
-      std::move(resource_request), traffic_annotation);
-  // Don't follow redirects to prevent leaking URL data to HTTP sites.
-  simple_url_loader_->SetOnRedirectCallback(
-      base::BindRepeating(&CommonNameMismatchHandler::OnSimpleLoaderRedirect,
-                          base::Unretained(this)));
-  simple_url_loader_->SetOnResponseStartedCallback(
-      base::BindOnce(&CommonNameMismatchHandler::OnSimpleLoaderResponseStarted,
-                     base::Unretained(this)));
-  simple_url_loader_->DownloadToString(
-      url_loader_factory_.get(),
-      base::BindOnce(&CommonNameMismatchHandler::OnSimpleLoaderComplete,
-                     base::Unretained(this)),
-      1 /*max_body_size*/);
-}
-
-// static
-bool CommonNameMismatchHandler::GetSuggestedUrl(
-    const GURL& request_url,
-    const std::vector<std::string>& dns_names,
-    GURL* suggested_url) {
-  std::string www_mismatch_hostname;
-  if (!ssl_errors::GetWWWSubDomainMatch(request_url, dns_names,
-                                        &www_mismatch_hostname)) {
-    return false;
-  }
-  // The full URL should be pinged, not just the new hostname. So, get the
-  // |suggested_url| with the |request_url|'s hostname replaced with
-  // new hostname. Keep resource path, query params the same.
-  GURL::Replacements replacements;
-  replacements.SetHostStr(www_mismatch_hostname);
-  *suggested_url = request_url.ReplaceComponents(replacements);
-  return true;
-}
-
-void CommonNameMismatchHandler::Cancel() {
-  simple_url_loader_.reset();
-  check_url_callback_.Reset();
-}
-
-void CommonNameMismatchHandler::OnSimpleLoaderHandler(
-    const GURL& final_url,
-    const network::mojom::URLResponseHead* head) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(IsCheckingSuggestedUrl());
-  DCHECK(!check_url_callback_.is_null());
-
-  SuggestedUrlCheckResult result = SUGGESTED_URL_NOT_AVAILABLE;
-
-  // Make sure the URL is a HTTPS page and returns a proper response code.
-  int response_code = -1;
-  // head may be null here, if called from OnSimpleLoaderComplete.
-  if (head && head->headers) {
-    response_code = head->headers->response_code();
-  }
-  if (response_code == 200 && final_url.SchemeIsCryptographic() &&
-      final_url.host() != request_url_.host()) {
-    DCHECK_EQ(final_url.host(), final_url.host());
-    result = SUGGESTED_URL_AVAILABLE;
-  }
-  simple_url_loader_.reset();
-  std::move(check_url_callback_).Run(result, check_url_);
-}
-
-void CommonNameMismatchHandler::OnSimpleLoaderRedirect(
-    const net::RedirectInfo& redirect_info,
-    const network::mojom::URLResponseHead& response_head,
-    std::vector<std::string>* to_be_removed_headers) {
-  OnSimpleLoaderHandler(redirect_info.new_url, &response_head);
-}
-
-void CommonNameMismatchHandler::OnSimpleLoaderResponseStarted(
-    const GURL& final_url,
-    const network::mojom::URLResponseHead& response_head) {
-  OnSimpleLoaderHandler(final_url, &response_head);
-}
-
-void CommonNameMismatchHandler::OnSimpleLoaderComplete(
-    std::unique_ptr<std::string> response_body) {
-  OnSimpleLoaderHandler(simple_url_loader_->GetFinalURL(),
-                        simple_url_loader_->ResponseInfo());
-}
-
-bool CommonNameMismatchHandler::IsCheckingSuggestedUrl() const {
-  return !!simple_url_loader_;
-}
diff --git a/chrome/browser/ssl/common_name_mismatch_handler.h b/chrome/browser/ssl/common_name_mismatch_handler.h
deleted file mode 100644
index 0a864ad..0000000
--- a/chrome/browser/ssl/common_name_mismatch_handler.h
+++ /dev/null
@@ -1,108 +0,0 @@
-// Copyright (c) 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_SSL_COMMON_NAME_MISMATCH_HANDLER_H_
-#define CHROME_BROWSER_SSL_COMMON_NAME_MISMATCH_HANDLER_H_
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_refptr.h"
-#include "base/sequence_checker.h"
-#include "base/time/time.h"
-#include "services/network/public/mojom/url_response_head.mojom-forward.h"
-#include "url/gurl.h"
-
-namespace net {
-struct RedirectInfo;
-}  // namespace net
-
-namespace network {
-class SharedURLLoaderFactory;
-class SimpleURLLoader;
-}  // namespace network
-
-// This class handles errors due to common name mismatches
-// (|ERR_CERT_COMMON_NAME_INVALID|) and helps remediate them by suggesting
-// alternative URLs that may be what the user intended to load.
-class CommonNameMismatchHandler {
- public:
-  enum SuggestedUrlCheckResult {
-    // The request succeeds with good response code i.e. URL exists and its
-    // certificate is valid.
-    SUGGESTED_URL_AVAILABLE,
-    // Suggested URL is either not available or has a bad certificate.
-    SUGGESTED_URL_NOT_AVAILABLE
-  };
-
-  enum TestingState {
-    NOT_TESTING,
-    // Disables the actual request to the |suggested_url|.
-    IGNORE_REQUESTS_FOR_TESTING
-  };
-
-  typedef base::Callback<void(SuggestedUrlCheckResult result,
-                              const GURL& suggested_url)>
-      CheckUrlCallback;
-
-  CommonNameMismatchHandler(
-      const GURL& request_url,
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
-  ~CommonNameMismatchHandler();
-
-  // Performs a network request to suggested URL. After completion, runs the
-  // |callback|.
-  void CheckSuggestedUrl(const GURL& url, const CheckUrlCallback& callback);
-
-  // Determines if, for |request_url| serving a certificate that is valid for
-  // the domain names |dns_names|, there is a name that the certificate is
-  // valid for that closely matches the original name in |request_url|. If
-  // so, returns true, and sets |*suggested_url| to a URL that is unlikely
-  // to cause an ERR_CERT_COMMON_NAME_INVALID error.
-  static bool GetSuggestedUrl(const GURL& request_url,
-                              const std::vector<std::string>& dns_names,
-                              GURL* suggested_url);
-
-  // Used in tests, to disable the request to |suggested_url|.
-  // If |testing_state| is IGNORE_REQUESTS_FOR_TESTING, then the
-  // callback won't get called.
-  static void set_state_for_testing(TestingState testing_state) {
-    testing_state_ = testing_state;
-  }
-
-  // Cancels the request to |suggested_url|
-  void Cancel();
-
- private:
-  void OnSimpleLoaderHandler(const GURL& final_url,
-                             const network::mojom::URLResponseHead* head);
-  void OnSimpleLoaderRedirect(
-      const net::RedirectInfo& redirect_info,
-      const network::mojom::URLResponseHead& response_head,
-      std::vector<std::string>* to_be_removed_headers);
-  void OnSimpleLoaderResponseStarted(
-      const GURL& final_url,
-      const network::mojom::URLResponseHead& response_head);
-  void OnSimpleLoaderComplete(std::unique_ptr<std::string> response_body);
-
-  // Returns true if the check is currently running.
-  bool IsCheckingSuggestedUrl() const;
-
-  static TestingState testing_state_;
-  const GURL request_url_;
-  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
-  GURL check_url_;
-  CheckUrlCallback check_url_callback_;
-  std::unique_ptr<network::SimpleURLLoader> simple_url_loader_;
-
-  SEQUENCE_CHECKER(sequence_checker_);
-
-  DISALLOW_COPY_AND_ASSIGN(CommonNameMismatchHandler);
-};
-
-#endif  // CHROME_BROWSER_SSL_COMMON_NAME_MISMATCH_HANDLER_H_
diff --git a/chrome/browser/ssl/mitm_software_blocking_page.cc b/chrome/browser/ssl/mitm_software_blocking_page.cc
index 5f6237b3..d7eb3d0 100644
--- a/chrome/browser/ssl/mitm_software_blocking_page.cc
+++ b/chrome/browser/ssl/mitm_software_blocking_page.cc
@@ -10,7 +10,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ssl/chrome_security_blocking_page_factory.h"
 #include "chrome/browser/ssl/ssl_error_controller_client.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/security_interstitials/content/cert_report_helper.h"
 #include "components/security_interstitials/content/ssl_cert_reporter.h"
 #include "components/security_interstitials/core/metrics_helper.h"
diff --git a/chrome/browser/ssl/security_state_tab_helper.cc b/chrome/browser/ssl/security_state_tab_helper.cc
index b9baf3c..22ca56c 100644
--- a/chrome/browser/ssl/security_state_tab_helper.cc
+++ b/chrome/browser/ssl/security_state_tab_helper.cc
@@ -125,9 +125,8 @@
 SecurityStateTabHelper::~SecurityStateTabHelper() {}
 
 security_state::SecurityLevel SecurityStateTabHelper::GetSecurityLevel() {
-  return security_state::GetSecurityLevel(
-      *GetVisibleSecurityState(), UsedPolicyInstalledCertificate(),
-      base::BindRepeating(&content::IsOriginSecure));
+  return security_state::GetSecurityLevel(*GetVisibleSecurityState(),
+                                          UsedPolicyInstalledCertificate());
 }
 
 std::unique_ptr<security_state::VisibleSecurityState>
diff --git a/chrome/browser/ssl/security_state_tab_helper_browsertest.cc b/chrome/browser/ssl/security_state_tab_helper_browsertest.cc
index b4aa5ee..67d2add 100644
--- a/chrome/browser/ssl/security_state_tab_helper_browsertest.cc
+++ b/chrome/browser/ssl/security_state_tab_helper_browsertest.cc
@@ -50,9 +50,9 @@
 #include "components/policy/core/common/policy_types.h"
 #include "components/policy/policy_constants.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/features.h"
-#include "components/safe_browsing/password_protection/metrics_util.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/content/password_protection/metrics_util.h"
+#include "components/safe_browsing/core/features.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "components/security_interstitials/content/security_interstitial_tab_helper.h"
 #include "components/security_interstitials/content/ssl_blocking_page.h"
 #include "components/security_state/content/ssl_status_input_event_data.h"
diff --git a/chrome/browser/ssl/ssl_browsertest.cc b/chrome/browser/ssl/ssl_browsertest.cc
index 3e43582..58f7ce9 100644
--- a/chrome/browser/ssl/ssl_browsertest.cc
+++ b/chrome/browser/ssl/ssl_browsertest.cc
@@ -56,12 +56,10 @@
 #include "chrome/browser/ssl/certificate_reporting_test_utils.h"
 #include "chrome/browser/ssl/chrome_security_blocking_page_factory.h"
 #include "chrome/browser/ssl/chrome_ssl_host_state_delegate.h"
-#include "chrome/browser/ssl/common_name_mismatch_handler.h"
 #include "chrome/browser/ssl/mitm_software_blocking_page.h"
 #include "chrome/browser/ssl/security_state_tab_helper.h"
 #include "chrome/browser/ssl/ssl_browsertest_util.h"
 #include "chrome/browser/ssl/ssl_error_assistant.h"
-#include "chrome/browser/ssl/ssl_error_assistant.pb.h"
 #include "chrome/browser/ssl/ssl_error_controller_client.h"
 #include "chrome/browser/ssl/ssl_error_handler.h"
 #include "chrome/browser/ui/browser.h"
@@ -92,13 +90,15 @@
 #include "components/policy/core/common/policy_map.h"
 #include "components/policy/policy_constants.h"
 #include "components/prefs/testing_pref_service.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/security_interstitials/content/bad_clock_blocking_page.h"
 #include "components/security_interstitials/content/captive_portal_blocking_page.h"
 #include "components/security_interstitials/content/cert_report_helper.h"
+#include "components/security_interstitials/content/common_name_mismatch_handler.h"
 #include "components/security_interstitials/content/security_interstitial_controller_client.h"
 #include "components/security_interstitials/content/security_interstitial_tab_helper.h"
 #include "components/security_interstitials/content/ssl_blocking_page.h"
+#include "components/security_interstitials/content/ssl_error_assistant.pb.h"
 #include "components/security_interstitials/core/controller_client.h"
 #include "components/security_interstitials/core/metrics_helper.h"
 #include "components/security_state/core/features.h"
diff --git a/chrome/browser/ssl/ssl_error_assistant.h b/chrome/browser/ssl/ssl_error_assistant.h
index 91c8354..5b2663d 100644
--- a/chrome/browser/ssl/ssl_error_assistant.h
+++ b/chrome/browser/ssl/ssl_error_assistant.h
@@ -10,7 +10,7 @@
 #include <vector>
 
 #include "base/optional.h"
-#include "chrome/browser/ssl/ssl_error_assistant.pb.h"
+#include "components/security_interstitials/content/ssl_error_assistant.pb.h"
 #include "net/ssl/ssl_info.h"
 #include "url/gurl.h"
 
diff --git a/chrome/browser/ssl/ssl_error_assistant_unittest.cc b/chrome/browser/ssl/ssl_error_assistant_unittest.cc
index 88b9e0b..01502b3 100644
--- a/chrome/browser/ssl/ssl_error_assistant_unittest.cc
+++ b/chrome/browser/ssl/ssl_error_assistant_unittest.cc
@@ -6,8 +6,8 @@
 
 #include <memory>
 
-#include "chrome/browser/ssl/ssl_error_assistant.pb.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "components/security_interstitials/content/ssl_error_assistant.pb.h"
 #include "crypto/sha2.h"
 #include "net/cert/asn1_util.h"
 #include "net/test/cert_test_util.h"
diff --git a/chrome/browser/ssl/ssl_error_controller_client.cc b/chrome/browser/ssl/ssl_error_controller_client.cc
index ff81768..dd794d4 100644
--- a/chrome/browser/ssl/ssl_error_controller_client.cc
+++ b/chrome/browser/ssl/ssl_error_controller_client.cc
@@ -22,7 +22,7 @@
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/web_contents.h"
 
diff --git a/chrome/browser/ssl/ssl_error_handler.h b/chrome/browser/ssl/ssl_error_handler.h
index 3994a7fb..6933e3ee 100644
--- a/chrome/browser/ssl/ssl_error_handler.h
+++ b/chrome/browser/ssl/ssl_error_handler.h
@@ -12,10 +12,10 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/timer/timer.h"
-#include "chrome/browser/ssl/common_name_mismatch_handler.h"
-#include "chrome/browser/ssl/ssl_error_assistant.pb.h"
+#include "components/security_interstitials/content/common_name_mismatch_handler.h"
 #include "components/security_interstitials/content/security_interstitial_page.h"
 #include "components/security_interstitials/content/ssl_cert_reporter.h"
+#include "components/security_interstitials/content/ssl_error_assistant.pb.h"
 #include "components/ssl_errors/error_classification.h"
 #include "content/public/browser/certificate_request_result_type.h"
 #include "content/public/browser/notification_observer.h"
diff --git a/chrome/browser/ssl/ssl_error_handler_unittest.cc b/chrome/browser/ssl/ssl_error_handler_unittest.cc
index ee6f125..a40114e8 100644
--- a/chrome/browser/ssl/ssl_error_handler_unittest.cc
+++ b/chrome/browser/ssl/ssl_error_handler_unittest.cc
@@ -21,9 +21,7 @@
 #include "chrome/browser/captive_portal/captive_portal_service.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ssl/common_name_mismatch_handler.h"
 #include "chrome/browser/ssl/ssl_error_assistant.h"
-#include "chrome/browser/ssl/ssl_error_assistant.pb.h"
 #include "chrome/browser/ssl/ssl_error_handler.h"
 #include "chrome/common/buildflags.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
@@ -32,6 +30,8 @@
 #include "components/network_time/network_time_test_utils.h"
 #include "components/network_time/network_time_tracker.h"
 #include "components/prefs/testing_pref_service.h"
+#include "components/security_interstitials/content/common_name_mismatch_handler.h"
+#include "components/security_interstitials/content/ssl_error_assistant.pb.h"
 #include "components/security_interstitials/core/ssl_error_options_mask.h"
 #include "components/security_interstitials/core/ssl_error_ui.h"
 #include "content/public/browser/browser_task_traits.h"
diff --git a/chrome/browser/subresource_filter/chrome_subresource_filter_client.cc b/chrome/browser/subresource_filter/chrome_subresource_filter_client.cc
index e3b36df2..dd8a9ae 100644
--- a/chrome/browser/subresource_filter/chrome_subresource_filter_client.cc
+++ b/chrome/browser/subresource_filter/chrome_subresource_filter_client.cc
@@ -21,7 +21,7 @@
 #include "chrome/browser/subresource_filter/subresource_filter_profile_context_factory.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings_types.h"
-#include "components/safe_browsing/db/database_manager.h"
+#include "components/safe_browsing/core/db/database_manager.h"
 #include "components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h"
 #include "components/subresource_filter/content/browser/ruleset_service.h"
 #include "components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.h"
diff --git a/chrome/browser/subresource_filter/subresource_filter_abusive_unittest.cc b/chrome/browser/subresource_filter/subresource_filter_abusive_unittest.cc
index 5bdd906..a3fefa4 100644
--- a/chrome/browser/subresource_filter/subresource_filter_abusive_unittest.cc
+++ b/chrome/browser/subresource_filter/subresource_filter_abusive_unittest.cc
@@ -11,7 +11,7 @@
 #include "chrome/browser/subresource_filter/subresource_filter_test_harness.h"
 #include "chrome/browser/ui/blocked_content/popup_blocker.h"
 #include "chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker.h"
-#include "components/safe_browsing/db/util.h"
+#include "components/safe_browsing/core/db/util.h"
 #include "components/subresource_filter/content/browser/fake_safe_browsing_database_manager.h"
 #include "components/subresource_filter/core/browser/subresource_filter_constants.h"
 #include "components/subresource_filter/core/browser/subresource_filter_features.h"
diff --git a/chrome/browser/subresource_filter/subresource_filter_browser_test_harness.cc b/chrome/browser/subresource_filter/subresource_filter_browser_test_harness.cc
index d8ad398..827806a 100644
--- a/chrome/browser/subresource_filter/subresource_filter_browser_test_harness.cc
+++ b/chrome/browser/subresource_filter/subresource_filter_browser_test_harness.cc
@@ -25,9 +25,9 @@
 #include "chrome/browser/ui/navigation_correction_tab_observer.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/chrome_paths.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
-#include "components/safe_browsing/db/v4_test_util.h"
-#include "components/safe_browsing/features.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/core/db/v4_test_util.h"
+#include "components/safe_browsing/core/features.h"
 #include "components/subresource_filter/content/browser/ruleset_service.h"
 #include "components/subresource_filter/core/common/common_features.h"
 #include "content/public/browser/web_contents.h"
diff --git a/chrome/browser/subresource_filter/subresource_filter_browser_test_harness.h b/chrome/browser/subresource_filter/subresource_filter_browser_test_harness.h
index d63e1d0e..b923a0b 100644
--- a/chrome/browser/subresource_filter/subresource_filter_browser_test_harness.h
+++ b/chrome/browser/subresource_filter/subresource_filter_browser_test_harness.h
@@ -13,7 +13,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/subresource_filter/test_ruleset_publisher.h"
 #include "chrome/test/base/in_process_browser_test.h"
-#include "components/safe_browsing/db/util.h"
+#include "components/safe_browsing/core/db/util.h"
 #include "components/subresource_filter/core/browser/subresource_filter_features_test_support.h"
 #include "components/subresource_filter/core/common/test_ruleset_creator.h"
 #include "components/url_pattern_index/proto/rules.pb.h"
diff --git a/chrome/browser/subresource_filter/subresource_filter_browsertest.cc b/chrome/browser/subresource_filter/subresource_filter_browsertest.cc
index ba3cafb..a6bededa 100644
--- a/chrome/browser/subresource_filter/subresource_filter_browsertest.cc
+++ b/chrome/browser/subresource_filter/subresource_filter_browsertest.cc
@@ -37,7 +37,7 @@
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/test/base/ui_test_utils.h"
-#include "components/safe_browsing/db/v4_test_util.h"
+#include "components/safe_browsing/core/db/v4_test_util.h"
 #include "components/security_interstitials/content/unsafe_resource.h"
 #include "components/subresource_filter/content/browser/async_document_subresource_filter.h"
 #include "components/subresource_filter/content/browser/async_document_subresource_filter_test_utils.h"
diff --git a/chrome/browser/subresource_filter/subresource_filter_intercepting_browsertest.cc b/chrome/browser/subresource_filter/subresource_filter_intercepting_browsertest.cc
index 72f54b9..325de476 100644
--- a/chrome/browser/subresource_filter/subresource_filter_intercepting_browsertest.cc
+++ b/chrome/browser/subresource_filter/subresource_filter_intercepting_browsertest.cc
@@ -12,10 +12,10 @@
 #include "chrome/browser/safe_browsing/test_safe_browsing_database_helper.h"
 #include "chrome/browser/subresource_filter/subresource_filter_browser_test_harness.h"
 #include "chrome/test/base/ui_test_utils.h"
-#include "components/safe_browsing/db/safebrowsing.pb.h"
-#include "components/safe_browsing/db/v4_embedded_test_server_util.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
-#include "components/safe_browsing/db/v4_test_util.h"
+#include "components/safe_browsing/core/db/safebrowsing.pb.h"
+#include "components/safe_browsing/core/db/v4_embedded_test_server_util.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/core/db/v4_test_util.h"
 #include "components/subresource_filter/core/browser/subresource_filter_constants.h"
 #include "components/subresource_filter/core/browser/subresource_filter_features.h"
 #include "content/public/browser/web_contents.h"
diff --git a/chrome/browser/subresource_filter/subresource_filter_popup_browsertest.cc b/chrome/browser/subresource_filter/subresource_filter_popup_browsertest.cc
index 637ab0b..8f4809e 100644
--- a/chrome/browser/subresource_filter/subresource_filter_popup_browsertest.cc
+++ b/chrome/browser/subresource_filter/subresource_filter_popup_browsertest.cc
@@ -22,7 +22,7 @@
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings_types.h"
-#include "components/safe_browsing/db/util.h"
+#include "components/safe_browsing/core/db/util.h"
 #include "components/subresource_filter/core/browser/subresource_filter_constants.h"
 #include "components/subresource_filter/core/browser/subresource_filter_features.h"
 #include "components/url_pattern_index/proto/rules.pb.h"
diff --git a/chrome/browser/subresource_filter/subresource_filter_test_harness.cc b/chrome/browser/subresource_filter/subresource_filter_test_harness.cc
index 86ef164..327c057 100644
--- a/chrome/browser/subresource_filter/subresource_filter_test_harness.cc
+++ b/chrome/browser/subresource_filter/subresource_filter_test_harness.cc
@@ -22,7 +22,7 @@
 #include "chrome/browser/subresource_filter/test_ruleset_publisher.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
 #include "components/subresource_filter/content/browser/ruleset_service.h"
 #include "components/subresource_filter/content/browser/subresource_filter_observer_test_utils.h"
 #include "components/subresource_filter/core/common/activation_decision.h"
diff --git a/chrome/browser/subresource_filter/subresource_filter_unittest.cc b/chrome/browser/subresource_filter/subresource_filter_unittest.cc
index 53fb89d..82d1741 100644
--- a/chrome/browser/subresource_filter/subresource_filter_unittest.cc
+++ b/chrome/browser/subresource_filter/subresource_filter_unittest.cc
@@ -10,8 +10,8 @@
 #include "chrome/browser/subresource_filter/subresource_filter_content_settings_manager.h"
 #include "chrome/browser/subresource_filter/subresource_filter_test_harness.h"
 #include "chrome/test/base/testing_browser_process.h"
-#include "components/safe_browsing/db/util.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/core/db/util.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
 #include "components/subresource_filter/content/browser/content_activation_list_utils.h"
 #include "components/subresource_filter/content/browser/fake_safe_browsing_database_manager.h"
 #include "components/subresource_filter/content/browser/subresource_filter_observer_test_utils.h"
diff --git a/chrome/browser/sync/test/integration/single_client_secondary_account_sync_test.cc b/chrome/browser/sync/test/integration/single_client_secondary_account_sync_test.cc
index 00af0583..6153735b 100644
--- a/chrome/browser/sync/test/integration/single_client_secondary_account_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_secondary_account_sync_test.cc
@@ -83,13 +83,8 @@
   // transport mode.
   secondary_account_helper::SignInSecondaryAccount(
       profile(), &test_url_loader_factory_, "user@email.com");
-  if (browser_defaults::kSyncAutoStarts) {
-    EXPECT_EQ(syncer::SyncService::TransportState::INITIALIZING,
-              GetSyncService(0)->GetTransportState());
-  } else {
-    EXPECT_EQ(syncer::SyncService::TransportState::START_DEFERRED,
-              GetSyncService(0)->GetTransportState());
-  }
+  EXPECT_EQ(syncer::SyncService::TransportState::INITIALIZING,
+            GetSyncService(0)->GetTransportState());
 
   EXPECT_TRUE(GetClient(0)->AwaitSyncTransportActive());
 
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index f998bfb..94245543 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -485,17 +485,17 @@
     "//components/rappor",
     "//components/renderer_context_menu",
     "//components/resources",
-    "//components/safe_browsing:csd_proto",
-    "//components/safe_browsing:features",
-    "//components/safe_browsing/common",
-    "//components/safe_browsing/common:safe_browsing_prefs",
-    "//components/safe_browsing/db:database_manager",
-    "//components/safe_browsing/db:util",
-    "//components/safe_browsing/password_protection",
-    "//components/safe_browsing/password_protection:password_protection_metrics_util",
-    "//components/safe_browsing/triggers:ad_popup_trigger",
-    "//components/safe_browsing/triggers:ad_redirect_trigger",
-    "//components/safe_browsing/web_ui",
+    "//components/safe_browsing/content/password_protection",
+    "//components/safe_browsing/content/password_protection:password_protection_metrics_util",
+    "//components/safe_browsing/content/triggers:ad_popup_trigger",
+    "//components/safe_browsing/content/triggers:ad_redirect_trigger",
+    "//components/safe_browsing/content/web_ui",
+    "//components/safe_browsing/core:csd_proto",
+    "//components/safe_browsing/core:features",
+    "//components/safe_browsing/core/common",
+    "//components/safe_browsing/core/common:safe_browsing_prefs",
+    "//components/safe_browsing/core/db:database_manager",
+    "//components/safe_browsing/core/db:util",
     "//components/search",
     "//components/search_engines",
     "//components/security_interstitials/content:security_interstitial_page",
@@ -4078,8 +4078,8 @@
     deps += [
       "//chrome/browser/ui/webui/reset_password:mojo_bindings",
       "//chrome/common/safe_browsing:proto",
-      "//components/safe_browsing:csd_proto",
-      "//components/safe_browsing/password_protection:password_protection_metrics_util",
+      "//components/safe_browsing/content/password_protection:password_protection_metrics_util",
+      "//components/safe_browsing/core:csd_proto",
     ]
   }
 
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index e347a2f..ae32bcd 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -2819,6 +2819,9 @@
       <message name="IDS_MENU_FIND_IN_PAGE" desc="Menu item allowing users to find text within the current page. [CHAR-LIMIT=27]">
         Find in page
       </message>
+      <message name="IDS_MENU_PAINT_PREVIEW_CAPTURE" desc="Menu item allowing users to capture a paint preview of a page. [CHAR-LIMIT=27]" translateable="false">
+        Capture Paint Preview
+      </message>
       <message name="IDS_MENU_REQUEST_DESKTOP_SITE" desc="Menu item in Chrome's overflow/options menu. By default, when a user navigates to a web page, Chrome shows the mobile site, that is, the version of the site designed for mobile phones. If this menu item is selected, however, Chrome will try to load the 'desktop' site instead, i.e. the site designed for desktop computers or laptop computers, which have larger screens. [CHAR-LIMIT=24]">
         Desktop site
       </message>
@@ -3999,7 +4002,7 @@
 
       <!-- NFC prompt -->
       <message name="IDS_NFC_DISABLED_ON_DEVICE_MESSAGE" desc="Text shown in a prompt to turn on NFC after user granted NFC permission to a website.">
-        To continue, turn on NFC on your phone
+        To continue, turn on NFC on your device
       </message>
       <message name="IDS_NFC_PROMPT_TURN_ON" desc="Text on the positive button of the nfc prompt">
         Turn on
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_NFC_DISABLED_ON_DEVICE_MESSAGE.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_NFC_DISABLED_ON_DEVICE_MESSAGE.png.sha1
index 36723f0..6890e8c 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_NFC_DISABLED_ON_DEVICE_MESSAGE.png.sha1
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_NFC_DISABLED_ON_DEVICE_MESSAGE.png.sha1
@@ -1 +1 @@
-b61af89a20cbeaf1474ceb0bb4c3caa7494c517b
\ No newline at end of file
+62a49f3bc3f4746fcc14b3397ae99ada9a17d968
\ No newline at end of file
diff --git a/chrome/browser/ui/blocked_content/popup_blocker.cc b/chrome/browser/ui/blocked_content/popup_blocker.cc
index 80e1004..4e44e99 100644
--- a/chrome/browser/ui/blocked_content/popup_blocker.cc
+++ b/chrome/browser/ui/blocked_content/popup_blocker.cc
@@ -16,7 +16,7 @@
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/embedder_support/switches.h"
-#include "components/safe_browsing/triggers/ad_popup_trigger.h"
+#include "components/safe_browsing/content/triggers/ad_popup_trigger.h"
 #include "content/public/browser/page_navigator.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
diff --git a/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker.cc b/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker.cc
index 71a0e2e..80fe07986 100644
--- a/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker.cc
+++ b/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker.cc
@@ -12,8 +12,8 @@
 #include "chrome/common/pref_names.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/db/util.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/core/db/util.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
 #include "components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.h"
 #include "content/public/browser/back_forward_cache.h"
 #include "content/public/browser/navigation_handle.h"
diff --git a/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker.h b/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker.h
index 33f22c04..c3700c6 100644
--- a/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker.h
+++ b/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker.h
@@ -11,7 +11,7 @@
 #include "base/macros.h"
 #include "base/optional.h"
 #include "base/scoped_observer.h"
-#include "components/safe_browsing/db/util.h"
+#include "components/safe_browsing/core/db/util.h"
 #include "components/subresource_filter/content/browser/subresource_filter_observer.h"
 #include "components/subresource_filter/content/browser/subresource_filter_observer_manager.h"
 #include "content/public/browser/web_contents_observer.h"
diff --git a/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker_browsertest.cc b/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker_browsertest.cc
index adc5bea1..27346877 100644
--- a/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker_browsertest.cc
+++ b/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker_browsertest.cc
@@ -36,9 +36,9 @@
 #include "components/policy/core/common/policy_types.h"
 #include "components/policy/policy_constants.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/db/safebrowsing.pb.h"
-#include "components/safe_browsing/db/v4_embedded_test_server_util.h"
-#include "components/safe_browsing/db/v4_test_util.h"
+#include "components/safe_browsing/core/db/safebrowsing.pb.h"
+#include "components/safe_browsing/core/db/v4_embedded_test_server_util.h"
+#include "components/safe_browsing/core/db/v4_test_util.h"
 #include "components/subresource_filter/core/browser/subresource_filter_features.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/web_contents.h"
diff --git a/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker_unittest.cc b/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker_unittest.cc
index 1064216..22c9f00 100644
--- a/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker_unittest.cc
+++ b/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker_unittest.cc
@@ -24,7 +24,7 @@
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
 #include "components/subresource_filter/content/browser/fake_safe_browsing_database_manager.h"
 #include "components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.h"
 #include "components/subresource_filter/core/browser/subresource_filter_constants.h"
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index d31391e2..4cbd946 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -174,7 +174,7 @@
 #include "components/page_load_metrics/browser/metrics_web_contents_observer.h"
 #include "components/page_load_metrics/common/page_load_metrics.mojom.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/triggers/ad_redirect_trigger.h"
+#include "components/safe_browsing/content/triggers/ad_redirect_trigger.h"
 #include "components/search/search.h"
 #include "components/security_state/content/content_utils.h"
 #include "components/security_state/core/security_state.h"
diff --git a/chrome/browser/ui/page_info/page_info.cc b/chrome/browser/ui/page_info/page_info.cc
index ca7717d..2b311c02 100644
--- a/chrome/browser/ui/page_info/page_info.cc
+++ b/chrome/browser/ui/page_info/page_info.cc
@@ -59,8 +59,8 @@
 #include "components/content_settings/core/common/content_settings_utils.h"
 #include "components/password_manager/core/browser/password_manager_metrics_util.h"
 #include "components/safe_browsing/buildflags.h"
-#include "components/safe_browsing/password_protection/metrics_util.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/content/password_protection/metrics_util.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "components/security_state/core/features.h"
 #include "components/signin/public/identity_manager/account_info.h"
 #include "components/ssl_errors/error_info.h"
diff --git a/chrome/browser/ui/page_info/page_info_ui.cc b/chrome/browser/ui/page_info/page_info_ui.cc
index 3f2c28f..c79125e0 100644
--- a/chrome/browser/ui/page_info/page_info_ui.cc
+++ b/chrome/browser/ui/page_info/page_info_ui.cc
@@ -43,7 +43,7 @@
 #endif
 
 #if BUILDFLAG(FULL_SAFE_BROWSING)
-#include "components/safe_browsing/password_protection/password_protection_service.h"
+#include "components/safe_browsing/content/password_protection/password_protection_service.h"
 #endif
 
 namespace {
diff --git a/chrome/browser/ui/tab_contents/chrome_web_contents_view_handle_drop_unittest.cc b/chrome/browser/ui/tab_contents/chrome_web_contents_view_handle_drop_unittest.cc
index 647c09b4..3465c2a 100644
--- a/chrome/browser/ui/tab_contents/chrome_web_contents_view_handle_drop_unittest.cc
+++ b/chrome/browser/ui/tab_contents/chrome_web_contents_view_handle_drop_unittest.cc
@@ -19,8 +19,8 @@
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
 #include "components/prefs/scoped_user_pref_update.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/features.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/features.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/drop_data.h"
 #include "content/public/test/browser_task_environment.h"
diff --git a/chrome/browser/ui/views/download/download_item_view.cc b/chrome/browser/ui/views/download/download_item_view.cc
index ab26909..b72dfab 100644
--- a/chrome/browser/ui/views/download/download_item_view.cc
+++ b/chrome/browser/ui/views/download/download_item_view.cc
@@ -50,8 +50,8 @@
 #include "components/download/public/common/download_item.h"
 #include "components/prefs/pref_service.h"
 #include "components/safe_browsing/buildflags.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/features.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/features.h"
 #include "components/url_formatter/elide_url.h"
 #include "components/vector_icons/vector_icons.h"
 #include "content/public/browser/download_item_utils.h"
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index bcb4caf..c6c1235 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -138,7 +138,7 @@
 #include "components/omnibox/browser/omnibox_popup_view.h"
 #include "components/omnibox/browser/omnibox_view.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/password_protection/metrics_util.h"
+#include "components/safe_browsing/content/password_protection/metrics_util.h"
 #include "components/sessions/core/tab_restore_service.h"
 #include "components/translate/core/browser/language_state.h"
 #include "components/version_info/channel.h"
diff --git a/chrome/browser/ui/views/frame/browser_window_factory.cc b/chrome/browser/ui/views/frame/browser_window_factory.cc
index 8d53cc9..35024eee 100644
--- a/chrome/browser/ui/views/frame/browser_window_factory.cc
+++ b/chrome/browser/ui/views/frame/browser_window_factory.cc
@@ -8,7 +8,7 @@
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/frame/native_browser_frame_factory.h"
 #include "chrome/grit/chromium_strings.h"
-#include "components/safe_browsing/password_protection/metrics_util.h"
+#include "components/safe_browsing/content/password_protection/metrics_util.h"
 #if defined(USE_AURA)
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window.h"
diff --git a/chrome/browser/ui/views/page_info/page_info_bubble_view_browsertest.cc b/chrome/browser/ui/views/page_info/page_info_bubble_view_browsertest.cc
index 053a594..241684d 100644
--- a/chrome/browser/ui/views/page_info/page_info_bubble_view_browsertest.cc
+++ b/chrome/browser/ui/views/page_info/page_info_bubble_view_browsertest.cc
@@ -29,8 +29,8 @@
 #include "components/content_settings/core/browser/content_settings_registry.h"
 #include "components/content_settings/core/common/content_settings_types.h"
 #include "components/password_manager/core/browser/password_manager_metrics_util.h"
-#include "components/safe_browsing/features.h"
-#include "components/safe_browsing/password_protection/metrics_util.h"
+#include "components/safe_browsing/content/password_protection/metrics_util.h"
+#include "components/safe_browsing/core/features.h"
 #include "components/strings/grit/components_strings.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_frame_host.h"
diff --git a/chrome/browser/ui/views/page_info/page_info_bubble_view_sync_browsertest.cc b/chrome/browser/ui/views/page_info/page_info_bubble_view_sync_browsertest.cc
index 7d32f9956..c182414 100644
--- a/chrome/browser/ui/views/page_info/page_info_bubble_view_sync_browsertest.cc
+++ b/chrome/browser/ui/views/page_info/page_info_bubble_view_sync_browsertest.cc
@@ -16,7 +16,7 @@
 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/password_manager/core/browser/password_manager_metrics_util.h"
-#include "components/safe_browsing/password_protection/metrics_util.h"
+#include "components/safe_browsing/content/password_protection/metrics_util.h"
 #include "components/signin/public/base/signin_pref_names.h"
 #include "components/signin/public/identity_manager/account_info.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
diff --git a/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view_browsertest.cc b/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view_browsertest.cc
index c29114c..fa1f5a1e 100644
--- a/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view_browsertest.cc
+++ b/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view_browsertest.cc
@@ -33,7 +33,7 @@
 #include "chrome/common/chrome_features.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
 #include "components/security_interstitials/core/common_string_util.h"
 #include "components/security_state/core/features.h"
 #include "components/security_state/core/security_state.h"
diff --git a/chrome/browser/ui/views/safe_browsing/password_reuse_modal_warning_dialog_browsertest.cc b/chrome/browser/ui/views/safe_browsing/password_reuse_modal_warning_dialog_browsertest.cc
index b9650ca..18c114c 100644
--- a/chrome/browser/ui/views/safe_browsing/password_reuse_modal_warning_dialog_browsertest.cc
+++ b/chrome/browser/ui/views/safe_browsing/password_reuse_modal_warning_dialog_browsertest.cc
@@ -14,8 +14,8 @@
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/constrained_window/constrained_window_views.h"
 #include "components/password_manager/core/browser/password_manager_metrics_util.h"
-#include "components/safe_browsing/features.h"
-#include "components/safe_browsing/password_protection/password_protection_service.h"
+#include "components/safe_browsing/content/password_protection/password_protection_service.h"
+#include "components/safe_browsing/core/features.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/ui/views/sync/one_click_signin_dialog_view.cc b/chrome/browser/ui/views/sync/one_click_signin_dialog_view.cc
index d7c02dbd..0a190f4 100644
--- a/chrome/browser/ui/views/sync/one_click_signin_dialog_view.cc
+++ b/chrome/browser/ui/views/sync/one_click_signin_dialog_view.cc
@@ -42,7 +42,6 @@
 
   dialog_view_ = new OneClickSigninDialogView(email, std::move(delegate),
                                               std::move(confirmed_callback));
-  dialog_view_->Init();
   constrained_window::CreateBrowserModalDialogViews(dialog_view_, window)
       ->Show();
 }
@@ -58,68 +57,6 @@
     dialog_view_->GetWidget()->Close();
 }
 
-OneClickSigninDialogView::OneClickSigninDialogView(
-    const base::string16& email,
-    std::unique_ptr<OneClickSigninLinksDelegate> delegate,
-    base::OnceCallback<void(bool)> confirmed_callback)
-    : delegate_(std::move(delegate)),
-      email_(email),
-      confirmed_callback_(std::move(confirmed_callback)) {
-  DialogDelegate::set_button_label(
-      ui::DIALOG_BUTTON_OK,
-      l10n_util::GetStringUTF16(IDS_ONE_CLICK_SIGNIN_DIALOG_OK_BUTTON));
-  DialogDelegate::set_button_label(
-      ui::DIALOG_BUTTON_CANCEL,
-      l10n_util::GetStringUTF16(IDS_ONE_CLICK_SIGNIN_DIALOG_UNDO_BUTTON));
-
-  auto advanced_link = std::make_unique<views::Link>(
-      l10n_util::GetStringUTF16(IDS_ONE_CLICK_SIGNIN_DIALOG_ADVANCED));
-  advanced_link->set_listener(this);
-  advanced_link->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-  advanced_link_ = DialogDelegate::SetExtraView(std::move(advanced_link));
-
-  DCHECK(!confirmed_callback_.is_null());
-  set_margins(ChromeLayoutProvider::Get()->GetDialogInsetsForContentType(
-      views::TEXT, views::TEXT));
-  chrome::RecordDialogCreation(chrome::DialogIdentifier::ONE_CLICK_SIGNIN);
-}
-
-OneClickSigninDialogView::~OneClickSigninDialogView() {
-  if (!confirmed_callback_.is_null())
-    std::move(confirmed_callback_).Run(false);
-}
-
-void OneClickSigninDialogView::Init() {
-  views::GridLayout* layout =
-      SetLayoutManager(std::make_unique<views::GridLayout>());
-
-  // Column set for descriptive text and link.
-  views::ColumnSet* cs = layout->AddColumnSet(0);
-  cs->AddColumn(views::GridLayout::FILL, views::GridLayout::CENTER, 1.0,
-                views::GridLayout::USE_PREF, 0, 0);
-
-  layout->StartRow(views::GridLayout::kFixedSize, 0);
-
-  auto label = std::make_unique<views::Label>(l10n_util::GetStringFUTF16(
-      IDS_ONE_CLICK_SIGNIN_DIALOG_MESSAGE_NEW, email_));
-  label->SetMultiLine(true);
-  label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-  // Minimum width for the multi-line label.
-  constexpr int kMinimumDialogLabelWidth = 400;
-  label->SizeToFit(kMinimumDialogLabelWidth);
-  layout->AddView(std::move(label));
-
-  layout->StartRow(views::GridLayout::kFixedSize, 0);
-
-  auto learn_more_link =
-      std::make_unique<views::Link>(l10n_util::GetStringUTF16(IDS_LEARN_MORE));
-  learn_more_link->set_listener(this);
-  learn_more_link->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-  learn_more_link_ =
-      layout->AddView(std::move(learn_more_link), 1, 1,
-                      views::GridLayout::TRAILING, views::GridLayout::CENTER);
-}
-
 base::string16 OneClickSigninDialogView::GetWindowTitle() const {
   return l10n_util::GetStringUTF16(IDS_ONE_CLICK_SIGNIN_DIALOG_TITLE_NEW);
 }
@@ -148,3 +85,64 @@
   std::move(confirmed_callback_).Run(true);
   return true;
 }
+
+OneClickSigninDialogView::OneClickSigninDialogView(
+    const base::string16& email,
+    std::unique_ptr<OneClickSigninLinksDelegate> delegate,
+    base::OnceCallback<void(bool)> confirmed_callback)
+    : delegate_(std::move(delegate)),
+      email_(email),
+      confirmed_callback_(std::move(confirmed_callback)) {
+  DCHECK(!confirmed_callback_.is_null());
+
+  views::GridLayout* layout =
+      SetLayoutManager(std::make_unique<views::GridLayout>());
+
+  // Column set for descriptive text and link.
+  views::ColumnSet* cs = layout->AddColumnSet(0);
+  cs->AddColumn(views::GridLayout::FILL, views::GridLayout::CENTER, 1.0,
+                views::GridLayout::USE_PREF, 0, 0);
+
+  layout->StartRow(views::GridLayout::kFixedSize, 0);
+
+  auto label = std::make_unique<views::Label>(l10n_util::GetStringFUTF16(
+      IDS_ONE_CLICK_SIGNIN_DIALOG_MESSAGE_NEW, email_));
+  label->SetMultiLine(true);
+  label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+  // Minimum width for the multi-line label.
+  constexpr int kMinimumDialogLabelWidth = 400;
+  label->SizeToFit(kMinimumDialogLabelWidth);
+  layout->AddView(std::move(label));
+
+  layout->StartRow(views::GridLayout::kFixedSize, 0);
+
+  auto learn_more_link =
+      std::make_unique<views::Link>(l10n_util::GetStringUTF16(IDS_LEARN_MORE));
+  learn_more_link->set_listener(this);
+  learn_more_link->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+  learn_more_link_ =
+      layout->AddView(std::move(learn_more_link), 1, 1,
+                      views::GridLayout::TRAILING, views::GridLayout::CENTER);
+
+  auto advanced_link = std::make_unique<views::Link>(
+      l10n_util::GetStringUTF16(IDS_ONE_CLICK_SIGNIN_DIALOG_ADVANCED));
+  advanced_link->set_listener(this);
+  advanced_link->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+  advanced_link_ = DialogDelegate::SetExtraView(std::move(advanced_link));
+
+  DialogDelegate::set_button_label(
+      ui::DIALOG_BUTTON_OK,
+      l10n_util::GetStringUTF16(IDS_ONE_CLICK_SIGNIN_DIALOG_OK_BUTTON));
+  DialogDelegate::set_button_label(
+      ui::DIALOG_BUTTON_CANCEL,
+      l10n_util::GetStringUTF16(IDS_ONE_CLICK_SIGNIN_DIALOG_UNDO_BUTTON));
+
+  set_margins(ChromeLayoutProvider::Get()->GetDialogInsetsForContentType(
+      views::TEXT, views::TEXT));
+  chrome::RecordDialogCreation(chrome::DialogIdentifier::ONE_CLICK_SIGNIN);
+}
+
+OneClickSigninDialogView::~OneClickSigninDialogView() {
+  if (!confirmed_callback_.is_null())
+    std::move(confirmed_callback_).Run(false);
+}
diff --git a/chrome/browser/ui/views/sync/one_click_signin_dialog_view.h b/chrome/browser/ui/views/sync/one_click_signin_dialog_view.h
index bb2d6bd..b2be132 100644
--- a/chrome/browser/ui/views/sync/one_click_signin_dialog_view.h
+++ b/chrome/browser/ui/views/sync/one_click_signin_dialog_view.h
@@ -37,6 +37,15 @@
   // method is meant to be called only from tests.
   static OneClickSigninDialogView* view_for_testing() { return dialog_view_; }
 
+  // Overridden from views::DialogDelegateView:
+  base::string16 GetWindowTitle() const override;
+  ui::ModalType GetModalType() const override;
+  void WindowClosing() override;
+  bool Accept() override;
+
+  // Overridden from views::LinkListener:
+  void LinkClicked(views::Link* source, int event_flags) override;
+
  protected:
   // Creates a OneClickSigninDialogView.
   OneClickSigninDialogView(
@@ -52,17 +61,6 @@
   FRIEND_TEST_ALL_PREFIXES(OneClickSigninDialogViewTest, AdvancedLink);
   FRIEND_TEST_ALL_PREFIXES(OneClickSigninDialogViewTest, LearnMoreLink);
 
-  void Init();
-
-  // Overridden from views::DialogDelegateView:
-  base::string16 GetWindowTitle() const override;
-  ui::ModalType GetModalType() const override;
-  void WindowClosing() override;
-  bool Accept() override;
-
-  // Overridden from views::LinkListener:
-  void LinkClicked(views::Link* source, int event_flags) override;
-
   // Delegate to handle clicking on links in the bubble.
   std::unique_ptr<OneClickSigninLinksDelegate> delegate_;
 
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
index 1d57551..f663ebf 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -78,8 +78,8 @@
 #include "components/nacl/common/buildflags.h"
 #include "components/prefs/pref_service.h"
 #include "components/safe_browsing/buildflags.h"
-#include "components/safe_browsing/web_ui/constants.h"
-#include "components/safe_browsing/web_ui/safe_browsing_ui.h"
+#include "components/safe_browsing/content/web_ui/safe_browsing_ui.h"
+#include "components/safe_browsing/core/web_ui/constants.h"
 #include "components/security_interstitials/content/connection_help_ui.h"
 #include "components/security_interstitials/content/known_interception_disclosure_ui.h"
 #include "components/security_interstitials/content/urls.h"
diff --git a/chrome/browser/ui/webui/downloads/downloads_dom_handler.cc b/chrome/browser/ui/webui/downloads/downloads_dom_handler.cc
index 1dd4c53..84b4377 100644
--- a/chrome/browser/ui/webui/downloads/downloads_dom_handler.cc
+++ b/chrome/browser/ui/webui/downloads/downloads_dom_handler.cc
@@ -38,7 +38,7 @@
 #include "chrome/common/url_constants.h"
 #include "components/download/public/common/download_item.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/download_manager.h"
 #include "content/public/browser/render_process_host.h"
diff --git a/chrome/browser/ui/webui/interstitials/interstitial_ui.cc b/chrome/browser/ui/webui/interstitials/interstitial_ui.cc
index d109ef5..a87f8ca 100644
--- a/chrome/browser/ui/webui/interstitials/interstitial_ui.cc
+++ b/chrome/browser/ui/webui/interstitials/interstitial_ui.cc
@@ -24,7 +24,7 @@
 #include "chrome/common/buildflags.h"
 #include "chrome/common/url_constants.h"
 #include "components/grit/components_resources.h"
-#include "components/safe_browsing/db/database_manager.h"
+#include "components/safe_browsing/core/db/database_manager.h"
 #include "components/security_interstitials/content/bad_clock_blocking_page.h"
 #include "components/security_interstitials/content/origin_policy_ui.h"
 #include "components/security_interstitials/core/ssl_error_options_mask.h"
diff --git a/chrome/browser/ui/webui/management_ui.cc b/chrome/browser/ui/webui/management_ui.cc
index 9488e6a..8426606 100644
--- a/chrome/browser/ui/webui/management_ui.cc
+++ b/chrome/browser/ui/webui/management_ui.cc
@@ -16,7 +16,7 @@
 #include "chrome/grit/browser_resources.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/grit/theme_resources.h"
-#include "components/safe_browsing/common/safebrowsing_constants.h"
+#include "components/safe_browsing/core/common/safebrowsing_constants.h"
 #include "components/strings/grit/components_strings.h"
 #include "extensions/buildflags/buildflags.h"
 #include "ui/base/l10n/l10n_util.h"
diff --git a/chrome/browser/ui/webui/management_ui_handler.cc b/chrome/browser/ui/webui/management_ui_handler.cc
index 85acd3d..7f4f048 100644
--- a/chrome/browser/ui/webui/management_ui_handler.cc
+++ b/chrome/browser/ui/webui/management_ui_handler.cc
@@ -24,7 +24,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/pref_names.h"
 
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/strings/grit/components_strings.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/web_contents.h"
diff --git a/chrome/browser/ui/webui/reset_password/reset_password_ui.cc b/chrome/browser/ui/webui/reset_password/reset_password_ui.cc
index 82980ec..abb08f0 100644
--- a/chrome/browser/ui/webui/reset_password/reset_password_ui.cc
+++ b/chrome/browser/ui/webui/reset_password/reset_password_ui.cc
@@ -14,10 +14,10 @@
 #include "chrome/browser/ui/webui/reset_password/reset_password.mojom.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/browser_resources.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/password_protection/metrics_util.h"
-#include "components/safe_browsing/password_protection/password_protection_service.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/content/password_protection/metrics_util.h"
+#include "components/safe_browsing/content/password_protection/password_protection_service.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "components/strings/grit/components_strings.h"
 #include "components/url_formatter/url_formatter.h"
 #include "components/user_prefs/user_prefs.h"
diff --git a/chrome/browser/ui/webui/settings/change_password_handler.cc b/chrome/browser/ui/webui/settings/change_password_handler.cc
index b34815f..6382a152 100644
--- a/chrome/browser/ui/webui/settings/change_password_handler.cc
+++ b/chrome/browser/ui/webui/settings/change_password_handler.cc
@@ -8,8 +8,8 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/safe_browsing/chrome_password_protection_service.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/password_protection/metrics_util.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/content/password_protection/metrics_util.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
 
diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
index 663a9d0..977398b0 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -53,7 +53,7 @@
 #include "components/omnibox/common/omnibox_features.h"
 #include "components/password_manager/core/browser/manage_passwords_referrer.h"
 #include "components/password_manager/core/common/password_manager_features.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/signin/public/base/signin_buildflags.h"
 #include "components/strings/grit/components_strings.h"
 #include "components/subresource_filter/core/browser/subresource_filter_features.h"
diff --git a/chrome/browser/unified_consent/unified_consent_service_factory.cc b/chrome/browser/unified_consent/unified_consent_service_factory.cc
index 9aea657..1e4a574 100644
--- a/chrome/browser/unified_consent/unified_consent_service_factory.cc
+++ b/chrome/browser/unified_consent/unified_consent_service_factory.cc
@@ -12,7 +12,7 @@
 #include "chrome/common/pref_names.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/prefs/pref_registry_simple.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/spellcheck/browser/pref_names.h"
 #include "components/sync_preferences/pref_service_syncable.h"
 #include "components/unified_consent/unified_consent_metrics.h"
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn
index c0ac939c..c953f23 100644
--- a/chrome/common/BUILD.gn
+++ b/chrome/common/BUILD.gn
@@ -229,7 +229,7 @@
     "//components/policy/core/common",
     "//components/prefs",
     "//components/safe_browsing:buildflags",
-    "//components/safe_browsing/web_ui:constants",
+    "//components/safe_browsing/core/web_ui:constants",
     "//components/services/heap_profiling/public/cpp",
     "//components/strings",
     "//components/translate/content/common",
diff --git a/chrome/common/DEPS b/chrome/common/DEPS
index 33b6ceeed..8aaade3 100644
--- a/chrome/common/DEPS
+++ b/chrome/common/DEPS
@@ -38,8 +38,8 @@
   "+components/prefs",
   "+components/printing/common",
   "+components/safe_browsing/buildflags.h",
-  "+components/safe_browsing/proto/csd.pb.h",
-  "+components/safe_browsing/web_ui/constants.h",
+  "+components/safe_browsing/core/proto/csd.pb.h",
+  "+components/safe_browsing/core/web_ui/constants.h",
   "+components/services/app_service/public",
   "+components/strings/grit/components_strings.h",
   "+components/translate/core/common",
diff --git a/chrome/common/extensions/api/safe_browsing_private.idl b/chrome/common/extensions/api/safe_browsing_private.idl
index c7935da5..96544469 100644
--- a/chrome/common/extensions/api/safe_browsing_private.idl
+++ b/chrome/common/extensions/api/safe_browsing_private.idl
@@ -83,7 +83,7 @@
     DOMString? url;
   };
 
-  // From ReferrerChainEntry in //src/components/safe_browsing/proto/csd.proto
+  // From ReferrerChainEntry in //src/components/safe_browsing/core/proto/csd.proto
   dictionary ReferrerChainEntry {
     // URL of this entry.
     DOMString url;
diff --git a/chrome/common/safe_browsing/BUILD.gn b/chrome/common/safe_browsing/BUILD.gn
index e695c46b..c705eb04 100644
--- a/chrome/common/safe_browsing/BUILD.gn
+++ b/chrome/common/safe_browsing/BUILD.gn
@@ -59,7 +59,7 @@
       deps += [ ":disk_image_type_sniffer_mac" ]
     }
     public_deps = [
-      "//components/safe_browsing:csd_proto",
+      "//components/safe_browsing/core:csd_proto",
     ]
   }
 
@@ -71,10 +71,10 @@
     deps = [
       ":file_type_policies",
       "//base",
-      "//components/safe_browsing:features",
+      "//components/safe_browsing/core:features",
     ]
     public_deps = [
-      "//components/safe_browsing:csd_proto",
+      "//components/safe_browsing/core:csd_proto",
     ]
   }
 
@@ -90,7 +90,7 @@
       ":file_type_policies",
       "//base",
       "//base:i18n",
-      "//components/safe_browsing:features",
+      "//components/safe_browsing/core:features",
       "//third_party/unrar:unrar",
     ]
 
@@ -109,7 +109,7 @@
     ]
 
     public_deps = [
-      "//components/safe_browsing:csd_proto",
+      "//components/safe_browsing/core:csd_proto",
     ]
   }
 
@@ -139,7 +139,7 @@
 
     public_deps = [
       "//base",
-      "//components/safe_browsing:csd_proto",
+      "//components/safe_browsing/core:csd_proto",
       "//crypto",
     ]
 
@@ -186,14 +186,14 @@
       ":binary_feature_extractor",
       ":download_type_util",
       ":rar_analyzer",
-      "//components/safe_browsing:features",
+      "//components/safe_browsing/core:features",
     ]
 
     public_deps = [
       ":proto",
       "//base:i18n",
       "//chrome/common:mojo_bindings",
-      "//components/safe_browsing:csd_proto",
+      "//components/safe_browsing/core:csd_proto",
       "//crypto",
       "//ipc",
       "//third_party/zlib/google:zip",
diff --git a/chrome/common/safe_browsing/archive_analyzer_results.h b/chrome/common/safe_browsing/archive_analyzer_results.h
index 09c526db..b7f676e 100644
--- a/chrome/common/safe_browsing/archive_analyzer_results.h
+++ b/chrome/common/safe_browsing/archive_analyzer_results.h
@@ -12,7 +12,7 @@
 
 #include "base/files/file_path.h"
 #include "build/build_config.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 
 namespace base {
 class File;
diff --git a/chrome/common/safe_browsing/binary_feature_extractor.cc b/chrome/common/safe_browsing/binary_feature_extractor.cc
index 896768a6..31e4729b 100644
--- a/chrome/common/safe_browsing/binary_feature_extractor.cc
+++ b/chrome/common/safe_browsing/binary_feature_extractor.cc
@@ -10,7 +10,7 @@
 #include "base/files/file.h"
 #include "base/files/file_path.h"
 #include "base/files/memory_mapped_file.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "crypto/secure_hash.h"
 #include "crypto/sha2.h"
 
diff --git a/chrome/common/safe_browsing/binary_feature_extractor_fuzzer.cc b/chrome/common/safe_browsing/binary_feature_extractor_fuzzer.cc
index d69de8f..ebd157f 100644
--- a/chrome/common/safe_browsing/binary_feature_extractor_fuzzer.cc
+++ b/chrome/common/safe_browsing/binary_feature_extractor_fuzzer.cc
@@ -9,7 +9,7 @@
 
 #include <string>
 
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
   static safe_browsing::BinaryFeatureExtractor* extractor =
diff --git a/chrome/common/safe_browsing/binary_feature_extractor_mac.cc b/chrome/common/safe_browsing/binary_feature_extractor_mac.cc
index 9edb648..b731e68 100644
--- a/chrome/common/safe_browsing/binary_feature_extractor_mac.cc
+++ b/chrome/common/safe_browsing/binary_feature_extractor_mac.cc
@@ -8,7 +8,7 @@
 #include <stdint.h>
 
 #include "chrome/common/safe_browsing/mach_o_image_reader_mac.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/common/safe_browsing/binary_feature_extractor_mac_unittest.cc b/chrome/common/safe_browsing/binary_feature_extractor_mac_unittest.cc
index a1f44455b..2e4e546 100644
--- a/chrome/common/safe_browsing/binary_feature_extractor_mac_unittest.cc
+++ b/chrome/common/safe_browsing/binary_feature_extractor_mac_unittest.cc
@@ -7,7 +7,7 @@
 #include "base/files/file_path.h"
 #include "base/path_service.h"
 #include "chrome/common/chrome_paths.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace safe_browsing {
diff --git a/chrome/common/safe_browsing/binary_feature_extractor_unittest.cc b/chrome/common/safe_browsing/binary_feature_extractor_unittest.cc
index 8cc9823..d9f38b2 100644
--- a/chrome/common/safe_browsing/binary_feature_extractor_unittest.cc
+++ b/chrome/common/safe_browsing/binary_feature_extractor_unittest.cc
@@ -13,7 +13,7 @@
 #include "base/files/file.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/path_service.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "crypto/sha2.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/chrome/common/safe_browsing/binary_feature_extractor_win.cc b/chrome/common/safe_browsing/binary_feature_extractor_win.cc
index b80720d..91bf24c 100644
--- a/chrome/common/safe_browsing/binary_feature_extractor_win.cc
+++ b/chrome/common/safe_browsing/binary_feature_extractor_win.cc
@@ -14,7 +14,7 @@
 #include "base/logging.h"
 #include "base/threading/scoped_thread_priority.h"
 #include "chrome/common/safe_browsing/pe_image_reader_win.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/common/safe_browsing/binary_feature_extractor_win_unittest.cc b/chrome/common/safe_browsing/binary_feature_extractor_win_unittest.cc
index 619d63c..aa112b6 100644
--- a/chrome/common/safe_browsing/binary_feature_extractor_win_unittest.cc
+++ b/chrome/common/safe_browsing/binary_feature_extractor_win_unittest.cc
@@ -12,7 +12,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/path_service.h"
 #include "chrome/common/chrome_paths.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "net/cert/x509_cert_types.h"
 #include "net/cert/x509_certificate.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/common/safe_browsing/download_type_util.cc b/chrome/common/safe_browsing/download_type_util.cc
index a796740..0e495e9 100644
--- a/chrome/common/safe_browsing/download_type_util.cc
+++ b/chrome/common/safe_browsing/download_type_util.cc
@@ -12,7 +12,7 @@
 #include "base/macros.h"
 #include "base/strings/string_util.h"
 #include "chrome/common/safe_browsing/file_type_policies.h"
-#include "components/safe_browsing/features.h"
+#include "components/safe_browsing/core/features.h"
 
 namespace safe_browsing {
 namespace download_type_util {
diff --git a/chrome/common/safe_browsing/download_type_util.h b/chrome/common/safe_browsing/download_type_util.h
index 87f17e4..385da0d 100644
--- a/chrome/common/safe_browsing/download_type_util.h
+++ b/chrome/common/safe_browsing/download_type_util.h
@@ -6,7 +6,7 @@
 #define CHROME_COMMON_SAFE_BROWSING_DOWNLOAD_TYPE_UTIL_H_
 
 #include "base/files/file_path.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 
 namespace safe_browsing {
 namespace download_type_util {
diff --git a/chrome/common/safe_browsing/download_type_util_unittest.cc b/chrome/common/safe_browsing/download_type_util_unittest.cc
index 9e507ba..41fa201 100644
--- a/chrome/common/safe_browsing/download_type_util_unittest.cc
+++ b/chrome/common/safe_browsing/download_type_util_unittest.cc
@@ -6,7 +6,7 @@
 
 #include "base/feature_list.h"
 #include "base/files/file_path.h"
-#include "components/safe_browsing/features.h"
+#include "components/safe_browsing/core/features.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace safe_browsing {
diff --git a/chrome/common/safe_browsing/rar_analyzer.cc b/chrome/common/safe_browsing/rar_analyzer.cc
index 62af2be..7ad9604 100644
--- a/chrome/common/safe_browsing/rar_analyzer.cc
+++ b/chrome/common/safe_browsing/rar_analyzer.cc
@@ -16,7 +16,7 @@
 #include "chrome/common/safe_browsing/archive_analyzer_results.h"
 #include "chrome/common/safe_browsing/download_type_util.h"
 #include "chrome/common/safe_browsing/file_type_policies.h"
-#include "components/safe_browsing/features.h"
+#include "components/safe_browsing/core/features.h"
 #include "third_party/unrar/src/unrar_wrapper.h"
 
 namespace safe_browsing {
diff --git a/chrome/common/safe_browsing/zip_analyzer.cc b/chrome/common/safe_browsing/zip_analyzer.cc
index df0913e..1378485 100644
--- a/chrome/common/safe_browsing/zip_analyzer.cc
+++ b/chrome/common/safe_browsing/zip_analyzer.cc
@@ -21,7 +21,7 @@
 #include "build/build_config.h"
 #include "chrome/common/safe_browsing/archive_analyzer_results.h"
 #include "chrome/common/safe_browsing/file_type_policies.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "third_party/zlib/google/zip_reader.h"
 
 namespace safe_browsing {
diff --git a/chrome/common/webui_url_constants.cc b/chrome/common/webui_url_constants.cc
index e5b7d54..1a93146 100644
--- a/chrome/common/webui_url_constants.cc
+++ b/chrome/common/webui_url_constants.cc
@@ -7,7 +7,7 @@
 #include "base/stl_util.h"
 #include "base/strings/string_piece.h"
 #include "components/nacl/common/buildflags.h"
-#include "components/safe_browsing/web_ui/constants.h"
+#include "components/safe_browsing/core/web_ui/constants.h"
 #include "extensions/buildflags/buildflags.h"
 
 #if defined(OS_CHROMEOS)
diff --git a/chrome/renderer/BUILD.gn b/chrome/renderer/BUILD.gn
index c6d2f686..4b54fa5 100644
--- a/chrome/renderer/BUILD.gn
+++ b/chrome/renderer/BUILD.gn
@@ -151,8 +151,8 @@
     "//components/rappor/public/mojom",
     "//components/resources:components_resources",
     "//components/safe_browsing:buildflags",
-    "//components/safe_browsing/common:interfaces",
-    "//components/safe_browsing/renderer:throttles",
+    "//components/safe_browsing/content/renderer:throttles",
+    "//components/safe_browsing/core/common:interfaces",
     "//components/security_interstitials/content/renderer:security_interstitial_page_controller",
     "//components/security_interstitials/core:",
     "//components/security_interstitials/core/common/mojom:",
@@ -251,8 +251,8 @@
 
   if (safe_browsing_mode != 0) {
     deps += [
-      "//components/safe_browsing/common",
-      "//components/safe_browsing/renderer",
+      "//components/safe_browsing/content/renderer",
+      "//components/safe_browsing/core/common",
     ]
     if (safe_browsing_mode == 1) {
       sources += [
@@ -277,7 +277,7 @@
       ]
       deps += [
         "//chrome/common/safe_browsing:proto",
-        "//components/safe_browsing:csd_proto",
+        "//components/safe_browsing/core:csd_proto",
         "//third_party/smhasher:murmurhash3",
       ]
     }
diff --git a/chrome/renderer/DEPS b/chrome/renderer/DEPS
index 54783aa..9a81204 100644
--- a/chrome/renderer/DEPS
+++ b/chrome/renderer/DEPS
@@ -35,9 +35,9 @@
   "+components/printing/renderer",
   "+components/rappor/public/mojom",
   "+components/safe_browsing/buildflags.h",
-  "+components/safe_browsing/common",
-  "+components/safe_browsing/features.h",
-  "+components/safe_browsing/renderer",
+  "+components/safe_browsing/content/renderer",
+  "+components/safe_browsing/core/common",
+  "+components/safe_browsing/core/features.h",
   "+components/spellcheck/renderer",
   "+components/spellcheck/spellcheck_buildflags.h",
   "+components/startup_metric_utils/common",
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index 1950be1..22bad45 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -86,7 +86,7 @@
 #include "components/paint_preview/buildflags/buildflags.h"
 #include "components/pdf/renderer/pepper_pdf_host.h"
 #include "components/safe_browsing/buildflags.h"
-#include "components/safe_browsing/renderer/threat_dom_details.h"
+#include "components/safe_browsing/content/renderer/threat_dom_details.h"
 #include "components/spellcheck/spellcheck_buildflags.h"
 #include "components/startup_metric_utils/common/startup_metric.mojom.h"
 #include "components/subresource_filter/content/renderer/subresource_filter_agent.h"
diff --git a/chrome/renderer/safe_browsing/DEPS b/chrome/renderer/safe_browsing/DEPS
index 5f5942167..8807843 100644
--- a/chrome/renderer/safe_browsing/DEPS
+++ b/chrome/renderer/safe_browsing/DEPS
@@ -1,8 +1,8 @@
 include_rules = [
-  "+components/safe_browsing/common",
-  "+components/safe_browsing/proto/csd.pb.h",
-  "+components/safe_browsing/renderer",
-  "+components/safe_browsing/features.h",
+  "+components/safe_browsing/content/renderer",
+  "+components/safe_browsing/core/common",
+  "+components/safe_browsing/core/proto/csd.pb.h",
+  "+components/safe_browsing/core/features.h",
   "+third_party/smhasher",
 ]
 
diff --git a/chrome/renderer/safe_browsing/phishing_classifier.cc b/chrome/renderer/safe_browsing/phishing_classifier.cc
index ee16a74..72bf749 100644
--- a/chrome/renderer/safe_browsing/phishing_classifier.cc
+++ b/chrome/renderer/safe_browsing/phishing_classifier.cc
@@ -23,7 +23,7 @@
 #include "chrome/renderer/safe_browsing/phishing_term_feature_extractor.h"
 #include "chrome/renderer/safe_browsing/phishing_url_feature_extractor.h"
 #include "chrome/renderer/safe_browsing/scorer.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "content/public/renderer/render_frame.h"
 #include "crypto/sha2.h"
 #include "third_party/blink/public/platform/web_url.h"
diff --git a/chrome/renderer/safe_browsing/phishing_classifier_browsertest.cc b/chrome/renderer/safe_browsing/phishing_classifier_browsertest.cc
index 243b1a4..57829c0 100644
--- a/chrome/renderer/safe_browsing/phishing_classifier_browsertest.cc
+++ b/chrome/renderer/safe_browsing/phishing_classifier_browsertest.cc
@@ -19,7 +19,7 @@
 #include "chrome/renderer/safe_browsing/murmurhash3_util.h"
 #include "chrome/renderer/safe_browsing/scorer.h"
 #include "chrome/test/base/chrome_render_view_test.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "content/public/renderer/render_frame.h"
 #include "content/public/renderer/render_view.h"
 #include "crypto/sha2.h"
diff --git a/chrome/renderer/safe_browsing/phishing_classifier_delegate.cc b/chrome/renderer/safe_browsing/phishing_classifier_delegate.cc
index 73d6b08..fe2cd667 100644
--- a/chrome/renderer/safe_browsing/phishing_classifier_delegate.cc
+++ b/chrome/renderer/safe_browsing/phishing_classifier_delegate.cc
@@ -18,9 +18,9 @@
 #include "chrome/renderer/safe_browsing/feature_extractor_clock.h"
 #include "chrome/renderer/safe_browsing/phishing_classifier.h"
 #include "chrome/renderer/safe_browsing/scorer.h"
-#include "components/safe_browsing/common/safe_browsing.mojom-forward.h"
-#include "components/safe_browsing/common/safe_browsing.mojom-shared.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/common/safe_browsing.mojom-forward.h"
+#include "components/safe_browsing/core/common/safe_browsing.mojom-shared.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "content/public/renderer/document_state.h"
 #include "content/public/renderer/render_frame.h"
 #include "content/public/renderer/render_thread.h"
diff --git a/chrome/renderer/safe_browsing/phishing_classifier_delegate.h b/chrome/renderer/safe_browsing/phishing_classifier_delegate.h
index 91755fe..060b4e1a 100644
--- a/chrome/renderer/safe_browsing/phishing_classifier_delegate.h
+++ b/chrome/renderer/safe_browsing/phishing_classifier_delegate.h
@@ -11,7 +11,7 @@
 
 #include "base/macros.h"
 #include "base/strings/string16.h"
-#include "components/safe_browsing/common/safe_browsing.mojom.h"
+#include "components/safe_browsing/core/common/safe_browsing.mojom.h"
 #include "content/public/renderer/render_frame_observer.h"
 #include "content/public/renderer/render_thread_observer.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
diff --git a/chrome/renderer/safe_browsing/phishing_classifier_delegate_browsertest.cc b/chrome/renderer/safe_browsing/phishing_classifier_delegate_browsertest.cc
index ff04757c..ec8fda9 100644
--- a/chrome/renderer/safe_browsing/phishing_classifier_delegate_browsertest.cc
+++ b/chrome/renderer/safe_browsing/phishing_classifier_delegate_browsertest.cc
@@ -13,8 +13,8 @@
 #include "chrome/renderer/safe_browsing/scorer.h"
 #include "chrome/test/base/chrome_render_view_test.h"
 #include "chrome/test/base/chrome_unit_test_suite.h"
-#include "components/safe_browsing/common/safe_browsing.mojom-shared.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/common/safe_browsing.mojom-shared.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "content/public/renderer/render_frame.h"
 #include "content/public/renderer/render_view.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
diff --git a/chrome/renderer/safe_browsing/threat_dom_details_browsertest.cc b/chrome/renderer/safe_browsing/threat_dom_details_browsertest.cc
index f8a0ae5..a1f4d80a 100644
--- a/chrome/renderer/safe_browsing/threat_dom_details_browsertest.cc
+++ b/chrome/renderer/safe_browsing/threat_dom_details_browsertest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/safe_browsing/renderer/threat_dom_details.h"
+#include "components/safe_browsing/content/renderer/threat_dom_details.h"
 
 #include <memory>
 #include "base/strings/string_split.h"
@@ -10,8 +10,8 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "chrome/test/base/chrome_render_view_test.h"
-#include "components/safe_browsing/common/safe_browsing.mojom.h"
-#include "components/safe_browsing/features.h"
+#include "components/safe_browsing/core/common/safe_browsing.mojom.h"
+#include "components/safe_browsing/core/features.h"
 #include "components/variations/variations_associated_data.h"
 #include "content/public/renderer/render_view.h"
 #include "net/base/escape.h"
diff --git a/chrome/renderer/url_loader_throttle_provider_impl.cc b/chrome/renderer/url_loader_throttle_provider_impl.cc
index 6b01bd4..dfc32af 100644
--- a/chrome/renderer/url_loader_throttle_provider_impl.cc
+++ b/chrome/renderer/url_loader_throttle_provider_impl.cc
@@ -22,8 +22,8 @@
 #include "components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_throttle_manager.h"
-#include "components/safe_browsing/features.h"
-#include "components/safe_browsing/renderer/renderer_url_loader_throttle.h"
+#include "components/safe_browsing/content/renderer/renderer_url_loader_throttle.h"
+#include "components/safe_browsing/core/features.h"
 #include "content/public/common/content_features.h"
 #include "content/public/renderer/render_frame.h"
 #include "content/public/renderer/render_thread.h"
diff --git a/chrome/renderer/url_loader_throttle_provider_impl.h b/chrome/renderer/url_loader_throttle_provider_impl.h
index ae5b86d9..219f1e29 100644
--- a/chrome/renderer/url_loader_throttle_provider_impl.h
+++ b/chrome/renderer/url_loader_throttle_provider_impl.h
@@ -10,7 +10,7 @@
 
 #include "base/threading/thread_checker.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy.mojom.h"
-#include "components/safe_browsing/common/safe_browsing.mojom.h"
+#include "components/safe_browsing/core/common/safe_browsing.mojom.h"
 #include "content/public/renderer/url_loader_throttle_provider.h"
 #include "extensions/buildflags/buildflags.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
diff --git a/chrome/renderer/websocket_handshake_throttle_provider_impl.cc b/chrome/renderer/websocket_handshake_throttle_provider_impl.cc
index 84679006..8a677f4 100644
--- a/chrome/renderer/websocket_handshake_throttle_provider_impl.cc
+++ b/chrome/renderer/websocket_handshake_throttle_provider_impl.cc
@@ -6,7 +6,7 @@
 
 #include <utility>
 
-#include "components/safe_browsing/renderer/websocket_sb_handshake_throttle.h"
+#include "components/safe_browsing/content/renderer/websocket_sb_handshake_throttle.h"
 #include "content/public/common/service_names.mojom.h"
 #include "content/public/renderer/render_thread.h"
 #include "third_party/blink/public/platform/websocket_handshake_throttle.h"
diff --git a/chrome/renderer/websocket_handshake_throttle_provider_impl.h b/chrome/renderer/websocket_handshake_throttle_provider_impl.h
index 06465bed..7bf02125 100644
--- a/chrome/renderer/websocket_handshake_throttle_provider_impl.h
+++ b/chrome/renderer/websocket_handshake_throttle_provider_impl.h
@@ -9,7 +9,7 @@
 
 #include "base/macros.h"
 #include "base/threading/thread_checker.h"
-#include "components/safe_browsing/common/safe_browsing.mojom.h"
+#include "components/safe_browsing/core/common/safe_browsing.mojom.h"
 #include "content/public/renderer/websocket_handshake_throttle_provider.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/remote.h"
diff --git a/chrome/services/file_util/public/cpp/DEPS b/chrome/services/file_util/public/cpp/DEPS
index aeec82e..bcc0150 100644
--- a/chrome/services/file_util/public/cpp/DEPS
+++ b/chrome/services/file_util/public/cpp/DEPS
@@ -1,5 +1,5 @@
 include_rules = [
   "+components/safe_browsing/buildflags.h",
-  "+components/safe_browsing/proto",
+  "+components/safe_browsing/core/proto",
   "+content/public/browser",
 ]
diff --git a/chrome/services/file_util/public/cpp/sandboxed_rar_analyzer_unittest.cc b/chrome/services/file_util/public/cpp/sandboxed_rar_analyzer_unittest.cc
index 0dff105c..afa08e2 100644
--- a/chrome/services/file_util/public/cpp/sandboxed_rar_analyzer_unittest.cc
+++ b/chrome/services/file_util/public/cpp/sandboxed_rar_analyzer_unittest.cc
@@ -18,7 +18,7 @@
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/safe_browsing/archive_analyzer_results.h"
 #include "chrome/services/file_util/file_util_service.h"
-#include "components/safe_browsing/features.h"
+#include "components/safe_browsing/core/features.h"
 #include "content/public/test/browser_task_environment.h"
 #include "content/public/test/test_utils.h"
 #include "crypto/sha2.h"
diff --git a/chrome/services/file_util/public/mojom/safe_archive_analyzer.typemap b/chrome/services/file_util/public/mojom/safe_archive_analyzer.typemap
index c5207d3..640310f3 100644
--- a/chrome/services/file_util/public/mojom/safe_archive_analyzer.typemap
+++ b/chrome/services/file_util/public/mojom/safe_archive_analyzer.typemap
@@ -11,7 +11,7 @@
 public_deps = [
   "//chrome/common/safe_browsing:proto",
   "//components/safe_browsing:buildflags",
-  "//components/safe_browsing:csd_proto",
+  "//components/safe_browsing/core:csd_proto",
 ]
 
 type_mappings = [ "chrome.mojom.SafeArchiveAnalyzerResults=::safe_browsing::ArchiveAnalyzerResults" ]
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index c3e76d3..47f400e 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -196,7 +196,7 @@
     "//components/performance_manager/test_support",
     "//components/prefs:test_support",
     "//components/rappor:test_support",
-    "//components/safe_browsing/db:v4_test_util",
+    "//components/safe_browsing/core/db:v4_test_util",
     "//components/search_engines:test_support",
     "//components/sessions:test_support",
     "//components/signin/public/base:test_support",
@@ -709,7 +709,7 @@
       "//components/policy:chrome_settings_proto_generated_compile",
       "//components/resources",
       "//components/safe_browsing:buildflags",
-      "//components/safe_browsing/db:test_database_manager",
+      "//components/safe_browsing/core/db:test_database_manager",
       "//components/services/patch/public/mojom",
       "//components/services/quarantine:test_support",
       "//components/signin/core/browser",
@@ -1007,6 +1007,7 @@
       "../browser/metrics/variations/force_field_trials_browsertest.cc",
       "../browser/navigation_predictor/navigation_predictor_browsertest.cc",
       "../browser/navigation_predictor/navigation_predictor_preconnect_client_browsertest.cc",
+      "../browser/navigation_predictor/search_engine_preconnector_browsertest.cc",
       "../browser/net/chrome_accept_header_browsertest.cc",
       "../browser/net/chrome_mojo_proxy_resolver_factory_browsertest.cc",
       "../browser/net/chrome_network_delegate_browsertest.cc",
@@ -2882,27 +2883,37 @@
 group("performance_test_suite") {
   testonly = true
   deps = [
+    "//chrome/test:telemetry_perf_tests",
+  ]
+
+  data_deps = [
     "//base:base_perftests",
     "//chrome/test:angle_perftests",
     "//chrome/test:dawn_perf_tests",
-    "//chrome/test:telemetry_perf_tests",
+    "//components:components_perftests",
     "//components:components_perftests",
     "//components/tracing:tracing_perftests",
     "//gpu:command_buffer_perftests",
     "//gpu:command_buffer_perftests",
     "//gpu:gpu_perftests",
+    "//media:media_perftests",
+    "//testing:run_perf_test",
   ]
 
   if (!is_android && !is_fuchsia) {
-    deps += [
+    data_deps += [
       "//chrome/test:load_library_perf_tests",
       "//ui/views:views_perftests",
     ]
   }
 
-  data_deps = [
-    "//testing:run_perf_test",
-  ]
+  if (!is_android) {
+    data_deps += [ "//chrome/test:performance_browser_tests" ]
+  }
+
+  if (!is_ios && !is_fuchsia && !is_android) {
+    data_deps += [ "//net:net_perftests" ]
+  }
 }
 
 # Difference between this and performance_test_suite is that this runs a devil
@@ -3648,10 +3659,10 @@
     "//components/page_load_metrics/common:test_support",
     "//components/resources",
     "//components/safe_browsing:buildflags",
-    "//components/safe_browsing:features",
-    "//components/safe_browsing/db",
-    "//components/safe_browsing/db:test_database_manager",
-    "//components/safe_browsing/password_protection:mock_password_protection",
+    "//components/safe_browsing/content/password_protection:mock_password_protection",
+    "//components/safe_browsing/core:features",
+    "//components/safe_browsing/core/db",
+    "//components/safe_browsing/core/db:test_database_manager",
     "//components/services/patch/content",
     "//components/services/unzip/content",
     "//components/spellcheck:buildflags",
@@ -4915,11 +4926,10 @@
       ":test_proto",
       "../common/safe_browsing:mock_binary_feature_extractor",
       "//chrome/services/file_util/public/cpp:unit_tests",
-      "//components/safe_browsing:ping_manager_unittest",
-      "//components/safe_browsing/browser:unittests",
-      "//components/safe_browsing/db:v4_test_util",
-      "//components/safe_browsing/renderer:websocket_sb_handshake_throttle_unittest",
-      "//components/safe_browsing/triggers:ad_redirect_trigger",
+      "//components/safe_browsing/content/renderer:websocket_sb_handshake_throttle_unittest",
+      "//components/safe_browsing/content/triggers:ad_redirect_trigger",
+      "//components/safe_browsing/core:ping_manager_unittest",
+      "//components/safe_browsing/core/db:v4_test_util",
     ]
   } else if (safe_browsing_mode == 2) {
     sources += [ "../browser/safe_browsing/telemetry/android/android_telemetry_service_unittest.cc" ]
@@ -6599,7 +6609,7 @@
     ]
     deps = [
       "//base",
-      "//components/safe_browsing:csd_proto",
+      "//components/safe_browsing/core:csd_proto",
       "//crypto",
     ]
     libs = [ "wintrust.lib" ]
diff --git a/chrome/test/DEPS b/chrome/test/DEPS
index ea91348..4686ebfa 100644
--- a/chrome/test/DEPS
+++ b/chrome/test/DEPS
@@ -38,7 +38,7 @@
   "+components/policy",
   "+components/prefs",
   "+components/safe_browsing/buildflags.h",
-  "+components/safe_browsing/db",
+  "+components/safe_browsing/core/db",
   "+components/search_engines",
   "+components/services/quarantine",
   "+components/signin/public",
diff --git a/chrome/test/base/in_process_browser_test_browsertest.cc b/chrome/test/base/in_process_browser_test_browsertest.cc
index a7abb83..c8f712a 100644
--- a/chrome/test/base/in_process_browser_test_browsertest.cc
+++ b/chrome/test/base/in_process_browser_test_browsertest.cc
@@ -21,7 +21,6 @@
 #include "content/public/common/content_switches.h"
 #include "net/base/filename_util.h"
 #include "net/base/net_errors.h"
-#include "net/dns/public/resolve_error_info.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -45,8 +44,7 @@
   explicit LoadFailObserver(content::WebContents* contents)
       : content::WebContentsObserver(contents),
         failed_load_(false),
-        error_code_(net::OK),
-        resolve_error_info_(net::ResolveErrorInfo(net::OK)) {}
+        error_code_(net::OK) { }
 
   void DidFinishNavigation(
       content::NavigationHandle* navigation_handle) override {
@@ -55,21 +53,16 @@
 
     failed_load_ = true;
     error_code_ = navigation_handle->GetNetErrorCode();
-    resolve_error_info_ = navigation_handle->GetResolveErrorInfo();
     validated_url_ = navigation_handle->GetURL();
   }
 
   bool failed_load() const { return failed_load_; }
   net::Error error_code() const { return error_code_; }
-  net::ResolveErrorInfo resolve_error_info() const {
-    return resolve_error_info_;
-  }
   const GURL& validated_url() const { return validated_url_; }
 
  private:
   bool failed_load_;
   net::Error error_code_;
-  net::ResolveErrorInfo resolve_error_info_;
   GURL validated_url_;
 
   DISALLOW_COPY_AND_ASSIGN(LoadFailObserver);
@@ -91,8 +84,7 @@
     LoadFailObserver observer(contents);
     ui_test_utils::NavigateToURL(browser(), url);
     EXPECT_TRUE(observer.failed_load());
-    EXPECT_EQ(net::ERR_NAME_NOT_RESOLVED, observer.error_code());
-    EXPECT_EQ(net::ERR_NOT_IMPLEMENTED, observer.resolve_error_info().error);
+    EXPECT_EQ(net::ERR_NOT_IMPLEMENTED, observer.error_code());
     EXPECT_EQ(url, observer.validated_url());
   }
 }
diff --git a/chrome/test/ppapi/ppapi_filechooser_browsertest.cc b/chrome/test/ppapi/ppapi_filechooser_browsertest.cc
index cdace5a..10303eb 100644
--- a/chrome/test/ppapi/ppapi_filechooser_browsertest.cc
+++ b/chrome/test/ppapi/ppapi_filechooser_browsertest.cc
@@ -25,7 +25,7 @@
 #include "chrome/browser/safe_browsing/download_protection/download_protection_service.h"
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "chrome/browser/safe_browsing/test_safe_browsing_service.h"
-#include "components/safe_browsing/db/test_database_manager.h"
+#include "components/safe_browsing/core/db/test_database_manager.h"
 
 using safe_browsing::DownloadProtectionService;
 using safe_browsing::SafeBrowsingService;
diff --git a/chrome/tools/build/mac/FILES.cfg b/chrome/tools/build/mac/FILES.cfg
index 2089a89..91654c3 100644
--- a/chrome/tools/build/mac/FILES.cfg
+++ b/chrome/tools/build/mac/FILES.cfg
@@ -129,4 +129,9 @@
     'buildtype': ['dev', 'official'],
     'archive': 'devtools-frontend.zip',
   },
+  # Updater files:
+  {
+    'filename': 'GoogleUpdate.app',
+    'buildtype': ['official'],
+  }
 ]
diff --git a/chrome/tools/safe_browsing/DEPS b/chrome/tools/safe_browsing/DEPS
index 5574d3b4..c0b665e 100644
--- a/chrome/tools/safe_browsing/DEPS
+++ b/chrome/tools/safe_browsing/DEPS
@@ -1,3 +1,3 @@
 include_rules = [
-  "+components/safe_browsing/proto/csd.pb.h",
+  "+components/safe_browsing/core/proto/csd.pb.h",
 ]
diff --git a/chrome/tools/safe_browsing/sb_sigutil.cc b/chrome/tools/safe_browsing/sb_sigutil.cc
index 81973a4..8d60a65 100644
--- a/chrome/tools/safe_browsing/sb_sigutil.cc
+++ b/chrome/tools/safe_browsing/sb_sigutil.cc
@@ -16,7 +16,7 @@
 #include "base/logging.h"
 #include "base/memory/ref_counted.h"
 #include "chrome/common/safe_browsing/binary_feature_extractor.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 
 // Command-line switch for the executable to extract a signature from.
 static const char kExecutable[] = "executable";
diff --git a/chrome/utility/safe_browsing/mac/BUILD.gn b/chrome/utility/safe_browsing/mac/BUILD.gn
index 34773a2..58cb512 100644
--- a/chrome/utility/safe_browsing/mac/BUILD.gn
+++ b/chrome/utility/safe_browsing/mac/BUILD.gn
@@ -17,7 +17,7 @@
     "//base",
     "//chrome/common/safe_browsing:archive_analyzer_results",
     "//chrome/common/safe_browsing:binary_feature_extractor",
-    "//components/safe_browsing:csd_proto",
+    "//components/safe_browsing/core:csd_proto",
   ]
 }
 
diff --git a/chrome/utility/safe_browsing/mac/DEPS b/chrome/utility/safe_browsing/mac/DEPS
index 5574d3b4..c0b665e 100644
--- a/chrome/utility/safe_browsing/mac/DEPS
+++ b/chrome/utility/safe_browsing/mac/DEPS
@@ -1,3 +1,3 @@
 include_rules = [
-  "+components/safe_browsing/proto/csd.pb.h",
+  "+components/safe_browsing/core/proto/csd.pb.h",
 ]
diff --git a/chrome/utility/safe_browsing/mac/dmg_analyzer.cc b/chrome/utility/safe_browsing/mac/dmg_analyzer.cc
index aa24a64..0f50ce6 100644
--- a/chrome/utility/safe_browsing/mac/dmg_analyzer.cc
+++ b/chrome/utility/safe_browsing/mac/dmg_analyzer.cc
@@ -20,7 +20,7 @@
 #include "chrome/common/safe_browsing/mach_o_image_reader_mac.h"
 #include "chrome/utility/safe_browsing/mac/dmg_iterator.h"
 #include "chrome/utility/safe_browsing/mac/read_stream.h"
-#include "components/safe_browsing/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "crypto/secure_hash.h"
 #include "crypto/sha2.h"
 
diff --git a/components/BUILD.gn b/components/BUILD.gn
index 02b93fb48..911d999 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -259,10 +259,11 @@
       "//components/performance_manager:unit_tests",
       "//components/policy/content:unit_tests",
       "//components/previews/content:unit_tests",
-      "//components/safe_browsing/common:unit_tests",
-      "//components/safe_browsing/password_protection:password_protection_unittest",
-      "//components/safe_browsing/triggers:unit_tests",
-      "//components/safe_browsing/web_ui:unit_tests",
+      "//components/safe_browsing/content/password_protection:password_protection_unittest",
+      "//components/safe_browsing/content/triggers:unit_tests",
+      "//components/safe_browsing/content/web_ui:unit_tests",
+      "//components/safe_browsing/core/common:unit_tests",
+      "//components/safe_browsing/core/triggers:unit_tests",
       "//components/security_interstitials/content:unit_tests",
       "//components/security_state/content:unit_tests",
       "//components/services/heap_profiling:unit_tests",
@@ -397,8 +398,8 @@
   }
   if (safe_browsing_mode == 1) {
     deps += [
-      "//components/safe_browsing:verdict_cache_manager_unittest",
-      "//components/safe_browsing/db:unit_tests_desktop",
+      "//components/safe_browsing/core:verdict_cache_manager_unittest",
+      "//components/safe_browsing/core/db:unit_tests_desktop",
     ]
   } else if (safe_browsing_mode == 2) {
     deps += [ "//components/safe_browsing/android:unit_tests_mobile" ]
@@ -406,7 +407,7 @@
 
   if (!is_ios) {
     deps += [
-      "//components/safe_browsing/realtime:unit_tests",
+      "//components/safe_browsing/core/realtime:unit_tests",
       "//components/webrtc_logging/browser:unit_tests",
       "//components/webrtc_logging/common:unit_tests",
     ]
@@ -692,11 +693,11 @@
     }
 
     if (!is_android) {
-      sources += [ "safe_browsing/db/v4_store_perftest.cc" ]
+      sources += [ "safe_browsing/core/db/v4_store_perftest.cc" ]
       deps += [
-        "//components/safe_browsing/db:v4_protocol_manager_util",
-        "//components/safe_browsing/db:v4_store",
-        "//components/safe_browsing/db:v4_test_util",
+        "//components/safe_browsing/core/db:v4_protocol_manager_util",
+        "//components/safe_browsing/core/db:v4_store",
+        "//components/safe_browsing/core/db:v4_test_util",
         "//crypto",
       ]
     }
diff --git a/components/autofill_assistant/browser/actions/collect_user_data_action.cc b/components/autofill_assistant/browser/actions/collect_user_data_action.cc
index 72a3cb8a..9c032465 100644
--- a/components/autofill_assistant/browser/actions/collect_user_data_action.cc
+++ b/components/autofill_assistant/browser/actions/collect_user_data_action.cc
@@ -518,6 +518,10 @@
     for (const auto& value : user_data->additional_values_to_store) {
       delegate_->GetClientMemory()->set_additional_value(value.first,
                                                          value.second);
+      if (!value.second.empty()) {
+        processed_action_proto_->mutable_collect_user_data_result()
+            ->add_set_text_input_memory_keys(value.first);
+      }
     }
 
     processed_action_proto_->mutable_collect_user_data_result()
diff --git a/components/autofill_assistant/browser/actions/collect_user_data_action_unittest.cc b/components/autofill_assistant/browser/actions/collect_user_data_action_unittest.cc
index 36f0cb4..03d7c33 100644
--- a/components/autofill_assistant/browser/actions/collect_user_data_action_unittest.cc
+++ b/components/autofill_assistant/browser/actions/collect_user_data_action_unittest.cc
@@ -60,6 +60,7 @@
 using ::testing::Property;
 using ::testing::Return;
 using ::testing::SizeIs;
+using ::testing::UnorderedElementsAre;
 
 class CollectUserDataActionTest : public content::RenderViewHostTestHarness {
  public:
@@ -828,15 +829,28 @@
   input_field_2->set_input_type(TextInputProto::INPUT_ALPHANUMERIC);
   input_field_2->set_client_memory_key("key2");
 
+  auto* input_field_3 =
+      text_input_section->mutable_text_input_section()->add_input_fields();
+  input_field_3->set_input_type(TextInputProto::INPUT_ALPHANUMERIC);
+  input_field_3->set_client_memory_key("key3");
+
   EXPECT_FALSE(client_memory_.has_additional_value("key1"));
   EXPECT_FALSE(client_memory_.has_additional_value("key2"));
+  EXPECT_FALSE(client_memory_.has_additional_value("key3"));
   CollectUserDataAction action(&mock_action_delegate_, action_proto);
+
   EXPECT_CALL(
       callback_,
-      Run(Pointee(Property(&ProcessedActionProto::status, ACTION_APPLIED))));
+      Run(Pointee(AllOf(
+          Property(&ProcessedActionProto::status, ACTION_APPLIED),
+          Property(
+              &ProcessedActionProto::collect_user_data_result,
+              Property(&CollectUserDataResultProto::set_text_input_memory_keys,
+                       UnorderedElementsAre("key1", "key2")))))));
   action.ProcessAction(callback_.Get());
   EXPECT_EQ(*client_memory_.additional_value("key1"), "initial");
   EXPECT_EQ(*client_memory_.additional_value("key2"), "modified");
+  EXPECT_EQ(*client_memory_.additional_value("key3"), "");
 }
 
 TEST_F(CollectUserDataActionTest, AllowedBasicCardNetworks) {
diff --git a/components/autofill_assistant/browser/service.proto b/components/autofill_assistant/browser/service.proto
index 698b0ab9..8eeeaee 100644
--- a/components/autofill_assistant/browser/service.proto
+++ b/components/autofill_assistant/browser/service.proto
@@ -511,6 +511,10 @@
   optional DateTimeProto date_time_end = 8;
   // The values obtained by the generic user interface.
   optional ModelProto model = 9;
+  // The memory keys of all non-empty text inputs, corresponding to the memory
+  // keys specified in |TextInputProto|. The values themselves are stored in the
+  // client and do not leave the device.
+  repeated string set_text_input_memory_keys = 10;
 }
 
 message ProcessedActionProto {
diff --git a/components/cronet/stale_host_resolver.cc b/components/cronet/stale_host_resolver.cc
index 58fb0c8..5097042 100644
--- a/components/cronet/stale_host_resolver.cc
+++ b/components/cronet/stale_host_resolver.cc
@@ -216,10 +216,8 @@
   cache_parameters.source = net::HostResolverSource::LOCAL_ONLY;
   cache_request_ = resolver_->inner_resolver_->CreateRequest(
       host_, network_isolation_key_, net_log_, cache_parameters);
-  int error =
+  cache_error_ =
       cache_request_->Start(base::BindOnce([](int error) { NOTREACHED(); }));
-  DCHECK_NE(net::ERR_IO_PENDING, error);
-  cache_error_ = cache_request_->GetResolveErrorInfo().error;
   DCHECK_NE(net::ERR_IO_PENDING, cache_error_);
   // If it's a fresh cache hit (or literal), return it synchronously.
   if (cache_error_ != net::ERR_DNS_CACHE_MISS &&
diff --git a/components/exo/wayland/zcr_remote_shell.cc b/components/exo/wayland/zcr_remote_shell.cc
index cf7dcfd..fd860f9 100644
--- a/components/exo/wayland/zcr_remote_shell.cc
+++ b/components/exo/wayland/zcr_remote_shell.cc
@@ -550,6 +550,28 @@
   NOTIMPLEMENTED();
 }
 
+void remote_surface_set_pip_original_window(wl_client* client,
+                                            wl_resource* resource) {
+  auto* widget = GetUserDataAs<ShellSurfaceBase>(resource)->GetWidget();
+  if (!widget) {
+    LOG(ERROR) << "no widget found for setting pip original window";
+    return;
+  }
+
+  widget->GetNativeWindow()->SetProperty(ash::kPipOriginalWindowKey, true);
+}
+
+void remote_surface_unset_pip_original_window(wl_client* client,
+                                              wl_resource* resource) {
+  auto* widget = GetUserDataAs<ShellSurfaceBase>(resource)->GetWidget();
+  if (!widget) {
+    LOG(ERROR) << "no widget found for unsetting pip original window";
+    return;
+  }
+
+  widget->GetNativeWindow()->SetProperty(ash::kPipOriginalWindowKey, false);
+}
+
 const struct zcr_remote_surface_v1_interface remote_surface_implementation = {
     remote_surface_destroy,
     remote_surface_set_app_id,
@@ -596,7 +618,9 @@
     remote_surface_set_aspect_ratio,
     remote_surface_block_ime,
     remote_surface_unblock_ime,
-    remote_surface_set_accessibility_id};
+    remote_surface_set_accessibility_id,
+    remote_surface_set_pip_original_window,
+    remote_surface_unset_pip_original_window};
 
 ////////////////////////////////////////////////////////////////////////////////
 // notification_surface_interface:
diff --git a/components/exo/wayland/zcr_remote_shell.h b/components/exo/wayland/zcr_remote_shell.h
index 6b230ed..151f787 100644
--- a/components/exo/wayland/zcr_remote_shell.h
+++ b/components/exo/wayland/zcr_remote_shell.h
@@ -22,7 +22,7 @@
 namespace exo {
 namespace wayland {
 
-constexpr uint32_t kZcrRemoteShellVersion = 25;
+constexpr uint32_t kZcrRemoteShellVersion = 26;
 
 void bind_remote_shell(wl_client* client,
                        void* data,
diff --git a/components/flags_ui/resources/flags.html b/components/flags_ui/resources/flags.html
index 46df47e..52ae93a 100644
--- a/components/flags_ui/resources/flags.html
+++ b/components/flags_ui/resources/flags.html
@@ -1,5 +1,5 @@
 <!doctype html>
-<html dir="ltr" lang="en">
+<html dir="$i18n{textdirection}" lang="$i18n{language}">
 <head>
 <meta charset="utf-8">
 <if expr="not is_ios">
@@ -86,7 +86,7 @@
                     : 'experiment-switched flex-container'">
               <div class="flex">
                 <h3 class="experiment-name" jscontent="name"
-                    jsvalues="title: is_default ? '' : $i18n{experiment-enabled};
+                    jsvalues="title: is_default ? '' : '$i18n{experiment-enabled}';
                         id:internal_name + '_name'"></h3>
                 <p>
                   <span jsvalues=".innerHTML:description"></span> –
@@ -133,7 +133,7 @@
                     : 'experiment-switched flex-container'">
               <div class="flex">
                 <h3 class="experiment-name" jscontent="name"
-                    jsvalues="title: is_default ? '' : $i18n{experiment-enabled};
+                    jsvalues="title: is_default ? '' : '$i18n{experiment-enabled}';
                         id:internal_name + '_name'"></h3>
                 <p>
                   <span jsvalues=".innerHTML:description"></span> –
diff --git a/components/history/core/browser/history_service.cc b/components/history/core/browser/history_service.cc
index 337ca82..a89c3bb 100644
--- a/components/history/core/browser/history_service.cc
+++ b/components/history/core/browser/history_service.cc
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// The history system runs on a background thread so that potentially slow
+// The history system runs on a background sequence so that potentially slow
 // database operations don't delay the browser. This backend processing is
 // represented by HistoryBackend. The HistoryService's job is to dispatch to
 // that thread.
 //
-// Main thread                       History thread
+// Main thread                       backend_task_runner_
 // -----------                       --------------
 // HistoryService <----------------> HistoryBackend
 //                                   -> HistoryDatabase
@@ -71,7 +71,7 @@
 
 // static
 const base::Feature HistoryService::kHistoryServiceUsesTaskScheduler{
-    "HistoryServiceUsesTaskScheduler", base::FEATURE_DISABLED_BY_DEFAULT};
+    "HistoryServiceUsesTaskScheduler", base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Sends messages from the backend to us on the main thread. This must be a
 // separate class from the history service so that it can hold a reference to
diff --git a/components/history/core/browser/history_service.h b/components/history/core/browser/history_service.h
index b8d30d6..88dbf07 100644
--- a/components/history/core/browser/history_service.h
+++ b/components/history/core/browser/history_service.h
@@ -468,7 +468,7 @@
 
   // Generic Stuff -------------------------------------------------------------
 
-  // Schedules a HistoryDBTask for running on the history backend thread. See
+  // Schedules a HistoryDBTask for running on the history backend. See
   // HistoryDBTask for details on what this does. Takes ownership of |task|.
   virtual base::CancelableTaskTracker::TaskId ScheduleDBTask(
       const base::Location& from_here,
@@ -493,15 +493,15 @@
 
   // Testing -------------------------------------------------------------------
 
-  // Runs |flushed| after bouncing off the history thread.
+  // Runs |flushed| after the backend has processed all other pre-existing
+  // tasks.
   void FlushForTest(base::OnceClosure flushed);
 
   // Designed for unit tests, this passes the given task on to the history
   // backend to be called once the history backend has terminated. This allows
-  // callers to know when the history thread is complete and the database files
-  // can be deleted and the next test run. Otherwise, the history thread may
-  // still be running, causing problems in subsequent tests.
-  //
+  // callers to know when the history backend has been safely deleted and the
+  // database files can be deleted and the next test run.
+
   // There can be only one closing task, so this will override any previously
   // set task. We will take ownership of the pointer and delete it when done.
   // The task will be run on the calling thread (this function is threadsafe).
@@ -511,9 +511,9 @@
   // into the database. This assumes the URL doesn't exist in the database
   //
   // Calling this function many times may be slow because each call will
-  // dispatch to the history thread and will be a separate database
-  // transaction. If this functionality is needed for importing many URLs,
-  // callers should use AddPagesWithDetails() instead.
+  // post a separate database transaction in a task. If this functionality
+  // is needed for importing many URLs, callers should use AddPagesWithDetails()
+  // instead.
   //
   // Note that this routine (and AddPageWithDetails()) always adds a single
   // visit using the |last_visit| timestamp, and a PageTransition type of LINK,
@@ -594,9 +594,9 @@
   // that is only set by unittests which causes the backend to not init its DB.
   bool Init(bool no_db, const HistoryDatabaseParams& history_database_params);
 
-  // Called by the HistoryURLProvider class to schedule an autocomplete, it
-  // will be called back on the internal history thread with the history
-  // database so it can query. See history_url_provider.h for a diagram.
+  // Called by the HistoryURLProvider class to schedule an autocomplete, it will
+  // be called back with the history database so it can query. See
+  // history_url_provider.h for a diagram.
   void ScheduleAutocomplete(
       base::OnceCallback<void(HistoryBackend*, URLDatabase*)> callback);
 
@@ -834,8 +834,8 @@
   void NotifyProfileError(sql::InitStatus init_status,
                           const std::string& diagnostics);
 
-  // Call to schedule a given task for running on the history thread with the
-  // specified priority. The task will have ownership taken.
+  // Call to post a given task for running on the history backend sequence with
+  // the specified priority. The task will have ownership taken.
   void ScheduleTask(SchedulePriority priority, base::OnceClosure task);
 
   // Called when the favicons for the given page URLs (e.g.
@@ -858,17 +858,16 @@
   // Cleanup() is called.
   scoped_refptr<base::SequencedTaskRunner> backend_task_runner_;
 
-  // This class has most of the implementation and runs on the 'thread_'.
-  // You MUST communicate with this class ONLY through the thread_'s
-  // task_runner().
+  // This class has most of the implementation. You MUST communicate with this
+  // class ONLY through |backend_task_runner_|.
   //
   // This pointer will be null once Cleanup() has been called, meaning no
-  // more calls should be made to the history thread.
+  // more tasks should be scheduled.
   scoped_refptr<HistoryBackend> history_backend_;
 
   // A cache of the user-typed URLs kept in memory that is used by the
   // autocomplete system. This will be null until the database has been created
-  // on the background thread.
+  // in the backend.
   // TODO(mrossetti): Consider changing ownership. See http://crbug.com/138321
   std::unique_ptr<InMemoryHistoryBackend> in_memory_backend_;
 
diff --git a/components/invalidation/impl/BUILD.gn b/components/invalidation/impl/BUILD.gn
index 12db09e9..e9a992d 100644
--- a/components/invalidation/impl/BUILD.gn
+++ b/components/invalidation/impl/BUILD.gn
@@ -50,10 +50,10 @@
     "invalidator_storage.h",
     "mock_ack_handler.cc",
     "mock_ack_handler.h",
-    "per_user_topic_registration_request.cc",
-    "per_user_topic_registration_request.h",
     "per_user_topic_subscription_manager.cc",
     "per_user_topic_subscription_manager.h",
+    "per_user_topic_subscription_request.cc",
+    "per_user_topic_subscription_request.h",
     "profile_identity_provider.cc",
     "profile_identity_provider.h",
     "profile_invalidation_provider.cc",
@@ -138,8 +138,8 @@
     "fcm_network_handler_unittests.cc",
     "invalidation_logger_unittest.cc",
     "invalidator_registrar_with_memory_unittest.cc",
-    "per_user_topic_registration_request_unittest.cc",
     "per_user_topic_subscription_manager_unittest.cc",
+    "per_user_topic_subscription_request_unittest.cc",
   ]
   deps = [
     ":impl",
diff --git a/components/invalidation/impl/per_user_topic_registration_request.cc b/components/invalidation/impl/per_user_topic_registration_request.cc
deleted file mode 100644
index ff981ba..0000000
--- a/components/invalidation/impl/per_user_topic_registration_request.cc
+++ /dev/null
@@ -1,381 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/invalidation/impl/per_user_topic_registration_request.h"
-
-#include <memory>
-
-#include "base/bind.h"
-#include "base/json/json_writer.h"
-#include "base/metrics/histogram_functions.h"
-#include "base/strings/stringprintf.h"
-#include "base/values.h"
-#include "components/sync/base/model_type.h"
-#include "net/http/http_status_code.h"
-#include "net/url_request/url_fetcher.h"
-
-using net::HttpRequestHeaders;
-
-namespace {
-
-const char kPublicTopicNameKey[] = "publicTopicName";
-const char kPrivateTopicNameKey[] = "privateTopicName";
-
-const std::string* GetTopicName(const base::Value& value) {
-  if (!value.is_dict())
-    return nullptr;
-  if (value.FindBoolKey("isPublic").value_or(false))
-    return value.FindStringKey(kPublicTopicNameKey);
-  return value.FindStringKey(kPrivateTopicNameKey);
-}
-
-bool IsNetworkError(int net_error) {
-  // Note: ERR_HTTP_RESPONSE_CODE_FAILURE isn't a real network error - it
-  // indicates that the network request itself succeeded, but we received an
-  // HTTP error. The alternative to special-casing this error would be to call
-  // SetAllowHttpErrorResults(true) on the SimpleUrlLoader, but that also means
-  // we'd download the response body in case of HTTP errors, which we don't
-  // need.
-  return net_error != net::OK &&
-         net_error != net::ERR_HTTP_RESPONSE_CODE_FAILURE;
-}
-
-// Subscription status values for UMA_HISTOGRAM.
-enum class SubscriptionStatus {
-  kSuccess = 0,
-  kNetworkFailure = 1,
-  kHttpFailure = 2,
-  kParsingFailure = 3,
-  kFailure = 4,
-  kMaxValue = kFailure,
-};
-
-void RecordRequestStatus(
-    SubscriptionStatus status,
-    syncer::PerUserTopicRegistrationRequest::RequestType type,
-    const std::string& topic,
-    int net_error = net::OK,
-    int response_code = 200) {
-  switch (type) {
-    case syncer::PerUserTopicRegistrationRequest::SUBSCRIBE: {
-      base::UmaHistogramEnumeration(
-          "FCMInvalidations.SubscriptionRequestStatus", status);
-      break;
-    }
-    case syncer::PerUserTopicRegistrationRequest::UNSUBSCRIBE: {
-      base::UmaHistogramEnumeration(
-          "FCMInvalidations.UnsubscriptionRequestStatus", status);
-      break;
-    }
-  }
-  if (type != syncer::PerUserTopicRegistrationRequest::SUBSCRIBE) {
-    return;
-  }
-
-  if (IsNetworkError(net_error)) {
-    // Tracks the cases, when request fails due to network error.
-    base::UmaHistogramSparse("FCMInvalidations.FailedSubscriptionsErrorCode",
-                             -net_error);
-    DVLOG(1) << "Subscription request failed with error: " << net_error << ": "
-             << net::ErrorToString(net_error);
-  } else {
-    // Log a histogram to track response success vs. failure rates.
-    base::UmaHistogramSparse("FCMInvalidations.SubscriptionResponseCode",
-                             response_code);
-    // If the topic corresponds to a Sync ModelType, use that as the histogram
-    // suffix. Otherwise (e.g. Drive or Policy), just use "OTHER" for now.
-    // TODO(crbug.com/1029698): Depending on sync is a layering violation.
-    // Eventually the "whitelisted for metrics" bit should be part of a Topic.
-    syncer::ModelType model_type;  // Unused.
-    std::string suffix =
-        syncer::NotificationTypeToRealModelType(topic, &model_type) ? topic
-                                                                    : "OTHER";
-    base::UmaHistogramSparse(
-        "FCMInvalidations.SubscriptionResponseCodeForTopic." + suffix,
-        response_code);
-  }
-}
-
-}  // namespace
-
-namespace syncer {
-
-PerUserTopicRegistrationRequest::PerUserTopicRegistrationRequest() {}
-
-PerUserTopicRegistrationRequest::~PerUserTopicRegistrationRequest() = default;
-
-void PerUserTopicRegistrationRequest::Start(
-    CompletedCallback callback,
-    network::mojom::URLLoaderFactory* loader_factory) {
-  DCHECK(request_completed_callback_.is_null()) << "Request already running!";
-  request_completed_callback_ = std::move(callback);
-
-  simple_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
-      loader_factory,
-      base::BindOnce(&PerUserTopicRegistrationRequest::OnURLFetchComplete,
-                     weak_ptr_factory_.GetWeakPtr()));
-}
-
-void PerUserTopicRegistrationRequest::OnURLFetchComplete(
-    std::unique_ptr<std::string> response_body) {
-  int response_code = 0;
-  if (simple_loader_->ResponseInfo() &&
-      simple_loader_->ResponseInfo()->headers) {
-    response_code = simple_loader_->ResponseInfo()->headers->response_code();
-  }
-  OnURLFetchCompleteInternal(simple_loader_->NetError(), response_code,
-                             std::move(response_body));
-}
-
-void PerUserTopicRegistrationRequest::OnURLFetchCompleteInternal(
-    int net_error,
-    int response_code,
-    std::unique_ptr<std::string> response_body) {
-  if (IsNetworkError(net_error)) {
-    std::move(request_completed_callback_)
-        .Run(Status(StatusCode::FAILED, base::StringPrintf("Network Error")),
-             std::string());
-    RecordRequestStatus(SubscriptionStatus::kNetworkFailure, type_, topic_,
-                        net_error, response_code);
-    return;
-  }
-
-  if (response_code != net::HTTP_OK) {
-    StatusCode status = StatusCode::FAILED;
-    if (response_code == net::HTTP_UNAUTHORIZED) {
-      status = StatusCode::AUTH_FAILURE;
-    } else if (response_code >= 400 && response_code <= 499) {
-      status = StatusCode::FAILED_NON_RETRIABLE;
-    }
-    RecordRequestStatus(SubscriptionStatus::kHttpFailure, type_, topic_,
-                        net_error, response_code);
-    std::move(request_completed_callback_)
-        .Run(
-            Status(status, base::StringPrintf("HTTP Error: %d", response_code)),
-            std::string());
-    return;
-  }
-
-  if (type_ == UNSUBSCRIBE) {
-    // No response body expected for DELETE requests.
-    RecordRequestStatus(SubscriptionStatus::kSuccess, type_, topic_, net_error,
-                        response_code);
-    std::move(request_completed_callback_)
-        .Run(Status(StatusCode::SUCCESS, std::string()), std::string());
-    return;
-  }
-
-  if (!response_body || response_body->empty()) {
-    RecordRequestStatus(SubscriptionStatus::kParsingFailure, type_, topic_,
-                        net_error, response_code);
-    std::move(request_completed_callback_)
-        .Run(Status(StatusCode::FAILED, base::StringPrintf("Body missing")),
-             std::string());
-    return;
-  }
-
-  data_decoder::DataDecoder::ParseJsonIsolated(
-      *response_body,
-      base::BindOnce(&PerUserTopicRegistrationRequest::OnJsonParse,
-                     weak_ptr_factory_.GetWeakPtr()));
-}
-
-void PerUserTopicRegistrationRequest::OnJsonParse(
-    data_decoder::DataDecoder::ValueOrError result) {
-  if (!result.value) {
-    RecordRequestStatus(SubscriptionStatus::kParsingFailure, type_, topic_);
-    std::move(request_completed_callback_)
-        .Run(Status(StatusCode::FAILED, base::StringPrintf("Body parse error")),
-             std::string());
-    return;
-  }
-
-  const std::string* topic_name = GetTopicName(*result.value);
-  if (topic_name) {
-    RecordRequestStatus(SubscriptionStatus::kSuccess, type_, topic_);
-    std::move(request_completed_callback_)
-        .Run(Status(StatusCode::SUCCESS, std::string()), *topic_name);
-  } else {
-    RecordRequestStatus(SubscriptionStatus::kParsingFailure, type_, topic_);
-    std::move(request_completed_callback_)
-        .Run(Status(StatusCode::FAILED,
-                    base::StringPrintf("Missing topic name")),
-             std::string());
-  }
-}
-
-PerUserTopicRegistrationRequest::Builder::Builder() = default;
-PerUserTopicRegistrationRequest::Builder::Builder(
-    PerUserTopicRegistrationRequest::Builder&&) = default;
-PerUserTopicRegistrationRequest::Builder::~Builder() = default;
-
-std::unique_ptr<PerUserTopicRegistrationRequest>
-PerUserTopicRegistrationRequest::Builder::Build() const {
-  DCHECK(!scope_.empty());
-  auto request = base::WrapUnique(new PerUserTopicRegistrationRequest());
-
-  std::string url;
-  switch (type_) {
-    case SUBSCRIBE:
-      url = base::StringPrintf(
-          "%s/v1/perusertopics/%s/rel/topics/?subscriber_token=%s",
-          scope_.c_str(), project_id_.c_str(), instance_id_token_.c_str());
-      break;
-    case UNSUBSCRIBE:
-      std::string public_param;
-      if (topic_is_public_) {
-        public_param = "subscription.is_public=true&";
-      }
-      url = base::StringPrintf(
-          "%s/v1/perusertopics/%s/rel/topics/%s?%ssubscriber_token=%s",
-          scope_.c_str(), project_id_.c_str(), topic_.c_str(),
-          public_param.c_str(), instance_id_token_.c_str());
-      break;
-  }
-  GURL full_url(url);
-
-  DCHECK(full_url.is_valid());
-
-  request->url_ = full_url;
-  request->type_ = type_;
-  request->topic_ = topic_;
-
-  std::string body;
-  if (type_ == SUBSCRIBE)
-    body = BuildBody();
-  net::HttpRequestHeaders headers = BuildHeaders();
-  request->simple_loader_ = BuildURLFetcher(headers, body, full_url);
-
-  // Log the request for debugging network issues.
-  DVLOG(1) << "Building a subscription request to " << full_url << ":\n"
-           << headers.ToString() << "\n"
-           << body;
-  return request;
-}
-
-PerUserTopicRegistrationRequest::Builder&
-PerUserTopicRegistrationRequest::Builder::SetInstanceIdToken(
-    const std::string& token) {
-  instance_id_token_ = token;
-  return *this;
-}
-
-PerUserTopicRegistrationRequest::Builder&
-PerUserTopicRegistrationRequest::Builder::SetScope(const std::string& scope) {
-  scope_ = scope;
-  return *this;
-}
-
-PerUserTopicRegistrationRequest::Builder&
-PerUserTopicRegistrationRequest::Builder::SetAuthenticationHeader(
-    const std::string& auth_header) {
-  auth_header_ = auth_header;
-  return *this;
-}
-
-PerUserTopicRegistrationRequest::Builder&
-PerUserTopicRegistrationRequest::Builder::SetPublicTopicName(
-    const Topic& topic) {
-  topic_ = topic;
-  return *this;
-}
-
-PerUserTopicRegistrationRequest::Builder&
-PerUserTopicRegistrationRequest::Builder::SetProjectId(
-    const std::string& project_id) {
-  project_id_ = project_id;
-  return *this;
-}
-
-PerUserTopicRegistrationRequest::Builder&
-PerUserTopicRegistrationRequest::Builder::SetType(RequestType type) {
-  type_ = type;
-  return *this;
-}
-
-PerUserTopicRegistrationRequest::Builder&
-PerUserTopicRegistrationRequest::Builder::SetTopicIsPublic(
-    bool topic_is_public) {
-  topic_is_public_ = topic_is_public;
-  return *this;
-}
-
-HttpRequestHeaders PerUserTopicRegistrationRequest::Builder::BuildHeaders()
-    const {
-  HttpRequestHeaders headers;
-  if (!auth_header_.empty()) {
-    headers.SetHeader(HttpRequestHeaders::kAuthorization, auth_header_);
-  }
-  return headers;
-}
-
-std::string PerUserTopicRegistrationRequest::Builder::BuildBody() const {
-  base::DictionaryValue request;
-
-  request.SetString("public_topic_name", topic_);
-  if (topic_is_public_) {
-    request.SetBoolean("is_public", true);
-  }
-
-  std::string request_json;
-  bool success = base::JSONWriter::Write(request, &request_json);
-  DCHECK(success);
-  return request_json;
-}
-
-std::unique_ptr<network::SimpleURLLoader>
-PerUserTopicRegistrationRequest::Builder::BuildURLFetcher(
-    const HttpRequestHeaders& headers,
-    const std::string& body,
-    const GURL& url) const {
-  net::NetworkTrafficAnnotationTag traffic_annotation =
-      net::DefineNetworkTrafficAnnotation("per_user_topic_registration_request",
-                                          R"(
-        semantics {
-          sender: "Register the Sync client for listening of the specific topic"
-          description:
-            "Chromium can receive Sync invalidations via FCM messages."
-            "This request registers the client for receiving messages for the"
-            "concrete topic. In case of Chrome Sync topic is a ModelType,"
-            "e.g. BOOKMARK"
-          trigger:
-            "Subscription takes place only once per profile per topic. "
-          data:
-            "An OAuth2 token is sent as an authorization header."
-          destination: GOOGLE_OWNED_SERVICE
-        }
-        policy {
-          cookies_allowed: NO
-          setting:
-            "This feature can not be disabled by settings now"
-          chrome_policy: {
-             SyncDisabled {
-               policy_options {mode: MANDATORY}
-               SyncDisabled: false
-             }
-          }
-        })");
-
-  auto request = std::make_unique<network::ResourceRequest>();
-  switch (type_) {
-    case SUBSCRIBE:
-      request->method = "POST";
-      break;
-    case UNSUBSCRIBE:
-      request->method = "DELETE";
-      break;
-  }
-  request->url = url;
-  request->headers = headers;
-  // TODO(crbug.com/1020117): Should we set request->credentials_mode to kOmit,
-  // to match "cookies_allowed: NO" above?
-
-  std::unique_ptr<network::SimpleURLLoader> url_loader =
-      network::SimpleURLLoader::Create(std::move(request), traffic_annotation);
-  url_loader->AttachStringForUpload(body, "application/json; charset=UTF-8");
-
-  return url_loader;
-}
-
-}  // namespace syncer
diff --git a/components/invalidation/impl/per_user_topic_registration_request.h b/components/invalidation/impl/per_user_topic_registration_request.h
deleted file mode 100644
index cf49ad17..0000000
--- a/components/invalidation/impl/per_user_topic_registration_request.h
+++ /dev/null
@@ -1,114 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_INVALIDATION_IMPL_PER_USER_TOPIC_REGISTRATION_REQUEST_H_
-#define COMPONENTS_INVALIDATION_IMPL_PER_USER_TOPIC_REGISTRATION_REQUEST_H_
-
-#include <memory>
-#include <string>
-#include <utility>
-
-#include "base/callback.h"
-#include "base/memory/weak_ptr.h"
-#include "base/optional.h"
-#include "base/time/time.h"
-#include "components/invalidation/impl/status.h"
-#include "components/invalidation/public/invalidation_util.h"
-#include "net/http/http_request_headers.h"
-#include "services/data_decoder/public/cpp/data_decoder.h"
-#include "services/network/public/cpp/simple_url_loader.h"
-#include "services/network/public/mojom/url_loader_factory.mojom.h"
-
-namespace syncer {
-
-// A single request to register against per user topic service.
-class PerUserTopicRegistrationRequest {
- public:
-  // The request result consists of the request status and name of the private
-  // topic. The |topic_name| will be empty in the case of error.
-  using CompletedCallback =
-      base::OnceCallback<void(const Status& status,
-                              const std::string& topic_name)>;
-  enum RequestType { SUBSCRIBE, UNSUBSCRIBE };
-
-  // Builds authenticated PerUserTopicRegistrationRequests.
-  class Builder {
-   public:
-    Builder();
-    Builder(Builder&&);
-    ~Builder();
-
-    // Builds a Request object in order to perform the registration.
-    std::unique_ptr<PerUserTopicRegistrationRequest> Build() const;
-
-    Builder& SetInstanceIdToken(const std::string& token);
-    Builder& SetScope(const std::string& scope);
-    Builder& SetAuthenticationHeader(const std::string& auth_header);
-
-    Builder& SetPublicTopicName(const Topic& topic);
-    Builder& SetProjectId(const std::string& project_id);
-
-    Builder& SetType(RequestType type);
-
-    Builder& SetTopicIsPublic(bool topic_is_public);
-
-   private:
-    net::HttpRequestHeaders BuildHeaders() const;
-    std::string BuildBody() const;
-    std::unique_ptr<network::SimpleURLLoader> BuildURLFetcher(
-        const net::HttpRequestHeaders& headers,
-        const std::string& body,
-        const GURL& url) const;
-
-    // GCM subscription token obtained from GCM driver (instanceID::getToken()).
-    std::string instance_id_token_;
-    Topic topic_;
-    std::string project_id_;
-
-    std::string scope_;
-    std::string auth_header_;
-    RequestType type_;
-    bool topic_is_public_ = false;
-
-    DISALLOW_COPY_AND_ASSIGN(Builder);
-  };
-
-  ~PerUserTopicRegistrationRequest();
-
-  // Starts an async request. The callback is invoked when the request succeeds
-  // or fails. The callback is not called if the request is destroyed.
-  void Start(CompletedCallback callback,
-             network::mojom::URLLoaderFactory* loader_factory);
-
-  GURL GetUrlForTesting() const { return url_; }
-
- private:
-  PerUserTopicRegistrationRequest();
-  void OnURLFetchComplete(std::unique_ptr<std::string> response_body);
-  void OnURLFetchCompleteInternal(int net_error,
-                                  int response_code,
-                                  std::unique_ptr<std::string> response_body);
-
-  void OnJsonParse(data_decoder::DataDecoder::ValueOrError result);
-
-  // The fetcher for subscribing.
-  std::unique_ptr<network::SimpleURLLoader> simple_loader_;
-
-  // The callback to notify when URLFetcher finished and results are available.
-  // When the request is finished/aborted/destroyed, it's called in the dtor!
-  CompletedCallback request_completed_callback_;
-
-  // Full URL. Used in tests only.
-  GURL url_;
-  RequestType type_;
-  std::string topic_;
-
-  base::WeakPtrFactory<PerUserTopicRegistrationRequest> weak_ptr_factory_{this};
-
-  DISALLOW_COPY_AND_ASSIGN(PerUserTopicRegistrationRequest);
-};
-
-}  // namespace syncer
-
-#endif  // COMPONENTS_INVALIDATION_IMPL_PER_USER_TOPIC_REGISTRATION_REQUEST_H_
diff --git a/components/invalidation/impl/per_user_topic_registration_request_unittest.cc b/components/invalidation/impl/per_user_topic_registration_request_unittest.cc
deleted file mode 100644
index d5d8bb071..0000000
--- a/components/invalidation/impl/per_user_topic_registration_request_unittest.cc
+++ /dev/null
@@ -1,330 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/invalidation/impl/per_user_topic_registration_request.h"
-
-#include "base/bind.h"
-#include "base/json/json_reader.h"
-#include "base/memory/ptr_util.h"
-#include "base/run_loop.h"
-#include "base/strings/stringprintf.h"
-#include "base/test/gtest_util.h"
-#include "base/test/mock_callback.h"
-#include "base/test/task_environment.h"
-#include "base/test/test_simple_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/values.h"
-#include "net/url_request/test_url_fetcher_factory.h"
-#include "net/url_request/url_request_test_util.h"
-#include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
-#include "services/network/test/test_url_loader_factory.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace syncer {
-
-namespace {
-
-using testing::_;
-using testing::SaveArg;
-
-MATCHER_P(EqualsJSON, json, "equals JSON") {
-  std::unique_ptr<base::Value> expected =
-      base::JSONReader::ReadDeprecated(json);
-  if (!expected) {
-    *result_listener << "INTERNAL ERROR: couldn't parse expected JSON";
-    return false;
-  }
-
-  std::string err_msg;
-  int err_line, err_col;
-  std::unique_ptr<base::Value> actual =
-      base::JSONReader::ReadAndReturnErrorDeprecated(
-          arg, base::JSON_PARSE_RFC, nullptr, &err_msg, &err_line, &err_col);
-  if (!actual) {
-    *result_listener << "input:" << err_line << ":" << err_col << ": "
-                     << "parse error: " << err_msg;
-    return false;
-  }
-  return *expected == *actual;
-}
-
-network::mojom::URLResponseHeadPtr CreateHeadersForTest(int responce_code) {
-  auto head = network::mojom::URLResponseHead::New();
-  head->headers = new net::HttpResponseHeaders(base::StringPrintf(
-      "HTTP/1.1 %d OK\nContent-type: text/html\n\n", responce_code));
-  head->mime_type = "text/html";
-  return head;
-}
-
-}  // namespace
-
-class PerUserTopicRegistrationRequestTest : public testing::Test {
- public:
-  PerUserTopicRegistrationRequestTest() {}
-
-  GURL url(PerUserTopicRegistrationRequest* request) {
-    return request->GetUrlForTesting();
-  }
-
-  network::TestURLLoaderFactory* url_loader_factory() {
-    return &url_loader_factory_;
-  }
-
- private:
-  base::test::SingleThreadTaskEnvironment task_environment_;
-  data_decoder::test::InProcessDataDecoder in_process_data_decoder_;
-  network::TestURLLoaderFactory url_loader_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(PerUserTopicRegistrationRequestTest);
-};
-
-TEST_F(PerUserTopicRegistrationRequestTest,
-       ShouldNotInvokeCallbackWhenCancelled) {
-  std::string token = "1234567890";
-  std::string url = "http://valid-url.test";
-  std::string topic = "test";
-  std::string project_id = "smarty-pants-12345";
-  PerUserTopicRegistrationRequest::RequestType type =
-      PerUserTopicRegistrationRequest::SUBSCRIBE;
-
-  base::MockCallback<PerUserTopicRegistrationRequest::CompletedCallback>
-      callback;
-  EXPECT_CALL(callback, Run(_, _)).Times(0);
-
-  PerUserTopicRegistrationRequest::Builder builder;
-  std::unique_ptr<PerUserTopicRegistrationRequest> request =
-      builder.SetInstanceIdToken(token)
-          .SetScope(url)
-          .SetPublicTopicName(topic)
-          .SetProjectId(project_id)
-          .SetType(type)
-          .Build();
-  request->Start(callback.Get(), url_loader_factory());
-  base::RunLoop().RunUntilIdle();
-
-  // Destroy the request before getting any response.
-  request.reset();
-}
-
-TEST_F(PerUserTopicRegistrationRequestTest, ShouldSubscribeWithoutErrors) {
-  std::string token = "1234567890";
-  std::string base_url = "http://valid-url.test";
-  std::string topic = "test";
-  std::string project_id = "smarty-pants-12345";
-  PerUserTopicRegistrationRequest::RequestType type =
-      PerUserTopicRegistrationRequest::SUBSCRIBE;
-
-  base::MockCallback<PerUserTopicRegistrationRequest::CompletedCallback>
-      callback;
-  Status status(StatusCode::FAILED, "initial");
-  std::string private_topic;
-  EXPECT_CALL(callback, Run(_, _))
-      .WillOnce(DoAll(SaveArg<0>(&status), SaveArg<1>(&private_topic)));
-
-  PerUserTopicRegistrationRequest::Builder builder;
-  std::unique_ptr<PerUserTopicRegistrationRequest> request =
-      builder.SetInstanceIdToken(token)
-          .SetScope(base_url)
-          .SetPublicTopicName(topic)
-          .SetProjectId(project_id)
-          .SetType(type)
-          .Build();
-  std::string response_body = R"(
-    {
-      "privateTopicName": "test-pr"
-    }
-  )";
-
-  network::URLLoaderCompletionStatus response_status(net::OK);
-  response_status.decoded_body_length = response_body.size();
-
-  url_loader_factory()->AddResponse(url(request.get()),
-                                    CreateHeadersForTest(net::HTTP_OK),
-                                    response_body, response_status);
-  request->Start(callback.Get(), url_loader_factory());
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_EQ(status.code, StatusCode::SUCCESS);
-  EXPECT_EQ(private_topic, "test-pr");
-}
-
-TEST_F(PerUserTopicRegistrationRequestTest,
-       ShouleNotSubscribeWhenNetworkProblem) {
-  std::string token = "1234567890";
-  std::string base_url = "http://valid-url.test";
-  std::string topic = "test";
-  std::string project_id = "smarty-pants-12345";
-  PerUserTopicRegistrationRequest::RequestType type =
-      PerUserTopicRegistrationRequest::SUBSCRIBE;
-
-  base::MockCallback<PerUserTopicRegistrationRequest::CompletedCallback>
-      callback;
-  Status status(StatusCode::FAILED, "initial");
-  std::string private_topic;
-  EXPECT_CALL(callback, Run(_, _))
-      .WillOnce(DoAll(SaveArg<0>(&status), SaveArg<1>(&private_topic)));
-
-  PerUserTopicRegistrationRequest::Builder builder;
-  std::unique_ptr<PerUserTopicRegistrationRequest> request =
-      builder.SetInstanceIdToken(token)
-          .SetScope(base_url)
-          .SetPublicTopicName(topic)
-          .SetProjectId(project_id)
-          .SetType(type)
-          .Build();
-  std::string response_body = R"(
-    {
-      "privateTopicName": "test-pr"
-    }
-  )";
-
-  network::URLLoaderCompletionStatus response_status(net::ERR_TIMED_OUT);
-  response_status.decoded_body_length = response_body.size();
-
-  url_loader_factory()->AddResponse(url(request.get()),
-                                    CreateHeadersForTest(net::HTTP_OK),
-                                    response_body, response_status);
-  request->Start(callback.Get(), url_loader_factory());
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_EQ(status.code, StatusCode::FAILED);
-}
-
-TEST_F(PerUserTopicRegistrationRequestTest,
-       ShouldNotSubscribeWhenWrongResponse) {
-  std::string token = "1234567890";
-  std::string base_url = "http://valid-url.test";
-  std::string topic = "test";
-  std::string project_id = "smarty-pants-12345";
-  PerUserTopicRegistrationRequest::RequestType type =
-      PerUserTopicRegistrationRequest::SUBSCRIBE;
-
-  base::MockCallback<PerUserTopicRegistrationRequest::CompletedCallback>
-      callback;
-  Status status(StatusCode::SUCCESS, "initial");
-  std::string private_topic;
-
-  EXPECT_CALL(callback, Run(_, _))
-      .WillOnce(DoAll(SaveArg<0>(&status), SaveArg<1>(&private_topic)));
-
-  PerUserTopicRegistrationRequest::Builder builder;
-  std::unique_ptr<PerUserTopicRegistrationRequest> request =
-      builder.SetInstanceIdToken(token)
-          .SetScope(base_url)
-          .SetPublicTopicName(topic)
-          .SetProjectId(project_id)
-          .SetType(type)
-          .Build();
-  std::string response_body = R"(
-    {}
-  )";
-
-  network::URLLoaderCompletionStatus response_status(net::OK);
-  response_status.decoded_body_length = response_body.size();
-
-  url_loader_factory()->AddResponse(url(request.get()),
-                                    CreateHeadersForTest(net::HTTP_OK),
-                                    response_body, response_status);
-  request->Start(callback.Get(), url_loader_factory());
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_EQ(status.code, StatusCode::FAILED);
-  EXPECT_EQ(status.message, "Missing topic name");
-}
-
-TEST_F(PerUserTopicRegistrationRequestTest, ShouldUnsubscribe) {
-  std::string token = "1234567890";
-  std::string base_url = "http://valid-url.test";
-  std::string topic = "test";
-  std::string project_id = "smarty-pants-12345";
-  PerUserTopicRegistrationRequest::RequestType type =
-      PerUserTopicRegistrationRequest::UNSUBSCRIBE;
-
-  base::MockCallback<PerUserTopicRegistrationRequest::CompletedCallback>
-      callback;
-  Status status(StatusCode::FAILED, "initial");
-  std::string private_topic;
-
-  EXPECT_CALL(callback, Run(_, _))
-      .WillOnce(DoAll(SaveArg<0>(&status), SaveArg<1>(&private_topic)));
-
-  PerUserTopicRegistrationRequest::Builder builder;
-  std::unique_ptr<PerUserTopicRegistrationRequest> request =
-      builder.SetInstanceIdToken(token)
-          .SetScope(base_url)
-          .SetPublicTopicName(topic)
-          .SetProjectId(project_id)
-          .SetType(type)
-          .Build();
-  std::string response_body = R"(
-    {}
-  )";
-
-  network::URLLoaderCompletionStatus response_status(net::OK);
-  response_status.decoded_body_length = response_body.size();
-
-  url_loader_factory()->AddResponse(url(request.get()),
-                                    CreateHeadersForTest(net::HTTP_OK),
-                                    response_body, response_status);
-  request->Start(callback.Get(), url_loader_factory());
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_EQ(status.code, StatusCode::SUCCESS);
-  EXPECT_EQ(status.message, std::string());
-}
-
-class PerUserTopicRegistrationRequestParamTest
-    : public PerUserTopicRegistrationRequestTest,
-      public testing::WithParamInterface<net::HttpStatusCode> {
- public:
-  PerUserTopicRegistrationRequestParamTest() = default;
-  ~PerUserTopicRegistrationRequestParamTest() override = default;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(PerUserTopicRegistrationRequestParamTest);
-};
-
-TEST_P(PerUserTopicRegistrationRequestParamTest,
-       ShouldNotSubscribeWhenNonRepeatableError) {
-  std::string token = "1234567890";
-  std::string base_url = "http://valid-url.test";
-  std::string topic = "test";
-  std::string project_id = "smarty-pants-12345";
-  PerUserTopicRegistrationRequest::RequestType type =
-      PerUserTopicRegistrationRequest::SUBSCRIBE;
-
-  base::MockCallback<PerUserTopicRegistrationRequest::CompletedCallback>
-      callback;
-  Status status(StatusCode::FAILED, "initial");
-  std::string private_topic;
-  EXPECT_CALL(callback, Run(_, _))
-      .WillOnce(DoAll(SaveArg<0>(&status), SaveArg<1>(&private_topic)));
-
-  PerUserTopicRegistrationRequest::Builder builder;
-  std::unique_ptr<PerUserTopicRegistrationRequest> request =
-      builder.SetInstanceIdToken(token)
-          .SetScope(base_url)
-          .SetPublicTopicName(topic)
-          .SetProjectId(project_id)
-          .SetType(type)
-          .Build();
-  network::URLLoaderCompletionStatus response_status(net::OK);
-
-  url_loader_factory()->AddResponse(
-      url(request.get()), CreateHeadersForTest(GetParam()),
-      /* response_body */ std::string(), response_status);
-  request->Start(callback.Get(), url_loader_factory());
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_EQ(status.code, StatusCode::FAILED_NON_RETRIABLE);
-}
-
-INSTANTIATE_TEST_SUITE_P(All,
-                         PerUserTopicRegistrationRequestParamTest,
-                         testing::Values(net::HTTP_BAD_REQUEST,
-                                         net::HTTP_FORBIDDEN,
-                                         net::HTTP_NOT_FOUND));
-
-}  // namespace syncer
diff --git a/components/invalidation/impl/per_user_topic_subscription_manager.cc b/components/invalidation/impl/per_user_topic_subscription_manager.cc
index 6377d5e7..051bc7db 100644
--- a/components/invalidation/impl/per_user_topic_subscription_manager.cc
+++ b/components/invalidation/impl/per_user_topic_subscription_manager.cc
@@ -58,7 +58,7 @@
     base::OnceCallback<void(Topic topic,
                             Status code,
                             std::string private_topic_name,
-                            PerUserTopicRegistrationRequest::RequestType type)>;
+                            PerUserTopicSubscriptionRequest::RequestType type)>;
 
 static const net::BackoffEntry::Policy kBackoffPolicy = {
     // Number of initial errors (in sequence) to ignore before applying
@@ -163,7 +163,7 @@
 struct PerUserTopicSubscriptionManager::SubscriptionEntry {
   SubscriptionEntry(const Topic& topic,
                     SubscriptionFinishedCallback completion_callback,
-                    PerUserTopicRegistrationRequest::RequestType type,
+                    PerUserTopicSubscriptionRequest::RequestType type,
                     bool topic_is_public = false);
   ~SubscriptionEntry();
 
@@ -175,12 +175,12 @@
   const Topic topic;
   const bool topic_is_public;
   SubscriptionFinishedCallback completion_callback;
-  PerUserTopicRegistrationRequest::RequestType type;
+  PerUserTopicSubscriptionRequest::RequestType type;
 
   base::OneShotTimer request_retry_timer_;
   net::BackoffEntry request_backoff_;
 
-  std::unique_ptr<PerUserTopicRegistrationRequest> request;
+  std::unique_ptr<PerUserTopicSubscriptionRequest> request;
 
   DISALLOW_COPY_AND_ASSIGN(SubscriptionEntry);
 };
@@ -188,7 +188,7 @@
 PerUserTopicSubscriptionManager::SubscriptionEntry::SubscriptionEntry(
     const Topic& topic,
     SubscriptionFinishedCallback completion_callback,
-    PerUserTopicRegistrationRequest::RequestType type,
+    PerUserTopicSubscriptionRequest::RequestType type,
     bool topic_is_public)
     : topic(topic),
       topic_is_public(topic_is_public),
@@ -290,7 +290,7 @@
           base::BindOnce(
               &PerUserTopicSubscriptionManager::SubscriptionFinishedForTopic,
               base::Unretained(this)),
-          PerUserTopicRegistrationRequest::SUBSCRIBE, topic.second.is_public);
+          PerUserTopicSubscriptionRequest::SUBSCRIBE, topic.second.is_public);
     }
   }
 
@@ -308,7 +308,7 @@
           base::BindOnce(
               &PerUserTopicSubscriptionManager::SubscriptionFinishedForTopic,
               base::Unretained(this)),
-          PerUserTopicRegistrationRequest::UNSUBSCRIBE);
+          PerUserTopicSubscriptionRequest::UNSUBSCRIBE);
       private_topic_to_topic_.erase(it->second);
       it = topic_to_private_topic_.erase(it);
       // The decision to unsubscribe from invalidations for |topic| was
@@ -349,7 +349,7 @@
                  << " which is not in the registration map";
     return;
   }
-  PerUserTopicRegistrationRequest::Builder builder;
+  PerUserTopicSubscriptionRequest::Builder builder;
   // Resetting request in case it's running.
   // TODO(crbug.com/1020117): Should probably call it->second->Cancel() instead.
   it->second->request.reset();
@@ -372,11 +372,11 @@
 void PerUserTopicSubscriptionManager::ActOnSuccessfulSubscription(
     const Topic& topic,
     const std::string& private_topic_name,
-    PerUserTopicRegistrationRequest::RequestType type) {
+    PerUserTopicSubscriptionRequest::RequestType type) {
   auto it = pending_subscriptions_.find(topic);
   it->second->request_backoff_.InformOfRequest(true);
   pending_subscriptions_.erase(it);
-  if (type == PerUserTopicRegistrationRequest::SUBSCRIBE) {
+  if (type == PerUserTopicSubscriptionRequest::SUBSCRIBE) {
     // If this was a subscription, update the prefs now (if it was an
     // unsubscription, we've already updated the prefs when scheduling the
     // request).
@@ -392,7 +392,7 @@
   // pending.
   bool all_subscriptions_completed = true;
   for (const auto& entry : pending_subscriptions_) {
-    if (entry.second->type == PerUserTopicRegistrationRequest::SUBSCRIBE) {
+    if (entry.second->type == PerUserTopicSubscriptionRequest::SUBSCRIBE) {
       all_subscriptions_completed = false;
     }
   }
@@ -424,7 +424,7 @@
     Topic topic,
     Status code,
     std::string private_topic_name,
-    PerUserTopicRegistrationRequest::RequestType type) {
+    PerUserTopicSubscriptionRequest::RequestType type) {
   if (code.IsSuccess()) {
     ActOnSuccessfulSubscription(topic, private_topic_name, type);
   } else {
@@ -435,7 +435,7 @@
       RequestAccessToken();
     } else {
       // If one of the subscription requests failed, emit SUBSCRIPTION_FAILURE.
-      if (type == PerUserTopicRegistrationRequest::SUBSCRIBE &&
+      if (type == PerUserTopicSubscriptionRequest::SUBSCRIBE &&
           base::FeatureList::IsEnabled(
               invalidation::switches::kFCMInvalidationsConservativeEnabling)) {
         NotifySubscriptionChannelStateChange(
diff --git a/components/invalidation/impl/per_user_topic_subscription_manager.h b/components/invalidation/impl/per_user_topic_subscription_manager.h
index d157b8b0..c8d6755 100644
--- a/components/invalidation/impl/per_user_topic_subscription_manager.h
+++ b/components/invalidation/impl/per_user_topic_subscription_manager.h
@@ -14,7 +14,7 @@
 #include "base/time/time.h"
 #include "base/timer/timer.h"
 #include "components/invalidation/impl/channels_states.h"
-#include "components/invalidation/impl/per_user_topic_registration_request.h"
+#include "components/invalidation/impl/per_user_topic_subscription_request.h"
 #include "components/invalidation/public/identity_provider.h"
 #include "components/invalidation/public/invalidation_export.h"
 #include "components/invalidation/public/invalidation_util.h"
@@ -113,13 +113,13 @@
   void ActOnSuccessfulSubscription(
       const Topic& topic,
       const std::string& private_topic_name,
-      PerUserTopicRegistrationRequest::RequestType type);
+      PerUserTopicSubscriptionRequest::RequestType type);
   void ScheduleRequestForRepetition(const Topic& topic);
   void SubscriptionFinishedForTopic(
       Topic topic,
       Status code,
       std::string private_topic_name,
-      PerUserTopicRegistrationRequest::RequestType type);
+      PerUserTopicSubscriptionRequest::RequestType type);
 
   void RequestAccessToken();
 
diff --git a/components/invalidation/impl/per_user_topic_subscription_request.cc b/components/invalidation/impl/per_user_topic_subscription_request.cc
new file mode 100644
index 0000000..35bf742
--- /dev/null
+++ b/components/invalidation/impl/per_user_topic_subscription_request.cc
@@ -0,0 +1,382 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/invalidation/impl/per_user_topic_subscription_request.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/json/json_writer.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/strings/stringprintf.h"
+#include "base/values.h"
+#include "components/sync/base/model_type.h"
+#include "net/http/http_status_code.h"
+#include "net/url_request/url_fetcher.h"
+
+using net::HttpRequestHeaders;
+
+namespace {
+
+const char kPublicTopicNameKey[] = "publicTopicName";
+const char kPrivateTopicNameKey[] = "privateTopicName";
+
+const std::string* GetTopicName(const base::Value& value) {
+  if (!value.is_dict())
+    return nullptr;
+  if (value.FindBoolKey("isPublic").value_or(false))
+    return value.FindStringKey(kPublicTopicNameKey);
+  return value.FindStringKey(kPrivateTopicNameKey);
+}
+
+bool IsNetworkError(int net_error) {
+  // Note: ERR_HTTP_RESPONSE_CODE_FAILURE isn't a real network error - it
+  // indicates that the network request itself succeeded, but we received an
+  // HTTP error. The alternative to special-casing this error would be to call
+  // SetAllowHttpErrorResults(true) on the SimpleUrlLoader, but that also means
+  // we'd download the response body in case of HTTP errors, which we don't
+  // need.
+  return net_error != net::OK &&
+         net_error != net::ERR_HTTP_RESPONSE_CODE_FAILURE;
+}
+
+// Subscription status values for UMA_HISTOGRAM.
+enum class SubscriptionStatus {
+  kSuccess = 0,
+  kNetworkFailure = 1,
+  kHttpFailure = 2,
+  kParsingFailure = 3,
+  kFailure = 4,
+  kMaxValue = kFailure,
+};
+
+void RecordRequestStatus(
+    SubscriptionStatus status,
+    syncer::PerUserTopicSubscriptionRequest::RequestType type,
+    const std::string& topic,
+    int net_error = net::OK,
+    int response_code = 200) {
+  switch (type) {
+    case syncer::PerUserTopicSubscriptionRequest::SUBSCRIBE: {
+      base::UmaHistogramEnumeration(
+          "FCMInvalidations.SubscriptionRequestStatus", status);
+      break;
+    }
+    case syncer::PerUserTopicSubscriptionRequest::UNSUBSCRIBE: {
+      base::UmaHistogramEnumeration(
+          "FCMInvalidations.UnsubscriptionRequestStatus", status);
+      break;
+    }
+  }
+  if (type != syncer::PerUserTopicSubscriptionRequest::SUBSCRIBE) {
+    return;
+  }
+
+  if (IsNetworkError(net_error)) {
+    // Tracks the cases, when request fails due to network error.
+    base::UmaHistogramSparse("FCMInvalidations.FailedSubscriptionsErrorCode",
+                             -net_error);
+    DVLOG(1) << "Subscription request failed with error: " << net_error << ": "
+             << net::ErrorToString(net_error);
+  } else {
+    // Log a histogram to track response success vs. failure rates.
+    base::UmaHistogramSparse("FCMInvalidations.SubscriptionResponseCode",
+                             response_code);
+    // If the topic corresponds to a Sync ModelType, use that as the histogram
+    // suffix. Otherwise (e.g. Drive or Policy), just use "OTHER" for now.
+    // TODO(crbug.com/1029698): Depending on sync is a layering violation.
+    // Eventually the "whitelisted for metrics" bit should be part of a Topic.
+    syncer::ModelType model_type;  // Unused.
+    std::string suffix =
+        syncer::NotificationTypeToRealModelType(topic, &model_type) ? topic
+                                                                    : "OTHER";
+    base::UmaHistogramSparse(
+        "FCMInvalidations.SubscriptionResponseCodeForTopic." + suffix,
+        response_code);
+  }
+}
+
+}  // namespace
+
+namespace syncer {
+
+PerUserTopicSubscriptionRequest::PerUserTopicSubscriptionRequest() {}
+
+PerUserTopicSubscriptionRequest::~PerUserTopicSubscriptionRequest() = default;
+
+void PerUserTopicSubscriptionRequest::Start(
+    CompletedCallback callback,
+    network::mojom::URLLoaderFactory* loader_factory) {
+  DCHECK(request_completed_callback_.is_null()) << "Request already running!";
+  request_completed_callback_ = std::move(callback);
+
+  simple_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+      loader_factory,
+      base::BindOnce(&PerUserTopicSubscriptionRequest::OnURLFetchComplete,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void PerUserTopicSubscriptionRequest::OnURLFetchComplete(
+    std::unique_ptr<std::string> response_body) {
+  int response_code = 0;
+  if (simple_loader_->ResponseInfo() &&
+      simple_loader_->ResponseInfo()->headers) {
+    response_code = simple_loader_->ResponseInfo()->headers->response_code();
+  }
+  OnURLFetchCompleteInternal(simple_loader_->NetError(), response_code,
+                             std::move(response_body));
+}
+
+void PerUserTopicSubscriptionRequest::OnURLFetchCompleteInternal(
+    int net_error,
+    int response_code,
+    std::unique_ptr<std::string> response_body) {
+  if (IsNetworkError(net_error)) {
+    std::move(request_completed_callback_)
+        .Run(Status(StatusCode::FAILED, base::StringPrintf("Network Error")),
+             std::string());
+    RecordRequestStatus(SubscriptionStatus::kNetworkFailure, type_, topic_,
+                        net_error, response_code);
+    return;
+  }
+
+  if (response_code != net::HTTP_OK) {
+    StatusCode status = StatusCode::FAILED;
+    if (response_code == net::HTTP_UNAUTHORIZED) {
+      status = StatusCode::AUTH_FAILURE;
+    } else if (response_code >= 400 && response_code <= 499) {
+      status = StatusCode::FAILED_NON_RETRIABLE;
+    }
+    RecordRequestStatus(SubscriptionStatus::kHttpFailure, type_, topic_,
+                        net_error, response_code);
+    std::move(request_completed_callback_)
+        .Run(
+            Status(status, base::StringPrintf("HTTP Error: %d", response_code)),
+            std::string());
+    return;
+  }
+
+  if (type_ == UNSUBSCRIBE) {
+    // No response body expected for DELETE requests.
+    RecordRequestStatus(SubscriptionStatus::kSuccess, type_, topic_, net_error,
+                        response_code);
+    std::move(request_completed_callback_)
+        .Run(Status(StatusCode::SUCCESS, std::string()), std::string());
+    return;
+  }
+
+  if (!response_body || response_body->empty()) {
+    RecordRequestStatus(SubscriptionStatus::kParsingFailure, type_, topic_,
+                        net_error, response_code);
+    std::move(request_completed_callback_)
+        .Run(Status(StatusCode::FAILED, base::StringPrintf("Body missing")),
+             std::string());
+    return;
+  }
+
+  data_decoder::DataDecoder::ParseJsonIsolated(
+      *response_body,
+      base::BindOnce(&PerUserTopicSubscriptionRequest::OnJsonParse,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void PerUserTopicSubscriptionRequest::OnJsonParse(
+    data_decoder::DataDecoder::ValueOrError result) {
+  if (!result.value) {
+    RecordRequestStatus(SubscriptionStatus::kParsingFailure, type_, topic_);
+    std::move(request_completed_callback_)
+        .Run(Status(StatusCode::FAILED, base::StringPrintf("Body parse error")),
+             std::string());
+    return;
+  }
+
+  const std::string* topic_name = GetTopicName(*result.value);
+  if (topic_name) {
+    RecordRequestStatus(SubscriptionStatus::kSuccess, type_, topic_);
+    std::move(request_completed_callback_)
+        .Run(Status(StatusCode::SUCCESS, std::string()), *topic_name);
+  } else {
+    RecordRequestStatus(SubscriptionStatus::kParsingFailure, type_, topic_);
+    std::move(request_completed_callback_)
+        .Run(Status(StatusCode::FAILED,
+                    base::StringPrintf("Missing topic name")),
+             std::string());
+  }
+}
+
+PerUserTopicSubscriptionRequest::Builder::Builder() = default;
+PerUserTopicSubscriptionRequest::Builder::Builder(
+    PerUserTopicSubscriptionRequest::Builder&&) = default;
+PerUserTopicSubscriptionRequest::Builder::~Builder() = default;
+
+std::unique_ptr<PerUserTopicSubscriptionRequest>
+PerUserTopicSubscriptionRequest::Builder::Build() const {
+  DCHECK(!scope_.empty());
+  auto request = base::WrapUnique(new PerUserTopicSubscriptionRequest());
+
+  std::string url;
+  switch (type_) {
+    case SUBSCRIBE:
+      url = base::StringPrintf(
+          "%s/v1/perusertopics/%s/rel/topics/?subscriber_token=%s",
+          scope_.c_str(), project_id_.c_str(), instance_id_token_.c_str());
+      break;
+    case UNSUBSCRIBE:
+      std::string public_param;
+      if (topic_is_public_) {
+        public_param = "subscription.is_public=true&";
+      }
+      url = base::StringPrintf(
+          "%s/v1/perusertopics/%s/rel/topics/%s?%ssubscriber_token=%s",
+          scope_.c_str(), project_id_.c_str(), topic_.c_str(),
+          public_param.c_str(), instance_id_token_.c_str());
+      break;
+  }
+  GURL full_url(url);
+
+  DCHECK(full_url.is_valid());
+
+  request->url_ = full_url;
+  request->type_ = type_;
+  request->topic_ = topic_;
+
+  std::string body;
+  if (type_ == SUBSCRIBE)
+    body = BuildBody();
+  net::HttpRequestHeaders headers = BuildHeaders();
+  request->simple_loader_ = BuildURLFetcher(headers, body, full_url);
+
+  // Log the request for debugging network issues.
+  DVLOG(1) << "Building a subscription request to " << full_url << ":\n"
+           << headers.ToString() << "\n"
+           << body;
+  return request;
+}
+
+PerUserTopicSubscriptionRequest::Builder&
+PerUserTopicSubscriptionRequest::Builder::SetInstanceIdToken(
+    const std::string& token) {
+  instance_id_token_ = token;
+  return *this;
+}
+
+PerUserTopicSubscriptionRequest::Builder&
+PerUserTopicSubscriptionRequest::Builder::SetScope(const std::string& scope) {
+  scope_ = scope;
+  return *this;
+}
+
+PerUserTopicSubscriptionRequest::Builder&
+PerUserTopicSubscriptionRequest::Builder::SetAuthenticationHeader(
+    const std::string& auth_header) {
+  auth_header_ = auth_header;
+  return *this;
+}
+
+PerUserTopicSubscriptionRequest::Builder&
+PerUserTopicSubscriptionRequest::Builder::SetPublicTopicName(
+    const Topic& topic) {
+  topic_ = topic;
+  return *this;
+}
+
+PerUserTopicSubscriptionRequest::Builder&
+PerUserTopicSubscriptionRequest::Builder::SetProjectId(
+    const std::string& project_id) {
+  project_id_ = project_id;
+  return *this;
+}
+
+PerUserTopicSubscriptionRequest::Builder&
+PerUserTopicSubscriptionRequest::Builder::SetType(RequestType type) {
+  type_ = type;
+  return *this;
+}
+
+PerUserTopicSubscriptionRequest::Builder&
+PerUserTopicSubscriptionRequest::Builder::SetTopicIsPublic(
+    bool topic_is_public) {
+  topic_is_public_ = topic_is_public;
+  return *this;
+}
+
+HttpRequestHeaders PerUserTopicSubscriptionRequest::Builder::BuildHeaders()
+    const {
+  HttpRequestHeaders headers;
+  if (!auth_header_.empty()) {
+    headers.SetHeader(HttpRequestHeaders::kAuthorization, auth_header_);
+  }
+  return headers;
+}
+
+std::string PerUserTopicSubscriptionRequest::Builder::BuildBody() const {
+  base::DictionaryValue request;
+
+  request.SetString("public_topic_name", topic_);
+  if (topic_is_public_) {
+    request.SetBoolean("is_public", true);
+  }
+
+  std::string request_json;
+  bool success = base::JSONWriter::Write(request, &request_json);
+  DCHECK(success);
+  return request_json;
+}
+
+std::unique_ptr<network::SimpleURLLoader>
+PerUserTopicSubscriptionRequest::Builder::BuildURLFetcher(
+    const HttpRequestHeaders& headers,
+    const std::string& body,
+    const GURL& url) const {
+  net::NetworkTrafficAnnotationTag traffic_annotation =
+      net::DefineNetworkTrafficAnnotation("per_user_topic_registration_request",
+                                          R"(
+        semantics {
+          sender:
+            "Subscribe the Sync client for listening to the specific topic"
+          description:
+            "Chromium can receive Sync invalidations via FCM messages."
+            "This request subscribes the client for receiving messages for the"
+            "concrete topic. In case of Chrome Sync topic is a ModelType,"
+            "e.g. BOOKMARK"
+          trigger:
+            "Subscription takes place only once per profile per topic. "
+          data:
+            "An OAuth2 token is sent as an authorization header."
+          destination: GOOGLE_OWNED_SERVICE
+        }
+        policy {
+          cookies_allowed: NO
+          setting:
+            "This feature can not be disabled by settings now"
+          chrome_policy: {
+             SyncDisabled {
+               policy_options {mode: MANDATORY}
+               SyncDisabled: false
+             }
+          }
+        })");
+
+  auto request = std::make_unique<network::ResourceRequest>();
+  switch (type_) {
+    case SUBSCRIBE:
+      request->method = "POST";
+      break;
+    case UNSUBSCRIBE:
+      request->method = "DELETE";
+      break;
+  }
+  request->url = url;
+  request->headers = headers;
+  // TODO(crbug.com/1020117): Should we set request->credentials_mode to kOmit,
+  // to match "cookies_allowed: NO" above?
+
+  std::unique_ptr<network::SimpleURLLoader> url_loader =
+      network::SimpleURLLoader::Create(std::move(request), traffic_annotation);
+  url_loader->AttachStringForUpload(body, "application/json; charset=UTF-8");
+
+  return url_loader;
+}
+
+}  // namespace syncer
diff --git a/components/invalidation/impl/per_user_topic_subscription_request.h b/components/invalidation/impl/per_user_topic_subscription_request.h
new file mode 100644
index 0000000..9c2c7200
--- /dev/null
+++ b/components/invalidation/impl/per_user_topic_subscription_request.h
@@ -0,0 +1,115 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_INVALIDATION_IMPL_PER_USER_TOPIC_SUBSCRIPTION_REQUEST_H_
+#define COMPONENTS_INVALIDATION_IMPL_PER_USER_TOPIC_SUBSCRIPTION_REQUEST_H_
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "base/time/time.h"
+#include "components/invalidation/impl/status.h"
+#include "components/invalidation/public/invalidation_util.h"
+#include "net/http/http_request_headers.h"
+#include "services/data_decoder/public/cpp/data_decoder.h"
+#include "services/network/public/cpp/simple_url_loader.h"
+#include "services/network/public/mojom/url_loader_factory.mojom.h"
+
+namespace syncer {
+
+// A single request to subscribe to a topic on the per-user-topic service.
+class PerUserTopicSubscriptionRequest {
+ public:
+  // The request result consists of the request status and name of the private
+  // topic. The |topic_name| will be empty in the case of error.
+  using CompletedCallback =
+      base::OnceCallback<void(const Status& status,
+                              const std::string& topic_name)>;
+  enum RequestType { SUBSCRIBE, UNSUBSCRIBE };
+
+  // Builds authenticated PerUserTopicSubscriptionRequests.
+  class Builder {
+   public:
+    Builder();
+    Builder(Builder&&);
+    ~Builder();
+
+    // Builds a Request object in order to perform the subscription.
+    std::unique_ptr<PerUserTopicSubscriptionRequest> Build() const;
+
+    Builder& SetInstanceIdToken(const std::string& token);
+    Builder& SetScope(const std::string& scope);
+    Builder& SetAuthenticationHeader(const std::string& auth_header);
+
+    Builder& SetPublicTopicName(const Topic& topic);
+    Builder& SetProjectId(const std::string& project_id);
+
+    Builder& SetType(RequestType type);
+
+    Builder& SetTopicIsPublic(bool topic_is_public);
+
+   private:
+    net::HttpRequestHeaders BuildHeaders() const;
+    std::string BuildBody() const;
+    std::unique_ptr<network::SimpleURLLoader> BuildURLFetcher(
+        const net::HttpRequestHeaders& headers,
+        const std::string& body,
+        const GURL& url) const;
+
+    // GCM subscription token obtained from GCM driver (instanceID::getToken()).
+    std::string instance_id_token_;
+    Topic topic_;
+    std::string project_id_;
+
+    std::string scope_;
+    std::string auth_header_;
+    RequestType type_;
+    bool topic_is_public_ = false;
+
+    DISALLOW_COPY_AND_ASSIGN(Builder);
+  };
+
+  ~PerUserTopicSubscriptionRequest();
+
+  // Starts an async request. The callback is invoked when the request succeeds
+  // or fails. The callback is not called if the request is destroyed.
+  void Start(CompletedCallback callback,
+             network::mojom::URLLoaderFactory* loader_factory);
+
+  GURL GetUrlForTesting() const { return url_; }
+
+ private:
+  PerUserTopicSubscriptionRequest();
+
+  void OnURLFetchComplete(std::unique_ptr<std::string> response_body);
+  void OnURLFetchCompleteInternal(int net_error,
+                                  int response_code,
+                                  std::unique_ptr<std::string> response_body);
+
+  void OnJsonParse(data_decoder::DataDecoder::ValueOrError result);
+
+  // The fetcher for subscribing.
+  std::unique_ptr<network::SimpleURLLoader> simple_loader_;
+
+  // The callback to notify when URLFetcher finished and results are available.
+  // When the request is finished/aborted/destroyed, it's called in the dtor!
+  CompletedCallback request_completed_callback_;
+
+  // Full URL. Used in tests only.
+  GURL url_;
+  RequestType type_;
+  std::string topic_;
+
+  base::WeakPtrFactory<PerUserTopicSubscriptionRequest> weak_ptr_factory_{this};
+
+  DISALLOW_COPY_AND_ASSIGN(PerUserTopicSubscriptionRequest);
+};
+
+}  // namespace syncer
+
+#endif  // COMPONENTS_INVALIDATION_IMPL_PER_USER_TOPIC_SUBSCRIPTION_REQUEST_H_
diff --git a/components/invalidation/impl/per_user_topic_subscription_request_unittest.cc b/components/invalidation/impl/per_user_topic_subscription_request_unittest.cc
new file mode 100644
index 0000000..bf02218
--- /dev/null
+++ b/components/invalidation/impl/per_user_topic_subscription_request_unittest.cc
@@ -0,0 +1,330 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/invalidation/impl/per_user_topic_subscription_request.h"
+
+#include "base/bind.h"
+#include "base/json/json_reader.h"
+#include "base/memory/ptr_util.h"
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/gtest_util.h"
+#include "base/test/mock_callback.h"
+#include "base/test/task_environment.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/values.h"
+#include "net/url_request/test_url_fetcher_factory.h"
+#include "net/url_request/url_request_test_util.h"
+#include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace syncer {
+
+namespace {
+
+using testing::_;
+using testing::SaveArg;
+
+MATCHER_P(EqualsJSON, json, "equals JSON") {
+  std::unique_ptr<base::Value> expected =
+      base::JSONReader::ReadDeprecated(json);
+  if (!expected) {
+    *result_listener << "INTERNAL ERROR: couldn't parse expected JSON";
+    return false;
+  }
+
+  std::string err_msg;
+  int err_line, err_col;
+  std::unique_ptr<base::Value> actual =
+      base::JSONReader::ReadAndReturnErrorDeprecated(
+          arg, base::JSON_PARSE_RFC, nullptr, &err_msg, &err_line, &err_col);
+  if (!actual) {
+    *result_listener << "input:" << err_line << ":" << err_col << ": "
+                     << "parse error: " << err_msg;
+    return false;
+  }
+  return *expected == *actual;
+}
+
+network::mojom::URLResponseHeadPtr CreateHeadersForTest(int responce_code) {
+  auto head = network::mojom::URLResponseHead::New();
+  head->headers = new net::HttpResponseHeaders(base::StringPrintf(
+      "HTTP/1.1 %d OK\nContent-type: text/html\n\n", responce_code));
+  head->mime_type = "text/html";
+  return head;
+}
+
+}  // namespace
+
+class PerUserTopicSubscriptionRequestTest : public testing::Test {
+ public:
+  PerUserTopicSubscriptionRequestTest() {}
+
+  GURL url(PerUserTopicSubscriptionRequest* request) {
+    return request->GetUrlForTesting();
+  }
+
+  network::TestURLLoaderFactory* url_loader_factory() {
+    return &url_loader_factory_;
+  }
+
+ private:
+  base::test::SingleThreadTaskEnvironment task_environment_;
+  data_decoder::test::InProcessDataDecoder in_process_data_decoder_;
+  network::TestURLLoaderFactory url_loader_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(PerUserTopicSubscriptionRequestTest);
+};
+
+TEST_F(PerUserTopicSubscriptionRequestTest,
+       ShouldNotInvokeCallbackWhenCancelled) {
+  std::string token = "1234567890";
+  std::string url = "http://valid-url.test";
+  std::string topic = "test";
+  std::string project_id = "smarty-pants-12345";
+  PerUserTopicSubscriptionRequest::RequestType type =
+      PerUserTopicSubscriptionRequest::SUBSCRIBE;
+
+  base::MockCallback<PerUserTopicSubscriptionRequest::CompletedCallback>
+      callback;
+  EXPECT_CALL(callback, Run(_, _)).Times(0);
+
+  PerUserTopicSubscriptionRequest::Builder builder;
+  std::unique_ptr<PerUserTopicSubscriptionRequest> request =
+      builder.SetInstanceIdToken(token)
+          .SetScope(url)
+          .SetPublicTopicName(topic)
+          .SetProjectId(project_id)
+          .SetType(type)
+          .Build();
+  request->Start(callback.Get(), url_loader_factory());
+  base::RunLoop().RunUntilIdle();
+
+  // Destroy the request before getting any response.
+  request.reset();
+}
+
+TEST_F(PerUserTopicSubscriptionRequestTest, ShouldSubscribeWithoutErrors) {
+  std::string token = "1234567890";
+  std::string base_url = "http://valid-url.test";
+  std::string topic = "test";
+  std::string project_id = "smarty-pants-12345";
+  PerUserTopicSubscriptionRequest::RequestType type =
+      PerUserTopicSubscriptionRequest::SUBSCRIBE;
+
+  base::MockCallback<PerUserTopicSubscriptionRequest::CompletedCallback>
+      callback;
+  Status status(StatusCode::FAILED, "initial");
+  std::string private_topic;
+  EXPECT_CALL(callback, Run(_, _))
+      .WillOnce(DoAll(SaveArg<0>(&status), SaveArg<1>(&private_topic)));
+
+  PerUserTopicSubscriptionRequest::Builder builder;
+  std::unique_ptr<PerUserTopicSubscriptionRequest> request =
+      builder.SetInstanceIdToken(token)
+          .SetScope(base_url)
+          .SetPublicTopicName(topic)
+          .SetProjectId(project_id)
+          .SetType(type)
+          .Build();
+  std::string response_body = R"(
+    {
+      "privateTopicName": "test-pr"
+    }
+  )";
+
+  network::URLLoaderCompletionStatus response_status(net::OK);
+  response_status.decoded_body_length = response_body.size();
+
+  url_loader_factory()->AddResponse(url(request.get()),
+                                    CreateHeadersForTest(net::HTTP_OK),
+                                    response_body, response_status);
+  request->Start(callback.Get(), url_loader_factory());
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(status.code, StatusCode::SUCCESS);
+  EXPECT_EQ(private_topic, "test-pr");
+}
+
+TEST_F(PerUserTopicSubscriptionRequestTest,
+       ShouleNotSubscribeWhenNetworkProblem) {
+  std::string token = "1234567890";
+  std::string base_url = "http://valid-url.test";
+  std::string topic = "test";
+  std::string project_id = "smarty-pants-12345";
+  PerUserTopicSubscriptionRequest::RequestType type =
+      PerUserTopicSubscriptionRequest::SUBSCRIBE;
+
+  base::MockCallback<PerUserTopicSubscriptionRequest::CompletedCallback>
+      callback;
+  Status status(StatusCode::FAILED, "initial");
+  std::string private_topic;
+  EXPECT_CALL(callback, Run(_, _))
+      .WillOnce(DoAll(SaveArg<0>(&status), SaveArg<1>(&private_topic)));
+
+  PerUserTopicSubscriptionRequest::Builder builder;
+  std::unique_ptr<PerUserTopicSubscriptionRequest> request =
+      builder.SetInstanceIdToken(token)
+          .SetScope(base_url)
+          .SetPublicTopicName(topic)
+          .SetProjectId(project_id)
+          .SetType(type)
+          .Build();
+  std::string response_body = R"(
+    {
+      "privateTopicName": "test-pr"
+    }
+  )";
+
+  network::URLLoaderCompletionStatus response_status(net::ERR_TIMED_OUT);
+  response_status.decoded_body_length = response_body.size();
+
+  url_loader_factory()->AddResponse(url(request.get()),
+                                    CreateHeadersForTest(net::HTTP_OK),
+                                    response_body, response_status);
+  request->Start(callback.Get(), url_loader_factory());
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(status.code, StatusCode::FAILED);
+}
+
+TEST_F(PerUserTopicSubscriptionRequestTest,
+       ShouldNotSubscribeWhenWrongResponse) {
+  std::string token = "1234567890";
+  std::string base_url = "http://valid-url.test";
+  std::string topic = "test";
+  std::string project_id = "smarty-pants-12345";
+  PerUserTopicSubscriptionRequest::RequestType type =
+      PerUserTopicSubscriptionRequest::SUBSCRIBE;
+
+  base::MockCallback<PerUserTopicSubscriptionRequest::CompletedCallback>
+      callback;
+  Status status(StatusCode::SUCCESS, "initial");
+  std::string private_topic;
+
+  EXPECT_CALL(callback, Run(_, _))
+      .WillOnce(DoAll(SaveArg<0>(&status), SaveArg<1>(&private_topic)));
+
+  PerUserTopicSubscriptionRequest::Builder builder;
+  std::unique_ptr<PerUserTopicSubscriptionRequest> request =
+      builder.SetInstanceIdToken(token)
+          .SetScope(base_url)
+          .SetPublicTopicName(topic)
+          .SetProjectId(project_id)
+          .SetType(type)
+          .Build();
+  std::string response_body = R"(
+    {}
+  )";
+
+  network::URLLoaderCompletionStatus response_status(net::OK);
+  response_status.decoded_body_length = response_body.size();
+
+  url_loader_factory()->AddResponse(url(request.get()),
+                                    CreateHeadersForTest(net::HTTP_OK),
+                                    response_body, response_status);
+  request->Start(callback.Get(), url_loader_factory());
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(status.code, StatusCode::FAILED);
+  EXPECT_EQ(status.message, "Missing topic name");
+}
+
+TEST_F(PerUserTopicSubscriptionRequestTest, ShouldUnsubscribe) {
+  std::string token = "1234567890";
+  std::string base_url = "http://valid-url.test";
+  std::string topic = "test";
+  std::string project_id = "smarty-pants-12345";
+  PerUserTopicSubscriptionRequest::RequestType type =
+      PerUserTopicSubscriptionRequest::UNSUBSCRIBE;
+
+  base::MockCallback<PerUserTopicSubscriptionRequest::CompletedCallback>
+      callback;
+  Status status(StatusCode::FAILED, "initial");
+  std::string private_topic;
+
+  EXPECT_CALL(callback, Run(_, _))
+      .WillOnce(DoAll(SaveArg<0>(&status), SaveArg<1>(&private_topic)));
+
+  PerUserTopicSubscriptionRequest::Builder builder;
+  std::unique_ptr<PerUserTopicSubscriptionRequest> request =
+      builder.SetInstanceIdToken(token)
+          .SetScope(base_url)
+          .SetPublicTopicName(topic)
+          .SetProjectId(project_id)
+          .SetType(type)
+          .Build();
+  std::string response_body = R"(
+    {}
+  )";
+
+  network::URLLoaderCompletionStatus response_status(net::OK);
+  response_status.decoded_body_length = response_body.size();
+
+  url_loader_factory()->AddResponse(url(request.get()),
+                                    CreateHeadersForTest(net::HTTP_OK),
+                                    response_body, response_status);
+  request->Start(callback.Get(), url_loader_factory());
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(status.code, StatusCode::SUCCESS);
+  EXPECT_EQ(status.message, std::string());
+}
+
+class PerUserTopicSubscriptionRequestParamTest
+    : public PerUserTopicSubscriptionRequestTest,
+      public testing::WithParamInterface<net::HttpStatusCode> {
+ public:
+  PerUserTopicSubscriptionRequestParamTest() = default;
+  ~PerUserTopicSubscriptionRequestParamTest() override = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PerUserTopicSubscriptionRequestParamTest);
+};
+
+TEST_P(PerUserTopicSubscriptionRequestParamTest,
+       ShouldNotSubscribeWhenNonRepeatableError) {
+  std::string token = "1234567890";
+  std::string base_url = "http://valid-url.test";
+  std::string topic = "test";
+  std::string project_id = "smarty-pants-12345";
+  PerUserTopicSubscriptionRequest::RequestType type =
+      PerUserTopicSubscriptionRequest::SUBSCRIBE;
+
+  base::MockCallback<PerUserTopicSubscriptionRequest::CompletedCallback>
+      callback;
+  Status status(StatusCode::FAILED, "initial");
+  std::string private_topic;
+  EXPECT_CALL(callback, Run(_, _))
+      .WillOnce(DoAll(SaveArg<0>(&status), SaveArg<1>(&private_topic)));
+
+  PerUserTopicSubscriptionRequest::Builder builder;
+  std::unique_ptr<PerUserTopicSubscriptionRequest> request =
+      builder.SetInstanceIdToken(token)
+          .SetScope(base_url)
+          .SetPublicTopicName(topic)
+          .SetProjectId(project_id)
+          .SetType(type)
+          .Build();
+  network::URLLoaderCompletionStatus response_status(net::OK);
+
+  url_loader_factory()->AddResponse(
+      url(request.get()), CreateHeadersForTest(GetParam()),
+      /* response_body */ std::string(), response_status);
+  request->Start(callback.Get(), url_loader_factory());
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(status.code, StatusCode::FAILED_NON_RETRIABLE);
+}
+
+INSTANTIATE_TEST_SUITE_P(All,
+                         PerUserTopicSubscriptionRequestParamTest,
+                         testing::Values(net::HTTP_BAD_REQUEST,
+                                         net::HTTP_FORBIDDEN,
+                                         net::HTTP_NOT_FOUND));
+
+}  // namespace syncer
diff --git a/components/omnibox/browser/zero_suggest_provider.cc b/components/omnibox/browser/zero_suggest_provider.cc
index 0b932f5..df84290 100644
--- a/components/omnibox/browser/zero_suggest_provider.cc
+++ b/components/omnibox/browser/zero_suggest_provider.cc
@@ -696,11 +696,8 @@
   const auto field_trial_variants =
       OmniboxFieldTrial::GetZeroSuggestVariants(current_page_classification_);
 
-  if (base::Contains(field_trial_variants, kNoneVariant) ||
-      base::FeatureList::IsEnabled(
-          omnibox::kOmniboxPopupShortcutIconsInZeroState)) {
+  if (base::Contains(field_trial_variants, kNoneVariant))
     return NONE;
-  }
 
   // TODO(tommycli): Since this can be configured via ZeroSuggestVariant, we
   // should eliminate this special case and use a field trial configuration.
diff --git a/components/omnibox/common/omnibox_features.cc b/components/omnibox/common/omnibox_features.cc
index ec51512..e08b550 100644
--- a/components/omnibox/common/omnibox_features.cc
+++ b/components/omnibox/common/omnibox_features.cc
@@ -230,12 +230,6 @@
 const base::Feature kAutocompleteTitles{"OmniboxAutocompleteTitles",
                                         base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Feature to replace the standard ZeroSuggest with icons for most visited sites
-// and collections (bookmarks, history, recent tabs, reading list). Only
-// available on iOS.
-const base::Feature kOmniboxPopupShortcutIconsInZeroState{
-    "OmniboxPopupShortcutIconsInZeroState", base::FEATURE_DISABLED_BY_DEFAULT};
-
 // Feature to use material design weather icons in the omnibox when displaying
 // weather answers.
 const base::Feature kOmniboxMaterialDesignWeatherIcons{
diff --git a/components/omnibox/common/omnibox_features.h b/components/omnibox/common/omnibox_features.h
index 831fb41..4865db3 100644
--- a/components/omnibox/common/omnibox_features.h
+++ b/components/omnibox/common/omnibox_features.h
@@ -41,7 +41,6 @@
 extern const base::Feature kSpeculativeServiceWorkerStartOnQueryInput;
 extern const base::Feature kDocumentProvider;
 extern const base::Feature kAutocompleteTitles;
-extern const base::Feature kOmniboxPopupShortcutIconsInZeroState;
 extern const base::Feature kOmniboxMaterialDesignWeatherIcons;
 extern const base::Feature kOmniboxDisableInstantExtendedLimit;
 extern const base::Feature kOmniboxSearchEngineLogo;
diff --git a/components/paint_preview/browser/android/BUILD.gn b/components/paint_preview/browser/android/BUILD.gn
new file mode 100644
index 0000000..39c1e99
--- /dev/null
+++ b/components/paint_preview/browser/android/BUILD.gn
@@ -0,0 +1,41 @@
+# Copyright 2020 The Chromium Authors.All rights reserved.
+# Use of this source code is governed by a BSD - style license that can be
+# found in the LICENSE file.
+
+import("//build/config/android/rules.gni")
+
+assert(is_android, "This directory should only be compiled for Android.")
+
+generate_jni("jni_headers") {
+  sources = [
+    "java/src/org/chromium/components/paintpreview/browser/PaintPreviewUtils.java",
+  ]
+}
+
+android_library("java") {
+  annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
+
+  sources = [
+    "java/src/org/chromium/components/paintpreview/browser/PaintPreviewUtils.java",
+  ]
+
+  deps = [
+    "//base:base_java",
+    "//base:jni_java",
+    "//content/public/android:content_java",
+  ]
+}
+
+source_set("android") {
+  sources = [
+    "paint_preview_utils.cc",
+  ]
+
+  deps = [
+    ":jni_headers",
+    "//base",
+    "//components/paint_preview/browser",
+    "//components/paint_preview/buildflags",
+    "//content/public/browser",
+  ]
+}
diff --git a/components/paint_preview/browser/android/java/DEPS b/components/paint_preview/browser/android/java/DEPS
new file mode 100644
index 0000000..5beb9bb
--- /dev/null
+++ b/components/paint_preview/browser/android/java/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+content/public/android/java/src/org/chromium/content_public/browser/WebContents.java",
+]
diff --git a/components/paint_preview/browser/android/java/src/org/chromium/components/paintpreview/browser/PaintPreviewUtils.java b/components/paint_preview/browser/android/java/src/org/chromium/components/paintpreview/browser/PaintPreviewUtils.java
new file mode 100644
index 0000000..5e4fcdcc
--- /dev/null
+++ b/components/paint_preview/browser/android/java/src/org/chromium/components/paintpreview/browser/PaintPreviewUtils.java
@@ -0,0 +1,26 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.components.paintpreview.browser;
+
+import org.chromium.base.annotations.NativeMethods;
+import org.chromium.content_public.browser.WebContents;
+
+/**
+ * Helper to capture paint previews via native.
+ */
+public class PaintPreviewUtils {
+    /**
+     * Captures a paint preview of the passed contents.
+     * @param contents The WebContents of the page to capture.
+     */
+    public static void capturePaintPreview(WebContents contents) {
+        PaintPreviewUtilsJni.get().capturePaintPreview(contents);
+    }
+
+    @NativeMethods
+    interface Natives {
+        void capturePaintPreview(WebContents webContents);
+    }
+}
diff --git a/components/paint_preview/browser/android/paint_preview_utils.cc b/components/paint_preview/browser/android/paint_preview_utils.cc
new file mode 100644
index 0000000..5251426
--- /dev/null
+++ b/components/paint_preview/browser/android/paint_preview_utils.cc
@@ -0,0 +1,146 @@
+// Copyright 2020 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 <jni.h>
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/task/post_task.h"
+#include "base/threading/scoped_blocking_call.h"
+#include "base/unguessable_token.h"
+#include "components/paint_preview/browser/android/jni_headers/PaintPreviewUtils_jni.h"
+#include "components/paint_preview/buildflags/buildflags.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_contents_user_data.h"
+
+#if BUILDFLAG(ENABLE_PAINT_PREVIEW)
+#include "components/paint_preview/browser/file_manager.h"
+#include "components/paint_preview/browser/paint_preview_client.h"
+#endif  // BUILDFLAG(ENABLE_PAINT_PREVIEW)
+
+namespace {
+
+const char kPaintPreviewTestTag[] = "PaintPreviewTest ";
+
+#if BUILDFLAG(ENABLE_PAINT_PREVIEW)
+const char kPaintPreviewDir[] = "paint_preview";
+const char kCaptureTestDir[] = "capture_test";
+
+struct CaptureMetrics {
+  int compressed_size_bytes;
+  int capture_time_us;
+  bool success;
+};
+
+void CleanupAndLogResult(const base::FilePath& root_dir,
+                         const CaptureMetrics& metrics) {
+  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
+                                                base::BlockingType::MAY_BLOCK);
+  base::DeleteFileRecursively(root_dir);
+  VLOG(1) << kPaintPreviewTestTag
+          << "Capture Finished: " << ((metrics.success) ? "Success" : "Failure")
+          << "\n"
+          << "Compressed size " << metrics.compressed_size_bytes << " bytes\n"
+          << "Time taken in native " << metrics.capture_time_us << " us";
+}
+
+void MeasureSize(const base::FilePath& root_dir,
+                 const GURL& url,
+                 std::unique_ptr<paint_preview::PaintPreviewProto> proto,
+                 CaptureMetrics metrics) {
+  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
+                                                base::BlockingType::MAY_BLOCK);
+  if (!metrics.success) {
+    metrics.success = false;
+    CleanupAndLogResult(root_dir, metrics);
+    return;
+  }
+
+  paint_preview::FileManager manager(root_dir);
+  base::FilePath path;
+  bool success = manager.CreateOrGetDirectoryFor(url, &path);
+  if (!success) {
+    VLOG(1) << kPaintPreviewTestTag << "Failure: could not create url dir.";
+    metrics.success = false;
+    CleanupAndLogResult(root_dir, metrics);
+    return;
+  }
+  base::File file(path,
+                  base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
+  std::string str_proto = proto->SerializeAsString();
+  file.WriteAtCurrentPos(str_proto.data(), str_proto.size());
+  manager.CompressDirectoryFor(url);
+  metrics.compressed_size_bytes = manager.GetSizeOfArtifactsFor(url);
+  CleanupAndLogResult(root_dir, metrics);
+  return;
+}
+
+void OnCaptured(base::TimeTicks start_time,
+                const base::FilePath& root_dir,
+                const GURL& url,
+                base::UnguessableToken guid,
+                paint_preview::mojom::PaintPreviewStatus status,
+                std::unique_ptr<paint_preview::PaintPreviewProto> proto) {
+  base::TimeDelta time_delta = base::TimeTicks::Now() - start_time;
+  CaptureMetrics result = {
+      0, time_delta.InMicroseconds(),
+      status == paint_preview::mojom::PaintPreviewStatus::kOk};
+  base::PostTask(
+      FROM_HERE, {base::ThreadPool(), base::MayBlock()},
+      base::BindOnce(&MeasureSize, root_dir, url, std::move(proto), result));
+}
+#endif  // BUILDFLAG(ENABLE_PAINT_PREVIEW)
+
+}  // namespace
+
+// If the ENABLE_PAINT_PREVIEW buildflags is set this method will trigger a
+// series of actions;
+// 1. Capture a paint preview via the client and measure the time taken.
+// 2. Zip a folder containing the artifacts and measure the size of the zip.
+// 3. Delete the resulting zip archive.
+// 4. Log the results.
+// If the buildflag is not set this is just a stub.
+static void JNI_PaintPreviewUtils_CapturePaintPreview(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& jweb_contents) {
+#if BUILDFLAG(ENABLE_PAINT_PREVIEW)
+  auto* contents = content::WebContents::FromJavaWebContents(jweb_contents);
+  paint_preview::PaintPreviewClient::CreateForWebContents(contents);
+  auto* client = paint_preview::PaintPreviewClient::FromWebContents(contents);
+  if (!client) {
+    VLOG(1) << kPaintPreviewTestTag << "Failure: client could not be created.";
+    return;
+  }
+  paint_preview::PaintPreviewClient::PaintPreviewParams params;
+  params.document_guid = base::UnguessableToken::Create();
+  params.is_main_frame = true;
+  base::FilePath root_path = contents->GetBrowserContext()
+                                 ->GetPath()
+                                 .AppendASCII(kPaintPreviewDir)
+                                 .AppendASCII(kCaptureTestDir);
+  params.root_dir = root_path;
+  GURL url = contents->GetLastCommittedURL();
+  paint_preview::FileManager manager(root_path);
+  bool success = manager.CreateOrGetDirectoryFor(url, &params.root_dir);
+  if (!success) {
+    VLOG(1) << kPaintPreviewTestTag << "Failure: could not create output dir.";
+    return;
+  }
+  auto start_time = base::TimeTicks::Now();
+  client->CapturePaintPreview(
+      params, contents->GetMainFrame(),
+      base::BindOnce(&OnCaptured, start_time, root_path, url));
+#else
+  // In theory this is unreachable as the codepath to reach here is only exposed
+  // if the buildflag for ENABLE_PAINT_PREVIEW is set. However, this function
+  // will still be compiled as it is called from JNI so this is here as a
+  // placeholder.
+  VLOG(1) << kPaintPreviewTestTag
+          << "Failure: compiled without buildflag ENABLE_PAINT_PREVIEW.";
+#endif  // BUILDFLAG(ENABLE_PAINT_PREVIEW)
+}
diff --git a/components/paint_preview/features/BUILD.gn b/components/paint_preview/features/BUILD.gn
new file mode 100644
index 0000000..af2dc32
--- /dev/null
+++ b/components/paint_preview/features/BUILD.gn
@@ -0,0 +1,13 @@
+# Copyright 2020 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("features") {
+  sources = [
+    "features.cc",
+    "features.h",
+  ]
+  deps = [
+    "//base",
+  ]
+}
diff --git a/components/paint_preview/features/features.cc b/components/paint_preview/features/features.cc
new file mode 100644
index 0000000..35c7f3a
--- /dev/null
+++ b/components/paint_preview/features/features.cc
@@ -0,0 +1,14 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/paint_preview/features/features.h"
+
+#include "base/feature_list.h"
+
+namespace paint_preview {
+
+const base::Feature kPaintPreviewTest{"PaintPreviewTest",
+                                      base::FEATURE_DISABLED_BY_DEFAULT};
+
+}  // namespace paint_preview
diff --git a/components/paint_preview/features/features.h b/components/paint_preview/features/features.h
new file mode 100644
index 0000000..14058da
--- /dev/null
+++ b/components/paint_preview/features/features.h
@@ -0,0 +1,20 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_PAINT_PREVIEW_FEATURES_FEATURES_H_
+#define COMPONENTS_PAINT_PREVIEW_FEATURES_FEATURES_H_
+
+#include "base/feature_list.h"
+
+namespace paint_preview {
+
+// Used to enable a main menu item on Android that captures a paint preview for
+// the current page. Metrics for the capture are logged and a toast is raised.
+// The resulting paint preview is then deleted. This intended to test whether
+// capturing works on a specific site.
+extern const base::Feature kPaintPreviewTest;
+
+}  // namespace paint_preview
+
+#endif  // COMPONENTS_PAINT_PREVIEW_FEATURES_FEATURES_H_
diff --git a/components/password_manager/core/browser/BUILD.gn b/components/password_manager/core/browser/BUILD.gn
index 61f51d7..c0e023c 100644
--- a/components/password_manager/core/browser/BUILD.gn
+++ b/components/password_manager/core/browser/BUILD.gn
@@ -272,7 +272,7 @@
     "//components/password_manager/core/common",
     "//components/pref_registry",
     "//components/prefs",
-    "//components/safe_browsing:features",
+    "//components/safe_browsing/core:features",
     "//components/security_state/core",
     "//components/signin/public/identity_manager",
     "//components/strings",
@@ -302,7 +302,7 @@
       "http_credentials_cleaner.cc",
       "http_credentials_cleaner.h",
     ]
-    deps += [ "//components/safe_browsing/common:safe_browsing_prefs" ]
+    deps += [ "//components/safe_browsing/core/common:safe_browsing_prefs" ]
   }
 
   if ((is_posix && !is_mac && !is_ios) || is_fuchsia) {
@@ -631,7 +631,7 @@
     "//components/password_manager/core/browser/leak_detection:unit_tests",
     "//components/password_manager/core/common",
     "//components/prefs:test_support",
-    "//components/safe_browsing:features",
+    "//components/safe_browsing/core:features",
     "//components/security_state/core",
     "//components/signin/public/identity_manager:test_support",
     "//components/strings",
@@ -655,7 +655,7 @@
   ]
 
   if (password_reuse_detection_support) {
-    deps += [ "//components/safe_browsing/common:safe_browsing_prefs" ]
+    deps += [ "//components/safe_browsing/core/common:safe_browsing_prefs" ]
   }
 }
 
diff --git a/components/password_manager/core/browser/DEPS b/components/password_manager/core/browser/DEPS
index 34d1217..7bd83f9 100644
--- a/components/password_manager/core/browser/DEPS
+++ b/components/password_manager/core/browser/DEPS
@@ -5,8 +5,8 @@
   "+components/pref_registry",
   "+components/security_state",
   "+components/safe_browsing/buildflags.h",
-  "+components/safe_browsing/common/safe_browsing_prefs.h",
-  "+components/safe_browsing/features.h",
+  "+components/safe_browsing/core/common/safe_browsing_prefs.h",
+  "+components/safe_browsing/core/features.h",
   "+components/signin/public",
   "+components/sync/base",
   "+components/sync/driver",
diff --git a/components/password_manager/core/browser/compromised_credentials_observer.cc b/components/password_manager/core/browser/compromised_credentials_observer.cc
index 82d5487..03328332 100644
--- a/components/password_manager/core/browser/compromised_credentials_observer.cc
+++ b/components/password_manager/core/browser/compromised_credentials_observer.cc
@@ -8,7 +8,7 @@
 
 #include "base/metrics/histogram_macros.h"
 #include "components/password_manager/core/common/password_manager_features.h"
-#include "components/safe_browsing/features.h"
+#include "components/safe_browsing/core/features.h"
 
 namespace password_manager {
 
diff --git a/components/password_manager/core/browser/compromised_credentials_table.cc b/components/password_manager/core/browser/compromised_credentials_table.cc
index 46a235b..05902f4 100644
--- a/components/password_manager/core/browser/compromised_credentials_table.cc
+++ b/components/password_manager/core/browser/compromised_credentials_table.cc
@@ -7,7 +7,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "components/password_manager/core/browser/sql_table_builder.h"
 #include "components/password_manager/core/common/password_manager_features.h"
-#include "components/safe_browsing/features.h"
+#include "components/safe_browsing/core/features.h"
 #include "sql/database.h"
 #include "sql/statement.h"
 
diff --git a/components/password_manager/core/browser/compromised_credentials_table_unittest.cc b/components/password_manager/core/browser/compromised_credentials_table_unittest.cc
index b3f6376..2333ff08 100644
--- a/components/password_manager/core/browser/compromised_credentials_table_unittest.cc
+++ b/components/password_manager/core/browser/compromised_credentials_table_unittest.cc
@@ -12,7 +12,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "components/password_manager/core/common/password_manager_features.h"
-#include "components/safe_browsing/features.h"
+#include "components/safe_browsing/core/features.h"
 #include "sql/database.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/components/password_manager/core/browser/credential_manager_impl_unittest.cc b/components/password_manager/core/browser/credential_manager_impl_unittest.cc
index fa7045f1..a155bf7 100644
--- a/components/password_manager/core/browser/credential_manager_impl_unittest.cc
+++ b/components/password_manager/core/browser/credential_manager_impl_unittest.cc
@@ -27,7 +27,7 @@
 #include "components/password_manager/core/common/password_manager_pref_names.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/testing_pref_service.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/components/password_manager/core/browser/leak_detection_delegate.cc b/components/password_manager/core/browser/leak_detection_delegate.cc
index 315704a..792b4fb 100644
--- a/components/password_manager/core/browser/leak_detection_delegate.cc
+++ b/components/password_manager/core/browser/leak_detection_delegate.cc
@@ -19,7 +19,7 @@
 #include "components/password_manager/core/common/password_manager_features.h"
 #include "components/password_manager/core/common/password_manager_pref_names.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
 namespace password_manager {
diff --git a/components/password_manager/core/browser/leak_detection_delegate_unittest.cc b/components/password_manager/core/browser/leak_detection_delegate_unittest.cc
index fc2669c..ae607489 100644
--- a/components/password_manager/core/browser/leak_detection_delegate_unittest.cc
+++ b/components/password_manager/core/browser/leak_detection_delegate_unittest.cc
@@ -18,7 +18,7 @@
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/testing_pref_service.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/components/password_manager/core/browser/login_database.cc b/components/password_manager/core/browser/login_database.cc
index 0848cbb8..8a709183 100644
--- a/components/password_manager/core/browser/login_database.cc
+++ b/components/password_manager/core/browser/login_database.cc
@@ -40,7 +40,7 @@
 #include "components/password_manager/core/browser/psl_matching_helper.h"
 #include "components/password_manager/core/browser/sql_table_builder.h"
 #include "components/password_manager/core/common/password_manager_features.h"
-#include "components/safe_browsing/features.h"
+#include "components/safe_browsing/core/features.h"
 #include "components/sync/protocol/entity_metadata.pb.h"
 #include "components/sync/protocol/model_type_state.pb.h"
 #include "google_apis/gaia/gaia_auth_util.h"
diff --git a/components/password_manager/core/browser/password_manager_unittest.cc b/components/password_manager/core/browser/password_manager_unittest.cc
index 7d0ac06..d4fc28d7 100644
--- a/components/password_manager/core/browser/password_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_manager_unittest.cc
@@ -47,7 +47,7 @@
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/testing_pref_service.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/ukm/test_ukm_recorder.h"
 #include "net/cert/cert_status_flags.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
diff --git a/components/password_manager/core/browser/password_reuse_detector.cc b/components/password_manager/core/browser/password_reuse_detector.cc
index 6b199907..ed6c654e 100644
--- a/components/password_manager/core/browser/password_reuse_detector.cc
+++ b/components/password_manager/core/browser/password_reuse_detector.cc
@@ -13,7 +13,7 @@
 #include "components/password_manager/core/browser/password_hash_data.h"
 #include "components/password_manager/core/browser/password_reuse_detector_consumer.h"
 #include "components/password_manager/core/browser/psl_matching_helper.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "google_apis/gaia/gaia_auth_util.h"
 #include "google_apis/gaia/gaia_urls.h"
 #include "url/origin.h"
diff --git a/components/password_manager/core/browser/password_store.cc b/components/password_manager/core/browser/password_store.cc
index 604fa614..afc9621 100644
--- a/components/password_manager/core/browser/password_store.cc
+++ b/components/password_manager/core/browser/password_store.cc
@@ -42,7 +42,7 @@
 #if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
 #include "base/strings/string16.h"
 #include "components/password_manager/core/browser/password_store_signin_notifier.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #endif
 
 using autofill::PasswordForm;
diff --git a/components/password_manager/core/browser/password_sync_util.cc b/components/password_manager/core/browser/password_sync_util.cc
index a783dace..3b9a791 100644
--- a/components/password_manager/core/browser/password_sync_util.cc
+++ b/components/password_manager/core/browser/password_sync_util.cc
@@ -15,7 +15,7 @@
 #include "url/origin.h"
 
 #if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #endif  // SYNC_PASSWORD_REUSE_DETECTION_ENABLED
 
 using autofill::PasswordForm;
diff --git a/components/password_manager/core/browser/password_sync_util_unittest.cc b/components/password_manager/core/browser/password_sync_util_unittest.cc
index 320ce3c..6bcea0fc 100644
--- a/components/password_manager/core/browser/password_sync_util_unittest.cc
+++ b/components/password_manager/core/browser/password_sync_util_unittest.cc
@@ -16,7 +16,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 #if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
-#include "components/safe_browsing/common/safe_browsing_prefs.h"  // nogncheck
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"  // nogncheck
 #endif  // SYNC_PASSWORD_REUSE_DETECTION_ENABLED
 
 using autofill::PasswordForm;
diff --git a/components/password_manager/core/browser/sync_credentials_filter_unittest.cc b/components/password_manager/core/browser/sync_credentials_filter_unittest.cc
index 79042922..62b66d6 100644
--- a/components/password_manager/core/browser/sync_credentials_filter_unittest.cc
+++ b/components/password_manager/core/browser/sync_credentials_filter_unittest.cc
@@ -32,7 +32,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 #if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
-#include "components/safe_browsing/common/safe_browsing_prefs.h"  // nogncheck
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"  // nogncheck
 #endif  // SYNC_PASSWORD_REUSE_DETECTION_ENABLED
 
 using autofill::PasswordForm;
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 053454a..8a5ac6b 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -16241,20 +16241,11 @@
       'caption': '''Allow collection of WebRTC event logs from Google services''',
       'tags': ['google-sharing'],
       'desc': '''
-      If the policy is set to true, <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> is allowed to collect WebRTC event logs from Google services (e.g. Google Meet), and upload those logs to Google.
+      Setting the policy to True allows <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> to collect WebRTC event logs from Google services such as Hangouts Meet and upload them to Google. These logs have diagnostic information for debugging issues with audio or video meetings in <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph>, such as the time and size of RTP packets, feedback about congestion on the network, and metadata about time and quality of audio and video frames. These logs have no audio or video content from the meeting. To make debugging easier, Google might associate these logs, by means of a session ID, with other logs collected by the Google service itself.
 
-      If the policy is set to false, <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> may not collect nor upload such logs.
+      Setting the policy to False results in no collection or uploading of such logs.
 
-      If the policy is unset, up to and including M76, <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> may not collect nor upload such logs.
-
-      If the policy is unset, since M77, <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> may collect or upload such logs by default if the browser profile is considered managed, i.e. if the profile receives cloud or machine level policies, and is not a child, ephemeral, login, or incognito profile.
-
-      These logs contain diagnostic information helpful when debugging issues with audio or video calls in Chrome, such as the time and size of sent and received RTP packets, feedback about congestion on the network, and metadata about time and quality of audio and video frames. These logs do not contain audio or video contents from the call.
-
-      This data collection by Chrome can only be triggered by Google's web services, such as Google Hangouts or Google Meet.
-
-      Google may associate these logs, by means of a session ID, with other logs collected by the Google service itself; this is intended to make debugging easier.
-      ''',
+      Leaving the policy unset on versions up to and including M76 means <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> defaults to not being able to collect and upload these logs. Starting at M77, <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> defaults to being able to collect and upload these logs from most profiles affected by cloud-based user-level enterprise policies. From M77 up to and including M80, <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> can also collect and upload these logs by default from profiles affected by <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> on-premise management.''',
     },
     {
       'name': 'PowerSmartDimEnabled',
@@ -17310,7 +17301,9 @@
 
       This policy is intended to give enterprises a chance to migrate to 3rd party software that does not depend on hooking the networking APIs. Proxy servers are recommended over LSPs and Win32 API patching.
 
-      If this policy is not set, networking code may run out of the browser process depending on field trials of the NetworkService experiment.''',
+      If this policy is not set, networking code will run out of the browser process by default.
+
+      This policy will be removed in <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> version 82.''',
     },
     {
       'name': 'VoiceInteractionContextEnabled',
diff --git a/components/resources/safe_browsing_resources.grdp b/components/resources/safe_browsing_resources.grdp
index c10bace..ecee7a1 100644
--- a/components/resources/safe_browsing_resources.grdp
+++ b/components/resources/safe_browsing_resources.grdp
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <grit-part>
-  <include name="IDR_SAFE_BROWSING_HTML" file="..\..\components\safe_browsing\web_ui\resources\safe_browsing.html" type="BINDATA" compress="gzip" />
-  <include name="IDR_SAFE_BROWSING_CSS" file="..\..\components\safe_browsing\web_ui\resources\safe_browsing.css" type="BINDATA" compress="gzip" />
-  <include name="IDR_SAFE_BROWSING_JS" file="..\..\components\safe_browsing\web_ui\resources\safe_browsing.js" type="BINDATA" compress="gzip" />
+  <include name="IDR_SAFE_BROWSING_HTML" file="..\..\components\safe_browsing\content\web_ui\resources\safe_browsing.html" type="BINDATA" compress="gzip" />
+  <include name="IDR_SAFE_BROWSING_CSS" file="..\..\components\safe_browsing\content\web_ui\resources\safe_browsing.css" type="BINDATA" compress="gzip" />
+  <include name="IDR_SAFE_BROWSING_JS" file="..\..\components\safe_browsing\content\web_ui\resources\safe_browsing.js" type="BINDATA" compress="gzip" />
 </grit-part>
diff --git a/components/safe_browsing/BUILD.gn b/components/safe_browsing/BUILD.gn
index dda10cc..89ed2c0 100644
--- a/components/safe_browsing/BUILD.gn
+++ b/components/safe_browsing/BUILD.gn
@@ -4,7 +4,6 @@
 
 import("//build/buildflag_header.gni")
 import("//components/safe_browsing/buildflags.gni")
-import("//third_party/protobuf/proto_library.gni")
 
 buildflag_header("buildflags") {
   header = "buildflags.h"
@@ -27,165 +26,3 @@
     flags += [ "SAFE_BROWSING_DB_REMOTE=1" ]
   }
 }
-
-static_library("features") {
-  sources = [
-    "features.cc",
-    "features.h",
-  ]
-
-  deps = [
-    "//base:base",
-    "//components/safe_browsing:buildflags",
-  ]
-}
-
-# safe_browsing/ pulls in content/, which doesn't work on iOS.
-# TODO(thakis): This should be `safe_browsing_mode != 0`, but chromecast builds
-# set safe_browsing_mode to 0 and build chrome/, and chrome/ currently
-# unconditionally depends on things in this build file. Make these dependencies
-# conditional on safe_browsing_mode != 0 and then change the conditional here.
-if (!is_ios) {
-  assert(!is_ios, "safe_browsing/ pulls in content/ which doesn't work on iOS")
-
-  proto_library("csd_proto") {
-    sources = [
-      "proto/csd.proto",
-    ]
-  }
-
-  proto_library("webui_proto") {
-    sources = [
-      "proto/webui.proto",
-    ]
-  }
-
-  proto_library("realtimeapi_proto") {
-    sources = [
-      "proto/realtimeapi.proto",
-    ]
-
-    deps = [
-      ":csd_proto",
-    ]
-  }
-
-  proto_library("webprotect_proto") {
-    sources = [
-      "proto/webprotect.proto",
-    ]
-  }
-
-  source_set("safe_browsing") {
-    sources = [
-      "base_blocking_page.cc",
-      "base_blocking_page.h",
-      "base_ui_manager.cc",
-      "base_ui_manager.h",
-      "safe_browsing_controller_client.cc",
-      "safe_browsing_controller_client.h",
-    ]
-    public_deps = [
-      "//components/security_interstitials/content:security_interstitial_page",
-    ]
-    deps = [
-      ":features",
-      ":ping_manager",
-      ":verdict_cache_manager",
-      "//base:base",
-      "//base:i18n",
-      "//components/safe_browsing/common:common",
-      "//components/safe_browsing/common:safe_browsing_prefs",
-      "//components/safe_browsing/db:database_manager",
-      "//components/safe_browsing/web_ui:constants",
-      "//components/security_interstitials/core:core",
-      "//content/public/browser:browser",
-      "//content/public/common:common",
-      "//net:net",
-    ]
-  }
-
-  static_library("ping_manager") {
-    sources = [
-      "ping_manager.cc",
-      "ping_manager.h",
-    ]
-
-    public_deps = [
-      "//google_apis:google_apis",
-    ]
-
-    deps = [
-      "//base:base",
-      "//components/safe_browsing/db:hit_report",
-      "//components/safe_browsing/db:util",
-      "//content/public/browser:browser",
-      "//net:net",
-    ]
-  }
-
-  source_set("ping_manager_unittest") {
-    testonly = true
-    sources = [
-      "ping_manager_unittest.cc",
-    ]
-
-    deps = [
-      ":ping_manager",
-      "//base:base",
-      "//components/safe_browsing/db:v4_test_util",
-      "//net:net",
-      "//net:test_support",
-      "//testing/gtest",
-    ]
-  }
-
-  source_set("public") {
-    sources = [
-      "safe_browsing_service_interface.cc",
-      "safe_browsing_service_interface.h",
-    ]
-
-    deps = [
-      "//base:base",
-      "//content/public/browser",
-    ]
-  }
-
-  source_set("verdict_cache_manager") {
-    sources = [
-      "verdict_cache_manager.cc",
-      "verdict_cache_manager.h",
-    ]
-
-    deps = [
-      ":csd_proto",
-      ":realtimeapi_proto",
-      "//base",
-      "//components/content_settings/core/browser",
-      "//components/history/core/browser",
-      "//components/password_manager/core/browser:browser",
-      "//components/safe_browsing/db:v4_protocol_manager_util",
-      "//content/public/browser",
-      "//url",
-    ]
-  }
-
-  source_set("verdict_cache_manager_unittest") {
-    testonly = true
-    sources = [
-      "verdict_cache_manager_unittest.cc",
-    ]
-
-    deps = [
-      ":csd_proto",
-      ":realtimeapi_proto",
-      ":verdict_cache_manager",
-      "//base",
-      "//components/content_settings/core/browser",
-      "//components/sync_preferences:test_support",
-      "//content/test:test_support",
-      "//testing/gtest",
-    ]
-  }
-}
diff --git a/components/safe_browsing/android/BUILD.gn b/components/safe_browsing/android/BUILD.gn
index 935edd2..aabbb82 100644
--- a/components/safe_browsing/android/BUILD.gn
+++ b/components/safe_browsing/android/BUILD.gn
@@ -30,7 +30,7 @@
     ":remote_database_manager",
     ":safe_browsing_api_handler",
     ":safe_browsing_api_handler_util",
-    "//components/safe_browsing/db:safe_browsing_db_shared",
+    "//components/safe_browsing/core/db:safe_browsing_db_shared",
   ]
 }
 
@@ -42,10 +42,10 @@
   deps = [
     ":safe_browsing_api_handler",
     "//base:base",
-    "//components/safe_browsing/db:database_manager",
-    "//components/safe_browsing/db:v4_get_hash_protocol_manager",
-    "//components/safe_browsing/db:v4_protocol_manager_util",
-    "//components/safe_browsing/realtime:url_lookup_service",
+    "//components/safe_browsing/core/db:database_manager",
+    "//components/safe_browsing/core/db:v4_get_hash_protocol_manager",
+    "//components/safe_browsing/core/db:v4_protocol_manager_util",
+    "//components/safe_browsing/core/realtime:url_lookup_service",
     "//components/variations",
     "//content/public/browser",
     "//net",
@@ -60,8 +60,8 @@
   ]
   deps = [
     "//base",
-    "//components/safe_browsing/db:metadata_proto",
-    "//components/safe_browsing/db:util",
+    "//components/safe_browsing/core/db:metadata_proto",
+    "//components/safe_browsing/core/db:util",
   ]
 }
 
@@ -73,8 +73,8 @@
   deps = [
     ":safe_browsing_api_handler_util",
     "//base",
-    "//components/safe_browsing:features",
-    "//components/safe_browsing/db:util",
+    "//components/safe_browsing/core:features",
+    "//components/safe_browsing/core/db:util",
     "//content/public/browser:browser",
     "//url",
   ]
@@ -99,10 +99,10 @@
     ":safe_browsing_api_handler",
     ":safe_browsing_api_handler_util",
     "//base",
-    "//components/safe_browsing/db:metadata_proto",
-    "//components/safe_browsing/db:unit_tests_shared",
-    "//components/safe_browsing/db:util",
-    "//components/safe_browsing/db:v4_test_util",
+    "//components/safe_browsing/core/db:metadata_proto",
+    "//components/safe_browsing/core/db:unit_tests_shared",
+    "//components/safe_browsing/core/db:util",
+    "//components/safe_browsing/core/db:v4_test_util",
     "//components/variations",
     "//content/test:test_support",
     "//testing/gtest",
diff --git a/components/safe_browsing/android/remote_database_manager.cc b/components/safe_browsing/android/remote_database_manager.cc
index 4bd0bed..3fb4a10 100644
--- a/components/safe_browsing/android/remote_database_manager.cc
+++ b/components/safe_browsing/android/remote_database_manager.cc
@@ -14,8 +14,8 @@
 #include "base/strings/string_split.h"
 #include "base/timer/elapsed_timer.h"
 #include "components/safe_browsing/android/safe_browsing_api_handler.h"
-#include "components/safe_browsing/db/v4_get_hash_protocol_manager.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/core/db/v4_get_hash_protocol_manager.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
 #include "components/variations/variations_associated_data.h"
 #include "content/public/browser/browser_thread.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
diff --git a/components/safe_browsing/android/remote_database_manager.h b/components/safe_browsing/android/remote_database_manager.h
index c1e84447..e5a5ad3 100644
--- a/components/safe_browsing/android/remote_database_manager.h
+++ b/components/safe_browsing/android/remote_database_manager.h
@@ -16,8 +16,8 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
-#include "components/safe_browsing/db/database_manager.h"
-#include "components/safe_browsing/realtime/url_lookup_service.h"
+#include "components/safe_browsing/core/db/database_manager.h"
+#include "components/safe_browsing/core/realtime/url_lookup_service.h"
 #include "url/gurl.h"
 
 namespace safe_browsing {
diff --git a/components/safe_browsing/android/safe_browsing_api_handler.h b/components/safe_browsing/android/safe_browsing_api_handler.h
index fcefe82..d8d50dc 100644
--- a/components/safe_browsing/android/safe_browsing_api_handler.h
+++ b/components/safe_browsing/android/safe_browsing_api_handler.h
@@ -13,8 +13,8 @@
 #include <vector>
 
 #include "base/callback.h"
-#include "components/safe_browsing/db/util.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/core/db/util.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
 #include "url/gurl.h"
 
 namespace safe_browsing {
diff --git a/components/safe_browsing/android/safe_browsing_api_handler_bridge.cc b/components/safe_browsing/android/safe_browsing_api_handler_bridge.cc
index 05f961b..5b3e1b0d 100644
--- a/components/safe_browsing/android/safe_browsing_api_handler_bridge.cc
+++ b/components/safe_browsing/android/safe_browsing_api_handler_bridge.cc
@@ -18,8 +18,8 @@
 #include "base/trace_event/trace_event.h"
 #include "components/safe_browsing/android/jni_headers/SafeBrowsingApiBridge_jni.h"
 #include "components/safe_browsing/android/safe_browsing_api_handler_util.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
-#include "components/safe_browsing/features.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/core/features.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 
diff --git a/components/safe_browsing/android/safe_browsing_api_handler_bridge.h b/components/safe_browsing/android/safe_browsing_api_handler_bridge.h
index ad5086c..69b4580 100644
--- a/components/safe_browsing/android/safe_browsing_api_handler_bridge.h
+++ b/components/safe_browsing/android/safe_browsing_api_handler_bridge.h
@@ -15,7 +15,7 @@
 #include "base/android/jni_android.h"
 #include "base/macros.h"
 #include "components/safe_browsing/android/safe_browsing_api_handler.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
 #include "url/gurl.h"
 
 namespace safe_browsing {
diff --git a/components/safe_browsing/android/safe_browsing_api_handler_unittest.cc b/components/safe_browsing/android/safe_browsing_api_handler_unittest.cc
index e9b9c617..8bafc39 100644
--- a/components/safe_browsing/android/safe_browsing_api_handler_unittest.cc
+++ b/components/safe_browsing/android/safe_browsing_api_handler_unittest.cc
@@ -7,9 +7,9 @@
 #include <string>
 
 #include "base/strings/stringprintf.h"
-#include "components/safe_browsing/db/metadata.pb.h"
-#include "components/safe_browsing/db/util.h"
-#include "components/safe_browsing/db/v4_test_util.h"
+#include "components/safe_browsing/core/db/metadata.pb.h"
+#include "components/safe_browsing/core/db/util.h"
+#include "components/safe_browsing/core/db/v4_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace safe_browsing {
diff --git a/components/safe_browsing/android/safe_browsing_api_handler_util.cc b/components/safe_browsing/android/safe_browsing_api_handler_util.cc
index a8a9c0ae..a1c8a95 100644
--- a/components/safe_browsing/android/safe_browsing_api_handler_util.cc
+++ b/components/safe_browsing/android/safe_browsing_api_handler_util.cc
@@ -14,8 +14,8 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
 #include "base/values.h"
-#include "components/safe_browsing/db/metadata.pb.h"
-#include "components/safe_browsing/db/util.h"
+#include "components/safe_browsing/core/db/metadata.pb.h"
+#include "components/safe_browsing/core/db/util.h"
 
 namespace safe_browsing {
 namespace {
diff --git a/components/safe_browsing/android/safe_browsing_api_handler_util.h b/components/safe_browsing/android/safe_browsing_api_handler_util.h
index 8d21e175..495d94c 100644
--- a/components/safe_browsing/android/safe_browsing_api_handler_util.h
+++ b/components/safe_browsing/android/safe_browsing_api_handler_util.h
@@ -9,7 +9,7 @@
 
 #include <string>
 
-#include "components/safe_browsing/db/util.h"
+#include "components/safe_browsing/core/db/util.h"
 
 namespace safe_browsing {
 
diff --git a/components/safe_browsing/base_blocking_page.cc b/components/safe_browsing/base_blocking_page.cc
deleted file mode 100644
index fc93d52..0000000
--- a/components/safe_browsing/base_blocking_page.cc
+++ /dev/null
@@ -1,422 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/base_blocking_page.h"
-
-#include <memory>
-
-#include "base/bind.h"
-#include "base/feature_list.h"
-#include "base/lazy_instance.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/time/time.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/features.h"
-#include "components/safe_browsing/safe_browsing_controller_client.h"
-#include "components/security_interstitials/content/security_interstitial_controller_client.h"
-#include "components/security_interstitials/core/metrics_helper.h"
-#include "components/security_interstitials/core/safe_browsing_loud_error_ui.h"
-#include "content/public/browser/interstitial_page.h"
-#include "content/public/browser/navigation_entry.h"
-#include "content/public/browser/web_contents.h"
-
-using content::InterstitialPage;
-using content::WebContents;
-using security_interstitials::BaseSafeBrowsingErrorUI;
-using security_interstitials::SafeBrowsingLoudErrorUI;
-using security_interstitials::SecurityInterstitialControllerClient;
-
-namespace safe_browsing {
-
-namespace {
-
-// After a safe browsing interstitial where the user opted-in to the report
-// but clicked "proceed anyway", we delay the call to
-// ThreatDetails::FinishCollection() by this much time (in
-// milliseconds).
-const int64_t kThreatDetailsProceedDelayMilliSeconds = 3000;
-
-base::LazyInstance<BaseBlockingPage::UnsafeResourceMap>::Leaky
-    g_unsafe_resource_map = LAZY_INSTANCE_INITIALIZER;
-
-}  // namespace
-
-BaseBlockingPage::BaseBlockingPage(
-    BaseUIManager* ui_manager,
-    WebContents* web_contents,
-    const GURL& main_frame_url,
-    const UnsafeResourceList& unsafe_resources,
-    std::unique_ptr<SecurityInterstitialControllerClient> controller_client,
-    const BaseSafeBrowsingErrorUI::SBErrorDisplayOptions& display_options)
-    : SecurityInterstitialPage(web_contents,
-                               unsafe_resources[0].url,
-                               std::move(controller_client)),
-      ui_manager_(ui_manager),
-      main_frame_url_(main_frame_url),
-      navigation_entry_index_to_remove_(
-          IsMainPageLoadBlocked(unsafe_resources) ||
-                  base::FeatureList::IsEnabled(kCommittedSBInterstitials)
-              ? -1
-              : web_contents->GetController().GetLastCommittedEntryIndex()),
-      unsafe_resources_(unsafe_resources),
-      proceeded_(false),
-      threat_details_proceed_delay_ms_(kThreatDetailsProceedDelayMilliSeconds),
-      sb_error_ui_(std::make_unique<SafeBrowsingLoudErrorUI>(
-          unsafe_resources_[0].url,
-          main_frame_url_,
-          GetInterstitialReason(unsafe_resources_),
-          display_options,
-          ui_manager->app_locale(),
-          base::Time::NowFromSystemTime(),
-          controller(),
-          /* created_prior_to_navigation */
-          IsMainPageLoadBlocked(unsafe_resources) &&
-              base::FeatureList::IsEnabled(kCommittedSBInterstitials))) {}
-
-BaseBlockingPage::~BaseBlockingPage() {}
-
-// static
-const security_interstitials::BaseSafeBrowsingErrorUI::SBErrorDisplayOptions
-BaseBlockingPage::CreateDefaultDisplayOptions(
-    const UnsafeResourceList& unsafe_resources) {
-  return BaseSafeBrowsingErrorUI::SBErrorDisplayOptions(
-      IsMainPageLoadBlocked(unsafe_resources),
-      false,                 // kSafeBrowsingExtendedReportingOptInAllowed
-      false,                 // is_off_the_record
-      false,                 // is_extended_reporting
-      false,                 // is_sber_policy_managed
-      false,                 // kSafeBrowsingProceedAnywayDisabled
-      false,                 // should_open_links_in_new_tab
-      true,                  // always_show_back_to_safety
-      "cpn_safe_browsing");  // help_center_article_link
-}
-
-// static
-void BaseBlockingPage::ShowBlockingPage(
-    BaseUIManager* ui_manager,
-    const UnsafeResource& unsafe_resource) {
-  WebContents* web_contents = unsafe_resource.web_contents_getter.Run();
-
-  if (InterstitialPage::GetInterstitialPage(web_contents) &&
-      unsafe_resource.is_subresource) {
-    // This is an interstitial for a page's resource, let's queue it.
-    UnsafeResourceMap* unsafe_resource_map = GetUnsafeResourcesMap();
-    (*unsafe_resource_map)[web_contents].push_back(unsafe_resource);
-  } else {
-    // There is no interstitial currently showing in that tab, or we are about
-    // to display a new one for the main frame. If there is already an
-    // interstitial, showing the new one will automatically hide the old one.
-    content::NavigationEntry* entry =
-        unsafe_resource.GetNavigationEntryForResource();
-    const UnsafeResourceList unsafe_resources{unsafe_resource};
-    BaseBlockingPage* blocking_page = new BaseBlockingPage(
-        ui_manager, web_contents, entry ? entry->GetURL() : GURL(),
-        unsafe_resources,
-        CreateControllerClient(web_contents, unsafe_resources, ui_manager,
-                               nullptr),
-        CreateDefaultDisplayOptions(unsafe_resources));
-    blocking_page->Show();
-  }
-}
-
-// static
-bool BaseBlockingPage::IsMainPageLoadBlocked(
-    const UnsafeResourceList& unsafe_resources) {
-  // If there is more than one unsafe resource, the main page load must not be
-  // blocked. Otherwise, check if the one resource is.
-  return unsafe_resources.size() == 1 &&
-         unsafe_resources[0].IsMainPageLoadBlocked();
-}
-
-void BaseBlockingPage::OnProceed() {
-  set_proceeded(true);
-  OnInterstitialClosing();
-
-  // Send the threat details, if we opted to.
-  FinishThreatDetails(
-      base::TimeDelta::FromMilliseconds(threat_details_proceed_delay_ms_),
-      true, /* did_proceed */
-      controller()->metrics_helper()->NumVisits());
-
-  ui_manager_->OnBlockingPageDone(unsafe_resources_, true /* proceed */,
-                                  web_contents(), main_frame_url_);
-
-  HandleSubresourcesAfterProceed();
-}
-
-void BaseBlockingPage::HandleSubresourcesAfterProceed() {}
-
-void BaseBlockingPage::SetThreatDetailsProceedDelayForTesting(int64_t delay) {
-  threat_details_proceed_delay_ms_ = delay;
-}
-
-void BaseBlockingPage::OnDontProceed() {
-  // With committed interstitials we shouldn't hit this code.
-  DCHECK(
-      !base::FeatureList::IsEnabled(safe_browsing::kCommittedSBInterstitials));
-
-  // We could have already called Proceed(), in which case we must not notify
-  // the SafeBrowsingUIManager again, as the client has been deleted.
-  if (proceeded_)
-    return;
-
-  OnInterstitialClosing();
-
-  // Send the malware details, if we opted to.
-  FinishThreatDetails(base::TimeDelta(), false /* did_proceed */,
-                      controller()->metrics_helper()->NumVisits());  // No delay
-
-  OnDontProceedDone();
-}
-
-void BaseBlockingPage::CommandReceived(const std::string& page_cmd) {
-  if (page_cmd == "\"pageLoadComplete\"") {
-    // content::WaitForRenderFrameReady sends this message when the page
-    // load completes. Ignore it.
-    return;
-  }
-
-  int command = 0;
-  bool retval = base::StringToInt(page_cmd, &command);
-  DCHECK(retval) << page_cmd;
-  auto interstitial_command =
-      static_cast<security_interstitials::SecurityInterstitialCommand>(command);
-
-  if (base::FeatureList::IsEnabled(safe_browsing::kCommittedSBInterstitials) &&
-      interstitial_command ==
-          security_interstitials::SecurityInterstitialCommand::CMD_PROCEED) {
-    // With committed interstitials, OnProceed() doesn't get called, so handle
-    // adding to the allow list here.
-    set_proceeded(true);
-    ui_manager()->OnBlockingPageDone(unsafe_resources(), true /* proceed */,
-                                     web_contents(), main_frame_url());
-  }
-
-  sb_error_ui_->HandleCommand(interstitial_command);
-}
-
-bool BaseBlockingPage::ShouldCreateNewNavigation() const {
-  return sb_error_ui_->is_main_frame_load_blocked();
-}
-
-void BaseBlockingPage::PopulateInterstitialStrings(
-    base::DictionaryValue* load_time_data) {
-  sb_error_ui_->PopulateStringsForHtml(load_time_data);
-}
-
-void BaseBlockingPage::OnInterstitialClosing() {
-  UpdateMetricsAfterSecurityInterstitial();
-}
-
-void BaseBlockingPage::FinishThreatDetails(const base::TimeDelta& delay,
-                                           bool did_proceed,
-                                           int num_visits) {}
-
-// static
-BaseBlockingPage::UnsafeResourceMap*
-BaseBlockingPage::GetUnsafeResourcesMap() {
-  return g_unsafe_resource_map.Pointer();
-}
-
-// static
-std::string BaseBlockingPage::GetMetricPrefix(
-    const UnsafeResourceList& unsafe_resources,
-    BaseSafeBrowsingErrorUI::SBInterstitialReason interstitial_reason) {
-  bool primary_subresource = unsafe_resources[0].is_subresource;
-  switch (interstitial_reason) {
-    case BaseSafeBrowsingErrorUI::SB_REASON_MALWARE:
-      return primary_subresource ? "malware_subresource" : "malware";
-    case BaseSafeBrowsingErrorUI::SB_REASON_HARMFUL:
-      return primary_subresource ? "harmful_subresource" : "harmful";
-    case BaseSafeBrowsingErrorUI::SB_REASON_BILLING:
-      return primary_subresource ? "billing_subresource" : "billing";
-    case BaseSafeBrowsingErrorUI::SB_REASON_PHISHING:
-      ThreatPatternType threat_pattern_type =
-          unsafe_resources[0].threat_metadata.threat_pattern_type;
-      if (threat_pattern_type == ThreatPatternType::PHISHING ||
-          threat_pattern_type == ThreatPatternType::NONE)
-        return primary_subresource ? "phishing_subresource" : "phishing";
-      else if (threat_pattern_type == ThreatPatternType::SOCIAL_ENGINEERING_ADS)
-        return primary_subresource ? "social_engineering_ads_subresource"
-                                   : "social_engineering_ads";
-      else if (threat_pattern_type ==
-               ThreatPatternType::SOCIAL_ENGINEERING_LANDING)
-        return primary_subresource ? "social_engineering_landing_subresource"
-                                   : "social_engineering_landing";
-  }
-  NOTREACHED();
-  return "unkown_metric_prefix";
-}
-
-// We populate a parallel set of metrics to differentiate some threat sources.
-// static
-std::string BaseBlockingPage::GetExtraMetricsSuffix(
-    const UnsafeResourceList& unsafe_resources) {
-  switch (unsafe_resources[0].threat_source) {
-    case safe_browsing::ThreatSource::DATA_SAVER:
-      return "from_data_saver";
-    case safe_browsing::ThreatSource::REMOTE:
-    case safe_browsing::ThreatSource::LOCAL_PVER3:
-      // REMOTE and LOCAL_PVER3 can be distinguished in the logs
-      // by platform type: Remote is mobile, local_pver3 is desktop.
-      return "from_device";
-    case safe_browsing::ThreatSource::LOCAL_PVER4:
-      return "from_device_v4";
-    case safe_browsing::ThreatSource::CLIENT_SIDE_DETECTION:
-      return "from_client_side_detection";
-    case safe_browsing::ThreatSource::PASSWORD_PROTECTION_SERVICE:
-      return "from_password_protection_service";
-    case safe_browsing::ThreatSource::UNKNOWN:
-      break;
-  }
-  NOTREACHED();
-  return std::string();
-}
-
-// static
-security_interstitials::BaseSafeBrowsingErrorUI::SBInterstitialReason
-BaseBlockingPage::GetInterstitialReason(
-    const UnsafeResourceList& unsafe_resources) {
-  bool harmful = false;
-  for (auto iter = unsafe_resources.begin(); iter != unsafe_resources.end();
-       ++iter) {
-    const BaseUIManager::UnsafeResource& resource = *iter;
-    safe_browsing::SBThreatType threat_type = resource.threat_type;
-    if (threat_type == SB_THREAT_TYPE_BILLING)
-      return BaseSafeBrowsingErrorUI::SB_REASON_BILLING;
-
-    if (threat_type == SB_THREAT_TYPE_URL_MALWARE ||
-        threat_type == SB_THREAT_TYPE_URL_CLIENT_SIDE_MALWARE) {
-      return BaseSafeBrowsingErrorUI::SB_REASON_MALWARE;
-    }
-
-    if (threat_type == SB_THREAT_TYPE_URL_UNWANTED) {
-      harmful = true;
-    } else {
-      DCHECK(threat_type == SB_THREAT_TYPE_URL_PHISHING ||
-             threat_type == SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING);
-    }
-  }
-
-  if (harmful)
-    return BaseSafeBrowsingErrorUI::SB_REASON_HARMFUL;
-  return BaseSafeBrowsingErrorUI::SB_REASON_PHISHING;
-}
-
-BaseUIManager* BaseBlockingPage::ui_manager() const {
-  return ui_manager_;
-}
-
-const GURL BaseBlockingPage::main_frame_url() const {
-  return main_frame_url_;
-}
-
-BaseBlockingPage::UnsafeResourceList
-BaseBlockingPage::unsafe_resources() const {
-  return unsafe_resources_;
-}
-
-bool BaseBlockingPage::proceeded() const {
-  return proceeded_;
-}
-
-int64_t BaseBlockingPage::threat_details_proceed_delay() const {
-  return threat_details_proceed_delay_ms_;
-}
-
-BaseSafeBrowsingErrorUI* BaseBlockingPage::sb_error_ui() const {
-  return sb_error_ui_.get();
-}
-
-void BaseBlockingPage::set_proceeded(bool proceeded) {
-  proceeded_ = proceeded;
-}
-
-// static
-security_interstitials::MetricsHelper::ReportDetails
-BaseBlockingPage::GetReportingInfo(const UnsafeResourceList& unsafe_resources) {
-  BaseSafeBrowsingErrorUI::SBInterstitialReason interstitial_reason =
-      GetInterstitialReason(unsafe_resources);
-
-  security_interstitials::MetricsHelper::ReportDetails reporting_info;
-  reporting_info.metric_prefix =
-      GetMetricPrefix(unsafe_resources, interstitial_reason);
-  reporting_info.extra_suffix = GetExtraMetricsSuffix(unsafe_resources);
-  return reporting_info;
-}
-
-// static
-std::unique_ptr<SecurityInterstitialControllerClient>
-BaseBlockingPage::CreateControllerClient(
-    content::WebContents* web_contents,
-    const UnsafeResourceList& unsafe_resources,
-    BaseUIManager* ui_manager,
-    PrefService* pref_service) {
-  history::HistoryService* history_service =
-      ui_manager->history_service(web_contents);
-
-  std::unique_ptr<security_interstitials::MetricsHelper> metrics_helper =
-      std::make_unique<security_interstitials::MetricsHelper>(
-          unsafe_resources[0].url, GetReportingInfo(unsafe_resources),
-          history_service);
-
-  return std::make_unique<SafeBrowsingControllerClient>(
-      web_contents, std::move(metrics_helper), pref_service,
-      ui_manager->app_locale(), ui_manager->default_safe_page());
-}
-
-int BaseBlockingPage::GetHTMLTemplateId() {
-  return sb_error_ui_->GetHTMLTemplateId();
-}
-
-void BaseBlockingPage::set_sb_error_ui(
-    std::unique_ptr<BaseSafeBrowsingErrorUI> sb_error_ui) {
-  sb_error_ui_ = std::move(sb_error_ui);
-}
-
-void BaseBlockingPage::OnDontProceedDone() {
-  if (!sb_error_ui_->is_proceed_anyway_disabled()) {
-    controller()->metrics_helper()->RecordUserDecision(
-        security_interstitials::MetricsHelper::DONT_PROCEED);
-  }
-
-  ui_manager_->OnBlockingPageDone(unsafe_resources_, false /* proceed */,
-                                  web_contents(), main_frame_url_);
-
-  // The user does not want to proceed, clear the queued unsafe resources
-  // notifications we received while the interstitial was showing.
-  UnsafeResourceMap* unsafe_resource_map = GetUnsafeResourcesMap();
-  auto iter = unsafe_resource_map->find(web_contents());
-  if (iter != unsafe_resource_map->end() && !iter->second.empty()) {
-    ui_manager_->OnBlockingPageDone(iter->second, false, web_contents(),
-                                    main_frame_url_);
-    unsafe_resource_map->erase(iter);
-  }
-
-  // We don't remove the navigation entry if the tab is being destroyed as this
-  // would trigger a navigation that would cause trouble as the render view host
-  // for the tab has by then already been destroyed.  We also don't delete the
-  // current entry if it has been committed again, which is possible on a page
-  // that had a subresource warning.
-  const int last_committed_index =
-      web_contents()->GetController().GetLastCommittedEntryIndex();
-  if (navigation_entry_index_to_remove_ != -1 &&
-      navigation_entry_index_to_remove_ != last_committed_index &&
-      !web_contents()->IsBeingDestroyed()) {
-    CHECK(web_contents()->GetController().RemoveEntryAtIndex(
-        navigation_entry_index_to_remove_));
-  }
-}
-
-// static
-bool BaseBlockingPage::ShouldReportThreatDetails(SBThreatType threat_type) {
-  return threat_type == SB_THREAT_TYPE_BILLING ||
-         threat_type == SB_THREAT_TYPE_URL_CLIENT_SIDE_MALWARE ||
-         threat_type == SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING ||
-         threat_type == SB_THREAT_TYPE_URL_MALWARE ||
-         threat_type == SB_THREAT_TYPE_URL_PHISHING ||
-         threat_type == SB_THREAT_TYPE_URL_UNWANTED;
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/base_blocking_page.h b/components/safe_browsing/base_blocking_page.h
deleted file mode 100644
index 7ef7640..0000000
--- a/components/safe_browsing/base_blocking_page.h
+++ /dev/null
@@ -1,167 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_BASE_BLOCKING_PAGE_H_
-#define COMPONENTS_SAFE_BROWSING_BASE_BLOCKING_PAGE_H_
-
-#include <map>
-#include <string>
-#include <vector>
-
-#include "base/macros.h"
-#include "components/safe_browsing/base_ui_manager.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
-#include "components/security_interstitials/content/security_interstitial_page.h"
-#include "components/security_interstitials/core/base_safe_browsing_error_ui.h"
-#include "components/security_interstitials/core/metrics_helper.h"
-#include "content/public/browser/interstitial_page_delegate.h"
-#include "url/gurl.h"
-
-namespace safe_browsing {
-
-// Base class for managing the SafeBrowsing interstitial pages.
-class BaseBlockingPage
-    : public security_interstitials::SecurityInterstitialPage {
- public:
-  typedef security_interstitials::UnsafeResource UnsafeResource;
-  typedef security_interstitials::BaseSafeBrowsingErrorUI
-      BaseSafeBrowsingErrorUI;
-  typedef std::vector<UnsafeResource> UnsafeResourceList;
-  typedef std::unordered_map<content::WebContents*, UnsafeResourceList>
-      UnsafeResourceMap;
-
-  ~BaseBlockingPage() override;
-
-  static const BaseSafeBrowsingErrorUI::SBErrorDisplayOptions
-  CreateDefaultDisplayOptions(const UnsafeResourceList& unsafe_resources);
-
-  // Shows a blocking page warning the user about phishing/malware for a
-  // specific resource.
-  // This can be called several times. If an interstitial is already showing
-  // and the user decides to proceed, it will be discarded and a new one will be
-  // displayed.
-  static void ShowBlockingPage(BaseUIManager* ui_manager,
-                               const UnsafeResource& resource);
-
-  // Returns true if the passed |unsafe_resources| is blocking the load of
-  // the main page.
-  static bool IsMainPageLoadBlocked(const UnsafeResourceList& unsafe_resources);
-
-  // InterstitialPageDelegate methods:
-  void OnProceed() override;
-  void OnDontProceed() override;
-  void CommandReceived(const std::string& command) override;
-
-  // Checks the threat type to decide if we should report ThreatDetails.
-  static bool ShouldReportThreatDetails(SBThreatType threat_type);
-
- protected:
-  // Don't instantiate this class directly, use ShowBlockingPage instead.
-  BaseBlockingPage(
-      BaseUIManager* ui_manager,
-      content::WebContents* web_contents,
-      const GURL& main_frame_url,
-      const UnsafeResourceList& unsafe_resources,
-      std::unique_ptr<
-          security_interstitials::SecurityInterstitialControllerClient>
-          controller_client,
-      const BaseSafeBrowsingErrorUI::SBErrorDisplayOptions& display_options);
-
-  // SecurityInterstitialPage methods:
-  bool ShouldCreateNewNavigation() const override;
-  void PopulateInterstitialStrings(
-      base::DictionaryValue* load_time_data) override;
-  void OnInterstitialClosing() override;
-
-  // Called when the interstitial is going away. Intentionally do nothing in
-  // this base class.
-  virtual void FinishThreatDetails(const base::TimeDelta& delay,
-                                   bool did_proceed,
-                                   int num_visits);
-
-  // A list of SafeBrowsingUIManager::UnsafeResource for a tab that the user
-  // should be warned about. They are queued when displaying more than one
-  // interstitial at a time.
-  static UnsafeResourceMap* GetUnsafeResourcesMap();
-
-  static std::string GetMetricPrefix(
-      const UnsafeResourceList& unsafe_resources,
-      BaseSafeBrowsingErrorUI::SBInterstitialReason interstitial_reason);
-
-  static std::string GetExtraMetricsSuffix(
-      const UnsafeResourceList& unsafe_resources);
-
-  // Return the most severe interstitial reason from a list of unsafe resources.
-  // Severity ranking: malware > UwS (harmful) > phishing.
-  static BaseSafeBrowsingErrorUI::SBInterstitialReason GetInterstitialReason(
-      const UnsafeResourceList& unsafe_resources);
-
-  BaseUIManager* ui_manager() const;
-
-  const GURL main_frame_url() const;
-
-  UnsafeResourceList unsafe_resources() const;
-
-  bool proceeded() const;
-
-  int64_t threat_details_proceed_delay() const;
-
-  BaseSafeBrowsingErrorUI* sb_error_ui() const;
-
-  void set_proceeded(bool proceeded);
-
-  static security_interstitials::MetricsHelper::ReportDetails GetReportingInfo(
-      const UnsafeResourceList& unsafe_resources);
-
-  // Called after OnProceed(). Does nothing in this class, but can be overridden
-  // to handle malicious subresources.
-  virtual void HandleSubresourcesAfterProceed();
-
-  void SetThreatDetailsProceedDelayForTesting(int64_t delay);
-
-  static std::unique_ptr<
-      security_interstitials::SecurityInterstitialControllerClient>
-  CreateControllerClient(content::WebContents* web_contents,
-                         const UnsafeResourceList& unsafe_resources,
-                         BaseUIManager* ui_manager,
-                         PrefService* pref_service);
-
-  int GetHTMLTemplateId() override;
-
-  void set_sb_error_ui(std::unique_ptr<BaseSafeBrowsingErrorUI> sb_error_ui);
-
-  void OnDontProceedDone();
-
- private:
-  // For reporting back user actions.
-  BaseUIManager* ui_manager_;
-
-  // The URL of the main frame that caused the warning.
-  GURL main_frame_url_;
-
-  // The index of a navigation entry that should be removed when DontProceed()
-  // is invoked, -1 if entry should not be removed.
-  const int navigation_entry_index_to_remove_;
-
-  // The list of unsafe resources this page is warning about.
-  UnsafeResourceList unsafe_resources_;
-
-  // Indicate whether user has proceeded this blocking page.
-  bool proceeded_;
-
-  // After a safe browsing interstitial where the user opted-in to the
-  // report but clicked "proceed anyway", we delay the call to
-  // ThreatDetails::FinishCollection() by this much time (in
-  // milliseconds), in order to get data from the blocked resource itself.
-  int64_t threat_details_proceed_delay_ms_;
-
-  // For displaying safe browsing interstitial.
-  std::unique_ptr<BaseSafeBrowsingErrorUI> sb_error_ui_;
-
-  DISALLOW_COPY_AND_ASSIGN(BaseBlockingPage);
-};
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_BASE_BLOCKING_PAGE_H_
diff --git a/components/safe_browsing/base_ui_manager.cc b/components/safe_browsing/base_ui_manager.cc
deleted file mode 100644
index 1324b4b4..0000000
--- a/components/safe_browsing/base_ui_manager.cc
+++ /dev/null
@@ -1,446 +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.
-
-#include <utility>
-
-#include "components/safe_browsing/base_ui_manager.h"
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/feature_list.h"
-#include "base/i18n/rtl.h"
-#include "base/memory/ptr_util.h"
-#include "base/supports_user_data.h"
-#include "components/safe_browsing/base_blocking_page.h"
-#include "components/safe_browsing/features.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/navigation_entry.h"
-#include "content/public/browser/web_contents.h"
-
-using content::BrowserThread;
-using content::NavigationEntry;
-using content::WebContents;
-using safe_browsing::HitReport;
-using safe_browsing::SBThreatType;
-
-namespace {
-
-const void* const kWhitelistKey = &kWhitelistKey;
-
-// A WhitelistUrlSet holds the set of URLs that have been whitelisted
-// for a specific WebContents, along with pending entries that are still
-// undecided. Each URL is associated with the first SBThreatType that
-// was seen for that URL. The URLs in this set should come from
-// GetWhitelistUrl() or GetMainFrameWhitelistUrlForResource() (in
-// SafeBrowsingUIManager)
-class WhitelistUrlSet : public base::SupportsUserData::Data {
- public:
-  WhitelistUrlSet() {}
-  bool Contains(const GURL& url, SBThreatType* threat_type) {
-    auto found = map_.find(url);
-    if (found == map_.end())
-      return false;
-    if (threat_type)
-      *threat_type = found->second;
-    return true;
-  }
-  void RemovePending(const GURL& url) {
-    DCHECK(pending_.end() != pending_.find(url));
-    if (--pending_[url].second < 1)
-      pending_.erase(url);
-  }
-  void Remove(const GURL& url) { map_.erase(url); }
-  void Insert(const GURL& url, SBThreatType threat_type) {
-    if (Contains(url, nullptr))
-      return;
-    map_[url] = threat_type;
-    RemoveAllPending(url);
-  }
-  bool ContainsPending(const GURL& url, SBThreatType* threat_type) {
-    auto found = pending_.find(url);
-    if (found == pending_.end())
-      return false;
-    if (threat_type)
-      *threat_type = found->second.first;
-    return true;
-  }
-  void InsertPending(const GURL url, SBThreatType threat_type) {
-    if (pending_.find(url) != pending_.end()) {
-      pending_[url].first = threat_type;
-      pending_[url].second++;
-      return;
-    }
-    pending_[url] = {threat_type, 1};
-  }
-
- protected:
-  // Method to remove all the instances of a website in the pending list
-  // disregarding the count. Used when adding a site to the permanent list.
-  void RemoveAllPending(const GURL& url) { pending_.erase(url); }
-
- private:
-  std::map<GURL, SBThreatType> map_;
-  // Keep a count of how many times a site has been added to the pending list
-  // in order to solve a problem where upon reloading an interstitial, a site
-  // would be re-added to and removed from the whitelist in the wrong order.
-  std::map<GURL, std::pair<SBThreatType, int>> pending_;
-  DISALLOW_COPY_AND_ASSIGN(WhitelistUrlSet);
-};
-
-// Returns the URL that should be used in a WhitelistUrlSet for the
-// resource loaded from |url| on a navigation |entry|.
-GURL GetWhitelistUrl(const GURL& url,
-                     bool is_subresource,
-                     NavigationEntry* entry) {
-  if (is_subresource) {
-    if (!entry)
-      return GURL();
-    return entry->GetURL().GetWithEmptyPath();
-  }
-  return url.GetWithEmptyPath();
-}
-
-WhitelistUrlSet* GetOrCreateWhitelist(WebContents* web_contents) {
-  WhitelistUrlSet* site_list =
-      static_cast<WhitelistUrlSet*>(web_contents->GetUserData(kWhitelistKey));
-  if (!site_list) {
-    site_list = new WhitelistUrlSet;
-    web_contents->SetUserData(kWhitelistKey, base::WrapUnique(site_list));
-  }
-  return site_list;
-}
-
-}  // namespace
-
-namespace safe_browsing {
-
-BaseUIManager::BaseUIManager() {}
-
-BaseUIManager::~BaseUIManager() {}
-
-bool BaseUIManager::IsWhitelisted(const UnsafeResource& resource) {
-  NavigationEntry* entry = nullptr;
-  if (resource.is_subresource) {
-    entry = resource.GetNavigationEntryForResource();
-  }
-  SBThreatType unused_threat_type;
-  return IsUrlWhitelistedOrPendingForWebContents(
-      resource.url, resource.is_subresource, entry,
-      resource.web_contents_getter.Run(), true, &unused_threat_type);
-}
-
-// Check if the user has already seen and/or ignored a SB warning for this
-// WebContents and top-level domain.
-bool BaseUIManager::IsUrlWhitelistedOrPendingForWebContents(
-    const GURL& url,
-    bool is_subresource,
-    NavigationEntry* entry,
-    WebContents* web_contents,
-    bool whitelist_only,
-    SBThreatType* threat_type) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-  GURL lookup_url = GetWhitelistUrl(url, is_subresource, entry);
-  if (lookup_url.is_empty())
-    return false;
-
-  WhitelistUrlSet* site_list =
-      static_cast<WhitelistUrlSet*>(web_contents->GetUserData(kWhitelistKey));
-  if (!site_list)
-    return false;
-
-  bool whitelisted = site_list->Contains(lookup_url, threat_type);
-  if (whitelist_only) {
-    return whitelisted;
-  } else {
-    return whitelisted || site_list->ContainsPending(lookup_url, threat_type);
-  }
-}
-
-void BaseUIManager::OnBlockingPageDone(
-    const std::vector<UnsafeResource>& resources,
-    bool proceed,
-    WebContents* web_contents,
-    const GURL& main_frame_url) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  for (const auto& resource : resources) {
-    if (!resource.callback.is_null()) {
-      DCHECK(resource.callback_thread);
-      resource.callback_thread->PostTask(
-          FROM_HERE, base::BindOnce(resource.callback, proceed));
-    }
-
-    GURL whitelist_url = GetWhitelistUrl(
-        main_frame_url, false /* is subresource */,
-        nullptr /* no navigation entry needed for main resource */);
-    if (proceed) {
-      AddToWhitelistUrlSet(whitelist_url, web_contents,
-                           false /* Pending -> permanent */,
-                           resource.threat_type);
-    } else if (web_contents) {
-      // |web_contents| doesn't exist if the tab has been closed.
-      RemoveWhitelistUrlSet(whitelist_url, web_contents,
-                            true /* from_pending_only */);
-    }
-  }
-}
-
-void BaseUIManager::DisplayBlockingPage(
-    const UnsafeResource& resource) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (resource.is_subresource && !resource.is_subframe) {
-    // Sites tagged as serving Unwanted Software should only show a warning for
-    // main-frame or sub-frame resource. Similar warning restrictions should be
-    // applied to malware sites tagged as "landing sites" (see "Types of
-    // Malware sites" under
-    // https://developers.google.com/safe-browsing/developers_guide_v3#UserWarnings).
-    if (resource.threat_type == SB_THREAT_TYPE_URL_UNWANTED ||
-        (resource.threat_type == SB_THREAT_TYPE_URL_MALWARE &&
-         resource.threat_metadata.threat_pattern_type ==
-             ThreatPatternType::MALWARE_LANDING)) {
-      if (!resource.callback.is_null()) {
-        DCHECK(resource.callback_thread);
-        resource.callback_thread->PostTask(
-            FROM_HERE, base::BindOnce(resource.callback, true));
-      }
-      return;
-    }
-  }
-
-  // The tab might have been closed. If it was closed, just act as if "Don't
-  // Proceed" had been chosen.
-  WebContents* web_contents = resource.web_contents_getter.Run();
-  if (!web_contents) {
-    OnBlockingPageDone(std::vector<UnsafeResource>{resource},
-                       false /* proceed */,
-                       web_contents,
-                       GetMainFrameWhitelistUrlForResource(resource));
-    return;
-  }
-
-  // Check if the user has already ignored a SB warning for the same WebContents
-  // and top-level domain.
-  if (IsWhitelisted(resource)) {
-    if (!resource.callback.is_null()) {
-      DCHECK(resource.callback_thread);
-      resource.callback_thread->PostTask(
-          FROM_HERE, base::BindOnce(resource.callback, true));
-    }
-
-    return;
-  }
-
-  if (resource.threat_type != SB_THREAT_TYPE_SAFE &&
-      resource.threat_type != SB_THREAT_TYPE_BILLING) {
-    // TODO(vakh): crbug/883462: The reports for SB_THREAT_TYPE_BILLING should
-    // be disabled for M70 but enabled for a later release (M71?).
-    CreateAndSendHitReport(resource);
-  }
-
-  AddToWhitelistUrlSet(GetMainFrameWhitelistUrlForResource(resource),
-                       resource.web_contents_getter.Run(),
-                       true /* A decision is now pending */,
-                       resource.threat_type);
-  if (SafeBrowsingInterstitialsAreCommittedNavigations()) {
-    GURL unsafe_url = (resource.IsMainPageLoadBlocked() ||
-                       !resource.GetNavigationEntryForResource())
-                          ? resource.url
-                          : resource.GetNavigationEntryForResource()->GetURL();
-    AddUnsafeResource(unsafe_url, resource);
-    // With committed interstitials we just cancel the load from here, the
-    // actual interstitial will be shown from the
-    // SafeBrowsingNavigationThrottle.
-    resource.callback_thread->PostTask(
-        FROM_HERE, base::BindOnce(resource.callback, false));
-    if (!resource.IsMainPageLoadBlocked() && !IsWhitelisted(resource)) {
-      // For subresource triggered interstitials, we trigger the error page
-      // navigation from here since there will be no navigation to intercept
-      // in the throttle.
-      content::WebContents* contents = resource.web_contents_getter.Run();
-      content::NavigationEntry* entry =
-          resource.GetNavigationEntryForResource();
-      // entry can be null if we are on a brand new tab, and a resource is added
-      // via javascript without a navigation.
-      GURL blocked_url = entry ? entry->GetURL() : resource.url;
-
-      // Blocking pages handle both user interaction, and generation of the
-      // interstitial HTML. In the case of subresources, we need the HTML
-      // content prior to (and in a different process than when) installing the
-      // command handlers. For this reason we create a blocking page here just
-      // to generate the HTML, and immediately delete it.
-      BaseBlockingPage* blocking_page =
-          CreateBlockingPageForSubresource(contents, blocked_url, resource);
-      contents->GetController().LoadPostCommitErrorPage(
-          contents->GetMainFrame(), blocked_url,
-          blocking_page->GetHTMLContents(), net::ERR_BLOCKED_BY_CLIENT);
-      delete blocking_page;
-    }
-    return;
-  }
-  ShowBlockingPageForResource(resource);
-}
-
-void BaseUIManager::EnsureWhitelistCreated(
-    WebContents* web_contents) {
-  GetOrCreateWhitelist(web_contents);
-}
-
-void BaseUIManager::CreateAndSendHitReport(const UnsafeResource& resource) {}
-
-void BaseUIManager::ShowBlockingPageForResource(
-    const UnsafeResource& resource) {
-  BaseBlockingPage::ShowBlockingPage(this, resource);
-}
-
-bool BaseUIManager::SafeBrowsingInterstitialsAreCommittedNavigations() {
-  return base::FeatureList::IsEnabled(kCommittedSBInterstitials);
-}
-
-BaseBlockingPage* BaseUIManager::CreateBlockingPageForSubresource(
-    content::WebContents* contents,
-    const GURL& blocked_url,
-    const UnsafeResource& unsafe_resource) {
-  // TODO(carlosil): This can be removed once all implementations of SB use
-  // committed interstitials. In the meantime, there is no create method for the
-  // non-committed implementations, and this code won't be called if committed
-  // interstitials are disabled.
-  NOTREACHED();
-  return nullptr;
-}
-
-// A SafeBrowsing hit is sent after a blocking page for malware/phishing
-// or after the warning dialog for download urls, only for extended_reporting
-// users who are not in incognito mode.
-void BaseUIManager::MaybeReportSafeBrowsingHit(
-    const HitReport& hit_report,
-    content::WebContents* web_contents) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  return;
-}
-
-// If the user had opted-in to send ThreatDetails, this gets called
-// when the report is ready.
-void BaseUIManager::SendSerializedThreatDetails(
-    const std::string& serialized) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  return;
-}
-
-// Record this domain in the given WebContents as either whitelisted or
-// pending whitelisting (if an interstitial is currently displayed). If an
-// existing WhitelistUrlSet does not yet exist, create a new WhitelistUrlSet.
-void BaseUIManager::AddToWhitelistUrlSet(const GURL& whitelist_url,
-                                         WebContents* web_contents,
-                                         bool pending,
-                                         SBThreatType threat_type) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-  // A WebContents might not exist if the tab has been closed.
-  if (!web_contents)
-    return;
-
-  WhitelistUrlSet* site_list = GetOrCreateWhitelist(web_contents);
-
-  if (whitelist_url.is_empty())
-    return;
-
-  if (pending) {
-    site_list->InsertPending(whitelist_url, threat_type);
-  } else {
-    site_list->Insert(whitelist_url, threat_type);
-  }
-
-  // Notify security UI that security state has changed.
-  web_contents->DidChangeVisibleSecurityState();
-}
-
-const std::string BaseUIManager::app_locale() const {
-  return base::i18n::GetConfiguredLocale();
-}
-
-history::HistoryService* BaseUIManager::history_service(
-    content::WebContents* web_contents) {
-  return nullptr;
-}
-
-const GURL BaseUIManager::default_safe_page() const {
-  return GURL(url::kAboutBlankURL);
-}
-
-void BaseUIManager::AddUnsafeResource(
-    GURL url,
-    security_interstitials::UnsafeResource resource) {
-  unsafe_resources_.push_back(std::make_pair(url, resource));
-}
-
-bool BaseUIManager::PopUnsafeResourceForURL(
-    GURL url,
-    security_interstitials::UnsafeResource* resource) {
-  for (auto it = unsafe_resources_.begin(); it != unsafe_resources_.end();
-       it++) {
-    if (it->first == url) {
-      *resource = it->second;
-      unsafe_resources_.erase(it);
-      return true;
-    }
-  }
-  return false;
-}
-
-void BaseUIManager::RemoveWhitelistUrlSet(const GURL& whitelist_url,
-                                          WebContents* web_contents,
-                                          bool from_pending_only) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-  // A WebContents might not exist if the tab has been closed.
-  if (!web_contents)
-    return;
-
-  // Use |web_contents| rather than |resource.web_contents_getter|
-  // here. By this point, a "Back" navigation could have already been
-  // committed, so the page loading |resource| might be gone and
-  // |web_contents_getter| may no longer be valid.
-  WhitelistUrlSet* site_list =
-      static_cast<WhitelistUrlSet*>(web_contents->GetUserData(kWhitelistKey));
-
-  if (whitelist_url.is_empty())
-    return;
-
-  // Note that this function does not DCHECK that |whitelist_url|
-  // appears in the pending whitelist. In the common case, it's expected
-  // that a URL is in the pending whitelist when it is removed, but it's
-  // not always the case. For example, if there are several blocking
-  // pages queued up for different resources on the same page, and the
-  // user goes back to dimiss the first one, the subsequent blocking
-  // pages get dismissed as well (as if the user had clicked "Back to
-  // safety" on each of them). In this case, the first dismissal will
-  // remove the main-frame URL from the pending whitelist, so the
-  // main-frame URL will have already been removed when the subsequent
-  // blocking pages are dismissed.
-  if (site_list && site_list->ContainsPending(whitelist_url, nullptr)) {
-    site_list->RemovePending(whitelist_url);
-  }
-
-  if (!from_pending_only && site_list &&
-      site_list->Contains(whitelist_url, nullptr)) {
-    site_list->Remove(whitelist_url);
-  }
-
-  // Notify security UI that security state has changed.
-  web_contents->DidChangeVisibleSecurityState();
-}
-
-// static
-GURL BaseUIManager::GetMainFrameWhitelistUrlForResource(
-    const security_interstitials::UnsafeResource& resource) {
-  if (resource.is_subresource) {
-    NavigationEntry* entry = resource.GetNavigationEntryForResource();
-    if (!entry)
-      return GURL();
-    return entry->GetURL().GetWithEmptyPath();
-  }
-  return resource.url.GetWithEmptyPath();
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/base_ui_manager.h b/components/safe_browsing/base_ui_manager.h
deleted file mode 100644
index 80961d8..0000000
--- a/components/safe_browsing/base_ui_manager.h
+++ /dev/null
@@ -1,171 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_BASE_UI_MANAGER_H_
-#define COMPONENTS_SAFE_BROWSING_BASE_UI_MANAGER_H_
-
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/bind_helpers.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "components/security_interstitials/content/unsafe_resource.h"
-
-class GURL;
-
-namespace content {
-class NavigationEntry;
-class WebContents;
-}  // namespace content
-
-namespace history {
-class HistoryService;
-}  // namespace history
-
-namespace safe_browsing {
-
-class BaseBlockingPage;
-
-// Construction needs to happen on the main thread.
-class BaseUIManager
-    : public base::RefCountedThreadSafe<BaseUIManager> {
- public:
-  typedef security_interstitials::UnsafeResource UnsafeResource;
-
-  BaseUIManager();
-
-  // Called on the UI thread to display an interstitial page.
-  // |resource| is the unsafe resource that triggered the interstitial.
-  virtual void DisplayBlockingPage(const UnsafeResource& resource);
-
-  // This is a no-op in the base class, but should be overridden to send threat
-  // details. Called on the UI thread by the ThreatDetails with the serialized
-  // protocol buffer.
-  virtual void SendSerializedThreatDetails(const std::string& serialized);
-
-  // Updates the whitelist URL set for |web_contents|. Called on the UI thread.
-  void AddToWhitelistUrlSet(const GURL& whitelist_url,
-                            content::WebContents* web_contents,
-                            bool is_pending,
-                            SBThreatType threat_type);
-
-  // This is a no-op in the base class, but should be overridden to report hits
-  // to the unsafe contents (malware, phishing, unsafe download URL)
-  // to the server. Can only be called on UI thread. Will only upload a hit
-  // report if the user has enabled SBER and is not currently in incognito mode.
-  virtual void MaybeReportSafeBrowsingHit(
-      const safe_browsing::HitReport& hit_report,
-      content::WebContents* web_contents);
-
-  // A convenience wrapper method for IsUrlWhitelistedOrPendingForWebContents.
-  virtual bool IsWhitelisted(const UnsafeResource& resource);
-
-  // Checks if we already displayed or are displaying an interstitial
-  // for the top-level site |url| in a given WebContents. If
-  // |whitelist_only|, it returns true only if the user chose to ignore
-  // the interstitial. Otherwise, it returns true if an interstitial for
-  // |url| is already displaying *or* if the user has seen an
-  // interstitial for |url| before in this WebContents and proceeded
-  // through it. Called on the UI thread.
-  //
-  // If the resource was found in the whitelist or pending for the
-  // whitelist, |threat_type| will be set to the SBThreatType for which
-  // the URL was first whitelisted.
-  virtual bool IsUrlWhitelistedOrPendingForWebContents(
-      const GURL& url,
-      bool is_subresource,
-      content::NavigationEntry* entry,
-      content::WebContents* web_contents,
-      bool whitelist_only,
-      SBThreatType* threat_type);
-
-  // The blocking page for |web_contents| on the UI thread has
-  // completed, with |proceed| set to true if the user has chosen to
-  // proceed through the blocking page and false
-  // otherwise. |web_contents| is the WebContents that was displaying
-  // the blocking page. |main_frame_url| is the top-level URL on which
-  // the blocking page was displayed. If |proceed| is true,
-  // |main_frame_url| is whitelisted so that the user will not see
-  // another warning for that URL in this WebContents.
-  virtual void OnBlockingPageDone(const std::vector<UnsafeResource>& resources,
-                                  bool proceed,
-                                  content::WebContents* web_contents,
-                                  const GURL& main_frame_url);
-
-  virtual const std::string app_locale() const;
-
-  virtual history::HistoryService* history_service(
-      content::WebContents* web_contents);
-
-  // The default safe page when there is no entry in the history to go back to.
-  // e.g. about::blank page, or chrome's new tab page.
-  virtual const GURL default_safe_page() const;
-
-  // Adds an UnsafeResource |resource| for |url| to unsafe_resources_,
-  // this should be called whenever a resource load is blocked due to a SB hit.
-  void AddUnsafeResource(GURL url,
-                         security_interstitials::UnsafeResource resource);
-
-  // Checks if an UnsafeResource |resource| exists for |url|, if so, it is
-  // removed from the vector, assigned to |resource| and the function returns
-  // true. Otherwise the function returns false and nothing gets assigned to
-  // |resource|.
-  bool PopUnsafeResourceForURL(
-      GURL url,
-      security_interstitials::UnsafeResource* resource);
-
- protected:
-  friend class ChromePasswordProtectionService;
-  virtual ~BaseUIManager();
-
-  // Removes |whitelist_url| from the whitelist for |web_contents|.
-  // Called on the UI thread.
-  void RemoveWhitelistUrlSet(const GURL& whitelist_url,
-                             content::WebContents* web_contents,
-                             bool from_pending_only);
-
-  // Ensures that |web_contents| has its whitelist set in its userdata
-  static void EnsureWhitelistCreated(content::WebContents* web_contents);
-
-  // Returns the URL that should be used in a WhitelistUrlSet for the given
-  // |resource|.
-  static GURL GetMainFrameWhitelistUrlForResource(
-      const security_interstitials::UnsafeResource& resource);
-
-  // BaseUIManager does not send SafeBrowsingHitReport. Subclasses should
-  // implement the reporting logic themselves if needed.
-  virtual void CreateAndSendHitReport(const UnsafeResource& resource);
-
-  // Calls BaseBlockingPage::ShowBlockingPage(). Override this if using a
-  // different blocking page.
-  virtual void ShowBlockingPageForResource(const UnsafeResource& resource);
-
- private:
-  // When true, we immediately cancel navigations that have been blocked by Safe
-  // Browsing, otherwise we call show on the interstitial.
-  bool SafeBrowsingInterstitialsAreCommittedNavigations();
-
-  friend class base::RefCountedThreadSafe<BaseUIManager>;
-
-  // Creates a blocking page, used for interstitials triggered by subresources.
-  // Should be overridden with a blocking page implementation.
-  virtual BaseBlockingPage* CreateBlockingPageForSubresource(
-      content::WebContents* contents,
-      const GURL& blocked_url,
-      const UnsafeResource& unsafe_resource);
-
-  // Stores unsafe resources so they can be fetched from a navigation throttle
-  // in the committed interstitials flow. Implemented as a pair vector since
-  // most of the time it will be empty or contain a single element.
-  std::vector<std::pair<GURL, security_interstitials::UnsafeResource>>
-      unsafe_resources_;
-
-  DISALLOW_COPY_AND_ASSIGN(BaseUIManager);
-};
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_BASE_UI_MANAGER_H_
diff --git a/components/safe_browsing/browser/BUILD.gn b/components/safe_browsing/browser/BUILD.gn
deleted file mode 100644
index 65a3904..0000000
--- a/components/safe_browsing/browser/BUILD.gn
+++ /dev/null
@@ -1,82 +0,0 @@
-# Copyright 2017 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//build/config/features.gni")
-import("//build/config/jumbo.gni")
-
-jumbo_source_set("browser") {
-  sources = [
-    "browser_url_loader_throttle.cc",
-    "browser_url_loader_throttle.h",
-    "mojo_safe_browsing_impl.cc",
-    "mojo_safe_browsing_impl.h",
-    "safe_browsing_url_checker_impl.cc",
-    "safe_browsing_url_checker_impl.h",
-    "threat_details.cc",
-    "threat_details.h",
-    "threat_details_cache.cc",
-    "threat_details_cache.h",
-    "threat_details_history.cc",
-    "threat_details_history.h",
-    "url_checker_delegate.h",
-  ]
-  deps = [
-    "//components/history/core/browser:browser",
-    "//components/safe_browsing:csd_proto",
-    "//components/safe_browsing:features",
-    "//components/safe_browsing:realtimeapi_proto",
-    "//components/safe_browsing:safe_browsing",
-    "//components/safe_browsing:verdict_cache_manager",
-    "//components/safe_browsing/browser:network_context",
-    "//components/safe_browsing/browser:referrer_chain_provider",
-    "//components/safe_browsing/common:common",
-    "//components/safe_browsing/db:database_manager",
-    "//components/safe_browsing/realtime:policy_engine",
-    "//components/safe_browsing/realtime:url_lookup_service",
-    "//components/safe_browsing/web_ui:constants",
-    "//components/safe_browsing/web_ui:web_ui",
-    "//components/security_interstitials/content:security_interstitial_page",
-    "//content/public/browser:browser",
-    "//net:extras",
-  ]
-}
-
-source_set("network_context") {
-  sources = [
-    "safe_browsing_network_context.cc",
-    "safe_browsing_network_context.h",
-  ]
-
-  deps = [
-    "//components/safe_browsing/common:common",
-    "//content/public/browser:browser",
-    "//net:extras",
-  ]
-}
-
-source_set("referrer_chain_provider") {
-  sources = [
-    "referrer_chain_provider.h",
-  ]
-  deps = [
-    "//components/safe_browsing:csd_proto",
-    "//components/sessions",
-  ]
-}
-
-source_set("unittests") {
-  testonly = true
-  sources = []
-
-  deps = [
-    ":browser",
-    "//base",
-    "//components/safe_browsing",
-    "//components/safe_browsing/db:test_database_manager",
-    "//content/public/browser",
-    "//content/test:test_support",
-    "//net:test_support",
-    "//testing/gtest",
-  ]
-}
diff --git a/components/safe_browsing/browser/DEPS b/components/safe_browsing/browser/DEPS
deleted file mode 100644
index 0bd0634..0000000
--- a/components/safe_browsing/browser/DEPS
+++ /dev/null
@@ -1,21 +0,0 @@
-include_rules = [
-  "+components/history/core/browser",
-  "+components/safe_browsing/proto/csd.pb.h",
-  "+components/sessions/core/session_id.h",
-  "+content/public/browser",
-  "+ipc/ipc_message.h",
-  "+net/cookies",
-  "+net/extras",
-  "+net/http",
-  "+net/ssl",
-  "+net/traffic_annotation",
-  "+services/network/network_context.h",
-  "+services/network/public",
-  "+services/service_manager/public/cpp",
-]
-
-specific_include_rules = {
-  ".*test\.cc": [
-    "+content/public/test/browser_task_environment.h",
-  ],
-}
diff --git a/components/safe_browsing/browser/browser_url_loader_throttle.cc b/components/safe_browsing/browser/browser_url_loader_throttle.cc
deleted file mode 100644
index f66686b6..0000000
--- a/components/safe_browsing/browser/browser_url_loader_throttle.cc
+++ /dev/null
@@ -1,324 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/browser/browser_url_loader_throttle.h"
-
-#include "base/bind.h"
-#include "base/logging.h"
-#include "base/trace_event/trace_event.h"
-#include "components/safe_browsing/browser/safe_browsing_url_checker_impl.h"
-#include "components/safe_browsing/browser/url_checker_delegate.h"
-#include "components/safe_browsing/common/safebrowsing_constants.h"
-#include "components/safe_browsing/common/utils.h"
-#include "components/safe_browsing/realtime/policy_engine.h"
-#include "components/safe_browsing/verdict_cache_manager.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/web_contents.h"
-#include "net/log/net_log_event_type.h"
-#include "net/url_request/redirect_info.h"
-#include "services/network/public/cpp/resource_request.h"
-#include "services/network/public/mojom/url_response_head.mojom.h"
-
-namespace safe_browsing {
-
-// TODO(http://crbug.com/824843): Remove this if safe browsing is moved to the
-// UI thread.
-class BrowserURLLoaderThrottle::CheckerOnIO
-    : public base::SupportsWeakPtr<BrowserURLLoaderThrottle::CheckerOnIO> {
- public:
-  CheckerOnIO(
-      GetDelegateCallback delegate_getter,
-      content::ResourceContext* resource_context,
-      int frame_tree_node_id,
-      base::RepeatingCallback<content::WebContents*()> web_contents_getter,
-      base::WeakPtr<BrowserURLLoaderThrottle> throttle,
-      bool real_time_lookup_enabled,
-      base::WeakPtr<VerdictCacheManager> cache_manager)
-      : delegate_getter_(std::move(delegate_getter)),
-        resource_context_(resource_context),
-        frame_tree_node_id_(frame_tree_node_id),
-        web_contents_getter_(web_contents_getter),
-        throttle_(std::move(throttle)),
-        real_time_lookup_enabled_(real_time_lookup_enabled),
-        cache_manager_(cache_manager) {}
-
-  // Starts the initial safe browsing check. This check and future checks may be
-  // skipped after checking with the UrlCheckerDelegate.
-  void Start(const net::HttpRequestHeaders& headers,
-             int load_flags,
-             content::ResourceType resource_type,
-             bool has_user_gesture,
-             bool originated_from_service_worker,
-             const GURL& url,
-             const std::string& method) {
-    DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-    scoped_refptr<UrlCheckerDelegate> url_checker_delegate =
-        std::move(delegate_getter_).Run(resource_context_);
-    skip_checks_ = !url_checker_delegate ||
-                   !url_checker_delegate->GetDatabaseManager()->IsSupported() ||
-                   url_checker_delegate->ShouldSkipRequestCheck(
-                       resource_context_, url, frame_tree_node_id_,
-                       -1 /* render_process_id */, -1 /* render_frame_id */,
-                       originated_from_service_worker);
-    if (skip_checks_) {
-      base::PostTask(
-          FROM_HERE, {content::BrowserThread::UI},
-          base::BindOnce(&BrowserURLLoaderThrottle::SkipChecks, throttle_));
-      return;
-    }
-
-    url_checker_ = std::make_unique<SafeBrowsingUrlCheckerImpl>(
-        headers, load_flags, resource_type, has_user_gesture,
-        url_checker_delegate, web_contents_getter_, real_time_lookup_enabled_,
-        cache_manager_);
-
-    CheckUrl(url, method);
-  }
-
-  // Checks the specified |url| using |url_checker_|.
-  void CheckUrl(const GURL& url, const std::string& method) {
-    DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-    if (skip_checks_) {
-      base::PostTask(
-          FROM_HERE, {content::BrowserThread::UI},
-          base::BindOnce(&BrowserURLLoaderThrottle::SkipChecks, throttle_));
-      return;
-    }
-
-    DCHECK(url_checker_);
-    url_checker_->CheckUrl(
-        url, method,
-        base::BindOnce(&BrowserURLLoaderThrottle::CheckerOnIO::OnCheckUrlResult,
-                       base::Unretained(this)));
-  }
-
- private:
-  // If |slow_check_notifier| is non-null, it indicates that a "slow check" is
-  // ongoing, i.e., the URL may be unsafe and a more time-consuming process is
-  // required to get the final result. In that case, the rest of the callback
-  // arguments should be ignored. This method sets the |slow_check_notifier|
-  // output parameter to a callback to receive the final result.
-  void OnCheckUrlResult(NativeUrlCheckNotifier* slow_check_notifier,
-                        bool proceed,
-                        bool showed_interstitial) {
-    if (!slow_check_notifier) {
-      OnCompleteCheck(false /* slow_check */, proceed, showed_interstitial);
-      return;
-    }
-
-    base::PostTask(
-        FROM_HERE, {content::BrowserThread::UI},
-        base::BindOnce(&BrowserURLLoaderThrottle::NotifySlowCheck, throttle_));
-
-    // In this case |proceed| and |showed_interstitial| should be ignored. The
-    // result will be returned by calling |*slow_check_notifier| callback.
-    *slow_check_notifier =
-        base::BindOnce(&BrowserURLLoaderThrottle::CheckerOnIO::OnCompleteCheck,
-                       base::Unretained(this), true /* slow_check */);
-  }
-
-  // |slow_check| indicates whether it reports the result of a slow check.
-  // (Please see comments of OnCheckUrlResult() for what slow check means).
-  void OnCompleteCheck(bool slow_check,
-                       bool proceed,
-                       bool showed_interstitial) {
-    base::PostTask(
-        FROM_HERE, {content::BrowserThread::UI},
-        base::BindOnce(&BrowserURLLoaderThrottle::OnCompleteCheck, throttle_,
-                       slow_check, proceed, showed_interstitial));
-  }
-
-  // The following member stays valid until |url_checker_| is created.
-  GetDelegateCallback delegate_getter_;
-
-  content::ResourceContext* resource_context_;
-  std::unique_ptr<SafeBrowsingUrlCheckerImpl> url_checker_;
-  int frame_tree_node_id_;
-  base::RepeatingCallback<content::WebContents*()> web_contents_getter_;
-  bool skip_checks_ = false;
-  base::WeakPtr<BrowserURLLoaderThrottle> throttle_;
-  bool real_time_lookup_enabled_ = false;
-  base::WeakPtr<VerdictCacheManager> cache_manager_;
-};
-
-// static
-std::unique_ptr<BrowserURLLoaderThrottle> BrowserURLLoaderThrottle::Create(
-    GetDelegateCallback delegate_getter,
-    const base::RepeatingCallback<content::WebContents*()>& web_contents_getter,
-    int frame_tree_node_id,
-    content::ResourceContext* resource_context,
-    base::WeakPtr<VerdictCacheManager> cache_manager) {
-  return base::WrapUnique<BrowserURLLoaderThrottle>(
-      new BrowserURLLoaderThrottle(std::move(delegate_getter),
-                                   web_contents_getter, frame_tree_node_id,
-                                   resource_context, cache_manager));
-}
-
-BrowserURLLoaderThrottle::BrowserURLLoaderThrottle(
-    GetDelegateCallback delegate_getter,
-    const base::RepeatingCallback<content::WebContents*()>& web_contents_getter,
-    int frame_tree_node_id,
-    content::ResourceContext* resource_context,
-    base::WeakPtr<VerdictCacheManager> cache_manager) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-
-  // Decide whether to do real time URL lookups or not.
-  content::WebContents* web_contents = web_contents_getter.Run();
-  bool real_time_lookup_enabled =
-      web_contents ? RealTimePolicyEngine::CanPerformFullURLLookup(
-                         web_contents->GetBrowserContext())
-                   : false;
-
-  io_checker_ = std::make_unique<CheckerOnIO>(
-      std::move(delegate_getter), resource_context, frame_tree_node_id,
-      web_contents_getter, weak_factory_.GetWeakPtr(), real_time_lookup_enabled,
-      cache_manager);
-}
-
-BrowserURLLoaderThrottle::~BrowserURLLoaderThrottle() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  if (deferred_)
-    TRACE_EVENT_ASYNC_END0("safe_browsing", "Deferred", this);
-
-  DeleteCheckerOnIO();
-}
-
-void BrowserURLLoaderThrottle::WillStartRequest(
-    network::ResourceRequest* request,
-    bool* defer) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  DCHECK_EQ(0u, pending_checks_);
-  DCHECK(!blocked_);
-
-  original_url_ = request->url;
-  pending_checks_++;
-  base::PostTask(
-      FROM_HERE, {content::BrowserThread::IO},
-      base::BindOnce(
-          &BrowserURLLoaderThrottle::CheckerOnIO::Start,
-          io_checker_->AsWeakPtr(), request->headers, request->load_flags,
-          static_cast<content::ResourceType>(request->resource_type),
-          request->has_user_gesture, request->originated_from_service_worker,
-          request->url, request->method));
-}
-
-void BrowserURLLoaderThrottle::WillRedirectRequest(
-    net::RedirectInfo* redirect_info,
-    const network::mojom::URLResponseHead& /* response_head */,
-    bool* defer,
-    std::vector<std::string>* /* to_be_removed_headers */,
-    net::HttpRequestHeaders* /* modified_headers */) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  if (blocked_) {
-    // OnCheckUrlResult() has set |blocked_| to true and called
-    // |delegate_->CancelWithError|, but this method is called before the
-    // request is actually cancelled. In that case, simply defer the request.
-    *defer = true;
-    return;
-  }
-
-  if (skip_checks_)
-    return;
-
-  pending_checks_++;
-  base::PostTask(
-      FROM_HERE, {content::BrowserThread::IO},
-      base::BindOnce(&BrowserURLLoaderThrottle::CheckerOnIO::CheckUrl,
-                     io_checker_->AsWeakPtr(), redirect_info->new_url,
-                     redirect_info->new_method));
-}
-
-void BrowserURLLoaderThrottle::WillProcessResponse(
-    const GURL& response_url,
-    network::mojom::URLResponseHead* response_head,
-    bool* defer) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  if (blocked_) {
-    // OnCheckUrlResult() has set |blocked_| to true and called
-    // |delegate_->CancelWithError|, but this method is called before the
-    // request is actually cancelled. In that case, simply defer the request.
-    *defer = true;
-    return;
-  }
-
-  if (pending_checks_ == 0)
-    return;
-
-  DCHECK(!deferred_);
-  deferred_ = true;
-  defer_start_time_ = base::TimeTicks::Now();
-  *defer = true;
-  TRACE_EVENT_ASYNC_BEGIN1("safe_browsing", "Deferred", this, "original_url",
-                           original_url_.spec());
-}
-
-void BrowserURLLoaderThrottle::OnCompleteCheck(bool slow_check,
-                                               bool proceed,
-                                               bool showed_interstitial) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  DCHECK(!blocked_);
-
-  DCHECK_LT(0u, pending_checks_);
-  pending_checks_--;
-
-  if (slow_check) {
-    DCHECK_LT(0u, pending_slow_checks_);
-    pending_slow_checks_--;
-  }
-
-  user_action_involved_ = user_action_involved_ || showed_interstitial;
-  // If the resource load is currently deferred and is going to exit that state
-  // (either being cancelled or resumed), record the total delay.
-  if (deferred_ && (!proceed || pending_checks_ == 0))
-    total_delay_ = base::TimeTicks::Now() - defer_start_time_;
-
-  if (proceed) {
-    if (pending_slow_checks_ == 0 && slow_check)
-      delegate_->ResumeReadingBodyFromNet();
-
-    if (pending_checks_ == 0 && deferred_) {
-      deferred_ = false;
-      TRACE_EVENT_ASYNC_END0("safe_browsing", "Deferred", this);
-      delegate_->Resume();
-    }
-  } else {
-    blocked_ = true;
-
-    DeleteCheckerOnIO();
-    pending_checks_ = 0;
-    pending_slow_checks_ = 0;
-    delegate_->CancelWithError(GetNetErrorCodeForSafeBrowsing(),
-                               kCustomCancelReasonForURLLoader);
-  }
-}
-
-void BrowserURLLoaderThrottle::SkipChecks() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  // Future checks for redirects will be skipped.
-  skip_checks_ = true;
-
-  pending_checks_--;
-  if (pending_checks_ == 0 && deferred_)
-    delegate_->Resume();
-}
-
-void BrowserURLLoaderThrottle::NotifySlowCheck() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  pending_slow_checks_++;
-
-  // Pending slow checks indicate that the resource may be unsafe. In that case,
-  // pause reading response body from network to minimize the chance of
-  // processing unsafe contents (e.g., writing unsafe contents into cache),
-  // until we get the results. According to the results, we may resume reading
-  // or cancel the resource load.
-  if (pending_slow_checks_ == 1)
-    delegate_->PauseReadingBodyFromNet();
-}
-
-void BrowserURLLoaderThrottle::DeleteCheckerOnIO() {
-  base::DeleteSoon(FROM_HERE, {content::BrowserThread::IO},
-                   std::move(io_checker_));
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/browser/browser_url_loader_throttle.h b/components/safe_browsing/browser/browser_url_loader_throttle.h
deleted file mode 100644
index b40f235..0000000
--- a/components/safe_browsing/browser/browser_url_loader_throttle.h
+++ /dev/null
@@ -1,137 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_BROWSER_BROWSER_URL_LOADER_THROTTLE_H_
-#define COMPONENTS_SAFE_BROWSING_BROWSER_BROWSER_URL_LOADER_THROTTLE_H_
-
-#include <memory>
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/task/post_task.h"
-#include "base/time/time.h"
-#include "content/public/browser/browser_thread.h"
-#include "third_party/blink/public/common/loader/url_loader_throttle.h"
-#include "url/gurl.h"
-
-namespace content {
-class ResourceContext;
-class WebContents;
-}
-
-namespace net {
-class HttpRequestHeaders;
-}
-
-namespace safe_browsing {
-
-class UrlCheckerDelegate;
-
-class VerdictCacheManager;
-
-// BrowserURLLoaderThrottle is used in the browser process to query
-// SafeBrowsing to determine whether a URL and also its redirect URLs are safe
-// to load.
-//
-// This throttle never defers starting the URL request or following redirects,
-// no matter on mobile or desktop. If any of the checks for the original URL
-// and redirect chain are not complete by the time the response headers are
-// available, the request is deferred until all the checks are done. It cancels
-// the load if any URLs turn out to be bad.
-class BrowserURLLoaderThrottle : public blink::URLLoaderThrottle {
- public:
-  using GetDelegateCallback =
-      base::OnceCallback<scoped_refptr<UrlCheckerDelegate>(
-          content::ResourceContext*)>;
-
-  static std::unique_ptr<BrowserURLLoaderThrottle> Create(
-      GetDelegateCallback delegate_getter,
-      const base::RepeatingCallback<content::WebContents*()>&
-          web_contents_getter,
-      int frame_tree_node_id,
-      content::ResourceContext* resource_context,
-      base::WeakPtr<VerdictCacheManager> cache_manager);
-
-  ~BrowserURLLoaderThrottle() override;
-
-  // blink::URLLoaderThrottle implementation.
-  void WillStartRequest(network::ResourceRequest* request,
-                        bool* defer) override;
-  void WillRedirectRequest(net::RedirectInfo* redirect_info,
-                           const network::mojom::URLResponseHead& response_head,
-                           bool* defer,
-                           std::vector<std::string>* to_be_removed_headers,
-                           net::HttpRequestHeaders* modified_headers) override;
-  void WillProcessResponse(const GURL& response_url,
-                           network::mojom::URLResponseHead* response_head,
-                           bool* defer) override;
-
- private:
-  // CheckerOnIO handles calling methods on SafeBrowsingUrlCheckerImpl, which
-  // must be called on the IO thread. The results are synced back to the
-  // throttle.
-  // TODO(http://crbug.com/824843): Remove this if safe browsing is moved to the
-  // UI thread.
-  class CheckerOnIO;
-
-  using NativeUrlCheckNotifier =
-      base::OnceCallback<void(bool /* proceed */,
-                              bool /* showed_interstitial */)>;
-
-  // |web_contents_getter| is used for displaying SafeBrowsing UI when
-  // necessary.
-  BrowserURLLoaderThrottle(
-      GetDelegateCallback delegate_getter,
-      const base::RepeatingCallback<content::WebContents*()>&
-          web_contents_getter,
-      int frame_tree_node_id,
-      content::ResourceContext* resource_context,
-      base::WeakPtr<VerdictCacheManager> cache_manager);
-
-  // |slow_check| indicates whether it reports the result of a slow check.
-  // (Please see comments of CheckerOnIO::OnCheckUrlResult() for what slow check
-  // means).
-  void OnCompleteCheck(bool slow_check, bool proceed, bool showed_interstitial);
-
-  // Called to skip future safe browsing checks and resume the request if
-  // necessary.
-  void SkipChecks();
-
-  // Called when a slow safe browsing check is ongoing.
-  void NotifySlowCheck();
-
-  // Destroys |io_checker_| on the IO thread.
-  void DeleteCheckerOnIO();
-
-  size_t pending_checks_ = 0;
-  // How many slow checks that haven't received results.
-  size_t pending_slow_checks_ = 0;
-  bool blocked_ = false;
-
-  // The time when we started deferring the request.
-  base::TimeTicks defer_start_time_;
-  bool deferred_ = false;
-
-  // The total delay caused by SafeBrowsing deferring the resource load.
-  base::TimeDelta total_delay_;
-  // Whether the interstitial page has been shown and therefore user action has
-  // been involved.
-  bool user_action_involved_ = false;
-
-  GURL original_url_;
-
-  // Whether future safe browsing checks should be skipped.
-  bool skip_checks_ = false;
-
-  std::unique_ptr<CheckerOnIO> io_checker_;
-
-  base::WeakPtrFactory<BrowserURLLoaderThrottle> weak_factory_{this};
-
-  DISALLOW_COPY_AND_ASSIGN(BrowserURLLoaderThrottle);
-};
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_BROWSER_BROWSER_URL_LOADER_THROTTLE_H_
diff --git a/components/safe_browsing/browser/mojo_safe_browsing_impl.cc b/components/safe_browsing/browser/mojo_safe_browsing_impl.cc
deleted file mode 100644
index b7b4a1a..0000000
--- a/components/safe_browsing/browser/mojo_safe_browsing_impl.cc
+++ /dev/null
@@ -1,182 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/browser/mojo_safe_browsing_impl.h"
-
-#include <memory>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/supports_user_data.h"
-#include "components/safe_browsing/browser/safe_browsing_url_checker_impl.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/resource_context.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/common/resource_type.h"
-#include "mojo/public/cpp/bindings/self_owned_receiver.h"
-#include "net/base/load_flags.h"
-
-namespace safe_browsing {
-namespace {
-
-content::WebContents* GetWebContentsFromID(int render_process_id,
-                                           int render_frame_id) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  content::RenderFrameHost* render_frame_host =
-      content::RenderFrameHost::FromID(render_process_id, render_frame_id);
-  if (!render_frame_host)
-    return nullptr;
-
-  return content::WebContents::FromRenderFrameHost(render_frame_host);
-}
-
-// This class wraps a callback for checking URL, and runs it on destruction,
-// if it hasn't been run yet.
-class CheckUrlCallbackWrapper {
- public:
-  using Callback = base::OnceCallback<
-      void(mojo::PendingReceiver<mojom::UrlCheckNotifier>, bool, bool)>;
-
-  explicit CheckUrlCallbackWrapper(Callback callback)
-      : callback_(std::move(callback)) {}
-  ~CheckUrlCallbackWrapper() {
-    if (callback_)
-      Run(mojo::NullReceiver(), true, false);
-  }
-
-  void Run(mojo::PendingReceiver<mojom::UrlCheckNotifier> slow_check_notifier,
-           bool proceed,
-           bool showed_interstitial) {
-    std::move(callback_).Run(std::move(slow_check_notifier), proceed,
-                             showed_interstitial);
-  }
-
- private:
-  Callback callback_;
-};
-
-// UserData object that owns MojoSafeBrowsingImpl. This is used rather than
-// having MojoSafeBrowsingImpl directly extend base::SupportsUserData::Data to
-// avoid naming conflicts between Data::Clone() and
-// mojom::SafeBrowsing::Clone().
-class SafeBrowserUserData : public base::SupportsUserData::Data {
- public:
-  explicit SafeBrowserUserData(std::unique_ptr<MojoSafeBrowsingImpl> impl)
-      : impl_(std::move(impl)) {}
-  ~SafeBrowserUserData() override = default;
-
- private:
-  std::unique_ptr<MojoSafeBrowsingImpl> impl_;
-
-  DISALLOW_COPY_AND_ASSIGN(SafeBrowserUserData);
-};
-
-}  // namespace
-
-MojoSafeBrowsingImpl::MojoSafeBrowsingImpl(
-    scoped_refptr<UrlCheckerDelegate> delegate,
-    int render_process_id,
-    content::ResourceContext* resource_context)
-    : delegate_(std::move(delegate)),
-      render_process_id_(render_process_id),
-      resource_context_(resource_context) {
-  DCHECK(resource_context_);
-
-  // It is safe to bind |this| as Unretained because |receivers_| is owned by
-  // |this| and will not call this callback after it is destroyed.
-  receivers_.set_disconnect_handler(base::BindRepeating(
-      &MojoSafeBrowsingImpl::OnMojoDisconnect, base::Unretained(this)));
-}
-
-MojoSafeBrowsingImpl::~MojoSafeBrowsingImpl() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-}
-
-// static
-void MojoSafeBrowsingImpl::MaybeCreate(
-    int render_process_id,
-    content::ResourceContext* resource_context,
-    const base::RepeatingCallback<scoped_refptr<UrlCheckerDelegate>()>&
-        delegate_getter,
-    mojo::PendingReceiver<mojom::SafeBrowsing> receiver) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-
-  scoped_refptr<UrlCheckerDelegate> delegate = delegate_getter.Run();
-
-  if (!resource_context || !delegate ||
-      !delegate->GetDatabaseManager()->IsSupported())
-    return;
-
-  std::unique_ptr<MojoSafeBrowsingImpl> impl(new MojoSafeBrowsingImpl(
-      std::move(delegate), render_process_id, resource_context));
-  impl->Clone(std::move(receiver));
-
-  MojoSafeBrowsingImpl* raw_impl = impl.get();
-  std::unique_ptr<SafeBrowserUserData> user_data =
-      std::make_unique<SafeBrowserUserData>(std::move(impl));
-  raw_impl->user_data_key_ = user_data.get();
-  resource_context->SetUserData(raw_impl->user_data_key_, std::move(user_data));
-}
-
-void MojoSafeBrowsingImpl::CreateCheckerAndCheck(
-    int32_t render_frame_id,
-    mojo::PendingReceiver<mojom::SafeBrowsingUrlChecker> receiver,
-    const GURL& url,
-    const std::string& method,
-    const net::HttpRequestHeaders& headers,
-    int32_t load_flags,
-    content::ResourceType resource_type,
-    bool has_user_gesture,
-    bool originated_from_service_worker,
-    CreateCheckerAndCheckCallback callback) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-
-  if (delegate_->ShouldSkipRequestCheck(resource_context_, url,
-                                        -1 /* frame_tree_node_id */,
-                                        render_process_id_, render_frame_id,
-                                        originated_from_service_worker)) {
-    // Ensure that we don't destroy an uncalled CreateCheckerAndCheckCallback
-    if (callback) {
-      std::move(callback).Run(mojo::NullReceiver(), true /* proceed */,
-                              false /* showed_interstitial */);
-    }
-
-    // This will drop |receiver|. The result is that the renderer side will
-    // consider all URLs in the redirect chain of this receiver as safe.
-    return;
-  }
-
-  // This is not called for frame resources, and real time URL checks currently
-  // only support frame resources. If we extend real time URL checks to support
-  // non-main frames, we will need to provide the user preferences and cache
-  // manager regarding real time lookup here.
-  auto checker_impl = std::make_unique<SafeBrowsingUrlCheckerImpl>(
-      headers, static_cast<int>(load_flags), resource_type, has_user_gesture,
-      delegate_,
-      base::BindRepeating(&GetWebContentsFromID, render_process_id_,
-                          static_cast<int>(render_frame_id)),
-      /*real_time_lookup_enabled=*/false, /*cache_manager=*/nullptr);
-
-  checker_impl->CheckUrl(
-      url, method,
-      base::BindOnce(
-          &CheckUrlCallbackWrapper::Run,
-          base::Owned(new CheckUrlCallbackWrapper(std::move(callback)))));
-  mojo::MakeSelfOwnedReceiver(std::move(checker_impl), std::move(receiver));
-}
-
-void MojoSafeBrowsingImpl::Clone(
-    mojo::PendingReceiver<mojom::SafeBrowsing> receiver) {
-  receivers_.Add(this, std::move(receiver));
-}
-
-void MojoSafeBrowsingImpl::OnMojoDisconnect() {
-  if (receivers_.empty()) {
-    resource_context_->RemoveUserData(user_data_key_);
-    // This object is destroyed.
-  }
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/browser/mojo_safe_browsing_impl.h b/components/safe_browsing/browser/mojo_safe_browsing_impl.h
deleted file mode 100644
index c483412f..0000000
--- a/components/safe_browsing/browser/mojo_safe_browsing_impl.h
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_BROWSER_MOJO_SAFE_BROWSING_IMPL_H_
-#define COMPONENTS_SAFE_BROWSING_BROWSER_MOJO_SAFE_BROWSING_IMPL_H_
-
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "components/safe_browsing/browser/url_checker_delegate.h"
-#include "components/safe_browsing/common/safe_browsing.mojom.h"
-#include "ipc/ipc_message.h"
-#include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "mojo/public/cpp/bindings/receiver_set.h"
-
-namespace content {
-class ResourceContext;
-}
-
-namespace safe_browsing {
-
-// This class implements the Mojo interface for renderers to perform
-// SafeBrowsing URL checks.
-// A MojoSafeBrowsingImpl instance is destructed when the Mojo message pipe is
-// disconnected or |resource_context_| is destructed.
-class MojoSafeBrowsingImpl : public mojom::SafeBrowsing {
- public:
-  ~MojoSafeBrowsingImpl() override;
-
-  static void MaybeCreate(
-      int render_process_id,
-      content::ResourceContext* resource_context,
-      const base::RepeatingCallback<scoped_refptr<UrlCheckerDelegate>()>&
-          delegate_getter,
-      mojo::PendingReceiver<mojom::SafeBrowsing> receiver);
-
- private:
-  MojoSafeBrowsingImpl(scoped_refptr<UrlCheckerDelegate> delegate,
-                       int render_process_id,
-                       content::ResourceContext* resource_context);
-
-  // mojom::SafeBrowsing implementation.
-  void CreateCheckerAndCheck(
-      int32_t render_frame_id,
-      mojo::PendingReceiver<mojom::SafeBrowsingUrlChecker> receiver,
-      const GURL& url,
-      const std::string& method,
-      const net::HttpRequestHeaders& headers,
-      int32_t load_flags,
-      content::ResourceType resource_type,
-      bool has_user_gesture,
-      bool originated_from_service_worker,
-      CreateCheckerAndCheckCallback callback) override;
-  void Clone(mojo::PendingReceiver<mojom::SafeBrowsing> receiver) override;
-
-  void OnMojoDisconnect();
-
-  // This is an instance of SafeBrowserUserData that is set as user-data on
-  // |resource_context_|. SafeBrowserUserData owns |this|.
-  const void* user_data_key_ = nullptr;
-
-  mojo::ReceiverSet<mojom::SafeBrowsing> receivers_;
-  scoped_refptr<UrlCheckerDelegate> delegate_;
-  int render_process_id_ = MSG_ROUTING_NONE;
-
-  // Not owned by this object. It is always valid during the lifetime of this
-  // object.
-  content::ResourceContext* resource_context_;
-
-  DISALLOW_COPY_AND_ASSIGN(MojoSafeBrowsingImpl);
-};
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_BROWSER_MOJO_SAFE_BROWSING_IMPL_H_
diff --git a/components/safe_browsing/browser/referrer_chain_provider.h b/components/safe_browsing/browser/referrer_chain_provider.h
deleted file mode 100644
index dd193d7..0000000
--- a/components/safe_browsing/browser/referrer_chain_provider.h
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_BROWSER_REFERRER_CHAIN_PROVIDER_H_
-#define COMPONENTS_SAFE_BROWSING_BROWSER_REFERRER_CHAIN_PROVIDER_H_
-
-#include "components/safe_browsing/proto/csd.pb.h"
-#include "components/sessions/core/session_id.h"
-#include "url/gurl.h"
-
-namespace content {
-class WebContents;
-}
-
-namespace safe_browsing {
-using ReferrerChain =
-    google::protobuf::RepeatedPtrField<safe_browsing::ReferrerChainEntry>;
-
-class ReferrerChainProvider {
- 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,
-    SUCCESS_REFERRER = 6,  // Successfully identified extra referrers beyond the
-                           // landing referrer.
-
-    // Always at the end.
-    ATTRIBUTION_FAILURE_TYPE_MAX
-  };
-
-  virtual AttributionResult IdentifyReferrerChainByWebContents(
-      content::WebContents* web_contents,
-      int user_gesture_count_limit,
-      ReferrerChain* out_referrer_chain) = 0;
-
-  virtual AttributionResult IdentifyReferrerChainByEventURL(
-      const GURL& event_url,
-      SessionID event_tab_id,
-      int user_gesture_count_limit,
-      ReferrerChain* out_referrer_chain) = 0;
-};
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_BROWSER_REFERRER_CHAIN_PROVIDER_H_
diff --git a/components/safe_browsing/browser/safe_browsing_network_context.cc b/components/safe_browsing/browser/safe_browsing_network_context.cc
deleted file mode 100644
index 20d5b04c..0000000
--- a/components/safe_browsing/browser/safe_browsing_network_context.cc
+++ /dev/null
@@ -1,177 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/browser/safe_browsing_network_context.h"
-
-#include <memory>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/files/file_util.h"
-#include "base/task/post_task.h"
-#include "components/safe_browsing/common/safebrowsing_constants.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/network_context_client_base.h"
-#include "content/public/browser/network_service_instance.h"
-#include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "mojo/public/cpp/bindings/pending_remote.h"
-#include "mojo/public/cpp/bindings/remote.h"
-#include "mojo/public/cpp/bindings/self_owned_receiver.h"
-#include "net/net_buildflags.h"
-#include "services/network/network_context.h"
-#include "services/network/public/mojom/url_loader_factory.mojom.h"
-
-namespace safe_browsing {
-
-class SafeBrowsingNetworkContext::SharedURLLoaderFactory
-    : public network::SharedURLLoaderFactory {
- public:
-  SharedURLLoaderFactory(
-      const base::FilePath& user_data_dir,
-      NetworkContextParamsFactory network_context_params_factory)
-      : user_data_dir_(user_data_dir),
-        network_context_params_factory_(
-            std::move(network_context_params_factory)) {}
-
-  void Reset() {
-    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-    url_loader_factory_.reset();
-    network_context_.reset();
-  }
-
-  network::mojom::NetworkContext* GetNetworkContext() {
-    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-    if (!network_context_ || !network_context_.is_connected()) {
-      network_context_.reset();
-      content::GetNetworkService()->CreateNetworkContext(
-          network_context_.BindNewPipeAndPassReceiver(),
-          CreateNetworkContextParams());
-
-      mojo::PendingRemote<network::mojom::NetworkContextClient> client_remote;
-      mojo::MakeSelfOwnedReceiver(
-          std::make_unique<content::NetworkContextClientBase>(),
-          client_remote.InitWithNewPipeAndPassReceiver());
-      network_context_->SetClient(std::move(client_remote));
-    }
-    return network_context_.get();
-  }
-
-  void FlushForTesting() {
-    if (network_context_)
-      network_context_.FlushForTesting();
-    if (url_loader_factory_)
-      url_loader_factory_.FlushForTesting();
-  }
-
- protected:
-  // network::URLLoaderFactory implementation:
-  void CreateLoaderAndStart(
-      mojo::PendingReceiver<network::mojom::URLLoader> loader,
-      int32_t routing_id,
-      int32_t request_id,
-      uint32_t options,
-      const network::ResourceRequest& request,
-      mojo::PendingRemote<network::mojom::URLLoaderClient> client,
-      const net::MutableNetworkTrafficAnnotationTag& traffic_annotation)
-      override {
-    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-    GetURLLoaderFactory()->CreateLoaderAndStart(
-        std::move(loader), routing_id, request_id, options, request,
-        std::move(client), traffic_annotation);
-  }
-
-  void Clone(mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver)
-      override {
-    GetURLLoaderFactory()->Clone(std::move(receiver));
-  }
-
-  // network::SharedURLLoaderFactory implementation:
-  std::unique_ptr<network::PendingSharedURLLoaderFactory> Clone() override {
-    NOTREACHED();
-    return nullptr;
-  }
-
-  network::mojom::URLLoaderFactory* GetURLLoaderFactory() {
-    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-    if (!url_loader_factory_ || !url_loader_factory_.is_connected()) {
-      network::mojom::URLLoaderFactoryParamsPtr params =
-          network::mojom::URLLoaderFactoryParams::New();
-      params->process_id = network::mojom::kBrowserProcessId;
-      params->is_corb_enabled = false;
-      params->is_trusted = true;
-      GetNetworkContext()->CreateURLLoaderFactory(
-          url_loader_factory_.BindNewPipeAndPassReceiver(), std::move(params));
-    }
-    return url_loader_factory_.get();
-  }
-
- private:
-  friend class base::RefCounted<SharedURLLoaderFactory>;
-  ~SharedURLLoaderFactory() override = default;
-
-  network::mojom::NetworkContextParamsPtr CreateNetworkContextParams() {
-    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-    network::mojom::NetworkContextParamsPtr network_context_params =
-        network_context_params_factory_.Run();
-
-    network_context_params->context_name = std::string("safe_browsing");
-
-    network_context_params->http_cache_enabled = false;
-
-    // These are needed for PAC scripts that use FTP URLs.
-#if !BUILDFLAG(DISABLE_FTP_SUPPORT)
-    network_context_params->enable_ftp_url_support = true;
-#endif  // !BUILDFLAG(DISABLE_FTP_SUPPORT)
-
-    base::FilePath cookie_path = user_data_dir_.Append(
-        base::FilePath::StringType(kSafeBrowsingBaseFilename) + kCookiesFile);
-    network_context_params->cookie_path = cookie_path;
-    network_context_params->enable_encrypted_cookies = false;
-
-    return network_context_params;
-  }
-
-  base::FilePath user_data_dir_;
-  NetworkContextParamsFactory network_context_params_factory_;
-  mojo::Remote<network::mojom::NetworkContext> network_context_;
-  mojo::Remote<network::mojom::URLLoaderFactory> url_loader_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(SharedURLLoaderFactory);
-};
-
-SafeBrowsingNetworkContext::SafeBrowsingNetworkContext(
-    const base::FilePath& user_data_dir,
-    NetworkContextParamsFactory network_context_params_factory) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-  url_loader_factory_ = base::MakeRefCounted<SharedURLLoaderFactory>(
-      user_data_dir, std::move(network_context_params_factory));
-}
-
-SafeBrowsingNetworkContext::~SafeBrowsingNetworkContext() {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-}
-
-scoped_refptr<network::SharedURLLoaderFactory>
-SafeBrowsingNetworkContext::GetURLLoaderFactory() {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-  return url_loader_factory_;
-}
-
-network::mojom::NetworkContext*
-SafeBrowsingNetworkContext::GetNetworkContext() {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-  return url_loader_factory_->GetNetworkContext();
-}
-
-void SafeBrowsingNetworkContext::FlushForTesting() {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-  url_loader_factory_->FlushForTesting();
-}
-
-void SafeBrowsingNetworkContext::ServiceShuttingDown() {
-  url_loader_factory_->Reset();
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/browser/safe_browsing_network_context.h b/components/safe_browsing/browser/safe_browsing_network_context.h
deleted file mode 100644
index 5e57a5e..0000000
--- a/components/safe_browsing/browser/safe_browsing_network_context.h
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_BROWSER_SAFE_BROWSING_NETWORK_CONTEXT_H_
-#define COMPONENTS_SAFE_BROWSING_BROWSER_SAFE_BROWSING_NETWORK_CONTEXT_H_
-
-#include "base/callback.h"
-#include "base/files/file_path.h"
-#include "base/memory/ref_counted.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "services/network/public/mojom/network_service.mojom.h"
-
-namespace network {
-namespace mojom {
-class NetworkContext;
-}
-}  // namespace network
-
-namespace safe_browsing {
-
-// This class owns the NetworkContext that is used for requests by Safe
-// Browsing.
-// All methods are called on the UI thread.
-class SafeBrowsingNetworkContext {
- public:
-  // |user_data_dir| and |network_context_params_factory| are used
-  // to construct a URLRequestContext through the network service.
-  using NetworkContextParamsFactory =
-      base::RepeatingCallback<network::mojom::NetworkContextParamsPtr()>;
-  SafeBrowsingNetworkContext(
-      const base::FilePath& user_data_dir,
-      NetworkContextParamsFactory network_context_params_factory);
-  ~SafeBrowsingNetworkContext();
-
-  // Returns a SharedURLLoaderFactory.
-  scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory();
-
-  // Returns a NetworkContext.
-  network::mojom::NetworkContext* GetNetworkContext();
-
-  // Flushes NetworkContext and URLLoaderFactory pipes.
-  void FlushForTesting();
-
-  // Called at shutdown to ensure that the URLLoaderFactory is cleaned up.
-  void ServiceShuttingDown();
-
- private:
-  class SharedURLLoaderFactory;
-
-  scoped_refptr<SharedURLLoaderFactory> url_loader_factory_;
-};
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_BROWSER_SAFE_BROWSING_NETWORK_CONTEXT_H_
diff --git a/components/safe_browsing/browser/safe_browsing_url_checker_impl.cc b/components/safe_browsing/browser/safe_browsing_url_checker_impl.cc
deleted file mode 100644
index 56356811..0000000
--- a/components/safe_browsing/browser/safe_browsing_url_checker_impl.cc
+++ /dev/null
@@ -1,577 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/browser/safe_browsing_url_checker_impl.h"
-
-#include "base/bind.h"
-#include "base/metrics/histogram_functions.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/metrics/histogram_macros_local.h"
-#include "base/task/post_task.h"
-#include "base/trace_event/trace_event.h"
-#include "components/safe_browsing/browser/url_checker_delegate.h"
-#include "components/safe_browsing/realtime/policy_engine.h"
-#include "components/safe_browsing/realtime/url_lookup_service.h"
-#include "components/safe_browsing/verdict_cache_manager.h"
-#include "components/safe_browsing/web_ui/constants.h"
-#include "components/safe_browsing/web_ui/safe_browsing_ui.h"
-#include "components/security_interstitials/content/unsafe_resource.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/web_contents.h"
-#include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "net/base/load_flags.h"
-#include "net/http/http_request_headers.h"
-#include "net/log/net_log_event_type.h"
-
-namespace safe_browsing {
-namespace {
-
-// Maximum time in milliseconds to wait for the SafeBrowsing service reputation
-// check. After this amount of time the outstanding check will be aborted, and
-// the resource will be treated as if it were safe.
-const int kCheckUrlTimeoutMs = 5000;
-
-void RecordCheckUrlTimeout(bool timed_out) {
-  UMA_HISTOGRAM_BOOLEAN("SafeBrowsing.CheckUrl.Timeout", timed_out);
-}
-
-}  // namespace
-
-SafeBrowsingUrlCheckerImpl::Notifier::Notifier(CheckUrlCallback callback)
-    : callback_(std::move(callback)) {}
-
-SafeBrowsingUrlCheckerImpl::Notifier::Notifier(
-    NativeCheckUrlCallback native_callback)
-    : native_callback_(std::move(native_callback)) {}
-
-SafeBrowsingUrlCheckerImpl::Notifier::~Notifier() = default;
-
-SafeBrowsingUrlCheckerImpl::Notifier::Notifier(Notifier&& other) = default;
-
-SafeBrowsingUrlCheckerImpl::Notifier& SafeBrowsingUrlCheckerImpl::Notifier::
-operator=(Notifier&& other) = default;
-
-void SafeBrowsingUrlCheckerImpl::Notifier::OnStartSlowCheck() {
-  if (callback_) {
-    std::move(callback_).Run(slow_check_notifier_.BindNewPipeAndPassReceiver(),
-                             false, false);
-    return;
-  }
-
-  DCHECK(native_callback_);
-  std::move(native_callback_).Run(&native_slow_check_notifier_, false, false);
-}
-
-void SafeBrowsingUrlCheckerImpl::Notifier::OnCompleteCheck(
-    bool proceed,
-    bool showed_interstitial) {
-  if (callback_) {
-    std::move(callback_).Run(mojo::NullReceiver(), proceed,
-                             showed_interstitial);
-    return;
-  }
-
-  if (native_callback_) {
-    std::move(native_callback_).Run(nullptr, proceed, showed_interstitial);
-    return;
-  }
-
-  if (slow_check_notifier_) {
-    slow_check_notifier_->OnCompleteCheck(proceed, showed_interstitial);
-    slow_check_notifier_.reset();
-    return;
-  }
-
-  std::move(native_slow_check_notifier_).Run(proceed, showed_interstitial);
-}
-
-SafeBrowsingUrlCheckerImpl::UrlInfo::UrlInfo(const GURL& in_url,
-                                             const std::string& in_method,
-                                             Notifier in_notifier)
-    : url(in_url), method(in_method), notifier(std::move(in_notifier)) {}
-
-SafeBrowsingUrlCheckerImpl::UrlInfo::UrlInfo(UrlInfo&& other) = default;
-
-SafeBrowsingUrlCheckerImpl::UrlInfo::~UrlInfo() = default;
-
-SafeBrowsingUrlCheckerImpl::SafeBrowsingUrlCheckerImpl(
-    const net::HttpRequestHeaders& headers,
-    int load_flags,
-    content::ResourceType resource_type,
-    bool has_user_gesture,
-    scoped_refptr<UrlCheckerDelegate> url_checker_delegate,
-    const base::RepeatingCallback<content::WebContents*()>& web_contents_getter,
-    bool real_time_lookup_enabled,
-    base::WeakPtr<VerdictCacheManager> cache_manager_on_ui)
-    : headers_(headers),
-      load_flags_(load_flags),
-      resource_type_(resource_type),
-      has_user_gesture_(has_user_gesture),
-      web_contents_getter_(web_contents_getter),
-      url_checker_delegate_(std::move(url_checker_delegate)),
-      database_manager_(url_checker_delegate_->GetDatabaseManager()),
-      real_time_lookup_enabled_(real_time_lookup_enabled),
-      cache_manager_on_ui_(cache_manager_on_ui) {}
-
-SafeBrowsingUrlCheckerImpl::~SafeBrowsingUrlCheckerImpl() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-
-  if (state_ == STATE_CHECKING_URL) {
-    database_manager_->CancelCheck(this);
-
-    TRACE_EVENT_ASYNC_END1("safe_browsing", "CheckUrl", this, "result",
-                           "request_canceled");
-  }
-}
-
-void SafeBrowsingUrlCheckerImpl::CheckUrl(const GURL& url,
-                                          const std::string& method,
-                                          CheckUrlCallback callback) {
-  CheckUrlImpl(url, method, Notifier(std::move(callback)));
-}
-
-void SafeBrowsingUrlCheckerImpl::CheckUrl(const GURL& url,
-                                          const std::string& method,
-                                          NativeCheckUrlCallback callback) {
-  CheckUrlImpl(url, method, Notifier(std::move(callback)));
-}
-
-void SafeBrowsingUrlCheckerImpl::OnCheckBrowseUrlResult(
-    const GURL& url,
-    SBThreatType threat_type,
-    const ThreatMetadata& metadata) {
-  OnUrlResult(url, threat_type, metadata);
-}
-
-void SafeBrowsingUrlCheckerImpl::OnUrlResult(const GURL& url,
-                                             SBThreatType threat_type,
-                                             const ThreatMetadata& metadata) {
-  DCHECK_EQ(STATE_CHECKING_URL, state_);
-  DCHECK_LT(next_index_, urls_.size());
-  DCHECK_EQ(urls_[next_index_].url, url);
-
-  timer_.Stop();
-  RecordCheckUrlTimeout(/*timed_out=*/false);
-
-  TRACE_EVENT_ASYNC_END1(
-      "safe_browsing", "CheckUrl", this, "result",
-      threat_type == SB_THREAT_TYPE_SAFE ? "safe" : "unsafe");
-
-  if (threat_type == SB_THREAT_TYPE_SAFE ||
-      threat_type == SB_THREAT_TYPE_SUSPICIOUS_SITE) {
-    state_ = STATE_NONE;
-
-    if (threat_type == SB_THREAT_TYPE_SUSPICIOUS_SITE) {
-      url_checker_delegate_->NotifySuspiciousSiteDetected(web_contents_getter_);
-    }
-
-    if (!RunNextCallback(true, false))
-      return;
-
-    ProcessUrls();
-    return;
-  }
-
-  if (load_flags_ & net::LOAD_PREFETCH) {
-    // Destroy the prefetch with FINAL_STATUS_SAFEBROSWING.
-    if (resource_type_ == content::ResourceType::kMainFrame) {
-      url_checker_delegate_->MaybeDestroyPrerenderContents(
-          web_contents_getter_);
-    }
-    // Record the result of canceled unsafe prefetch. This is used as a signal
-    // for testing.
-    LOCAL_HISTOGRAM_ENUMERATION("SB2Test.ResourceTypes2.UnsafePrefetchCanceled",
-                                resource_type_);
-
-    BlockAndProcessUrls(false);
-    return;
-  }
-
-  UMA_HISTOGRAM_ENUMERATION("SB2.ResourceTypes2.Unsafe", resource_type_);
-
-  security_interstitials::UnsafeResource resource;
-  resource.url = url;
-  resource.original_url = urls_[0].url;
-  if (urls_.size() > 1) {
-    resource.redirect_urls.reserve(urls_.size() - 1);
-    for (size_t i = 1; i < urls_.size(); ++i)
-      resource.redirect_urls.push_back(urls_[i].url);
-  }
-  resource.is_subresource = resource_type_ != content::ResourceType::kMainFrame;
-  resource.is_subframe = resource_type_ == content::ResourceType::kSubFrame;
-  resource.threat_type = threat_type;
-  resource.threat_metadata = metadata;
-  resource.callback =
-      base::BindRepeating(&SafeBrowsingUrlCheckerImpl::OnBlockingPageComplete,
-                          weak_factory_.GetWeakPtr());
-  resource.callback_thread =
-      base::CreateSingleThreadTaskRunner({content::BrowserThread::IO});
-  resource.web_contents_getter = web_contents_getter_;
-  resource.threat_source = database_manager_->GetThreatSource();
-
-  state_ = STATE_DISPLAYING_BLOCKING_PAGE;
-  url_checker_delegate_->StartDisplayingBlockingPageHelper(
-      resource, urls_[next_index_].method, headers_,
-      resource_type_ == content::ResourceType::kMainFrame, has_user_gesture_);
-}
-
-void SafeBrowsingUrlCheckerImpl::OnTimeout() {
-  RecordCheckUrlTimeout(/*timed_out=*/true);
-
-  database_manager_->CancelCheck(this);
-
-  // Any pending callbacks on this URL check should be skipped.
-  weak_factory_.InvalidateWeakPtrs();
-
-  OnUrlResult(urls_[next_index_].url, safe_browsing::SB_THREAT_TYPE_SAFE,
-              ThreatMetadata());
-}
-
-void SafeBrowsingUrlCheckerImpl::CheckUrlImpl(const GURL& url,
-                                              const std::string& method,
-                                              Notifier notifier) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-
-  DVLOG(1) << "SafeBrowsingUrlCheckerImpl checks URL: " << url;
-  urls_.emplace_back(url, method, std::move(notifier));
-
-  ProcessUrls();
-}
-
-void SafeBrowsingUrlCheckerImpl::ProcessUrls() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-  DCHECK_NE(STATE_BLOCKED, state_);
-
-  if (state_ == STATE_CHECKING_URL ||
-      state_ == STATE_DISPLAYING_BLOCKING_PAGE) {
-    return;
-  }
-
-  while (next_index_ < urls_.size()) {
-    DCHECK_EQ(STATE_NONE, state_);
-
-    const GURL& url = urls_[next_index_].url;
-    if (url_checker_delegate_->IsUrlWhitelisted(url)) {
-      if (!RunNextCallback(true, false))
-        return;
-
-      continue;
-    }
-
-    // TODO(yzshen): Consider moving CanCheckResourceType() to the renderer
-    // side. That would save some IPCs. It requires a method on the
-    // SafeBrowsing mojo interface to query all supported resource types.
-    if (!database_manager_->CanCheckResourceType(resource_type_)) {
-      // TODO(vakh): Consider changing this metric to
-      // SafeBrowsing.V4ResourceType to be consistent with the other PVer4
-      // metrics.
-      UMA_HISTOGRAM_ENUMERATION("SB2.ResourceTypes2.Skipped", resource_type_);
-
-      if (!RunNextCallback(true, false))
-        return;
-
-      continue;
-    }
-
-    // TODO(vakh): Consider changing this metric to SafeBrowsing.V4ResourceType
-    // to be consistent with the other PVer4 metrics.
-    UMA_HISTOGRAM_ENUMERATION("SB2.ResourceTypes2.Checked", resource_type_);
-
-    SBThreatType threat_type = CheckWebUIUrls(url);
-    if (threat_type != safe_browsing::SB_THREAT_TYPE_SAFE) {
-      state_ = STATE_CHECKING_URL;
-      TRACE_EVENT_ASYNC_BEGIN1("safe_browsing", "CheckUrl", this, "url",
-                               url.spec());
-
-      base::PostTask(
-          FROM_HERE, {content::BrowserThread::IO},
-          base::BindOnce(&SafeBrowsingUrlCheckerImpl::OnCheckBrowseUrlResult,
-                         weak_factory_.GetWeakPtr(), url, threat_type,
-                         ThreatMetadata()));
-      break;
-    }
-
-    TRACE_EVENT_ASYNC_BEGIN1("safe_browsing", "CheckUrl", this, "url",
-                             url.spec());
-
-    // Start a timer to abort the check if it takes too long.
-    timer_.Start(FROM_HERE,
-                 base::TimeDelta::FromMilliseconds(kCheckUrlTimeoutMs), this,
-                 &SafeBrowsingUrlCheckerImpl::OnTimeout);
-
-    bool safe_synchronously;
-    if (CanPerformFullURLLookup(url)) {
-      UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.RT.ResourceTypes.Checked",
-                                resource_type_);
-      safe_synchronously = false;
-      AsyncMatch match =
-          database_manager_->CheckUrlForHighConfidenceAllowlist(url, this);
-      UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.RT.LocalMatch.Result", match);
-      switch (match) {
-        case AsyncMatch::ASYNC:
-          // Hash-prefix matched. A call to
-          // |OnCheckUrlForHighConfidenceAllowlist| will follow.
-          break;
-        case AsyncMatch::MATCH:
-          // Full-hash matched locally so queue a call to
-          // |OnCheckUrlForHighConfidenceAllowlist| to trigger the hash-based
-          // checking.
-          base::PostTask(
-              FROM_HERE, {content::BrowserThread::IO},
-              base::BindOnce(&SafeBrowsingUrlCheckerImpl::
-                                 OnCheckUrlForHighConfidenceAllowlist,
-                             weak_factory_.GetWeakPtr(),
-                             /*did_match_allowlist=*/true));
-          break;
-        case AsyncMatch::NO_MATCH:
-          // No match found locally. Queue the call to
-          // |OnCheckUrlForHighConfidenceAllowlist| to perform the full URL
-          // lookup.
-          base::PostTask(
-              FROM_HERE, {content::BrowserThread::IO},
-              base::BindOnce(&SafeBrowsingUrlCheckerImpl::
-                                 OnCheckUrlForHighConfidenceAllowlist,
-                             weak_factory_.GetWeakPtr(),
-                             /*did_match_allowlist=*/false));
-          break;
-      }
-    } else {
-      safe_synchronously = database_manager_->CheckBrowseUrl(
-          url, url_checker_delegate_->GetThreatTypes(), this);
-    }
-
-    if (safe_synchronously) {
-      timer_.Stop();
-      RecordCheckUrlTimeout(/*timed_out=*/false);
-
-      if (!RunNextCallback(true, false))
-        return;
-
-      continue;
-    }
-
-    state_ = STATE_CHECKING_URL;
-
-    // Only send out notification of starting a slow check if the database
-    // manager actually supports fast checks (i.e., synchronous checks) but is
-    // not able to complete the check synchronously in this case.
-    // Don't send out notification if the database manager doesn't support
-    // synchronous checks at all (e.g., on mobile).
-    if (!database_manager_->ChecksAreAlwaysAsync())
-      urls_[next_index_].notifier.OnStartSlowCheck();
-
-    break;
-  }
-}
-
-void SafeBrowsingUrlCheckerImpl::BlockAndProcessUrls(bool showed_interstitial) {
-  DVLOG(1) << "SafeBrowsingUrlCheckerImpl blocks URL: "
-           << urls_[next_index_].url;
-  state_ = STATE_BLOCKED;
-
-  // If user decided to not proceed through a warning, mark all the remaining
-  // redirects as "bad".
-  while (next_index_ < urls_.size()) {
-    if (!RunNextCallback(false, showed_interstitial))
-      return;
-  }
-}
-
-bool SafeBrowsingUrlCheckerImpl::CanPerformFullURLLookup(const GURL& url) {
-  if (!real_time_lookup_enabled_)
-    return false;
-
-  if (!RealTimePolicyEngine::CanPerformFullURLLookupForResourceType(
-          resource_type_))
-    return false;
-
-  auto* rt_lookup_service = database_manager_->GetRealTimeUrlLookupService();
-  if (!rt_lookup_service || !rt_lookup_service->CanCheckUrl(url))
-    return false;
-
-  bool in_backoff = rt_lookup_service->IsInBackoffMode();
-  UMA_HISTOGRAM_BOOLEAN("SafeBrowsing.RT.Backoff.State", in_backoff);
-  return !in_backoff;
-}
-
-void SafeBrowsingUrlCheckerImpl::OnBlockingPageComplete(bool proceed) {
-  DCHECK_EQ(STATE_DISPLAYING_BLOCKING_PAGE, state_);
-
-  if (proceed) {
-    state_ = STATE_NONE;
-    if (!RunNextCallback(true, true))
-      return;
-    ProcessUrls();
-  } else {
-    BlockAndProcessUrls(true);
-  }
-}
-
-SBThreatType SafeBrowsingUrlCheckerImpl::CheckWebUIUrls(const GURL& url) {
-  if (url == kChromeUISafeBrowsingMatchMalwareUrl)
-    return safe_browsing::SB_THREAT_TYPE_URL_MALWARE;
-  if (url == kChromeUISafeBrowsingMatchPhishingUrl)
-    return safe_browsing::SB_THREAT_TYPE_URL_PHISHING;
-  if (url == kChromeUISafeBrowsingMatchUnwantedUrl)
-    return safe_browsing::SB_THREAT_TYPE_URL_UNWANTED;
-  if (url == kChromeUISafeBrowsingMatchBillingUrl)
-    return safe_browsing::SB_THREAT_TYPE_BILLING;
-
-  return safe_browsing::SB_THREAT_TYPE_SAFE;
-}
-
-bool SafeBrowsingUrlCheckerImpl::RunNextCallback(bool proceed,
-                                                 bool showed_interstitial) {
-  DCHECK_LT(next_index_, urls_.size());
-
-  auto weak_self = weak_factory_.GetWeakPtr();
-  urls_[next_index_++].notifier.OnCompleteCheck(proceed, showed_interstitial);
-  return !!weak_self;
-}
-
-void SafeBrowsingUrlCheckerImpl::OnCheckUrlForHighConfidenceAllowlist(
-    bool did_match_allowlist) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-  DCHECK_EQ(content::ResourceType::kMainFrame, resource_type_);
-
-  const GURL& url = urls_[next_index_].url;
-  if (did_match_allowlist) {
-    // If the URL matches the high-confidence allowlist, still do the hash based
-    // checks.
-    if (database_manager_->CheckBrowseUrl(
-            url, url_checker_delegate_->GetThreatTypes(), this)) {
-      // No match found in the local database. Safe to call |OnUrlResult| here
-      // directly.
-      OnUrlResult(url, SB_THREAT_TYPE_SAFE, ThreatMetadata());
-    }
-    return;
-  }
-
-  base::PostTask(
-      FROM_HERE, {content::BrowserThread::UI},
-      base::BindOnce(
-          &SafeBrowsingUrlCheckerImpl::StartGetCachedRealTimeUrlVerdictOnUI,
-          weak_factory_.GetWeakPtr(), cache_manager_on_ui_, url,
-          base::TimeTicks::Now()));
-}
-
-// static
-void SafeBrowsingUrlCheckerImpl::StartGetCachedRealTimeUrlVerdictOnUI(
-    base::WeakPtr<SafeBrowsingUrlCheckerImpl> weak_checker_on_io,
-    base::WeakPtr<VerdictCacheManager> cache_manager_on_ui,
-    const GURL& url,
-    base::TimeTicks get_cache_start_time) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-
-  std::unique_ptr<RTLookupResponse::ThreatInfo> cached_threat_info =
-      std::make_unique<RTLookupResponse::ThreatInfo>();
-
-  base::UmaHistogramBoolean("SafeBrowsing.RT.HasValidCacheManager",
-                            !!cache_manager_on_ui);
-
-  RTLookupResponse::ThreatInfo::VerdictType verdict_type =
-      cache_manager_on_ui
-          ? cache_manager_on_ui->GetCachedRealTimeUrlVerdict(
-                url, cached_threat_info.get())
-          : RTLookupResponse::ThreatInfo::VERDICT_TYPE_UNSPECIFIED;
-  base::PostTask(
-      FROM_HERE, {content::BrowserThread::IO},
-      base::BindOnce(
-          &SafeBrowsingUrlCheckerImpl::OnGetCachedRealTimeUrlVerdictDoneOnIO,
-          weak_checker_on_io, verdict_type, std::move(cached_threat_info), url,
-          get_cache_start_time));
-}
-
-void SafeBrowsingUrlCheckerImpl::OnGetCachedRealTimeUrlVerdictDoneOnIO(
-    RTLookupResponse::ThreatInfo::VerdictType verdict_type,
-    std::unique_ptr<RTLookupResponse::ThreatInfo> cached_threat_info,
-    const GURL& url,
-    base::TimeTicks get_cache_start_time) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-
-  base::UmaHistogramSparse("SafeBrowsing.RT.GetCacheResult", verdict_type);
-  UMA_HISTOGRAM_TIMES("SafeBrowsing.RT.GetCache.Time",
-                      base::TimeTicks::Now() - get_cache_start_time);
-
-  if (verdict_type == RTLookupResponse::ThreatInfo::SAFE) {
-    OnUrlResult(url, SB_THREAT_TYPE_SAFE, ThreatMetadata());
-    return;
-  } else if (verdict_type == RTLookupResponse::ThreatInfo::DANGEROUS) {
-    OnUrlResult(url,
-                RealTimeUrlLookupService::GetSBThreatTypeForRTThreatType(
-                    cached_threat_info->threat_type()),
-                ThreatMetadata());
-    return;
-  }
-
-  RTLookupRequestCallback request_callback =
-      base::BindOnce(&SafeBrowsingUrlCheckerImpl::OnRTLookupRequest,
-                     weak_factory_.GetWeakPtr());
-
-  RTLookupResponseCallback response_callback =
-      base::BindOnce(&SafeBrowsingUrlCheckerImpl::OnRTLookupResponse,
-                     weak_factory_.GetWeakPtr());
-
-  auto* rt_lookup_service = database_manager_->GetRealTimeUrlLookupService();
-  rt_lookup_service->StartLookup(url, std::move(request_callback),
-                                 std::move(response_callback));
-}
-
-void SafeBrowsingUrlCheckerImpl::OnRTLookupRequest(
-    std::unique_ptr<RTLookupRequest> request) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-
-  // The following is to log this RTLookupRequest on any open
-  // chrome://safe-browsing pages.
-  base::PostTaskAndReplyWithResult(
-      FROM_HERE, {content::BrowserThread::UI},
-      base::BindOnce(&WebUIInfoSingleton::AddToRTLookupPings,
-                     base::Unretained(WebUIInfoSingleton::GetInstance()),
-                     *request),
-      base::BindOnce(&SafeBrowsingUrlCheckerImpl::SetWebUIToken,
-                     weak_factory_.GetWeakPtr()));
-}
-
-void SafeBrowsingUrlCheckerImpl::OnRTLookupResponse(
-    std::unique_ptr<RTLookupResponse> response) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-  DCHECK_EQ(content::ResourceType::kMainFrame, resource_type_);
-
-  if (url_web_ui_token_ != -1) {
-    // The following is to log this RTLookupResponse on any open
-    // chrome://safe-browsing pages.
-    base::PostTask(
-        FROM_HERE, {content::BrowserThread::UI},
-        base::BindOnce(&WebUIInfoSingleton::AddToRTLookupResponses,
-                       base::Unretained(WebUIInfoSingleton::GetInstance()),
-                       url_web_ui_token_, *response));
-  }
-
-  const GURL& url = urls_[next_index_].url;
-
-  SBThreatType sb_threat_type = SB_THREAT_TYPE_SAFE;
-  if (response && (response->threat_info_size() > 0)) {
-    base::PostTask(FROM_HERE, {content::BrowserThread::UI},
-                   base::BindOnce(&VerdictCacheManager::CacheRealTimeUrlVerdict,
-                                  cache_manager_on_ui_, url, *response,
-                                  base::Time::Now()));
-
-    // TODO(crbug.com/1033692): Only take the first threat info into account
-    // because threat infos are returned in decreasing order of severity.
-    // Consider extend it to support multiple threat types.
-    if (response->threat_info(0).verdict_type() ==
-        RTLookupResponse::ThreatInfo::DANGEROUS) {
-      sb_threat_type = RealTimeUrlLookupService::GetSBThreatTypeForRTThreatType(
-          response->threat_info(0).threat_type());
-    }
-  }
-  OnUrlResult(url, sb_threat_type, ThreatMetadata());
-}
-
-void SafeBrowsingUrlCheckerImpl::SetWebUIToken(int token) {
-  url_web_ui_token_ = token;
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/browser/safe_browsing_url_checker_impl.h b/components/safe_browsing/browser/safe_browsing_url_checker_impl.h
deleted file mode 100644
index 7358f08..0000000
--- a/components/safe_browsing/browser/safe_browsing_url_checker_impl.h
+++ /dev/null
@@ -1,236 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_BROWSER_SAFE_BROWSING_URL_CHECKER_IMPL_H_
-#define COMPONENTS_SAFE_BROWSING_BROWSER_SAFE_BROWSING_URL_CHECKER_IMPL_H_
-
-#include <vector>
-
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#include "base/timer/timer.h"
-#include "components/safe_browsing/common/safe_browsing.mojom.h"
-#include "components/safe_browsing/db/database_manager.h"
-#include "components/safe_browsing/proto/realtimeapi.pb.h"
-#include "content/public/common/resource_type.h"
-#include "mojo/public/cpp/bindings/remote.h"
-#include "net/http/http_request_headers.h"
-#include "url/gurl.h"
-
-namespace content {
-class WebContents;
-}
-
-namespace safe_browsing {
-
-class UrlCheckerDelegate;
-
-class VerdictCacheManager;
-
-// A SafeBrowsingUrlCheckerImpl instance is used to perform SafeBrowsing check
-// for a URL and its redirect URLs. It implements Mojo interface so that it can
-// be used to handle queries from renderers. But it is also used to handle
-// queries from the browser. In that case, the public methods are called
-// directly instead of through Mojo.
-//
-// To be considered "safe", a URL must not appear in the SafeBrowsing blacklists
-// (see SafeBrowsingService for details).
-//
-// Note that the SafeBrowsing check takes at most kCheckUrlTimeoutMs
-// milliseconds. If it takes longer than this, then the system defaults to
-// treating the URL as safe.
-//
-// If the URL is classified as dangerous, a warning interstitial page is
-// displayed. In that case, the user can click through the warning page if they
-// decides to procced with loading the URL anyway.
-class SafeBrowsingUrlCheckerImpl : public mojom::SafeBrowsingUrlChecker,
-                                   public SafeBrowsingDatabaseManager::Client {
- public:
-  using NativeUrlCheckNotifier =
-      base::OnceCallback<void(bool /* proceed */,
-                              bool /* showed_interstitial */)>;
-
-  // If |slow_check_notifier| is not null, the callback is supposed to update
-  // this output parameter with a callback to receive complete notification. In
-  // that case, |proceed| and |showed_interstitial| should be ignored.
-  using NativeCheckUrlCallback =
-      base::OnceCallback<void(NativeUrlCheckNotifier* /* slow_check_notifier */,
-                              bool /* proceed */,
-                              bool /* showed_interstitial */)>;
-
-  // Constructor for SafeBrowsingUrlCheckerImpl. |real_time_lookup_enabled|
-  // indicates whether or not the profile has enabled real time URL lookups, as
-  // computed by the RealTimePolicyEngine. This must be computed in advance,
-  // since this class only exists on the IO thread.
-  SafeBrowsingUrlCheckerImpl(
-      const net::HttpRequestHeaders& headers,
-      int load_flags,
-      content::ResourceType resource_type,
-      bool has_user_gesture,
-      scoped_refptr<UrlCheckerDelegate> url_checker_delegate,
-      const base::RepeatingCallback<content::WebContents*()>&
-          web_contents_getter,
-      bool real_time_lookup_enabled,
-      base::WeakPtr<VerdictCacheManager> cache_manager_on_ui);
-
-  ~SafeBrowsingUrlCheckerImpl() override;
-
-  // mojom::SafeBrowsingUrlChecker implementation.
-  // NOTE: |callback| could be run synchronously before this method returns. Be
-  // careful if |callback| could destroy this object.
-  void CheckUrl(const GURL& url,
-                const std::string& method,
-                CheckUrlCallback callback) override;
-
-  // NOTE: |callback| could be run synchronously before this method returns. Be
-  // careful if |callback| could destroy this object.
-  void CheckUrl(const GURL& url,
-                const std::string& method,
-                NativeCheckUrlCallback callback);
-
- private:
-  class Notifier {
-   public:
-    explicit Notifier(CheckUrlCallback callback);
-    explicit Notifier(NativeCheckUrlCallback native_callback);
-
-    ~Notifier();
-
-    Notifier(Notifier&& other);
-    Notifier& operator=(Notifier&& other);
-
-    void OnStartSlowCheck();
-    void OnCompleteCheck(bool proceed, bool showed_interstitial);
-
-   private:
-    // Used in the mojo interface case.
-    CheckUrlCallback callback_;
-    mojo::Remote<mojom::UrlCheckNotifier> slow_check_notifier_;
-
-    // Used in the native call case.
-    NativeCheckUrlCallback native_callback_;
-    NativeUrlCheckNotifier native_slow_check_notifier_;
-  };
-
-  // SafeBrowsingDatabaseManager::Client implementation:
-  void OnCheckBrowseUrlResult(const GURL& url,
-                              SBThreatType threat_type,
-                              const ThreatMetadata& metadata) override;
-  void OnCheckUrlForHighConfidenceAllowlist(bool did_match_allowlist) override;
-
-  // This function has to be static because it is called in UI thread,
-  // |weak_checker_on_io| can only be accessed from IO thread.
-  // This function is called if the url doesn't match the allowlist.
-  static void StartGetCachedRealTimeUrlVerdictOnUI(
-      base::WeakPtr<SafeBrowsingUrlCheckerImpl> weak_checker_on_io,
-      base::WeakPtr<VerdictCacheManager> cache_manager_on_ui,
-      const GURL& url,
-      base::TimeTicks get_cache_start_time);
-
-  // This function will start real time url lookup if there is no cache match.
-  void OnGetCachedRealTimeUrlVerdictDoneOnIO(
-      RTLookupResponse::ThreatInfo::VerdictType verdict_type,
-      std::unique_ptr<RTLookupResponse::ThreatInfo> cached_threat_info,
-      const GURL& url,
-      base::TimeTicks get_cache_start_time);
-
-  void OnTimeout();
-
-  void OnUrlResult(const GURL& url,
-                   SBThreatType threat_type,
-                   const ThreatMetadata& metadata);
-
-  void CheckUrlImpl(const GURL& url,
-                    const std::string& method,
-                    Notifier notifier);
-
-  // NOTE: this method runs callbacks which could destroy this object.
-  void ProcessUrls();
-
-  // NOTE: this method runs callbacks which could destroy this object.
-  void BlockAndProcessUrls(bool showed_interstitial);
-
-  void OnBlockingPageComplete(bool proceed);
-
-  // Helper method that checks whether |url|'s reputation can be checked using
-  // real time lookups.
-  bool CanPerformFullURLLookup(const GURL& url);
-
-  SBThreatType CheckWebUIUrls(const GURL& url);
-
-  // Returns false if this object has been destroyed by the callback. In that
-  // case none of the members of this object should be touched again.
-  bool RunNextCallback(bool proceed, bool showed_interstitial);
-
-  // Called when the |request| from the real-time lookup service is sent.
-  void OnRTLookupRequest(std::unique_ptr<RTLookupRequest> request);
-
-  // Called when the |response| from the real-time lookup service is received.
-  void OnRTLookupResponse(std::unique_ptr<RTLookupResponse> response);
-
-  void SetWebUIToken(int token);
-
-  enum State {
-    // Haven't started checking or checking is complete.
-    STATE_NONE,
-    // We have one outstanding URL-check.
-    STATE_CHECKING_URL,
-    // We're displaying a blocking page.
-    STATE_DISPLAYING_BLOCKING_PAGE,
-    // The blocking page has returned *not* to proceed.
-    STATE_BLOCKED
-  };
-
-  struct UrlInfo {
-    UrlInfo(const GURL& url, const std::string& method, Notifier notifier);
-    UrlInfo(UrlInfo&& other);
-
-    ~UrlInfo();
-
-    GURL url;
-    std::string method;
-    Notifier notifier;
-  };
-
-  const net::HttpRequestHeaders headers_;
-  const int load_flags_;
-  const content::ResourceType resource_type_;
-  const bool has_user_gesture_;
-  base::RepeatingCallback<content::WebContents*()> web_contents_getter_;
-  scoped_refptr<UrlCheckerDelegate> url_checker_delegate_;
-  scoped_refptr<SafeBrowsingDatabaseManager> database_manager_;
-
-  // The redirect chain for this resource, including the original URL and
-  // subsequent redirect URLs.
-  std::vector<UrlInfo> urls_;
-  // |urls_| before |next_index_| have been checked. If |next_index_| is smaller
-  // than the size of |urls_|, the URL at |next_index_| is being processed.
-  size_t next_index_ = 0;
-
-  // Token used for displaying url real time lookup pings. A single token is
-  // sufficient since real time check only happens on main frame url.
-  int url_web_ui_token_ = -1;
-
-  State state_ = STATE_NONE;
-
-  // Timer to abort the SafeBrowsing check if it takes too long.
-  base::OneShotTimer timer_;
-
-  // Whether real time lookup is enabled for this request.
-  bool real_time_lookup_enabled_;
-
-  // Unowned object used for getting and storing real time url check cache.
-  // Must be NOT nullptr when real time url check is enabled and profile is not
-  // delete. Can only be accessed in UI thread.
-  base::WeakPtr<VerdictCacheManager> cache_manager_on_ui_;
-
-  base::WeakPtrFactory<SafeBrowsingUrlCheckerImpl> weak_factory_{this};
-
-  DISALLOW_COPY_AND_ASSIGN(SafeBrowsingUrlCheckerImpl);
-};
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_BROWSER_SAFE_BROWSING_URL_CHECKER_IMPL_H_
diff --git a/components/safe_browsing/browser/threat_details.cc b/components/safe_browsing/browser/threat_details.cc
deleted file mode 100644
index 9c2b6c4..0000000
--- a/components/safe_browsing/browser/threat_details.cc
+++ /dev/null
@@ -1,903 +0,0 @@
-// Copyright (c) 2012 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.
-//
-// Implementation of the ThreatDetails class.
-
-#include "components/safe_browsing/browser/threat_details.h"
-
-#include <stddef.h>
-#include <stdint.h>
-#include <unordered_set>
-#include <utility>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/feature_list.h"
-#include "base/lazy_instance.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/stl_util.h"
-#include "base/strings/string_piece.h"
-#include "base/strings/string_util.h"
-#include "base/task/post_task.h"
-#include "components/history/core/browser/history_service.h"
-#include "components/safe_browsing/base_ui_manager.h"
-#include "components/safe_browsing/browser/referrer_chain_provider.h"
-#include "components/safe_browsing/browser/threat_details_cache.h"
-#include "components/safe_browsing/browser/threat_details_history.h"
-#include "components/safe_browsing/db/hit_report.h"
-#include "components/safe_browsing/features.h"
-#include "components/safe_browsing/web_ui/safe_browsing_ui.h"
-#include "content/public/browser/back_forward_cache.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/global_routing_id.h"
-#include "content/public/browser/navigation_controller.h"
-#include "content/public/browser/navigation_entry.h"
-#include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/render_process_host.h"
-#include "content/public/browser/web_contents.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "services/service_manager/public/cpp/interface_provider.h"
-
-using content::BrowserThread;
-using content::NavigationEntry;
-using content::RenderFrameHost;
-using content::WebContents;
-
-// Keep in sync with KMaxNodes in components/safe_browsing/renderer/
-// threat_dom_details.cc
-static const uint32_t kMaxDomNodes = 500;
-
-namespace safe_browsing {
-
-// static
-ThreatDetailsFactory* ThreatDetails::factory_ = nullptr;
-
-namespace {
-
-// An element ID indicating that an HTML Element has no parent.
-const int kElementIdNoParent = -1;
-
-// The number of user gestures to trace back for the referrer chain.
-const int kThreatDetailsUserGestureLimit = 2;
-
-typedef std::unordered_set<std::string> StringSet;
-// A set of HTTPS headers that are allowed to be collected. Contains both
-// request and response headers. All entries in this list should be lower-case
-// to support case-insensitive comparison.
-struct WhitelistedHttpsHeadersTraits
-    : base::internal::DestructorAtExitLazyInstanceTraits<StringSet> {
-  static StringSet* New(void* instance) {
-    StringSet* headers =
-        base::internal::DestructorAtExitLazyInstanceTraits<StringSet>::New(
-            instance);
-    headers->insert({"google-creative-id", "google-lineitem-id", "referer",
-                     "content-type", "content-length", "date", "server",
-                     "cache-control", "pragma", "expires"});
-    return headers;
-  }
-};
-base::LazyInstance<StringSet, WhitelistedHttpsHeadersTraits>
-    g_https_headers_whitelist = LAZY_INSTANCE_INITIALIZER;
-
-// Helper function that converts SBThreatType to
-// ClientSafeBrowsingReportRequest::ReportType.
-ClientSafeBrowsingReportRequest::ReportType GetReportTypeFromSBThreatType(
-    SBThreatType threat_type) {
-  switch (threat_type) {
-    case SB_THREAT_TYPE_URL_PHISHING:
-      return ClientSafeBrowsingReportRequest::URL_PHISHING;
-    case SB_THREAT_TYPE_URL_MALWARE:
-      return ClientSafeBrowsingReportRequest::URL_MALWARE;
-    case SB_THREAT_TYPE_URL_UNWANTED:
-      return ClientSafeBrowsingReportRequest::URL_UNWANTED;
-    case SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING:
-      return ClientSafeBrowsingReportRequest::URL_CLIENT_SIDE_PHISHING;
-    case SB_THREAT_TYPE_URL_CLIENT_SIDE_MALWARE:
-      return ClientSafeBrowsingReportRequest::URL_CLIENT_SIDE_MALWARE;
-    case SB_THREAT_TYPE_BLOCKED_AD_POPUP:
-      return ClientSafeBrowsingReportRequest::BLOCKED_AD_POPUP;
-    case SB_THREAT_TYPE_AD_SAMPLE:
-      return ClientSafeBrowsingReportRequest::AD_SAMPLE;
-    case SB_THREAT_TYPE_BLOCKED_AD_REDIRECT:
-      return ClientSafeBrowsingReportRequest::BLOCKED_AD_REDIRECT;
-    case SB_THREAT_TYPE_SAVED_PASSWORD_REUSE:
-    case SB_THREAT_TYPE_SIGNED_IN_SYNC_PASSWORD_REUSE:
-    case SB_THREAT_TYPE_SIGNED_IN_NON_SYNC_PASSWORD_REUSE:
-    case SB_THREAT_TYPE_ENTERPRISE_PASSWORD_REUSE:
-      return ClientSafeBrowsingReportRequest::URL_PASSWORD_PROTECTION_PHISHING;
-    case SB_THREAT_TYPE_SUSPICIOUS_SITE:
-      return ClientSafeBrowsingReportRequest::URL_SUSPICIOUS;
-    case SB_THREAT_TYPE_BILLING:
-      return ClientSafeBrowsingReportRequest::BILLING;
-    case SB_THREAT_TYPE_APK_DOWNLOAD:
-      return ClientSafeBrowsingReportRequest::APK_DOWNLOAD;
-    case SB_THREAT_TYPE_UNUSED:
-    case SB_THREAT_TYPE_SAFE:
-    case SB_THREAT_TYPE_URL_BINARY_MALWARE:
-    case SB_THREAT_TYPE_EXTENSION:
-    case SB_THREAT_TYPE_BLACKLISTED_RESOURCE:
-    case SB_THREAT_TYPE_API_ABUSE:
-    case SB_THREAT_TYPE_SUBRESOURCE_FILTER:
-    case SB_THREAT_TYPE_CSD_WHITELIST:
-    case SB_THREAT_TYPE_HIGH_CONFIDENCE_ALLOWLIST:
-    case DEPRECATED_SB_THREAT_TYPE_URL_PASSWORD_PROTECTION_PHISHING:
-      // Gated by SafeBrowsingBlockingPage::ShouldReportThreatDetails.
-      NOTREACHED() << "We should not send report for threat type: "
-                   << threat_type;
-      return ClientSafeBrowsingReportRequest::UNKNOWN;
-  }
-}
-
-// Clears the specified HTTPS resource of any sensitive data, only retaining
-// data that is whitelisted for collection.
-void ClearHttpsResource(ClientSafeBrowsingReportRequest::Resource* resource) {
-  // Make a copy of the original resource to retain all data.
-  ClientSafeBrowsingReportRequest::Resource orig_resource(*resource);
-
-  // Clear the request headers and copy over any whitelisted ones.
-  resource->clear_request();
-  for (int i = 0; i < orig_resource.request().headers_size(); ++i) {
-    ClientSafeBrowsingReportRequest::HTTPHeader* orig_header =
-        orig_resource.mutable_request()->mutable_headers(i);
-    if (g_https_headers_whitelist.Get().count(
-            base::ToLowerASCII(orig_header->name())) > 0) {
-      resource->mutable_request()->add_headers()->Swap(orig_header);
-    }
-  }
-  // Also copy some other request fields.
-  resource->mutable_request()->mutable_bodydigest()->swap(
-      *orig_resource.mutable_request()->mutable_bodydigest());
-  resource->mutable_request()->set_bodylength(
-      orig_resource.request().bodylength());
-
-  // ...repeat for response headers.
-  resource->clear_response();
-  for (int i = 0; i < orig_resource.response().headers_size(); ++i) {
-    ClientSafeBrowsingReportRequest::HTTPHeader* orig_header =
-        orig_resource.mutable_response()->mutable_headers(i);
-    if (g_https_headers_whitelist.Get().count(
-            base::ToLowerASCII(orig_header->name())) > 0) {
-      resource->mutable_response()->add_headers()->Swap(orig_header);
-    }
-  }
-  // Also copy some other response fields.
-  resource->mutable_response()->mutable_bodydigest()->swap(
-      *orig_resource.mutable_response()->mutable_bodydigest());
-  resource->mutable_response()->set_bodylength(
-      orig_resource.response().bodylength());
-  resource->mutable_response()->mutable_remote_ip()->swap(
-      *orig_resource.mutable_response()->mutable_remote_ip());
-}
-
-std::string GetElementKey(const int frame_tree_node_id,
-                          const int element_node_id) {
-  return base::StringPrintf("%d-%d", frame_tree_node_id, element_node_id);
-}
-
-using CSBRR = safe_browsing::ClientSafeBrowsingReportRequest;
-CSBRR::SafeBrowsingUrlApiType GetUrlApiTypeForThreatSource(
-    safe_browsing::ThreatSource source) {
-  switch (source) {
-    case safe_browsing::ThreatSource::DATA_SAVER:
-      return CSBRR::FLYWHEEL;
-    case safe_browsing::ThreatSource::LOCAL_PVER3:
-      return CSBRR::PVER3_NATIVE;
-    case safe_browsing::ThreatSource::LOCAL_PVER4:
-      return CSBRR::PVER4_NATIVE;
-    case safe_browsing::ThreatSource::REMOTE:
-      return CSBRR::ANDROID_SAFETYNET;
-    case safe_browsing::ThreatSource::UNKNOWN:
-    case safe_browsing::ThreatSource::CLIENT_SIDE_DETECTION:
-    case safe_browsing::ThreatSource::PASSWORD_PROTECTION_SERVICE:
-      break;
-  }
-  return CSBRR::SAFE_BROWSING_URL_API_TYPE_UNSPECIFIED;
-}
-
-void TrimElements(const std::set<int> target_ids,
-                  ElementMap* elements,
-                  ResourceMap* resources) {
-  if (target_ids.empty()) {
-    elements->clear();
-    resources->clear();
-    return;
-  }
-
-  // First, scan over the elements and create a list ordered by element ID as
-  // well as a reverse mapping from element ID to its parent ID.
-  std::vector<HTMLElement*> elements_by_id(elements->size());
-
-  // The parent vector is initialized with |kElementIdNoParent| so we can
-  // identify elements that have no parent.
-  std::vector<int> element_id_to_parent_id(elements->size(),
-                                           kElementIdNoParent);
-  for (const auto& element_pair : *elements) {
-    HTMLElement* element = element_pair.second.get();
-    elements_by_id[element->id()] = element;
-
-    for (int child_id : element->child_ids()) {
-      element_id_to_parent_id[child_id] = element->id();
-    }
-  }
-
-  // Create a similar map for resources, ordered by resource ID.
-  std::vector<std::string> resource_id_to_url(resources->size());
-  for (const auto& resource_pair : *resources) {
-    const std::string& url = resource_pair.first;
-    ClientSafeBrowsingReportRequest::Resource* resource =
-        resource_pair.second.get();
-    resource_id_to_url[resource->id()] = url;
-  }
-
-  // Take a second pass and determine which element IDs to keep. We want to keep
-  // the immediate parent, the siblings, and the children of the target ids.
-  // By keeping the parent of the target and all of its children, this covers
-  // the target's siblings as well.
-  std::vector<int> element_ids_to_keep;
-  // Resource IDs are also tracked so that we remember which resources are
-  // attached to elements that we are keeping. This avoids deleting resources
-  // that are shared between kept elements and trimmed elements.
-  std::vector<int> kept_resource_ids;
-  for (int target_id : target_ids) {
-    const int parent_id = element_id_to_parent_id[target_id];
-    if (parent_id == kElementIdNoParent) {
-      // If one of the target elements has no parent then we skip trimming the
-      // report further. Since we collect all siblings of this element, it will
-      // effectively span the whole report, so no trimming necessary.
-      return;
-    }
-
-    // Otherwise, insert the parent ID into the list of ids to keep. This will
-    // capture the parent and siblings of the target element, as well as each of
-    // their children.
-    if (!base::Contains(element_ids_to_keep, parent_id)) {
-      element_ids_to_keep.push_back(parent_id);
-
-      // Check if this element has a resource. If so, remember to also keep the
-      // resource.
-      const HTMLElement& elem = *elements_by_id[parent_id];
-      if (elem.has_resource_id()) {
-        kept_resource_ids.push_back(elem.resource_id());
-      }
-    }
-  }
-
-  // Walk through |element_ids_to_keep| and append the children of each of
-  // element to |element_ids_to_keep|. This is effectively a breadth-first
-  // traversal of the tree. The list will stop growing when we reach the leaf
-  // nodes that have no more children.
-  for (size_t index = 0; index < element_ids_to_keep.size(); ++index) {
-    int cur_element_id = element_ids_to_keep[index];
-    const HTMLElement& element = *(elements_by_id[cur_element_id]);
-    if (element.has_resource_id()) {
-      kept_resource_ids.push_back(element.resource_id());
-    }
-    for (int child_id : element.child_ids()) {
-      element_ids_to_keep.push_back(child_id);
-
-      // Check if each child element has a resource. If so, remember to also
-      // keep the resource.
-      const HTMLElement& child_element = *elements_by_id[child_id];
-      if (child_element.has_resource_id()) {
-        kept_resource_ids.push_back(child_element.resource_id());
-      }
-    }
-  }
-  // Sort the list for easier lookup below.
-  std::sort(element_ids_to_keep.begin(), element_ids_to_keep.end());
-
-  // Now we know which elements we want to keep, scan through |elements| and
-  // erase anything that we aren't keeping.
-  for (auto element_iter = elements->begin();
-       element_iter != elements->end();) {
-    const HTMLElement& element = *element_iter->second;
-
-    // Delete any elements that we do not want to keep.
-    if (!base::Contains(element_ids_to_keep, element.id())) {
-      // If this element has a resource then maybe delete the resouce too. Some
-      // resources may be shared between kept and trimmed elements, and those
-      // ones should not be deleted.
-      if (element.has_resource_id() &&
-          !base::Contains(kept_resource_ids, element.resource_id())) {
-        const std::string& resource_url =
-            resource_id_to_url[element.resource_id()];
-        resources->erase(resource_url);
-      }
-      element_iter = elements->erase(element_iter);
-    } else {
-      ++element_iter;
-    }
-  }
-}
-
-}  // namespace
-
-// The default ThreatDetailsFactory.  Global, made a singleton so we
-// don't leak it.
-class ThreatDetailsFactoryImpl : public ThreatDetailsFactory {
- public:
-  std::unique_ptr<ThreatDetails> CreateThreatDetails(
-      BaseUIManager* ui_manager,
-      WebContents* web_contents,
-      const security_interstitials::UnsafeResource& unsafe_resource,
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      history::HistoryService* history_service,
-      ReferrerChainProvider* referrer_chain_provider,
-      bool trim_to_ad_tags,
-      ThreatDetailsDoneCallback done_callback) override {
-    // We can't use make_unique due to the protected constructor. We can't
-    // directly use std::unique_ptr<ThreatDetails>(new ThreatDetails(...))
-    // due to presubmit errors. So we use base::WrapUnique:
-    auto threat_details = base::WrapUnique(new ThreatDetails(
-        ui_manager, web_contents, unsafe_resource, url_loader_factory,
-        history_service, referrer_chain_provider, trim_to_ad_tags,
-        std::move(done_callback)));
-    threat_details->StartCollection();
-    return threat_details;
-  }
-
- private:
-  friend struct base::LazyInstanceTraitsBase<ThreatDetailsFactoryImpl>;
-
-  ThreatDetailsFactoryImpl() {}
-
-  DISALLOW_COPY_AND_ASSIGN(ThreatDetailsFactoryImpl);
-};
-
-static base::LazyInstance<ThreatDetailsFactoryImpl>::DestructorAtExit
-    g_threat_details_factory_impl = LAZY_INSTANCE_INITIALIZER;
-
-// Create a ThreatDetails for the given tab.
-/* static */
-std::unique_ptr<ThreatDetails> ThreatDetails::NewThreatDetails(
-    BaseUIManager* ui_manager,
-    WebContents* web_contents,
-    const UnsafeResource& resource,
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    history::HistoryService* history_service,
-    ReferrerChainProvider* referrer_chain_provider,
-    bool trim_to_ad_tags,
-    ThreatDetailsDoneCallback done_callback) {
-  // Set up the factory if this has not been done already (tests do that
-  // before this method is called).
-  if (!factory_)
-    factory_ = g_threat_details_factory_impl.Pointer();
-  return factory_->CreateThreatDetails(
-      ui_manager, web_contents, resource, url_loader_factory, history_service,
-      referrer_chain_provider, trim_to_ad_tags, std::move(done_callback));
-}
-
-// Create a ThreatDetails for the given tab. Runs in the UI thread.
-ThreatDetails::ThreatDetails(
-    BaseUIManager* ui_manager,
-    content::WebContents* web_contents,
-    const UnsafeResource& resource,
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    history::HistoryService* history_service,
-    ReferrerChainProvider* referrer_chain_provider,
-    bool trim_to_ad_tags,
-    ThreatDetailsDoneCallback done_callback)
-    : content::WebContentsObserver(web_contents),
-      url_loader_factory_(url_loader_factory),
-      ui_manager_(ui_manager),
-      resource_(resource),
-      referrer_chain_provider_(referrer_chain_provider),
-      cache_result_(false),
-      did_proceed_(false),
-      num_visits_(0),
-      trim_to_ad_tags_(trim_to_ad_tags),
-      cache_collector_(new ThreatDetailsCacheCollector),
-      done_callback_(std::move(done_callback)),
-      all_done_expected_(false),
-      is_all_done_(false) {
-  redirects_collector_ = new ThreatDetailsRedirectsCollector(
-      history_service ? history_service->AsWeakPtr()
-                      : base::WeakPtr<history::HistoryService>());
-}
-
-// TODO(lpz): Consider making this constructor delegate to the parameterized one
-// above.
-ThreatDetails::ThreatDetails()
-    : cache_result_(false),
-      did_proceed_(false),
-      num_visits_(0),
-      trim_to_ad_tags_(false),
-      all_done_expected_(false),
-      is_all_done_(false) {}
-
-ThreatDetails::~ThreatDetails() {
-  DCHECK_EQ(all_done_expected_, is_all_done_);
-}
-
-bool ThreatDetails::IsReportableUrl(const GURL& url) const {
-  // TODO(panayiotis): also skip internal urls.
-  return url.SchemeIs("http") || url.SchemeIs("https");
-}
-
-// Looks for a Resource for the given url in resources_.  If found, it
-// updates |resource|. Otherwise, it creates a new message, adds it to
-// resources_ and updates |resource| to point to it.
-//
-ClientSafeBrowsingReportRequest::Resource* ThreatDetails::FindOrCreateResource(
-    const GURL& url) {
-  auto& resource = resources_[url.spec()];
-  if (!resource) {
-    // Create the resource for |url|.
-    int id = resources_.size() - 1;
-    std::unique_ptr<ClientSafeBrowsingReportRequest::Resource> new_resource(
-        new ClientSafeBrowsingReportRequest::Resource());
-    new_resource->set_url(url.spec());
-    new_resource->set_id(id);
-    resource = std::move(new_resource);
-  }
-  return resource.get();
-}
-
-HTMLElement* ThreatDetails::FindOrCreateElement(
-    const std::string& element_key) {
-  auto& element = elements_[element_key];
-  if (!element) {
-    // Create an entry for this element.
-    int element_dom_id = elements_.size() - 1;
-    std::unique_ptr<HTMLElement> new_element(new HTMLElement());
-    new_element->set_id(element_dom_id);
-    element = std::move(new_element);
-  }
-  return element.get();
-}
-
-ClientSafeBrowsingReportRequest::Resource* ThreatDetails::AddUrl(
-    const GURL& url,
-    const GURL& parent,
-    const std::string& tagname,
-    const std::vector<GURL>* children) {
-  if (!url.is_valid() || !IsReportableUrl(url))
-    return nullptr;
-
-  // Find (or create) the resource for the url.
-  ClientSafeBrowsingReportRequest::Resource* url_resource =
-      FindOrCreateResource(url);
-  if (!tagname.empty())
-    url_resource->set_tag_name(tagname);
-  if (!parent.is_empty() && IsReportableUrl(parent)) {
-    // Add the resource for the parent.
-    ClientSafeBrowsingReportRequest::Resource* parent_resource =
-        FindOrCreateResource(parent);
-    // Update the parent-child relation
-    url_resource->set_parent_id(parent_resource->id());
-  }
-  if (children) {
-    for (auto it = children->begin(); it != children->end(); ++it) {
-      // TODO(lpz): Should this first check if the child URL is reportable
-      // before creating the resource?
-      ClientSafeBrowsingReportRequest::Resource* child_resource =
-          FindOrCreateResource(*it);
-      bool duplicate_child = false;
-      for (auto child_id : url_resource->child_ids()) {
-        if (child_id == child_resource->id()) {
-          duplicate_child = true;
-          break;
-        }
-      }
-      if (!duplicate_child)
-        url_resource->add_child_ids(child_resource->id());
-    }
-  }
-
-  return url_resource;
-}
-
-void ThreatDetails::AddDomElement(
-    const int frame_tree_node_id,
-    const int element_node_id,
-    const std::string& tagname,
-    const int parent_element_node_id,
-    const std::vector<mojom::AttributeNameValuePtr> attributes,
-    const std::string& inner_html,
-    const ClientSafeBrowsingReportRequest::Resource* resource) {
-  // Create the element. It should not exist already since this function should
-  // only be called once for each element.
-  const std::string element_key =
-      GetElementKey(frame_tree_node_id, element_node_id);
-  HTMLElement* cur_element = FindOrCreateElement(element_key);
-
-  // Set some basic metadata about the element.
-  const std::string tag_name_upper = base::ToUpperASCII(tagname);
-  if (!tag_name_upper.empty()) {
-    cur_element->set_tag(tag_name_upper);
-  }
-  for (const mojom::AttributeNameValuePtr& attribute : attributes) {
-    HTMLElement::Attribute* attribute_pb = cur_element->add_attribute();
-    attribute_pb->set_name(std::move(attribute->name));
-    attribute_pb->set_value(std::move(attribute->value));
-
-    // Remember which the IDs of elements that represent ads so we can trim the
-    // report down to just those parts later.
-    if (trim_to_ad_tags_ && attribute_pb->name() == "data-google-query-id") {
-      trimmed_dom_element_ids_.insert(cur_element->id());
-    }
-  }
-
-  if (!inner_html.empty()) {
-    cur_element->set_inner_html(inner_html);
-  }
-
-  if (resource) {
-    cur_element->set_resource_id(resource->id());
-  }
-
-  // Next we try to lookup the parent of the current element and add ourselves
-  // as a child of it.
-  HTMLElement* parent_element = nullptr;
-  if (parent_element_node_id == 0) {
-    // No parent indicates that this element is at the top of the current frame.
-    // Remember that this is a top-level element of the frame with the
-    // current |frame_tree_node_id|. If this element is inside an iframe, a
-    // second pass will insert this element as a child of its parent iframe.
-    frame_tree_id_to_children_map_[frame_tree_node_id].insert(
-        cur_element->id());
-  } else {
-    // We have a parent ID, so this element is just a child of something inside
-    // of our current frame. We can easily lookup our parent.
-    const std::string& parent_key =
-        GetElementKey(frame_tree_node_id, parent_element_node_id);
-    if (base::Contains(elements_, parent_key)) {
-      parent_element = elements_[parent_key].get();
-    }
-  }
-
-  // If a parent element was found, add ourselves as a child, ensuring not to
-  // duplicate child IDs.
-  if (parent_element) {
-    bool duplicate_child = false;
-    for (const int child_id : parent_element->child_ids()) {
-      if (child_id == cur_element->id()) {
-        duplicate_child = true;
-        break;
-      }
-    }
-    if (!duplicate_child) {
-      parent_element->add_child_ids(cur_element->id());
-    }
-  }
-}
-
-void ThreatDetails::StartCollection() {
-  DVLOG(1) << "Starting to compute threat details.";
-  report_.reset(new ClientSafeBrowsingReportRequest());
-
-  if (IsReportableUrl(resource_.url)) {
-    report_->set_url(resource_.url.spec());
-    report_->set_type(GetReportTypeFromSBThreatType(resource_.threat_type));
-  }
-
-  GURL referrer_url;
-  GURL page_url;
-
-  // With committed interstitials, the information is pre-filled into the
-  // UnsafeResource, since the navigation entry we have at this point is for the
-  // navigation to the interstitial, and the entry with the page details get
-  // destroyed when leaving the interstitial.
-  if (!resource_.navigation_url.is_empty()) {
-    DCHECK(
-        base::FeatureList::IsEnabled(safe_browsing::kCommittedSBInterstitials));
-    page_url = resource_.navigation_url;
-    referrer_url = resource_.referrer_url;
-  } else {
-    NavigationEntry* nav_entry = resource_.GetNavigationEntryForResource();
-    if (nav_entry) {
-      page_url = nav_entry->GetURL();
-      referrer_url = nav_entry->GetReferrer().url;
-    }
-  }
-
-  if (IsReportableUrl(page_url))
-    report_->set_page_url(page_url.spec());
-
-  if (IsReportableUrl(referrer_url))
-    report_->set_referrer_url(referrer_url.spec());
-
-  // Add the nodes, starting from the page url.
-  AddUrl(page_url, GURL(), std::string(), nullptr);
-
-  // Add the resource_url and its original url, if non-empty and different.
-  if (!resource_.original_url.is_empty() &&
-      resource_.url != resource_.original_url) {
-    // Add original_url, as the parent of resource_url.
-    AddUrl(resource_.original_url, GURL(), std::string(), nullptr);
-    AddUrl(resource_.url, resource_.original_url, std::string(), nullptr);
-  } else {
-    AddUrl(resource_.url, GURL(), std::string(), nullptr);
-  }
-
-  // Add the redirect urls, if non-empty. The redirect urls do not include the
-  // original url, but include the unsafe url which is the last one of the
-  // redirect urls chain
-  GURL parent_url;
-  // Set the original url as the parent of the first redirect url if it's not
-  // empty.
-  if (!resource_.original_url.is_empty())
-    parent_url = resource_.original_url;
-
-  // Set the previous redirect url as the parent of the next one
-  for (size_t i = 0; i < resource_.redirect_urls.size(); ++i) {
-    AddUrl(resource_.redirect_urls[i], parent_url, std::string(), nullptr);
-    parent_url = resource_.redirect_urls[i];
-  }
-
-  // Add the referrer url.
-  if (!referrer_url.is_empty())
-    AddUrl(referrer_url, GURL(), std::string(), nullptr);
-
-  if (!resource_.IsMainPageLoadBlocked()) {
-    // Get URLs of frames, scripts etc from the DOM.
-    // OnReceivedThreatDOMDetails will be called when the renderer replies.
-    // TODO(mattm): In theory, if the user proceeds through the warning DOM
-    // detail collection could be started once the page loads.
-    web_contents()->ForEachFrame(base::BindRepeating(
-        &ThreatDetails::RequestThreatDOMDetails, GetWeakPtr()));
-  }
-}
-
-void ThreatDetails::RequestThreatDOMDetails(content::RenderFrameHost* frame) {
-  content::BackForwardCache::DisableForRenderFrameHost(
-      frame, "safe_browsing::ThreatDetails");
-  mojo::Remote<safe_browsing::mojom::ThreatReporter> threat_reporter;
-  frame->GetRemoteInterfaces()->GetInterface(
-      threat_reporter.BindNewPipeAndPassReceiver());
-  safe_browsing::mojom::ThreatReporter* raw_threat_report =
-      threat_reporter.get();
-  pending_render_frame_hosts_.push_back(frame);
-  raw_threat_report->GetThreatDOMDetails(
-      base::BindOnce(&ThreatDetails::OnReceivedThreatDOMDetails, GetWeakPtr(),
-                     std::move(threat_reporter), frame));
-}
-
-// When the renderer is done, this is called.
-void ThreatDetails::OnReceivedThreatDOMDetails(
-    mojo::Remote<mojom::ThreatReporter> threat_reporter,
-    content::RenderFrameHost* sender,
-    std::vector<mojom::ThreatDOMDetailsNodePtr> params) {
-  // If the RenderFrameHost was closed between sending the IPC and this callback
-  // running, |sender| will be invalid.
-  const auto sender_it = std::find(pending_render_frame_hosts_.begin(),
-                                   pending_render_frame_hosts_.end(), sender);
-  if (sender_it == pending_render_frame_hosts_.end()) {
-    return;
-  }
-
-  pending_render_frame_hosts_.erase(sender_it);
-
-  // Lookup the FrameTreeNode ID of any child frames in the list of DOM nodes.
-  const int sender_process_id = sender->GetProcess()->GetID();
-  const int sender_frame_tree_node_id = sender->GetFrameTreeNodeId();
-  KeyToFrameTreeIdMap child_frame_tree_map;
-  for (const mojom::ThreatDOMDetailsNodePtr& node : params) {
-    if (node->child_frame_routing_id == 0)
-      continue;
-
-    const std::string cur_element_key =
-        GetElementKey(sender_frame_tree_node_id, node->node_id);
-    int child_frame_tree_node_id =
-        content::RenderFrameHost::GetFrameTreeNodeIdForRoutingId(
-            sender_process_id, node->child_frame_routing_id);
-    if (child_frame_tree_node_id !=
-        content::RenderFrameHost::kNoFrameTreeNodeId) {
-      child_frame_tree_map[cur_element_key] = child_frame_tree_node_id;
-    }
-  }
-
-  AddDOMDetails(sender_frame_tree_node_id, std::move(params),
-                child_frame_tree_map);
-}
-
-void ThreatDetails::AddDOMDetails(
-    const int frame_tree_node_id,
-    std::vector<mojom::ThreatDOMDetailsNodePtr> params,
-    const KeyToFrameTreeIdMap& child_frame_tree_map) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DVLOG(1) << "Nodes from the DOM: " << params.size();
-
-  // If we have already started getting redirects from history service,
-  // don't modify state, otherwise will invalidate the iterators.
-  if (redirects_collector_->HasStarted())
-    return;
-
-  // If we have already started collecting data from the HTTP cache, don't
-  // modify our state.
-  if (cache_collector_->HasStarted())
-    return;
-
-  // Exit early if there are no nodes to process.
-  if (params.empty())
-    return;
-
-  // Copy FrameTreeNode IDs for the child frame into the combined mapping.
-  iframe_key_to_frame_tree_id_map_.insert(child_frame_tree_map.begin(),
-                                          child_frame_tree_map.end());
-
-  // Add the urls from the DOM to |resources_|. The renderer could be sending
-  // bogus messages, so limit the number of nodes we accept.
-  // Also update |elements_| with the DOM structure.
-  for (size_t i = 0; i < params.size() && i < kMaxDomNodes; ++i) {
-    mojom::ThreatDOMDetailsNode& node = *params[i];
-    DVLOG(1) << node.url << ", " << node.tag_name << ", " << node.parent;
-    ClientSafeBrowsingReportRequest::Resource* resource = nullptr;
-    if (!node.url.is_empty()) {
-      resource = AddUrl(node.url, node.parent, node.tag_name, &(node.children));
-    }
-    // Check for a tag_name to avoid adding the summary node to the DOM.
-    if (!node.tag_name.empty()) {
-      AddDomElement(frame_tree_node_id, node.node_id, node.tag_name,
-                    node.parent_node_id, std::move(node.attributes),
-                    node.inner_html, resource);
-    }
-  }
-}
-
-// Called from the SB Service on the IO thread, after the user has
-// closed the tab, or clicked proceed or goback.  Since the user needs
-// to take an action, we expect this to be called after
-// OnReceivedThreatDOMDetails in most cases. If not, we don't include
-// the DOM data in our report.
-void ThreatDetails::FinishCollection(bool did_proceed, int num_visit) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-  all_done_expected_ = true;
-
-  // Do a second pass over the elements and update iframe elements to have
-  // references to their children. Children may have been received from a
-  // different renderer than the iframe element.
-  for (auto& element_pair : elements_) {
-    const std::string& element_key = element_pair.first;
-    HTMLElement* element = element_pair.second.get();
-    if (base::Contains(iframe_key_to_frame_tree_id_map_, element_key)) {
-      int frame_tree_id_of_iframe_renderer =
-          iframe_key_to_frame_tree_id_map_[element_key];
-      const std::unordered_set<int>& child_ids =
-          frame_tree_id_to_children_map_[frame_tree_id_of_iframe_renderer];
-      for (const int child_id : child_ids) {
-        element->add_child_ids(child_id);
-      }
-    }
-  }
-
-  did_proceed_ = did_proceed;
-  num_visits_ = num_visit;
-  std::vector<GURL> urls;
-  for (ResourceMap::const_iterator it = resources_.begin();
-       it != resources_.end(); ++it) {
-    urls.push_back(GURL(it->first));
-  }
-  redirects_collector_->StartHistoryCollection(
-      urls, base::BindOnce(&ThreatDetails::OnRedirectionCollectionReady,
-                           GetWeakPtr()));
-}
-
-void ThreatDetails::OnRedirectionCollectionReady() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  const std::vector<RedirectChain>& redirects =
-      redirects_collector_->GetCollectedUrls();
-
-  for (size_t i = 0; i < redirects.size(); ++i)
-    AddRedirectUrlList(redirects[i]);
-
-  // Call the cache collector
-  cache_collector_->StartCacheCollection(
-      url_loader_factory_, &resources_, &cache_result_,
-      base::BindOnce(&ThreatDetails::OnCacheCollectionReady, GetWeakPtr()));
-}
-
-void ThreatDetails::AddRedirectUrlList(const std::vector<GURL>& urls) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  for (size_t i = 0; i < urls.size() - 1; ++i) {
-    AddUrl(urls[i], urls[i + 1], std::string(), nullptr);
-  }
-}
-
-void ThreatDetails::OnCacheCollectionReady() {
-  DVLOG(1) << "OnCacheCollectionReady.";
-
-  // All URLs have been collected, trim the report if necessary.
-  if (trim_to_ad_tags_) {
-    TrimElements(trimmed_dom_element_ids_, &elements_, &resources_);
-    // If trimming the report removed all the elements then don't bother
-    // sending it.
-    if (elements_.empty()) {
-      AllDone();
-      return;
-    }
-  }
-  // Add all the urls in our |resources_| maps to the |report_| protocol buffer.
-  for (auto& resource_pair : resources_) {
-    ClientSafeBrowsingReportRequest::Resource* pb_resource =
-        report_->add_resources();
-    pb_resource->Swap(resource_pair.second.get());
-    const GURL url(pb_resource->url());
-    if (url.SchemeIs("https")) {
-      // Sanitize the HTTPS resource by clearing out private data (like cookie
-      // headers).
-      DVLOG(1) << "Clearing out HTTPS resource: " << pb_resource->url();
-      ClearHttpsResource(pb_resource);
-      // Keep id, parent_id, child_ids, and tag_name.
-    }
-  }
-  for (auto& element_pair : elements_) {
-    report_->add_dom()->Swap(element_pair.second.get());
-  }
-
-  report_->set_did_proceed(did_proceed_);
-  // Only sets repeat_visit if num_visits_ >= 0.
-  if (num_visits_ >= 0) {
-    report_->set_repeat_visit(num_visits_ > 0);
-  }
-  report_->set_complete(cache_result_);
-
-  report_->mutable_client_properties()->set_url_api_type(
-      GetUrlApiTypeForThreatSource(resource_.threat_source));
-
-  // Fill the referrer chain if applicable.
-  MaybeFillReferrerChain();
-
-  // Send the report, using the SafeBrowsingService.
-  std::string serialized;
-  if (!report_->SerializeToString(&serialized)) {
-    DLOG(ERROR) << "Unable to serialize the threat report.";
-    AllDone();
-    return;
-  }
-
-  base::PostTask(
-      FROM_HERE, {content::BrowserThread::UI},
-      base::BindOnce(&WebUIInfoSingleton::AddToCSBRRsSent,
-                     base::Unretained(WebUIInfoSingleton::GetInstance()),
-                     std::move(report_)));
-
-  ui_manager_->SendSerializedThreatDetails(serialized);
-
-  AllDone();
-}
-
-void ThreatDetails::MaybeFillReferrerChain() {
-  if (!referrer_chain_provider_)
-    return;
-
-  if (!report_ ||
-      (report_->type() != ClientSafeBrowsingReportRequest::URL_SUSPICIOUS &&
-       report_->type() != ClientSafeBrowsingReportRequest::APK_DOWNLOAD)) {
-    return;
-  }
-
-  referrer_chain_provider_->IdentifyReferrerChainByWebContents(
-      web_contents(), kThreatDetailsUserGestureLimit,
-      report_->mutable_referrer_chain());
-}
-
-void ThreatDetails::AllDone() {
-  is_all_done_ = true;
-  base::PostTask(FROM_HERE, {content::BrowserThread::UI},
-                 base::BindOnce(std::move(done_callback_),
-                                base::Unretained(web_contents())));
-}
-
-void ThreatDetails::FrameDeleted(RenderFrameHost* render_frame_host) {
-  auto render_frame_host_it =
-      std::find(pending_render_frame_hosts_.begin(),
-                pending_render_frame_hosts_.end(), render_frame_host);
-  if (render_frame_host_it != pending_render_frame_hosts_.end()) {
-    pending_render_frame_hosts_.erase(render_frame_host_it);
-  }
-}
-
-void ThreatDetails::RenderFrameHostChanged(RenderFrameHost* old_host,
-                                           RenderFrameHost* new_host) {
-  FrameDeleted(old_host);
-}
-
-base::WeakPtr<ThreatDetails> ThreatDetails::GetWeakPtr() {
-  return weak_factory_.GetWeakPtr();
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/browser/threat_details.h b/components/safe_browsing/browser/threat_details.h
deleted file mode 100644
index 8855bd0..0000000
--- a/components/safe_browsing/browser/threat_details.h
+++ /dev/null
@@ -1,307 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_BROWSER_THREAT_DETAILS_H_
-#define COMPONENTS_SAFE_BROWSING_BROWSER_THREAT_DETAILS_H_
-
-// A class that encapsulates the detailed threat reports sent when
-// users opt-in to do so from the safe browsing warning page.
-
-// An instance of this class is generated when a safe browsing warning page
-// is shown (SafeBrowsingBlockingPage).
-
-#include <memory>
-#include <string>
-#include <unordered_map>
-#include <unordered_set>
-#include <vector>
-
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "components/safe_browsing/common/safe_browsing.mojom.h"
-#include "components/safe_browsing/proto/csd.pb.h"
-#include "components/security_interstitials/content/unsafe_resource.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/web_contents_observer.h"
-#include "mojo/public/cpp/bindings/remote.h"
-
-namespace history {
-class HistoryService;
-}  // namespace history
-
-namespace network {
-class SharedURLLoaderFactory;
-}  // namespace network
-
-namespace safe_browsing {
-
-class BaseUIManager;
-class ReferrerChainProvider;
-
-// Maps a URL to its Resource.
-class ThreatDetailsCacheCollector;
-class ThreatDetailsRedirectsCollector;
-class ThreatDetailsFactory;
-
-using ResourceMap = std::unordered_map<
-    std::string,
-    std::unique_ptr<ClientSafeBrowsingReportRequest::Resource>>;
-
-// Maps a key of an HTML element to its corresponding HTMLElement proto message.
-// HTML Element keys have the form "<frame_id>-<node_id>", where |frame_id| is
-// the FrameTreeNode ID of the frame containing the element, and
-// |node_id| is a sequential ID for the element generated by the renderer.
-using ElementMap =
-    std::unordered_map<std::string, std::unique_ptr<HTMLElement>>;
-
-// Maps the key of an iframe element to the FrameTreeNode ID of the frame that
-// rendered the contents of the iframe.
-using KeyToFrameTreeIdMap = std::unordered_map<std::string, int>;
-
-// Maps a FrameTreeNode ID of a frame to a set of child IDs. The child IDs are
-// the Element IDs of the top-level HTML Elements in this frame.
-using FrameTreeIdToChildIdsMap =
-    std::unordered_map<int, std::unordered_set<int>>;
-
-// Callback used to notify a caller that ThreatDetails has finished creating and
-// sending a report.
-using ThreatDetailsDoneCallback =
-    base::OnceCallback<void(content::WebContents*)>;
-
-class ThreatDetails : public content::WebContentsObserver {
- public:
-  typedef security_interstitials::UnsafeResource UnsafeResource;
-
-  ~ThreatDetails() override;
-
-  // Constructs a new ThreatDetails instance, using the factory.
-  static std::unique_ptr<ThreatDetails> NewThreatDetails(
-      BaseUIManager* ui_manager,
-      content::WebContents* web_contents,
-      const UnsafeResource& resource,
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      history::HistoryService* history_service,
-      ReferrerChainProvider* referrer_chain_provider,
-      bool trim_to_ad_tags,
-      ThreatDetailsDoneCallback done_callback);
-
-  // Makes the passed |factory| the factory used to instantiate
-  // SafeBrowsingBlockingPage objects. Useful for tests.
-  static void RegisterFactory(ThreatDetailsFactory* factory) {
-    factory_ = factory;
-  }
-
-  // The SafeBrowsingBlockingPage calls this from the IO thread when
-  // the user is leaving the blocking page and has opted-in to sending
-  // the report. We start the redirection urls collection from history service
-  // in UI thread; then do cache collection back in IO thread. We also record
-  // if the user did proceed with the warning page, and how many times user
-  // visited this page before. When we are done, we send the report.
-  virtual void FinishCollection(bool did_proceed, int num_visits);
-
-  void OnCacheCollectionReady();
-
-  void OnRedirectionCollectionReady();
-
-  // WebContentsObserver implementation:
-  void FrameDeleted(content::RenderFrameHost* render_frame_host) override;
-  void RenderFrameHostChanged(content::RenderFrameHost* old_host,
-                              content::RenderFrameHost* new_host) override;
-
-  base::WeakPtr<ThreatDetails> GetWeakPtr();
-
- protected:
-  friend class ThreatDetailsFactoryImpl;
-  friend class TestThreatDetailsFactory;
-  friend class ThreatDetailsTest;
-
-  ThreatDetails(
-      BaseUIManager* ui_manager,
-      content::WebContents* web_contents,
-      const UnsafeResource& resource,
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      history::HistoryService* history_service,
-      ReferrerChainProvider* referrer_chain_provider,
-      bool trim_to_ad_tags,
-      ThreatDetailsDoneCallback done_callback);
-
-  // Default constructor for testing only.
-  ThreatDetails();
-
-  virtual void AddDOMDetails(const int frame_tree_node_id,
-                             std::vector<mojom::ThreatDOMDetailsNodePtr> params,
-                             const KeyToFrameTreeIdMap& child_frame_tree_map);
-
-  // The report protocol buffer.
-  std::unique_ptr<ClientSafeBrowsingReportRequest> report_;
-
-  // Used to get a pointer to the HTTP cache.
-  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
-
-  // Starts the collection of the report.
-  void StartCollection();
-
- private:
-  // Whether the url is "public" so we can add it to the report.
-  bool IsReportableUrl(const GURL& url) const;
-
-  // Finds an existing Resource for the given url, or creates a new one if not
-  // found, and adds it to |resources_|. Returns the found/created resource.
-  ClientSafeBrowsingReportRequest::Resource* FindOrCreateResource(
-      const GURL& url);
-
-  // Finds an existing HTMLElement for a given key, or creates a new one if not
-  // found and adds it to |elements_|. Returns the found/created element.
-  HTMLElement* FindOrCreateElement(const std::string& element_key);
-
-  // Adds a Resource to resources_ with the given parent-child
-  // relationship. |parent| and |tagname| can be empty, |children| can be NULL.
-  // Returns the Resource that was affected, or null if no work was done.
-  ClientSafeBrowsingReportRequest::Resource* AddUrl(
-      const GURL& url,
-      const GURL& parent,
-      const std::string& tagname,
-      const std::vector<GURL>* children);
-
-  void RequestThreatDOMDetails(content::RenderFrameHost* frame);
-
-  void OnReceivedThreatDOMDetails(
-      mojo::Remote<mojom::ThreatReporter> threat_reporter,
-      content::RenderFrameHost* sender,
-      std::vector<mojom::ThreatDOMDetailsNodePtr> params);
-
-  void AddRedirectUrlList(const std::vector<GURL>& urls);
-
-  // Adds an HTML Element to the DOM structure. |frame_tree_node_id| is the
-  // unique ID of the frame the element came from. |element_node_id| is a unique
-  // ID of the element within the frame. |tag_name| is the tag of the element.
-  // |parent_element_node_id| is the unique ID of the parent element within the
-  // frame. |attributes| contains the names and values of the element's
-  // attributes. |inner_html| is set if the element contains inline JavaScript.
-  // |resource| is set if this element is a resource.
-  void AddDomElement(const int frame_tree_node_id,
-                     const int element_node_id,
-                     const std::string& tag_name,
-                     const int parent_element_node_id,
-                     const std::vector<mojom::AttributeNameValuePtr> attributes,
-                     const std::string& inner_html,
-                     const ClientSafeBrowsingReportRequest::Resource* resource);
-
-  // Populates the referrer chain data in |report_|. This may be skipped if the
-  // referrer chain provider isn't available, or the type of report doesn't
-  // include the referrer chain.
-  void MaybeFillReferrerChain();
-
-  // Called when the report is complete. Runs |done_callback_|.
-  void AllDone();
-
-  scoped_refptr<BaseUIManager> ui_manager_;
-
-  const UnsafeResource resource_;
-
-  ReferrerChainProvider* referrer_chain_provider_;
-
-  // For every Url we collect we create a Resource message. We keep
-  // them in a map so we can avoid duplicates.
-  ResourceMap resources_;
-
-  // Store all HTML elements collected, keep them in a map for easy lookup.
-  ElementMap elements_;
-
-  // For each iframe element encountered we map the key of the iframe to the
-  // FrameTreeNode ID of the frame containing the contents of that iframe.
-  // We populate this map when receiving results from ThreatDomDetails, and use
-  // it in a second pass (after FinishCollection) to attach children to iframe
-  // elements.
-  // Should only be accessed on the IO thread.
-  KeyToFrameTreeIdMap iframe_key_to_frame_tree_id_map_;
-
-  // When getting a set of elements from a frame, we store the frame's
-  // FrameTreeNode ID and a collection of all top-level elements in that frame.
-  // It is populated as we receive sets of nodes from different renderers.
-  // It is used together with |iframe_key_to_frame_tree_id_map_| in a second
-  // pass to insert child elements under their parent iframe elements.
-  FrameTreeIdToChildIdsMap frame_tree_id_to_children_map_;
-
-  // Result from the cache extractor.
-  bool cache_result_;
-
-  // Whether user did proceed with the safe browsing blocking page or
-  // not.
-  bool did_proceed_;
-
-  // How many times this user has visited this page before.
-  int num_visits_;
-
-  // Whether this report should be trimmed down to only ad tags, not the entire
-  // page contents. Used for sampling ads.
-  bool trim_to_ad_tags_;
-
-  // A vector containing the IDs of the DOM Elements to trim to. If an element
-  // ID is in this list, then its siblings and its children should be included
-  // in the report. Only populated if this report will be trimmed.
-  std::set<int> trimmed_dom_element_ids_;
-
-  // The factory used to instantiate SafeBrowsingBlockingPage objects.
-  // Useful for tests, so they can provide their own implementation of
-  // SafeBrowsingBlockingPage.
-  static ThreatDetailsFactory* factory_;
-
-  // Used to collect details from the HTTP Cache.
-  scoped_refptr<ThreatDetailsCacheCollector> cache_collector_;
-
-  // Used to collect redirect urls from the history service
-  scoped_refptr<ThreatDetailsRedirectsCollector> redirects_collector_;
-
-  // Callback to run when the report is finished.
-  ThreatDetailsDoneCallback done_callback_;
-
-  // Whether this ThreatDetails has begun finalizing the report and is expected
-  // to invoke |done_callback_| when it finishes.
-  bool all_done_expected_;
-
-  // Whether the |done_callback_| has been invoked.
-  bool is_all_done_;
-
-  // The set of RenderFrameHosts that have pending requests and haven't been
-  // deleted.
-  std::vector<content::RenderFrameHost*> pending_render_frame_hosts_;
-
-  // Used for references to |this| bound in callbacks.
-  base::WeakPtrFactory<ThreatDetails> weak_factory_{this};
-
-  FRIEND_TEST_ALL_PREFIXES(ThreatDetailsTest, HistoryServiceUrls);
-  FRIEND_TEST_ALL_PREFIXES(ThreatDetailsTest, HttpsResourceSanitization);
-  FRIEND_TEST_ALL_PREFIXES(ThreatDetailsTest, HTTPCacheNoEntries);
-  FRIEND_TEST_ALL_PREFIXES(ThreatDetailsTest, HTTPCache);
-  FRIEND_TEST_ALL_PREFIXES(ThreatDetailsTest, ThreatDOMDetails_AmbiguousDOM);
-  FRIEND_TEST_ALL_PREFIXES(ThreatDetailsTest,
-                           ThreatDOMDetails_EmptyReportNotSent);
-  FRIEND_TEST_ALL_PREFIXES(ThreatDetailsTest, ThreatDOMDetails_MultipleFrames);
-  FRIEND_TEST_ALL_PREFIXES(ThreatDetailsTest, ThreatDOMDetails_TrimToAdTags);
-  FRIEND_TEST_ALL_PREFIXES(ThreatDetailsTest, ThreatDOMDetails);
-
-  DISALLOW_COPY_AND_ASSIGN(ThreatDetails);
-};
-
-// Factory for creating ThreatDetails.  Useful for tests.
-class ThreatDetailsFactory {
- public:
-  virtual ~ThreatDetailsFactory() {}
-
-  virtual std::unique_ptr<ThreatDetails> CreateThreatDetails(
-      BaseUIManager* ui_manager,
-      content::WebContents* web_contents,
-      const security_interstitials::UnsafeResource& unsafe_resource,
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      history::HistoryService* history_service,
-      ReferrerChainProvider* referrer_chain_provider,
-      bool trim_to_ad_tags,
-      ThreatDetailsDoneCallback done_callback) = 0;
-};
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_BROWSER_THREAT_DETAILS_H_
diff --git a/components/safe_browsing/browser/threat_details_cache.cc b/components/safe_browsing/browser/threat_details_cache.cc
deleted file mode 100644
index b5a6b7f..0000000
--- a/components/safe_browsing/browser/threat_details_cache.cc
+++ /dev/null
@@ -1,241 +0,0 @@
-// Copyright (c) 2012 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.
-//
-// Implementation of the ThreatDetails class.
-
-#include "components/safe_browsing/browser/threat_details.h"
-
-#include <stdint.h>
-
-#include "base/bind.h"
-#include "base/hash/md5.h"
-#include "base/lazy_instance.h"
-#include "base/strings/string_util.h"
-#include "base/task/post_task.h"
-#include "components/safe_browsing/browser/threat_details_cache.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
-#include "net/base/ip_endpoint.h"
-#include "net/base/load_flags.h"
-#include "net/base/net_errors.h"
-#include "net/http/http_response_headers.h"
-#include "net/traffic_annotation/network_traffic_annotation.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "services/network/public/cpp/simple_url_loader.h"
-
-using content::BrowserThread;
-
-// Only send small files for now, a better strategy would use the size
-// of the whole report and the user's bandwidth.
-static const uint32_t kMaxBodySizeBytes = 1024;
-
-namespace safe_browsing {
-
-ThreatDetailsCacheCollector::ThreatDetailsCacheCollector()
-    : resources_(nullptr), result_(nullptr), has_started_(false) {}
-
-void ThreatDetailsCacheCollector::StartCacheCollection(
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    ResourceMap* resources,
-    bool* result,
-    base::OnceClosure callback) {
-  // Start the data collection from the HTTP cache. We use a URLFetcher
-  // and set the right flags so we only hit the cache.
-  DVLOG(1) << "Getting cache data for all urls...";
-  url_loader_factory_ = url_loader_factory;
-  resources_ = resources;
-  resources_it_ = resources_->begin();
-  result_ = result;
-  callback_ = std::move(callback);
-  has_started_ = true;
-
-  // Post a task in the message loop, so the callers don't need to
-  // check if we call their callback immediately.
-  base::PostTask(FROM_HERE, {BrowserThread::UI},
-                 base::BindOnce(&ThreatDetailsCacheCollector::OpenEntry, this));
-}
-
-bool ThreatDetailsCacheCollector::HasStarted() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  return has_started_;
-}
-
-ThreatDetailsCacheCollector::~ThreatDetailsCacheCollector() {}
-
-// Fetch a URL and advance to the next one when done.
-void ThreatDetailsCacheCollector::OpenEntry() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DVLOG(1) << "OpenEntry";
-
-  if (resources_it_ == resources_->end()) {
-    AllDone(true);
-    return;
-  }
-
-  if (!url_loader_factory_) {
-    DVLOG(1) << "Missing URLLoaderFactory";
-    AllDone(false);
-    return;
-  }
-
-  net::NetworkTrafficAnnotationTag traffic_annotation =
-      net::DefineNetworkTrafficAnnotation("safe_browsing_cache_collector", R"(
-        semantics {
-          sender: "Threat Details Cache Collector"
-          description:
-            "This request fetches different items from safe browsing cache "
-            "and DOES NOT make an actual network request."
-          trigger:
-            "When safe browsing extended report is collecting data."
-          data:
-            "None"
-          destination: OTHER
-        }
-        policy {
-          cookies_allowed: NO
-          setting:
-            "Users can enable or disable this feature by stopping sending "
-            "security incident reports to Google via disabling 'Automatically "
-            "report details of possible security incidents to Google.' in "
-            "Chrome's settings under Advanced Settings, Privacy. The feature "
-            "is disabled by default."
-          chrome_policy {
-            SafeBrowsingExtendedReportingOptInAllowed {
-              policy_options {mode: MANDATORY}
-              SafeBrowsingExtendedReportingOptInAllowed: false
-            }
-          }
-        })");
-
-  auto resource_request = std::make_unique<network::ResourceRequest>();
-  resource_request->url = GURL(resources_it_->first);
-  // Only from cache, and don't use cookies.
-  resource_request->load_flags =
-      net::LOAD_ONLY_FROM_CACHE | net::LOAD_SKIP_CACHE_VALIDATION;
-  resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
-  current_load_ = network::SimpleURLLoader::Create(std::move(resource_request),
-                                                   traffic_annotation);
-  current_load_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
-      url_loader_factory_.get(),
-      base::BindOnce(&ThreatDetailsCacheCollector::OnURLLoaderComplete,
-                     base::Unretained(this)));
-}
-
-ClientSafeBrowsingReportRequest::Resource*
-ThreatDetailsCacheCollector::GetResource(const GURL& url) {
-  auto it = resources_->find(url.spec());
-  if (it != resources_->end()) {
-    return it->second.get();
-  }
-  return nullptr;
-}
-
-void ThreatDetailsCacheCollector::OnURLLoaderComplete(
-    std::unique_ptr<std::string> response_body) {
-  DVLOG(1) << "OnURLLoaderComplete";
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK(current_load_);
-  if (current_load_->NetError() == net::ERR_CACHE_MISS) {
-    // Cache miss, skip this resource.
-    DVLOG(1) << "Cache miss for url: " << current_load_->GetFinalURL();
-    AdvanceEntry();
-    return;
-  }
-
-  if (current_load_->NetError() != net::OK) {
-    // Some other error occurred, e.g. the request could have been cancelled.
-    DVLOG(1) << "Unsuccessful fetch: " << current_load_->GetFinalURL();
-    AdvanceEntry();
-    return;
-  }
-
-  // Set the response headers and body to the right resource, which
-  // might not be the same as the one we asked for.
-  // For redirects, resources_it_->first != url.spec().
-  ClientSafeBrowsingReportRequest::Resource* resource =
-      GetResource(current_load_->GetFinalURL());
-  if (!resource) {
-    DVLOG(1) << "Cannot find resource for url:" << current_load_->GetFinalURL();
-    AdvanceEntry();
-    return;
-  }
-
-  ReadResponse(resource);
-  std::string data;
-  if (response_body)
-    data = *response_body;
-  ReadData(resource, data);
-  AdvanceEntry();
-}
-
-void ThreatDetailsCacheCollector::ReadResponse(
-    ClientSafeBrowsingReportRequest::Resource* pb_resource) {
-  DVLOG(1) << "ReadResponse";
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (!current_load_->ResponseInfo() ||
-      !current_load_->ResponseInfo()->headers) {
-    DVLOG(1) << "Missing response headers.";
-    return;
-  }
-  net::HttpResponseHeaders* headers =
-      current_load_->ResponseInfo()->headers.get();
-
-  ClientSafeBrowsingReportRequest::HTTPResponse* pb_response =
-      pb_resource->mutable_response();
-  pb_response->mutable_firstline()->set_code(headers->response_code());
-  size_t iter = 0;
-  std::string name, value;
-  while (headers->EnumerateHeaderLines(&iter, &name, &value)) {
-    // Strip any Set-Cookie headers.
-    if (base::LowerCaseEqualsASCII(name, "set-cookie"))
-      continue;
-    ClientSafeBrowsingReportRequest::HTTPHeader* pb_header =
-        pb_response->add_headers();
-    pb_header->set_name(name);
-    pb_header->set_value(value);
-  }
-
-  bool was_fetched_via_proxy =
-      current_load_->ResponseInfo()->proxy_server.is_valid() &&
-      !current_load_->ResponseInfo()->proxy_server.is_direct();
-  if (!was_fetched_via_proxy) {
-    pb_response->set_remote_ip(
-        current_load_->ResponseInfo()->remote_endpoint.ToString());
-  }
-}
-
-void ThreatDetailsCacheCollector::ReadData(
-    ClientSafeBrowsingReportRequest::Resource* pb_resource,
-    const std::string& data) {
-  DVLOG(1) << "ReadData";
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  ClientSafeBrowsingReportRequest::HTTPResponse* pb_response =
-      pb_resource->mutable_response();
-  if (data.size() <= kMaxBodySizeBytes) {  // Only send small bodies for now.
-    pb_response->set_body(data);
-  }
-  pb_response->set_bodylength(data.size());
-  pb_response->set_bodydigest(base::MD5String(data));
-}
-
-void ThreatDetailsCacheCollector::AdvanceEntry() {
-  DVLOG(1) << "AdvanceEntry";
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  // Advance to the next resource.
-  ++resources_it_;
-  current_load_.reset();
-
-  // Create a task so we don't take over the UI thread for too long.
-  base::PostTask(FROM_HERE, {BrowserThread::UI},
-                 base::BindOnce(&ThreatDetailsCacheCollector::OpenEntry, this));
-}
-
-void ThreatDetailsCacheCollector::AllDone(bool success) {
-  DVLOG(1) << "AllDone";
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  *result_ = success;
-  base::PostTask(FROM_HERE, {BrowserThread::UI}, std::move(callback_));
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/browser/threat_details_cache.h b/components/safe_browsing/browser/threat_details_cache.h
deleted file mode 100644
index b026ff26..0000000
--- a/components/safe_browsing/browser/threat_details_cache.h
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_BROWSER_THREAT_DETAILS_CACHE_H_
-#define COMPONENTS_SAFE_BROWSING_BROWSER_THREAT_DETAILS_CACHE_H_
-
-// A class that gets threat details from the HTTP Cache.
-// An instance of this class is generated by ThreatDetails.
-
-#include <string>
-#include <unordered_map>
-#include <vector>
-
-#include "base/callback.h"
-#include "base/memory/ref_counted.h"
-
-namespace network {
-class SimpleURLLoader;
-class SharedURLLoaderFactory;
-}
-
-namespace safe_browsing {
-
-// Maps a URL to its Resource.
-typedef std::unordered_map<
-    std::string,
-    std::unique_ptr<ClientSafeBrowsingReportRequest::Resource>>
-    ResourceMap;
-
-class ThreatDetailsCacheCollector
-    : public base::RefCounted<ThreatDetailsCacheCollector> {
- public:
-  ThreatDetailsCacheCollector();
-
-  // We use |request_context_getter|, we modify |resources| and
-  // |result|, and we call |callback|, so they must all remain alive
-  // for the lifetime of this object.
-  void StartCacheCollection(
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      ResourceMap* resources,
-      bool* result,
-      base::OnceClosure callback);
-
-  // Returns whether or not StartCacheCollection has been called.
-  bool HasStarted();
-
- protected:
-  // Called after the request completes (either successfully or with failure).
-  void OnURLLoaderComplete(std::unique_ptr<std::string> response_body);
-
- private:
-  friend class base::RefCounted<ThreatDetailsCacheCollector>;
-
-  ~ThreatDetailsCacheCollector();
-
-  // Points to the url for which we are fetching the HTTP cache entry or
-  // redirect chain.
-  ResourceMap::iterator resources_it_;
-
-  // Points to the resources_ map in the ThreatDetails.
-  ResourceMap* resources_;
-
-  // Points to the cache_result_ in the ThreatDetails.
-  bool* result_;
-
-  // Method we call when we are done. The caller must be alive for the
-  // whole time, we are modifying its state (see above).
-  base::OnceClosure callback_;
-
-  // Set to true as soon as StartCacheCollection is called.
-  bool has_started_;
-
-  // Used to get a pointer to the current URLLoaderFactory.
-  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
-
-  // The current SimpleURLLoader.
-  std::unique_ptr<network::SimpleURLLoader> current_load_;
-
-  // Returns the resource from resources_ that corresponds to |url|
-  ClientSafeBrowsingReportRequest::Resource* GetResource(const GURL& url);
-
-  // Creates a new URLFetcher and starts it.
-  void OpenEntry();
-
-  // Read the HTTP response from |current_load_| and add it to |pb_resource|.
-  void ReadResponse(ClientSafeBrowsingReportRequest::Resource* pb_resource);
-
-  // Read the body |data| and add it to |pb_resource|.
-  void ReadData(ClientSafeBrowsingReportRequest::Resource* pb_resource,
-                const std::string& data);
-
-  // Called when we are done.
-  void AllDone(bool success);
-
-  // Advances to the next entry in resources_it_.
-  void AdvanceEntry();
-};
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_BROWSER_THREAT_DETAILS_CACHE_H_
diff --git a/components/safe_browsing/browser/threat_details_history.cc b/components/safe_browsing/browser/threat_details_history.cc
deleted file mode 100644
index e41e03e..0000000
--- a/components/safe_browsing/browser/threat_details_history.cc
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright (c) 2012 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.
-//
-// Implementation of the ThreatDetailsRedirectsCollector class.
-
-#include "components/safe_browsing/browser/threat_details_history.h"
-
-#include <stddef.h>
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/task/post_task.h"
-#include "components/safe_browsing/browser/threat_details.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
-
-using content::BrowserThread;
-
-namespace safe_browsing {
-
-ThreatDetailsRedirectsCollector::ThreatDetailsRedirectsCollector(
-    const base::WeakPtr<history::HistoryService>& history_service)
-    : has_started_(false), history_service_(history_service) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-  if (history_service) {
-    history_service_observer_.Add(history_service.get());
-  }
-}
-
-void ThreatDetailsRedirectsCollector::StartHistoryCollection(
-    const std::vector<GURL>& urls,
-    base::OnceClosure callback) {
-  DVLOG(1) << "Num of urls to check in history service: " << urls.size();
-  has_started_ = true;
-  callback_ = std::move(callback);
-
-  if (urls.size() == 0) {
-    AllDone();
-    return;
-  }
-
-  base::PostTask(
-      FROM_HERE, {BrowserThread::UI},
-      base::BindOnce(&ThreatDetailsRedirectsCollector::StartGetRedirects, this,
-                     urls));
-}
-
-bool ThreatDetailsRedirectsCollector::HasStarted() const {
-  return has_started_;
-}
-
-const std::vector<RedirectChain>&
-ThreatDetailsRedirectsCollector::GetCollectedUrls() const {
-  return redirects_urls_;
-}
-
-ThreatDetailsRedirectsCollector::~ThreatDetailsRedirectsCollector() {}
-
-void ThreatDetailsRedirectsCollector::StartGetRedirects(
-    const std::vector<GURL>& urls) {
-  // History access from profile needs to happen in UI thread
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  for (size_t i = 0; i < urls.size(); ++i) {
-    urls_.push_back(urls[i]);
-  }
-  urls_it_ = urls_.begin();
-  GetRedirects(*urls_it_);
-}
-
-void ThreatDetailsRedirectsCollector::GetRedirects(const GURL& url) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-  if (!history_service_) {
-    AllDone();
-    return;
-  }
-
-  history_service_->QueryRedirectsTo(
-      url,
-      base::BindOnce(&ThreatDetailsRedirectsCollector::OnGotQueryRedirectsTo,
-                     base::Unretained(this), url),
-      &request_tracker_);
-}
-
-void ThreatDetailsRedirectsCollector::OnGotQueryRedirectsTo(
-    const GURL& url,
-    history::RedirectList redirect_list) {
-  if (!redirect_list.empty()) {
-    std::vector<GURL> urllist;
-    urllist.push_back(url);
-    urllist.insert(urllist.end(), redirect_list.begin(), redirect_list.end());
-    redirects_urls_.push_back(urllist);
-  }
-
-  // Proceed to next url
-  ++urls_it_;
-
-  if (urls_it_ == urls_.end()) {
-    AllDone();
-    return;
-  }
-
-  GetRedirects(*urls_it_);
-}
-
-void ThreatDetailsRedirectsCollector::AllDone() {
-  DVLOG(1) << "AllDone";
-  base::PostTask(FROM_HERE, {BrowserThread::UI}, std::move(callback_));
-}
-
-void ThreatDetailsRedirectsCollector::HistoryServiceBeingDeleted(
-    history::HistoryService* history_service) {
-  history_service_observer_.Remove(history_service);
-  history_service_.reset();
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/browser/threat_details_history.h b/components/safe_browsing/browser/threat_details_history.h
deleted file mode 100644
index 5c1f9a0..0000000
--- a/components/safe_browsing/browser/threat_details_history.h
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_BROWSER_THREAT_DETAILS_HISTORY_H_
-#define COMPONENTS_SAFE_BROWSING_BROWSER_THREAT_DETAILS_HISTORY_H_
-
-// This class gets redirect chain for urls from the history service.
-
-#include <string>
-#include <vector>
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/scoped_observer.h"
-#include "base/sequenced_task_runner_helpers.h"
-#include "base/task/cancelable_task_tracker.h"
-#include "components/history/core/browser/history_service.h"
-#include "components/history/core/browser/history_service_observer.h"
-#include "content/public/browser/browser_thread.h"
-
-namespace safe_browsing {
-
-typedef std::vector<GURL> RedirectChain;
-
-class ThreatDetailsRedirectsCollector
-    : public base::RefCounted<ThreatDetailsRedirectsCollector>,
-      public history::HistoryServiceObserver {
- public:
-  explicit ThreatDetailsRedirectsCollector(
-      const base::WeakPtr<history::HistoryService>& history_service);
-
-  // Collects urls' redirects chain information from the history service.
-  // We get access to history service via web_contents in UI thread.
-  void StartHistoryCollection(const std::vector<GURL>& urls,
-                              base::OnceClosure callback);
-
-  // Returns whether or not StartCacheCollection has been called.
-  bool HasStarted() const;
-
-  // Returns the redirect urls we get from history service
-  const std::vector<RedirectChain>& GetCollectedUrls() const;
-
-  // history::HistoryServiceObserver
-  void HistoryServiceBeingDeleted(
-      history::HistoryService* history_service) override;
-
- private:
-  friend class base::RefCounted<ThreatDetailsRedirectsCollector>;
-
-  ~ThreatDetailsRedirectsCollector() override;
-
-  void StartGetRedirects(const std::vector<GURL>& urls);
-  void GetRedirects(const GURL& url);
-  void OnGotQueryRedirectsTo(const GURL& url,
-                             history::RedirectList redirect_list);
-
-  // Runs the callback when redirects collecting is all done.
-  void AllDone();
-
-  base::CancelableTaskTracker request_tracker_;
-
-  // Method we call when we are done. The caller must be alive for the
-  // whole time, we are modifying its state (see above).
-  base::OnceClosure callback_;
-
-  // Sets to true once StartHistoryCollection is called
-  bool has_started_;
-
-  // The urls we need to get redirects for
-  std::vector<GURL> urls_;
-  // The iterator goes over urls_
-  std::vector<GURL>::iterator urls_it_;
-  // The collected directs from history service
-  std::vector<RedirectChain> redirects_urls_;
-
-  base::WeakPtr<history::HistoryService> history_service_;
-  ScopedObserver<history::HistoryService, history::HistoryServiceObserver>
-      history_service_observer_{this};
-
-  DISALLOW_COPY_AND_ASSIGN(ThreatDetailsRedirectsCollector);
-};
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_BROWSER_THREAT_DETAILS_HISTORY_H_
diff --git a/components/safe_browsing/browser/url_checker_delegate.h b/components/safe_browsing/browser/url_checker_delegate.h
deleted file mode 100644
index 7ab1d1b..0000000
--- a/components/safe_browsing/browser/url_checker_delegate.h
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_BROWSER_URL_CHECKER_DELEGATE_H_
-#define COMPONENTS_SAFE_BROWSING_BROWSER_URL_CHECKER_DELEGATE_H_
-
-#include <string>
-
-#include "base/callback.h"
-#include "base/memory/ref_counted.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
-
-namespace content {
-class ResourceContext;
-class WebContents;
-}
-
-namespace net {
-class HttpRequestHeaders;
-}
-
-namespace security_interstitials {
-struct UnsafeResource;
-}
-
-namespace safe_browsing {
-
-class BaseUIManager;
-class SafeBrowsingDatabaseManager;
-
-// Delegate interface for SafeBrowsingUrlCheckerImpl and SafeBrowsing's
-// content::ResourceThrottle subclasses. They delegate to this interface those
-// operations that different embedders (Chrome and Android WebView) handle
-// differently.
-//
-// All methods should only be called from the IO thread.
-class UrlCheckerDelegate
-    : public base::RefCountedThreadSafe<UrlCheckerDelegate> {
- public:
-  // Destroys prerender contents if necessary. The parameter is a
-  // WebContents::OnceGetter, but that type is not visible from here.
-  virtual void MaybeDestroyPrerenderContents(
-      base::OnceCallback<content::WebContents*()> web_contents_getter) = 0;
-
-  // Starts displaying the SafeBrowsing interstitial page.
-  virtual void StartDisplayingBlockingPageHelper(
-      const security_interstitials::UnsafeResource& resource,
-      const std::string& method,
-      const net::HttpRequestHeaders& headers,
-      bool is_main_frame,
-      bool has_user_gesture) = 0;
-
-  // A whitelisted URL is considered safe and therefore won't be checked with
-  // the SafeBrowsing database.
-  virtual bool IsUrlWhitelisted(const GURL& url) = 0;
-
-  // If the method returns true, the entire request won't be checked, including
-  // the original URL and redirects.
-  // If neither of |render_process_id| and |render_frame_id| is -1, they will be
-  // used to identify the frame making the request; otherwise
-  // |frame_tree_node_id| will be used. Please note that |frame_tree_node_id|
-  // could also be -1, if a request is not associated with a frame.
-  virtual bool ShouldSkipRequestCheck(
-      content::ResourceContext* resource_context,
-      const GURL& original_url,
-      int frame_tree_node_id,
-      int render_process_id,
-      int render_frame_id,
-      bool originated_from_service_worker) = 0;
-
-  // Notifies the SafeBrowsing Trigger Manager that a suspicious site has been
-  // detected. |web_contents_getter| is used to determine which tab the site
-  // was detected on.
-  virtual void NotifySuspiciousSiteDetected(
-      const base::RepeatingCallback<content::WebContents*()>&
-          web_contents_getter) = 0;
-
-  virtual const SBThreatTypeSet& GetThreatTypes() = 0;
-  virtual SafeBrowsingDatabaseManager* GetDatabaseManager() = 0;
-  virtual BaseUIManager* GetUIManager() = 0;
-
- protected:
-  friend class base::RefCountedThreadSafe<UrlCheckerDelegate>;
-  virtual ~UrlCheckerDelegate() {}
-};
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_BROWSER_URL_CHECKER_DELEGATE_H_
diff --git a/components/safe_browsing/common/BUILD.gn b/components/safe_browsing/common/BUILD.gn
deleted file mode 100644
index 6ebbdd1..0000000
--- a/components/safe_browsing/common/BUILD.gn
+++ /dev/null
@@ -1,83 +0,0 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//build/config/features.gni")
-import("//components/safe_browsing/buildflags.gni")
-import("//mojo/public/tools/bindings/mojom.gni")
-
-source_set("common") {
-  sources = [
-    "safebrowsing_constants.cc",
-    "safebrowsing_constants.h",
-    "safebrowsing_switches.cc",
-    "safebrowsing_switches.h",
-    "utils.cc",
-    "utils.h",
-  ]
-
-  deps = [
-    "//base",
-    "//components/policy/core/browser:browser",
-    "//components/prefs:prefs",
-    "//components/safe_browsing:csd_proto",
-    "//components/safe_browsing:features",
-    "//crypto:crypto",
-    "//ipc",
-    "//url/ipc:url_ipc",
-  ]
-
-  public_deps = [
-    ":interfaces",
-  ]
-}
-
-static_library("safe_browsing_prefs") {
-  sources = [
-    "safe_browsing_prefs.cc",
-    "safe_browsing_prefs.h",
-  ]
-
-  deps = [
-    "//base:base",
-    "//components/pref_registry:pref_registry",
-    "//components/prefs",
-    "//components/safe_browsing:features",
-    "//content/public/browser:browser",
-    "//net:net",
-  ]
-}
-
-source_set("unit_tests") {
-  testonly = true
-  sources = [
-    "safe_browsing_prefs_unittest.cc",
-  ]
-  deps = [
-    ":safe_browsing_prefs",
-    "//base:base",
-    "//base/test:test_support",
-    "//components/prefs:test_support",
-    "//components/safe_browsing:features",
-    "//content/test:test_support",
-    "//testing/gtest",
-    "//url:url",
-  ]
-}
-
-mojom("interfaces") {
-  sources = [
-    "safe_browsing.mojom",
-  ]
-
-  public_deps = [
-    "//content/public/common:resource_type_bindings",
-    "//services/network/public/mojom",
-    "//url/mojom:url_mojom_gurl",
-  ]
-
-  enabled_features = []
-  if (safe_browsing_mode == 1) {
-    enabled_features += [ "full_safe_browsing" ]
-  }
-}
diff --git a/components/safe_browsing/common/safe_browsing_prefs.cc b/components/safe_browsing/common/safe_browsing_prefs.cc
deleted file mode 100644
index 6e57810..0000000
--- a/components/safe_browsing/common/safe_browsing_prefs.cc
+++ /dev/null
@@ -1,416 +0,0 @@
-// Copyright (c) 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 "components/safe_browsing/common/safe_browsing_prefs.h"
-
-#include "base/command_line.h"
-#include "base/logging.h"
-#include "base/metrics/histogram_macros.h"
-#include "components/pref_registry/pref_registry_syncable.h"
-#include "components/prefs/pref_registry_simple.h"
-#include "components/prefs/pref_service.h"
-#include "components/safe_browsing/features.h"
-#include "content/public/browser/browser_thread.h"
-#include "net/base/url_util.h"
-#include "url/gurl.h"
-#include "url/url_canon.h"
-
-namespace {
-
-// The Extended Reporting pref that is currently active, used for UMA metrics.
-// These values are written to logs.  New enum values can be added, but
-// existing enums must never be renumbered or deleted and reused.
-enum ActiveExtendedReportingPref {
-  SBER1_PREF = 0,
-  SBER2_PREF = 1,
-  // New prefs must be added before MAX_SBER_PREF
-  MAX_SBER_PREF
-};
-
-// Update the correct UMA metric based on which pref was changed and which UI
-// the change was made on.
-void RecordExtendedReportingPrefChanged(
-    const PrefService& prefs,
-    safe_browsing::ExtendedReportingOptInLocation location) {
-  bool pref_value = safe_browsing::IsExtendedReportingEnabled(prefs);
-
-  switch (location) {
-    case safe_browsing::SBER_OPTIN_SITE_CHROME_SETTINGS:
-      UMA_HISTOGRAM_BOOLEAN(
-          "SafeBrowsing.Pref.Scout.SetPref.SBER2Pref.ChromeSettings",
-          pref_value);
-      break;
-    case safe_browsing::SBER_OPTIN_SITE_ANDROID_SETTINGS:
-      UMA_HISTOGRAM_BOOLEAN(
-          "SafeBrowsing.Pref.Scout.SetPref.SBER2Pref.AndroidSettings",
-          pref_value);
-      break;
-    case safe_browsing::SBER_OPTIN_SITE_DOWNLOAD_FEEDBACK_POPUP:
-      UMA_HISTOGRAM_BOOLEAN(
-          "SafeBrowsing.Pref.Scout.SetPref.SBER2Pref.DownloadPopup",
-          pref_value);
-      break;
-    case safe_browsing::SBER_OPTIN_SITE_SECURITY_INTERSTITIAL:
-      UMA_HISTOGRAM_BOOLEAN(
-          "SafeBrowsing.Pref.Scout.SetPref.SBER2Pref.SecurityInterstitial",
-          pref_value);
-      break;
-    default:
-      NOTREACHED();
-  }
-}
-
-// A helper function to return a GURL containing just the scheme, host, port,
-// and path from a URL. Equivalent to clearing any username, password, query,
-// and ref. Return empty URL if |url| is not valid.
-GURL GetSimplifiedURL(const GURL& url) {
-  if (!url.is_valid() || !url.IsStandard())
-    return GURL();
-
-  url::Replacements<char> replacements;
-  replacements.ClearUsername();
-  replacements.ClearPassword();
-  replacements.ClearQuery();
-  replacements.ClearRef();
-
-  return url.ReplaceComponents(replacements);
-}
-
-}  // namespace
-
-namespace prefs {
-const char kSafeBrowsingEnabled[] = "safebrowsing.enabled";
-const char kSafeBrowsingExtendedReportingOptInAllowed[] =
-    "safebrowsing.extended_reporting_opt_in_allowed";
-const char kSafeBrowsingIncidentsSent[] = "safebrowsing.incidents_sent";
-const char kSafeBrowsingProceedAnywayDisabled[] =
-    "safebrowsing.proceed_anyway_disabled";
-const char kSafeBrowsingSawInterstitialScoutReporting[] =
-    "safebrowsing.saw_interstitial_sber2";
-const char kSafeBrowsingScoutReportingEnabled[] =
-    "safebrowsing.scout_reporting_enabled";
-const char kSafeBrowsingTriggerEventTimestamps[] =
-    "safebrowsing.trigger_event_timestamps";
-const char kSafeBrowsingUnhandledGaiaPasswordReuses[] =
-    "safebrowsing.unhandled_sync_password_reuses";
-const char kSafeBrowsingNextPasswordCaptureEventLogTime[] =
-    "safebrowsing.next_password_capture_event_log_time";
-const char kSafeBrowsingWhitelistDomains[] =
-    "safebrowsing.safe_browsing_whitelist_domains";
-const char kPasswordProtectionChangePasswordURL[] =
-    "safebrowsing.password_protection_change_password_url";
-const char kPasswordProtectionLoginURLs[] =
-    "safebrowsing.password_protection_login_urls";
-const char kPasswordProtectionWarningTrigger[] =
-    "safebrowsing.password_protection_warning_trigger";
-const char kAdvancedProtectionLastRefreshInUs[] =
-    "safebrowsing.advanced_protection_last_refresh";
-const char kSafeBrowsingRealTimeLookupEnabled[] =
-    "safebrowsing.real_time_lookup_enabled";
-const char kSafeBrowsingSendFilesForMalwareCheck[] =
-    "safebrowsing.send_files_for_malware_check";
-const char kUnsafeEventsReportingEnabled[] =
-    "safebrowsing.unsafe_events_reporting";
-const char kBlockLargeFileTransfer[] =
-    "safebrowsing.block_large_file_transfers";
-const char kDelayDeliveryUntilVerdict[] =
-    "safebrowsing.delay_delivery_until_verdict";
-const char kAllowPasswordProtectedFiles[] =
-    "safebrowsing.allow_password_protected_files";
-const char kCheckContentCompliance[] = "safebrowsing.check_content_compliance";
-const char kURLsToCheckComplianceOfDownloadedContent[] =
-    "safebrowsing.urls_to_check_compliance_of_downloaded_content";
-const char kURLsToCheckForMalwareOfUploadedContent[] =
-    "safebrowsing.urls_to_check_for_malware_of_uploaded_content";
-const char kURLsToNotCheckComplianceOfUploadedContent[] =
-    "policy.urls_to_not_check_compliance_of_uploaded_content";
-
-}  // namespace prefs
-
-namespace safe_browsing {
-
-bool ExtendedReportingPrefExists(const PrefService& prefs) {
-  return prefs.HasPrefPath(prefs::kSafeBrowsingScoutReportingEnabled);
-}
-
-ExtendedReportingLevel GetExtendedReportingLevel(const PrefService& prefs) {
-  return IsExtendedReportingEnabled(prefs) ? SBER_LEVEL_SCOUT : SBER_LEVEL_OFF;
-}
-
-bool IsExtendedReportingOptInAllowed(const PrefService& prefs) {
-  return prefs.GetBoolean(prefs::kSafeBrowsingExtendedReportingOptInAllowed);
-}
-
-bool IsExtendedReportingEnabled(const PrefService& prefs) {
-  return prefs.GetBoolean(prefs::kSafeBrowsingScoutReportingEnabled);
-}
-
-bool IsExtendedReportingPolicyManaged(const PrefService& prefs) {
-  return prefs.IsManagedPreference(prefs::kSafeBrowsingScoutReportingEnabled);
-}
-
-void RecordExtendedReportingMetrics(const PrefService& prefs) {
-  // This metric tracks the extended browsing opt-in based on whichever setting
-  // the user is currently seeing. It tells us whether extended reporting is
-  // happening for this user.
-  UMA_HISTOGRAM_BOOLEAN("SafeBrowsing.Pref.Extended",
-                        IsExtendedReportingEnabled(prefs));
-
-  // Track whether this user has ever seen a security interstitial.
-  UMA_HISTOGRAM_BOOLEAN(
-      "SafeBrowsing.Pref.SawInterstitial.SBER2Pref",
-      prefs.GetBoolean(prefs::kSafeBrowsingSawInterstitialScoutReporting));
-}
-
-void RegisterProfilePrefs(PrefRegistrySimple* registry) {
-  registry->RegisterBooleanPref(prefs::kSafeBrowsingScoutReportingEnabled,
-                                false);
-  registry->RegisterBooleanPref(
-      prefs::kSafeBrowsingSawInterstitialScoutReporting, false);
-  registry->RegisterBooleanPref(
-      prefs::kSafeBrowsingExtendedReportingOptInAllowed, true);
-  registry->RegisterBooleanPref(
-      prefs::kSafeBrowsingEnabled, true,
-      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
-  registry->RegisterBooleanPref(prefs::kSafeBrowsingProceedAnywayDisabled,
-                                false);
-  registry->RegisterDictionaryPref(prefs::kSafeBrowsingIncidentsSent);
-  registry->RegisterDictionaryPref(
-      prefs::kSafeBrowsingUnhandledGaiaPasswordReuses);
-  registry->RegisterStringPref(
-      prefs::kSafeBrowsingNextPasswordCaptureEventLogTime,
-      "0");  // int64 as string
-  registry->RegisterListPref(prefs::kSafeBrowsingWhitelistDomains);
-  registry->RegisterStringPref(prefs::kPasswordProtectionChangePasswordURL, "");
-  registry->RegisterListPref(prefs::kPasswordProtectionLoginURLs);
-  registry->RegisterIntegerPref(prefs::kPasswordProtectionWarningTrigger,
-                                PASSWORD_PROTECTION_OFF);
-  registry->RegisterInt64Pref(prefs::kAdvancedProtectionLastRefreshInUs, 0);
-  registry->RegisterBooleanPref(prefs::kSafeBrowsingRealTimeLookupEnabled,
-                                false);
-  registry->RegisterIntegerPref(prefs::kSafeBrowsingSendFilesForMalwareCheck,
-                                DO_NOT_SCAN);
-}
-
-void RegisterLocalStatePrefs(PrefRegistrySimple* registry) {
-  registry->RegisterDictionaryPref(prefs::kSafeBrowsingTriggerEventTimestamps);
-  registry->RegisterBooleanPref(prefs::kUnsafeEventsReportingEnabled, false);
-  registry->RegisterIntegerPref(prefs::kBlockLargeFileTransfer, 0);
-  registry->RegisterIntegerPref(prefs::kDelayDeliveryUntilVerdict, DELAY_NONE);
-  registry->RegisterIntegerPref(
-      prefs::kAllowPasswordProtectedFiles,
-      AllowPasswordProtectedFilesValues::ALLOW_UPLOADS_AND_DOWNLOADS);
-  registry->RegisterIntegerPref(prefs::kCheckContentCompliance, CHECK_NONE);
-  registry->RegisterListPref(prefs::kURLsToCheckComplianceOfDownloadedContent);
-  registry->RegisterListPref(prefs::kURLsToNotCheckComplianceOfUploadedContent);
-  registry->RegisterListPref(prefs::kURLsToCheckForMalwareOfUploadedContent);
-}
-
-void SetExtendedReportingPrefAndMetric(
-    PrefService* prefs,
-    bool value,
-    ExtendedReportingOptInLocation location) {
-  prefs->SetBoolean(prefs::kSafeBrowsingScoutReportingEnabled, value);
-  RecordExtendedReportingPrefChanged(*prefs, location);
-}
-
-void SetExtendedReportingPref(PrefService* prefs, bool value) {
-  prefs->SetBoolean(prefs::kSafeBrowsingScoutReportingEnabled, value);
-}
-
-void UpdateMetricsAfterSecurityInterstitial(const PrefService& prefs,
-                                            bool on_show_pref_existed,
-                                            bool on_show_pref_value) {
-  const bool cur_pref_value = IsExtendedReportingEnabled(prefs);
-
-  if (!on_show_pref_existed) {
-    if (!ExtendedReportingPrefExists(prefs)) {
-      // User seeing pref for the first time, didn't touch the checkbox (left it
-      // unchecked).
-      UMA_HISTOGRAM_ENUMERATION(
-          "SafeBrowsing.Pref.Scout.Decision.First_LeftUnchecked", SBER2_PREF,
-          MAX_SBER_PREF);
-      return;
-    }
-
-    // Pref currently exists so user did something to the checkbox
-    if (cur_pref_value) {
-      // User turned the pref on.
-      UMA_HISTOGRAM_ENUMERATION(
-          "SafeBrowsing.Pref.Scout.Decision.First_Enabled", SBER2_PREF,
-          MAX_SBER_PREF);
-      return;
-    }
-
-    // Otherwise, user turned the pref off, but because it didn't exist when
-    // the interstitial was first shown, they must have turned it on and then
-    // off before the interstitial was closed.
-    UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.Pref.Scout.Decision.First_Disabled",
-                              SBER2_PREF, MAX_SBER_PREF);
-    return;
-  }
-
-  // At this point, the pref existed when the interstitial was shown so this is
-  // a repeat appearance of the opt-in. Existence can't be removed during an
-  // interstitial so no need to check whether the pref currently exists.
-  if (on_show_pref_value && cur_pref_value) {
-    // User left the pref on.
-    UMA_HISTOGRAM_ENUMERATION(
-        "SafeBrowsing.Pref.Scout.Decision.Repeat_LeftEnabled", SBER2_PREF,
-        MAX_SBER_PREF);
-    return;
-  } else if (on_show_pref_value && !cur_pref_value) {
-    // User turned the pref off.
-    UMA_HISTOGRAM_ENUMERATION(
-        "SafeBrowsing.Pref.Scout.Decision.Repeat_Disabled", SBER2_PREF,
-        MAX_SBER_PREF);
-    return;
-  } else if (!on_show_pref_value && cur_pref_value) {
-    // User turned the pref on.
-    UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.Pref.Scout.Decision.Repeat_Enabled",
-                              SBER2_PREF, MAX_SBER_PREF);
-    return;
-  } else {
-    // Both on_show and cur values are false - user left the pref off.
-    UMA_HISTOGRAM_ENUMERATION(
-        "SafeBrowsing.Pref.Scout.Decision.Repeat_LeftDisabled", SBER2_PREF,
-        MAX_SBER_PREF);
-    return;
-  }
-}
-
-void UpdatePrefsBeforeSecurityInterstitial(PrefService* prefs) {
-  // Remember that this user saw an interstitial.
-  prefs->SetBoolean(prefs::kSafeBrowsingSawInterstitialScoutReporting, true);
-}
-
-base::ListValue GetSafeBrowsingPreferencesList(PrefService* prefs) {
-  base::ListValue preferences_list;
-
-  const char* safe_browsing_preferences[] = {
-      prefs::kSafeBrowsingEnabled,
-      prefs::kSafeBrowsingExtendedReportingOptInAllowed,
-      prefs::kSafeBrowsingScoutReportingEnabled};
-
-  // Add the status of the preferences if they are Enabled or Disabled for the
-  // user.
-  for (const char* preference : safe_browsing_preferences) {
-    preferences_list.Append(base::Value(preference));
-    bool enabled = prefs->GetBoolean(preference);
-    preferences_list.Append(base::Value(enabled ? "Enabled" : "Disabled"));
-  }
-  return preferences_list;
-}
-
-void GetSafeBrowsingWhitelistDomainsPref(
-    const PrefService& prefs,
-    std::vector<std::string>* out_canonicalized_domain_list) {
-  const base::ListValue* pref_value =
-      prefs.GetList(prefs::kSafeBrowsingWhitelistDomains);
-  CanonicalizeDomainList(*pref_value, out_canonicalized_domain_list);
-}
-
-void CanonicalizeDomainList(
-    const base::ListValue& raw_domain_list,
-    std::vector<std::string>* out_canonicalized_domain_list) {
-  out_canonicalized_domain_list->clear();
-  for (auto it = raw_domain_list.GetList().begin();
-       it != raw_domain_list.GetList().end(); it++) {
-    // Verify if it is valid domain string.
-    url::CanonHostInfo host_info;
-    std::string canonical_host =
-        net::CanonicalizeHost(it->GetString(), &host_info);
-    if (!canonical_host.empty())
-      out_canonicalized_domain_list->push_back(canonical_host);
-  }
-}
-
-bool IsURLWhitelistedByPolicy(const GURL& url,
-                              StringListPrefMember* pref_member) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-  if (!pref_member)
-    return false;
-
-  std::vector<std::string> sb_whitelist_domains = pref_member->GetValue();
-  return std::find_if(sb_whitelist_domains.begin(), sb_whitelist_domains.end(),
-                      [&url](const std::string& domain) {
-                        return url.DomainIs(domain);
-                      }) != sb_whitelist_domains.end();
-}
-
-bool IsURLWhitelistedByPolicy(const GURL& url, const PrefService& pref) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  if (!pref.HasPrefPath(prefs::kSafeBrowsingWhitelistDomains))
-    return false;
-  const base::ListValue* whitelist =
-      pref.GetList(prefs::kSafeBrowsingWhitelistDomains);
-  for (const base::Value& value : whitelist->GetList()) {
-    if (url.DomainIs(value.GetString()))
-      return true;
-  }
-  return false;
-}
-
-void GetPasswordProtectionLoginURLsPref(const PrefService& prefs,
-                                        std::vector<GURL>* out_login_url_list) {
-  const base::ListValue* pref_value =
-      prefs.GetList(prefs::kPasswordProtectionLoginURLs);
-  out_login_url_list->clear();
-  for (const base::Value& value : pref_value->GetList()) {
-    GURL login_url(value.GetString());
-    // Skip invalid or none-http/https login URLs.
-    if (login_url.is_valid() && login_url.SchemeIsHTTPOrHTTPS())
-      out_login_url_list->push_back(login_url);
-  }
-}
-
-bool MatchesPasswordProtectionLoginURL(const GURL& url,
-                                       const PrefService& prefs) {
-  if (!url.is_valid())
-    return false;
-
-  std::vector<GURL> login_urls;
-  GetPasswordProtectionLoginURLsPref(prefs, &login_urls);
-  return MatchesURLList(url, login_urls);
-}
-
-bool MatchesURLList(const GURL& target_url, const std::vector<GURL> url_list) {
-  if (url_list.empty() || !target_url.is_valid())
-    return false;
-  GURL simple_target_url = GetSimplifiedURL(target_url);
-  for (const GURL& url : url_list) {
-    if (GetSimplifiedURL(url) == simple_target_url) {
-      return true;
-    }
-  }
-  return false;
-}
-
-GURL GetPasswordProtectionChangePasswordURLPref(const PrefService& prefs) {
-  if (!prefs.HasPrefPath(prefs::kPasswordProtectionChangePasswordURL))
-    return GURL();
-  GURL change_password_url_from_pref(
-      prefs.GetString(prefs::kPasswordProtectionChangePasswordURL));
-  // Skip invalid or non-http/https URL.
-  if (change_password_url_from_pref.is_valid() &&
-      change_password_url_from_pref.SchemeIsHTTPOrHTTPS()) {
-    return change_password_url_from_pref;
-  }
-
-  return GURL();
-}
-
-bool MatchesPasswordProtectionChangePasswordURL(const GURL& url,
-                                                const PrefService& prefs) {
-  if (!url.is_valid())
-    return false;
-
-  GURL change_password_url = GetPasswordProtectionChangePasswordURLPref(prefs);
-  if (change_password_url.is_empty())
-    return false;
-
-  return GetSimplifiedURL(change_password_url) == GetSimplifiedURL(url);
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/common/safe_browsing_prefs.h b/components/safe_browsing/common/safe_browsing_prefs.h
deleted file mode 100644
index 724c1307..0000000
--- a/components/safe_browsing/common/safe_browsing_prefs.h
+++ /dev/null
@@ -1,320 +0,0 @@
-// Copyright (c) 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.
-//
-// Safe Browsing preferences and some basic utility functions for using them.
-
-#ifndef COMPONENTS_SAFE_BROWSING_COMMON_SAFE_BROWSING_PREFS_H_
-#define COMPONENTS_SAFE_BROWSING_COMMON_SAFE_BROWSING_PREFS_H_
-
-#include <string>
-#include <vector>
-
-#include "base/feature_list.h"
-#include "base/values.h"
-#include "components/prefs/pref_member.h"
-
-class PrefRegistrySimple;
-class PrefService;
-class GURL;
-
-namespace prefs {
-// Boolean that is true when SafeBrowsing is enabled.
-extern const char kSafeBrowsingEnabled[];
-
-// Boolean that tells us whether users are given the option to opt in to Safe
-// Browsing extended reporting. This is exposed as a preference that can be
-// overridden by enterprise policy.
-extern const char kSafeBrowsingExtendedReportingOptInAllowed[];
-
-// A dictionary mapping incident types to a dict of incident key:digest pairs.
-// The key is a string: a filename or pref name. Digests are 4 bytes. This pref
-// is only set/updated if Chrome (Windows only) notices certain security
-// incidents, e.g. the user downloaded binaries with invalid signatures.
-extern const char kSafeBrowsingIncidentsSent[];
-
-// Boolean that is true when the SafeBrowsing interstitial should not allow
-// users to proceed anyway.
-extern const char kSafeBrowsingProceedAnywayDisabled[];
-
-// Boolean indicating whether the user has ever seen a security interstitial.
-extern const char kSafeBrowsingSawInterstitialScoutReporting[];
-
-// Boolean indicating whether Safe Browsing Scout reporting is enabled, which
-// collects data for malware detection.
-extern const char kSafeBrowsingScoutReportingEnabled[];
-
-// Dictionary containing safe browsing triggers and the list of times they have
-// fired recently. The keys are TriggerTypes (4-byte ints) and the values are
-// lists of doubles.
-extern const char kSafeBrowsingTriggerEventTimestamps[];
-
-// Dictionary that records the origin and navigation ID pairs of unhandled gaia
-// password reuses. The keys are origin strings and the ID values are 8-byte
-// ints. Only set/update if a Chrome user reuses their Gaia password on a
-// phishing site.
-extern const char kSafeBrowsingUnhandledGaiaPasswordReuses[];
-
-// Integer timestamp of next time the PasswordCaptured event should be logged.
-extern const char kSafeBrowsingNextPasswordCaptureEventLogTime[];
-
-// List of domains where Safe Browsing should trust. That means Safe Browsing
-// won't check for malware/phishing/Uws on resources on these domains, or
-// trigger warnings. Used for enterprise only.
-extern const char kSafeBrowsingWhitelistDomains[];
-
-// String indicating the URL where password protection service should send user
-// to change their password if they've been phished. Password protection service
-// also captures new password on this page in a change password event. Used for
-// enterprise only.
-extern const char kPasswordProtectionChangePasswordURL[];
-
-// List of string indicating the URL(s) users use to log in. Password protection
-// service will capture passwords on these URLs.
-// This is managed by enterprise policy and has no effect on users who are not
-// managed by enterprise policy.
-extern const char kPasswordProtectionLoginURLs[];
-
-// Integer indicating the password protection warning trigger. This is managed
-// by enterprise policy and has no effect on users who are not managed by
-// enterprise policy.
-extern const char kPasswordProtectionWarningTrigger[];
-
-// Last time Chrome refreshes advanced protection status for sign-in users (in
-// microseconds);
-extern const char kAdvancedProtectionLastRefreshInUs[];
-
-// Whether or not to check URLs in real time. This is configured by enterprise
-// policy. For consumers, this pref is irrelevant.
-extern const char kSafeBrowsingRealTimeLookupEnabled[];
-
-// Whether or not to send downloads to Safe Browsing for deep scanning. This
-// is configured by enterprise policy.
-extern const char kSafeBrowsingSendFilesForMalwareCheck[];
-
-// Boolean that indidicates if Chrome reports unsafe events to Google.
-extern const char kUnsafeEventsReportingEnabled[];
-
-// Integer that specifies if large files are blocked form either uploads or
-// downloads or both.
-extern const char kBlockLargeFileTransfer[];
-
-// Integer that specifies if delivery to the user of potentially unsafe data
-// is delayed until a verdict about the data is known.
-extern const char kDelayDeliveryUntilVerdict[];
-
-// Integer that specifies if password protected files can be either uploaded
-// or downloaded or both.
-extern const char kAllowPasswordProtectedFiles[];
-
-// Integer that indidicates if Chrome checks data for content compliance.
-extern const char kCheckContentCompliance[];
-
-// List of url patterns where Chrome should check compliance of downloaded
-// files.
-extern const char kURLsToCheckComplianceOfDownloadedContent[];
-
-// List of url patterns where Chrome should check for malware of uploaded files.
-extern const char kURLsToCheckForMalwareOfUploadedContent[];
-
-// List of url patterns where Chrome should not check compliance of uploaded
-// files.
-extern const char kURLsToNotCheckComplianceOfUploadedContent[];
-
-}  // namespace prefs
-
-namespace safe_browsing {
-
-// Enumerates the level of Safe Browsing Extended Reporting that is currently
-// available.
-enum ExtendedReportingLevel {
-  // Extended reporting is off.
-  SBER_LEVEL_OFF = 0,
-  // The Legacy level of extended reporting is available, reporting happens in
-  // response to security incidents.
-  SBER_LEVEL_LEGACY = 1,
-  // The Scout level of extended reporting is available, some data can be
-  // collected to actively detect dangerous apps and sites.
-  SBER_LEVEL_SCOUT = 2,
-};
-
-// Enumerates all the places where the Safe Browsing Extended Reporting
-// preference can be changed.
-// These values are written to logs.  New enum values can be added, but
-// existing enums must never be renumbered or deleted and reused.
-enum ExtendedReportingOptInLocation {
-  // The chrome://settings UI.
-  SBER_OPTIN_SITE_CHROME_SETTINGS = 0,
-  // The Android settings UI.
-  SBER_OPTIN_SITE_ANDROID_SETTINGS = 1,
-  // The Download Feedback popup.
-  SBER_OPTIN_SITE_DOWNLOAD_FEEDBACK_POPUP = 2,
-  // Any security interstitial (malware, SSL, etc).
-  SBER_OPTIN_SITE_SECURITY_INTERSTITIAL = 3,
-  // New sites must be added before SBER_OPTIN_SITE_MAX.
-  SBER_OPTIN_SITE_MAX
-};
-
-// Enumerates all the triggers of password protection.
-enum PasswordProtectionTrigger {
-  // Password protection is off.
-  PASSWORD_PROTECTION_OFF = 0,
-  // Password protection triggered by password reuse event.
-  // Not used for now.
-  PASSWORD_REUSE = 1,
-  // Password protection triggered by password reuse event on phishing page.
-  PHISHING_REUSE = 2,
-  // New triggers must be added before PASSWORD_PROTECTION_TRIGGER_MAX.
-  PASSWORD_PROTECTION_TRIGGER_MAX,
-};
-
-// Enum representing possible values of the SendFilesForMalwareCheck policy.
-// This must be kept in sync with policy_templates.json.
-enum SendFilesForMalwareCheckValues {
-  DO_NOT_SCAN = 0,
-  SEND_DOWNLOADS = 2,
-  SEND_UPLOADS = 3,
-  SEND_UPLOADS_AND_DOWNLOADS = 4,
-  // New options must be added before SEND_FILES_FOR_MALWARE_CHECK_MAX.
-  SEND_FILES_FOR_MALWARE_CHECK_MAX = SEND_UPLOADS_AND_DOWNLOADS,
-};
-
-// Enum representing possible values of the CheckContentCompliance policy. This
-// must be kept in sync with policy_templates.json.
-enum CheckContentComplianceValues {
-  CHECK_NONE = 0,
-  CHECK_DOWNLOADS = 1,
-  CHECK_UPLOADS = 2,
-  CHECK_UPLOADS_AND_DOWNLOADS = 3,
-  // New options must be added before CHECK_CONTENT_COMPLIANCE_MAX.
-  CHECK_CONTENT_COMPLIANCE_MAX = CHECK_UPLOADS_AND_DOWNLOADS,
-};
-
-// Enum representing possible values of the AllowPasswordProtectedFiles policy.
-// This must be kept in sync with policy_templates.json.
-enum AllowPasswordProtectedFilesValues {
-  ALLOW_NONE = 0,
-  ALLOW_DOWNLOADS = 1,
-  ALLOW_UPLOADS = 2,
-  ALLOW_UPLOADS_AND_DOWNLOADS = 3,
-};
-
-// Enum representing possible values of the BlockLargeFileTransfer policy. This
-// must be kept in sync with policy_templates.json.
-enum BlockLargeFileTransferValues {
-  BLOCK_NONE = 0,
-  BLOCK_LARGE_DOWNLOADS = 1,
-  BLOCK_LARGE_UPLOADS = 2,
-  BLOCK_LARGE_UPLOADS_AND_DOWNLOADS = 3,
-};
-
-// Enum representing possible values of the DelayDeliveryUntilVerdict policy.
-// This must be kept in sync with policy_templates.json.
-enum DelayDeliveryUntilVerdictValues {
-  DELAY_NONE = 0,
-  DELAY_DOWNLOADS = 1,
-  DELAY_UPLOADS = 2,
-  DELAY_UPLOADS_AND_DOWNLOADS = 3,
-};
-
-// Returns whether the currently active Safe Browsing Extended Reporting
-// preference exists (eg: has been set before).
-bool ExtendedReportingPrefExists(const PrefService& prefs);
-
-// Returns the level of reporting available for the current user.
-ExtendedReportingLevel GetExtendedReportingLevel(const PrefService& prefs);
-
-// Returns whether the user is able to modify the Safe Browsing Extended
-// Reporting opt-in.
-bool IsExtendedReportingOptInAllowed(const PrefService& prefs);
-
-// Returns whether Safe Browsing Extended Reporting is currently enabled.
-// This should be used to decide if any of the reporting preferences are set,
-// regardless of which specific one is set.
-bool IsExtendedReportingEnabled(const PrefService& prefs);
-
-// Returns whether the active Extended Reporting pref is currently managed by
-// enterprise policy, meaning the user can't change it.
-bool IsExtendedReportingPolicyManaged(const PrefService& prefs);
-
-// Updates UMA metrics about Safe Browsing Extended Reporting states.
-void RecordExtendedReportingMetrics(const PrefService& prefs);
-
-// Registers user preferences related to Safe Browsing.
-void RegisterProfilePrefs(PrefRegistrySimple* registry);
-
-// Registers local state prefs related to Safe Browsing.
-void RegisterLocalStatePrefs(PrefRegistrySimple* registry);
-
-// Sets the currently active Safe Browsing Extended Reporting preference to the
-// specified value. The |location| indicates the UI where the change was
-// made.
-void SetExtendedReportingPrefAndMetric(PrefService* prefs,
-                                       bool value,
-                                       ExtendedReportingOptInLocation location);
-// This variant is used to simplify test code by omitting the location.
-void SetExtendedReportingPref(PrefService* prefs, bool value);
-
-// Called when a security interstitial is closed by the user.
-// |on_show_pref_existed| indicates whether the pref existed when the
-// interstitial was shown. |on_show_pref_value| contains the pref value when the
-// interstitial was shown.
-void UpdateMetricsAfterSecurityInterstitial(const PrefService& prefs,
-                                            bool on_show_pref_existed,
-                                            bool on_show_pref_value);
-
-// Called to indicate that a security interstitial is about to be shown to the
-// user. This may trigger the user to begin seeing the Scout opt-in text
-// depending on their experiment state.
-void UpdatePrefsBeforeSecurityInterstitial(PrefService* prefs);
-
-// Returns a list of preferences to be shown in chrome://safe-browsing. The
-// preferences are passed as an alternating sequence of preference names and
-// values represented as strings.
-base::ListValue GetSafeBrowsingPreferencesList(PrefService* prefs);
-
-// Returns a list of valid domains that Safe Browsing service trusts.
-void GetSafeBrowsingWhitelistDomainsPref(
-    const PrefService& prefs,
-    std::vector<std::string>* out_canonicalized_domain_list);
-
-// Helper function to validate and canonicalize a list of domain strings.
-void CanonicalizeDomainList(
-    const base::ListValue& raw_domain_list,
-    std::vector<std::string>* out_canonicalized_domain_list);
-
-// Helper function to determine if |url| matches Safe Browsing whitelist domains
-// (a.k. a prefs::kSafeBrowsingWhitelistDomains).
-// Called on IO thread.
-bool IsURLWhitelistedByPolicy(const GURL& url,
-                              StringListPrefMember* pref_member);
-
-// Helper function to determine if |url| matches Safe Browsing whitelist domains
-// (a.k. a prefs::kSafeBrowsingWhitelistDomains).
-// Called on UI thread.
-bool IsURLWhitelistedByPolicy(const GURL& url, const PrefService& pref);
-
-// Helper function to get the pref value of password protection login URLs.
-void GetPasswordProtectionLoginURLsPref(const PrefService& prefs,
-                                        std::vector<GURL>* out_login_url_list);
-
-// Helper function that returns true if |url| matches any password protection
-// login URLs. Returns false otherwise.
-bool MatchesPasswordProtectionLoginURL(const GURL& url,
-                                       const PrefService& prefs);
-
-// Helper function to get the pref value of password protection change password
-// URL.
-GURL GetPasswordProtectionChangePasswordURLPref(const PrefService& prefs);
-
-// Helper function that returns true if |url| matches password protection
-// change password URL. Returns false otherwise.
-bool MatchesPasswordProtectionChangePasswordURL(const GURL& url,
-                                                const PrefService& prefs);
-
-// Helper function to match a |target_url| against |url_list|.
-bool MatchesURLList(const GURL& target_url, const std::vector<GURL> url_list);
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_COMMON_SAFE_BROWSING_PREFS_H_
diff --git a/components/safe_browsing/common/safe_browsing_prefs_unittest.cc b/components/safe_browsing/common/safe_browsing_prefs_unittest.cc
deleted file mode 100644
index 760d640..0000000
--- a/components/safe_browsing/common/safe_browsing_prefs_unittest.cc
+++ /dev/null
@@ -1,176 +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.
-
-#include <string>
-#include <vector>
-
-#include "base/command_line.h"
-#include "base/strings/string_piece.h"
-#include "base/strings/string_util.h"
-#include "base/test/scoped_feature_list.h"
-#include "build/build_config.h"
-#include "components/prefs/pref_registry_simple.h"
-#include "components/prefs/testing_pref_service.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/features.h"
-#include "content/public/test/browser_task_environment.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
-
-namespace safe_browsing {
-
-class SafeBrowsingPrefsTest : public ::testing::Test {
- protected:
-  void SetUp() override {
-    prefs_.registry()->RegisterBooleanPref(
-        prefs::kSafeBrowsingScoutReportingEnabled, false);
-    prefs_.registry()->RegisterBooleanPref(
-        prefs::kSafeBrowsingSawInterstitialScoutReporting, false);
-    prefs_.registry()->RegisterStringPref(
-        prefs::kPasswordProtectionChangePasswordURL, "");
-    prefs_.registry()->RegisterListPref(prefs::kPasswordProtectionLoginURLs);
-    prefs_.registry()->RegisterBooleanPref(
-        prefs::kSafeBrowsingExtendedReportingOptInAllowed, true);
-    prefs_.registry()->RegisterListPref(prefs::kSafeBrowsingWhitelistDomains);
-  }
-
-  void ResetPrefs(bool scout_reporting) {
-    prefs_.SetBoolean(prefs::kSafeBrowsingScoutReportingEnabled,
-                      scout_reporting);
-  }
-
-  void ExpectPrefs(bool scout_reporting) {
-    LOG(INFO) << "Pref values: scout=" << scout_reporting;
-    EXPECT_EQ(scout_reporting,
-              prefs_.GetBoolean(prefs::kSafeBrowsingScoutReportingEnabled));
-  }
-
-  void ExpectPrefsExist(bool scout_reporting) {
-    LOG(INFO) << "Prefs exist: scout=" << scout_reporting;
-    EXPECT_EQ(scout_reporting,
-              prefs_.HasPrefPath(prefs::kSafeBrowsingScoutReportingEnabled));
-  }
-  TestingPrefServiceSimple prefs_;
-
- private:
-  content::BrowserTaskEnvironment task_environment_;
-};
-
-// TODO(crbug.com/881476) disabled for flaky crashes.
-#if defined(OS_WIN)
-#define MAYBE_GetSafeBrowsingExtendedReportingLevel \
-  DISABLED_GetSafeBrowsingExtendedReportingLevel
-#else
-#define MAYBE_GetSafeBrowsingExtendedReportingLevel \
-  GetSafeBrowsingExtendedReportingLevel
-#endif
-TEST_F(SafeBrowsingPrefsTest, MAYBE_GetSafeBrowsingExtendedReportingLevel) {
-  // By Default, extended reporting is off.
-  EXPECT_EQ(SBER_LEVEL_OFF, GetExtendedReportingLevel(prefs_));
-
-  // The value of the Scout pref affects the reporting level directly.
-  ResetPrefs(/*scout_reporting=*/true);
-  EXPECT_EQ(SBER_LEVEL_SCOUT, GetExtendedReportingLevel(prefs_));
-  // Scout pref off, so reporting is off.
-  ResetPrefs(/*scout_reporting=*/false);
-  EXPECT_EQ(SBER_LEVEL_OFF, GetExtendedReportingLevel(prefs_));
-}
-
-// TODO(crbug.com/881476) disabled for flaky crashes.
-#if defined(OS_WIN)
-#define MAYBE_VerifyMatchesPasswordProtectionLoginURL \
-  DISABLED_VerifyMatchesPasswordProtectionLoginURL
-#else
-#define MAYBE_VerifyMatchesPasswordProtectionLoginURL \
-  VerifyMatchesPasswordProtectionLoginURL
-#endif
-TEST_F(SafeBrowsingPrefsTest, MAYBE_VerifyMatchesPasswordProtectionLoginURL) {
-  GURL url("https://mydomain.com/login.html#ref?username=alice");
-  EXPECT_FALSE(prefs_.HasPrefPath(prefs::kPasswordProtectionLoginURLs));
-  EXPECT_FALSE(MatchesPasswordProtectionLoginURL(url, prefs_));
-
-  base::ListValue login_urls;
-  login_urls.AppendString("https://otherdomain.com/login.html");
-  prefs_.Set(prefs::kPasswordProtectionLoginURLs, login_urls);
-  EXPECT_TRUE(prefs_.HasPrefPath(prefs::kPasswordProtectionLoginURLs));
-  EXPECT_FALSE(MatchesPasswordProtectionLoginURL(url, prefs_));
-
-  login_urls.AppendString("https://mydomain.com/login.html");
-  prefs_.Set(prefs::kPasswordProtectionLoginURLs, login_urls);
-  EXPECT_TRUE(prefs_.HasPrefPath(prefs::kPasswordProtectionLoginURLs));
-  EXPECT_TRUE(MatchesPasswordProtectionLoginURL(url, prefs_));
-}
-
-TEST_F(SafeBrowsingPrefsTest,
-       VerifyMatchesPasswordProtectionChangePasswordURL) {
-  GURL url("https://mydomain.com/change_password.html#ref?username=alice");
-  EXPECT_FALSE(prefs_.HasPrefPath(prefs::kPasswordProtectionChangePasswordURL));
-  EXPECT_FALSE(MatchesPasswordProtectionChangePasswordURL(url, prefs_));
-
-  prefs_.SetString(prefs::kPasswordProtectionChangePasswordURL,
-                   "https://otherdomain.com/change_password.html");
-  EXPECT_TRUE(prefs_.HasPrefPath(prefs::kPasswordProtectionChangePasswordURL));
-  EXPECT_FALSE(MatchesPasswordProtectionChangePasswordURL(url, prefs_));
-
-  prefs_.SetString(prefs::kPasswordProtectionChangePasswordURL,
-                   "https://mydomain.com/change_password.html");
-  EXPECT_TRUE(prefs_.HasPrefPath(prefs::kPasswordProtectionChangePasswordURL));
-  EXPECT_TRUE(MatchesPasswordProtectionChangePasswordURL(url, prefs_));
-}
-
-TEST_F(SafeBrowsingPrefsTest, IsExtendedReportingPolicyManaged) {
-  // This test checks that manipulating SBEROptInAllowed and the management
-  // state of SBER behaves as expected. Below, we describe what should happen
-  // to the results of IsExtendedReportingPolicyManaged and
-  // IsExtendedReportingOptInAllowed.
-
-  // Confirm default state, SBER should be disabled, OptInAllowed should
-  // be enabled, and SBER is not managed.
-  EXPECT_FALSE(IsExtendedReportingEnabled(prefs_));
-  EXPECT_TRUE(IsExtendedReportingOptInAllowed(prefs_));
-  EXPECT_FALSE(IsExtendedReportingPolicyManaged(prefs_));
-
-  // Setting SBEROptInAllowed to false disallows opt-in but doesn't change
-  // whether SBER is managed.
-  prefs_.SetBoolean(prefs::kSafeBrowsingExtendedReportingOptInAllowed, false);
-  EXPECT_FALSE(IsExtendedReportingOptInAllowed(prefs_));
-  EXPECT_FALSE(IsExtendedReportingPolicyManaged(prefs_));
-  // Setting the value back to true reverts back to the default.
-  prefs_.SetBoolean(prefs::kSafeBrowsingExtendedReportingOptInAllowed, true);
-  EXPECT_TRUE(IsExtendedReportingOptInAllowed(prefs_));
-  EXPECT_FALSE(IsExtendedReportingPolicyManaged(prefs_));
-
-  // Make the SBER pref managed and enable it and ensure that the pref gets
-  // the expected value. Making SBER managed doesn't change the
-  // SBEROptInAllowed setting.
-  prefs_.SetManagedPref(prefs::kSafeBrowsingScoutReportingEnabled,
-                        std::make_unique<base::Value>(true));
-  EXPECT_TRUE(
-      prefs_.IsManagedPreference(prefs::kSafeBrowsingScoutReportingEnabled));
-  // The value of the pref comes from the policy.
-  EXPECT_TRUE(IsExtendedReportingEnabled(prefs_));
-  // SBER being managed doesn't change the SBEROptInAllowed pref.
-  EXPECT_TRUE(IsExtendedReportingOptInAllowed(prefs_));
-}
-
-TEST_F(SafeBrowsingPrefsTest, VerifyIsURLWhitelistedByPolicy) {
-  GURL target_url("https://www.foo.com");
-  // When PrefMember is null, URL is not whitelisted.
-  EXPECT_FALSE(IsURLWhitelistedByPolicy(target_url, nullptr));
-
-  EXPECT_FALSE(prefs_.HasPrefPath(prefs::kSafeBrowsingWhitelistDomains));
-  base::ListValue whitelisted_domains;
-  whitelisted_domains.AppendString("foo.com");
-  prefs_.Set(prefs::kSafeBrowsingWhitelistDomains, whitelisted_domains);
-  StringListPrefMember string_list_pref;
-  string_list_pref.Init(prefs::kSafeBrowsingWhitelistDomains, &prefs_);
-  EXPECT_TRUE(IsURLWhitelistedByPolicy(target_url, prefs_));
-  EXPECT_TRUE(IsURLWhitelistedByPolicy(target_url, &string_list_pref));
-
-  GURL not_whitelisted_url("https://www.bar.com");
-  EXPECT_FALSE(IsURLWhitelistedByPolicy(not_whitelisted_url, prefs_));
-  EXPECT_FALSE(
-      IsURLWhitelistedByPolicy(not_whitelisted_url, &string_list_pref));
-}
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/common/safebrowsing_constants.cc b/components/safe_browsing/common/safebrowsing_constants.cc
deleted file mode 100644
index 1e2569eb..0000000
--- a/components/safe_browsing/common/safebrowsing_constants.cc
+++ /dev/null
@@ -1,28 +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.
-
-#include "components/safe_browsing/common/safebrowsing_constants.h"
-
-#include "components/safe_browsing/features.h"
-#include "net/base/net_errors.h"
-
-namespace safe_browsing {
-
-const base::FilePath::CharType kSafeBrowsingBaseFilename[] =
-    FILE_PATH_LITERAL("Safe Browsing");
-
-const base::FilePath::CharType kCookiesFile[] = FILE_PATH_LITERAL(" Cookies");
-
-// The URL for the Safe Browsing page.
-const char kSafeBrowsingUrl[] = "https://safebrowsing.google.com/";
-
-const char kCustomCancelReasonForURLLoader[] = "SafeBrowsing";
-
-int GetNetErrorCodeForSafeBrowsing() {
-  return base::FeatureList::IsEnabled(safe_browsing::kCommittedSBInterstitials)
-             ? net::ERR_BLOCKED_BY_CLIENT
-             : net::ERR_ABORTED;
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/common/safebrowsing_constants.h b/components/safe_browsing/common/safebrowsing_constants.h
deleted file mode 100644
index 0aea673..0000000
--- a/components/safe_browsing/common/safebrowsing_constants.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_COMMON_SAFEBROWSING_CONSTANTS_H_
-#define COMPONENTS_SAFE_BROWSING_COMMON_SAFEBROWSING_CONSTANTS_H_
-
-#include "base/files/file_path.h"
-
-namespace safe_browsing {
-
-extern const base::FilePath::CharType kSafeBrowsingBaseFilename[];
-
-// Filename suffix for the cookie database.
-extern const base::FilePath::CharType kCookiesFile[];
-
-// The URL for the Safe Browsing page.
-extern const char kSafeBrowsingUrl[];
-
-// When a network::mojom::URLLoader is cancelled because of SafeBrowsing, this
-// custom cancellation reason could be used to notify the implementation side.
-// Please see network::mojom::URLLoader::kClientDisconnectReason for more
-// details.
-extern const char kCustomCancelReasonForURLLoader[];
-
-// Returns the error_code to use when Safe Browsing blocks a request.
-int GetNetErrorCodeForSafeBrowsing();
-}
-
-#endif  // COMPONENTS_SAFE_BROWSING_COMMON_SAFEBROWSING_CONSTANTS_H_
diff --git a/components/safe_browsing/common/safebrowsing_switches.cc b/components/safe_browsing/common/safebrowsing_switches.cc
deleted file mode 100644
index 5f62d06..0000000
--- a/components/safe_browsing/common/safebrowsing_switches.cc
+++ /dev/null
@@ -1,20 +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.
-
-#include "components/safe_browsing/common/safebrowsing_switches.h"
-
-namespace safe_browsing {
-namespace switches {
-
-// List of comma-separated sha256 hashes of executable files which the
-// download-protection service should treat as "dangerous."  For a file to
-// show a warning, it also must be considered a dangerous filetype and not
-// be whitelisted otherwise (by signature or URL) and must be on a supported
-// OS. Hashes are in hex. This is used for manual testing when looking
-// for ways to by-pass download protection.
-const char kSbManualDownloadBlacklist[] =
-    "safebrowsing-manual-download-blacklist";
-
-}  // namespace switches
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/common/safebrowsing_switches.h b/components/safe_browsing/common/safebrowsing_switches.h
deleted file mode 100644
index a4c14da..0000000
--- a/components/safe_browsing/common/safebrowsing_switches.h
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_COMMON_SAFEBROWSING_SWITCHES_H_
-#define COMPONENTS_SAFE_BROWSING_COMMON_SAFEBROWSING_SWITCHES_H_
-
-namespace safe_browsing {
-namespace switches {
-
-extern const char kSbManualDownloadBlacklist[];
-
-}  // namespace switches
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_COMMON_SAFEBROWSING_SWITCHES_H_
diff --git a/components/safe_browsing/common/utils.cc b/components/safe_browsing/common/utils.cc
deleted file mode 100644
index 6ed6e625..0000000
--- a/components/safe_browsing/common/utils.cc
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/common/utils.h"
-
-#include "base/metrics/histogram_macros.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/time/time.h"
-#include "build/build_config.h"
-#include "components/policy/core/browser/browser_policy_connector.h"
-#include "components/prefs/pref_service.h"
-#include "crypto/sha2.h"
-
-#if defined(OS_WIN)
-#include "base/enterprise_util.h"
-#endif
-
-namespace safe_browsing {
-
-std::string ShortURLForReporting(const GURL& url) {
-  std::string spec(url.spec());
-  if (url.SchemeIs(url::kDataScheme)) {
-    size_t comma_pos = spec.find(',');
-    if (comma_pos != std::string::npos && comma_pos != spec.size() - 1) {
-      std::string hash_value = crypto::SHA256HashString(spec);
-      spec.erase(comma_pos + 1);
-      spec += base::HexEncode(hash_value.data(), hash_value.size());
-    }
-  }
-  return spec;
-}
-
-ChromeUserPopulation::ProfileManagementStatus GetProfileManagementStatus(
-    const policy::BrowserPolicyConnector* bpc) {
-#if defined(OS_WIN)
-  if (base::IsMachineExternallyManaged())
-    return ChromeUserPopulation::ENTERPRISE_MANAGED;
-  else
-    return ChromeUserPopulation::NOT_MANAGED;
-#elif defined(OS_CHROMEOS)
-  if (!bpc || !bpc->IsEnterpriseManaged())
-    return ChromeUserPopulation::NOT_MANAGED;
-  return ChromeUserPopulation::ENTERPRISE_MANAGED;
-#else
-  return ChromeUserPopulation::UNAVAILABLE;
-#endif  // #if defined(OS_WIN) || defined(OS_CHROMEOS)
-}
-
-void SetDelayInPref(PrefService* prefs,
-                    const char* pref_name,
-                    const base::TimeDelta& delay) {
-  base::Time next_event = base::Time::Now() + delay;
-  int64_t seconds_since_epoch =
-      next_event.ToDeltaSinceWindowsEpoch().InSeconds();
-  prefs->SetInt64(pref_name, seconds_since_epoch);
-}
-
-base::TimeDelta GetDelayFromPref(PrefService* prefs, const char* pref_name) {
-  const base::TimeDelta zero_delay;
-  if (!prefs->HasPrefPath(pref_name))
-    return zero_delay;
-
-  int64_t seconds_since_epoch = prefs->GetInt64(pref_name);
-  if (seconds_since_epoch <= 0)
-    return zero_delay;
-
-  base::Time next_event = base::Time::FromDeltaSinceWindowsEpoch(
-      base::TimeDelta::FromSeconds(seconds_since_epoch));
-  base::Time now = base::Time::Now();
-  if (now > next_event)
-    return zero_delay;
-  else
-    return next_event - now;
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/common/utils.h b/components/safe_browsing/common/utils.h
deleted file mode 100644
index 230e7cb..0000000
--- a/components/safe_browsing/common/utils.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-// Safe Browsing utility functions.
-
-#ifndef COMPONENTS_SAFE_BROWSING_COMMON_UTILS_H_
-#define COMPONENTS_SAFE_BROWSING_COMMON_UTILS_H_
-
-#include "base/time/time.h"
-#include "components/safe_browsing/proto/csd.pb.h"
-#include "url/gurl.h"
-
-namespace policy {
-class BrowserPolicyConnector;
-}  // namespace policy
-
-namespace base {
-class TimeDelta;
-}  // namespace base
-
-class PrefService;
-
-namespace safe_browsing {
-
-// Shorten URL by replacing its contents with its SHA256 hash if it has data
-// scheme.
-std::string ShortURLForReporting(const GURL& url);
-
-// Gets the |ProfileManagementStatus| for the current machine. The method
-// currently works only on Windows and ChromeOS. The |bpc| parameter is used
-// only on ChromeOS, and may be |nullptr|.
-ChromeUserPopulation::ProfileManagementStatus GetProfileManagementStatus(
-    const policy::BrowserPolicyConnector* bpc);
-
-// Util for storing a future alarm time in a pref. |delay| is how much time into
-// the future the alarm is set for. Calling GetDelayFromPref() later will return
-// a shorter delay, or 0 if it's unset or passed..
-void SetDelayInPref(PrefService* prefs,
-                    const char* pref_name,
-                    const base::TimeDelta& delay);
-base::TimeDelta GetDelayFromPref(PrefService* prefs, const char* pref_name);
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_COMMON_UTILS_H_
diff --git a/components/safe_browsing/content/BUILD.gn b/components/safe_browsing/content/BUILD.gn
new file mode 100644
index 0000000..46504ef1
--- /dev/null
+++ b/components/safe_browsing/content/BUILD.gn
@@ -0,0 +1,42 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import("//build/buildflag_header.gni")
+import("//components/safe_browsing/buildflags.gni")
+
+# safe_browsing/ pulls in content/, which doesn't work on iOS.
+# TODO(thakis): This should be `safe_browsing_mode != 0`, but chromecast builds
+# set safe_browsing_mode to 0 and build chrome/, and chrome/ currently
+# unconditionally depends on things in this build file. Make these dependencies
+# conditional on safe_browsing_mode != 0 and then change the conditional here.
+if (!is_ios) {
+  assert(!is_ios, "safe_browsing/ pulls in content/ which doesn't work on iOS")
+  source_set("content") {
+    sources = [
+      "base_blocking_page.cc",
+      "base_blocking_page.h",
+      "base_ui_manager.cc",
+      "base_ui_manager.h",
+      "safe_browsing_controller_client.cc",
+      "safe_browsing_controller_client.h",
+    ]
+    public_deps = [
+      "//components/security_interstitials/content:security_interstitial_page",
+    ]
+    deps = [
+      "//base:base",
+      "//base:i18n",
+      "//components/safe_browsing/core:features",
+      "//components/safe_browsing/core:ping_manager",
+      "//components/safe_browsing/core:verdict_cache_manager",
+      "//components/safe_browsing/core/common:common",
+      "//components/safe_browsing/core/common:safe_browsing_prefs",
+      "//components/safe_browsing/core/db:database_manager",
+      "//components/safe_browsing/core/web_ui:constants",
+      "//components/security_interstitials/core:core",
+      "//content/public/browser:browser",
+      "//content/public/common:common",
+      "//net:net",
+    ]
+  }
+}
diff --git a/components/safe_browsing/content/base_blocking_page.cc b/components/safe_browsing/content/base_blocking_page.cc
new file mode 100644
index 0000000..8eb9bb7
--- /dev/null
+++ b/components/safe_browsing/content/base_blocking_page.cc
@@ -0,0 +1,422 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/content/base_blocking_page.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/feature_list.h"
+#include "base/lazy_instance.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/time/time.h"
+#include "components/safe_browsing/content/safe_browsing_controller_client.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/features.h"
+#include "components/security_interstitials/content/security_interstitial_controller_client.h"
+#include "components/security_interstitials/core/metrics_helper.h"
+#include "components/security_interstitials/core/safe_browsing_loud_error_ui.h"
+#include "content/public/browser/interstitial_page.h"
+#include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/web_contents.h"
+
+using content::InterstitialPage;
+using content::WebContents;
+using security_interstitials::BaseSafeBrowsingErrorUI;
+using security_interstitials::SafeBrowsingLoudErrorUI;
+using security_interstitials::SecurityInterstitialControllerClient;
+
+namespace safe_browsing {
+
+namespace {
+
+// After a safe browsing interstitial where the user opted-in to the report
+// but clicked "proceed anyway", we delay the call to
+// ThreatDetails::FinishCollection() by this much time (in
+// milliseconds).
+const int64_t kThreatDetailsProceedDelayMilliSeconds = 3000;
+
+base::LazyInstance<BaseBlockingPage::UnsafeResourceMap>::Leaky
+    g_unsafe_resource_map = LAZY_INSTANCE_INITIALIZER;
+
+}  // namespace
+
+BaseBlockingPage::BaseBlockingPage(
+    BaseUIManager* ui_manager,
+    WebContents* web_contents,
+    const GURL& main_frame_url,
+    const UnsafeResourceList& unsafe_resources,
+    std::unique_ptr<SecurityInterstitialControllerClient> controller_client,
+    const BaseSafeBrowsingErrorUI::SBErrorDisplayOptions& display_options)
+    : SecurityInterstitialPage(web_contents,
+                               unsafe_resources[0].url,
+                               std::move(controller_client)),
+      ui_manager_(ui_manager),
+      main_frame_url_(main_frame_url),
+      navigation_entry_index_to_remove_(
+          IsMainPageLoadBlocked(unsafe_resources) ||
+                  base::FeatureList::IsEnabled(kCommittedSBInterstitials)
+              ? -1
+              : web_contents->GetController().GetLastCommittedEntryIndex()),
+      unsafe_resources_(unsafe_resources),
+      proceeded_(false),
+      threat_details_proceed_delay_ms_(kThreatDetailsProceedDelayMilliSeconds),
+      sb_error_ui_(std::make_unique<SafeBrowsingLoudErrorUI>(
+          unsafe_resources_[0].url,
+          main_frame_url_,
+          GetInterstitialReason(unsafe_resources_),
+          display_options,
+          ui_manager->app_locale(),
+          base::Time::NowFromSystemTime(),
+          controller(),
+          /* created_prior_to_navigation */
+          IsMainPageLoadBlocked(unsafe_resources) &&
+              base::FeatureList::IsEnabled(kCommittedSBInterstitials))) {}
+
+BaseBlockingPage::~BaseBlockingPage() {}
+
+// static
+const security_interstitials::BaseSafeBrowsingErrorUI::SBErrorDisplayOptions
+BaseBlockingPage::CreateDefaultDisplayOptions(
+    const UnsafeResourceList& unsafe_resources) {
+  return BaseSafeBrowsingErrorUI::SBErrorDisplayOptions(
+      IsMainPageLoadBlocked(unsafe_resources),
+      false,                 // kSafeBrowsingExtendedReportingOptInAllowed
+      false,                 // is_off_the_record
+      false,                 // is_extended_reporting
+      false,                 // is_sber_policy_managed
+      false,                 // kSafeBrowsingProceedAnywayDisabled
+      false,                 // should_open_links_in_new_tab
+      true,                  // always_show_back_to_safety
+      "cpn_safe_browsing");  // help_center_article_link
+}
+
+// static
+void BaseBlockingPage::ShowBlockingPage(
+    BaseUIManager* ui_manager,
+    const UnsafeResource& unsafe_resource) {
+  WebContents* web_contents = unsafe_resource.web_contents_getter.Run();
+
+  if (InterstitialPage::GetInterstitialPage(web_contents) &&
+      unsafe_resource.is_subresource) {
+    // This is an interstitial for a page's resource, let's queue it.
+    UnsafeResourceMap* unsafe_resource_map = GetUnsafeResourcesMap();
+    (*unsafe_resource_map)[web_contents].push_back(unsafe_resource);
+  } else {
+    // There is no interstitial currently showing in that tab, or we are about
+    // to display a new one for the main frame. If there is already an
+    // interstitial, showing the new one will automatically hide the old one.
+    content::NavigationEntry* entry =
+        unsafe_resource.GetNavigationEntryForResource();
+    const UnsafeResourceList unsafe_resources{unsafe_resource};
+    BaseBlockingPage* blocking_page = new BaseBlockingPage(
+        ui_manager, web_contents, entry ? entry->GetURL() : GURL(),
+        unsafe_resources,
+        CreateControllerClient(web_contents, unsafe_resources, ui_manager,
+                               nullptr),
+        CreateDefaultDisplayOptions(unsafe_resources));
+    blocking_page->Show();
+  }
+}
+
+// static
+bool BaseBlockingPage::IsMainPageLoadBlocked(
+    const UnsafeResourceList& unsafe_resources) {
+  // If there is more than one unsafe resource, the main page load must not be
+  // blocked. Otherwise, check if the one resource is.
+  return unsafe_resources.size() == 1 &&
+         unsafe_resources[0].IsMainPageLoadBlocked();
+}
+
+void BaseBlockingPage::OnProceed() {
+  set_proceeded(true);
+  OnInterstitialClosing();
+
+  // Send the threat details, if we opted to.
+  FinishThreatDetails(
+      base::TimeDelta::FromMilliseconds(threat_details_proceed_delay_ms_),
+      true, /* did_proceed */
+      controller()->metrics_helper()->NumVisits());
+
+  ui_manager_->OnBlockingPageDone(unsafe_resources_, true /* proceed */,
+                                  web_contents(), main_frame_url_);
+
+  HandleSubresourcesAfterProceed();
+}
+
+void BaseBlockingPage::HandleSubresourcesAfterProceed() {}
+
+void BaseBlockingPage::SetThreatDetailsProceedDelayForTesting(int64_t delay) {
+  threat_details_proceed_delay_ms_ = delay;
+}
+
+void BaseBlockingPage::OnDontProceed() {
+  // With committed interstitials we shouldn't hit this code.
+  DCHECK(
+      !base::FeatureList::IsEnabled(safe_browsing::kCommittedSBInterstitials));
+
+  // We could have already called Proceed(), in which case we must not notify
+  // the SafeBrowsingUIManager again, as the client has been deleted.
+  if (proceeded_)
+    return;
+
+  OnInterstitialClosing();
+
+  // Send the malware details, if we opted to.
+  FinishThreatDetails(base::TimeDelta(), false /* did_proceed */,
+                      controller()->metrics_helper()->NumVisits());  // No delay
+
+  OnDontProceedDone();
+}
+
+void BaseBlockingPage::CommandReceived(const std::string& page_cmd) {
+  if (page_cmd == "\"pageLoadComplete\"") {
+    // content::WaitForRenderFrameReady sends this message when the page
+    // load completes. Ignore it.
+    return;
+  }
+
+  int command = 0;
+  bool retval = base::StringToInt(page_cmd, &command);
+  DCHECK(retval) << page_cmd;
+  auto interstitial_command =
+      static_cast<security_interstitials::SecurityInterstitialCommand>(command);
+
+  if (base::FeatureList::IsEnabled(safe_browsing::kCommittedSBInterstitials) &&
+      interstitial_command ==
+          security_interstitials::SecurityInterstitialCommand::CMD_PROCEED) {
+    // With committed interstitials, OnProceed() doesn't get called, so handle
+    // adding to the allow list here.
+    set_proceeded(true);
+    ui_manager()->OnBlockingPageDone(unsafe_resources(), true /* proceed */,
+                                     web_contents(), main_frame_url());
+  }
+
+  sb_error_ui_->HandleCommand(interstitial_command);
+}
+
+bool BaseBlockingPage::ShouldCreateNewNavigation() const {
+  return sb_error_ui_->is_main_frame_load_blocked();
+}
+
+void BaseBlockingPage::PopulateInterstitialStrings(
+    base::DictionaryValue* load_time_data) {
+  sb_error_ui_->PopulateStringsForHtml(load_time_data);
+}
+
+void BaseBlockingPage::OnInterstitialClosing() {
+  UpdateMetricsAfterSecurityInterstitial();
+}
+
+void BaseBlockingPage::FinishThreatDetails(const base::TimeDelta& delay,
+                                           bool did_proceed,
+                                           int num_visits) {}
+
+// static
+BaseBlockingPage::UnsafeResourceMap*
+BaseBlockingPage::GetUnsafeResourcesMap() {
+  return g_unsafe_resource_map.Pointer();
+}
+
+// static
+std::string BaseBlockingPage::GetMetricPrefix(
+    const UnsafeResourceList& unsafe_resources,
+    BaseSafeBrowsingErrorUI::SBInterstitialReason interstitial_reason) {
+  bool primary_subresource = unsafe_resources[0].is_subresource;
+  switch (interstitial_reason) {
+    case BaseSafeBrowsingErrorUI::SB_REASON_MALWARE:
+      return primary_subresource ? "malware_subresource" : "malware";
+    case BaseSafeBrowsingErrorUI::SB_REASON_HARMFUL:
+      return primary_subresource ? "harmful_subresource" : "harmful";
+    case BaseSafeBrowsingErrorUI::SB_REASON_BILLING:
+      return primary_subresource ? "billing_subresource" : "billing";
+    case BaseSafeBrowsingErrorUI::SB_REASON_PHISHING:
+      ThreatPatternType threat_pattern_type =
+          unsafe_resources[0].threat_metadata.threat_pattern_type;
+      if (threat_pattern_type == ThreatPatternType::PHISHING ||
+          threat_pattern_type == ThreatPatternType::NONE)
+        return primary_subresource ? "phishing_subresource" : "phishing";
+      else if (threat_pattern_type == ThreatPatternType::SOCIAL_ENGINEERING_ADS)
+        return primary_subresource ? "social_engineering_ads_subresource"
+                                   : "social_engineering_ads";
+      else if (threat_pattern_type ==
+               ThreatPatternType::SOCIAL_ENGINEERING_LANDING)
+        return primary_subresource ? "social_engineering_landing_subresource"
+                                   : "social_engineering_landing";
+  }
+  NOTREACHED();
+  return "unkown_metric_prefix";
+}
+
+// We populate a parallel set of metrics to differentiate some threat sources.
+// static
+std::string BaseBlockingPage::GetExtraMetricsSuffix(
+    const UnsafeResourceList& unsafe_resources) {
+  switch (unsafe_resources[0].threat_source) {
+    case safe_browsing::ThreatSource::DATA_SAVER:
+      return "from_data_saver";
+    case safe_browsing::ThreatSource::REMOTE:
+    case safe_browsing::ThreatSource::LOCAL_PVER3:
+      // REMOTE and LOCAL_PVER3 can be distinguished in the logs
+      // by platform type: Remote is mobile, local_pver3 is desktop.
+      return "from_device";
+    case safe_browsing::ThreatSource::LOCAL_PVER4:
+      return "from_device_v4";
+    case safe_browsing::ThreatSource::CLIENT_SIDE_DETECTION:
+      return "from_client_side_detection";
+    case safe_browsing::ThreatSource::PASSWORD_PROTECTION_SERVICE:
+      return "from_password_protection_service";
+    case safe_browsing::ThreatSource::UNKNOWN:
+      break;
+  }
+  NOTREACHED();
+  return std::string();
+}
+
+// static
+security_interstitials::BaseSafeBrowsingErrorUI::SBInterstitialReason
+BaseBlockingPage::GetInterstitialReason(
+    const UnsafeResourceList& unsafe_resources) {
+  bool harmful = false;
+  for (auto iter = unsafe_resources.begin(); iter != unsafe_resources.end();
+       ++iter) {
+    const BaseUIManager::UnsafeResource& resource = *iter;
+    safe_browsing::SBThreatType threat_type = resource.threat_type;
+    if (threat_type == SB_THREAT_TYPE_BILLING)
+      return BaseSafeBrowsingErrorUI::SB_REASON_BILLING;
+
+    if (threat_type == SB_THREAT_TYPE_URL_MALWARE ||
+        threat_type == SB_THREAT_TYPE_URL_CLIENT_SIDE_MALWARE) {
+      return BaseSafeBrowsingErrorUI::SB_REASON_MALWARE;
+    }
+
+    if (threat_type == SB_THREAT_TYPE_URL_UNWANTED) {
+      harmful = true;
+    } else {
+      DCHECK(threat_type == SB_THREAT_TYPE_URL_PHISHING ||
+             threat_type == SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING);
+    }
+  }
+
+  if (harmful)
+    return BaseSafeBrowsingErrorUI::SB_REASON_HARMFUL;
+  return BaseSafeBrowsingErrorUI::SB_REASON_PHISHING;
+}
+
+BaseUIManager* BaseBlockingPage::ui_manager() const {
+  return ui_manager_;
+}
+
+const GURL BaseBlockingPage::main_frame_url() const {
+  return main_frame_url_;
+}
+
+BaseBlockingPage::UnsafeResourceList
+BaseBlockingPage::unsafe_resources() const {
+  return unsafe_resources_;
+}
+
+bool BaseBlockingPage::proceeded() const {
+  return proceeded_;
+}
+
+int64_t BaseBlockingPage::threat_details_proceed_delay() const {
+  return threat_details_proceed_delay_ms_;
+}
+
+BaseSafeBrowsingErrorUI* BaseBlockingPage::sb_error_ui() const {
+  return sb_error_ui_.get();
+}
+
+void BaseBlockingPage::set_proceeded(bool proceeded) {
+  proceeded_ = proceeded;
+}
+
+// static
+security_interstitials::MetricsHelper::ReportDetails
+BaseBlockingPage::GetReportingInfo(const UnsafeResourceList& unsafe_resources) {
+  BaseSafeBrowsingErrorUI::SBInterstitialReason interstitial_reason =
+      GetInterstitialReason(unsafe_resources);
+
+  security_interstitials::MetricsHelper::ReportDetails reporting_info;
+  reporting_info.metric_prefix =
+      GetMetricPrefix(unsafe_resources, interstitial_reason);
+  reporting_info.extra_suffix = GetExtraMetricsSuffix(unsafe_resources);
+  return reporting_info;
+}
+
+// static
+std::unique_ptr<SecurityInterstitialControllerClient>
+BaseBlockingPage::CreateControllerClient(
+    content::WebContents* web_contents,
+    const UnsafeResourceList& unsafe_resources,
+    BaseUIManager* ui_manager,
+    PrefService* pref_service) {
+  history::HistoryService* history_service =
+      ui_manager->history_service(web_contents);
+
+  std::unique_ptr<security_interstitials::MetricsHelper> metrics_helper =
+      std::make_unique<security_interstitials::MetricsHelper>(
+          unsafe_resources[0].url, GetReportingInfo(unsafe_resources),
+          history_service);
+
+  return std::make_unique<SafeBrowsingControllerClient>(
+      web_contents, std::move(metrics_helper), pref_service,
+      ui_manager->app_locale(), ui_manager->default_safe_page());
+}
+
+int BaseBlockingPage::GetHTMLTemplateId() {
+  return sb_error_ui_->GetHTMLTemplateId();
+}
+
+void BaseBlockingPage::set_sb_error_ui(
+    std::unique_ptr<BaseSafeBrowsingErrorUI> sb_error_ui) {
+  sb_error_ui_ = std::move(sb_error_ui);
+}
+
+void BaseBlockingPage::OnDontProceedDone() {
+  if (!sb_error_ui_->is_proceed_anyway_disabled()) {
+    controller()->metrics_helper()->RecordUserDecision(
+        security_interstitials::MetricsHelper::DONT_PROCEED);
+  }
+
+  ui_manager_->OnBlockingPageDone(unsafe_resources_, false /* proceed */,
+                                  web_contents(), main_frame_url_);
+
+  // The user does not want to proceed, clear the queued unsafe resources
+  // notifications we received while the interstitial was showing.
+  UnsafeResourceMap* unsafe_resource_map = GetUnsafeResourcesMap();
+  auto iter = unsafe_resource_map->find(web_contents());
+  if (iter != unsafe_resource_map->end() && !iter->second.empty()) {
+    ui_manager_->OnBlockingPageDone(iter->second, false, web_contents(),
+                                    main_frame_url_);
+    unsafe_resource_map->erase(iter);
+  }
+
+  // We don't remove the navigation entry if the tab is being destroyed as this
+  // would trigger a navigation that would cause trouble as the render view host
+  // for the tab has by then already been destroyed.  We also don't delete the
+  // current entry if it has been committed again, which is possible on a page
+  // that had a subresource warning.
+  const int last_committed_index =
+      web_contents()->GetController().GetLastCommittedEntryIndex();
+  if (navigation_entry_index_to_remove_ != -1 &&
+      navigation_entry_index_to_remove_ != last_committed_index &&
+      !web_contents()->IsBeingDestroyed()) {
+    CHECK(web_contents()->GetController().RemoveEntryAtIndex(
+        navigation_entry_index_to_remove_));
+  }
+}
+
+// static
+bool BaseBlockingPage::ShouldReportThreatDetails(SBThreatType threat_type) {
+  return threat_type == SB_THREAT_TYPE_BILLING ||
+         threat_type == SB_THREAT_TYPE_URL_CLIENT_SIDE_MALWARE ||
+         threat_type == SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING ||
+         threat_type == SB_THREAT_TYPE_URL_MALWARE ||
+         threat_type == SB_THREAT_TYPE_URL_PHISHING ||
+         threat_type == SB_THREAT_TYPE_URL_UNWANTED;
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/content/base_blocking_page.h b/components/safe_browsing/content/base_blocking_page.h
new file mode 100644
index 0000000..a1bba3c
--- /dev/null
+++ b/components/safe_browsing/content/base_blocking_page.h
@@ -0,0 +1,167 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CONTENT_BASE_BLOCKING_PAGE_H_
+#define COMPONENTS_SAFE_BROWSING_CONTENT_BASE_BLOCKING_PAGE_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "components/safe_browsing/content/base_ui_manager.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
+#include "components/security_interstitials/content/security_interstitial_page.h"
+#include "components/security_interstitials/core/base_safe_browsing_error_ui.h"
+#include "components/security_interstitials/core/metrics_helper.h"
+#include "content/public/browser/interstitial_page_delegate.h"
+#include "url/gurl.h"
+
+namespace safe_browsing {
+
+// Base class for managing the SafeBrowsing interstitial pages.
+class BaseBlockingPage
+    : public security_interstitials::SecurityInterstitialPage {
+ public:
+  typedef security_interstitials::UnsafeResource UnsafeResource;
+  typedef security_interstitials::BaseSafeBrowsingErrorUI
+      BaseSafeBrowsingErrorUI;
+  typedef std::vector<UnsafeResource> UnsafeResourceList;
+  typedef std::unordered_map<content::WebContents*, UnsafeResourceList>
+      UnsafeResourceMap;
+
+  ~BaseBlockingPage() override;
+
+  static const BaseSafeBrowsingErrorUI::SBErrorDisplayOptions
+  CreateDefaultDisplayOptions(const UnsafeResourceList& unsafe_resources);
+
+  // Shows a blocking page warning the user about phishing/malware for a
+  // specific resource.
+  // This can be called several times. If an interstitial is already showing
+  // and the user decides to proceed, it will be discarded and a new one will be
+  // displayed.
+  static void ShowBlockingPage(BaseUIManager* ui_manager,
+                               const UnsafeResource& resource);
+
+  // Returns true if the passed |unsafe_resources| is blocking the load of
+  // the main page.
+  static bool IsMainPageLoadBlocked(const UnsafeResourceList& unsafe_resources);
+
+  // InterstitialPageDelegate methods:
+  void OnProceed() override;
+  void OnDontProceed() override;
+  void CommandReceived(const std::string& command) override;
+
+  // Checks the threat type to decide if we should report ThreatDetails.
+  static bool ShouldReportThreatDetails(SBThreatType threat_type);
+
+ protected:
+  // Don't instantiate this class directly, use ShowBlockingPage instead.
+  BaseBlockingPage(
+      BaseUIManager* ui_manager,
+      content::WebContents* web_contents,
+      const GURL& main_frame_url,
+      const UnsafeResourceList& unsafe_resources,
+      std::unique_ptr<
+          security_interstitials::SecurityInterstitialControllerClient>
+          controller_client,
+      const BaseSafeBrowsingErrorUI::SBErrorDisplayOptions& display_options);
+
+  // SecurityInterstitialPage methods:
+  bool ShouldCreateNewNavigation() const override;
+  void PopulateInterstitialStrings(
+      base::DictionaryValue* load_time_data) override;
+  void OnInterstitialClosing() override;
+
+  // Called when the interstitial is going away. Intentionally do nothing in
+  // this base class.
+  virtual void FinishThreatDetails(const base::TimeDelta& delay,
+                                   bool did_proceed,
+                                   int num_visits);
+
+  // A list of SafeBrowsingUIManager::UnsafeResource for a tab that the user
+  // should be warned about. They are queued when displaying more than one
+  // interstitial at a time.
+  static UnsafeResourceMap* GetUnsafeResourcesMap();
+
+  static std::string GetMetricPrefix(
+      const UnsafeResourceList& unsafe_resources,
+      BaseSafeBrowsingErrorUI::SBInterstitialReason interstitial_reason);
+
+  static std::string GetExtraMetricsSuffix(
+      const UnsafeResourceList& unsafe_resources);
+
+  // Return the most severe interstitial reason from a list of unsafe resources.
+  // Severity ranking: malware > UwS (harmful) > phishing.
+  static BaseSafeBrowsingErrorUI::SBInterstitialReason GetInterstitialReason(
+      const UnsafeResourceList& unsafe_resources);
+
+  BaseUIManager* ui_manager() const;
+
+  const GURL main_frame_url() const;
+
+  UnsafeResourceList unsafe_resources() const;
+
+  bool proceeded() const;
+
+  int64_t threat_details_proceed_delay() const;
+
+  BaseSafeBrowsingErrorUI* sb_error_ui() const;
+
+  void set_proceeded(bool proceeded);
+
+  static security_interstitials::MetricsHelper::ReportDetails GetReportingInfo(
+      const UnsafeResourceList& unsafe_resources);
+
+  // Called after OnProceed(). Does nothing in this class, but can be overridden
+  // to handle malicious subresources.
+  virtual void HandleSubresourcesAfterProceed();
+
+  void SetThreatDetailsProceedDelayForTesting(int64_t delay);
+
+  static std::unique_ptr<
+      security_interstitials::SecurityInterstitialControllerClient>
+  CreateControllerClient(content::WebContents* web_contents,
+                         const UnsafeResourceList& unsafe_resources,
+                         BaseUIManager* ui_manager,
+                         PrefService* pref_service);
+
+  int GetHTMLTemplateId() override;
+
+  void set_sb_error_ui(std::unique_ptr<BaseSafeBrowsingErrorUI> sb_error_ui);
+
+  void OnDontProceedDone();
+
+ private:
+  // For reporting back user actions.
+  BaseUIManager* ui_manager_;
+
+  // The URL of the main frame that caused the warning.
+  GURL main_frame_url_;
+
+  // The index of a navigation entry that should be removed when DontProceed()
+  // is invoked, -1 if entry should not be removed.
+  const int navigation_entry_index_to_remove_;
+
+  // The list of unsafe resources this page is warning about.
+  UnsafeResourceList unsafe_resources_;
+
+  // Indicate whether user has proceeded this blocking page.
+  bool proceeded_;
+
+  // After a safe browsing interstitial where the user opted-in to the
+  // report but clicked "proceed anyway", we delay the call to
+  // ThreatDetails::FinishCollection() by this much time (in
+  // milliseconds), in order to get data from the blocked resource itself.
+  int64_t threat_details_proceed_delay_ms_;
+
+  // For displaying safe browsing interstitial.
+  std::unique_ptr<BaseSafeBrowsingErrorUI> sb_error_ui_;
+
+  DISALLOW_COPY_AND_ASSIGN(BaseBlockingPage);
+};
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CONTENT_BASE_BLOCKING_PAGE_H_
diff --git a/components/safe_browsing/content/base_ui_manager.cc b/components/safe_browsing/content/base_ui_manager.cc
new file mode 100644
index 0000000..8568ba4
--- /dev/null
+++ b/components/safe_browsing/content/base_ui_manager.cc
@@ -0,0 +1,446 @@
+// 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 <utility>
+
+#include "components/safe_browsing/content/base_ui_manager.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/feature_list.h"
+#include "base/i18n/rtl.h"
+#include "base/memory/ptr_util.h"
+#include "base/supports_user_data.h"
+#include "components/safe_browsing/content/base_blocking_page.h"
+#include "components/safe_browsing/core/features.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/web_contents.h"
+
+using content::BrowserThread;
+using content::NavigationEntry;
+using content::WebContents;
+using safe_browsing::HitReport;
+using safe_browsing::SBThreatType;
+
+namespace {
+
+const void* const kWhitelistKey = &kWhitelistKey;
+
+// A WhitelistUrlSet holds the set of URLs that have been whitelisted
+// for a specific WebContents, along with pending entries that are still
+// undecided. Each URL is associated with the first SBThreatType that
+// was seen for that URL. The URLs in this set should come from
+// GetWhitelistUrl() or GetMainFrameWhitelistUrlForResource() (in
+// SafeBrowsingUIManager)
+class WhitelistUrlSet : public base::SupportsUserData::Data {
+ public:
+  WhitelistUrlSet() {}
+  bool Contains(const GURL& url, SBThreatType* threat_type) {
+    auto found = map_.find(url);
+    if (found == map_.end())
+      return false;
+    if (threat_type)
+      *threat_type = found->second;
+    return true;
+  }
+  void RemovePending(const GURL& url) {
+    DCHECK(pending_.end() != pending_.find(url));
+    if (--pending_[url].second < 1)
+      pending_.erase(url);
+  }
+  void Remove(const GURL& url) { map_.erase(url); }
+  void Insert(const GURL& url, SBThreatType threat_type) {
+    if (Contains(url, nullptr))
+      return;
+    map_[url] = threat_type;
+    RemoveAllPending(url);
+  }
+  bool ContainsPending(const GURL& url, SBThreatType* threat_type) {
+    auto found = pending_.find(url);
+    if (found == pending_.end())
+      return false;
+    if (threat_type)
+      *threat_type = found->second.first;
+    return true;
+  }
+  void InsertPending(const GURL url, SBThreatType threat_type) {
+    if (pending_.find(url) != pending_.end()) {
+      pending_[url].first = threat_type;
+      pending_[url].second++;
+      return;
+    }
+    pending_[url] = {threat_type, 1};
+  }
+
+ protected:
+  // Method to remove all the instances of a website in the pending list
+  // disregarding the count. Used when adding a site to the permanent list.
+  void RemoveAllPending(const GURL& url) { pending_.erase(url); }
+
+ private:
+  std::map<GURL, SBThreatType> map_;
+  // Keep a count of how many times a site has been added to the pending list
+  // in order to solve a problem where upon reloading an interstitial, a site
+  // would be re-added to and removed from the whitelist in the wrong order.
+  std::map<GURL, std::pair<SBThreatType, int>> pending_;
+  DISALLOW_COPY_AND_ASSIGN(WhitelistUrlSet);
+};
+
+// Returns the URL that should be used in a WhitelistUrlSet for the
+// resource loaded from |url| on a navigation |entry|.
+GURL GetWhitelistUrl(const GURL& url,
+                     bool is_subresource,
+                     NavigationEntry* entry) {
+  if (is_subresource) {
+    if (!entry)
+      return GURL();
+    return entry->GetURL().GetWithEmptyPath();
+  }
+  return url.GetWithEmptyPath();
+}
+
+WhitelistUrlSet* GetOrCreateWhitelist(WebContents* web_contents) {
+  WhitelistUrlSet* site_list =
+      static_cast<WhitelistUrlSet*>(web_contents->GetUserData(kWhitelistKey));
+  if (!site_list) {
+    site_list = new WhitelistUrlSet;
+    web_contents->SetUserData(kWhitelistKey, base::WrapUnique(site_list));
+  }
+  return site_list;
+}
+
+}  // namespace
+
+namespace safe_browsing {
+
+BaseUIManager::BaseUIManager() {}
+
+BaseUIManager::~BaseUIManager() {}
+
+bool BaseUIManager::IsWhitelisted(const UnsafeResource& resource) {
+  NavigationEntry* entry = nullptr;
+  if (resource.is_subresource) {
+    entry = resource.GetNavigationEntryForResource();
+  }
+  SBThreatType unused_threat_type;
+  return IsUrlWhitelistedOrPendingForWebContents(
+      resource.url, resource.is_subresource, entry,
+      resource.web_contents_getter.Run(), true, &unused_threat_type);
+}
+
+// Check if the user has already seen and/or ignored a SB warning for this
+// WebContents and top-level domain.
+bool BaseUIManager::IsUrlWhitelistedOrPendingForWebContents(
+    const GURL& url,
+    bool is_subresource,
+    NavigationEntry* entry,
+    WebContents* web_contents,
+    bool whitelist_only,
+    SBThreatType* threat_type) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  GURL lookup_url = GetWhitelistUrl(url, is_subresource, entry);
+  if (lookup_url.is_empty())
+    return false;
+
+  WhitelistUrlSet* site_list =
+      static_cast<WhitelistUrlSet*>(web_contents->GetUserData(kWhitelistKey));
+  if (!site_list)
+    return false;
+
+  bool whitelisted = site_list->Contains(lookup_url, threat_type);
+  if (whitelist_only) {
+    return whitelisted;
+  } else {
+    return whitelisted || site_list->ContainsPending(lookup_url, threat_type);
+  }
+}
+
+void BaseUIManager::OnBlockingPageDone(
+    const std::vector<UnsafeResource>& resources,
+    bool proceed,
+    WebContents* web_contents,
+    const GURL& main_frame_url) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  for (const auto& resource : resources) {
+    if (!resource.callback.is_null()) {
+      DCHECK(resource.callback_thread);
+      resource.callback_thread->PostTask(
+          FROM_HERE, base::BindOnce(resource.callback, proceed));
+    }
+
+    GURL whitelist_url = GetWhitelistUrl(
+        main_frame_url, false /* is subresource */,
+        nullptr /* no navigation entry needed for main resource */);
+    if (proceed) {
+      AddToWhitelistUrlSet(whitelist_url, web_contents,
+                           false /* Pending -> permanent */,
+                           resource.threat_type);
+    } else if (web_contents) {
+      // |web_contents| doesn't exist if the tab has been closed.
+      RemoveWhitelistUrlSet(whitelist_url, web_contents,
+                            true /* from_pending_only */);
+    }
+  }
+}
+
+void BaseUIManager::DisplayBlockingPage(
+    const UnsafeResource& resource) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  if (resource.is_subresource && !resource.is_subframe) {
+    // Sites tagged as serving Unwanted Software should only show a warning for
+    // main-frame or sub-frame resource. Similar warning restrictions should be
+    // applied to malware sites tagged as "landing sites" (see "Types of
+    // Malware sites" under
+    // https://developers.google.com/safe-browsing/developers_guide_v3#UserWarnings).
+    if (resource.threat_type == SB_THREAT_TYPE_URL_UNWANTED ||
+        (resource.threat_type == SB_THREAT_TYPE_URL_MALWARE &&
+         resource.threat_metadata.threat_pattern_type ==
+             ThreatPatternType::MALWARE_LANDING)) {
+      if (!resource.callback.is_null()) {
+        DCHECK(resource.callback_thread);
+        resource.callback_thread->PostTask(
+            FROM_HERE, base::BindOnce(resource.callback, true));
+      }
+      return;
+    }
+  }
+
+  // The tab might have been closed. If it was closed, just act as if "Don't
+  // Proceed" had been chosen.
+  WebContents* web_contents = resource.web_contents_getter.Run();
+  if (!web_contents) {
+    OnBlockingPageDone(std::vector<UnsafeResource>{resource},
+                       false /* proceed */,
+                       web_contents,
+                       GetMainFrameWhitelistUrlForResource(resource));
+    return;
+  }
+
+  // Check if the user has already ignored a SB warning for the same WebContents
+  // and top-level domain.
+  if (IsWhitelisted(resource)) {
+    if (!resource.callback.is_null()) {
+      DCHECK(resource.callback_thread);
+      resource.callback_thread->PostTask(
+          FROM_HERE, base::BindOnce(resource.callback, true));
+    }
+
+    return;
+  }
+
+  if (resource.threat_type != SB_THREAT_TYPE_SAFE &&
+      resource.threat_type != SB_THREAT_TYPE_BILLING) {
+    // TODO(vakh): crbug/883462: The reports for SB_THREAT_TYPE_BILLING should
+    // be disabled for M70 but enabled for a later release (M71?).
+    CreateAndSendHitReport(resource);
+  }
+
+  AddToWhitelistUrlSet(GetMainFrameWhitelistUrlForResource(resource),
+                       resource.web_contents_getter.Run(),
+                       true /* A decision is now pending */,
+                       resource.threat_type);
+  if (SafeBrowsingInterstitialsAreCommittedNavigations()) {
+    GURL unsafe_url = (resource.IsMainPageLoadBlocked() ||
+                       !resource.GetNavigationEntryForResource())
+                          ? resource.url
+                          : resource.GetNavigationEntryForResource()->GetURL();
+    AddUnsafeResource(unsafe_url, resource);
+    // With committed interstitials we just cancel the load from here, the
+    // actual interstitial will be shown from the
+    // SafeBrowsingNavigationThrottle.
+    resource.callback_thread->PostTask(
+        FROM_HERE, base::BindOnce(resource.callback, false));
+    if (!resource.IsMainPageLoadBlocked() && !IsWhitelisted(resource)) {
+      // For subresource triggered interstitials, we trigger the error page
+      // navigation from here since there will be no navigation to intercept
+      // in the throttle.
+      content::WebContents* contents = resource.web_contents_getter.Run();
+      content::NavigationEntry* entry =
+          resource.GetNavigationEntryForResource();
+      // entry can be null if we are on a brand new tab, and a resource is added
+      // via javascript without a navigation.
+      GURL blocked_url = entry ? entry->GetURL() : resource.url;
+
+      // Blocking pages handle both user interaction, and generation of the
+      // interstitial HTML. In the case of subresources, we need the HTML
+      // content prior to (and in a different process than when) installing the
+      // command handlers. For this reason we create a blocking page here just
+      // to generate the HTML, and immediately delete it.
+      BaseBlockingPage* blocking_page =
+          CreateBlockingPageForSubresource(contents, blocked_url, resource);
+      contents->GetController().LoadPostCommitErrorPage(
+          contents->GetMainFrame(), blocked_url,
+          blocking_page->GetHTMLContents(), net::ERR_BLOCKED_BY_CLIENT);
+      delete blocking_page;
+    }
+    return;
+  }
+  ShowBlockingPageForResource(resource);
+}
+
+void BaseUIManager::EnsureWhitelistCreated(
+    WebContents* web_contents) {
+  GetOrCreateWhitelist(web_contents);
+}
+
+void BaseUIManager::CreateAndSendHitReport(const UnsafeResource& resource) {}
+
+void BaseUIManager::ShowBlockingPageForResource(
+    const UnsafeResource& resource) {
+  BaseBlockingPage::ShowBlockingPage(this, resource);
+}
+
+bool BaseUIManager::SafeBrowsingInterstitialsAreCommittedNavigations() {
+  return base::FeatureList::IsEnabled(kCommittedSBInterstitials);
+}
+
+BaseBlockingPage* BaseUIManager::CreateBlockingPageForSubresource(
+    content::WebContents* contents,
+    const GURL& blocked_url,
+    const UnsafeResource& unsafe_resource) {
+  // TODO(carlosil): This can be removed once all implementations of SB use
+  // committed interstitials. In the meantime, there is no create method for the
+  // non-committed implementations, and this code won't be called if committed
+  // interstitials are disabled.
+  NOTREACHED();
+  return nullptr;
+}
+
+// A SafeBrowsing hit is sent after a blocking page for malware/phishing
+// or after the warning dialog for download urls, only for extended_reporting
+// users who are not in incognito mode.
+void BaseUIManager::MaybeReportSafeBrowsingHit(
+    const HitReport& hit_report,
+    content::WebContents* web_contents) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  return;
+}
+
+// If the user had opted-in to send ThreatDetails, this gets called
+// when the report is ready.
+void BaseUIManager::SendSerializedThreatDetails(
+    const std::string& serialized) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  return;
+}
+
+// Record this domain in the given WebContents as either whitelisted or
+// pending whitelisting (if an interstitial is currently displayed). If an
+// existing WhitelistUrlSet does not yet exist, create a new WhitelistUrlSet.
+void BaseUIManager::AddToWhitelistUrlSet(const GURL& whitelist_url,
+                                         WebContents* web_contents,
+                                         bool pending,
+                                         SBThreatType threat_type) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  // A WebContents might not exist if the tab has been closed.
+  if (!web_contents)
+    return;
+
+  WhitelistUrlSet* site_list = GetOrCreateWhitelist(web_contents);
+
+  if (whitelist_url.is_empty())
+    return;
+
+  if (pending) {
+    site_list->InsertPending(whitelist_url, threat_type);
+  } else {
+    site_list->Insert(whitelist_url, threat_type);
+  }
+
+  // Notify security UI that security state has changed.
+  web_contents->DidChangeVisibleSecurityState();
+}
+
+const std::string BaseUIManager::app_locale() const {
+  return base::i18n::GetConfiguredLocale();
+}
+
+history::HistoryService* BaseUIManager::history_service(
+    content::WebContents* web_contents) {
+  return nullptr;
+}
+
+const GURL BaseUIManager::default_safe_page() const {
+  return GURL(url::kAboutBlankURL);
+}
+
+void BaseUIManager::AddUnsafeResource(
+    GURL url,
+    security_interstitials::UnsafeResource resource) {
+  unsafe_resources_.push_back(std::make_pair(url, resource));
+}
+
+bool BaseUIManager::PopUnsafeResourceForURL(
+    GURL url,
+    security_interstitials::UnsafeResource* resource) {
+  for (auto it = unsafe_resources_.begin(); it != unsafe_resources_.end();
+       it++) {
+    if (it->first == url) {
+      *resource = it->second;
+      unsafe_resources_.erase(it);
+      return true;
+    }
+  }
+  return false;
+}
+
+void BaseUIManager::RemoveWhitelistUrlSet(const GURL& whitelist_url,
+                                          WebContents* web_contents,
+                                          bool from_pending_only) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  // A WebContents might not exist if the tab has been closed.
+  if (!web_contents)
+    return;
+
+  // Use |web_contents| rather than |resource.web_contents_getter|
+  // here. By this point, a "Back" navigation could have already been
+  // committed, so the page loading |resource| might be gone and
+  // |web_contents_getter| may no longer be valid.
+  WhitelistUrlSet* site_list =
+      static_cast<WhitelistUrlSet*>(web_contents->GetUserData(kWhitelistKey));
+
+  if (whitelist_url.is_empty())
+    return;
+
+  // Note that this function does not DCHECK that |whitelist_url|
+  // appears in the pending whitelist. In the common case, it's expected
+  // that a URL is in the pending whitelist when it is removed, but it's
+  // not always the case. For example, if there are several blocking
+  // pages queued up for different resources on the same page, and the
+  // user goes back to dimiss the first one, the subsequent blocking
+  // pages get dismissed as well (as if the user had clicked "Back to
+  // safety" on each of them). In this case, the first dismissal will
+  // remove the main-frame URL from the pending whitelist, so the
+  // main-frame URL will have already been removed when the subsequent
+  // blocking pages are dismissed.
+  if (site_list && site_list->ContainsPending(whitelist_url, nullptr)) {
+    site_list->RemovePending(whitelist_url);
+  }
+
+  if (!from_pending_only && site_list &&
+      site_list->Contains(whitelist_url, nullptr)) {
+    site_list->Remove(whitelist_url);
+  }
+
+  // Notify security UI that security state has changed.
+  web_contents->DidChangeVisibleSecurityState();
+}
+
+// static
+GURL BaseUIManager::GetMainFrameWhitelistUrlForResource(
+    const security_interstitials::UnsafeResource& resource) {
+  if (resource.is_subresource) {
+    NavigationEntry* entry = resource.GetNavigationEntryForResource();
+    if (!entry)
+      return GURL();
+    return entry->GetURL().GetWithEmptyPath();
+  }
+  return resource.url.GetWithEmptyPath();
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/content/base_ui_manager.h b/components/safe_browsing/content/base_ui_manager.h
new file mode 100644
index 0000000..34870d4
--- /dev/null
+++ b/components/safe_browsing/content/base_ui_manager.h
@@ -0,0 +1,171 @@
+// 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 COMPONENTS_SAFE_BROWSING_CONTENT_BASE_UI_MANAGER_H_
+#define COMPONENTS_SAFE_BROWSING_CONTENT_BASE_UI_MANAGER_H_
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/bind_helpers.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "components/security_interstitials/content/unsafe_resource.h"
+
+class GURL;
+
+namespace content {
+class NavigationEntry;
+class WebContents;
+}  // namespace content
+
+namespace history {
+class HistoryService;
+}  // namespace history
+
+namespace safe_browsing {
+
+class BaseBlockingPage;
+
+// Construction needs to happen on the main thread.
+class BaseUIManager
+    : public base::RefCountedThreadSafe<BaseUIManager> {
+ public:
+  typedef security_interstitials::UnsafeResource UnsafeResource;
+
+  BaseUIManager();
+
+  // Called on the UI thread to display an interstitial page.
+  // |resource| is the unsafe resource that triggered the interstitial.
+  virtual void DisplayBlockingPage(const UnsafeResource& resource);
+
+  // This is a no-op in the base class, but should be overridden to send threat
+  // details. Called on the UI thread by the ThreatDetails with the serialized
+  // protocol buffer.
+  virtual void SendSerializedThreatDetails(const std::string& serialized);
+
+  // Updates the whitelist URL set for |web_contents|. Called on the UI thread.
+  void AddToWhitelistUrlSet(const GURL& whitelist_url,
+                            content::WebContents* web_contents,
+                            bool is_pending,
+                            SBThreatType threat_type);
+
+  // This is a no-op in the base class, but should be overridden to report hits
+  // to the unsafe contents (malware, phishing, unsafe download URL)
+  // to the server. Can only be called on UI thread. Will only upload a hit
+  // report if the user has enabled SBER and is not currently in incognito mode.
+  virtual void MaybeReportSafeBrowsingHit(
+      const safe_browsing::HitReport& hit_report,
+      content::WebContents* web_contents);
+
+  // A convenience wrapper method for IsUrlWhitelistedOrPendingForWebContents.
+  virtual bool IsWhitelisted(const UnsafeResource& resource);
+
+  // Checks if we already displayed or are displaying an interstitial
+  // for the top-level site |url| in a given WebContents. If
+  // |whitelist_only|, it returns true only if the user chose to ignore
+  // the interstitial. Otherwise, it returns true if an interstitial for
+  // |url| is already displaying *or* if the user has seen an
+  // interstitial for |url| before in this WebContents and proceeded
+  // through it. Called on the UI thread.
+  //
+  // If the resource was found in the whitelist or pending for the
+  // whitelist, |threat_type| will be set to the SBThreatType for which
+  // the URL was first whitelisted.
+  virtual bool IsUrlWhitelistedOrPendingForWebContents(
+      const GURL& url,
+      bool is_subresource,
+      content::NavigationEntry* entry,
+      content::WebContents* web_contents,
+      bool whitelist_only,
+      SBThreatType* threat_type);
+
+  // The blocking page for |web_contents| on the UI thread has
+  // completed, with |proceed| set to true if the user has chosen to
+  // proceed through the blocking page and false
+  // otherwise. |web_contents| is the WebContents that was displaying
+  // the blocking page. |main_frame_url| is the top-level URL on which
+  // the blocking page was displayed. If |proceed| is true,
+  // |main_frame_url| is whitelisted so that the user will not see
+  // another warning for that URL in this WebContents.
+  virtual void OnBlockingPageDone(const std::vector<UnsafeResource>& resources,
+                                  bool proceed,
+                                  content::WebContents* web_contents,
+                                  const GURL& main_frame_url);
+
+  virtual const std::string app_locale() const;
+
+  virtual history::HistoryService* history_service(
+      content::WebContents* web_contents);
+
+  // The default safe page when there is no entry in the history to go back to.
+  // e.g. about::blank page, or chrome's new tab page.
+  virtual const GURL default_safe_page() const;
+
+  // Adds an UnsafeResource |resource| for |url| to unsafe_resources_,
+  // this should be called whenever a resource load is blocked due to a SB hit.
+  void AddUnsafeResource(GURL url,
+                         security_interstitials::UnsafeResource resource);
+
+  // Checks if an UnsafeResource |resource| exists for |url|, if so, it is
+  // removed from the vector, assigned to |resource| and the function returns
+  // true. Otherwise the function returns false and nothing gets assigned to
+  // |resource|.
+  bool PopUnsafeResourceForURL(
+      GURL url,
+      security_interstitials::UnsafeResource* resource);
+
+ protected:
+  friend class ChromePasswordProtectionService;
+  virtual ~BaseUIManager();
+
+  // Removes |whitelist_url| from the whitelist for |web_contents|.
+  // Called on the UI thread.
+  void RemoveWhitelistUrlSet(const GURL& whitelist_url,
+                             content::WebContents* web_contents,
+                             bool from_pending_only);
+
+  // Ensures that |web_contents| has its whitelist set in its userdata
+  static void EnsureWhitelistCreated(content::WebContents* web_contents);
+
+  // Returns the URL that should be used in a WhitelistUrlSet for the given
+  // |resource|.
+  static GURL GetMainFrameWhitelistUrlForResource(
+      const security_interstitials::UnsafeResource& resource);
+
+  // BaseUIManager does not send SafeBrowsingHitReport. Subclasses should
+  // implement the reporting logic themselves if needed.
+  virtual void CreateAndSendHitReport(const UnsafeResource& resource);
+
+  // Calls BaseBlockingPage::ShowBlockingPage(). Override this if using a
+  // different blocking page.
+  virtual void ShowBlockingPageForResource(const UnsafeResource& resource);
+
+ private:
+  // When true, we immediately cancel navigations that have been blocked by Safe
+  // Browsing, otherwise we call show on the interstitial.
+  bool SafeBrowsingInterstitialsAreCommittedNavigations();
+
+  friend class base::RefCountedThreadSafe<BaseUIManager>;
+
+  // Creates a blocking page, used for interstitials triggered by subresources.
+  // Should be overridden with a blocking page implementation.
+  virtual BaseBlockingPage* CreateBlockingPageForSubresource(
+      content::WebContents* contents,
+      const GURL& blocked_url,
+      const UnsafeResource& unsafe_resource);
+
+  // Stores unsafe resources so they can be fetched from a navigation throttle
+  // in the committed interstitials flow. Implemented as a pair vector since
+  // most of the time it will be empty or contain a single element.
+  std::vector<std::pair<GURL, security_interstitials::UnsafeResource>>
+      unsafe_resources_;
+
+  DISALLOW_COPY_AND_ASSIGN(BaseUIManager);
+};
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CONTENT_BASE_UI_MANAGER_H_
diff --git a/components/safe_browsing/content/browser/BUILD.gn b/components/safe_browsing/content/browser/BUILD.gn
new file mode 100644
index 0000000..38849c3
--- /dev/null
+++ b/components/safe_browsing/content/browser/BUILD.gn
@@ -0,0 +1,41 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/features.gni")
+import("//build/config/jumbo.gni")
+
+jumbo_source_set("browser") {
+  sources = [
+    "browser_url_loader_throttle.cc",
+    "browser_url_loader_throttle.h",
+    "mojo_safe_browsing_impl.cc",
+    "mojo_safe_browsing_impl.h",
+    "threat_details.cc",
+    "threat_details.h",
+    "threat_details_cache.cc",
+    "threat_details_cache.h",
+    "threat_details_history.cc",
+    "threat_details_history.h",
+  ]
+  deps = [
+    "//components/history/core/browser",
+    "//components/safe_browsing/content",
+    "//components/safe_browsing/content/web_ui:web_ui",
+    "//components/safe_browsing/core:csd_proto",
+    "//components/safe_browsing/core:features",
+    "//components/safe_browsing/core:realtimeapi_proto",
+    "//components/safe_browsing/core/browser",
+    "//components/safe_browsing/core/browser:network_context",
+    "//components/safe_browsing/core/browser:referrer_chain_provider",
+    "//components/safe_browsing/core/common:common",
+    "//components/safe_browsing/core/db:database_manager",
+    "//components/safe_browsing/core/realtime:policy_engine",
+    "//components/safe_browsing/core/realtime:url_lookup_service",
+    "//components/safe_browsing/core:verdict_cache_manager",
+    "//components/safe_browsing/core/web_ui:constants",
+    "//components/security_interstitials/content:security_interstitial_page",
+    "//content/public/browser:browser",
+    "//net:extras",
+  ]
+}
diff --git a/components/safe_browsing/content/browser/DEPS b/components/safe_browsing/content/browser/DEPS
new file mode 100644
index 0000000..3e20f42
--- /dev/null
+++ b/components/safe_browsing/content/browser/DEPS
@@ -0,0 +1,21 @@
+include_rules = [
+  "+components/history/core/browser",
+  "+components/safe_browsing/core/proto/csd.pb.h",
+  "+components/sessions/core/session_id.h",
+  "+content/public/browser",
+  "+ipc/ipc_message.h",
+  "+net/cookies",
+  "+net/extras",
+  "+net/http",
+  "+net/ssl",
+  "+net/traffic_annotation",
+  "+services/network/network_context.h",
+  "+services/network/public",
+  "+services/service_manager/public/cpp",
+]
+
+specific_include_rules = {
+  ".*test\.cc": [
+    "+content/public/test/browser_task_environment.h",
+  ],
+}
diff --git a/components/safe_browsing/content/browser/browser_url_loader_throttle.cc b/components/safe_browsing/content/browser/browser_url_loader_throttle.cc
new file mode 100644
index 0000000..b06b11eb
--- /dev/null
+++ b/components/safe_browsing/content/browser/browser_url_loader_throttle.cc
@@ -0,0 +1,318 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/content/browser/browser_url_loader_throttle.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/trace_event/trace_event.h"
+#include "components/safe_browsing/core/browser/safe_browsing_url_checker_impl.h"
+#include "components/safe_browsing/core/browser/url_checker_delegate.h"
+#include "components/safe_browsing/core/common/safebrowsing_constants.h"
+#include "components/safe_browsing/core/common/utils.h"
+#include "components/safe_browsing/core/realtime/policy_engine.h"
+#include "components/safe_browsing/core/verdict_cache_manager.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/web_contents.h"
+#include "net/log/net_log_event_type.h"
+#include "net/url_request/redirect_info.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/mojom/url_response_head.mojom.h"
+
+namespace safe_browsing {
+
+// TODO(http://crbug.com/824843): Remove this if safe browsing is moved to the
+// UI thread.
+class BrowserURLLoaderThrottle::CheckerOnIO
+    : public base::SupportsWeakPtr<BrowserURLLoaderThrottle::CheckerOnIO> {
+ public:
+  CheckerOnIO(
+      GetDelegateCallback delegate_getter,
+      int frame_tree_node_id,
+      base::RepeatingCallback<content::WebContents*()> web_contents_getter,
+      base::WeakPtr<BrowserURLLoaderThrottle> throttle,
+      bool real_time_lookup_enabled,
+      base::WeakPtr<VerdictCacheManager> cache_manager)
+      : delegate_getter_(std::move(delegate_getter)),
+        frame_tree_node_id_(frame_tree_node_id),
+        web_contents_getter_(web_contents_getter),
+        throttle_(std::move(throttle)),
+        real_time_lookup_enabled_(real_time_lookup_enabled),
+        cache_manager_(cache_manager) {}
+
+  // Starts the initial safe browsing check. This check and future checks may be
+  // skipped after checking with the UrlCheckerDelegate.
+  void Start(const net::HttpRequestHeaders& headers,
+             int load_flags,
+             content::ResourceType resource_type,
+             bool has_user_gesture,
+             bool originated_from_service_worker,
+             const GURL& url,
+             const std::string& method) {
+    DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+    scoped_refptr<UrlCheckerDelegate> url_checker_delegate =
+        std::move(delegate_getter_).Run();
+    skip_checks_ =
+        !url_checker_delegate ||
+        !url_checker_delegate->GetDatabaseManager()->IsSupported() ||
+        url_checker_delegate->ShouldSkipRequestCheck(
+            url, frame_tree_node_id_, -1 /* render_process_id */,
+            -1 /* render_frame_id */, originated_from_service_worker);
+    if (skip_checks_) {
+      base::PostTask(
+          FROM_HERE, {content::BrowserThread::UI},
+          base::BindOnce(&BrowserURLLoaderThrottle::SkipChecks, throttle_));
+      return;
+    }
+
+    url_checker_ = std::make_unique<SafeBrowsingUrlCheckerImpl>(
+        headers, load_flags, resource_type, has_user_gesture,
+        url_checker_delegate, web_contents_getter_, real_time_lookup_enabled_,
+        cache_manager_);
+
+    CheckUrl(url, method);
+  }
+
+  // Checks the specified |url| using |url_checker_|.
+  void CheckUrl(const GURL& url, const std::string& method) {
+    DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+    if (skip_checks_) {
+      base::PostTask(
+          FROM_HERE, {content::BrowserThread::UI},
+          base::BindOnce(&BrowserURLLoaderThrottle::SkipChecks, throttle_));
+      return;
+    }
+
+    DCHECK(url_checker_);
+    url_checker_->CheckUrl(
+        url, method,
+        base::BindOnce(&BrowserURLLoaderThrottle::CheckerOnIO::OnCheckUrlResult,
+                       base::Unretained(this)));
+  }
+
+ private:
+  // If |slow_check_notifier| is non-null, it indicates that a "slow check" is
+  // ongoing, i.e., the URL may be unsafe and a more time-consuming process is
+  // required to get the final result. In that case, the rest of the callback
+  // arguments should be ignored. This method sets the |slow_check_notifier|
+  // output parameter to a callback to receive the final result.
+  void OnCheckUrlResult(NativeUrlCheckNotifier* slow_check_notifier,
+                        bool proceed,
+                        bool showed_interstitial) {
+    if (!slow_check_notifier) {
+      OnCompleteCheck(false /* slow_check */, proceed, showed_interstitial);
+      return;
+    }
+
+    base::PostTask(
+        FROM_HERE, {content::BrowserThread::UI},
+        base::BindOnce(&BrowserURLLoaderThrottle::NotifySlowCheck, throttle_));
+
+    // In this case |proceed| and |showed_interstitial| should be ignored. The
+    // result will be returned by calling |*slow_check_notifier| callback.
+    *slow_check_notifier =
+        base::BindOnce(&BrowserURLLoaderThrottle::CheckerOnIO::OnCompleteCheck,
+                       base::Unretained(this), true /* slow_check */);
+  }
+
+  // |slow_check| indicates whether it reports the result of a slow check.
+  // (Please see comments of OnCheckUrlResult() for what slow check means).
+  void OnCompleteCheck(bool slow_check,
+                       bool proceed,
+                       bool showed_interstitial) {
+    base::PostTask(
+        FROM_HERE, {content::BrowserThread::UI},
+        base::BindOnce(&BrowserURLLoaderThrottle::OnCompleteCheck, throttle_,
+                       slow_check, proceed, showed_interstitial));
+  }
+
+  // The following member stays valid until |url_checker_| is created.
+  GetDelegateCallback delegate_getter_;
+
+  std::unique_ptr<SafeBrowsingUrlCheckerImpl> url_checker_;
+  int frame_tree_node_id_;
+  base::RepeatingCallback<content::WebContents*()> web_contents_getter_;
+  bool skip_checks_ = false;
+  base::WeakPtr<BrowserURLLoaderThrottle> throttle_;
+  bool real_time_lookup_enabled_ = false;
+  base::WeakPtr<VerdictCacheManager> cache_manager_;
+};
+
+// static
+std::unique_ptr<BrowserURLLoaderThrottle> BrowserURLLoaderThrottle::Create(
+    GetDelegateCallback delegate_getter,
+    const base::RepeatingCallback<content::WebContents*()>& web_contents_getter,
+    int frame_tree_node_id,
+    base::WeakPtr<VerdictCacheManager> cache_manager) {
+  return base::WrapUnique<BrowserURLLoaderThrottle>(
+      new BrowserURLLoaderThrottle(std::move(delegate_getter),
+                                   web_contents_getter, frame_tree_node_id,
+                                   cache_manager));
+}
+
+BrowserURLLoaderThrottle::BrowserURLLoaderThrottle(
+    GetDelegateCallback delegate_getter,
+    const base::RepeatingCallback<content::WebContents*()>& web_contents_getter,
+    int frame_tree_node_id,
+    base::WeakPtr<VerdictCacheManager> cache_manager) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  // Decide whether to do real time URL lookups or not.
+  content::WebContents* web_contents = web_contents_getter.Run();
+  bool real_time_lookup_enabled =
+      web_contents ? RealTimePolicyEngine::CanPerformFullURLLookup(
+                         web_contents->GetBrowserContext())
+                   : false;
+
+  io_checker_ = std::make_unique<CheckerOnIO>(
+      std::move(delegate_getter), frame_tree_node_id, web_contents_getter,
+      weak_factory_.GetWeakPtr(), real_time_lookup_enabled, cache_manager);
+}
+
+BrowserURLLoaderThrottle::~BrowserURLLoaderThrottle() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  if (deferred_)
+    TRACE_EVENT_ASYNC_END0("safe_browsing", "Deferred", this);
+
+  DeleteCheckerOnIO();
+}
+
+void BrowserURLLoaderThrottle::WillStartRequest(
+    network::ResourceRequest* request,
+    bool* defer) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  DCHECK_EQ(0u, pending_checks_);
+  DCHECK(!blocked_);
+
+  original_url_ = request->url;
+  pending_checks_++;
+  base::PostTask(
+      FROM_HERE, {content::BrowserThread::IO},
+      base::BindOnce(
+          &BrowserURLLoaderThrottle::CheckerOnIO::Start,
+          io_checker_->AsWeakPtr(), request->headers, request->load_flags,
+          static_cast<content::ResourceType>(request->resource_type),
+          request->has_user_gesture, request->originated_from_service_worker,
+          request->url, request->method));
+}
+
+void BrowserURLLoaderThrottle::WillRedirectRequest(
+    net::RedirectInfo* redirect_info,
+    const network::mojom::URLResponseHead& /* response_head */,
+    bool* defer,
+    std::vector<std::string>* /* to_be_removed_headers */,
+    net::HttpRequestHeaders* /* modified_headers */) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  if (blocked_) {
+    // OnCheckUrlResult() has set |blocked_| to true and called
+    // |delegate_->CancelWithError|, but this method is called before the
+    // request is actually cancelled. In that case, simply defer the request.
+    *defer = true;
+    return;
+  }
+
+  if (skip_checks_)
+    return;
+
+  pending_checks_++;
+  base::PostTask(
+      FROM_HERE, {content::BrowserThread::IO},
+      base::BindOnce(&BrowserURLLoaderThrottle::CheckerOnIO::CheckUrl,
+                     io_checker_->AsWeakPtr(), redirect_info->new_url,
+                     redirect_info->new_method));
+}
+
+void BrowserURLLoaderThrottle::WillProcessResponse(
+    const GURL& response_url,
+    network::mojom::URLResponseHead* response_head,
+    bool* defer) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  if (blocked_) {
+    // OnCheckUrlResult() has set |blocked_| to true and called
+    // |delegate_->CancelWithError|, but this method is called before the
+    // request is actually cancelled. In that case, simply defer the request.
+    *defer = true;
+    return;
+  }
+
+  if (pending_checks_ == 0)
+    return;
+
+  DCHECK(!deferred_);
+  deferred_ = true;
+  defer_start_time_ = base::TimeTicks::Now();
+  *defer = true;
+  TRACE_EVENT_ASYNC_BEGIN1("safe_browsing", "Deferred", this, "original_url",
+                           original_url_.spec());
+}
+
+void BrowserURLLoaderThrottle::OnCompleteCheck(bool slow_check,
+                                               bool proceed,
+                                               bool showed_interstitial) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  DCHECK(!blocked_);
+
+  DCHECK_LT(0u, pending_checks_);
+  pending_checks_--;
+
+  if (slow_check) {
+    DCHECK_LT(0u, pending_slow_checks_);
+    pending_slow_checks_--;
+  }
+
+  user_action_involved_ = user_action_involved_ || showed_interstitial;
+  // If the resource load is currently deferred and is going to exit that state
+  // (either being cancelled or resumed), record the total delay.
+  if (deferred_ && (!proceed || pending_checks_ == 0))
+    total_delay_ = base::TimeTicks::Now() - defer_start_time_;
+
+  if (proceed) {
+    if (pending_slow_checks_ == 0 && slow_check)
+      delegate_->ResumeReadingBodyFromNet();
+
+    if (pending_checks_ == 0 && deferred_) {
+      deferred_ = false;
+      TRACE_EVENT_ASYNC_END0("safe_browsing", "Deferred", this);
+      delegate_->Resume();
+    }
+  } else {
+    blocked_ = true;
+
+    DeleteCheckerOnIO();
+    pending_checks_ = 0;
+    pending_slow_checks_ = 0;
+    delegate_->CancelWithError(GetNetErrorCodeForSafeBrowsing(),
+                               kCustomCancelReasonForURLLoader);
+  }
+}
+
+void BrowserURLLoaderThrottle::SkipChecks() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  // Future checks for redirects will be skipped.
+  skip_checks_ = true;
+
+  pending_checks_--;
+  if (pending_checks_ == 0 && deferred_)
+    delegate_->Resume();
+}
+
+void BrowserURLLoaderThrottle::NotifySlowCheck() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  pending_slow_checks_++;
+
+  // Pending slow checks indicate that the resource may be unsafe. In that case,
+  // pause reading response body from network to minimize the chance of
+  // processing unsafe contents (e.g., writing unsafe contents into cache),
+  // until we get the results. According to the results, we may resume reading
+  // or cancel the resource load.
+  if (pending_slow_checks_ == 1)
+    delegate_->PauseReadingBodyFromNet();
+}
+
+void BrowserURLLoaderThrottle::DeleteCheckerOnIO() {
+  base::DeleteSoon(FROM_HERE, {content::BrowserThread::IO},
+                   std::move(io_checker_));
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/content/browser/browser_url_loader_throttle.h b/components/safe_browsing/content/browser/browser_url_loader_throttle.h
new file mode 100644
index 0000000..b3545e4
--- /dev/null
+++ b/components/safe_browsing/content/browser/browser_url_loader_throttle.h
@@ -0,0 +1,133 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CONTENT_BROWSER_BROWSER_URL_LOADER_THROTTLE_H_
+#define COMPONENTS_SAFE_BROWSING_CONTENT_BROWSER_BROWSER_URL_LOADER_THROTTLE_H_
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/task/post_task.h"
+#include "base/time/time.h"
+#include "content/public/browser/browser_thread.h"
+#include "third_party/blink/public/common/loader/url_loader_throttle.h"
+#include "url/gurl.h"
+
+namespace content {
+class WebContents;
+}
+
+namespace net {
+class HttpRequestHeaders;
+}
+
+namespace safe_browsing {
+
+class UrlCheckerDelegate;
+
+class VerdictCacheManager;
+
+// BrowserURLLoaderThrottle is used in the browser process to query
+// SafeBrowsing to determine whether a URL and also its redirect URLs are safe
+// to load.
+//
+// This throttle never defers starting the URL request or following redirects,
+// no matter on mobile or desktop. If any of the checks for the original URL
+// and redirect chain are not complete by the time the response headers are
+// available, the request is deferred until all the checks are done. It cancels
+// the load if any URLs turn out to be bad.
+class BrowserURLLoaderThrottle : public blink::URLLoaderThrottle {
+ public:
+  using GetDelegateCallback =
+      base::OnceCallback<scoped_refptr<UrlCheckerDelegate>()>;
+
+  static std::unique_ptr<BrowserURLLoaderThrottle> Create(
+      GetDelegateCallback delegate_getter,
+      const base::RepeatingCallback<content::WebContents*()>&
+          web_contents_getter,
+      int frame_tree_node_id,
+      base::WeakPtr<VerdictCacheManager> cache_manager);
+
+  ~BrowserURLLoaderThrottle() override;
+
+  // blink::URLLoaderThrottle implementation.
+  void WillStartRequest(network::ResourceRequest* request,
+                        bool* defer) override;
+  void WillRedirectRequest(net::RedirectInfo* redirect_info,
+                           const network::mojom::URLResponseHead& response_head,
+                           bool* defer,
+                           std::vector<std::string>* to_be_removed_headers,
+                           net::HttpRequestHeaders* modified_headers) override;
+  void WillProcessResponse(const GURL& response_url,
+                           network::mojom::URLResponseHead* response_head,
+                           bool* defer) override;
+
+ private:
+  // CheckerOnIO handles calling methods on SafeBrowsingUrlCheckerImpl, which
+  // must be called on the IO thread. The results are synced back to the
+  // throttle.
+  // TODO(http://crbug.com/824843): Remove this if safe browsing is moved to the
+  // UI thread.
+  class CheckerOnIO;
+
+  using NativeUrlCheckNotifier =
+      base::OnceCallback<void(bool /* proceed */,
+                              bool /* showed_interstitial */)>;
+
+  // |web_contents_getter| is used for displaying SafeBrowsing UI when
+  // necessary.
+  BrowserURLLoaderThrottle(
+      GetDelegateCallback delegate_getter,
+      const base::RepeatingCallback<content::WebContents*()>&
+          web_contents_getter,
+      int frame_tree_node_id,
+      base::WeakPtr<VerdictCacheManager> cache_manager);
+
+  // |slow_check| indicates whether it reports the result of a slow check.
+  // (Please see comments of CheckerOnIO::OnCheckUrlResult() for what slow check
+  // means).
+  void OnCompleteCheck(bool slow_check, bool proceed, bool showed_interstitial);
+
+  // Called to skip future safe browsing checks and resume the request if
+  // necessary.
+  void SkipChecks();
+
+  // Called when a slow safe browsing check is ongoing.
+  void NotifySlowCheck();
+
+  // Destroys |io_checker_| on the IO thread.
+  void DeleteCheckerOnIO();
+
+  size_t pending_checks_ = 0;
+  // How many slow checks that haven't received results.
+  size_t pending_slow_checks_ = 0;
+  bool blocked_ = false;
+
+  // The time when we started deferring the request.
+  base::TimeTicks defer_start_time_;
+  bool deferred_ = false;
+
+  // The total delay caused by SafeBrowsing deferring the resource load.
+  base::TimeDelta total_delay_;
+  // Whether the interstitial page has been shown and therefore user action has
+  // been involved.
+  bool user_action_involved_ = false;
+
+  GURL original_url_;
+
+  // Whether future safe browsing checks should be skipped.
+  bool skip_checks_ = false;
+
+  std::unique_ptr<CheckerOnIO> io_checker_;
+
+  base::WeakPtrFactory<BrowserURLLoaderThrottle> weak_factory_{this};
+
+  DISALLOW_COPY_AND_ASSIGN(BrowserURLLoaderThrottle);
+};
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CONTENT_BROWSER_BROWSER_URL_LOADER_THROTTLE_H_
diff --git a/components/safe_browsing/content/browser/mojo_safe_browsing_impl.cc b/components/safe_browsing/content/browser/mojo_safe_browsing_impl.cc
new file mode 100644
index 0000000..965baea
--- /dev/null
+++ b/components/safe_browsing/content/browser/mojo_safe_browsing_impl.cc
@@ -0,0 +1,181 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/content/browser/mojo_safe_browsing_impl.h"
+
+#include <memory>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/supports_user_data.h"
+#include "components/safe_browsing/core/browser/safe_browsing_url_checker_impl.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/resource_context.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/resource_type.h"
+#include "mojo/public/cpp/bindings/self_owned_receiver.h"
+#include "net/base/load_flags.h"
+
+namespace safe_browsing {
+namespace {
+
+content::WebContents* GetWebContentsFromID(int render_process_id,
+                                           int render_frame_id) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  content::RenderFrameHost* render_frame_host =
+      content::RenderFrameHost::FromID(render_process_id, render_frame_id);
+  if (!render_frame_host)
+    return nullptr;
+
+  return content::WebContents::FromRenderFrameHost(render_frame_host);
+}
+
+// This class wraps a callback for checking URL, and runs it on destruction,
+// if it hasn't been run yet.
+class CheckUrlCallbackWrapper {
+ public:
+  using Callback = base::OnceCallback<
+      void(mojo::PendingReceiver<mojom::UrlCheckNotifier>, bool, bool)>;
+
+  explicit CheckUrlCallbackWrapper(Callback callback)
+      : callback_(std::move(callback)) {}
+  ~CheckUrlCallbackWrapper() {
+    if (callback_)
+      Run(mojo::NullReceiver(), true, false);
+  }
+
+  void Run(mojo::PendingReceiver<mojom::UrlCheckNotifier> slow_check_notifier,
+           bool proceed,
+           bool showed_interstitial) {
+    std::move(callback_).Run(std::move(slow_check_notifier), proceed,
+                             showed_interstitial);
+  }
+
+ private:
+  Callback callback_;
+};
+
+// UserData object that owns MojoSafeBrowsingImpl. This is used rather than
+// having MojoSafeBrowsingImpl directly extend base::SupportsUserData::Data to
+// avoid naming conflicts between Data::Clone() and
+// mojom::SafeBrowsing::Clone().
+class SafeBrowserUserData : public base::SupportsUserData::Data {
+ public:
+  explicit SafeBrowserUserData(std::unique_ptr<MojoSafeBrowsingImpl> impl)
+      : impl_(std::move(impl)) {}
+  ~SafeBrowserUserData() override = default;
+
+ private:
+  std::unique_ptr<MojoSafeBrowsingImpl> impl_;
+
+  DISALLOW_COPY_AND_ASSIGN(SafeBrowserUserData);
+};
+
+}  // namespace
+
+MojoSafeBrowsingImpl::MojoSafeBrowsingImpl(
+    scoped_refptr<UrlCheckerDelegate> delegate,
+    int render_process_id,
+    content::ResourceContext* resource_context)
+    : delegate_(std::move(delegate)),
+      render_process_id_(render_process_id),
+      resource_context_(resource_context) {
+  DCHECK(resource_context_);
+
+  // It is safe to bind |this| as Unretained because |receivers_| is owned by
+  // |this| and will not call this callback after it is destroyed.
+  receivers_.set_disconnect_handler(base::BindRepeating(
+      &MojoSafeBrowsingImpl::OnMojoDisconnect, base::Unretained(this)));
+}
+
+MojoSafeBrowsingImpl::~MojoSafeBrowsingImpl() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+}
+
+// static
+void MojoSafeBrowsingImpl::MaybeCreate(
+    int render_process_id,
+    content::ResourceContext* resource_context,
+    const base::RepeatingCallback<scoped_refptr<UrlCheckerDelegate>()>&
+        delegate_getter,
+    mojo::PendingReceiver<mojom::SafeBrowsing> receiver) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+  scoped_refptr<UrlCheckerDelegate> delegate = delegate_getter.Run();
+
+  if (!resource_context || !delegate ||
+      !delegate->GetDatabaseManager()->IsSupported())
+    return;
+
+  std::unique_ptr<MojoSafeBrowsingImpl> impl(new MojoSafeBrowsingImpl(
+      std::move(delegate), render_process_id, resource_context));
+  impl->Clone(std::move(receiver));
+
+  MojoSafeBrowsingImpl* raw_impl = impl.get();
+  std::unique_ptr<SafeBrowserUserData> user_data =
+      std::make_unique<SafeBrowserUserData>(std::move(impl));
+  raw_impl->user_data_key_ = user_data.get();
+  resource_context->SetUserData(raw_impl->user_data_key_, std::move(user_data));
+}
+
+void MojoSafeBrowsingImpl::CreateCheckerAndCheck(
+    int32_t render_frame_id,
+    mojo::PendingReceiver<mojom::SafeBrowsingUrlChecker> receiver,
+    const GURL& url,
+    const std::string& method,
+    const net::HttpRequestHeaders& headers,
+    int32_t load_flags,
+    content::ResourceType resource_type,
+    bool has_user_gesture,
+    bool originated_from_service_worker,
+    CreateCheckerAndCheckCallback callback) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+  if (delegate_->ShouldSkipRequestCheck(url, -1 /* frame_tree_node_id */,
+                                        render_process_id_, render_frame_id,
+                                        originated_from_service_worker)) {
+    // Ensure that we don't destroy an uncalled CreateCheckerAndCheckCallback
+    if (callback) {
+      std::move(callback).Run(mojo::NullReceiver(), true /* proceed */,
+                              false /* showed_interstitial */);
+    }
+
+    // This will drop |receiver|. The result is that the renderer side will
+    // consider all URLs in the redirect chain of this receiver as safe.
+    return;
+  }
+
+  // This is not called for frame resources, and real time URL checks currently
+  // only support frame resources. If we extend real time URL checks to support
+  // non-main frames, we will need to provide the user preferences and cache
+  // manager regarding real time lookup here.
+  auto checker_impl = std::make_unique<SafeBrowsingUrlCheckerImpl>(
+      headers, static_cast<int>(load_flags), resource_type, has_user_gesture,
+      delegate_,
+      base::BindRepeating(&GetWebContentsFromID, render_process_id_,
+                          static_cast<int>(render_frame_id)),
+      /*real_time_lookup_enabled=*/false, /*cache_manager=*/nullptr);
+
+  checker_impl->CheckUrl(
+      url, method,
+      base::BindOnce(
+          &CheckUrlCallbackWrapper::Run,
+          base::Owned(new CheckUrlCallbackWrapper(std::move(callback)))));
+  mojo::MakeSelfOwnedReceiver(std::move(checker_impl), std::move(receiver));
+}
+
+void MojoSafeBrowsingImpl::Clone(
+    mojo::PendingReceiver<mojom::SafeBrowsing> receiver) {
+  receivers_.Add(this, std::move(receiver));
+}
+
+void MojoSafeBrowsingImpl::OnMojoDisconnect() {
+  if (receivers_.empty()) {
+    resource_context_->RemoveUserData(user_data_key_);
+    // This object is destroyed.
+  }
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/content/browser/mojo_safe_browsing_impl.h b/components/safe_browsing/content/browser/mojo_safe_browsing_impl.h
new file mode 100644
index 0000000..4e53c6a7
--- /dev/null
+++ b/components/safe_browsing/content/browser/mojo_safe_browsing_impl.h
@@ -0,0 +1,75 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CONTENT_BROWSER_MOJO_SAFE_BROWSING_IMPL_H_
+#define COMPONENTS_SAFE_BROWSING_CONTENT_BROWSER_MOJO_SAFE_BROWSING_IMPL_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "components/safe_browsing/core/browser/url_checker_delegate.h"
+#include "components/safe_browsing/core/common/safe_browsing.mojom.h"
+#include "ipc/ipc_message.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
+
+namespace content {
+class ResourceContext;
+}
+
+namespace safe_browsing {
+
+// This class implements the Mojo interface for renderers to perform
+// SafeBrowsing URL checks.
+// A MojoSafeBrowsingImpl instance is destructed when the Mojo message pipe is
+// disconnected or |resource_context_| is destructed.
+class MojoSafeBrowsingImpl : public mojom::SafeBrowsing {
+ public:
+  ~MojoSafeBrowsingImpl() override;
+
+  static void MaybeCreate(
+      int render_process_id,
+      content::ResourceContext* resource_context,
+      const base::RepeatingCallback<scoped_refptr<UrlCheckerDelegate>()>&
+          delegate_getter,
+      mojo::PendingReceiver<mojom::SafeBrowsing> receiver);
+
+ private:
+  MojoSafeBrowsingImpl(scoped_refptr<UrlCheckerDelegate> delegate,
+                       int render_process_id,
+                       content::ResourceContext* resource_context);
+
+  // mojom::SafeBrowsing implementation.
+  void CreateCheckerAndCheck(
+      int32_t render_frame_id,
+      mojo::PendingReceiver<mojom::SafeBrowsingUrlChecker> receiver,
+      const GURL& url,
+      const std::string& method,
+      const net::HttpRequestHeaders& headers,
+      int32_t load_flags,
+      content::ResourceType resource_type,
+      bool has_user_gesture,
+      bool originated_from_service_worker,
+      CreateCheckerAndCheckCallback callback) override;
+  void Clone(mojo::PendingReceiver<mojom::SafeBrowsing> receiver) override;
+
+  void OnMojoDisconnect();
+
+  // This is an instance of SafeBrowserUserData that is set as user-data on
+  // |resource_context_|. SafeBrowserUserData owns |this|.
+  const void* user_data_key_ = nullptr;
+
+  mojo::ReceiverSet<mojom::SafeBrowsing> receivers_;
+  scoped_refptr<UrlCheckerDelegate> delegate_;
+  int render_process_id_ = MSG_ROUTING_NONE;
+
+  // Not owned by this object. It is always valid during the lifetime of this
+  // object.
+  content::ResourceContext* resource_context_;
+
+  DISALLOW_COPY_AND_ASSIGN(MojoSafeBrowsingImpl);
+};
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CONTENT_BROWSER_MOJO_SAFE_BROWSING_IMPL_H_
diff --git a/components/safe_browsing/content/browser/threat_details.cc b/components/safe_browsing/content/browser/threat_details.cc
new file mode 100644
index 0000000..35cc062
--- /dev/null
+++ b/components/safe_browsing/content/browser/threat_details.cc
@@ -0,0 +1,903 @@
+// Copyright (c) 2012 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.
+//
+// Implementation of the ThreatDetails class.
+
+#include "components/safe_browsing/content/browser/threat_details.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <unordered_set>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/feature_list.h"
+#include "base/lazy_instance.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/stl_util.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "base/task/post_task.h"
+#include "components/history/core/browser/history_service.h"
+#include "components/safe_browsing/content/base_ui_manager.h"
+#include "components/safe_browsing/content/browser/threat_details_cache.h"
+#include "components/safe_browsing/content/browser/threat_details_history.h"
+#include "components/safe_browsing/content/web_ui/safe_browsing_ui.h"
+#include "components/safe_browsing/core/browser/referrer_chain_provider.h"
+#include "components/safe_browsing/core/db/hit_report.h"
+#include "components/safe_browsing/core/features.h"
+#include "content/public/browser/back_forward_cache.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/global_routing_id.h"
+#include "content/public/browser/navigation_controller.h"
+#include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/web_contents.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/service_manager/public/cpp/interface_provider.h"
+
+using content::BrowserThread;
+using content::NavigationEntry;
+using content::RenderFrameHost;
+using content::WebContents;
+
+// Keep in sync with KMaxNodes in components/safe_browsing/content/renderer/
+// threat_dom_details.cc
+static const uint32_t kMaxDomNodes = 500;
+
+namespace safe_browsing {
+
+// static
+ThreatDetailsFactory* ThreatDetails::factory_ = nullptr;
+
+namespace {
+
+// An element ID indicating that an HTML Element has no parent.
+const int kElementIdNoParent = -1;
+
+// The number of user gestures to trace back for the referrer chain.
+const int kThreatDetailsUserGestureLimit = 2;
+
+typedef std::unordered_set<std::string> StringSet;
+// A set of HTTPS headers that are allowed to be collected. Contains both
+// request and response headers. All entries in this list should be lower-case
+// to support case-insensitive comparison.
+struct WhitelistedHttpsHeadersTraits
+    : base::internal::DestructorAtExitLazyInstanceTraits<StringSet> {
+  static StringSet* New(void* instance) {
+    StringSet* headers =
+        base::internal::DestructorAtExitLazyInstanceTraits<StringSet>::New(
+            instance);
+    headers->insert({"google-creative-id", "google-lineitem-id", "referer",
+                     "content-type", "content-length", "date", "server",
+                     "cache-control", "pragma", "expires"});
+    return headers;
+  }
+};
+base::LazyInstance<StringSet, WhitelistedHttpsHeadersTraits>
+    g_https_headers_whitelist = LAZY_INSTANCE_INITIALIZER;
+
+// Helper function that converts SBThreatType to
+// ClientSafeBrowsingReportRequest::ReportType.
+ClientSafeBrowsingReportRequest::ReportType GetReportTypeFromSBThreatType(
+    SBThreatType threat_type) {
+  switch (threat_type) {
+    case SB_THREAT_TYPE_URL_PHISHING:
+      return ClientSafeBrowsingReportRequest::URL_PHISHING;
+    case SB_THREAT_TYPE_URL_MALWARE:
+      return ClientSafeBrowsingReportRequest::URL_MALWARE;
+    case SB_THREAT_TYPE_URL_UNWANTED:
+      return ClientSafeBrowsingReportRequest::URL_UNWANTED;
+    case SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING:
+      return ClientSafeBrowsingReportRequest::URL_CLIENT_SIDE_PHISHING;
+    case SB_THREAT_TYPE_URL_CLIENT_SIDE_MALWARE:
+      return ClientSafeBrowsingReportRequest::URL_CLIENT_SIDE_MALWARE;
+    case SB_THREAT_TYPE_BLOCKED_AD_POPUP:
+      return ClientSafeBrowsingReportRequest::BLOCKED_AD_POPUP;
+    case SB_THREAT_TYPE_AD_SAMPLE:
+      return ClientSafeBrowsingReportRequest::AD_SAMPLE;
+    case SB_THREAT_TYPE_BLOCKED_AD_REDIRECT:
+      return ClientSafeBrowsingReportRequest::BLOCKED_AD_REDIRECT;
+    case SB_THREAT_TYPE_SAVED_PASSWORD_REUSE:
+    case SB_THREAT_TYPE_SIGNED_IN_SYNC_PASSWORD_REUSE:
+    case SB_THREAT_TYPE_SIGNED_IN_NON_SYNC_PASSWORD_REUSE:
+    case SB_THREAT_TYPE_ENTERPRISE_PASSWORD_REUSE:
+      return ClientSafeBrowsingReportRequest::URL_PASSWORD_PROTECTION_PHISHING;
+    case SB_THREAT_TYPE_SUSPICIOUS_SITE:
+      return ClientSafeBrowsingReportRequest::URL_SUSPICIOUS;
+    case SB_THREAT_TYPE_BILLING:
+      return ClientSafeBrowsingReportRequest::BILLING;
+    case SB_THREAT_TYPE_APK_DOWNLOAD:
+      return ClientSafeBrowsingReportRequest::APK_DOWNLOAD;
+    case SB_THREAT_TYPE_UNUSED:
+    case SB_THREAT_TYPE_SAFE:
+    case SB_THREAT_TYPE_URL_BINARY_MALWARE:
+    case SB_THREAT_TYPE_EXTENSION:
+    case SB_THREAT_TYPE_BLACKLISTED_RESOURCE:
+    case SB_THREAT_TYPE_API_ABUSE:
+    case SB_THREAT_TYPE_SUBRESOURCE_FILTER:
+    case SB_THREAT_TYPE_CSD_WHITELIST:
+    case SB_THREAT_TYPE_HIGH_CONFIDENCE_ALLOWLIST:
+    case DEPRECATED_SB_THREAT_TYPE_URL_PASSWORD_PROTECTION_PHISHING:
+      // Gated by SafeBrowsingBlockingPage::ShouldReportThreatDetails.
+      NOTREACHED() << "We should not send report for threat type: "
+                   << threat_type;
+      return ClientSafeBrowsingReportRequest::UNKNOWN;
+  }
+}
+
+// Clears the specified HTTPS resource of any sensitive data, only retaining
+// data that is whitelisted for collection.
+void ClearHttpsResource(ClientSafeBrowsingReportRequest::Resource* resource) {
+  // Make a copy of the original resource to retain all data.
+  ClientSafeBrowsingReportRequest::Resource orig_resource(*resource);
+
+  // Clear the request headers and copy over any whitelisted ones.
+  resource->clear_request();
+  for (int i = 0; i < orig_resource.request().headers_size(); ++i) {
+    ClientSafeBrowsingReportRequest::HTTPHeader* orig_header =
+        orig_resource.mutable_request()->mutable_headers(i);
+    if (g_https_headers_whitelist.Get().count(
+            base::ToLowerASCII(orig_header->name())) > 0) {
+      resource->mutable_request()->add_headers()->Swap(orig_header);
+    }
+  }
+  // Also copy some other request fields.
+  resource->mutable_request()->mutable_bodydigest()->swap(
+      *orig_resource.mutable_request()->mutable_bodydigest());
+  resource->mutable_request()->set_bodylength(
+      orig_resource.request().bodylength());
+
+  // ...repeat for response headers.
+  resource->clear_response();
+  for (int i = 0; i < orig_resource.response().headers_size(); ++i) {
+    ClientSafeBrowsingReportRequest::HTTPHeader* orig_header =
+        orig_resource.mutable_response()->mutable_headers(i);
+    if (g_https_headers_whitelist.Get().count(
+            base::ToLowerASCII(orig_header->name())) > 0) {
+      resource->mutable_response()->add_headers()->Swap(orig_header);
+    }
+  }
+  // Also copy some other response fields.
+  resource->mutable_response()->mutable_bodydigest()->swap(
+      *orig_resource.mutable_response()->mutable_bodydigest());
+  resource->mutable_response()->set_bodylength(
+      orig_resource.response().bodylength());
+  resource->mutable_response()->mutable_remote_ip()->swap(
+      *orig_resource.mutable_response()->mutable_remote_ip());
+}
+
+std::string GetElementKey(const int frame_tree_node_id,
+                          const int element_node_id) {
+  return base::StringPrintf("%d-%d", frame_tree_node_id, element_node_id);
+}
+
+using CSBRR = safe_browsing::ClientSafeBrowsingReportRequest;
+CSBRR::SafeBrowsingUrlApiType GetUrlApiTypeForThreatSource(
+    safe_browsing::ThreatSource source) {
+  switch (source) {
+    case safe_browsing::ThreatSource::DATA_SAVER:
+      return CSBRR::FLYWHEEL;
+    case safe_browsing::ThreatSource::LOCAL_PVER3:
+      return CSBRR::PVER3_NATIVE;
+    case safe_browsing::ThreatSource::LOCAL_PVER4:
+      return CSBRR::PVER4_NATIVE;
+    case safe_browsing::ThreatSource::REMOTE:
+      return CSBRR::ANDROID_SAFETYNET;
+    case safe_browsing::ThreatSource::UNKNOWN:
+    case safe_browsing::ThreatSource::CLIENT_SIDE_DETECTION:
+    case safe_browsing::ThreatSource::PASSWORD_PROTECTION_SERVICE:
+      break;
+  }
+  return CSBRR::SAFE_BROWSING_URL_API_TYPE_UNSPECIFIED;
+}
+
+void TrimElements(const std::set<int> target_ids,
+                  ElementMap* elements,
+                  ResourceMap* resources) {
+  if (target_ids.empty()) {
+    elements->clear();
+    resources->clear();
+    return;
+  }
+
+  // First, scan over the elements and create a list ordered by element ID as
+  // well as a reverse mapping from element ID to its parent ID.
+  std::vector<HTMLElement*> elements_by_id(elements->size());
+
+  // The parent vector is initialized with |kElementIdNoParent| so we can
+  // identify elements that have no parent.
+  std::vector<int> element_id_to_parent_id(elements->size(),
+                                           kElementIdNoParent);
+  for (const auto& element_pair : *elements) {
+    HTMLElement* element = element_pair.second.get();
+    elements_by_id[element->id()] = element;
+
+    for (int child_id : element->child_ids()) {
+      element_id_to_parent_id[child_id] = element->id();
+    }
+  }
+
+  // Create a similar map for resources, ordered by resource ID.
+  std::vector<std::string> resource_id_to_url(resources->size());
+  for (const auto& resource_pair : *resources) {
+    const std::string& url = resource_pair.first;
+    ClientSafeBrowsingReportRequest::Resource* resource =
+        resource_pair.second.get();
+    resource_id_to_url[resource->id()] = url;
+  }
+
+  // Take a second pass and determine which element IDs to keep. We want to keep
+  // the immediate parent, the siblings, and the children of the target ids.
+  // By keeping the parent of the target and all of its children, this covers
+  // the target's siblings as well.
+  std::vector<int> element_ids_to_keep;
+  // Resource IDs are also tracked so that we remember which resources are
+  // attached to elements that we are keeping. This avoids deleting resources
+  // that are shared between kept elements and trimmed elements.
+  std::vector<int> kept_resource_ids;
+  for (int target_id : target_ids) {
+    const int parent_id = element_id_to_parent_id[target_id];
+    if (parent_id == kElementIdNoParent) {
+      // If one of the target elements has no parent then we skip trimming the
+      // report further. Since we collect all siblings of this element, it will
+      // effectively span the whole report, so no trimming necessary.
+      return;
+    }
+
+    // Otherwise, insert the parent ID into the list of ids to keep. This will
+    // capture the parent and siblings of the target element, as well as each of
+    // their children.
+    if (!base::Contains(element_ids_to_keep, parent_id)) {
+      element_ids_to_keep.push_back(parent_id);
+
+      // Check if this element has a resource. If so, remember to also keep the
+      // resource.
+      const HTMLElement& elem = *elements_by_id[parent_id];
+      if (elem.has_resource_id()) {
+        kept_resource_ids.push_back(elem.resource_id());
+      }
+    }
+  }
+
+  // Walk through |element_ids_to_keep| and append the children of each of
+  // element to |element_ids_to_keep|. This is effectively a breadth-first
+  // traversal of the tree. The list will stop growing when we reach the leaf
+  // nodes that have no more children.
+  for (size_t index = 0; index < element_ids_to_keep.size(); ++index) {
+    int cur_element_id = element_ids_to_keep[index];
+    const HTMLElement& element = *(elements_by_id[cur_element_id]);
+    if (element.has_resource_id()) {
+      kept_resource_ids.push_back(element.resource_id());
+    }
+    for (int child_id : element.child_ids()) {
+      element_ids_to_keep.push_back(child_id);
+
+      // Check if each child element has a resource. If so, remember to also
+      // keep the resource.
+      const HTMLElement& child_element = *elements_by_id[child_id];
+      if (child_element.has_resource_id()) {
+        kept_resource_ids.push_back(child_element.resource_id());
+      }
+    }
+  }
+  // Sort the list for easier lookup below.
+  std::sort(element_ids_to_keep.begin(), element_ids_to_keep.end());
+
+  // Now we know which elements we want to keep, scan through |elements| and
+  // erase anything that we aren't keeping.
+  for (auto element_iter = elements->begin();
+       element_iter != elements->end();) {
+    const HTMLElement& element = *element_iter->second;
+
+    // Delete any elements that we do not want to keep.
+    if (!base::Contains(element_ids_to_keep, element.id())) {
+      // If this element has a resource then maybe delete the resouce too. Some
+      // resources may be shared between kept and trimmed elements, and those
+      // ones should not be deleted.
+      if (element.has_resource_id() &&
+          !base::Contains(kept_resource_ids, element.resource_id())) {
+        const std::string& resource_url =
+            resource_id_to_url[element.resource_id()];
+        resources->erase(resource_url);
+      }
+      element_iter = elements->erase(element_iter);
+    } else {
+      ++element_iter;
+    }
+  }
+}
+
+}  // namespace
+
+// The default ThreatDetailsFactory.  Global, made a singleton so we
+// don't leak it.
+class ThreatDetailsFactoryImpl : public ThreatDetailsFactory {
+ public:
+  std::unique_ptr<ThreatDetails> CreateThreatDetails(
+      BaseUIManager* ui_manager,
+      WebContents* web_contents,
+      const security_interstitials::UnsafeResource& unsafe_resource,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      history::HistoryService* history_service,
+      ReferrerChainProvider* referrer_chain_provider,
+      bool trim_to_ad_tags,
+      ThreatDetailsDoneCallback done_callback) override {
+    // We can't use make_unique due to the protected constructor. We can't
+    // directly use std::unique_ptr<ThreatDetails>(new ThreatDetails(...))
+    // due to presubmit errors. So we use base::WrapUnique:
+    auto threat_details = base::WrapUnique(new ThreatDetails(
+        ui_manager, web_contents, unsafe_resource, url_loader_factory,
+        history_service, referrer_chain_provider, trim_to_ad_tags,
+        std::move(done_callback)));
+    threat_details->StartCollection();
+    return threat_details;
+  }
+
+ private:
+  friend struct base::LazyInstanceTraitsBase<ThreatDetailsFactoryImpl>;
+
+  ThreatDetailsFactoryImpl() {}
+
+  DISALLOW_COPY_AND_ASSIGN(ThreatDetailsFactoryImpl);
+};
+
+static base::LazyInstance<ThreatDetailsFactoryImpl>::DestructorAtExit
+    g_threat_details_factory_impl = LAZY_INSTANCE_INITIALIZER;
+
+// Create a ThreatDetails for the given tab.
+/* static */
+std::unique_ptr<ThreatDetails> ThreatDetails::NewThreatDetails(
+    BaseUIManager* ui_manager,
+    WebContents* web_contents,
+    const UnsafeResource& resource,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    history::HistoryService* history_service,
+    ReferrerChainProvider* referrer_chain_provider,
+    bool trim_to_ad_tags,
+    ThreatDetailsDoneCallback done_callback) {
+  // Set up the factory if this has not been done already (tests do that
+  // before this method is called).
+  if (!factory_)
+    factory_ = g_threat_details_factory_impl.Pointer();
+  return factory_->CreateThreatDetails(
+      ui_manager, web_contents, resource, url_loader_factory, history_service,
+      referrer_chain_provider, trim_to_ad_tags, std::move(done_callback));
+}
+
+// Create a ThreatDetails for the given tab. Runs in the UI thread.
+ThreatDetails::ThreatDetails(
+    BaseUIManager* ui_manager,
+    content::WebContents* web_contents,
+    const UnsafeResource& resource,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    history::HistoryService* history_service,
+    ReferrerChainProvider* referrer_chain_provider,
+    bool trim_to_ad_tags,
+    ThreatDetailsDoneCallback done_callback)
+    : content::WebContentsObserver(web_contents),
+      url_loader_factory_(url_loader_factory),
+      ui_manager_(ui_manager),
+      resource_(resource),
+      referrer_chain_provider_(referrer_chain_provider),
+      cache_result_(false),
+      did_proceed_(false),
+      num_visits_(0),
+      trim_to_ad_tags_(trim_to_ad_tags),
+      cache_collector_(new ThreatDetailsCacheCollector),
+      done_callback_(std::move(done_callback)),
+      all_done_expected_(false),
+      is_all_done_(false) {
+  redirects_collector_ = new ThreatDetailsRedirectsCollector(
+      history_service ? history_service->AsWeakPtr()
+                      : base::WeakPtr<history::HistoryService>());
+}
+
+// TODO(lpz): Consider making this constructor delegate to the parameterized one
+// above.
+ThreatDetails::ThreatDetails()
+    : cache_result_(false),
+      did_proceed_(false),
+      num_visits_(0),
+      trim_to_ad_tags_(false),
+      all_done_expected_(false),
+      is_all_done_(false) {}
+
+ThreatDetails::~ThreatDetails() {
+  DCHECK_EQ(all_done_expected_, is_all_done_);
+}
+
+bool ThreatDetails::IsReportableUrl(const GURL& url) const {
+  // TODO(panayiotis): also skip internal urls.
+  return url.SchemeIs("http") || url.SchemeIs("https");
+}
+
+// Looks for a Resource for the given url in resources_.  If found, it
+// updates |resource|. Otherwise, it creates a new message, adds it to
+// resources_ and updates |resource| to point to it.
+//
+ClientSafeBrowsingReportRequest::Resource* ThreatDetails::FindOrCreateResource(
+    const GURL& url) {
+  auto& resource = resources_[url.spec()];
+  if (!resource) {
+    // Create the resource for |url|.
+    int id = resources_.size() - 1;
+    std::unique_ptr<ClientSafeBrowsingReportRequest::Resource> new_resource(
+        new ClientSafeBrowsingReportRequest::Resource());
+    new_resource->set_url(url.spec());
+    new_resource->set_id(id);
+    resource = std::move(new_resource);
+  }
+  return resource.get();
+}
+
+HTMLElement* ThreatDetails::FindOrCreateElement(
+    const std::string& element_key) {
+  auto& element = elements_[element_key];
+  if (!element) {
+    // Create an entry for this element.
+    int element_dom_id = elements_.size() - 1;
+    std::unique_ptr<HTMLElement> new_element(new HTMLElement());
+    new_element->set_id(element_dom_id);
+    element = std::move(new_element);
+  }
+  return element.get();
+}
+
+ClientSafeBrowsingReportRequest::Resource* ThreatDetails::AddUrl(
+    const GURL& url,
+    const GURL& parent,
+    const std::string& tagname,
+    const std::vector<GURL>* children) {
+  if (!url.is_valid() || !IsReportableUrl(url))
+    return nullptr;
+
+  // Find (or create) the resource for the url.
+  ClientSafeBrowsingReportRequest::Resource* url_resource =
+      FindOrCreateResource(url);
+  if (!tagname.empty())
+    url_resource->set_tag_name(tagname);
+  if (!parent.is_empty() && IsReportableUrl(parent)) {
+    // Add the resource for the parent.
+    ClientSafeBrowsingReportRequest::Resource* parent_resource =
+        FindOrCreateResource(parent);
+    // Update the parent-child relation
+    url_resource->set_parent_id(parent_resource->id());
+  }
+  if (children) {
+    for (auto it = children->begin(); it != children->end(); ++it) {
+      // TODO(lpz): Should this first check if the child URL is reportable
+      // before creating the resource?
+      ClientSafeBrowsingReportRequest::Resource* child_resource =
+          FindOrCreateResource(*it);
+      bool duplicate_child = false;
+      for (auto child_id : url_resource->child_ids()) {
+        if (child_id == child_resource->id()) {
+          duplicate_child = true;
+          break;
+        }
+      }
+      if (!duplicate_child)
+        url_resource->add_child_ids(child_resource->id());
+    }
+  }
+
+  return url_resource;
+}
+
+void ThreatDetails::AddDomElement(
+    const int frame_tree_node_id,
+    const int element_node_id,
+    const std::string& tagname,
+    const int parent_element_node_id,
+    const std::vector<mojom::AttributeNameValuePtr> attributes,
+    const std::string& inner_html,
+    const ClientSafeBrowsingReportRequest::Resource* resource) {
+  // Create the element. It should not exist already since this function should
+  // only be called once for each element.
+  const std::string element_key =
+      GetElementKey(frame_tree_node_id, element_node_id);
+  HTMLElement* cur_element = FindOrCreateElement(element_key);
+
+  // Set some basic metadata about the element.
+  const std::string tag_name_upper = base::ToUpperASCII(tagname);
+  if (!tag_name_upper.empty()) {
+    cur_element->set_tag(tag_name_upper);
+  }
+  for (const mojom::AttributeNameValuePtr& attribute : attributes) {
+    HTMLElement::Attribute* attribute_pb = cur_element->add_attribute();
+    attribute_pb->set_name(std::move(attribute->name));
+    attribute_pb->set_value(std::move(attribute->value));
+
+    // Remember which the IDs of elements that represent ads so we can trim the
+    // report down to just those parts later.
+    if (trim_to_ad_tags_ && attribute_pb->name() == "data-google-query-id") {
+      trimmed_dom_element_ids_.insert(cur_element->id());
+    }
+  }
+
+  if (!inner_html.empty()) {
+    cur_element->set_inner_html(inner_html);
+  }
+
+  if (resource) {
+    cur_element->set_resource_id(resource->id());
+  }
+
+  // Next we try to lookup the parent of the current element and add ourselves
+  // as a child of it.
+  HTMLElement* parent_element = nullptr;
+  if (parent_element_node_id == 0) {
+    // No parent indicates that this element is at the top of the current frame.
+    // Remember that this is a top-level element of the frame with the
+    // current |frame_tree_node_id|. If this element is inside an iframe, a
+    // second pass will insert this element as a child of its parent iframe.
+    frame_tree_id_to_children_map_[frame_tree_node_id].insert(
+        cur_element->id());
+  } else {
+    // We have a parent ID, so this element is just a child of something inside
+    // of our current frame. We can easily lookup our parent.
+    const std::string& parent_key =
+        GetElementKey(frame_tree_node_id, parent_element_node_id);
+    if (base::Contains(elements_, parent_key)) {
+      parent_element = elements_[parent_key].get();
+    }
+  }
+
+  // If a parent element was found, add ourselves as a child, ensuring not to
+  // duplicate child IDs.
+  if (parent_element) {
+    bool duplicate_child = false;
+    for (const int child_id : parent_element->child_ids()) {
+      if (child_id == cur_element->id()) {
+        duplicate_child = true;
+        break;
+      }
+    }
+    if (!duplicate_child) {
+      parent_element->add_child_ids(cur_element->id());
+    }
+  }
+}
+
+void ThreatDetails::StartCollection() {
+  DVLOG(1) << "Starting to compute threat details.";
+  report_.reset(new ClientSafeBrowsingReportRequest());
+
+  if (IsReportableUrl(resource_.url)) {
+    report_->set_url(resource_.url.spec());
+    report_->set_type(GetReportTypeFromSBThreatType(resource_.threat_type));
+  }
+
+  GURL referrer_url;
+  GURL page_url;
+
+  // With committed interstitials, the information is pre-filled into the
+  // UnsafeResource, since the navigation entry we have at this point is for the
+  // navigation to the interstitial, and the entry with the page details get
+  // destroyed when leaving the interstitial.
+  if (!resource_.navigation_url.is_empty()) {
+    DCHECK(
+        base::FeatureList::IsEnabled(safe_browsing::kCommittedSBInterstitials));
+    page_url = resource_.navigation_url;
+    referrer_url = resource_.referrer_url;
+  } else {
+    NavigationEntry* nav_entry = resource_.GetNavigationEntryForResource();
+    if (nav_entry) {
+      page_url = nav_entry->GetURL();
+      referrer_url = nav_entry->GetReferrer().url;
+    }
+  }
+
+  if (IsReportableUrl(page_url))
+    report_->set_page_url(page_url.spec());
+
+  if (IsReportableUrl(referrer_url))
+    report_->set_referrer_url(referrer_url.spec());
+
+  // Add the nodes, starting from the page url.
+  AddUrl(page_url, GURL(), std::string(), nullptr);
+
+  // Add the resource_url and its original url, if non-empty and different.
+  if (!resource_.original_url.is_empty() &&
+      resource_.url != resource_.original_url) {
+    // Add original_url, as the parent of resource_url.
+    AddUrl(resource_.original_url, GURL(), std::string(), nullptr);
+    AddUrl(resource_.url, resource_.original_url, std::string(), nullptr);
+  } else {
+    AddUrl(resource_.url, GURL(), std::string(), nullptr);
+  }
+
+  // Add the redirect urls, if non-empty. The redirect urls do not include the
+  // original url, but include the unsafe url which is the last one of the
+  // redirect urls chain
+  GURL parent_url;
+  // Set the original url as the parent of the first redirect url if it's not
+  // empty.
+  if (!resource_.original_url.is_empty())
+    parent_url = resource_.original_url;
+
+  // Set the previous redirect url as the parent of the next one
+  for (size_t i = 0; i < resource_.redirect_urls.size(); ++i) {
+    AddUrl(resource_.redirect_urls[i], parent_url, std::string(), nullptr);
+    parent_url = resource_.redirect_urls[i];
+  }
+
+  // Add the referrer url.
+  if (!referrer_url.is_empty())
+    AddUrl(referrer_url, GURL(), std::string(), nullptr);
+
+  if (!resource_.IsMainPageLoadBlocked()) {
+    // Get URLs of frames, scripts etc from the DOM.
+    // OnReceivedThreatDOMDetails will be called when the renderer replies.
+    // TODO(mattm): In theory, if the user proceeds through the warning DOM
+    // detail collection could be started once the page loads.
+    web_contents()->ForEachFrame(base::BindRepeating(
+        &ThreatDetails::RequestThreatDOMDetails, GetWeakPtr()));
+  }
+}
+
+void ThreatDetails::RequestThreatDOMDetails(content::RenderFrameHost* frame) {
+  content::BackForwardCache::DisableForRenderFrameHost(
+      frame, "safe_browsing::ThreatDetails");
+  mojo::Remote<safe_browsing::mojom::ThreatReporter> threat_reporter;
+  frame->GetRemoteInterfaces()->GetInterface(
+      threat_reporter.BindNewPipeAndPassReceiver());
+  safe_browsing::mojom::ThreatReporter* raw_threat_report =
+      threat_reporter.get();
+  pending_render_frame_hosts_.push_back(frame);
+  raw_threat_report->GetThreatDOMDetails(
+      base::BindOnce(&ThreatDetails::OnReceivedThreatDOMDetails, GetWeakPtr(),
+                     std::move(threat_reporter), frame));
+}
+
+// When the renderer is done, this is called.
+void ThreatDetails::OnReceivedThreatDOMDetails(
+    mojo::Remote<mojom::ThreatReporter> threat_reporter,
+    content::RenderFrameHost* sender,
+    std::vector<mojom::ThreatDOMDetailsNodePtr> params) {
+  // If the RenderFrameHost was closed between sending the IPC and this callback
+  // running, |sender| will be invalid.
+  const auto sender_it = std::find(pending_render_frame_hosts_.begin(),
+                                   pending_render_frame_hosts_.end(), sender);
+  if (sender_it == pending_render_frame_hosts_.end()) {
+    return;
+  }
+
+  pending_render_frame_hosts_.erase(sender_it);
+
+  // Lookup the FrameTreeNode ID of any child frames in the list of DOM nodes.
+  const int sender_process_id = sender->GetProcess()->GetID();
+  const int sender_frame_tree_node_id = sender->GetFrameTreeNodeId();
+  KeyToFrameTreeIdMap child_frame_tree_map;
+  for (const mojom::ThreatDOMDetailsNodePtr& node : params) {
+    if (node->child_frame_routing_id == 0)
+      continue;
+
+    const std::string cur_element_key =
+        GetElementKey(sender_frame_tree_node_id, node->node_id);
+    int child_frame_tree_node_id =
+        content::RenderFrameHost::GetFrameTreeNodeIdForRoutingId(
+            sender_process_id, node->child_frame_routing_id);
+    if (child_frame_tree_node_id !=
+        content::RenderFrameHost::kNoFrameTreeNodeId) {
+      child_frame_tree_map[cur_element_key] = child_frame_tree_node_id;
+    }
+  }
+
+  AddDOMDetails(sender_frame_tree_node_id, std::move(params),
+                child_frame_tree_map);
+}
+
+void ThreatDetails::AddDOMDetails(
+    const int frame_tree_node_id,
+    std::vector<mojom::ThreatDOMDetailsNodePtr> params,
+    const KeyToFrameTreeIdMap& child_frame_tree_map) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DVLOG(1) << "Nodes from the DOM: " << params.size();
+
+  // If we have already started getting redirects from history service,
+  // don't modify state, otherwise will invalidate the iterators.
+  if (redirects_collector_->HasStarted())
+    return;
+
+  // If we have already started collecting data from the HTTP cache, don't
+  // modify our state.
+  if (cache_collector_->HasStarted())
+    return;
+
+  // Exit early if there are no nodes to process.
+  if (params.empty())
+    return;
+
+  // Copy FrameTreeNode IDs for the child frame into the combined mapping.
+  iframe_key_to_frame_tree_id_map_.insert(child_frame_tree_map.begin(),
+                                          child_frame_tree_map.end());
+
+  // Add the urls from the DOM to |resources_|. The renderer could be sending
+  // bogus messages, so limit the number of nodes we accept.
+  // Also update |elements_| with the DOM structure.
+  for (size_t i = 0; i < params.size() && i < kMaxDomNodes; ++i) {
+    mojom::ThreatDOMDetailsNode& node = *params[i];
+    DVLOG(1) << node.url << ", " << node.tag_name << ", " << node.parent;
+    ClientSafeBrowsingReportRequest::Resource* resource = nullptr;
+    if (!node.url.is_empty()) {
+      resource = AddUrl(node.url, node.parent, node.tag_name, &(node.children));
+    }
+    // Check for a tag_name to avoid adding the summary node to the DOM.
+    if (!node.tag_name.empty()) {
+      AddDomElement(frame_tree_node_id, node.node_id, node.tag_name,
+                    node.parent_node_id, std::move(node.attributes),
+                    node.inner_html, resource);
+    }
+  }
+}
+
+// Called from the SB Service on the IO thread, after the user has
+// closed the tab, or clicked proceed or goback.  Since the user needs
+// to take an action, we expect this to be called after
+// OnReceivedThreatDOMDetails in most cases. If not, we don't include
+// the DOM data in our report.
+void ThreatDetails::FinishCollection(bool did_proceed, int num_visit) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  all_done_expected_ = true;
+
+  // Do a second pass over the elements and update iframe elements to have
+  // references to their children. Children may have been received from a
+  // different renderer than the iframe element.
+  for (auto& element_pair : elements_) {
+    const std::string& element_key = element_pair.first;
+    HTMLElement* element = element_pair.second.get();
+    if (base::Contains(iframe_key_to_frame_tree_id_map_, element_key)) {
+      int frame_tree_id_of_iframe_renderer =
+          iframe_key_to_frame_tree_id_map_[element_key];
+      const std::unordered_set<int>& child_ids =
+          frame_tree_id_to_children_map_[frame_tree_id_of_iframe_renderer];
+      for (const int child_id : child_ids) {
+        element->add_child_ids(child_id);
+      }
+    }
+  }
+
+  did_proceed_ = did_proceed;
+  num_visits_ = num_visit;
+  std::vector<GURL> urls;
+  for (ResourceMap::const_iterator it = resources_.begin();
+       it != resources_.end(); ++it) {
+    urls.push_back(GURL(it->first));
+  }
+  redirects_collector_->StartHistoryCollection(
+      urls, base::BindOnce(&ThreatDetails::OnRedirectionCollectionReady,
+                           GetWeakPtr()));
+}
+
+void ThreatDetails::OnRedirectionCollectionReady() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  const std::vector<RedirectChain>& redirects =
+      redirects_collector_->GetCollectedUrls();
+
+  for (size_t i = 0; i < redirects.size(); ++i)
+    AddRedirectUrlList(redirects[i]);
+
+  // Call the cache collector
+  cache_collector_->StartCacheCollection(
+      url_loader_factory_, &resources_, &cache_result_,
+      base::BindOnce(&ThreatDetails::OnCacheCollectionReady, GetWeakPtr()));
+}
+
+void ThreatDetails::AddRedirectUrlList(const std::vector<GURL>& urls) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  for (size_t i = 0; i < urls.size() - 1; ++i) {
+    AddUrl(urls[i], urls[i + 1], std::string(), nullptr);
+  }
+}
+
+void ThreatDetails::OnCacheCollectionReady() {
+  DVLOG(1) << "OnCacheCollectionReady.";
+
+  // All URLs have been collected, trim the report if necessary.
+  if (trim_to_ad_tags_) {
+    TrimElements(trimmed_dom_element_ids_, &elements_, &resources_);
+    // If trimming the report removed all the elements then don't bother
+    // sending it.
+    if (elements_.empty()) {
+      AllDone();
+      return;
+    }
+  }
+  // Add all the urls in our |resources_| maps to the |report_| protocol buffer.
+  for (auto& resource_pair : resources_) {
+    ClientSafeBrowsingReportRequest::Resource* pb_resource =
+        report_->add_resources();
+    pb_resource->Swap(resource_pair.second.get());
+    const GURL url(pb_resource->url());
+    if (url.SchemeIs("https")) {
+      // Sanitize the HTTPS resource by clearing out private data (like cookie
+      // headers).
+      DVLOG(1) << "Clearing out HTTPS resource: " << pb_resource->url();
+      ClearHttpsResource(pb_resource);
+      // Keep id, parent_id, child_ids, and tag_name.
+    }
+  }
+  for (auto& element_pair : elements_) {
+    report_->add_dom()->Swap(element_pair.second.get());
+  }
+
+  report_->set_did_proceed(did_proceed_);
+  // Only sets repeat_visit if num_visits_ >= 0.
+  if (num_visits_ >= 0) {
+    report_->set_repeat_visit(num_visits_ > 0);
+  }
+  report_->set_complete(cache_result_);
+
+  report_->mutable_client_properties()->set_url_api_type(
+      GetUrlApiTypeForThreatSource(resource_.threat_source));
+
+  // Fill the referrer chain if applicable.
+  MaybeFillReferrerChain();
+
+  // Send the report, using the SafeBrowsingService.
+  std::string serialized;
+  if (!report_->SerializeToString(&serialized)) {
+    DLOG(ERROR) << "Unable to serialize the threat report.";
+    AllDone();
+    return;
+  }
+
+  base::PostTask(
+      FROM_HERE, {content::BrowserThread::UI},
+      base::BindOnce(&WebUIInfoSingleton::AddToCSBRRsSent,
+                     base::Unretained(WebUIInfoSingleton::GetInstance()),
+                     std::move(report_)));
+
+  ui_manager_->SendSerializedThreatDetails(serialized);
+
+  AllDone();
+}
+
+void ThreatDetails::MaybeFillReferrerChain() {
+  if (!referrer_chain_provider_)
+    return;
+
+  if (!report_ ||
+      (report_->type() != ClientSafeBrowsingReportRequest::URL_SUSPICIOUS &&
+       report_->type() != ClientSafeBrowsingReportRequest::APK_DOWNLOAD)) {
+    return;
+  }
+
+  referrer_chain_provider_->IdentifyReferrerChainByWebContents(
+      web_contents(), kThreatDetailsUserGestureLimit,
+      report_->mutable_referrer_chain());
+}
+
+void ThreatDetails::AllDone() {
+  is_all_done_ = true;
+  base::PostTask(FROM_HERE, {content::BrowserThread::UI},
+                 base::BindOnce(std::move(done_callback_),
+                                base::Unretained(web_contents())));
+}
+
+void ThreatDetails::FrameDeleted(RenderFrameHost* render_frame_host) {
+  auto render_frame_host_it =
+      std::find(pending_render_frame_hosts_.begin(),
+                pending_render_frame_hosts_.end(), render_frame_host);
+  if (render_frame_host_it != pending_render_frame_hosts_.end()) {
+    pending_render_frame_hosts_.erase(render_frame_host_it);
+  }
+}
+
+void ThreatDetails::RenderFrameHostChanged(RenderFrameHost* old_host,
+                                           RenderFrameHost* new_host) {
+  FrameDeleted(old_host);
+}
+
+base::WeakPtr<ThreatDetails> ThreatDetails::GetWeakPtr() {
+  return weak_factory_.GetWeakPtr();
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/content/browser/threat_details.h b/components/safe_browsing/content/browser/threat_details.h
new file mode 100644
index 0000000..73ae22c
--- /dev/null
+++ b/components/safe_browsing/content/browser/threat_details.h
@@ -0,0 +1,307 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CONTENT_BROWSER_THREAT_DETAILS_H_
+#define COMPONENTS_SAFE_BROWSING_CONTENT_BROWSER_THREAT_DETAILS_H_
+
+// A class that encapsulates the detailed threat reports sent when
+// users opt-in to do so from the safe browsing warning page.
+
+// An instance of this class is generated when a safe browsing warning page
+// is shown (SafeBrowsingBlockingPage).
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "components/safe_browsing/core/common/safe_browsing.mojom.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
+#include "components/security_interstitials/content/unsafe_resource.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "mojo/public/cpp/bindings/remote.h"
+
+namespace history {
+class HistoryService;
+}  // namespace history
+
+namespace network {
+class SharedURLLoaderFactory;
+}  // namespace network
+
+namespace safe_browsing {
+
+class BaseUIManager;
+class ReferrerChainProvider;
+
+// Maps a URL to its Resource.
+class ThreatDetailsCacheCollector;
+class ThreatDetailsRedirectsCollector;
+class ThreatDetailsFactory;
+
+using ResourceMap = std::unordered_map<
+    std::string,
+    std::unique_ptr<ClientSafeBrowsingReportRequest::Resource>>;
+
+// Maps a key of an HTML element to its corresponding HTMLElement proto message.
+// HTML Element keys have the form "<frame_id>-<node_id>", where |frame_id| is
+// the FrameTreeNode ID of the frame containing the element, and
+// |node_id| is a sequential ID for the element generated by the renderer.
+using ElementMap =
+    std::unordered_map<std::string, std::unique_ptr<HTMLElement>>;
+
+// Maps the key of an iframe element to the FrameTreeNode ID of the frame that
+// rendered the contents of the iframe.
+using KeyToFrameTreeIdMap = std::unordered_map<std::string, int>;
+
+// Maps a FrameTreeNode ID of a frame to a set of child IDs. The child IDs are
+// the Element IDs of the top-level HTML Elements in this frame.
+using FrameTreeIdToChildIdsMap =
+    std::unordered_map<int, std::unordered_set<int>>;
+
+// Callback used to notify a caller that ThreatDetails has finished creating and
+// sending a report.
+using ThreatDetailsDoneCallback =
+    base::OnceCallback<void(content::WebContents*)>;
+
+class ThreatDetails : public content::WebContentsObserver {
+ public:
+  typedef security_interstitials::UnsafeResource UnsafeResource;
+
+  ~ThreatDetails() override;
+
+  // Constructs a new ThreatDetails instance, using the factory.
+  static std::unique_ptr<ThreatDetails> NewThreatDetails(
+      BaseUIManager* ui_manager,
+      content::WebContents* web_contents,
+      const UnsafeResource& resource,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      history::HistoryService* history_service,
+      ReferrerChainProvider* referrer_chain_provider,
+      bool trim_to_ad_tags,
+      ThreatDetailsDoneCallback done_callback);
+
+  // Makes the passed |factory| the factory used to instantiate
+  // SafeBrowsingBlockingPage objects. Useful for tests.
+  static void RegisterFactory(ThreatDetailsFactory* factory) {
+    factory_ = factory;
+  }
+
+  // The SafeBrowsingBlockingPage calls this from the IO thread when
+  // the user is leaving the blocking page and has opted-in to sending
+  // the report. We start the redirection urls collection from history service
+  // in UI thread; then do cache collection back in IO thread. We also record
+  // if the user did proceed with the warning page, and how many times user
+  // visited this page before. When we are done, we send the report.
+  virtual void FinishCollection(bool did_proceed, int num_visits);
+
+  void OnCacheCollectionReady();
+
+  void OnRedirectionCollectionReady();
+
+  // WebContentsObserver implementation:
+  void FrameDeleted(content::RenderFrameHost* render_frame_host) override;
+  void RenderFrameHostChanged(content::RenderFrameHost* old_host,
+                              content::RenderFrameHost* new_host) override;
+
+  base::WeakPtr<ThreatDetails> GetWeakPtr();
+
+ protected:
+  friend class ThreatDetailsFactoryImpl;
+  friend class TestThreatDetailsFactory;
+  friend class ThreatDetailsTest;
+
+  ThreatDetails(
+      BaseUIManager* ui_manager,
+      content::WebContents* web_contents,
+      const UnsafeResource& resource,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      history::HistoryService* history_service,
+      ReferrerChainProvider* referrer_chain_provider,
+      bool trim_to_ad_tags,
+      ThreatDetailsDoneCallback done_callback);
+
+  // Default constructor for testing only.
+  ThreatDetails();
+
+  virtual void AddDOMDetails(const int frame_tree_node_id,
+                             std::vector<mojom::ThreatDOMDetailsNodePtr> params,
+                             const KeyToFrameTreeIdMap& child_frame_tree_map);
+
+  // The report protocol buffer.
+  std::unique_ptr<ClientSafeBrowsingReportRequest> report_;
+
+  // Used to get a pointer to the HTTP cache.
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
+
+  // Starts the collection of the report.
+  void StartCollection();
+
+ private:
+  // Whether the url is "public" so we can add it to the report.
+  bool IsReportableUrl(const GURL& url) const;
+
+  // Finds an existing Resource for the given url, or creates a new one if not
+  // found, and adds it to |resources_|. Returns the found/created resource.
+  ClientSafeBrowsingReportRequest::Resource* FindOrCreateResource(
+      const GURL& url);
+
+  // Finds an existing HTMLElement for a given key, or creates a new one if not
+  // found and adds it to |elements_|. Returns the found/created element.
+  HTMLElement* FindOrCreateElement(const std::string& element_key);
+
+  // Adds a Resource to resources_ with the given parent-child
+  // relationship. |parent| and |tagname| can be empty, |children| can be NULL.
+  // Returns the Resource that was affected, or null if no work was done.
+  ClientSafeBrowsingReportRequest::Resource* AddUrl(
+      const GURL& url,
+      const GURL& parent,
+      const std::string& tagname,
+      const std::vector<GURL>* children);
+
+  void RequestThreatDOMDetails(content::RenderFrameHost* frame);
+
+  void OnReceivedThreatDOMDetails(
+      mojo::Remote<mojom::ThreatReporter> threat_reporter,
+      content::RenderFrameHost* sender,
+      std::vector<mojom::ThreatDOMDetailsNodePtr> params);
+
+  void AddRedirectUrlList(const std::vector<GURL>& urls);
+
+  // Adds an HTML Element to the DOM structure. |frame_tree_node_id| is the
+  // unique ID of the frame the element came from. |element_node_id| is a unique
+  // ID of the element within the frame. |tag_name| is the tag of the element.
+  // |parent_element_node_id| is the unique ID of the parent element within the
+  // frame. |attributes| contains the names and values of the element's
+  // attributes. |inner_html| is set if the element contains inline JavaScript.
+  // |resource| is set if this element is a resource.
+  void AddDomElement(const int frame_tree_node_id,
+                     const int element_node_id,
+                     const std::string& tag_name,
+                     const int parent_element_node_id,
+                     const std::vector<mojom::AttributeNameValuePtr> attributes,
+                     const std::string& inner_html,
+                     const ClientSafeBrowsingReportRequest::Resource* resource);
+
+  // Populates the referrer chain data in |report_|. This may be skipped if the
+  // referrer chain provider isn't available, or the type of report doesn't
+  // include the referrer chain.
+  void MaybeFillReferrerChain();
+
+  // Called when the report is complete. Runs |done_callback_|.
+  void AllDone();
+
+  scoped_refptr<BaseUIManager> ui_manager_;
+
+  const UnsafeResource resource_;
+
+  ReferrerChainProvider* referrer_chain_provider_;
+
+  // For every Url we collect we create a Resource message. We keep
+  // them in a map so we can avoid duplicates.
+  ResourceMap resources_;
+
+  // Store all HTML elements collected, keep them in a map for easy lookup.
+  ElementMap elements_;
+
+  // For each iframe element encountered we map the key of the iframe to the
+  // FrameTreeNode ID of the frame containing the contents of that iframe.
+  // We populate this map when receiving results from ThreatDomDetails, and use
+  // it in a second pass (after FinishCollection) to attach children to iframe
+  // elements.
+  // Should only be accessed on the IO thread.
+  KeyToFrameTreeIdMap iframe_key_to_frame_tree_id_map_;
+
+  // When getting a set of elements from a frame, we store the frame's
+  // FrameTreeNode ID and a collection of all top-level elements in that frame.
+  // It is populated as we receive sets of nodes from different renderers.
+  // It is used together with |iframe_key_to_frame_tree_id_map_| in a second
+  // pass to insert child elements under their parent iframe elements.
+  FrameTreeIdToChildIdsMap frame_tree_id_to_children_map_;
+
+  // Result from the cache extractor.
+  bool cache_result_;
+
+  // Whether user did proceed with the safe browsing blocking page or
+  // not.
+  bool did_proceed_;
+
+  // How many times this user has visited this page before.
+  int num_visits_;
+
+  // Whether this report should be trimmed down to only ad tags, not the entire
+  // page contents. Used for sampling ads.
+  bool trim_to_ad_tags_;
+
+  // A vector containing the IDs of the DOM Elements to trim to. If an element
+  // ID is in this list, then its siblings and its children should be included
+  // in the report. Only populated if this report will be trimmed.
+  std::set<int> trimmed_dom_element_ids_;
+
+  // The factory used to instantiate SafeBrowsingBlockingPage objects.
+  // Useful for tests, so they can provide their own implementation of
+  // SafeBrowsingBlockingPage.
+  static ThreatDetailsFactory* factory_;
+
+  // Used to collect details from the HTTP Cache.
+  scoped_refptr<ThreatDetailsCacheCollector> cache_collector_;
+
+  // Used to collect redirect urls from the history service
+  scoped_refptr<ThreatDetailsRedirectsCollector> redirects_collector_;
+
+  // Callback to run when the report is finished.
+  ThreatDetailsDoneCallback done_callback_;
+
+  // Whether this ThreatDetails has begun finalizing the report and is expected
+  // to invoke |done_callback_| when it finishes.
+  bool all_done_expected_;
+
+  // Whether the |done_callback_| has been invoked.
+  bool is_all_done_;
+
+  // The set of RenderFrameHosts that have pending requests and haven't been
+  // deleted.
+  std::vector<content::RenderFrameHost*> pending_render_frame_hosts_;
+
+  // Used for references to |this| bound in callbacks.
+  base::WeakPtrFactory<ThreatDetails> weak_factory_{this};
+
+  FRIEND_TEST_ALL_PREFIXES(ThreatDetailsTest, HistoryServiceUrls);
+  FRIEND_TEST_ALL_PREFIXES(ThreatDetailsTest, HttpsResourceSanitization);
+  FRIEND_TEST_ALL_PREFIXES(ThreatDetailsTest, HTTPCacheNoEntries);
+  FRIEND_TEST_ALL_PREFIXES(ThreatDetailsTest, HTTPCache);
+  FRIEND_TEST_ALL_PREFIXES(ThreatDetailsTest, ThreatDOMDetails_AmbiguousDOM);
+  FRIEND_TEST_ALL_PREFIXES(ThreatDetailsTest,
+                           ThreatDOMDetails_EmptyReportNotSent);
+  FRIEND_TEST_ALL_PREFIXES(ThreatDetailsTest, ThreatDOMDetails_MultipleFrames);
+  FRIEND_TEST_ALL_PREFIXES(ThreatDetailsTest, ThreatDOMDetails_TrimToAdTags);
+  FRIEND_TEST_ALL_PREFIXES(ThreatDetailsTest, ThreatDOMDetails);
+
+  DISALLOW_COPY_AND_ASSIGN(ThreatDetails);
+};
+
+// Factory for creating ThreatDetails.  Useful for tests.
+class ThreatDetailsFactory {
+ public:
+  virtual ~ThreatDetailsFactory() {}
+
+  virtual std::unique_ptr<ThreatDetails> CreateThreatDetails(
+      BaseUIManager* ui_manager,
+      content::WebContents* web_contents,
+      const security_interstitials::UnsafeResource& unsafe_resource,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      history::HistoryService* history_service,
+      ReferrerChainProvider* referrer_chain_provider,
+      bool trim_to_ad_tags,
+      ThreatDetailsDoneCallback done_callback) = 0;
+};
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CONTENT_BROWSER_THREAT_DETAILS_H_
diff --git a/components/safe_browsing/content/browser/threat_details_cache.cc b/components/safe_browsing/content/browser/threat_details_cache.cc
new file mode 100644
index 0000000..f123958b
--- /dev/null
+++ b/components/safe_browsing/content/browser/threat_details_cache.cc
@@ -0,0 +1,241 @@
+// Copyright (c) 2012 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.
+//
+// Implementation of the ThreatDetailsCache class.
+
+#include "components/safe_browsing/content/browser/threat_details_cache.h"
+
+#include <stdint.h>
+
+#include "base/bind.h"
+#include "base/hash/md5.h"
+#include "base/lazy_instance.h"
+#include "base/strings/string_util.h"
+#include "base/task/post_task.h"
+#include "components/safe_browsing/content/browser/threat_details.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/load_flags.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_response_headers.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/simple_url_loader.h"
+
+using content::BrowserThread;
+
+// Only send small files for now, a better strategy would use the size
+// of the whole report and the user's bandwidth.
+static const uint32_t kMaxBodySizeBytes = 1024;
+
+namespace safe_browsing {
+
+ThreatDetailsCacheCollector::ThreatDetailsCacheCollector()
+    : resources_(nullptr), result_(nullptr), has_started_(false) {}
+
+void ThreatDetailsCacheCollector::StartCacheCollection(
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    ResourceMap* resources,
+    bool* result,
+    base::OnceClosure callback) {
+  // Start the data collection from the HTTP cache. We use a URLFetcher
+  // and set the right flags so we only hit the cache.
+  DVLOG(1) << "Getting cache data for all urls...";
+  url_loader_factory_ = url_loader_factory;
+  resources_ = resources;
+  resources_it_ = resources_->begin();
+  result_ = result;
+  callback_ = std::move(callback);
+  has_started_ = true;
+
+  // Post a task in the message loop, so the callers don't need to
+  // check if we call their callback immediately.
+  base::PostTask(FROM_HERE, {BrowserThread::UI},
+                 base::BindOnce(&ThreatDetailsCacheCollector::OpenEntry, this));
+}
+
+bool ThreatDetailsCacheCollector::HasStarted() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  return has_started_;
+}
+
+ThreatDetailsCacheCollector::~ThreatDetailsCacheCollector() {}
+
+// Fetch a URL and advance to the next one when done.
+void ThreatDetailsCacheCollector::OpenEntry() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DVLOG(1) << "OpenEntry";
+
+  if (resources_it_ == resources_->end()) {
+    AllDone(true);
+    return;
+  }
+
+  if (!url_loader_factory_) {
+    DVLOG(1) << "Missing URLLoaderFactory";
+    AllDone(false);
+    return;
+  }
+
+  net::NetworkTrafficAnnotationTag traffic_annotation =
+      net::DefineNetworkTrafficAnnotation("safe_browsing_cache_collector", R"(
+        semantics {
+          sender: "Threat Details Cache Collector"
+          description:
+            "This request fetches different items from safe browsing cache "
+            "and DOES NOT make an actual network request."
+          trigger:
+            "When safe browsing extended report is collecting data."
+          data:
+            "None"
+          destination: OTHER
+        }
+        policy {
+          cookies_allowed: NO
+          setting:
+            "Users can enable or disable this feature by stopping sending "
+            "security incident reports to Google via disabling 'Automatically "
+            "report details of possible security incidents to Google.' in "
+            "Chrome's settings under Advanced Settings, Privacy. The feature "
+            "is disabled by default."
+          chrome_policy {
+            SafeBrowsingExtendedReportingOptInAllowed {
+              policy_options {mode: MANDATORY}
+              SafeBrowsingExtendedReportingOptInAllowed: false
+            }
+          }
+        })");
+
+  auto resource_request = std::make_unique<network::ResourceRequest>();
+  resource_request->url = GURL(resources_it_->first);
+  // Only from cache, and don't use cookies.
+  resource_request->load_flags =
+      net::LOAD_ONLY_FROM_CACHE | net::LOAD_SKIP_CACHE_VALIDATION;
+  resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
+  current_load_ = network::SimpleURLLoader::Create(std::move(resource_request),
+                                                   traffic_annotation);
+  current_load_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+      url_loader_factory_.get(),
+      base::BindOnce(&ThreatDetailsCacheCollector::OnURLLoaderComplete,
+                     base::Unretained(this)));
+}
+
+ClientSafeBrowsingReportRequest::Resource*
+ThreatDetailsCacheCollector::GetResource(const GURL& url) {
+  auto it = resources_->find(url.spec());
+  if (it != resources_->end()) {
+    return it->second.get();
+  }
+  return nullptr;
+}
+
+void ThreatDetailsCacheCollector::OnURLLoaderComplete(
+    std::unique_ptr<std::string> response_body) {
+  DVLOG(1) << "OnURLLoaderComplete";
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK(current_load_);
+  if (current_load_->NetError() == net::ERR_CACHE_MISS) {
+    // Cache miss, skip this resource.
+    DVLOG(1) << "Cache miss for url: " << current_load_->GetFinalURL();
+    AdvanceEntry();
+    return;
+  }
+
+  if (current_load_->NetError() != net::OK) {
+    // Some other error occurred, e.g. the request could have been cancelled.
+    DVLOG(1) << "Unsuccessful fetch: " << current_load_->GetFinalURL();
+    AdvanceEntry();
+    return;
+  }
+
+  // Set the response headers and body to the right resource, which
+  // might not be the same as the one we asked for.
+  // For redirects, resources_it_->first != url.spec().
+  ClientSafeBrowsingReportRequest::Resource* resource =
+      GetResource(current_load_->GetFinalURL());
+  if (!resource) {
+    DVLOG(1) << "Cannot find resource for url:" << current_load_->GetFinalURL();
+    AdvanceEntry();
+    return;
+  }
+
+  ReadResponse(resource);
+  std::string data;
+  if (response_body)
+    data = *response_body;
+  ReadData(resource, data);
+  AdvanceEntry();
+}
+
+void ThreatDetailsCacheCollector::ReadResponse(
+    ClientSafeBrowsingReportRequest::Resource* pb_resource) {
+  DVLOG(1) << "ReadResponse";
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  if (!current_load_->ResponseInfo() ||
+      !current_load_->ResponseInfo()->headers) {
+    DVLOG(1) << "Missing response headers.";
+    return;
+  }
+  net::HttpResponseHeaders* headers =
+      current_load_->ResponseInfo()->headers.get();
+
+  ClientSafeBrowsingReportRequest::HTTPResponse* pb_response =
+      pb_resource->mutable_response();
+  pb_response->mutable_firstline()->set_code(headers->response_code());
+  size_t iter = 0;
+  std::string name, value;
+  while (headers->EnumerateHeaderLines(&iter, &name, &value)) {
+    // Strip any Set-Cookie headers.
+    if (base::LowerCaseEqualsASCII(name, "set-cookie"))
+      continue;
+    ClientSafeBrowsingReportRequest::HTTPHeader* pb_header =
+        pb_response->add_headers();
+    pb_header->set_name(name);
+    pb_header->set_value(value);
+  }
+
+  bool was_fetched_via_proxy =
+      current_load_->ResponseInfo()->proxy_server.is_valid() &&
+      !current_load_->ResponseInfo()->proxy_server.is_direct();
+  if (!was_fetched_via_proxy) {
+    pb_response->set_remote_ip(
+        current_load_->ResponseInfo()->remote_endpoint.ToString());
+  }
+}
+
+void ThreatDetailsCacheCollector::ReadData(
+    ClientSafeBrowsingReportRequest::Resource* pb_resource,
+    const std::string& data) {
+  DVLOG(1) << "ReadData";
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  ClientSafeBrowsingReportRequest::HTTPResponse* pb_response =
+      pb_resource->mutable_response();
+  if (data.size() <= kMaxBodySizeBytes) {  // Only send small bodies for now.
+    pb_response->set_body(data);
+  }
+  pb_response->set_bodylength(data.size());
+  pb_response->set_bodydigest(base::MD5String(data));
+}
+
+void ThreatDetailsCacheCollector::AdvanceEntry() {
+  DVLOG(1) << "AdvanceEntry";
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  // Advance to the next resource.
+  ++resources_it_;
+  current_load_.reset();
+
+  // Create a task so we don't take over the UI thread for too long.
+  base::PostTask(FROM_HERE, {BrowserThread::UI},
+                 base::BindOnce(&ThreatDetailsCacheCollector::OpenEntry, this));
+}
+
+void ThreatDetailsCacheCollector::AllDone(bool success) {
+  DVLOG(1) << "AllDone";
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  *result_ = success;
+  base::PostTask(FROM_HERE, {BrowserThread::UI}, std::move(callback_));
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/content/browser/threat_details_cache.h b/components/safe_browsing/content/browser/threat_details_cache.h
new file mode 100644
index 0000000..8342690
--- /dev/null
+++ b/components/safe_browsing/content/browser/threat_details_cache.h
@@ -0,0 +1,105 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CONTENT_BROWSER_THREAT_DETAILS_CACHE_H_
+#define COMPONENTS_SAFE_BROWSING_CONTENT_BROWSER_THREAT_DETAILS_CACHE_H_
+
+// A class that gets threat details from the HTTP Cache.
+// An instance of this class is generated by ThreatDetails.
+
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
+
+namespace network {
+class SimpleURLLoader;
+class SharedURLLoaderFactory;
+}
+
+class GURL;
+
+namespace safe_browsing {
+
+// Maps a URL to its Resource.
+typedef std::unordered_map<
+    std::string,
+    std::unique_ptr<ClientSafeBrowsingReportRequest::Resource>>
+    ResourceMap;
+
+class ThreatDetailsCacheCollector
+    : public base::RefCounted<ThreatDetailsCacheCollector> {
+ public:
+  ThreatDetailsCacheCollector();
+
+  // We use |request_context_getter|, we modify |resources| and
+  // |result|, and we call |callback|, so they must all remain alive
+  // for the lifetime of this object.
+  void StartCacheCollection(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      ResourceMap* resources,
+      bool* result,
+      base::OnceClosure callback);
+
+  // Returns whether or not StartCacheCollection has been called.
+  bool HasStarted();
+
+ protected:
+  // Called after the request completes (either successfully or with failure).
+  void OnURLLoaderComplete(std::unique_ptr<std::string> response_body);
+
+ private:
+  friend class base::RefCounted<ThreatDetailsCacheCollector>;
+
+  ~ThreatDetailsCacheCollector();
+
+  // Points to the url for which we are fetching the HTTP cache entry or
+  // redirect chain.
+  ResourceMap::iterator resources_it_;
+
+  // Points to the resources_ map in the ThreatDetails.
+  ResourceMap* resources_;
+
+  // Points to the cache_result_ in the ThreatDetails.
+  bool* result_;
+
+  // Method we call when we are done. The caller must be alive for the
+  // whole time, we are modifying its state (see above).
+  base::OnceClosure callback_;
+
+  // Set to true as soon as StartCacheCollection is called.
+  bool has_started_;
+
+  // Used to get a pointer to the current URLLoaderFactory.
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
+
+  // The current SimpleURLLoader.
+  std::unique_ptr<network::SimpleURLLoader> current_load_;
+
+  // Returns the resource from resources_ that corresponds to |url|
+  ClientSafeBrowsingReportRequest::Resource* GetResource(const GURL& url);
+
+  // Creates a new URLFetcher and starts it.
+  void OpenEntry();
+
+  // Read the HTTP response from |current_load_| and add it to |pb_resource|.
+  void ReadResponse(ClientSafeBrowsingReportRequest::Resource* pb_resource);
+
+  // Read the body |data| and add it to |pb_resource|.
+  void ReadData(ClientSafeBrowsingReportRequest::Resource* pb_resource,
+                const std::string& data);
+
+  // Called when we are done.
+  void AllDone(bool success);
+
+  // Advances to the next entry in resources_it_.
+  void AdvanceEntry();
+};
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CONTENT_BROWSER_THREAT_DETAILS_CACHE_H_
diff --git a/components/safe_browsing/content/browser/threat_details_history.cc b/components/safe_browsing/content/browser/threat_details_history.cc
new file mode 100644
index 0000000..d434e97c
--- /dev/null
+++ b/components/safe_browsing/content/browser/threat_details_history.cc
@@ -0,0 +1,119 @@
+// Copyright (c) 2012 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.
+//
+// Implementation of the ThreatDetailsRedirectsCollector class.
+
+#include "components/safe_browsing/content/browser/threat_details_history.h"
+
+#include <stddef.h>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/task/post_task.h"
+#include "components/safe_browsing/content/browser/threat_details.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+
+using content::BrowserThread;
+
+namespace safe_browsing {
+
+ThreatDetailsRedirectsCollector::ThreatDetailsRedirectsCollector(
+    const base::WeakPtr<history::HistoryService>& history_service)
+    : has_started_(false), history_service_(history_service) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  if (history_service) {
+    history_service_observer_.Add(history_service.get());
+  }
+}
+
+void ThreatDetailsRedirectsCollector::StartHistoryCollection(
+    const std::vector<GURL>& urls,
+    base::OnceClosure callback) {
+  DVLOG(1) << "Num of urls to check in history service: " << urls.size();
+  has_started_ = true;
+  callback_ = std::move(callback);
+
+  if (urls.size() == 0) {
+    AllDone();
+    return;
+  }
+
+  base::PostTask(
+      FROM_HERE, {BrowserThread::UI},
+      base::BindOnce(&ThreatDetailsRedirectsCollector::StartGetRedirects, this,
+                     urls));
+}
+
+bool ThreatDetailsRedirectsCollector::HasStarted() const {
+  return has_started_;
+}
+
+const std::vector<RedirectChain>&
+ThreatDetailsRedirectsCollector::GetCollectedUrls() const {
+  return redirects_urls_;
+}
+
+ThreatDetailsRedirectsCollector::~ThreatDetailsRedirectsCollector() {}
+
+void ThreatDetailsRedirectsCollector::StartGetRedirects(
+    const std::vector<GURL>& urls) {
+  // History access from profile needs to happen in UI thread
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  for (size_t i = 0; i < urls.size(); ++i) {
+    urls_.push_back(urls[i]);
+  }
+  urls_it_ = urls_.begin();
+  GetRedirects(*urls_it_);
+}
+
+void ThreatDetailsRedirectsCollector::GetRedirects(const GURL& url) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  if (!history_service_) {
+    AllDone();
+    return;
+  }
+
+  history_service_->QueryRedirectsTo(
+      url,
+      base::BindOnce(&ThreatDetailsRedirectsCollector::OnGotQueryRedirectsTo,
+                     base::Unretained(this), url),
+      &request_tracker_);
+}
+
+void ThreatDetailsRedirectsCollector::OnGotQueryRedirectsTo(
+    const GURL& url,
+    history::RedirectList redirect_list) {
+  if (!redirect_list.empty()) {
+    std::vector<GURL> urllist;
+    urllist.push_back(url);
+    urllist.insert(urllist.end(), redirect_list.begin(), redirect_list.end());
+    redirects_urls_.push_back(urllist);
+  }
+
+  // Proceed to next url
+  ++urls_it_;
+
+  if (urls_it_ == urls_.end()) {
+    AllDone();
+    return;
+  }
+
+  GetRedirects(*urls_it_);
+}
+
+void ThreatDetailsRedirectsCollector::AllDone() {
+  DVLOG(1) << "AllDone";
+  base::PostTask(FROM_HERE, {BrowserThread::UI}, std::move(callback_));
+}
+
+void ThreatDetailsRedirectsCollector::HistoryServiceBeingDeleted(
+    history::HistoryService* history_service) {
+  history_service_observer_.Remove(history_service);
+  history_service_.reset();
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/content/browser/threat_details_history.h b/components/safe_browsing/content/browser/threat_details_history.h
new file mode 100644
index 0000000..efc037c
--- /dev/null
+++ b/components/safe_browsing/content/browser/threat_details_history.h
@@ -0,0 +1,87 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CONTENT_BROWSER_THREAT_DETAILS_HISTORY_H_
+#define COMPONENTS_SAFE_BROWSING_CONTENT_BROWSER_THREAT_DETAILS_HISTORY_H_
+
+// This class gets redirect chain for urls from the history service.
+
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/scoped_observer.h"
+#include "base/sequenced_task_runner_helpers.h"
+#include "base/task/cancelable_task_tracker.h"
+#include "components/history/core/browser/history_service.h"
+#include "components/history/core/browser/history_service_observer.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace safe_browsing {
+
+typedef std::vector<GURL> RedirectChain;
+
+class ThreatDetailsRedirectsCollector
+    : public base::RefCounted<ThreatDetailsRedirectsCollector>,
+      public history::HistoryServiceObserver {
+ public:
+  explicit ThreatDetailsRedirectsCollector(
+      const base::WeakPtr<history::HistoryService>& history_service);
+
+  // Collects urls' redirects chain information from the history service.
+  // We get access to history service via web_contents in UI thread.
+  void StartHistoryCollection(const std::vector<GURL>& urls,
+                              base::OnceClosure callback);
+
+  // Returns whether or not StartCacheCollection has been called.
+  bool HasStarted() const;
+
+  // Returns the redirect urls we get from history service
+  const std::vector<RedirectChain>& GetCollectedUrls() const;
+
+  // history::HistoryServiceObserver
+  void HistoryServiceBeingDeleted(
+      history::HistoryService* history_service) override;
+
+ private:
+  friend class base::RefCounted<ThreatDetailsRedirectsCollector>;
+
+  ~ThreatDetailsRedirectsCollector() override;
+
+  void StartGetRedirects(const std::vector<GURL>& urls);
+  void GetRedirects(const GURL& url);
+  void OnGotQueryRedirectsTo(const GURL& url,
+                             history::RedirectList redirect_list);
+
+  // Runs the callback when redirects collecting is all done.
+  void AllDone();
+
+  base::CancelableTaskTracker request_tracker_;
+
+  // Method we call when we are done. The caller must be alive for the
+  // whole time, we are modifying its state (see above).
+  base::OnceClosure callback_;
+
+  // Sets to true once StartHistoryCollection is called
+  bool has_started_;
+
+  // The urls we need to get redirects for
+  std::vector<GURL> urls_;
+  // The iterator goes over urls_
+  std::vector<GURL>::iterator urls_it_;
+  // The collected directs from history service
+  std::vector<RedirectChain> redirects_urls_;
+
+  base::WeakPtr<history::HistoryService> history_service_;
+  ScopedObserver<history::HistoryService, history::HistoryServiceObserver>
+      history_service_observer_{this};
+
+  DISALLOW_COPY_AND_ASSIGN(ThreatDetailsRedirectsCollector);
+};
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CONTENT_BROWSER_THREAT_DETAILS_HISTORY_H_
diff --git a/components/safe_browsing/content/password_protection/BUILD.gn b/components/safe_browsing/content/password_protection/BUILD.gn
new file mode 100644
index 0000000..0a8818d
--- /dev/null
+++ b/components/safe_browsing/content/password_protection/BUILD.gn
@@ -0,0 +1,120 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//components/safe_browsing/buildflags.gni")
+import("//extensions/buildflags/buildflags.gni")
+
+source_set("password_protection") {
+  if (safe_browsing_mode == 1 || safe_browsing_mode == 2) {
+    sources = [
+      "password_protection_navigation_throttle.cc",
+      "password_protection_navigation_throttle.h",
+      "password_protection_request.cc",
+      "password_protection_request.h",
+      "password_protection_service.cc",
+      "password_protection_service.h",
+      "visual_utils.cc",
+      "visual_utils.h",
+    ]
+
+    public_deps = [
+      "//google_apis:google_apis",
+    ]
+
+    deps = [
+      ":password_protection_metrics_util",
+      "//base:base",
+      "//components/content_settings/core/browser:browser",
+      "//components/history/core/browser:browser",
+      "//components/password_manager/core/browser:browser",
+      "//components/safe_browsing/content/web_ui:web_ui",
+      "//components/safe_browsing/core:csd_proto",
+      "//components/safe_browsing/core:features",
+      "//components/safe_browsing/core/browser:referrer_chain_provider",
+      "//components/safe_browsing/core/common:common",
+      "//components/safe_browsing/core/common:interfaces",
+      "//components/safe_browsing/core/common:safe_browsing_prefs",
+      "//components/safe_browsing/core/db:allowlist_checker_client",
+      "//components/safe_browsing/core/db:database_manager",
+      "//components/safe_browsing/core/db:v4_protocol_manager_util",
+      "//components/sessions",
+      "//components/signin/public/base",
+      "//components/signin/public/identity_manager",
+      "//content/public/browser:browser",
+      "//net:net",
+      "//third_party/protobuf:protobuf_lite",
+    ]
+  }
+  if (safe_browsing_mode == 1) {
+    deps += [ "//components/zoom" ]
+  }
+}
+
+source_set("password_protection_metrics_util") {
+  sources = [
+    "metrics_util.cc",
+    "metrics_util.h",
+  ]
+
+  deps = [
+    "//base",
+    "//components/safe_browsing/core:csd_proto",
+    "//net:net",
+    "//ui/gfx/geometry:geometry",
+  ]
+}
+
+source_set("password_protection_unittest") {
+  testonly = true
+  if (safe_browsing_mode == 1) {
+    sources = [
+      "password_protection_service_unittest.cc",
+      "visual_utils_unittest.cc",
+    ]
+    deps = [
+      ":mock_password_protection",
+      ":password_protection",
+      ":password_protection_metrics_util",
+      "//base",
+      "//base/test:test_support",
+      "//components/content_settings/core/browser:browser",
+      "//components/history/core/browser:browser",
+      "//components/password_manager/core/browser:browser",
+      "//components/safe_browsing/core:csd_proto",
+      "//components/safe_browsing/core:features",
+      "//components/safe_browsing/core:verdict_cache_manager",
+      "//components/safe_browsing/core/common:interfaces",
+      "//components/safe_browsing/core/db:test_database_manager",
+      "//components/signin/public/base",
+      "//components/signin/public/identity_manager",
+      "//components/sync_preferences:test_support",
+      "//content/test:test_support",
+      "//net:test_support",
+      "//services/network:test_support",
+      "//testing/gmock",
+      "//testing/gtest",
+      "//third_party/protobuf:protobuf_lite",
+    ]
+  }
+}
+
+source_set("mock_password_protection") {
+  testonly = true
+  if (safe_browsing_mode == 1) {
+    sources = [
+      "mock_password_protection_service.cc",
+      "mock_password_protection_service.h",
+    ]
+    deps = [
+      ":password_protection",
+      "//base",
+      "//components/content_settings/core/browser:browser",
+      "//components/safe_browsing/core/db:database_manager",
+      "//net:test_support",
+      "//services/network/public/cpp:cpp",
+      "//testing/gmock",
+      "//testing/gtest",
+    ]
+  }
+}
diff --git a/components/safe_browsing/password_protection/DEPS b/components/safe_browsing/content/password_protection/DEPS
similarity index 100%
rename from components/safe_browsing/password_protection/DEPS
rename to components/safe_browsing/content/password_protection/DEPS
diff --git a/components/safe_browsing/content/password_protection/metrics_util.cc b/components/safe_browsing/content/password_protection/metrics_util.cc
new file mode 100644
index 0000000..233e4b3
--- /dev/null
+++ b/components/safe_browsing/content/password_protection/metrics_util.cc
@@ -0,0 +1,392 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/content/password_protection/metrics_util.h"
+
+#include "base/metrics/histogram_functions.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/time/time.h"
+#include "net/http/http_status_code.h"
+
+namespace safe_browsing {
+
+const char kAnyPasswordEntryRequestOutcomeHistogram[] =
+    "PasswordProtection.RequestOutcome.AnyPasswordEntry";
+const char kAnyPasswordEntryVerdictHistogram[] =
+    "PasswordProtection.Verdict.AnyPasswordEntry";
+const char kEnterprisePasswordEntryRequestOutcomeHistogram[] =
+    "PasswordProtection.RequestOutcome.NonGaiaEnterprisePasswordEntry";
+const char kEnterprisePasswordEntryVerdictHistogram[] =
+    "PasswordProtection.Verdict.NonGaiaEnterprisePasswordEntry";
+const char kEnterprisePasswordPageInfoHistogram[] =
+    "PasswordProtection.PageInfoAction.NonGaiaEnterprisePasswordEntry";
+const char kEnterprisePasswordInterstitialHistogram[] =
+    "PasswordProtection.InterstitialAction.NonGaiaEnterprisePasswordEntry";
+const char kEnterprisePasswordWarningDialogHistogram[] =
+    "PasswordProtection.ModalWarningDialogAction."
+    "NonGaiaEnterprisePasswordEntry";
+const char kGmailSyncPasswordEntryRequestOutcomeHistogram[] =
+    "PasswordProtection.RequestOutcome.GmailSyncPasswordEntry";
+const char kGmailNonSyncPasswordEntryRequestOutcomeHistogram[] =
+    "PasswordProtection.RequestOutcome.GmailNonSyncPasswordEntry";
+const char kGSuiteSyncPasswordEntryRequestOutcomeHistogram[] =
+    "PasswordProtection.RequestOutcome.GSuiteSyncPasswordEntry";
+const char kGSuiteNonSyncPasswordEntryRequestOutcomeHistogram[] =
+    "PasswordProtection.RequestOutcome.GSuiteNonSyncPasswordEntry";
+const char kSavedPasswordEntryRequestOutcomeHistogram[] =
+    "PasswordProtection.RequestOutcome.SavedPasswordEntry";
+const char kUnknownPrimaryAccountPasswordEntryVerdictHistogram[] =
+    "PasswordProtection.Verdict.UnknownPrimaryPasswordEntry";
+const char kUnknownNonPrimaryAccountPasswordEntryVerdictHistogram[] =
+    "PasswordProtection.Verdict.UnknownNonPrimaryPasswordEntry";
+const char kGSuiteSyncPasswordEntryVerdictHistogram[] =
+    "PasswordProtection.Verdict.GSuiteSyncPasswordEntry";
+const char kGSuiteNonSyncPasswordEntryVerdictHistogram[] =
+    "PasswordProtection.Verdict.GSuiteNonSyncPasswordEntry";
+const char kGmailSyncPasswordEntryVerdictHistogram[] =
+    "PasswordProtection.Verdict.GmailSyncPasswordEntry";
+const char kGmailNonSyncPasswordEntryVerdictHistogram[] =
+    "PasswordProtection.Verdict.GmailNonSyncPasswordEntry";
+const char kSavedPasswordEntryVerdictHistogram[] =
+    "PasswordProtection.Verdict.SavedPasswordEntry";
+const char kGmailNonSyncPasswordInterstitialHistogram[] =
+    "PasswordProtection.InterstitialAction.GmailNonSyncPasswordEntry";
+const char kGmailSyncPasswordPageInfoHistogram[] =
+    "PasswordProtection.PageInfoAction.GmailSyncPasswordEntry";
+const char kGmailNonSyncPasswordPageInfoHistogram[] =
+    "PasswordProtection.PageInfoAction.GmailNonSyncPasswordEntry";
+const char kSavedPasswordPageInfoHistogram[] =
+    "PasswordProtection.PageInfoAction.SavedPasswordEntry";
+const char kGmailSyncPasswordWarningDialogHistogram[] =
+    "PasswordProtection.ModalWarningDialogAction.GmailSyncPasswordEntry";
+const char kGmailNonSyncPasswordWarningDialogHistogram[] =
+    "PasswordProtection.ModalWarningDialogAction.GmailNonSyncPasswordEntry";
+const char kNonSyncPasswordInterstitialHistogram[] =
+    "PasswordProtection.InterstitialAction.NonSyncPasswordEntry";
+const char kNonSyncPasswordPageInfoHistogram[] =
+    "PasswordProtection.PageInfoAction.NonSyncPasswordEntry";
+const char kGSuiteSyncPasswordInterstitialHistogram[] =
+    "PasswordProtection.InterstitialAction.GSuiteSyncPasswordEntry";
+const char kGSuiteNonSyncPasswordInterstitialHistogram[] =
+    "PasswordProtection.InterstitialAction.GSuiteNonSyncPasswordEntry";
+const char kGSuiteSyncPasswordPageInfoHistogram[] =
+    "PasswordProtection.PageInfoAction.GSuiteSyncPasswordEntry";
+const char kGSuiteNonSyncPasswordPageInfoHistogram[] =
+    "PasswordProtection.PageInfoAction.GSuiteNonSyncPasswordEntry";
+const char kGSuiteSyncPasswordWarningDialogHistogram[] =
+    "PasswordProtection.ModalWarningDialogAction.GSuiteSyncPasswordEntry";
+const char kGSuiteNonSyncPasswordWarningDialogHistogram[] =
+    "PasswordProtection.ModalWarningDialogAction.GSuiteNonSyncPasswordEntry";
+const char kSavedPasswordWarningDialogHistogram[] =
+    "PasswordProtection.ModalWarningDialogAction.SavedPasswordEntry";
+const char kNonSyncPasswordWarningDialogHistogram[] =
+    "PasswordProtection.ModalWarningDialogAction.NonSyncPasswordEntry";
+const char kPasswordOnFocusRequestOutcomeHistogram[] =
+    "PasswordProtection.RequestOutcome.PasswordFieldOnFocus";
+const char kPasswordOnFocusVerdictHistogram[] =
+    "PasswordProtection.Verdict.PasswordFieldOnFocus";
+const char kNonSyncPasswordEntryRequestOutcomeHistogram[] =
+    "PasswordProtection.RequestOutcome.NonSyncPasswordEntry";
+const char kSyncPasswordEntryRequestOutcomeHistogram[] =
+    "PasswordProtection.RequestOutcome.SyncPasswordEntry";
+const char kSyncPasswordEntryVerdictHistogram[] =
+    "PasswordProtection.Verdict.SyncPasswordEntry";
+const char kNonSyncPasswordEntryVerdictHistogram[] =
+    "PasswordProtection.Verdict.NonSyncPasswordEntry";
+const char kSyncPasswordChromeSettingsHistogram[] =
+    "PasswordProtection.ChromeSettingsAction.SyncPasswordEntry";
+const char kSyncPasswordInterstitialHistogram[] =
+    "PasswordProtection.InterstitialAction.SyncPasswordEntry";
+const char kSyncPasswordPageInfoHistogram[] =
+    "PasswordProtection.PageInfoAction.SyncPasswordEntry";
+const char kSyncPasswordWarningDialogHistogram[] =
+    "PasswordProtection.ModalWarningDialogAction.SyncPasswordEntry";
+const char kEnterprisePasswordAlertHistogram[] =
+    "PasswordProtection.PasswordAlertModeOutcome."
+    "NonGaiaEnterprisePasswordEntry";
+const char kGsuiteSyncPasswordAlertHistogram[] =
+    "PasswordProtection.PasswordAlertModeOutcome.GSuiteSyncPasswordEntry";
+const char kGsuiteNonSyncPasswordAlertHistogram[] =
+    "PasswordProtection.PasswordAlertModeOutcome.GSuiteNonSyncPasswordEntry";
+
+void LogPasswordEntryRequestOutcome(
+    RequestOutcome outcome,
+    ReusedPasswordAccountType password_account_type) {
+  UMA_HISTOGRAM_ENUMERATION(kAnyPasswordEntryRequestOutcomeHistogram, outcome);
+
+  bool is_gsuite_user =
+      password_account_type.account_type() == ReusedPasswordAccountType::GSUITE;
+  bool is_gmail_user =
+      password_account_type.account_type() == ReusedPasswordAccountType::GMAIL;
+  bool is_primary_account_password = password_account_type.is_account_syncing();
+  if (is_primary_account_password) {
+    base::UmaHistogramEnumeration(
+        is_gsuite_user ? kGSuiteSyncPasswordEntryRequestOutcomeHistogram
+                       : kGmailSyncPasswordEntryRequestOutcomeHistogram,
+        outcome);
+    UMA_HISTOGRAM_ENUMERATION(kSyncPasswordEntryRequestOutcomeHistogram,
+                              outcome);
+  } else if (password_account_type.account_type() ==
+             ReusedPasswordAccountType::NON_GAIA_ENTERPRISE) {
+    UMA_HISTOGRAM_ENUMERATION(kEnterprisePasswordEntryRequestOutcomeHistogram,
+                              outcome);
+  } else if (password_account_type.account_type() ==
+             ReusedPasswordAccountType::SAVED_PASSWORD) {
+    UMA_HISTOGRAM_ENUMERATION(kSavedPasswordEntryRequestOutcomeHistogram,
+                              outcome);
+  } else if (is_gsuite_user || is_gmail_user) {
+    base::UmaHistogramEnumeration(
+        is_gsuite_user ? kGSuiteNonSyncPasswordEntryRequestOutcomeHistogram
+                       : kGmailNonSyncPasswordEntryRequestOutcomeHistogram,
+        outcome);
+    UMA_HISTOGRAM_ENUMERATION(kNonSyncPasswordEntryRequestOutcomeHistogram,
+                              outcome);
+  }
+}
+
+void LogPasswordOnFocusRequestOutcome(RequestOutcome outcome) {
+  UMA_HISTOGRAM_ENUMERATION(kPasswordOnFocusRequestOutcomeHistogram, outcome);
+}
+
+void LogPasswordAlertModeOutcome(
+    RequestOutcome outcome,
+    ReusedPasswordAccountType password_account_type) {
+  DCHECK_NE(ReusedPasswordAccountType::GMAIL,
+            password_account_type.account_type());
+  if (password_account_type.account_type() ==
+      ReusedPasswordAccountType::NON_GAIA_ENTERPRISE) {
+    UMA_HISTOGRAM_ENUMERATION(kEnterprisePasswordAlertHistogram, outcome);
+  } else {
+    if (password_account_type.is_account_syncing()) {
+      UMA_HISTOGRAM_ENUMERATION(kGsuiteSyncPasswordAlertHistogram, outcome);
+    } else {
+      UMA_HISTOGRAM_ENUMERATION(kGsuiteNonSyncPasswordAlertHistogram, outcome);
+    }
+  }
+}
+
+void LogNoPingingReason(LoginReputationClientRequest::TriggerType trigger_type,
+                        RequestOutcome reason,
+                        ReusedPasswordAccountType password_account_type) {
+  DCHECK(trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
+         trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
+
+  if (trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) {
+    UMA_HISTOGRAM_ENUMERATION(kPasswordOnFocusRequestOutcomeHistogram, reason);
+  } else {
+    LogPasswordEntryRequestOutcome(reason, password_account_type);
+  }
+}
+
+void LogPasswordProtectionVerdict(
+    LoginReputationClientRequest::TriggerType trigger_type,
+    ReusedPasswordAccountType password_account_type,
+    VerdictType verdict_type) {
+  bool is_gsuite_user =
+      password_account_type.account_type() == ReusedPasswordAccountType::GSUITE;
+  bool is_gmail_user =
+      password_account_type.account_type() == ReusedPasswordAccountType::GMAIL;
+  bool is_account_syncing = password_account_type.is_account_syncing();
+
+  switch (trigger_type) {
+    case LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE:
+      UMA_HISTOGRAM_ENUMERATION(
+          kPasswordOnFocusVerdictHistogram, verdict_type,
+          (LoginReputationClientResponse_VerdictType_VerdictType_MAX + 1));
+      break;
+    case LoginReputationClientRequest::PASSWORD_REUSE_EVENT:
+      UMA_HISTOGRAM_ENUMERATION(
+          kAnyPasswordEntryVerdictHistogram, verdict_type,
+          (LoginReputationClientResponse_VerdictType_VerdictType_MAX + 1));
+      if (is_account_syncing) {
+        if (password_account_type.account_type() ==
+            ReusedPasswordAccountType::UNKNOWN) {
+          UMA_HISTOGRAM_ENUMERATION(
+              kUnknownPrimaryAccountPasswordEntryVerdictHistogram, verdict_type,
+              (LoginReputationClientResponse_VerdictType_VerdictType_MAX + 1));
+        }
+        UMA_HISTOGRAM_ENUMERATION(
+            kSyncPasswordEntryVerdictHistogram, verdict_type,
+            (LoginReputationClientResponse_VerdictType_VerdictType_MAX + 1));
+      } else if (is_gsuite_user || is_gmail_user) {
+        UMA_HISTOGRAM_ENUMERATION(
+            kNonSyncPasswordEntryVerdictHistogram, verdict_type,
+            (LoginReputationClientResponse_VerdictType_VerdictType_MAX + 1));
+      }
+      if (is_gsuite_user) {
+        if (is_account_syncing) {
+          UMA_HISTOGRAM_ENUMERATION(
+              kGSuiteSyncPasswordEntryVerdictHistogram, verdict_type,
+              (LoginReputationClientResponse_VerdictType_VerdictType_MAX + 1));
+        } else {
+          UMA_HISTOGRAM_ENUMERATION(
+              kGSuiteNonSyncPasswordEntryVerdictHistogram, verdict_type,
+              (LoginReputationClientResponse_VerdictType_VerdictType_MAX + 1));
+        }
+      } else if (is_gmail_user) {
+        if (is_account_syncing) {
+          UMA_HISTOGRAM_ENUMERATION(
+              kGmailSyncPasswordEntryVerdictHistogram, verdict_type,
+              (LoginReputationClientResponse_VerdictType_VerdictType_MAX + 1));
+        } else {
+          UMA_HISTOGRAM_ENUMERATION(
+              kGmailNonSyncPasswordEntryVerdictHistogram, verdict_type,
+              (LoginReputationClientResponse_VerdictType_VerdictType_MAX + 1));
+        }
+      } else if (password_account_type.account_type() ==
+                 ReusedPasswordAccountType::NON_GAIA_ENTERPRISE) {
+        UMA_HISTOGRAM_ENUMERATION(
+            kEnterprisePasswordEntryVerdictHistogram, verdict_type,
+            (LoginReputationClientResponse_VerdictType_VerdictType_MAX + 1));
+      } else if (password_account_type.account_type() ==
+                 ReusedPasswordAccountType::SAVED_PASSWORD) {
+        UMA_HISTOGRAM_ENUMERATION(
+            kSavedPasswordEntryVerdictHistogram, verdict_type,
+            (LoginReputationClientResponse_VerdictType_VerdictType_MAX + 1));
+      } else {
+        UMA_HISTOGRAM_ENUMERATION(
+            kUnknownNonPrimaryAccountPasswordEntryVerdictHistogram,
+            verdict_type,
+            (LoginReputationClientResponse_VerdictType_VerdictType_MAX + 1));
+      }
+      break;
+    default:
+      NOTREACHED();
+  }
+}
+
+void LogSyncAccountType(SyncAccountType sync_account_type) {
+  UMA_HISTOGRAM_ENUMERATION(
+      "PasswordProtection.PasswordReuseSyncAccountType", sync_account_type,
+      LoginReputationClientRequest::PasswordReuseEvent::SyncAccountType_MAX +
+          1);
+}
+
+void LogPasswordProtectionNetworkResponseAndDuration(
+    int response_code,
+    const base::TimeTicks& request_start_time) {
+  base::UmaHistogramSparse(
+      "PasswordProtection.PasswordProtectionResponseOrErrorCode",
+      response_code);
+  if (response_code == net::HTTP_OK) {
+    UMA_HISTOGRAM_TIMES("PasswordProtection.RequestNetworkDuration",
+                        base::TimeTicks::Now() - request_start_time);
+  }
+}
+
+void LogPasswordProtectionSampleReportSent() {
+  base::UmaHistogramBoolean("PasswordProtection.SampleReportSent", true);
+}
+
+void LogWarningAction(WarningUIType ui_type,
+                      WarningAction action,
+                      ReusedPasswordAccountType password_account_type) {
+  // |password_type| can be unknown if user directly navigates to
+  // chrome://reset-password page. In this case, do not record user action.
+  if (password_account_type.account_type() ==
+          ReusedPasswordAccountType::UNKNOWN &&
+      ui_type == WarningUIType::INTERSTITIAL) {
+    return;
+  }
+
+  bool is_gsuite_user =
+      password_account_type.account_type() == ReusedPasswordAccountType::GSUITE;
+  bool is_primary_account_password = password_account_type.is_account_syncing();
+  switch (ui_type) {
+    case WarningUIType::PAGE_INFO:
+      if (is_primary_account_password) {
+        UMA_HISTOGRAM_ENUMERATION(kSyncPasswordPageInfoHistogram, action);
+        if (is_gsuite_user) {
+          UMA_HISTOGRAM_ENUMERATION(kGSuiteSyncPasswordPageInfoHistogram,
+                                    action);
+        } else {
+          UMA_HISTOGRAM_ENUMERATION(kGmailSyncPasswordPageInfoHistogram,
+                                    action);
+        }
+      } else if (password_account_type.account_type() ==
+                 ReusedPasswordAccountType::NON_GAIA_ENTERPRISE) {
+        UMA_HISTOGRAM_ENUMERATION(kEnterprisePasswordPageInfoHistogram, action);
+      } else if (password_account_type.account_type() ==
+                 ReusedPasswordAccountType::SAVED_PASSWORD) {
+        UMA_HISTOGRAM_ENUMERATION(kSavedPasswordPageInfoHistogram, action);
+      } else {
+        UMA_HISTOGRAM_ENUMERATION(kNonSyncPasswordPageInfoHistogram, action);
+        if (is_gsuite_user) {
+          UMA_HISTOGRAM_ENUMERATION(kGSuiteNonSyncPasswordPageInfoHistogram,
+                                    action);
+        } else {
+          UMA_HISTOGRAM_ENUMERATION(kGmailNonSyncPasswordPageInfoHistogram,
+                                    action);
+        }
+      }
+      break;
+    case WarningUIType::MODAL_DIALOG:
+      if (is_primary_account_password) {
+        UMA_HISTOGRAM_ENUMERATION(kSyncPasswordWarningDialogHistogram, action);
+        if (is_gsuite_user) {
+          UMA_HISTOGRAM_ENUMERATION(kGSuiteSyncPasswordWarningDialogHistogram,
+                                    action);
+        } else {
+          UMA_HISTOGRAM_ENUMERATION(kGmailSyncPasswordWarningDialogHistogram,
+                                    action);
+        }
+      } else if (password_account_type.account_type() ==
+                 ReusedPasswordAccountType::NON_GAIA_ENTERPRISE) {
+        UMA_HISTOGRAM_ENUMERATION(kEnterprisePasswordWarningDialogHistogram,
+                                  action);
+      } else if (password_account_type.account_type() ==
+                 ReusedPasswordAccountType::SAVED_PASSWORD) {
+        UMA_HISTOGRAM_ENUMERATION(kSavedPasswordWarningDialogHistogram, action);
+      } else {
+        UMA_HISTOGRAM_ENUMERATION(kNonSyncPasswordWarningDialogHistogram,
+                                  action);
+        if (is_gsuite_user) {
+          UMA_HISTOGRAM_ENUMERATION(
+              kGSuiteNonSyncPasswordWarningDialogHistogram, action);
+        } else {
+          UMA_HISTOGRAM_ENUMERATION(kGmailNonSyncPasswordWarningDialogHistogram,
+                                    action);
+        }
+      }
+      break;
+    case WarningUIType::CHROME_SETTINGS:
+      DCHECK(is_primary_account_password);
+      UMA_HISTOGRAM_ENUMERATION(kSyncPasswordChromeSettingsHistogram, action);
+      break;
+    case WarningUIType::INTERSTITIAL:
+      if (is_primary_account_password) {
+        UMA_HISTOGRAM_ENUMERATION(kSyncPasswordInterstitialHistogram, action);
+        if (is_gsuite_user) {
+          UMA_HISTOGRAM_ENUMERATION(kGSuiteSyncPasswordInterstitialHistogram,
+                                    action);
+        }
+      } else if (password_account_type.account_type() ==
+                 ReusedPasswordAccountType::NON_GAIA_ENTERPRISE) {
+        UMA_HISTOGRAM_ENUMERATION(kEnterprisePasswordInterstitialHistogram,
+                                  action);
+      } else {
+        UMA_HISTOGRAM_ENUMERATION(kNonSyncPasswordInterstitialHistogram,
+                                  action);
+        if (is_gsuite_user) {
+          UMA_HISTOGRAM_ENUMERATION(kGSuiteNonSyncPasswordInterstitialHistogram,
+                                    action);
+        } else {
+          UMA_HISTOGRAM_ENUMERATION(kGmailNonSyncPasswordInterstitialHistogram,
+                                    action);
+        }
+      }
+      break;
+    case WarningUIType::NOT_USED:
+      NOTREACHED();
+      break;
+  }
+}
+
+void LogNumberOfReuseBeforeSyncPasswordChange(size_t reuse_count) {
+  UMA_HISTOGRAM_COUNTS_100(
+      "PasswordProtection.GaiaPasswordReusesBeforeGaiaPasswordChanged",
+      reuse_count);
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/content/password_protection/metrics_util.h b/components/safe_browsing/content/password_protection/metrics_util.h
new file mode 100644
index 0000000..8a0c75d
--- /dev/null
+++ b/components/safe_browsing/content/password_protection/metrics_util.h
@@ -0,0 +1,209 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CONTENT_PASSWORD_PROTECTION_METRICS_UTIL_H_
+#define COMPONENTS_SAFE_BROWSING_CONTENT_PASSWORD_PROTECTION_METRICS_UTIL_H_
+
+#include "base/macros.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace base {
+class TimeTicks;
+}
+
+namespace safe_browsing {
+
+// UMA metrics
+extern const char kAnyPasswordEntryRequestOutcomeHistogram[];
+extern const char kAnyPasswordEntryVerdictHistogram[];
+extern const char kEnterprisePasswordEntryRequestOutcomeHistogram[];
+extern const char kEnterprisePasswordEntryVerdictHistogram[];
+extern const char kEnterprisePasswordInterstitialHistogram[];
+extern const char kEnterprisePasswordPageInfoHistogram[];
+extern const char kEnterprisePasswordWarningDialogHistogram[];
+extern const char kGmailNonSyncPasswordInterstitialHistogram[];
+extern const char kGmailSyncPasswordPageInfoHistogram[];
+extern const char kGmailNonSyncPasswordPageInfoHistogram[];
+extern const char kGmailSyncPasswordWarningDialogHistogram[];
+extern const char kGmailNonSyncPasswordWarningDialogHistogram[];
+extern const char kNonSyncPasswordInterstitialHistogram[];
+extern const char kNonSyncPasswordPageInfoHistogram[];
+extern const char kGmailSyncPasswordEntryRequestOutcomeHistogram[];
+extern const char kGmailNonSyncPasswordEntryRequestOutcomeHistogram[];
+extern const char kGSuiteNonSyncPasswordEntryRequestOutcomeHistogram[];
+extern const char kGSuiteSyncPasswordEntryVerdictHistogram[];
+
+extern const char kGSuiteSyncPasswordEntryRequestOutcomeHistogram[];
+extern const char kGSuiteNonSyncPasswordEntryVerdictHistogram[];
+extern const char kGmailSyncPasswordEntryVerdictHistogram[];
+extern const char kGmailNonSyncPasswordEntryVerdictHistogram[];
+extern const char kGSuiteSyncPasswordInterstitialHistogram[];
+extern const char kGSuiteNonSyncPasswordInterstitialHistogram[];
+extern const char kGSuiteSyncPasswordPageInfoHistogram[];
+extern const char kGSuiteNonSyncPasswordPageInfoHistogram[];
+extern const char kGSuiteSyncPasswordWarningDialogHistogram[];
+extern const char kGSuiteNonSyncPasswordWarningDialogHistogram[];
+extern const char kPasswordOnFocusRequestOutcomeHistogram[];
+extern const char kPasswordOnFocusVerdictHistogram[];
+extern const char kNonSyncPasswordEntryRequestOutcomeHistogram[];
+extern const char kProtectedPasswordEntryRequestOutcomeHistogram[];
+extern const char kNonSyncPasswordEntryVerdictHistogram[];
+extern const char kSyncPasswordChromeSettingsHistogram[];
+extern const char kSyncPasswordEntryRequestOutcomeHistogram[];
+extern const char kSyncPasswordEntryVerdictHistogram[];
+extern const char kSyncPasswordInterstitialHistogram[];
+extern const char kSyncPasswordPageInfoHistogram[];
+extern const char kSyncPasswordWarningDialogHistogram[];
+extern const char kEnterprisePasswordAlertHistogram[];
+extern const char kGsuiteSyncPasswordAlertHistogram[];
+extern const char kGsuiteNonSyncPasswordAlertHistogram[];
+
+using ReusedPasswordAccountType =
+    LoginReputationClientRequest::PasswordReuseEvent::ReusedPasswordAccountType;
+using SyncAccountType =
+    LoginReputationClientRequest::PasswordReuseEvent::SyncAccountType;
+using VerdictType = LoginReputationClientResponse::VerdictType;
+
+// The outcome of the request. These values are used for UMA.
+// DO NOT CHANGE THE ORDERING OF THESE VALUES.
+enum class RequestOutcome {
+  // Request outcome unknown.
+  UNKNOWN = 0,
+  // Request successfully sent.
+  SUCCEEDED = 1,
+  // Request canceled.
+  CANCELED = 2,
+  // Request timeout.
+  TIMEDOUT = 3,
+  // No request sent because URL matches whitelist.
+  MATCHED_WHITELIST = 4,
+  // No request sent because response already cached.
+  RESPONSE_ALREADY_CACHED = 5,
+  DEPRECATED_NO_EXTENDED_REPORTING = 6,
+  // No request sent because user is in incognito mode.
+  DISABLED_DUE_TO_INCOGNITO = 7,
+  // No request sent because request is malformed.
+  REQUEST_MALFORMED = 8,
+  // Net error.
+  FETCH_FAILED = 9,
+  // Response received but malformed.
+  RESPONSE_MALFORMED = 10,
+  // No request sent since password protection service is no longer available.
+  SERVICE_DESTROYED = 11,
+  // No request sent because pinging feature is disable.
+  DISABLED_DUE_TO_FEATURE_DISABLED = 12,
+  // No request sent because the user is not extended reporting user.
+  DISABLED_DUE_TO_USER_POPULATION = 13,
+  // No request sent because the reputation of the URL is not computable.
+  URL_NOT_VALID_FOR_REPUTATION_COMPUTING = 14,
+  // No request sent because URL matches enterprise whitelist.
+  MATCHED_ENTERPRISE_WHITELIST = 15,
+  // No request sent because URL matches enterprise change password URL.
+  MATCHED_ENTERPRISE_CHANGE_PASSWORD_URL = 16,
+  // No request sent because URL matches enterprise login URL.
+  MATCHED_ENTERPRISE_LOGIN_URL = 17,
+  // No request sent if the admin configures password protection to
+  // warn on ALL password reuses (rather than just phishing sites).
+  PASSWORD_ALERT_MODE = 18,
+  // No request sent if the admin turns off password protection.
+  TURNED_OFF_BY_ADMIN = 19,
+  // No request sent because Safe Browsing is disabled.
+  SAFE_BROWSING_DISABLED = 20,
+  // No request sent because user is not signed-in.
+  USER_NOT_SIGNED_IN = 21,
+  kMaxValue = USER_NOT_SIGNED_IN,
+};
+
+// Enum values indicates if a password protection warning is shown or
+// represents user's action on warnings. These values are used for UMA.
+// DO NOT CHANGE THE ORDERING OF THESE VALUES.
+enum class WarningAction {
+  // Warning shows up.
+  SHOWN = 0,
+
+  // User clicks on "Change Password" button.
+  CHANGE_PASSWORD = 1,
+
+  // User clicks on "Ignore" button.
+  IGNORE_WARNING = 2,
+
+  // Dialog closed in reaction to change of user state.
+  CLOSE = 3,
+
+  // User explicitly mark the site as legitimate.
+  MARK_AS_LEGITIMATE = 4,
+
+  kMaxValue = MARK_AS_LEGITIMATE,
+};
+
+// Type of password protection warning UI.
+enum class WarningUIType {
+  NOT_USED = 0,
+  // Page in bubble.
+  PAGE_INFO = 1,
+  // Modal warning dialog.
+  MODAL_DIALOG = 2,
+  // chrome://settings page.
+  CHROME_SETTINGS = 3,
+  // chrome://reset-password interstitial.
+  INTERSTITIAL = 4
+};
+
+// Logs the |outcome| to several UMA metrics, depending on the value
+// of |password_type| and |sync_account_type|.
+void LogPasswordEntryRequestOutcome(
+    RequestOutcome outcome,
+    ReusedPasswordAccountType password_account_type);
+
+// Logs the |outcome| to several UMA metrics for password on focus pings.
+void LogPasswordOnFocusRequestOutcome(RequestOutcome outcome);
+
+// Logs the |outcome| to several UMA metrics for password alert mode.
+void LogPasswordAlertModeOutcome(
+    RequestOutcome outcome,
+    ReusedPasswordAccountType password_account_type);
+
+// Logs password protection verdict based on |trigger_type|, |password_type|,
+// and |sync_account_type|.
+void LogPasswordProtectionVerdict(
+    LoginReputationClientRequest::TriggerType trigger_type,
+    ReusedPasswordAccountType password_account_type,
+    VerdictType verdict_type);
+
+// Logs |reason| for why there's no ping sent out.
+void LogNoPingingReason(LoginReputationClientRequest::TriggerType trigger_type,
+                        RequestOutcome reason,
+                        ReusedPasswordAccountType password_account_type);
+
+// Logs the type of sync account.
+void LogSyncAccountType(SyncAccountType sync_account_type);
+
+// Logs the network response and duration of a password protection ping.
+void LogPasswordProtectionNetworkResponseAndDuration(
+    int response_code,
+    const base::TimeTicks& request_start_time);
+
+// Logs when a sample ping of allowlist URLs is sent to Safe Browsing.
+void LogPasswordProtectionSampleReportSent();
+
+// Records user action on warnings to corresponding UMA histograms.
+void LogWarningAction(WarningUIType ui_type,
+                      WarningAction action,
+                      ReusedPasswordAccountType password_account_type);
+
+// Logs the number of verdict migrated to the new caching structure.
+void LogNumberOfVerdictMigrated(size_t verdicts_migrated);
+
+// Logs how many time user reused their sync password before they change it.
+void LogNumberOfReuseBeforeSyncPasswordChange(size_t reuse_count);
+
+// Logs the size of referrer chain by |verdict_type|.
+void LogReferrerChainSize(
+    LoginReputationClientResponse::VerdictType verdict_type,
+    int referrer_chain_size);
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CONTENT_PASSWORD_PROTECTION_METRICS_UTIL_H_
diff --git a/components/safe_browsing/content/password_protection/mock_password_protection_service.cc b/components/safe_browsing/content/password_protection/mock_password_protection_service.cc
new file mode 100644
index 0000000..1d82b42a
--- /dev/null
+++ b/components/safe_browsing/content/password_protection/mock_password_protection_service.cc
@@ -0,0 +1,26 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/content/password_protection/mock_password_protection_service.h"
+
+#include "components/content_settings/core/browser/host_content_settings_map.h"
+#include "components/safe_browsing/core/db/database_manager.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+
+namespace safe_browsing {
+
+MockPasswordProtectionService::MockPasswordProtectionService()
+    : PasswordProtectionService(nullptr, nullptr, nullptr) {}
+
+MockPasswordProtectionService::MockPasswordProtectionService(
+    const scoped_refptr<SafeBrowsingDatabaseManager>& database_manager,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    history::HistoryService* history_service)
+    : PasswordProtectionService(database_manager,
+                                url_loader_factory,
+                                history_service) {}
+
+MockPasswordProtectionService::~MockPasswordProtectionService() {}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/content/password_protection/mock_password_protection_service.h b/components/safe_browsing/content/password_protection/mock_password_protection_service.h
new file mode 100644
index 0000000..74e3931
--- /dev/null
+++ b/components/safe_browsing/content/password_protection/mock_password_protection_service.h
@@ -0,0 +1,105 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CONTENT_PASSWORD_PROTECTION_MOCK_PASSWORD_PROTECTION_SERVICE_H_
+#define COMPONENTS_SAFE_BROWSING_CONTENT_PASSWORD_PROTECTION_MOCK_PASSWORD_PROTECTION_SERVICE_H_
+
+#include "base/macros.h"
+#include "components/safe_browsing/content/password_protection/password_protection_service.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace safe_browsing {
+
+class MockPasswordProtectionService : public PasswordProtectionService {
+ public:
+  MockPasswordProtectionService();
+  MockPasswordProtectionService(
+      const scoped_refptr<SafeBrowsingDatabaseManager>& database_manager,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      history::HistoryService* history_service);
+  ~MockPasswordProtectionService() override;
+
+  // safe_browsing::PasswordProtectionService
+  MOCK_CONST_METHOD0(GetSyncAccountType,
+                     safe_browsing::LoginReputationClientRequest::
+                         PasswordReuseEvent::SyncAccountType());
+  MOCK_CONST_METHOD0(GetBrowserPolicyConnector,
+                     const policy::BrowserPolicyConnector*());
+  MOCK_CONST_METHOD0(GetCurrentContentAreaSize, gfx::Size());
+  MOCK_CONST_METHOD0(GetAccountInfo, AccountInfo());
+  MOCK_CONST_METHOD0(IsPrimaryAccountSyncing, bool());
+  MOCK_CONST_METHOD0(IsPrimaryAccountSignedIn, bool());
+  MOCK_CONST_METHOD0(IsPrimaryAccountGmail, bool());
+  MOCK_CONST_METHOD1(GetPasswordProtectionWarningTriggerPref,
+                     PasswordProtectionTrigger(ReusedPasswordAccountType));
+  MOCK_CONST_METHOD1(GetSignedInNonSyncAccount,
+                     AccountInfo(const std::string&));
+  MOCK_CONST_METHOD1(IsOtherGaiaAccountGmail, bool(const std::string&));
+  MOCK_CONST_METHOD2(IsURLWhitelistedForPasswordEntry,
+                     bool(const GURL&, RequestOutcome*));
+
+  MOCK_METHOD0(CanSendSamplePing, bool());
+  MOCK_METHOD0(IsExtendedReporting, bool());
+  MOCK_METHOD0(IsIncognito, bool());
+  MOCK_METHOD0(IsHistorySyncEnabled, bool());
+  MOCK_METHOD0(IsUnderAdvancedProtection, bool());
+  MOCK_METHOD0(ReportPasswordChanged, void());
+  MOCK_METHOD1(UserClickedThroughSBInterstitial, bool(content::WebContents*));
+  MOCK_METHOD1(MaybeLogPasswordReuseDetectedEvent, void(content::WebContents*));
+  MOCK_METHOD1(SanitizeReferrerChain, void(ReferrerChain*));
+  MOCK_METHOD2(ShowInterstitial,
+               void(content::WebContents*, ReusedPasswordAccountType));
+  MOCK_METHOD2(PersistPhishedSavedPasswordCredential,
+               void(const std::string&, const std::vector<std::string>&));
+  MOCK_METHOD3(IsPingingEnabled,
+               bool(LoginReputationClientRequest::TriggerType,
+                    ReusedPasswordAccountType,
+                    RequestOutcome*));
+  MOCK_METHOD5(ShowModalWarning,
+               void(content::WebContents*,
+                    RequestOutcome,
+                    LoginReputationClientResponse::VerdictType,
+                    const std::string&,
+                    ReusedPasswordAccountType));
+  MOCK_METHOD4(
+      MaybeReportPasswordReuseDetected,
+      void(content::WebContents*, const std::string&, PasswordType, bool));
+  MOCK_METHOD3(UpdateSecurityState,
+               void(safe_browsing::SBThreatType,
+                    ReusedPasswordAccountType,
+                    content::WebContents*));
+  MOCK_METHOD2(RemoveUnhandledSyncPasswordReuseOnURLsDeleted,
+               void(bool, const history::URLRows&));
+  MOCK_METHOD3(FillReferrerChain,
+               void(const GURL&,
+                    SessionID,
+                    LoginReputationClientRequest::Frame*));
+  MOCK_METHOD4(MaybeLogPasswordReuseLookupEvent,
+               void(content::WebContents*,
+                    RequestOutcome,
+                    PasswordType,
+                    const safe_browsing::LoginReputationClientResponse*));
+  MOCK_METHOD3(CanShowInterstitial,
+               bool(RequestOutcome, ReusedPasswordAccountType, const GURL&));
+  MOCK_METHOD5(MaybeStartPasswordFieldOnFocusRequest,
+               void(content::WebContents*,
+                    const GURL&,
+                    const GURL&,
+                    const GURL&,
+                    const std::string&));
+  MOCK_METHOD6(MaybeStartProtectedPasswordEntryRequest,
+               void(content::WebContents*,
+                    const GURL&,
+                    const std::string&,
+                    PasswordType,
+                    const std::vector<std::string>&,
+                    bool));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockPasswordProtectionService);
+};
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CONTENT_PASSWORD_PROTECTION_MOCK_PASSWORD_PROTECTION_SERVICE_H_
diff --git a/components/safe_browsing/content/password_protection/password_protection_navigation_throttle.cc b/components/safe_browsing/content/password_protection/password_protection_navigation_throttle.cc
new file mode 100644
index 0000000..61b548a
--- /dev/null
+++ b/components/safe_browsing/content/password_protection/password_protection_navigation_throttle.cc
@@ -0,0 +1,57 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/content/password_protection/password_protection_navigation_throttle.h"
+
+#include "components/safe_browsing/content/password_protection/password_protection_request.h"
+#include "content/public/browser/navigation_handle.h"
+
+namespace safe_browsing {
+PasswordProtectionNavigationThrottle::PasswordProtectionNavigationThrottle(
+    content::NavigationHandle* navigation_handle,
+    scoped_refptr<PasswordProtectionRequest> request,
+    bool is_warning_showing)
+    : content::NavigationThrottle(navigation_handle),
+      request_(request),
+      is_warning_showing_(is_warning_showing) {
+  // Only call AddThrottle() if there is no modal warning showing. If there's a
+  // modal dialog, PPNavigationThrottle will simply cancel this navigation
+  // immediately, therefore no need to keep track of it.
+  if (!is_warning_showing_)
+    request_->AddThrottle(this);
+}
+
+PasswordProtectionNavigationThrottle::~PasswordProtectionNavigationThrottle() {
+  if (request_)
+    request_->RemoveThrottle(this);
+}
+
+content::NavigationThrottle::ThrottleCheckResult
+PasswordProtectionNavigationThrottle::WillStartRequest() {
+  if (is_warning_showing_)
+    return content::NavigationThrottle::CANCEL;
+  return content::NavigationThrottle::DEFER;
+}
+
+content::NavigationThrottle::ThrottleCheckResult
+PasswordProtectionNavigationThrottle::WillRedirectRequest() {
+  if (is_warning_showing_)
+    return content::NavigationThrottle::CANCEL;
+  return content::NavigationThrottle::DEFER;
+}
+
+const char* PasswordProtectionNavigationThrottle::GetNameForLogging() {
+  return "PasswordProtectionNavigationThrottle";
+}
+
+void PasswordProtectionNavigationThrottle::ResumeNavigation() {
+  Resume();
+}
+
+void PasswordProtectionNavigationThrottle::CancelNavigation(
+    content::NavigationThrottle::ThrottleCheckResult result) {
+  CancelDeferredNavigation(result);
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/content/password_protection/password_protection_navigation_throttle.h b/components/safe_browsing/content/password_protection/password_protection_navigation_throttle.h
new file mode 100644
index 0000000..98ae045d
--- /dev/null
+++ b/components/safe_browsing/content/password_protection/password_protection_navigation_throttle.h
@@ -0,0 +1,54 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CONTENT_PASSWORD_PROTECTION_PASSWORD_PROTECTION_NAVIGATION_THROTTLE_H_
+#define COMPONENTS_SAFE_BROWSING_CONTENT_PASSWORD_PROTECTION_PASSWORD_PROTECTION_NAVIGATION_THROTTLE_H_
+
+#include "base/memory/ref_counted.h"
+#include "content/public/browser/navigation_throttle.h"
+
+namespace content {
+class NavigationHandle;
+}  // namespace content
+
+namespace safe_browsing {
+class PasswordProtectionRequest;
+
+// PasswordProtectionNavigationThrottle defers or cancel navigation under the
+// following condition:
+// (1) if a navigation starts when there is a on-going sync password reuse ping,
+//     this throttle defers this navigation. When the verdict comes back, if the
+//     verdict results in showing a modal warning dialog, the deferred
+//     navigation will be canceled; otherwise, the deferred navigation will be
+//     resumed.
+// (2) if a navigation starts when there is a modal warning showing, this
+//     throttle simply cancels this navigation.
+class PasswordProtectionNavigationThrottle
+    : public content::NavigationThrottle {
+ public:
+  PasswordProtectionNavigationThrottle(
+      content::NavigationHandle* navigation_handle,
+      scoped_refptr<PasswordProtectionRequest> request,
+      bool is_warning_showing);
+  ~PasswordProtectionNavigationThrottle() override;
+
+  // content::NavigationThrottle:
+  content::NavigationThrottle::ThrottleCheckResult WillStartRequest() override;
+  content::NavigationThrottle::ThrottleCheckResult WillRedirectRequest()
+      override;
+  const char* GetNameForLogging() override;
+
+  void ResumeNavigation();
+  void CancelNavigation(
+      content::NavigationThrottle::ThrottleCheckResult result);
+
+ private:
+  scoped_refptr<PasswordProtectionRequest> request_;
+  bool is_warning_showing_;
+  DISALLOW_COPY_AND_ASSIGN(PasswordProtectionNavigationThrottle);
+};
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CONTENT_PASSWORD_PROTECTION_PASSWORD_PROTECTION_NAVIGATION_THROTTLE_H_
diff --git a/components/safe_browsing/content/password_protection/password_protection_request.cc b/components/safe_browsing/content/password_protection/password_protection_request.cc
new file mode 100644
index 0000000..1fe7bbb
--- /dev/null
+++ b/components/safe_browsing/content/password_protection/password_protection_request.cc
@@ -0,0 +1,599 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/content/password_protection/password_protection_request.h"
+
+#include <cstddef>
+#include <memory>
+
+#include "base/bind.h"
+#include "base/memory/weak_ptr.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/task/post_task.h"
+#include "base/time/time.h"
+#include "components/password_manager/core/browser/password_manager_metrics_util.h"
+#include "components/password_manager/core/browser/password_reuse_detector.h"
+#include "components/safe_browsing/content/password_protection/metrics_util.h"
+#include "components/safe_browsing/content/password_protection/password_protection_navigation_throttle.h"
+#include "components/safe_browsing/content/password_protection/visual_utils.h"
+#include "components/safe_browsing/content/web_ui/safe_browsing_ui.h"
+#include "components/safe_browsing/core/common/safe_browsing.mojom.h"
+#include "components/safe_browsing/core/db/allowlist_checker_client.h"
+#include "components/safe_browsing/core/features.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
+#include "components/zoom/zoom_controller.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/render_widget_host_view.h"
+#include "content/public/browser/web_contents.h"
+#include "net/base/escape.h"
+#include "net/base/load_flags.h"
+#include "net/base/url_util.h"
+#include "net/http/http_status_code.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "services/network/public/cpp/simple_url_loader.h"
+#include "services/service_manager/public/cpp/interface_provider.h"
+#include "url/origin.h"
+
+using content::BrowserThread;
+using content::WebContents;
+
+namespace safe_browsing {
+
+using ReusedPasswordAccountType =
+    LoginReputationClientRequest::PasswordReuseEvent::ReusedPasswordAccountType;
+
+namespace {
+
+// Cap on how many reused domains can be included in a report, to limit
+// the size of the report. UMA suggests 99.9% will have < 200 domains.
+const int kMaxReusedDomains = 200;
+
+#if BUILDFLAG(FULL_SAFE_BROWSING)
+// The maximum time to wait for DOM features to be collected, in milliseconds.
+const int kDomFeatureTimeoutMs = 3000;
+
+// Parameters chosen to ensure privacy is preserved by visual features.
+const int kMinWidthForVisualFeatures = 576;
+const int kMinHeightForVisualFeatures = 576;
+const float kMaxZoomForVisualFeatures = 2.0;
+
+std::unique_ptr<VisualFeatures> ExtractVisualFeatures(
+    const SkBitmap& screenshot) {
+  auto features = std::make_unique<VisualFeatures>();
+  visual_utils::GetHistogramForImage(screenshot,
+                                     features->mutable_color_histogram());
+  visual_utils::GetBlurredImage(screenshot, features->mutable_image());
+  return features;
+}
+#endif
+
+}  // namespace
+
+PasswordProtectionRequest::PasswordProtectionRequest(
+    WebContents* web_contents,
+    const GURL& main_frame_url,
+    const GURL& password_form_action,
+    const GURL& password_form_frame_url,
+    const std::string& username,
+    PasswordType password_type,
+    const std::vector<std::string>& matching_domains,
+    LoginReputationClientRequest::TriggerType type,
+    bool password_field_exists,
+    PasswordProtectionService* pps,
+    int request_timeout_in_ms)
+    : content::WebContentsObserver(web_contents),
+      web_contents_(web_contents),
+      main_frame_url_(main_frame_url),
+      password_form_action_(password_form_action),
+      password_form_frame_url_(password_form_frame_url),
+      username_(username),
+      password_type_(password_type),
+      matching_domains_(matching_domains),
+      trigger_type_(type),
+      password_field_exists_(password_field_exists),
+      password_protection_service_(pps),
+      request_timeout_in_ms_(request_timeout_in_ms),
+      request_proto_(std::make_unique<LoginReputationClientRequest>()),
+      is_modal_warning_showing_(false) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  DCHECK(trigger_type_ == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
+         trigger_type_ == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
+  DCHECK(trigger_type_ != LoginReputationClientRequest::PASSWORD_REUSE_EVENT ||
+         password_type_ != PasswordType::SAVED_PASSWORD ||
+         matching_domains_.size() > 0);
+
+  request_proto_->set_trigger_type(trigger_type_);
+}
+
+PasswordProtectionRequest::~PasswordProtectionRequest() {
+  weakptr_factory_.InvalidateWeakPtrs();
+}
+
+void PasswordProtectionRequest::Start() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  CheckWhitelist();
+}
+
+void PasswordProtectionRequest::CheckWhitelist() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  // In order to send pings for about:blank, we skip the whitelist check for
+  // URLs with unsupported schemes.
+  if (!password_protection_service_->database_manager()->CanCheckUrl(
+          main_frame_url_)) {
+    OnWhitelistCheckDone(false);
+    return;
+  }
+
+  // Start a task on the IO thread to check the whitelist. It may
+  // callback immediately on the IO thread or take some time if a full-hash-
+  // check is required.
+  auto result_callback =
+      base::BindOnce(&OnWhitelistCheckDoneOnIO, GetWeakPtr());
+  tracker_.PostTask(
+      base::CreateSingleThreadTaskRunner({BrowserThread::IO}).get(), FROM_HERE,
+      base::BindOnce(&AllowlistCheckerClient::StartCheckCsdWhitelist,
+                     password_protection_service_->database_manager(),
+                     main_frame_url_, std::move(result_callback)));
+}
+
+// static
+void PasswordProtectionRequest::OnWhitelistCheckDoneOnIO(
+    base::WeakPtr<PasswordProtectionRequest> weak_request,
+    bool match_whitelist) {
+  // Don't access weak_request on IO thread. Move it back to UI thread first.
+  base::PostTask(
+      FROM_HERE, {BrowserThread::UI},
+      base::BindOnce(&PasswordProtectionRequest::OnWhitelistCheckDone,
+                     weak_request, match_whitelist));
+}
+
+void PasswordProtectionRequest::OnWhitelistCheckDone(bool match_whitelist) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  if (match_whitelist) {
+    if (password_protection_service_->CanSendSamplePing()) {
+      FillRequestProto(/*is_sampled_ping=*/true);
+    }
+    Finish(RequestOutcome::MATCHED_WHITELIST, nullptr);
+  } else {
+    // In case the request to Safe Browsing takes too long,
+    // we set a timer to cancel that request and return an "unspecified verdict"
+    // so that the navigation isn't blocked indefinitely.
+    StartTimeout();
+    CheckCachedVerdicts();
+  }
+}
+
+void PasswordProtectionRequest::CheckCachedVerdicts() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  if (!password_protection_service_) {
+    Finish(RequestOutcome::SERVICE_DESTROYED, nullptr);
+    return;
+  }
+
+  std::unique_ptr<LoginReputationClientResponse> cached_response =
+      std::make_unique<LoginReputationClientResponse>();
+  ReusedPasswordAccountType password_account_type =
+      password_protection_service_
+          ->GetPasswordProtectionReusedPasswordAccountType(password_type_,
+                                                           username_);
+
+  auto verdict = password_protection_service_->GetCachedVerdict(
+      main_frame_url_, trigger_type_, password_account_type,
+      cached_response.get());
+
+  if (verdict != LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED) {
+    set_request_outcome(RequestOutcome::RESPONSE_ALREADY_CACHED);
+    Finish(RequestOutcome::RESPONSE_ALREADY_CACHED, std::move(cached_response));
+  } else {
+    FillRequestProto(/*is_sampled_ping=*/false);
+  }
+}
+
+void PasswordProtectionRequest::FillRequestProto(bool is_sampled_ping) {
+  request_proto_->set_page_url(main_frame_url_.spec());
+  LoginReputationClientRequest::Frame* main_frame =
+      request_proto_->add_frames();
+  main_frame->set_url(main_frame_url_.spec());
+  main_frame->set_frame_index(0 /* main frame */);
+  password_protection_service_->FillReferrerChain(
+      main_frame_url_, SessionID::InvalidValue(), main_frame);
+
+  // If a sample ping is send, only the URL and referrer chain is sent in the
+  // request.
+  if (is_sampled_ping) {
+    LogPasswordProtectionSampleReportSent();
+    request_proto_->set_report_type(
+        LoginReputationClientRequest::SAMPLE_REPORT);
+    request_proto_->clear_trigger_type();
+    if (main_frame->referrer_chain_size() > 0) {
+      password_protection_service_->SanitizeReferrerChain(
+          main_frame->mutable_referrer_chain());
+    }
+    SendRequest();
+    return;
+  } else {
+    request_proto_->set_report_type(LoginReputationClientRequest::FULL_REPORT);
+  }
+
+  password_protection_service_->FillUserPopulation(trigger_type_,
+                                                   request_proto_.get());
+  request_proto_->set_stored_verdict_cnt(
+      password_protection_service_->GetStoredVerdictCount(trigger_type_));
+
+  bool clicked_through_interstitial =
+      password_protection_service_->UserClickedThroughSBInterstitial(
+          web_contents_);
+  request_proto_->set_clicked_through_interstitial(
+      clicked_through_interstitial);
+  request_proto_->set_content_type(web_contents_->GetContentsMimeType());
+
+#if BUILDFLAG(FULL_SAFE_BROWSING)
+  if (password_protection_service_->IsExtendedReporting() &&
+      !password_protection_service_->IsIncognito()) {
+    gfx::Size content_area_size =
+        password_protection_service_->GetCurrentContentAreaSize();
+    request_proto_->set_content_area_height(content_area_size.height());
+    request_proto_->set_content_area_width(content_area_size.width());
+  }
+#endif
+
+  switch (trigger_type_) {
+    case LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE: {
+      LoginReputationClientRequest::Frame::Form* password_form;
+      if (password_form_frame_url_ == main_frame_url_) {
+        main_frame->set_has_password_field(true);
+        password_form = main_frame->add_forms();
+      } else {
+        LoginReputationClientRequest::Frame* password_frame =
+            request_proto_->add_frames();
+        password_frame->set_url(password_form_frame_url_.spec());
+        password_frame->set_has_password_field(true);
+        password_form = password_frame->add_forms();
+      }
+      password_form->set_action_url(password_form_action_.spec());
+      break;
+    }
+    case LoginReputationClientRequest::PASSWORD_REUSE_EVENT: {
+      main_frame->set_has_password_field(password_field_exists_);
+      LoginReputationClientRequest::PasswordReuseEvent* reuse_event =
+          request_proto_->mutable_password_reuse_event();
+      bool matches_signin_password =
+          password_type_ == PasswordType::PRIMARY_ACCOUNT_PASSWORD;
+      reuse_event->set_is_chrome_signin_password(matches_signin_password);
+      reuse_event->set_reused_password_type(
+          password_protection_service_->GetPasswordProtectionReusedPasswordType(
+              password_type_));
+      if (matches_signin_password) {
+        reuse_event->set_sync_account_type(
+            password_protection_service_->GetSyncAccountType());
+        LogSyncAccountType(reuse_event->sync_account_type());
+      }
+
+      if ((password_protection_service_->IsExtendedReporting()) &&
+          !password_protection_service_->IsIncognito()) {
+        for (const auto& domain : matching_domains_) {
+          reuse_event->add_domains_matching_password(domain);
+          if (reuse_event->domains_matching_password_size() >=
+              kMaxReusedDomains)
+            break;
+        }
+      }
+      if (base::FeatureList::IsEnabled(
+              safe_browsing::kPasswordProtectionForSignedInUsers)) {
+        ReusedPasswordAccountType password_account_type_to_add =
+            password_protection_service_
+                ->GetPasswordProtectionReusedPasswordAccountType(password_type_,
+                                                                 username_);
+        *reuse_event->mutable_reused_password_account_type() =
+            password_account_type_to_add;
+      }
+      break;
+    }
+    default:
+      NOTREACHED();
+  }
+
+#if BUILDFLAG(FULL_SAFE_BROWSING)
+  // Get the page DOM features.
+  content::RenderFrameHost* rfh = web_contents_->GetMainFrame();
+  password_protection_service_->GetPhishingDetector(rfh->GetRemoteInterfaces(),
+                                                    &phishing_detector_);
+  dom_features_collection_complete_ = false;
+  phishing_detector_->StartPhishingDetection(
+      main_frame_url_,
+      base::BindRepeating(&PasswordProtectionRequest::OnGetDomFeatures,
+                          GetWeakPtr()));
+  base::PostDelayedTask(
+      FROM_HERE, {BrowserThread::UI},
+      base::BindOnce(&PasswordProtectionRequest::OnGetDomFeatureTimeout,
+                     GetWeakPtr()),
+      base::TimeDelta::FromMilliseconds(kDomFeatureTimeoutMs));
+  dom_feature_start_time_ = base::TimeTicks::Now();
+#else
+  SendRequest();
+#endif
+}
+
+#if BUILDFLAG(FULL_SAFE_BROWSING)
+void PasswordProtectionRequest::OnGetDomFeatures(
+    mojom::PhishingDetectorResult result,
+    const std::string& verdict) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  if (dom_features_collection_complete_)
+    return;
+
+  UMA_HISTOGRAM_ENUMERATION("PasswordProtection.RendererDomFeatureResult",
+                            result);
+
+  if (result != mojom::PhishingDetectorResult::SUCCESS &&
+      result != mojom::PhishingDetectorResult::INVALID_SCORE)
+    return;
+
+  dom_features_collection_complete_ = true;
+  ClientPhishingRequest dom_features_request;
+  if (dom_features_request.ParseFromString(verdict)) {
+    for (const ClientPhishingRequest::Feature& feature :
+         dom_features_request.feature_map()) {
+      DomFeatures::Feature* new_feature =
+          request_proto_->mutable_dom_features()->add_feature_map();
+      new_feature->set_name(feature.name());
+      new_feature->set_value(feature.value());
+    }
+
+    for (const ClientPhishingRequest::Feature& feature :
+         dom_features_request.non_model_feature_map()) {
+      DomFeatures::Feature* new_feature =
+          request_proto_->mutable_dom_features()->add_feature_map();
+      new_feature->set_name(feature.name());
+      new_feature->set_value(feature.value());
+    }
+
+    request_proto_->mutable_dom_features()->mutable_shingle_hashes()->Swap(
+        dom_features_request.mutable_shingle_hashes());
+    request_proto_->mutable_dom_features()->set_model_version(
+        dom_features_request.model_version());
+  }
+
+  UMA_HISTOGRAM_TIMES("PasswordProtection.DomFeatureExtractionDuration",
+                      base::TimeTicks::Now() - dom_feature_start_time_);
+
+  MaybeCollectVisualFeatures();
+}
+
+void PasswordProtectionRequest::OnGetDomFeatureTimeout() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  if (!dom_features_collection_complete_) {
+    dom_features_collection_complete_ = true;
+    MaybeCollectVisualFeatures();
+  }
+}
+
+void PasswordProtectionRequest::MaybeCollectVisualFeatures() {
+  // Once the DOM features are collected, either collect visual features, or go
+  // straight to sending the ping.
+  if (trigger_type_ == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE &&
+      password_protection_service_->IsExtendedReporting() &&
+      zoom::ZoomController::GetZoomLevelForWebContents(web_contents_) <=
+          kMaxZoomForVisualFeatures &&
+      request_proto_->content_area_width() >= kMinWidthForVisualFeatures &&
+      request_proto_->content_area_height() >= kMinHeightForVisualFeatures) {
+    CollectVisualFeatures();
+  } else {
+    SendRequest();
+  }
+}
+
+void PasswordProtectionRequest::CollectVisualFeatures() {
+  content::RenderWidgetHostView* view =
+      web_contents_ ? web_contents_->GetRenderWidgetHostView() : nullptr;
+
+  if (!view) {
+    SendRequest();
+    return;
+  }
+
+  visual_feature_start_time_ = base::TimeTicks::Now();
+
+  view->CopyFromSurface(
+      gfx::Rect(), gfx::Size(),
+      base::BindOnce(&PasswordProtectionRequest::OnScreenshotTaken,
+                     GetWeakPtr()));
+}
+
+void PasswordProtectionRequest::OnScreenshotTaken(const SkBitmap& screenshot) {
+  // Do the feature extraction on a worker thread, to avoid blocking the UI.
+  base::PostTaskAndReplyWithResult(
+      FROM_HERE,
+      {base::ThreadPool(), base::MayBlock(), base::TaskPriority::BEST_EFFORT,
+       base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
+      base::BindOnce(&ExtractVisualFeatures, screenshot),
+      base::BindOnce(&PasswordProtectionRequest::OnVisualFeatureCollectionDone,
+                     GetWeakPtr()));
+}
+
+void PasswordProtectionRequest::OnVisualFeatureCollectionDone(
+    std::unique_ptr<VisualFeatures> visual_features) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  request_proto_->mutable_visual_features()->Swap(visual_features.get());
+
+  UMA_HISTOGRAM_TIMES("PasswordProtection.VisualFeatureExtractionDuration",
+                      base::TimeTicks::Now() - visual_feature_start_time_);
+
+  SendRequest();
+}
+#endif
+
+void PasswordProtectionRequest::SendRequest() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  web_ui_token_ =
+      WebUIInfoSingleton::GetInstance()->AddToPGPings(*request_proto_);
+
+  std::string serialized_request;
+  if (!request_proto_->SerializeToString(&serialized_request)) {
+    Finish(RequestOutcome::REQUEST_MALFORMED, nullptr);
+    return;
+  }
+
+  net::NetworkTrafficAnnotationTag traffic_annotation =
+      net::DefineNetworkTrafficAnnotation("password_protection_request", R"(
+        semantics {
+          sender: "Safe Browsing"
+          description:
+            "When the user is about to log in to a new, uncommon site, Chrome "
+            "will send a request to Safe Browsing to determine if the page is "
+            "phishing. It'll then show a warning if the page poses a risk of "
+            "phishing."
+          trigger:
+            "When a user focuses on a password field on a page that they "
+            "haven't visited before and that isn't popular or known to be safe."
+          data:
+            "URL and referrer of the current page, password form action, and "
+            "iframe structure."
+          destination: GOOGLE_OWNED_SERVICE
+        }
+        policy {
+          cookies_allowed: YES
+          cookies_store: "Safe Browsing Cookie Store"
+          setting:
+            "Users can control this feature via 'Protect you and your device "
+            "from dangerous sites'. By default, this setting is enabled."
+            "Alternatively, you can turn it off via "
+            "'PasswordProtectionWarningTrigger' enterprise policy setting."
+          chrome_policy {
+            PasswordProtectionWarningTrigger {
+              policy_options {mode: MANDATORY}
+              PasswordProtectionWarningTrigger: 2
+            }
+          }
+        })");
+  auto resource_request = std::make_unique<network::ResourceRequest>();
+  resource_request->url =
+      PasswordProtectionService::GetPasswordProtectionRequestUrl();
+  resource_request->method = "POST";
+  resource_request->load_flags = net::LOAD_DISABLE_CACHE;
+  url_loader_ = network::SimpleURLLoader::Create(std::move(resource_request),
+                                                 traffic_annotation);
+  url_loader_->AttachStringForUpload(serialized_request,
+                                     "application/octet-stream");
+  request_start_time_ = base::TimeTicks::Now();
+  url_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+      password_protection_service_->url_loader_factory().get(),
+      base::BindOnce(&PasswordProtectionRequest::OnURLLoaderComplete,
+                     base::Unretained(this)));
+}
+
+void PasswordProtectionRequest::StartTimeout() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  // If request is not done withing 10 seconds, we cancel this request.
+  // The weak pointer used for the timeout will be invalidated (and
+  // hence would prevent the timeout) if the check completes on time and
+  // execution reaches Finish().
+  base::PostDelayedTask(
+      FROM_HERE, {BrowserThread::UI},
+      base::BindOnce(&PasswordProtectionRequest::Cancel, GetWeakPtr(), true),
+      base::TimeDelta::FromMilliseconds(request_timeout_in_ms_));
+}
+
+void PasswordProtectionRequest::OnURLLoaderComplete(
+    std::unique_ptr<std::string> response_body) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  int response_code = 0;
+  if (url_loader_->ResponseInfo() && url_loader_->ResponseInfo()->headers)
+    response_code = url_loader_->ResponseInfo()->headers->response_code();
+
+  const bool is_success = url_loader_->NetError() == net::OK;
+
+  LogPasswordProtectionNetworkResponseAndDuration(
+      is_success ? response_code : url_loader_->NetError(),
+      request_start_time_);
+
+  if (!is_success || net::HTTP_OK != response_code) {
+    Finish(RequestOutcome::FETCH_FAILED, nullptr);
+    return;
+  }
+
+  std::unique_ptr<LoginReputationClientResponse> response =
+      std::make_unique<LoginReputationClientResponse>();
+  DCHECK(response_body);
+  url_loader_.reset();  // We don't need it anymore.
+  if (response_body && response->ParseFromString(*response_body)) {
+    WebUIInfoSingleton::GetInstance()->AddToPGResponses(web_ui_token_,
+                                                        *response);
+    set_request_outcome(RequestOutcome::SUCCEEDED);
+    Finish(RequestOutcome::SUCCEEDED, std::move(response));
+  } else {
+    Finish(RequestOutcome::RESPONSE_MALFORMED, nullptr);
+  }
+}
+
+void PasswordProtectionRequest::Finish(
+    RequestOutcome outcome,
+    std::unique_ptr<LoginReputationClientResponse> response) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  tracker_.TryCancelAll();
+
+  // If the request is canceled, the PasswordProtectionService is already
+  // partially destroyed, and we won't be able to log accurate metrics.
+  if (outcome != RequestOutcome::CANCELED) {
+    ReusedPasswordAccountType password_account_type =
+        password_protection_service_
+            ->GetPasswordProtectionReusedPasswordAccountType(password_type_,
+                                                             username_);
+    if (trigger_type_ == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) {
+      LogPasswordOnFocusRequestOutcome(outcome);
+    } else {
+#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
+      LogPasswordEntryRequestOutcome(outcome, password_account_type);
+#endif
+
+#if defined(SYNC_PASSWORD_REUSE_WARNING_ENABLED)
+      if (password_type_ == PasswordType::PRIMARY_ACCOUNT_PASSWORD) {
+        password_protection_service_->MaybeLogPasswordReuseLookupEvent(
+            web_contents_, outcome, password_type_, response.get());
+      }
+#endif
+    }
+
+    if (outcome == RequestOutcome::SUCCEEDED && response) {
+      LogPasswordProtectionVerdict(trigger_type_, password_account_type,
+                                   response->verdict_type());
+    }
+  }
+  password_protection_service_->RequestFinished(this, outcome,
+                                                std::move(response));
+}
+
+void PasswordProtectionRequest::Cancel(bool timed_out) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  url_loader_.reset();
+  // If request is canceled because |password_protection_service_| is shutting
+  // down, ignore all these deferred navigations.
+  if (!timed_out) {
+    throttles_.clear();
+  }
+
+  Finish(timed_out ? RequestOutcome::TIMEDOUT : RequestOutcome::CANCELED,
+         nullptr);
+}
+
+void PasswordProtectionRequest::HandleDeferredNavigations() {
+  for (auto* throttle : throttles_) {
+    if (is_modal_warning_showing_)
+      throttle->CancelNavigation(content::NavigationThrottle::CANCEL);
+    else
+      throttle->ResumeNavigation();
+  }
+  throttles_.clear();
+}
+
+void PasswordProtectionRequest::WebContentsDestroyed() {
+  Cancel(/*timed_out=*/false);
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/content/password_protection/password_protection_request.h b/components/safe_browsing/content/password_protection/password_protection_request.h
new file mode 100644
index 0000000..1510b72
--- /dev/null
+++ b/components/safe_browsing/content/password_protection/password_protection_request.h
@@ -0,0 +1,284 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CONTENT_PASSWORD_PROTECTION_PASSWORD_PROTECTION_REQUEST_H_
+#define COMPONENTS_SAFE_BROWSING_CONTENT_PASSWORD_PROTECTION_PASSWORD_PROTECTION_REQUEST_H_
+
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/task/cancelable_task_tracker.h"
+#include "base/time/time.h"
+#include "components/password_manager/core/browser/password_manager_metrics_util.h"
+#include "components/safe_browsing/buildflags.h"
+#include "components/safe_browsing/content/password_protection/metrics_util.h"
+#include "components/safe_browsing/content/password_protection/password_protection_service.h"
+#include "components/safe_browsing/core/common/safe_browsing.mojom.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+
+class GURL;
+
+namespace network {
+class SimpleURLLoader;
+}
+
+namespace safe_browsing {
+
+class PasswordProtectionNavigationThrottle;
+
+using password_manager::metrics_util::PasswordType;
+
+// A request for checking if an unfamiliar login form or a password reuse event
+// is safe. PasswordProtectionRequest objects are owned by
+// PasswordProtectionService indicated by |password_protection_service_|.
+// PasswordProtectionService is RefCountedThreadSafe such that it can post task
+// safely between IO and UI threads. It can only be destroyed on UI thread.
+//
+// PasswordProtectionRequest flow:
+// Step| Thread |                    Task
+// (1) |   UI   | If incognito or !SBER, quit request.
+// (2) |   UI   | Add task to IO thread for whitelist checking.
+// (3) |   IO   | Check whitelist and return the result back to UI thread.
+// (4) |   UI   | If whitelisted, check verdict cache; else quit request.
+// (5) |   UI   | If verdict cached, quit request; else prepare request proto.
+// (6) |   UI   | Collect features related to the DOM of the page.
+// (7) |   UI   | If appropriate, compute visual features of the page.
+// (7) |   UI   | Start a timeout task, and send network request.
+// (8) |   UI   | On receiving response, handle response and finish.
+//     |        | On request timeout, cancel request.
+//     |        | On deletion of |password_protection_service_|, cancel request.
+class PasswordProtectionRequest : public base::RefCountedThreadSafe<
+                                      PasswordProtectionRequest,
+                                      content::BrowserThread::DeleteOnUIThread>,
+                                  public content::WebContentsObserver {
+ public:
+  PasswordProtectionRequest(content::WebContents* web_contents,
+                            const GURL& main_frame_url,
+                            const GURL& password_form_action,
+                            const GURL& password_form_frame_url,
+                            const std::string& username,
+                            PasswordType password_type,
+                            const std::vector<std::string>& matching_origins,
+                            LoginReputationClientRequest::TriggerType type,
+                            bool password_field_exists,
+                            PasswordProtectionService* pps,
+                            int request_timeout_in_ms);
+
+  base::WeakPtr<PasswordProtectionRequest> GetWeakPtr() {
+    return weakptr_factory_.GetWeakPtr();
+  }
+
+  // Starts processing request by checking extended reporting and incognito
+  // conditions.
+  void Start();
+
+  // Cancels the current request. |timed_out| indicates if this cancellation is
+  // due to timeout. This function will call Finish() to destroy |this|.
+  void Cancel(bool timed_out);
+
+  // Processes the received response.
+  void OnURLLoaderComplete(std::unique_ptr<std::string> response_body);
+
+  GURL main_frame_url() const { return main_frame_url_; }
+
+  const LoginReputationClientRequest* request_proto() const {
+    return request_proto_.get();
+  }
+
+  content::WebContents* web_contents() const { return web_contents_; }
+
+  LoginReputationClientRequest::TriggerType trigger_type() const {
+    return trigger_type_;
+  }
+
+  const std::string username() const { return username_; }
+
+  PasswordType password_type() const { return password_type_; }
+
+  const std::vector<std::string> matching_domains() const& {
+    return matching_domains_;
+  }
+
+  bool is_modal_warning_showing() const { return is_modal_warning_showing_; }
+
+  void set_is_modal_warning_showing(bool is_warning_showing) {
+    is_modal_warning_showing_ = is_warning_showing;
+  }
+
+  RequestOutcome request_outcome() const { return request_outcome_; }
+
+  void set_request_outcome(RequestOutcome request_outcome) {
+    request_outcome_ = request_outcome;
+  }
+
+  // Keeps track of created navigation throttle.
+  void AddThrottle(PasswordProtectionNavigationThrottle* throttle) {
+    throttles_.insert(throttle);
+  }
+
+  void RemoveThrottle(PasswordProtectionNavigationThrottle* throttle) {
+    throttles_.erase(throttle);
+  }
+
+  // Cancels navigation if there is modal warning showing, resumes it otherwise.
+  void HandleDeferredNavigations();
+
+  // WebContentsObserver implementation
+  void WebContentsDestroyed() override;
+
+ protected:
+  friend class base::RefCountedThreadSafe<PasswordProtectionRequest>;
+
+ private:
+  friend struct content::BrowserThread::DeleteOnThread<
+      content::BrowserThread::UI>;
+  friend class base::DeleteHelper<PasswordProtectionRequest>;
+  friend class PasswordProtectionServiceTest;
+  friend class ChromePasswordProtectionServiceTest;
+  ~PasswordProtectionRequest() override;
+
+  // Start checking the whitelist.
+  void CheckWhitelist();
+
+  static void OnWhitelistCheckDoneOnIO(
+      base::WeakPtr<PasswordProtectionRequest> weak_request,
+      bool match_whitelist);
+
+  // If |main_frame_url_| matches whitelist, call Finish() immediately;
+  // otherwise call CheckCachedVerdicts().
+  void OnWhitelistCheckDone(bool match_whitelist);
+
+  // Looks up cached verdicts. If verdict is already cached, call SendRequest();
+  // otherwise call Finish().
+  void CheckCachedVerdicts();
+
+  // Fill |request_proto_| with appropriate values.
+  void FillRequestProto(bool is_sampled_ping);
+
+#if BUILDFLAG(FULL_SAFE_BROWSING)
+  // Collects visual features from the current login page.
+  void CollectVisualFeatures();
+
+  // Processes the screenshot of the login page into visual features.
+  void OnScreenshotTaken(const SkBitmap& bitmap);
+
+  // Called when the visual feature extraction is complete.
+  void OnVisualFeatureCollectionDone(
+      std::unique_ptr<VisualFeatures> visual_features);
+
+  // Called when the DOM feature extraction is complete.
+  void OnGetDomFeatures(mojom::PhishingDetectorResult result,
+                        const std::string& verdict);
+
+  // Called when the DOM feature extraction times out.
+  void OnGetDomFeatureTimeout();
+
+  // If appropriate, collects visual features, otherwise continues on to sending
+  // the request.
+  void MaybeCollectVisualFeatures();
+#endif
+
+  // Initiates network request to Safe Browsing backend.
+  void SendRequest();
+
+  // Start a timer to cancel the request if it takes too long.
+  void StartTimeout();
+
+  // |this| will be destroyed after calling this function.
+  void Finish(RequestOutcome outcome,
+              std::unique_ptr<LoginReputationClientResponse> response);
+
+  // WebContents of the password protection event.
+  content::WebContents* web_contents_;
+
+  // Main frame URL of the login form.
+  const GURL main_frame_url_;
+
+  // The action URL of the password form.
+  const GURL password_form_action_;
+
+  // Frame url of the detected password form.
+  const GURL password_form_frame_url_;
+
+  // The username of the reused password hash. The username can be an email or
+  // a username for a non-GAIA or saved-password reuse. No validation has been
+  // done on it.
+  const std::string username_;
+
+  // Type of the reused password.
+  const PasswordType password_type_;
+
+  // Domains from the Password Manager that match this password.
+  // Should be non-empty if |reused_password_type_| == SAVED_PASSWORD.
+  // Otherwise, may or may not be empty.
+  const std::vector<std::string> matching_domains_;
+
+  // If this request is for unfamiliar login page or for a password reuse event.
+  const LoginReputationClientRequest::TriggerType trigger_type_;
+
+  // If there is a password field on the page.
+  const bool password_field_exists_;
+
+  // When request is sent.
+  base::TimeTicks request_start_time_;
+
+  // SimpleURLLoader instance for sending request and receiving response.
+  std::unique_ptr<network::SimpleURLLoader> url_loader_;
+
+  // The PasswordProtectionService instance owns |this|.
+  // Can only be accessed on UI thread.
+  PasswordProtectionService* password_protection_service_;
+
+  // The outcome of the password protection request.
+  RequestOutcome request_outcome_;
+
+  // If we haven't receive response after this period of time, we cancel this
+  // request.
+  const int request_timeout_in_ms_;
+
+  std::unique_ptr<LoginReputationClientRequest> request_proto_;
+
+  // Needed for canceling tasks posted to different threads.
+  base::CancelableTaskTracker tracker_;
+
+  // Navigation throttles created for this |web_contents_| during |this|'s
+  // lifetime. These throttles are owned by their corresponding
+  // NavigationHandler instances.
+  std::set<PasswordProtectionNavigationThrottle*> throttles_;
+
+  // Whether there is a modal warning triggered by this request.
+  bool is_modal_warning_showing_;
+
+  // If a request is sent, this is the token returned by the WebUI.
+  int web_ui_token_;
+
+#if BUILDFLAG(FULL_SAFE_BROWSING)
+  // When we start extracting visual features.
+  base::TimeTicks visual_feature_start_time_;
+
+  // The Mojo pipe used for extracting DOM features from the renderer.
+  mojo::Remote<safe_browsing::mojom::PhishingDetector> phishing_detector_;
+
+  // When we start extracting DOM features. Used to compute the duration of DOM
+  // feature extraction, which is logged at
+  // PasswordProtection.DomFeatureExtractionDuration.
+  base::TimeTicks dom_feature_start_time_;
+
+  // Whether the DOM features collection is finished, either by timeout or by
+  // successfully gathering the features.
+  bool dom_features_collection_complete_;
+#endif
+
+  base::WeakPtrFactory<PasswordProtectionRequest> weakptr_factory_{this};
+  DISALLOW_COPY_AND_ASSIGN(PasswordProtectionRequest);
+};
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CONTENT_PASSWORD_PROTECTION_PASSWORD_PROTECTION_REQUEST_H_
diff --git a/components/safe_browsing/content/password_protection/password_protection_service.cc b/components/safe_browsing/content/password_protection/password_protection_service.cc
new file mode 100644
index 0000000..af0f044
--- /dev/null
+++ b/components/safe_browsing/content/password_protection/password_protection_service.cc
@@ -0,0 +1,598 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/content/password_protection/password_protection_service.h"
+
+#include <stddef.h>
+
+#include <memory>
+#include <string>
+
+#include "base/base64.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/task/post_task.h"
+#include "components/content_settings/core/browser/host_content_settings_map.h"
+#include "components/password_manager/core/browser/password_manager_metrics_util.h"
+#include "components/password_manager/core/browser/password_reuse_detector.h"
+#include "components/safe_browsing/content/password_protection/password_protection_navigation_throttle.h"
+#include "components/safe_browsing/content/password_protection/password_protection_request.h"
+#include "components/safe_browsing/core/common/utils.h"
+#include "components/safe_browsing/core/db/database_manager.h"
+#include "components/safe_browsing/core/features.h"
+#include "components/zoom/zoom_controller.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/web_contents.h"
+#include "google_apis/google_api_keys.h"
+#include "net/base/escape.h"
+#include "net/base/url_util.h"
+#include "third_party/blink/public/common/page/page_zoom.h"
+
+using content::BrowserThread;
+using content::WebContents;
+using history::HistoryService;
+using password_manager::metrics_util::PasswordType;
+
+namespace safe_browsing {
+
+using PasswordReuseEvent = LoginReputationClientRequest::PasswordReuseEvent;
+
+namespace {
+
+// Keys for storing password protection verdict into a DictionaryValue.
+const int kRequestTimeoutMs = 10000;
+const char kPasswordProtectionRequestUrl[] =
+    "https://sb-ssl.google.com/safebrowsing/clientreport/login";
+
+}  // namespace
+
+PasswordProtectionService::PasswordProtectionService(
+    const scoped_refptr<SafeBrowsingDatabaseManager>& database_manager,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    HistoryService* history_service)
+    : database_manager_(database_manager),
+      url_loader_factory_(url_loader_factory) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  if (history_service)
+    history_service_observer_.Add(history_service);
+
+  common_spoofed_domains_ = {
+      "login.live.com"
+      "facebook.com",
+      "box.com",
+      "paypal.com",
+      "apple.com",
+      "yahoo.com",
+      "adobe.com",
+      "amazon.com",
+      "linkedin.com",
+      "att.com"};
+}
+
+PasswordProtectionService::~PasswordProtectionService() {
+  tracker_.TryCancelAll();
+  CancelPendingRequests();
+  history_service_observer_.RemoveAll();
+  weak_factory_.InvalidateWeakPtrs();
+}
+
+bool PasswordProtectionService::CanGetReputationOfURL(const GURL& url) {
+  if (!url.is_valid() || !url.SchemeIsHTTPOrHTTPS() || net::IsLocalhost(url))
+    return false;
+
+  const std::string hostname = url.HostNoBrackets();
+  return !net::IsHostnameNonUnique(hostname) &&
+         hostname.find('.') != std::string::npos;
+}
+
+#if defined(ON_FOCUS_PING_ENABLED)
+void PasswordProtectionService::MaybeStartPasswordFieldOnFocusRequest(
+    WebContents* web_contents,
+    const GURL& main_frame_url,
+    const GURL& password_form_action,
+    const GURL& password_form_frame_url,
+    const std::string& hosted_domain) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  RequestOutcome reason;
+  if (!base::FeatureList::IsEnabled(safe_browsing::kSendOnFocusPing)) {
+    return;
+  }
+  if (CanSendPing(LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+                  main_frame_url,
+                  GetPasswordProtectionReusedPasswordAccountType(
+                      PasswordType::PASSWORD_TYPE_UNKNOWN,
+                      /*username=*/""),
+                  &reason)) {
+    StartRequest(web_contents, main_frame_url, password_form_action,
+                 password_form_frame_url, /* username */ "",
+                 PasswordType::PASSWORD_TYPE_UNKNOWN,
+                 {}, /* matching_domains: not used for this type */
+                 LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE, true);
+  }
+}
+#endif
+
+#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
+void PasswordProtectionService::MaybeStartProtectedPasswordEntryRequest(
+    WebContents* web_contents,
+    const GURL& main_frame_url,
+    const std::string& username,
+    PasswordType password_type,
+    const std::vector<std::string>& matching_domains,
+    bool password_field_exists) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  if (!base::FeatureList::IsEnabled(safe_browsing::kSendPasswordReusePing)) {
+    return;
+  }
+  ReusedPasswordAccountType reused_password_account_type =
+      GetPasswordProtectionReusedPasswordAccountType(password_type, username);
+  RequestOutcome reason;
+  // Need to populate |reason| to be passed into CanShowInterstitial.
+  bool can_send_ping =
+      CanSendPing(LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+                  main_frame_url, reused_password_account_type, &reason);
+  if (IsSupportedPasswordTypeForPinging(password_type)) {
+#if BUILDFLAG(FULL_SAFE_BROWSING)
+    // Collect metrics about typical page-zoom on login pages.
+    double zoom_level =
+        zoom::ZoomController::GetZoomLevelForWebContents(web_contents);
+    UMA_HISTOGRAM_COUNTS_1000(
+        "PasswordProtection.PageZoomFactor",
+        static_cast<int>(100 * blink::PageZoomLevelToZoomFactor(zoom_level)));
+#endif  // defined(FULL_SAFE_BROWSING)
+    if (can_send_ping) {
+      StartRequest(web_contents, main_frame_url, GURL(), GURL(), username,
+                   password_type, matching_domains,
+                   LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+                   password_field_exists);
+    } else {
+#if defined(SYNC_PASSWORD_REUSE_WARNING_ENABLED)
+      if (reused_password_account_type.is_account_syncing())
+        MaybeLogPasswordReuseLookupEvent(web_contents, reason, password_type,
+                                         nullptr);
+#endif  // defined(SYNC_PASSWORD_REUSE_WARNING_ENABLED)
+    }
+  }
+
+#if defined(SYNC_PASSWORD_REUSE_WARNING_ENABLED)
+  if (CanShowInterstitial(reason, reused_password_account_type,
+                          main_frame_url)) {
+    username_for_last_shown_warning_ = username;
+    reused_password_account_type_for_last_shown_warning_ =
+        reused_password_account_type;
+    ShowInterstitial(web_contents, reused_password_account_type);
+  }
+#endif  // defined(SYNC_PASSWORD_REUSE_WARNING_ENABLED)
+}
+#endif  // defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
+
+#if defined(SYNC_PASSWORD_REUSE_WARNING_ENABLED)
+bool PasswordProtectionService::ShouldShowModalWarning(
+    LoginReputationClientRequest::TriggerType trigger_type,
+    ReusedPasswordAccountType password_type,
+    LoginReputationClientResponse::VerdictType verdict_type) {
+  if (trigger_type != LoginReputationClientRequest::PASSWORD_REUSE_EVENT ||
+      !IsSupportedPasswordTypeForModalWarning(password_type)) {
+    return false;
+  }
+
+  return (verdict_type == LoginReputationClientResponse::PHISHING ||
+          verdict_type == LoginReputationClientResponse::LOW_REPUTATION) &&
+         IsWarningEnabled(password_type);
+}
+
+void PasswordProtectionService::RemoveWarningRequestsByWebContents(
+    content::WebContents* web_contents) {
+  for (auto it = warning_requests_.begin(); it != warning_requests_.end();) {
+    if (it->get()->web_contents() == web_contents)
+      it = warning_requests_.erase(it);
+    else
+      ++it;
+  }
+}
+
+bool PasswordProtectionService::IsModalWarningShowingInWebContents(
+    content::WebContents* web_contents) {
+  for (const auto& request : warning_requests_) {
+    if (request->web_contents() == web_contents)
+      return true;
+  }
+  return false;
+}
+#endif
+
+LoginReputationClientResponse::VerdictType
+PasswordProtectionService::GetCachedVerdict(
+    const GURL& url,
+    LoginReputationClientRequest::TriggerType trigger_type,
+    ReusedPasswordAccountType password_type,
+    LoginReputationClientResponse* out_response) {
+  return LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED;
+}
+
+void PasswordProtectionService::CacheVerdict(
+    const GURL& url,
+    LoginReputationClientRequest::TriggerType trigger_type,
+    ReusedPasswordAccountType password_type,
+    const LoginReputationClientResponse& verdict,
+    const base::Time& receive_time) {}
+
+void PasswordProtectionService::StartRequest(
+    WebContents* web_contents,
+    const GURL& main_frame_url,
+    const GURL& password_form_action,
+    const GURL& password_form_frame_url,
+    const std::string& username,
+    PasswordType password_type,
+    const std::vector<std::string>& matching_domains,
+    LoginReputationClientRequest::TriggerType trigger_type,
+    bool password_field_exists) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  scoped_refptr<PasswordProtectionRequest> request(
+      new PasswordProtectionRequest(
+          web_contents, main_frame_url, password_form_action,
+          password_form_frame_url, username, password_type, matching_domains,
+          trigger_type, password_field_exists, this, GetRequestTimeoutInMS()));
+  request->Start();
+  pending_requests_.insert(std::move(request));
+}
+
+bool PasswordProtectionService::CanSendPing(
+    LoginReputationClientRequest::TriggerType trigger_type,
+    const GURL& main_frame_url,
+    ReusedPasswordAccountType password_type,
+    RequestOutcome* reason) {
+  *reason = RequestOutcome::UNKNOWN;
+  bool is_pinging_enabled =
+      IsPingingEnabled(trigger_type, password_type, reason);
+  // Pinging is enabled for password_reuse trigger level; however we need to
+  // make sure *reason is set appropriately.
+  PasswordProtectionTrigger trigger_level =
+      GetPasswordProtectionWarningTriggerPref(password_type);
+  if (trigger_level == PASSWORD_REUSE) {
+    *reason = RequestOutcome::PASSWORD_ALERT_MODE;
+  }
+  if (is_pinging_enabled &&
+      !IsURLWhitelistedForPasswordEntry(main_frame_url, reason)) {
+    return true;
+  }
+  LogNoPingingReason(trigger_type, *reason, password_type);
+  return false;
+}
+
+void PasswordProtectionService::RequestFinished(
+    PasswordProtectionRequest* request,
+    RequestOutcome outcome,
+    std::unique_ptr<LoginReputationClientResponse> response) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK(request);
+
+  if (response) {
+    ReusedPasswordAccountType password_type =
+        GetPasswordProtectionReusedPasswordAccountType(request->password_type(),
+                                                       request->username());
+    if (outcome != RequestOutcome::RESPONSE_ALREADY_CACHED) {
+      CacheVerdict(request->main_frame_url(), request->trigger_type(),
+                   password_type, *response, base::Time::Now());
+    }
+    bool enable_warning_for_non_sync_users = base::FeatureList::IsEnabled(
+        safe_browsing::kPasswordProtectionForSignedInUsers);
+    if (!enable_warning_for_non_sync_users &&
+        request->password_type() == PasswordType::OTHER_GAIA_PASSWORD) {
+      return;
+    }
+
+    // If it's password alert mode and a Gsuite/enterprise account, we do not
+    // show a modal warning.
+    if (outcome == RequestOutcome::PASSWORD_ALERT_MODE &&
+        (password_type.account_type() == ReusedPasswordAccountType::GSUITE ||
+         password_type.account_type() ==
+             ReusedPasswordAccountType::NON_GAIA_ENTERPRISE)) {
+      return;
+    }
+
+#if defined(SYNC_PASSWORD_REUSE_WARNING_ENABLED)
+    if (ShouldShowModalWarning(request->trigger_type(), password_type,
+                               response->verdict_type())) {
+      username_for_last_shown_warning_ = request->username();
+      reused_password_account_type_for_last_shown_warning_ = password_type;
+      saved_passwords_matching_domains_ = request->matching_domains();
+      ShowModalWarning(request->web_contents(), request->request_outcome(),
+                       response->verdict_type(), response->verdict_token(),
+                       password_type);
+      request->set_is_modal_warning_showing(true);
+    }
+#endif
+  }
+
+  request->HandleDeferredNavigations();
+
+  // If the request is canceled, the PasswordProtectionService is already
+  // partially destroyed, and we won't be able to log accurate metrics.
+  if (outcome != RequestOutcome::CANCELED) {
+    auto verdict =
+        response ? response->verdict_type()
+                 : LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED;
+
+// Disabled on Android, because enterprise reporting extension is not supported.
+#if !defined(OS_ANDROID)
+    MaybeReportPasswordReuseDetected(
+        request->web_contents(), request->username(), request->password_type(),
+        verdict == LoginReputationClientResponse::PHISHING);
+#endif
+
+    // Persist a bit in CompromisedCredentials table when saved password is
+    // reused on a phishing or low reputation site.
+    auto is_unsafe_url =
+        verdict == LoginReputationClientResponse::PHISHING ||
+        verdict == LoginReputationClientResponse::LOW_REPUTATION;
+    if (is_unsafe_url) {
+      PersistPhishedSavedPasswordCredential(request->username(),
+                                            request->matching_domains());
+    }
+  }
+
+  // Remove request from |pending_requests_| list. If it triggers warning, add
+  // it into the !warning_reqeusts_| list.
+  for (auto it = pending_requests_.begin(); it != pending_requests_.end();
+       it++) {
+    if (it->get() == request) {
+      if (request->is_modal_warning_showing())
+        warning_requests_.insert(std::move(request));
+      pending_requests_.erase(it);
+      break;
+    }
+  }
+}
+
+void PasswordProtectionService::CancelPendingRequests() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  for (auto it = pending_requests_.begin(); it != pending_requests_.end();) {
+    PasswordProtectionRequest* request = it->get();
+    // These are the requests for whom we're still waiting for verdicts.
+    // We need to advance the iterator before we cancel because canceling
+    // the request will invalidate it when RequestFinished is called.
+    it++;
+    request->Cancel(false);
+  }
+  DCHECK(pending_requests_.empty());
+}
+
+int PasswordProtectionService::GetStoredVerdictCount(
+    LoginReputationClientRequest::TriggerType trigger_type) {
+  return -1;
+}
+
+scoped_refptr<SafeBrowsingDatabaseManager>
+PasswordProtectionService::database_manager() {
+  return database_manager_;
+}
+
+GURL PasswordProtectionService::GetPasswordProtectionRequestUrl() {
+  GURL url(kPasswordProtectionRequestUrl);
+  std::string api_key = google_apis::GetAPIKey();
+  DCHECK(!api_key.empty());
+  return url.Resolve("?key=" + net::EscapeQueryParamValue(api_key, true));
+}
+
+int PasswordProtectionService::GetRequestTimeoutInMS() {
+  return kRequestTimeoutMs;
+}
+
+void PasswordProtectionService::FillUserPopulation(
+    LoginReputationClientRequest::TriggerType trigger_type,
+    LoginReputationClientRequest* request_proto) {
+  ChromeUserPopulation* user_population = request_proto->mutable_population();
+  user_population->set_user_population(
+      IsExtendedReporting() ? ChromeUserPopulation::EXTENDED_REPORTING
+                            : ChromeUserPopulation::SAFE_BROWSING);
+  user_population->set_profile_management_status(
+      GetProfileManagementStatus(GetBrowserPolicyConnector()));
+  user_population->set_is_history_sync_enabled(IsHistorySyncEnabled());
+#if BUILDFLAG(FULL_SAFE_BROWSING)
+  user_population->set_is_under_advanced_protection(
+      IsUnderAdvancedProtection());
+#endif
+  user_population->set_is_incognito(IsIncognito());
+}
+
+void PasswordProtectionService::OnURLsDeleted(
+    history::HistoryService* history_service,
+    const history::DeletionInfo& deletion_info) {
+  base::PostTask(
+      FROM_HERE, {BrowserThread::UI},
+      base::BindRepeating(&PasswordProtectionService::
+                              RemoveUnhandledSyncPasswordReuseOnURLsDeleted,
+                          GetWeakPtr(), deletion_info.IsAllHistory(),
+                          deletion_info.deleted_rows()));
+}
+
+void PasswordProtectionService::HistoryServiceBeingDeleted(
+    history::HistoryService* history_service) {
+  history_service_observer_.RemoveAll();
+}
+
+std::unique_ptr<PasswordProtectionNavigationThrottle>
+PasswordProtectionService::MaybeCreateNavigationThrottle(
+    content::NavigationHandle* navigation_handle) {
+  if (!navigation_handle->IsRendererInitiated())
+    return nullptr;
+
+  content::WebContents* web_contents = navigation_handle->GetWebContents();
+  for (scoped_refptr<PasswordProtectionRequest> request : pending_requests_) {
+    if (request->web_contents() == web_contents &&
+        request->trigger_type() ==
+            safe_browsing::LoginReputationClientRequest::PASSWORD_REUSE_EVENT &&
+        IsSupportedPasswordTypeForModalWarning(
+            GetPasswordProtectionReusedPasswordAccountType(
+                request->password_type(), username_for_last_shown_warning()))) {
+      return std::make_unique<PasswordProtectionNavigationThrottle>(
+          navigation_handle, request, /*is_warning_showing=*/false);
+    }
+  }
+
+  for (scoped_refptr<PasswordProtectionRequest> request : warning_requests_) {
+    if (request->web_contents() == web_contents) {
+      return std::make_unique<PasswordProtectionNavigationThrottle>(
+          navigation_handle, request, /*is_warning_showing=*/true);
+    }
+  }
+  return nullptr;
+}
+
+bool PasswordProtectionService::IsWarningEnabled(
+    ReusedPasswordAccountType password_type) {
+  return GetPasswordProtectionWarningTriggerPref(password_type) ==
+         PHISHING_REUSE;
+}
+
+// static
+ReusedPasswordType
+PasswordProtectionService::GetPasswordProtectionReusedPasswordType(
+    password_manager::metrics_util::PasswordType password_type) {
+  switch (password_type) {
+    case PasswordType::SAVED_PASSWORD:
+      return PasswordReuseEvent::SAVED_PASSWORD;
+    case PasswordType::PRIMARY_ACCOUNT_PASSWORD:
+      return PasswordReuseEvent::SIGN_IN_PASSWORD;
+    case PasswordType::OTHER_GAIA_PASSWORD:
+      return PasswordReuseEvent::OTHER_GAIA_PASSWORD;
+    case PasswordType::ENTERPRISE_PASSWORD:
+      return PasswordReuseEvent::ENTERPRISE_PASSWORD;
+    case PasswordType::PASSWORD_TYPE_UNKNOWN:
+      return PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN;
+    case PasswordType::PASSWORD_TYPE_COUNT:
+      break;
+  }
+  NOTREACHED();
+  return PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN;
+}
+
+ReusedPasswordAccountType
+PasswordProtectionService::GetPasswordProtectionReusedPasswordAccountType(
+    password_manager::metrics_util::PasswordType password_type,
+    const std::string& username) const {
+  ReusedPasswordAccountType reused_password_account_type;
+  switch (password_type) {
+    case PasswordType::SAVED_PASSWORD:
+      reused_password_account_type.set_account_type(
+          ReusedPasswordAccountType::SAVED_PASSWORD);
+      return reused_password_account_type;
+    case PasswordType::ENTERPRISE_PASSWORD:
+      reused_password_account_type.set_account_type(
+          ReusedPasswordAccountType::NON_GAIA_ENTERPRISE);
+      return reused_password_account_type;
+    case PasswordType::PRIMARY_ACCOUNT_PASSWORD: {
+      reused_password_account_type.set_is_account_syncing(
+          IsPrimaryAccountSyncing());
+      if (!IsPrimaryAccountSignedIn()) {
+        reused_password_account_type.set_account_type(
+            ReusedPasswordAccountType::UNKNOWN);
+        return reused_password_account_type;
+      }
+      reused_password_account_type.set_account_type(
+          IsPrimaryAccountGmail() ? ReusedPasswordAccountType::GMAIL
+                                  : ReusedPasswordAccountType::GSUITE);
+      return reused_password_account_type;
+    }
+    case PasswordType::OTHER_GAIA_PASSWORD: {
+      AccountInfo account_info = GetSignedInNonSyncAccount(username);
+      if (account_info.account_id.empty()) {
+        reused_password_account_type.set_account_type(
+            ReusedPasswordAccountType::UNKNOWN);
+        return reused_password_account_type;
+      }
+      reused_password_account_type.set_account_type(
+          IsOtherGaiaAccountGmail(username)
+              ? ReusedPasswordAccountType::GMAIL
+              : ReusedPasswordAccountType::GSUITE);
+      return reused_password_account_type;
+    }
+    case PasswordType::PASSWORD_TYPE_UNKNOWN:
+    case PasswordType::PASSWORD_TYPE_COUNT:
+      reused_password_account_type.set_account_type(
+          ReusedPasswordAccountType::UNKNOWN);
+      return reused_password_account_type;
+  }
+  NOTREACHED();
+  return reused_password_account_type;
+}
+
+// static
+PasswordType
+PasswordProtectionService::ConvertReusedPasswordAccountTypeToPasswordType(
+    ReusedPasswordAccountType password_type) {
+  if (password_type.is_account_syncing()) {
+    return PasswordType::PRIMARY_ACCOUNT_PASSWORD;
+  } else if (password_type.account_type() ==
+             ReusedPasswordAccountType::NON_GAIA_ENTERPRISE) {
+    return PasswordType::ENTERPRISE_PASSWORD;
+  } else if (password_type.account_type() ==
+             ReusedPasswordAccountType::SAVED_PASSWORD) {
+    return PasswordType::SAVED_PASSWORD;
+  } else if (password_type.account_type() ==
+             ReusedPasswordAccountType::UNKNOWN) {
+    return PasswordType::PASSWORD_TYPE_UNKNOWN;
+  } else {
+    return PasswordType::OTHER_GAIA_PASSWORD;
+  }
+}
+
+bool PasswordProtectionService::IsSupportedPasswordTypeForPinging(
+    PasswordType password_type) const {
+  switch (password_type) {
+    case PasswordType::SAVED_PASSWORD:
+      return true;
+    case PasswordType::PRIMARY_ACCOUNT_PASSWORD:
+      return true;
+    case PasswordType::ENTERPRISE_PASSWORD:
+      return true;
+    case PasswordType::OTHER_GAIA_PASSWORD:
+      return base::FeatureList::IsEnabled(
+          safe_browsing::kPasswordProtectionForSignedInUsers);
+    case PasswordType::PASSWORD_TYPE_UNKNOWN:
+    case PasswordType::PASSWORD_TYPE_COUNT:
+      return false;
+  }
+  NOTREACHED();
+  return false;
+}
+
+bool PasswordProtectionService::IsSupportedPasswordTypeForModalWarning(
+    ReusedPasswordAccountType password_type) const {
+  if (password_type.account_type() ==
+      ReusedPasswordAccountType::NON_GAIA_ENTERPRISE)
+    return true;
+
+  if (password_type.account_type() ==
+          ReusedPasswordAccountType::SAVED_PASSWORD &&
+      base::FeatureList::IsEnabled(
+          safe_browsing::kPasswordProtectionForSavedPasswords))
+    return true;
+
+  if (password_type.account_type() != ReusedPasswordAccountType::GMAIL &&
+      password_type.account_type() != ReusedPasswordAccountType::GSUITE)
+    return false;
+
+  return password_type.is_account_syncing() ||
+         base::FeatureList::IsEnabled(
+             safe_browsing::kPasswordProtectionForSignedInUsers);
+}
+
+#if BUILDFLAG(FULL_SAFE_BROWSING)
+void PasswordProtectionService::GetPhishingDetector(
+    service_manager::InterfaceProvider* provider,
+    mojo::Remote<mojom::PhishingDetector>* phishing_detector) {
+  provider->GetInterface(phishing_detector->BindNewPipeAndPassReceiver());
+}
+#endif
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/content/password_protection/password_protection_service.h b/components/safe_browsing/content/password_protection/password_protection_service.h
new file mode 100644
index 0000000..0dce4bb
--- /dev/null
+++ b/components/safe_browsing/content/password_protection/password_protection_service.h
@@ -0,0 +1,496 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CONTENT_PASSWORD_PROTECTION_PASSWORD_PROTECTION_SERVICE_H_
+#define COMPONENTS_SAFE_BROWSING_CONTENT_PASSWORD_PROTECTION_PASSWORD_PROTECTION_SERVICE_H_
+
+#include <set>
+#include <unordered_map>
+
+#include "base/callback.h"
+#include "base/feature_list.h"
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/scoped_observer.h"
+#include "base/task/cancelable_task_tracker.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "components/history/core/browser/history_service.h"
+#include "components/history/core/browser/history_service_observer.h"
+#include "components/password_manager/core/browser/password_manager_metrics_util.h"
+#include "components/safe_browsing/buildflags.h"
+#include "components/safe_browsing/content/password_protection/metrics_util.h"
+#include "components/safe_browsing/core/browser/referrer_chain_provider.h"
+#include "components/safe_browsing/core/common/safe_browsing.mojom.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
+#include "components/sessions/core/session_id.h"
+#include "components/signin/public/identity_manager/account_info.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/service_manager/public/cpp/interface_provider.h"
+#include "third_party/protobuf/src/google/protobuf/repeated_field.h"
+
+namespace content {
+class WebContents;
+class NavigationHandle;
+}
+
+namespace policy {
+class BrowserPolicyConnector;
+}
+
+class GURL;
+class HostContentSettingsMap;
+
+namespace safe_browsing {
+
+class PasswordProtectionNavigationThrottle;
+class PasswordProtectionRequest;
+class SafeBrowsingDatabaseManager;
+
+using ReusedPasswordAccountType =
+    LoginReputationClientRequest::PasswordReuseEvent::ReusedPasswordAccountType;
+using ReusedPasswordType =
+    LoginReputationClientRequest::PasswordReuseEvent::ReusedPasswordType;
+using password_manager::metrics_util::PasswordType;
+
+// Manage password protection pings and verdicts. There is one instance of this
+// class per profile. Therefore, every PasswordProtectionService instance is
+// associated with a unique HistoryService instance and a unique
+// HostContentSettingsMap instance.
+class PasswordProtectionService : public history::HistoryServiceObserver {
+ public:
+  PasswordProtectionService(
+      const scoped_refptr<SafeBrowsingDatabaseManager>& database_manager,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      history::HistoryService* history_service);
+
+  ~PasswordProtectionService() override;
+
+  base::WeakPtr<PasswordProtectionService> GetWeakPtr() {
+    return weak_factory_.GetWeakPtr();
+  }
+
+  // Looks up |settings| to find the cached verdict response. If verdict is not
+  // available or is expired, return VERDICT_TYPE_UNSPECIFIED. Can be called on
+  // any thread.
+  virtual LoginReputationClientResponse::VerdictType GetCachedVerdict(
+      const GURL& url,
+      LoginReputationClientRequest::TriggerType trigger_type,
+      ReusedPasswordAccountType password_type,
+      LoginReputationClientResponse* out_response);
+
+  // Stores |verdict| in |settings| based on its |trigger_type|, |url|,
+  // reused |password_type|, |verdict| and |receive_time|.
+  virtual void CacheVerdict(
+      const GURL& url,
+      LoginReputationClientRequest::TriggerType trigger_type,
+      ReusedPasswordAccountType password_type,
+      const LoginReputationClientResponse& verdict,
+      const base::Time& receive_time);
+
+  // Creates an instance of PasswordProtectionRequest and call Start() on that
+  // instance. This function also insert this request object in |requests_| for
+  // record keeping.
+  void StartRequest(content::WebContents* web_contents,
+                    const GURL& main_frame_url,
+                    const GURL& password_form_action,
+                    const GURL& password_form_frame_url,
+                    const std::string& username,
+                    PasswordType password_type,
+                    const std::vector<std::string>& matching_domains,
+                    LoginReputationClientRequest::TriggerType trigger_type,
+                    bool password_field_exists);
+
+#if defined(ON_FOCUS_PING_ENABLED)
+  virtual void MaybeStartPasswordFieldOnFocusRequest(
+      content::WebContents* web_contents,
+      const GURL& main_frame_url,
+      const GURL& password_form_action,
+      const GURL& password_form_frame_url,
+      const std::string& hosted_domain);
+#endif
+
+#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
+  virtual void MaybeStartProtectedPasswordEntryRequest(
+      content::WebContents* web_contents,
+      const GURL& main_frame_url,
+      const std::string& username,
+      PasswordType password_type,
+      const std::vector<std::string>& matching_domains,
+      bool password_field_exists);
+#endif
+
+#if defined(SYNC_PASSWORD_REUSE_WARNING_ENABLED)
+  // Records a Chrome Sync event that sync password reuse was detected.
+  virtual void MaybeLogPasswordReuseDetectedEvent(
+      content::WebContents* web_contents) = 0;
+
+  // If we want to show password reuse modal warning.
+  bool ShouldShowModalWarning(
+      LoginReputationClientRequest::TriggerType trigger_type,
+      ReusedPasswordAccountType password_type,
+      LoginReputationClientResponse::VerdictType verdict_type);
+
+  // Shows modal warning dialog on the current |web_contents| and pass the
+  // |verdict_token| to callback of this dialog.
+  virtual void ShowModalWarning(
+      content::WebContents* web_contents,
+      RequestOutcome outcome,
+      LoginReputationClientResponse::VerdictType verdict_type,
+      const std::string& verdict_token,
+      ReusedPasswordAccountType password_type) = 0;
+
+  // Shows chrome://reset-password interstitial.
+  virtual void ShowInterstitial(content::WebContents* web_contens,
+                                ReusedPasswordAccountType password_type) = 0;
+#endif
+
+// The following functions are disabled on Android, because enterprise reporting
+// extension is not supported.
+#if !defined(OS_ANDROID)
+  // Triggers the safeBrowsingPrivate.OnPolicySpecifiedPasswordReuseDetected.
+  virtual void MaybeReportPasswordReuseDetected(
+      content::WebContents* web_contents,
+      const std::string& username,
+      PasswordType password_type,
+      bool is_phishing_url) = 0;
+
+  // Called when a protected password change is detected. Must be called on
+  // UI thread.
+  virtual void ReportPasswordChanged() = 0;
+#endif
+
+#if defined(SYNC_PASSWORD_REUSE_WARNING_ENABLED)
+  virtual void UpdateSecurityState(safe_browsing::SBThreatType threat_type,
+                                   ReusedPasswordAccountType password_type,
+                                   content::WebContents* web_contents) = 0;
+#endif
+
+  scoped_refptr<SafeBrowsingDatabaseManager> database_manager();
+
+  // Safe Browsing backend cannot get a reliable reputation of a URL if
+  // (1) URL is not valid
+  // (2) URL doesn't have http or https scheme
+  // (3) It maps to a local host.
+  // (4) Its hostname is an IP Address in an IANA-reserved range.
+  // (5) Its hostname is a not-yet-assigned by ICANN gTLD.
+  // (6) Its hostname is a dotless domain.
+  static bool CanGetReputationOfURL(const GURL& url);
+
+  // If user has clicked through any Safe Browsing interstitial on this given
+  // |web_contents|.
+  virtual bool UserClickedThroughSBInterstitial(
+      content::WebContents* web_contents) = 0;
+
+  // Called when a new navigation is starting. Create throttle if there is a
+  // pending sync password reuse ping or if there is a modal warning dialog
+  // showing in the corresponding web contents.
+  std::unique_ptr<PasswordProtectionNavigationThrottle>
+  MaybeCreateNavigationThrottle(content::NavigationHandle* navigation_handle);
+
+  // Returns if the warning UI is enabled.
+  bool IsWarningEnabled(ReusedPasswordAccountType password_type);
+
+  // Returns the pref value of password protection warning trigger.
+  virtual PasswordProtectionTrigger GetPasswordProtectionWarningTriggerPref(
+      ReusedPasswordAccountType password_type) const = 0;
+
+  // If |url| matches Safe Browsing whitelist domains, password protection
+  // change password URL, or password protection login URLs in the enterprise
+  // policy.
+  virtual bool IsURLWhitelistedForPasswordEntry(
+      const GURL& url,
+      RequestOutcome* reason) const = 0;
+
+  // Persist the phished saved password credential in the "compromised
+  // credentials" table. Calls the password store to add a row for each domain
+  // where the phished saved password is used on.
+  virtual void PersistPhishedSavedPasswordCredential(
+      const std::string& username,
+      const std::vector<std::string>& matching_domains) = 0;
+
+  // Converts from password::metrics_util::PasswordType to
+  // LoginReputationClientRequest::PasswordReuseEvent::ReusedPasswordType.
+  static ReusedPasswordType GetPasswordProtectionReusedPasswordType(
+      PasswordType password_type);
+
+  // Converts from password_manager::metrics_util::PasswordType
+  // to PasswordReuseEvent::ReusedPasswordAccountType. |username| is only
+  // used if |password_type| is OTHER_GAIA_PASSWORD because it needs to be
+  // compared to the list of signed in accounts.
+  ReusedPasswordAccountType GetPasswordProtectionReusedPasswordAccountType(
+      PasswordType password_type,
+      const std::string& username) const;
+
+  // Converts from ReusedPasswordAccountType to
+  // password_manager::metrics_util::PasswordType.
+  static PasswordType ConvertReusedPasswordAccountTypeToPasswordType(
+      ReusedPasswordAccountType password_type);
+
+  // If we can send ping for this type of reused password.
+  bool IsSupportedPasswordTypeForPinging(PasswordType password_type) const;
+
+  // If we can show modal warning for this type of reused password.
+  bool IsSupportedPasswordTypeForModalWarning(
+      ReusedPasswordAccountType password_type) const;
+
+  const ReusedPasswordAccountType&
+  reused_password_account_type_for_last_shown_warning() const {
+    return reused_password_account_type_for_last_shown_warning_;
+  }
+#if defined(UNIT_TEST)
+  void set_reused_password_account_type_for_last_shown_warning(
+      ReusedPasswordAccountType
+          reused_password_account_type_for_last_shown_warning) {
+    reused_password_account_type_for_last_shown_warning_ =
+        reused_password_account_type_for_last_shown_warning;
+  }
+#endif
+
+  const std::string& username_for_last_shown_warning() const {
+    return username_for_last_shown_warning_;
+  }
+#if defined(UNIT_TEST)
+  void set_username_for_last_shown_warning(const std::string& username) {
+    username_for_last_shown_warning_ = username;
+  }
+#endif
+
+  const std::vector<std::string>& saved_passwords_matching_domains() const {
+    return saved_passwords_matching_domains_;
+  }
+#if defined(UNIT_TEST)
+  void set_saved_passwords_matching_domains(
+      const std::vector<std::string>& matching_domains) {
+    saved_passwords_matching_domains_ = matching_domains;
+  }
+#endif
+
+  virtual AccountInfo GetAccountInfo() const = 0;
+
+ protected:
+  friend class PasswordProtectionRequest;
+
+  // Chrome can send password protection ping if it is allowed by for the
+  // |trigger_type| and if Safe Browsing can compute reputation of
+  // |main_frame_url| (e.g. Safe Browsing is not able to compute reputation of a
+  // private IP or a local host). Update |reason| if sending ping is not
+  // allowed. |password_type| is used for UMA metric recording.
+  bool CanSendPing(LoginReputationClientRequest::TriggerType trigger_type,
+                   const GURL& main_frame_url,
+                   ReusedPasswordAccountType password_type,
+                   RequestOutcome* reason);
+
+  // Called by a PasswordProtectionRequest instance when it finishes to remove
+  // itself from |requests_|.
+  virtual void RequestFinished(
+      PasswordProtectionRequest* request,
+      RequestOutcome outcome,
+      std::unique_ptr<LoginReputationClientResponse> response);
+
+  // Called by a PasswordProtectionRequest instance to check if a sample ping
+  // can be sent to Safe Browsing.
+  virtual bool CanSendSamplePing() = 0;
+
+  // Sanitize referrer chain by only keeping origin information of all URLs.
+  virtual void SanitizeReferrerChain(ReferrerChain* referrer_chain) = 0;
+
+  // Cancels all requests in |requests_|, empties it, and releases references to
+  // the requests.
+  void CancelPendingRequests();
+
+  // Gets the total number of verdicts of the specified |trigger_type| we cached
+  // for this profile. This counts both expired and active verdicts.
+  virtual int GetStoredVerdictCount(
+      LoginReputationClientRequest::TriggerType trigger_type);
+
+  // Gets an unowned |BrowserPolicyConnector| for the current platform.
+  virtual const policy::BrowserPolicyConnector* GetBrowserPolicyConnector()
+      const = 0;
+
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory() {
+    return url_loader_factory_;
+  }
+
+  // Returns the URL where PasswordProtectionRequest instances send requests.
+  static GURL GetPasswordProtectionRequestUrl();
+
+  // Gets the request timeout in milliseconds.
+  static int GetRequestTimeoutInMS();
+
+  // Obtains referrer chain of |event_url| and |event_tab_id| and adds this
+  // info into |frame|.
+  virtual void FillReferrerChain(
+      const GURL& event_url,
+      SessionID
+          event_tab_id,  // SessionID::InvalidValue() if tab not available.
+      LoginReputationClientRequest::Frame* frame) = 0;
+
+  void FillUserPopulation(
+      LoginReputationClientRequest::TriggerType trigger_type,
+      LoginReputationClientRequest* request_proto);
+
+  virtual bool IsExtendedReporting() = 0;
+
+  virtual bool IsIncognito() = 0;
+
+  virtual bool IsPingingEnabled(
+      LoginReputationClientRequest::TriggerType trigger_type,
+      ReusedPasswordAccountType password_type,
+      RequestOutcome* reason) = 0;
+
+  virtual bool IsHistorySyncEnabled() = 0;
+
+  // If primary account is syncing.
+  virtual bool IsPrimaryAccountSyncing() const = 0;
+
+  // If primary account is signed in.
+  virtual bool IsPrimaryAccountSignedIn() const = 0;
+
+  // If a domain is not defined for the primary account. This means the primary
+  // account is a Gmail account.
+  virtual bool IsPrimaryAccountGmail() const = 0;
+
+  // If the domain for the non sync account is equal to |kNoHostedDomainFound|,
+  // this means that the account is a Gmail account.
+  virtual bool IsOtherGaiaAccountGmail(const std::string& username) const = 0;
+
+  // Gets the account based off of the username from a list of signed in
+  // accounts.
+  virtual AccountInfo GetSignedInNonSyncAccount(
+      const std::string& username) const = 0;
+
+#if BUILDFLAG(FULL_SAFE_BROWSING)
+  virtual bool IsUnderAdvancedProtection() = 0;
+#endif
+
+#if defined(SYNC_PASSWORD_REUSE_WARNING_ENABLED)
+  // Records a Chrome Sync event for the result of the URL reputation lookup
+  // if the user enters their sync password on a website.
+  virtual void MaybeLogPasswordReuseLookupEvent(
+      content::WebContents* web_contents,
+      RequestOutcome,
+      PasswordType password_type,
+      const LoginReputationClientResponse*) = 0;
+
+  void RemoveWarningRequestsByWebContents(content::WebContents* web_contents);
+
+  bool IsModalWarningShowingInWebContents(content::WebContents* web_contents);
+
+  // Determines if we should show chrome://reset-password interstitial based on
+  // previous request outcome, the reused |password_type| and the
+  // |main_frame_url|.
+  virtual bool CanShowInterstitial(RequestOutcome reason,
+                                   ReusedPasswordAccountType password_type,
+                                   const GURL& main_frame_url) = 0;
+#endif
+
+  void CheckCsdWhitelistOnIOThread(const GURL& url, bool* check_result);
+
+  // Gets the type of sync account associated with current profile or
+  // |NOT_SIGNED_IN|.
+  virtual LoginReputationClientRequest::PasswordReuseEvent::SyncAccountType
+  GetSyncAccountType() const = 0;
+
+  const std::list<std::string>& common_spoofed_domains() const {
+    return common_spoofed_domains_;
+  }
+
+ private:
+  friend class PasswordProtectionServiceTest;
+  friend class TestPasswordProtectionService;
+  friend class ChromePasswordProtectionServiceTest;
+  friend class ChromePasswordProtectionServiceBrowserTest;
+  FRIEND_TEST_ALL_PREFIXES(PasswordProtectionServiceTest,
+                           TestParseInvalidVerdictEntry);
+  FRIEND_TEST_ALL_PREFIXES(PasswordProtectionServiceTest,
+                           TestParseValidVerdictEntry);
+  FRIEND_TEST_ALL_PREFIXES(PasswordProtectionServiceTest,
+                           TestPathVariantsMatchCacheExpression);
+  FRIEND_TEST_ALL_PREFIXES(PasswordProtectionServiceTest,
+                           TestRemoveCachedVerdictOnURLsDeleted);
+  FRIEND_TEST_ALL_PREFIXES(PasswordProtectionServiceTest,
+                           TestCleanUpExpiredVerdict);
+
+  // Overridden from history::HistoryServiceObserver.
+  void OnURLsDeleted(history::HistoryService* history_service,
+                     const history::DeletionInfo& deletion_info) override;
+
+  void HistoryServiceBeingDeleted(
+      history::HistoryService* history_service) override;
+
+  // Posted to UI thread by OnURLsDeleted(...). This function remove the related
+  // entries in kSafeBrowsingUnhandledSyncPasswordReuses.
+  virtual void RemoveUnhandledSyncPasswordReuseOnURLsDeleted(
+      bool all_history,
+      const history::URLRows& deleted_rows) = 0;
+
+  static bool PathVariantsMatchCacheExpression(
+      const std::vector<std::string>& generated_paths,
+      const std::string& cache_expression_path);
+
+  void RecordNoPingingReason(
+      LoginReputationClientRequest::TriggerType trigger_type,
+      RequestOutcome reason,
+      PasswordType password_type);
+
+#if BUILDFLAG(FULL_SAFE_BROWSING)
+  // Get the content area size of current browsing window.
+  virtual gfx::Size GetCurrentContentAreaSize() const = 0;
+
+  // Binds the |phishing_detector| to the appropriate interface, as provided by
+  // |provider|.
+  virtual void GetPhishingDetector(
+      service_manager::InterfaceProvider* provider,
+      mojo::Remote<mojom::PhishingDetector>* phishing_detector);
+#endif
+
+  // The username of the account which password has been reused on. It is only
+  // set once a modal warning or interstitial is verified to be shown.
+  std::string username_for_last_shown_warning_ = "";
+
+  // The last ReusedPasswordAccountType that was shown a warning or
+  // interstitial.
+  ReusedPasswordAccountType
+      reused_password_account_type_for_last_shown_warning_;
+
+  std::vector<std::string> saved_passwords_matching_domains_;
+
+  scoped_refptr<SafeBrowsingDatabaseManager> database_manager_;
+
+  // The context we use to issue network requests. This request_context_getter
+  // is obtained from SafeBrowsingService so that we can use the Safe Browsing
+  // cookie store.
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
+
+  // Set of pending PasswordProtectionRequests that are still waiting for
+  // verdict.
+  std::set<scoped_refptr<PasswordProtectionRequest>> pending_requests_;
+
+  // Set of PasswordProtectionRequests that are triggering modal warnings.
+  std::set<scoped_refptr<PasswordProtectionRequest>> warning_requests_;
+
+  // List of most commonly spoofed domains to default to on the password warning
+  // dialog.
+  std::list<std::string> common_spoofed_domains_;
+
+  ScopedObserver<history::HistoryService, history::HistoryServiceObserver>
+      history_service_observer_{this};
+
+  // Weakptr can only cancel task if it is posted to the same thread. Therefore,
+  // we need CancelableTaskTracker to cancel tasks posted to IO thread.
+  base::CancelableTaskTracker tracker_;
+
+  base::WeakPtrFactory<PasswordProtectionService> weak_factory_{this};
+  DISALLOW_COPY_AND_ASSIGN(PasswordProtectionService);
+};
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CONTENT_PASSWORD_PROTECTION_PASSWORD_PROTECTION_SERVICE_H_
diff --git a/components/safe_browsing/content/password_protection/password_protection_service_unittest.cc b/components/safe_browsing/content/password_protection/password_protection_service_unittest.cc
new file mode 100644
index 0000000..6003107
--- /dev/null
+++ b/components/safe_browsing/content/password_protection/password_protection_service_unittest.cc
@@ -0,0 +1,1446 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#include "components/safe_browsing/content/password_protection/password_protection_service.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/null_task_runner.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "build/build_config.h"
+#include "components/content_settings/core/browser/host_content_settings_map.h"
+#include "components/password_manager/core/browser/password_manager_metrics_util.h"
+#include "components/password_manager/core/browser/password_reuse_detector.h"
+#include "components/safe_browsing/content/password_protection/metrics_util.h"
+#include "components/safe_browsing/content/password_protection/mock_password_protection_service.h"
+#include "components/safe_browsing/content/password_protection/password_protection_navigation_throttle.h"
+#include "components/safe_browsing/content/password_protection/password_protection_request.h"
+#include "components/safe_browsing/core/common/safe_browsing.mojom-forward.h"
+#include "components/safe_browsing/core/common/safe_browsing.mojom.h"
+#include "components/safe_browsing/core/db/test_database_manager.h"
+#include "components/safe_browsing/core/features.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
+#include "components/safe_browsing/core/verdict_cache_manager.h"
+#include "components/signin/public/identity_manager/account_info.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_task_environment.h"
+#include "content/public/test/test_browser_context.h"
+#include "content/public/test/test_renderer_host.h"
+#include "content/public/test/web_contents_tester.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/system/message_pipe.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "services/service_manager/public/cpp/interface_provider.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::AnyNumber;
+using testing::ElementsAre;
+using testing::IsEmpty;
+using testing::Return;
+
+namespace {
+
+const char kFormActionUrl[] = "https://form_action.com/";
+const char kPasswordFrameUrl[] = "https://password_frame.com/";
+const char kSavedDomain[] = "saved_domain.com";
+const char kSavedDomain2[] = "saved_domain2.com";
+const char kTargetUrl[] = "http://foo.com/";
+const char kUserName[] = "username";
+
+const unsigned int kMinute = 60;
+const unsigned int kDay = 24 * 60 * kMinute;
+
+}  // namespace
+
+namespace safe_browsing {
+
+using PasswordReuseEvent = LoginReputationClientRequest::PasswordReuseEvent;
+
+class MockSafeBrowsingDatabaseManager : public TestSafeBrowsingDatabaseManager {
+ public:
+  MockSafeBrowsingDatabaseManager() {}
+
+  MOCK_METHOD2(CheckCsdWhitelistUrl,
+               AsyncMatch(const GURL&, SafeBrowsingDatabaseManager::Client*));
+
+ protected:
+  ~MockSafeBrowsingDatabaseManager() override {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockSafeBrowsingDatabaseManager);
+};
+
+class TestPhishingDetector : public mojom::PhishingDetector {
+ public:
+  TestPhishingDetector() : should_timeout_(false) {}
+  ~TestPhishingDetector() override {}
+
+  void Bind(mojo::ScopedMessagePipeHandle handle) {
+    receiver_.Bind(
+        mojo::PendingReceiver<mojom::PhishingDetector>(std::move(handle)));
+  }
+
+  void StartPhishingDetection(
+      const GURL& url,
+      StartPhishingDetectionCallback callback) override {
+    if (should_timeout_) {
+      deferred_callbacks_.push_back(std::move(callback));
+    } else {
+      ReturnFeatures(url, std::move(callback));
+    }
+  }
+  void ReturnFeatures(const GURL& url,
+                      StartPhishingDetectionCallback callback) {
+    ClientPhishingRequest verdict;
+    verdict.set_is_phishing(false);
+    verdict.set_client_score(0.1);
+    std::move(callback).Run(mojom::PhishingDetectorResult::SUCCESS,
+                            verdict.SerializeAsString());
+  }
+
+  void set_should_timeout(bool timeout) { should_timeout_ = timeout; }
+
+ private:
+  bool should_timeout_;
+  std::vector<StartPhishingDetectionCallback> deferred_callbacks_;
+  mojo::Receiver<mojom::PhishingDetector> receiver_{this};
+
+  DISALLOW_COPY_AND_ASSIGN(TestPhishingDetector);
+};
+
+class TestPasswordProtectionService : public MockPasswordProtectionService {
+ public:
+  TestPasswordProtectionService(
+      const scoped_refptr<SafeBrowsingDatabaseManager>& database_manager,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      scoped_refptr<HostContentSettingsMap> content_setting_map)
+      : MockPasswordProtectionService(database_manager,
+                                      url_loader_factory,
+                                      nullptr),
+        cache_manager_(
+            std::make_unique<VerdictCacheManager>(nullptr,
+                                                  content_setting_map.get())) {}
+
+  void RequestFinished(
+      PasswordProtectionRequest* request,
+      RequestOutcome outcome,
+      std::unique_ptr<LoginReputationClientResponse> response) override {
+    latest_request_ = request;
+    latest_response_ = std::move(response);
+    run_loop_.Quit();
+  }
+
+  LoginReputationClientResponse* latest_response() {
+    return latest_response_.get();
+  }
+
+  void WaitForResponse() { run_loop_.Run(); }
+
+  ~TestPasswordProtectionService() override {}
+
+  size_t GetPendingRequestsCount() { return pending_requests_.size(); }
+
+  const LoginReputationClientRequest* GetLatestRequestProto() {
+    return latest_request_ ? latest_request_->request_proto() : nullptr;
+  }
+
+  void GetPhishingDetector(
+      service_manager::InterfaceProvider* provider,
+      mojo::Remote<mojom::PhishingDetector>* phishing_detector) override {
+    service_manager::InterfaceProvider::TestApi test_api(provider);
+    test_api.SetBinderForName(
+        mojom::PhishingDetector::Name_,
+        base::BindRepeating(&TestPhishingDetector::Bind,
+                            base::Unretained(&test_phishing_detector_)));
+    provider->GetInterface(phishing_detector->BindNewPipeAndPassReceiver());
+    test_api.ClearBinderForName(mojom::PhishingDetector::Name_);
+  }
+
+  void CacheVerdict(const GURL& url,
+                    LoginReputationClientRequest::TriggerType trigger_type,
+                    ReusedPasswordAccountType password_type,
+                    const LoginReputationClientResponse& verdict,
+                    const base::Time& receive_time) override {
+    if (!CanGetReputationOfURL(url) || IsIncognito())
+      return;
+
+    cache_manager_->CachePhishGuardVerdict(url, trigger_type, password_type,
+                                           verdict, receive_time);
+  }
+
+  LoginReputationClientResponse::VerdictType GetCachedVerdict(
+      const GURL& url,
+      LoginReputationClientRequest::TriggerType trigger_type,
+      ReusedPasswordAccountType password_type,
+      LoginReputationClientResponse* out_response) override {
+    if (!url.is_valid() || !CanGetReputationOfURL(url))
+      return LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED;
+
+    return cache_manager_->GetCachedPhishGuardVerdict(
+        url, trigger_type, password_type, out_response);
+  }
+
+  int GetStoredVerdictCount(
+      LoginReputationClientRequest::TriggerType trigger_type) override {
+    return cache_manager_->GetStoredPhishGuardVerdictCount(trigger_type);
+  }
+
+  void SetDomFeatureCollectionTimeout(bool should_timeout) {
+    test_phishing_detector_.set_should_timeout(should_timeout);
+  }
+
+ private:
+  PasswordProtectionRequest* latest_request_;
+  base::RunLoop run_loop_;
+  std::unique_ptr<LoginReputationClientResponse> latest_response_;
+  TestPhishingDetector test_phishing_detector_;
+
+  // The TestPasswordProtectionService manages its own cache, rather than using
+  // the global one.
+  std::unique_ptr<VerdictCacheManager> cache_manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestPasswordProtectionService);
+};
+
+class MockPasswordProtectionNavigationThrottle
+    : public PasswordProtectionNavigationThrottle {
+ public:
+  MockPasswordProtectionNavigationThrottle(
+      content::NavigationHandle* navigation_handle,
+      scoped_refptr<PasswordProtectionRequest> request,
+      bool is_warning_showing)
+      : PasswordProtectionNavigationThrottle(navigation_handle,
+                                             request,
+                                             is_warning_showing) {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockPasswordProtectionNavigationThrottle);
+};
+
+class PasswordProtectionServiceTest : public ::testing::TestWithParam<bool> {
+ public:
+  PasswordProtectionServiceTest()
+      : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
+
+  LoginReputationClientResponse CreateVerdictProto(
+      LoginReputationClientResponse::VerdictType verdict,
+      int cache_duration_sec,
+      const std::string& cache_expression) {
+    LoginReputationClientResponse verdict_proto;
+    verdict_proto.set_verdict_type(verdict);
+    verdict_proto.set_cache_duration_sec(cache_duration_sec);
+    verdict_proto.set_cache_expression(cache_expression);
+    return verdict_proto;
+  }
+
+  void SetUp() override {
+    HostContentSettingsMap::RegisterProfilePrefs(test_pref_service_.registry());
+    content_setting_map_ = new HostContentSettingsMap(
+        &test_pref_service_, false /* is_off_the_record */,
+        false /* store_last_modified */,
+        false /* migrate_requesting_and_top_level_origin_settings */);
+    database_manager_ = new MockSafeBrowsingDatabaseManager();
+    password_protection_service_ =
+        std::make_unique<TestPasswordProtectionService>(
+            database_manager_,
+            base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+                &test_url_loader_factory_),
+            content_setting_map_);
+    EXPECT_CALL(*password_protection_service_, IsExtendedReporting())
+        .WillRepeatedly(Return(GetParam()));
+    EXPECT_CALL(*password_protection_service_, IsIncognito())
+        .WillRepeatedly(Return(false));
+    EXPECT_CALL(*password_protection_service_,
+                IsURLWhitelistedForPasswordEntry(_, _))
+        .WillRepeatedly(Return(false));
+    EXPECT_CALL(*password_protection_service_,
+                GetPasswordProtectionWarningTriggerPref(_))
+        .WillRepeatedly(Return(PASSWORD_PROTECTION_OFF));
+    url_ = PasswordProtectionService::GetPasswordProtectionRequestUrl();
+  }
+
+  void TearDown() override {
+    password_protection_service_.reset();
+    content_setting_map_->ShutdownOnUIThread();
+  }
+
+  // Sets up |database_manager_| and |pending_requests_| as needed.
+  void InitializeAndStartPasswordOnFocusRequest(
+      bool match_whitelist,
+      int timeout_in_ms,
+      content::WebContents* web_contents) {
+    GURL target_url(kTargetUrl);
+    EXPECT_CALL(*database_manager_, CheckCsdWhitelistUrl(target_url, _))
+        .WillRepeatedly(
+            Return(match_whitelist ? AsyncMatch::MATCH : AsyncMatch::NO_MATCH));
+
+    request_ = new PasswordProtectionRequest(
+        web_contents, target_url, GURL(kFormActionUrl), GURL(kPasswordFrameUrl),
+        kUserName, PasswordType::PASSWORD_TYPE_UNKNOWN, {},
+        LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE, true,
+        password_protection_service_.get(), timeout_in_ms);
+    request_->Start();
+  }
+
+  void InitializeAndStartPasswordEntryRequest(
+      PasswordType type,
+      const std::vector<std::string>& matching_domains,
+      bool match_whitelist,
+      int timeout_in_ms,
+      content::WebContents* web_contents) {
+    GURL target_url(kTargetUrl);
+    EXPECT_CALL(*database_manager_, CheckCsdWhitelistUrl(target_url, _))
+        .WillRepeatedly(
+            Return(match_whitelist ? AsyncMatch::MATCH : AsyncMatch::NO_MATCH));
+
+    request_ = new PasswordProtectionRequest(
+        web_contents, target_url, GURL(), GURL(), kUserName, type,
+        matching_domains, LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+        true, password_protection_service_.get(), timeout_in_ms);
+    request_->Start();
+  }
+
+  void CacheVerdict(const GURL& url,
+                    LoginReputationClientRequest::TriggerType trigger,
+                    ReusedPasswordAccountType password_type,
+                    LoginReputationClientResponse::VerdictType verdict,
+                    int cache_duration_sec,
+                    const std::string& cache_expression,
+                    const base::Time& verdict_received_time) {
+    ASSERT_FALSE(cache_expression.empty());
+    LoginReputationClientResponse response(
+        CreateVerdictProto(verdict, cache_duration_sec, cache_expression));
+    password_protection_service_->CacheVerdict(url, trigger, password_type,
+                                               response, verdict_received_time);
+  }
+
+  void CacheInvalidVerdict(ReusedPasswordAccountType password_type) {
+    GURL invalid_hostname("http://invalid.com");
+    std::unique_ptr<base::DictionaryValue> verdict_dictionary =
+        base::DictionaryValue::From(content_setting_map_->GetWebsiteSetting(
+            invalid_hostname, GURL(), ContentSettingsType::PASSWORD_PROTECTION,
+            std::string(), nullptr));
+
+    if (!verdict_dictionary)
+      verdict_dictionary = std::make_unique<base::DictionaryValue>();
+
+    std::unique_ptr<base::DictionaryValue> invalid_verdict_entry =
+        std::make_unique<base::DictionaryValue>();
+    invalid_verdict_entry->SetString("invalid", "invalid_string");
+
+    std::unique_ptr<base::DictionaryValue> invalid_cache_expression_entry =
+        std::make_unique<base::DictionaryValue>();
+    invalid_cache_expression_entry->SetWithoutPathExpansion(
+        "invalid_cache_expression", std::move(invalid_verdict_entry));
+    verdict_dictionary->SetWithoutPathExpansion(
+        base::NumberToString(static_cast<std::underlying_type_t<PasswordType>>(
+            password_protection_service_
+                ->ConvertReusedPasswordAccountTypeToPasswordType(
+                    password_type))),
+        std::move(invalid_cache_expression_entry));
+    content_setting_map_->SetWebsiteSettingDefaultScope(
+        invalid_hostname, GURL(), ContentSettingsType::PASSWORD_PROTECTION,
+        std::string(), std::move(verdict_dictionary));
+  }
+
+  size_t GetStoredVerdictCount(LoginReputationClientRequest::TriggerType type) {
+    return password_protection_service_->GetStoredVerdictCount(type);
+  }
+
+  std::unique_ptr<content::WebContents> GetWebContents() {
+    return base::WrapUnique(content::WebContentsTester::CreateTestWebContents(
+        content::WebContents::CreateParams(&browser_context_)));
+  }
+
+  void VerifyContentAreaSizeCollection(
+      const LoginReputationClientRequest& request) {
+    bool should_report_content_size =
+        password_protection_service_->IsExtendedReporting() &&
+        !password_protection_service_->IsIncognito();
+    EXPECT_EQ(should_report_content_size, request.has_content_area_height());
+    EXPECT_EQ(should_report_content_size, request.has_content_area_width());
+  }
+
+  size_t GetNumberOfNavigationThrottles() {
+    return request_ ? request_->throttles_.size() : 0u;
+  }
+
+ protected:
+  // |task_environment_| is needed here because this test involves both UI and
+  // IO threads.
+  content::BrowserTaskEnvironment task_environment_;
+  scoped_refptr<MockSafeBrowsingDatabaseManager> database_manager_;
+  sync_preferences::TestingPrefServiceSyncable test_pref_service_;
+  scoped_refptr<HostContentSettingsMap> content_setting_map_;
+  GURL url_;
+  network::TestURLLoaderFactory test_url_loader_factory_;
+  std::unique_ptr<TestPasswordProtectionService> password_protection_service_;
+  scoped_refptr<PasswordProtectionRequest> request_;
+  base::HistogramTester histograms_;
+  content::TestBrowserContext browser_context_;
+  content::RenderViewHostTestEnabler rvh_test_enabler_;
+};
+
+TEST_P(PasswordProtectionServiceTest, TestCachePasswordReuseVerdicts) {
+  ASSERT_EQ(0U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+  EXPECT_CALL(*password_protection_service_, IsPrimaryAccountSignedIn())
+      .WillRepeatedly(Return(true));
+  // Assume each verdict has a TTL of 10 minutes.
+  // Cache a verdict for http://www.test.com/foo/index.html
+  ReusedPasswordAccountType reused_password_account_type;
+  reused_password_account_type.set_account_type(
+      ReusedPasswordAccountType::GSUITE);
+  reused_password_account_type.set_is_account_syncing(true);
+  CacheVerdict(GURL("http://www.test.com/foo/index.html"),
+               LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+               reused_password_account_type,
+               LoginReputationClientResponse::SAFE, 10 * kMinute,
+               "test.com/foo/", base::Time::Now());
+
+  EXPECT_EQ(1U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+
+  // Cache another verdict with the some origin and cache_expression should
+  // override the cache.
+  CacheVerdict(GURL("http://www.test.com/foo/index2.html"),
+               LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+               reused_password_account_type,
+               LoginReputationClientResponse::PHISHING, 10 * kMinute,
+               "test.com/foo/", base::Time::Now());
+  EXPECT_EQ(1U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+  LoginReputationClientResponse out_verdict;
+  EXPECT_EQ(LoginReputationClientResponse::PHISHING,
+            password_protection_service_->GetCachedVerdict(
+                GURL("http://www.test.com/foo/index2.html"),
+                LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+                reused_password_account_type, &out_verdict));
+
+  // Cache a password reuse verdict with a different password type but same
+  // origin and cache expression should add a new entry.
+  reused_password_account_type.set_account_type(
+      ReusedPasswordAccountType::NON_GAIA_ENTERPRISE);
+  CacheVerdict(GURL("http://www.test.com/foo/index2.html"),
+               LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+               reused_password_account_type,
+               LoginReputationClientResponse::PHISHING, 10 * kMinute,
+               "test.com/foo/", base::Time::Now());
+  EXPECT_EQ(2U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+  EXPECT_EQ(LoginReputationClientResponse::PHISHING,
+            password_protection_service_->GetCachedVerdict(
+                GURL("http://www.test.com/foo/index2.html"),
+                LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+                reused_password_account_type, &out_verdict));
+
+  // Cache another verdict with the same origin but different cache_expression
+  // will not increase setting count, but will increase the number of verdicts
+  // in the given origin.
+  CacheVerdict(GURL("http://www.test.com/bar/index2.html"),
+               LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+               reused_password_account_type,
+               LoginReputationClientResponse::SAFE, 10 * kMinute,
+               "test.com/bar/", base::Time::Now());
+  EXPECT_EQ(3U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+
+  // Now cache a UNFAMILIAR_LOGIN_PAGE verdict, stored verdict count for
+  // PASSWORD_REUSE_EVENT should be the same.
+  CacheVerdict(GURL("http://www.test.com/foobar/index3.html"),
+               LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+               reused_password_account_type,
+               LoginReputationClientResponse::SAFE, 10 * kMinute,
+               "test.com/foobar/", base::Time::Now());
+  EXPECT_EQ(3U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+  EXPECT_EQ(1U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
+}
+
+TEST_P(PasswordProtectionServiceTest, TestCachePasswordReuseVerdictsIncognito) {
+  EXPECT_CALL(*password_protection_service_, IsIncognito())
+      .WillRepeatedly(Return(true));
+  ASSERT_EQ(0U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+
+  ReusedPasswordAccountType reused_password_account_type;
+  reused_password_account_type.set_account_type(
+      ReusedPasswordAccountType::GSUITE);
+  reused_password_account_type.set_is_account_syncing(true);
+  // No verdict will be cached for incognito profile.
+  CacheVerdict(GURL("http://www.test.com/foo/index.html"),
+               LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+               reused_password_account_type,
+               LoginReputationClientResponse::SAFE, 10 * kMinute,
+               "test.com/foo/", base::Time::Now());
+
+  EXPECT_EQ(0U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+
+  // Try cache another verdict with the some origin and cache_expression.
+  // Verdict count should not increase.
+  CacheVerdict(GURL("http://www.test.com/foo/index2.html"),
+               LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+               reused_password_account_type,
+               LoginReputationClientResponse::PHISHING, 10 * kMinute,
+               "test.com/foo/", base::Time::Now());
+  EXPECT_EQ(0U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+
+  // Now cache a UNFAMILIAR_LOGIN_PAGE verdict, verdict count should not
+  // increase.
+  CacheVerdict(GURL("http://www.test.com/foobar/index3.html"),
+               LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+               reused_password_account_type,
+               LoginReputationClientResponse::SAFE, 10 * kMinute,
+               "test.com/foobar/", base::Time::Now());
+  EXPECT_EQ(0U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+  EXPECT_EQ(0U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
+}
+
+TEST_P(PasswordProtectionServiceTest, TestCacheUnfamiliarLoginVerdicts) {
+  ASSERT_EQ(0U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
+  ReusedPasswordAccountType reused_password_account_type;
+  reused_password_account_type.set_account_type(
+      ReusedPasswordAccountType::UNKNOWN);
+  reused_password_account_type.set_is_account_syncing(true);
+  // Assume each verdict has a TTL of 10 minutes.
+  // Cache a verdict for http://www.test.com/foo/index.html
+  CacheVerdict(GURL("http://www.test.com/foo/index.html"),
+               LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+               reused_password_account_type,
+               LoginReputationClientResponse::SAFE, 10 * kMinute,
+               "test.com/foo/", base::Time::Now());
+
+  EXPECT_EQ(1U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
+
+  // Cache another verdict with the same origin but different cache_expression
+  // will not increase setting count, but will increase the number of verdicts
+  // in the given origin.
+  CacheVerdict(GURL("http://www.test.com/bar/index2.html"),
+               LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+               reused_password_account_type,
+               LoginReputationClientResponse::SAFE, 10 * kMinute,
+               "test.com/bar/", base::Time::Now());
+  EXPECT_EQ(2U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
+
+  // Now cache a PASSWORD_REUSE_EVENT verdict, stored verdict count for
+  // UNFAMILIAR_LOGIN_PAGE should be the same.
+  CacheVerdict(GURL("http://www.test.com/foobar/index3.html"),
+               LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+               reused_password_account_type,
+               LoginReputationClientResponse::SAFE, 10 * kMinute,
+               "test.com/foobar/", base::Time::Now());
+  EXPECT_EQ(2U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
+  EXPECT_EQ(1U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+}
+
+TEST_P(PasswordProtectionServiceTest,
+       TestCacheUnfamiliarLoginVerdictsIncognito) {
+  EXPECT_CALL(*password_protection_service_, IsIncognito())
+      .WillRepeatedly(Return(true));
+  ASSERT_EQ(0U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
+
+  ReusedPasswordAccountType reused_password_account_type;
+  reused_password_account_type.set_account_type(
+      ReusedPasswordAccountType::UNKNOWN);
+  reused_password_account_type.set_is_account_syncing(true);
+  // No verdict will be cached for incognito profile.
+  CacheVerdict(GURL("http://www.test.com/foo/index.html"),
+               LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+               reused_password_account_type,
+               LoginReputationClientResponse::SAFE, 10 * kMinute,
+               "test.com/foo/", base::Time::Now());
+
+  EXPECT_EQ(0U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
+
+  CacheVerdict(GURL("http://www.test.com/bar/index2.html"),
+               LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+               reused_password_account_type,
+               LoginReputationClientResponse::SAFE, 10 * kMinute,
+               "test.com/bar/", base::Time::Now());
+  EXPECT_EQ(0U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
+
+  // Now cache a PASSWORD_REUSE_EVENT verdict. Verdict count should not
+  // increase.
+  reused_password_account_type.set_account_type(
+      ReusedPasswordAccountType::GSUITE);
+  reused_password_account_type.set_is_account_syncing(true);
+  CacheVerdict(GURL("http://www.test.com/foobar/index3.html"),
+               LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+               reused_password_account_type,
+               LoginReputationClientResponse::SAFE, 10 * kMinute,
+               "test.com/foobar/", base::Time::Now());
+  EXPECT_EQ(0U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
+  EXPECT_EQ(0U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+}
+
+TEST_P(PasswordProtectionServiceTest, TestGetCachedVerdicts) {
+  ASSERT_EQ(0U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+  ASSERT_EQ(0U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
+  ReusedPasswordAccountType reused_password_account_type;
+  reused_password_account_type.set_account_type(
+      ReusedPasswordAccountType::GSUITE);
+  reused_password_account_type.set_is_account_syncing(true);
+  // Prepare 4 verdicts of the same origin with different cache expressions,
+  // or password type, one is expired, one is not, one is of a different
+  // trigger type, and the other is with a different password type.
+  base::Time now = base::Time::Now();
+  CacheVerdict(GURL("http://test.com/login.html"),
+               LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+               reused_password_account_type,
+               LoginReputationClientResponse::SAFE, 10 * kMinute, "test.com/",
+               now);
+  CacheVerdict(
+      GURL("http://test.com/def/index.jsp"),
+      LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+      reused_password_account_type, LoginReputationClientResponse::PHISHING,
+      10 * kMinute, "test.com/def/",
+      base::Time::FromDoubleT(now.ToDoubleT() - kDay));  // Yesterday, expired.
+  reused_password_account_type.set_account_type(
+      ReusedPasswordAccountType::UNKNOWN);
+  CacheVerdict(GURL("http://test.com/bar/login.html"),
+               LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+               reused_password_account_type,
+               LoginReputationClientResponse::PHISHING, 10 * kMinute,
+               "test.com/bar/", now);
+  reused_password_account_type.set_account_type(
+      ReusedPasswordAccountType::NON_GAIA_ENTERPRISE);
+  CacheVerdict(GURL("http://test.com/login.html"),
+               LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+               reused_password_account_type,
+               LoginReputationClientResponse::SAFE, 10 * kMinute, "test.com/",
+               now);
+
+  ASSERT_EQ(3U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+  ASSERT_EQ(1U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
+
+  // Return VERDICT_TYPE_UNSPECIFIED if look up for a URL with unknown origin.
+  LoginReputationClientResponse actual_verdict;
+  reused_password_account_type.set_account_type(
+      ReusedPasswordAccountType::GSUITE);
+  EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
+            password_protection_service_->GetCachedVerdict(
+                GURL("http://www.unknown.com/"),
+                LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+                reused_password_account_type, &actual_verdict));
+  reused_password_account_type.set_account_type(
+      ReusedPasswordAccountType::NON_GAIA_ENTERPRISE);
+  EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
+            password_protection_service_->GetCachedVerdict(
+                GURL("http://www.unknown.com/"),
+                LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+                reused_password_account_type, &actual_verdict));
+
+  // Return SAFE if look up for a URL that matches "test.com" cache expression.
+  reused_password_account_type.set_account_type(
+      ReusedPasswordAccountType::GSUITE);
+  EXPECT_EQ(LoginReputationClientResponse::SAFE,
+            password_protection_service_->GetCachedVerdict(
+                GURL("http://test.com/xyz/foo.jsp"),
+                LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+                reused_password_account_type, &actual_verdict));
+  reused_password_account_type.set_account_type(
+      ReusedPasswordAccountType::NON_GAIA_ENTERPRISE);
+  EXPECT_EQ(LoginReputationClientResponse::SAFE,
+            password_protection_service_->GetCachedVerdict(
+                GURL("http://test.com/xyz/foo.jsp"),
+                LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+                reused_password_account_type, &actual_verdict));
+
+  // Return VERDICT_TYPE_UNSPECIFIED if look up for a URL whose variants match
+  // test.com/def, but the corresponding verdict is expired.
+  reused_password_account_type.set_account_type(
+      ReusedPasswordAccountType::GSUITE);
+  EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
+            password_protection_service_->GetCachedVerdict(
+                GURL("http://test.com/def/ghi/index.html"),
+                LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+                reused_password_account_type, &actual_verdict));
+
+  // Return PHISHING. Matches "test.com/bar/" cache expression.
+  reused_password_account_type.set_account_type(
+      ReusedPasswordAccountType::UNKNOWN);
+  EXPECT_EQ(LoginReputationClientResponse::PHISHING,
+            password_protection_service_->GetCachedVerdict(
+                GURL("http://test.com/bar/foo.jsp"),
+                LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+                reused_password_account_type, &actual_verdict));
+
+  // Now cache SAFE verdict for the full path.
+  CacheVerdict(GURL("http://test.com/bar/foo.jsp"),
+               LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+               reused_password_account_type,
+               LoginReputationClientResponse::SAFE, 10 * kMinute,
+               "test.com/bar/foo.jsp", now);
+
+  // Return SAFE now. Matches the full cache expression.
+  EXPECT_EQ(LoginReputationClientResponse::SAFE,
+            password_protection_service_->GetCachedVerdict(
+                GURL("http://test.com/bar/foo.jsp"),
+                LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+                reused_password_account_type, &actual_verdict));
+}
+
+TEST_P(PasswordProtectionServiceTest, TestDoesNotCacheAboutBlank) {
+  ASSERT_EQ(0U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+  ReusedPasswordAccountType reused_password_account_type;
+  reused_password_account_type.set_account_type(
+      ReusedPasswordAccountType::UNKNOWN);
+
+  // Should not actually cache, since about:blank is not valid for reputation
+  // computing.
+  CacheVerdict(
+      GURL("about:blank"), LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+      reused_password_account_type, LoginReputationClientResponse::SAFE,
+      10 * kMinute, "about:blank", base::Time::Now());
+
+  EXPECT_EQ(0U, GetStoredVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+}
+
+TEST_P(PasswordProtectionServiceTest, VerifyCanGetReputationOfURL) {
+  // Invalid main frame URL.
+  EXPECT_FALSE(PasswordProtectionService::CanGetReputationOfURL(GURL()));
+
+  // Main frame URL scheme is not HTTP or HTTPS.
+  EXPECT_FALSE(PasswordProtectionService::CanGetReputationOfURL(
+      GURL("data:text/html, <p>hellow")));
+
+  // Main frame URL is a local host.
+  EXPECT_FALSE(PasswordProtectionService::CanGetReputationOfURL(
+      GURL("http://localhost:80")));
+  EXPECT_FALSE(PasswordProtectionService::CanGetReputationOfURL(
+      GURL("http://127.0.0.1")));
+
+  // Main frame URL is a private IP address or anything in an IANA-reserved
+  // range.
+  EXPECT_FALSE(PasswordProtectionService::CanGetReputationOfURL(
+      GURL("http://192.168.1.0/")));
+  EXPECT_FALSE(PasswordProtectionService::CanGetReputationOfURL(
+      GURL("http://10.0.1.0/")));
+  EXPECT_FALSE(PasswordProtectionService::CanGetReputationOfURL(
+      GURL("http://[FEED::BEEF]")));
+
+  // Main frame URL is a no-yet-assigned y ICANN gTLD.
+  EXPECT_FALSE(PasswordProtectionService::CanGetReputationOfURL(
+      GURL("http://intranet")));
+  EXPECT_FALSE(PasswordProtectionService::CanGetReputationOfURL(
+      GURL("http://host.intranet.example")));
+
+  // Main frame URL is a dotless domain.
+  EXPECT_FALSE(PasswordProtectionService::CanGetReputationOfURL(
+      GURL("http://go/example")));
+
+  // Main frame URL is anything else.
+  EXPECT_TRUE(PasswordProtectionService::CanGetReputationOfURL(
+      GURL("http://www.chromium.org")));
+}
+
+TEST_P(PasswordProtectionServiceTest, TestNoRequestSentForWhitelistedURL) {
+  histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogram, 0);
+  std::unique_ptr<content::WebContents> web_contents = GetWebContents();
+  content::WebContentsTester::For(web_contents.get())
+      ->SetLastCommittedURL(GURL("http://safe.com/"));
+  InitializeAndStartPasswordOnFocusRequest(true /* match whitelist */,
+                                           10000 /* timeout in ms */,
+                                           web_contents.get());
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(nullptr, password_protection_service_->latest_response());
+  EXPECT_THAT(
+      histograms_.GetAllSamples(kPasswordOnFocusRequestOutcomeHistogram),
+      ElementsAre(base::Bucket(4 /* MATCHED_WHITELIST */, 1)));
+}
+
+// crbug.com/1010007: crashes on win
+#if defined(OS_WIN)
+#define MAYBE_TestNoRequestSentIfVerdictAlreadyCached \
+  DISABLED_TestNoRequestSentIfVerdictAlreadyCached
+#else
+#define MAYBE_TestNoRequestSentIfVerdictAlreadyCached \
+  TestNoRequestSentIfVerdictAlreadyCached
+#endif
+TEST_P(PasswordProtectionServiceTest,
+       MAYBE_TestNoRequestSentIfVerdictAlreadyCached) {
+  histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogram, 0);
+  ReusedPasswordAccountType reused_password_account_type;
+  reused_password_account_type.set_account_type(
+      ReusedPasswordAccountType::UNKNOWN);
+  std::unique_ptr<content::WebContents> web_contents = GetWebContents();
+  CacheVerdict(GURL(kTargetUrl),
+               LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+               reused_password_account_type,
+               LoginReputationClientResponse::LOW_REPUTATION, 10 * kMinute,
+               GURL(kTargetUrl).host().append("/"), base::Time::Now());
+  InitializeAndStartPasswordOnFocusRequest(/*match_whitelist=*/false,
+                                           /*timeout_in_ms=*/10000,
+                                           web_contents.get());
+  base::RunLoop().RunUntilIdle();
+  EXPECT_THAT(
+      histograms_.GetAllSamples(kPasswordOnFocusRequestOutcomeHistogram),
+      ElementsAre(base::Bucket(5 /* RESPONSE_ALREADY_CACHED */, 1)));
+  EXPECT_EQ(LoginReputationClientResponse::LOW_REPUTATION,
+            password_protection_service_->latest_response()->verdict_type());
+}
+
+TEST_P(PasswordProtectionServiceTest, TestResponseFetchFailed) {
+  histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogram, 0);
+  // Set up failed response.
+  network::URLLoaderCompletionStatus status(net::ERR_FAILED);
+  test_url_loader_factory_.AddResponse(
+      url_, network::mojom::URLResponseHead::New(), std::string(), status);
+  std::unique_ptr<content::WebContents> web_contents = GetWebContents();
+
+  InitializeAndStartPasswordOnFocusRequest(/*match_whitelist=*/false,
+                                           /*timeout_in_ms=*/10000,
+                                           web_contents.get());
+  password_protection_service_->WaitForResponse();
+  EXPECT_EQ(nullptr, password_protection_service_->latest_response());
+  EXPECT_THAT(
+      histograms_.GetAllSamples(kPasswordOnFocusRequestOutcomeHistogram),
+      ElementsAre(base::Bucket(9 /* FETCH_FAILED */, 1)));
+}
+
+TEST_P(PasswordProtectionServiceTest, TestMalformedResponse) {
+  histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogram, 0);
+  // Set up malformed response.
+  test_url_loader_factory_.AddResponse(url_.spec(), "invalid response");
+  std::unique_ptr<content::WebContents> web_contents = GetWebContents();
+
+  InitializeAndStartPasswordOnFocusRequest(/*match_whitelist=*/false,
+                                           /*timeout_in_ms=*/10000,
+                                           web_contents.get());
+  password_protection_service_->WaitForResponse();
+  EXPECT_EQ(nullptr, password_protection_service_->latest_response());
+  EXPECT_THAT(
+      histograms_.GetAllSamples(kPasswordOnFocusRequestOutcomeHistogram),
+      ElementsAre(base::Bucket(10 /* RESPONSE_MALFORMED */, 1)));
+}
+
+TEST_P(PasswordProtectionServiceTest, TestRequestTimedout) {
+  histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogram, 0);
+  std::unique_ptr<content::WebContents> web_contents = GetWebContents();
+  InitializeAndStartPasswordOnFocusRequest(/*match_whitelist=*/false,
+                                           /*timeout_in_ms=*/0,
+                                           web_contents.get());
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(nullptr, password_protection_service_->latest_response());
+  EXPECT_THAT(
+      histograms_.GetAllSamples(kPasswordOnFocusRequestOutcomeHistogram),
+      ElementsAre(base::Bucket(3 /* TIMEDOUT */, 1)));
+}
+
+TEST_P(PasswordProtectionServiceTest,
+       TestPasswordOnFocusRequestAndResponseSuccessfull) {
+  histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogram, 0);
+  // Set up valid response.
+  LoginReputationClientResponse expected_response =
+      CreateVerdictProto(LoginReputationClientResponse::PHISHING, 10 * kMinute,
+                         GURL(kTargetUrl).host());
+  test_url_loader_factory_.AddResponse(url_.spec(),
+                                       expected_response.SerializeAsString());
+  std::unique_ptr<content::WebContents> web_contents = GetWebContents();
+
+  InitializeAndStartPasswordOnFocusRequest(/*match_whitelist=*/false,
+                                           /*timeout_in_ms=*/10000,
+                                           web_contents.get());
+  password_protection_service_->WaitForResponse();
+  EXPECT_THAT(
+      histograms_.GetAllSamples(kPasswordOnFocusRequestOutcomeHistogram),
+      ElementsAre(base::Bucket(1 /* SUCCEEDED */, 1)));
+  EXPECT_THAT(histograms_.GetAllSamples(kPasswordOnFocusVerdictHistogram),
+              ElementsAre(base::Bucket(3 /* PHISHING */, 1)));
+  LoginReputationClientResponse* actual_response =
+      password_protection_service_->latest_response();
+  EXPECT_EQ(expected_response.verdict_type(), actual_response->verdict_type());
+  EXPECT_EQ(expected_response.cache_expression(),
+            actual_response->cache_expression());
+  EXPECT_EQ(expected_response.cache_duration_sec(),
+            actual_response->cache_duration_sec());
+}
+
+TEST_P(PasswordProtectionServiceTest,
+       TestProtectedPasswordEntryRequestAndResponseSuccessfull) {
+  histograms_.ExpectTotalCount(kAnyPasswordEntryRequestOutcomeHistogram, 0);
+  histograms_.ExpectTotalCount(kSyncPasswordEntryRequestOutcomeHistogram, 0);
+  histograms_.ExpectTotalCount(kNonSyncPasswordEntryRequestOutcomeHistogram, 0);
+  // Set up valid response.
+  LoginReputationClientResponse expected_response =
+      CreateVerdictProto(LoginReputationClientResponse::PHISHING, 10 * kMinute,
+                         GURL(kTargetUrl).host());
+  test_url_loader_factory_.AddResponse(url_.spec(),
+                                       expected_response.SerializeAsString());
+  std::unique_ptr<content::WebContents> web_contents = GetWebContents();
+
+  // Initiate a saved password entry request (w/ no sync password).
+  AccountInfo account_info;
+  account_info.account_id = CoreAccountId("account_id");
+  account_info.email = "email";
+  account_info.gaia = "gaia";
+  EXPECT_CALL(*password_protection_service_, GetSignedInNonSyncAccount(_))
+      .WillRepeatedly(Return(account_info));
+
+  InitializeAndStartPasswordEntryRequest(
+      PasswordType::OTHER_GAIA_PASSWORD, {"gmail.com"},
+      /*match_whitelist=*/false,
+      /*timeout_in_ms=*/10000, web_contents.get());
+  password_protection_service_->WaitForResponse();
+
+  // UMA: request outcomes
+  EXPECT_THAT(
+      histograms_.GetAllSamples(kAnyPasswordEntryRequestOutcomeHistogram),
+      ElementsAre(base::Bucket(1 /* SUCCEEDED */, 1)));
+  histograms_.ExpectTotalCount(kSyncPasswordEntryRequestOutcomeHistogram, 0);
+  EXPECT_THAT(
+      histograms_.GetAllSamples(kNonSyncPasswordEntryRequestOutcomeHistogram),
+      ElementsAre(base::Bucket(1 /* SUCCEEDED */, 1)));
+  EXPECT_THAT(histograms_.GetAllSamples(kNonSyncPasswordEntryVerdictHistogram),
+              ElementsAre(base::Bucket(3 /* PHISHING */, 1)));
+
+  // UMA: verdicts
+  EXPECT_THAT(histograms_.GetAllSamples(kAnyPasswordEntryVerdictHistogram),
+              ElementsAre(base::Bucket(3 /* PHISHING */, 1)));
+  histograms_.ExpectTotalCount(kSyncPasswordEntryVerdictHistogram, 0);
+  EXPECT_THAT(histograms_.GetAllSamples(kNonSyncPasswordEntryVerdictHistogram),
+              ElementsAre(base::Bucket(3 /* PHISHING */, 1)));
+}
+
+TEST_P(PasswordProtectionServiceTest,
+       TestSyncPasswordEntryRequestAndResponseSuccessfull) {
+  histograms_.ExpectTotalCount(kAnyPasswordEntryRequestOutcomeHistogram, 0);
+  histograms_.ExpectTotalCount(kSyncPasswordEntryRequestOutcomeHistogram, 0);
+  histograms_.ExpectTotalCount(kNonSyncPasswordEntryRequestOutcomeHistogram, 0);
+  // Set up valid response.
+  LoginReputationClientResponse expected_response =
+      CreateVerdictProto(LoginReputationClientResponse::PHISHING, 10 * kMinute,
+                         GURL(kTargetUrl).host());
+  test_url_loader_factory_.AddResponse(url_.spec(),
+                                       expected_response.SerializeAsString());
+  EXPECT_CALL(*password_protection_service_, IsPrimaryAccountSyncing())
+      .WillRepeatedly(Return(true));
+  EXPECT_CALL(*password_protection_service_, IsPrimaryAccountSignedIn())
+      .WillRepeatedly(Return(true));
+  // Initiate a sync password entry request (w/ no saved password).
+  std::unique_ptr<content::WebContents> web_contents = GetWebContents();
+  InitializeAndStartPasswordEntryRequest(
+      PasswordType::PRIMARY_ACCOUNT_PASSWORD, {},
+      /*match_whitelist=*/false,
+      /*timeout_in_ms=*/10000, web_contents.get());
+  password_protection_service_->WaitForResponse();
+
+  // UMA: request outcomes
+  EXPECT_THAT(
+      histograms_.GetAllSamples(kAnyPasswordEntryRequestOutcomeHistogram),
+      ElementsAre(base::Bucket(1 /* SUCCEEDED */, 1)));
+  EXPECT_THAT(
+      histograms_.GetAllSamples(kSyncPasswordEntryRequestOutcomeHistogram),
+      ElementsAre(base::Bucket(1 /* SUCCEEDED */, 1)));
+  histograms_.ExpectTotalCount(kNonSyncPasswordEntryRequestOutcomeHistogram, 0);
+
+  // UMA: verdicts
+  EXPECT_THAT(histograms_.GetAllSamples(kAnyPasswordEntryVerdictHistogram),
+              ElementsAre(base::Bucket(3 /* PHISHING */, 1)));
+  EXPECT_THAT(histograms_.GetAllSamples(kSyncPasswordEntryVerdictHistogram),
+              ElementsAre(base::Bucket(3 /* PHISHING */, 1)));
+  histograms_.ExpectTotalCount(kNonSyncPasswordEntryVerdictHistogram, 0);
+}
+
+TEST_P(PasswordProtectionServiceTest, TestTearDownWithPendingRequests) {
+  histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogram, 0);
+  GURL target_url(kTargetUrl);
+  EXPECT_CALL(*database_manager_, CheckCsdWhitelistUrl(target_url, _))
+      .WillRepeatedly(Return(AsyncMatch::NO_MATCH));
+  std::unique_ptr<content::WebContents> web_contents = GetWebContents();
+  password_protection_service_->StartRequest(
+      web_contents.get(), target_url, GURL("http://foo.com/submit"),
+      GURL("http://foo.com/frame"), "username", PasswordType::SAVED_PASSWORD,
+      {}, LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE, true);
+
+  // Destroy password_protection_service_ while there is one request pending.
+  password_protection_service_.reset();
+  base::RunLoop().RunUntilIdle();
+
+  // We should not log on TearDown, since that can dispatch calls to pure
+  // virtual methods.
+  EXPECT_THAT(
+      histograms_.GetAllSamples(kPasswordOnFocusRequestOutcomeHistogram),
+      IsEmpty());
+}
+
+TEST_P(PasswordProtectionServiceTest, VerifyPasswordOnFocusRequestProto) {
+  // Set up valid response.
+  LoginReputationClientResponse expected_response =
+      CreateVerdictProto(LoginReputationClientResponse::PHISHING, 10 * kMinute,
+                         GURL(kTargetUrl).host());
+  test_url_loader_factory_.AddResponse(url_.spec(),
+                                       expected_response.SerializeAsString());
+  std::unique_ptr<content::WebContents> web_contents = GetWebContents();
+
+  InitializeAndStartPasswordOnFocusRequest(/*match_whitelist=*/false,
+                                           /*timeout_in_ms=*/10000,
+                                           web_contents.get());
+  password_protection_service_->WaitForResponse();
+
+  const LoginReputationClientRequest* actual_request =
+      password_protection_service_->GetLatestRequestProto();
+  EXPECT_EQ(kTargetUrl, actual_request->page_url());
+  EXPECT_EQ(LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+            actual_request->trigger_type());
+  ASSERT_EQ(2, actual_request->frames_size());
+  EXPECT_EQ(kTargetUrl, actual_request->frames(0).url());
+  EXPECT_EQ(kPasswordFrameUrl, actual_request->frames(1).url());
+  EXPECT_EQ(true, actual_request->frames(1).has_password_field());
+  ASSERT_EQ(1, actual_request->frames(1).forms_size());
+  EXPECT_EQ(kFormActionUrl, actual_request->frames(1).forms(0).action_url());
+  VerifyContentAreaSizeCollection(*actual_request);
+}
+
+TEST_P(PasswordProtectionServiceTest,
+       VerifyPasswordOnFocusRequestProtoForAllowlistMatch) {
+  // Set up valid response.
+  LoginReputationClientResponse expected_response =
+      CreateVerdictProto(LoginReputationClientResponse::PHISHING, 10 * kMinute,
+                         GURL(kTargetUrl).host());
+  test_url_loader_factory_.AddResponse(url_.spec(),
+                                       expected_response.SerializeAsString());
+  std::unique_ptr<content::WebContents> web_contents = GetWebContents();
+
+  EXPECT_CALL(*password_protection_service_, CanSendSamplePing())
+      .WillRepeatedly(Return(true));
+  InitializeAndStartPasswordOnFocusRequest(/*match_whitelist=*/true,
+                                           /*timeout_in_ms=*/10000,
+                                           web_contents.get());
+  password_protection_service_->WaitForResponse();
+
+  const LoginReputationClientRequest* actual_request =
+      password_protection_service_->GetLatestRequestProto();
+  EXPECT_EQ(kTargetUrl, actual_request->page_url());
+  ASSERT_EQ(1, actual_request->frames_size());
+  EXPECT_EQ(kTargetUrl, actual_request->frames(0).url());
+}
+
+TEST_P(PasswordProtectionServiceTest,
+       VerifySyncPasswordProtectionRequestProto) {
+  // Set up valid response.
+  LoginReputationClientResponse expected_response =
+      CreateVerdictProto(LoginReputationClientResponse::PHISHING, 10 * kMinute,
+                         GURL(kTargetUrl).host());
+  test_url_loader_factory_.AddResponse(url_.spec(),
+                                       expected_response.SerializeAsString());
+  std::unique_ptr<content::WebContents> web_contents = GetWebContents();
+
+  // Initialize request triggered by chrome sync password reuse.
+  InitializeAndStartPasswordEntryRequest(
+      PasswordType::PRIMARY_ACCOUNT_PASSWORD, {}, false /* match whitelist */,
+      100000 /* timeout in ms*/, web_contents.get());
+  password_protection_service_->WaitForResponse();
+
+  const LoginReputationClientRequest* actual_request =
+      password_protection_service_->GetLatestRequestProto();
+  EXPECT_EQ(kTargetUrl, actual_request->page_url());
+  EXPECT_EQ(LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+            actual_request->trigger_type());
+  EXPECT_EQ(1, actual_request->frames_size());
+  EXPECT_EQ(kTargetUrl, actual_request->frames(0).url());
+  EXPECT_TRUE(actual_request->frames(0).has_password_field());
+  ASSERT_TRUE(actual_request->has_password_reuse_event());
+  const auto& reuse_event = actual_request->password_reuse_event();
+  EXPECT_TRUE(reuse_event.is_chrome_signin_password());
+  EXPECT_EQ(0, reuse_event.domains_matching_password_size());
+  VerifyContentAreaSizeCollection(*actual_request);
+}
+
+TEST_P(PasswordProtectionServiceTest,
+       VerifyNonSyncPasswordProtectionRequestProto) {
+  // Set up valid response.
+  LoginReputationClientResponse expected_response =
+      CreateVerdictProto(LoginReputationClientResponse::PHISHING, 10 * kMinute,
+                         GURL(kTargetUrl).host());
+  test_url_loader_factory_.AddResponse(url_.spec(),
+                                       expected_response.SerializeAsString());
+  std::unique_ptr<content::WebContents> web_contents = GetWebContents();
+
+  // Initialize request triggered by saved password reuse.
+  InitializeAndStartPasswordEntryRequest(
+      PasswordType::SAVED_PASSWORD, {kSavedDomain, kSavedDomain2},
+      false /* match whitelist */, 100000 /* timeout in ms*/,
+      web_contents.get());
+  password_protection_service_->WaitForResponse();
+
+  const LoginReputationClientRequest* actual_request =
+      password_protection_service_->GetLatestRequestProto();
+  ASSERT_TRUE(actual_request->has_password_reuse_event());
+  const auto& reuse_event = actual_request->password_reuse_event();
+  EXPECT_FALSE(reuse_event.is_chrome_signin_password());
+
+  if (password_protection_service_->IsExtendedReporting() &&
+      !password_protection_service_->IsIncognito()) {
+    ASSERT_EQ(2, reuse_event.domains_matching_password_size());
+    EXPECT_EQ(kSavedDomain, reuse_event.domains_matching_password(0));
+    EXPECT_EQ(kSavedDomain2, reuse_event.domains_matching_password(1));
+  } else {
+    EXPECT_EQ(0, reuse_event.domains_matching_password_size());
+  }
+  VerifyContentAreaSizeCollection(*actual_request);
+}
+
+TEST_P(PasswordProtectionServiceTest, VerifyShouldShowModalWarning) {
+  EXPECT_CALL(*password_protection_service_,
+              GetPasswordProtectionWarningTriggerPref(_))
+      .WillRepeatedly(Return(PHISHING_REUSE));
+  EXPECT_CALL(*password_protection_service_, IsPrimaryAccountSignedIn())
+      .WillRepeatedly(Return(true));
+  AccountInfo account_info;
+  account_info.account_id = CoreAccountId("account_id");
+  account_info.email = "email";
+  account_info.gaia = "gaia";
+  EXPECT_CALL(*password_protection_service_, GetSignedInNonSyncAccount(_))
+      .WillRepeatedly(Return(account_info));
+
+  // Don't show modal warning if it is not a password reuse ping.
+  ReusedPasswordAccountType reused_password_account_type;
+  reused_password_account_type.set_account_type(
+      ReusedPasswordAccountType::UNKNOWN);
+  reused_password_account_type.set_is_account_syncing(true);
+  EXPECT_FALSE(password_protection_service_->ShouldShowModalWarning(
+      LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+      reused_password_account_type, LoginReputationClientResponse::PHISHING));
+
+  // Don't show modal warning if it is a saved password reuse and the experiment
+  // isn't on.
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndDisableFeature(
+      safe_browsing::kPasswordProtectionForSignedInUsers);
+  reused_password_account_type.set_account_type(
+      ReusedPasswordAccountType::SAVED_PASSWORD);
+  EXPECT_FALSE(password_protection_service_->ShouldShowModalWarning(
+      LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+      reused_password_account_type, LoginReputationClientResponse::PHISHING));
+
+  {
+    base::test::ScopedFeatureList feature_list;
+    feature_list.InitAndEnableFeature(
+        safe_browsing::kPasswordProtectionForSavedPasswords);
+    EXPECT_TRUE(password_protection_service_->ShouldShowModalWarning(
+        LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+        reused_password_account_type, LoginReputationClientResponse::PHISHING));
+    EXPECT_TRUE(password_protection_service_->ShouldShowModalWarning(
+        LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+        reused_password_account_type,
+        LoginReputationClientResponse::LOW_REPUTATION));
+  }
+
+  {
+    base::test::ScopedFeatureList feature_list;
+    feature_list.InitAndDisableFeature(
+        safe_browsing::kPasswordProtectionForSignedInUsers);
+
+    // Don't show modal warning if non-sync gaia account experiment is not
+    // on.
+    reused_password_account_type.set_account_type(
+        ReusedPasswordAccountType::GMAIL);
+    reused_password_account_type.set_is_account_syncing(false);
+    EXPECT_FALSE(password_protection_service_->ShouldShowModalWarning(
+        LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+        reused_password_account_type, LoginReputationClientResponse::PHISHING));
+  }
+  {
+    base::test::ScopedFeatureList feature_list;
+    feature_list.InitAndEnableFeature(
+        safe_browsing::kPasswordProtectionForSignedInUsers);
+    // Show modal warning if non-sync gaia account experiment is on.
+    reused_password_account_type.set_account_type(
+        ReusedPasswordAccountType::GMAIL);
+    reused_password_account_type.set_is_account_syncing(false);
+    EXPECT_TRUE(password_protection_service_->ShouldShowModalWarning(
+        LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+        reused_password_account_type, LoginReputationClientResponse::PHISHING));
+  }
+
+  // Don't show modal warning if reused password type unknown.
+  reused_password_account_type.set_account_type(
+      ReusedPasswordAccountType::UNKNOWN);
+  EXPECT_FALSE(password_protection_service_->ShouldShowModalWarning(
+      LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+      reused_password_account_type, LoginReputationClientResponse::PHISHING));
+
+  reused_password_account_type.set_account_type(
+      ReusedPasswordAccountType::GMAIL);
+  reused_password_account_type.set_is_account_syncing(true);
+  EXPECT_TRUE(password_protection_service_->ShouldShowModalWarning(
+      LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+      reused_password_account_type, LoginReputationClientResponse::PHISHING));
+
+  // For a GSUITE account, don't show warning if password protection is set to
+  // off by enterprise policy.
+  reused_password_account_type.set_account_type(
+      ReusedPasswordAccountType::GSUITE);
+  EXPECT_CALL(*password_protection_service_,
+              GetPasswordProtectionWarningTriggerPref(_))
+      .WillRepeatedly(Return(PASSWORD_PROTECTION_OFF));
+  EXPECT_FALSE(password_protection_service_->ShouldShowModalWarning(
+      LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+      reused_password_account_type, LoginReputationClientResponse::PHISHING));
+
+  // For a GSUITE account, show warning if password protection is set to
+  // PHISHING_REUSE.
+  EXPECT_CALL(*password_protection_service_,
+              GetPasswordProtectionWarningTriggerPref(_))
+      .WillRepeatedly(Return(PHISHING_REUSE));
+  EXPECT_EQ(
+      PHISHING_REUSE,
+      password_protection_service_->GetPasswordProtectionWarningTriggerPref(
+          reused_password_account_type));
+  EXPECT_TRUE(password_protection_service_->ShouldShowModalWarning(
+      LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+      reused_password_account_type, LoginReputationClientResponse::PHISHING));
+
+  // Modal dialog warning is also shown on LOW_REPUTATION verdict.
+  EXPECT_TRUE(password_protection_service_->ShouldShowModalWarning(
+      LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+      reused_password_account_type,
+      LoginReputationClientResponse::LOW_REPUTATION));
+
+  // Modal dialog warning should not be shown for enterprise password reuse
+  // if it is turned off by policy.
+  EXPECT_CALL(*password_protection_service_,
+              GetPasswordProtectionWarningTriggerPref(_))
+      .WillRepeatedly(Return(PASSWORD_PROTECTION_OFF));
+  EXPECT_FALSE(password_protection_service_->ShouldShowModalWarning(
+      LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+      reused_password_account_type, LoginReputationClientResponse::PHISHING));
+
+  // Show modal warning for enterprise password reuse if the trigger is
+  // configured to PHISHING_REUSE.
+  reused_password_account_type.set_account_type(
+      ReusedPasswordAccountType::NON_GAIA_ENTERPRISE);
+  EXPECT_CALL(*password_protection_service_,
+              GetPasswordProtectionWarningTriggerPref(_))
+      .WillRepeatedly(Return(PHISHING_REUSE));
+  EXPECT_TRUE(password_protection_service_->ShouldShowModalWarning(
+      LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+      reused_password_account_type, LoginReputationClientResponse::PHISHING));
+}
+
+TEST_P(PasswordProtectionServiceTest, VerifyContentTypeIsPopulated) {
+  LoginReputationClientResponse response =
+      CreateVerdictProto(LoginReputationClientResponse::SAFE, 10 * kMinute,
+                         GURL(kTargetUrl).host());
+  test_url_loader_factory_.AddResponse(url_.spec(),
+                                       response.SerializeAsString());
+
+  std::unique_ptr<content::WebContents> web_contents = GetWebContents();
+
+  content::WebContentsTester::For(web_contents.get())
+      ->SetMainFrameMimeType("application/pdf");
+
+  InitializeAndStartPasswordOnFocusRequest(false /* match whitelist */,
+                                           10000 /* timeout in ms */,
+                                           web_contents.get());
+
+  password_protection_service_->WaitForResponse();
+
+  EXPECT_EQ(
+      "application/pdf",
+      password_protection_service_->GetLatestRequestProto()->content_type());
+}
+
+TEST_P(PasswordProtectionServiceTest, VerifyIsSupportedPasswordTypeForPinging) {
+  EXPECT_CALL(*password_protection_service_, IsPrimaryAccountSignedIn())
+      .WillRepeatedly(Return(true));
+  AccountInfo account_info;
+  account_info.account_id = CoreAccountId("account_id");
+  account_info.email = "email";
+  account_info.gaia = "gaia";
+  EXPECT_CALL(*password_protection_service_, GetSignedInNonSyncAccount(_))
+      .WillRepeatedly(Return(account_info));
+
+  EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging(
+      PasswordType::SAVED_PASSWORD));
+  EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging(
+      PasswordType::PRIMARY_ACCOUNT_PASSWORD));
+  EXPECT_FALSE(password_protection_service_->IsSupportedPasswordTypeForPinging(
+      PasswordType::OTHER_GAIA_PASSWORD));
+  EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging(
+      PasswordType::ENTERPRISE_PASSWORD));
+
+  EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging(
+      PasswordType::SAVED_PASSWORD));
+  EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging(
+      PasswordType::ENTERPRISE_PASSWORD));
+  {
+    base::test::ScopedFeatureList feature_list;
+    feature_list.InitAndDisableFeature(
+        safe_browsing::kPasswordProtectionForSignedInUsers);
+    // Only ping for signed in, non-syncing users if the experiment is on.
+    EXPECT_FALSE(
+        password_protection_service_->IsSupportedPasswordTypeForPinging(
+            PasswordType::OTHER_GAIA_PASSWORD));
+  }
+  {
+    base::test::ScopedFeatureList feature_list;
+    feature_list.InitAndEnableFeature(
+        safe_browsing::kPasswordProtectionForSignedInUsers);
+    EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging(
+        PasswordType::OTHER_GAIA_PASSWORD));
+  }
+}
+
+TEST_P(PasswordProtectionServiceTest, TestPingsForAboutBlank) {
+  histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogram, 0);
+  LoginReputationClientResponse expected_response =
+      CreateVerdictProto(LoginReputationClientResponse::PHISHING, 10 * kMinute,
+                         GURL("about:blank").host());
+  test_url_loader_factory_.AddResponse(url_.spec(),
+                                       expected_response.SerializeAsString());
+  std::unique_ptr<content::WebContents> web_contents = GetWebContents();
+  password_protection_service_->StartRequest(
+      web_contents.get(), GURL("about:blank"), GURL(), GURL(), "username",
+      PasswordType::SAVED_PASSWORD, {"example.com"},
+      LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE, true);
+  base::RunLoop().RunUntilIdle();
+  histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogram, 1);
+}
+
+TEST_P(PasswordProtectionServiceTest,
+       TestVisualFeaturesPopulatedInOnFocusPing) {
+  LoginReputationClientResponse expected_response =
+      CreateVerdictProto(LoginReputationClientResponse::PHISHING, 10 * kMinute,
+                         GURL("about:blank").host());
+  test_url_loader_factory_.AddResponse(url_.spec(),
+                                       expected_response.SerializeAsString());
+  EXPECT_CALL(*password_protection_service_, GetCurrentContentAreaSize())
+      .Times(AnyNumber())
+      .WillOnce(Return(gfx::Size(1000, 1000)));
+  std::unique_ptr<content::WebContents> web_contents = GetWebContents();
+  password_protection_service_->StartRequest(
+      web_contents.get(), GURL("about:blank"), GURL(), GURL(), kUserName,
+      PasswordType::SAVED_PASSWORD, {"example.com"},
+      LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE, true);
+  base::RunLoop().RunUntilIdle();
+
+  bool is_sber = GetParam();
+  if (is_sber) {
+    password_protection_service_->WaitForResponse();
+    ASSERT_NE(nullptr, password_protection_service_->GetLatestRequestProto());
+    EXPECT_TRUE(password_protection_service_->GetLatestRequestProto()
+                    ->has_visual_features());
+  }
+}
+
+TEST_P(PasswordProtectionServiceTest, TestDomFeaturesPopulated) {
+  LoginReputationClientResponse expected_response =
+      CreateVerdictProto(LoginReputationClientResponse::PHISHING, 10 * kMinute,
+                         GURL("about:blank").host());
+  test_url_loader_factory_.AddResponse(url_.spec(),
+                                       expected_response.SerializeAsString());
+  EXPECT_CALL(*password_protection_service_, GetCurrentContentAreaSize())
+      .Times(AnyNumber())
+      .WillOnce(Return(gfx::Size(1000, 1000)));
+  std::unique_ptr<content::WebContents> web_contents = GetWebContents();
+  password_protection_service_->StartRequest(
+      web_contents.get(), GURL("about:blank"), GURL(), GURL(), kUserName,
+      PasswordType::SAVED_PASSWORD, {"example.com"},
+      LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE, true);
+  base::RunLoop().RunUntilIdle();
+
+  password_protection_service_->WaitForResponse();
+  ASSERT_NE(nullptr, password_protection_service_->GetLatestRequestProto());
+  EXPECT_TRUE(password_protection_service_->GetLatestRequestProto()
+                  ->has_dom_features());
+}
+
+TEST_P(PasswordProtectionServiceTest, TestDomFeaturesTimeout) {
+  password_protection_service_->SetDomFeatureCollectionTimeout(true);
+  LoginReputationClientResponse expected_response =
+      CreateVerdictProto(LoginReputationClientResponse::PHISHING, 10 * kMinute,
+                         GURL("about:blank").host());
+  test_url_loader_factory_.AddResponse(url_.spec(),
+                                       expected_response.SerializeAsString());
+  EXPECT_CALL(*password_protection_service_, GetCurrentContentAreaSize())
+      .Times(AnyNumber())
+      .WillOnce(Return(gfx::Size(1000, 1000)));
+  std::unique_ptr<content::WebContents> web_contents = GetWebContents();
+  password_protection_service_->StartRequest(
+      web_contents.get(), GURL("about:blank"), GURL(), GURL(), kUserName,
+      PasswordType::SAVED_PASSWORD, {"example.com"},
+      LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE, true);
+  task_environment_.FastForwardUntilNoTasksRemain();
+
+  password_protection_service_->WaitForResponse();
+  ASSERT_NE(nullptr, password_protection_service_->GetLatestRequestProto());
+  EXPECT_FALSE(password_protection_service_->GetLatestRequestProto()
+                   ->has_dom_features());
+}
+
+TEST_P(PasswordProtectionServiceTest, TestRequestCancelOnTimeout) {
+  std::unique_ptr<content::WebContents> web_contents = GetWebContents();
+  InitializeAndStartPasswordOnFocusRequest(true /* match whitelist */,
+                                           10000 /* timeout in ms */,
+                                           web_contents.get());
+  auto throttle = std::make_unique<MockPasswordProtectionNavigationThrottle>(
+      nullptr, request_, false);
+  EXPECT_EQ(1U, GetNumberOfNavigationThrottles());
+  request_->Cancel(true /* timeout */);
+  EXPECT_EQ(1U, GetNumberOfNavigationThrottles());
+}
+
+TEST_P(PasswordProtectionServiceTest, TestRequestCancelNotOnTimeout) {
+  std::unique_ptr<content::WebContents> web_contents = GetWebContents();
+  InitializeAndStartPasswordOnFocusRequest(true /* match whitelist */,
+                                           10000 /* timeout in ms */,
+                                           web_contents.get());
+  auto throttle = std::make_unique<MockPasswordProtectionNavigationThrottle>(
+      nullptr, request_, false);
+  EXPECT_EQ(1U, GetNumberOfNavigationThrottles());
+  request_->Cancel(false /* timeout */);
+  EXPECT_EQ(0U, GetNumberOfNavigationThrottles());
+}
+
+TEST_P(PasswordProtectionServiceTest, TestWebContentsDestroyed) {
+  std::unique_ptr<content::WebContents> web_contents = GetWebContents();
+  InitializeAndStartPasswordOnFocusRequest(false /* match whitelist */,
+                                           10000 /* timeout in ms */,
+                                           web_contents.get());
+  web_contents.reset();
+  task_environment_.FastForwardUntilNoTasksRemain();
+}
+
+INSTANTIATE_TEST_SUITE_P(Regular,
+                         PasswordProtectionServiceTest,
+                         ::testing::Values(false));
+INSTANTIATE_TEST_SUITE_P(SBER,
+                         PasswordProtectionServiceTest,
+                         ::testing::Values(true));
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/content/password_protection/visual_utils.cc b/components/safe_browsing/content/password_protection/visual_utils.cc
new file mode 100644
index 0000000..26a0863
--- /dev/null
+++ b/components/safe_browsing/content/password_protection/visual_utils.cc
@@ -0,0 +1,176 @@
+// Copyright 2018 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 <unordered_map>
+#include <vector>
+
+#include "components/safe_browsing/content/password_protection/visual_utils.h"
+
+#include "base/logging.h"
+#include "base/numerics/checked_math.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkPixmap.h"
+
+namespace safe_browsing {
+namespace visual_utils {
+
+namespace {
+
+// WARNING: The following parameters are highly privacy and performance
+// sensitive. These should not be changed without thorough review.
+const int kPHashDownsampleWidth = 288;
+const int kPHashDownsampleHeight = 288;
+const int kPHashBlockSize = 6;
+
+}  // namespace
+
+// A QuantizedColor takes the highest 3 bits of R, G, and B, and concatenates
+// them.
+QuantizedColor SkColorToQuantizedColor(SkColor color) {
+  return (SkColorGetR(color) >> 5) << 6 | (SkColorGetG(color) >> 5) << 3 |
+         (SkColorGetB(color) >> 5);
+}
+
+int GetQuantizedR(QuantizedColor color) {
+  return color >> 6;
+}
+
+int GetQuantizedG(QuantizedColor color) {
+  return (color >> 3) & 7;
+}
+
+int GetQuantizedB(QuantizedColor color) {
+  return color & 7;
+}
+
+bool GetHistogramForImage(const SkBitmap& image,
+                          VisualFeatures::ColorHistogram* histogram) {
+  if (image.drawsNothing())
+    return false;
+
+  std::unordered_map<QuantizedColor, int> color_to_count;
+  std::unordered_map<QuantizedColor, double> color_to_total_x;
+  std::unordered_map<QuantizedColor, double> color_to_total_y;
+  for (int x = 0; x < image.width(); x++) {
+    for (int y = 0; y < image.height(); y++) {
+      QuantizedColor color = SkColorToQuantizedColor(image.getColor(x, y));
+      color_to_count[color]++;
+      color_to_total_x[color] += static_cast<float>(x) / image.width();
+      color_to_total_y[color] += static_cast<float>(y) / image.height();
+    }
+  }
+
+  int normalization_factor;
+  if (!base::CheckMul(image.width(), image.height())
+           .AssignIfValid(&normalization_factor))
+    return false;
+
+  for (const auto& entry : color_to_count) {
+    const QuantizedColor& color = entry.first;
+    int count = entry.second;
+
+    VisualFeatures::ColorHistogramBin* bin = histogram->add_bins();
+    bin->set_weight(static_cast<float>(count) / normalization_factor);
+    bin->set_centroid_x(color_to_total_x[color] / count);
+    bin->set_centroid_y(color_to_total_y[color] / count);
+    bin->set_quantized_r(GetQuantizedR(color));
+    bin->set_quantized_g(GetQuantizedG(color));
+    bin->set_quantized_b(GetQuantizedB(color));
+  }
+
+  return true;
+}
+
+bool GetBlurredImage(const SkBitmap& image,
+                     VisualFeatures::BlurredImage* blurred_image) {
+  if (image.drawsNothing())
+    return false;
+
+  // Use the Rec. 2020 color space, in case the user input is wide-gamut.
+  sk_sp<SkColorSpace> rec2020 = SkColorSpace::MakeRGB(
+      {2.22222f, 0.909672f, 0.0903276f, 0.222222f, 0.0812429f, 0, 0},
+      SkNamedGamut::kRec2020);
+
+  // We scale down twice, once with medium quality, then with a block mean
+  // average to be consistent with the backend.
+  // TODO(drubery): Investigate whether this is necessary for performance or
+  // not.
+  SkImageInfo downsampled_info =
+      SkImageInfo::Make(kPHashDownsampleWidth, kPHashDownsampleHeight,
+                        SkColorType::kRGBA_8888_SkColorType,
+                        SkAlphaType::kUnpremul_SkAlphaType, rec2020);
+  SkBitmap downsampled;
+  if (!downsampled.tryAllocPixels(downsampled_info))
+    return false;
+  image.pixmap().scalePixels(downsampled.pixmap(),
+                             SkFilterQuality::kMedium_SkFilterQuality);
+
+  std::unique_ptr<SkBitmap> blurred =
+      BlockMeanAverage(downsampled, kPHashBlockSize);
+
+  blurred_image->set_width(blurred->width());
+  blurred_image->set_height(blurred->height());
+  blurred_image->clear_data();
+
+  const uint32_t* rgba = blurred->getAddr32(0, 0);
+  for (int i = 0; i < blurred->width() * blurred->height(); i++) {
+    // Data is stored in BGR order.
+    *blurred_image->mutable_data() += static_cast<char>((rgba[i] >> 0) & 0xff);
+    *blurred_image->mutable_data() += static_cast<char>((rgba[i] >> 8) & 0xff);
+    *blurred_image->mutable_data() += static_cast<char>((rgba[i] >> 16) & 0xff);
+  }
+
+  return true;
+}
+
+std::unique_ptr<SkBitmap> BlockMeanAverage(const SkBitmap& image,
+                                           int block_size) {
+  // Compute the number of blocks in the target image, rounding up to account
+  // for partial blocks.
+  int num_blocks_high =
+      std::ceil(static_cast<float>(image.height()) / block_size);
+  int num_blocks_wide =
+      std::ceil(static_cast<float>(image.width()) / block_size);
+
+  SkImageInfo target_info = SkImageInfo::Make(
+      num_blocks_wide, num_blocks_high, SkColorType::kRGBA_8888_SkColorType,
+      SkAlphaType::kUnpremul_SkAlphaType, image.refColorSpace());
+  auto target = std::make_unique<SkBitmap>();
+  if (!target->tryAllocPixels(target_info))
+    return target;
+
+  for (int block_x = 0; block_x < num_blocks_wide; block_x++) {
+    for (int block_y = 0; block_y < num_blocks_high; block_y++) {
+      int r_total = 0, g_total = 0, b_total = 0, sample_count = 0;
+
+      // Compute boundary for the current block, taking into account the
+      // possibility of partial blocks near the edges.
+      int x_start = block_x * block_size;
+      int x_end = std::min(x_start + block_size, image.width());
+
+      int y_start = block_y * block_size;
+      int y_end = std::min(y_start + block_size, image.height());
+      for (int i = x_start; i < x_end; i++) {
+        for (int j = y_start; j < y_end; j++) {
+          r_total += SkColorGetR(image.getColor(i, j));
+          g_total += SkColorGetG(image.getColor(i, j));
+          b_total += SkColorGetB(image.getColor(i, j));
+          sample_count++;
+        }
+      }
+
+      int r_mean = r_total / sample_count;
+      int g_mean = g_total / sample_count;
+      int b_mean = b_total / sample_count;
+
+      *target->getAddr32(block_x, block_y) =
+          (255 << 24) | (b_mean << 16) | (g_mean << 8) | (r_mean << 0);
+    }
+  }
+
+  return target;
+}
+
+}  // namespace visual_utils
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/content/password_protection/visual_utils.h b/components/safe_browsing/content/password_protection/visual_utils.h
new file mode 100644
index 0000000..6d2d24f
--- /dev/null
+++ b/components/safe_browsing/content/password_protection/visual_utils.h
@@ -0,0 +1,45 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CONTENT_PASSWORD_PROTECTION_VISUAL_UTILS_H_
+#define COMPONENTS_SAFE_BROWSING_CONTENT_PASSWORD_PROTECTION_VISUAL_UTILS_H_
+
+#include <string>
+
+#include "components/safe_browsing/core/proto/csd.pb.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+
+namespace safe_browsing {
+namespace visual_utils {
+
+using QuantizedColor = uint32_t;
+
+// Utility methods for working with QuantizedColors.
+QuantizedColor SkColorToQuantizedColor(SkColor color);
+int GetQuantizedR(QuantizedColor color);
+int GetQuantizedG(QuantizedColor color);
+int GetQuantizedB(QuantizedColor color);
+
+// Computes the color histogram for the image. This buckets the pixels according
+// to their QuantizedColor, then reports their weight and centroid.
+bool GetHistogramForImage(const SkBitmap& image,
+                          VisualFeatures::ColorHistogram* histogram);
+
+// Computes the BlurredImage for the given input image. This involves
+// downsampling the image to a certain fixed resolution, then blurring
+// by taking an average over fixed-size blocks of pixels.
+bool GetBlurredImage(const SkBitmap& image,
+                     VisualFeatures::BlurredImage* blurred_image);
+
+// Downsizes an image by averaging all the pixels in the source image that
+// contribute to the target image. Groups pixels into squares of size
+// |block_size|, potentially with partial blocks at the edge. The output
+// image has pixels the average of the pixels in each block.
+std::unique_ptr<SkBitmap> BlockMeanAverage(const SkBitmap& image,
+                                           int block_size);
+
+}  // namespace visual_utils
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CONTENT_PASSWORD_PROTECTION_VISUAL_UTILS_H_
diff --git a/components/safe_browsing/content/password_protection/visual_utils_unittest.cc b/components/safe_browsing/content/password_protection/visual_utils_unittest.cc
new file mode 100644
index 0000000..140c9788
--- /dev/null
+++ b/components/safe_browsing/content/password_protection/visual_utils_unittest.cc
@@ -0,0 +1,256 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/content/password_protection/visual_utils.h"
+
+#include "base/test/test_discardable_memory_allocator.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace safe_browsing {
+namespace visual_utils {
+
+namespace {
+
+// Pixel value constants, all in BGR order.
+const unsigned int kWhite = 0xffffffff;
+const unsigned int kBlack = 0xff000000;
+const unsigned int kRed = 0xff0000ff;
+const unsigned int kGreen = 0xff00ff00;
+const unsigned int kBlue = 0xffff0000;
+
+}  // namespace
+
+using ::testing::FloatEq;
+
+class VisualUtilsTest : public testing::Test {
+ protected:
+  void SetUp() override {
+    base::DiscardableMemoryAllocator::SetInstance(&test_allocator_);
+
+    sk_sp<SkColorSpace> rec2020 = SkColorSpace::MakeRGB(
+        {2.22222f, 0.909672f, 0.0903276f, 0.222222f, 0.0812429f, 0, 0},
+        SkNamedGamut::kRec2020);
+    SkImageInfo bitmap_info =
+        SkImageInfo::Make(1000, 1000, SkColorType::kRGBA_8888_SkColorType,
+                          SkAlphaType::kUnpremul_SkAlphaType, rec2020);
+
+    ASSERT_TRUE(bitmap_.tryAllocPixels(bitmap_info));
+  }
+
+  void TearDown() override {
+    base::DiscardableMemoryAllocator::SetInstance(nullptr);
+  }
+
+  // A test bitmap to work with. Initialized to be 1000x1000 in the Rec 2020
+  // color space.
+  SkBitmap bitmap_;
+
+ private:
+  // A DiscardableMemoryAllocator is needed for certain Skia operations.
+  base::TestDiscardableMemoryAllocator test_allocator_;
+};
+
+TEST_F(VisualUtilsTest, TestSkColorToQuantizedColor) {
+  // Test quantization
+  EXPECT_EQ(SkColorToQuantizedColor(SkColorSetRGB(0, 0, 31)), 0u);
+  EXPECT_EQ(SkColorToQuantizedColor(SkColorSetRGB(0, 0, 32)), 1u);
+  EXPECT_EQ(SkColorToQuantizedColor(SkColorSetRGB(0, 31, 0)), 0u);
+  EXPECT_EQ(SkColorToQuantizedColor(SkColorSetRGB(0, 32, 0)), 8u);
+  EXPECT_EQ(SkColorToQuantizedColor(SkColorSetRGB(31, 0, 0)), 0u);
+  EXPECT_EQ(SkColorToQuantizedColor(SkColorSetRGB(32, 0, 0)), 64u);
+
+  // Test composition of RGB quantized values
+  EXPECT_EQ(SkColorToQuantizedColor(SkColorSetRGB(0, 0, 0)), 0u);
+  EXPECT_EQ(SkColorToQuantizedColor(SkColorSetRGB(0, 0, 255)), 7u);
+  EXPECT_EQ(SkColorToQuantizedColor(SkColorSetRGB(0, 255, 255)), 63u);
+  EXPECT_EQ(SkColorToQuantizedColor(SkColorSetRGB(255, 255, 255)), 511u);
+}
+
+TEST_F(VisualUtilsTest, GetQuantizedR) {
+  EXPECT_EQ(GetQuantizedR(0), 0);
+  EXPECT_EQ(GetQuantizedR(64), 1);
+  EXPECT_EQ(GetQuantizedR(448), 7);
+}
+
+TEST_F(VisualUtilsTest, GetQuantizedG) {
+  EXPECT_EQ(GetQuantizedG(0), 0);
+  EXPECT_EQ(GetQuantizedG(8), 1);
+  EXPECT_EQ(GetQuantizedG(56), 7);
+}
+
+TEST_F(VisualUtilsTest, GetQuantizedB) {
+  EXPECT_EQ(GetQuantizedB(0), 0);
+  EXPECT_EQ(GetQuantizedB(1), 1);
+  EXPECT_EQ(GetQuantizedB(7), 7);
+}
+
+TEST_F(VisualUtilsTest, GetHistogramForImageWhite) {
+  VisualFeatures::ColorHistogram histogram;
+  SkBitmap bitmap;
+
+  // Draw white over half the image
+  for (int x = 0; x < 1000; x++)
+    for (int y = 0; y < 1000; y++)
+      *bitmap_.getAddr32(x, y) = kWhite;
+
+  ASSERT_TRUE(GetHistogramForImage(bitmap_, &histogram));
+  ASSERT_EQ(histogram.bins_size(), 1);
+  EXPECT_THAT(histogram.bins(0).centroid_x(),
+              FloatEq(0.4995));  // All pixels are the same color, so centroid_x
+                                 // is (0+1+...+999)/1000/1000 = 0.4995
+  EXPECT_THAT(histogram.bins(0).centroid_y(), FloatEq(0.4995));
+  EXPECT_EQ(histogram.bins(0).quantized_r(), 7);
+  EXPECT_EQ(histogram.bins(0).quantized_g(), 7);
+  EXPECT_EQ(histogram.bins(0).quantized_b(), 7);
+  EXPECT_THAT(histogram.bins(0).weight(), FloatEq(1.0));
+}
+
+TEST_F(VisualUtilsTest, GetHistogramForImageHalfWhiteHalfBlack) {
+  VisualFeatures::ColorHistogram histogram;
+
+  // Draw white over half the image
+  for (int x = 0; x < 1000; x++)
+    for (int y = 0; y < 500; y++)
+      *bitmap_.getAddr32(x, y) = kWhite;
+
+  // Draw black over half the image.
+  for (int x = 0; x < 1000; x++)
+    for (int y = 500; y < 1000; y++)
+      *bitmap_.getAddr32(x, y) = kBlack;
+
+  ASSERT_TRUE(GetHistogramForImage(bitmap_, &histogram));
+  ASSERT_EQ(histogram.bins_size(), 2);
+
+  EXPECT_THAT(histogram.bins(0).centroid_x(), FloatEq(0.4995));
+  EXPECT_THAT(histogram.bins(0).centroid_y(), FloatEq(0.7495));
+  EXPECT_EQ(histogram.bins(0).quantized_r(), 0);
+  EXPECT_EQ(histogram.bins(0).quantized_g(), 0);
+  EXPECT_EQ(histogram.bins(0).quantized_b(), 0);
+  EXPECT_THAT(histogram.bins(0).weight(), FloatEq(0.5));
+
+  EXPECT_THAT(histogram.bins(1).centroid_x(), FloatEq(0.4995));
+  EXPECT_THAT(histogram.bins(1).centroid_y(), FloatEq(0.2495));
+  EXPECT_EQ(histogram.bins(1).quantized_r(), 7);
+  EXPECT_EQ(histogram.bins(1).quantized_g(), 7);
+  EXPECT_EQ(histogram.bins(1).quantized_b(), 7);
+  EXPECT_THAT(histogram.bins(1).weight(), FloatEq(0.5));
+}
+
+TEST_F(VisualUtilsTest, BlurImageWhite) {
+  VisualFeatures::BlurredImage blurred;
+
+  // Draw white over the image
+  for (int x = 0; x < 1000; x++)
+    for (int y = 0; y < 1000; y++)
+      *bitmap_.getAddr32(x, y) = kWhite;
+
+  ASSERT_TRUE(GetBlurredImage(bitmap_, &blurred));
+  ASSERT_EQ(48, blurred.width());
+  ASSERT_EQ(48, blurred.height());
+  ASSERT_EQ(3u * 48u * 48u, blurred.data().size());
+  for (size_t i = 0; i < 48u * 48u; i++) {
+    EXPECT_EQ('\xff', blurred.data()[3 * i]);
+    EXPECT_EQ('\xff', blurred.data()[3 * i + 1]);
+    EXPECT_EQ('\xff', blurred.data()[3 * i + 2]);
+  }
+}
+
+TEST_F(VisualUtilsTest, BlurImageRed) {
+  VisualFeatures::BlurredImage blurred;
+
+  // Draw red over the image.
+  for (int x = 0; x < 1000; x++)
+    for (int y = 0; y < 1000; y++)
+      *bitmap_.getAddr32(x, y) = kRed;
+
+  ASSERT_TRUE(GetBlurredImage(bitmap_, &blurred));
+  ASSERT_EQ(48, blurred.width());
+  ASSERT_EQ(48, blurred.height());
+  ASSERT_EQ(3u * 48u * 48u, blurred.data().size());
+  for (size_t i = 0; i < 48u * 48u; i++) {
+    EXPECT_EQ('\xff', blurred.data()[3 * i]);
+    EXPECT_EQ('\x00', blurred.data()[3 * i + 1]);
+    EXPECT_EQ('\x00', blurred.data()[3 * i + 2]);
+  }
+}
+
+TEST_F(VisualUtilsTest, BlurImageHalfWhiteHalfBlack) {
+  VisualFeatures::BlurredImage blurred;
+
+  // Draw black over half the image.
+  for (int x = 0; x < 1000; x++)
+    for (int y = 0; y < 500; y++)
+      *bitmap_.getAddr32(x, y) = kBlack;
+
+  // Draw white over half the image
+  for (int x = 0; x < 1000; x++)
+    for (int y = 500; y < 1000; y++)
+      *bitmap_.getAddr32(x, y) = kWhite;
+
+  ASSERT_TRUE(GetBlurredImage(bitmap_, &blurred));
+  ASSERT_EQ(48, blurred.width());
+  ASSERT_EQ(48, blurred.height());
+  ASSERT_EQ(3u * 48u * 48u, blurred.data().size());
+  // The middle blocks may have been blurred to something between white and
+  // black, so only verify the first 22 and last 22 rows.
+  for (size_t i = 0; i < 22u * 48u; i++) {
+    EXPECT_EQ('\x00', blurred.data()[3 * i]);
+    EXPECT_EQ('\x00', blurred.data()[3 * i + 1]);
+    EXPECT_EQ('\x00', blurred.data()[3 * i + 2]);
+  }
+
+  for (size_t i = 26u * 48u; i < 48u * 48u; i++) {
+    EXPECT_EQ('\xff', blurred.data()[3 * i]);
+    EXPECT_EQ('\xff', blurred.data()[3 * i + 1]);
+    EXPECT_EQ('\xff', blurred.data()[3 * i + 2]);
+  }
+}
+
+TEST_F(VisualUtilsTest, BlockMeanAverageOneBlock) {
+  // Draw black over half the image.
+  for (int x = 0; x < 1000; x++)
+    for (int y = 0; y < 500; y++)
+      *bitmap_.getAddr32(x, y) = kBlack;
+
+  // Draw white over half the image
+  for (int x = 0; x < 1000; x++)
+    for (int y = 500; y < 1000; y++)
+      *bitmap_.getAddr32(x, y) = kWhite;
+
+  std::unique_ptr<SkBitmap> blocks = BlockMeanAverage(bitmap_, 1000);
+  ASSERT_EQ(1, blocks->width());
+  ASSERT_EQ(1, blocks->height());
+  EXPECT_EQ(blocks->getColor(0, 0), SkColorSetRGB(127, 127, 127));
+}
+
+TEST_F(VisualUtilsTest, BlockMeanAveragePartialBlocks) {
+  // Draw a white, red, green, and blue box with the expected block sizes.
+  for (int x = 0; x < 600; x++)
+    for (int y = 0; y < 600; y++)
+      *bitmap_.getAddr32(x, y) = kWhite;
+
+  for (int x = 600; x < 1000; x++)
+    for (int y = 0; y < 600; y++)
+      *bitmap_.getAddr32(x, y) = kRed;
+
+  for (int x = 0; x < 600; x++)
+    for (int y = 600; y < 1000; y++)
+      *bitmap_.getAddr32(x, y) = kGreen;
+
+  for (int x = 600; x < 1000; x++)
+    for (int y = 600; y < 1000; y++)
+      *bitmap_.getAddr32(x, y) = kBlue;
+
+  std::unique_ptr<SkBitmap> blocks = BlockMeanAverage(bitmap_, 600);
+  ASSERT_EQ(2, blocks->width());
+  ASSERT_EQ(2, blocks->height());
+  EXPECT_EQ(*blocks->getAddr32(0, 0), kWhite);
+  EXPECT_EQ(*blocks->getAddr32(1, 0), kRed);
+  EXPECT_EQ(*blocks->getAddr32(0, 1), kGreen);
+  EXPECT_EQ(*blocks->getAddr32(1, 1), kBlue);
+}
+
+}  // namespace visual_utils
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/content/renderer/BUILD.gn b/components/safe_browsing/content/renderer/BUILD.gn
new file mode 100644
index 0000000..29701c7
--- /dev/null
+++ b/components/safe_browsing/content/renderer/BUILD.gn
@@ -0,0 +1,66 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/features.gni")
+import("//components/safe_browsing/buildflags.gni")
+
+source_set("renderer") {
+  if (safe_browsing_mode != 0) {
+    sources = [
+      "threat_dom_details.cc",
+      "threat_dom_details.h",
+    ]
+    deps = [
+      "//base",
+      "//components/safe_browsing/core:features",
+      "//components/safe_browsing/core/common:common",
+      "//content/public/renderer",
+      "//ipc",
+      "//third_party/blink/public:blink",
+      "//url/ipc:url_ipc",
+    ]
+  }
+}
+
+source_set("throttles") {
+  sources = [
+    "renderer_url_loader_throttle.cc",
+    "renderer_url_loader_throttle.h",
+    "websocket_sb_handshake_throttle.cc",
+    "websocket_sb_handshake_throttle.h",
+  ]
+
+  deps = [
+    "//base:base",
+    "//components/safe_browsing/core:features",
+    "//components/safe_browsing/core/common:common",
+    "//components/safe_browsing/core/common:interfaces",
+    "//content/public/common:common",
+    "//content/public/renderer:renderer",
+    "//ipc",
+    "//net",
+    "//services/service_manager/public/cpp:cpp",
+    "//third_party/blink/public:blink",
+    "//url:url",
+  ]
+}
+
+source_set("websocket_sb_handshake_throttle_unittest") {
+  testonly = true
+  sources = [
+    "websocket_sb_handshake_throttle_unittest.cc",
+  ]
+
+  deps = [
+    ":throttles",
+    "//base:base",
+    "//base/test:test_support",
+    "//components/safe_browsing/core/common:interfaces",
+    "//content/public/common",
+    "//ipc",
+    "//testing/gmock",
+    "//testing/gtest",
+    "//third_party/blink/public:blink",
+  ]
+}
diff --git a/components/safe_browsing/renderer/DEPS b/components/safe_browsing/content/renderer/DEPS
similarity index 100%
rename from components/safe_browsing/renderer/DEPS
rename to components/safe_browsing/content/renderer/DEPS
diff --git a/components/safe_browsing/content/renderer/renderer_url_loader_throttle.cc b/components/safe_browsing/content/renderer/renderer_url_loader_throttle.cc
new file mode 100644
index 0000000..011933e
--- /dev/null
+++ b/components/safe_browsing/content/renderer/renderer_url_loader_throttle.cc
@@ -0,0 +1,211 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/content/renderer/renderer_url_loader_throttle.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/trace_event/trace_event.h"
+#include "components/safe_browsing/core/common/safebrowsing_constants.h"
+#include "components/safe_browsing/core/common/utils.h"
+#include "net/url_request/redirect_info.h"
+#include "services/network/public/cpp/resource_request.h"
+
+namespace safe_browsing {
+
+RendererURLLoaderThrottle::RendererURLLoaderThrottle(
+    mojom::SafeBrowsing* safe_browsing,
+    int render_frame_id)
+    : safe_browsing_(safe_browsing), render_frame_id_(render_frame_id) {}
+
+RendererURLLoaderThrottle::~RendererURLLoaderThrottle() {
+  if (deferred_)
+    TRACE_EVENT_ASYNC_END0("safe_browsing", "Deferred", this);
+}
+
+void RendererURLLoaderThrottle::DetachFromCurrentSequence() {
+  // Create a new pipe to the SafeBrowsing interface that can be bound to a
+  // different sequence.
+  safe_browsing_->Clone(
+      safe_browsing_pending_remote_.InitWithNewPipeAndPassReceiver());
+  safe_browsing_ = nullptr;
+}
+
+void RendererURLLoaderThrottle::WillStartRequest(
+    network::ResourceRequest* request,
+    bool* defer) {
+  DCHECK_EQ(0u, pending_checks_);
+  DCHECK(!blocked_);
+  DCHECK(!url_checker_);
+
+  if (safe_browsing_pending_remote_.is_valid()) {
+    // Bind the pipe created in DetachFromCurrentSequence to the current
+    // sequence.
+    safe_browsing_remote_.Bind(std::move(safe_browsing_pending_remote_));
+    safe_browsing_ = safe_browsing_remote_.get();
+  }
+
+  original_url_ = request->url;
+  pending_checks_++;
+  // Use a weak pointer to self because |safe_browsing_| may not be owned by
+  // this object.
+  net::HttpRequestHeaders headers;
+  headers.CopyFrom(request->headers);
+  safe_browsing_->CreateCheckerAndCheck(
+      render_frame_id_, url_checker_.BindNewPipeAndPassReceiver(), request->url,
+      request->method, headers, request->load_flags,
+      static_cast<content::ResourceType>(request->resource_type),
+      request->has_user_gesture, request->originated_from_service_worker,
+      base::BindOnce(&RendererURLLoaderThrottle::OnCheckUrlResult,
+                     weak_factory_.GetWeakPtr()));
+  safe_browsing_ = nullptr;
+
+  url_checker_.set_disconnect_handler(base::BindOnce(
+      &RendererURLLoaderThrottle::OnMojoDisconnect, base::Unretained(this)));
+}
+
+void RendererURLLoaderThrottle::WillRedirectRequest(
+    net::RedirectInfo* redirect_info,
+    const network::mojom::URLResponseHead& /* response_head */,
+    bool* /* defer */,
+    std::vector<std::string>* /* to_be_removed_headers */,
+    net::HttpRequestHeaders* /* modified_headers */) {
+  // If |blocked_| is true, the resource load has been canceled and there
+  // shouldn't be such a notification.
+  DCHECK(!blocked_);
+
+  if (!url_checker_) {
+    DCHECK_EQ(0u, pending_checks_);
+    return;
+  }
+
+  pending_checks_++;
+  url_checker_->CheckUrl(
+      redirect_info->new_url, redirect_info->new_method,
+      base::BindOnce(&RendererURLLoaderThrottle::OnCheckUrlResult,
+                     base::Unretained(this)));
+}
+
+void RendererURLLoaderThrottle::WillProcessResponse(
+    const GURL& response_url,
+    network::mojom::URLResponseHead* response_head,
+    bool* defer) {
+  // If |blocked_| is true, the resource load has been canceled and there
+  // shouldn't be such a notification.
+  DCHECK(!blocked_);
+
+  if (pending_checks_ == 0)
+    return;
+
+  DCHECK(!deferred_);
+  deferred_ = true;
+  defer_start_time_ = base::TimeTicks::Now();
+  *defer = true;
+  TRACE_EVENT_ASYNC_BEGIN1("safe_browsing", "Deferred", this, "original_url",
+                           original_url_.spec());
+}
+
+void RendererURLLoaderThrottle::OnCompleteCheck(bool proceed,
+                                                bool showed_interstitial) {
+  OnCompleteCheckInternal(true /* slow_check */, proceed, showed_interstitial);
+}
+
+void RendererURLLoaderThrottle::OnCheckUrlResult(
+    mojo::PendingReceiver<mojom::UrlCheckNotifier> slow_check_notifier,
+    bool proceed,
+    bool showed_interstitial) {
+  // When this is the callback of safe_browsing_->CreateCheckerAndCheck(), it is
+  // possible that we get here after a check with |url_checker_| has completed
+  // and blocked the request.
+  if (blocked_ || !url_checker_)
+    return;
+
+  if (!slow_check_notifier.is_valid()) {
+    OnCompleteCheckInternal(false /* slow_check */, proceed,
+                            showed_interstitial);
+    return;
+  }
+
+  pending_slow_checks_++;
+  // Pending slow checks indicate that the resource may be unsafe. In that case,
+  // pause reading response body from network to minimize the chance of
+  // processing unsafe contents (e.g., writing unsafe contents into cache),
+  // until we get the results. According to the results, we may resume reading
+  // or cancel the resource load.
+  if (pending_slow_checks_ == 1)
+    delegate_->PauseReadingBodyFromNet();
+
+  if (!notifier_receivers_) {
+    notifier_receivers_ =
+        std::make_unique<mojo::ReceiverSet<mojom::UrlCheckNotifier>>();
+  }
+  notifier_receivers_->Add(this, std::move(slow_check_notifier));
+}
+
+void RendererURLLoaderThrottle::OnCompleteCheckInternal(
+    bool slow_check,
+    bool proceed,
+    bool showed_interstitial) {
+  DCHECK(!blocked_);
+  DCHECK(url_checker_);
+
+  DCHECK_LT(0u, pending_checks_);
+  pending_checks_--;
+
+  if (slow_check) {
+    DCHECK_LT(0u, pending_slow_checks_);
+    pending_slow_checks_--;
+  }
+
+  user_action_involved_ = user_action_involved_ || showed_interstitial;
+  // If the resource load is currently deferred and is going to exit that state
+  // (either being cancelled or resumed), record the total delay.
+  if (deferred_ && (!proceed || pending_checks_ == 0))
+    total_delay_ = base::TimeTicks::Now() - defer_start_time_;
+
+  if (proceed) {
+    if (pending_slow_checks_ == 0 && slow_check)
+      delegate_->ResumeReadingBodyFromNet();
+
+    if (pending_checks_ == 0 && deferred_) {
+      deferred_ = false;
+      TRACE_EVENT_ASYNC_END0("safe_browsing", "Deferred", this);
+      delegate_->Resume();
+    }
+  } else {
+    blocked_ = true;
+
+    url_checker_.reset();
+    notifier_receivers_.reset();
+    pending_checks_ = 0;
+    pending_slow_checks_ = 0;
+    delegate_->CancelWithError(GetNetErrorCodeForSafeBrowsing(),
+                               kCustomCancelReasonForURLLoader);
+  }
+}
+
+void RendererURLLoaderThrottle::OnMojoDisconnect() {
+  DCHECK(!blocked_);
+
+  // If a service-side disconnect happens, treat all URLs as if they are safe.
+  url_checker_.reset();
+  notifier_receivers_.reset();
+
+  pending_checks_ = 0;
+
+  if (pending_slow_checks_ > 0) {
+    pending_slow_checks_ = 0;
+    delegate_->ResumeReadingBodyFromNet();
+  }
+
+  if (deferred_) {
+    total_delay_ = base::TimeTicks::Now() - defer_start_time_;
+
+    deferred_ = false;
+    TRACE_EVENT_ASYNC_END0("safe_browsing", "Deferred", this);
+    delegate_->Resume();
+  }
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/content/renderer/renderer_url_loader_throttle.h b/components/safe_browsing/content/renderer/renderer_url_loader_throttle.h
new file mode 100644
index 0000000..20eeb0a9
--- /dev/null
+++ b/components/safe_browsing/content/renderer/renderer_url_loader_throttle.h
@@ -0,0 +1,100 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CONTENT_RENDERER_RENDERER_URL_LOADER_THROTTLE_H_
+#define COMPONENTS_SAFE_BROWSING_CONTENT_RENDERER_RENDERER_URL_LOADER_THROTTLE_H_
+
+#include <memory>
+
+#include "base/memory/weak_ptr.h"
+#include "components/safe_browsing/core/common/safe_browsing.mojom.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "third_party/blink/public/common/loader/url_loader_throttle.h"
+#include "url/gurl.h"
+
+namespace safe_browsing {
+
+// RendererURLLoaderThrottle is used in renderer processes to query
+// SafeBrowsing and determine whether a URL and its redirect URLs are safe to
+// load. It defers response processing until all URL checks are completed;
+// cancels the load if any URLs turn out to be bad.
+class RendererURLLoaderThrottle : public blink::URLLoaderThrottle,
+                                  public mojom::UrlCheckNotifier {
+ public:
+  // |safe_browsing| must stay alive until WillStartRequest() (if it is called)
+  // or the end of this object.
+  // |render_frame_id| is used for displaying SafeBrowsing UI when necessary.
+  RendererURLLoaderThrottle(mojom::SafeBrowsing* safe_browsing,
+                            int render_frame_id);
+  ~RendererURLLoaderThrottle() override;
+
+ private:
+  // blink::URLLoaderThrottle implementation.
+  void DetachFromCurrentSequence() override;
+  void WillStartRequest(network::ResourceRequest* request,
+                        bool* defer) override;
+  void WillRedirectRequest(net::RedirectInfo* redirect_info,
+                           const network::mojom::URLResponseHead& response_head,
+                           bool* defer,
+                           std::vector<std::string>* to_be_removed_headers,
+                           net::HttpRequestHeaders* modified_headers) override;
+  void WillProcessResponse(const GURL& response_url,
+                           network::mojom::URLResponseHead* response_head,
+                           bool* defer) override;
+
+  // mojom::UrlCheckNotifier implementation.
+  void OnCompleteCheck(bool proceed, bool showed_interstitial) override;
+
+  void OnCheckUrlResult(
+      mojo::PendingReceiver<mojom::UrlCheckNotifier> slow_check_notifier,
+      bool proceed,
+      bool showed_interstitial);
+
+  // Called by the two methods above.
+  // |slow_check| indicates whether it reports the result of a slow check.
+  // (Please see comments in safe_browsing.mojom for what slow check means).
+  void OnCompleteCheckInternal(bool slow_check,
+                               bool proceed,
+                               bool showed_interstitial);
+
+  void OnMojoDisconnect();
+
+  mojom::SafeBrowsing* safe_browsing_;
+  const int render_frame_id_;
+
+  // These fields hold the connection to this instance's private connection to
+  // the Safe Browsing service if DetachFromCurrentThread has been called.
+  mojo::PendingRemote<mojom::SafeBrowsing> safe_browsing_pending_remote_;
+  mojo::Remote<mojom::SafeBrowsing> safe_browsing_remote_;
+
+  mojo::Remote<mojom::SafeBrowsingUrlChecker> url_checker_;
+
+  size_t pending_checks_ = 0;
+  size_t pending_slow_checks_ = 0;
+  bool blocked_ = false;
+
+  // The time when we started deferring the request.
+  base::TimeTicks defer_start_time_;
+  bool deferred_ = false;
+
+  // The total delay caused by SafeBrowsing deferring the resource load.
+  base::TimeDelta total_delay_;
+  // Whether the interstitial page has been shown and therefore user action has
+  // been involved.
+  bool user_action_involved_ = false;
+
+  std::unique_ptr<mojo::ReceiverSet<mojom::UrlCheckNotifier>>
+      notifier_receivers_;
+
+  GURL original_url_;
+
+  base::WeakPtrFactory<RendererURLLoaderThrottle> weak_factory_{this};
+};
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CONTENT_RENDERER_RENDERER_URL_LOADER_THROTTLE_H_
diff --git a/components/safe_browsing/content/renderer/threat_dom_details.cc b/components/safe_browsing/content/renderer/threat_dom_details.cc
new file mode 100644
index 0000000..fd6879e
--- /dev/null
+++ b/components/safe_browsing/content/renderer/threat_dom_details.cc
@@ -0,0 +1,347 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/content/renderer/threat_dom_details.h"
+
+#include <algorithm>
+#include <map>
+#include <string>
+#include <unordered_set>
+
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "base/metrics/field_trial_params.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_split.h"
+#include "base/strings/stringprintf.h"
+#include "components/safe_browsing/core/features.h"
+#include "content/public/renderer/render_frame.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/public/web/web_document.h"
+#include "third_party/blink/public/web/web_element.h"
+#include "third_party/blink/public/web/web_element_collection.h"
+#include "third_party/blink/public/web/web_frame.h"
+#include "third_party/blink/public/web/web_local_frame.h"
+
+namespace safe_browsing {
+
+// A map for keeping track of the identity of DOM Elements, used to generate
+// unique IDs for each element and lookup elements IDs by parent Element, to
+// maintain proper parent/child relationships.
+// They key is a WebNode from the DOM, which is basically a pointer so can be
+// copied into the map when inserting new elements.
+// The values are indices into the resource vector, and are used to retrieve IPC
+// messages generated by ThreatDOMDetails.
+using ElementToNodeMap = std::map<blink::WebNode, size_t>;
+
+// The name of the param containing the tags and attributes list.
+const char kTagAndAttributeParamName[] = "tag_attribute_csv";
+
+namespace {
+
+// Predicate used to search |tag_and_attributes_list_| by tag_name.
+class TagNameIs {
+ public:
+  explicit TagNameIs(const std::string& tag) : tag_(tag) {}
+  bool operator()(const TagAndAttributesItem& tag_and_attribute) {
+    return tag_ == tag_and_attribute.tag_name;
+  }
+
+ private:
+  std::string tag_;
+};
+
+void GetDefaultTagAndAttributeList(
+    std::vector<TagAndAttributesItem>* tag_and_attributes_list) {
+  tag_and_attributes_list->clear();
+  // These entries must be sorted by tag name.
+  bool should_capture_js =
+      base::FeatureList::IsEnabled(kCaptureInlineJavascriptForGoogleAds);
+  if (should_capture_js)
+    tag_and_attributes_list->push_back(TagAndAttributesItem("a", {"onclick"}));
+  // These entries must be sorted by tag name.
+  // These tags are related to identifying Google ads.
+  tag_and_attributes_list->push_back(
+      TagAndAttributesItem("div", {"data-google-query-id", "id"}));
+  tag_and_attributes_list->push_back(TagAndAttributesItem("iframe", {"id"}));
+  if (should_capture_js)
+    tag_and_attributes_list->push_back(
+        TagAndAttributesItem("img", {"onclick"}));
+}
+
+void ParseTagAndAttributeParams(
+    std::vector<TagAndAttributesItem>* tag_and_attributes_list) {
+  DCHECK(tag_and_attributes_list);
+  // If the feature is disabled we just use the default list. Otherwise the list
+  // from the Finch param will be the one used.
+  if (!base::FeatureList::IsEnabled(kThreatDomDetailsTagAndAttributeFeature)) {
+    GetDefaultTagAndAttributeList(tag_and_attributes_list);
+    return;
+  }
+  tag_and_attributes_list->clear();
+  const std::string& tag_attribute_csv_param =
+      base::GetFieldTrialParamValueByFeature(
+          kThreatDomDetailsTagAndAttributeFeature, kTagAndAttributeParamName);
+  if (tag_attribute_csv_param.empty()) {
+    return;
+  }
+
+  std::vector<std::string> split =
+      base::SplitString(tag_attribute_csv_param, ",", base::TRIM_WHITESPACE,
+                        base::SPLIT_WANT_NONEMPTY);
+  // If we don't have the right number of pairs in the csv then don't bother
+  // parsing further.
+  if (split.size() % 2 != 0) {
+    return;
+  }
+  for (size_t i = 0; i < split.size(); i += 2) {
+    const std::string& tag_name = split[i];
+    const std::string& attribute = split[i + 1];
+    auto item_iter =
+        std::find_if(tag_and_attributes_list->begin(),
+                     tag_and_attributes_list->end(), TagNameIs(tag_name));
+    if (item_iter == tag_and_attributes_list->end()) {
+      TagAndAttributesItem item;
+      item.tag_name = tag_name;
+      item.attributes.push_back(attribute);
+      tag_and_attributes_list->push_back(item);
+    } else {
+      item_iter->attributes.push_back(attribute);
+    }
+  }
+
+  std::sort(tag_and_attributes_list->begin(), tag_and_attributes_list->end(),
+            [](const TagAndAttributesItem& a, const TagAndAttributesItem& b) {
+              return a.tag_name < b.tag_name;
+            });
+}
+
+mojom::ThreatDOMDetailsNode* GetNodeForElement(
+    const blink::WebNode& element,
+    const safe_browsing::ElementToNodeMap& element_to_node_map,
+    std::vector<mojom::ThreatDOMDetailsNodePtr>* resources) {
+  DCHECK_GT(element_to_node_map.count(element), 0u);
+  size_t resource_index = element_to_node_map.at(element);
+  return (*resources)[resource_index].get();
+}
+
+std::string TruncateAttributeString(const std::string& input) {
+  if (input.length() <= ThreatDOMDetails::kMaxAttributeStringLength) {
+    return input;
+  }
+
+  std::string truncated;
+  base::TruncateUTF8ToByteSize(
+      input, ThreatDOMDetails::kMaxAttributeStringLength - 3, &truncated);
+  truncated.append("...");
+  return truncated;
+}
+
+// Handler for the various HTML elements that we extract URLs from.
+void HandleElement(
+    const blink::WebElement& element,
+    const std::vector<TagAndAttributesItem>& tag_and_attributes_list,
+    mojom::ThreatDOMDetailsNode* summary_node,
+    std::vector<mojom::ThreatDOMDetailsNodePtr>* resources,
+    safe_browsing::ElementToNodeMap* element_to_node_map) {
+  // Retrieve the link and resolve the link in case it's relative.
+  blink::WebURL full_url =
+      element.GetDocument().CompleteURL(element.GetAttribute("src"));
+
+  const GURL& child_url = GURL(full_url);
+  if (!child_url.is_empty() && child_url.is_valid()) {
+    summary_node->children.push_back(child_url);
+  }
+
+  mojom::ThreatDOMDetailsNodePtr child_node =
+      mojom::ThreatDOMDetailsNode::New();
+  child_node->url = child_url;
+  child_node->tag_name = element.TagName().Utf8();
+  child_node->parent = summary_node->url;
+  // The body of an iframe may be in a different renderer. Look up the routing
+  // ID of the local or remote frame and store it with the iframe node. If this
+  // element is not a frame then the result of the lookup will be null.
+  blink::WebFrame* subframe = blink::WebFrame::FromFrameOwnerElement(element);
+  if (subframe) {
+    child_node->child_frame_routing_id =
+        content::RenderFrame::GetRoutingIdForWebFrame(subframe);
+  }
+  if (base::FeatureList::IsEnabled(kCaptureInlineJavascriptForGoogleAds) &&
+      child_node->tag_name == "SCRIPT") {
+    child_node->inner_html = element.TextContent().Utf8();
+  }
+  // Populate the element's attributes, but only collect the ones that are
+  // configured in the finch study.
+  const auto& tag_attribute_iter = std::find_if(
+      tag_and_attributes_list.begin(), tag_and_attributes_list.end(),
+      TagNameIs(base::ToLowerASCII(child_node->tag_name)));
+  if (tag_attribute_iter != tag_and_attributes_list.end()) {
+    const std::vector<std::string> attributes_to_collect =
+        tag_attribute_iter->attributes;
+    for (const std::string& attribute : attributes_to_collect) {
+      blink::WebString attr_webstring = blink::WebString::FromASCII(attribute);
+      if (!element.HasAttribute(attr_webstring)) {
+        continue;
+      }
+      mojom::AttributeNameValuePtr attribute_name_value =
+          mojom::AttributeNameValue::New(
+              attribute, TruncateAttributeString(
+                             element.GetAttribute(attr_webstring).Ascii()));
+      child_node->attributes.push_back(std::move(attribute_name_value));
+      if (child_node->attributes.size() == ThreatDOMDetails::kMaxAttributes) {
+        break;
+      }
+    }
+  }
+
+  // Update the ID mapping. First generate the ID for the current node.
+  // Then, if its parent is available, set the current node's parent ID, and
+  // also update the parent's children with the current node's ID.
+  const int child_id = static_cast<int>(element_to_node_map->size()) + 1;
+  child_node->node_id = child_id;
+  blink::WebNode cur_parent_element = element.ParentNode();
+  while (!cur_parent_element.IsNull()) {
+    if (element_to_node_map->count(cur_parent_element) > 0) {
+      mojom::ThreatDOMDetailsNode* parent_node = GetNodeForElement(
+          cur_parent_element, *element_to_node_map, resources);
+      child_node->parent_node_id = parent_node->node_id;
+      parent_node->child_node_ids.push_back(child_id);
+
+      // TODO(lpz): Consider also updating the URL-level parent/child mapping
+      // here. Eg: child_node.parent=parent_node.url, and
+      // parent_node.children.push_back(child_url).
+      break;
+    } else {
+      // It's possible that the direct parent of this node wasn't handled, so it
+      // isn't represented in |element_to_node_map|. Try walking up the
+      // hierarchy to see if a parent further up was handled.
+      cur_parent_element = cur_parent_element.ParentNode();
+    }
+  }
+  // Add the child node to the list of resources.
+  resources->push_back(std::move(child_node));
+  // .. and remember which index it was inserted at so we can look it up later.
+  (*element_to_node_map)[element] = resources->size() - 1;
+}
+
+bool ShouldHandleElement(
+    const blink::WebElement& element,
+    const std::vector<TagAndAttributesItem>& tag_and_attributes_list) {
+  // Resources with a SRC are always handled.
+  if ((element.HasHTMLTagName("iframe") || element.HasHTMLTagName("frame") ||
+       element.HasHTMLTagName("embed") || element.HasHTMLTagName("script")) &&
+      element.HasAttribute("src")) {
+    return true;
+  }
+  if (base::FeatureList::IsEnabled(kCaptureInlineJavascriptForGoogleAds) &&
+      element.HasHTMLTagName("script")) {
+    return true;
+  }
+  std::string tag_name_lower = base::ToLowerASCII(element.TagName().Ascii());
+  const auto& tag_attribute_iter =
+      std::find_if(tag_and_attributes_list.begin(),
+                   tag_and_attributes_list.end(), TagNameIs(tag_name_lower));
+  if (tag_attribute_iter == tag_and_attributes_list.end()) {
+    return false;
+  }
+
+  const std::vector<std::string>& valid_attributes =
+      tag_attribute_iter->attributes;
+  for (const std::string& attribute : valid_attributes) {
+    if (element.HasAttribute(blink::WebString::FromASCII(attribute))) {
+      return true;
+    }
+  }
+  return false;
+}
+
+}  // namespace
+
+TagAndAttributesItem::TagAndAttributesItem() {}
+
+TagAndAttributesItem::TagAndAttributesItem(
+    const std::string& tag_name_param,
+    const std::vector<std::string>& attributes_param)
+    : tag_name(tag_name_param), attributes(attributes_param) {}
+
+TagAndAttributesItem::TagAndAttributesItem(const TagAndAttributesItem& item)
+    : tag_name(item.tag_name), attributes(item.attributes) {}
+
+TagAndAttributesItem::~TagAndAttributesItem() {}
+
+uint32_t ThreatDOMDetails::kMaxNodes = 500;
+uint32_t ThreatDOMDetails::kMaxAttributes = 100;
+uint32_t ThreatDOMDetails::kMaxAttributeStringLength = 100;
+
+// static
+ThreatDOMDetails* ThreatDOMDetails::Create(
+    content::RenderFrame* render_frame,
+    service_manager::BinderRegistry* registry) {
+  // Private constructor and public static Create() method to facilitate
+  // stubbing out this class for binary-size reduction purposes.
+  return new ThreatDOMDetails(render_frame, registry);
+}
+
+void ThreatDOMDetails::OnThreatReporterReceiver(
+    mojo::PendingReceiver<mojom::ThreatReporter> receiver) {
+  threat_reporter_receivers_.Add(this, std::move(receiver));
+}
+
+ThreatDOMDetails::ThreatDOMDetails(content::RenderFrame* render_frame,
+                                   service_manager::BinderRegistry* registry)
+    : content::RenderFrameObserver(render_frame) {
+  ParseTagAndAttributeParams(&tag_and_attributes_list_);
+  // Base::Unretained() is safe here because both the registry and the
+  // ThreatDOMDetails are scoped to the same render frame.
+  registry->AddInterface(base::BindRepeating(
+      &ThreatDOMDetails::OnThreatReporterReceiver, base::Unretained(this)));
+}
+
+ThreatDOMDetails::~ThreatDOMDetails() {}
+
+void ThreatDOMDetails::GetThreatDOMDetails(
+    GetThreatDOMDetailsCallback callback) {
+  std::vector<mojom::ThreatDOMDetailsNodePtr> resources;
+  ExtractResources(&resources);
+  // Notify the browser.
+  std::move(callback).Run(std::move(resources));
+}
+
+void ThreatDOMDetails::ExtractResources(
+    std::vector<mojom::ThreatDOMDetailsNodePtr>* resources) {
+  blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
+  if (!frame)
+    return;
+  mojom::ThreatDOMDetailsNodePtr details_node =
+      mojom::ThreatDOMDetailsNode::New();
+  blink::WebDocument document = frame->GetDocument();
+  details_node->url = GURL(document.Url());
+  if (document.IsNull()) {
+    // Nothing in this frame. Just report its URL.
+    resources->push_back(std::move(details_node));
+    return;
+  }
+
+  ElementToNodeMap element_to_node_map;
+  blink::WebElementCollection elements = document.All();
+  blink::WebElement element = elements.FirstItem();
+  for (; !element.IsNull(); element = elements.NextItem()) {
+    if (ShouldHandleElement(element, tag_and_attributes_list_)) {
+      HandleElement(element, tag_and_attributes_list_, details_node.get(),
+                    resources, &element_to_node_map);
+      if (resources->size() >= kMaxNodes) {
+        // We have reached kMaxNodes, exit early.
+        break;
+      }
+    }
+  }
+  resources->push_back(std::move(details_node));
+}
+
+void ThreatDOMDetails::OnDestruct() {
+  delete this;
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/content/renderer/threat_dom_details.h b/components/safe_browsing/content/renderer/threat_dom_details.h
new file mode 100644
index 0000000..e42da6a
--- /dev/null
+++ b/components/safe_browsing/content/renderer/threat_dom_details.h
@@ -0,0 +1,94 @@
+// Copyright (c) 2012 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.
+//
+// ThreatDOMDetails iterates over a document's frames and gathers
+// interesting URLs such as those of scripts and frames. When done, it sends
+// them to the ThreatDetails that requested them.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CONTENT_RENDERER_THREAT_DOM_DETAILS_H_
+#define COMPONENTS_SAFE_BROWSING_CONTENT_RENDERER_THREAT_DOM_DETAILS_H_
+
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/feature_list.h"
+#include "components/safe_browsing/core/common/safe_browsing.mojom.h"
+#include "content/public/renderer/render_frame_observer.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
+#include "services/service_manager/public/cpp/binder_registry.h"
+
+namespace safe_browsing {
+
+extern const char kTagAndAttributeParamName[];
+
+// Represents the tag name of an HTML Element and its associated attributes.
+// Used to determine which elements to collect. Populated from the param value
+// of |kThreatDomDetailsTagAndAttributeFeature|.
+class TagAndAttributesItem {
+ public:
+  TagAndAttributesItem();
+  TagAndAttributesItem(const std::string& tag_name_param,
+                       const std::vector<std::string>& attributes_param);
+  TagAndAttributesItem(const TagAndAttributesItem& item);
+  ~TagAndAttributesItem();
+
+  std::string tag_name;
+  std::vector<std::string> attributes;
+};
+
+// There is one ThreatDOMDetails per RenderFrame.
+class ThreatDOMDetails : public content::RenderFrameObserver,
+                         public mojom::ThreatReporter {
+ public:
+  // An upper limit on the number of nodes we collect. Not const for the test.
+  static uint32_t kMaxNodes;
+
+  // An upper limit on the number of attributes to collect per node. Not const
+  // for the test.
+  static uint32_t kMaxAttributes;
+
+  // An upper limit on the length of an attribute string.
+  static uint32_t kMaxAttributeStringLength;
+
+  static ThreatDOMDetails* Create(content::RenderFrame* render_frame,
+                                  service_manager::BinderRegistry* registry);
+  ~ThreatDOMDetails() override;
+
+  // Begins extracting resource urls for the page currently loaded in
+  // this object's RenderFrame.
+  // Exposed for testing.
+  void ExtractResources(std::vector<mojom::ThreatDOMDetailsNodePtr>* resources);
+
+  std::vector<TagAndAttributesItem> GetTagAndAttributesListForTest() {
+    return tag_and_attributes_list_;
+  }
+
+ private:
+  // Creates a ThreatDOMDetails for the specified RenderFrame.
+  // The ThreatDOMDetails should be destroyed prior to destroying
+  // the RenderFrame.
+  ThreatDOMDetails(content::RenderFrame* render_frame,
+                   service_manager::BinderRegistry* registry);
+
+  void OnDestruct() override;
+
+  // safe_browsing::mojom::ThreatReporter:
+  void GetThreatDOMDetails(GetThreatDOMDetailsCallback callback) override;
+
+  void OnThreatReporterReceiver(
+      mojo::PendingReceiver<mojom::ThreatReporter> receiver);
+
+  mojo::ReceiverSet<mojom::ThreatReporter> threat_reporter_receivers_;
+
+  // A list of tag names and associates attributes, used to determine which
+  // elements need to be collected.
+  std::vector<TagAndAttributesItem> tag_and_attributes_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(ThreatDOMDetails);
+};
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CONTENT_RENDERER_THREAT_DOM_DETAILS_H_
diff --git a/components/safe_browsing/content/renderer/websocket_sb_handshake_throttle.cc b/components/safe_browsing/content/renderer/websocket_sb_handshake_throttle.cc
new file mode 100644
index 0000000..b9beaf1e
--- /dev/null
+++ b/components/safe_browsing/content/renderer/websocket_sb_handshake_throttle.cc
@@ -0,0 +1,117 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/content/renderer/websocket_sb_handshake_throttle.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/logging.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/stringprintf.h"
+#include "content/public/common/resource_type.h"
+#include "content/public/renderer/render_frame.h"
+#include "ipc/ipc_message.h"
+#include "net/http/http_request_headers.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/public/platform/web_url.h"
+
+namespace safe_browsing {
+
+WebSocketSBHandshakeThrottle::WebSocketSBHandshakeThrottle(
+    mojom::SafeBrowsing* safe_browsing,
+    int render_frame_id)
+    : render_frame_id_(render_frame_id),
+      safe_browsing_(safe_browsing),
+      result_(Result::UNKNOWN) {}
+
+WebSocketSBHandshakeThrottle::~WebSocketSBHandshakeThrottle() {
+  // ThrottleHandshake() should always be called, but since that is done all the
+  // way over in Blink, just avoid logging if it is not called rather than
+  // DCHECK()ing.
+  if (start_time_.is_null())
+    return;
+  if (result_ == Result::UNKNOWN) {
+    result_ = Result::ABANDONED;
+    UMA_HISTOGRAM_TIMES("SafeBrowsing.WebSocket.Elapsed.Abandoned",
+                        base::TimeTicks::Now() - start_time_);
+  }
+}
+
+void WebSocketSBHandshakeThrottle::ThrottleHandshake(
+    const blink::WebURL& url,
+    blink::WebSocketHandshakeThrottle::OnCompletion completion_callback) {
+  DCHECK(!url_checker_);
+  DCHECK(!completion_callback_);
+  completion_callback_ = std::move(completion_callback);
+  url_ = url;
+  int load_flags = 0;
+  start_time_ = base::TimeTicks::Now();
+  safe_browsing_->CreateCheckerAndCheck(
+      render_frame_id_, url_checker_.BindNewPipeAndPassReceiver(), url, "GET",
+      net::HttpRequestHeaders(), load_flags,
+      content::ResourceType::kSubResource, false /* has_user_gesture */,
+      false /* originated_from_service_worker */,
+      base::BindOnce(&WebSocketSBHandshakeThrottle::OnCheckResult,
+                     weak_factory_.GetWeakPtr()));
+
+  // This use of base::Unretained() is safe because the handler will not be
+  // called after |url_checker_| is destroyed, and it is owned by this object.
+  url_checker_.set_disconnect_handler(base::BindOnce(
+      &WebSocketSBHandshakeThrottle::OnMojoDisconnect, base::Unretained(this)));
+}
+
+void WebSocketSBHandshakeThrottle::OnCompleteCheck(bool proceed,
+                                                   bool showed_interstitial) {
+  DCHECK(!start_time_.is_null());
+  base::TimeDelta elapsed = base::TimeTicks::Now() - start_time_;
+  if (proceed) {
+    result_ = Result::SAFE;
+    UMA_HISTOGRAM_TIMES("SafeBrowsing.WebSocket.Elapsed.Safe", elapsed);
+    std::move(completion_callback_).Run(base::nullopt);
+  } else {
+    // When the insterstitial is dismissed the page is navigated and this object
+    // is destroyed before reaching here.
+    result_ = Result::BLOCKED;
+    UMA_HISTOGRAM_TIMES("SafeBrowsing.WebSocket.Elapsed.Blocked", elapsed);
+    std::move(completion_callback_)
+        .Run(blink::WebString::FromUTF8(base::StringPrintf(
+            "WebSocket connection to %s failed safe browsing check",
+            url_.spec().c_str())));
+  }
+  // |this| is destroyed here.
+}
+
+void WebSocketSBHandshakeThrottle::OnCheckResult(
+    mojo::PendingReceiver<mojom::UrlCheckNotifier> slow_check_notifier,
+    bool proceed,
+    bool showed_interstitial) {
+  if (!slow_check_notifier.is_valid()) {
+    OnCompleteCheck(proceed, showed_interstitial);
+    return;
+  }
+
+  // TODO(yzshen): Notify the network service to pause processing response body.
+  if (!notifier_receiver_) {
+    notifier_receiver_ =
+        std::make_unique<mojo::Receiver<mojom::UrlCheckNotifier>>(this);
+  }
+  notifier_receiver_->Bind(std::move(slow_check_notifier));
+}
+
+void WebSocketSBHandshakeThrottle::OnMojoDisconnect() {
+  DCHECK_EQ(result_, Result::UNKNOWN);
+
+  url_checker_.reset();
+  notifier_receiver_.reset();
+
+  // Make the destructor record NOT_SUPPORTED in the result histogram.
+  result_ = Result::NOT_SUPPORTED;
+  // Don't record the time elapsed because it's unlikely to be meaningful.
+  std::move(completion_callback_).Run(base::nullopt);
+  // |this| is destroyed here.
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/content/renderer/websocket_sb_handshake_throttle.h b/components/safe_browsing/content/renderer/websocket_sb_handshake_throttle.h
new file mode 100644
index 0000000..eecfede
--- /dev/null
+++ b/components/safe_browsing/content/renderer/websocket_sb_handshake_throttle.h
@@ -0,0 +1,72 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Implementation of SafeBrowsing for WebSockets. This code runs inside the
+// render process, calling the interface defined in safe_browsing.mojom to
+// communicate with the SafeBrowsing service.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CONTENT_RENDERER_WEBSOCKET_SB_HANDSHAKE_THROTTLE_H_
+#define COMPONENTS_SAFE_BROWSING_CONTENT_RENDERER_WEBSOCKET_SB_HANDSHAKE_THROTTLE_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "components/safe_browsing/core/common/safe_browsing.mojom.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "third_party/blink/public/platform/websocket_handshake_throttle.h"
+#include "url/gurl.h"
+
+namespace safe_browsing {
+
+class WebSocketSBHandshakeThrottle : public blink::WebSocketHandshakeThrottle,
+                                     public mojom::UrlCheckNotifier {
+ public:
+  WebSocketSBHandshakeThrottle(mojom::SafeBrowsing* safe_browsing,
+                               int render_frame_id);
+  ~WebSocketSBHandshakeThrottle() override;
+
+  void ThrottleHandshake(const blink::WebURL& url,
+                         blink::WebSocketHandshakeThrottle::OnCompletion
+                             completion_callback) override;
+
+ private:
+  // These values are logged to UMA so do not renumber or reuse.
+  enum class Result {
+    UNKNOWN = 0,
+    SAFE = 1,
+    BLOCKED = 2,
+    ABANDONED = 3,
+    NOT_SUPPORTED = 4,
+    RESULT_COUNT
+  };
+
+  // mojom::UrlCheckNotifier implementation.
+  void OnCompleteCheck(bool proceed, bool showed_interstitial) override;
+
+  void OnCheckResult(
+      mojo::PendingReceiver<mojom::UrlCheckNotifier> slow_check_notifier,
+      bool proceed,
+      bool showed_interstitial);
+  void OnMojoDisconnect();
+
+  const int render_frame_id_;
+  GURL url_;
+  blink::WebSocketHandshakeThrottle::OnCompletion completion_callback_;
+  mojo::Remote<mojom::SafeBrowsingUrlChecker> url_checker_;
+  mojom::SafeBrowsing* safe_browsing_;
+  std::unique_ptr<mojo::Receiver<mojom::UrlCheckNotifier>> notifier_receiver_;
+  base::TimeTicks start_time_;
+  Result result_;
+
+  base::WeakPtrFactory<WebSocketSBHandshakeThrottle> weak_factory_{this};
+
+  DISALLOW_COPY_AND_ASSIGN(WebSocketSBHandshakeThrottle);
+};
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CONTENT_RENDERER_WEBSOCKET_SB_HANDSHAKE_THROTTLE_H_
diff --git a/components/safe_browsing/content/renderer/websocket_sb_handshake_throttle_unittest.cc b/components/safe_browsing/content/renderer/websocket_sb_handshake_throttle_unittest.cc
new file mode 100644
index 0000000..4e55f44
--- /dev/null
+++ b/components/safe_browsing/content/renderer/websocket_sb_handshake_throttle_unittest.cc
@@ -0,0 +1,195 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/content/renderer/websocket_sb_handshake_throttle.h"
+
+#include <utility>
+
+#include "base/callback.h"
+#include "base/run_loop.h"
+#include "base/test/task_environment.h"
+#include "components/safe_browsing/core/common/safe_browsing.mojom.h"
+#include "content/public/common/resource_type.h"
+#include "ipc/ipc_message.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "net/http/http_request_headers.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/public/platform/web_url.h"
+
+namespace safe_browsing {
+
+namespace {
+
+constexpr char kTestUrl[] = "wss://test/";
+
+class FakeSafeBrowsing : public mojom::SafeBrowsing {
+ public:
+  FakeSafeBrowsing()
+      : render_frame_id_(),
+        load_flags_(-1),
+        resource_type_(),
+        has_user_gesture_(false),
+        originated_from_service_worker_(false) {}
+
+  void CreateCheckerAndCheck(
+      int32_t render_frame_id,
+      mojo::PendingReceiver<mojom::SafeBrowsingUrlChecker> receiver,
+      const GURL& url,
+      const std::string& method,
+      const net::HttpRequestHeaders& headers,
+      int32_t load_flags,
+      content::ResourceType resource_type,
+      bool has_user_gesture,
+      bool originated_from_service_worker,
+      CreateCheckerAndCheckCallback callback) override {
+    render_frame_id_ = render_frame_id;
+    receiver_ = std::move(receiver);
+    url_ = url;
+    method_ = method;
+    headers_ = headers;
+    load_flags_ = load_flags;
+    resource_type_ = resource_type;
+    has_user_gesture_ = has_user_gesture;
+    originated_from_service_worker_ = originated_from_service_worker;
+    callback_ = std::move(callback);
+    run_loop_.Quit();
+  }
+
+  void Clone(mojo::PendingReceiver<mojom::SafeBrowsing> receiver) override {
+    NOTREACHED();
+  }
+
+  void RunUntilCalled() { run_loop_.Run(); }
+
+  int32_t render_frame_id_;
+  mojo::PendingReceiver<mojom::SafeBrowsingUrlChecker> receiver_;
+  GURL url_;
+  std::string method_;
+  net::HttpRequestHeaders headers_;
+  int32_t load_flags_;
+  content::ResourceType resource_type_;
+  bool has_user_gesture_;
+  bool originated_from_service_worker_;
+  CreateCheckerAndCheckCallback callback_;
+  base::RunLoop run_loop_;
+};
+
+class FakeCallback {
+ public:
+  enum Result { RESULT_NOT_CALLED, RESULT_SUCCESS, RESULT_ERROR };
+
+  FakeCallback() : result_(RESULT_NOT_CALLED) {}
+
+  void OnCompletion(const base::Optional<blink::WebString>& message) {
+    if (message) {
+      result_ = RESULT_ERROR;
+      message_ = *message;
+      run_loop_.Quit();
+      return;
+    }
+
+    result_ = RESULT_SUCCESS;
+    run_loop_.Quit();
+  }
+
+  void RunUntilCalled() { run_loop_.Run(); }
+
+  void RunUntilIdle() { base::RunLoop().RunUntilIdle(); }
+
+  Result result_;
+  blink::WebString message_;
+  base::RunLoop run_loop_;
+};
+
+class WebSocketSBHandshakeThrottleTest : public ::testing::Test {
+ protected:
+  WebSocketSBHandshakeThrottleTest() : mojo_receiver_(&safe_browsing_) {
+    mojo_receiver_.Bind(safe_browsing_remote_.BindNewPipeAndPassReceiver());
+    throttle_ = std::make_unique<WebSocketSBHandshakeThrottle>(
+        safe_browsing_remote_.get(), MSG_ROUTING_NONE);
+  }
+
+  base::test::TaskEnvironment message_loop_;
+  FakeSafeBrowsing safe_browsing_;
+  mojo::Receiver<mojom::SafeBrowsing> mojo_receiver_;
+  mojo::Remote<mojom::SafeBrowsing> safe_browsing_remote_;
+  std::unique_ptr<WebSocketSBHandshakeThrottle> throttle_;
+  FakeCallback fake_callback_;
+};
+
+TEST_F(WebSocketSBHandshakeThrottleTest, Construction) {}
+
+TEST_F(WebSocketSBHandshakeThrottleTest, CheckArguments) {
+  throttle_->ThrottleHandshake(
+      GURL(kTestUrl), base::BindOnce(&FakeCallback::OnCompletion,
+                                     base::Unretained(&fake_callback_)));
+  safe_browsing_.RunUntilCalled();
+  EXPECT_EQ(MSG_ROUTING_NONE, safe_browsing_.render_frame_id_);
+  EXPECT_EQ(GURL(kTestUrl), safe_browsing_.url_);
+  EXPECT_EQ("GET", safe_browsing_.method_);
+  EXPECT_TRUE(safe_browsing_.headers_.GetHeaderVector().empty());
+  EXPECT_EQ(0, safe_browsing_.load_flags_);
+  EXPECT_EQ(content::ResourceType::kSubResource, safe_browsing_.resource_type_);
+  EXPECT_FALSE(safe_browsing_.has_user_gesture_);
+  EXPECT_FALSE(safe_browsing_.originated_from_service_worker_);
+  EXPECT_TRUE(safe_browsing_.callback_);
+}
+
+TEST_F(WebSocketSBHandshakeThrottleTest, Safe) {
+  throttle_->ThrottleHandshake(
+      GURL(kTestUrl), base::BindOnce(&FakeCallback::OnCompletion,
+                                     base::Unretained(&fake_callback_)));
+  safe_browsing_.RunUntilCalled();
+  std::move(safe_browsing_.callback_).Run(mojo::NullReceiver(), true, false);
+  fake_callback_.RunUntilCalled();
+  EXPECT_EQ(FakeCallback::RESULT_SUCCESS, fake_callback_.result_);
+}
+
+TEST_F(WebSocketSBHandshakeThrottleTest, Unsafe) {
+  throttle_->ThrottleHandshake(
+      GURL(kTestUrl), base::BindOnce(&FakeCallback::OnCompletion,
+                                     base::Unretained(&fake_callback_)));
+  safe_browsing_.RunUntilCalled();
+  std::move(safe_browsing_.callback_).Run(mojo::NullReceiver(), false, false);
+  fake_callback_.RunUntilCalled();
+  EXPECT_EQ(FakeCallback::RESULT_ERROR, fake_callback_.result_);
+  EXPECT_EQ(
+      blink::WebString(
+          "WebSocket connection to wss://test/ failed safe browsing check"),
+      fake_callback_.message_);
+}
+
+TEST_F(WebSocketSBHandshakeThrottleTest, SlowCheckNotifier) {
+  throttle_->ThrottleHandshake(
+      GURL(kTestUrl), base::BindOnce(&FakeCallback::OnCompletion,
+                                     base::Unretained(&fake_callback_)));
+  safe_browsing_.RunUntilCalled();
+
+  mojo::Remote<mojom::UrlCheckNotifier> slow_check_notifier;
+  std::move(safe_browsing_.callback_)
+      .Run(slow_check_notifier.BindNewPipeAndPassReceiver(), false, false);
+  fake_callback_.RunUntilIdle();
+  EXPECT_EQ(FakeCallback::RESULT_NOT_CALLED, fake_callback_.result_);
+
+  slow_check_notifier->OnCompleteCheck(true, false);
+  fake_callback_.RunUntilCalled();
+  EXPECT_EQ(FakeCallback::RESULT_SUCCESS, fake_callback_.result_);
+}
+
+TEST_F(WebSocketSBHandshakeThrottleTest, MojoServiceNotThere) {
+  mojo_receiver_.reset();
+  throttle_->ThrottleHandshake(
+      GURL(kTestUrl), base::BindOnce(&FakeCallback::OnCompletion,
+                                     base::Unretained(&fake_callback_)));
+  fake_callback_.RunUntilCalled();
+  EXPECT_EQ(FakeCallback::RESULT_SUCCESS, fake_callback_.result_);
+}
+
+}  // namespace
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/content/safe_browsing_controller_client.cc b/components/safe_browsing/content/safe_browsing_controller_client.cc
new file mode 100644
index 0000000..a6f563b
--- /dev/null
+++ b/components/safe_browsing/content/safe_browsing_controller_client.cc
@@ -0,0 +1,51 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/content/safe_browsing_controller_client.h"
+
+#include "base/feature_list.h"
+#include "components/safe_browsing/core/features.h"
+#include "components/security_interstitials/core/metrics_helper.h"
+
+namespace safe_browsing {
+
+SafeBrowsingControllerClient::SafeBrowsingControllerClient(
+    content::WebContents* web_contents,
+    std::unique_ptr<security_interstitials::MetricsHelper> metrics_helper,
+    PrefService* prefs,
+    const std::string& app_locale,
+    const GURL& default_safe_page)
+    : security_interstitials::SecurityInterstitialControllerClient(
+          web_contents,
+          std::move(metrics_helper),
+          prefs,
+          app_locale,
+          default_safe_page) {}
+
+SafeBrowsingControllerClient::~SafeBrowsingControllerClient() {}
+
+void SafeBrowsingControllerClient::Proceed() {
+  if (!interstitial_page()) {
+    DCHECK(
+        base::FeatureList::IsEnabled(safe_browsing::kCommittedSBInterstitials));
+    // In this case, committed interstitials are enabled, the site has already
+    // been added to the whitelist, so reload will proceed.
+    Reload();
+    return;
+  }
+  security_interstitials::SecurityInterstitialControllerClient::Proceed();
+}
+
+void SafeBrowsingControllerClient::GoBack() {
+  if (!interstitial_page()) {
+    // In this case, committed interstitials are enabled, so we do a regular
+    // back navigation.
+    SecurityInterstitialControllerClient::GoBackAfterNavigationCommitted();
+    return;
+  }
+
+  SecurityInterstitialControllerClient::GoBack();
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/content/safe_browsing_controller_client.h b/components/safe_browsing/content/safe_browsing_controller_client.h
new file mode 100644
index 0000000..6394286
--- /dev/null
+++ b/components/safe_browsing/content/safe_browsing_controller_client.h
@@ -0,0 +1,46 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CONTENT_SAFE_BROWSING_CONTROLLER_CLIENT_H_
+#define COMPONENTS_SAFE_BROWSING_CONTENT_SAFE_BROWSING_CONTROLLER_CLIENT_H_
+
+#include "base/macros.h"
+#include "components/security_interstitials/content/security_interstitial_controller_client.h"
+
+namespace content {
+class WebContents;
+}
+
+namespace security_interstitials {
+class MetricsHelper;
+}
+
+class PrefService;
+
+namespace safe_browsing {
+
+// Provides embedder-specific logic for the Safe Browsing interstitial page
+// controller.
+class SafeBrowsingControllerClient
+    : public security_interstitials::SecurityInterstitialControllerClient {
+ public:
+  SafeBrowsingControllerClient(
+      content::WebContents* web_contents,
+      std::unique_ptr<security_interstitials::MetricsHelper> metrics_helper,
+      PrefService* prefs,
+      const std::string& app_locale,
+      const GURL& default_safe_page);
+  ~SafeBrowsingControllerClient() override;
+
+  void Proceed() override;
+
+  void GoBack() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SafeBrowsingControllerClient);
+};
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CONTENT_SAFE_BROWSING_CONTROLLER_CLIENT_H_
diff --git a/components/safe_browsing/content/triggers/BUILD.gn b/components/safe_browsing/content/triggers/BUILD.gn
new file mode 100644
index 0000000..30be54c
--- /dev/null
+++ b/components/safe_browsing/content/triggers/BUILD.gn
@@ -0,0 +1,106 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/features.gni")
+
+source_set("trigger_util") {
+  sources = [
+    "trigger_util.cc",
+    "trigger_util.h",
+  ]
+  deps = [
+    "//components/safe_browsing/core/triggers",
+    "//content/public/browser",
+  ]
+}
+
+source_set("ad_popup_trigger") {
+  sources = [
+    "ad_popup_trigger.cc",
+    "ad_popup_trigger.h",
+  ]
+  deps = [
+    ":trigger_util",
+    "//base:base",
+    "//components/safe_browsing/core:features",
+    "//components/safe_browsing/core/triggers",
+    "//components/safe_browsing/core/triggers:trigger_throttler",
+    "//content/public/browser",
+  ]
+}
+
+source_set("ad_redirect_trigger") {
+  sources = [
+    "ad_redirect_trigger.cc",
+    "ad_redirect_trigger.h",
+  ]
+  deps = [
+    ":trigger_util",
+    "//base:base",
+    "//components/safe_browsing/core:features",
+    "//components/safe_browsing/core/triggers",
+    "//components/safe_browsing/core/triggers:trigger_throttler",
+    "//content/public/browser",
+    "//content/public/common",
+  ]
+}
+
+source_set("ad_sampler_trigger") {
+  sources = [
+    "ad_sampler_trigger.cc",
+    "ad_sampler_trigger.h",
+  ]
+  deps = [
+    ":trigger_util",
+    "//base:base",
+    "//components/safe_browsing/core:features",
+    "//components/safe_browsing/core/triggers",
+    "//components/safe_browsing/core/triggers:trigger_throttler",
+    "//content/public/browser",
+  ]
+}
+
+source_set("suspicious_site_trigger") {
+  sources = [
+    "suspicious_site_trigger.cc",
+    "suspicious_site_trigger.h",
+  ]
+  deps = [
+    "//base:base",
+    "//components/history/core/browser:browser",
+    "//components/prefs:prefs",
+    "//components/safe_browsing/core:features",
+    "//components/safe_browsing/core/triggers",
+    "//components/safe_browsing/core/triggers:trigger_throttler",
+    "//content/public/browser",
+    "//net:net",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "ad_popup_trigger_unittest.cc",
+    "ad_sampler_trigger_unittest.cc",
+    "mock_trigger_manager.cc",
+    "mock_trigger_manager.h",
+    "suspicious_site_trigger_unittest.cc",
+  ]
+  deps = [
+    ":ad_popup_trigger",
+    ":ad_sampler_trigger",
+    ":suspicious_site_trigger",
+    "//base",
+    "//base/test:test_support",
+    "//components/prefs:test_support",
+    "//components/safe_browsing/core:features",
+    "//components/safe_browsing/core/browser:browser",
+    "//components/safe_browsing/core/triggers",
+    "//components/subresource_filter/content/browser:test_support",
+    "//content/public/browser:browser",
+    "//content/test:test_support",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+}
diff --git a/components/safe_browsing/content/triggers/ad_popup_trigger.cc b/components/safe_browsing/content/triggers/ad_popup_trigger.cc
new file mode 100644
index 0000000..ab8c1b38
--- /dev/null
+++ b/components/safe_browsing/content/triggers/ad_popup_trigger.cc
@@ -0,0 +1,150 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/content/triggers/ad_popup_trigger.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/feature_list.h"
+#include "base/metrics/field_trial_params.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/rand_util.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/task/post_task.h"
+#include "components/safe_browsing/content/triggers/trigger_util.h"
+#include "components/safe_browsing/core/features.h"
+#include "components/safe_browsing/core/triggers/trigger_manager.h"
+#include "components/safe_browsing/core/triggers/trigger_throttler.h"
+#include "components/security_interstitials/content/unsafe_resource.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/web_contents.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/simple_url_loader.h"
+
+namespace safe_browsing {
+
+namespace {
+
+// Number of milliseconds to allow data collection to run before sending a
+// report (since this trigger runs in the background).
+const int64_t kAdPopupCollectionPeriodMilliseconds = 5000;
+
+// Range of number of milliseconds to wait after a page finished loading before
+// starting a report. Allows ads which load in the background to finish loading.
+const int64_t kMaxAdPopupCollectionStartDelayMilliseconds = 5000;
+const int64_t kMinAdPopupCollectionStartDelayMilliseconds = 500;
+
+void RecordAdPopupTriggerAction(AdPopupTriggerAction action) {
+  UMA_HISTOGRAM_ENUMERATION(kAdPopupTriggerActionMetricName, action);
+}
+
+}  // namespace
+
+// Metric for tracking what the Ad Popup trigger does on each navigation.
+const char kAdPopupTriggerActionMetricName[] =
+    "SafeBrowsing.Triggers.AdPopup.Action";
+
+AdPopupTrigger::AdPopupTrigger(
+    content::WebContents* web_contents,
+    TriggerManager* trigger_manager,
+    PrefService* prefs,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    history::HistoryService* history_service)
+    : web_contents_(web_contents),
+      start_report_delay_ms_(
+          base::RandInt(kMinAdPopupCollectionStartDelayMilliseconds,
+                        kMaxAdPopupCollectionStartDelayMilliseconds)),
+      finish_report_delay_ms_(kAdPopupCollectionPeriodMilliseconds),
+      trigger_manager_(trigger_manager),
+      prefs_(prefs),
+      url_loader_factory_(url_loader_factory),
+      history_service_(history_service),
+      task_runner_(
+          base::CreateSingleThreadTaskRunner({content::BrowserThread::UI})) {}
+
+AdPopupTrigger::~AdPopupTrigger() {}
+
+// static
+void AdPopupTrigger::CreateForWebContents(
+    content::WebContents* web_contents,
+    TriggerManager* trigger_manager,
+    PrefService* prefs,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    history::HistoryService* history_service) {
+  DCHECK(web_contents);
+  if (!FromWebContents(web_contents)) {
+    web_contents->SetUserData(UserDataKey(),
+                              base::WrapUnique(new AdPopupTrigger(
+                                  web_contents, trigger_manager, prefs,
+                                  url_loader_factory, history_service)));
+  }
+}
+
+void AdPopupTrigger::CreateAdPopupReport() {
+  SBErrorOptions error_options =
+      TriggerManager::GetSBErrorDisplayOptions(*prefs_, web_contents_);
+  security_interstitials::UnsafeResource resource;
+  resource.threat_type = SB_THREAT_TYPE_BLOCKED_AD_POPUP;
+  resource.url = web_contents_->GetURL();
+  resource.web_contents_getter = resource.GetWebContentsGetter(
+      web_contents_->GetMainFrame()->GetProcess()->GetID(),
+      web_contents_->GetMainFrame()->GetRoutingID());
+  TriggerManagerReason reason = TriggerManagerReason::NO_REASON;
+  if (!trigger_manager_->StartCollectingThreatDetailsWithReason(
+          TriggerType::AD_POPUP, web_contents_, resource, url_loader_factory_,
+          history_service_, error_options, &reason)) {
+    if (reason == TriggerManagerReason::DAILY_QUOTA_EXCEEDED) {
+      RecordAdPopupTriggerAction(
+          AdPopupTriggerAction::POPUP_DAILY_QUOTA_EXCEEDED);
+    } else {
+      RecordAdPopupTriggerAction(
+          AdPopupTriggerAction::POPUP_COULD_NOT_START_REPORT);
+    }
+    return;
+  }
+  // Call into TriggerManager to finish the reports after a short delay. Any
+  // ads that are detected during this delay will be rejected by TriggerManager
+  // because a report is already being collected, so we won't send multiple
+  // reports for the same page.
+  task_runner_->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(
+          IgnoreResult(&TriggerManager::FinishCollectingThreatDetails),
+          base::Unretained(trigger_manager_), TriggerType::AD_POPUP,
+          base::Unretained(web_contents_), base::TimeDelta(),
+          /*did_proceed=*/false, /*num_visits=*/0, error_options),
+      base::TimeDelta::FromMilliseconds(finish_report_delay_ms_));
+
+  RecordAdPopupTriggerAction(AdPopupTriggerAction::POPUP_REPORTED);
+}
+
+void AdPopupTrigger::PopupWasBlocked(content::RenderFrameHost* render_frame) {
+  RecordAdPopupTriggerAction(AdPopupTriggerAction::POPUP_CHECK);
+  if (!DetectGoogleAd(render_frame, web_contents_->GetURL())) {
+    RecordAdPopupTriggerAction(AdPopupTriggerAction::POPUP_NO_GOOGLE_AD);
+    return;
+  }
+  // Create a report after a short delay. The delay gives more time for ads to
+  // finish loading in the background. This is best-effort.
+  task_runner_->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(&AdPopupTrigger::CreateAdPopupReport,
+                     weak_ptr_factory_.GetWeakPtr()),
+      base::TimeDelta::FromMilliseconds(start_report_delay_ms_));
+}
+
+void AdPopupTrigger::SetTaskRunnerForTest(
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+  task_runner_ = task_runner;
+}
+
+WEB_CONTENTS_USER_DATA_KEY_IMPL(AdPopupTrigger)
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/content/triggers/ad_popup_trigger.h b/components/safe_browsing/content/triggers/ad_popup_trigger.h
new file mode 100644
index 0000000..a10a166
--- /dev/null
+++ b/components/safe_browsing/content/triggers/ad_popup_trigger.h
@@ -0,0 +1,111 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CONTENT_TRIGGERS_AD_POPUP_TRIGGER_H_
+#define COMPONENTS_SAFE_BROWSING_CONTENT_TRIGGERS_AD_POPUP_TRIGGER_H_
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "content/public/browser/web_contents_user_data.h"
+
+class PrefService;
+
+namespace history {
+class HistoryService;
+}
+
+namespace network {
+class SharedURLLoaderFactory;
+}  // namespace network
+
+namespace safe_browsing {
+class TriggerManager;
+
+// Metric for tracking what the Ad Popup trigger does on each navigation.
+extern const char kAdPopupTriggerActionMetricName[];
+
+enum class AdPopupTriggerAction {
+  // An event occurred that caused the trigger to perform its checks.
+  POPUP_CHECK = 0,
+  // A popup cause by an ad was detected and a report was collected.
+  POPUP_REPORTED = 1,
+  // No ad was detected.
+  POPUP_NO_GOOGLE_AD = 2,
+  // An ad was detected on the page causing a popup and could have been
+  // reported, but the trigger manager rejected the report (eg: because user is
+  // incognito or has not opted into extended reporting).
+  POPUP_COULD_NOT_START_REPORT = 3,
+  // Daily quota for ads that caused blocked popups was met.
+  POPUP_DAILY_QUOTA_EXCEEDED = 4,
+  // New events must be added before kMaxValue, and the value of kMaxValue
+  // updated.
+  kMaxValue = POPUP_DAILY_QUOTA_EXCEEDED
+};
+
+// This class is notified when a popup caused by an ad in the browser is
+// blocked. If the Ad is a Google Ad, this class sends a report to Google.
+// Design doc: go/extending-chrind-q2-2019-1
+class AdPopupTrigger : public content::WebContentsUserData<AdPopupTrigger> {
+ public:
+  ~AdPopupTrigger() override;
+
+  static void CreateForWebContents(
+      content::WebContents* web_contents,
+      TriggerManager* trigger_manager,
+      PrefService* prefs,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      history::HistoryService* history_service);
+
+  void PopupWasBlocked(content::RenderFrameHost* render_frame);
+
+ private:
+  friend class AdPopupTriggerTest;
+  friend class content::WebContentsUserData<AdPopupTrigger>;
+
+  AdPopupTrigger(
+      content::WebContents* web_contents,
+      TriggerManager* trigger_manager,
+      PrefService* prefs,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      history::HistoryService* history_service);
+
+  // Called to create an ad popup report.
+  void CreateAdPopupReport();
+
+  // Sets a task runner to use for tests.
+  void SetTaskRunnerForTest(
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+
+  // WebContents of the current tab.
+  content::WebContents* web_contents_;
+
+  // The delay (in milliseconds) to wait before starting a report. Can be
+  // ovewritten for tests.
+  int64_t start_report_delay_ms_;
+
+  // The delay (in milliseconds) to wait before finishing a report. Can be
+  // overwritten for tests.
+  int64_t finish_report_delay_ms_;
+
+  // TriggerManager gets called if this trigger detects apopup caused by ad and
+  // wants to collect some data about it. Not owned.
+  TriggerManager* trigger_manager_;
+
+  PrefService* prefs_;
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
+  history::HistoryService* history_service_;
+
+  // Task runner for posting delayed tasks. Normally set to the runner for the
+  // UI thread, but can be overwritten for tests.
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
+  base::WeakPtrFactory<AdPopupTrigger> weak_ptr_factory_{this};
+  WEB_CONTENTS_USER_DATA_KEY_DECL();
+
+  DISALLOW_COPY_AND_ASSIGN(AdPopupTrigger);
+};
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CONTENT_TRIGGERS_AD_POPUP_TRIGGER_H_
diff --git a/components/safe_browsing/content/triggers/ad_popup_trigger_unittest.cc b/components/safe_browsing/content/triggers/ad_popup_trigger_unittest.cc
new file mode 100644
index 0000000..56fb756
--- /dev/null
+++ b/components/safe_browsing/content/triggers/ad_popup_trigger_unittest.cc
@@ -0,0 +1,219 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/content/triggers/ad_popup_trigger.h"
+
+#include "base/metrics/field_trial_params.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/test/test_simple_task_runner.h"
+#include "build/build_config.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/safe_browsing/content/triggers/mock_trigger_manager.h"
+#include "components/safe_browsing/core/features.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_task_environment.h"
+#include "content/public/test/navigation_simulator.h"
+#include "content/public/test/test_renderer_host.h"
+#include "testing/gmock/include/gmock/gmock-generated-function-mockers.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using content::NavigationSimulator;
+using content::RenderFrameHost;
+using content::RenderFrameHostTester;
+
+using testing::_;
+using testing::Return;
+
+namespace safe_browsing {
+
+namespace {
+const char kAdUrl[] = "https://tpc.googlesyndication.com/safeframe/1";
+const char kNonAdUrl[] = "https://foo.com/";
+const char kAdName[] = "google_ads_iframe_1";
+const char kNonAdName[] = "foo";
+}  // namespace
+
+class AdPopupTriggerTest : public content::RenderViewHostTestHarness {
+ public:
+  AdPopupTriggerTest() : task_runner_(new base::TestSimpleTaskRunner) {}
+  ~AdPopupTriggerTest() override {}
+
+  void SetUp() override {
+    content::RenderViewHostTestHarness::SetUp();
+
+    // Enable any prefs required for the trigger to run.
+    safe_browsing::RegisterProfilePrefs(prefs_.registry());
+    prefs_.SetBoolean(prefs::kSafeBrowsingExtendedReportingOptInAllowed, true);
+    prefs_.SetBoolean(prefs::kSafeBrowsingScoutReportingEnabled, true);
+  }
+
+  void CreateTrigger() {
+    safe_browsing::AdPopupTrigger::CreateForWebContents(
+        web_contents(), &trigger_manager_, &prefs_, nullptr, nullptr);
+
+    safe_browsing::AdPopupTrigger* ad_popup_trigger =
+        safe_browsing::AdPopupTrigger::FromWebContents(web_contents());
+
+    // Give the trigger a test task runner that we can synchronize on.
+    ad_popup_trigger->SetTaskRunnerForTest(task_runner_);
+  }
+
+  // Returns the final RenderFrameHost after navigation commits.
+  RenderFrameHost* NavigateFrame(const std::string& url,
+                                 RenderFrameHost* frame) {
+    GURL gurl(url);
+    auto navigation_simulator =
+        NavigationSimulator::CreateRendererInitiated(gurl, frame);
+    navigation_simulator->Commit();
+    return navigation_simulator->GetFinalRenderFrameHost();
+  }
+
+  // Returns the final RenderFrameHost after navigation commits.
+  RenderFrameHost* NavigateMainFrame(const std::string& url) {
+    return NavigateFrame(url, web_contents()->GetMainFrame());
+  }
+
+  // Returns the final RenderFrameHost after navigation commits.
+  RenderFrameHost* CreateAndNavigatePopup(const std::string& url,
+                                          const std::string& frame_name,
+                                          RenderFrameHost* parent) {
+    RenderFrameHost* popup_opener_frame =
+        RenderFrameHostTester::For(parent)->AppendChild(frame_name);
+    RenderFrameHost* final_frame_host = NavigateFrame(url, popup_opener_frame);
+    // Call the trigger's PopupWasBlocked event handler directly since it
+    // doesn't happen as part of the navigation. This should check if the frame
+    // opening the popup is an ad.
+    safe_browsing::AdPopupTrigger::FromWebContents(web_contents())
+        ->PopupWasBlocked(final_frame_host);
+    return final_frame_host;
+  }
+
+  void WaitForTaskRunnerIdle() {
+    task_runner_->RunUntilIdle();
+    base::RunLoop().RunUntilIdle();
+  }
+
+  MockTriggerManager* get_trigger_manager() { return &trigger_manager_; }
+  base::HistogramTester* get_histograms() { return &histograms_; }
+
+ private:
+  TestingPrefServiceSimple prefs_;
+  MockTriggerManager trigger_manager_;
+  base::HistogramTester histograms_;
+  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+};
+
+TEST_F(AdPopupTriggerTest, PopupWithAds) {
+  // Make sure the trigger fires when there are ads on the page.
+  CreateTrigger();
+  EXPECT_CALL(*get_trigger_manager(),
+              StartCollectingThreatDetailsWithReason(
+                  TriggerType::AD_POPUP, web_contents(), _, _, _, _, _))
+      .Times(1)
+      .WillRepeatedly(Return(true));
+  EXPECT_CALL(*get_trigger_manager(),
+              FinishCollectingThreatDetails(TriggerType::AD_POPUP,
+                                            web_contents(), _, _, _, _))
+      .Times(1);
+
+  // This page contains two popups - one originating from an ad subframe and one
+  // from a non ad subframe.
+  RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
+  CreateAndNavigatePopup(kNonAdUrl, kNonAdName, main_frame);
+  CreateAndNavigatePopup(kNonAdUrl, kAdName, main_frame);
+
+  // Wait for any posted tasks to finish.
+  WaitForTaskRunnerIdle();
+
+  // Two popup navigations, one will cause a report
+  get_histograms()->ExpectBucketCount(kAdPopupTriggerActionMetricName,
+                                      AdPopupTriggerAction::POPUP_CHECK, 2);
+  get_histograms()->ExpectBucketCount(kAdPopupTriggerActionMetricName,
+                                      AdPopupTriggerAction::POPUP_REPORTED, 1);
+  get_histograms()->ExpectBucketCount(kAdPopupTriggerActionMetricName,
+                                      AdPopupTriggerAction::POPUP_NO_GOOGLE_AD,
+                                      1);
+}
+
+// TODO(https://crbug.com/1009917): Fix flakes on Windows bots.
+#if defined(OS_WIN)
+#define MAYBE_ReportRejectedByTriggerManager \
+  DISABLED_ReportRejectedByTriggerManager
+#else
+#define MAYBE_ReportRejectedByTriggerManager ReportRejectedByTriggerManager
+#endif
+TEST_F(AdPopupTriggerTest, MAYBE_ReportRejectedByTriggerManager) {
+  // If the trigger manager rejects the report, we don't try to finish/send the
+  // report.
+  CreateTrigger();
+  EXPECT_CALL(*get_trigger_manager(),
+              StartCollectingThreatDetailsWithReason(_, _, _, _, _, _, _))
+      .Times(2);
+  EXPECT_CALL(*get_trigger_manager(),
+              FinishCollectingThreatDetails(_, _, _, _, _, _))
+      .Times(0);
+
+  RenderFrameHost* main_frame = NavigateMainFrame(kAdUrl);
+  CreateAndNavigatePopup(kAdUrl, kAdName, main_frame);
+  CreateAndNavigatePopup(kNonAdUrl, kAdName, main_frame);
+
+  WaitForTaskRunnerIdle();
+
+  get_histograms()->ExpectBucketCount(kAdPopupTriggerActionMetricName,
+                                      AdPopupTriggerAction::POPUP_CHECK, 2);
+  get_histograms()->ExpectBucketCount(kAdPopupTriggerActionMetricName,
+                                      AdPopupTriggerAction::POPUP_REPORTED, 0);
+  get_histograms()->ExpectBucketCount(
+      kAdPopupTriggerActionMetricName,
+      AdPopupTriggerAction::POPUP_COULD_NOT_START_REPORT, 2);
+}
+
+TEST_F(AdPopupTriggerTest, DISABLED_PopupWithNoAds) {
+  // Make sure the trigger doesn't fire when there are no ads on the page.
+  CreateTrigger();
+  EXPECT_CALL(*get_trigger_manager(),
+              StartCollectingThreatDetailsWithReason(_, _, _, _, _, _, _))
+      .Times(0);
+  EXPECT_CALL(*get_trigger_manager(),
+              FinishCollectingThreatDetails(_, _, _, _, _, _))
+      .Times(0);
+
+  RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
+  CreateAndNavigatePopup(kNonAdUrl, kNonAdName, main_frame);
+  CreateAndNavigatePopup(kNonAdUrl, kNonAdName, main_frame);
+
+  get_histograms()->ExpectBucketCount(kAdPopupTriggerActionMetricName,
+                                      AdPopupTriggerAction::POPUP_CHECK, 2);
+  get_histograms()->ExpectBucketCount(kAdPopupTriggerActionMetricName,
+                                      AdPopupTriggerAction::POPUP_NO_GOOGLE_AD,
+                                      2);
+}
+
+TEST_F(AdPopupTriggerTest, PopupNotFromAdForPageWithAd) {
+  // Make sure that no report is generated when there is an ad on the page but
+  // the popup is caused from a different frame.
+  CreateTrigger();
+  EXPECT_CALL(*get_trigger_manager(),
+              StartCollectingThreatDetailsWithReason(_, _, _, _, _, _, _))
+      .Times(0);
+  EXPECT_CALL(*get_trigger_manager(),
+              FinishCollectingThreatDetails(_, _, _, _, _, _))
+      .Times(0);
+
+  RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
+  RenderFrameHostTester::For(main_frame)->AppendChild(kAdName);
+  CreateAndNavigatePopup(kNonAdUrl, kNonAdName, main_frame);
+
+  // Two navigations (main frame, one subframe), each with no ad.
+  get_histograms()->ExpectBucketCount(kAdPopupTriggerActionMetricName,
+                                      AdPopupTriggerAction::POPUP_CHECK, 1);
+  get_histograms()->ExpectBucketCount(kAdPopupTriggerActionMetricName,
+                                      AdPopupTriggerAction::POPUP_NO_GOOGLE_AD,
+                                      1);
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/content/triggers/ad_redirect_trigger.cc b/components/safe_browsing/content/triggers/ad_redirect_trigger.cc
new file mode 100644
index 0000000..3e01b3d
--- /dev/null
+++ b/components/safe_browsing/content/triggers/ad_redirect_trigger.cc
@@ -0,0 +1,153 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/content/triggers/ad_redirect_trigger.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/feature_list.h"
+#include "base/metrics/field_trial_params.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/rand_util.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/task/post_task.h"
+#include "components/safe_browsing/content/triggers/trigger_util.h"
+#include "components/safe_browsing/core/features.h"
+#include "components/safe_browsing/core/triggers/trigger_manager.h"
+#include "components/safe_browsing/core/triggers/trigger_throttler.h"
+#include "components/security_interstitials/content/unsafe_resource.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/web_contents.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/simple_url_loader.h"
+
+namespace safe_browsing {
+
+// Number of milliseconds to allow data collection to run before sending a
+// report (since this trigger runs in the background).
+const int64_t kAdRedirectCollectionPeriodMilliseconds = 5000;
+
+// Range of number of milliseconds to wait after a page finished loading before
+// starting a report. Allows ads which load in the background to finish loading.
+const int64_t kMaxAdRedirectCollectionStartDelayMilliseconds = 5000;
+const int64_t kMinAdRedirectCollectionStartDelayMilliseconds = 500;
+
+// Metric for tracking what the Ad Redirect trigger does on each navigation.
+const char kAdRedirectTriggerActionMetricName[] =
+    "SafeBrowsing.Triggers.AdRedirect.Action";
+
+namespace {
+
+void RecordAdRedirectTriggerAction(AdRedirectTriggerAction action) {
+  UMA_HISTOGRAM_ENUMERATION(kAdRedirectTriggerActionMetricName, action);
+}
+
+}  // namespace
+
+AdRedirectTrigger::AdRedirectTrigger(
+    content::WebContents* web_contents,
+    TriggerManager* trigger_manager,
+    PrefService* prefs,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    history::HistoryService* history_service)
+    : web_contents_(web_contents),
+      start_report_delay_ms_(
+          base::RandInt(kMinAdRedirectCollectionStartDelayMilliseconds,
+                        kMaxAdRedirectCollectionStartDelayMilliseconds)),
+      finish_report_delay_ms_(kAdRedirectCollectionPeriodMilliseconds),
+      trigger_manager_(trigger_manager),
+      prefs_(prefs),
+      url_loader_factory_(url_loader_factory),
+      history_service_(history_service),
+      task_runner_(
+          base::CreateSingleThreadTaskRunner({content::BrowserThread::UI})) {}
+
+AdRedirectTrigger::~AdRedirectTrigger() {}
+
+// static
+void AdRedirectTrigger::CreateForWebContents(
+    content::WebContents* web_contents,
+    TriggerManager* trigger_manager,
+    PrefService* prefs,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    history::HistoryService* history_service) {
+  DCHECK(web_contents);
+  if (!FromWebContents(web_contents)) {
+    web_contents->SetUserData(UserDataKey(),
+                              base::WrapUnique(new AdRedirectTrigger(
+                                  web_contents, trigger_manager, prefs,
+                                  url_loader_factory, history_service)));
+  }
+}
+
+void AdRedirectTrigger::CreateAdRedirectReport() {
+  SBErrorOptions error_options =
+      TriggerManager::GetSBErrorDisplayOptions(*prefs_, web_contents_);
+  security_interstitials::UnsafeResource resource;
+  resource.threat_type = SB_THREAT_TYPE_BLOCKED_AD_REDIRECT;
+  resource.url = web_contents_->GetURL();
+  resource.web_contents_getter = resource.GetWebContentsGetter(
+      web_contents_->GetMainFrame()->GetProcess()->GetID(),
+      web_contents_->GetMainFrame()->GetRoutingID());
+  TriggerManagerReason reason;
+  if (!trigger_manager_->StartCollectingThreatDetailsWithReason(
+          TriggerType::AD_REDIRECT, web_contents_, resource,
+          url_loader_factory_, history_service_, error_options, &reason)) {
+    if (reason == TriggerManagerReason::DAILY_QUOTA_EXCEEDED) {
+      RecordAdRedirectTriggerAction(
+          AdRedirectTriggerAction::REDIRECT_DAILY_QUOTA_EXCEEDED);
+    } else {
+      RecordAdRedirectTriggerAction(
+          AdRedirectTriggerAction::REDIRECT_COULD_NOT_START_REPORT);
+    }
+    return;
+  }
+  // Call into TriggerManager to finish the reports after a short delay. Any
+  // ads that are detected during this delay will be rejected by TriggerManager
+  // because a report is already being collected, so we won't send multiple
+  // reports for the same page.
+  task_runner_->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(
+          IgnoreResult(&TriggerManager::FinishCollectingThreatDetails),
+          base::Unretained(trigger_manager_), TriggerType::AD_REDIRECT,
+          base::Unretained(web_contents_), base::TimeDelta(),
+          /*did_proceed=*/false, /*num_visits=*/0, error_options),
+      base::TimeDelta::FromMilliseconds(finish_report_delay_ms_));
+  RecordAdRedirectTriggerAction(AdRedirectTriggerAction::AD_REDIRECT);
+}
+
+void AdRedirectTrigger::OnDidBlockNavigation(const GURL& initiator_url) {
+  RecordAdRedirectTriggerAction(AdRedirectTriggerAction::REDIRECT_CHECK);
+  content::RenderFrameHost* initiator_frame =
+      web_contents_->GetOriginalOpener();
+  // Use focused frame as proxy if there is no opener.
+  if (!initiator_frame)
+    initiator_frame = web_contents_->GetFocusedFrame();
+  if (!DetectGoogleAd(initiator_frame, initiator_url)) {
+    RecordAdRedirectTriggerAction(
+        AdRedirectTriggerAction::REDIRECT_NO_GOOGLE_AD);
+    return;
+  }
+  // Create a report after a short delay.
+  task_runner_->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(&AdRedirectTrigger::CreateAdRedirectReport,
+                     weak_ptr_factory_.GetWeakPtr()),
+      base::TimeDelta::FromMilliseconds(start_report_delay_ms_));
+}
+
+void AdRedirectTrigger::SetDelayForTest(int start_report_delay,
+                                        int finish_report_delay) {
+  start_report_delay_ms_ = start_report_delay;
+  finish_report_delay_ms_ = finish_report_delay;
+}
+
+WEB_CONTENTS_USER_DATA_KEY_IMPL(AdRedirectTrigger)
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/content/triggers/ad_redirect_trigger.h b/components/safe_browsing/content/triggers/ad_redirect_trigger.h
new file mode 100644
index 0000000..641c7fa
--- /dev/null
+++ b/components/safe_browsing/content/triggers/ad_redirect_trigger.h
@@ -0,0 +1,131 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPONENTS_SAFE_BROWSING_CONTENT_TRIGGERS_AD_REDIRECT_TRIGGER_H_
+#define COMPONENTS_SAFE_BROWSING_CONTENT_TRIGGERS_AD_REDIRECT_TRIGGER_H_
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "content/public/browser/web_contents_user_data.h"
+
+class PrefService;
+
+namespace history {
+class HistoryService;
+}
+
+namespace network {
+class SharedURLLoaderFactory;
+}  // namespace network
+
+namespace safe_browsing {
+class TriggerManager;
+
+using FrameTreeNodeId = int;
+
+// Metric for tracking what the Ad Redirect trigger does on each navigation.
+extern const char kAdRedirectTriggerActionMetricName[];
+
+// Actions performed by this trigger. These values are written to logs. New enum
+// values can be added, but existing enums must never be renumbered or deleted
+// and reused.
+enum class AdRedirectTriggerAction {
+  // A redirect event occurred that caused the trigger to perform its checks.
+  REDIRECT_CHECK = 0,
+  // A redirect caused by an ad was detected and a report was collected.
+  AD_REDIRECT = 1,
+  // No google ad was detected from the frame causing an autoredirect.
+  REDIRECT_NO_GOOGLE_AD = 2,
+  // An ad was detected on the page causing the redirect and could have been
+  // reported, but the trigger manager rejected the report (eg: because user is
+  // incognito or has not opted into extended reporting).
+  REDIRECT_COULD_NOT_START_REPORT = 3,
+  // Daily quota for blocked ad redirect reporting was met.
+  REDIRECT_DAILY_QUOTA_EXCEEDED = 4,
+  // New events must be added before kMaxValue, and the value of kMaxValue
+  // updated.
+  kMaxValue = REDIRECT_DAILY_QUOTA_EXCEEDED
+};
+
+// This class if notified when a redirect navigation is blocked in the renderer
+// because there was no user gesture(an autoredirect). If the frame causing the
+// autoredirect navigation is a Google Ad frame, this class sends a report to
+// Google.
+// Design doc: go/extending-chrind-q2-2019-1
+class AdRedirectTrigger
+    : public content::WebContentsUserData<AdRedirectTrigger> {
+ public:
+  ~AdRedirectTrigger() override;
+
+  // |web_contents| is the WebContent of the page that a redirect event took
+  // place on. |trigger_manager| ensures the trigger is only fired when
+  // appropriate and keeps track of how often the trigger is fired. |prefs|
+  // allows us to check that the user has opted in to Extended Reporting.
+  // |url_loader_factory| allows us to access mojom::URLLoaderFactory.
+  // |history_service| records page titles, visit times, and favicons, as well
+  // as information about downloads.
+  static void CreateForWebContents(
+      content::WebContents* web_contents,
+      TriggerManager* trigger_manager,
+      PrefService* prefs,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      history::HistoryService* history_service);
+
+  // This method is called on the browser thread when a frame tries to navigate
+  // its top level frame from the |initiator_url| without ever having received a
+  // user gesture. If the frame causing the redirect navigation is a Google Ad
+  // frame a report is sent to Google.
+  void OnDidBlockNavigation(const GURL& initiator_url);
+
+ private:
+  friend class AdRedirectTriggerBrowserTest;
+  friend class content::WebContentsUserData<AdRedirectTrigger>;
+
+  AdRedirectTrigger(
+      content::WebContents* web_contents,
+      TriggerManager* trigger_manager,
+      PrefService* prefs,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      history::HistoryService* history_service);
+
+  // Called to create an ad redirect report.
+  void CreateAdRedirectReport();
+
+  // Sets report delay for test.
+  void SetDelayForTest(int start_report_delay, int finish_report_delay);
+
+  // WebContents of the current tab.
+  content::WebContents* web_contents_;
+
+  // The delay (in milliseconds) to wait before starting a report. Can be
+  // ovewritten for tests.
+  int64_t start_report_delay_ms_;
+
+  // The delay (in milliseconds) to wait before finishing a report. Can be
+  // overwritten for tests.
+  int64_t finish_report_delay_ms_;
+
+  // TriggerManager gets called if this trigger detects an autoredirect caused
+  // by a page with an ad and wants to collect some data about it. Not owned.
+  TriggerManager* trigger_manager_;
+
+  PrefService* prefs_;
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
+  history::HistoryService* history_service_;
+
+  // Task runner for posting delayed tasks. Normally set to the runner for the
+  // UI thread, but can be overwritten for tests.
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
+  base::WeakPtrFactory<AdRedirectTrigger> weak_ptr_factory_{this};
+
+  WEB_CONTENTS_USER_DATA_KEY_DECL();
+
+  DISALLOW_COPY_AND_ASSIGN(AdRedirectTrigger);
+};
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CONTENT_TRIGGERS_AD_REDIRECT_TRIGGER_H_
diff --git a/components/safe_browsing/content/triggers/ad_sampler_trigger.cc b/components/safe_browsing/content/triggers/ad_sampler_trigger.cc
new file mode 100644
index 0000000..c6fa19a
--- /dev/null
+++ b/components/safe_browsing/content/triggers/ad_sampler_trigger.cc
@@ -0,0 +1,190 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/content/triggers/ad_sampler_trigger.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/feature_list.h"
+#include "base/metrics/field_trial_params.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/rand_util.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/task/post_task.h"
+#include "components/safe_browsing/content/triggers/trigger_util.h"
+#include "components/safe_browsing/core/features.h"
+#include "components/safe_browsing/core/triggers/trigger_manager.h"
+#include "components/safe_browsing/core/triggers/trigger_throttler.h"
+#include "components/security_interstitials/content/unsafe_resource.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/web_contents.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/simple_url_loader.h"
+
+namespace safe_browsing {
+
+// Param name of the denominator for controlling sampling frequency.
+const char kAdSamplerFrequencyDenominatorParam[] =
+    "safe_browsing_ad_sampler_frequency_denominator";
+
+// Default frequency denominator for the ad sampler.
+const size_t kAdSamplerDefaultFrequency = 1000;
+
+// A frequency denominator with this value indicates sampling is disabled.
+const size_t kAdSamplerFrequencyDisabled = 0;
+
+// Number of milliseconds to allow data collection to run before sending a
+// report (since this trigger runs in the background).
+const int64_t kAdSampleCollectionPeriodMilliseconds = 5000;
+
+// Range of number of milliseconds to wait after a page finished loading before
+// starting a report. Allows ads which load in the background to finish loading.
+const int64_t kMaxAdSampleCollectionStartDelayMilliseconds = 5000;
+const int64_t kMinAdSampleCollectionStartDelayMilliseconds = 500;
+
+// Metric for tracking what the Ad Sampler trigger does on each navigation.
+const char kAdSamplerTriggerActionMetricName[] =
+    "SafeBrowsing.Triggers.AdSampler.Action";
+
+namespace {
+
+size_t GetSamplerFrequencyDenominator() {
+  if (!base::FeatureList::IsEnabled(kAdSamplerTriggerFeature))
+    return kAdSamplerDefaultFrequency;
+
+  const std::string sampler_frequency_denominator =
+      base::GetFieldTrialParamValueByFeature(
+          kAdSamplerTriggerFeature, kAdSamplerFrequencyDenominatorParam);
+  int result;
+  if (!base::StringToInt(sampler_frequency_denominator, &result))
+    return kAdSamplerDefaultFrequency;
+
+  return result;
+}
+
+bool ShouldSampleAd(const size_t frequency_denominator) {
+  return frequency_denominator != kAdSamplerFrequencyDisabled &&
+         (base::RandUint64() % frequency_denominator) == 0;
+}
+
+}  // namespace
+
+AdSamplerTrigger::AdSamplerTrigger(
+    content::WebContents* web_contents,
+    TriggerManager* trigger_manager,
+    PrefService* prefs,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    history::HistoryService* history_service)
+    : content::WebContentsObserver(web_contents),
+      sampler_frequency_denominator_(GetSamplerFrequencyDenominator()),
+      start_report_delay_ms_(
+          base::RandInt(kMinAdSampleCollectionStartDelayMilliseconds,
+                        kMaxAdSampleCollectionStartDelayMilliseconds)),
+      finish_report_delay_ms_(kAdSampleCollectionPeriodMilliseconds),
+      trigger_manager_(trigger_manager),
+      prefs_(prefs),
+      url_loader_factory_(url_loader_factory),
+      history_service_(history_service),
+      task_runner_(
+          base::CreateSingleThreadTaskRunner({content::BrowserThread::UI})) {}
+
+AdSamplerTrigger::~AdSamplerTrigger() {}
+
+// static
+void AdSamplerTrigger::CreateForWebContents(
+    content::WebContents* web_contents,
+    TriggerManager* trigger_manager,
+    PrefService* prefs,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    history::HistoryService* history_service) {
+  DCHECK(web_contents);
+  if (!FromWebContents(web_contents)) {
+    web_contents->SetUserData(UserDataKey(),
+                              base::WrapUnique(new AdSamplerTrigger(
+                                  web_contents, trigger_manager, prefs,
+                                  url_loader_factory, history_service)));
+  }
+}
+
+void AdSamplerTrigger::DidFinishLoad(
+    content::RenderFrameHost* render_frame_host,
+    const GURL& validated_url) {
+  UMA_HISTOGRAM_ENUMERATION(kAdSamplerTriggerActionMetricName, TRIGGER_CHECK,
+                            MAX_ACTIONS);
+  // We are using light-weight ad detection logic here so it's safe to do the
+  // check on each navigation for the sake of metrics.
+  if (!DetectGoogleAd(render_frame_host, validated_url)) {
+    UMA_HISTOGRAM_ENUMERATION(kAdSamplerTriggerActionMetricName,
+                              NO_SAMPLE_NO_AD, MAX_ACTIONS);
+    return;
+  }
+  if (!ShouldSampleAd(sampler_frequency_denominator_)) {
+    UMA_HISTOGRAM_ENUMERATION(kAdSamplerTriggerActionMetricName,
+                              NO_SAMPLE_AD_SKIPPED_FOR_FREQUENCY, MAX_ACTIONS);
+    return;
+  }
+
+  // Create a report after a short delay. The delay gives more time for ads to
+  // finish loading in the background. This is best-effort.
+  task_runner_->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(&AdSamplerTrigger::CreateAdSampleReport,
+                     weak_ptr_factory_.GetWeakPtr()),
+      base::TimeDelta::FromMilliseconds(start_report_delay_ms_));
+}
+
+void AdSamplerTrigger::CreateAdSampleReport() {
+  SBErrorOptions error_options =
+      TriggerManager::GetSBErrorDisplayOptions(*prefs_, web_contents());
+
+  security_interstitials::UnsafeResource resource;
+  resource.threat_type = SB_THREAT_TYPE_AD_SAMPLE;
+  resource.url = web_contents()->GetURL();
+  resource.web_contents_getter = resource.GetWebContentsGetter(
+      web_contents()->GetMainFrame()->GetProcess()->GetID(),
+      web_contents()->GetMainFrame()->GetRoutingID());
+
+  if (!trigger_manager_->StartCollectingThreatDetails(
+          TriggerType::AD_SAMPLE, web_contents(), resource, url_loader_factory_,
+          history_service_, error_options)) {
+    UMA_HISTOGRAM_ENUMERATION(kAdSamplerTriggerActionMetricName,
+                              NO_SAMPLE_COULD_NOT_START_REPORT, MAX_ACTIONS);
+    return;
+  }
+
+  // Call into TriggerManager to finish the reports after a short delay. Any
+  // ads that are detected during this delay will be rejected by TriggerManager
+  // because a report is already being collected, so we won't send multiple
+  // reports for the same page.
+  task_runner_->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(
+          IgnoreResult(&TriggerManager::FinishCollectingThreatDetails),
+          base::Unretained(trigger_manager_), TriggerType::AD_SAMPLE,
+          base::Unretained(web_contents()), base::TimeDelta(),
+          /*did_proceed=*/false, /*num_visits=*/0, error_options),
+      base::TimeDelta::FromMilliseconds(finish_report_delay_ms_));
+
+  UMA_HISTOGRAM_ENUMERATION(kAdSamplerTriggerActionMetricName, AD_SAMPLED,
+                            MAX_ACTIONS);
+}
+
+void AdSamplerTrigger::SetSamplerFrequencyForTest(size_t denominator) {
+  sampler_frequency_denominator_ = denominator;
+}
+
+void AdSamplerTrigger::SetTaskRunnerForTest(
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+  task_runner_ = task_runner;
+}
+
+WEB_CONTENTS_USER_DATA_KEY_IMPL(AdSamplerTrigger)
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/content/triggers/ad_sampler_trigger.h b/components/safe_browsing/content/triggers/ad_sampler_trigger.h
new file mode 100644
index 0000000..589bb1c
--- /dev/null
+++ b/components/safe_browsing/content/triggers/ad_sampler_trigger.h
@@ -0,0 +1,131 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CONTENT_TRIGGERS_AD_SAMPLER_TRIGGER_H_
+#define COMPONENTS_SAFE_BROWSING_CONTENT_TRIGGERS_AD_SAMPLER_TRIGGER_H_
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_contents_user_data.h"
+
+class PrefService;
+
+namespace history {
+class HistoryService;
+}
+
+namespace network {
+class SharedURLLoaderFactory;
+}  // namespace network
+
+namespace safe_browsing {
+class TriggerManager;
+
+// Param name of the denominator for controlling sampling frequency.
+extern const char kAdSamplerFrequencyDenominatorParam[];
+
+// Default frequency for the ad sampler, if not configured in Finch.
+extern const size_t kAdSamplerDefaultFrequency;
+
+// A frequency denominator with this value indicates sampling is disabled.
+extern const size_t kAdSamplerFrequencyDisabled;
+
+// Metric for tracking what the Ad Sampler trigger does on each navigation.
+extern const char kAdSamplerTriggerActionMetricName[];
+
+// Actions performed by this trigger. These values are written to logs. New enum
+// values can be added, but existing enums must never be renumbered or deleted
+// and reused.
+enum AdSamplerTriggerAction {
+  // An event occurred that caused the trigger to perform its checks.
+  TRIGGER_CHECK = 0,
+  // An ad was detected and a sample was collected.
+  AD_SAMPLED = 1,
+  // An ad was detected but no sample was taken to honour sampling frequency.
+  NO_SAMPLE_AD_SKIPPED_FOR_FREQUENCY = 2,
+  // No ad was detected.
+  NO_SAMPLE_NO_AD = 3,
+  // An ad was detected and could have been sampled, but the trigger manager
+  // rejected the report (eg: because a report was already in progress).
+  NO_SAMPLE_COULD_NOT_START_REPORT = 4,
+  // New actions must be added before MAX_ACTIONS.
+  MAX_ACTIONS
+};
+
+// This class periodically checks for Google ads on the page and may decide to
+// send a report to Google with the ad's structure for further analysis.
+class AdSamplerTrigger : public content::WebContentsObserver,
+                         public content::WebContentsUserData<AdSamplerTrigger> {
+ public:
+  ~AdSamplerTrigger() override;
+
+  static void CreateForWebContents(
+      content::WebContents* web_contents,
+      TriggerManager* trigger_manager,
+      PrefService* prefs,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      history::HistoryService* history_service);
+
+  // content::WebContentsObserver implementation.
+  void DidFinishLoad(content::RenderFrameHost* render_frame_host,
+                     const GURL& validated_url) override;
+
+ private:
+  friend class AdSamplerTriggerTest;
+  friend class content::WebContentsUserData<AdSamplerTrigger>;
+  FRIEND_TEST_ALL_PREFIXES(AdSamplerTriggerTestFinch,
+                           FrequencyDenominatorFeature);
+
+  AdSamplerTrigger(
+      content::WebContents* web_contents,
+      TriggerManager* trigger_manager,
+      PrefService* prefs,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      history::HistoryService* history_service);
+
+  // Called to create an ad sample report.
+  void CreateAdSampleReport();
+
+  // Sets |sampler_frequency_denominator_| for tests.
+  void SetSamplerFrequencyForTest(size_t denominator);
+
+  // Sets a task runner to use for tests.
+  void SetTaskRunnerForTest(
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+
+  // Ad samples will be collected with frequency
+  // 1/|sampler_frequency_denominator_|
+  size_t sampler_frequency_denominator_;
+
+  // The delay (in milliseconds) to wait before starting a report. Can be
+  // ovewritten for tests.
+  int64_t start_report_delay_ms_;
+
+  // The delay (in milliseconds) to wait before finishing a report. Can be
+  // overwritten for tests.
+  int64_t finish_report_delay_ms_;
+
+  // TriggerManager gets called if this trigger detects an ad and wants to
+  // collect some data about it. Not owned.
+  TriggerManager* trigger_manager_;
+
+  PrefService* prefs_;
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
+  history::HistoryService* history_service_;
+
+  // Task runner for posting delayed tasks. Normally set to the runner for the
+  // UI thread, but can be overwritten for tests.
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
+  base::WeakPtrFactory<AdSamplerTrigger> weak_ptr_factory_{this};
+
+  WEB_CONTENTS_USER_DATA_KEY_DECL();
+
+  DISALLOW_COPY_AND_ASSIGN(AdSamplerTrigger);
+};
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CONTENT_TRIGGERS_AD_SAMPLER_TRIGGER_H_
diff --git a/components/safe_browsing/content/triggers/ad_sampler_trigger_unittest.cc b/components/safe_browsing/content/triggers/ad_sampler_trigger_unittest.cc
new file mode 100644
index 0000000..6b3eca6
--- /dev/null
+++ b/components/safe_browsing/content/triggers/ad_sampler_trigger_unittest.cc
@@ -0,0 +1,235 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/content/triggers/ad_sampler_trigger.h"
+
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/test/test_simple_task_runner.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/safe_browsing/content/triggers/mock_trigger_manager.h"
+#include "components/safe_browsing/core/features.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_task_environment.h"
+#include "content/public/test/navigation_simulator.h"
+#include "content/public/test/test_renderer_host.h"
+#include "testing/gmock/include/gmock/gmock-generated-function-mockers.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using content::NavigationSimulator;
+using content::RenderFrameHost;
+using content::RenderFrameHostTester;
+
+using testing::_;
+using testing::Return;
+
+namespace safe_browsing {
+
+namespace {
+const char kAdUrl[] = "https://tpc.googlesyndication.com/safeframe/1";
+const char kNonAdUrl[] = "https://foo.com/";
+const char kAdName[] = "google_ads_iframe_1";
+const char kNonAdName[] = "foo";
+}  // namespace
+
+class AdSamplerTriggerTest : public content::RenderViewHostTestHarness {
+ public:
+  AdSamplerTriggerTest() : task_runner_(new base::TestSimpleTaskRunner) {}
+  ~AdSamplerTriggerTest() override {}
+
+  void SetUp() override {
+    content::RenderViewHostTestHarness::SetUp();
+
+    // Enable any prefs required for the trigger to run.
+    safe_browsing::RegisterProfilePrefs(prefs_.registry());
+    prefs_.SetBoolean(prefs::kSafeBrowsingExtendedReportingOptInAllowed, true);
+    prefs_.SetBoolean(prefs::kSafeBrowsingScoutReportingEnabled, true);
+  }
+
+  void CreateTriggerWithFrequency(const size_t denominator) {
+    safe_browsing::AdSamplerTrigger::CreateForWebContents(
+        web_contents(), &trigger_manager_, &prefs_, nullptr, nullptr);
+
+    safe_browsing::AdSamplerTrigger* ad_sampler =
+        safe_browsing::AdSamplerTrigger::FromWebContents(web_contents());
+    ad_sampler->SetSamplerFrequencyForTest(denominator);
+
+    // Give the trigger a test task runner that we can synchronize on.
+    ad_sampler->SetTaskRunnerForTest(task_runner_);
+  }
+
+  // Returns the final RenderFrameHost after navigation commits.
+  RenderFrameHost* NavigateFrame(const std::string& url,
+                                 RenderFrameHost* frame) {
+    return NavigationSimulator::NavigateAndCommitFromDocument(GURL(url), frame);
+  }
+
+  // Returns the final RenderFrameHost after navigation commits.
+  RenderFrameHost* NavigateMainFrame(const std::string& url) {
+    return NavigateFrame(url, web_contents()->GetMainFrame());
+  }
+
+  // Returns the final RenderFrameHost after navigation commits.
+  RenderFrameHost* CreateAndNavigateSubFrame(const std::string& url,
+                                             const std::string& frame_name,
+                                             RenderFrameHost* parent) {
+    RenderFrameHost* subframe =
+        RenderFrameHostTester::For(parent)->AppendChild(frame_name);
+    return NavigateFrame(url, subframe);
+  }
+
+  void WaitForTaskRunnerIdle() {
+    task_runner_->RunUntilIdle();
+    base::RunLoop().RunUntilIdle();
+  }
+
+  MockTriggerManager* get_trigger_manager() { return &trigger_manager_; }
+  base::HistogramTester* get_histograms() { return &histograms_; }
+
+ private:
+  TestingPrefServiceSimple prefs_;
+  MockTriggerManager trigger_manager_;
+  base::HistogramTester histograms_;
+  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+};
+
+TEST_F(AdSamplerTriggerTest, TriggerDisabledBySamplingFrequency) {
+  // Make sure the trigger doesn't fire when the sampling frequency is set to
+  // zero, which disables the trigger.
+  CreateTriggerWithFrequency(kAdSamplerFrequencyDisabled);
+  EXPECT_CALL(*get_trigger_manager(),
+              StartCollectingThreatDetails(_, _, _, _, _, _))
+      .Times(0);
+  EXPECT_CALL(*get_trigger_manager(),
+              FinishCollectingThreatDetails(_, _, _, _, _, _))
+      .Times(0);
+
+  // This page contains two ads - one identifiable by its URL, the other by the
+  // name of the frame.
+  RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
+  CreateAndNavigateSubFrame(kAdUrl, kNonAdName, main_frame);
+  CreateAndNavigateSubFrame(kNonAdUrl, kAdName, main_frame);
+
+  // Three navigations (main frame, two subframes). One frame with no ads, and
+  // two skipped ad samples.
+  get_histograms()->ExpectBucketCount(kAdSamplerTriggerActionMetricName,
+                                      TRIGGER_CHECK, 3);
+  get_histograms()->ExpectBucketCount(kAdSamplerTriggerActionMetricName,
+                                      NO_SAMPLE_NO_AD, 1);
+  get_histograms()->ExpectBucketCount(kAdSamplerTriggerActionMetricName,
+                                      NO_SAMPLE_AD_SKIPPED_FOR_FREQUENCY, 2);
+}
+
+TEST_F(AdSamplerTriggerTest, DISABLED_PageWithNoAds) {
+  // Make sure the trigger doesn't fire when there are no ads on the page.
+  CreateTriggerWithFrequency(/*denominator=*/1);
+
+  EXPECT_CALL(*get_trigger_manager(),
+              StartCollectingThreatDetails(_, _, _, _, _, _))
+      .Times(0);
+  EXPECT_CALL(*get_trigger_manager(),
+              FinishCollectingThreatDetails(_, _, _, _, _, _))
+      .Times(0);
+
+  RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
+  CreateAndNavigateSubFrame(kNonAdUrl, kNonAdName, main_frame);
+  CreateAndNavigateSubFrame(kNonAdUrl, kNonAdName, main_frame);
+
+  // Three navigations (main frame, two subframes), each with no ad.
+  get_histograms()->ExpectBucketCount(kAdSamplerTriggerActionMetricName,
+                                      TRIGGER_CHECK, 3);
+  get_histograms()->ExpectBucketCount(kAdSamplerTriggerActionMetricName,
+                                      NO_SAMPLE_NO_AD, 3);
+}
+
+TEST_F(AdSamplerTriggerTest, PageWithMultipleAds) {
+  // Make sure the trigger fires when there are ads on the page. We expect
+  // one call for each ad detected.
+  CreateTriggerWithFrequency(/*denominator=*/1);
+  EXPECT_CALL(*get_trigger_manager(),
+              StartCollectingThreatDetails(TriggerType::AD_SAMPLE,
+                                           web_contents(), _, _, _, _))
+      .Times(2)
+      .WillRepeatedly(Return(true));
+  EXPECT_CALL(*get_trigger_manager(),
+              FinishCollectingThreatDetails(TriggerType::AD_SAMPLE,
+                                            web_contents(), _, _, _, _))
+      .Times(2);
+
+  // This page contains two ads - one identifiable by its URL, the other by the
+  // name of the frame.
+  RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
+  CreateAndNavigateSubFrame(kAdUrl, kNonAdName, main_frame);
+  CreateAndNavigateSubFrame(kNonAdUrl, kAdName, main_frame);
+
+  // Wait for any posted tasks to finish.
+  WaitForTaskRunnerIdle();
+
+  // Three navigations (main frame, two subframes). Main frame with no ads, and
+  // two sampled ads
+  get_histograms()->ExpectBucketCount(kAdSamplerTriggerActionMetricName,
+                                      TRIGGER_CHECK, 3);
+  get_histograms()->ExpectBucketCount(kAdSamplerTriggerActionMetricName,
+                                      NO_SAMPLE_NO_AD, 1);
+  get_histograms()->ExpectBucketCount(kAdSamplerTriggerActionMetricName,
+                                      AD_SAMPLED, 2);
+}
+
+TEST_F(AdSamplerTriggerTest, ReportRejectedByTriggerManager) {
+  // If the trigger manager rejects the report, we don't try to finish/send the
+  // report.
+  CreateTriggerWithFrequency(/*denominator=*/1);
+  EXPECT_CALL(*get_trigger_manager(),
+              StartCollectingThreatDetails(TriggerType::AD_SAMPLE,
+                                           web_contents(), _, _, _, _))
+      .Times(1)
+      .WillOnce(Return(false));
+  EXPECT_CALL(*get_trigger_manager(),
+              FinishCollectingThreatDetails(TriggerType::AD_SAMPLE,
+                                            web_contents(), _, _, _, _))
+      .Times(0);
+
+  // One ad on the page, identified by its URL.
+  RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
+  CreateAndNavigateSubFrame(kAdUrl, kNonAdName, main_frame);
+  CreateAndNavigateSubFrame(kNonAdUrl, kNonAdName, main_frame);
+
+  // Wait for any posted tasks to finish.
+  WaitForTaskRunnerIdle();
+
+  // Three navigations (main frame, two subframes). Two frames with no ads, and
+  // one ad rejected by trigger manager.
+  get_histograms()->ExpectBucketCount(kAdSamplerTriggerActionMetricName,
+                                      TRIGGER_CHECK, 3);
+  get_histograms()->ExpectBucketCount(kAdSamplerTriggerActionMetricName,
+                                      NO_SAMPLE_NO_AD, 2);
+  get_histograms()->ExpectBucketCount(kAdSamplerTriggerActionMetricName,
+                                      NO_SAMPLE_COULD_NOT_START_REPORT, 1);
+}
+
+TEST(AdSamplerTriggerTestFinch, FrequencyDenominatorFeature) {
+  // Make sure that setting the frequency denominator via Finch params works as
+  // expected, and that the default frequency is used when no Finch config is
+  // given.
+  content::BrowserTaskEnvironment task_environment;
+  AdSamplerTrigger trigger_default(nullptr, nullptr, nullptr, nullptr, nullptr);
+  EXPECT_EQ(kAdSamplerDefaultFrequency,
+            trigger_default.sampler_frequency_denominator_);
+
+  const size_t kDenominatorInt = 12345;
+
+  base::FieldTrialParams feature_params;
+  feature_params[std::string(
+      safe_browsing::kAdSamplerFrequencyDenominatorParam)] =
+      base::NumberToString(kDenominatorInt);
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeatureWithParameters(
+      safe_browsing::kAdSamplerTriggerFeature, feature_params);
+
+  AdSamplerTrigger trigger_finch(nullptr, nullptr, nullptr, nullptr, nullptr);
+  EXPECT_EQ(kDenominatorInt, trigger_finch.sampler_frequency_denominator_);
+}
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/content/triggers/mock_trigger_manager.cc b/components/safe_browsing/content/triggers/mock_trigger_manager.cc
new file mode 100644
index 0000000..cc19bc0
--- /dev/null
+++ b/components/safe_browsing/content/triggers/mock_trigger_manager.cc
@@ -0,0 +1,14 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/content/triggers/mock_trigger_manager.h"
+
+namespace safe_browsing {
+
+MockTriggerManager::MockTriggerManager()
+    : TriggerManager(nullptr, nullptr, nullptr) {}
+
+MockTriggerManager::~MockTriggerManager() {}
+
+}  // namespace safe_browsing
\ No newline at end of file
diff --git a/components/safe_browsing/content/triggers/mock_trigger_manager.h b/components/safe_browsing/content/triggers/mock_trigger_manager.h
new file mode 100644
index 0000000..843e8f6
--- /dev/null
+++ b/components/safe_browsing/content/triggers/mock_trigger_manager.h
@@ -0,0 +1,52 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CONTENT_TRIGGERS_MOCK_TRIGGER_MANAGER_H_
+#define COMPONENTS_SAFE_BROWSING_CONTENT_TRIGGERS_MOCK_TRIGGER_MANAGER_H_
+
+#include "base/macros.h"
+#include "components/safe_browsing/core/triggers/trigger_manager.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace safe_browsing {
+
+class MockTriggerManager : public TriggerManager {
+ public:
+  MockTriggerManager();
+  ~MockTriggerManager() override;
+
+  MOCK_METHOD6(
+      StartCollectingThreatDetails,
+      bool(TriggerType trigger_type,
+           content::WebContents* web_contents,
+           const security_interstitials::UnsafeResource& resource,
+           scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+           history::HistoryService* history_service,
+           const SBErrorOptions& error_display_options));
+  MOCK_METHOD7(
+      StartCollectingThreatDetailsWithReason,
+      bool(TriggerType trigger_type,
+           content::WebContents* web_contents,
+           const security_interstitials::UnsafeResource& resource,
+           scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+           history::HistoryService* history_service,
+           const SBErrorOptions& error_display_options,
+           TriggerManagerReason* out_reason));
+
+  MOCK_METHOD6(FinishCollectingThreatDetails,
+               bool(TriggerType trigger_type,
+                    content::WebContents* web_contents,
+                    const base::TimeDelta& delay,
+                    bool did_proceed,
+                    int num_visits,
+                    const SBErrorOptions& error_display_options));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockTriggerManager);
+};
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CONTENT_TRIGGERS_MOCK_TRIGGER_MANAGER_H_
diff --git a/components/safe_browsing/content/triggers/suspicious_site_trigger.cc b/components/safe_browsing/content/triggers/suspicious_site_trigger.cc
new file mode 100644
index 0000000..1c6e1e6
--- /dev/null
+++ b/components/safe_browsing/content/triggers/suspicious_site_trigger.cc
@@ -0,0 +1,293 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/content/triggers/suspicious_site_trigger.h"
+
+#include "base/bind.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/single_thread_task_runner.h"
+#include "base/task/post_task.h"
+#include "components/history/core/browser/history_service.h"
+#include "components/prefs/pref_service.h"
+#include "components/safe_browsing/core/triggers/trigger_manager.h"
+#include "components/safe_browsing/core/triggers/trigger_throttler.h"
+#include "components/security_interstitials/content/unsafe_resource.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/web_contents.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+
+namespace safe_browsing {
+
+namespace {
+// Number of milliseconds to allow data collection to run before sending a
+// report (since this trigger runs in the background).
+const int64_t kSuspiciousSiteCollectionPeriodMilliseconds = 5000;
+}  // namespace
+
+const char kSuspiciousSiteTriggerEventMetricName[] =
+    "SafeBrowsing.Triggers.SuspiciousSite.Event";
+
+const char kSuspiciousSiteTriggerReportRejectionMetricName[] =
+    "SafeBrowsing.Triggers.SuspiciousSite.ReportRejectionReason";
+
+const char kSuspiciousSiteTriggerReportDelayStateMetricName[] =
+    "SafeBrowsing.Triggers.SuspiciousSite.DelayTimerState";
+
+void NotifySuspiciousSiteTriggerDetected(
+    const base::RepeatingCallback<content::WebContents*()>&
+        web_contents_getter) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  content::WebContents* web_contents = web_contents_getter.Run();
+  if (web_contents) {
+    safe_browsing::SuspiciousSiteTrigger* trigger =
+        safe_browsing::SuspiciousSiteTrigger::FromWebContents(web_contents);
+    if (trigger)
+      trigger->SuspiciousSiteDetected();
+  }
+}
+
+SuspiciousSiteTrigger::SuspiciousSiteTrigger(
+    content::WebContents* web_contents,
+    TriggerManager* trigger_manager,
+    PrefService* prefs,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    history::HistoryService* history_service,
+    bool monitor_mode)
+    : content::WebContentsObserver(web_contents),
+      finish_report_delay_ms_(kSuspiciousSiteCollectionPeriodMilliseconds),
+      current_state_(monitor_mode ? TriggerState::MONITOR_MODE
+                                  : TriggerState::IDLE),
+      trigger_manager_(trigger_manager),
+      prefs_(prefs),
+      url_loader_factory_(url_loader_factory),
+      history_service_(history_service),
+      task_runner_(
+          base::CreateSingleThreadTaskRunner({content::BrowserThread::UI})) {}
+
+SuspiciousSiteTrigger::~SuspiciousSiteTrigger() {}
+
+// static
+void SuspiciousSiteTrigger::CreateForWebContents(
+    content::WebContents* web_contents,
+    TriggerManager* trigger_manager,
+    PrefService* prefs,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    history::HistoryService* history_service,
+    bool monitor_mode) {
+  if (!FromWebContents(web_contents)) {
+    web_contents->SetUserData(
+        UserDataKey(), base::WrapUnique(new SuspiciousSiteTrigger(
+                           web_contents, trigger_manager, prefs,
+                           url_loader_factory, history_service, monitor_mode)));
+  }
+}
+
+bool SuspiciousSiteTrigger::MaybeStartReport() {
+  SBErrorOptions error_options =
+      TriggerManager::GetSBErrorDisplayOptions(*prefs_, web_contents());
+
+  security_interstitials::UnsafeResource resource;
+  resource.threat_type = SB_THREAT_TYPE_SUSPICIOUS_SITE;
+  resource.url = web_contents()->GetLastCommittedURL();
+  resource.web_contents_getter = resource.GetWebContentsGetter(
+      web_contents()->GetMainFrame()->GetProcess()->GetID(),
+      web_contents()->GetMainFrame()->GetRoutingID());
+
+  TriggerManagerReason reason;
+  if (!trigger_manager_->StartCollectingThreatDetailsWithReason(
+          TriggerType::SUSPICIOUS_SITE, web_contents(), resource,
+          url_loader_factory_, history_service_, error_options, &reason)) {
+    UMA_HISTOGRAM_ENUMERATION(kSuspiciousSiteTriggerEventMetricName,
+                              SuspiciousSiteTriggerEvent::REPORT_START_FAILED);
+    UMA_HISTOGRAM_ENUMERATION(kSuspiciousSiteTriggerReportRejectionMetricName,
+                              reason);
+    return false;
+  }
+
+  // Call back into the trigger after a short delay, allowing the report
+  // to complete.
+  task_runner_->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(&SuspiciousSiteTrigger::ReportDelayTimerFired,
+                     weak_ptr_factory_.GetWeakPtr()),
+      base::TimeDelta::FromMilliseconds(finish_report_delay_ms_));
+
+  UMA_HISTOGRAM_ENUMERATION(kSuspiciousSiteTriggerEventMetricName,
+                            SuspiciousSiteTriggerEvent::REPORT_STARTED);
+  return true;
+}
+
+void SuspiciousSiteTrigger::FinishReport() {
+  SBErrorOptions error_options =
+      TriggerManager::GetSBErrorDisplayOptions(*prefs_, web_contents());
+  if (trigger_manager_->FinishCollectingThreatDetails(
+          TriggerType::SUSPICIOUS_SITE, web_contents(), base::TimeDelta(),
+          /*did_proceed=*/false, /*num_visits=*/0, error_options)) {
+    UMA_HISTOGRAM_ENUMERATION(kSuspiciousSiteTriggerEventMetricName,
+                              SuspiciousSiteTriggerEvent::REPORT_FINISHED);
+  } else {
+    UMA_HISTOGRAM_ENUMERATION(kSuspiciousSiteTriggerEventMetricName,
+                              SuspiciousSiteTriggerEvent::REPORT_FINISH_FAILED);
+  }
+}
+
+void SuspiciousSiteTrigger::SuspiciousSiteDetectedWhenMonitoring() {
+  DCHECK_EQ(TriggerState::MONITOR_MODE, current_state_);
+  SBErrorOptions error_options =
+      TriggerManager::GetSBErrorDisplayOptions(*prefs_, web_contents());
+  TriggerManagerReason reason;
+  if (trigger_manager_->CanStartDataCollectionWithReason(
+          error_options, TriggerType::SUSPICIOUS_SITE, &reason) ||
+      reason == TriggerManagerReason::DAILY_QUOTA_EXCEEDED) {
+    UMA_HISTOGRAM_ENUMERATION(
+        kSuspiciousSiteTriggerEventMetricName,
+        SuspiciousSiteTriggerEvent::REPORT_POSSIBLE_BUT_SKIPPED);
+  }
+}
+
+void SuspiciousSiteTrigger::DidStartLoading() {
+  UMA_HISTOGRAM_ENUMERATION(kSuspiciousSiteTriggerEventMetricName,
+                            SuspiciousSiteTriggerEvent::PAGE_LOAD_START);
+  switch (current_state_) {
+    case TriggerState::IDLE:
+      // Load started, move to loading state.
+      current_state_ = TriggerState::LOADING;
+      return;
+
+    case TriggerState::LOADING:
+      // No-op, still loading.
+      return;
+
+    case TriggerState::LOADING_WILL_REPORT:
+      // This happens if the user leaves the suspicious page before it
+      // finishes loading. A report can't be created in this case since the
+      // page is now gone.
+      UMA_HISTOGRAM_ENUMERATION(
+          kSuspiciousSiteTriggerEventMetricName,
+          SuspiciousSiteTriggerEvent::PENDING_REPORT_CANCELLED_BY_LOAD);
+      current_state_ = TriggerState::LOADING;
+      return;
+
+    case TriggerState::REPORT_STARTED:
+      // A new page load has started while creating the current report.
+      // Finish the report immediately with whatever data has been captured
+      // so far. A report timer will have already started, but it will be
+      // ignored when it fires.
+      current_state_ = TriggerState::LOADING;
+      FinishReport();
+      return;
+
+    case TriggerState::MONITOR_MODE:
+      // No-op, monitoring only.
+      return;
+  }
+}
+
+void SuspiciousSiteTrigger::DidStopLoading() {
+  UMA_HISTOGRAM_ENUMERATION(kSuspiciousSiteTriggerEventMetricName,
+                            SuspiciousSiteTriggerEvent::PAGE_LOAD_FINISH);
+
+  switch (current_state_) {
+    case TriggerState::IDLE:
+      // No-op, load stopped and we're already idle.
+      return;
+
+    case TriggerState::LOADING:
+      // Load finished, return to Idle state.
+      current_state_ = TriggerState::IDLE;
+      return;
+
+    case TriggerState::LOADING_WILL_REPORT:
+      // Suspicious site detected mid-load and the page has now
+      // finished loading, so try starting a report now.
+      // If we fail to start a report for whatever reason, return to Idle.
+      if (MaybeStartReport()) {
+        current_state_ = TriggerState::REPORT_STARTED;
+      } else {
+        current_state_ = TriggerState::IDLE;
+      }
+      return;
+
+    case TriggerState::REPORT_STARTED:
+      // No-op. Let the report continue running.
+      return;
+
+    case TriggerState::MONITOR_MODE:
+      // No-op, monitoring only.
+      return;
+  }
+}
+
+void SuspiciousSiteTrigger::SuspiciousSiteDetected() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  UMA_HISTOGRAM_ENUMERATION(
+      kSuspiciousSiteTriggerEventMetricName,
+      SuspiciousSiteTriggerEvent::SUSPICIOUS_SITE_DETECTED);
+
+  switch (current_state_) {
+    case TriggerState::IDLE:
+      // Suspicious site detected while idle, start a report immediately.
+      // If we fail to start a report for whatever reason, remain Idle.
+      if (MaybeStartReport()) {
+        current_state_ = TriggerState::REPORT_STARTED;
+      }
+      return;
+
+    case TriggerState::LOADING:
+      // Suspicious site detected in the middle of the load, remember this
+      // and let the page finish loading. The report will be started after
+      // the page has loaded.
+      current_state_ = TriggerState::LOADING_WILL_REPORT;
+      return;
+
+    case TriggerState::LOADING_WILL_REPORT:
+      // No-op. Current page has multiple suspicious URLs in it, remain in
+      // the LOADING_WILL_REPORT state. A report will begin when the page
+      // finishes loading.
+      return;
+
+    case TriggerState::REPORT_STARTED:
+      // No-op. The current report should capture all suspicious sites.
+      return;
+
+    case TriggerState::MONITOR_MODE:
+      // We monitor how often a suspicious site hit could result in a report.
+      SuspiciousSiteDetectedWhenMonitoring();
+      return;
+  }
+}
+
+void SuspiciousSiteTrigger::ReportDelayTimerFired() {
+  UMA_HISTOGRAM_ENUMERATION(kSuspiciousSiteTriggerEventMetricName,
+                            SuspiciousSiteTriggerEvent::REPORT_DELAY_TIMER);
+  UMA_HISTOGRAM_ENUMERATION(kSuspiciousSiteTriggerReportDelayStateMetricName,
+                            current_state_);
+  switch (current_state_) {
+    case TriggerState::IDLE:
+    case TriggerState::LOADING:
+    case TriggerState::LOADING_WILL_REPORT:
+    case TriggerState::MONITOR_MODE:
+      // Invalid, expecting to be in REPORT_STARTED state.
+      return;
+
+    case TriggerState::REPORT_STARTED:
+      // The delay timer has fired so complete the current report.
+      current_state_ = TriggerState::IDLE;
+      FinishReport();
+      return;
+  }
+}
+
+void SuspiciousSiteTrigger::SetTaskRunnerForTest(
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+  task_runner_ = task_runner;
+}
+
+WEB_CONTENTS_USER_DATA_KEY_IMPL(SuspiciousSiteTrigger)
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/content/triggers/suspicious_site_trigger.h b/components/safe_browsing/content/triggers/suspicious_site_trigger.h
new file mode 100644
index 0000000..9d85f89a
--- /dev/null
+++ b/components/safe_browsing/content/triggers/suspicious_site_trigger.h
@@ -0,0 +1,191 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CONTENT_TRIGGERS_SUSPICIOUS_SITE_TRIGGER_H_
+#define COMPONENTS_SAFE_BROWSING_CONTENT_TRIGGERS_SUSPICIOUS_SITE_TRIGGER_H_
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_contents_user_data.h"
+
+class PrefService;
+
+namespace history {
+class HistoryService;
+}
+
+namespace network {
+class SharedURLLoaderFactory;
+}  // namespace network
+
+namespace safe_browsing {
+class TriggerManager;
+
+// Metric for tracking what the Suspicious Site trigger does on each event.
+extern const char kSuspiciousSiteTriggerEventMetricName[];
+
+// Metric for tracking how often reports from this trigger are rejected by the
+// trigger manager, and for what reason.
+extern const char kSuspiciousSiteTriggerReportRejectionMetricName[];
+
+// Metric for tracking the state of the trigger when the report delay timer
+// fires.
+extern const char kSuspiciousSiteTriggerReportDelayStateMetricName[];
+
+// Tracks events this trigger listens for or actions it performs. These values
+// are written to logs. New enum values can be added, but existing enums must
+// never be renumbered or deleted and reused.
+enum class SuspiciousSiteTriggerEvent {
+  // A page load started.
+  PAGE_LOAD_START = 0,
+  // A page load finished.
+  PAGE_LOAD_FINISH = 1,
+  // A suspicious site was detected.
+  SUSPICIOUS_SITE_DETECTED = 2,
+  // The report delay timer fired.
+  REPORT_DELAY_TIMER = 3,
+  // A suspicious site report was started.
+  REPORT_STARTED = 4,
+  // A suspicious site report was created and sent.
+  REPORT_FINISHED = 5,
+  // The trigger was waiting for a load to finish before creating a report but
+  // a new load started before the previous load could finish, so the report
+  // was cancelled.
+  PENDING_REPORT_CANCELLED_BY_LOAD = 6,
+  // The trigger tried to start the report but it was rejected by the trigger
+  // manager.
+  REPORT_START_FAILED = 7,
+  // The trigger tried to finish the report but it was rejected by the trigger
+  // manager.
+  REPORT_FINISH_FAILED = 8,
+  // The trigger could have sent a report but it was skipped, typically because
+  // the trigger was out of quota.
+  REPORT_POSSIBLE_BUT_SKIPPED = 9,
+  // New events must be added before kMaxValue, and the value of kMaxValue
+  // updated.
+  kMaxValue = REPORT_POSSIBLE_BUT_SKIPPED
+};
+
+// Notify a suspicious site trigger on a particular tab that a suspicious site
+// was detected. |web_contents_getter| specifies the tab where the site was
+// detected.
+// Must be called on UI thread.
+void NotifySuspiciousSiteTriggerDetected(
+    const base::RepeatingCallback<content::WebContents*()>&
+        web_contents_getter);
+
+// This class watches tab-level events such as the start and end of a page
+// load, and also listens for events from the SuspiciousSiteURLThrottle that
+// indicate there was a hit on the suspicious site list. This trigger is
+// repsonsible for creating reports about the page at the right time, based on
+// the sequence of such events.
+class SuspiciousSiteTrigger
+    : public content::WebContentsObserver,
+      public content::WebContentsUserData<SuspiciousSiteTrigger> {
+ public:
+  // The different states the trigger could be in.
+  // These values are written to logs. New enum values can be added, but
+  // existing enums must never be renumbered or deleted and reused.
+  enum class TriggerState {
+    // Trigger is idle, page is not loading, no report requested.
+    IDLE = 0,
+    // Page load has started, no report requested.
+    LOADING = 1,
+    // Page load has started and a report is requested. The report will be
+    // created when the page load finishes.
+    LOADING_WILL_REPORT = 2,
+    // A page load finished and a report for the page has started.
+    REPORT_STARTED = 3,
+    // The trigger is in monitoring mode where it listens for events and
+    // increments some metrics but never sends reports. The trigger will never
+    // leave this state.
+    MONITOR_MODE = 4,
+    // New states must be added before kMaxValue and the value of kMaxValue
+    // updated.
+    kMaxValue = MONITOR_MODE
+  };
+
+  ~SuspiciousSiteTrigger() override;
+
+  static void CreateForWebContents(
+      content::WebContents* web_contents,
+      TriggerManager* trigger_manager,
+      PrefService* prefs,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      history::HistoryService* history_service,
+      bool monitor_mode);
+
+  // content::WebContentsObserver implementations.
+  void DidStartLoading() override;
+  void DidStopLoading() override;
+
+  // Called when a suspicious site has been detected on the tab that this
+  // trigger is running on.
+  void SuspiciousSiteDetected();
+
+ private:
+  friend class content::WebContentsUserData<SuspiciousSiteTrigger>;
+  friend class SuspiciousSiteTriggerTest;
+
+  SuspiciousSiteTrigger(
+      content::WebContents* web_contents,
+      TriggerManager* trigger_manager,
+      PrefService* prefs,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      history::HistoryService* history_service,
+      bool monitor_mode);
+
+  // Tries to start a report. Returns whether a report started successfully.
+  // If a report is started, a delayed callback will also begin to notify
+  // the trigger when the report should be completed and sent.
+  bool MaybeStartReport();
+
+  // Calls into the trigger manager to finish the active report and send it.
+  void FinishReport();
+
+  // Called when a suspicious site is detected while in monitor mode. We update
+  // metrics if we determine that a report could have been sent had the trigger
+  // been active.
+  void SuspiciousSiteDetectedWhenMonitoring();
+
+  // Called when the report delay timer fires, indicating that the active
+  // report should be completed and sent.
+  void ReportDelayTimerFired();
+
+  // Sets a task runner to use for tests.
+  void SetTaskRunnerForTest(
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+
+  // The delay (in milliseconds) to wait before finishing a report. Can be
+  // overwritten for tests.
+  int64_t finish_report_delay_ms_;
+
+  // Current state of the trigger. Used to synchronize page load events with
+  // suspicious site list hit events so that reports can be generated at the
+  // right time.
+  TriggerState current_state_;
+
+  // TriggerManager gets called if this trigger detects a suspicious site and
+  // wants to collect data abou tit. Not owned.
+  TriggerManager* trigger_manager_;
+
+  PrefService* prefs_;
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
+  history::HistoryService* history_service_;
+
+  // Task runner for posting delayed tasks. Normally set to the runner for the
+  // UI thread, but can be overwritten for tests.
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
+  base::WeakPtrFactory<SuspiciousSiteTrigger> weak_ptr_factory_{this};
+
+  WEB_CONTENTS_USER_DATA_KEY_DECL();
+
+  DISALLOW_COPY_AND_ASSIGN(SuspiciousSiteTrigger);
+};
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CONTENT_TRIGGERS_SUSPICIOUS_SITE_TRIGGER_H_
diff --git a/components/safe_browsing/content/triggers/suspicious_site_trigger_unittest.cc b/components/safe_browsing/content/triggers/suspicious_site_trigger_unittest.cc
new file mode 100644
index 0000000..d6758c9
--- /dev/null
+++ b/components/safe_browsing/content/triggers/suspicious_site_trigger_unittest.cc
@@ -0,0 +1,527 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/content/triggers/suspicious_site_trigger.h"
+
+#include <string>
+
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/test_simple_task_runner.h"
+#include "build/build_config.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/safe_browsing/content/triggers/mock_trigger_manager.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "content/public/test/browser_task_environment.h"
+#include "content/public/test/navigation_simulator.h"
+#include "content/public/test/test_renderer_host.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using content::NavigationSimulator;
+using content::RenderFrameHost;
+using content::RenderFrameHostTester;
+
+using testing::_;
+using testing::DoAll;
+using testing::Return;
+using testing::SetArgPointee;
+
+namespace safe_browsing {
+
+namespace {
+const char kSuspiciousUrl[] = "https://suspicious.com/";
+const char kCleanUrl[] = "https://foo.com/";
+const char kCleanUrl2[] = "https://bar.com/";
+
+// A matcher for the VisibleURLChangeMidLoad_Suspicious test.
+MATCHER_P(ResourceHasUrl, gurl, "") {
+  return arg.url == gurl;
+}
+}  // namespace
+
+class SuspiciousSiteTriggerTest : public content::RenderViewHostTestHarness {
+ public:
+  SuspiciousSiteTriggerTest() : task_runner_(new base::TestSimpleTaskRunner) {}
+  ~SuspiciousSiteTriggerTest() override {}
+
+  void SetUp() override {
+    content::RenderViewHostTestHarness::SetUp();
+
+    // Enable any prefs required for the trigger to run.
+    safe_browsing::RegisterProfilePrefs(prefs_.registry());
+    prefs_.SetBoolean(prefs::kSafeBrowsingExtendedReportingOptInAllowed, true);
+    prefs_.SetBoolean(prefs::kSafeBrowsingScoutReportingEnabled, true);
+  }
+
+  void CreateTrigger(bool monitor_mode) {
+    safe_browsing::SuspiciousSiteTrigger::CreateForWebContents(
+        web_contents(), &trigger_manager_, &prefs_, nullptr, nullptr,
+        monitor_mode);
+    safe_browsing::SuspiciousSiteTrigger* trigger =
+        safe_browsing::SuspiciousSiteTrigger::FromWebContents(web_contents());
+    // Give the trigger a test task runner that we can synchronize on.
+    trigger->SetTaskRunnerForTest(task_runner_);
+  }
+
+  // Returns the final RenderFrameHost after navigation commits.
+  RenderFrameHost* NavigateFrame(const std::string& url,
+                                 RenderFrameHost* frame) {
+    GURL gurl(url);
+    auto navigation_simulator =
+        NavigationSimulator::CreateRendererInitiated(gurl, frame);
+    navigation_simulator->SetKeepLoading(true);
+    navigation_simulator->Commit();
+    RenderFrameHost* final_frame_host =
+        navigation_simulator->GetFinalRenderFrameHost();
+    return final_frame_host;
+  }
+
+  // Returns the final RenderFrameHost after navigation commits.
+  RenderFrameHost* NavigateMainFrame(const std::string& url) {
+    return NavigateFrame(url, web_contents()->GetMainFrame());
+  }
+
+  // Returns the final RenderFrameHost after navigation commits.
+  RenderFrameHost* CreateAndNavigateSubFrame(const std::string& url,
+                                             RenderFrameHost* parent) {
+    RenderFrameHost* subframe =
+        RenderFrameHostTester::For(parent)->AppendChild("subframe");
+    return NavigateFrame(url, subframe);
+  }
+
+  // Changes the visible URL (in the URL bar) without committing a navigation.
+  void ChangeVisibleURLWithoutNavigation(const std::string& url) {
+    GURL gurl(url);
+    auto navigation_simulator =
+        NavigationSimulator::CreateBrowserInitiated(gurl, web_contents());
+    navigation_simulator->Start();
+  }
+
+  void StartNewFakeLoad() {
+    // This fakes a new LoadStart event in the trigger, since the navigation
+    // simulator doesn't restart the load when we start a new navigation.
+    safe_browsing::SuspiciousSiteTrigger::FromWebContents(web_contents())
+        ->DidStartLoading();
+  }
+
+  void FinishAllNavigations() {
+    // Call the trigger's DidStopLoading event handler directly since it is not
+    // called as part of the navigating individual frames.
+    safe_browsing::SuspiciousSiteTrigger::FromWebContents(web_contents())
+        ->DidStopLoading();
+  }
+
+  void TriggerSuspiciousSite() {
+    // Notify the trigger that a suspicious site was detected.
+    safe_browsing::SuspiciousSiteTrigger::FromWebContents(web_contents())
+        ->SuspiciousSiteDetected();
+  }
+
+  void WaitForTaskRunnerIdle() {
+    task_runner_->RunUntilIdle();
+    base::RunLoop().RunUntilIdle();
+  }
+
+  // Checks the trigger event histogram and ensures that |event| happened
+  // |count| times.
+  void ExpectEventHistogramCount(const SuspiciousSiteTriggerEvent event,
+                                 int count) {
+    histograms_.ExpectBucketCount(kSuspiciousSiteTriggerEventMetricName,
+                                  static_cast<int>(event), count);
+  }
+
+  // Checks the histogram that tracks what state the trigger was in when the
+  // delay timer fired. Ensures that the trigger was in |state| and occured
+  // |count| times.
+  void ExpectDelayStateHistogramCount(
+      const SuspiciousSiteTrigger::TriggerState state,
+      int count) {
+    histograms_.ExpectBucketCount(
+        kSuspiciousSiteTriggerReportDelayStateMetricName,
+        static_cast<int>(state), count);
+  }
+
+  // Checks the report rejection histogram and makes sure that |count| reports
+  // were rejected for |reason|.
+  void ExpectReportRejectionHistogramCount(const TriggerManagerReason reason,
+                                           int count) {
+    histograms_.ExpectBucketCount(
+        kSuspiciousSiteTriggerReportRejectionMetricName,
+        static_cast<int>(reason), count);
+  }
+
+  // Checks the report rejection histogram and makes sure it was empty,
+  // indicating no errors occurred.
+  void ExpectNoReportRejection() {
+    histograms_.ExpectTotalCount(
+        kSuspiciousSiteTriggerReportRejectionMetricName, 0);
+  }
+
+  MockTriggerManager* get_trigger_manager() { return &trigger_manager_; }
+
+ private:
+  TestingPrefServiceSimple prefs_;
+  MockTriggerManager trigger_manager_;
+  base::HistogramTester histograms_;
+  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+};
+
+TEST_F(SuspiciousSiteTriggerTest, RegularPageNonSuspicious) {
+  // In a normal case where there are no suspicious URLs on the page, the
+  // trigger should not fire.
+  CreateTrigger(/*monitor_mode=*/false);
+
+  EXPECT_CALL(*get_trigger_manager(),
+              StartCollectingThreatDetailsWithReason(_, _, _, _, _, _, _))
+      .Times(0);
+  EXPECT_CALL(*get_trigger_manager(),
+              FinishCollectingThreatDetails(_, _, _, _, _, _))
+      .Times(0);
+
+  RenderFrameHost* main_frame = NavigateMainFrame(kCleanUrl);
+  CreateAndNavigateSubFrame(kCleanUrl, main_frame);
+  CreateAndNavigateSubFrame(kCleanUrl, main_frame);
+  FinishAllNavigations();
+
+  // One page load start and finish. No suspicious sites and no reports sent.
+  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::PAGE_LOAD_START, 1);
+  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::PAGE_LOAD_FINISH, 1);
+  ExpectEventHistogramCount(
+      SuspiciousSiteTriggerEvent::SUSPICIOUS_SITE_DETECTED, 0);
+  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::REPORT_STARTED, 0);
+  ExpectNoReportRejection();
+}
+
+// crbug.com/1010037: fails on win.
+#if defined(OS_WIN)
+#define MAYBE_SuspiciousHitDuringLoad DISABLED_SuspiciousHitDuringLoad
+#else
+#define MAYBE_SuspiciousHitDuringLoad SuspiciousHitDuringLoad
+#endif
+TEST_F(SuspiciousSiteTriggerTest, MAYBE_SuspiciousHitDuringLoad) {
+  // When a suspicious site is detected in the middle of a page load, a report
+  // is created after the page load has finished.
+  CreateTrigger(/*monitor_mode=*/false);
+
+  EXPECT_CALL(*get_trigger_manager(),
+              StartCollectingThreatDetailsWithReason(_, _, _, _, _, _, _))
+      .Times(1)
+      .WillOnce(Return(true));
+  EXPECT_CALL(*get_trigger_manager(),
+              FinishCollectingThreatDetails(_, _, _, _, _, _))
+      .Times(1)
+      .WillOnce(Return(true));
+
+  RenderFrameHost* main_frame = NavigateMainFrame(kCleanUrl);
+  CreateAndNavigateSubFrame(kSuspiciousUrl, main_frame);
+  TriggerSuspiciousSite();
+  CreateAndNavigateSubFrame(kCleanUrl, main_frame);
+  FinishAllNavigations();
+
+  WaitForTaskRunnerIdle();
+
+  // One page load start and finish. One suspicious site detected and one
+  // report started and sent after the page finished loading.
+  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::PAGE_LOAD_START, 1);
+  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::PAGE_LOAD_FINISH, 1);
+  ExpectEventHistogramCount(
+      SuspiciousSiteTriggerEvent::SUSPICIOUS_SITE_DETECTED, 1);
+  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::REPORT_STARTED, 1);
+
+  // Ensure the delay timer fired and it happened in the REPORT_STARTED state
+  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::REPORT_DELAY_TIMER, 1);
+  ExpectDelayStateHistogramCount(
+      SuspiciousSiteTrigger::TriggerState::REPORT_STARTED, 1);
+  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::REPORT_FINISHED, 1);
+  ExpectNoReportRejection();
+}
+
+TEST_F(SuspiciousSiteTriggerTest, SuspiciousHitAfterLoad) {
+  // When a suspicious site is detected in after a page load, a report is
+  // created immediately.
+  CreateTrigger(/*monitor_mode=*/false);
+
+  EXPECT_CALL(*get_trigger_manager(),
+              StartCollectingThreatDetailsWithReason(_, _, _, _, _, _, _))
+      .Times(1)
+      .WillOnce(Return(true));
+  EXPECT_CALL(*get_trigger_manager(),
+              FinishCollectingThreatDetails(_, _, _, _, _, _))
+      .Times(1)
+      .WillOnce(Return(true));
+
+  RenderFrameHost* main_frame = NavigateMainFrame(kCleanUrl);
+  CreateAndNavigateSubFrame(kSuspiciousUrl, main_frame);
+  CreateAndNavigateSubFrame(kCleanUrl, main_frame);
+  FinishAllNavigations();
+  TriggerSuspiciousSite();
+
+  WaitForTaskRunnerIdle();
+
+  // One page load start and finish. One suspicious site detected and one
+  // report started and sent.
+  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::PAGE_LOAD_START, 1);
+  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::PAGE_LOAD_FINISH, 1);
+  ExpectEventHistogramCount(
+      SuspiciousSiteTriggerEvent::SUSPICIOUS_SITE_DETECTED, 1);
+  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::REPORT_STARTED, 1);
+
+  // Ensure the delay timer fired and it happened in the REPORT_STARTED state
+  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::REPORT_DELAY_TIMER, 1);
+  ExpectDelayStateHistogramCount(
+      SuspiciousSiteTrigger::TriggerState::REPORT_STARTED, 1);
+  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::REPORT_FINISHED, 1);
+  ExpectNoReportRejection();
+}
+
+TEST_F(SuspiciousSiteTriggerTest, DISABLED_ReportRejectedByTriggerManager) {
+  // If the trigger manager rejects the report then no report is sent.
+  CreateTrigger(/*monitor_mode=*/false);
+
+  EXPECT_CALL(*get_trigger_manager(),
+              StartCollectingThreatDetailsWithReason(_, _, _, _, _, _, _))
+      .Times(1)
+      .WillOnce(
+          DoAll(SetArgPointee<6>(TriggerManagerReason::DAILY_QUOTA_EXCEEDED),
+                Return(false)));
+  EXPECT_CALL(*get_trigger_manager(),
+              FinishCollectingThreatDetails(_, _, _, _, _, _))
+      .Times(0);
+
+  RenderFrameHost* main_frame = NavigateMainFrame(kCleanUrl);
+  CreateAndNavigateSubFrame(kSuspiciousUrl, main_frame);
+  TriggerSuspiciousSite();
+  CreateAndNavigateSubFrame(kCleanUrl, main_frame);
+  FinishAllNavigations();
+
+  WaitForTaskRunnerIdle();
+
+  // One page load start and finish. One suspicious site detected but no report
+  // is sent because it's rejected. Error stats should reflect the rejection.
+  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::PAGE_LOAD_START, 1);
+  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::PAGE_LOAD_FINISH, 1);
+  ExpectEventHistogramCount(
+      SuspiciousSiteTriggerEvent::SUSPICIOUS_SITE_DETECTED, 1);
+
+  // Ensure no report was started or finished, and no delay timer fired.
+  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::REPORT_STARTED, 0);
+  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::REPORT_DELAY_TIMER, 0);
+  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::REPORT_FINISHED, 0);
+
+  // Ensure that starting a report failed, and it was rejected for the
+  // expected reason (quota).
+  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::REPORT_START_FAILED, 1);
+  ExpectReportRejectionHistogramCount(
+      TriggerManagerReason::DAILY_QUOTA_EXCEEDED, 1);
+}
+
+TEST_F(SuspiciousSiteTriggerTest, NewNavigationMidLoad_NotSuspicious) {
+  // Exercise what happens when a new navigation begins in the middle of a page
+  // load when no suspicious site is detected.
+  CreateTrigger(/*monitor_mode=*/false);
+
+  EXPECT_CALL(*get_trigger_manager(),
+              StartCollectingThreatDetailsWithReason(_, _, _, _, _, _, _))
+      .Times(0);
+  EXPECT_CALL(*get_trigger_manager(),
+              FinishCollectingThreatDetails(_, _, _, _, _, _))
+      .Times(0);
+
+  RenderFrameHost* main_frame = NavigateMainFrame(kCleanUrl);
+  CreateAndNavigateSubFrame(kCleanUrl, main_frame);
+  CreateAndNavigateSubFrame(kCleanUrl, main_frame);
+  // Begin a brand new load before the first one is finished.
+  StartNewFakeLoad();
+  FinishAllNavigations();
+
+  // Two page load start events, but only one finish. No suspicious sites
+  // detected and no reports sent.
+  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::PAGE_LOAD_START, 2);
+  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::PAGE_LOAD_FINISH, 1);
+  ExpectEventHistogramCount(
+      SuspiciousSiteTriggerEvent::SUSPICIOUS_SITE_DETECTED, 0);
+  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::REPORT_STARTED, 0);
+  ExpectNoReportRejection();
+}
+
+// Flaky. http://crbug.com/1010686
+TEST_F(SuspiciousSiteTriggerTest, DISABLED_NewNavigationMidLoad_Suspicious) {
+  // Exercise what happens when a new navigation begins in the middle of a page
+  // load when a suspicious site was detected. The report of the first site
+  // must be cancelled because we were waiting for the first load to finish
+  // before beginning the report.
+  CreateTrigger(/*monitor_mode=*/false);
+
+  EXPECT_CALL(*get_trigger_manager(),
+              StartCollectingThreatDetailsWithReason(_, _, _, _, _, _, _))
+      .Times(0);
+  EXPECT_CALL(*get_trigger_manager(),
+              FinishCollectingThreatDetails(_, _, _, _, _, _))
+      .Times(0);
+
+  RenderFrameHost* main_frame = NavigateMainFrame(kCleanUrl);
+  CreateAndNavigateSubFrame(kCleanUrl, main_frame);
+  // Trigger a suspicious site. We wait for this page load to finish before
+  // creating the report.
+  TriggerSuspiciousSite();
+  CreateAndNavigateSubFrame(kCleanUrl, main_frame);
+  // Begin a brand new load before the first one is finished. This will cancel
+  // the report that is queued.
+  StartNewFakeLoad();
+  FinishAllNavigations();
+
+  // Two page load start events, but only one finish. One suspicious site
+  // detected but no reports created because the report gets cancelled.
+  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::PAGE_LOAD_START, 2);
+  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::PAGE_LOAD_FINISH, 1);
+  ExpectEventHistogramCount(
+      SuspiciousSiteTriggerEvent::SUSPICIOUS_SITE_DETECTED, 1);
+  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::REPORT_STARTED, 0);
+  ExpectNoReportRejection();
+
+  // Ensure that the repot got cancelled by the second load.
+  ExpectEventHistogramCount(
+      SuspiciousSiteTriggerEvent::PENDING_REPORT_CANCELLED_BY_LOAD, 1);
+}
+
+TEST_F(SuspiciousSiteTriggerTest, MonitorMode_NotSuspicious) {
+  // Testing the trigger in monitoring mode, it should never send reports.
+  // In a normal case where there are no suspicious URLs on the page, the
+  // trigger should not fire.
+  CreateTrigger(/*monitor_mode=*/true);
+
+  EXPECT_CALL(*get_trigger_manager(),
+              StartCollectingThreatDetailsWithReason(_, _, _, _, _, _, _))
+      .Times(0);
+  EXPECT_CALL(*get_trigger_manager(),
+              FinishCollectingThreatDetails(_, _, _, _, _, _))
+      .Times(0);
+
+  RenderFrameHost* main_frame = NavigateMainFrame(kCleanUrl);
+  CreateAndNavigateSubFrame(kCleanUrl, main_frame);
+  CreateAndNavigateSubFrame(kCleanUrl, main_frame);
+  FinishAllNavigations();
+
+  // One page load start and finish. No suspicious sites and no reports sent.
+  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::PAGE_LOAD_START, 1);
+  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::PAGE_LOAD_FINISH, 1);
+  ExpectEventHistogramCount(
+      SuspiciousSiteTriggerEvent::SUSPICIOUS_SITE_DETECTED, 0);
+  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::REPORT_STARTED, 0);
+  ExpectNoReportRejection();
+}
+
+TEST_F(SuspiciousSiteTriggerTest, MonitorMode_SuspiciousHitDuringLoad) {
+  // Testing the trigger in monitoring mode, it should never send reports.
+  // When a suspicious site is detected in the middle of a page load, a report
+  // is created after the page load has finished.
+  CreateTrigger(/*monitor_mode=*/true);
+
+  EXPECT_CALL(*get_trigger_manager(),
+              StartCollectingThreatDetailsWithReason(_, _, _, _, _, _, _))
+      .Times(0);
+  EXPECT_CALL(*get_trigger_manager(),
+              FinishCollectingThreatDetails(_, _, _, _, _, _))
+      .Times(0);
+
+  RenderFrameHost* main_frame = NavigateMainFrame(kCleanUrl);
+  CreateAndNavigateSubFrame(kSuspiciousUrl, main_frame);
+  TriggerSuspiciousSite();
+  CreateAndNavigateSubFrame(kCleanUrl, main_frame);
+  FinishAllNavigations();
+
+  WaitForTaskRunnerIdle();
+
+  // One page load start and finish. One suspicious site detected and one
+  // possible report that gets skipped.
+  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::PAGE_LOAD_START, 1);
+  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::PAGE_LOAD_FINISH, 1);
+  ExpectEventHistogramCount(
+      SuspiciousSiteTriggerEvent::SUSPICIOUS_SITE_DETECTED, 1);
+  ExpectEventHistogramCount(
+      SuspiciousSiteTriggerEvent::REPORT_POSSIBLE_BUT_SKIPPED, 1);
+
+  // No reports are started or finished, no delay timer fired.
+  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::REPORT_STARTED, 0);
+  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::REPORT_FINISHED, 0);
+  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::REPORT_DELAY_TIMER, 0);
+  ExpectNoReportRejection();
+}
+
+TEST_F(SuspiciousSiteTriggerTest, VisibleURLChangeMidLoad_NotSuspicious) {
+  // Exercise what happens when the visible URL changes during load and no
+  // suspicious site was detected.
+  CreateTrigger(/*monitor_mode=*/false);
+
+  EXPECT_CALL(*get_trigger_manager(),
+              StartCollectingThreatDetailsWithReason(_, _, _, _, _, _, _))
+      .Times(0);
+  EXPECT_CALL(*get_trigger_manager(),
+              FinishCollectingThreatDetails(_, _, _, _, _, _))
+      .Times(0);
+
+  NavigateMainFrame(kCleanUrl);
+  // Change visible URL by starting a new navigation without committing it.
+  // Sanity check the visible URL changed.
+  ChangeVisibleURLWithoutNavigation(kCleanUrl2);
+  GURL expected_clean_url_2(kCleanUrl2);
+  EXPECT_EQ(expected_clean_url_2, web_contents()->GetVisibleURL());
+  FinishAllNavigations();
+
+  WaitForTaskRunnerIdle();
+
+  // One page load start and finish. No suspicious sites and no reports sent.
+  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::PAGE_LOAD_START, 1);
+  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::PAGE_LOAD_FINISH, 1);
+  ExpectEventHistogramCount(
+      SuspiciousSiteTriggerEvent::SUSPICIOUS_SITE_DETECTED, 0);
+  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::REPORT_STARTED, 0);
+  ExpectNoReportRejection();
+}
+
+TEST_F(SuspiciousSiteTriggerTest, VisibleURLChangeMidLoad_Suspicious) {
+  // Exercise what happens when the visible URL changes after a suspicious site
+  // has already been detected.
+  CreateTrigger(/*monitor_mode=*/false);
+
+  NavigateMainFrame(kSuspiciousUrl);
+
+  // The resource eventually sent to the trigger manager should include the
+  // original (suspicious) URL.
+  GURL suspicious_url(kSuspiciousUrl);
+  EXPECT_CALL(*get_trigger_manager(),
+              StartCollectingThreatDetailsWithReason(
+                  _, _, ResourceHasUrl(suspicious_url), _, _, _, _))
+      .Times(1)
+      .WillOnce(Return(true));
+  EXPECT_CALL(*get_trigger_manager(),
+              FinishCollectingThreatDetails(_, _, _, _, _, _))
+      .Times(1)
+      .WillOnce(Return(true));
+
+  // Change visible URL by starting a new navigation without committing it.
+  // Sanity check the visible URL changed.
+  ChangeVisibleURLWithoutNavigation(kCleanUrl);
+  GURL expected_clean_url(kCleanUrl);
+  EXPECT_EQ(expected_clean_url, web_contents()->GetVisibleURL());
+  TriggerSuspiciousSite();
+  FinishAllNavigations();
+
+  WaitForTaskRunnerIdle();
+
+  // One page load start and finish. One suspicious site detected and one
+  // report started and sent after the page finished loading.
+  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::PAGE_LOAD_START, 1);
+  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::PAGE_LOAD_FINISH, 1);
+  ExpectEventHistogramCount(
+      SuspiciousSiteTriggerEvent::SUSPICIOUS_SITE_DETECTED, 1);
+  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::REPORT_STARTED, 1);
+
+  // Ensure the delay timer fired and it happened in the REPORT_STARTED state
+  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::REPORT_DELAY_TIMER, 1);
+  ExpectDelayStateHistogramCount(
+      SuspiciousSiteTrigger::TriggerState::REPORT_STARTED, 1);
+  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::REPORT_FINISHED, 1);
+  ExpectNoReportRejection();
+}
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/content/triggers/trigger_util.cc b/components/safe_browsing/content/triggers/trigger_util.cc
new file mode 100644
index 0000000..0d1434f6
--- /dev/null
+++ b/components/safe_browsing/content/triggers/trigger_util.cc
@@ -0,0 +1,40 @@
+// Copyright 2019 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.
+
+// This file contains some useful utilities for the safe_browsing/triggers
+// classes
+
+#include "components/safe_browsing/content/triggers/trigger_util.h"
+
+#include <string>
+
+#include "content/public/browser/render_frame_host.h"
+
+namespace safe_browsing {
+
+bool DetectGoogleAd(content::RenderFrameHost* render_frame_host,
+                    const GURL& frame_url) {
+  // In case the navigation aborted, look up the RFH by the Frame Tree Node
+  // ID. It returns the committed frame host or the initial frame host for the
+  // frame if no committed host exists. Using a previous host is fine because
+  // once a frame has an ad we always consider it to have an ad.
+  // We use the unsafe method of FindFrameByFrameTreeNodeId because we're not
+  // concerned with which process the frame lives on (we only want to know if an
+  // ad could be present on the page right now).
+  if (render_frame_host) {
+    const std::string& frame_name = render_frame_host->GetFrameName();
+    if (base::StartsWith(frame_name, "google_ads_iframe",
+                         base::CompareCase::SENSITIVE) ||
+        base::StartsWith(frame_name, "google_ads_frame",
+                         base::CompareCase::SENSITIVE)) {
+      return true;
+    }
+  }
+
+  return frame_url.host_piece() == "tpc.googlesyndication.com" &&
+         base::StartsWith(frame_url.path_piece(), "/safeframe",
+                          base::CompareCase::SENSITIVE);
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/content/triggers/trigger_util.h b/components/safe_browsing/content/triggers/trigger_util.h
new file mode 100644
index 0000000..a644578
--- /dev/null
+++ b/components/safe_browsing/content/triggers/trigger_util.h
@@ -0,0 +1,25 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CONTENT_TRIGGERS_TRIGGER_UTIL_H_
+#define COMPONENTS_SAFE_BROWSING_CONTENT_TRIGGERS_TRIGGER_UTIL_H_
+
+#include "url/gurl.h"
+
+// This file contains some useful utilities for the safe_browsing/triggers
+// classes
+namespace content {
+class RenderFrameHost;
+}
+
+namespace safe_browsing {
+
+// Based on heuristics, guesses whether |render_frame_host| is showing a Google
+// Ad, or the |frame_url| is a Google Ad URL.
+bool DetectGoogleAd(content::RenderFrameHost* render_frame_host,
+                    const GURL& frame_url);
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CONTENT_TRIGGERS_TRIGGER_UTIL_H_
diff --git a/components/safe_browsing/content/web_ui/BUILD.gn b/components/safe_browsing/content/web_ui/BUILD.gn
new file mode 100644
index 0000000..b3a3d1b5
--- /dev/null
+++ b/components/safe_browsing/content/web_ui/BUILD.gn
@@ -0,0 +1,51 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//third_party/protobuf/proto_library.gni")
+
+static_library("web_ui") {
+  sources = [
+    "safe_browsing_ui.cc",
+    "safe_browsing_ui.h",
+  ]
+
+  deps = [
+    "//base",
+    "//components/password_manager/core/browser:hash_password_manager",
+    "//components/resources:components_resources_grit",
+    "//components/resources:components_scaled_resources_grit",
+    "//components/safe_browsing:buildflags",
+    "//components/safe_browsing/core:csd_proto",
+    "//components/safe_browsing/core:features",
+    "//components/safe_browsing/core:realtimeapi_proto",
+    "//components/safe_browsing/core/browser:network_context",
+    "//components/safe_browsing/core/browser:referrer_chain_provider",
+    "//components/safe_browsing/core/common:safe_browsing_prefs",
+    "//components/safe_browsing/core/db:v4_local_database_manager",
+    "//components/safe_browsing/core/web_ui:constants",
+    "//components/strings:components_strings_grit",
+    "//components/sync/protocol:protocol",
+    "//components/user_prefs:user_prefs",
+    "//content/public/browser",
+    "//net",
+    "//url",
+  ]
+
+  public_deps = [
+    "//components/safe_browsing/core:webui_proto",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "safe_browsing_ui_unittest.cc",
+  ]
+
+  deps = [
+    ":web_ui",
+    "//content/test:test_support",
+    "//testing/gtest",
+  ]
+}
diff --git a/components/safe_browsing/content/web_ui/DEPS b/components/safe_browsing/content/web_ui/DEPS
new file mode 100644
index 0000000..4019a99d
--- /dev/null
+++ b/components/safe_browsing/content/web_ui/DEPS
@@ -0,0 +1,9 @@
+include_rules = [
+  "+components/grit/components_resources.h",
+  "+components/password_manager/core/browser/hash_password_manager.h",
+  "+components/user_prefs",
+  "+components/safe_browsing/core/proto/csd.pb.h",
+  "+components/strings/grit/components_strings.h",
+  "+components/grit/components_scaled_resources.h",
+  "+components/safe_browsing_db",
+]
diff --git a/components/safe_browsing/web_ui/resources/OWNERS b/components/safe_browsing/content/web_ui/resources/OWNERS
similarity index 100%
rename from components/safe_browsing/web_ui/resources/OWNERS
rename to components/safe_browsing/content/web_ui/resources/OWNERS
diff --git a/components/safe_browsing/web_ui/resources/safe_browsing.css b/components/safe_browsing/content/web_ui/resources/safe_browsing.css
similarity index 100%
rename from components/safe_browsing/web_ui/resources/safe_browsing.css
rename to components/safe_browsing/content/web_ui/resources/safe_browsing.css
diff --git a/components/safe_browsing/web_ui/resources/safe_browsing.html b/components/safe_browsing/content/web_ui/resources/safe_browsing.html
similarity index 100%
rename from components/safe_browsing/web_ui/resources/safe_browsing.html
rename to components/safe_browsing/content/web_ui/resources/safe_browsing.html
diff --git a/components/safe_browsing/web_ui/resources/safe_browsing.js b/components/safe_browsing/content/web_ui/resources/safe_browsing.js
similarity index 100%
rename from components/safe_browsing/web_ui/resources/safe_browsing.js
rename to components/safe_browsing/content/web_ui/resources/safe_browsing.js
diff --git a/components/safe_browsing/content/web_ui/safe_browsing_ui.cc b/components/safe_browsing/content/web_ui/safe_browsing_ui.cc
new file mode 100644
index 0000000..09886ba
--- /dev/null
+++ b/components/safe_browsing/content/web_ui/safe_browsing_ui.cc
@@ -0,0 +1,1752 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/content/web_ui/safe_browsing_ui.h"
+
+#include <stddef.h>
+#include <algorithm>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/base64.h"
+#include "base/base64url.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/i18n/time_formatting.h"
+#include "base/json/json_string_value_serializer.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/singleton.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/task/post_task.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "components/grit/components_resources.h"
+#include "components/grit/components_scaled_resources.h"
+#include "components/password_manager/core/browser/hash_password_manager.h"
+#include "components/safe_browsing/buildflags.h"
+#include "components/safe_browsing/core/browser/referrer_chain_provider.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/features.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
+#include "components/safe_browsing/core/web_ui/constants.h"
+#include "components/strings/grit/components_strings.h"
+#include "components/user_prefs/user_prefs.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/web_contents.h"
+
+#if BUILDFLAG(SAFE_BROWSING_DB_LOCAL)
+#include "components/safe_browsing/core/db/v4_local_database_manager.h"
+#endif
+
+using base::Time;
+using sync_pb::GaiaPasswordReuse;
+using PasswordCaptured = sync_pb::UserEventSpecifics::GaiaPasswordCaptured;
+using PasswordReuseLookup = sync_pb::GaiaPasswordReuse::PasswordReuseLookup;
+using PasswordReuseDetected = sync_pb::GaiaPasswordReuse::PasswordReuseDetected;
+using PasswordReuseDialogInteraction =
+    sync_pb::GaiaPasswordReuse::PasswordReuseDialogInteraction;
+
+namespace safe_browsing {
+WebUIInfoSingleton::WebUIInfoSingleton() = default;
+
+WebUIInfoSingleton::~WebUIInfoSingleton() = default;
+
+// static
+WebUIInfoSingleton* WebUIInfoSingleton::GetInstance() {
+  return base::Singleton<WebUIInfoSingleton>::get();
+}
+
+// static
+bool WebUIInfoSingleton::HasListener() {
+  return GetInstance()->has_test_listener_ ||
+         !GetInstance()->webui_instances_.empty();
+}
+
+void WebUIInfoSingleton::AddToClientDownloadRequestsSent(
+    std::unique_ptr<ClientDownloadRequest> client_download_request) {
+  if (!HasListener())
+    return;
+
+  for (auto* webui_listener : webui_instances_)
+    webui_listener->NotifyClientDownloadRequestJsListener(
+        client_download_request.get());
+  client_download_requests_sent_.push_back(std::move(client_download_request));
+}
+
+void WebUIInfoSingleton::ClearClientDownloadRequestsSent() {
+  std::vector<std::unique_ptr<ClientDownloadRequest>>().swap(
+      client_download_requests_sent_);
+}
+
+void WebUIInfoSingleton::AddToClientDownloadResponsesReceived(
+    std::unique_ptr<ClientDownloadResponse> client_download_response) {
+  if (!HasListener())
+    return;
+
+  for (auto* webui_listener : webui_instances_)
+    webui_listener->NotifyClientDownloadResponseJsListener(
+        client_download_response.get());
+  client_download_responses_received_.push_back(
+      std::move(client_download_response));
+}
+
+void WebUIInfoSingleton::ClearClientDownloadResponsesReceived() {
+  std::vector<std::unique_ptr<ClientDownloadResponse>>().swap(
+      client_download_responses_received_);
+}
+
+void WebUIInfoSingleton::AddToCSBRRsSent(
+    std::unique_ptr<ClientSafeBrowsingReportRequest> csbrr) {
+  if (!HasListener())
+    return;
+
+  for (auto* webui_listener : webui_instances_)
+    webui_listener->NotifyCSBRRJsListener(csbrr.get());
+  csbrrs_sent_.push_back(std::move(csbrr));
+}
+
+void WebUIInfoSingleton::ClearCSBRRsSent() {
+  std::vector<std::unique_ptr<ClientSafeBrowsingReportRequest>>().swap(
+      csbrrs_sent_);
+}
+
+void WebUIInfoSingleton::AddToPGEvents(
+    const sync_pb::UserEventSpecifics& event) {
+  if (!HasListener())
+    return;
+
+  for (auto* webui_listener : webui_instances_)
+    webui_listener->NotifyPGEventJsListener(event);
+
+  pg_event_log_.push_back(event);
+}
+
+void WebUIInfoSingleton::ClearPGEvents() {
+  std::vector<sync_pb::UserEventSpecifics>().swap(pg_event_log_);
+}
+
+void WebUIInfoSingleton::AddToSecurityEvents(
+    const sync_pb::GaiaPasswordReuse& event) {
+  if (!HasListener())
+    return;
+
+  for (auto* webui_listener : webui_instances_)
+    webui_listener->NotifySecurityEventJsListener(event);
+
+  security_event_log_.push_back(event);
+}
+
+void WebUIInfoSingleton::ClearSecurityEvents() {
+  std::vector<sync_pb::GaiaPasswordReuse>().swap(security_event_log_);
+}
+
+int WebUIInfoSingleton::AddToPGPings(
+    const LoginReputationClientRequest& request) {
+  if (!HasListener())
+    return -1;
+
+  for (auto* webui_listener : webui_instances_)
+    webui_listener->NotifyPGPingJsListener(pg_pings_.size(), request);
+
+  pg_pings_.push_back(request);
+
+  return pg_pings_.size() - 1;
+}
+
+void WebUIInfoSingleton::AddToPGResponses(
+    int token,
+    const LoginReputationClientResponse& response) {
+  if (!HasListener())
+    return;
+
+  for (auto* webui_listener : webui_instances_)
+    webui_listener->NotifyPGResponseJsListener(token, response);
+
+  pg_responses_[token] = response;
+}
+
+void WebUIInfoSingleton::ClearPGPings() {
+  std::vector<LoginReputationClientRequest>().swap(pg_pings_);
+  std::map<int, LoginReputationClientResponse>().swap(pg_responses_);
+}
+
+int WebUIInfoSingleton::AddToRTLookupPings(const RTLookupRequest request) {
+  if (!HasListener())
+    return -1;
+
+  for (auto* webui_listener : webui_instances_)
+    webui_listener->NotifyRTLookupPingJsListener(rt_lookup_pings_.size(),
+                                                 request);
+
+  rt_lookup_pings_.push_back(request);
+
+  return rt_lookup_pings_.size() - 1;
+}
+
+void WebUIInfoSingleton::AddToRTLookupResponses(
+    int token,
+    const RTLookupResponse response) {
+  if (!HasListener())
+    return;
+
+  for (auto* webui_listener : webui_instances_)
+    webui_listener->NotifyRTLookupResponseJsListener(token, response);
+
+  rt_lookup_responses_[token] = response;
+}
+
+void WebUIInfoSingleton::ClearRTLookupPings() {
+  std::vector<RTLookupRequest>().swap(rt_lookup_pings_);
+  std::map<int, RTLookupResponse>().swap(rt_lookup_responses_);
+}
+
+void WebUIInfoSingleton::LogMessage(const std::string& message) {
+  if (!HasListener())
+    return;
+
+  base::Time timestamp = base::Time::Now();
+  log_messages_.push_back(std::make_pair(timestamp, message));
+
+  base::PostTask(FROM_HERE, {content::BrowserThread::UI},
+                 base::BindOnce(&WebUIInfoSingleton::NotifyLogMessageListeners,
+                                timestamp, message));
+}
+
+void WebUIInfoSingleton::ClearLogMessages() {
+  std::vector<std::pair<base::Time, std::string>>().swap(log_messages_);
+}
+
+/* static */ void WebUIInfoSingleton::NotifyLogMessageListeners(
+    const base::Time& timestamp,
+    const std::string& message) {
+  WebUIInfoSingleton* web_ui_info = GetInstance();
+
+  for (auto* webui_listener : web_ui_info->webui_instances())
+    webui_listener->NotifyLogMessageJsListener(timestamp, message);
+}
+
+void WebUIInfoSingleton::AddToReportingEvents(const base::Value& event) {
+  if (!HasListener())
+    return;
+
+  for (auto* webui_listener : webui_instances_)
+    webui_listener->NotifyReportingEventJsListener(event);
+
+  reporting_events_.push_back(event.Clone());
+}
+
+void WebUIInfoSingleton::ClearReportingEvents() {
+  std::vector<base::Value>().swap(reporting_events_);
+}
+
+void WebUIInfoSingleton::RegisterWebUIInstance(SafeBrowsingUIHandler* webui) {
+  webui_instances_.push_back(webui);
+}
+
+void WebUIInfoSingleton::UnregisterWebUIInstance(SafeBrowsingUIHandler* webui) {
+  base::Erase(webui_instances_, webui);
+  if (!HasListener()) {
+    ClearCSBRRsSent();
+    ClearClientDownloadRequestsSent();
+    ClearClientDownloadResponsesReceived();
+    ClearPGEvents();
+    ClearPGPings();
+    ClearRTLookupPings();
+    ClearLogMessages();
+  }
+}
+
+network::mojom::CookieManager* WebUIInfoSingleton::GetCookieManager() {
+  if (!cookie_manager_remote_)
+    InitializeCookieManager();
+
+  return cookie_manager_remote_.get();
+}
+
+void WebUIInfoSingleton::InitializeCookieManager() {
+  DCHECK(network_context_);
+
+  // Reset |cookie_manager_remote_|, and only re-initialize it if we have a
+  // listening SafeBrowsingUIHandler.
+  cookie_manager_remote_.reset();
+
+  if (HasListener()) {
+    network_context_->GetNetworkContext()->GetCookieManager(
+        cookie_manager_remote_.BindNewPipeAndPassReceiver());
+
+    // base::Unretained is safe because |this| owns |cookie_manager_remote_|.
+    cookie_manager_remote_.set_disconnect_handler(base::BindOnce(
+        &WebUIInfoSingleton::InitializeCookieManager, base::Unretained(this)));
+  }
+}
+
+namespace {
+#if BUILDFLAG(SAFE_BROWSING_DB_LOCAL)
+
+base::Value UserReadableTimeFromMillisSinceEpoch(int64_t time_in_milliseconds) {
+  base::Time time = base::Time::UnixEpoch() +
+                    base::TimeDelta::FromMilliseconds(time_in_milliseconds);
+  return base::Value(
+      base::UTF16ToASCII(base::TimeFormatShortDateAndTime(time)));
+}
+
+void AddStoreInfo(const DatabaseManagerInfo::DatabaseInfo::StoreInfo store_info,
+                  base::ListValue* database_info_list) {
+  if (store_info.has_file_name()) {
+    database_info_list->Append(base::Value(store_info.file_name()));
+  } else {
+    database_info_list->Append(base::Value("Unknown store"));
+  }
+
+  std::string store_info_string = "<blockquote>";
+  if (store_info.has_file_size_bytes()) {
+    store_info_string +=
+        "Size (in bytes): " + std::to_string(store_info.file_size_bytes()) +
+        "<br>";
+  }
+
+  if (store_info.has_update_status()) {
+    store_info_string +=
+        "Update status: " + std::to_string(store_info.update_status()) + "<br>";
+  }
+
+  if (store_info.has_last_apply_update_time_millis()) {
+    store_info_string += "Last update time: " +
+                         UserReadableTimeFromMillisSinceEpoch(
+                             store_info.last_apply_update_time_millis())
+                             .GetString() +
+                         "<br>";
+  }
+
+  if (store_info.has_checks_attempted()) {
+    store_info_string += "Number of database checks: " +
+                         std::to_string(store_info.checks_attempted()) + "<br>";
+  }
+
+  store_info_string += "</blockquote>";
+
+  database_info_list->Append(base::Value(store_info_string));
+}
+
+void AddDatabaseInfo(const DatabaseManagerInfo::DatabaseInfo database_info,
+                     base::ListValue* database_info_list) {
+  if (database_info.has_database_size_bytes()) {
+    database_info_list->Append(base::Value("Database size (in bytes)"));
+    database_info_list->Append(
+        base::Value(static_cast<double>(database_info.database_size_bytes())));
+  }
+
+  // Add the information specific to each store.
+  for (int i = 0; i < database_info.store_info_size(); i++) {
+    AddStoreInfo(database_info.store_info(i), database_info_list);
+  }
+}
+
+void AddUpdateInfo(const DatabaseManagerInfo::UpdateInfo update_info,
+                   base::ListValue* database_info_list) {
+  if (update_info.has_network_status_code()) {
+    // Network status of the last GetUpdate().
+    database_info_list->Append(base::Value("Last update network status code"));
+    database_info_list->Append(base::Value(update_info.network_status_code()));
+  }
+  if (update_info.has_last_update_time_millis()) {
+    database_info_list->Append(base::Value("Last update time"));
+    database_info_list->Append(UserReadableTimeFromMillisSinceEpoch(
+        update_info.last_update_time_millis()));
+  }
+  if (update_info.has_next_update_time_millis()) {
+    database_info_list->Append(base::Value("Next update time"));
+    database_info_list->Append(UserReadableTimeFromMillisSinceEpoch(
+        update_info.next_update_time_millis()));
+  }
+}
+
+void ParseFullHashInfo(
+    const FullHashCacheInfo::FullHashCache::CachedHashPrefixInfo::FullHashInfo
+        full_hash_info,
+    base::DictionaryValue* full_hash_info_dict) {
+  if (full_hash_info.has_positive_expiry()) {
+    full_hash_info_dict->SetString(
+        "Positive expiry",
+        UserReadableTimeFromMillisSinceEpoch(full_hash_info.positive_expiry())
+            .GetString());
+  }
+  if (full_hash_info.has_full_hash()) {
+    std::string full_hash;
+    base::Base64UrlEncode(full_hash_info.full_hash(),
+                          base::Base64UrlEncodePolicy::INCLUDE_PADDING,
+                          &full_hash);
+    full_hash_info_dict->SetString("Full hash (base64)", full_hash);
+  }
+  if (full_hash_info.list_identifier().has_platform_type()) {
+    full_hash_info_dict->SetInteger(
+        "platform_type", full_hash_info.list_identifier().platform_type());
+  }
+  if (full_hash_info.list_identifier().has_threat_entry_type()) {
+    full_hash_info_dict->SetInteger(
+        "threat_entry_type",
+        full_hash_info.list_identifier().threat_entry_type());
+  }
+  if (full_hash_info.list_identifier().has_threat_type()) {
+    full_hash_info_dict->SetInteger(
+        "threat_type", full_hash_info.list_identifier().threat_type());
+  }
+}
+
+void ParseFullHashCache(const FullHashCacheInfo::FullHashCache full_hash_cache,
+                        base::ListValue* full_hash_cache_list) {
+  base::DictionaryValue full_hash_cache_parsed;
+
+  if (full_hash_cache.has_hash_prefix()) {
+    std::string hash_prefix;
+    base::Base64UrlEncode(full_hash_cache.hash_prefix(),
+                          base::Base64UrlEncodePolicy::INCLUDE_PADDING,
+                          &hash_prefix);
+    full_hash_cache_parsed.SetString("Hash prefix (base64)", hash_prefix);
+  }
+  if (full_hash_cache.cached_hash_prefix_info().has_negative_expiry()) {
+    full_hash_cache_parsed.SetString(
+        "Negative expiry",
+        UserReadableTimeFromMillisSinceEpoch(
+            full_hash_cache.cached_hash_prefix_info().negative_expiry())
+            .GetString());
+  }
+
+  full_hash_cache_list->Append(std::move(full_hash_cache_parsed));
+
+  for (auto full_hash_info_it :
+       full_hash_cache.cached_hash_prefix_info().full_hash_info()) {
+    base::DictionaryValue full_hash_info_dict;
+    ParseFullHashInfo(full_hash_info_it, &full_hash_info_dict);
+    full_hash_cache_list->Append(std::move(full_hash_info_dict));
+  }
+}
+
+void ParseFullHashCacheInfo(const FullHashCacheInfo full_hash_cache_info_proto,
+                            base::ListValue* full_hash_cache_info) {
+  if (full_hash_cache_info_proto.has_number_of_hits()) {
+    base::DictionaryValue number_of_hits;
+    number_of_hits.SetInteger("Number of cache hits",
+                              full_hash_cache_info_proto.number_of_hits());
+    full_hash_cache_info->Append(std::move(number_of_hits));
+  }
+
+  // Record FullHashCache list.
+  for (auto full_hash_cache_it : full_hash_cache_info_proto.full_hash_cache()) {
+    base::ListValue full_hash_cache_list;
+    ParseFullHashCache(full_hash_cache_it, &full_hash_cache_list);
+    full_hash_cache_info->Append(std::move(full_hash_cache_list));
+  }
+}
+
+std::string AddFullHashCacheInfo(
+    const FullHashCacheInfo full_hash_cache_info_proto) {
+  std::string full_hash_cache_parsed;
+
+  base::ListValue full_hash_cache;
+  ParseFullHashCacheInfo(full_hash_cache_info_proto, &full_hash_cache);
+
+  base::Value* full_hash_cache_tree = &full_hash_cache;
+
+  JSONStringValueSerializer serializer(&full_hash_cache_parsed);
+  serializer.set_pretty_print(true);
+  serializer.Serialize(*full_hash_cache_tree);
+
+  return full_hash_cache_parsed;
+}
+
+#endif
+
+base::Value SerializeReferrer(const ReferrerChainEntry& referrer) {
+  base::DictionaryValue referrer_dict;
+  referrer_dict.SetKey("url", base::Value(referrer.url()));
+  referrer_dict.SetKey("main_frame_url",
+                       base::Value(referrer.main_frame_url()));
+
+  std::string url_type;
+  switch (referrer.type()) {
+    case ReferrerChainEntry::EVENT_URL:
+      url_type = "EVENT_URL";
+      break;
+    case ReferrerChainEntry::LANDING_PAGE:
+      url_type = "LANDING_PAGE";
+      break;
+    case ReferrerChainEntry::LANDING_REFERRER:
+      url_type = "LANDING_REFERRER";
+      break;
+    case ReferrerChainEntry::CLIENT_REDIRECT:
+      url_type = "CLIENT_REDIRECT";
+      break;
+    case ReferrerChainEntry::DEPRECATED_SERVER_REDIRECT:
+      url_type = "DEPRECATED_SERVER_REDIRECT";
+      break;
+    case ReferrerChainEntry::RECENT_NAVIGATION:
+      url_type = "RECENT_NAVIGATION";
+      break;
+    case ReferrerChainEntry::REFERRER:
+      url_type = "REFERRER";
+      break;
+  }
+  referrer_dict.SetKey("type", base::Value(url_type));
+
+  base::ListValue ip_addresses;
+  for (const std::string& ip_address : referrer.ip_addresses()) {
+    ip_addresses.Append(base::Value(ip_address));
+  }
+  referrer_dict.SetKey("ip_addresses", std::move(ip_addresses));
+
+  referrer_dict.SetKey("referrer_url", base::Value(referrer.referrer_url()));
+
+  referrer_dict.SetKey("referrer_main_frame_url",
+                       base::Value(referrer.referrer_main_frame_url()));
+
+  referrer_dict.SetKey("is_retargeting",
+                       base::Value(referrer.is_retargeting()));
+
+  referrer_dict.SetKey("navigation_time_msec",
+                       base::Value(referrer.navigation_time_msec()));
+
+  base::ListValue server_redirects;
+  for (const ReferrerChainEntry::ServerRedirect& server_redirect :
+       referrer.server_redirect_chain()) {
+    server_redirects.Append(base::Value(server_redirect.url()));
+  }
+  referrer_dict.SetKey("server_redirect_chain", std::move(server_redirects));
+
+  std::string navigation_initiation;
+  switch (referrer.navigation_initiation()) {
+    case ReferrerChainEntry::UNDEFINED:
+      navigation_initiation = "UNDEFINED";
+      break;
+    case ReferrerChainEntry::BROWSER_INITIATED:
+      navigation_initiation = "BROWSER_INITIATED";
+      break;
+    case ReferrerChainEntry::RENDERER_INITIATED_WITHOUT_USER_GESTURE:
+      navigation_initiation = "RENDERER_INITIATED_WITHOUT_USER_GESTURE";
+      break;
+    case ReferrerChainEntry::RENDERER_INITIATED_WITH_USER_GESTURE:
+      navigation_initiation = "RENDERER_INITIATED_WITH_USER_GESTURE";
+      break;
+  }
+  referrer_dict.SetKey("navigation_initiation",
+                       base::Value(navigation_initiation));
+
+  referrer_dict.SetKey(
+      "maybe_launched_by_external_application",
+      base::Value(referrer.maybe_launched_by_external_application()));
+
+  return std::move(referrer_dict);
+}
+
+std::string SerializeClientDownloadRequest(const ClientDownloadRequest& cdr) {
+  base::DictionaryValue dict;
+  if (cdr.has_url())
+    dict.SetString("url", cdr.url());
+  if (cdr.has_download_type())
+    dict.SetInteger("download_type", cdr.download_type());
+  if (cdr.has_length())
+    dict.SetInteger("length", cdr.length());
+  if (cdr.has_file_basename())
+    dict.SetString("file_basename", cdr.file_basename());
+  if (cdr.has_archive_valid())
+    dict.SetBoolean("archive_valid", cdr.archive_valid());
+
+  auto archived_binaries = std::make_unique<base::ListValue>();
+  for (const auto& archived_binary : cdr.archived_binary()) {
+    auto dict_archived_binary = std::make_unique<base::DictionaryValue>();
+    if (archived_binary.has_file_basename())
+      dict_archived_binary->SetString("file_basename",
+                                      archived_binary.file_basename());
+    if (archived_binary.has_download_type())
+      dict_archived_binary->SetInteger("download_type",
+                                       archived_binary.download_type());
+    if (archived_binary.has_length())
+      dict_archived_binary->SetInteger("length", archived_binary.length());
+    if (archived_binary.is_encrypted())
+      dict_archived_binary->SetBoolean("is_encrypted", true);
+    if (archived_binary.digests().has_sha256()) {
+      const std::string& sha256 = archived_binary.digests().sha256();
+      dict_archived_binary->SetString(
+          "digests.sha256", base::HexEncode(sha256.c_str(), sha256.size()));
+    }
+    archived_binaries->Append(std::move(dict_archived_binary));
+  }
+  dict.SetList("archived_binary", std::move(archived_binaries));
+
+  auto referrer_chain = std::make_unique<base::ListValue>();
+  for (const auto& referrer_chain_entry : cdr.referrer_chain()) {
+    referrer_chain->Append(SerializeReferrer(referrer_chain_entry));
+  }
+  dict.SetList("referrer_chain", std::move(referrer_chain));
+
+  dict.SetBoolean("request_ap_verdicts", cdr.request_ap_verdicts());
+
+  dict.SetInteger("archive_file_count", cdr.archive_file_count());
+  dict.SetInteger("archive_directory_count", cdr.archive_directory_count());
+
+  dict.SetBoolean("request_ap_verdicts", cdr.request_ap_verdicts());
+
+  base::Value* request_tree = &dict;
+  std::string request_serialized;
+  JSONStringValueSerializer serializer(&request_serialized);
+  serializer.set_pretty_print(true);
+  serializer.Serialize(*request_tree);
+  return request_serialized;
+}
+
+std::string SerializeClientDownloadResponse(const ClientDownloadResponse& cdr) {
+  base::DictionaryValue dict;
+
+  switch (cdr.verdict()) {
+    case ClientDownloadResponse::SAFE:
+      dict.SetKey("verdict", base::Value("SAFE"));
+      break;
+    case ClientDownloadResponse::DANGEROUS:
+      dict.SetKey("verdict", base::Value("DANGEROUS"));
+      break;
+    case ClientDownloadResponse::UNCOMMON:
+      dict.SetKey("verdict", base::Value("UNCOMMON"));
+      break;
+    case ClientDownloadResponse::POTENTIALLY_UNWANTED:
+      dict.SetKey("verdict", base::Value("POTENTIALLY_UNWANTED"));
+      break;
+    case ClientDownloadResponse::DANGEROUS_HOST:
+      dict.SetKey("verdict", base::Value("DANGEROUS_HOST"));
+      break;
+    case ClientDownloadResponse::UNKNOWN:
+      dict.SetKey("verdict", base::Value("UNKNOWN"));
+      break;
+  }
+
+  if (cdr.has_more_info()) {
+    dict.SetPath({"more_info", "description"},
+                 base::Value(cdr.more_info().description()));
+    dict.SetPath({"more_info", "url"}, base::Value(cdr.more_info().url()));
+  }
+
+  if (cdr.has_token()) {
+    dict.SetKey("token", base::Value(cdr.token()));
+  }
+
+  if (cdr.has_upload()) {
+    dict.SetKey("upload", base::Value(cdr.upload()));
+  }
+
+  base::Value* request_tree = &dict;
+  std::string request_serialized;
+  JSONStringValueSerializer serializer(&request_serialized);
+  serializer.set_pretty_print(true);
+  serializer.Serialize(*request_tree);
+  return request_serialized;
+}
+
+std::string SerializeCSBRR(const ClientSafeBrowsingReportRequest& report) {
+  base::DictionaryValue report_request;
+  if (report.has_type()) {
+    report_request.SetInteger("type", static_cast<int>(report.type()));
+  }
+  if (report.has_page_url())
+    report_request.SetString("page_url", report.page_url());
+  if (report.has_client_country()) {
+    report_request.SetString("client_country", report.client_country());
+  }
+  if (report.has_repeat_visit()) {
+    report_request.SetInteger("repeat_visit", report.repeat_visit());
+  }
+  if (report.has_did_proceed()) {
+    report_request.SetInteger("did_proceed", report.did_proceed());
+  }
+  std::string serialized;
+  if (report.SerializeToString(&serialized)) {
+    std::string base64_encoded;
+    base::Base64Encode(serialized, &base64_encoded);
+    report_request.SetString("csbrr(base64)", base64_encoded);
+  }
+
+  base::Value* report_request_tree = &report_request;
+  std::string report_request_serialized;
+  JSONStringValueSerializer serializer(&report_request_serialized);
+  serializer.set_pretty_print(true);
+  serializer.Serialize(*report_request_tree);
+  return report_request_serialized;
+}
+
+base::Value SerializeReuseLookup(
+    const PasswordReuseLookup password_reuse_lookup) {
+  std::string lookup_result;
+  switch (password_reuse_lookup.lookup_result()) {
+    case PasswordReuseLookup::UNSPECIFIED:
+      lookup_result = "UNSPECIFIED";
+      break;
+    case PasswordReuseLookup::WHITELIST_HIT:
+      lookup_result = "WHITELIST_HIT";
+      break;
+    case PasswordReuseLookup::CACHE_HIT:
+      lookup_result = "CACHE_HIT";
+      break;
+    case PasswordReuseLookup::REQUEST_SUCCESS:
+      lookup_result = "REQUEST_SUCCESS";
+      break;
+    case PasswordReuseLookup::REQUEST_FAILURE:
+      lookup_result = "REQUEST_FAILURE";
+      break;
+    case PasswordReuseLookup::URL_UNSUPPORTED:
+      lookup_result = "URL_UNSUPPORTED";
+      break;
+    case PasswordReuseLookup::ENTERPRISE_WHITELIST_HIT:
+      lookup_result = "ENTERPRISE_WHITELIST_HIT";
+      break;
+    case PasswordReuseLookup::TURNED_OFF_BY_POLICY:
+      lookup_result = "TURNED_OFF_BY_POLICY";
+      break;
+  }
+  return base::Value(lookup_result);
+}
+
+base::Value SerializeVerdict(const PasswordReuseLookup password_reuse_lookup) {
+  std::string verdict;
+  switch (password_reuse_lookup.verdict()) {
+    case PasswordReuseLookup::VERDICT_UNSPECIFIED:
+      verdict = "VERDICT_UNSPECIFIED";
+      break;
+    case PasswordReuseLookup::SAFE:
+      verdict = "SAFE";
+      break;
+    case PasswordReuseLookup::LOW_REPUTATION:
+      verdict = "LOW_REPUTATION";
+      break;
+    case PasswordReuseLookup::PHISHING:
+      verdict = "PHISHING";
+      break;
+  }
+  return base::Value(verdict);
+}
+
+base::DictionaryValue SerializePGEvent(
+    const sync_pb::UserEventSpecifics& event) {
+  base::DictionaryValue result;
+
+  base::Time timestamp = base::Time::FromDeltaSinceWindowsEpoch(
+      base::TimeDelta::FromMicroseconds(event.event_time_usec()));
+  result.SetDouble("time", timestamp.ToJsTime());
+
+  base::DictionaryValue event_dict;
+
+  // Nominally only one of the following if() statements would be true.
+  // Note that top-level path is either password_captured, or one of the fields
+  // under GaiaPasswordReuse (ie. we've flattened the namespace for simplicity).
+
+  if (event.has_gaia_password_captured_event()) {
+    std::string event_trigger;
+
+    switch (event.gaia_password_captured_event().event_trigger()) {
+      case PasswordCaptured::UNSPECIFIED:
+        event_trigger = "UNSPECIFIED";
+        break;
+      case PasswordCaptured::USER_LOGGED_IN:
+        event_trigger = "USER_LOGGED_IN";
+        break;
+      case PasswordCaptured::EXPIRED_28D_TIMER:
+        event_trigger = "EXPIRED_28D_TIMER";
+        break;
+    }
+
+    event_dict.SetPath({"password_captured", "event_trigger"},
+                       base::Value(event_trigger));
+  }
+
+  GaiaPasswordReuse reuse = event.gaia_password_reuse_event();
+  if (reuse.has_reuse_detected()) {
+    event_dict.SetPath({"reuse_detected", "status", "enabled"},
+                       base::Value(reuse.reuse_detected().status().enabled()));
+
+    std::string reporting_population;
+    switch (
+        reuse.reuse_detected().status().safe_browsing_reporting_population()) {
+      case PasswordReuseDetected::SafeBrowsingStatus::
+          REPORTING_POPULATION_UNSPECIFIED:
+        reporting_population = "REPORTING_POPULATION_UNSPECIFIED";
+        break;
+      case PasswordReuseDetected::SafeBrowsingStatus::NONE:
+        reporting_population = "NONE";
+        break;
+      case PasswordReuseDetected::SafeBrowsingStatus::EXTENDED_REPORTING:
+        reporting_population = "EXTENDED_REPORTING";
+        break;
+      case PasswordReuseDetected::SafeBrowsingStatus::SCOUT:
+        reporting_population = "SCOUT";
+        break;
+    }
+    event_dict.SetPath({"reuse_detected", "status", "reporting_population"},
+                       base::Value(reporting_population));
+  }
+
+  if (reuse.has_reuse_lookup()) {
+    event_dict.SetPath({"reuse_lookup", "lookup_result"},
+                       SerializeReuseLookup(reuse.reuse_lookup()));
+    event_dict.SetPath({"reuse_lookup", "verdict"},
+                       SerializeVerdict(reuse.reuse_lookup()));
+    event_dict.SetPath({"reuse_lookup", "verdict_token"},
+                       base::Value(reuse.reuse_lookup().verdict_token()));
+  }
+
+  if (reuse.has_dialog_interaction()) {
+    std::string interaction_result;
+    switch (reuse.dialog_interaction().interaction_result()) {
+      case PasswordReuseDialogInteraction::UNSPECIFIED:
+        interaction_result = "UNSPECIFIED";
+        break;
+      case PasswordReuseDialogInteraction::WARNING_ACTION_TAKEN:
+        interaction_result = "WARNING_ACTION_TAKEN";
+        break;
+      case PasswordReuseDialogInteraction::WARNING_ACTION_IGNORED:
+        interaction_result = "WARNING_ACTION_IGNORED";
+        break;
+      case PasswordReuseDialogInteraction::WARNING_UI_IGNORED:
+        interaction_result = "WARNING_UI_IGNORED";
+        break;
+      case PasswordReuseDialogInteraction::WARNING_ACTION_TAKEN_ON_SETTINGS:
+        interaction_result = "WARNING_ACTION_TAKEN_ON_SETTINGS";
+        break;
+    }
+    event_dict.SetPath({"dialog_interaction", "interaction_result"},
+                       base::Value(interaction_result));
+  }
+
+  std::string event_serialized;
+  JSONStringValueSerializer serializer(&event_serialized);
+  serializer.set_pretty_print(true);
+  serializer.Serialize(event_dict);
+  result.SetString("message", event_serialized);
+  return result;
+}
+
+base::DictionaryValue SerializeSecurityEvent(
+    const sync_pb::GaiaPasswordReuse& event) {
+  base::DictionaryValue result;
+
+  base::DictionaryValue event_dict;
+  if (event.has_reuse_lookup()) {
+    event_dict.SetPath({"reuse_lookup", "lookup_result"},
+                       SerializeReuseLookup(event.reuse_lookup()));
+    event_dict.SetPath({"reuse_lookup", "verdict"},
+                       SerializeVerdict(event.reuse_lookup()));
+    event_dict.SetPath({"reuse_lookup", "verdict_token"},
+                       base::Value(event.reuse_lookup().verdict_token()));
+  }
+
+  std::string event_serialized;
+  JSONStringValueSerializer serializer(&event_serialized);
+  serializer.set_pretty_print(true);
+  serializer.Serialize(event_dict);
+  result.SetString("message", event_serialized);
+  return result;
+}
+
+base::Value SerializeFrame(const LoginReputationClientRequest::Frame& frame) {
+  base::DictionaryValue frame_dict;
+  frame_dict.SetKey("frame_index", base::Value(frame.frame_index()));
+  frame_dict.SetKey("parent_frame_index",
+                    base::Value(frame.parent_frame_index()));
+  frame_dict.SetKey("url", base::Value(frame.url()));
+  frame_dict.SetKey("has_password_field",
+                    base::Value(frame.has_password_field()));
+
+  base::ListValue referrer_list;
+  for (const ReferrerChainEntry& referrer : frame.referrer_chain()) {
+    referrer_list.Append(SerializeReferrer(referrer));
+  }
+  frame_dict.SetKey("referrer_chain", std::move(referrer_list));
+
+  frame_dict.SetPath(
+      {"referrer_chain_options", "recent_navigations_to_collect"},
+      base::Value(
+          frame.referrer_chain_options().recent_navigations_to_collect()));
+
+  base::ListValue form_list;
+  for (const LoginReputationClientRequest::Frame::Form& form : frame.forms()) {
+    base::DictionaryValue form_dict;
+    form_dict.SetKey("action_url", base::Value(form.action_url()));
+    form_dict.SetKey("has_password_field",
+                     base::Value(form.has_password_field()));
+    form_list.Append(std::move(form_dict));
+  }
+  frame_dict.SetKey("forms", std::move(form_list));
+
+  return std::move(frame_dict);
+}
+
+base::Value SerializePasswordReuseEvent(
+    const LoginReputationClientRequest::PasswordReuseEvent& event) {
+  base::DictionaryValue event_dict;
+
+  base::ListValue domains_list;
+  for (const std::string& domain : event.domains_matching_password()) {
+    domains_list.Append(base::Value(domain));
+  }
+  event_dict.SetKey("domains_matching_password", std::move(domains_list));
+
+  event_dict.SetKey("frame_id", base::Value(event.frame_id()));
+  event_dict.SetKey("is_chrome_signin_password",
+                    base::Value(event.is_chrome_signin_password()));
+
+  std::string sync_account_type;
+  switch (event.sync_account_type()) {
+    case LoginReputationClientRequest::PasswordReuseEvent::NOT_SIGNED_IN:
+      sync_account_type = "NOT_SIGNED_IN";
+      break;
+    case LoginReputationClientRequest::PasswordReuseEvent::GMAIL:
+      sync_account_type = "GMAIL";
+      break;
+    case LoginReputationClientRequest::PasswordReuseEvent::GSUITE:
+      sync_account_type = "GSUITE";
+      break;
+  }
+  event_dict.SetKey("sync_account_type", base::Value(sync_account_type));
+
+  std::string reused_password_type;
+  switch (event.reused_password_type()) {
+    case LoginReputationClientRequest::PasswordReuseEvent::
+        REUSED_PASSWORD_TYPE_UNKNOWN:
+      reused_password_type = "REUSED_PASSWORD_TYPE_UNKNOWN";
+      break;
+    case LoginReputationClientRequest::PasswordReuseEvent::SAVED_PASSWORD:
+      reused_password_type = "SAVED_PASSWORD";
+      break;
+    case LoginReputationClientRequest::PasswordReuseEvent::SIGN_IN_PASSWORD:
+      reused_password_type = "SIGN_IN_PASSWORD";
+      break;
+    case LoginReputationClientRequest::PasswordReuseEvent::OTHER_GAIA_PASSWORD:
+      reused_password_type = "OTHER_GAIA_PASSWORD";
+      break;
+    case LoginReputationClientRequest::PasswordReuseEvent::ENTERPRISE_PASSWORD:
+      reused_password_type = "ENTERPRISE_PASSWORD";
+      break;
+  }
+  event_dict.SetKey("reused_password_type", base::Value(reused_password_type));
+
+  return std::move(event_dict);
+}
+
+base::Value SerializeChromeUserPopulation(
+    const ChromeUserPopulation& population) {
+  base::DictionaryValue population_dict;
+
+  std::string user_population;
+  switch (population.user_population()) {
+    case ChromeUserPopulation::UNKNOWN_USER_POPULATION:
+      user_population = "UNKNOWN_USER_POPULATION";
+      break;
+    case ChromeUserPopulation::SAFE_BROWSING:
+      user_population = "SAFE_BROWSING";
+      break;
+    case ChromeUserPopulation::EXTENDED_REPORTING:
+      user_population = "EXTENDED_REPORTING";
+      break;
+  }
+  population_dict.SetKey("user_population", base::Value(user_population));
+
+  population_dict.SetKey("is_history_sync_enabled",
+                         base::Value(population.is_history_sync_enabled()));
+
+  base::ListValue finch_list;
+  for (const std::string& finch_group : population.finch_active_groups()) {
+    finch_list.Append(base::Value(finch_group));
+  }
+  population_dict.SetKey("finch_active_groups", std::move(finch_list));
+
+  std::string management_status;
+  switch (population.profile_management_status()) {
+    case ChromeUserPopulation::UNKNOWN:
+      management_status = "UNKNOWN";
+      break;
+    case ChromeUserPopulation::UNAVAILABLE:
+      management_status = "UNAVAILABLE";
+      break;
+    case ChromeUserPopulation::NOT_MANAGED:
+      management_status = "NOT_MANAGED";
+      break;
+    case ChromeUserPopulation::ENTERPRISE_MANAGED:
+      management_status = "ENTERPRISE_MANAGED";
+      break;
+  }
+  population_dict.SetKey("profile_management_status",
+                         base::Value(management_status));
+  population_dict.SetKey(
+      "is_under_advanced_protection",
+      base::Value(population.is_under_advanced_protection()));
+  population_dict.SetKey("is_incognito",
+                         base::Value(population.is_incognito()));
+
+  return std::move(population_dict);
+}
+
+base::Value SerializeRTThreatInfo(
+    const RTLookupResponse::ThreatInfo& threat_info) {
+  base::DictionaryValue threat_info_dict;
+  std::string threat_type;
+  switch (threat_info.threat_type()) {
+    case RTLookupResponse::ThreatInfo::THREAT_TYPE_UNSPECIFIED:
+      threat_type = "THREAT_TYPE_UNSPECIFIED";
+      break;
+    case RTLookupResponse::ThreatInfo::WEB_MALWARE:
+      threat_type = "WEB_MALWARE";
+      break;
+    case RTLookupResponse::ThreatInfo::SOCIAL_ENGINEERING:
+      threat_type = "SOCIAL_ENGINEERING";
+      break;
+    case RTLookupResponse::ThreatInfo::UNWANTED_SOFTWARE:
+      threat_type = "UNWANTED_SOFTWARE";
+      break;
+    case RTLookupResponse::ThreatInfo::UNCLEAR_BILLING:
+      threat_type = "UNCLEAR_BILLING";
+      break;
+  }
+  threat_info_dict.SetKey("threat_type", base::Value(threat_type));
+
+  threat_info_dict.SetKey(
+      "cache_duration_sec",
+      base::Value(static_cast<double>(threat_info.cache_duration_sec())));
+
+  threat_info_dict.SetKey("cache_expression",
+                          base::Value(threat_info.cache_expression()));
+
+  std::string verdict_type;
+  switch (threat_info.verdict_type()) {
+    case RTLookupResponse::ThreatInfo::VERDICT_TYPE_UNSPECIFIED:
+      verdict_type = "VERDICT_TYPE_UNSPECIFIED";
+      break;
+    case RTLookupResponse::ThreatInfo::SAFE:
+      verdict_type = "SAFE";
+      break;
+    case RTLookupResponse::ThreatInfo::DANGEROUS:
+      verdict_type = "DANGEROUS";
+      break;
+  }
+  threat_info_dict.SetKey("verdict_type", base::Value(verdict_type));
+
+  return std::move(threat_info_dict);
+}
+
+base::Value SerializeDomFeatures(const DomFeatures& dom_features) {
+  base::DictionaryValue dom_features_dict;
+  auto feature_map = std::make_unique<base::ListValue>();
+  for (const auto& feature : dom_features.feature_map()) {
+    auto feature_dict = std::make_unique<base::DictionaryValue>();
+    feature_dict->SetStringKey("name", feature.name());
+    feature_dict->SetDoubleKey("value", feature.value());
+    feature_map->Append(std::move(feature_dict));
+  }
+  dom_features_dict.SetList("feature_map", std::move(feature_map));
+
+  auto shingle_hashes = std::make_unique<base::ListValue>();
+  for (const auto& hash : dom_features.shingle_hashes()) {
+    shingle_hashes->AppendInteger(hash);
+  }
+  dom_features_dict.SetList("shingle_hashes", std::move(shingle_hashes));
+
+  dom_features_dict.SetInteger("model_version", dom_features.model_version());
+
+  return std::move(dom_features_dict);
+}
+
+std::string SerializePGPing(const LoginReputationClientRequest& request) {
+  base::DictionaryValue request_dict;
+
+  request_dict.SetKey("page_url", base::Value(request.page_url()));
+
+  std::string trigger_type;
+  switch (request.trigger_type()) {
+    case LoginReputationClientRequest::TRIGGER_TYPE_UNSPECIFIED:
+      trigger_type = "TRIGGER_TYPE_UNSPECIFIED";
+      break;
+    case LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE:
+      trigger_type = "UNFAMILIAR_LOGIN_PAGE";
+      break;
+    case LoginReputationClientRequest::PASSWORD_REUSE_EVENT:
+      trigger_type = "PASSWORD_REUSE_EVENT";
+      break;
+  }
+  request_dict.SetKey("trigger_type", base::Value(trigger_type));
+
+  base::ListValue frames_list;
+  for (const LoginReputationClientRequest::Frame& frame : request.frames()) {
+    frames_list.Append(SerializeFrame(frame));
+  }
+  request_dict.SetKey("frames", std::move(frames_list));
+
+  request_dict.SetKey(
+      "password_reuse_event",
+      SerializePasswordReuseEvent(request.password_reuse_event()));
+  request_dict.SetKey("stored_verdict_cnt",
+                      base::Value(request.stored_verdict_cnt()));
+  request_dict.SetKey("population",
+                      SerializeChromeUserPopulation(request.population()));
+  request_dict.SetKey("clicked_through_interstitial",
+                      base::Value(request.clicked_through_interstitial()));
+  request_dict.SetKey("content_type", base::Value(request.content_type()));
+
+  if (request.has_content_area_height()) {
+    request_dict.SetKey("content_area_height",
+                        base::Value(request.content_area_height()));
+  }
+  if (request.has_content_area_width()) {
+    request_dict.SetKey("content_area_width",
+                        base::Value(request.content_area_width()));
+  }
+
+  if (request.has_dom_features()) {
+    request_dict.SetKey("dom_features",
+                        SerializeDomFeatures(request.dom_features()));
+  }
+
+  std::string request_serialized;
+  JSONStringValueSerializer serializer(&request_serialized);
+  serializer.set_pretty_print(true);
+  serializer.Serialize(request_dict);
+  return request_serialized;
+}
+
+std::string SerializePGResponse(const LoginReputationClientResponse& response) {
+  base::DictionaryValue response_dict;
+
+  std::string verdict;
+  switch (response.verdict_type()) {
+    case LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED:
+      verdict = "VERDICT_TYPE_UNSPECIFIED";
+      break;
+    case LoginReputationClientResponse::SAFE:
+      verdict = "SAFE";
+      break;
+    case LoginReputationClientResponse::LOW_REPUTATION:
+      verdict = "LOW_REPUTATION";
+      break;
+    case LoginReputationClientResponse::PHISHING:
+      verdict = "PHISHING";
+      break;
+  }
+  response_dict.SetKey("verdict_type", base::Value(verdict));
+  response_dict.SetKey("cache_duration_sec",
+                       base::Value(int(response.cache_duration_sec())));
+  response_dict.SetKey("cache_expression",
+                       base::Value(response.cache_expression()));
+  response_dict.SetKey("verdict_token", base::Value(response.verdict_token()));
+
+  std::string response_serialized;
+  JSONStringValueSerializer serializer(&response_serialized);
+  serializer.set_pretty_print(true);
+  serializer.Serialize(response_dict);
+  return response_serialized;
+}
+
+std::string SerializeRTLookupPing(const RTLookupRequest& request) {
+  base::DictionaryValue request_dict;
+
+  request_dict.SetKey("url", base::Value(request.url()));
+  request_dict.SetKey("population",
+                      SerializeChromeUserPopulation(request.population()));
+
+  std::string lookupType;
+  switch (request.lookup_type()) {
+    case RTLookupRequest::LOOKUP_TYPE_UNSPECIFIED:
+      lookupType = "LOOKUP_TYPE_UNSPECIFIED";
+      break;
+    case RTLookupRequest::NAVIGATION:
+      lookupType = "NAVIGATION";
+      break;
+    case RTLookupRequest::DOWNLOAD:
+      lookupType = "DOWNLOAD";
+      break;
+  }
+
+  request_dict.SetKey("lookup_type", base::Value(lookupType));
+
+  std::string request_serialized;
+  JSONStringValueSerializer serializer(&request_serialized);
+  serializer.set_pretty_print(true);
+  serializer.Serialize(request_dict);
+  return request_serialized;
+}
+
+std::string SerializeRTLookupResponse(const RTLookupResponse& response) {
+  base::DictionaryValue response_dict;
+
+  base::ListValue threat_info_list;
+  for (const RTLookupResponse::ThreatInfo& threat_info :
+       response.threat_info()) {
+    threat_info_list.Append(SerializeRTThreatInfo(threat_info));
+  }
+  response_dict.SetKey("threat_infos", std::move(threat_info_list));
+
+  std::string response_serialized;
+  JSONStringValueSerializer serializer(&response_serialized);
+  serializer.set_pretty_print(true);
+  serializer.Serialize(response_dict);
+  return response_serialized;
+}
+
+base::Value SerializeLogMessage(const base::Time& timestamp,
+                                const std::string& message) {
+  base::DictionaryValue result;
+  result.SetDouble("time", timestamp.ToJsTime());
+  result.SetString("message", message);
+  return std::move(result);
+}
+
+base::Value SerializeReportingEvent(const base::Value& event) {
+  base::DictionaryValue result;
+
+  std::string event_serialized;
+  JSONStringValueSerializer serializer(&event_serialized);
+  serializer.set_pretty_print(true);
+  serializer.Serialize(event);
+
+  result.SetString("message", event_serialized);
+
+  return std::move(result);
+}
+
+}  // namespace
+
+SafeBrowsingUI::SafeBrowsingUI(content::WebUI* web_ui)
+    : content::WebUIController(web_ui) {
+  // Set up the chrome://safe-browsing source.
+
+  content::WebUIDataSource* html_source = content::WebUIDataSource::Create(
+      safe_browsing::kChromeUISafeBrowsingHost);
+
+  content::BrowserContext* browser_context =
+      web_ui->GetWebContents()->GetBrowserContext();
+
+  // Register callback handler.
+  // Handles messages from JavaScript to C++ via chrome.send().
+  web_ui->AddMessageHandler(
+      std::make_unique<SafeBrowsingUIHandler>(browser_context));
+
+  // Add required resources.
+  html_source->AddResourcePath("safe_browsing.css", IDR_SAFE_BROWSING_CSS);
+  html_source->AddResourcePath("safe_browsing.js", IDR_SAFE_BROWSING_JS);
+  html_source->SetDefaultResource(IDR_SAFE_BROWSING_HTML);
+
+  content::WebUIDataSource::Add(browser_context, html_source);
+}
+
+SafeBrowsingUI::~SafeBrowsingUI() {}
+
+SafeBrowsingUIHandler::SafeBrowsingUIHandler(content::BrowserContext* context)
+    : browser_context_(context) {}
+
+SafeBrowsingUIHandler::~SafeBrowsingUIHandler() {
+  WebUIInfoSingleton::GetInstance()->UnregisterWebUIInstance(this);
+}
+
+void SafeBrowsingUIHandler::OnJavascriptAllowed() {
+  // We don't want to register the SafeBrowsingUIHandler with the
+  // WebUIInfoSingleton at construction, since this can lead to
+  // messages being sent to the renderer before it's ready. So register it here.
+  WebUIInfoSingleton::GetInstance()->RegisterWebUIInstance(this);
+}
+
+void SafeBrowsingUIHandler::OnJavascriptDisallowed() {
+  // In certain situations, Javascript can become disallowed before the
+  // destructor is called (e.g. tab refresh/renderer crash). In these situation,
+  // we want to stop receiving JS messages.
+  WebUIInfoSingleton::GetInstance()->UnregisterWebUIInstance(this);
+}
+
+void SafeBrowsingUIHandler::GetExperiments(const base::ListValue* args) {
+  AllowJavascript();
+  std::string callback_id;
+  args->GetString(0, &callback_id);
+  ResolveJavascriptCallback(base::Value(callback_id), GetFeatureStatusList());
+}
+
+void SafeBrowsingUIHandler::GetPrefs(const base::ListValue* args) {
+  AllowJavascript();
+  std::string callback_id;
+  args->GetString(0, &callback_id);
+  ResolveJavascriptCallback(base::Value(callback_id),
+                            safe_browsing::GetSafeBrowsingPreferencesList(
+                                user_prefs::UserPrefs::Get(browser_context_)));
+}
+
+void SafeBrowsingUIHandler::GetCookie(const base::ListValue* args) {
+  std::string callback_id;
+  args->GetString(0, &callback_id);
+
+  WebUIInfoSingleton::GetInstance()->GetCookieManager()->GetAllCookies(
+      base::BindOnce(&SafeBrowsingUIHandler::OnGetCookie,
+                     weak_factory_.GetWeakPtr(), std::move(callback_id)));
+}
+
+void SafeBrowsingUIHandler::OnGetCookie(
+    const std::string& callback_id,
+    const std::vector<net::CanonicalCookie>& cookies) {
+  DCHECK_GE(1u, cookies.size());
+
+  std::string cookie = "No cookie";
+  double time = 0.0;
+  if (!cookies.empty()) {
+    cookie = cookies[0].Value();
+    time = cookies[0].CreationDate().ToJsTime();
+  }
+
+  base::Value response(base::Value::Type::LIST);
+  response.Append(base::Value(cookie));
+  response.Append(base::Value(time));
+
+  AllowJavascript();
+  ResolveJavascriptCallback(base::Value(callback_id), std::move(response));
+}
+
+void SafeBrowsingUIHandler::GetSavedPasswords(const base::ListValue* args) {
+  password_manager::HashPasswordManager hash_manager(
+      user_prefs::UserPrefs::Get(browser_context_));
+
+  base::ListValue saved_passwords;
+  for (const password_manager::PasswordHashData& hash_data :
+       hash_manager.RetrieveAllPasswordHashes()) {
+    saved_passwords.AppendString(hash_data.username);
+    saved_passwords.AppendBoolean(hash_data.is_gaia_password);
+  }
+
+  AllowJavascript();
+  std::string callback_id;
+  args->GetString(0, &callback_id);
+  ResolveJavascriptCallback(base::Value(callback_id), saved_passwords);
+}
+
+void SafeBrowsingUIHandler::GetDatabaseManagerInfo(
+    const base::ListValue* args) {
+  base::ListValue database_manager_info;
+
+#if BUILDFLAG(SAFE_BROWSING_DB_LOCAL)
+  const V4LocalDatabaseManager* local_database_manager_instance =
+      V4LocalDatabaseManager::current_local_database_manager();
+  if (local_database_manager_instance) {
+    DatabaseManagerInfo database_manager_info_proto;
+    FullHashCacheInfo full_hash_cache_info_proto;
+
+    local_database_manager_instance->CollectDatabaseManagerInfo(
+        &database_manager_info_proto, &full_hash_cache_info_proto);
+
+    if (database_manager_info_proto.has_update_info()) {
+      AddUpdateInfo(database_manager_info_proto.update_info(),
+                    &database_manager_info);
+    }
+    if (database_manager_info_proto.has_database_info()) {
+      AddDatabaseInfo(database_manager_info_proto.database_info(),
+                      &database_manager_info);
+    }
+
+    database_manager_info.Append(
+        base::Value(AddFullHashCacheInfo(full_hash_cache_info_proto)));
+  }
+#endif
+
+  AllowJavascript();
+  std::string callback_id;
+  args->GetString(0, &callback_id);
+
+  ResolveJavascriptCallback(base::Value(callback_id), database_manager_info);
+}
+
+void SafeBrowsingUIHandler::GetSentClientDownloadRequests(
+    const base::ListValue* args) {
+  const std::vector<std::unique_ptr<ClientDownloadRequest>>& cdrs =
+      WebUIInfoSingleton::GetInstance()->client_download_requests_sent();
+
+  base::ListValue cdrs_sent;
+
+  for (const auto& cdr : cdrs) {
+    cdrs_sent.Append(base::Value(SerializeClientDownloadRequest(*cdr)));
+  }
+
+  AllowJavascript();
+  std::string callback_id;
+  args->GetString(0, &callback_id);
+  ResolveJavascriptCallback(base::Value(callback_id), cdrs_sent);
+}
+
+void SafeBrowsingUIHandler::GetReceivedClientDownloadResponses(
+    const base::ListValue* args) {
+  const std::vector<std::unique_ptr<ClientDownloadResponse>>& cdrs =
+      WebUIInfoSingleton::GetInstance()->client_download_responses_received();
+
+  base::ListValue cdrs_received;
+
+  for (const auto& cdr : cdrs) {
+    cdrs_received.Append(base::Value(SerializeClientDownloadResponse(*cdr)));
+  }
+
+  AllowJavascript();
+  std::string callback_id;
+  args->GetString(0, &callback_id);
+  ResolveJavascriptCallback(base::Value(callback_id), cdrs_received);
+}
+
+void SafeBrowsingUIHandler::GetSentCSBRRs(const base::ListValue* args) {
+  const std::vector<std::unique_ptr<ClientSafeBrowsingReportRequest>>& reports =
+      WebUIInfoSingleton::GetInstance()->csbrrs_sent();
+
+  base::ListValue sent_reports;
+
+  for (const auto& report : reports) {
+    sent_reports.Append(base::Value(SerializeCSBRR(*report)));
+  }
+
+  AllowJavascript();
+  std::string callback_id;
+  args->GetString(0, &callback_id);
+  ResolveJavascriptCallback(base::Value(callback_id), sent_reports);
+}
+
+void SafeBrowsingUIHandler::GetPGEvents(const base::ListValue* args) {
+  const std::vector<sync_pb::UserEventSpecifics>& events =
+      WebUIInfoSingleton::GetInstance()->pg_event_log();
+
+  base::ListValue events_sent;
+
+  for (const sync_pb::UserEventSpecifics& event : events)
+    events_sent.Append(SerializePGEvent(event));
+
+  AllowJavascript();
+  std::string callback_id;
+  args->GetString(0, &callback_id);
+  ResolveJavascriptCallback(base::Value(callback_id), events_sent);
+}
+
+void SafeBrowsingUIHandler::GetSecurityEvents(const base::ListValue* args) {
+  const std::vector<sync_pb::GaiaPasswordReuse>& events =
+      WebUIInfoSingleton::GetInstance()->security_event_log();
+
+  base::ListValue events_sent;
+
+  for (const sync_pb::GaiaPasswordReuse& event : events)
+    events_sent.Append(SerializeSecurityEvent(event));
+
+  AllowJavascript();
+  std::string callback_id;
+  args->GetString(0, &callback_id);
+  ResolveJavascriptCallback(base::Value(callback_id), events_sent);
+}
+
+void SafeBrowsingUIHandler::GetPGPings(const base::ListValue* args) {
+  const std::vector<LoginReputationClientRequest> requests =
+      WebUIInfoSingleton::GetInstance()->pg_pings();
+
+  base::ListValue pings_sent;
+  for (size_t request_index = 0; request_index < requests.size();
+       request_index++) {
+    base::ListValue ping_entry;
+    ping_entry.Append(base::Value(int(request_index)));
+    ping_entry.Append(base::Value(SerializePGPing(requests[request_index])));
+    pings_sent.Append(std::move(ping_entry));
+  }
+
+  AllowJavascript();
+  std::string callback_id;
+  args->GetString(0, &callback_id);
+  ResolveJavascriptCallback(base::Value(callback_id), pings_sent);
+}
+
+void SafeBrowsingUIHandler::GetPGResponses(const base::ListValue* args) {
+  const std::map<int, LoginReputationClientResponse> responses =
+      WebUIInfoSingleton::GetInstance()->pg_responses();
+
+  base::ListValue responses_sent;
+  for (const auto& token_and_response : responses) {
+    base::ListValue response_entry;
+    response_entry.Append(base::Value(token_and_response.first));
+    response_entry.Append(
+        base::Value(SerializePGResponse(token_and_response.second)));
+    responses_sent.Append(std::move(response_entry));
+  }
+
+  AllowJavascript();
+  std::string callback_id;
+  args->GetString(0, &callback_id);
+  ResolveJavascriptCallback(base::Value(callback_id), responses_sent);
+}
+
+void SafeBrowsingUIHandler::GetRTLookupPings(const base::ListValue* args) {
+  const std::vector<RTLookupRequest> requests =
+      WebUIInfoSingleton::GetInstance()->rt_lookup_pings();
+
+  base::ListValue pings_sent;
+  for (size_t request_index = 0; request_index < requests.size();
+       request_index++) {
+    base::ListValue ping_entry;
+    ping_entry.Append(base::Value(int(request_index)));
+    ping_entry.Append(
+        base::Value(SerializeRTLookupPing(requests[request_index])));
+    pings_sent.Append(std::move(ping_entry));
+  }
+
+  AllowJavascript();
+  std::string callback_id;
+  args->GetString(0, &callback_id);
+  ResolveJavascriptCallback(base::Value(callback_id), pings_sent);
+}
+
+void SafeBrowsingUIHandler::GetRTLookupResponses(const base::ListValue* args) {
+  const std::map<int, RTLookupResponse> responses =
+      WebUIInfoSingleton::GetInstance()->rt_lookup_responses();
+
+  base::ListValue responses_sent;
+  for (const auto& token_and_response : responses) {
+    base::ListValue response_entry;
+    response_entry.Append(base::Value(token_and_response.first));
+    response_entry.Append(
+        base::Value(SerializeRTLookupResponse(token_and_response.second)));
+    responses_sent.Append(std::move(response_entry));
+  }
+
+  AllowJavascript();
+  std::string callback_id;
+  args->GetString(0, &callback_id);
+  ResolveJavascriptCallback(base::Value(callback_id), responses_sent);
+}
+
+void SafeBrowsingUIHandler::GetReferrerChain(const base::ListValue* args) {
+  std::string url_string;
+  args->GetString(1, &url_string);
+
+  ReferrerChainProvider* provider =
+      WebUIInfoSingleton::GetInstance()->referrer_chain_provider();
+
+  std::string callback_id;
+  args->GetString(0, &callback_id);
+
+  if (!provider) {
+    AllowJavascript();
+    ResolveJavascriptCallback(base::Value(callback_id), base::Value(""));
+    return;
+  }
+
+  ReferrerChain referrer_chain;
+  provider->IdentifyReferrerChainByEventURL(
+      GURL(url_string), SessionID::InvalidValue(), 2, &referrer_chain);
+
+  base::ListValue referrer_list;
+  for (const ReferrerChainEntry& entry : referrer_chain) {
+    referrer_list.Append(SerializeReferrer(entry));
+  }
+
+  std::string referrer_chain_serialized;
+  JSONStringValueSerializer serializer(&referrer_chain_serialized);
+  serializer.set_pretty_print(true);
+  serializer.Serialize(referrer_list);
+
+  AllowJavascript();
+  ResolveJavascriptCallback(base::Value(callback_id),
+                            base::Value(referrer_chain_serialized));
+}
+
+void SafeBrowsingUIHandler::GetReportingEvents(const base::ListValue* args) {
+  base::ListValue reporting_events;
+  for (const auto& reporting_event :
+       WebUIInfoSingleton::GetInstance()->reporting_events()) {
+    reporting_events.Append(reporting_event.Clone());
+  }
+
+  AllowJavascript();
+  std::string callback_id;
+  args->GetString(0, &callback_id);
+  ResolveJavascriptCallback(base::Value(callback_id), reporting_events);
+}
+
+void SafeBrowsingUIHandler::GetLogMessages(const base::ListValue* args) {
+  const std::vector<std::pair<base::Time, std::string>>& log_messages =
+      WebUIInfoSingleton::GetInstance()->log_messages();
+
+  base::ListValue messages_received;
+  for (const auto& message : log_messages) {
+    messages_received.Append(
+        base::Value(SerializeLogMessage(message.first, message.second)));
+  }
+
+  AllowJavascript();
+  std::string callback_id;
+  args->GetString(0, &callback_id);
+  ResolveJavascriptCallback(base::Value(callback_id), messages_received);
+}
+
+void SafeBrowsingUIHandler::NotifyClientDownloadRequestJsListener(
+    ClientDownloadRequest* client_download_request) {
+  AllowJavascript();
+  FireWebUIListener(
+      "sent-client-download-requests-update",
+      base::Value(SerializeClientDownloadRequest(*client_download_request)));
+}
+
+void SafeBrowsingUIHandler::NotifyClientDownloadResponseJsListener(
+    ClientDownloadResponse* client_download_response) {
+  AllowJavascript();
+  FireWebUIListener(
+      "received-client-download-responses-update",
+      base::Value(SerializeClientDownloadResponse(*client_download_response)));
+}
+
+void SafeBrowsingUIHandler::NotifyCSBRRJsListener(
+    ClientSafeBrowsingReportRequest* csbrr) {
+  AllowJavascript();
+  FireWebUIListener("sent-csbrr-update", base::Value(SerializeCSBRR(*csbrr)));
+}
+
+void SafeBrowsingUIHandler::NotifyPGEventJsListener(
+    const sync_pb::UserEventSpecifics& event) {
+  AllowJavascript();
+  FireWebUIListener("sent-pg-event", SerializePGEvent(event));
+}
+
+void SafeBrowsingUIHandler::NotifySecurityEventJsListener(
+    const sync_pb::GaiaPasswordReuse& event) {
+  AllowJavascript();
+  FireWebUIListener("sent-security-event", SerializeSecurityEvent(event));
+}
+
+void SafeBrowsingUIHandler::NotifyPGPingJsListener(
+    int token,
+    const LoginReputationClientRequest& request) {
+  base::ListValue request_list;
+  request_list.Append(base::Value(token));
+  request_list.Append(base::Value(SerializePGPing(request)));
+
+  AllowJavascript();
+  FireWebUIListener("pg-pings-update", request_list);
+}
+
+void SafeBrowsingUIHandler::NotifyPGResponseJsListener(
+    int token,
+    const LoginReputationClientResponse& response) {
+  base::ListValue response_list;
+  response_list.Append(base::Value(token));
+  response_list.Append(base::Value(SerializePGResponse(response)));
+
+  AllowJavascript();
+  FireWebUIListener("pg-responses-update", response_list);
+}
+
+void SafeBrowsingUIHandler::NotifyRTLookupPingJsListener(
+    int token,
+    const RTLookupRequest& request) {
+  base::ListValue request_list;
+  request_list.Append(base::Value(token));
+  request_list.Append(base::Value(SerializeRTLookupPing(request)));
+
+  AllowJavascript();
+  FireWebUIListener("rt-lookup-pings-update", request_list);
+}
+
+void SafeBrowsingUIHandler::NotifyRTLookupResponseJsListener(
+    int token,
+    const RTLookupResponse& response) {
+  base::ListValue response_list;
+  response_list.Append(base::Value(token));
+  response_list.Append(base::Value(SerializeRTLookupResponse(response)));
+
+  AllowJavascript();
+  FireWebUIListener("rt-lookup-responses-update", response_list);
+}
+
+void SafeBrowsingUIHandler::NotifyLogMessageJsListener(
+    const base::Time& timestamp,
+    const std::string& message) {
+  AllowJavascript();
+  FireWebUIListener("log-messages-update",
+                    base::Value(SerializeLogMessage(timestamp, message)));
+}
+
+void SafeBrowsingUIHandler::NotifyReportingEventJsListener(
+    const base::Value& event) {
+  AllowJavascript();
+  FireWebUIListener("reporting-events-update", SerializeReportingEvent(event));
+}
+
+void SafeBrowsingUIHandler::RegisterMessages() {
+  web_ui()->RegisterMessageCallback(
+      "getExperiments",
+      base::BindRepeating(&SafeBrowsingUIHandler::GetExperiments,
+                          base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "getPrefs", base::BindRepeating(&SafeBrowsingUIHandler::GetPrefs,
+                                      base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "getCookie", base::BindRepeating(&SafeBrowsingUIHandler::GetCookie,
+                                       base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "getSavedPasswords",
+      base::BindRepeating(&SafeBrowsingUIHandler::GetSavedPasswords,
+                          base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "getDatabaseManagerInfo",
+      base::BindRepeating(&SafeBrowsingUIHandler::GetDatabaseManagerInfo,
+                          base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "getSentClientDownloadRequests",
+      base::BindRepeating(&SafeBrowsingUIHandler::GetSentClientDownloadRequests,
+                          base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "getReceivedClientDownloadResponses",
+      base::BindRepeating(
+          &SafeBrowsingUIHandler::GetReceivedClientDownloadResponses,
+          base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "getSentCSBRRs",
+      base::BindRepeating(&SafeBrowsingUIHandler::GetSentCSBRRs,
+                          base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "getPGEvents", base::BindRepeating(&SafeBrowsingUIHandler::GetPGEvents,
+                                         base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "getSecurityEvents",
+      base::BindRepeating(&SafeBrowsingUIHandler::GetSecurityEvents,
+                          base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "getPGPings", base::BindRepeating(&SafeBrowsingUIHandler::GetPGPings,
+                                        base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "getPGResponses",
+      base::BindRepeating(&SafeBrowsingUIHandler::GetPGResponses,
+                          base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "getRTLookupPings",
+      base::BindRepeating(&SafeBrowsingUIHandler::GetRTLookupPings,
+                          base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "getRTLookupResponses",
+      base::BindRepeating(&SafeBrowsingUIHandler::GetRTLookupResponses,
+                          base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "getLogMessages",
+      base::BindRepeating(&SafeBrowsingUIHandler::GetLogMessages,
+                          base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "getReferrerChain",
+      base::BindRepeating(&SafeBrowsingUIHandler::GetReferrerChain,
+                          base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "getReportingEvents",
+      base::BindRepeating(&SafeBrowsingUIHandler::GetReportingEvents,
+                          base::Unretained(this)));
+}
+
+void SafeBrowsingUIHandler::SetWebUIForTesting(content::WebUI* web_ui) {
+  set_web_ui(web_ui);
+}
+
+CrSBLogMessage::CrSBLogMessage() {}
+
+CrSBLogMessage::~CrSBLogMessage() {
+  WebUIInfoSingleton::GetInstance()->LogMessage(stream_.str());
+  DLOG(WARNING) << stream_.str();
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/content/web_ui/safe_browsing_ui.h b/components/safe_browsing/content/web_ui/safe_browsing_ui.h
new file mode 100644
index 0000000..704516c
--- /dev/null
+++ b/components/safe_browsing/content/web_ui/safe_browsing_ui.h
@@ -0,0 +1,482 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CONTENT_WEB_UI_SAFE_BROWSING_UI_H_
+#define COMPONENTS_SAFE_BROWSING_CONTENT_WEB_UI_SAFE_BROWSING_UI_H_
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "components/safe_browsing/core/browser/safe_browsing_network_context.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/realtimeapi.pb.h"
+#include "components/safe_browsing/core/proto/webui.pb.h"
+#include "components/sync/protocol/user_event_specifics.pb.h"
+#include "content/public/browser/web_ui_controller.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/browser/web_ui_message_handler.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "services/network/public/mojom/cookie_manager.mojom.h"
+#include "services/network/public/mojom/network_context.mojom.h"
+
+namespace base {
+class ListValue;
+template <typename T>
+struct DefaultSingletonTraits;
+}  // namespace base
+
+namespace safe_browsing {
+class WebUIInfoSingleton;
+class ReferrerChainProvider;
+
+class SafeBrowsingUIHandler : public content::WebUIMessageHandler {
+ public:
+  SafeBrowsingUIHandler(content::BrowserContext* context);
+  ~SafeBrowsingUIHandler() override;
+
+  // Callback when Javascript becomes allowed in the WebUI.
+  void OnJavascriptAllowed() override;
+
+  // Callback when Javascript becomes disallowed in the WebUI.
+  void OnJavascriptDisallowed() override;
+
+  // Get the experiments that are currently enabled per Chrome instance.
+  void GetExperiments(const base::ListValue* args);
+
+  // Get the Safe Browsing related preferences for the current user.
+  void GetPrefs(const base::ListValue* args);
+
+  // Get the Safe Browsing cookie.
+  void GetCookie(const base::ListValue* args);
+
+  // Get the current captured passwords.
+  void GetSavedPasswords(const base::ListValue* args);
+
+  // Get the information related to the Safe Browsing database and full hash
+  // cache.
+  void GetDatabaseManagerInfo(const base::ListValue* args);
+
+  // Get the ClientDownloadRequests that have been collected since the oldest
+  // currently open chrome://safe-browsing tab was opened.
+  void GetSentClientDownloadRequests(const base::ListValue* args);
+
+  // Get the ClientDownloadReponses that have been collected since the oldest
+  // currently open chrome://safe-browsing tab was opened.
+  void GetReceivedClientDownloadResponses(const base::ListValue* args);
+
+  // Get the ThreatDetails that have been collected since the oldest currently
+  // open chrome://safe-browsing tab was opened.
+  void GetSentCSBRRs(const base::ListValue* args);
+
+  // Get the PhishGuard events that have been collected since the oldest
+  // currently open chrome://safe-browsing tab was opened.
+  void GetPGEvents(const base::ListValue* args);
+
+  // Get the Security events that have been collected since the oldest
+  // currently open chrome://safe-browsing tab was opened.
+  void GetSecurityEvents(const base::ListValue* args);
+
+  // Get the PhishGuard pings that have been sent since the oldest currently
+  // open chrome://safe-browsing tab was opened.
+  void GetPGPings(const base::ListValue* args);
+
+  // Get the PhishGuard responses that have been received since the oldest
+  // currently open chrome://safe-browsing tab was opened.
+  void GetPGResponses(const base::ListValue* args);
+
+  // Get the real time lookup pings that have been sent since the oldest
+  // currently open chrome://safe-browsing tab was opened.
+  void GetRTLookupPings(const base::ListValue* args);
+
+  // Get the real time lookup responses that have been received since the oldest
+  // currently open chrome://safe-browsing tab was opened.
+  void GetRTLookupResponses(const base::ListValue* args);
+
+  // Get the current referrer chain for a given URL.
+  void GetReferrerChain(const base::ListValue* args);
+
+  // Get the list of log messages that have been received since the oldest
+  // currently open chrome://safe-browsing tab was opened.
+  void GetLogMessages(const base::ListValue* args);
+
+  // Get the reporting events that have been collected since the oldest
+  // currently open chrome://safe-browsing tab was opened.
+  void GetReportingEvents(const base::ListValue* args);
+
+  // Register callbacks for WebUI messages.
+  void RegisterMessages() override;
+
+  // Sets the WebUI for testing
+  void SetWebUIForTesting(content::WebUI* web_ui);
+
+ private:
+  friend class WebUIInfoSingleton;
+
+  // Called when any new ClientDownloadRequest messages are sent while one or
+  // more WebUI tabs are open.
+  void NotifyClientDownloadRequestJsListener(
+      ClientDownloadRequest* client_download_request);
+
+  // Called when any new ClientDownloadResponse messages are received while one
+  // or more WebUI tabs are open.
+  void NotifyClientDownloadResponseJsListener(
+      ClientDownloadResponse* client_download_response);
+
+  // Get the new ThreatDetails messages sent from ThreatDetails when a ping is
+  // sent, while one or more WebUI tabs are opened.
+  void NotifyCSBRRJsListener(ClientSafeBrowsingReportRequest* csbrr);
+
+  // Called when any new PhishGuard events are sent while one or more WebUI tabs
+  // are open.
+  void NotifyPGEventJsListener(const sync_pb::UserEventSpecifics& event);
+
+  // Called when any new Security events are sent while one or more WebUI tabs
+  // are open.
+  void NotifySecurityEventJsListener(const sync_pb::GaiaPasswordReuse& event);
+
+  // Called when any new PhishGuard pings are sent while one or more WebUI tabs
+  // are open.
+  void NotifyPGPingJsListener(int token,
+                              const LoginReputationClientRequest& request);
+
+  // Called when any new PhishGuard responses are received while one or more
+  // WebUI tabs are open.
+  void NotifyPGResponseJsListener(
+      int token,
+      const LoginReputationClientResponse& response);
+
+  // Called when any new real time lookup pings are sent while one or more
+  // WebUI tabs are open.
+  void NotifyRTLookupPingJsListener(int token, const RTLookupRequest& request);
+
+  // Called when any new real time lookup responses are received while one or
+  // more WebUI tabs are open.
+  void NotifyRTLookupResponseJsListener(int token,
+                                        const RTLookupResponse& response);
+
+  // Called when any new log messages are received while one or more WebUI tabs
+  // are open.
+  void NotifyLogMessageJsListener(const base::Time& timestamp,
+                                  const std::string& message);
+
+  // Called when any new reporting events are sent while one or more WebUI tabs
+  // are open.
+  void NotifyReportingEventJsListener(const base::Value& event);
+
+  // Callback when the CookieManager has returned the cookie.
+  void OnGetCookie(const std::string& callback_id,
+                   const std::vector<net::CanonicalCookie>& cookies);
+
+  content::BrowserContext* browser_context_;
+
+  // List that keeps all the WebUI listener objects.
+  static std::vector<SafeBrowsingUIHandler*> webui_list_;
+
+  base::WeakPtrFactory<SafeBrowsingUIHandler> weak_factory_{this};
+
+  DISALLOW_COPY_AND_ASSIGN(SafeBrowsingUIHandler);
+};
+
+// The WebUI for chrome://safe-browsing
+class SafeBrowsingUI : public content::WebUIController {
+ public:
+  explicit SafeBrowsingUI(content::WebUI* web_ui);
+  ~SafeBrowsingUI() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SafeBrowsingUI);
+};
+
+class WebUIInfoSingleton {
+ public:
+  static WebUIInfoSingleton* GetInstance();
+
+  // Returns true when there is a listening chrome://safe-browsing tab.
+  static bool HasListener();
+
+  // Add the new message in |client_download_requests_sent_| and send it to all
+  // the open chrome://safe-browsing tabs.
+  void AddToClientDownloadRequestsSent(
+      std::unique_ptr<ClientDownloadRequest> report_request);
+
+  // Clear the list of the sent ClientDownloadRequest messages.
+  void ClearClientDownloadRequestsSent();
+
+  // Add the new message in |client_download_responses_received_| and send it to
+  // all the open chrome://safe-browsing tabs.
+  void AddToClientDownloadResponsesReceived(
+      std::unique_ptr<ClientDownloadResponse> response);
+
+  // Clear the list of the received ClientDownloadResponse messages.
+  void ClearClientDownloadResponsesReceived();
+
+  // Add the new message in |csbrrs_sent_| and send it to all the open
+  // chrome://safe-browsing tabs.
+  void AddToCSBRRsSent(std::unique_ptr<ClientSafeBrowsingReportRequest> csbrr);
+
+  // Clear the list of the sent ClientSafeBrowsingReportRequest messages.
+  void ClearCSBRRsSent();
+
+  // Add the new message in |pg_event_log_| and send it to all the open
+  // chrome://safe-browsing tabs.
+  void AddToPGEvents(const sync_pb::UserEventSpecifics& event);
+
+  // Clear the list of sent PhishGuard events.
+  void ClearPGEvents();
+
+  // Add the new message in |security_event_log_| and send it to all the open
+  // chrome://safe-browsing tabs.
+  void AddToSecurityEvents(const sync_pb::GaiaPasswordReuse& event);
+
+  // Clear the list of sent Security events.
+  void ClearSecurityEvents();
+
+  // Add the new ping to |pg_pings_| and send it to all the open
+  // chrome://safe-browsing tabs. Returns a token that can be used in
+  // |AddToPGReponses| to correlate a ping and response.
+  int AddToPGPings(const LoginReputationClientRequest& request);
+
+  // Add the new response to |pg_responses_| and send it to all the open
+  // chrome://safe-browsing tabs.
+  void AddToPGResponses(int token,
+                        const LoginReputationClientResponse& response);
+
+  // Clear the list of sent PhishGuard pings and responses.
+  void ClearPGPings();
+
+  // Add the new ping to |rt_lookup_pings_|. Returns a token that can be used in
+  // |AddToRTLookupResponses| to correlate a ping and response.
+  int AddToRTLookupPings(const RTLookupRequest request);
+
+  // Add the new response to |rt_lookup_responses_| and send it to all the open
+  // chrome://safe-browsing tabs.
+  void AddToRTLookupResponses(int token, const RTLookupResponse response);
+
+  // Clear the list of sent RT Lookup pings and responses.
+  void ClearRTLookupPings();
+
+  // Log an arbitrary message. Frequently used for debugging.
+  void LogMessage(const std::string& message);
+
+  // Clear the log messages.
+  void ClearLogMessages();
+
+  // Notify listeners of changes to the log messages. Static to avoid this being
+  // called after the destruction of the WebUIInfoSingleton
+  static void NotifyLogMessageListeners(const base::Time& timestamp,
+                                        const std::string& message);
+
+  // Add the reporting event to |reporting_events_| and send it to all the open
+  // chrome://safe-browsing tabs.
+  void AddToReportingEvents(const base::Value& event);
+
+  // Clear |reporting_events_|.
+  void ClearReportingEvents();
+
+  // Register the new WebUI listener object.
+  void RegisterWebUIInstance(SafeBrowsingUIHandler* webui);
+
+  // Unregister the WebUI listener object, and clean the list of reports, if
+  // this is last listener.
+  void UnregisterWebUIInstance(SafeBrowsingUIHandler* webui);
+
+  // Get the list of the sent ClientDownloadRequests that have been collected
+  // since the oldest currently open chrome://safe-browsing tab was opened.
+  const std::vector<std::unique_ptr<ClientDownloadRequest>>&
+  client_download_requests_sent() const {
+    return client_download_requests_sent_;
+  }
+
+  // Get the list of the sent ClientDownloadResponses that have been collected
+  // since the oldest currently open chrome://safe-browsing tab was opened.
+  const std::vector<std::unique_ptr<ClientDownloadResponse>>&
+  client_download_responses_received() const {
+    return client_download_responses_received_;
+  }
+
+  // Get the list of the sent CSBRR reports that have been collected since the
+  // oldest currently open chrome://safe-browsing tab was opened.
+  const std::vector<std::unique_ptr<ClientSafeBrowsingReportRequest>>&
+  csbrrs_sent() const {
+    return csbrrs_sent_;
+  }
+
+  // Get the list of WebUI listener objects.
+  const std::vector<SafeBrowsingUIHandler*>& webui_instances() const {
+    return webui_instances_;
+  }
+
+  // Get the list of PhishGuard events since the oldest currently open
+  // chrome://safe-browsing tab was opened.
+  const std::vector<sync_pb::UserEventSpecifics>& pg_event_log() const {
+    return pg_event_log_;
+  }
+
+  // Get the list of Security events since the oldest currently open
+  // chrome://safe-browsing tab was opened.
+  const std::vector<sync_pb::GaiaPasswordReuse>& security_event_log() const {
+    return security_event_log_;
+  }
+
+  // Get the list of PhishGuard pings since the oldest currently open
+  // chrome://safe-browsing tab was opened.
+  const std::vector<LoginReputationClientRequest>& pg_pings() const {
+    return pg_pings_;
+  }
+
+  // Get the list of PhishGuard pings since the oldest currently open
+  // chrome://safe-browsing tab was opened.
+  const std::map<int, LoginReputationClientResponse>& pg_responses() const {
+    return pg_responses_;
+  }
+
+  // Get the list of real time lookup pings since the oldest currently open
+  // chrome://safe-browsing tab was opened.
+  const std::vector<RTLookupRequest>& rt_lookup_pings() const {
+    return rt_lookup_pings_;
+  }
+
+  // Get the list of real time lookup pings since the oldest currently open
+  // chrome://safe-browsing tab was opened.
+  const std::map<int, RTLookupResponse>& rt_lookup_responses() const {
+    return rt_lookup_responses_;
+  }
+
+  ReferrerChainProvider* referrer_chain_provider() {
+    return referrer_chain_provider_;
+  }
+
+  void set_referrer_chain_provider(ReferrerChainProvider* provider) {
+    referrer_chain_provider_ = provider;
+  }
+
+  const std::vector<std::pair<base::Time, std::string>>& log_messages() {
+    return log_messages_;
+  }
+
+  const std::vector<base::Value>& reporting_events() {
+    return reporting_events_;
+  }
+
+  network::mojom::CookieManager* GetCookieManager();
+
+  void set_network_context(SafeBrowsingNetworkContext* network_context) {
+    network_context_ = network_context;
+  }
+
+  void AddListenerForTesting() { has_test_listener_ = true; }
+
+ private:
+  WebUIInfoSingleton();
+  ~WebUIInfoSingleton();
+
+  void InitializeCookieManager();
+
+  friend struct base::DefaultSingletonTraits<WebUIInfoSingleton>;
+
+  // List of ClientDownloadRequests sent since since the oldest currently open
+  // chrome://safe-browsing tab was opened.
+  // "ClientDownloadRequests" cannot be const, due to being used by functions
+  // that call AllowJavascript(), which is not marked const.
+  std::vector<std::unique_ptr<ClientDownloadRequest>>
+      client_download_requests_sent_;
+
+  // List of ClientDownloadResponses received since since the oldest currently
+  // open chrome://safe-browsing tab was opened. "ClientDownloadReponse" cannot
+  // be const, due to being used by functions that call AllowJavascript(), which
+  // is not marked const.
+  std::vector<std::unique_ptr<ClientDownloadResponse>>
+      client_download_responses_received_;
+
+  // List of CSBRRs sent since since the oldest currently open
+  // chrome://safe-browsing tab was opened.
+  // "ClientSafeBrowsingReportRequest" cannot be const, due to being used by
+  // functions that call AllowJavascript(), which is not marked const.
+  std::vector<std::unique_ptr<ClientSafeBrowsingReportRequest>> csbrrs_sent_;
+
+  // List of PhishGuard events sent since the oldest currently open
+  // chrome://safe-browsing tab was opened.
+  std::vector<sync_pb::UserEventSpecifics> pg_event_log_;
+
+  // List of Security events sent since the oldest currently open
+  // chrome://safe-browsing tab was opened.
+  std::vector<sync_pb::GaiaPasswordReuse> security_event_log_;
+
+  // List of PhishGuard pings sent since the oldest currently open
+  // chrome://safe-browsing tab was opened.
+  std::vector<LoginReputationClientRequest> pg_pings_;
+
+  // List of PhishGuard responses received since the oldest currently open
+  // chrome://safe-browsing tab was opened.
+  std::map<int, LoginReputationClientResponse> pg_responses_;
+
+  // List of real time lookup pings sent since the oldest currently open
+  // chrome://safe-browsing tab was opened.
+  std::vector<RTLookupRequest> rt_lookup_pings_;
+
+  // List of real time lookup responses received since the oldest currently open
+  // chrome://safe-browsing tab was opened.
+  std::map<int, RTLookupResponse> rt_lookup_responses_;
+
+  // List of WebUI listener objects. "SafeBrowsingUIHandler*" cannot be const,
+  // due to being used by functions that call AllowJavascript(), which is not
+  // marked const.
+  std::vector<SafeBrowsingUIHandler*> webui_instances_;
+
+  // List of messages logged since the oldest currently open
+  // chrome://safe-browsing tab was opened.
+  std::vector<std::pair<base::Time, std::string>> log_messages_;
+
+  // List of reporting events logged since the oldest currently open
+  // chrome://safe-browsing tab was opened.
+  std::vector<base::Value> reporting_events_;
+
+  // The current referrer chain provider, if any. Can be nullptr.
+  ReferrerChainProvider* referrer_chain_provider_ = nullptr;
+
+  // The current NetworkContext for Safe Browsing pings.
+  SafeBrowsingNetworkContext* network_context_ = nullptr;
+
+  // The current CookieManager for the Safe Browsing cookie.
+  mojo::Remote<network::mojom::CookieManager> cookie_manager_remote_;
+
+  // Whether there is a test listener.
+  bool has_test_listener_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(WebUIInfoSingleton);
+};
+
+// Used for streaming messages to the WebUIInfoSingleton. Collects streamed
+// messages, then sends them to the WebUIInfoSingleton when destroyed. Intended
+// to be used in CRSBLOG macro.
+class CrSBLogMessage {
+ public:
+  CrSBLogMessage();
+  ~CrSBLogMessage();
+
+  std::ostream& stream() { return stream_; }
+
+ private:
+  std::ostringstream stream_;
+};
+
+// Used to consume a stream so that we don't even evaluate the streamed data if
+// there are no chrome://safe-browsing tabs open.
+class CrSBLogVoidify {
+ public:
+  CrSBLogVoidify() = default;
+
+  // This has to be an operator with a precedence lower than <<,
+  // but higher than ?:
+  void operator&(std::ostream&) {}
+};
+
+#define CRSBLOG                                         \
+  (!::safe_browsing::WebUIInfoSingleton::HasListener()) \
+      ? static_cast<void>(0)                            \
+      : ::safe_browsing::CrSBLogVoidify() &             \
+            ::safe_browsing::CrSBLogMessage().stream()
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CONTENT_WEB_UI_SAFE_BROWSING_UI_H_
diff --git a/components/safe_browsing/content/web_ui/safe_browsing_ui_unittest.cc b/components/safe_browsing/content/web_ui/safe_browsing_ui_unittest.cc
new file mode 100644
index 0000000..7c0c928
--- /dev/null
+++ b/components/safe_browsing/content/web_ui/safe_browsing_ui_unittest.cc
@@ -0,0 +1,63 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/content/web_ui/safe_browsing_ui.h"
+#include "content/public/test/browser_task_environment.h"
+#include "content/public/test/test_browser_context.h"
+#include "content/public/test/test_web_ui.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace safe_browsing {
+
+class SafeBrowsingUITest : public testing::Test {
+ public:
+  SafeBrowsingUITest() {}
+
+  void SetUp() override {}
+
+  int SetMemberInt(int member_int) {
+    member_int_ = member_int;
+    return member_int_;
+  }
+
+  SafeBrowsingUIHandler* RegisterNewHandler() {
+    auto handler_unique =
+        std::make_unique<SafeBrowsingUIHandler>(&browser_context_);
+
+    SafeBrowsingUIHandler* handler = handler_unique.get();
+    handler->SetWebUIForTesting(&web_ui_);
+    WebUIInfoSingleton::GetInstance()->RegisterWebUIInstance(handler);
+
+    web_ui_.AddMessageHandler(std::move(handler_unique));
+    return handler;
+  }
+
+  void UnregisterHandler(SafeBrowsingUIHandler* handler) {
+    WebUIInfoSingleton::GetInstance()->UnregisterWebUIInstance(handler);
+  }
+
+ protected:
+  int member_int_;
+  content::TestWebUI web_ui_;
+  content::BrowserTaskEnvironment task_environment_;
+  content::TestBrowserContext browser_context_;
+};
+
+TEST_F(SafeBrowsingUITest, CRSBLOGDoesNotEvaluateWhenNoListeners) {
+  member_int_ = 0;
+
+  // Start with no listeners, so SetMemberInt() should not be evaluated.
+  CRSBLOG << SetMemberInt(1);
+  EXPECT_EQ(member_int_, 0);
+
+  // Register a listener, so SetMemberInt() will be evaluated.
+  SafeBrowsingUIHandler* handler = RegisterNewHandler();
+
+  CRSBLOG << SetMemberInt(1);
+  EXPECT_EQ(member_int_, 1);
+
+  UnregisterHandler(handler);
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/BUILD.gn b/components/safe_browsing/core/BUILD.gn
new file mode 100644
index 0000000..2c6b49f5
--- /dev/null
+++ b/components/safe_browsing/core/BUILD.gn
@@ -0,0 +1,138 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//third_party/protobuf/proto_library.gni")
+
+static_library("features") {
+  sources = [
+    "features.cc",
+    "features.h",
+  ]
+
+  deps = [
+    "//base:base",
+    "//components/safe_browsing:buildflags",
+  ]
+}
+
+# safe_browsing/ pulls in content/, which doesn't work on iOS.
+# TODO(thakis): This should be `safe_browsing_mode != 0`, but chromecast builds
+# set safe_browsing_mode to 0 and build chrome/, and chrome/ currently
+# unconditionally depends on things in this build file. Make these dependencies
+# conditional on safe_browsing_mode != 0 and then change the conditional here.
+if (!is_ios) {
+  assert(!is_ios, "safe_browsing/ pulls in content/ which doesn't work on iOS")
+
+  proto_library("csd_proto") {
+    sources = [
+      "proto/csd.proto",
+    ]
+  }
+
+  proto_library("webui_proto") {
+    sources = [
+      "proto/webui.proto",
+    ]
+  }
+
+  proto_library("realtimeapi_proto") {
+    sources = [
+      "proto/realtimeapi.proto",
+    ]
+
+    deps = [
+      ":csd_proto",
+    ]
+  }
+
+  proto_library("webprotect_proto") {
+    sources = [
+      "proto/webprotect.proto",
+    ]
+  }
+
+  static_library("ping_manager") {
+    sources = [
+      "ping_manager.cc",
+      "ping_manager.h",
+    ]
+
+    public_deps = [
+      "//google_apis:google_apis",
+    ]
+
+    deps = [
+      "//base:base",
+      "//components/safe_browsing/core/db:hit_report",
+      "//components/safe_browsing/core/db:util",
+      "//content/public/browser:browser",
+      "//net:net",
+    ]
+  }
+
+  source_set("ping_manager_unittest") {
+    testonly = true
+    sources = [
+      "ping_manager_unittest.cc",
+    ]
+
+    deps = [
+      ":ping_manager",
+      "//base:base",
+      "//components/safe_browsing/core/db:v4_test_util",
+      "//net:net",
+      "//net:test_support",
+      "//testing/gtest",
+    ]
+  }
+
+  source_set("public") {
+    sources = [
+      "safe_browsing_service_interface.cc",
+      "safe_browsing_service_interface.h",
+    ]
+
+    deps = [
+      "//base:base",
+      "//content/public/browser",
+    ]
+  }
+
+  source_set("verdict_cache_manager") {
+    sources = [
+      "verdict_cache_manager.cc",
+      "verdict_cache_manager.h",
+    ]
+
+    deps = [
+      ":csd_proto",
+      ":realtimeapi_proto",
+      "//base",
+      "//components/content_settings/core/browser",
+      "//components/history/core/browser",
+      "//components/password_manager/core/browser:browser",
+      "//components/safe_browsing/core/db:v4_protocol_manager_util",
+      "//content/public/browser",
+      "//url",
+    ]
+  }
+
+  source_set("verdict_cache_manager_unittest") {
+    testonly = true
+    sources = [
+      "verdict_cache_manager_unittest.cc",
+    ]
+
+    deps = [
+      ":csd_proto",
+      ":realtimeapi_proto",
+      ":verdict_cache_manager",
+      "//base",
+      "//components/content_settings/core/browser",
+      "//components/sync_preferences:test_support",
+      "//content/test:test_support",
+      "//testing/gtest",
+    ]
+  }
+}
diff --git a/components/safe_browsing/core/browser/BUILD.gn b/components/safe_browsing/core/browser/BUILD.gn
new file mode 100644
index 0000000..7ae7a17
--- /dev/null
+++ b/components/safe_browsing/core/browser/BUILD.gn
@@ -0,0 +1,56 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/features.gni")
+import("//build/config/jumbo.gni")
+
+jumbo_source_set("browser") {
+  sources = [
+    "safe_browsing_url_checker_impl.cc",
+    "safe_browsing_url_checker_impl.h",
+    "url_checker_delegate.h",
+  ]
+  deps = [
+    "//components/history/core/browser:browser",
+    "//components/safe_browsing/content",
+    "//components/safe_browsing/content/web_ui:web_ui",
+    "//components/safe_browsing/core:csd_proto",
+    "//components/safe_browsing/core:features",
+    "//components/safe_browsing/core:realtimeapi_proto",
+    "//components/safe_browsing/core/browser:network_context",
+    "//components/safe_browsing/core/browser:referrer_chain_provider",
+    "//components/safe_browsing/core/common:common",
+    "//components/safe_browsing/core/db:database_manager",
+    "//components/safe_browsing/core/realtime:policy_engine",
+    "//components/safe_browsing/core/realtime:url_lookup_service",
+    "//components/safe_browsing/core:verdict_cache_manager",
+    "//components/safe_browsing/core/web_ui:constants",
+    "//components/security_interstitials/content:security_interstitial_page",
+    "//content/public/browser:browser",
+    "//net:extras",
+  ]
+}
+
+source_set("network_context") {
+  sources = [
+    "safe_browsing_network_context.cc",
+    "safe_browsing_network_context.h",
+  ]
+
+  deps = [
+    "//components/safe_browsing/core/common:common",
+    "//content/public/browser:browser",
+    "//net:extras",
+  ]
+}
+
+source_set("referrer_chain_provider") {
+  sources = [
+    "referrer_chain_provider.h",
+  ]
+  deps = [
+    "//components/safe_browsing/core:csd_proto",
+    "//components/sessions",
+  ]
+}
diff --git a/components/safe_browsing/core/browser/DEPS b/components/safe_browsing/core/browser/DEPS
new file mode 100644
index 0000000..3e20f42
--- /dev/null
+++ b/components/safe_browsing/core/browser/DEPS
@@ -0,0 +1,21 @@
+include_rules = [
+  "+components/history/core/browser",
+  "+components/safe_browsing/core/proto/csd.pb.h",
+  "+components/sessions/core/session_id.h",
+  "+content/public/browser",
+  "+ipc/ipc_message.h",
+  "+net/cookies",
+  "+net/extras",
+  "+net/http",
+  "+net/ssl",
+  "+net/traffic_annotation",
+  "+services/network/network_context.h",
+  "+services/network/public",
+  "+services/service_manager/public/cpp",
+]
+
+specific_include_rules = {
+  ".*test\.cc": [
+    "+content/public/test/browser_task_environment.h",
+  ],
+}
diff --git a/components/safe_browsing/core/browser/referrer_chain_provider.h b/components/safe_browsing/core/browser/referrer_chain_provider.h
new file mode 100644
index 0000000..6f9f7bf
--- /dev/null
+++ b/components/safe_browsing/core/browser/referrer_chain_provider.h
@@ -0,0 +1,49 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CORE_BROWSER_REFERRER_CHAIN_PROVIDER_H_
+#define COMPONENTS_SAFE_BROWSING_CORE_BROWSER_REFERRER_CHAIN_PROVIDER_H_
+
+#include "components/safe_browsing/core/proto/csd.pb.h"
+#include "components/sessions/core/session_id.h"
+#include "url/gurl.h"
+
+namespace content {
+class WebContents;
+}
+
+namespace safe_browsing {
+using ReferrerChain =
+    google::protobuf::RepeatedPtrField<safe_browsing::ReferrerChainEntry>;
+
+class ReferrerChainProvider {
+ 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,
+    SUCCESS_REFERRER = 6,  // Successfully identified extra referrers beyond the
+                           // landing referrer.
+
+    // Always at the end.
+    ATTRIBUTION_FAILURE_TYPE_MAX
+  };
+
+  virtual AttributionResult IdentifyReferrerChainByWebContents(
+      content::WebContents* web_contents,
+      int user_gesture_count_limit,
+      ReferrerChain* out_referrer_chain) = 0;
+
+  virtual AttributionResult IdentifyReferrerChainByEventURL(
+      const GURL& event_url,
+      SessionID event_tab_id,
+      int user_gesture_count_limit,
+      ReferrerChain* out_referrer_chain) = 0;
+};
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CORE_BROWSER_REFERRER_CHAIN_PROVIDER_H_
diff --git a/components/safe_browsing/core/browser/safe_browsing_network_context.cc b/components/safe_browsing/core/browser/safe_browsing_network_context.cc
new file mode 100644
index 0000000..ca31d367
--- /dev/null
+++ b/components/safe_browsing/core/browser/safe_browsing_network_context.cc
@@ -0,0 +1,177 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/core/browser/safe_browsing_network_context.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/task/post_task.h"
+#include "components/safe_browsing/core/common/safebrowsing_constants.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/network_context_client_base.h"
+#include "content/public/browser/network_service_instance.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "mojo/public/cpp/bindings/self_owned_receiver.h"
+#include "net/net_buildflags.h"
+#include "services/network/network_context.h"
+#include "services/network/public/mojom/url_loader_factory.mojom.h"
+
+namespace safe_browsing {
+
+class SafeBrowsingNetworkContext::SharedURLLoaderFactory
+    : public network::SharedURLLoaderFactory {
+ public:
+  SharedURLLoaderFactory(
+      const base::FilePath& user_data_dir,
+      NetworkContextParamsFactory network_context_params_factory)
+      : user_data_dir_(user_data_dir),
+        network_context_params_factory_(
+            std::move(network_context_params_factory)) {}
+
+  void Reset() {
+    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+    url_loader_factory_.reset();
+    network_context_.reset();
+  }
+
+  network::mojom::NetworkContext* GetNetworkContext() {
+    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+    if (!network_context_ || !network_context_.is_connected()) {
+      network_context_.reset();
+      content::GetNetworkService()->CreateNetworkContext(
+          network_context_.BindNewPipeAndPassReceiver(),
+          CreateNetworkContextParams());
+
+      mojo::PendingRemote<network::mojom::NetworkContextClient> client_remote;
+      mojo::MakeSelfOwnedReceiver(
+          std::make_unique<content::NetworkContextClientBase>(),
+          client_remote.InitWithNewPipeAndPassReceiver());
+      network_context_->SetClient(std::move(client_remote));
+    }
+    return network_context_.get();
+  }
+
+  void FlushForTesting() {
+    if (network_context_)
+      network_context_.FlushForTesting();
+    if (url_loader_factory_)
+      url_loader_factory_.FlushForTesting();
+  }
+
+ protected:
+  // network::URLLoaderFactory implementation:
+  void CreateLoaderAndStart(
+      mojo::PendingReceiver<network::mojom::URLLoader> loader,
+      int32_t routing_id,
+      int32_t request_id,
+      uint32_t options,
+      const network::ResourceRequest& request,
+      mojo::PendingRemote<network::mojom::URLLoaderClient> client,
+      const net::MutableNetworkTrafficAnnotationTag& traffic_annotation)
+      override {
+    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+    GetURLLoaderFactory()->CreateLoaderAndStart(
+        std::move(loader), routing_id, request_id, options, request,
+        std::move(client), traffic_annotation);
+  }
+
+  void Clone(mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver)
+      override {
+    GetURLLoaderFactory()->Clone(std::move(receiver));
+  }
+
+  // network::SharedURLLoaderFactory implementation:
+  std::unique_ptr<network::PendingSharedURLLoaderFactory> Clone() override {
+    NOTREACHED();
+    return nullptr;
+  }
+
+  network::mojom::URLLoaderFactory* GetURLLoaderFactory() {
+    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+    if (!url_loader_factory_ || !url_loader_factory_.is_connected()) {
+      network::mojom::URLLoaderFactoryParamsPtr params =
+          network::mojom::URLLoaderFactoryParams::New();
+      params->process_id = network::mojom::kBrowserProcessId;
+      params->is_corb_enabled = false;
+      params->is_trusted = true;
+      GetNetworkContext()->CreateURLLoaderFactory(
+          url_loader_factory_.BindNewPipeAndPassReceiver(), std::move(params));
+    }
+    return url_loader_factory_.get();
+  }
+
+ private:
+  friend class base::RefCounted<SharedURLLoaderFactory>;
+  ~SharedURLLoaderFactory() override = default;
+
+  network::mojom::NetworkContextParamsPtr CreateNetworkContextParams() {
+    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+    network::mojom::NetworkContextParamsPtr network_context_params =
+        network_context_params_factory_.Run();
+
+    network_context_params->context_name = std::string("safe_browsing");
+
+    network_context_params->http_cache_enabled = false;
+
+    // These are needed for PAC scripts that use FTP URLs.
+#if !BUILDFLAG(DISABLE_FTP_SUPPORT)
+    network_context_params->enable_ftp_url_support = true;
+#endif  // !BUILDFLAG(DISABLE_FTP_SUPPORT)
+
+    base::FilePath cookie_path = user_data_dir_.Append(
+        base::FilePath::StringType(kSafeBrowsingBaseFilename) + kCookiesFile);
+    network_context_params->cookie_path = cookie_path;
+    network_context_params->enable_encrypted_cookies = false;
+
+    return network_context_params;
+  }
+
+  base::FilePath user_data_dir_;
+  NetworkContextParamsFactory network_context_params_factory_;
+  mojo::Remote<network::mojom::NetworkContext> network_context_;
+  mojo::Remote<network::mojom::URLLoaderFactory> url_loader_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(SharedURLLoaderFactory);
+};
+
+SafeBrowsingNetworkContext::SafeBrowsingNetworkContext(
+    const base::FilePath& user_data_dir,
+    NetworkContextParamsFactory network_context_params_factory) {
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+  url_loader_factory_ = base::MakeRefCounted<SharedURLLoaderFactory>(
+      user_data_dir, std::move(network_context_params_factory));
+}
+
+SafeBrowsingNetworkContext::~SafeBrowsingNetworkContext() {
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+}
+
+scoped_refptr<network::SharedURLLoaderFactory>
+SafeBrowsingNetworkContext::GetURLLoaderFactory() {
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+  return url_loader_factory_;
+}
+
+network::mojom::NetworkContext*
+SafeBrowsingNetworkContext::GetNetworkContext() {
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+  return url_loader_factory_->GetNetworkContext();
+}
+
+void SafeBrowsingNetworkContext::FlushForTesting() {
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+  url_loader_factory_->FlushForTesting();
+}
+
+void SafeBrowsingNetworkContext::ServiceShuttingDown() {
+  url_loader_factory_->Reset();
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/browser/safe_browsing_network_context.h b/components/safe_browsing/core/browser/safe_browsing_network_context.h
new file mode 100644
index 0000000..429639bc
--- /dev/null
+++ b/components/safe_browsing/core/browser/safe_browsing_network_context.h
@@ -0,0 +1,56 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CORE_BROWSER_SAFE_BROWSING_NETWORK_CONTEXT_H_
+#define COMPONENTS_SAFE_BROWSING_CORE_BROWSER_SAFE_BROWSING_NETWORK_CONTEXT_H_
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/mojom/network_service.mojom.h"
+
+namespace network {
+namespace mojom {
+class NetworkContext;
+}
+}  // namespace network
+
+namespace safe_browsing {
+
+// This class owns the NetworkContext that is used for requests by Safe
+// Browsing.
+// All methods are called on the UI thread.
+class SafeBrowsingNetworkContext {
+ public:
+  // |user_data_dir| and |network_context_params_factory| are used
+  // to construct a URLRequestContext through the network service.
+  using NetworkContextParamsFactory =
+      base::RepeatingCallback<network::mojom::NetworkContextParamsPtr()>;
+  SafeBrowsingNetworkContext(
+      const base::FilePath& user_data_dir,
+      NetworkContextParamsFactory network_context_params_factory);
+  ~SafeBrowsingNetworkContext();
+
+  // Returns a SharedURLLoaderFactory.
+  scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory();
+
+  // Returns a NetworkContext.
+  network::mojom::NetworkContext* GetNetworkContext();
+
+  // Flushes NetworkContext and URLLoaderFactory pipes.
+  void FlushForTesting();
+
+  // Called at shutdown to ensure that the URLLoaderFactory is cleaned up.
+  void ServiceShuttingDown();
+
+ private:
+  class SharedURLLoaderFactory;
+
+  scoped_refptr<SharedURLLoaderFactory> url_loader_factory_;
+};
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CORE_BROWSER_SAFE_BROWSING_NETWORK_CONTEXT_H_
diff --git a/components/safe_browsing/core/browser/safe_browsing_url_checker_impl.cc b/components/safe_browsing/core/browser/safe_browsing_url_checker_impl.cc
new file mode 100644
index 0000000..6d71b99f
--- /dev/null
+++ b/components/safe_browsing/core/browser/safe_browsing_url_checker_impl.cc
@@ -0,0 +1,577 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/core/browser/safe_browsing_url_checker_impl.h"
+
+#include "base/bind.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/metrics/histogram_macros_local.h"
+#include "base/task/post_task.h"
+#include "base/trace_event/trace_event.h"
+#include "components/safe_browsing/content/web_ui/safe_browsing_ui.h"
+#include "components/safe_browsing/core/browser/url_checker_delegate.h"
+#include "components/safe_browsing/core/realtime/policy_engine.h"
+#include "components/safe_browsing/core/realtime/url_lookup_service.h"
+#include "components/safe_browsing/core/verdict_cache_manager.h"
+#include "components/safe_browsing/core/web_ui/constants.h"
+#include "components/security_interstitials/content/unsafe_resource.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/web_contents.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "net/base/load_flags.h"
+#include "net/http/http_request_headers.h"
+#include "net/log/net_log_event_type.h"
+
+namespace safe_browsing {
+namespace {
+
+// Maximum time in milliseconds to wait for the SafeBrowsing service reputation
+// check. After this amount of time the outstanding check will be aborted, and
+// the resource will be treated as if it were safe.
+const int kCheckUrlTimeoutMs = 5000;
+
+void RecordCheckUrlTimeout(bool timed_out) {
+  UMA_HISTOGRAM_BOOLEAN("SafeBrowsing.CheckUrl.Timeout", timed_out);
+}
+
+}  // namespace
+
+SafeBrowsingUrlCheckerImpl::Notifier::Notifier(CheckUrlCallback callback)
+    : callback_(std::move(callback)) {}
+
+SafeBrowsingUrlCheckerImpl::Notifier::Notifier(
+    NativeCheckUrlCallback native_callback)
+    : native_callback_(std::move(native_callback)) {}
+
+SafeBrowsingUrlCheckerImpl::Notifier::~Notifier() = default;
+
+SafeBrowsingUrlCheckerImpl::Notifier::Notifier(Notifier&& other) = default;
+
+SafeBrowsingUrlCheckerImpl::Notifier& SafeBrowsingUrlCheckerImpl::Notifier::
+operator=(Notifier&& other) = default;
+
+void SafeBrowsingUrlCheckerImpl::Notifier::OnStartSlowCheck() {
+  if (callback_) {
+    std::move(callback_).Run(slow_check_notifier_.BindNewPipeAndPassReceiver(),
+                             false, false);
+    return;
+  }
+
+  DCHECK(native_callback_);
+  std::move(native_callback_).Run(&native_slow_check_notifier_, false, false);
+}
+
+void SafeBrowsingUrlCheckerImpl::Notifier::OnCompleteCheck(
+    bool proceed,
+    bool showed_interstitial) {
+  if (callback_) {
+    std::move(callback_).Run(mojo::NullReceiver(), proceed,
+                             showed_interstitial);
+    return;
+  }
+
+  if (native_callback_) {
+    std::move(native_callback_).Run(nullptr, proceed, showed_interstitial);
+    return;
+  }
+
+  if (slow_check_notifier_) {
+    slow_check_notifier_->OnCompleteCheck(proceed, showed_interstitial);
+    slow_check_notifier_.reset();
+    return;
+  }
+
+  std::move(native_slow_check_notifier_).Run(proceed, showed_interstitial);
+}
+
+SafeBrowsingUrlCheckerImpl::UrlInfo::UrlInfo(const GURL& in_url,
+                                             const std::string& in_method,
+                                             Notifier in_notifier)
+    : url(in_url), method(in_method), notifier(std::move(in_notifier)) {}
+
+SafeBrowsingUrlCheckerImpl::UrlInfo::UrlInfo(UrlInfo&& other) = default;
+
+SafeBrowsingUrlCheckerImpl::UrlInfo::~UrlInfo() = default;
+
+SafeBrowsingUrlCheckerImpl::SafeBrowsingUrlCheckerImpl(
+    const net::HttpRequestHeaders& headers,
+    int load_flags,
+    content::ResourceType resource_type,
+    bool has_user_gesture,
+    scoped_refptr<UrlCheckerDelegate> url_checker_delegate,
+    const base::RepeatingCallback<content::WebContents*()>& web_contents_getter,
+    bool real_time_lookup_enabled,
+    base::WeakPtr<VerdictCacheManager> cache_manager_on_ui)
+    : headers_(headers),
+      load_flags_(load_flags),
+      resource_type_(resource_type),
+      has_user_gesture_(has_user_gesture),
+      web_contents_getter_(web_contents_getter),
+      url_checker_delegate_(std::move(url_checker_delegate)),
+      database_manager_(url_checker_delegate_->GetDatabaseManager()),
+      real_time_lookup_enabled_(real_time_lookup_enabled),
+      cache_manager_on_ui_(cache_manager_on_ui) {}
+
+SafeBrowsingUrlCheckerImpl::~SafeBrowsingUrlCheckerImpl() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+  if (state_ == STATE_CHECKING_URL) {
+    database_manager_->CancelCheck(this);
+
+    TRACE_EVENT_ASYNC_END1("safe_browsing", "CheckUrl", this, "result",
+                           "request_canceled");
+  }
+}
+
+void SafeBrowsingUrlCheckerImpl::CheckUrl(const GURL& url,
+                                          const std::string& method,
+                                          CheckUrlCallback callback) {
+  CheckUrlImpl(url, method, Notifier(std::move(callback)));
+}
+
+void SafeBrowsingUrlCheckerImpl::CheckUrl(const GURL& url,
+                                          const std::string& method,
+                                          NativeCheckUrlCallback callback) {
+  CheckUrlImpl(url, method, Notifier(std::move(callback)));
+}
+
+void SafeBrowsingUrlCheckerImpl::OnCheckBrowseUrlResult(
+    const GURL& url,
+    SBThreatType threat_type,
+    const ThreatMetadata& metadata) {
+  OnUrlResult(url, threat_type, metadata);
+}
+
+void SafeBrowsingUrlCheckerImpl::OnUrlResult(const GURL& url,
+                                             SBThreatType threat_type,
+                                             const ThreatMetadata& metadata) {
+  DCHECK_EQ(STATE_CHECKING_URL, state_);
+  DCHECK_LT(next_index_, urls_.size());
+  DCHECK_EQ(urls_[next_index_].url, url);
+
+  timer_.Stop();
+  RecordCheckUrlTimeout(/*timed_out=*/false);
+
+  TRACE_EVENT_ASYNC_END1(
+      "safe_browsing", "CheckUrl", this, "result",
+      threat_type == SB_THREAT_TYPE_SAFE ? "safe" : "unsafe");
+
+  if (threat_type == SB_THREAT_TYPE_SAFE ||
+      threat_type == SB_THREAT_TYPE_SUSPICIOUS_SITE) {
+    state_ = STATE_NONE;
+
+    if (threat_type == SB_THREAT_TYPE_SUSPICIOUS_SITE) {
+      url_checker_delegate_->NotifySuspiciousSiteDetected(web_contents_getter_);
+    }
+
+    if (!RunNextCallback(true, false))
+      return;
+
+    ProcessUrls();
+    return;
+  }
+
+  if (load_flags_ & net::LOAD_PREFETCH) {
+    // Destroy the prefetch with FINAL_STATUS_SAFEBROSWING.
+    if (resource_type_ == content::ResourceType::kMainFrame) {
+      url_checker_delegate_->MaybeDestroyPrerenderContents(
+          web_contents_getter_);
+    }
+    // Record the result of canceled unsafe prefetch. This is used as a signal
+    // for testing.
+    LOCAL_HISTOGRAM_ENUMERATION("SB2Test.ResourceTypes2.UnsafePrefetchCanceled",
+                                resource_type_);
+
+    BlockAndProcessUrls(false);
+    return;
+  }
+
+  UMA_HISTOGRAM_ENUMERATION("SB2.ResourceTypes2.Unsafe", resource_type_);
+
+  security_interstitials::UnsafeResource resource;
+  resource.url = url;
+  resource.original_url = urls_[0].url;
+  if (urls_.size() > 1) {
+    resource.redirect_urls.reserve(urls_.size() - 1);
+    for (size_t i = 1; i < urls_.size(); ++i)
+      resource.redirect_urls.push_back(urls_[i].url);
+  }
+  resource.is_subresource = resource_type_ != content::ResourceType::kMainFrame;
+  resource.is_subframe = resource_type_ == content::ResourceType::kSubFrame;
+  resource.threat_type = threat_type;
+  resource.threat_metadata = metadata;
+  resource.callback =
+      base::BindRepeating(&SafeBrowsingUrlCheckerImpl::OnBlockingPageComplete,
+                          weak_factory_.GetWeakPtr());
+  resource.callback_thread =
+      base::CreateSingleThreadTaskRunner({content::BrowserThread::IO});
+  resource.web_contents_getter = web_contents_getter_;
+  resource.threat_source = database_manager_->GetThreatSource();
+
+  state_ = STATE_DISPLAYING_BLOCKING_PAGE;
+  url_checker_delegate_->StartDisplayingBlockingPageHelper(
+      resource, urls_[next_index_].method, headers_,
+      resource_type_ == content::ResourceType::kMainFrame, has_user_gesture_);
+}
+
+void SafeBrowsingUrlCheckerImpl::OnTimeout() {
+  RecordCheckUrlTimeout(/*timed_out=*/true);
+
+  database_manager_->CancelCheck(this);
+
+  // Any pending callbacks on this URL check should be skipped.
+  weak_factory_.InvalidateWeakPtrs();
+
+  OnUrlResult(urls_[next_index_].url, safe_browsing::SB_THREAT_TYPE_SAFE,
+              ThreatMetadata());
+}
+
+void SafeBrowsingUrlCheckerImpl::CheckUrlImpl(const GURL& url,
+                                              const std::string& method,
+                                              Notifier notifier) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+  DVLOG(1) << "SafeBrowsingUrlCheckerImpl checks URL: " << url;
+  urls_.emplace_back(url, method, std::move(notifier));
+
+  ProcessUrls();
+}
+
+void SafeBrowsingUrlCheckerImpl::ProcessUrls() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  DCHECK_NE(STATE_BLOCKED, state_);
+
+  if (state_ == STATE_CHECKING_URL ||
+      state_ == STATE_DISPLAYING_BLOCKING_PAGE) {
+    return;
+  }
+
+  while (next_index_ < urls_.size()) {
+    DCHECK_EQ(STATE_NONE, state_);
+
+    const GURL& url = urls_[next_index_].url;
+    if (url_checker_delegate_->IsUrlWhitelisted(url)) {
+      if (!RunNextCallback(true, false))
+        return;
+
+      continue;
+    }
+
+    // TODO(yzshen): Consider moving CanCheckResourceType() to the renderer
+    // side. That would save some IPCs. It requires a method on the
+    // SafeBrowsing mojo interface to query all supported resource types.
+    if (!database_manager_->CanCheckResourceType(resource_type_)) {
+      // TODO(vakh): Consider changing this metric to
+      // SafeBrowsing.V4ResourceType to be consistent with the other PVer4
+      // metrics.
+      UMA_HISTOGRAM_ENUMERATION("SB2.ResourceTypes2.Skipped", resource_type_);
+
+      if (!RunNextCallback(true, false))
+        return;
+
+      continue;
+    }
+
+    // TODO(vakh): Consider changing this metric to SafeBrowsing.V4ResourceType
+    // to be consistent with the other PVer4 metrics.
+    UMA_HISTOGRAM_ENUMERATION("SB2.ResourceTypes2.Checked", resource_type_);
+
+    SBThreatType threat_type = CheckWebUIUrls(url);
+    if (threat_type != safe_browsing::SB_THREAT_TYPE_SAFE) {
+      state_ = STATE_CHECKING_URL;
+      TRACE_EVENT_ASYNC_BEGIN1("safe_browsing", "CheckUrl", this, "url",
+                               url.spec());
+
+      base::PostTask(
+          FROM_HERE, {content::BrowserThread::IO},
+          base::BindOnce(&SafeBrowsingUrlCheckerImpl::OnCheckBrowseUrlResult,
+                         weak_factory_.GetWeakPtr(), url, threat_type,
+                         ThreatMetadata()));
+      break;
+    }
+
+    TRACE_EVENT_ASYNC_BEGIN1("safe_browsing", "CheckUrl", this, "url",
+                             url.spec());
+
+    // Start a timer to abort the check if it takes too long.
+    timer_.Start(FROM_HERE,
+                 base::TimeDelta::FromMilliseconds(kCheckUrlTimeoutMs), this,
+                 &SafeBrowsingUrlCheckerImpl::OnTimeout);
+
+    bool safe_synchronously;
+    if (CanPerformFullURLLookup(url)) {
+      UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.RT.ResourceTypes.Checked",
+                                resource_type_);
+      safe_synchronously = false;
+      AsyncMatch match =
+          database_manager_->CheckUrlForHighConfidenceAllowlist(url, this);
+      UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.RT.LocalMatch.Result", match);
+      switch (match) {
+        case AsyncMatch::ASYNC:
+          // Hash-prefix matched. A call to
+          // |OnCheckUrlForHighConfidenceAllowlist| will follow.
+          break;
+        case AsyncMatch::MATCH:
+          // Full-hash matched locally so queue a call to
+          // |OnCheckUrlForHighConfidenceAllowlist| to trigger the hash-based
+          // checking.
+          base::PostTask(
+              FROM_HERE, {content::BrowserThread::IO},
+              base::BindOnce(&SafeBrowsingUrlCheckerImpl::
+                                 OnCheckUrlForHighConfidenceAllowlist,
+                             weak_factory_.GetWeakPtr(),
+                             /*did_match_allowlist=*/true));
+          break;
+        case AsyncMatch::NO_MATCH:
+          // No match found locally. Queue the call to
+          // |OnCheckUrlForHighConfidenceAllowlist| to perform the full URL
+          // lookup.
+          base::PostTask(
+              FROM_HERE, {content::BrowserThread::IO},
+              base::BindOnce(&SafeBrowsingUrlCheckerImpl::
+                                 OnCheckUrlForHighConfidenceAllowlist,
+                             weak_factory_.GetWeakPtr(),
+                             /*did_match_allowlist=*/false));
+          break;
+      }
+    } else {
+      safe_synchronously = database_manager_->CheckBrowseUrl(
+          url, url_checker_delegate_->GetThreatTypes(), this);
+    }
+
+    if (safe_synchronously) {
+      timer_.Stop();
+      RecordCheckUrlTimeout(/*timed_out=*/false);
+
+      if (!RunNextCallback(true, false))
+        return;
+
+      continue;
+    }
+
+    state_ = STATE_CHECKING_URL;
+
+    // Only send out notification of starting a slow check if the database
+    // manager actually supports fast checks (i.e., synchronous checks) but is
+    // not able to complete the check synchronously in this case.
+    // Don't send out notification if the database manager doesn't support
+    // synchronous checks at all (e.g., on mobile).
+    if (!database_manager_->ChecksAreAlwaysAsync())
+      urls_[next_index_].notifier.OnStartSlowCheck();
+
+    break;
+  }
+}
+
+void SafeBrowsingUrlCheckerImpl::BlockAndProcessUrls(bool showed_interstitial) {
+  DVLOG(1) << "SafeBrowsingUrlCheckerImpl blocks URL: "
+           << urls_[next_index_].url;
+  state_ = STATE_BLOCKED;
+
+  // If user decided to not proceed through a warning, mark all the remaining
+  // redirects as "bad".
+  while (next_index_ < urls_.size()) {
+    if (!RunNextCallback(false, showed_interstitial))
+      return;
+  }
+}
+
+bool SafeBrowsingUrlCheckerImpl::CanPerformFullURLLookup(const GURL& url) {
+  if (!real_time_lookup_enabled_)
+    return false;
+
+  if (!RealTimePolicyEngine::CanPerformFullURLLookupForResourceType(
+          resource_type_))
+    return false;
+
+  auto* rt_lookup_service = database_manager_->GetRealTimeUrlLookupService();
+  if (!rt_lookup_service || !rt_lookup_service->CanCheckUrl(url))
+    return false;
+
+  bool in_backoff = rt_lookup_service->IsInBackoffMode();
+  UMA_HISTOGRAM_BOOLEAN("SafeBrowsing.RT.Backoff.State", in_backoff);
+  return !in_backoff;
+}
+
+void SafeBrowsingUrlCheckerImpl::OnBlockingPageComplete(bool proceed) {
+  DCHECK_EQ(STATE_DISPLAYING_BLOCKING_PAGE, state_);
+
+  if (proceed) {
+    state_ = STATE_NONE;
+    if (!RunNextCallback(true, true))
+      return;
+    ProcessUrls();
+  } else {
+    BlockAndProcessUrls(true);
+  }
+}
+
+SBThreatType SafeBrowsingUrlCheckerImpl::CheckWebUIUrls(const GURL& url) {
+  if (url == kChromeUISafeBrowsingMatchMalwareUrl)
+    return safe_browsing::SB_THREAT_TYPE_URL_MALWARE;
+  if (url == kChromeUISafeBrowsingMatchPhishingUrl)
+    return safe_browsing::SB_THREAT_TYPE_URL_PHISHING;
+  if (url == kChromeUISafeBrowsingMatchUnwantedUrl)
+    return safe_browsing::SB_THREAT_TYPE_URL_UNWANTED;
+  if (url == kChromeUISafeBrowsingMatchBillingUrl)
+    return safe_browsing::SB_THREAT_TYPE_BILLING;
+
+  return safe_browsing::SB_THREAT_TYPE_SAFE;
+}
+
+bool SafeBrowsingUrlCheckerImpl::RunNextCallback(bool proceed,
+                                                 bool showed_interstitial) {
+  DCHECK_LT(next_index_, urls_.size());
+
+  auto weak_self = weak_factory_.GetWeakPtr();
+  urls_[next_index_++].notifier.OnCompleteCheck(proceed, showed_interstitial);
+  return !!weak_self;
+}
+
+void SafeBrowsingUrlCheckerImpl::OnCheckUrlForHighConfidenceAllowlist(
+    bool did_match_allowlist) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  DCHECK_EQ(content::ResourceType::kMainFrame, resource_type_);
+
+  const GURL& url = urls_[next_index_].url;
+  if (did_match_allowlist) {
+    // If the URL matches the high-confidence allowlist, still do the hash based
+    // checks.
+    if (database_manager_->CheckBrowseUrl(
+            url, url_checker_delegate_->GetThreatTypes(), this)) {
+      // No match found in the local database. Safe to call |OnUrlResult| here
+      // directly.
+      OnUrlResult(url, SB_THREAT_TYPE_SAFE, ThreatMetadata());
+    }
+    return;
+  }
+
+  base::PostTask(
+      FROM_HERE, {content::BrowserThread::UI},
+      base::BindOnce(
+          &SafeBrowsingUrlCheckerImpl::StartGetCachedRealTimeUrlVerdictOnUI,
+          weak_factory_.GetWeakPtr(), cache_manager_on_ui_, url,
+          base::TimeTicks::Now()));
+}
+
+// static
+void SafeBrowsingUrlCheckerImpl::StartGetCachedRealTimeUrlVerdictOnUI(
+    base::WeakPtr<SafeBrowsingUrlCheckerImpl> weak_checker_on_io,
+    base::WeakPtr<VerdictCacheManager> cache_manager_on_ui,
+    const GURL& url,
+    base::TimeTicks get_cache_start_time) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  std::unique_ptr<RTLookupResponse::ThreatInfo> cached_threat_info =
+      std::make_unique<RTLookupResponse::ThreatInfo>();
+
+  base::UmaHistogramBoolean("SafeBrowsing.RT.HasValidCacheManager",
+                            !!cache_manager_on_ui);
+
+  RTLookupResponse::ThreatInfo::VerdictType verdict_type =
+      cache_manager_on_ui
+          ? cache_manager_on_ui->GetCachedRealTimeUrlVerdict(
+                url, cached_threat_info.get())
+          : RTLookupResponse::ThreatInfo::VERDICT_TYPE_UNSPECIFIED;
+  base::PostTask(
+      FROM_HERE, {content::BrowserThread::IO},
+      base::BindOnce(
+          &SafeBrowsingUrlCheckerImpl::OnGetCachedRealTimeUrlVerdictDoneOnIO,
+          weak_checker_on_io, verdict_type, std::move(cached_threat_info), url,
+          get_cache_start_time));
+}
+
+void SafeBrowsingUrlCheckerImpl::OnGetCachedRealTimeUrlVerdictDoneOnIO(
+    RTLookupResponse::ThreatInfo::VerdictType verdict_type,
+    std::unique_ptr<RTLookupResponse::ThreatInfo> cached_threat_info,
+    const GURL& url,
+    base::TimeTicks get_cache_start_time) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+  base::UmaHistogramSparse("SafeBrowsing.RT.GetCacheResult", verdict_type);
+  UMA_HISTOGRAM_TIMES("SafeBrowsing.RT.GetCache.Time",
+                      base::TimeTicks::Now() - get_cache_start_time);
+
+  if (verdict_type == RTLookupResponse::ThreatInfo::SAFE) {
+    OnUrlResult(url, SB_THREAT_TYPE_SAFE, ThreatMetadata());
+    return;
+  } else if (verdict_type == RTLookupResponse::ThreatInfo::DANGEROUS) {
+    OnUrlResult(url,
+                RealTimeUrlLookupService::GetSBThreatTypeForRTThreatType(
+                    cached_threat_info->threat_type()),
+                ThreatMetadata());
+    return;
+  }
+
+  RTLookupRequestCallback request_callback =
+      base::BindOnce(&SafeBrowsingUrlCheckerImpl::OnRTLookupRequest,
+                     weak_factory_.GetWeakPtr());
+
+  RTLookupResponseCallback response_callback =
+      base::BindOnce(&SafeBrowsingUrlCheckerImpl::OnRTLookupResponse,
+                     weak_factory_.GetWeakPtr());
+
+  auto* rt_lookup_service = database_manager_->GetRealTimeUrlLookupService();
+  rt_lookup_service->StartLookup(url, std::move(request_callback),
+                                 std::move(response_callback));
+}
+
+void SafeBrowsingUrlCheckerImpl::OnRTLookupRequest(
+    std::unique_ptr<RTLookupRequest> request) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+  // The following is to log this RTLookupRequest on any open
+  // chrome://safe-browsing pages.
+  base::PostTaskAndReplyWithResult(
+      FROM_HERE, {content::BrowserThread::UI},
+      base::BindOnce(&WebUIInfoSingleton::AddToRTLookupPings,
+                     base::Unretained(WebUIInfoSingleton::GetInstance()),
+                     *request),
+      base::BindOnce(&SafeBrowsingUrlCheckerImpl::SetWebUIToken,
+                     weak_factory_.GetWeakPtr()));
+}
+
+void SafeBrowsingUrlCheckerImpl::OnRTLookupResponse(
+    std::unique_ptr<RTLookupResponse> response) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  DCHECK_EQ(content::ResourceType::kMainFrame, resource_type_);
+
+  if (url_web_ui_token_ != -1) {
+    // The following is to log this RTLookupResponse on any open
+    // chrome://safe-browsing pages.
+    base::PostTask(
+        FROM_HERE, {content::BrowserThread::UI},
+        base::BindOnce(&WebUIInfoSingleton::AddToRTLookupResponses,
+                       base::Unretained(WebUIInfoSingleton::GetInstance()),
+                       url_web_ui_token_, *response));
+  }
+
+  const GURL& url = urls_[next_index_].url;
+
+  SBThreatType sb_threat_type = SB_THREAT_TYPE_SAFE;
+  if (response && (response->threat_info_size() > 0)) {
+    base::PostTask(FROM_HERE, {content::BrowserThread::UI},
+                   base::BindOnce(&VerdictCacheManager::CacheRealTimeUrlVerdict,
+                                  cache_manager_on_ui_, url, *response,
+                                  base::Time::Now()));
+
+    // TODO(crbug.com/1033692): Only take the first threat info into account
+    // because threat infos are returned in decreasing order of severity.
+    // Consider extend it to support multiple threat types.
+    if (response->threat_info(0).verdict_type() ==
+        RTLookupResponse::ThreatInfo::DANGEROUS) {
+      sb_threat_type = RealTimeUrlLookupService::GetSBThreatTypeForRTThreatType(
+          response->threat_info(0).threat_type());
+    }
+  }
+  OnUrlResult(url, sb_threat_type, ThreatMetadata());
+}
+
+void SafeBrowsingUrlCheckerImpl::SetWebUIToken(int token) {
+  url_web_ui_token_ = token;
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/browser/safe_browsing_url_checker_impl.h b/components/safe_browsing/core/browser/safe_browsing_url_checker_impl.h
new file mode 100644
index 0000000..cb2a40e
--- /dev/null
+++ b/components/safe_browsing/core/browser/safe_browsing_url_checker_impl.h
@@ -0,0 +1,236 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CORE_BROWSER_SAFE_BROWSING_URL_CHECKER_IMPL_H_
+#define COMPONENTS_SAFE_BROWSING_CORE_BROWSER_SAFE_BROWSING_URL_CHECKER_IMPL_H_
+
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/timer/timer.h"
+#include "components/safe_browsing/core/common/safe_browsing.mojom.h"
+#include "components/safe_browsing/core/db/database_manager.h"
+#include "components/safe_browsing/core/proto/realtimeapi.pb.h"
+#include "content/public/common/resource_type.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "net/http/http_request_headers.h"
+#include "url/gurl.h"
+
+namespace content {
+class WebContents;
+}
+
+namespace safe_browsing {
+
+class UrlCheckerDelegate;
+
+class VerdictCacheManager;
+
+// A SafeBrowsingUrlCheckerImpl instance is used to perform SafeBrowsing check
+// for a URL and its redirect URLs. It implements Mojo interface so that it can
+// be used to handle queries from renderers. But it is also used to handle
+// queries from the browser. In that case, the public methods are called
+// directly instead of through Mojo.
+//
+// To be considered "safe", a URL must not appear in the SafeBrowsing blacklists
+// (see SafeBrowsingService for details).
+//
+// Note that the SafeBrowsing check takes at most kCheckUrlTimeoutMs
+// milliseconds. If it takes longer than this, then the system defaults to
+// treating the URL as safe.
+//
+// If the URL is classified as dangerous, a warning interstitial page is
+// displayed. In that case, the user can click through the warning page if they
+// decides to procced with loading the URL anyway.
+class SafeBrowsingUrlCheckerImpl : public mojom::SafeBrowsingUrlChecker,
+                                   public SafeBrowsingDatabaseManager::Client {
+ public:
+  using NativeUrlCheckNotifier =
+      base::OnceCallback<void(bool /* proceed */,
+                              bool /* showed_interstitial */)>;
+
+  // If |slow_check_notifier| is not null, the callback is supposed to update
+  // this output parameter with a callback to receive complete notification. In
+  // that case, |proceed| and |showed_interstitial| should be ignored.
+  using NativeCheckUrlCallback =
+      base::OnceCallback<void(NativeUrlCheckNotifier* /* slow_check_notifier */,
+                              bool /* proceed */,
+                              bool /* showed_interstitial */)>;
+
+  // Constructor for SafeBrowsingUrlCheckerImpl. |real_time_lookup_enabled|
+  // indicates whether or not the profile has enabled real time URL lookups, as
+  // computed by the RealTimePolicyEngine. This must be computed in advance,
+  // since this class only exists on the IO thread.
+  SafeBrowsingUrlCheckerImpl(
+      const net::HttpRequestHeaders& headers,
+      int load_flags,
+      content::ResourceType resource_type,
+      bool has_user_gesture,
+      scoped_refptr<UrlCheckerDelegate> url_checker_delegate,
+      const base::RepeatingCallback<content::WebContents*()>&
+          web_contents_getter,
+      bool real_time_lookup_enabled,
+      base::WeakPtr<VerdictCacheManager> cache_manager_on_ui);
+
+  ~SafeBrowsingUrlCheckerImpl() override;
+
+  // mojom::SafeBrowsingUrlChecker implementation.
+  // NOTE: |callback| could be run synchronously before this method returns. Be
+  // careful if |callback| could destroy this object.
+  void CheckUrl(const GURL& url,
+                const std::string& method,
+                CheckUrlCallback callback) override;
+
+  // NOTE: |callback| could be run synchronously before this method returns. Be
+  // careful if |callback| could destroy this object.
+  void CheckUrl(const GURL& url,
+                const std::string& method,
+                NativeCheckUrlCallback callback);
+
+ private:
+  class Notifier {
+   public:
+    explicit Notifier(CheckUrlCallback callback);
+    explicit Notifier(NativeCheckUrlCallback native_callback);
+
+    ~Notifier();
+
+    Notifier(Notifier&& other);
+    Notifier& operator=(Notifier&& other);
+
+    void OnStartSlowCheck();
+    void OnCompleteCheck(bool proceed, bool showed_interstitial);
+
+   private:
+    // Used in the mojo interface case.
+    CheckUrlCallback callback_;
+    mojo::Remote<mojom::UrlCheckNotifier> slow_check_notifier_;
+
+    // Used in the native call case.
+    NativeCheckUrlCallback native_callback_;
+    NativeUrlCheckNotifier native_slow_check_notifier_;
+  };
+
+  // SafeBrowsingDatabaseManager::Client implementation:
+  void OnCheckBrowseUrlResult(const GURL& url,
+                              SBThreatType threat_type,
+                              const ThreatMetadata& metadata) override;
+  void OnCheckUrlForHighConfidenceAllowlist(bool did_match_allowlist) override;
+
+  // This function has to be static because it is called in UI thread,
+  // |weak_checker_on_io| can only be accessed from IO thread.
+  // This function is called if the url doesn't match the allowlist.
+  static void StartGetCachedRealTimeUrlVerdictOnUI(
+      base::WeakPtr<SafeBrowsingUrlCheckerImpl> weak_checker_on_io,
+      base::WeakPtr<VerdictCacheManager> cache_manager_on_ui,
+      const GURL& url,
+      base::TimeTicks get_cache_start_time);
+
+  // This function will start real time url lookup if there is no cache match.
+  void OnGetCachedRealTimeUrlVerdictDoneOnIO(
+      RTLookupResponse::ThreatInfo::VerdictType verdict_type,
+      std::unique_ptr<RTLookupResponse::ThreatInfo> cached_threat_info,
+      const GURL& url,
+      base::TimeTicks get_cache_start_time);
+
+  void OnTimeout();
+
+  void OnUrlResult(const GURL& url,
+                   SBThreatType threat_type,
+                   const ThreatMetadata& metadata);
+
+  void CheckUrlImpl(const GURL& url,
+                    const std::string& method,
+                    Notifier notifier);
+
+  // NOTE: this method runs callbacks which could destroy this object.
+  void ProcessUrls();
+
+  // NOTE: this method runs callbacks which could destroy this object.
+  void BlockAndProcessUrls(bool showed_interstitial);
+
+  void OnBlockingPageComplete(bool proceed);
+
+  // Helper method that checks whether |url|'s reputation can be checked using
+  // real time lookups.
+  bool CanPerformFullURLLookup(const GURL& url);
+
+  SBThreatType CheckWebUIUrls(const GURL& url);
+
+  // Returns false if this object has been destroyed by the callback. In that
+  // case none of the members of this object should be touched again.
+  bool RunNextCallback(bool proceed, bool showed_interstitial);
+
+  // Called when the |request| from the real-time lookup service is sent.
+  void OnRTLookupRequest(std::unique_ptr<RTLookupRequest> request);
+
+  // Called when the |response| from the real-time lookup service is received.
+  void OnRTLookupResponse(std::unique_ptr<RTLookupResponse> response);
+
+  void SetWebUIToken(int token);
+
+  enum State {
+    // Haven't started checking or checking is complete.
+    STATE_NONE,
+    // We have one outstanding URL-check.
+    STATE_CHECKING_URL,
+    // We're displaying a blocking page.
+    STATE_DISPLAYING_BLOCKING_PAGE,
+    // The blocking page has returned *not* to proceed.
+    STATE_BLOCKED
+  };
+
+  struct UrlInfo {
+    UrlInfo(const GURL& url, const std::string& method, Notifier notifier);
+    UrlInfo(UrlInfo&& other);
+
+    ~UrlInfo();
+
+    GURL url;
+    std::string method;
+    Notifier notifier;
+  };
+
+  const net::HttpRequestHeaders headers_;
+  const int load_flags_;
+  const content::ResourceType resource_type_;
+  const bool has_user_gesture_;
+  base::RepeatingCallback<content::WebContents*()> web_contents_getter_;
+  scoped_refptr<UrlCheckerDelegate> url_checker_delegate_;
+  scoped_refptr<SafeBrowsingDatabaseManager> database_manager_;
+
+  // The redirect chain for this resource, including the original URL and
+  // subsequent redirect URLs.
+  std::vector<UrlInfo> urls_;
+  // |urls_| before |next_index_| have been checked. If |next_index_| is smaller
+  // than the size of |urls_|, the URL at |next_index_| is being processed.
+  size_t next_index_ = 0;
+
+  // Token used for displaying url real time lookup pings. A single token is
+  // sufficient since real time check only happens on main frame url.
+  int url_web_ui_token_ = -1;
+
+  State state_ = STATE_NONE;
+
+  // Timer to abort the SafeBrowsing check if it takes too long.
+  base::OneShotTimer timer_;
+
+  // Whether real time lookup is enabled for this request.
+  bool real_time_lookup_enabled_;
+
+  // Unowned object used for getting and storing real time url check cache.
+  // Must be NOT nullptr when real time url check is enabled and profile is not
+  // delete. Can only be accessed in UI thread.
+  base::WeakPtr<VerdictCacheManager> cache_manager_on_ui_;
+
+  base::WeakPtrFactory<SafeBrowsingUrlCheckerImpl> weak_factory_{this};
+
+  DISALLOW_COPY_AND_ASSIGN(SafeBrowsingUrlCheckerImpl);
+};
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CORE_BROWSER_SAFE_BROWSING_URL_CHECKER_IMPL_H_
diff --git a/components/safe_browsing/core/browser/url_checker_delegate.h b/components/safe_browsing/core/browser/url_checker_delegate.h
new file mode 100644
index 0000000..1cbb75f
--- /dev/null
+++ b/components/safe_browsing/core/browser/url_checker_delegate.h
@@ -0,0 +1,88 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CORE_BROWSER_URL_CHECKER_DELEGATE_H_
+#define COMPONENTS_SAFE_BROWSING_CORE_BROWSER_URL_CHECKER_DELEGATE_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
+
+namespace content {
+class WebContents;
+}
+
+namespace net {
+class HttpRequestHeaders;
+}
+
+namespace security_interstitials {
+struct UnsafeResource;
+}
+
+namespace safe_browsing {
+
+class BaseUIManager;
+class SafeBrowsingDatabaseManager;
+
+// Delegate interface for SafeBrowsingUrlCheckerImpl and SafeBrowsing's
+// content::ResourceThrottle subclasses. They delegate to this interface those
+// operations that different embedders (Chrome and Android WebView) handle
+// differently.
+//
+// All methods should only be called from the IO thread.
+class UrlCheckerDelegate
+    : public base::RefCountedThreadSafe<UrlCheckerDelegate> {
+ public:
+  // Destroys prerender contents if necessary. The parameter is a
+  // WebContents::OnceGetter, but that type is not visible from here.
+  virtual void MaybeDestroyPrerenderContents(
+      base::OnceCallback<content::WebContents*()> web_contents_getter) = 0;
+
+  // Starts displaying the SafeBrowsing interstitial page.
+  virtual void StartDisplayingBlockingPageHelper(
+      const security_interstitials::UnsafeResource& resource,
+      const std::string& method,
+      const net::HttpRequestHeaders& headers,
+      bool is_main_frame,
+      bool has_user_gesture) = 0;
+
+  // A whitelisted URL is considered safe and therefore won't be checked with
+  // the SafeBrowsing database.
+  virtual bool IsUrlWhitelisted(const GURL& url) = 0;
+
+  // If the method returns true, the entire request won't be checked, including
+  // the original URL and redirects.
+  // If neither of |render_process_id| and |render_frame_id| is -1, they will be
+  // used to identify the frame making the request; otherwise
+  // |frame_tree_node_id| will be used. Please note that |frame_tree_node_id|
+  // could also be -1, if a request is not associated with a frame.
+  virtual bool ShouldSkipRequestCheck(
+      const GURL& original_url,
+      int frame_tree_node_id,
+      int render_process_id,
+      int render_frame_id,
+      bool originated_from_service_worker) = 0;
+
+  // Notifies the SafeBrowsing Trigger Manager that a suspicious site has been
+  // detected. |web_contents_getter| is used to determine which tab the site
+  // was detected on.
+  virtual void NotifySuspiciousSiteDetected(
+      const base::RepeatingCallback<content::WebContents*()>&
+          web_contents_getter) = 0;
+
+  virtual const SBThreatTypeSet& GetThreatTypes() = 0;
+  virtual SafeBrowsingDatabaseManager* GetDatabaseManager() = 0;
+  virtual BaseUIManager* GetUIManager() = 0;
+
+ protected:
+  friend class base::RefCountedThreadSafe<UrlCheckerDelegate>;
+  virtual ~UrlCheckerDelegate() {}
+};
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CORE_BROWSER_URL_CHECKER_DELEGATE_H_
diff --git a/components/safe_browsing/core/common/BUILD.gn b/components/safe_browsing/core/common/BUILD.gn
new file mode 100644
index 0000000..ac5ce8df
--- /dev/null
+++ b/components/safe_browsing/core/common/BUILD.gn
@@ -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.
+
+import("//build/config/features.gni")
+import("//components/safe_browsing/buildflags.gni")
+import("//mojo/public/tools/bindings/mojom.gni")
+
+source_set("common") {
+  sources = [
+    "safebrowsing_constants.cc",
+    "safebrowsing_constants.h",
+    "safebrowsing_switches.cc",
+    "safebrowsing_switches.h",
+    "utils.cc",
+    "utils.h",
+  ]
+
+  deps = [
+    "//base",
+    "//components/policy/core/browser:browser",
+    "//components/prefs:prefs",
+    "//components/safe_browsing/core:csd_proto",
+    "//components/safe_browsing/core:features",
+    "//crypto:crypto",
+    "//ipc",
+    "//url/ipc:url_ipc",
+  ]
+
+  public_deps = [
+    ":interfaces",
+  ]
+}
+
+static_library("safe_browsing_prefs") {
+  sources = [
+    "safe_browsing_prefs.cc",
+    "safe_browsing_prefs.h",
+  ]
+
+  deps = [
+    "//base:base",
+    "//components/pref_registry:pref_registry",
+    "//components/prefs",
+    "//components/safe_browsing/core:features",
+    "//content/public/browser:browser",
+    "//net:net",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "safe_browsing_prefs_unittest.cc",
+  ]
+  deps = [
+    ":safe_browsing_prefs",
+    "//base:base",
+    "//base/test:test_support",
+    "//components/prefs:test_support",
+    "//components/safe_browsing/core:features",
+    "//content/test:test_support",
+    "//testing/gtest",
+    "//url:url",
+  ]
+}
+
+mojom("interfaces") {
+  sources = [
+    "safe_browsing.mojom",
+  ]
+
+  public_deps = [
+    "//content/public/common:resource_type_bindings",
+    "//services/network/public/mojom",
+    "//url/mojom:url_mojom_gurl",
+  ]
+
+  enabled_features = []
+  if (safe_browsing_mode == 1) {
+    enabled_features += [ "full_safe_browsing" ]
+  }
+}
diff --git a/components/safe_browsing/common/DEPS b/components/safe_browsing/core/common/DEPS
similarity index 100%
rename from components/safe_browsing/common/DEPS
rename to components/safe_browsing/core/common/DEPS
diff --git a/components/safe_browsing/common/OWNERS b/components/safe_browsing/core/common/OWNERS
similarity index 100%
rename from components/safe_browsing/common/OWNERS
rename to components/safe_browsing/core/common/OWNERS
diff --git a/components/safe_browsing/common/safe_browsing.mojom b/components/safe_browsing/core/common/safe_browsing.mojom
similarity index 100%
rename from components/safe_browsing/common/safe_browsing.mojom
rename to components/safe_browsing/core/common/safe_browsing.mojom
diff --git a/components/safe_browsing/core/common/safe_browsing_prefs.cc b/components/safe_browsing/core/common/safe_browsing_prefs.cc
new file mode 100644
index 0000000..7e77e507
--- /dev/null
+++ b/components/safe_browsing/core/common/safe_browsing_prefs.cc
@@ -0,0 +1,416 @@
+// Copyright (c) 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 "components/safe_browsing/core/common/safe_browsing_prefs.h"
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/metrics/histogram_macros.h"
+#include "components/pref_registry/pref_registry_syncable.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+#include "components/safe_browsing/core/features.h"
+#include "content/public/browser/browser_thread.h"
+#include "net/base/url_util.h"
+#include "url/gurl.h"
+#include "url/url_canon.h"
+
+namespace {
+
+// The Extended Reporting pref that is currently active, used for UMA metrics.
+// These values are written to logs.  New enum values can be added, but
+// existing enums must never be renumbered or deleted and reused.
+enum ActiveExtendedReportingPref {
+  SBER1_PREF = 0,
+  SBER2_PREF = 1,
+  // New prefs must be added before MAX_SBER_PREF
+  MAX_SBER_PREF
+};
+
+// Update the correct UMA metric based on which pref was changed and which UI
+// the change was made on.
+void RecordExtendedReportingPrefChanged(
+    const PrefService& prefs,
+    safe_browsing::ExtendedReportingOptInLocation location) {
+  bool pref_value = safe_browsing::IsExtendedReportingEnabled(prefs);
+
+  switch (location) {
+    case safe_browsing::SBER_OPTIN_SITE_CHROME_SETTINGS:
+      UMA_HISTOGRAM_BOOLEAN(
+          "SafeBrowsing.Pref.Scout.SetPref.SBER2Pref.ChromeSettings",
+          pref_value);
+      break;
+    case safe_browsing::SBER_OPTIN_SITE_ANDROID_SETTINGS:
+      UMA_HISTOGRAM_BOOLEAN(
+          "SafeBrowsing.Pref.Scout.SetPref.SBER2Pref.AndroidSettings",
+          pref_value);
+      break;
+    case safe_browsing::SBER_OPTIN_SITE_DOWNLOAD_FEEDBACK_POPUP:
+      UMA_HISTOGRAM_BOOLEAN(
+          "SafeBrowsing.Pref.Scout.SetPref.SBER2Pref.DownloadPopup",
+          pref_value);
+      break;
+    case safe_browsing::SBER_OPTIN_SITE_SECURITY_INTERSTITIAL:
+      UMA_HISTOGRAM_BOOLEAN(
+          "SafeBrowsing.Pref.Scout.SetPref.SBER2Pref.SecurityInterstitial",
+          pref_value);
+      break;
+    default:
+      NOTREACHED();
+  }
+}
+
+// A helper function to return a GURL containing just the scheme, host, port,
+// and path from a URL. Equivalent to clearing any username, password, query,
+// and ref. Return empty URL if |url| is not valid.
+GURL GetSimplifiedURL(const GURL& url) {
+  if (!url.is_valid() || !url.IsStandard())
+    return GURL();
+
+  url::Replacements<char> replacements;
+  replacements.ClearUsername();
+  replacements.ClearPassword();
+  replacements.ClearQuery();
+  replacements.ClearRef();
+
+  return url.ReplaceComponents(replacements);
+}
+
+}  // namespace
+
+namespace prefs {
+const char kSafeBrowsingEnabled[] = "safebrowsing.enabled";
+const char kSafeBrowsingExtendedReportingOptInAllowed[] =
+    "safebrowsing.extended_reporting_opt_in_allowed";
+const char kSafeBrowsingIncidentsSent[] = "safebrowsing.incidents_sent";
+const char kSafeBrowsingProceedAnywayDisabled[] =
+    "safebrowsing.proceed_anyway_disabled";
+const char kSafeBrowsingSawInterstitialScoutReporting[] =
+    "safebrowsing.saw_interstitial_sber2";
+const char kSafeBrowsingScoutReportingEnabled[] =
+    "safebrowsing.scout_reporting_enabled";
+const char kSafeBrowsingTriggerEventTimestamps[] =
+    "safebrowsing.trigger_event_timestamps";
+const char kSafeBrowsingUnhandledGaiaPasswordReuses[] =
+    "safebrowsing.unhandled_sync_password_reuses";
+const char kSafeBrowsingNextPasswordCaptureEventLogTime[] =
+    "safebrowsing.next_password_capture_event_log_time";
+const char kSafeBrowsingWhitelistDomains[] =
+    "safebrowsing.safe_browsing_whitelist_domains";
+const char kPasswordProtectionChangePasswordURL[] =
+    "safebrowsing.password_protection_change_password_url";
+const char kPasswordProtectionLoginURLs[] =
+    "safebrowsing.password_protection_login_urls";
+const char kPasswordProtectionWarningTrigger[] =
+    "safebrowsing.password_protection_warning_trigger";
+const char kAdvancedProtectionLastRefreshInUs[] =
+    "safebrowsing.advanced_protection_last_refresh";
+const char kSafeBrowsingRealTimeLookupEnabled[] =
+    "safebrowsing.real_time_lookup_enabled";
+const char kSafeBrowsingSendFilesForMalwareCheck[] =
+    "safebrowsing.send_files_for_malware_check";
+const char kUnsafeEventsReportingEnabled[] =
+    "safebrowsing.unsafe_events_reporting";
+const char kBlockLargeFileTransfer[] =
+    "safebrowsing.block_large_file_transfers";
+const char kDelayDeliveryUntilVerdict[] =
+    "safebrowsing.delay_delivery_until_verdict";
+const char kAllowPasswordProtectedFiles[] =
+    "safebrowsing.allow_password_protected_files";
+const char kCheckContentCompliance[] = "safebrowsing.check_content_compliance";
+const char kURLsToCheckComplianceOfDownloadedContent[] =
+    "safebrowsing.urls_to_check_compliance_of_downloaded_content";
+const char kURLsToCheckForMalwareOfUploadedContent[] =
+    "safebrowsing.urls_to_check_for_malware_of_uploaded_content";
+const char kURLsToNotCheckComplianceOfUploadedContent[] =
+    "policy.urls_to_not_check_compliance_of_uploaded_content";
+
+}  // namespace prefs
+
+namespace safe_browsing {
+
+bool ExtendedReportingPrefExists(const PrefService& prefs) {
+  return prefs.HasPrefPath(prefs::kSafeBrowsingScoutReportingEnabled);
+}
+
+ExtendedReportingLevel GetExtendedReportingLevel(const PrefService& prefs) {
+  return IsExtendedReportingEnabled(prefs) ? SBER_LEVEL_SCOUT : SBER_LEVEL_OFF;
+}
+
+bool IsExtendedReportingOptInAllowed(const PrefService& prefs) {
+  return prefs.GetBoolean(prefs::kSafeBrowsingExtendedReportingOptInAllowed);
+}
+
+bool IsExtendedReportingEnabled(const PrefService& prefs) {
+  return prefs.GetBoolean(prefs::kSafeBrowsingScoutReportingEnabled);
+}
+
+bool IsExtendedReportingPolicyManaged(const PrefService& prefs) {
+  return prefs.IsManagedPreference(prefs::kSafeBrowsingScoutReportingEnabled);
+}
+
+void RecordExtendedReportingMetrics(const PrefService& prefs) {
+  // This metric tracks the extended browsing opt-in based on whichever setting
+  // the user is currently seeing. It tells us whether extended reporting is
+  // happening for this user.
+  UMA_HISTOGRAM_BOOLEAN("SafeBrowsing.Pref.Extended",
+                        IsExtendedReportingEnabled(prefs));
+
+  // Track whether this user has ever seen a security interstitial.
+  UMA_HISTOGRAM_BOOLEAN(
+      "SafeBrowsing.Pref.SawInterstitial.SBER2Pref",
+      prefs.GetBoolean(prefs::kSafeBrowsingSawInterstitialScoutReporting));
+}
+
+void RegisterProfilePrefs(PrefRegistrySimple* registry) {
+  registry->RegisterBooleanPref(prefs::kSafeBrowsingScoutReportingEnabled,
+                                false);
+  registry->RegisterBooleanPref(
+      prefs::kSafeBrowsingSawInterstitialScoutReporting, false);
+  registry->RegisterBooleanPref(
+      prefs::kSafeBrowsingExtendedReportingOptInAllowed, true);
+  registry->RegisterBooleanPref(
+      prefs::kSafeBrowsingEnabled, true,
+      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
+  registry->RegisterBooleanPref(prefs::kSafeBrowsingProceedAnywayDisabled,
+                                false);
+  registry->RegisterDictionaryPref(prefs::kSafeBrowsingIncidentsSent);
+  registry->RegisterDictionaryPref(
+      prefs::kSafeBrowsingUnhandledGaiaPasswordReuses);
+  registry->RegisterStringPref(
+      prefs::kSafeBrowsingNextPasswordCaptureEventLogTime,
+      "0");  // int64 as string
+  registry->RegisterListPref(prefs::kSafeBrowsingWhitelistDomains);
+  registry->RegisterStringPref(prefs::kPasswordProtectionChangePasswordURL, "");
+  registry->RegisterListPref(prefs::kPasswordProtectionLoginURLs);
+  registry->RegisterIntegerPref(prefs::kPasswordProtectionWarningTrigger,
+                                PASSWORD_PROTECTION_OFF);
+  registry->RegisterInt64Pref(prefs::kAdvancedProtectionLastRefreshInUs, 0);
+  registry->RegisterBooleanPref(prefs::kSafeBrowsingRealTimeLookupEnabled,
+                                false);
+  registry->RegisterIntegerPref(prefs::kSafeBrowsingSendFilesForMalwareCheck,
+                                DO_NOT_SCAN);
+}
+
+void RegisterLocalStatePrefs(PrefRegistrySimple* registry) {
+  registry->RegisterDictionaryPref(prefs::kSafeBrowsingTriggerEventTimestamps);
+  registry->RegisterBooleanPref(prefs::kUnsafeEventsReportingEnabled, false);
+  registry->RegisterIntegerPref(prefs::kBlockLargeFileTransfer, 0);
+  registry->RegisterIntegerPref(prefs::kDelayDeliveryUntilVerdict, DELAY_NONE);
+  registry->RegisterIntegerPref(
+      prefs::kAllowPasswordProtectedFiles,
+      AllowPasswordProtectedFilesValues::ALLOW_UPLOADS_AND_DOWNLOADS);
+  registry->RegisterIntegerPref(prefs::kCheckContentCompliance, CHECK_NONE);
+  registry->RegisterListPref(prefs::kURLsToCheckComplianceOfDownloadedContent);
+  registry->RegisterListPref(prefs::kURLsToNotCheckComplianceOfUploadedContent);
+  registry->RegisterListPref(prefs::kURLsToCheckForMalwareOfUploadedContent);
+}
+
+void SetExtendedReportingPrefAndMetric(
+    PrefService* prefs,
+    bool value,
+    ExtendedReportingOptInLocation location) {
+  prefs->SetBoolean(prefs::kSafeBrowsingScoutReportingEnabled, value);
+  RecordExtendedReportingPrefChanged(*prefs, location);
+}
+
+void SetExtendedReportingPref(PrefService* prefs, bool value) {
+  prefs->SetBoolean(prefs::kSafeBrowsingScoutReportingEnabled, value);
+}
+
+void UpdateMetricsAfterSecurityInterstitial(const PrefService& prefs,
+                                            bool on_show_pref_existed,
+                                            bool on_show_pref_value) {
+  const bool cur_pref_value = IsExtendedReportingEnabled(prefs);
+
+  if (!on_show_pref_existed) {
+    if (!ExtendedReportingPrefExists(prefs)) {
+      // User seeing pref for the first time, didn't touch the checkbox (left it
+      // unchecked).
+      UMA_HISTOGRAM_ENUMERATION(
+          "SafeBrowsing.Pref.Scout.Decision.First_LeftUnchecked", SBER2_PREF,
+          MAX_SBER_PREF);
+      return;
+    }
+
+    // Pref currently exists so user did something to the checkbox
+    if (cur_pref_value) {
+      // User turned the pref on.
+      UMA_HISTOGRAM_ENUMERATION(
+          "SafeBrowsing.Pref.Scout.Decision.First_Enabled", SBER2_PREF,
+          MAX_SBER_PREF);
+      return;
+    }
+
+    // Otherwise, user turned the pref off, but because it didn't exist when
+    // the interstitial was first shown, they must have turned it on and then
+    // off before the interstitial was closed.
+    UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.Pref.Scout.Decision.First_Disabled",
+                              SBER2_PREF, MAX_SBER_PREF);
+    return;
+  }
+
+  // At this point, the pref existed when the interstitial was shown so this is
+  // a repeat appearance of the opt-in. Existence can't be removed during an
+  // interstitial so no need to check whether the pref currently exists.
+  if (on_show_pref_value && cur_pref_value) {
+    // User left the pref on.
+    UMA_HISTOGRAM_ENUMERATION(
+        "SafeBrowsing.Pref.Scout.Decision.Repeat_LeftEnabled", SBER2_PREF,
+        MAX_SBER_PREF);
+    return;
+  } else if (on_show_pref_value && !cur_pref_value) {
+    // User turned the pref off.
+    UMA_HISTOGRAM_ENUMERATION(
+        "SafeBrowsing.Pref.Scout.Decision.Repeat_Disabled", SBER2_PREF,
+        MAX_SBER_PREF);
+    return;
+  } else if (!on_show_pref_value && cur_pref_value) {
+    // User turned the pref on.
+    UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.Pref.Scout.Decision.Repeat_Enabled",
+                              SBER2_PREF, MAX_SBER_PREF);
+    return;
+  } else {
+    // Both on_show and cur values are false - user left the pref off.
+    UMA_HISTOGRAM_ENUMERATION(
+        "SafeBrowsing.Pref.Scout.Decision.Repeat_LeftDisabled", SBER2_PREF,
+        MAX_SBER_PREF);
+    return;
+  }
+}
+
+void UpdatePrefsBeforeSecurityInterstitial(PrefService* prefs) {
+  // Remember that this user saw an interstitial.
+  prefs->SetBoolean(prefs::kSafeBrowsingSawInterstitialScoutReporting, true);
+}
+
+base::ListValue GetSafeBrowsingPreferencesList(PrefService* prefs) {
+  base::ListValue preferences_list;
+
+  const char* safe_browsing_preferences[] = {
+      prefs::kSafeBrowsingEnabled,
+      prefs::kSafeBrowsingExtendedReportingOptInAllowed,
+      prefs::kSafeBrowsingScoutReportingEnabled};
+
+  // Add the status of the preferences if they are Enabled or Disabled for the
+  // user.
+  for (const char* preference : safe_browsing_preferences) {
+    preferences_list.Append(base::Value(preference));
+    bool enabled = prefs->GetBoolean(preference);
+    preferences_list.Append(base::Value(enabled ? "Enabled" : "Disabled"));
+  }
+  return preferences_list;
+}
+
+void GetSafeBrowsingWhitelistDomainsPref(
+    const PrefService& prefs,
+    std::vector<std::string>* out_canonicalized_domain_list) {
+  const base::ListValue* pref_value =
+      prefs.GetList(prefs::kSafeBrowsingWhitelistDomains);
+  CanonicalizeDomainList(*pref_value, out_canonicalized_domain_list);
+}
+
+void CanonicalizeDomainList(
+    const base::ListValue& raw_domain_list,
+    std::vector<std::string>* out_canonicalized_domain_list) {
+  out_canonicalized_domain_list->clear();
+  for (auto it = raw_domain_list.GetList().begin();
+       it != raw_domain_list.GetList().end(); it++) {
+    // Verify if it is valid domain string.
+    url::CanonHostInfo host_info;
+    std::string canonical_host =
+        net::CanonicalizeHost(it->GetString(), &host_info);
+    if (!canonical_host.empty())
+      out_canonicalized_domain_list->push_back(canonical_host);
+  }
+}
+
+bool IsURLWhitelistedByPolicy(const GURL& url,
+                              StringListPrefMember* pref_member) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  if (!pref_member)
+    return false;
+
+  std::vector<std::string> sb_whitelist_domains = pref_member->GetValue();
+  return std::find_if(sb_whitelist_domains.begin(), sb_whitelist_domains.end(),
+                      [&url](const std::string& domain) {
+                        return url.DomainIs(domain);
+                      }) != sb_whitelist_domains.end();
+}
+
+bool IsURLWhitelistedByPolicy(const GURL& url, const PrefService& pref) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  if (!pref.HasPrefPath(prefs::kSafeBrowsingWhitelistDomains))
+    return false;
+  const base::ListValue* whitelist =
+      pref.GetList(prefs::kSafeBrowsingWhitelistDomains);
+  for (const base::Value& value : whitelist->GetList()) {
+    if (url.DomainIs(value.GetString()))
+      return true;
+  }
+  return false;
+}
+
+void GetPasswordProtectionLoginURLsPref(const PrefService& prefs,
+                                        std::vector<GURL>* out_login_url_list) {
+  const base::ListValue* pref_value =
+      prefs.GetList(prefs::kPasswordProtectionLoginURLs);
+  out_login_url_list->clear();
+  for (const base::Value& value : pref_value->GetList()) {
+    GURL login_url(value.GetString());
+    // Skip invalid or none-http/https login URLs.
+    if (login_url.is_valid() && login_url.SchemeIsHTTPOrHTTPS())
+      out_login_url_list->push_back(login_url);
+  }
+}
+
+bool MatchesPasswordProtectionLoginURL(const GURL& url,
+                                       const PrefService& prefs) {
+  if (!url.is_valid())
+    return false;
+
+  std::vector<GURL> login_urls;
+  GetPasswordProtectionLoginURLsPref(prefs, &login_urls);
+  return MatchesURLList(url, login_urls);
+}
+
+bool MatchesURLList(const GURL& target_url, const std::vector<GURL> url_list) {
+  if (url_list.empty() || !target_url.is_valid())
+    return false;
+  GURL simple_target_url = GetSimplifiedURL(target_url);
+  for (const GURL& url : url_list) {
+    if (GetSimplifiedURL(url) == simple_target_url) {
+      return true;
+    }
+  }
+  return false;
+}
+
+GURL GetPasswordProtectionChangePasswordURLPref(const PrefService& prefs) {
+  if (!prefs.HasPrefPath(prefs::kPasswordProtectionChangePasswordURL))
+    return GURL();
+  GURL change_password_url_from_pref(
+      prefs.GetString(prefs::kPasswordProtectionChangePasswordURL));
+  // Skip invalid or non-http/https URL.
+  if (change_password_url_from_pref.is_valid() &&
+      change_password_url_from_pref.SchemeIsHTTPOrHTTPS()) {
+    return change_password_url_from_pref;
+  }
+
+  return GURL();
+}
+
+bool MatchesPasswordProtectionChangePasswordURL(const GURL& url,
+                                                const PrefService& prefs) {
+  if (!url.is_valid())
+    return false;
+
+  GURL change_password_url = GetPasswordProtectionChangePasswordURLPref(prefs);
+  if (change_password_url.is_empty())
+    return false;
+
+  return GetSimplifiedURL(change_password_url) == GetSimplifiedURL(url);
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/common/safe_browsing_prefs.h b/components/safe_browsing/core/common/safe_browsing_prefs.h
new file mode 100644
index 0000000..bc716bae
--- /dev/null
+++ b/components/safe_browsing/core/common/safe_browsing_prefs.h
@@ -0,0 +1,320 @@
+// Copyright (c) 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.
+//
+// Safe Browsing preferences and some basic utility functions for using them.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CORE_COMMON_SAFE_BROWSING_PREFS_H_
+#define COMPONENTS_SAFE_BROWSING_CORE_COMMON_SAFE_BROWSING_PREFS_H_
+
+#include <string>
+#include <vector>
+
+#include "base/feature_list.h"
+#include "base/values.h"
+#include "components/prefs/pref_member.h"
+
+class PrefRegistrySimple;
+class PrefService;
+class GURL;
+
+namespace prefs {
+// Boolean that is true when SafeBrowsing is enabled.
+extern const char kSafeBrowsingEnabled[];
+
+// Boolean that tells us whether users are given the option to opt in to Safe
+// Browsing extended reporting. This is exposed as a preference that can be
+// overridden by enterprise policy.
+extern const char kSafeBrowsingExtendedReportingOptInAllowed[];
+
+// A dictionary mapping incident types to a dict of incident key:digest pairs.
+// The key is a string: a filename or pref name. Digests are 4 bytes. This pref
+// is only set/updated if Chrome (Windows only) notices certain security
+// incidents, e.g. the user downloaded binaries with invalid signatures.
+extern const char kSafeBrowsingIncidentsSent[];
+
+// Boolean that is true when the SafeBrowsing interstitial should not allow
+// users to proceed anyway.
+extern const char kSafeBrowsingProceedAnywayDisabled[];
+
+// Boolean indicating whether the user has ever seen a security interstitial.
+extern const char kSafeBrowsingSawInterstitialScoutReporting[];
+
+// Boolean indicating whether Safe Browsing Scout reporting is enabled, which
+// collects data for malware detection.
+extern const char kSafeBrowsingScoutReportingEnabled[];
+
+// Dictionary containing safe browsing triggers and the list of times they have
+// fired recently. The keys are TriggerTypes (4-byte ints) and the values are
+// lists of doubles.
+extern const char kSafeBrowsingTriggerEventTimestamps[];
+
+// Dictionary that records the origin and navigation ID pairs of unhandled gaia
+// password reuses. The keys are origin strings and the ID values are 8-byte
+// ints. Only set/update if a Chrome user reuses their Gaia password on a
+// phishing site.
+extern const char kSafeBrowsingUnhandledGaiaPasswordReuses[];
+
+// Integer timestamp of next time the PasswordCaptured event should be logged.
+extern const char kSafeBrowsingNextPasswordCaptureEventLogTime[];
+
+// List of domains where Safe Browsing should trust. That means Safe Browsing
+// won't check for malware/phishing/Uws on resources on these domains, or
+// trigger warnings. Used for enterprise only.
+extern const char kSafeBrowsingWhitelistDomains[];
+
+// String indicating the URL where password protection service should send user
+// to change their password if they've been phished. Password protection service
+// also captures new password on this page in a change password event. Used for
+// enterprise only.
+extern const char kPasswordProtectionChangePasswordURL[];
+
+// List of string indicating the URL(s) users use to log in. Password protection
+// service will capture passwords on these URLs.
+// This is managed by enterprise policy and has no effect on users who are not
+// managed by enterprise policy.
+extern const char kPasswordProtectionLoginURLs[];
+
+// Integer indicating the password protection warning trigger. This is managed
+// by enterprise policy and has no effect on users who are not managed by
+// enterprise policy.
+extern const char kPasswordProtectionWarningTrigger[];
+
+// Last time Chrome refreshes advanced protection status for sign-in users (in
+// microseconds);
+extern const char kAdvancedProtectionLastRefreshInUs[];
+
+// Whether or not to check URLs in real time. This is configured by enterprise
+// policy. For consumers, this pref is irrelevant.
+extern const char kSafeBrowsingRealTimeLookupEnabled[];
+
+// Whether or not to send downloads to Safe Browsing for deep scanning. This
+// is configured by enterprise policy.
+extern const char kSafeBrowsingSendFilesForMalwareCheck[];
+
+// Boolean that indidicates if Chrome reports unsafe events to Google.
+extern const char kUnsafeEventsReportingEnabled[];
+
+// Integer that specifies if large files are blocked form either uploads or
+// downloads or both.
+extern const char kBlockLargeFileTransfer[];
+
+// Integer that specifies if delivery to the user of potentially unsafe data
+// is delayed until a verdict about the data is known.
+extern const char kDelayDeliveryUntilVerdict[];
+
+// Integer that specifies if password protected files can be either uploaded
+// or downloaded or both.
+extern const char kAllowPasswordProtectedFiles[];
+
+// Integer that indidicates if Chrome checks data for content compliance.
+extern const char kCheckContentCompliance[];
+
+// List of url patterns where Chrome should check compliance of downloaded
+// files.
+extern const char kURLsToCheckComplianceOfDownloadedContent[];
+
+// List of url patterns where Chrome should check for malware of uploaded files.
+extern const char kURLsToCheckForMalwareOfUploadedContent[];
+
+// List of url patterns where Chrome should not check compliance of uploaded
+// files.
+extern const char kURLsToNotCheckComplianceOfUploadedContent[];
+
+}  // namespace prefs
+
+namespace safe_browsing {
+
+// Enumerates the level of Safe Browsing Extended Reporting that is currently
+// available.
+enum ExtendedReportingLevel {
+  // Extended reporting is off.
+  SBER_LEVEL_OFF = 0,
+  // The Legacy level of extended reporting is available, reporting happens in
+  // response to security incidents.
+  SBER_LEVEL_LEGACY = 1,
+  // The Scout level of extended reporting is available, some data can be
+  // collected to actively detect dangerous apps and sites.
+  SBER_LEVEL_SCOUT = 2,
+};
+
+// Enumerates all the places where the Safe Browsing Extended Reporting
+// preference can be changed.
+// These values are written to logs.  New enum values can be added, but
+// existing enums must never be renumbered or deleted and reused.
+enum ExtendedReportingOptInLocation {
+  // The chrome://settings UI.
+  SBER_OPTIN_SITE_CHROME_SETTINGS = 0,
+  // The Android settings UI.
+  SBER_OPTIN_SITE_ANDROID_SETTINGS = 1,
+  // The Download Feedback popup.
+  SBER_OPTIN_SITE_DOWNLOAD_FEEDBACK_POPUP = 2,
+  // Any security interstitial (malware, SSL, etc).
+  SBER_OPTIN_SITE_SECURITY_INTERSTITIAL = 3,
+  // New sites must be added before SBER_OPTIN_SITE_MAX.
+  SBER_OPTIN_SITE_MAX
+};
+
+// Enumerates all the triggers of password protection.
+enum PasswordProtectionTrigger {
+  // Password protection is off.
+  PASSWORD_PROTECTION_OFF = 0,
+  // Password protection triggered by password reuse event.
+  // Not used for now.
+  PASSWORD_REUSE = 1,
+  // Password protection triggered by password reuse event on phishing page.
+  PHISHING_REUSE = 2,
+  // New triggers must be added before PASSWORD_PROTECTION_TRIGGER_MAX.
+  PASSWORD_PROTECTION_TRIGGER_MAX,
+};
+
+// Enum representing possible values of the SendFilesForMalwareCheck policy.
+// This must be kept in sync with policy_templates.json.
+enum SendFilesForMalwareCheckValues {
+  DO_NOT_SCAN = 0,
+  SEND_DOWNLOADS = 2,
+  SEND_UPLOADS = 3,
+  SEND_UPLOADS_AND_DOWNLOADS = 4,
+  // New options must be added before SEND_FILES_FOR_MALWARE_CHECK_MAX.
+  SEND_FILES_FOR_MALWARE_CHECK_MAX = SEND_UPLOADS_AND_DOWNLOADS,
+};
+
+// Enum representing possible values of the CheckContentCompliance policy. This
+// must be kept in sync with policy_templates.json.
+enum CheckContentComplianceValues {
+  CHECK_NONE = 0,
+  CHECK_DOWNLOADS = 1,
+  CHECK_UPLOADS = 2,
+  CHECK_UPLOADS_AND_DOWNLOADS = 3,
+  // New options must be added before CHECK_CONTENT_COMPLIANCE_MAX.
+  CHECK_CONTENT_COMPLIANCE_MAX = CHECK_UPLOADS_AND_DOWNLOADS,
+};
+
+// Enum representing possible values of the AllowPasswordProtectedFiles policy.
+// This must be kept in sync with policy_templates.json.
+enum AllowPasswordProtectedFilesValues {
+  ALLOW_NONE = 0,
+  ALLOW_DOWNLOADS = 1,
+  ALLOW_UPLOADS = 2,
+  ALLOW_UPLOADS_AND_DOWNLOADS = 3,
+};
+
+// Enum representing possible values of the BlockLargeFileTransfer policy. This
+// must be kept in sync with policy_templates.json.
+enum BlockLargeFileTransferValues {
+  BLOCK_NONE = 0,
+  BLOCK_LARGE_DOWNLOADS = 1,
+  BLOCK_LARGE_UPLOADS = 2,
+  BLOCK_LARGE_UPLOADS_AND_DOWNLOADS = 3,
+};
+
+// Enum representing possible values of the DelayDeliveryUntilVerdict policy.
+// This must be kept in sync with policy_templates.json.
+enum DelayDeliveryUntilVerdictValues {
+  DELAY_NONE = 0,
+  DELAY_DOWNLOADS = 1,
+  DELAY_UPLOADS = 2,
+  DELAY_UPLOADS_AND_DOWNLOADS = 3,
+};
+
+// Returns whether the currently active Safe Browsing Extended Reporting
+// preference exists (eg: has been set before).
+bool ExtendedReportingPrefExists(const PrefService& prefs);
+
+// Returns the level of reporting available for the current user.
+ExtendedReportingLevel GetExtendedReportingLevel(const PrefService& prefs);
+
+// Returns whether the user is able to modify the Safe Browsing Extended
+// Reporting opt-in.
+bool IsExtendedReportingOptInAllowed(const PrefService& prefs);
+
+// Returns whether Safe Browsing Extended Reporting is currently enabled.
+// This should be used to decide if any of the reporting preferences are set,
+// regardless of which specific one is set.
+bool IsExtendedReportingEnabled(const PrefService& prefs);
+
+// Returns whether the active Extended Reporting pref is currently managed by
+// enterprise policy, meaning the user can't change it.
+bool IsExtendedReportingPolicyManaged(const PrefService& prefs);
+
+// Updates UMA metrics about Safe Browsing Extended Reporting states.
+void RecordExtendedReportingMetrics(const PrefService& prefs);
+
+// Registers user preferences related to Safe Browsing.
+void RegisterProfilePrefs(PrefRegistrySimple* registry);
+
+// Registers local state prefs related to Safe Browsing.
+void RegisterLocalStatePrefs(PrefRegistrySimple* registry);
+
+// Sets the currently active Safe Browsing Extended Reporting preference to the
+// specified value. The |location| indicates the UI where the change was
+// made.
+void SetExtendedReportingPrefAndMetric(PrefService* prefs,
+                                       bool value,
+                                       ExtendedReportingOptInLocation location);
+// This variant is used to simplify test code by omitting the location.
+void SetExtendedReportingPref(PrefService* prefs, bool value);
+
+// Called when a security interstitial is closed by the user.
+// |on_show_pref_existed| indicates whether the pref existed when the
+// interstitial was shown. |on_show_pref_value| contains the pref value when the
+// interstitial was shown.
+void UpdateMetricsAfterSecurityInterstitial(const PrefService& prefs,
+                                            bool on_show_pref_existed,
+                                            bool on_show_pref_value);
+
+// Called to indicate that a security interstitial is about to be shown to the
+// user. This may trigger the user to begin seeing the Scout opt-in text
+// depending on their experiment state.
+void UpdatePrefsBeforeSecurityInterstitial(PrefService* prefs);
+
+// Returns a list of preferences to be shown in chrome://safe-browsing. The
+// preferences are passed as an alternating sequence of preference names and
+// values represented as strings.
+base::ListValue GetSafeBrowsingPreferencesList(PrefService* prefs);
+
+// Returns a list of valid domains that Safe Browsing service trusts.
+void GetSafeBrowsingWhitelistDomainsPref(
+    const PrefService& prefs,
+    std::vector<std::string>* out_canonicalized_domain_list);
+
+// Helper function to validate and canonicalize a list of domain strings.
+void CanonicalizeDomainList(
+    const base::ListValue& raw_domain_list,
+    std::vector<std::string>* out_canonicalized_domain_list);
+
+// Helper function to determine if |url| matches Safe Browsing whitelist domains
+// (a.k. a prefs::kSafeBrowsingWhitelistDomains).
+// Called on IO thread.
+bool IsURLWhitelistedByPolicy(const GURL& url,
+                              StringListPrefMember* pref_member);
+
+// Helper function to determine if |url| matches Safe Browsing whitelist domains
+// (a.k. a prefs::kSafeBrowsingWhitelistDomains).
+// Called on UI thread.
+bool IsURLWhitelistedByPolicy(const GURL& url, const PrefService& pref);
+
+// Helper function to get the pref value of password protection login URLs.
+void GetPasswordProtectionLoginURLsPref(const PrefService& prefs,
+                                        std::vector<GURL>* out_login_url_list);
+
+// Helper function that returns true if |url| matches any password protection
+// login URLs. Returns false otherwise.
+bool MatchesPasswordProtectionLoginURL(const GURL& url,
+                                       const PrefService& prefs);
+
+// Helper function to get the pref value of password protection change password
+// URL.
+GURL GetPasswordProtectionChangePasswordURLPref(const PrefService& prefs);
+
+// Helper function that returns true if |url| matches password protection
+// change password URL. Returns false otherwise.
+bool MatchesPasswordProtectionChangePasswordURL(const GURL& url,
+                                                const PrefService& prefs);
+
+// Helper function to match a |target_url| against |url_list|.
+bool MatchesURLList(const GURL& target_url, const std::vector<GURL> url_list);
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CORE_COMMON_SAFE_BROWSING_PREFS_H_
diff --git a/components/safe_browsing/core/common/safe_browsing_prefs_unittest.cc b/components/safe_browsing/core/common/safe_browsing_prefs_unittest.cc
new file mode 100644
index 0000000..44ba10f
--- /dev/null
+++ b/components/safe_browsing/core/common/safe_browsing_prefs_unittest.cc
@@ -0,0 +1,176 @@
+// 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 <string>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "base/test/scoped_feature_list.h"
+#include "build/build_config.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/features.h"
+#include "content/public/test/browser_task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace safe_browsing {
+
+class SafeBrowsingPrefsTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    prefs_.registry()->RegisterBooleanPref(
+        prefs::kSafeBrowsingScoutReportingEnabled, false);
+    prefs_.registry()->RegisterBooleanPref(
+        prefs::kSafeBrowsingSawInterstitialScoutReporting, false);
+    prefs_.registry()->RegisterStringPref(
+        prefs::kPasswordProtectionChangePasswordURL, "");
+    prefs_.registry()->RegisterListPref(prefs::kPasswordProtectionLoginURLs);
+    prefs_.registry()->RegisterBooleanPref(
+        prefs::kSafeBrowsingExtendedReportingOptInAllowed, true);
+    prefs_.registry()->RegisterListPref(prefs::kSafeBrowsingWhitelistDomains);
+  }
+
+  void ResetPrefs(bool scout_reporting) {
+    prefs_.SetBoolean(prefs::kSafeBrowsingScoutReportingEnabled,
+                      scout_reporting);
+  }
+
+  void ExpectPrefs(bool scout_reporting) {
+    LOG(INFO) << "Pref values: scout=" << scout_reporting;
+    EXPECT_EQ(scout_reporting,
+              prefs_.GetBoolean(prefs::kSafeBrowsingScoutReportingEnabled));
+  }
+
+  void ExpectPrefsExist(bool scout_reporting) {
+    LOG(INFO) << "Prefs exist: scout=" << scout_reporting;
+    EXPECT_EQ(scout_reporting,
+              prefs_.HasPrefPath(prefs::kSafeBrowsingScoutReportingEnabled));
+  }
+  TestingPrefServiceSimple prefs_;
+
+ private:
+  content::BrowserTaskEnvironment task_environment_;
+};
+
+// TODO(crbug.com/881476) disabled for flaky crashes.
+#if defined(OS_WIN)
+#define MAYBE_GetSafeBrowsingExtendedReportingLevel \
+  DISABLED_GetSafeBrowsingExtendedReportingLevel
+#else
+#define MAYBE_GetSafeBrowsingExtendedReportingLevel \
+  GetSafeBrowsingExtendedReportingLevel
+#endif
+TEST_F(SafeBrowsingPrefsTest, MAYBE_GetSafeBrowsingExtendedReportingLevel) {
+  // By Default, extended reporting is off.
+  EXPECT_EQ(SBER_LEVEL_OFF, GetExtendedReportingLevel(prefs_));
+
+  // The value of the Scout pref affects the reporting level directly.
+  ResetPrefs(/*scout_reporting=*/true);
+  EXPECT_EQ(SBER_LEVEL_SCOUT, GetExtendedReportingLevel(prefs_));
+  // Scout pref off, so reporting is off.
+  ResetPrefs(/*scout_reporting=*/false);
+  EXPECT_EQ(SBER_LEVEL_OFF, GetExtendedReportingLevel(prefs_));
+}
+
+// TODO(crbug.com/881476) disabled for flaky crashes.
+#if defined(OS_WIN)
+#define MAYBE_VerifyMatchesPasswordProtectionLoginURL \
+  DISABLED_VerifyMatchesPasswordProtectionLoginURL
+#else
+#define MAYBE_VerifyMatchesPasswordProtectionLoginURL \
+  VerifyMatchesPasswordProtectionLoginURL
+#endif
+TEST_F(SafeBrowsingPrefsTest, MAYBE_VerifyMatchesPasswordProtectionLoginURL) {
+  GURL url("https://mydomain.com/login.html#ref?username=alice");
+  EXPECT_FALSE(prefs_.HasPrefPath(prefs::kPasswordProtectionLoginURLs));
+  EXPECT_FALSE(MatchesPasswordProtectionLoginURL(url, prefs_));
+
+  base::ListValue login_urls;
+  login_urls.AppendString("https://otherdomain.com/login.html");
+  prefs_.Set(prefs::kPasswordProtectionLoginURLs, login_urls);
+  EXPECT_TRUE(prefs_.HasPrefPath(prefs::kPasswordProtectionLoginURLs));
+  EXPECT_FALSE(MatchesPasswordProtectionLoginURL(url, prefs_));
+
+  login_urls.AppendString("https://mydomain.com/login.html");
+  prefs_.Set(prefs::kPasswordProtectionLoginURLs, login_urls);
+  EXPECT_TRUE(prefs_.HasPrefPath(prefs::kPasswordProtectionLoginURLs));
+  EXPECT_TRUE(MatchesPasswordProtectionLoginURL(url, prefs_));
+}
+
+TEST_F(SafeBrowsingPrefsTest,
+       VerifyMatchesPasswordProtectionChangePasswordURL) {
+  GURL url("https://mydomain.com/change_password.html#ref?username=alice");
+  EXPECT_FALSE(prefs_.HasPrefPath(prefs::kPasswordProtectionChangePasswordURL));
+  EXPECT_FALSE(MatchesPasswordProtectionChangePasswordURL(url, prefs_));
+
+  prefs_.SetString(prefs::kPasswordProtectionChangePasswordURL,
+                   "https://otherdomain.com/change_password.html");
+  EXPECT_TRUE(prefs_.HasPrefPath(prefs::kPasswordProtectionChangePasswordURL));
+  EXPECT_FALSE(MatchesPasswordProtectionChangePasswordURL(url, prefs_));
+
+  prefs_.SetString(prefs::kPasswordProtectionChangePasswordURL,
+                   "https://mydomain.com/change_password.html");
+  EXPECT_TRUE(prefs_.HasPrefPath(prefs::kPasswordProtectionChangePasswordURL));
+  EXPECT_TRUE(MatchesPasswordProtectionChangePasswordURL(url, prefs_));
+}
+
+TEST_F(SafeBrowsingPrefsTest, IsExtendedReportingPolicyManaged) {
+  // This test checks that manipulating SBEROptInAllowed and the management
+  // state of SBER behaves as expected. Below, we describe what should happen
+  // to the results of IsExtendedReportingPolicyManaged and
+  // IsExtendedReportingOptInAllowed.
+
+  // Confirm default state, SBER should be disabled, OptInAllowed should
+  // be enabled, and SBER is not managed.
+  EXPECT_FALSE(IsExtendedReportingEnabled(prefs_));
+  EXPECT_TRUE(IsExtendedReportingOptInAllowed(prefs_));
+  EXPECT_FALSE(IsExtendedReportingPolicyManaged(prefs_));
+
+  // Setting SBEROptInAllowed to false disallows opt-in but doesn't change
+  // whether SBER is managed.
+  prefs_.SetBoolean(prefs::kSafeBrowsingExtendedReportingOptInAllowed, false);
+  EXPECT_FALSE(IsExtendedReportingOptInAllowed(prefs_));
+  EXPECT_FALSE(IsExtendedReportingPolicyManaged(prefs_));
+  // Setting the value back to true reverts back to the default.
+  prefs_.SetBoolean(prefs::kSafeBrowsingExtendedReportingOptInAllowed, true);
+  EXPECT_TRUE(IsExtendedReportingOptInAllowed(prefs_));
+  EXPECT_FALSE(IsExtendedReportingPolicyManaged(prefs_));
+
+  // Make the SBER pref managed and enable it and ensure that the pref gets
+  // the expected value. Making SBER managed doesn't change the
+  // SBEROptInAllowed setting.
+  prefs_.SetManagedPref(prefs::kSafeBrowsingScoutReportingEnabled,
+                        std::make_unique<base::Value>(true));
+  EXPECT_TRUE(
+      prefs_.IsManagedPreference(prefs::kSafeBrowsingScoutReportingEnabled));
+  // The value of the pref comes from the policy.
+  EXPECT_TRUE(IsExtendedReportingEnabled(prefs_));
+  // SBER being managed doesn't change the SBEROptInAllowed pref.
+  EXPECT_TRUE(IsExtendedReportingOptInAllowed(prefs_));
+}
+
+TEST_F(SafeBrowsingPrefsTest, VerifyIsURLWhitelistedByPolicy) {
+  GURL target_url("https://www.foo.com");
+  // When PrefMember is null, URL is not whitelisted.
+  EXPECT_FALSE(IsURLWhitelistedByPolicy(target_url, nullptr));
+
+  EXPECT_FALSE(prefs_.HasPrefPath(prefs::kSafeBrowsingWhitelistDomains));
+  base::ListValue whitelisted_domains;
+  whitelisted_domains.AppendString("foo.com");
+  prefs_.Set(prefs::kSafeBrowsingWhitelistDomains, whitelisted_domains);
+  StringListPrefMember string_list_pref;
+  string_list_pref.Init(prefs::kSafeBrowsingWhitelistDomains, &prefs_);
+  EXPECT_TRUE(IsURLWhitelistedByPolicy(target_url, prefs_));
+  EXPECT_TRUE(IsURLWhitelistedByPolicy(target_url, &string_list_pref));
+
+  GURL not_whitelisted_url("https://www.bar.com");
+  EXPECT_FALSE(IsURLWhitelistedByPolicy(not_whitelisted_url, prefs_));
+  EXPECT_FALSE(
+      IsURLWhitelistedByPolicy(not_whitelisted_url, &string_list_pref));
+}
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/common/safebrowsing_constants.cc b/components/safe_browsing/core/common/safebrowsing_constants.cc
new file mode 100644
index 0000000..e58fcc2
--- /dev/null
+++ b/components/safe_browsing/core/common/safebrowsing_constants.cc
@@ -0,0 +1,28 @@
+// 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 "components/safe_browsing/core/common/safebrowsing_constants.h"
+
+#include "components/safe_browsing/core/features.h"
+#include "net/base/net_errors.h"
+
+namespace safe_browsing {
+
+const base::FilePath::CharType kSafeBrowsingBaseFilename[] =
+    FILE_PATH_LITERAL("Safe Browsing");
+
+const base::FilePath::CharType kCookiesFile[] = FILE_PATH_LITERAL(" Cookies");
+
+// The URL for the Safe Browsing page.
+const char kSafeBrowsingUrl[] = "https://safebrowsing.google.com/";
+
+const char kCustomCancelReasonForURLLoader[] = "SafeBrowsing";
+
+int GetNetErrorCodeForSafeBrowsing() {
+  return base::FeatureList::IsEnabled(safe_browsing::kCommittedSBInterstitials)
+             ? net::ERR_BLOCKED_BY_CLIENT
+             : net::ERR_ABORTED;
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/common/safebrowsing_constants.h b/components/safe_browsing/core/common/safebrowsing_constants.h
new file mode 100644
index 0000000..7feeab2
--- /dev/null
+++ b/components/safe_browsing/core/common/safebrowsing_constants.h
@@ -0,0 +1,30 @@
+// 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 COMPONENTS_SAFE_BROWSING_CORE_COMMON_SAFEBROWSING_CONSTANTS_H_
+#define COMPONENTS_SAFE_BROWSING_CORE_COMMON_SAFEBROWSING_CONSTANTS_H_
+
+#include "base/files/file_path.h"
+
+namespace safe_browsing {
+
+extern const base::FilePath::CharType kSafeBrowsingBaseFilename[];
+
+// Filename suffix for the cookie database.
+extern const base::FilePath::CharType kCookiesFile[];
+
+// The URL for the Safe Browsing page.
+extern const char kSafeBrowsingUrl[];
+
+// When a network::mojom::URLLoader is cancelled because of SafeBrowsing, this
+// custom cancellation reason could be used to notify the implementation side.
+// Please see network::mojom::URLLoader::kClientDisconnectReason for more
+// details.
+extern const char kCustomCancelReasonForURLLoader[];
+
+// Returns the error_code to use when Safe Browsing blocks a request.
+int GetNetErrorCodeForSafeBrowsing();
+}
+
+#endif  // COMPONENTS_SAFE_BROWSING_CORE_COMMON_SAFEBROWSING_CONSTANTS_H_
diff --git a/components/safe_browsing/core/common/safebrowsing_switches.cc b/components/safe_browsing/core/common/safebrowsing_switches.cc
new file mode 100644
index 0000000..e197440
--- /dev/null
+++ b/components/safe_browsing/core/common/safebrowsing_switches.cc
@@ -0,0 +1,20 @@
+// 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 "components/safe_browsing/core/common/safebrowsing_switches.h"
+
+namespace safe_browsing {
+namespace switches {
+
+// List of comma-separated sha256 hashes of executable files which the
+// download-protection service should treat as "dangerous."  For a file to
+// show a warning, it also must be considered a dangerous filetype and not
+// be whitelisted otherwise (by signature or URL) and must be on a supported
+// OS. Hashes are in hex. This is used for manual testing when looking
+// for ways to by-pass download protection.
+const char kSbManualDownloadBlacklist[] =
+    "safebrowsing-manual-download-blacklist";
+
+}  // namespace switches
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/common/safebrowsing_switches.h b/components/safe_browsing/core/common/safebrowsing_switches.h
new file mode 100644
index 0000000..37e4af7
--- /dev/null
+++ b/components/safe_browsing/core/common/safebrowsing_switches.h
@@ -0,0 +1,16 @@
+// 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 COMPONENTS_SAFE_BROWSING_CORE_COMMON_SAFEBROWSING_SWITCHES_H_
+#define COMPONENTS_SAFE_BROWSING_CORE_COMMON_SAFEBROWSING_SWITCHES_H_
+
+namespace safe_browsing {
+namespace switches {
+
+extern const char kSbManualDownloadBlacklist[];
+
+}  // namespace switches
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CORE_COMMON_SAFEBROWSING_SWITCHES_H_
diff --git a/components/safe_browsing/core/common/utils.cc b/components/safe_browsing/core/common/utils.cc
new file mode 100644
index 0000000..369c63c
--- /dev/null
+++ b/components/safe_browsing/core/common/utils.cc
@@ -0,0 +1,77 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/core/common/utils.h"
+
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "components/policy/core/browser/browser_policy_connector.h"
+#include "components/prefs/pref_service.h"
+#include "crypto/sha2.h"
+
+#if defined(OS_WIN)
+#include "base/enterprise_util.h"
+#endif
+
+namespace safe_browsing {
+
+std::string ShortURLForReporting(const GURL& url) {
+  std::string spec(url.spec());
+  if (url.SchemeIs(url::kDataScheme)) {
+    size_t comma_pos = spec.find(',');
+    if (comma_pos != std::string::npos && comma_pos != spec.size() - 1) {
+      std::string hash_value = crypto::SHA256HashString(spec);
+      spec.erase(comma_pos + 1);
+      spec += base::HexEncode(hash_value.data(), hash_value.size());
+    }
+  }
+  return spec;
+}
+
+ChromeUserPopulation::ProfileManagementStatus GetProfileManagementStatus(
+    const policy::BrowserPolicyConnector* bpc) {
+#if defined(OS_WIN)
+  if (base::IsMachineExternallyManaged())
+    return ChromeUserPopulation::ENTERPRISE_MANAGED;
+  else
+    return ChromeUserPopulation::NOT_MANAGED;
+#elif defined(OS_CHROMEOS)
+  if (!bpc || !bpc->IsEnterpriseManaged())
+    return ChromeUserPopulation::NOT_MANAGED;
+  return ChromeUserPopulation::ENTERPRISE_MANAGED;
+#else
+  return ChromeUserPopulation::UNAVAILABLE;
+#endif  // #if defined(OS_WIN) || defined(OS_CHROMEOS)
+}
+
+void SetDelayInPref(PrefService* prefs,
+                    const char* pref_name,
+                    const base::TimeDelta& delay) {
+  base::Time next_event = base::Time::Now() + delay;
+  int64_t seconds_since_epoch =
+      next_event.ToDeltaSinceWindowsEpoch().InSeconds();
+  prefs->SetInt64(pref_name, seconds_since_epoch);
+}
+
+base::TimeDelta GetDelayFromPref(PrefService* prefs, const char* pref_name) {
+  const base::TimeDelta zero_delay;
+  if (!prefs->HasPrefPath(pref_name))
+    return zero_delay;
+
+  int64_t seconds_since_epoch = prefs->GetInt64(pref_name);
+  if (seconds_since_epoch <= 0)
+    return zero_delay;
+
+  base::Time next_event = base::Time::FromDeltaSinceWindowsEpoch(
+      base::TimeDelta::FromSeconds(seconds_since_epoch));
+  base::Time now = base::Time::Now();
+  if (now > next_event)
+    return zero_delay;
+  else
+    return next_event - now;
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/common/utils.h b/components/safe_browsing/core/common/utils.h
new file mode 100644
index 0000000..11da036
--- /dev/null
+++ b/components/safe_browsing/core/common/utils.h
@@ -0,0 +1,46 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Safe Browsing utility functions.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CORE_COMMON_UTILS_H_
+#define COMPONENTS_SAFE_BROWSING_CORE_COMMON_UTILS_H_
+
+#include "base/time/time.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
+#include "url/gurl.h"
+
+namespace policy {
+class BrowserPolicyConnector;
+}  // namespace policy
+
+namespace base {
+class TimeDelta;
+}  // namespace base
+
+class PrefService;
+
+namespace safe_browsing {
+
+// Shorten URL by replacing its contents with its SHA256 hash if it has data
+// scheme.
+std::string ShortURLForReporting(const GURL& url);
+
+// Gets the |ProfileManagementStatus| for the current machine. The method
+// currently works only on Windows and ChromeOS. The |bpc| parameter is used
+// only on ChromeOS, and may be |nullptr|.
+ChromeUserPopulation::ProfileManagementStatus GetProfileManagementStatus(
+    const policy::BrowserPolicyConnector* bpc);
+
+// Util for storing a future alarm time in a pref. |delay| is how much time into
+// the future the alarm is set for. Calling GetDelayFromPref() later will return
+// a shorter delay, or 0 if it's unset or passed..
+void SetDelayInPref(PrefService* prefs,
+                    const char* pref_name,
+                    const base::TimeDelta& delay);
+base::TimeDelta GetDelayFromPref(PrefService* prefs, const char* pref_name);
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CORE_COMMON_UTILS_H_
diff --git a/components/safe_browsing/core/db/BUILD.gn b/components/safe_browsing/core/db/BUILD.gn
new file mode 100644
index 0000000..e8a5e2c
--- /dev/null
+++ b/components/safe_browsing/core/db/BUILD.gn
@@ -0,0 +1,391 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//testing/libfuzzer/fuzzer_test.gni")
+import("//third_party/protobuf/proto_library.gni")
+
+proto_library("safebrowsing_proto") {
+  sources = [
+    "safebrowsing.proto",
+  ]
+}
+
+proto_library("v4_store_proto") {
+  sources = [
+    "v4_store.proto",
+  ]
+  link_deps = [ ":safebrowsing_proto" ]
+}
+
+proto_library("metadata_proto") {
+  sources = [
+    "metadata.proto",
+  ]
+}
+
+# This target is shared between the desktop and mobile versions.
+group("safe_browsing_db_shared") {
+  deps = [
+    ":database_manager",
+    ":hit_report",
+    ":safebrowsing_proto",
+    ":util",
+    "//components/safe_browsing/core/common:safe_browsing_prefs",
+  ]
+}
+
+# This target is for the desktop version.
+group("db") {
+  deps = [
+    ":safe_browsing_db_shared",
+    ":v4_local_database_manager",
+  ]
+}
+
+static_library("database_manager") {
+  sources = [
+    "database_manager.cc",
+    "database_manager.h",
+  ]
+  deps = [
+    ":hit_report",
+    ":util",
+    ":v4_get_hash_protocol_manager",
+    ":v4_protocol_manager_util",
+    "//base",
+    "//content/public/browser",
+    "//content/public/common",
+    "//net",
+    "//url",
+  ]
+
+  public_deps = [
+    ":safebrowsing_proto",
+  ]
+}
+
+static_library("hit_report") {
+  sources = [
+    "hit_report.cc",
+    "hit_report.h",
+  ]
+  public_deps = [
+    ":util",
+  ]
+  deps = [
+    "//components/metrics",
+    "//components/safe_browsing/core/common:safe_browsing_prefs",
+    "//url",
+  ]
+}
+
+static_library("test_database_manager") {
+  sources = [
+    "test_database_manager.cc",
+    "test_database_manager.h",
+  ]
+  deps = [
+    ":database_manager",
+    ":v4_protocol_manager_util",
+    "//base:base",
+    "//net",
+    "//services/network/public/cpp",
+  ]
+}
+
+static_library("util") {
+  sources = [
+    "util.cc",
+    "util.h",
+  ]
+  public_deps = [
+    ":v4_protocol_manager_util",
+    "//components/safe_browsing/core/common:safe_browsing_prefs",
+  ]
+  deps = [
+    "//base",
+    "//components/version_info:version_info",
+    "//crypto",
+    "//google_apis:google_apis",
+    "//net",
+    "//url",
+  ]
+  if (is_win) {
+    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+    cflags = [ "/wd4267" ]  # Conversion from size_t to 'type'.
+  }
+}
+
+static_library("v4_database") {
+  sources = [
+    "v4_database.cc",
+    "v4_database.h",
+  ]
+  public_deps = [
+    ":safebrowsing_proto",
+  ]
+  deps = [
+    ":v4_protocol_manager_util",
+    ":v4_store",
+    "//base",
+    "//components/safe_browsing/core:webui_proto",
+    "//content/public/browser",
+  ]
+}
+
+static_library("v4_get_hash_protocol_manager") {
+  sources = [
+    "v4_get_hash_protocol_manager.cc",
+    "v4_get_hash_protocol_manager.h",
+  ]
+  public_deps = [
+    ":safebrowsing_proto",
+  ]
+  deps = [
+    ":util",
+    ":v4_protocol_manager_util",
+    "//base",
+    "//components/safe_browsing/core:webui_proto",
+    "//content/public/browser",
+    "//net",
+    "//url",
+  ]
+}
+
+static_library("v4_local_database_manager") {
+  sources = [
+    "v4_local_database_manager.cc",
+    "v4_local_database_manager.h",
+  ]
+  deps = [
+    ":database_manager",
+    ":hit_report",
+    ":safebrowsing_proto",
+    ":v4_database",
+    ":v4_get_hash_protocol_manager",
+    ":v4_protocol_manager_util",
+    ":v4_update_protocol_manager",
+    "//base",
+    "//build:branding_buildflags",
+    "//components/safe_browsing/core:webui_proto",
+    "//components/safe_browsing/core/realtime:policy_engine",
+    "//components/safe_browsing/core/realtime:url_lookup_service",
+    "//content/public/browser",
+    "//crypto",
+    "//net",
+    "//url",
+  ]
+}
+
+source_set("v4_protocol_manager_util") {
+  sources = [
+    "v4_protocol_manager_util.cc",
+    "v4_protocol_manager_util.h",
+  ]
+  public_deps = [
+    ":safebrowsing_proto",
+  ]
+  deps = [
+    "//base",
+    "//components/safe_browsing/core/common:safe_browsing_prefs",
+    "//components/version_info:version_info",
+    "//google_apis:google_apis",
+    "//net",
+    "//url",
+  ]
+}
+
+source_set("prefix_iterator") {
+  sources = [
+    "prefix_iterator.cc",
+    "prefix_iterator.h",
+  ]
+  deps = [
+    ":v4_protocol_manager_util",
+    "//base",
+  ]
+}
+
+if (is_android) {
+  import("//build/config/android/rules.gni")
+  java_cpp_enum("sb_threat_values") {
+    sources = [
+      "v4_protocol_manager_util.h",
+    ]
+  }
+}
+
+source_set("v4_rice") {
+  sources = [
+    "v4_rice.cc",
+    "v4_rice.h",
+  ]
+  deps = [
+    "//base",
+    "//third_party/protobuf:protobuf_lite",
+  ]
+}
+
+source_set("v4_store") {
+  sources = [
+    "v4_store.cc",
+    "v4_store.h",
+  ]
+  public_deps = [
+    ":safebrowsing_proto",
+    ":v4_store_proto",
+  ]
+  deps = [
+    ":prefix_iterator",
+    ":v4_protocol_manager_util",
+    ":v4_rice",
+    "//base",
+    "//components/safe_browsing/core:webui_proto",
+    "//crypto",
+  ]
+}
+
+static_library("v4_test_util") {
+  testonly = true
+  sources = [
+    "v4_embedded_test_server_util.cc",
+    "v4_embedded_test_server_util.h",
+    "v4_test_util.cc",
+    "v4_test_util.h",
+  ]
+  deps = [
+    ":util",
+    ":v4_database",
+    ":v4_get_hash_protocol_manager",
+    ":v4_protocol_manager_util",
+    "//base",
+    "//net:test_support",
+    "//services/network/public/cpp",
+  ]
+}
+
+static_library("v4_update_protocol_manager") {
+  sources = [
+    "v4_update_protocol_manager.cc",
+    "v4_update_protocol_manager.h",
+  ]
+  deps = [
+    ":safebrowsing_proto",
+    ":util",
+    ":v4_protocol_manager_util",
+    "//base",
+    "//components/safe_browsing:buildflags",
+    "//components/safe_browsing/core/:webui_proto",
+    "//components/safe_browsing/core/common:safe_browsing_prefs",
+    "//net",
+    "//services/network/public/cpp",
+    "//url",
+  ]
+}
+
+source_set("unit_tests_shared") {
+  testonly = true
+  sources = [
+    "allowlist_checker_client_unittest.cc",
+    "database_manager_unittest.cc",
+    "v4_get_hash_protocol_manager_unittest.cc",
+    "v4_protocol_manager_util_unittest.cc",
+  ]
+  deps = [
+    ":allowlist_checker_client",
+    ":database_manager",
+    ":safebrowsing_proto",
+    ":test_database_manager",
+    ":util",
+    ":v4_get_hash_protocol_manager",
+    ":v4_protocol_manager_util",
+    ":v4_test_util",
+    "//base",
+    "//content/public/browser",
+    "//content/test:test_support",
+    "//net",
+    "//net:test_support",
+    "//services/network:test_support",
+    "//testing/gtest",
+  ]
+  if (is_win) {
+    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+    cflags = [ "/wd4267" ]  # Conversion from size_t to 'type'.
+  }
+}
+
+source_set("unit_tests_desktop") {
+  testonly = true
+  sources = [
+    "v4_database_unittest.cc",
+    "v4_local_database_manager_unittest.cc",
+    "v4_rice_unittest.cc",
+    "v4_store_unittest.cc",
+    "v4_update_protocol_manager_unittest.cc",
+  ]
+  deps = [
+    ":unit_tests_shared",
+    ":util",
+    ":v4_database",
+    ":v4_local_database_manager",
+    ":v4_protocol_manager_util",
+    ":v4_rice",
+    ":v4_store",
+    ":v4_store_proto",
+    ":v4_test_util",
+    ":v4_update_protocol_manager",
+    "//base",
+    "//components/prefs:test_support",
+    "//components/safe_browsing:buildflags",
+    "//components/safe_browsing/core:features",
+    "//components/safe_browsing/core/common:safe_browsing_prefs",
+    "//content/test:test_support",
+    "//crypto",
+    "//net",
+    "//net:test_support",
+    "//services/network:test_support",
+    "//services/network/public/cpp",
+    "//testing/gtest",
+    "//url",
+  ]
+  if (is_win) {
+    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+    cflags = [ "/wd4267" ]  # Conversion from size_t to 'type'.
+  }
+}
+
+static_library("allowlist_checker_client") {
+  sources = [
+    "allowlist_checker_client.cc",
+    "allowlist_checker_client.h",
+  ]
+  deps = [
+    ":database_manager",
+    "//base:base",
+    "//content/public/browser",
+    "//url:url",
+  ]
+}
+
+fuzzer_test("v4_get_hash_protocol_manager_fuzzer") {
+  sources = [
+    "v4_get_hash_protocol_manager_fuzzer.cc",
+  ]
+  deps = [
+    ":safebrowsing_proto",
+    ":v4_get_hash_protocol_manager",
+  ]
+}
+
+fuzzer_test("v4_store_fuzzer") {
+  sources = [
+    "v4_store_fuzzer.cc",
+  ]
+  deps = [
+    ":v4_protocol_manager_util",
+    ":v4_store",
+    ":v4_test_util",
+    "//base/test:test_support",
+  ]
+}
diff --git a/components/safe_browsing/core/db/DEPS b/components/safe_browsing/core/db/DEPS
new file mode 100644
index 0000000..b6b2541
--- /dev/null
+++ b/components/safe_browsing/core/db/DEPS
@@ -0,0 +1,19 @@
+include_rules = [
+  "+base",
+  "+components/safe_browsing/content/web_ui/webui.pb.h",
+  "+components/safe_browsing/core/common/safe_browsing_prefs.h",
+  "+components/safe_browsing/core/features.h",
+  "+components/safe_browsing/core/proto/webui.pb.h",
+  "+components/version_info",
+  "+content/public/browser",
+  "+content/public/common",
+  "+content/public/test",
+  "+crypto",
+  "+google_apis/google_api_keys.h",
+  "+net",
+  "+services/network/public",
+  "+services/network/test",
+  "+testing/gtest",
+  "+third_party/protobuf/src/google",
+  "+url",
+]
diff --git a/components/safe_browsing/db/README b/components/safe_browsing/core/db/README
similarity index 100%
rename from components/safe_browsing/db/README
rename to components/safe_browsing/core/db/README
diff --git a/components/safe_browsing/core/db/allowlist_checker_client.cc b/components/safe_browsing/core/db/allowlist_checker_client.cc
new file mode 100644
index 0000000..9845cdd
--- /dev/null
+++ b/components/safe_browsing/core/db/allowlist_checker_client.cc
@@ -0,0 +1,155 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/core/db/allowlist_checker_client.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace safe_browsing {
+
+namespace {
+
+// Number of milliseconds to wait for the response from the Safe Browsing
+// database manager before proceeding with the timeout behavior.
+const int kLookupTimeoutMS = 5000;
+}  // namespace
+
+// static
+void AllowlistCheckerClient::StartCheckCsdWhitelist(
+    scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
+    const GURL& url,
+    base::OnceCallback<void(bool)> callback_for_result) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+  // On timeout or if the list is unavailable, report match.
+  const bool kDefaultDoesMatchAllowlist = true;
+
+  std::unique_ptr<AllowlistCheckerClient> client = GetAllowlistCheckerClient(
+      database_manager, url, &callback_for_result, kDefaultDoesMatchAllowlist);
+  if (!client) {
+    std::move(callback_for_result).Run(kDefaultDoesMatchAllowlist);
+    return;
+  }
+
+  AsyncMatch match = database_manager->CheckCsdWhitelistUrl(url, client.get());
+  InvokeCallbackOrRelease(match, std::move(client));
+}
+
+// static
+void AllowlistCheckerClient::StartCheckHighConfidenceAllowlist(
+    scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
+    const GURL& url,
+    base::OnceCallback<void(bool)> callback_for_result) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+  // On timeout or if the list is unavailable, report no match.
+  const bool kDefaultDoesMatchAllowlist = false;
+
+  std::unique_ptr<AllowlistCheckerClient> client = GetAllowlistCheckerClient(
+      database_manager, url, &callback_for_result, kDefaultDoesMatchAllowlist);
+  if (!client) {
+    std::move(callback_for_result).Run(kDefaultDoesMatchAllowlist);
+    return;
+  }
+
+  AsyncMatch match =
+      database_manager->CheckUrlForHighConfidenceAllowlist(url, client.get());
+  InvokeCallbackOrRelease(match, std::move(client));
+}
+
+// static
+std::unique_ptr<AllowlistCheckerClient>
+AllowlistCheckerClient::GetAllowlistCheckerClient(
+    scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
+    const GURL& url,
+    base::OnceCallback<void(bool)>* callback_for_result,
+    bool default_does_match_allowlist) {
+  if (!url.is_valid() || !database_manager ||
+      !database_manager->CanCheckUrl(url)) {
+    return nullptr;
+  }
+
+  // Make a client for each request. The caller could have several in
+  // flight at once.
+  return std::make_unique<AllowlistCheckerClient>(
+      std::move(*callback_for_result), database_manager,
+      default_does_match_allowlist);
+}
+
+// static
+void AllowlistCheckerClient::InvokeCallbackOrRelease(
+    AsyncMatch match,
+    std::unique_ptr<AllowlistCheckerClient> client) {
+  switch (match) {
+    case AsyncMatch::MATCH:
+      std::move(client->callback_for_result_)
+          .Run(true /* did_match_allowlist */);
+      break;
+    case AsyncMatch::NO_MATCH:
+      std::move(client->callback_for_result_)
+          .Run(false /* did_match_allowlist */);
+      break;
+    case AsyncMatch::ASYNC:
+      // Client is now self-owned. When it gets called back with the result,
+      // it will delete itself.
+      client.release();
+      break;
+  }
+}
+
+AllowlistCheckerClient::AllowlistCheckerClient(
+    base::OnceCallback<void(bool)> callback_for_result,
+    scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
+    bool default_does_match_allowlist)
+    : callback_for_result_(std::move(callback_for_result)),
+      database_manager_(database_manager),
+      default_does_match_allowlist_(default_does_match_allowlist) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+  // Set a timer to fail open, i.e. call it "whitelisted", if the full
+  // check takes too long.
+  auto timeout_callback = base::BindOnce(&AllowlistCheckerClient::OnTimeout,
+                                         weak_factory_.GetWeakPtr());
+  timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kLookupTimeoutMS),
+               std::move(timeout_callback));
+}
+
+AllowlistCheckerClient::~AllowlistCheckerClient() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+}
+
+// SafeBrowsingDatabaseMananger::Client impl
+void AllowlistCheckerClient::OnCheckWhitelistUrlResult(
+    bool did_match_allowlist) {
+  OnCheckUrlResult(did_match_allowlist);
+}
+
+void AllowlistCheckerClient::OnCheckUrlForHighConfidenceAllowlist(
+    bool did_match_allowlist) {
+  OnCheckUrlResult(did_match_allowlist);
+}
+
+void AllowlistCheckerClient::OnCheckUrlResult(bool did_match_allowlist) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  timer_.Stop();
+
+  // The callback can only be invoked by other code paths if this object is not
+  // self-owned. Because this method is only invoked when we're self-owned, we
+  // know the callback must still be valid, and it must be safe to delete
+  // |this|.
+  DCHECK(callback_for_result_);
+  std::move(callback_for_result_).Run(did_match_allowlist);
+  delete this;
+}
+
+void AllowlistCheckerClient::OnTimeout() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  database_manager_->CancelCheck(this);
+  OnCheckUrlResult(default_does_match_allowlist_);
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/db/allowlist_checker_client.h b/components/safe_browsing/core/db/allowlist_checker_client.h
new file mode 100644
index 0000000..23d42ba
--- /dev/null
+++ b/components/safe_browsing/core/db/allowlist_checker_client.h
@@ -0,0 +1,97 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CORE_DB_ALLOWLIST_CHECKER_CLIENT_H_
+#define COMPONENTS_SAFE_BROWSING_CORE_DB_ALLOWLIST_CHECKER_CLIENT_H_
+
+#include "base/callback.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/timer/timer.h"
+#include "components/safe_browsing/core/db/database_manager.h"
+#include "url/gurl.h"
+
+namespace safe_browsing {
+
+// This provides a simpler interface to
+// SafeBrowsingDatabaseManager::CheckCsdWhitelistUrl() for callers that
+// don't want to track their own clients.
+
+class AllowlistCheckerClient : public SafeBrowsingDatabaseManager::Client {
+ public:
+  using BoolCallback = base::OnceCallback<void(bool /* is_whitelisted */)>;
+
+  // Static method to lookup |url| on the CSD allowlist. |callback| will be
+  // called when the lookup result is known, or on time out, or if the
+  // |database_manager| gets shut down, whichever happens first.
+  // Must be called on IO thread.
+  static void StartCheckCsdWhitelist(
+      scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
+      const GURL& url,
+      BoolCallback callback_for_result);
+
+  // Static method to lookup |url| on the high confidence allowlist. |callback|
+  // will be called when the lookup result is known, or on time out, or if the
+  // |database_manager| gets shut down, whichever happens first.
+  // Must be called on IO thread.
+  static void StartCheckHighConfidenceAllowlist(
+      scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
+      const GURL& url,
+      BoolCallback callback_for_result);
+
+  // public constructor for use with std::make_unique
+  AllowlistCheckerClient(
+      BoolCallback callback_for_result,
+      scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
+      bool default_does_match_allowlist);
+
+  ~AllowlistCheckerClient() override;
+
+  // SafeBrowsingDatabaseMananger::Client impl
+  void OnCheckWhitelistUrlResult(bool is_whitelisted) override;
+  void OnCheckUrlForHighConfidenceAllowlist(bool did_match_allowlist) override;
+
+ private:
+  // Helper method to instantiate a AllowlistCheckerClient object.
+  static std::unique_ptr<AllowlistCheckerClient> GetAllowlistCheckerClient(
+      scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
+      const GURL& url,
+      base::OnceCallback<void(bool)>* callback_for_result,
+      bool default_does_match_allowlist);
+
+  // Invokes |callback_for_result_| if the allowlist lookup completed
+  // synchronously i.e if |match| is |MATCH| or |NO_MATCH|. If, however, |match|
+  // is |ASYNC|, it releases the ownership of |client| so that it can be deleted
+  // in |OnCheckUrlResult| later.
+  static void InvokeCallbackOrRelease(
+      AsyncMatch match,
+      std::unique_ptr<AllowlistCheckerClient> client);
+
+  AllowlistCheckerClient() = delete;
+
+  // Calls the |callback_for_result_| with the result of the lookup or timeout.
+  void OnCheckUrlResult(bool did_match_allowlist);
+
+  // Called when the call to CheckCsdWhitelistUrl times out.
+  void OnTimeout();
+
+  // For setting up timeout behavior.
+  base::OneShotTimer timer_;
+
+  // The method to call when the match result is known.
+  BoolCallback callback_for_result_;
+
+  scoped_refptr<SafeBrowsingDatabaseManager> database_manager_;
+
+  // Whether to report allowlist match in any of the following cases:
+  // a) On timeout, or
+  // b) If the list is unavailable.
+  bool default_does_match_allowlist_;
+
+  base::WeakPtrFactory<AllowlistCheckerClient> weak_factory_{this};
+};
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CORE_DB_ALLOWLIST_CHECKER_CLIENT_H_
diff --git a/components/safe_browsing/core/db/allowlist_checker_client_unittest.cc b/components/safe_browsing/core/db/allowlist_checker_client_unittest.cc
new file mode 100644
index 0000000..80cd526
--- /dev/null
+++ b/components/safe_browsing/core/db/allowlist_checker_client_unittest.cc
@@ -0,0 +1,178 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#include "components/safe_browsing/core/db/allowlist_checker_client.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/run_loop.h"
+#include "base/test/mock_callback.h"
+#include "base/test/task_environment.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/safe_browsing/core/db/test_database_manager.h"
+#include "content/public/test/browser_task_environment.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace safe_browsing {
+
+using base::TimeDelta;
+using testing::_;
+using testing::Return;
+using testing::SaveArg;
+
+using BoolCallback = base::OnceCallback<void(bool /* did_match_allowlist */)>;
+using MockBoolCallback = testing::StrictMock<base::MockCallback<BoolCallback>>;
+
+namespace {
+class MockSafeBrowsingDatabaseManager : public TestSafeBrowsingDatabaseManager {
+ public:
+  MockSafeBrowsingDatabaseManager() {}
+
+  MOCK_METHOD1(CancelCheck, void(SafeBrowsingDatabaseManager::Client*));
+
+  MOCK_METHOD2(CheckCsdWhitelistUrl,
+               AsyncMatch(const GURL&, SafeBrowsingDatabaseManager::Client*));
+
+  MOCK_METHOD2(CheckUrlForHighConfidenceAllowlist,
+               AsyncMatch(const GURL&, SafeBrowsingDatabaseManager::Client*));
+
+ protected:
+  ~MockSafeBrowsingDatabaseManager() override {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockSafeBrowsingDatabaseManager);
+};
+}  // namespace
+
+class AllowlistCheckerClientTest : public testing::Test {
+ public:
+  AllowlistCheckerClientTest()
+      : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME),
+        target_url_("https://example.test") {}
+
+  void SetUp() override {
+    database_manager_ = new MockSafeBrowsingDatabaseManager;
+  }
+
+  void TearDown() override {
+    database_manager_ = nullptr;
+    base::RunLoop().RunUntilIdle();
+
+    // Verify no callback is remaining.
+    EXPECT_TRUE(task_environment_.MainThreadIsIdle());
+  }
+
+ protected:
+  content::BrowserTaskEnvironment task_environment_;
+
+  GURL target_url_;
+  scoped_refptr<MockSafeBrowsingDatabaseManager> database_manager_;
+};
+
+TEST_F(AllowlistCheckerClientTest, TestCsdListMatch) {
+  EXPECT_CALL(*database_manager_, CheckCsdWhitelistUrl(target_url_, _))
+      .WillOnce(Return(AsyncMatch::MATCH));
+  MockBoolCallback callback;
+  EXPECT_CALL(callback, Run(true /* did_match_allowlist */));
+  AllowlistCheckerClient::StartCheckCsdWhitelist(database_manager_, target_url_,
+                                                 callback.Get());
+}
+
+TEST_F(AllowlistCheckerClientTest, TestCsdListNoMatch) {
+  EXPECT_CALL(*database_manager_, CheckCsdWhitelistUrl(target_url_, _))
+      .WillOnce(Return(AsyncMatch::NO_MATCH));
+  MockBoolCallback callback;
+  EXPECT_CALL(callback, Run(false /* did_match_allowlist */));
+  AllowlistCheckerClient::StartCheckCsdWhitelist(database_manager_, target_url_,
+                                                 callback.Get());
+}
+
+TEST_F(AllowlistCheckerClientTest, TestCsdListAsyncNoMatch) {
+  SafeBrowsingDatabaseManager::Client* client;
+  EXPECT_CALL(*database_manager_, CheckCsdWhitelistUrl(target_url_, _))
+      .WillOnce(DoAll(SaveArg<1>(&client), Return(AsyncMatch::ASYNC)));
+
+  MockBoolCallback callback;
+  AllowlistCheckerClient::StartCheckCsdWhitelist(database_manager_, target_url_,
+                                                 callback.Get());
+  // Callback should not be called yet.
+
+  EXPECT_CALL(callback, Run(false /* did_match_allowlist */));
+  // The self-owned client deletes itself here.
+  client->OnCheckWhitelistUrlResult(false);
+}
+
+TEST_F(AllowlistCheckerClientTest, TestCsdListAsyncTimeout) {
+  SafeBrowsingDatabaseManager::Client* client;
+  EXPECT_CALL(*database_manager_, CheckCsdWhitelistUrl(target_url_, _))
+      .WillOnce(DoAll(SaveArg<1>(&client), Return(AsyncMatch::ASYNC)));
+  EXPECT_CALL(*database_manager_, CancelCheck(_)).Times(1);
+
+  MockBoolCallback callback;
+  AllowlistCheckerClient::StartCheckCsdWhitelist(database_manager_, target_url_,
+                                                 callback.Get());
+  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
+  // No callback yet.
+
+  EXPECT_CALL(callback, Run(true /* did_match_allowlist */));
+  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(5));
+}
+
+TEST_F(AllowlistCheckerClientTest, TestHighConfidenceListMatch) {
+  EXPECT_CALL(*database_manager_,
+              CheckUrlForHighConfidenceAllowlist(target_url_, _))
+      .WillOnce(Return(AsyncMatch::MATCH));
+
+  MockBoolCallback callback;
+  EXPECT_CALL(callback, Run(true /* did_match_allowlist */));
+  AllowlistCheckerClient::StartCheckHighConfidenceAllowlist(
+      database_manager_, target_url_, callback.Get());
+}
+
+TEST_F(AllowlistCheckerClientTest, TestHighConfidenceListNoMatch) {
+  EXPECT_CALL(*database_manager_,
+              CheckUrlForHighConfidenceAllowlist(target_url_, _))
+      .WillOnce(Return(AsyncMatch::NO_MATCH));
+
+  MockBoolCallback callback;
+  EXPECT_CALL(callback, Run(false /* did_match_allowlist */));
+  AllowlistCheckerClient::StartCheckHighConfidenceAllowlist(
+      database_manager_, target_url_, callback.Get());
+}
+
+TEST_F(AllowlistCheckerClientTest, TestHighConfidenceListAsyncNoMatch) {
+  SafeBrowsingDatabaseManager::Client* client;
+  EXPECT_CALL(*database_manager_,
+              CheckUrlForHighConfidenceAllowlist(target_url_, _))
+      .WillOnce(DoAll(SaveArg<1>(&client), Return(AsyncMatch::ASYNC)));
+
+  MockBoolCallback callback;
+  AllowlistCheckerClient::StartCheckHighConfidenceAllowlist(
+      database_manager_, target_url_, callback.Get());
+  // Callback should not be called yet.
+
+  EXPECT_CALL(callback, Run(false /* did_match_allowlist */));
+  // The self-owned client deletes itself here.
+  client->OnCheckWhitelistUrlResult(false);
+}
+
+TEST_F(AllowlistCheckerClientTest, TestHighConfidenceListAsyncTimeout) {
+  SafeBrowsingDatabaseManager::Client* client;
+  EXPECT_CALL(*database_manager_,
+              CheckUrlForHighConfidenceAllowlist(target_url_, _))
+      .WillOnce(DoAll(SaveArg<1>(&client), Return(AsyncMatch::ASYNC)));
+  EXPECT_CALL(*database_manager_, CancelCheck(_)).Times(1);
+
+  MockBoolCallback callback;
+  AllowlistCheckerClient::StartCheckHighConfidenceAllowlist(
+      database_manager_, target_url_, callback.Get());
+  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
+  // No callback yet.
+
+  EXPECT_CALL(callback, Run(false /* did_match_allowlist */));
+  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(5));
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/db/database_manager.cc b/components/safe_browsing/core/db/database_manager.cc
new file mode 100644
index 0000000..54916f6
--- /dev/null
+++ b/components/safe_browsing/core/db/database_manager.cc
@@ -0,0 +1,172 @@
+// 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 "components/safe_browsing/core/db/database_manager.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/task/post_task.h"
+#include "components/safe_browsing/core/db/v4_get_hash_protocol_manager.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "url/gurl.h"
+
+using content::BrowserThread;
+
+namespace safe_browsing {
+
+SafeBrowsingDatabaseManager::SafeBrowsingDatabaseManager()
+    : base::RefCountedDeleteOnSequence<SafeBrowsingDatabaseManager>(
+          base::CreateSingleThreadTaskRunner({content::BrowserThread::IO})),
+      enabled_(false) {}
+
+SafeBrowsingDatabaseManager::~SafeBrowsingDatabaseManager() {
+  DCHECK(!v4_get_hash_protocol_manager_);
+}
+
+bool SafeBrowsingDatabaseManager::CancelApiCheck(Client* client) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  auto it = FindClientApiCheck(client);
+  if (it != api_checks_.end()) {
+    api_checks_.erase(it);
+    return true;
+  }
+  NOTREACHED();
+  return false;
+}
+
+bool SafeBrowsingDatabaseManager::CheckApiBlacklistUrl(const GURL& url,
+                                                       Client* client) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK(v4_get_hash_protocol_manager_);
+
+  // Make sure we can check this url and that the service is enabled.
+  if (!enabled_ ||
+      !(url.SchemeIs(url::kHttpScheme) || url.SchemeIs(url::kHttpsScheme))) {
+    return true;
+  }
+
+  // There can only be one in-progress check for the same client at a time.
+  DCHECK(FindClientApiCheck(client) == api_checks_.end());
+
+  std::unique_ptr<SafeBrowsingApiCheck> check(
+      new SafeBrowsingApiCheck(url, client));
+  api_checks_.insert(check.get());
+
+  std::vector<std::string> list_client_states;
+  V4ProtocolManagerUtil::GetListClientStatesFromStoreStateMap(
+      GetStoreStateMap(), &list_client_states);
+
+  v4_get_hash_protocol_manager_->GetFullHashesWithApis(
+      url, list_client_states,
+      base::BindOnce(&SafeBrowsingDatabaseManager::OnThreatMetadataResponse,
+                     base::Unretained(this), std::move(check)));
+
+  return false;
+}
+
+SafeBrowsingDatabaseManager::ApiCheckSet::iterator
+SafeBrowsingDatabaseManager::FindClientApiCheck(Client* client) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  for (auto it = api_checks_.begin(); it != api_checks_.end(); ++it) {
+    if ((*it)->client() == client) {
+      return it;
+    }
+  }
+  return api_checks_.end();
+}
+
+// Keep the list returned here in sync with GetStoreStateMap()
+StoresToCheck SafeBrowsingDatabaseManager::GetStoresForFullHashRequests() {
+  return StoresToCheck({GetChromeUrlApiId()});
+}
+
+// Keep the list returned here in sync with GetStoresForFullHashRequests()
+std::unique_ptr<StoreStateMap> SafeBrowsingDatabaseManager::GetStoreStateMap() {
+  // This implementation is currently used only for RemoteDatabaseManager which
+  // only requests full hashes for GetChromeUrlApiId() list that has no local
+  // storage so the client state is always empty.
+
+  auto store_state_map = std::make_unique<StoreStateMap>();
+  (*store_state_map)[GetChromeUrlApiId()] = "";
+  return store_state_map;
+}
+
+void SafeBrowsingDatabaseManager::OnThreatMetadataResponse(
+    std::unique_ptr<SafeBrowsingApiCheck> check,
+    const ThreatMetadata& md) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK(check);
+
+  // If the check is not in |api_checks_| then the request was cancelled by the
+  // client.
+  auto it = api_checks_.find(check.get());
+  if (it == api_checks_.end())
+    return;
+
+  check->client()->OnCheckApiBlacklistUrlResult(check->url(), md);
+  api_checks_.erase(it);
+}
+
+void SafeBrowsingDatabaseManager::StartOnIOThread(
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    const V4ProtocolConfig& config) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  v4_get_hash_protocol_manager_ = V4GetHashProtocolManager::Create(
+      url_loader_factory, GetStoresForFullHashRequests(), config);
+}
+
+// |shutdown| not used. Destroys the v4 protocol managers. This may be called
+// multiple times during the life of the DatabaseManager.
+// Must be called on IO thread.
+void SafeBrowsingDatabaseManager::StopOnIOThread(bool shutdown) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  // Delete pending checks, calling back any clients with empty metadata.
+  for (const SafeBrowsingApiCheck* check : api_checks_) {
+    if (check->client()) {
+      check->client()->OnCheckApiBlacklistUrlResult(check->url(),
+                                                    ThreatMetadata());
+    }
+  }
+
+  // This cancels all in-flight GetHash requests.
+  v4_get_hash_protocol_manager_.reset();
+}
+
+RealTimeUrlLookupService*
+SafeBrowsingDatabaseManager::GetRealTimeUrlLookupService() {
+  return nullptr;
+}
+
+std::unique_ptr<base::CallbackList<void()>::Subscription>
+SafeBrowsingDatabaseManager::RegisterDatabaseUpdatedCallback(
+    const OnDatabaseUpdated& cb) {
+  return update_complete_callback_list_.Add(cb);
+}
+
+void SafeBrowsingDatabaseManager::NotifyDatabaseUpdateFinished() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  update_complete_callback_list_.Notify();
+}
+
+std::string SafeBrowsingDatabaseManager::GetSafetyNetId() const {
+  NOTREACHED() << "Only implemented on Android";
+  return "";
+}
+
+SafeBrowsingDatabaseManager::SafeBrowsingApiCheck::SafeBrowsingApiCheck(
+    const GURL& url,
+    Client* client)
+    : url_(url), client_(client) {}
+
+SafeBrowsingDatabaseManager::SafeBrowsingApiCheck::~SafeBrowsingApiCheck() {}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/db/database_manager.h b/components/safe_browsing/core/db/database_manager.h
new file mode 100644
index 0000000..1b8fd6d
--- /dev/null
+++ b/components/safe_browsing/core/db/database_manager.h
@@ -0,0 +1,351 @@
+// Copyright (c) 2012 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.
+//
+// The Safe Browsing service is responsible for downloading anti-phishing and
+// anti-malware tables and checking urls against them.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CORE_DB_DATABASE_MANAGER_H_
+#define COMPONENTS_SAFE_BROWSING_CORE_DB_DATABASE_MANAGER_H_
+
+#include <memory>
+#include <set>
+#include <string>
+#include <unordered_set>
+#include <vector>
+
+#include "base/callback_list.h"
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted_delete_on_sequence.h"
+#include "components/safe_browsing/core/db/hit_report.h"
+#include "components/safe_browsing/core/db/util.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
+#include "content/public/common/resource_type.h"
+#include "url/gurl.h"
+
+namespace network {
+class SharedURLLoaderFactory;
+}  // namespace network
+
+namespace safe_browsing {
+
+class RealTimeUrlLookupService;
+
+// Value returned by some functions that check an allowlist and may or may not
+// have an immediate answer.
+enum class AsyncMatch : int {
+  // If a hash prefix on the allowlist matches any of the computed hashes for
+  // the URL. In this case, the callback method on the client is called back
+  // later with the result.
+  ASYNC,
+
+  // If a full hash on the allowlist matches any of the computed hashes for the
+  // URL. The callback function isn't called.
+  MATCH,
+
+  // If Safe Browsing isn't enabled, or the allowlist hasn't been sync'd yet, or
+  // when no hash prefix or full hash in the allowlist matches the computed
+  // hashes of the URL. The callback function isn't called.
+  NO_MATCH,
+
+  kMaxValue = NO_MATCH,
+};
+
+struct V4ProtocolConfig;
+class V4GetHashProtocolManager;
+
+// Base class to either the locally-managed or a remotely-managed database.
+class SafeBrowsingDatabaseManager
+    : public base::RefCountedDeleteOnSequence<SafeBrowsingDatabaseManager> {
+ public:
+  // Callers requesting a result should derive from this class.
+  // The destructor should call db_manager->CancelCheck(client) if a
+  // request is still pending.
+  class Client {
+   public:
+    virtual ~Client() {}
+
+    // Called when the result of checking the API blacklist is known.
+    // TODO(kcarattini): Consider if we need |url| passed here, remove if not.
+    virtual void OnCheckApiBlacklistUrlResult(const GURL& url,
+                                              const ThreatMetadata& metadata) {}
+
+    // Called when the result of checking a browse URL is known or the result of
+    // checking the URL for subresource filter is known.
+    virtual void OnCheckBrowseUrlResult(const GURL& url,
+                                        SBThreatType threat_type,
+                                        const ThreatMetadata& metadata) {}
+
+    // Called when the result of checking a download URL is known.
+    virtual void OnCheckDownloadUrlResult(const std::vector<GURL>& url_chain,
+                                          SBThreatType threat_type) {}
+
+    // Called when the result of checking a set of extensions is known.
+    virtual void OnCheckExtensionsResult(const std::set<std::string>& threats) {
+    }
+
+    // Called when the result of checking the resource blacklist is known.
+    virtual void OnCheckResourceUrlResult(const GURL& url,
+                                          SBThreatType threat_type,
+                                          const std::string& threat_hash) {}
+
+    // Called when the result of checking a whitelist is known.
+    // Currently only used for CSD whitelist.
+    virtual void OnCheckWhitelistUrlResult(bool did_match_allowlist) {}
+
+    // Called when the result of checking the high-confidence allowlist is
+    // known.
+    virtual void OnCheckUrlForHighConfidenceAllowlist(
+        bool did_match_allowlist) {}
+  };
+
+  //
+  // Methods called by the client to cancel pending checks.
+  //
+
+  // Called on the IO thread to cancel a pending API check if the result is no
+  // longer needed. Returns true if the client was found and the check
+  // successfully cancelled.
+  virtual bool CancelApiCheck(Client* client);
+
+  // Called on the IO thread to cancel a pending check if the result is no
+  // longer needed.  Also called after the result has been handled. Api checks
+  // are handled separately. To cancel an API check use CancelApiCheck.
+  virtual void CancelCheck(Client* client) = 0;
+
+  //
+  // Methods to check whether the database manager supports a certain feature.
+  //
+
+  // Returns true if this resource type should be checked.
+  virtual bool CanCheckResourceType(
+      content::ResourceType resource_type) const = 0;
+
+  // Returns true if the url's scheme can be checked.
+  virtual bool CanCheckUrl(const GURL& url) const = 0;
+
+  // Returns true if checks are never done synchronously, and therefore
+  // always have some latency.
+  virtual bool ChecksAreAlwaysAsync() const = 0;
+
+  //
+  // Methods to check (possibly asynchronously) whether a given resource is
+  // safe. If the database manager can't determine it synchronously, the
+  // appropriate method on the |client| is called back when the reputation of
+  // the resource is known.
+  //
+
+  // Called on the IO thread to check if the given url has blacklisted APIs.
+  // |client| is called asynchronously with the result when it is ready. Callers
+  // should wait for results before calling this method a second time with the
+  // same client. This method has the same implementation for both the local and
+  // remote database managers since it pings Safe Browsing servers directly
+  // without accessing the database at all.  Returns true if we can
+  // synchronously determine that the url is safe. Otherwise it returns false,
+  // and |client| is called asynchronously with the result when it is ready.
+  virtual bool CheckApiBlacklistUrl(const GURL& url, Client* client);
+
+  // Check if the |url| matches any of the full-length hashes from the client-
+  // side phishing detection whitelist. The 3-state return value indicates
+  // the result or that |client| will get a callback later with the result.
+  virtual AsyncMatch CheckCsdWhitelistUrl(const GURL& url, Client* client) = 0;
+
+  // Called on the IO thread to check if the given url is safe or not.  If we
+  // can synchronously determine that the url is safe, CheckUrl returns true.
+  // Otherwise it returns false, and |client| is called asynchronously with the
+  // result when it is ready. The URL will only be checked for the threat types
+  // in |threat_types|.
+  virtual bool CheckBrowseUrl(const GURL& url,
+                              const SBThreatTypeSet& threat_types,
+                              Client* client) = 0;
+
+  // Check if the prefix for |url| is in safebrowsing download add lists.
+  // Result will be passed to callback in |client|.
+  virtual bool CheckDownloadUrl(const std::vector<GURL>& url_chain,
+                                Client* client) = 0;
+
+  // Check which prefixes in |extension_ids| are in the safebrowsing blacklist.
+  // Returns true if not, false if further checks need to be made in which case
+  // the result will be passed to |client|.
+  virtual bool CheckExtensionIDs(const std::set<std::string>& extension_ids,
+                                 Client* client) = 0;
+
+  // Check if |url| is in the resources blacklist. Returns true if not, false
+  // if further checks need to be made in which case the result will be passed
+  // to callback in |client|.
+  virtual bool CheckResourceUrl(const GURL& url, Client* client) = 0;
+
+  // Called on the IO thread to check if the given url belongs to a list the
+  // subresource cares about. If the url doesn't belong to any such list and the
+  // check can happen synchronously, returns true. Otherwise it returns false,
+  // and |client| is called asynchronously with the result when it is ready.
+  // Returns true if the list is not yet available.
+  virtual bool CheckUrlForSubresourceFilter(const GURL& url,
+                                            Client* client) = 0;
+
+  // Called on the IO thread to check whether |url| is safe by checking if it
+  // appears on a high-confidence allowlist. The 3-state return value indicates
+  // the result or that |client| will get a callback later with the result.
+  // The high confidence allowlist is a list of partial or full hashes of URLs
+  // that are expected to be safe so in the case of a match on this list, the
+  // realtime full URL Safe Browsing lookup isn't performed.
+  virtual AsyncMatch CheckUrlForHighConfidenceAllowlist(const GURL& url,
+                                                        Client* client) = 0;
+
+  //
+  // Match*(): Methods to synchronously check if various types are safe.
+  //
+
+  // Check if SHA-256 hash of |str| matches any of the full-length hashes from
+  // the download whitelist.  Returns true if there was a match and false
+  // otherwise. To make sure we are conservative we will return true if an error
+  // occurs.  This method must be called on the IO thread.
+  virtual bool MatchDownloadWhitelistString(const std::string& str) = 0;
+
+  // Check if the |url| matches any of the full-length hashes from the download
+  // whitelist.  Returns true if there was a match and false otherwise. To make
+  // sure we are conservative we will return true if an error occurs.  This
+  // method must be called on the IO thread.
+  virtual bool MatchDownloadWhitelistUrl(const GURL& url) = 0;
+
+  // Check if the given IP address (either IPv4 or IPv6) matches the malware
+  // IP blacklist.
+  virtual bool MatchMalwareIP(const std::string& ip_address) = 0;
+
+  //
+  // Methods to check the config of the DatabaseManager.
+  //
+
+  // Returns the lists that this DatabaseManager should get full hashes for.
+  virtual StoresToCheck GetStoresForFullHashRequests();
+
+  // Returns the client_state of each of the lists that this DatabaseManager
+  // syncs.
+  virtual std::unique_ptr<StoreStateMap> GetStoreStateMap();
+
+  // Returns the Safety Net ID of the device.
+  virtual std::string GetSafetyNetId() const;
+
+  // Returns the ThreatSource for this implementation.
+  virtual ThreatSource GetThreatSource() const = 0;
+
+  // Returns whether download protection is enabled.
+  virtual bool IsDownloadProtectionEnabled() const = 0;
+
+  // Returns true if URL-checking is supported on this build+device.
+  // If false, calls to CheckBrowseUrl may dcheck-fail.
+  virtual bool IsSupported() const = 0;
+
+  //
+  // Methods to indicate when to start or suspend the SafeBrowsing operations.
+  // These functions are always called on the IO thread.
+  //
+
+  // Called to initialize objects that are used on the io_thread, such as the
+  // v4 protocol manager.  This may be called multiple times during the life of
+  // the DatabaseManager. Must be called on IO thread. All subclasses should
+  // override this method, set enabled_ to true and call the base class method
+  // at the top of it.
+  virtual void StartOnIOThread(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      const V4ProtocolConfig& config);
+
+  //
+  // Method to manage getting database updates of the DatabaseManager.
+  //
+
+  // Subscribe to receive callbacks when the database is updated, both initially
+  // when it's loaded from disk at startup, and then periodically. These
+  // callbacks will be on the UI thread.
+  using OnDatabaseUpdated = base::RepeatingClosure;
+  std::unique_ptr<base::CallbackList<void()>::Subscription>
+  RegisterDatabaseUpdatedCallback(const OnDatabaseUpdated& cb);
+
+  // Called to stop or shutdown operations on the io_thread. All subclasses
+  // should override this method, set enabled_ to false and call the base class
+  // method at the bottom of it.
+  virtual void StopOnIOThread(bool shutdown);
+
+  virtual RealTimeUrlLookupService* GetRealTimeUrlLookupService();
+
+ protected:
+  // Bundled client info for an API abuse hash prefix check.
+  class SafeBrowsingApiCheck {
+   public:
+    SafeBrowsingApiCheck(const GURL& url, Client* client);
+    ~SafeBrowsingApiCheck();
+
+    const GURL& url() const { return url_; }
+    Client* client() const { return client_; }
+
+   private:
+    GURL url_;
+
+    // Not owned.
+    Client* client_;
+
+    DISALLOW_COPY_AND_ASSIGN(SafeBrowsingApiCheck);
+  };
+
+  SafeBrowsingDatabaseManager();
+
+  virtual ~SafeBrowsingDatabaseManager();
+
+  friend class base::RefCountedDeleteOnSequence<SafeBrowsingDatabaseManager>;
+  friend class base::DeleteHelper<SafeBrowsingDatabaseManager>;
+  friend class V4LocalDatabaseManager;
+
+  FRIEND_TEST_ALL_PREFIXES(SafeBrowsingDatabaseManagerTest,
+                           CheckApiBlacklistUrlPrefixes);
+  FRIEND_TEST_ALL_PREFIXES(SafeBrowsingDatabaseManagerTest,
+                           HandleGetHashesWithApisResults);
+  FRIEND_TEST_ALL_PREFIXES(SafeBrowsingDatabaseManagerTest,
+                           HandleGetHashesWithApisResultsNoMatch);
+  FRIEND_TEST_ALL_PREFIXES(SafeBrowsingDatabaseManagerTest,
+                           HandleGetHashesWithApisResultsMatches);
+  FRIEND_TEST_ALL_PREFIXES(SafeBrowsingDatabaseManagerTest, CancelApiCheck);
+  FRIEND_TEST_ALL_PREFIXES(SafeBrowsingDatabaseManagerTest, ResultsAreCached);
+  FRIEND_TEST_ALL_PREFIXES(SafeBrowsingDatabaseManagerTest,
+                           ResultsAreNotCachedOnNull);
+  FRIEND_TEST_ALL_PREFIXES(SafeBrowsingDatabaseManagerTest, GetCachedResults);
+  FRIEND_TEST_ALL_PREFIXES(SafeBrowsingDatabaseManagerTest,
+                           CachedResultsMerged);
+  FRIEND_TEST_ALL_PREFIXES(SafeBrowsingDatabaseManagerTest,
+                           CachedResultsAreEvicted);
+
+  // Called on the IO thread when the SafeBrowsingProtocolManager has received
+  // the full hash and api results for prefixes of the |url| argument in
+  // CheckApiBlacklistUrl.
+  void OnThreatMetadataResponse(std::unique_ptr<SafeBrowsingApiCheck> check,
+                                const ThreatMetadata& md);
+
+  typedef std::set<SafeBrowsingApiCheck*> ApiCheckSet;
+
+  // In-progress checks. This set owns the SafeBrowsingApiCheck pointers and is
+  // responsible for deleting them when removing from the set.
+  ApiCheckSet api_checks_;
+
+  // Whether the service is running. 'enabled_' is used by the
+  // SafeBrowsingDatabaseManager on the IO thread during normal operations.
+  bool enabled_;
+
+  // Make callbacks about the completion of database update process. This is
+  // currently used by the extension blacklist checker to disable any installed
+  // extensions that have been blacklisted since.
+  void NotifyDatabaseUpdateFinished();
+
+  // Created and destroyed via StartOnIOThread/StopOnIOThread.
+  std::unique_ptr<V4GetHashProtocolManager> v4_get_hash_protocol_manager_;
+
+  // A list of parties to be notified about database updates.
+  base::CallbackList<void()> update_complete_callback_list_;
+
+ private:
+  // Returns an iterator to the pending API check with the given |client|.
+  ApiCheckSet::iterator FindClientApiCheck(Client* client);
+};  // class SafeBrowsingDatabaseManager
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CORE_DB_DATABASE_MANAGER_H_
diff --git a/components/safe_browsing/core/db/database_manager_unittest.cc b/components/safe_browsing/core/db/database_manager_unittest.cc
new file mode 100644
index 0000000..3937511
--- /dev/null
+++ b/components/safe_browsing/core/db/database_manager_unittest.cc
@@ -0,0 +1,153 @@
+// 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 "components/safe_browsing/core/db/database_manager.h"
+
+#include <stddef.h>
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/base64.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/test/bind_test_util.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/safe_browsing/core/db/test_database_manager.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/core/db/v4_test_util.h"
+#include "content/public/test/browser_task_environment.h"
+#include "crypto/sha2.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace safe_browsing {
+
+namespace {
+
+class TestClient : public SafeBrowsingDatabaseManager::Client {
+ public:
+  TestClient() : callback_invoked_(false) {}
+  ~TestClient() override {}
+
+  void OnCheckApiBlacklistUrlResult(const GURL& url,
+                                    const ThreatMetadata& metadata) override {
+    blocked_permissions_ = metadata.api_permissions;
+    callback_invoked_ = true;
+    run_loop_.Quit();
+  }
+
+  const std::set<std::string>& GetBlockedPermissions() {
+    return blocked_permissions_;
+  }
+
+  void WaitForCallback() { run_loop_.Run(); }
+
+  bool callback_invoked() { return callback_invoked_; }
+
+ private:
+  std::set<std::string> blocked_permissions_;
+  bool callback_invoked_;
+  base::RunLoop run_loop_;
+  DISALLOW_COPY_AND_ASSIGN(TestClient);
+};
+
+}  // namespace
+
+class SafeBrowsingDatabaseManagerTest : public testing::Test {
+ protected:
+  void SetUp() override {
+    test_shared_loader_factory_ =
+        base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+            &test_url_loader_factory_);
+
+    db_manager_ = new TestSafeBrowsingDatabaseManager();
+    db_manager_->StartOnIOThread(test_shared_loader_factory_,
+                                 GetTestV4ProtocolConfig());
+  }
+
+  void TearDown() override {
+    db_manager_->StopOnIOThread(false);
+    db_manager_ = nullptr;
+    base::RunLoop().RunUntilIdle();
+  }
+
+  std::string GetStockV4GetHashResponse() {
+    ListIdentifier list_id = GetChromeUrlApiId();
+    FullHash full_hash = crypto::SHA256HashString("example.com/");
+
+    FindFullHashesResponse response;
+    response.mutable_negative_cache_duration()->set_seconds(600);
+    ThreatMatch* m = response.add_matches();
+    m->set_platform_type(list_id.platform_type());
+    m->set_threat_entry_type(list_id.threat_entry_type());
+    m->set_threat_type(list_id.threat_type());
+    m->mutable_threat()->set_hash(full_hash);
+    m->mutable_cache_duration()->set_seconds(300);
+
+    ThreatEntryMetadata::MetadataEntry* e =
+        m->mutable_threat_entry_metadata()->add_entries();
+    e->set_key("permission");
+    e->set_value("GEOLOCATION");
+
+    std::string res_data;
+    response.SerializeToString(&res_data);
+    return res_data;
+  }
+
+  network::TestURLLoaderFactory test_url_loader_factory_;
+  scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
+  scoped_refptr<SafeBrowsingDatabaseManager> db_manager_;
+
+ private:
+  content::BrowserTaskEnvironment task_environment_;
+};
+
+TEST_F(SafeBrowsingDatabaseManagerTest, CheckApiBlacklistUrlWrongScheme) {
+  EXPECT_TRUE(
+      db_manager_->CheckApiBlacklistUrl(GURL("file://example.txt"), nullptr));
+}
+
+TEST_F(SafeBrowsingDatabaseManagerTest, CancelApiCheck) {
+  TestClient client;
+  const GURL url("https://www.example.com/more");
+
+  EXPECT_FALSE(db_manager_->CheckApiBlacklistUrl(url, &client));
+  EXPECT_TRUE(db_manager_->CancelApiCheck(&client));
+
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_FALSE(client.callback_invoked());
+  EXPECT_EQ(0ul, client.GetBlockedPermissions().size());
+}
+
+TEST_F(SafeBrowsingDatabaseManagerTest, GetApiCheckResponse) {
+  TestClient client;
+  const GURL url("https://www.example.com/more");
+
+  GURL request_url;
+  test_url_loader_factory_.SetInterceptor(
+      base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
+        request_url = request.url;
+      }));
+
+  EXPECT_FALSE(db_manager_->CheckApiBlacklistUrl(url, &client));
+  test_url_loader_factory_.AddResponse(request_url.spec(),
+                                       GetStockV4GetHashResponse());
+  base::RunLoop().RunUntilIdle();
+
+  client.WaitForCallback();
+  ASSERT_EQ(1ul, client.GetBlockedPermissions().size());
+  EXPECT_EQ("GEOLOCATION", *(client.GetBlockedPermissions().begin()));
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/db/hit_report.cc b/components/safe_browsing/core/db/hit_report.cc
new file mode 100644
index 0000000..08533f5
--- /dev/null
+++ b/components/safe_browsing/core/db/hit_report.cc
@@ -0,0 +1,15 @@
+// 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 "components/safe_browsing/core/db/hit_report.h"
+
+namespace safe_browsing {
+
+HitReport::HitReport() {}
+
+HitReport::HitReport(const HitReport& other) = default;
+
+HitReport::~HitReport() {}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/db/hit_report.h b/components/safe_browsing/core/db/hit_report.h
new file mode 100644
index 0000000..3240932
--- /dev/null
+++ b/components/safe_browsing/core/db/hit_report.h
@@ -0,0 +1,55 @@
+// 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.
+//
+// Datastructures that hold details of a Safe Browsing hit for reporting.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CORE_DB_HIT_REPORT_H_
+#define COMPONENTS_SAFE_BROWSING_CORE_DB_HIT_REPORT_H_
+
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/db/util.h"
+#include "url/gurl.h"
+
+namespace safe_browsing {
+
+// What service classified this threat as unsafe.
+enum class ThreatSource {
+  UNKNOWN,
+  DATA_SAVER,             // From the Data Reduction service.
+  LOCAL_PVER3,            // From LocalSafeBrowsingDatabaseManager, protocol v3
+  LOCAL_PVER4,            // From V4LocalDatabaseManager, protocol v4
+  REMOTE,                 // From RemoteSafeBrowsingDatabaseManager
+  CLIENT_SIDE_DETECTION,  // From ClientSideDetectionHost
+  PASSWORD_PROTECTION_SERVICE,  // From PasswordProtectionService
+};
+
+// Data to report about the contents of a particular threat (malware, phishing,
+// unsafe download URL).  If post_data is non-empty, the request will be
+// sent as a POST instead of a GET.
+struct HitReport {
+  HitReport();
+  HitReport(const HitReport& other);
+  ~HitReport();
+
+  GURL malicious_url;
+  GURL page_url;
+  GURL referrer_url;
+
+  bool is_subresource;
+  SBThreatType threat_type;
+  ThreatSource threat_source;
+
+  // Opaque string used for tracking Pver4-based experiments.
+  // NOTE(vakh): Unused at the moment, but may be used later.
+  std::string population_id;
+
+  ExtendedReportingLevel extended_reporting_level;
+  bool is_metrics_reporting_active;
+
+  std::string post_data;
+};
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CORE_DB_HIT_REPORT_H_
diff --git a/components/safe_browsing/db/metadata.proto b/components/safe_browsing/core/db/metadata.proto
similarity index 100%
rename from components/safe_browsing/db/metadata.proto
rename to components/safe_browsing/core/db/metadata.proto
diff --git a/components/safe_browsing/core/db/prefix_iterator.cc b/components/safe_browsing/core/db/prefix_iterator.cc
new file mode 100644
index 0000000..2263598
--- /dev/null
+++ b/components/safe_browsing/core/db/prefix_iterator.cc
@@ -0,0 +1,17 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/core/db/prefix_iterator.h"
+
+namespace safe_browsing {
+
+PrefixIterator::PrefixIterator(base::StringPiece prefixes,
+                               size_t index,
+                               size_t size)
+    : prefixes_(prefixes), index_(index), size_(size) {}
+
+PrefixIterator::PrefixIterator(const PrefixIterator& rhs)
+    : prefixes_(rhs.prefixes_), index_(rhs.index_), size_(rhs.size_) {}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/db/prefix_iterator.h b/components/safe_browsing/core/db/prefix_iterator.h
new file mode 100644
index 0000000..998d6b5
--- /dev/null
+++ b/components/safe_browsing/core/db/prefix_iterator.h
@@ -0,0 +1,99 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CORE_DB_PREFIX_ITERATOR_H_
+#define COMPONENTS_SAFE_BROWSING_CORE_DB_PREFIX_ITERATOR_H_
+
+#include <cstddef>
+#include <iterator>
+
+#include "base/strings/string_piece.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
+
+namespace safe_browsing {
+
+// The prefix iterator is used to binary search within a |HashPrefixes|. It is
+// essentially a random access iterator that steps |PrefixSize| steps within the
+// underlying buffer.
+class PrefixIterator
+    : public std::iterator<std::random_access_iterator_tag, base::StringPiece> {
+ public:
+  using difference_type =
+      typename std::iterator<std::random_access_iterator_tag,
+                             base::StringPiece>::difference_type;
+
+  PrefixIterator(base::StringPiece prefixes, size_t index, size_t size);
+  PrefixIterator(const PrefixIterator& rhs);
+
+  base::StringPiece operator*() const { return GetPiece(index_); }
+  base::StringPiece operator[](const int& rhs) const {
+    return GetPiece(index_ + rhs);
+  }
+
+  PrefixIterator& operator=(const PrefixIterator& rhs) {
+    index_ = rhs.index_;
+    return *this;
+  }
+  PrefixIterator& operator+=(const int& rhs) {
+    index_ += rhs;
+    return *this;
+  }
+  PrefixIterator& operator-=(const int& rhs) {
+    index_ -= rhs;
+    return *this;
+  }
+  PrefixIterator& operator++() {
+    index_++;
+    return *this;
+  }
+  PrefixIterator& operator--() {
+    index_--;
+    return *this;
+  }
+
+  PrefixIterator operator+(const PrefixIterator& rhs) const {
+    return PrefixIterator(prefixes_, index_ + rhs.index_, size_);
+  }
+  difference_type operator-(const PrefixIterator& rhs) const {
+    return index_ - rhs.index_;
+  }
+  PrefixIterator operator+(const int& rhs) const {
+    return PrefixIterator(prefixes_, index_ + rhs, size_);
+  }
+  PrefixIterator operator-(const int& rhs) const {
+    return PrefixIterator(prefixes_, index_ - rhs, size_);
+  }
+
+  bool operator==(const PrefixIterator& rhs) const {
+    return index_ == rhs.index_;
+  }
+  bool operator!=(const PrefixIterator& rhs) const {
+    return index_ != rhs.index_;
+  }
+  bool operator>(const PrefixIterator& rhs) const {
+    return index_ > rhs.index_;
+  }
+  bool operator<(const PrefixIterator& rhs) const {
+    return index_ < rhs.index_;
+  }
+  bool operator>=(const PrefixIterator& rhs) const {
+    return index_ >= rhs.index_;
+  }
+  bool operator<=(const PrefixIterator& rhs) const {
+    return index_ <= rhs.index_;
+  }
+
+ private:
+  base::StringPiece GetPiece(size_t index) const {
+    return prefixes_.substr(index * size_, size_);
+  }
+
+  base::StringPiece prefixes_;
+  size_t index_;
+  size_t size_;
+};
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CORE_DB_PREFIX_ITERATOR_H_
diff --git a/components/safe_browsing/db/safebrowsing.proto b/components/safe_browsing/core/db/safebrowsing.proto
similarity index 100%
rename from components/safe_browsing/db/safebrowsing.proto
rename to components/safe_browsing/core/db/safebrowsing.proto
diff --git a/components/safe_browsing/core/db/test_database_manager.cc b/components/safe_browsing/core/db/test_database_manager.cc
new file mode 100644
index 0000000..02be91d
--- /dev/null
+++ b/components/safe_browsing/core/db/test_database_manager.cc
@@ -0,0 +1,131 @@
+// 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 "components/safe_browsing/core/db/test_database_manager.h"
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/logging.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+
+namespace safe_browsing {
+
+void TestSafeBrowsingDatabaseManager::CancelCheck(Client* client) {
+  NOTIMPLEMENTED();
+}
+
+bool TestSafeBrowsingDatabaseManager::CanCheckResourceType(
+    content::ResourceType resource_type) const {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+bool TestSafeBrowsingDatabaseManager::CanCheckUrl(const GURL& url) const {
+  NOTIMPLEMENTED();
+  return (url != GURL("about:blank"));
+}
+
+bool TestSafeBrowsingDatabaseManager::ChecksAreAlwaysAsync() const {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+bool TestSafeBrowsingDatabaseManager::CheckBrowseUrl(
+    const GURL& url,
+    const SBThreatTypeSet& threat_types,
+    Client* client) {
+  NOTIMPLEMENTED();
+  return true;
+}
+
+bool TestSafeBrowsingDatabaseManager::CheckDownloadUrl(
+    const std::vector<GURL>& url_chain,
+    Client* client) {
+  NOTIMPLEMENTED();
+  return true;
+}
+
+bool TestSafeBrowsingDatabaseManager::CheckExtensionIDs(
+    const std::set<std::string>& extension_ids,
+    Client* client) {
+  NOTIMPLEMENTED();
+  return true;
+}
+
+bool TestSafeBrowsingDatabaseManager::CheckResourceUrl(const GURL& url,
+                                                       Client* client) {
+  NOTIMPLEMENTED();
+  return true;
+}
+
+AsyncMatch TestSafeBrowsingDatabaseManager::CheckUrlForHighConfidenceAllowlist(
+    const GURL& url,
+    Client* client) {
+  NOTIMPLEMENTED();
+  return AsyncMatch::NO_MATCH;
+}
+
+bool TestSafeBrowsingDatabaseManager::CheckUrlForSubresourceFilter(
+    const GURL& url,
+    Client* client) {
+  NOTIMPLEMENTED();
+  return true;
+}
+
+AsyncMatch TestSafeBrowsingDatabaseManager::CheckCsdWhitelistUrl(
+    const GURL& url,
+    Client* client) {
+  NOTIMPLEMENTED();
+  return AsyncMatch::MATCH;
+}
+
+bool TestSafeBrowsingDatabaseManager::MatchDownloadWhitelistString(
+    const std::string& str) {
+  NOTIMPLEMENTED();
+  return true;
+}
+
+bool TestSafeBrowsingDatabaseManager::MatchDownloadWhitelistUrl(
+    const GURL& url) {
+  NOTIMPLEMENTED();
+  return true;
+}
+
+bool TestSafeBrowsingDatabaseManager::MatchMalwareIP(
+    const std::string& ip_address) {
+  NOTIMPLEMENTED();
+  return true;
+}
+
+safe_browsing::ThreatSource TestSafeBrowsingDatabaseManager::GetThreatSource()
+    const {
+  NOTIMPLEMENTED();
+  return safe_browsing::ThreatSource::UNKNOWN;
+}
+
+bool TestSafeBrowsingDatabaseManager::IsDownloadProtectionEnabled() const {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+bool TestSafeBrowsingDatabaseManager::IsSupported() const {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+void TestSafeBrowsingDatabaseManager::StartOnIOThread(
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    const V4ProtocolConfig& config) {
+  SafeBrowsingDatabaseManager::StartOnIOThread(url_loader_factory, config);
+  enabled_ = true;
+}
+
+void TestSafeBrowsingDatabaseManager::StopOnIOThread(bool shutdown) {
+  enabled_ = false;
+  SafeBrowsingDatabaseManager::StopOnIOThread(shutdown);
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/db/test_database_manager.h b/components/safe_browsing/core/db/test_database_manager.h
new file mode 100644
index 0000000..539f65f
--- /dev/null
+++ b/components/safe_browsing/core/db/test_database_manager.h
@@ -0,0 +1,57 @@
+// 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 COMPONENTS_SAFE_BROWSING_CORE_DB_TEST_DATABASE_MANAGER_H_
+#define COMPONENTS_SAFE_BROWSING_CORE_DB_TEST_DATABASE_MANAGER_H_
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "components/safe_browsing/core/db/database_manager.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
+
+namespace safe_browsing {
+
+// This is a non-pure-virtual implementation of the SafeBrowsingDatabaseManager
+// interface.  It's used in tests by overriding only the functions that get
+// called, and it'll complain if you call one that isn't overriden. The
+// non-abstract methods in the base class are not overridden.
+class TestSafeBrowsingDatabaseManager : public SafeBrowsingDatabaseManager {
+ public:
+  // SafeBrowsingDatabaseManager implementation:
+  void CancelCheck(Client* client) override;
+  bool CanCheckResourceType(content::ResourceType resource_type) const override;
+  bool CanCheckUrl(const GURL& url) const override;
+  bool ChecksAreAlwaysAsync() const override;
+  bool CheckBrowseUrl(const GURL& url,
+                      const SBThreatTypeSet& threat_types,
+                      Client* client) override;
+  AsyncMatch CheckCsdWhitelistUrl(const GURL& url, Client* client) override;
+  bool CheckDownloadUrl(const std::vector<GURL>& url_chain,
+                        Client* client) override;
+  bool CheckExtensionIDs(const std::set<std::string>& extension_ids,
+                         Client* client) override;
+  bool CheckResourceUrl(const GURL& url, Client* client) override;
+  AsyncMatch CheckUrlForHighConfidenceAllowlist(const GURL& url,
+                                                Client* client) override;
+  bool CheckUrlForSubresourceFilter(const GURL& url, Client* client) override;
+  bool MatchDownloadWhitelistString(const std::string& str) override;
+  bool MatchDownloadWhitelistUrl(const GURL& url) override;
+  bool MatchMalwareIP(const std::string& ip_address) override;
+  safe_browsing::ThreatSource GetThreatSource() const override;
+  bool IsDownloadProtectionEnabled() const override;
+  bool IsSupported() const override;
+  void StartOnIOThread(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      const V4ProtocolConfig& config) override;
+  void StopOnIOThread(bool shutdown) override;
+
+ protected:
+  ~TestSafeBrowsingDatabaseManager() override {}
+};
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CORE_DB_TEST_DATABASE_MANAGER_H_
diff --git a/components/safe_browsing/core/db/util.cc b/components/safe_browsing/core/db/util.cc
new file mode 100644
index 0000000..2f44d6d
--- /dev/null
+++ b/components/safe_browsing/core/db/util.cc
@@ -0,0 +1,55 @@
+// Copyright (c) 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 "components/safe_browsing/core/db/util.h"
+
+#include <stddef.h>
+
+namespace safe_browsing {
+
+ThreatMetadata::ThreatMetadata()
+    : threat_pattern_type(ThreatPatternType::NONE) {}
+
+ThreatMetadata::ThreatMetadata(const ThreatMetadata& other) = default;
+
+ThreatMetadata::~ThreatMetadata() {}
+
+bool ThreatMetadata::operator==(const ThreatMetadata& other) const {
+  return threat_pattern_type == other.threat_pattern_type &&
+         api_permissions == other.api_permissions &&
+         subresource_filter_match == other.subresource_filter_match &&
+         population_id == other.population_id;
+}
+
+bool ThreatMetadata::operator!=(const ThreatMetadata& other) const {
+  return !operator==(other);
+}
+
+std::unique_ptr<base::trace_event::TracedValue> ThreatMetadata::ToTracedValue()
+    const {
+  auto value = std::make_unique<base::trace_event::TracedValue>();
+
+  value->SetInteger("threat_pattern_type",
+                    static_cast<int>(threat_pattern_type));
+
+  value->BeginArray("api_permissions");
+  for (const std::string& permission : api_permissions) {
+    value->AppendString(permission);
+  }
+  value->EndArray();
+
+  value->BeginDictionary("subresource_filter_match");
+  for (const auto& it : subresource_filter_match) {
+    value->BeginArray("match_metadata");
+    value->AppendInteger(static_cast<int>(it.first));
+    value->AppendInteger(static_cast<int>(it.second));
+    value->EndArray();
+  }
+  value->EndDictionary();
+
+  value->SetString("popuplation_id", population_id);
+  return value;
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/db/util.h b/components/safe_browsing/core/db/util.h
new file mode 100644
index 0000000..60439ac
--- /dev/null
+++ b/components/safe_browsing/core/db/util.h
@@ -0,0 +1,81 @@
+// Copyright (c) 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.
+//
+// Utilities for the SafeBrowsing DB code.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CORE_DB_UTIL_H_
+#define COMPONENTS_SAFE_BROWSING_CORE_DB_UTIL_H_
+
+#include <stdint.h>
+
+#include <cstring>
+#include <memory>
+#include <set>
+#include <string>
+
+#include "base/containers/flat_map.h"
+#include "base/strings/string_piece.h"
+#include "base/time/time.h"
+#include "base/trace_event/traced_value.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
+
+namespace safe_browsing {
+
+// Metadata that indicates what kind of URL match this is.
+enum class ThreatPatternType : int {
+  NONE = 0,                        // Pattern type didn't appear in the metadata
+  MALWARE_LANDING = 1,             // The match is a malware landing page
+  MALWARE_DISTRIBUTION = 2,        // The match is a malware distribution page
+  SOCIAL_ENGINEERING_ADS = 3,      // The match is a social engineering ads page
+  SOCIAL_ENGINEERING_LANDING = 4,  // The match is a social engineering landing
+                                   // page
+  PHISHING = 5,                    // The match is a phishing page
+  THREAT_PATTERN_TYPE_MAX_VALUE
+};
+
+enum class SubresourceFilterType : int { ABUSIVE = 0, BETTER_ADS = 1 };
+
+enum class SubresourceFilterLevel : int { WARN = 0, ENFORCE = 1 };
+
+using SubresourceFilterMatch =
+    base::flat_map<SubresourceFilterType, SubresourceFilterLevel>;
+
+// Metadata that was returned by a GetFullHash call. This is the parsed version
+// of the PB (from Pver3, or Pver4 local) or JSON (from Pver4 via GMSCore).
+// Some fields are only applicable to certain lists.
+//
+// When adding elements to this struct, make sure you update operator== and
+// ToTracedValue.
+struct ThreatMetadata {
+  ThreatMetadata();
+  ThreatMetadata(const ThreatMetadata& other);
+  ~ThreatMetadata();
+
+  bool operator==(const ThreatMetadata& other) const;
+  bool operator!=(const ThreatMetadata& other) const;
+
+  // Returns the metadata in a format tracing can support.
+  std::unique_ptr<base::trace_event::TracedValue> ToTracedValue() const;
+
+  // Type of blacklisted page. Used on malware and UwS lists.
+  // This will be NONE if it wasn't present in the reponse.
+  ThreatPatternType threat_pattern_type;
+
+  // Set of permissions blocked. Used with threat_type API_ABUSE.
+  // This will be empty if it wasn't present in the response.
+  std::set<std::string> api_permissions;
+
+  // Map of list sub-types related to the SUBRESOURCE_FILTER threat type.
+  // Used instead of ThreatPatternType to allow multiple types at the same time.
+  SubresourceFilterMatch subresource_filter_match;
+
+  // Opaque base64 string used for user-population experiments in pver4.
+  // This will be empty if it wasn't present in the response.
+  std::string population_id;
+};
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CORE_DB_UTIL_H_
diff --git a/components/safe_browsing/core/db/v4_database.cc b/components/safe_browsing/core/db/v4_database.cc
new file mode 100644
index 0000000..0a05bfd3
--- /dev/null
+++ b/components/safe_browsing/core/db/v4_database.cc
@@ -0,0 +1,341 @@
+// 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 "components/safe_browsing/core/db/v4_database.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/files/file_util.h"
+#include "base/lazy_instance.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/task_runner_util.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/safe_browsing/core/proto/webui.pb.h"
+#include "content/public/browser/browser_thread.h"
+
+using content::BrowserThread;
+using base::TimeTicks;
+
+namespace safe_browsing {
+
+namespace {
+
+const char kV4DatabaseSizeMetric[] = "SafeBrowsing.V4Database.Size";
+
+// The factory that controls the creation of the V4Database object.
+base::LazyInstance<std::unique_ptr<V4DatabaseFactory>>::Leaky g_db_factory =
+    LAZY_INSTANCE_INITIALIZER;
+
+// The factory that controls the creation of V4Store objects.
+base::LazyInstance<std::unique_ptr<V4StoreFactory>>::Leaky g_store_factory =
+    LAZY_INSTANCE_INITIALIZER;
+
+// Verifies the checksums on a collection of stores.
+// Returns the IDs of stores whose checksums failed to verify.
+std::vector<ListIdentifier> VerifyChecksums(
+    std::vector<std::pair<ListIdentifier, V4Store*>> stores) {
+  std::vector<ListIdentifier> stores_to_reset;
+  for (const auto& store_map_iter : stores) {
+    if (!store_map_iter.second->VerifyChecksum()) {
+      stores_to_reset.push_back(store_map_iter.first);
+    }
+  }
+  return stores_to_reset;
+}
+
+}  // namespace
+
+std::unique_ptr<V4Database> V4DatabaseFactory::Create(
+    const scoped_refptr<base::SequencedTaskRunner>& db_task_runner,
+    std::unique_ptr<StoreMap> store_map) {
+  // Not using MakeUnique since the constructor of V4Database is protected.
+  return base::WrapUnique(new V4Database(db_task_runner, std::move(store_map)));
+}
+
+// static
+void V4Database::Create(
+    const scoped_refptr<base::SequencedTaskRunner>& db_task_runner,
+    const base::FilePath& base_path,
+    const ListInfos& list_infos,
+    NewDatabaseReadyCallback new_db_callback) {
+  DCHECK(base_path.IsAbsolute());
+  DCHECK(!list_infos.empty());
+
+  const scoped_refptr<base::SingleThreadTaskRunner> callback_task_runner =
+      base::ThreadTaskRunnerHandle::Get();
+  db_task_runner->PostTask(
+      FROM_HERE, base::BindOnce(&V4Database::CreateOnTaskRunner, db_task_runner,
+                                base_path, list_infos, callback_task_runner,
+                                std::move(new_db_callback)));
+}
+
+// static
+void V4Database::CreateOnTaskRunner(
+    const scoped_refptr<base::SequencedTaskRunner>& db_task_runner,
+    const base::FilePath& base_path,
+    const ListInfos& list_infos,
+    const scoped_refptr<base::SingleThreadTaskRunner>& callback_task_runner,
+    NewDatabaseReadyCallback new_db_callback) {
+  DCHECK(db_task_runner->RunsTasksInCurrentSequence());
+
+  if (!g_store_factory.Get())
+    g_store_factory.Get() = std::make_unique<V4StoreFactory>();
+
+  if (!base::CreateDirectory(base_path))
+    NOTREACHED();
+
+  std::unique_ptr<StoreMap> store_map = std::make_unique<StoreMap>();
+  for (const auto& it : list_infos) {
+    if (!it.fetch_updates()) {
+      // This list doesn't need to be fetched or stored on disk.
+      continue;
+    }
+
+    const base::FilePath store_path = base_path.AppendASCII(it.filename());
+    (*store_map)[it.list_id()] =
+        g_store_factory.Get()->CreateV4Store(db_task_runner, store_path);
+  }
+
+  if (!g_db_factory.Get())
+    g_db_factory.Get() = std::make_unique<V4DatabaseFactory>();
+
+  std::unique_ptr<V4Database> v4_database =
+      g_db_factory.Get()->Create(db_task_runner, std::move(store_map));
+
+  // Database is done loading, pass it to the new_db_callback on the caller's
+  // thread. This would unblock resource loads.
+  callback_task_runner->PostTask(
+      FROM_HERE,
+      base::BindOnce(std::move(new_db_callback), std::move(v4_database)));
+}
+
+// static
+void V4Database::RegisterDatabaseFactoryForTest(
+    std::unique_ptr<V4DatabaseFactory> factory) {
+  g_db_factory.Get() = std::move(factory);
+}
+
+// static
+void V4Database::RegisterStoreFactoryForTest(
+    std::unique_ptr<V4StoreFactory> factory) {
+  g_store_factory.Get() = std::move(factory);
+}
+
+V4Database::V4Database(
+    const scoped_refptr<base::SequencedTaskRunner>& db_task_runner,
+    std::unique_ptr<StoreMap> store_map)
+    : store_map_(std::move(store_map)),
+      db_task_runner_(db_task_runner),
+      pending_store_updates_(0) {
+  DCHECK(db_task_runner->RunsTasksInCurrentSequence());
+}
+
+// static
+void V4Database::Destroy(std::unique_ptr<V4Database> v4_database) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  V4Database* v4_database_raw = v4_database.release();
+  if (v4_database_raw) {
+    v4_database_raw->weak_factory_on_io_.InvalidateWeakPtrs();
+    v4_database_raw->db_task_runner_->DeleteSoon(FROM_HERE, v4_database_raw);
+  }
+}
+
+V4Database::~V4Database() {
+  DCHECK(db_task_runner_->RunsTasksInCurrentSequence());
+}
+
+void V4Database::ApplyUpdate(
+    std::unique_ptr<ParsedServerResponse> parsed_server_response,
+    DatabaseUpdatedCallback db_updated_callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK(!pending_store_updates_);
+  DCHECK(db_updated_callback_.is_null());
+
+  db_updated_callback_ = db_updated_callback;
+
+  // Post the V4Store update task on the task runner but get the callback on the
+  // current thread.
+  const scoped_refptr<base::SingleThreadTaskRunner> current_task_runner =
+      base::ThreadTaskRunnerHandle::Get();
+  for (std::unique_ptr<ListUpdateResponse>& response :
+       *parsed_server_response) {
+    ListIdentifier identifier(*response);
+    StoreMap::const_iterator iter = store_map_->find(identifier);
+    if (iter != store_map_->end()) {
+      const std::unique_ptr<V4Store>& old_store = iter->second;
+      if (old_store->state() != response->new_client_state()) {
+        // A different state implies there are updates to process.
+        pending_store_updates_++;
+        UpdatedStoreReadyCallback store_ready_callback =
+            base::BindOnce(&V4Database::UpdatedStoreReady,
+                           weak_factory_on_io_.GetWeakPtr(), identifier);
+        db_task_runner_->PostTask(
+            FROM_HERE, base::BindOnce(&V4Store::ApplyUpdate,
+                                      base::Unretained(old_store.get()),
+                                      std::move(response), current_task_runner,
+                                      std::move(store_ready_callback)));
+      }
+    } else {
+      NOTREACHED() << "Got update for unexpected identifier: " << identifier;
+    }
+  }
+
+  if (!pending_store_updates_) {
+    current_task_runner->PostTask(FROM_HERE, db_updated_callback_);
+    db_updated_callback_.Reset();
+  }
+}
+
+void V4Database::UpdatedStoreReady(ListIdentifier identifier,
+                                   std::unique_ptr<V4Store> new_store) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK(pending_store_updates_);
+  if (new_store) {
+    (*store_map_)[identifier].swap(new_store);
+    // |new_store| now is the store that needs to be destroyed on task runner.
+    V4Store::Destroy(std::move(new_store));
+  }
+
+  pending_store_updates_--;
+  if (!pending_store_updates_) {
+    db_updated_callback_.Run();
+    db_updated_callback_.Reset();
+  }
+}
+
+std::unique_ptr<StoreStateMap> V4Database::GetStoreStateMap() {
+  std::unique_ptr<StoreStateMap> store_state_map =
+      std::make_unique<StoreStateMap>();
+  for (const auto& store_map_iter : *store_map_) {
+    (*store_state_map)[store_map_iter.first] = store_map_iter.second->state();
+  }
+  return store_state_map;
+}
+
+bool V4Database::AreAnyStoresAvailable(
+    const StoresToCheck& stores_to_check) const {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  for (const ListIdentifier& identifier : stores_to_check) {
+    if (IsStoreAvailable(identifier))
+      return true;
+  }
+  return false;
+}
+
+bool V4Database::AreAllStoresAvailable(
+    const StoresToCheck& stores_to_check) const {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  for (const ListIdentifier& identifier : stores_to_check) {
+    if (!IsStoreAvailable(identifier))
+      return false;
+  }
+  return true;
+}
+
+void V4Database::GetStoresMatchingFullHash(
+    const FullHash& full_hash,
+    const StoresToCheck& stores_to_check,
+    StoreAndHashPrefixes* matched_store_and_hash_prefixes) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  matched_store_and_hash_prefixes->clear();
+  for (const ListIdentifier& identifier : stores_to_check) {
+    if (!IsStoreAvailable(identifier))
+      continue;
+    const auto& store_pair = store_map_->find(identifier);
+    DCHECK(store_pair != store_map_->end());
+    const std::unique_ptr<V4Store>& store = store_pair->second;
+    HashPrefix hash_prefix = store->GetMatchingHashPrefix(full_hash);
+    if (!hash_prefix.empty()) {
+      matched_store_and_hash_prefixes->emplace_back(identifier, hash_prefix);
+    }
+  }
+}
+
+void V4Database::ResetStores(
+    const std::vector<ListIdentifier>& stores_to_reset) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  for (const ListIdentifier& identifier : stores_to_reset) {
+    store_map_->at(identifier)->Reset();
+  }
+}
+
+void V4Database::VerifyChecksum(
+    DatabaseReadyForUpdatesCallback db_ready_for_updates_callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  // Make a threadsafe copy of store_map_ w/raw pointers that we can hand to
+  // the DB thread. The V4Stores ptrs are guaranteed to be valid because their
+  // deletion would be sequenced on the DB thread, after this posted task is
+  // serviced.
+  std::vector<std::pair<ListIdentifier, V4Store*>> stores;
+  for (const auto& next_store : *store_map_) {
+    stores.push_back(std::make_pair(next_store.first, next_store.second.get()));
+  }
+
+  base::PostTaskAndReplyWithResult(
+      db_task_runner_.get(), FROM_HERE,
+      base::BindOnce(&VerifyChecksums, stores),
+      base::BindOnce(&V4Database::OnChecksumVerified,
+                     weak_factory_on_io_.GetWeakPtr(),
+                     std::move(db_ready_for_updates_callback)));
+}
+
+void V4Database::OnChecksumVerified(
+    DatabaseReadyForUpdatesCallback db_ready_for_updates_callback,
+    const std::vector<ListIdentifier>& stores_to_reset) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  std::move(db_ready_for_updates_callback).Run(stores_to_reset);
+}
+
+bool V4Database::IsStoreAvailable(const ListIdentifier& identifier) const {
+  const auto& store_pair = store_map_->find(identifier);
+  return (store_pair != store_map_->end()) &&
+         store_pair->second->HasValidData();
+}
+
+void V4Database::RecordFileSizeHistograms() {
+  int64_t db_size = 0;
+  for (const auto& store_map_iter : *store_map_) {
+    const int64_t size =
+        store_map_iter.second->RecordAndReturnFileSize(kV4DatabaseSizeMetric);
+    db_size += size;
+  }
+  const int64_t db_size_kilobytes = static_cast<int64_t>(db_size / 1024);
+  UMA_HISTOGRAM_COUNTS_1M(kV4DatabaseSizeMetric, db_size_kilobytes);
+}
+
+void V4Database::CollectDatabaseInfo(
+    DatabaseManagerInfo::DatabaseInfo* database_info) {
+  // Records the database size in bytes.
+  int64_t db_size = 0;
+
+  for (const auto& store_map_iter : *store_map_) {
+    DatabaseManagerInfo::DatabaseInfo::StoreInfo* store_info =
+        database_info->add_store_info();
+    store_map_iter.second->CollectStoreInfo(store_info, kV4DatabaseSizeMetric);
+    db_size += store_info->file_size_bytes();
+  }
+
+  database_info->set_database_size_bytes(db_size);
+}
+
+ListInfo::ListInfo(const bool fetch_updates,
+                   const std::string& filename,
+                   const ListIdentifier& list_id,
+                   const SBThreatType sb_threat_type)
+    : fetch_updates_(fetch_updates),
+      filename_(filename),
+      list_id_(list_id),
+      sb_threat_type_(sb_threat_type) {
+  DCHECK(!fetch_updates_ || !filename_.empty());
+  DCHECK_NE(SB_THREAT_TYPE_SAFE, sb_threat_type_);
+}
+
+ListInfo::~ListInfo() {}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/db/v4_database.h b/components/safe_browsing/core/db/v4_database.h
new file mode 100644
index 0000000..d013fc3
--- /dev/null
+++ b/components/safe_browsing/core/db/v4_database.h
@@ -0,0 +1,245 @@
+// 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 COMPONENTS_SAFE_BROWSING_CORE_DB_V4_DATABASE_H_
+#define COMPONENTS_SAFE_BROWSING_CORE_DB_V4_DATABASE_H_
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequenced_task_runner.h"
+#include "base/single_thread_task_runner.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/core/db/v4_store.h"
+#include "components/safe_browsing/core/proto/webui.pb.h"
+
+class TestSafeBrowsingDatabaseHelper;
+
+namespace safe_browsing {
+
+class V4Database;
+
+// Scheduled when the database has been read from disk and is ready to process
+// resource reputation requests.
+using NewDatabaseReadyCallback =
+    base::OnceCallback<void(std::unique_ptr<V4Database>)>;
+
+// Scheduled when the checksum for all the stores in the database has been
+// verified to match the expected value. Stores for which the checksum did not
+// match are passed as the argument and need to be reset.
+using DatabaseReadyForUpdatesCallback =
+    base::OnceCallback<void(const std::vector<ListIdentifier>&)>;
+
+// This callback is scheduled once the database has finished processing the
+// update requests for all stores and is ready to process the next set of update
+// requests.
+using DatabaseUpdatedCallback = base::RepeatingClosure;
+
+// Maps the ListIdentifiers to their corresponding in-memory stores, which
+// contain the hash prefixes for that ListIdentifier as well as manage their
+// storage on disk.
+using StoreMap = std::unordered_map<ListIdentifier, std::unique_ptr<V4Store>>;
+
+// Associates metadata for a list with its ListIdentifier.
+class ListInfo {
+ public:
+  ListInfo(const bool fetch_updates,
+           const std::string& filename,
+           const ListIdentifier& list_id,
+           const SBThreatType sb_threat_type);
+  ~ListInfo();
+
+  const ListIdentifier& list_id() const { return list_id_; }
+  const std::string& filename() const { return filename_; }
+  SBThreatType sb_threat_type() const { return sb_threat_type_; }
+  bool fetch_updates() const { return fetch_updates_; }
+
+ private:
+  // Whether to fetch and store updates for this list.
+  bool fetch_updates_;
+
+  // The ASCII name of the file on disk. This file is created inside the
+  // user-data directory. For instance, the ListIdentifier could be for URL
+  // expressions for UwS on Windows platform, and the corresponding file on disk
+  // could be named: "UrlUws.store"
+  std::string filename_;
+
+  // The list being read from/written to the disk.
+  ListIdentifier list_id_;
+
+  // The threat type enum value for this store.
+  SBThreatType sb_threat_type_;
+
+  ListInfo() = delete;
+};
+
+using ListInfos = std::vector<ListInfo>;
+
+// Factory for creating V4Database. Tests implement this factory to create fake
+// databases for testing.
+class V4DatabaseFactory {
+ public:
+  virtual ~V4DatabaseFactory() {}
+  virtual std::unique_ptr<V4Database> Create(
+      const scoped_refptr<base::SequencedTaskRunner>& db_task_runner,
+      std::unique_ptr<StoreMap> store_map);
+};
+
+// The on-disk databases are shared among all profiles, as it doesn't contain
+// user-specific data. This object is not thread-safe, i.e. all its methods
+// should be used on the same thread that it was created on, unless specified
+// otherwise.
+// The hash-prefixes of each type are managed by a V4Store (including saving to
+// and reading from disk).
+// The V4Database serves as a single place to manage all the V4Stores.
+class V4Database {
+ public:
+  // Factory method to create a V4Database. It creates the database on the
+  // provided |db_task_runner| containing stores in |store_file_name_map|. When
+  // the database creation is complete, it runs the NewDatabaseReadyCallback on
+  // the same thread as it was called.
+  static void Create(
+      const scoped_refptr<base::SequencedTaskRunner>& db_task_runner,
+      const base::FilePath& base_path,
+      const ListInfos& list_infos,
+      NewDatabaseReadyCallback new_db_callback);
+
+  // Destroys the provided v4_database on its task_runner since this may be a
+  // long operation.
+  static void Destroy(std::unique_ptr<V4Database> v4_database);
+
+  virtual ~V4Database();
+
+  // Updates the stores with the response received from the SafeBrowsing service
+  // and calls the db_updated_callback when done.
+  void ApplyUpdate(std::unique_ptr<ParsedServerResponse> parsed_server_response,
+                   DatabaseUpdatedCallback db_updated_callback);
+
+  // Returns the current state of each of the stores being managed.
+  std::unique_ptr<StoreStateMap> GetStoreStateMap();
+
+  // Check if all the selected stores are available and populated.
+  // Returns false if any of |stores_to_check| don't have valid data.
+  // A store may be unavailble if either it hasn't yet gotten a proper
+  // full-update (just after install, or corrupted/missing file), or if it's
+  // not supported in this build (i.e. Chromium).
+  virtual bool AreAllStoresAvailable(
+      const StoresToCheck& stores_to_check) const;
+
+  // Check if any of the stores are available and populated.
+  // Returns false if all of |stores_to_check| don't have valid data.
+  virtual bool AreAnyStoresAvailable(
+      const StoresToCheck& stores_to_check) const;
+
+  // Searches for a hash prefix matching the |full_hash| in stores in the
+  // database, filtered by |stores_to_check|, and returns the identifier of the
+  // store along with the matching hash prefix in |matched_hash_prefix_map|.
+  virtual void GetStoresMatchingFullHash(
+      const FullHash& full_hash,
+      const StoresToCheck& stores_to_check,
+      StoreAndHashPrefixes* matched_store_and_full_hashes);
+
+  // Resets the stores in |stores_to_reset| to an empty state. This is done if
+  // the checksum doesn't match the expected value.
+  void ResetStores(const std::vector<ListIdentifier>& stores_to_reset);
+
+  // Schedules verification of the checksum of each store read from disk on task
+  // runner. If the checksum doesn't match, that store is passed to the
+  // |db_ready_for_updates_callback|. At the end,
+  // |db_ready_for_updates_callback| is scheduled (on the same thread as it was
+  // called) to indicate that the database updates can now be scheduled.
+  void VerifyChecksum(
+      DatabaseReadyForUpdatesCallback db_ready_for_updates_callback);
+
+  // Records the size of each of the stores managed by this database, along
+  // with the combined size of all the stores.
+  void RecordFileSizeHistograms();
+
+  // Populates the DatabaseInfo message of the safe_browsing_page proto.
+  void CollectDatabaseInfo(DatabaseManagerInfo::DatabaseInfo* database_info);
+
+ protected:
+  V4Database(const scoped_refptr<base::SequencedTaskRunner>& db_task_runner,
+             std::unique_ptr<StoreMap> store_map);
+
+  // The collection of V4Stores, keyed by ListIdentifier.
+  // The map itself lives on the V4Database's parent thread, but its V4Store
+  // objects live on the db_task_runner_thread.
+  // TODO(vakh): Consider writing a container object which encapsulates or
+  // harmonizes thread affinity for the associative container and the data.
+  const std::unique_ptr<StoreMap> store_map_;
+
+ private:
+  friend class ::TestSafeBrowsingDatabaseHelper;
+  friend class V4DatabaseFactory;
+  friend class V4EmbeddedTestServerBrowserTest;
+  friend class V4DatabaseTest;
+  friend class V4SafeBrowsingServiceTest;
+  FRIEND_TEST_ALL_PREFIXES(V4DatabaseTest, TestSetupDatabaseWithFakeStores);
+  FRIEND_TEST_ALL_PREFIXES(V4DatabaseTest,
+                           TestSetupDatabaseWithFakeStoresFailsReset);
+  FRIEND_TEST_ALL_PREFIXES(V4DatabaseTest, TestApplyUpdateWithNewStates);
+  FRIEND_TEST_ALL_PREFIXES(V4DatabaseTest, TestApplyUpdateWithNoNewState);
+  FRIEND_TEST_ALL_PREFIXES(V4DatabaseTest, TestApplyUpdateWithEmptyUpdate);
+  FRIEND_TEST_ALL_PREFIXES(V4DatabaseTest, TestApplyUpdateWithInvalidUpdate);
+  FRIEND_TEST_ALL_PREFIXES(V4DatabaseTest, TestSomeStoresMatchFullHash);
+
+  // Factory method to create a V4Database. When the database creation is
+  // complete, it calls the NewDatabaseReadyCallback on |callback_task_runner|.
+  static void CreateOnTaskRunner(
+      const scoped_refptr<base::SequencedTaskRunner>& db_task_runner,
+      const base::FilePath& base_path,
+      const ListInfos& list_infos,
+      const scoped_refptr<base::SingleThreadTaskRunner>& callback_task_runner,
+      NewDatabaseReadyCallback callback);
+
+  // Makes the passed |factory| the factory used to instantiate a V4Database.
+  // Only for tests.
+  static void RegisterDatabaseFactoryForTest(
+      std::unique_ptr<V4DatabaseFactory> factory);
+
+  // Makes the passed |factory| the factory used to instantiate a V4Store. Only
+  // for tests.
+  static void RegisterStoreFactoryForTest(
+      std::unique_ptr<V4StoreFactory> factory);
+
+  // Callback called when a new store has been created and is ready to be used.
+  // This method updates the store_map_ to point to the new store, which causes
+  // the old store to get deleted.
+  void UpdatedStoreReady(ListIdentifier identifier,
+                         std::unique_ptr<V4Store> store);
+
+  // See |VerifyChecksum|.
+  void OnChecksumVerified(
+      DatabaseReadyForUpdatesCallback db_ready_for_updates_callback,
+      const std::vector<ListIdentifier>& stores_to_reset);
+
+  bool IsStoreAvailable(const ListIdentifier& identifier) const;
+
+  const scoped_refptr<base::SequencedTaskRunner> db_task_runner_;
+
+  DatabaseUpdatedCallback db_updated_callback_;
+
+  // The number of stores for which the update request is pending. When this
+  // goes down to 0, that indicates that the database has updated all the stores
+  // that needed updating and is ready for the next update. It should only be
+  // accessed on the IO thread.
+  int pending_store_updates_;
+
+  // Only meant to be dereferenced and invalidated on the IO thread and hence
+  // named. For details, see the comment at the top of weak_ptr.h
+  base::WeakPtrFactory<V4Database> weak_factory_on_io_{this};
+
+  DISALLOW_COPY_AND_ASSIGN(V4Database);
+};
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CORE_DB_V4_DATABASE_H_
diff --git a/components/safe_browsing/core/db/v4_database_unittest.cc b/components/safe_browsing/core/db/v4_database_unittest.cc
new file mode 100644
index 0000000..1638a1b
--- /dev/null
+++ b/components/safe_browsing/core/db/v4_database_unittest.cc
@@ -0,0 +1,548 @@
+// 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 <unordered_map>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/debug/leak_annotations.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/run_loop.h"
+#include "base/test/test_simple_task_runner.h"
+#include "components/safe_browsing/core/db/v4_database.h"
+#include "components/safe_browsing/core/db/v4_store.h"
+#include "content/public/test/browser_task_environment.h"
+#include "testing/platform_test.h"
+
+namespace safe_browsing {
+
+class FakeV4Store : public V4Store {
+ public:
+  FakeV4Store(const scoped_refptr<base::SequencedTaskRunner>& task_runner,
+              const base::FilePath& store_path,
+              const bool hash_prefix_matches)
+      : V4Store(
+            task_runner,
+            base::FilePath(store_path.value() + FILE_PATH_LITERAL(".store"))),
+        hash_prefix_should_match_(hash_prefix_matches) {}
+
+  HashPrefix GetMatchingHashPrefix(const FullHash& full_hash) override {
+    return hash_prefix_should_match_ ? full_hash : HashPrefix();
+  }
+
+  bool HasValidData() const override { return true; }
+
+  void set_hash_prefix_matches(bool hash_prefix_matches) {
+    hash_prefix_should_match_ = hash_prefix_matches;
+  }
+
+ private:
+  bool hash_prefix_should_match_;
+};
+
+// This factory creates a "fake" store. It allows the caller to specify whether
+// the store has a hash prefix matching a full hash. This is used to test the
+// |GetStoresMatchingFullHash()| method in |V4Database|.
+class FakeV4StoreFactory : public V4StoreFactory {
+ public:
+  explicit FakeV4StoreFactory(bool hash_prefix_matches)
+      : hash_prefix_should_match_(hash_prefix_matches) {}
+
+  std::unique_ptr<V4Store> CreateV4Store(
+      const scoped_refptr<base::SequencedTaskRunner>& task_runner,
+      const base::FilePath& store_path) override {
+    return std::make_unique<FakeV4Store>(task_runner, store_path,
+                                         hash_prefix_should_match_);
+  }
+
+ private:
+  const bool hash_prefix_should_match_;
+};
+
+class V4DatabaseTest : public PlatformTest {
+ public:
+  V4DatabaseTest()
+      : task_runner_(new base::TestSimpleTaskRunner),
+        linux_malware_id_(LINUX_PLATFORM, URL, MALWARE_THREAT),
+        win_malware_id_(WINDOWS_PLATFORM, URL, MALWARE_THREAT) {}
+
+  void SetUp() override {
+    PlatformTest::SetUp();
+
+    // Setup a database in a temporary directory.
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+    database_dirname_ = temp_dir_.GetPath().AppendASCII("V4DatabaseTest");
+
+    created_but_not_called_back_ = false;
+    created_and_called_back_ = false;
+    verify_checksum_called_back_ = false;
+
+    callback_db_updated_ = base::BindRepeating(&V4DatabaseTest::DatabaseUpdated,
+                                               base::Unretained(this));
+
+    callback_db_ready_ = base::BindOnce(
+        &V4DatabaseTest::NewDatabaseReadyWithExpectedStorePathsAndIds,
+        base::Unretained(this));
+
+    SetupInfoMapAndExpectedState();
+  }
+
+  void TearDown() override {
+    V4Database::RegisterStoreFactoryForTest(nullptr);
+    PlatformTest::TearDown();
+  }
+
+  void RegisterFactory(bool hash_prefix_matches = true) {
+    V4Database::RegisterStoreFactoryForTest(
+        std::make_unique<FakeV4StoreFactory>(hash_prefix_matches));
+  }
+
+  void SetupInfoMapAndExpectedState() {
+    list_infos_.emplace_back(true, "win_url_malware", win_malware_id_,
+                             SB_THREAT_TYPE_URL_MALWARE);
+    expected_identifiers_.push_back(win_malware_id_);
+    expected_store_paths_.push_back(
+        database_dirname_.AppendASCII("win_url_malware.store"));
+
+    list_infos_.emplace_back(true, "linux_url_malware", linux_malware_id_,
+                             SB_THREAT_TYPE_URL_MALWARE);
+    expected_identifiers_.push_back(linux_malware_id_);
+    expected_store_paths_.push_back(
+        database_dirname_.AppendASCII("linux_url_malware.store"));
+  }
+
+  void DatabaseUpdated() {}
+
+  void NewDatabaseReadyWithExpectedStorePathsAndIds(
+      std::unique_ptr<V4Database> v4_database) {
+    ASSERT_TRUE(v4_database);
+    ASSERT_TRUE(v4_database->store_map_);
+
+    // The following check ensures that the callback was called asynchronously.
+    EXPECT_TRUE(created_but_not_called_back_);
+
+    ASSERT_EQ(expected_store_paths_.size(), v4_database->store_map_->size());
+    ASSERT_EQ(expected_identifiers_.size(), v4_database->store_map_->size());
+    for (size_t i = 0; i < expected_identifiers_.size(); i++) {
+      const auto& expected_identifier = expected_identifiers_[i];
+      const auto& store = (*v4_database->store_map_)[expected_identifier];
+      ASSERT_TRUE(store);
+      const auto& expected_store_path = expected_store_paths_[i];
+      EXPECT_EQ(expected_store_path, store->store_path());
+    }
+
+    EXPECT_FALSE(created_and_called_back_);
+    created_and_called_back_ = true;
+
+    v4_database_ = std::move(v4_database);
+  }
+
+  std::unique_ptr<ParsedServerResponse> CreateFakeServerResponse(
+      StoreStateMap store_state_map,
+      bool use_valid_response_type) {
+    auto parsed_server_response = std::make_unique<ParsedServerResponse>();
+    for (const auto& store_state_iter : store_state_map) {
+      ListIdentifier identifier = store_state_iter.first;
+      auto lur = std::make_unique<ListUpdateResponse>();
+      lur->set_platform_type(identifier.platform_type());
+      lur->set_threat_entry_type(identifier.threat_entry_type());
+      lur->set_threat_type(identifier.threat_type());
+      lur->set_new_client_state(store_state_iter.second);
+      if (use_valid_response_type) {
+        lur->set_response_type(ListUpdateResponse::FULL_UPDATE);
+      } else {
+        lur->set_response_type(ListUpdateResponse::RESPONSE_TYPE_UNSPECIFIED);
+      }
+      parsed_server_response->push_back(std::move(lur));
+    }
+    return parsed_server_response;
+  }
+
+  void VerifyExpectedStoresState(bool expect_new_stores) {
+    const StoreMap* new_store_map = v4_database_->store_map_.get();
+    std::unique_ptr<StoreStateMap> new_store_state_map =
+        v4_database_->GetStoreStateMap();
+    EXPECT_EQ(expected_store_state_map_.size(), new_store_map->size());
+    EXPECT_EQ(expected_store_state_map_.size(), new_store_state_map->size());
+    for (const auto& expected_iter : expected_store_state_map_) {
+      const ListIdentifier& identifier = expected_iter.first;
+      const std::string& state = expected_iter.second;
+      ASSERT_EQ(1u, new_store_map->count(identifier));
+      ASSERT_EQ(1u, new_store_state_map->count(identifier));
+
+      // Verify the expected state in the store map and the state map.
+      EXPECT_EQ(state, new_store_map->at(identifier)->state());
+      EXPECT_EQ(state, new_store_state_map->at(identifier));
+
+      if (expect_new_stores) {
+        // Verify that a new store was created.
+        EXPECT_NE(old_stores_map_.at(identifier),
+                  new_store_map->at(identifier).get());
+      } else {
+        // Verify that NO new store was created.
+        EXPECT_EQ(old_stores_map_.at(identifier),
+                  new_store_map->at(identifier).get());
+      }
+    }
+  }
+
+  void VerifyChecksumCallback(const std::vector<ListIdentifier>& stores) {
+    EXPECT_FALSE(verify_checksum_called_back_);
+    verify_checksum_called_back_ = true;
+  }
+
+  void WaitForTasksOnTaskRunner() {
+    task_runner_->RunPendingTasks();
+    base::RunLoop().RunUntilIdle();
+  }
+
+  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+  std::unique_ptr<V4Database> v4_database_;
+  base::FilePath database_dirname_;
+  base::ScopedTempDir temp_dir_;
+  content::BrowserTaskEnvironment task_environment_;
+  bool created_but_not_called_back_;
+  bool created_and_called_back_;
+  bool verify_checksum_called_back_;
+  ListInfos list_infos_;
+  std::vector<ListIdentifier> expected_identifiers_;
+  std::vector<base::FilePath> expected_store_paths_;
+  DatabaseUpdatedCallback callback_db_updated_;
+  NewDatabaseReadyCallback callback_db_ready_;
+  StoreStateMap expected_store_state_map_;
+  std::unordered_map<ListIdentifier, V4Store*> old_stores_map_;
+  const ListIdentifier linux_malware_id_, win_malware_id_;
+};
+
+// Test to set up the database with fake stores.
+TEST_F(V4DatabaseTest, TestSetupDatabaseWithFakeStores) {
+  RegisterFactory();
+
+  V4Database::Create(task_runner_, database_dirname_, list_infos_,
+                     std::move(callback_db_ready_));
+  created_but_not_called_back_ = true;
+  WaitForTasksOnTaskRunner();
+  EXPECT_EQ(true, created_and_called_back_);
+}
+
+// Test to check database updates as expected.
+TEST_F(V4DatabaseTest, TestApplyUpdateWithNewStates) {
+  RegisterFactory();
+
+  V4Database::Create(task_runner_, database_dirname_, list_infos_,
+                     std::move(callback_db_ready_));
+  created_but_not_called_back_ = true;
+  WaitForTasksOnTaskRunner();
+
+  // The database has now been created. Time to try to update it.
+  EXPECT_TRUE(v4_database_);
+  const StoreMap* db_stores = v4_database_->store_map_.get();
+  EXPECT_EQ(expected_store_paths_.size(), db_stores->size());
+  for (const auto& store_iter : *db_stores) {
+    V4Store* store = store_iter.second.get();
+    expected_store_state_map_[store_iter.first] = store->state() + "_fake";
+    old_stores_map_[store_iter.first] = store;
+  }
+
+  v4_database_->ApplyUpdate(
+      CreateFakeServerResponse(expected_store_state_map_, true),
+      callback_db_updated_);
+
+  // Wait for the ApplyUpdate callback to get called.
+  WaitForTasksOnTaskRunner();
+
+  VerifyExpectedStoresState(true);
+
+  // Wait for the old stores to get destroyed on task runner.
+  WaitForTasksOnTaskRunner();
+}
+
+// Test to ensure no state updates leads to no store updates.
+TEST_F(V4DatabaseTest, TestApplyUpdateWithNoNewState) {
+  RegisterFactory();
+
+  V4Database::Create(task_runner_, database_dirname_, list_infos_,
+                     std::move(callback_db_ready_));
+  created_but_not_called_back_ = true;
+  WaitForTasksOnTaskRunner();
+
+  // The database has now been created. Time to try to update it.
+  EXPECT_TRUE(v4_database_);
+  const StoreMap* db_stores = v4_database_->store_map_.get();
+  EXPECT_EQ(expected_store_paths_.size(), db_stores->size());
+  for (const auto& store_iter : *db_stores) {
+    V4Store* store = store_iter.second.get();
+    expected_store_state_map_[store_iter.first] = store->state();
+    old_stores_map_[store_iter.first] = store;
+  }
+
+  v4_database_->ApplyUpdate(
+      CreateFakeServerResponse(expected_store_state_map_, true),
+      callback_db_updated_);
+
+  WaitForTasksOnTaskRunner();
+
+  VerifyExpectedStoresState(false);
+}
+
+// Test to ensure no updates leads to no store updates.
+TEST_F(V4DatabaseTest, TestApplyUpdateWithEmptyUpdate) {
+  RegisterFactory();
+
+  V4Database::Create(task_runner_, database_dirname_, list_infos_,
+                     std::move(callback_db_ready_));
+  created_but_not_called_back_ = true;
+  WaitForTasksOnTaskRunner();
+
+  // The database has now been created. Time to try to update it.
+  EXPECT_TRUE(v4_database_);
+  const StoreMap* db_stores = v4_database_->store_map_.get();
+  EXPECT_EQ(expected_store_paths_.size(), db_stores->size());
+  for (const auto& store_iter : *db_stores) {
+    V4Store* store = store_iter.second.get();
+    expected_store_state_map_[store_iter.first] = store->state();
+    old_stores_map_[store_iter.first] = store;
+  }
+
+  auto parsed_server_response = std::make_unique<ParsedServerResponse>();
+  v4_database_->ApplyUpdate(std::move(parsed_server_response),
+                            callback_db_updated_);
+
+  WaitForTasksOnTaskRunner();
+
+  VerifyExpectedStoresState(false);
+}
+
+// Test to ensure invalid update leads to no store changes.
+TEST_F(V4DatabaseTest, TestApplyUpdateWithInvalidUpdate) {
+  RegisterFactory();
+
+  V4Database::Create(task_runner_, database_dirname_, list_infos_,
+                     std::move(callback_db_ready_));
+  created_but_not_called_back_ = true;
+  WaitForTasksOnTaskRunner();
+
+  // The database has now been created. Time to try to update it.
+  EXPECT_TRUE(v4_database_);
+  const StoreMap* db_stores = v4_database_->store_map_.get();
+  EXPECT_EQ(expected_store_paths_.size(), db_stores->size());
+  for (const auto& store_iter : *db_stores) {
+    V4Store* store = store_iter.second.get();
+    expected_store_state_map_[store_iter.first] = store->state();
+    old_stores_map_[store_iter.first] = store;
+  }
+
+  v4_database_->ApplyUpdate(
+      CreateFakeServerResponse(expected_store_state_map_, false),
+      callback_db_updated_);
+  WaitForTasksOnTaskRunner();
+
+  VerifyExpectedStoresState(false);
+}
+
+// Test to ensure the case that all stores match a given full hash.
+TEST_F(V4DatabaseTest, TestAllStoresMatchFullHash) {
+  bool hash_prefix_matches = true;
+  RegisterFactory(hash_prefix_matches);
+
+  V4Database::Create(task_runner_, database_dirname_, list_infos_,
+                     std::move(callback_db_ready_));
+  created_but_not_called_back_ = true;
+  WaitForTasksOnTaskRunner();
+  EXPECT_EQ(true, created_and_called_back_);
+
+  StoresToCheck stores_to_check({linux_malware_id_, win_malware_id_});
+  StoreAndHashPrefixes store_and_hash_prefixes;
+  v4_database_->GetStoresMatchingFullHash("anything", stores_to_check,
+                                          &store_and_hash_prefixes);
+  EXPECT_EQ(2u, store_and_hash_prefixes.size());
+  StoresToCheck stores_found;
+  for (const auto& it : store_and_hash_prefixes) {
+    stores_found.insert(it.list_id);
+  }
+  EXPECT_EQ(stores_to_check, stores_found);
+}
+
+// Test to ensure the case that no stores match a given full hash.
+TEST_F(V4DatabaseTest, TestNoStoreMatchesFullHash) {
+  bool hash_prefix_matches = false;
+  RegisterFactory(hash_prefix_matches);
+
+  V4Database::Create(task_runner_, database_dirname_, list_infos_,
+                     std::move(callback_db_ready_));
+  created_but_not_called_back_ = true;
+  WaitForTasksOnTaskRunner();
+  EXPECT_EQ(true, created_and_called_back_);
+
+  StoreAndHashPrefixes store_and_hash_prefixes;
+  v4_database_->GetStoresMatchingFullHash(
+      "anything", StoresToCheck({linux_malware_id_, win_malware_id_}),
+      &store_and_hash_prefixes);
+  EXPECT_TRUE(store_and_hash_prefixes.empty());
+}
+
+// Test to ensure the case that some stores match a given full hash.
+TEST_F(V4DatabaseTest, TestSomeStoresMatchFullHash) {
+  // Setup stores to not match the full hash.
+  bool hash_prefix_matches = false;
+  RegisterFactory(hash_prefix_matches);
+
+  V4Database::Create(task_runner_, database_dirname_, list_infos_,
+                     std::move(callback_db_ready_));
+  created_but_not_called_back_ = true;
+  WaitForTasksOnTaskRunner();
+  EXPECT_EQ(true, created_and_called_back_);
+
+  // Set the store corresponding to linux_malware_id_ to match the full hash.
+  FakeV4Store* store = static_cast<FakeV4Store*>(
+      v4_database_->store_map_->at(win_malware_id_).get());
+  store->set_hash_prefix_matches(true);
+
+  StoreAndHashPrefixes store_and_hash_prefixes;
+  v4_database_->GetStoresMatchingFullHash(
+      "anything", StoresToCheck({linux_malware_id_, win_malware_id_}),
+      &store_and_hash_prefixes);
+  EXPECT_EQ(1u, store_and_hash_prefixes.size());
+  EXPECT_EQ(store_and_hash_prefixes.begin()->list_id, win_malware_id_);
+  EXPECT_FALSE(store_and_hash_prefixes.begin()->hash_prefix.empty());
+}
+
+// Test to ensure the case that only some stores are reported to match a given
+// full hash because of StoresToCheck.
+TEST_F(V4DatabaseTest, TestSomeStoresMatchFullHashBecauseOfStoresToMatch) {
+  // Setup all stores to match the full hash.
+  bool hash_prefix_matches = true;
+  RegisterFactory(hash_prefix_matches);
+
+  V4Database::Create(task_runner_, database_dirname_, list_infos_,
+                     std::move(callback_db_ready_));
+  created_but_not_called_back_ = true;
+  WaitForTasksOnTaskRunner();
+  EXPECT_EQ(true, created_and_called_back_);
+
+  // Don't add win_malware_id_ to the StoresToCheck.
+  StoreAndHashPrefixes store_and_hash_prefixes;
+  v4_database_->GetStoresMatchingFullHash(
+      "anything", StoresToCheck({linux_malware_id_}), &store_and_hash_prefixes);
+  EXPECT_EQ(1u, store_and_hash_prefixes.size());
+  EXPECT_EQ(store_and_hash_prefixes.begin()->list_id, linux_malware_id_);
+  EXPECT_FALSE(store_and_hash_prefixes.begin()->hash_prefix.empty());
+}
+
+TEST_F(V4DatabaseTest, VerifyChecksumCalledAsync) {
+  bool hash_prefix_matches = true;
+  RegisterFactory(hash_prefix_matches);
+
+  V4Database::Create(task_runner_, database_dirname_, list_infos_,
+                     std::move(callback_db_ready_));
+  created_but_not_called_back_ = true;
+  WaitForTasksOnTaskRunner();
+  EXPECT_EQ(true, created_and_called_back_);
+
+  // verify_checksum_called_back_ set to false in the constructor.
+  EXPECT_FALSE(verify_checksum_called_back_);
+  // Now call VerifyChecksum and pass the callback that sets
+  // verify_checksum_called_back_ to true.
+  v4_database_->VerifyChecksum(base::BindOnce(
+      &V4DatabaseTest::VerifyChecksumCallback, base::Unretained(this)));
+  // verify_checksum_called_back_ should still be false since the checksum
+  // verification is async.
+  EXPECT_FALSE(verify_checksum_called_back_);
+  WaitForTasksOnTaskRunner();
+  EXPECT_TRUE(verify_checksum_called_back_);
+}
+
+TEST_F(V4DatabaseTest, VerifyChecksumCancelled) {
+  bool hash_prefix_matches = true;
+  RegisterFactory(hash_prefix_matches);
+
+  V4Database::Create(task_runner_, database_dirname_, list_infos_,
+                     std::move(callback_db_ready_));
+  created_but_not_called_back_ = true;
+  WaitForTasksOnTaskRunner();
+  EXPECT_EQ(true, created_and_called_back_);
+
+  EXPECT_FALSE(verify_checksum_called_back_);
+  v4_database_->VerifyChecksum(base::BindOnce(
+      &V4DatabaseTest::VerifyChecksumCallback, base::Unretained(this)));
+  EXPECT_FALSE(verify_checksum_called_back_);
+  // Destroy database.
+  V4Database::Destroy(std::move(v4_database_));
+  WaitForTasksOnTaskRunner();
+  // Callback should not be called since database is destroyed.
+  EXPECT_FALSE(verify_checksum_called_back_);
+}
+
+// Test that we can properly check for unsupported stores
+TEST_F(V4DatabaseTest, TestStoresAvailable) {
+  bool hash_prefix_matches = false;
+  RegisterFactory(hash_prefix_matches);
+
+  V4Database::Create(task_runner_, database_dirname_, list_infos_,
+                     std::move(callback_db_ready_));
+  created_but_not_called_back_ = true;
+  WaitForTasksOnTaskRunner();
+  EXPECT_EQ(true, created_and_called_back_);
+
+  // Doesn't exist in out list
+  const ListIdentifier bogus_id(LINUX_PLATFORM, CHROME_EXTENSION,
+                                CSD_WHITELIST);
+
+  EXPECT_TRUE(v4_database_->AreAllStoresAvailable(
+      StoresToCheck({linux_malware_id_, win_malware_id_})));
+  EXPECT_TRUE(v4_database_->AreAnyStoresAvailable(
+      StoresToCheck({linux_malware_id_, win_malware_id_})));
+
+  EXPECT_TRUE(
+      v4_database_->AreAllStoresAvailable(StoresToCheck({linux_malware_id_})));
+  EXPECT_TRUE(
+      v4_database_->AreAnyStoresAvailable(StoresToCheck({linux_malware_id_})));
+
+  EXPECT_FALSE(v4_database_->AreAllStoresAvailable(
+      StoresToCheck({linux_malware_id_, bogus_id})));
+  EXPECT_TRUE(v4_database_->AreAnyStoresAvailable(
+      StoresToCheck({linux_malware_id_, bogus_id})));
+
+  EXPECT_FALSE(v4_database_->AreAllStoresAvailable(StoresToCheck({bogus_id})));
+}
+
+// Test to ensure that the callback to the database is dropped when the database
+// gets destroyed. See http://crbug.com/683147#c5 for more details.
+TEST_F(V4DatabaseTest, UsingWeakPtrDropsCallback) {
+  RegisterFactory();
+
+  // Step 1: Create the database.
+  V4Database::Create(task_runner_, database_dirname_, list_infos_,
+                     std::move(callback_db_ready_));
+  created_but_not_called_back_ = true;
+  WaitForTasksOnTaskRunner();
+
+  // Step 2: Try to update the database. This posts V4Store::ApplyUpdate on the
+  // task runner.
+  auto parsed_server_response = std::make_unique<ParsedServerResponse>();
+  auto lur = std::make_unique<ListUpdateResponse>();
+  lur->set_platform_type(linux_malware_id_.platform_type());
+  lur->set_threat_entry_type(linux_malware_id_.threat_entry_type());
+  lur->set_threat_type(linux_malware_id_.threat_type());
+  lur->set_new_client_state("new_state");
+  lur->set_response_type(ListUpdateResponse::FULL_UPDATE);
+  parsed_server_response->push_back(std::move(lur));
+
+  // We pass |null_callback| as the second argument to |ApplyUpdate| since we
+  // expect it to not get called. This callback method is called from
+  // V4Database::UpdatedStoreReady but we expect that call to get dropped.
+  v4_database_->ApplyUpdate(std::move(parsed_server_response),
+                            base::NullCallback());
+
+  // Step 3: Before V4Store::ApplyUpdate gets executed on the task runner,
+  // destroy the database. This posts ~V4Database() on the task runner.
+  V4Database::Destroy(std::move(v4_database_));
+
+  // Step 4: Wait for the task runner to go to completion. The test should
+  // finish to completion and the |null_callback| should not get called.
+  WaitForTasksOnTaskRunner();
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/db/v4_embedded_test_server_util.cc b/components/safe_browsing/core/db/v4_embedded_test_server_util.cc
new file mode 100644
index 0000000..348c3b38
--- /dev/null
+++ b/components/safe_browsing/core/db/v4_embedded_test_server_util.cc
@@ -0,0 +1,115 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/core/db/v4_embedded_test_server_util.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/base64.h"
+#include "base/base64url.h"
+#include "base/bind.h"
+#include "base/logging.h"
+#include "components/safe_browsing/core/db/util.h"
+#include "components/safe_browsing/core/db/v4_test_util.h"
+#include "net/base/url_util.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "net/test/embedded_test_server/http_request.h"
+#include "net/test/embedded_test_server/http_response.h"
+#include "net/test/embedded_test_server/request_handler_util.h"
+
+namespace safe_browsing {
+
+namespace {
+
+// This method parses a request URL and returns a vector of HashPrefixes that
+// were being requested. It does this by:
+// 1. Finding the "req" query param.
+// 2. Base64 decoding it.
+// 3. Parsing the FindFullHashesRequest from the decoded string.
+std::vector<HashPrefix> GetPrefixesForRequest(const GURL& url) {
+  // Find the "req" query param.
+  std::string req;
+  bool success = net::GetValueForKeyInQuery(url, "$req", &req);
+  DCHECK(success) << "Requests to fullHashes:find should include the req param";
+
+  // Base64 decode it.
+  std::string decoded_output;
+  success = base::Base64UrlDecode(
+      req, base::Base64UrlDecodePolicy::REQUIRE_PADDING, &decoded_output);
+  DCHECK(success);
+
+  // Parse the FindFullHashRequest from the decoded output.
+  FindFullHashesRequest full_hash_req;
+  success = full_hash_req.ParseFromString(decoded_output);
+  DCHECK(success);
+
+  // Extract HashPrefixes from the request proto.
+  const ThreatInfo& info = full_hash_req.threat_info();
+  std::vector<HashPrefix> prefixes;
+  for (int i = 0; i < info.threat_entries_size(); ++i) {
+    prefixes.push_back(info.threat_entries(i).hash());
+  }
+  return prefixes;
+}
+
+// This function listens for requests to /v4/fullHashes:find, and responds with
+// predetermined responses.
+std::unique_ptr<net::test_server::HttpResponse> HandleFullHashRequest(
+    const std::map<GURL, ThreatMatch>& response_map,
+    const std::map<GURL, base::TimeDelta>& delay_map,
+    const net::test_server::HttpRequest& request) {
+  if (!(net::test_server::ShouldHandle(request, "/v4/fullHashes:find")))
+    return nullptr;
+  FindFullHashesResponse find_full_hashes_response;
+  find_full_hashes_response.mutable_negative_cache_duration()->set_seconds(600);
+
+  // Mock a response based on |response_map| and the prefixes scraped from the
+  // request URL.
+  //
+  // This loops through all prefixes requested, and finds all of the full hashes
+  // that match the prefix.
+  std::vector<HashPrefix> request_prefixes =
+      GetPrefixesForRequest(request.GetURL());
+  const base::TimeDelta* delay = nullptr;
+  for (const HashPrefix& prefix : request_prefixes) {
+    for (const auto& response : response_map) {
+      FullHash full_hash = V4ProtocolManagerUtil::GetFullHash(response.first);
+      if (V4ProtocolManagerUtil::FullHashMatchesHashPrefix(full_hash, prefix)) {
+        ThreatMatch* match = find_full_hashes_response.add_matches();
+        *match = response.second;
+        auto it = delay_map.find(response.first);
+        if (it != delay_map.end()) {
+          delay = &(it->second);
+        }
+      }
+    }
+  }
+
+  std::string serialized_response;
+  find_full_hashes_response.SerializeToString(&serialized_response);
+
+  auto http_response =
+      (delay ? std::make_unique<net::test_server::DelayedHttpResponse>(*delay)
+             : std::make_unique<net::test_server::BasicHttpResponse>());
+  http_response->set_content(serialized_response);
+  return http_response;
+}
+
+}  // namespace
+
+void StartRedirectingV4RequestsForTesting(
+    const std::map<GURL, ThreatMatch>& response_map,
+    net::test_server::EmbeddedTestServer* embedded_test_server,
+    const std::map<GURL, base::TimeDelta>& delay_map) {
+  // Static so accessing the underlying buffer won't cause use-after-free.
+  static std::string url_prefix;
+  url_prefix = embedded_test_server->GetURL("/v4").spec();
+  SetSbV4UrlPrefixForTesting(url_prefix.c_str());
+  embedded_test_server->RegisterRequestHandler(
+      base::BindRepeating(&HandleFullHashRequest, response_map, delay_map));
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/db/v4_embedded_test_server_util.h b/components/safe_browsing/core/db/v4_embedded_test_server_util.h
new file mode 100644
index 0000000..2749b8a
--- /dev/null
+++ b/components/safe_browsing/core/db/v4_embedded_test_server_util.h
@@ -0,0 +1,34 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CORE_DB_V4_EMBEDDED_TEST_SERVER_UTIL_H_
+#define COMPONENTS_SAFE_BROWSING_CORE_DB_V4_EMBEDDED_TEST_SERVER_UTIL_H_
+
+#include <map>
+
+#include "base/time/time.h"
+#include "components/safe_browsing/core/db/safebrowsing.pb.h"
+#include "url/gurl.h"
+
+namespace net {
+namespace test_server {
+class EmbeddedTestServer;
+}
+}  // namespace net
+
+namespace safe_browsing {
+
+// This method does three things:
+// 1. Rewrites the global V4 server URL prefix to point to the test server.
+// 2. Registers the FullHash request handler with the server.
+// 3. (Optionally) associates some delay with the resulting http response.
+void StartRedirectingV4RequestsForTesting(
+    const std::map<GURL, ThreatMatch>& response_map,
+    net::test_server::EmbeddedTestServer* embedded_test_server,
+    const std::map<GURL, base::TimeDelta>& delay_map =
+        std::map<GURL, base::TimeDelta>());
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CORE_DB_V4_EMBEDDED_TEST_SERVER_UTIL_H_
diff --git a/components/safe_browsing/core/db/v4_get_hash_protocol_manager.cc b/components/safe_browsing/core/db/v4_get_hash_protocol_manager.cc
new file mode 100644
index 0000000..4edcbf5b2
--- /dev/null
+++ b/components/safe_browsing/core/db/v4_get_hash_protocol_manager.cc
@@ -0,0 +1,872 @@
+// 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 "components/safe_browsing/core/db/v4_get_hash_protocol_manager.h"
+
+#include <utility>
+
+#include "base/base64url.h"
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/stl_util.h"
+#include "base/strings/string_split.h"
+#include "base/timer/timer.h"
+#include "base/trace_event/trace_event.h"
+#include "base/trace_event/traced_value.h"
+#include "content/public/browser/browser_thread.h"
+#include "net/base/load_flags.h"
+#include "net/http/http_response_headers.h"
+#include "net/http/http_status_code.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/simple_url_loader.h"
+
+using base::Time;
+using base::TimeDelta;
+using content::BrowserThread;
+
+namespace {
+
+// Record a GetHash result.
+void RecordGetHashResult(safe_browsing::V4OperationResult result) {
+  UMA_HISTOGRAM_ENUMERATION(
+      "SafeBrowsing.V4GetHash.Result", result,
+      safe_browsing::V4OperationResult::OPERATION_RESULT_MAX);
+}
+
+// Enumerate parsing failures for histogramming purposes.  DO NOT CHANGE
+// THE ORDERING OF THESE VALUES.
+enum ParseResultType {
+  // Error parsing the protocol buffer from a string.
+  PARSE_FROM_STRING_ERROR = 0,
+
+  // A match in the response had an unexpected THREAT_ENTRY_TYPE.
+  UNEXPECTED_THREAT_ENTRY_TYPE_ERROR = 1,
+
+  // A match in the response had an unexpected THREAT_TYPE.
+  UNEXPECTED_THREAT_TYPE_ERROR = 2,
+
+  // A match in the response had an unexpected PLATFORM_TYPE.
+  UNEXPECTED_PLATFORM_TYPE_ERROR = 3,
+
+  // A match in the response contained no metadata where metadata was
+  // expected.
+  NO_METADATA_ERROR = 4,
+
+  // A match in the response contained a ThreatType that was inconsistent
+  // with the other matches.
+  INCONSISTENT_THREAT_TYPE_ERROR = 5,
+
+  // A match in the response contained a metadata, but the metadata is invalid.
+  UNEXPECTED_METADATA_VALUE_ERROR = 6,
+
+  // A match in the response had no information in the threat field.
+  NO_THREAT_ERROR = 7,
+
+  // Memory space for histograms is determined by the max.  ALWAYS
+  // ADD NEW VALUES BEFORE THIS ONE.
+  PARSE_RESULT_TYPE_MAX = 8,
+};
+
+// Record parsing errors of a GetHash result.
+void RecordParseGetHashResult(ParseResultType result_type) {
+  UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.V4GetHash.Parse.Result", result_type,
+                            PARSE_RESULT_TYPE_MAX);
+}
+
+// Enumerate full hash cache hits/misses for histogramming purposes.
+// DO NOT CHANGE THE ORDERING OF THESE VALUES.
+enum V4FullHashCacheResultType {
+  // Full hashes for which there is no cache hit.
+  FULL_HASH_CACHE_MISS = 0,
+
+  // Full hashes with a cache hit.
+  FULL_HASH_CACHE_HIT = 1,
+
+  // Full hashes with a negative cache hit.
+  FULL_HASH_NEGATIVE_CACHE_HIT = 2,
+
+  // Memory space for histograms is determined by the max. ALWAYS
+  // ADD NEW VALUES BEFORE THIS ONE.
+  FULL_HASH_CACHE_RESULT_MAX
+};
+
+// Record a full hash cache hit result.
+void RecordV4FullHashCacheResult(V4FullHashCacheResultType result_type) {
+  UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.V4GetHash.CacheHit.Result",
+                            result_type, FULL_HASH_CACHE_RESULT_MAX);
+}
+
+// Enumerate GetHash hits/misses for histogramming purposes. DO NOT CHANGE THE
+// ORDERING OF THESE VALUES.
+enum V4GetHashCheckResultType {
+  // Successful responses which returned no full hashes.
+  GET_HASH_CHECK_EMPTY = 0,
+
+  // Successful responses for which one or more of the full hashes matched.
+  GET_HASH_CHECK_HIT = 1,
+
+  // Successful responses which weren't empty but have no matches.
+  GET_HASH_CHECK_MISS = 2,
+
+  // Memory space for histograms is determined by the max. ALWAYS
+  // ADD NEW VALUES BEFORE THIS ONE.
+  GET_HASH_CHECK_RESULT_MAX
+};
+
+// Record a GetHash hit result.
+void RecordV4GetHashCheckResult(V4GetHashCheckResultType result_type) {
+  UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.V4GetHash.Check.Result", result_type,
+                            GET_HASH_CHECK_RESULT_MAX);
+}
+
+const char kPermission[] = "permission";
+const char kPhaPatternType[] = "pha_pattern_type";
+const char kMalwareThreatType[] = "malware_threat_type";
+const char kSePatternType[] = "se_pattern_type";
+const char kLanding[] = "LANDING";
+const char kDistribution[] = "DISTRIBUTION";
+const char kSocialEngineeringAds[] = "SOCIAL_ENGINEERING_ADS";
+const char kSocialEngineeringLanding[] = "SOCIAL_ENGINEERING_LANDING";
+const char kPhishing[] = "PHISHING";
+
+}  // namespace
+
+namespace safe_browsing {
+
+// The default V4GetHashProtocolManagerFactory.
+class V4GetHashProtocolManagerFactoryImpl
+    : public V4GetHashProtocolManagerFactory {
+ public:
+  V4GetHashProtocolManagerFactoryImpl() {}
+  ~V4GetHashProtocolManagerFactoryImpl() override {}
+  std::unique_ptr<V4GetHashProtocolManager> CreateProtocolManager(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      const StoresToCheck& stores_to_check,
+      const V4ProtocolConfig& config) override {
+    return base::WrapUnique(new V4GetHashProtocolManager(
+        url_loader_factory, stores_to_check, config));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(V4GetHashProtocolManagerFactoryImpl);
+};
+
+// ----------------------------------------------------------------
+
+CachedHashPrefixInfo::CachedHashPrefixInfo() {}
+
+CachedHashPrefixInfo::CachedHashPrefixInfo(const CachedHashPrefixInfo& other) =
+    default;
+
+CachedHashPrefixInfo::~CachedHashPrefixInfo() {}
+
+// ----------------------------------------------------------------
+
+FullHashCallbackInfo::FullHashCallbackInfo() {}
+
+FullHashCallbackInfo::FullHashCallbackInfo(
+    const std::vector<FullHashInfo>& cached_full_hash_infos,
+    const std::vector<HashPrefix>& prefixes_requested,
+    std::unique_ptr<network::SimpleURLLoader> loader,
+    const FullHashToStoreAndHashPrefixesMap&
+        full_hash_to_store_and_hash_prefixes,
+    FullHashCallback callback,
+    const base::Time& network_start_time)
+    : cached_full_hash_infos(cached_full_hash_infos),
+      callback(std::move(callback)),
+      loader(std::move(loader)),
+      full_hash_to_store_and_hash_prefixes(
+          full_hash_to_store_and_hash_prefixes),
+      network_start_time(network_start_time),
+      prefixes_requested(prefixes_requested) {}
+
+FullHashCallbackInfo::~FullHashCallbackInfo() {}
+
+// ----------------------------------------------------------------
+
+FullHashInfo::FullHashInfo(const FullHash& full_hash,
+                           const ListIdentifier& list_id,
+                           const base::Time& positive_expiry)
+    : full_hash(full_hash),
+      list_id(list_id),
+      positive_expiry(positive_expiry) {}
+
+FullHashInfo::FullHashInfo(const FullHashInfo& other) = default;
+
+FullHashInfo::~FullHashInfo() {}
+
+bool FullHashInfo::operator==(const FullHashInfo& other) const {
+  return full_hash == other.full_hash && list_id == other.list_id &&
+         positive_expiry == other.positive_expiry && metadata == other.metadata;
+}
+
+bool FullHashInfo::operator!=(const FullHashInfo& other) const {
+  return !operator==(other);
+}
+
+// V4GetHashProtocolManager implementation --------------------------------
+
+// static
+V4GetHashProtocolManagerFactory* V4GetHashProtocolManager::factory_ = nullptr;
+
+// static
+std::unique_ptr<V4GetHashProtocolManager> V4GetHashProtocolManager::Create(
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    const StoresToCheck& stores_to_check,
+    const V4ProtocolConfig& config) {
+  if (!factory_)
+    factory_ = new V4GetHashProtocolManagerFactoryImpl();
+  return factory_->CreateProtocolManager(url_loader_factory, stores_to_check,
+                                         config);
+}
+
+// static
+void V4GetHashProtocolManager::RegisterFactory(
+    std::unique_ptr<V4GetHashProtocolManagerFactory> factory) {
+  delete factory_;
+  factory_ = factory.release();
+}
+
+V4GetHashProtocolManager::V4GetHashProtocolManager(
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    const StoresToCheck& stores_to_check,
+    const V4ProtocolConfig& config)
+    : gethash_error_count_(0),
+      gethash_back_off_mult_(1),
+      next_gethash_time_(Time::FromDoubleT(0)),
+      config_(config),
+      url_loader_factory_(url_loader_factory),
+      clock_(base::DefaultClock::GetInstance()) {
+  DCHECK(!stores_to_check.empty());
+  std::set<PlatformType> platform_types;
+  std::set<ThreatEntryType> threat_entry_types;
+  std::set<ThreatType> threat_types;
+  for (const ListIdentifier& store : stores_to_check) {
+    platform_types.insert(store.platform_type());
+    threat_entry_types.insert(store.threat_entry_type());
+    threat_types.insert(store.threat_type());
+  }
+  platform_types_.assign(platform_types.begin(), platform_types.end());
+  threat_entry_types_.assign(threat_entry_types.begin(),
+                             threat_entry_types.end());
+  threat_types_.assign(threat_types.begin(), threat_types.end());
+}
+
+V4GetHashProtocolManager::~V4GetHashProtocolManager() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+void V4GetHashProtocolManager::ClearCache() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  full_hash_cache_.clear();
+}
+
+void V4GetHashProtocolManager::GetFullHashes(
+    const FullHashToStoreAndHashPrefixesMap
+        full_hash_to_store_and_hash_prefixes,
+    const std::vector<std::string>& list_client_states,
+    FullHashCallback callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(!full_hash_to_store_and_hash_prefixes.empty());
+
+  std::vector<HashPrefix> prefixes_to_request;
+  std::vector<FullHashInfo> cached_full_hash_infos;
+  GetFullHashCachedResults(full_hash_to_store_and_hash_prefixes, Time::Now(),
+                           &prefixes_to_request, &cached_full_hash_infos);
+
+  if (prefixes_to_request.empty()) {
+    // 100% cache hits (positive or negative) so we can call the callback right
+    // away.
+    std::move(callback).Run(cached_full_hash_infos);
+    return;
+  }
+
+  // We need to wait the minimum waiting duration, and if we are in backoff,
+  // we need to check if we're past the next allowed time. If we are, we can
+  // proceed with the request. If not, we are required to return empty results
+  // (i.e. just use the results from cache and potentially report an unsafe
+  // resource as safe).
+  if (clock_->Now() <= next_gethash_time_) {
+    if (gethash_error_count_) {
+      RecordGetHashResult(V4OperationResult::BACKOFF_ERROR);
+    } else {
+      RecordGetHashResult(V4OperationResult::MIN_WAIT_DURATION_ERROR);
+    }
+    std::move(callback).Run(cached_full_hash_infos);
+    return;
+  }
+
+  net::NetworkTrafficAnnotationTag traffic_annotation =
+      net::DefineNetworkTrafficAnnotation("safe_browsing_v4_get_hash", R"(
+        semantics {
+          sender: "Safe Browsing"
+          description:
+            "When Safe Browsing detects that a URL might be dangerous based on "
+            "its local database, it sends a partial hash of that URL to Google "
+            "to verify it before showing a warning to the user. This partial "
+            "hash does not expose the URL to Google."
+          trigger:
+            "When a resource URL matches the local hash-prefix database of "
+            "potential threats (malware, phishing etc), and the full-hash "
+            "result is not already cached, this will be sent."
+          data:
+             "The 32-bit hash prefix of any potentially bad URLs. The URLs "
+             "themselves are not sent."
+          destination: GOOGLE_OWNED_SERVICE
+        }
+        policy {
+          cookies_allowed: YES
+          cookies_store: "Safe Browsing cookie store"
+          setting:
+            "Users can disable Safe Browsing by unchecking 'Protect you and "
+            "your device from dangerous sites' in Chromium settings under "
+            "Privacy. The feature is enabled by default."
+          chrome_policy {
+            SafeBrowsingEnabled {
+              policy_options {mode: MANDATORY}
+              SafeBrowsingEnabled: false
+            }
+          }
+        })");
+  auto resource_request = std::make_unique<network::ResourceRequest>();
+  std::string req_base64 =
+      GetHashRequest(prefixes_to_request, list_client_states);
+  GetHashUrlAndHeaders(req_base64, &resource_request->url,
+                       &resource_request->headers);
+
+  resource_request->load_flags = net::LOAD_DISABLE_CACHE;
+  std::unique_ptr<network::SimpleURLLoader> owned_loader =
+      network::SimpleURLLoader::Create(std::move(resource_request),
+                                       traffic_annotation);
+  network::SimpleURLLoader* loader = owned_loader.get();
+  owned_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+      url_loader_factory_.get(),
+      base::BindOnce(&V4GetHashProtocolManager::OnURLLoaderComplete,
+                     base::Unretained(this), loader));
+
+  pending_hash_requests_[loader].reset(new FullHashCallbackInfo(
+      cached_full_hash_infos, prefixes_to_request, std::move(owned_loader),
+      full_hash_to_store_and_hash_prefixes, std::move(callback),
+      clock_->Now()));
+  UMA_HISTOGRAM_COUNTS_100("SafeBrowsing.V4GetHash.CountOfPrefixes",
+                           prefixes_to_request.size());
+}
+
+void V4GetHashProtocolManager::GetFullHashesWithApis(
+    const GURL& url,
+    const std::vector<std::string>& list_client_states,
+    ThreatMetadataForApiCallback api_callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK(url.SchemeIs(url::kHttpScheme) || url.SchemeIs(url::kHttpsScheme));
+
+  std::vector<FullHash> full_hashes;
+  V4ProtocolManagerUtil::UrlToFullHashes(url.GetOrigin(), &full_hashes);
+
+  FullHashToStoreAndHashPrefixesMap full_hash_to_store_and_hash_prefixes;
+  for (const FullHash& full_hash : full_hashes) {
+    HashPrefix prefix;
+    bool result =
+        V4ProtocolManagerUtil::FullHashToSmallestHashPrefix(full_hash, &prefix);
+    DCHECK(result);
+    full_hash_to_store_and_hash_prefixes[full_hash].emplace_back(
+        GetChromeUrlApiId(), prefix);
+  }
+
+  GetFullHashes(full_hash_to_store_and_hash_prefixes, list_client_states,
+                base::BindOnce(&V4GetHashProtocolManager::OnFullHashForApi,
+                               base::Unretained(this), std::move(api_callback),
+                               full_hashes));
+}
+
+void V4GetHashProtocolManager::GetFullHashCachedResults(
+    const FullHashToStoreAndHashPrefixesMap&
+        full_hash_to_store_and_hash_prefixes,
+    const Time& now,
+    std::vector<HashPrefix>* prefixes_to_request,
+    std::vector<FullHashInfo>* cached_full_hash_infos) {
+  DCHECK(!full_hash_to_store_and_hash_prefixes.empty());
+  DCHECK(prefixes_to_request->empty());
+  DCHECK(cached_full_hash_infos->empty());
+
+  // Caching behavior is documented here:
+  // https://developers.google.com/safe-browsing/v4/caching#about-caching
+  //
+  // The cache operates as follows:
+  // Lookup:
+  //     Case 1: The prefix is in the cache.
+  //         Case a: The full hash is in the cache.
+  //             Case i : The positive full hash result has not expired.
+  //                      The result is unsafe and we do not need to send a new
+  //                      request.
+  //             Case ii: The positive full hash result has expired.
+  //                      We need to send a request for full hashes.
+  //         Case b: The full hash is not in the cache.
+  //             Case i : The negative cache entry has not expired.
+  //                      The result is still safe and we do not need to send a
+  //                      new request.
+  //             Case ii: The negative cache entry has expired.
+  //                      We need to send a request for full hashes.
+  //     Case 2: The prefix is not in the cache.
+  //             We need to send a request for full hashes.
+  //
+  // Note on eviction:
+  //   CachedHashPrefixInfo entries can be removed from the cache only when
+  //   the negative cache expire time and the cache expire time of all full
+  //   hash results for that prefix have expired.
+  //   Individual full hash results can be removed from the prefix's
+  //   cache entry if they expire AND their expire time is after the negative
+  //   cache expire time.
+
+  std::unordered_set<HashPrefix> unique_prefixes_to_request;
+  for (const auto& it : full_hash_to_store_and_hash_prefixes) {
+    const FullHash& full_hash = it.first;
+    const StoreAndHashPrefixes& matched = it.second;
+    for (const StoreAndHashPrefix& matched_it : matched) {
+      const ListIdentifier& list_id = matched_it.list_id;
+      const HashPrefix& prefix = matched_it.hash_prefix;
+      auto prefix_entry = full_hash_cache_.find(prefix);
+      if (prefix_entry != full_hash_cache_.end()) {
+        // Case 1.
+        const CachedHashPrefixInfo& cached_prefix_info = prefix_entry->second;
+        bool found_full_hash = false;
+        for (const FullHashInfo& full_hash_info :
+             cached_prefix_info.full_hash_infos) {
+          if (full_hash_info.full_hash == full_hash &&
+              full_hash_info.list_id == list_id) {
+            // Case a.
+            found_full_hash = true;
+            number_of_hits_++;
+            if (full_hash_info.positive_expiry > now) {
+              // Case i.
+              cached_full_hash_infos->push_back(full_hash_info);
+              RecordV4FullHashCacheResult(FULL_HASH_CACHE_HIT);
+            } else {
+              // Case ii.
+              unique_prefixes_to_request.insert(prefix);
+              RecordV4FullHashCacheResult(FULL_HASH_CACHE_MISS);
+            }
+            break;
+          }
+        }
+
+        if (!found_full_hash) {
+          // Case b.
+          if (cached_prefix_info.negative_expiry > now) {
+            // Case i.
+            RecordV4FullHashCacheResult(FULL_HASH_NEGATIVE_CACHE_HIT);
+          } else {
+            // Case ii.
+            unique_prefixes_to_request.insert(prefix);
+            RecordV4FullHashCacheResult(FULL_HASH_CACHE_MISS);
+          }
+        }
+      } else {
+        // Case 2.
+        unique_prefixes_to_request.insert(prefix);
+        RecordV4FullHashCacheResult(FULL_HASH_CACHE_MISS);
+      }
+    }
+  }
+
+  prefixes_to_request->insert(prefixes_to_request->begin(),
+                              unique_prefixes_to_request.begin(),
+                              unique_prefixes_to_request.end());
+}
+
+std::string V4GetHashProtocolManager::GetHashRequest(
+    const std::vector<HashPrefix>& prefixes_to_request,
+    const std::vector<std::string>& list_client_states) {
+  DCHECK(!prefixes_to_request.empty());
+
+  FindFullHashesRequest req;
+
+  V4ProtocolManagerUtil::SetClientInfoFromConfig(req.mutable_client(), config_);
+
+  for (const auto& client_state : list_client_states) {
+    req.add_client_states(client_state);
+  }
+
+  ThreatInfo* info = req.mutable_threat_info();
+  for (const PlatformType p : platform_types_) {
+    info->add_platform_types(p);
+  }
+  for (const ThreatEntryType tet : threat_entry_types_) {
+    info->add_threat_entry_types(tet);
+  }
+  for (const ThreatType tt : threat_types_) {
+    info->add_threat_types(tt);
+  }
+  for (const HashPrefix& prefix : prefixes_to_request) {
+    info->add_threat_entries()->set_hash(prefix);
+  }
+
+  // Serialize and Base64 encode.
+  std::string req_data, req_base64;
+  req.SerializeToString(&req_data);
+  base::Base64UrlEncode(req_data, base::Base64UrlEncodePolicy::INCLUDE_PADDING,
+                        &req_base64);
+  return req_base64;
+}
+
+void V4GetHashProtocolManager::GetHashUrlAndHeaders(
+    const std::string& req_base64,
+    GURL* gurl,
+    net::HttpRequestHeaders* headers) const {
+  V4ProtocolManagerUtil::GetRequestUrlAndHeaders(req_base64, "fullHashes:find",
+                                                 config_, gurl, headers);
+}
+
+void V4GetHashProtocolManager::HandleGetHashError(const Time& now) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  TimeDelta next = V4ProtocolManagerUtil::GetNextBackOffInterval(
+      &gethash_error_count_, &gethash_back_off_mult_);
+  next_gethash_time_ = now + next;
+}
+
+void V4GetHashProtocolManager::OnFullHashForApi(
+    ThreatMetadataForApiCallback api_callback,
+    const std::vector<FullHash>& full_hashes,
+    const std::vector<FullHashInfo>& full_hash_infos) {
+  ThreatMetadata md;
+  for (const FullHashInfo& full_hash_info : full_hash_infos) {
+    DCHECK_EQ(GetChromeUrlApiId(), full_hash_info.list_id);
+    DCHECK(base::Contains(full_hashes, full_hash_info.full_hash));
+    md.api_permissions.insert(full_hash_info.metadata.api_permissions.begin(),
+                              full_hash_info.metadata.api_permissions.end());
+  }
+
+  std::move(api_callback).Run(md);
+}
+
+bool V4GetHashProtocolManager::ParseHashResponse(
+    const std::string& response_data,
+    std::vector<FullHashInfo>* full_hash_infos,
+    Time* negative_cache_expire) {
+  FindFullHashesResponse response;
+
+  if (!response.ParseFromString(response_data)) {
+    RecordParseGetHashResult(PARSE_FROM_STRING_ERROR);
+    return false;
+  }
+
+  // negative_cache_duration should always be set.
+  DCHECK(response.has_negative_cache_duration());
+
+  // Seconds resolution is good enough so we ignore the nanos field.
+  *negative_cache_expire =
+      clock_->Now() +
+      TimeDelta::FromSeconds(response.negative_cache_duration().seconds());
+
+  if (response.has_minimum_wait_duration()) {
+    // Seconds resolution is good enough so we ignore the nanos field.
+    next_gethash_time_ =
+        clock_->Now() +
+        TimeDelta::FromSeconds(response.minimum_wait_duration().seconds());
+  }
+
+  for (const ThreatMatch& match : response.matches()) {
+    if (!match.has_platform_type()) {
+      RecordParseGetHashResult(UNEXPECTED_PLATFORM_TYPE_ERROR);
+      return false;
+    }
+    if (!match.has_threat_entry_type()) {
+      RecordParseGetHashResult(UNEXPECTED_THREAT_ENTRY_TYPE_ERROR);
+      return false;
+    }
+    if (!match.has_threat_type()) {
+      RecordParseGetHashResult(UNEXPECTED_THREAT_TYPE_ERROR);
+      return false;
+    }
+    if (!match.has_threat()) {
+      RecordParseGetHashResult(NO_THREAT_ERROR);
+      return false;
+    }
+
+    ListIdentifier list_id(match.platform_type(), match.threat_entry_type(),
+                           match.threat_type());
+    if (!base::Contains(platform_types_, list_id.platform_type()) ||
+        !base::Contains(threat_entry_types_, list_id.threat_entry_type()) ||
+        !base::Contains(threat_types_, list_id.threat_type())) {
+      // The server may send a ThreatMatch response for lists that we didn't ask
+      // for so ignore those ThreatMatch responses.
+      continue;
+    }
+
+    base::Time positive_expiry;
+    if (match.has_cache_duration()) {
+      // Seconds resolution is good enough so we ignore the nanos field.
+      positive_expiry = clock_->Now() + TimeDelta::FromSeconds(
+                                            match.cache_duration().seconds());
+    } else {
+      positive_expiry = clock_->Now() - base::TimeDelta::FromSeconds(1);
+    }
+    FullHashInfo full_hash_info(match.threat().hash(), list_id,
+                                positive_expiry);
+    ParseMetadata(match, &full_hash_info.metadata);
+    TRACE_EVENT2("safe_browsing", "V4GetHashProtocolManager::ParseHashResponse",
+                 "threat_type", full_hash_info.list_id.threat_type(),
+                 "metadata", full_hash_info.metadata.ToTracedValue());
+    full_hash_infos->push_back(full_hash_info);
+  }
+  return true;
+}
+
+// static
+void V4GetHashProtocolManager::ParseMetadata(const ThreatMatch& match,
+                                             ThreatMetadata* metadata) {
+  // Different threat types will handle the metadata differently.
+  if (match.threat_type() == API_ABUSE) {
+    if (!match.has_platform_type()) {
+      RecordParseGetHashResult(UNEXPECTED_PLATFORM_TYPE_ERROR);
+      return;
+    }
+
+    if (!match.has_threat_entry_metadata()) {
+      RecordParseGetHashResult(NO_METADATA_ERROR);
+      return;
+    }
+    // For API Abuse, store a list of the returned permissions.
+    for (const ThreatEntryMetadata::MetadataEntry& m :
+         match.threat_entry_metadata().entries()) {
+      if (m.key() != kPermission) {
+        RecordParseGetHashResult(UNEXPECTED_METADATA_VALUE_ERROR);
+        return;
+      }
+      metadata->api_permissions.insert(m.value());
+    }
+  } else if (match.threat_type() == MALWARE_THREAT ||
+             match.threat_type() == POTENTIALLY_HARMFUL_APPLICATION) {
+    for (const ThreatEntryMetadata::MetadataEntry& m :
+         match.threat_entry_metadata().entries()) {
+      if (m.key() == kPhaPatternType || m.key() == kMalwareThreatType) {
+        if (m.value() == kLanding) {
+          metadata->threat_pattern_type = ThreatPatternType::MALWARE_LANDING;
+          break;
+        } else if (m.value() == kDistribution) {
+          metadata->threat_pattern_type =
+              ThreatPatternType::MALWARE_DISTRIBUTION;
+          break;
+        } else {
+          RecordParseGetHashResult(UNEXPECTED_METADATA_VALUE_ERROR);
+          return;
+        }
+      }
+    }
+  } else if (match.threat_type() == SOCIAL_ENGINEERING) {
+    for (const ThreatEntryMetadata::MetadataEntry& m :
+         match.threat_entry_metadata().entries()) {
+      if (m.key() == kSePatternType) {
+        if (m.value() == kSocialEngineeringAds) {
+          metadata->threat_pattern_type =
+              ThreatPatternType::SOCIAL_ENGINEERING_ADS;
+          break;
+        } else if (m.value() == kSocialEngineeringLanding) {
+          metadata->threat_pattern_type =
+              ThreatPatternType::SOCIAL_ENGINEERING_LANDING;
+          break;
+        } else if (m.value() == kPhishing) {
+          metadata->threat_pattern_type = ThreatPatternType::PHISHING;
+          break;
+        } else {
+          RecordParseGetHashResult(UNEXPECTED_METADATA_VALUE_ERROR);
+          return;
+        }
+      }
+    }
+  } else if (match.threat_type() == SUBRESOURCE_FILTER) {
+    for (const ThreatEntryMetadata::MetadataEntry& m :
+         match.threat_entry_metadata().entries()) {
+      // Anything other than "warn" is interpreted as enforce, which should be
+      // more common (and therefore leaves us open to shorten it in the future).
+      auto get_enforcement = [](const std::string& value) {
+        return value == "warn" ? SubresourceFilterLevel::WARN
+                               : SubresourceFilterLevel::ENFORCE;
+      };
+      if (m.key() == "sf_absv") {
+        metadata->subresource_filter_match[SubresourceFilterType::ABUSIVE] =
+            get_enforcement(m.value());
+      } else if (m.key() == "sf_bas") {
+        metadata->subresource_filter_match[SubresourceFilterType::BETTER_ADS] =
+            get_enforcement(m.value());
+      }
+    }
+  } else if (match.has_threat_entry_metadata() &&
+             match.threat_entry_metadata().entries_size() > 1) {
+    RecordParseGetHashResult(UNEXPECTED_THREAT_TYPE_ERROR);
+  }
+}
+
+void V4GetHashProtocolManager::ResetGetHashErrors() {
+  gethash_error_count_ = 0;
+  gethash_back_off_mult_ = 1;
+  next_gethash_time_ = base::Time();
+}
+
+void V4GetHashProtocolManager::SetClockForTests(base::Clock* clock) {
+  clock_ = clock;
+}
+
+void V4GetHashProtocolManager::UpdateCache(
+    const std::vector<HashPrefix>& prefixes_requested,
+    const std::vector<FullHashInfo>& full_hash_infos,
+    const Time& negative_cache_expire) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  // If negative_cache_expire is null, don't cache the results since it's not
+  // clear till what time they should be considered valid.
+  if (negative_cache_expire.is_null()) {
+    return;
+  }
+
+  for (const HashPrefix& prefix : prefixes_requested) {
+    // Create or reset the cached result for this prefix.
+    CachedHashPrefixInfo& chpi = full_hash_cache_[prefix];
+    chpi.full_hash_infos.clear();
+    chpi.negative_expiry = negative_cache_expire;
+
+    for (const FullHashInfo& full_hash_info : full_hash_infos) {
+      if (V4ProtocolManagerUtil::FullHashMatchesHashPrefix(
+              full_hash_info.full_hash, prefix)) {
+        chpi.full_hash_infos.push_back(full_hash_info);
+      }
+    }
+  }
+}
+
+void V4GetHashProtocolManager::MergeResults(
+    const FullHashToStoreAndHashPrefixesMap&
+        full_hash_to_store_and_hash_prefixes,
+    const std::vector<FullHashInfo>& full_hash_infos,
+    std::vector<FullHashInfo>* merged_full_hash_infos) {
+  bool get_hash_hit = false;
+  for (const FullHashInfo& fhi : full_hash_infos) {
+    auto it = full_hash_to_store_and_hash_prefixes.find(fhi.full_hash);
+    if (full_hash_to_store_and_hash_prefixes.end() != it) {
+      for (const StoreAndHashPrefix& sahp : it->second) {
+        if (fhi.list_id == sahp.list_id) {
+          merged_full_hash_infos->push_back(fhi);
+          get_hash_hit = true;
+          break;
+        }
+      }
+    }
+  }
+
+  if (get_hash_hit) {
+    RecordV4GetHashCheckResult(GET_HASH_CHECK_HIT);
+  } else if (full_hash_infos.empty()) {
+    RecordV4GetHashCheckResult(GET_HASH_CHECK_EMPTY);
+  } else {
+    RecordV4GetHashCheckResult(GET_HASH_CHECK_MISS);
+  }
+}
+
+// SafeBrowsing request responses are handled here.
+void V4GetHashProtocolManager::OnURLLoaderComplete(
+    network::SimpleURLLoader* url_loader,
+    std::unique_ptr<std::string> response_body) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  int response_code = 0;
+  if (url_loader->ResponseInfo() && url_loader->ResponseInfo()->headers)
+    response_code = url_loader->ResponseInfo()->headers->response_code();
+
+  std::string data;
+  if (response_body)
+    data = *response_body;
+
+  OnURLLoaderCompleteInternal(url_loader, url_loader->NetError(), response_code,
+                              data);
+}
+
+void V4GetHashProtocolManager::OnURLLoaderCompleteInternal(
+    network::SimpleURLLoader* url_loader,
+    int net_error,
+    int response_code,
+    const std::string& data) {
+  auto it = pending_hash_requests_.find(url_loader);
+  DCHECK(it != pending_hash_requests_.end()) << "Request not found";
+  V4ProtocolManagerUtil::RecordHttpResponseOrErrorCode(
+      "SafeBrowsing.V4GetHash.Network.Result", net_error, response_code);
+
+  std::vector<FullHashInfo> full_hash_infos;
+  Time negative_cache_expire;
+  if (net_error == net::OK && response_code == net::HTTP_OK) {
+    RecordGetHashResult(V4OperationResult::STATUS_200);
+    ResetGetHashErrors();
+    if (!ParseHashResponse(data, &full_hash_infos, &negative_cache_expire)) {
+      full_hash_infos.clear();
+      RecordGetHashResult(V4OperationResult::PARSE_ERROR);
+    }
+  } else {
+    HandleGetHashError(clock_->Now());
+
+    DVLOG(1) << "SafeBrowsing GetEncodedFullHashes request for: "
+             << url_loader->GetFinalURL() << " failed with error: " << net_error
+             << " and response code: " << response_code;
+
+    if (net_error != net::OK) {
+      RecordGetHashResult(V4OperationResult::NETWORK_ERROR);
+    } else {
+      RecordGetHashResult(V4OperationResult::HTTP_ERROR);
+    }
+  }
+
+  const std::unique_ptr<FullHashCallbackInfo>& fhci = it->second;
+  UMA_HISTOGRAM_LONG_TIMES("SafeBrowsing.V4GetHash.Network.Time",
+                           clock_->Now() - fhci->network_start_time);
+  UpdateCache(fhci->prefixes_requested, full_hash_infos, negative_cache_expire);
+  MergeResults(fhci->full_hash_to_store_and_hash_prefixes, full_hash_infos,
+               &fhci->cached_full_hash_infos);
+
+  std::move(fhci->callback).Run(fhci->cached_full_hash_infos);
+
+  pending_hash_requests_.erase(it);
+}
+
+void V4GetHashProtocolManager::CollectFullHashCacheInfo(
+    FullHashCacheInfo* full_hash_cache_info) {
+  full_hash_cache_info->set_number_of_hits(number_of_hits_);
+
+  for (const auto& it : full_hash_cache_) {
+    FullHashCacheInfo::FullHashCache* full_hash_cache =
+        full_hash_cache_info->add_full_hash_cache();
+    full_hash_cache->set_hash_prefix(it.first);
+    full_hash_cache->mutable_cached_hash_prefix_info()->set_negative_expiry(
+        it.second.negative_expiry.ToJavaTime());
+
+    for (const auto& full_hash_infos_it : it.second.full_hash_infos) {
+      FullHashCacheInfo::FullHashCache::CachedHashPrefixInfo::FullHashInfo*
+          full_hash_info = full_hash_cache->mutable_cached_hash_prefix_info()
+                               ->add_full_hash_info();
+      full_hash_info->set_positive_expiry(
+          full_hash_infos_it.positive_expiry.ToJavaTime());
+      full_hash_info->set_full_hash(full_hash_infos_it.full_hash);
+
+      full_hash_info->mutable_list_identifier()->set_platform_type(
+          static_cast<int>(full_hash_infos_it.list_id.platform_type()));
+      full_hash_info->mutable_list_identifier()->set_threat_entry_type(
+          static_cast<int>(full_hash_infos_it.list_id.threat_entry_type()));
+      full_hash_info->mutable_list_identifier()->set_threat_type(
+          static_cast<int>(full_hash_infos_it.list_id.threat_type()));
+    }
+  }
+}
+
+#ifndef DEBUG
+std::ostream& operator<<(std::ostream& os, const FullHashInfo& fhi) {
+  os << "{full_hash: " << fhi.full_hash << "; list_id: " << fhi.list_id
+     << "; positive_expiry: " << fhi.positive_expiry
+     << "; metadata.api_permissions.size(): "
+     << fhi.metadata.api_permissions.size() << "}";
+  return os;
+}
+#endif
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/db/v4_get_hash_protocol_manager.h b/components/safe_browsing/core/db/v4_get_hash_protocol_manager.h
new file mode 100644
index 0000000..1b68e67
--- /dev/null
+++ b/components/safe_browsing/core/db/v4_get_hash_protocol_manager.h
@@ -0,0 +1,383 @@
+// 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 COMPONENTS_SAFE_BROWSING_CORE_DB_V4_GET_HASH_PROTOCOL_MANAGER_H_
+#define COMPONENTS_SAFE_BROWSING_CORE_DB_V4_GET_HASH_PROTOCOL_MANAGER_H_
+
+// A class that implements Chrome's interface with the SafeBrowsing V4 protocol.
+//
+// The V4GetHashProtocolManager handles formatting and making requests of, and
+// handling responses from, Google's SafeBrowsing servers. The purpose of this
+// class is to get full hash matches from the SB server for the given set of
+// hash prefixes.
+//
+// Design doc: go/design-doc-v4-full-hash-manager
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/sequence_checker.h"
+#include "base/time/default_clock.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "components/safe_browsing/core/db/safebrowsing.pb.h"
+#include "components/safe_browsing/core/db/util.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/core/proto/webui.pb.h"
+
+class GURL;
+
+namespace network {
+class SimpleURLLoader;
+class SharedURLLoaderFactory;
+}  // namespace network
+
+namespace safe_browsing {
+
+class V4GetHashProtocolManagerFuzzer;
+
+// The matching hash prefixes and corresponding stores, for each full hash
+// generated for a given URL.
+typedef std::unordered_map<FullHash, StoreAndHashPrefixes>
+    FullHashToStoreAndHashPrefixesMap;
+
+// ----------------------------------------------------------------
+
+// All information about a particular full hash i.e. negative TTL, store for
+// which it is valid, and metadata associated with that store.
+struct FullHashInfo {
+ public:
+  FullHash full_hash;
+
+  // The list for which this full hash is applicable.
+  ListIdentifier list_id;
+
+  // The expiration time of the full hash for a particular store.
+  base::Time positive_expiry;
+
+  // Any metadata for this full hash for a particular store.
+  ThreatMetadata metadata;
+
+  FullHashInfo(const FullHash& full_hash,
+               const ListIdentifier& list_id,
+               const base::Time& positive_expiry);
+  FullHashInfo(const FullHashInfo& other);
+  ~FullHashInfo();
+
+  bool operator==(const FullHashInfo& other) const;
+  bool operator!=(const FullHashInfo& other) const;
+
+ private:
+  FullHashInfo();
+};
+
+// Caches individual response from GETHASH response.
+struct CachedHashPrefixInfo {
+  // The negative TTL for the hash prefix that leads to this
+  // CachedHashPrefixInfo. The client should not send any more requests for that
+  // hash prefix until this time.
+  base::Time negative_expiry;
+
+  // The list of all full hashes (and related info) that start with a
+  // particular hash prefix and are known to be unsafe.
+  std::vector<FullHashInfo> full_hash_infos;
+
+  CachedHashPrefixInfo();
+  CachedHashPrefixInfo(const CachedHashPrefixInfo& other);
+  ~CachedHashPrefixInfo();
+};
+
+// Cached full hashes received from the server for the corresponding hash
+// prefixes.
+typedef std::unordered_map<HashPrefix, CachedHashPrefixInfo> FullHashCache;
+
+// FullHashCallback is invoked when GetFullHashes completes. The parameter is
+// the vector of full hash results. If empty, indicates that there were no
+// matches, and that the resource is safe.
+using FullHashCallback =
+    base::OnceCallback<void(const std::vector<FullHashInfo>&)>;
+
+// Information needed to update the cache and call the callback to post the
+// results.
+struct FullHashCallbackInfo {
+  FullHashCallbackInfo();
+  FullHashCallbackInfo(const std::vector<FullHashInfo>& cached_full_hash_infos,
+                       const std::vector<HashPrefix>& prefixes_requested,
+                       std::unique_ptr<network::SimpleURLLoader> loader,
+                       const FullHashToStoreAndHashPrefixesMap&
+                           full_hash_to_store_and_hash_prefixes,
+                       FullHashCallback callback,
+                       const base::Time& network_start_time);
+  ~FullHashCallbackInfo();
+
+  // The FullHashInfo objects retrieved from cache. These are merged with the
+  // results received from the server before invoking the callback.
+  std::vector<FullHashInfo> cached_full_hash_infos;
+
+  // The callback method to call after collecting the full hashes for given
+  // hash prefixes.
+  FullHashCallback callback;
+
+  // The loader that will return the response from the server. This is stored
+  // here as a unique pointer to be able to reason about its lifetime easily.
+  std::unique_ptr<network::SimpleURLLoader> loader;
+
+  // The generated full hashes and the corresponding prefixes and the stores in
+  // which to look for a full hash match.
+  FullHashToStoreAndHashPrefixesMap full_hash_to_store_and_hash_prefixes;
+
+  // Used to measure how long did it take to fetch the full hash response from
+  // the server.
+  base::Time network_start_time;
+
+  // The prefixes that were requested from the server.
+  std::vector<HashPrefix> prefixes_requested;
+};
+
+// ----------------------------------------------------------------
+
+class V4GetHashProtocolManagerFactory;
+
+class V4GetHashProtocolManager {
+ public:
+  // Invoked when GetFullHashesWithApis completes.
+  // Parameters:
+  //   - The API threat metadata for the given URL.
+  using ThreatMetadataForApiCallback =
+      base::OnceCallback<void(const ThreatMetadata& md)>;
+
+  virtual ~V4GetHashProtocolManager();
+
+  // Create an instance of the safe browsing v4 protocol manager.
+  static std::unique_ptr<V4GetHashProtocolManager> Create(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      const StoresToCheck& stores_to_check,
+      const V4ProtocolConfig& config);
+
+  // Makes the passed |factory| the factory used to instantiate
+  // a V4GetHashProtocolManager. Useful for tests.
+  static void RegisterFactory(
+      std::unique_ptr<V4GetHashProtocolManagerFactory> factory);
+
+  // Empties the cache.
+  void ClearCache();
+
+  // Retrieve the full hash for a set of prefixes, and invoke the callback
+  // argument when the results are retrieved. The callback may be invoked
+  // synchronously. |list_client_states| is needed for reporting the current
+  // state of the lists on the client; it does not affect the response from the
+  // server.
+  virtual void GetFullHashes(const FullHashToStoreAndHashPrefixesMap
+                                 full_hash_to_matching_hash_prefixes,
+                             const std::vector<std::string>& list_client_states,
+                             FullHashCallback callback);
+
+  // Retrieve the full hash and API metadata for the origin of |url|, and invoke
+  // the callback argument when the results are retrieved. The callback may be
+  // invoked synchronously.
+  virtual void GetFullHashesWithApis(
+      const GURL& url,
+      const std::vector<std::string>& list_client_states,
+      ThreatMetadataForApiCallback api_callback);
+
+  // Callback when the request completes
+  void OnURLLoaderComplete(network::SimpleURLLoader* url_loader,
+                           std::unique_ptr<std::string> response_body);
+
+  // Populates the protobuf with the FullHashCache data.
+  void CollectFullHashCacheInfo(FullHashCacheInfo* full_hash_cache_info);
+
+ protected:
+  // Constructs a V4GetHashProtocolManager that issues network requests using
+  // |url_loader_factory|.
+  V4GetHashProtocolManager(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      const StoresToCheck& stores_to_check,
+      const V4ProtocolConfig& config);
+
+ private:
+  FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest, TestGetHashRequest);
+  FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest, TestParseHashResponse);
+  FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest,
+                           TestParseHashResponseWrongThreatEntryType);
+  FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest,
+                           TestParseHashThreatPatternType);
+  FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest,
+                           TestParseSubresourceFilterMetadata);
+  FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest,
+                           TestParseHashResponseNonPermissionMetadata);
+  FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest,
+                           TestParseHashResponseInconsistentThreatTypes);
+  FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest,
+                           TestGetHashErrorHandlingOK);
+  FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest,
+                           TestResultsNotCachedForNegativeCacheDuration);
+  FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest,
+                           TestGetHashErrorHandlingNetwork);
+  FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest,
+                           TestGetHashErrorHandlingResponseCode);
+  FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest,
+                           TestGetHashErrorHandlingParallelRequests);
+  FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest, GetCachedResults);
+  FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest, TestUpdatesAreMerged);
+  friend class V4GetHashProtocolManagerTest;
+  friend class V4GetHashProtocolManagerFuzzer;
+  friend class V4GetHashProtocolManagerFactoryImpl;
+
+  FullHashCache* full_hash_cache_for_tests() { return &full_hash_cache_; }
+
+  void OnURLLoaderCompleteInternal(network::SimpleURLLoader* url_loader,
+                                   int net_error,
+                                   int response_code,
+                                   const std::string& data);
+
+  // Looks up the cached results for full hashes in
+  // |full_hash_to_store_and_hash_prefixes|. Fills |prefixes_to_request| with
+  // the prefixes that need to be requested. Fills |cached_full_hash_infos|
+  // with the cached results.
+  // Note: It is valid for both |prefixes_to_request| and
+  // |cached_full_hash_infos| to be empty after this function finishes.
+  void GetFullHashCachedResults(
+      const FullHashToStoreAndHashPrefixesMap&
+          full_hash_to_store_and_hash_prefixes,
+      const base::Time& now,
+      std::vector<HashPrefix>* prefixes_to_request,
+      std::vector<FullHashInfo>* cached_full_hash_infos);
+
+  // Fills a FindFullHashesRequest protocol buffer for a request.
+  // Returns the serialized and base 64 encoded request as a string.
+  // |prefixes_to_request| is the list of hash prefixes to get full hashes for.
+  // |list_client_states| is the client_state of each of the lists being synced.
+  std::string GetHashRequest(
+      const std::vector<HashPrefix>& prefixes_to_request,
+      const std::vector<std::string>& list_client_states);
+
+  void GetHashUrlAndHeaders(const std::string& request_base64,
+                            GURL* gurl,
+                            net::HttpRequestHeaders* headers) const;
+
+  // Updates internal state for each GetHash response error, assuming that
+  // the current time is |now|.
+  void HandleGetHashError(const base::Time& now);
+
+  // Merges the results from the cache and the results from the server. The
+  // response from the server may include information for full hashes from
+  // stores other than those required by this client so it filters out those
+  // results that the client did not ask for.
+  void MergeResults(const FullHashToStoreAndHashPrefixesMap&
+                        full_hash_to_store_and_hash_prefixes,
+                    const std::vector<FullHashInfo>& full_hash_infos,
+                    std::vector<FullHashInfo>* merged_full_hash_infos);
+
+  // Calls |api_callback| with an object of ThreatMetadata that contains
+  // permission API metadata for full hashes in those |full_hash_infos| that
+  // have a full hash in |full_hashes|.
+  void OnFullHashForApi(ThreatMetadataForApiCallback api_callback,
+                        const std::vector<FullHash>& full_hashes,
+                        const std::vector<FullHashInfo>& full_hash_infos);
+
+  // Parses a FindFullHashesResponse protocol buffer and fills the results in
+  // |full_hash_infos| and |negative_cache_expire|. |response_data| is a
+  // serialized FindFullHashes protocol buffer. |negative_cache_expire| is the
+  // cache expiry time of the hash prefixes that were requested. Returns true if
+  // parsing is successful; false otherwise.
+  bool ParseHashResponse(const std::string& response_data,
+                         std::vector<FullHashInfo>* full_hash_infos,
+                         base::Time* negative_cache_expire);
+
+  // Parses the store specific |metadata| information from |match|. Logs errors
+  // to UMA if the metadata information was not parsed correctly or was
+  // inconsistent with what's expected from that corresponding store.
+  static void ParseMetadata(const ThreatMatch& match, ThreatMetadata* metadata);
+
+  // Resets the gethash error counter and multiplier.
+  void ResetGetHashErrors();
+
+  // Overrides the clock used to check the time.
+  void SetClockForTests(base::Clock* clock);
+
+  // Updates the state of the full hash cache upon receiving a valid response
+  // from the server.
+  void UpdateCache(const std::vector<HashPrefix>& prefixes_requested,
+                   const std::vector<FullHashInfo>& full_hash_infos,
+                   const base::Time& negative_cache_expire);
+
+ protected:
+  // A cache of full hash results.
+  FullHashCache full_hash_cache_;
+
+ private:
+  // Map of GetHash requests to parameters which created it.
+  using PendingHashRequests =
+      std::unordered_map<const network::SimpleURLLoader*,
+                         std::unique_ptr<FullHashCallbackInfo>>;
+
+  // The factory that controls the creation of V4GetHashProtocolManager.
+  // This is used by tests.
+  static V4GetHashProtocolManagerFactory* factory_;
+
+  // The number of HTTP response errors since the the last successful HTTP
+  // response, used for request backoff timing.
+  size_t gethash_error_count_;
+
+  // Multiplier for the backoff error after the second.
+  size_t gethash_back_off_mult_;
+
+  PendingHashRequests pending_hash_requests_;
+
+  // For v4, the next gethash time is set to the backoff time is the last
+  // response was an error, or the minimum wait time if the last response was
+  // successful.
+  base::Time next_gethash_time_;
+
+  // The config of the client making Pver4 requests.
+  const V4ProtocolConfig config_;
+
+  // The URLLoaderFactory we use to issue network requests.
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
+
+  // Records number of cache hits since the beginning of this session.
+  int number_of_hits_ = 0;
+
+  // The clock used to vend times.
+  base::Clock* clock_;
+
+  // The following sets represent the combination of lists that we would always
+  // request from the server, irrespective of which list we found the hash
+  // prefix match in.
+  std::vector<PlatformType> platform_types_;
+  std::vector<ThreatEntryType> threat_entry_types_;
+  std::vector<ThreatType> threat_types_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  DISALLOW_COPY_AND_ASSIGN(V4GetHashProtocolManager);
+};
+
+// Interface of a factory to create V4GetHashProtocolManager.  Useful for tests.
+class V4GetHashProtocolManagerFactory {
+ public:
+  V4GetHashProtocolManagerFactory() {}
+  virtual ~V4GetHashProtocolManagerFactory() {}
+  virtual std::unique_ptr<V4GetHashProtocolManager> CreateProtocolManager(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      const StoresToCheck& stores_to_check,
+      const V4ProtocolConfig& config) = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(V4GetHashProtocolManagerFactory);
+};
+
+#ifndef NDEBUG
+std::ostream& operator<<(std::ostream& os, const FullHashInfo& id);
+#endif
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CORE_DB_V4_GET_HASH_PROTOCOL_MANAGER_H_
diff --git a/components/safe_browsing/core/db/v4_get_hash_protocol_manager_fuzzer.cc b/components/safe_browsing/core/db/v4_get_hash_protocol_manager_fuzzer.cc
new file mode 100644
index 0000000..fc18d53
--- /dev/null
+++ b/components/safe_browsing/core/db/v4_get_hash_protocol_manager_fuzzer.cc
@@ -0,0 +1,33 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/core/db/v4_get_hash_protocol_manager.h"
+
+#include <string>
+
+#include "components/safe_browsing/core/db/safebrowsing.pb.h"
+
+namespace safe_browsing {
+
+class V4GetHashProtocolManagerFuzzer {
+ public:
+  static int FuzzMetadata(const uint8_t* data, size_t size) {
+    FindFullHashesResponse response;
+    std::string input(reinterpret_cast<const char*>(data), size);
+    if (!response.ParseFromString(input))
+      return 0;
+    safe_browsing::ThreatMetadata metadata;
+    for (const ThreatMatch& match : response.matches()) {
+      V4GetHashProtocolManager::ParseMetadata(match, &metadata);
+    }
+    return 0;
+  }
+};
+
+}  // namespace safe_browsing
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  return safe_browsing::V4GetHashProtocolManagerFuzzer::FuzzMetadata(data,
+                                                                     size);
+}
diff --git a/components/safe_browsing/core/db/v4_get_hash_protocol_manager_unittest.cc b/components/safe_browsing/core/db/v4_get_hash_protocol_manager_unittest.cc
new file mode 100644
index 0000000..29430f8
--- /dev/null
+++ b/components/safe_browsing/core/db/v4_get_hash_protocol_manager_unittest.cc
@@ -0,0 +1,942 @@
+// 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 "components/safe_browsing/core/db/v4_get_hash_protocol_manager.h"
+
+#include <memory>
+#include <vector>
+
+#include "base/base64.h"
+#include "base/bind.h"
+#include "base/run_loop.h"
+#include "base/stl_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/simple_test_clock.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "components/safe_browsing/core/db/safebrowsing.pb.h"
+#include "components/safe_browsing/core/db/util.h"
+#include "components/safe_browsing/core/db/v4_test_util.h"
+#include "content/public/test/browser_task_environment.h"
+#include "net/base/escape.h"
+#include "net/base/load_flags.h"
+#include "net/base/net_errors.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "testing/platform_test.h"
+
+using base::Time;
+using base::TimeDelta;
+
+namespace safe_browsing {
+
+namespace {
+
+struct KeyValue {
+  std::string key;
+  std::string value;
+
+  explicit KeyValue(const std::string key, const std::string value)
+      : key(key), value(value) {}
+  explicit KeyValue(const KeyValue& other) = default;
+
+ private:
+  KeyValue();
+};
+
+struct ResponseInfo {
+  FullHash full_hash;
+  ListIdentifier list_id;
+  std::vector<KeyValue> key_values;
+
+  explicit ResponseInfo(FullHash full_hash, ListIdentifier list_id)
+      : full_hash(full_hash), list_id(list_id) {}
+  explicit ResponseInfo(const ResponseInfo& other)
+      : full_hash(other.full_hash),
+        list_id(other.list_id),
+        key_values(other.key_values) {}
+
+ private:
+  ResponseInfo();
+};
+
+}  // namespace
+
+class V4GetHashProtocolManagerTest : public PlatformTest {
+ public:
+  void SetUp() override {
+    PlatformTest::SetUp();
+    callback_called_ = false;
+    test_shared_loader_factory_ =
+        base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+            &test_url_loader_factory_);
+  }
+
+  std::unique_ptr<V4GetHashProtocolManager> CreateProtocolManager() {
+    StoresToCheck stores_to_check(
+        {GetUrlMalwareId(), GetChromeUrlApiId(),
+         ListIdentifier(CHROME_PLATFORM, URL, SOCIAL_ENGINEERING),
+         ListIdentifier(CHROME_PLATFORM, URL, POTENTIALLY_HARMFUL_APPLICATION),
+         ListIdentifier(CHROME_PLATFORM, URL, SUBRESOURCE_FILTER)});
+    return V4GetHashProtocolManager::Create(test_shared_loader_factory_,
+                                            stores_to_check,
+                                            GetTestV4ProtocolConfig());
+  }
+
+  static void SetupFetcherToReturnResponse(V4GetHashProtocolManager* pm,
+                                           int net_error,
+                                           int response_code,
+                                           const std::string& data) {
+    CHECK_EQ(pm->pending_hash_requests_.size(), 1u);
+    pm->OnURLLoaderCompleteInternal(
+        pm->pending_hash_requests_.begin()->second->loader.get(), net_error,
+        response_code, data);
+  }
+
+  static void SetupFullHashFetcherToReturnResponse(V4GetHashProtocolManager* pm,
+                                                   const FullHash& full_hash,
+                                                   int net_error,
+                                                   int response_code,
+                                                   const std::string& data) {
+    network::SimpleURLLoader* url_loader = nullptr;
+    for (const auto& pending_request : pm->pending_hash_requests_) {
+      if (pending_request.second->full_hash_to_store_and_hash_prefixes.count(
+              full_hash)) {
+        EXPECT_EQ(nullptr, url_loader);
+        url_loader = pending_request.second->loader.get();
+      }
+    }
+    ASSERT_TRUE(url_loader);
+
+    pm->OnURLLoaderCompleteInternal(url_loader, net_error, response_code, data);
+  }
+
+  static void SetupFetcherToReturnOKResponse(
+      V4GetHashProtocolManager* pm,
+      const std::vector<ResponseInfo>& infos) {
+    SetupFetcherToReturnResponse(pm, net::OK, 200, GetV4HashResponse(infos));
+  }
+
+  static std::vector<ResponseInfo> GetStockV4HashResponseInfos() {
+    ResponseInfo info(FullHash("Everything's shiny, Cap'n."),
+                      GetChromeUrlApiId());
+    info.key_values.emplace_back("permission", "NOTIFICATIONS");
+    std::vector<ResponseInfo> infos;
+    infos.push_back(info);
+    return infos;
+  }
+
+  static std::string GetStockV4HashResponse() {
+    return GetV4HashResponse(GetStockV4HashResponseInfos());
+  }
+
+  void SetTestClock(base::Time now, V4GetHashProtocolManager* pm) {
+    clock_.SetNow(now);
+    pm->SetClockForTests(&clock_);
+  }
+
+  void ValidateGetV4ApiResults(const ThreatMetadata& expected_md,
+                               const ThreatMetadata& actual_md) {
+    EXPECT_EQ(expected_md, actual_md);
+    callback_called_ = true;
+  }
+
+  void ValidateGetV4HashResults(
+      const std::vector<FullHashInfo>& expected_results,
+      const std::vector<FullHashInfo>& actual_results) {
+    EXPECT_EQ(expected_results.size(), actual_results.size());
+    for (size_t i = 0; i < actual_results.size(); i++) {
+      EXPECT_TRUE(expected_results[i] == actual_results[i]);
+    }
+    callback_called_ = true;
+  }
+
+  bool callback_called() const { return callback_called_; }
+  void reset_callback_called() { callback_called_ = false; }
+
+ private:
+  static std::string GetV4HashResponse(
+      std::vector<ResponseInfo> response_infos) {
+    FindFullHashesResponse res;
+    res.mutable_negative_cache_duration()->set_seconds(600);
+    for (const ResponseInfo& info : response_infos) {
+      ThreatMatch* m = res.add_matches();
+      m->set_platform_type(info.list_id.platform_type());
+      m->set_threat_entry_type(info.list_id.threat_entry_type());
+      m->set_threat_type(info.list_id.threat_type());
+      m->mutable_cache_duration()->set_seconds(300);
+      m->mutable_threat()->set_hash(info.full_hash);
+
+      for (const KeyValue& key_value : info.key_values) {
+        ThreatEntryMetadata::MetadataEntry* e =
+            m->mutable_threat_entry_metadata()->add_entries();
+        e->set_key(key_value.key);
+        e->set_value(key_value.value);
+      }
+    }
+
+    // Serialize.
+    std::string res_data;
+    res.SerializeToString(&res_data);
+
+    return res_data;
+  }
+
+  bool callback_called_;
+  base::SimpleTestClock clock_;
+  network::TestURLLoaderFactory test_url_loader_factory_;
+  scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
+  content::BrowserTaskEnvironment task_environment_;
+};
+
+TEST_F(V4GetHashProtocolManagerTest, TestGetHashErrorHandlingNetwork) {
+  std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager());
+
+  FullHashToStoreAndHashPrefixesMap matched_locally;
+  matched_locally[FullHash("AHashFull")].emplace_back(GetUrlSocEngId(),
+                                                      HashPrefix("AHash"));
+  std::vector<FullHashInfo> expected_results;
+  pm->GetFullHashes(
+      matched_locally, {},
+      base::BindOnce(&V4GetHashProtocolManagerTest::ValidateGetV4HashResults,
+                     base::Unretained(this), expected_results));
+
+  // Failed request status should result in error.
+  SetupFetcherToReturnResponse(pm.get(), net::ERR_CONNECTION_RESET, 200,
+                               GetStockV4HashResponse());
+
+  // Should have recorded one error, but back off multiplier is unchanged.
+  EXPECT_EQ(1ul, pm->gethash_error_count_);
+  EXPECT_EQ(1ul, pm->gethash_back_off_mult_);
+  EXPECT_TRUE(callback_called());
+}
+
+TEST_F(V4GetHashProtocolManagerTest, TestGetHashErrorHandlingResponseCode) {
+  std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager());
+
+  FullHashToStoreAndHashPrefixesMap matched_locally;
+  matched_locally[FullHash("AHashFull")].emplace_back(GetUrlSocEngId(),
+                                                      HashPrefix("AHash"));
+  std::vector<FullHashInfo> expected_results;
+  pm->GetFullHashes(
+      matched_locally, {},
+      base::BindOnce(&V4GetHashProtocolManagerTest::ValidateGetV4HashResults,
+                     base::Unretained(this), expected_results));
+
+  // Response code of anything other than 200 should result in error.
+  SetupFetcherToReturnResponse(pm.get(), net::OK, 204,
+                               GetStockV4HashResponse());
+
+  // Should have recorded one error, but back off multiplier is unchanged.
+  EXPECT_EQ(1ul, pm->gethash_error_count_);
+  EXPECT_EQ(1ul, pm->gethash_back_off_mult_);
+  EXPECT_TRUE(callback_called());
+}
+
+TEST_F(V4GetHashProtocolManagerTest, TestGetHashErrorHandlingParallelRequests) {
+  std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager());
+  std::vector<FullHashInfo> empty_results;
+  base::HistogramTester histogram_tester;
+
+  FullHashToStoreAndHashPrefixesMap matched_locally1;
+  matched_locally1[FullHash("AHash1Full")].emplace_back(GetUrlSocEngId(),
+                                                        HashPrefix("AHash1"));
+  pm->GetFullHashes(matched_locally1, {},
+                    base::BindRepeating(
+                        &V4GetHashProtocolManagerTest::ValidateGetV4HashResults,
+                        base::Unretained(this), empty_results));
+
+  FullHashToStoreAndHashPrefixesMap matched_locally2;
+  matched_locally2[FullHash("AHash2Full")].emplace_back(GetUrlSocEngId(),
+                                                        HashPrefix("AHash2"));
+  pm->GetFullHashes(matched_locally2, {},
+                    base::BindRepeating(
+                        &V4GetHashProtocolManagerTest::ValidateGetV4HashResults,
+                        base::Unretained(this), empty_results));
+
+  // Fail the first request.
+  SetupFullHashFetcherToReturnResponse(pm.get(), FullHash("AHash1Full"),
+                                       net::ERR_CONNECTION_RESET, 200,
+                                       GetStockV4HashResponse());
+
+  // Should have recorded one error, but back off multiplier is unchanged.
+  EXPECT_EQ(1ul, pm->gethash_error_count_);
+  EXPECT_EQ(1ul, pm->gethash_back_off_mult_);
+  EXPECT_TRUE(callback_called());
+  histogram_tester.ExpectUniqueSample("SafeBrowsing.V4GetHash.Result",
+                                      V4OperationResult::NETWORK_ERROR, 1);
+
+  reset_callback_called();
+
+  // Comple the second request successfully.
+  SetupFullHashFetcherToReturnResponse(pm.get(), FullHash("AHash2Full"),
+                                       net::OK, 200, GetStockV4HashResponse());
+
+  // Error counters are reset.
+  EXPECT_EQ(0ul, pm->gethash_error_count_);
+  EXPECT_EQ(1ul, pm->gethash_back_off_mult_);
+  EXPECT_TRUE(callback_called());
+  histogram_tester.ExpectBucketCount("SafeBrowsing.V4GetHash.Result",
+                                     V4OperationResult::STATUS_200, 1);
+
+  reset_callback_called();
+
+  // Start the third request.
+  FullHashToStoreAndHashPrefixesMap matched_locally3;
+  matched_locally3[FullHash("AHash3Full")].emplace_back(GetUrlSocEngId(),
+                                                        HashPrefix("AHash3"));
+  pm->GetFullHashes(matched_locally3, {},
+                    base::BindRepeating(
+                        &V4GetHashProtocolManagerTest::ValidateGetV4HashResults,
+                        base::Unretained(this), empty_results));
+
+  // The request is not failed right away.
+  EXPECT_FALSE(callback_called());
+  // The request is not reported as MIN_WAIT_DURATION_ERROR.
+  histogram_tester.ExpectBucketCount("SafeBrowsing.V4GetHash.Result",
+                                     V4OperationResult::MIN_WAIT_DURATION_ERROR,
+                                     0);
+}
+
+TEST_F(V4GetHashProtocolManagerTest, TestGetHashErrorHandlingOK) {
+  std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager());
+
+  base::Time now = base::Time::UnixEpoch();
+  SetTestClock(now, pm.get());
+
+  HashPrefix prefix("Everything");
+  FullHash full_hash("Everything's shiny, Cap'n.");
+  FullHashToStoreAndHashPrefixesMap matched_locally;
+  matched_locally[full_hash].push_back(
+      StoreAndHashPrefix(GetChromeUrlApiId(), prefix));
+  std::vector<FullHashInfo> expected_results;
+  FullHashInfo fhi(full_hash, GetChromeUrlApiId(),
+                   now + base::TimeDelta::FromSeconds(300));
+  fhi.metadata.api_permissions.insert("NOTIFICATIONS");
+  expected_results.push_back(fhi);
+
+  pm->GetFullHashes(
+      matched_locally, {},
+      base::BindOnce(&V4GetHashProtocolManagerTest::ValidateGetV4HashResults,
+                     base::Unretained(this), expected_results));
+
+  SetupFetcherToReturnOKResponse(pm.get(), GetStockV4HashResponseInfos());
+
+  // No error, back off multiplier is unchanged.
+  EXPECT_EQ(0ul, pm->gethash_error_count_);
+  EXPECT_EQ(1ul, pm->gethash_back_off_mult_);
+
+  // Verify the state of the cache.
+  const FullHashCache* cache = pm->full_hash_cache_for_tests();
+  // Check the cache.
+  ASSERT_EQ(1u, cache->size());
+  EXPECT_EQ(1u, cache->count(prefix));
+  const CachedHashPrefixInfo& cached_result = cache->at(prefix);
+  EXPECT_EQ(cached_result.negative_expiry,
+            now + base::TimeDelta::FromSeconds(600));
+  ASSERT_EQ(1u, cached_result.full_hash_infos.size());
+  EXPECT_EQ(FullHash("Everything's shiny, Cap'n."),
+            cached_result.full_hash_infos[0].full_hash);
+  EXPECT_TRUE(callback_called());
+}
+
+TEST_F(V4GetHashProtocolManagerTest,
+       TestResultsNotCachedForNegativeCacheDuration) {
+  std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager());
+
+  HashPrefix prefix("Everything");
+  std::vector<HashPrefix> prefixes_requested({prefix});
+  base::Time negative_cache_expire;
+  FullHash full_hash("Everything's shiny, Cap'n.");
+  std::vector<FullHashInfo> fhis;
+  fhis.emplace_back(full_hash, GetChromeUrlApiId(), base::Time::UnixEpoch());
+
+  pm->UpdateCache(prefixes_requested, fhis, negative_cache_expire);
+
+  // Verify the state of the cache.
+  const FullHashCache* cache = pm->full_hash_cache_for_tests();
+  // Check the cache.
+  EXPECT_EQ(0u, cache->size());
+}
+
+TEST_F(V4GetHashProtocolManagerTest, TestGetHashRequest) {
+  FindFullHashesRequest req;
+  ThreatInfo* info = req.mutable_threat_info();
+
+  const std::set<PlatformType> platform_types = {
+    GetCurrentPlatformType(),
+    CHROME_PLATFORM,
+  // TODO(crbug.com/1030487): This special case for Android will no longer be
+  // needed once GetCurrentPlatformType() returns ANDROID_PLATFORM on Android.
+#if defined(OS_ANDROID)
+    ANDROID_PLATFORM,
+#endif
+  };
+
+  for (const PlatformType& p : platform_types) {
+    info->add_platform_types(p);
+  }
+
+  info->add_threat_entry_types(URL);
+
+  for (const ThreatType& tt : std::set<ThreatType>{
+           MALWARE_THREAT, SOCIAL_ENGINEERING, POTENTIALLY_HARMFUL_APPLICATION,
+           API_ABUSE, SUBRESOURCE_FILTER}) {
+    info->add_threat_types(tt);
+  }
+
+  HashPrefix one = "hashone";
+  HashPrefix two = "hashtwo";
+  info->add_threat_entries()->set_hash(one);
+  info->add_threat_entries()->set_hash(two);
+
+  std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager());
+  req.mutable_client()->set_client_id(pm->config_.client_name);
+  req.mutable_client()->set_client_version(pm->config_.version);
+
+  std::vector<std::string> client_states = {"client_state_1", "client_state_2"};
+  for (const auto& client_state : client_states) {
+    req.add_client_states(client_state);
+  }
+
+  // Serialize and Base64 encode.
+  std::string req_data, req_base64;
+  req.SerializeToString(&req_data);
+  base::Base64Encode(req_data, &req_base64);
+
+  std::vector<HashPrefix> prefixes_to_request = {one, two};
+  EXPECT_EQ(req_base64, pm->GetHashRequest(prefixes_to_request, client_states));
+}
+
+TEST_F(V4GetHashProtocolManagerTest, TestParseHashResponse) {
+  std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager());
+
+  base::Time now = base::Time::UnixEpoch();
+  SetTestClock(now, pm.get());
+
+  FullHash full_hash("Everything's shiny, Cap'n.");
+  FindFullHashesResponse res;
+  res.mutable_negative_cache_duration()->set_seconds(600);
+  res.mutable_minimum_wait_duration()->set_seconds(400);
+  ThreatMatch* m = res.add_matches();
+  m->set_threat_type(API_ABUSE);
+  // TODO(crbug.com/1030487): This special case for Android will no longer be
+  // needed once GetCurrentPlatformType() returns ANDROID_PLATFORM on Android.
+#if defined(OS_ANDROID)
+  m->set_platform_type(ANDROID_PLATFORM);
+#else
+  m->set_platform_type(GetCurrentPlatformType());
+#endif
+  m->set_threat_entry_type(URL);
+  m->mutable_cache_duration()->set_seconds(300);
+  m->mutable_threat()->set_hash(full_hash);
+  ThreatEntryMetadata::MetadataEntry* e =
+      m->mutable_threat_entry_metadata()->add_entries();
+  e->set_key("permission");
+  e->set_value("NOTIFICATIONS");
+  // Add another ThreatMatch for a list we don't track. This response should
+  // get dropped.
+  m = res.add_matches();
+  m->set_threat_type(THREAT_TYPE_UNSPECIFIED);
+  m->set_platform_type(CHROME_PLATFORM);
+  m->set_threat_entry_type(URL);
+  m->mutable_cache_duration()->set_seconds(300);
+  m->mutable_threat()->set_hash(full_hash);
+
+  // Serialize.
+  std::string res_data;
+  res.SerializeToString(&res_data);
+
+  std::vector<FullHashInfo> full_hash_infos;
+  base::Time cache_expire;
+  EXPECT_TRUE(pm->ParseHashResponse(res_data, &full_hash_infos, &cache_expire));
+
+  EXPECT_EQ(now + base::TimeDelta::FromSeconds(600), cache_expire);
+  // Even though the server responded with two ThreatMatch responses, one
+  // should have been dropped.
+  ASSERT_EQ(1ul, full_hash_infos.size());
+  const FullHashInfo& fhi = full_hash_infos[0];
+  EXPECT_EQ(full_hash, fhi.full_hash);
+  EXPECT_EQ(GetChromeUrlApiId(), fhi.list_id);
+  EXPECT_EQ(1ul, fhi.metadata.api_permissions.size());
+  EXPECT_EQ(1ul, fhi.metadata.api_permissions.count("NOTIFICATIONS"));
+  EXPECT_EQ(now + base::TimeDelta::FromSeconds(300), fhi.positive_expiry);
+  EXPECT_EQ(now + base::TimeDelta::FromSeconds(400), pm->next_gethash_time_);
+}
+
+// Adds an entry with an ignored ThreatEntryType.
+TEST_F(V4GetHashProtocolManagerTest,
+       TestParseHashResponseWrongThreatEntryType) {
+  std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager());
+
+  base::Time now = base::Time::UnixEpoch();
+  SetTestClock(now, pm.get());
+
+  FindFullHashesResponse res;
+  res.mutable_negative_cache_duration()->set_seconds(600);
+  res.add_matches()->set_threat_entry_type(EXECUTABLE);
+
+  // Serialize.
+  std::string res_data;
+  res.SerializeToString(&res_data);
+
+  std::vector<FullHashInfo> full_hash_infos;
+  base::Time cache_expire;
+  EXPECT_FALSE(
+      pm->ParseHashResponse(res_data, &full_hash_infos, &cache_expire));
+
+  EXPECT_EQ(now + base::TimeDelta::FromSeconds(600), cache_expire);
+  // There should be no hash results.
+  EXPECT_EQ(0ul, full_hash_infos.size());
+}
+
+// Adds entries with a ThreatPatternType metadata.
+TEST_F(V4GetHashProtocolManagerTest, TestParseHashThreatPatternType) {
+  std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager());
+
+  base::Time now = base::Time::UnixEpoch();
+  SetTestClock(now, pm.get());
+
+  {
+    // Test social engineering pattern type.
+    FindFullHashesResponse se_res;
+    se_res.mutable_negative_cache_duration()->set_seconds(600);
+    ThreatMatch* se = se_res.add_matches();
+    se->set_threat_type(SOCIAL_ENGINEERING);
+    se->set_platform_type(CHROME_PLATFORM);
+    se->set_threat_entry_type(URL);
+    FullHash full_hash("Everything's shiny, Cap'n.");
+    se->mutable_threat()->set_hash(full_hash);
+    ThreatEntryMetadata::MetadataEntry* se_meta =
+        se->mutable_threat_entry_metadata()->add_entries();
+    se_meta->set_key("se_pattern_type");
+    se_meta->set_value("SOCIAL_ENGINEERING_LANDING");
+
+    std::string se_data;
+    se_res.SerializeToString(&se_data);
+
+    std::vector<FullHashInfo> full_hash_infos;
+    base::Time cache_expire;
+    EXPECT_TRUE(
+        pm->ParseHashResponse(se_data, &full_hash_infos, &cache_expire));
+    EXPECT_EQ(now + base::TimeDelta::FromSeconds(600), cache_expire);
+
+    // Ensure that the threat remains valid since we found a full hash match,
+    // even though the metadata information could not be parsed correctly.
+    ASSERT_EQ(1ul, full_hash_infos.size());
+    const FullHashInfo& fhi = full_hash_infos[0];
+    EXPECT_EQ(full_hash, fhi.full_hash);
+    const ListIdentifier list_id(CHROME_PLATFORM, URL, SOCIAL_ENGINEERING);
+    EXPECT_EQ(list_id, fhi.list_id);
+    EXPECT_EQ(ThreatPatternType::SOCIAL_ENGINEERING_LANDING,
+              fhi.metadata.threat_pattern_type);
+  }
+
+  {
+    // Test potentially harmful application pattern type.
+    FindFullHashesResponse pha_res;
+    pha_res.mutable_negative_cache_duration()->set_seconds(600);
+    ThreatMatch* pha = pha_res.add_matches();
+    pha->set_threat_type(POTENTIALLY_HARMFUL_APPLICATION);
+    pha->set_threat_entry_type(URL);
+    pha->set_platform_type(CHROME_PLATFORM);
+    FullHash full_hash("Not to fret.");
+    pha->mutable_threat()->set_hash(full_hash);
+    ThreatEntryMetadata::MetadataEntry* pha_meta =
+        pha->mutable_threat_entry_metadata()->add_entries();
+    pha_meta->set_key("pha_pattern_type");
+    pha_meta->set_value("LANDING");
+
+    std::string pha_data;
+    pha_res.SerializeToString(&pha_data);
+    std::vector<FullHashInfo> full_hash_infos;
+    base::Time cache_expire;
+    EXPECT_TRUE(
+        pm->ParseHashResponse(pha_data, &full_hash_infos, &cache_expire));
+    EXPECT_EQ(now + base::TimeDelta::FromSeconds(600), cache_expire);
+
+    ASSERT_EQ(1ul, full_hash_infos.size());
+    const FullHashInfo& fhi = full_hash_infos[0];
+    EXPECT_EQ(full_hash, fhi.full_hash);
+    const ListIdentifier list_id(CHROME_PLATFORM, URL,
+                                 POTENTIALLY_HARMFUL_APPLICATION);
+    EXPECT_EQ(list_id, fhi.list_id);
+    EXPECT_EQ(ThreatPatternType::MALWARE_LANDING,
+              fhi.metadata.threat_pattern_type);
+  }
+
+  {
+    // Test invalid pattern type.
+    FullHash full_hash("Not to fret.");
+    FindFullHashesResponse invalid_res;
+    invalid_res.mutable_negative_cache_duration()->set_seconds(600);
+    ThreatMatch* invalid = invalid_res.add_matches();
+    invalid->set_threat_type(POTENTIALLY_HARMFUL_APPLICATION);
+    invalid->set_threat_entry_type(URL);
+    invalid->set_platform_type(CHROME_PLATFORM);
+    invalid->mutable_threat()->set_hash(full_hash);
+    ThreatEntryMetadata::MetadataEntry* invalid_meta =
+        invalid->mutable_threat_entry_metadata()->add_entries();
+    invalid_meta->set_key("pha_pattern_type");
+    invalid_meta->set_value("INVALIDE_VALUE");
+
+    std::string invalid_data;
+    invalid_res.SerializeToString(&invalid_data);
+    std::vector<FullHashInfo> full_hash_infos;
+    base::Time cache_expire;
+    EXPECT_TRUE(
+        pm->ParseHashResponse(invalid_data, &full_hash_infos, &cache_expire));
+
+    // Ensure that the threat remains valid since we found a full hash match,
+    // even though the metadata information could not be parsed correctly.
+    ASSERT_EQ(1ul, full_hash_infos.size());
+    const auto& fhi = full_hash_infos[0];
+    EXPECT_EQ(full_hash, fhi.full_hash);
+    EXPECT_EQ(
+        ListIdentifier(CHROME_PLATFORM, URL, POTENTIALLY_HARMFUL_APPLICATION),
+        fhi.list_id);
+    EXPECT_EQ(ThreatPatternType::NONE, fhi.metadata.threat_pattern_type);
+  }
+}
+
+TEST_F(V4GetHashProtocolManagerTest, TestParseSubresourceFilterMetadata) {
+  typedef SubresourceFilterLevel Level;
+  typedef SubresourceFilterType Type;
+  const struct {
+    const char* abusive_type;
+    const char* bas_type;
+    SubresourceFilterMatch expected_match;
+  } test_cases[] = {
+      {"warn",
+       "enforce",
+       {{Type::ABUSIVE, Level::WARN}, {Type::BETTER_ADS, Level::ENFORCE}}},
+      {nullptr, "warn", {{Type::BETTER_ADS, Level::WARN}}},
+      {"asdf",
+       "",
+       {{Type::ABUSIVE, Level::ENFORCE}, {Type::BETTER_ADS, Level::ENFORCE}}},
+      {"warn", nullptr, {{Type::ABUSIVE, Level::WARN}}},
+      {nullptr, nullptr, {}},
+      {"",
+       "",
+       {{Type::ABUSIVE, Level::ENFORCE}, {Type::BETTER_ADS, Level::ENFORCE}}},
+  };
+
+  for (const auto& test_case : test_cases) {
+    SCOPED_TRACE(testing::Message() << "abusive: " << test_case.abusive_type
+                                    << " better ads: " << test_case.bas_type);
+    std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager());
+
+    base::Time now = base::Time::UnixEpoch();
+    SetTestClock(now, pm.get());
+    FindFullHashesResponse sf_res;
+    sf_res.mutable_negative_cache_duration()->set_seconds(600);
+    ThreatMatch* sf = sf_res.add_matches();
+    sf->set_threat_type(SUBRESOURCE_FILTER);
+    sf->set_platform_type(CHROME_PLATFORM);
+    sf->set_threat_entry_type(URL);
+    FullHash full_hash("Everything's shiny, Cap'n.");
+    sf->mutable_threat()->set_hash(full_hash);
+
+    // sf_absv.
+    if (test_case.abusive_type != nullptr) {
+      ThreatEntryMetadata::MetadataEntry* sf_absv =
+          sf->mutable_threat_entry_metadata()->add_entries();
+      sf_absv->set_key("sf_absv");
+      sf_absv->set_value(test_case.abusive_type);
+    }
+
+    // sf_bas
+    if (test_case.bas_type != nullptr) {
+      ThreatEntryMetadata::MetadataEntry* sf_bas =
+          sf->mutable_threat_entry_metadata()->add_entries();
+      sf_bas->set_key("sf_bas");
+      sf_bas->set_value(test_case.bas_type);
+    }
+
+    std::string sf_data;
+    sf_res.SerializeToString(&sf_data);
+
+    std::vector<FullHashInfo> full_hash_infos;
+    base::Time cache_expire;
+    EXPECT_TRUE(
+        pm->ParseHashResponse(sf_data, &full_hash_infos, &cache_expire));
+    EXPECT_EQ(now + base::TimeDelta::FromSeconds(600), cache_expire);
+
+    ASSERT_EQ(1ul, full_hash_infos.size());
+    const FullHashInfo& fhi = full_hash_infos[0];
+    EXPECT_EQ(full_hash, fhi.full_hash);
+    const ListIdentifier list_id(CHROME_PLATFORM, URL, SUBRESOURCE_FILTER);
+    EXPECT_EQ(list_id, fhi.list_id);
+    EXPECT_EQ(test_case.expected_match, fhi.metadata.subresource_filter_match);
+  }
+}
+
+// Adds metadata with a key value that is not "permission".
+TEST_F(V4GetHashProtocolManagerTest,
+       TestParseHashResponseNonPermissionMetadata) {
+  std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager());
+
+  base::Time now = base::Time::UnixEpoch();
+  SetTestClock(now, pm.get());
+
+  FullHash full_hash("Not to fret.");
+  FindFullHashesResponse res;
+  res.mutable_negative_cache_duration()->set_seconds(600);
+  ThreatMatch* m = res.add_matches();
+  m->set_threat_type(API_ABUSE);
+  // TODO(crbug.com/1030487): This special case for Android will no longer be
+  // needed once GetCurrentPlatformType() returns ANDROID_PLATFORM on Android.
+#if defined(OS_ANDROID)
+  m->set_platform_type(ANDROID_PLATFORM);
+#else
+  m->set_platform_type(GetCurrentPlatformType());
+#endif
+  m->set_threat_entry_type(URL);
+  m->mutable_threat()->set_hash(full_hash);
+  ThreatEntryMetadata::MetadataEntry* e =
+      m->mutable_threat_entry_metadata()->add_entries();
+  e->set_key("notpermission");
+  e->set_value("NOTGEOLOCATION");
+
+  // Serialize.
+  std::string res_data;
+  res.SerializeToString(&res_data);
+
+  std::vector<FullHashInfo> full_hash_infos;
+  base::Time cache_expire;
+  EXPECT_TRUE(pm->ParseHashResponse(res_data, &full_hash_infos, &cache_expire));
+
+  EXPECT_EQ(now + base::TimeDelta::FromSeconds(600), cache_expire);
+  ASSERT_EQ(1ul, full_hash_infos.size());
+  const auto& fhi = full_hash_infos[0];
+  EXPECT_EQ(full_hash, fhi.full_hash);
+  EXPECT_EQ(GetChromeUrlApiId(), fhi.list_id);
+  EXPECT_TRUE(fhi.metadata.api_permissions.empty());
+}
+
+TEST_F(V4GetHashProtocolManagerTest,
+       TestParseHashResponseInconsistentThreatTypes) {
+  std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager());
+
+  FindFullHashesResponse res;
+  res.mutable_negative_cache_duration()->set_seconds(600);
+  ThreatMatch* m1 = res.add_matches();
+  m1->set_threat_type(API_ABUSE);
+  m1->set_platform_type(CHROME_PLATFORM);
+  m1->set_threat_entry_type(URL);
+  m1->mutable_threat()->set_hash(FullHash("Everything's shiny, Cap'n."));
+  m1->mutable_threat_entry_metadata()->add_entries();
+  ThreatMatch* m2 = res.add_matches();
+  m2->set_threat_type(MALWARE_THREAT);
+  m2->set_threat_entry_type(URL);
+  m2->mutable_threat()->set_hash(FullHash("Not to fret."));
+
+  // Serialize.
+  std::string res_data;
+  res.SerializeToString(&res_data);
+
+  std::vector<FullHashInfo> full_hash_infos;
+  base::Time cache_expire;
+  EXPECT_FALSE(
+      pm->ParseHashResponse(res_data, &full_hash_infos, &cache_expire));
+}
+
+// Checks that results are looked up correctly in the cache.
+TEST_F(V4GetHashProtocolManagerTest, GetCachedResults) {
+  base::Time now = base::Time::UnixEpoch();
+  FullHash full_hash("example");
+  HashPrefix prefix("exam");
+  FullHashToStoreAndHashPrefixesMap matched_locally;
+  matched_locally[full_hash].emplace_back(GetUrlMalwareId(), prefix);
+  std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager());
+  FullHashCache* cache = pm->full_hash_cache_for_tests();
+
+  {
+    std::vector<HashPrefix> prefixes_to_request;
+    std::vector<FullHashInfo> cached_full_hash_infos;
+    cache->clear();
+
+    // Test with an empty cache. (Case: 2)
+    pm->GetFullHashCachedResults(matched_locally, now, &prefixes_to_request,
+                                 &cached_full_hash_infos);
+    EXPECT_TRUE(cache->empty());
+    ASSERT_EQ(1ul, prefixes_to_request.size());
+    EXPECT_EQ(prefix, prefixes_to_request[0]);
+    EXPECT_TRUE(cached_full_hash_infos.empty());
+  }
+
+  {
+    std::vector<HashPrefix> prefixes_to_request;
+    std::vector<FullHashInfo> cached_full_hash_infos;
+    cache->clear();
+
+    // Prefix has a cache entry but full hash is not there. (Case: 1-b-i)
+    CachedHashPrefixInfo* entry = &(*cache)[prefix];
+    entry->negative_expiry = now + base::TimeDelta::FromMinutes(5);
+    pm->GetFullHashCachedResults(matched_locally, now, &prefixes_to_request,
+                                 &cached_full_hash_infos);
+    EXPECT_TRUE(prefixes_to_request.empty());
+    EXPECT_TRUE(cached_full_hash_infos.empty());
+  }
+
+  {
+    std::vector<HashPrefix> prefixes_to_request;
+    std::vector<FullHashInfo> cached_full_hash_infos;
+    cache->clear();
+
+    // Expired negative cache entry. (Case: 1-b-ii)
+    CachedHashPrefixInfo* entry = &(*cache)[prefix];
+    entry->negative_expiry = now - base::TimeDelta::FromMinutes(5);
+    pm->GetFullHashCachedResults(matched_locally, now, &prefixes_to_request,
+                                 &cached_full_hash_infos);
+    ASSERT_EQ(1ul, prefixes_to_request.size());
+    EXPECT_EQ(prefix, prefixes_to_request[0]);
+    EXPECT_TRUE(cached_full_hash_infos.empty());
+  }
+
+  {
+    std::vector<HashPrefix> prefixes_to_request;
+    std::vector<FullHashInfo> cached_full_hash_infos;
+    cache->clear();
+
+    // Now put unexpired full hash in the cache. (Case: 1-a-i)
+    CachedHashPrefixInfo* entry = &(*cache)[prefix];
+    entry->negative_expiry = now + base::TimeDelta::FromMinutes(5);
+    entry->full_hash_infos.emplace_back(full_hash, GetUrlMalwareId(),
+                                        now + base::TimeDelta::FromMinutes(3));
+    pm->GetFullHashCachedResults(matched_locally, now, &prefixes_to_request,
+                                 &cached_full_hash_infos);
+    EXPECT_TRUE(prefixes_to_request.empty());
+    ASSERT_EQ(1ul, cached_full_hash_infos.size());
+    EXPECT_EQ(full_hash, cached_full_hash_infos[0].full_hash);
+  }
+
+  {
+    std::vector<HashPrefix> prefixes_to_request;
+    std::vector<FullHashInfo> cached_full_hash_infos;
+    cache->clear();
+
+    // Expire the full hash in the cache. (Case: 1-a-ii)
+    CachedHashPrefixInfo* entry = &(*cache)[prefix];
+    entry->negative_expiry = now + base::TimeDelta::FromMinutes(5);
+    entry->full_hash_infos.emplace_back(full_hash, GetUrlMalwareId(),
+                                        now - base::TimeDelta::FromMinutes(3));
+    pm->GetFullHashCachedResults(matched_locally, now, &prefixes_to_request,
+                                 &cached_full_hash_infos);
+    ASSERT_EQ(1ul, prefixes_to_request.size());
+    EXPECT_EQ(prefix, prefixes_to_request[0]);
+    EXPECT_TRUE(cached_full_hash_infos.empty());
+  }
+}
+
+TEST_F(V4GetHashProtocolManagerTest, TestUpdatesAreMerged) {
+  // We'll add one of the requested FullHashInfo objects into the cache, and
+  // inject the other one as a response from the server. The result should
+  // include both FullHashInfo objects.
+
+  std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager());
+  HashPrefix prefix_1("exam");
+  FullHash full_hash_1("example");
+  HashPrefix prefix_2("Everything");
+  FullHash full_hash_2("Everything's shiny, Cap'n.");
+
+  base::Time now = Time::Now();
+  SetTestClock(now, pm.get());
+
+  FullHashCache* cache = pm->full_hash_cache_for_tests();
+  CachedHashPrefixInfo* entry = &(*cache)[prefix_1];
+  entry->negative_expiry = now + base::TimeDelta::FromMinutes(100);
+  // Put one unexpired full hash in the cache for a store we'll look in.
+  entry->full_hash_infos.emplace_back(full_hash_1, GetUrlMalwareId(),
+                                      now + base::TimeDelta::FromSeconds(200));
+  // Put one unexpired full hash in the cache for a store we'll not look in.
+  entry->full_hash_infos.emplace_back(full_hash_1, GetUrlSocEngId(),
+                                      now + base::TimeDelta::FromSeconds(200));
+
+  // Request full hash information from two stores.
+  FullHashToStoreAndHashPrefixesMap matched_locally;
+  matched_locally[full_hash_1].push_back(
+      StoreAndHashPrefix(GetUrlMalwareId(), prefix_1));
+  matched_locally[full_hash_2].push_back(
+      StoreAndHashPrefix(GetChromeUrlApiId(), prefix_2));
+
+  // Expect full hash information from both stores.
+  std::vector<FullHashInfo> expected_results;
+  expected_results.emplace_back(full_hash_1, GetUrlMalwareId(),
+                                now + base::TimeDelta::FromSeconds(200));
+  expected_results.emplace_back(full_hash_2, GetChromeUrlApiId(),
+                                now + base::TimeDelta::FromSeconds(300));
+  expected_results[1].metadata.api_permissions.insert("NOTIFICATIONS");
+
+  pm->GetFullHashes(
+      matched_locally, {},
+      base::BindOnce(&V4GetHashProtocolManagerTest::ValidateGetV4HashResults,
+                     base::Unretained(this), expected_results));
+
+  SetupFetcherToReturnOKResponse(pm.get(), GetStockV4HashResponseInfos());
+
+  // No error, back off multiplier is unchanged.
+  EXPECT_EQ(0ul, pm->gethash_error_count_);
+  EXPECT_EQ(1ul, pm->gethash_back_off_mult_);
+
+  // Verify the state of the cache.
+  ASSERT_EQ(2u, cache->size());
+  const CachedHashPrefixInfo& cached_result_1 = cache->at(prefix_1);
+  EXPECT_EQ(cached_result_1.negative_expiry,
+            now + base::TimeDelta::FromMinutes(100));
+  ASSERT_EQ(2u, cached_result_1.full_hash_infos.size());
+  EXPECT_EQ(full_hash_1, cached_result_1.full_hash_infos[0].full_hash);
+  EXPECT_EQ(GetUrlMalwareId(), cached_result_1.full_hash_infos[0].list_id);
+
+  const CachedHashPrefixInfo& cached_result_2 = cache->at(prefix_2);
+  EXPECT_EQ(cached_result_2.negative_expiry,
+            now + base::TimeDelta::FromSeconds(600));
+  ASSERT_EQ(1u, cached_result_2.full_hash_infos.size());
+  EXPECT_EQ(full_hash_2, cached_result_2.full_hash_infos[0].full_hash);
+  EXPECT_EQ(GetChromeUrlApiId(), cached_result_2.full_hash_infos[0].list_id);
+  EXPECT_TRUE(callback_called());
+}
+
+// The server responds back with full hash information containing metadata
+// information for one of the full hashes for the URL in test.
+TEST_F(V4GetHashProtocolManagerTest, TestGetFullHashesWithApisMergesMetadata) {
+  const GURL url("https://www.example.com/more");
+  ThreatMetadata expected_md;
+  expected_md.api_permissions.insert("NOTIFICATIONS");
+  expected_md.api_permissions.insert("AUDIO_CAPTURE");
+  std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager());
+  pm->GetFullHashesWithApis(
+      url, {} /* list_client_states */,
+      base::BindOnce(&V4GetHashProtocolManagerTest::ValidateGetV4ApiResults,
+                     base::Unretained(this), expected_md));
+
+  // The following two random looking strings value are two of the full hashes
+  // produced by UrlToFullHashes in v4_protocol_manager_util.h for the URL:
+  // "https://www.example.com"
+  std::vector<ResponseInfo> infos;
+  FullHash full_hash;
+  base::Base64Decode("1ZzJ0/7NjPkg6t0DAS8L5Jf7jA48Pn7opQcP4UXYeXc=",
+                     &full_hash);
+  ResponseInfo info(full_hash, GetChromeUrlApiId());
+  info.key_values.emplace_back("permission", "NOTIFICATIONS");
+  infos.push_back(info);
+
+  base::Base64Decode("c9mG4AkGXxgsELy2pF2z1u2pSY-JMGVK8mU_ipOM2AE=",
+                     &full_hash);
+  info = ResponseInfo(full_hash, GetChromeUrlApiId());
+  info.key_values.emplace_back("permission", "AUDIO_CAPTURE");
+  infos.push_back(info);
+
+  full_hash = FullHash("Everything's shiny, Cap'n.");
+  info = ResponseInfo(full_hash, GetChromeUrlApiId());
+  info.key_values.emplace_back("permission", "GEOLOCATION");
+  infos.push_back(info);
+  SetupFetcherToReturnOKResponse(pm.get(), infos);
+
+  EXPECT_TRUE(callback_called());
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/db/v4_local_database_manager.cc b/components/safe_browsing/core/db/v4_local_database_manager.cc
new file mode 100644
index 0000000..a323cb16
--- /dev/null
+++ b/components/safe_browsing/core/db/v4_local_database_manager.cc
@@ -0,0 +1,1062 @@
+// 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 "components/safe_browsing/core/db/v4_local_database_manager.h"
+
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/files/file_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/string_tokenizer.h"
+#include "base/task/post_task.h"
+#include "build/branding_buildflags.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/core/realtime/policy_engine.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "crypto/sha2.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+
+using content::BrowserThread;
+
+namespace safe_browsing {
+
+namespace {
+
+using CommandLineSwitchAndThreatType = std::pair<std::string, ThreatType>;
+
+// The expiration time of the full hash stored in the artificial database.
+const int64_t kFullHashExpiryTimeInMinutes = 60;
+
+const ThreatSeverity kLeastSeverity =
+    std::numeric_limits<ThreatSeverity>::max();
+
+// The list of the name of any store files that are no longer used and can be
+// safely deleted from the disk. There's no overlap allowed between the files
+// on this list and the list returned by GetListInfos().
+const char* const kStoreFileNamesToDelete[] = {
+    "AnyIpMalware.store", "ChromeFilenameClientIncident.store",
+    "UrlSuspiciousSiteId.store"};
+
+ListInfos GetListInfos() {
+// NOTE(vakh): When adding a store here, add the corresponding store-specific
+// histograms also.
+// The first argument to ListInfo specifies whether to sync hash prefixes for
+// that list. This can be false for two reasons:
+// - The server doesn't support that list yet. Once the server adds support
+//   for it, it can be changed to true.
+// - The list doesn't have hash prefixes to match. All requests lead to full
+//   hash checks. For instance: GetChromeUrlApiId()
+
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+  const bool kSyncOnlyOnChromeBuilds = true;
+#else
+  const bool kSyncOnlyOnChromeBuilds = false;
+#endif
+  const bool kSyncAlways = true;
+  const bool kSyncNever = false;
+  return ListInfos({
+      ListInfo(kSyncAlways, "IpMalware.store", GetIpMalwareId(),
+               SB_THREAT_TYPE_UNUSED),
+      ListInfo(kSyncAlways, "UrlSoceng.store", GetUrlSocEngId(),
+               SB_THREAT_TYPE_URL_PHISHING),
+      ListInfo(kSyncAlways, "UrlMalware.store", GetUrlMalwareId(),
+               SB_THREAT_TYPE_URL_MALWARE),
+      ListInfo(kSyncAlways, "UrlUws.store", GetUrlUwsId(),
+               SB_THREAT_TYPE_URL_UNWANTED),
+      ListInfo(kSyncAlways, "UrlMalBin.store", GetUrlMalBinId(),
+               SB_THREAT_TYPE_URL_BINARY_MALWARE),
+      ListInfo(kSyncAlways, "ChromeExtMalware.store", GetChromeExtMalwareId(),
+               SB_THREAT_TYPE_EXTENSION),
+      ListInfo(kSyncOnlyOnChromeBuilds, "CertCsdDownloadWhitelist.store",
+               GetCertCsdDownloadWhitelistId(), SB_THREAT_TYPE_UNUSED),
+      ListInfo(kSyncOnlyOnChromeBuilds, "ChromeUrlClientIncident.store",
+               GetChromeUrlClientIncidentId(),
+               SB_THREAT_TYPE_BLACKLISTED_RESOURCE),
+      ListInfo(kSyncAlways, "UrlBilling.store", GetUrlBillingId(),
+               SB_THREAT_TYPE_BILLING),
+      ListInfo(kSyncOnlyOnChromeBuilds, "UrlCsdDownloadWhitelist.store",
+               GetUrlCsdDownloadWhitelistId(), SB_THREAT_TYPE_UNUSED),
+      ListInfo(kSyncOnlyOnChromeBuilds, "UrlCsdWhitelist.store",
+               GetUrlCsdWhitelistId(), SB_THREAT_TYPE_CSD_WHITELIST),
+      ListInfo(kSyncOnlyOnChromeBuilds, "UrlSubresourceFilter.store",
+               GetUrlSubresourceFilterId(), SB_THREAT_TYPE_SUBRESOURCE_FILTER),
+      ListInfo(kSyncOnlyOnChromeBuilds, "UrlSuspiciousSite.store",
+               GetUrlSuspiciousSiteId(), SB_THREAT_TYPE_SUSPICIOUS_SITE),
+      ListInfo(kSyncNever, "", GetChromeUrlApiId(), SB_THREAT_TYPE_API_ABUSE),
+      ListInfo(kSyncOnlyOnChromeBuilds, "UrlHighConfidenceAllowlist.store",
+               GetUrlHighConfidenceAllowlistId(),
+               SB_THREAT_TYPE_HIGH_CONFIDENCE_ALLOWLIST),
+  });
+  // NOTE(vakh): IMPORTANT: Please make sure that the server already supports
+  // any list before adding it to this list otherwise the prefix updates break
+  // for all Canary users.
+}
+
+std::vector<CommandLineSwitchAndThreatType> GetSwitchAndThreatTypes() {
+  static const std::vector<CommandLineSwitchAndThreatType>
+      command_line_switch_and_threat_type = {
+          {"mark_as_phishing", SOCIAL_ENGINEERING},
+          {"mark_as_malware", MALWARE_THREAT},
+          {"mark_as_uws", UNWANTED_SOFTWARE}};
+  return command_line_switch_and_threat_type;
+}
+
+// Returns the severity information about a given SafeBrowsing list. The lowest
+// value is 0, which represents the most severe list.
+ThreatSeverity GetThreatSeverity(const ListIdentifier& list_id) {
+  switch (list_id.threat_type()) {
+    case MALWARE_THREAT:
+    case SOCIAL_ENGINEERING:
+    case MALICIOUS_BINARY:
+      return 0;
+    case UNWANTED_SOFTWARE:
+      return 1;
+    case API_ABUSE:
+    case CLIENT_INCIDENT:
+    case SUBRESOURCE_FILTER:
+      return 2;
+    case CSD_WHITELIST:
+    case HIGH_CONFIDENCE_ALLOWLIST:
+      return 3;
+    case SUSPICIOUS:
+      return 4;
+    case BILLING:
+      return 15;
+    default:
+      NOTREACHED() << "Unexpected ThreatType encountered: "
+                   << list_id.threat_type();
+      return kLeastSeverity;
+  }
+}
+
+// This is only valid for types that are passed to GetBrowseUrl().
+ListIdentifier GetUrlIdFromSBThreatType(SBThreatType sb_threat_type) {
+  switch (sb_threat_type) {
+    case SB_THREAT_TYPE_URL_MALWARE:
+      return GetUrlMalwareId();
+
+    case SB_THREAT_TYPE_URL_PHISHING:
+      return GetUrlSocEngId();
+
+    case SB_THREAT_TYPE_URL_UNWANTED:
+      return GetUrlUwsId();
+
+    case SB_THREAT_TYPE_SUSPICIOUS_SITE:
+      return GetUrlSuspiciousSiteId();
+
+    case SB_THREAT_TYPE_BILLING:
+      return GetUrlBillingId();
+
+    default:
+      NOTREACHED();
+      // Compiler requires a return statement here.
+      return GetUrlMalwareId();
+  }
+}
+
+StoresToCheck CreateStoresToCheckFromSBThreatTypeSet(
+    const SBThreatTypeSet& threat_types) {
+  StoresToCheck stores_to_check;
+  for (SBThreatType sb_threat_type : threat_types) {
+    stores_to_check.insert(GetUrlIdFromSBThreatType(sb_threat_type));
+  }
+  return stores_to_check;
+}
+
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum StoreAvailabilityResult {
+  // Unknown availability. This is unexpected.
+  UNKNOWN = 0,
+
+  // The local database is not enabled.
+  NOT_ENABLED = 1,
+
+  // The database is still being loaded.
+  DATABASE_UNAVAILABLE = 2,
+
+  // The requested store is unavailable.
+  STORE_UNAVAILABLE = 3,
+
+  // The store is available.
+  AVAILABLE = 4,
+  COUNT,
+};
+
+}  // namespace
+
+V4LocalDatabaseManager::PendingCheck::PendingCheck(
+    Client* client,
+    ClientCallbackType client_callback_type,
+    const StoresToCheck& stores_to_check,
+    const std::vector<GURL>& urls)
+    : client(client),
+      client_callback_type(client_callback_type),
+      most_severe_threat_type(SB_THREAT_TYPE_SAFE),
+      stores_to_check(stores_to_check),
+      urls(urls) {
+  for (const auto& url : urls) {
+    V4ProtocolManagerUtil::UrlToFullHashes(url, &full_hashes);
+  }
+  full_hash_threat_types.assign(full_hashes.size(), SB_THREAT_TYPE_SAFE);
+}
+
+V4LocalDatabaseManager::PendingCheck::PendingCheck(
+    Client* client,
+    ClientCallbackType client_callback_type,
+    const StoresToCheck& stores_to_check,
+    const std::set<FullHash>& full_hashes_set)
+    : client(client),
+      client_callback_type(client_callback_type),
+      most_severe_threat_type(SB_THREAT_TYPE_SAFE),
+      stores_to_check(stores_to_check) {
+  full_hashes.assign(full_hashes_set.begin(), full_hashes_set.end());
+  DCHECK(full_hashes.size());
+  full_hash_threat_types.assign(full_hashes.size(), SB_THREAT_TYPE_SAFE);
+}
+
+V4LocalDatabaseManager::PendingCheck::~PendingCheck() {}
+
+// static
+const V4LocalDatabaseManager*
+    V4LocalDatabaseManager::current_local_database_manager_;
+
+// static
+scoped_refptr<V4LocalDatabaseManager> V4LocalDatabaseManager::Create(
+    const base::FilePath& base_path,
+    ExtendedReportingLevelCallback extended_reporting_level_callback) {
+  return base::WrapRefCounted(new V4LocalDatabaseManager(
+      base_path, extended_reporting_level_callback, nullptr));
+}
+
+void V4LocalDatabaseManager::CollectDatabaseManagerInfo(
+    DatabaseManagerInfo* database_manager_info,
+    FullHashCacheInfo* full_hash_cache_info) const {
+  if (v4_update_protocol_manager_) {
+    v4_update_protocol_manager_->CollectUpdateInfo(
+        database_manager_info->mutable_update_info());
+  }
+  if (v4_database_) {
+    v4_database_->CollectDatabaseInfo(
+        database_manager_info->mutable_database_info());
+  }
+  if (v4_get_hash_protocol_manager_) {
+    v4_get_hash_protocol_manager_->CollectFullHashCacheInfo(
+        full_hash_cache_info);
+  }
+}
+
+V4LocalDatabaseManager::V4LocalDatabaseManager(
+    const base::FilePath& base_path,
+    ExtendedReportingLevelCallback extended_reporting_level_callback,
+    scoped_refptr<base::SequencedTaskRunner> task_runner_for_tests)
+    : base_path_(base_path),
+      extended_reporting_level_callback_(extended_reporting_level_callback),
+      list_infos_(GetListInfos()),
+      task_runner_(task_runner_for_tests
+                       ? task_runner_for_tests
+                       : base::CreateSequencedTaskRunner(
+                             {base::ThreadPool(), base::MayBlock(),
+                              base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})) {
+  DCHECK(!base_path_.empty());
+  DCHECK(!list_infos_.empty());
+
+  DeleteUnusedStoreFiles();
+
+  DVLOG(1) << "V4LocalDatabaseManager::V4LocalDatabaseManager: "
+           << "base_path_: " << base_path_.AsUTF8Unsafe();
+}
+
+V4LocalDatabaseManager::~V4LocalDatabaseManager() {
+  DCHECK(!enabled_);
+}
+
+//
+// Start: SafeBrowsingDatabaseManager implementation
+//
+
+void V4LocalDatabaseManager::CancelCheck(Client* client) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK(enabled_);
+
+  auto pending_it = std::find_if(
+      std::begin(pending_checks_), std::end(pending_checks_),
+      [client](const PendingCheck* check) { return check->client == client; });
+  if (pending_it != pending_checks_.end()) {
+    pending_checks_.erase(pending_it);
+  }
+
+  auto queued_it =
+      std::find_if(std::begin(queued_checks_), std::end(queued_checks_),
+                   [&client](const std::unique_ptr<PendingCheck>& check) {
+                     return check->client == client;
+                   });
+  if (queued_it != queued_checks_.end()) {
+    queued_checks_.erase(queued_it);
+  }
+}
+
+bool V4LocalDatabaseManager::CanCheckResourceType(
+    content::ResourceType resource_type) const {
+  // We check all types since most checks are fast.
+  return true;
+}
+
+bool V4LocalDatabaseManager::CanCheckUrl(const GURL& url) const {
+  return url.SchemeIsHTTPOrHTTPS() || url.SchemeIs(url::kFtpScheme) ||
+         url.SchemeIsWSOrWSS();
+}
+
+bool V4LocalDatabaseManager::ChecksAreAlwaysAsync() const {
+  return false;
+}
+
+bool V4LocalDatabaseManager::CheckBrowseUrl(const GURL& url,
+                                            const SBThreatTypeSet& threat_types,
+                                            Client* client) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK(!threat_types.empty());
+  DCHECK(SBThreatTypeSetIsValidForCheckBrowseUrl(threat_types));
+
+  if (!enabled_ || !CanCheckUrl(url)) {
+    return true;
+  }
+
+  std::unique_ptr<PendingCheck> check = std::make_unique<PendingCheck>(
+      client, ClientCallbackType::CHECK_BROWSE_URL,
+      CreateStoresToCheckFromSBThreatTypeSet(threat_types),
+      std::vector<GURL>(1, url));
+
+  bool safe_synchronously = HandleCheck(std::move(check));
+  UMA_HISTOGRAM_BOOLEAN("SafeBrowsing.CheckBrowseUrl.HasLocalMatch",
+                        !safe_synchronously);
+  return safe_synchronously;
+}
+
+bool V4LocalDatabaseManager::CheckDownloadUrl(
+    const std::vector<GURL>& url_chain,
+    Client* client) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  if (!enabled_ || url_chain.empty()) {
+    return true;
+  }
+
+  std::unique_ptr<PendingCheck> check = std::make_unique<PendingCheck>(
+      client, ClientCallbackType::CHECK_DOWNLOAD_URLS,
+      StoresToCheck({GetUrlMalBinId()}), url_chain);
+
+  return HandleCheck(std::move(check));
+}
+
+bool V4LocalDatabaseManager::CheckExtensionIDs(
+    const std::set<FullHash>& extension_ids,
+    Client* client) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  if (!enabled_) {
+    return true;
+  }
+
+  std::unique_ptr<PendingCheck> check = std::make_unique<PendingCheck>(
+      client, ClientCallbackType::CHECK_EXTENSION_IDS,
+      StoresToCheck({GetChromeExtMalwareId()}), extension_ids);
+
+  return HandleCheck(std::move(check));
+}
+
+bool V4LocalDatabaseManager::CheckResourceUrl(const GURL& url, Client* client) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  StoresToCheck stores_to_check({GetChromeUrlClientIncidentId()});
+
+  if (!CanCheckUrl(url) || !AreAllStoresAvailableNow(stores_to_check)) {
+    // Fail open: Mark resource as safe immediately.
+    // TODO(nparker): This should queue the request if the DB isn't yet
+    // loaded, and later decide if this store is available.
+    // Currently this is the only store that requires full-hash-checks
+    // AND isn't supported on Chromium, so it's unique.
+    return true;
+  }
+
+  std::unique_ptr<PendingCheck> check = std::make_unique<PendingCheck>(
+      client, ClientCallbackType::CHECK_RESOURCE_URL, stores_to_check,
+      std::vector<GURL>(1, url));
+
+  return HandleCheck(std::move(check));
+}
+
+AsyncMatch V4LocalDatabaseManager::CheckUrlForHighConfidenceAllowlist(
+    const GURL& url,
+    Client* client) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  StoresToCheck stores_to_check({GetUrlHighConfidenceAllowlistId()});
+  if (!enabled_ || !CanCheckUrl(url) ||
+      !AreAllStoresAvailableNow(stores_to_check)) {
+    // NOTE(vakh): If Safe Browsing isn't enabled yet, or if the URL isn't a
+    // navigation URL, or if the allowlist isn't ready yet, return MATCH.
+    // The full URL check won't be performed, but hash-based check will still
+    // be done.
+    return AsyncMatch::MATCH;
+  }
+
+  std::unique_ptr<PendingCheck> check = std::make_unique<PendingCheck>(
+      client, ClientCallbackType::CHECK_HIGH_CONFIDENCE_ALLOWLIST,
+      stores_to_check, std::vector<GURL>(1, url));
+
+  return HandleWhitelistCheck(std::move(check));
+}
+
+bool V4LocalDatabaseManager::CheckUrlForSubresourceFilter(const GURL& url,
+                                                          Client* client) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  StoresToCheck stores_to_check(
+      {GetUrlSocEngId(), GetUrlSubresourceFilterId()});
+  if (!AreAnyStoresAvailableNow(stores_to_check) || !CanCheckUrl(url)) {
+    return true;
+  }
+
+  std::unique_ptr<PendingCheck> check = std::make_unique<PendingCheck>(
+      client, ClientCallbackType::CHECK_URL_FOR_SUBRESOURCE_FILTER,
+      stores_to_check, std::vector<GURL>(1, url));
+
+  return HandleCheck(std::move(check));
+}
+
+AsyncMatch V4LocalDatabaseManager::CheckCsdWhitelistUrl(const GURL& url,
+                                                        Client* client) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  StoresToCheck stores_to_check({GetUrlCsdWhitelistId()});
+  if (!AreAllStoresAvailableNow(stores_to_check) || !CanCheckUrl(url)) {
+    // Fail open: Whitelist everything. Otherwise we may run the
+    // CSD phishing/malware detector on popular domains and generate
+    // undue load on the client and server, or send Password Reputation
+    // requests on popular sites. This has the effect of disabling
+    // CSD phishing/malware detection and password reputation service
+    // until the store is first synced and/or loaded from disk.
+    return AsyncMatch::MATCH;
+  }
+
+  std::unique_ptr<PendingCheck> check = std::make_unique<PendingCheck>(
+      client, ClientCallbackType::CHECK_CSD_WHITELIST, stores_to_check,
+      std::vector<GURL>(1, url));
+
+  return HandleWhitelistCheck(std::move(check));
+}
+
+bool V4LocalDatabaseManager::MatchDownloadWhitelistString(
+    const std::string& str) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  StoresToCheck stores_to_check({GetCertCsdDownloadWhitelistId()});
+  if (!AreAllStoresAvailableNow(stores_to_check)) {
+    // Fail close: Whitelist nothing. This may generate download-protection
+    // pings for whitelisted binaries, but that's fine.
+    return false;
+  }
+
+  return HandleHashSynchronously(crypto::SHA256HashString(str),
+                                 stores_to_check);
+}
+
+bool V4LocalDatabaseManager::MatchDownloadWhitelistUrl(const GURL& url) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  StoresToCheck stores_to_check({GetUrlCsdDownloadWhitelistId()});
+
+  if (!AreAllStoresAvailableNow(stores_to_check) || !CanCheckUrl(url)) {
+    // Fail close: Whitelist nothing. This may generate download-protection
+    // pings for whitelisted domains, but that's fine.
+    return false;
+  }
+
+  return HandleUrlSynchronously(url, stores_to_check);
+}
+
+bool V4LocalDatabaseManager::MatchMalwareIP(const std::string& ip_address) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  if (!enabled_ || !v4_database_) {
+    return false;
+  }
+
+  FullHash hashed_encoded_ip;
+  if (!V4ProtocolManagerUtil::IPAddressToEncodedIPV6Hash(ip_address,
+                                                         &hashed_encoded_ip)) {
+    return false;
+  }
+
+  return HandleHashSynchronously(hashed_encoded_ip,
+                                 StoresToCheck({GetIpMalwareId()}));
+}
+
+ThreatSource V4LocalDatabaseManager::GetThreatSource() const {
+  return ThreatSource::LOCAL_PVER4;
+}
+
+bool V4LocalDatabaseManager::IsDownloadProtectionEnabled() const {
+  // TODO(vakh): Investigate the possibility of using a command line switch for
+  // this instead.
+  return true;
+}
+
+bool V4LocalDatabaseManager::IsSupported() const {
+  return true;
+}
+
+void V4LocalDatabaseManager::StartOnIOThread(
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    const V4ProtocolConfig& config) {
+  SafeBrowsingDatabaseManager::StartOnIOThread(url_loader_factory, config);
+
+  db_updated_callback_ = base::BindRepeating(
+      &V4LocalDatabaseManager::DatabaseUpdated, weak_factory_.GetWeakPtr());
+
+  SetupRealTimeUrlLookupService(url_loader_factory);
+  SetupUpdateProtocolManager(url_loader_factory, config);
+  SetupDatabase();
+
+  enabled_ = true;
+
+  current_local_database_manager_ = this;
+}
+
+void V4LocalDatabaseManager::StopOnIOThread(bool shutdown) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  enabled_ = false;
+
+  current_local_database_manager_ = nullptr;
+
+  pending_checks_.clear();
+
+  RespondSafeToQueuedChecks();
+
+  // Delete the V4Database. Any pending writes to disk are completed.
+  // This operation happens on the task_runner on which v4_database_ operates
+  // and doesn't block the IO thread.
+  V4Database::Destroy(std::move(v4_database_));
+
+  rt_url_lookup_service_.reset();
+
+  // Delete the V4UpdateProtocolManager.
+  // This cancels any in-flight update request.
+  v4_update_protocol_manager_.reset();
+
+  db_updated_callback_.Reset();
+
+  SafeBrowsingDatabaseManager::StopOnIOThread(shutdown);
+}
+
+//
+// End: SafeBrowsingDatabaseManager implementation
+//
+
+void V4LocalDatabaseManager::DatabaseReadyForChecks(
+    std::unique_ptr<V4Database> v4_database) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  // The following check is needed because it is possible that by the time the
+  // database is ready, StopOnIOThread has been called.
+  if (enabled_) {
+    V4Database::Destroy(std::move(v4_database_));
+    v4_database_ = std::move(v4_database);
+
+    v4_database_->RecordFileSizeHistograms();
+
+    PopulateArtificialDatabase();
+
+    // The consistency of the stores read from the disk needs to verified. Post
+    // that task on the task runner. It calls |DatabaseReadyForUpdates|
+    // callback with the stores to reset, if any, and then we can schedule the
+    // database updates.
+    v4_database_->VerifyChecksum(
+        base::BindOnce(&V4LocalDatabaseManager::DatabaseReadyForUpdates,
+                       weak_factory_.GetWeakPtr()));
+
+    ProcessQueuedChecks();
+  } else {
+    // Schedule the deletion of v4_database off IO thread.
+    V4Database::Destroy(std::move(v4_database));
+  }
+}
+
+void V4LocalDatabaseManager::DatabaseReadyForUpdates(
+    const std::vector<ListIdentifier>& stores_to_reset) {
+  if (enabled_) {
+    v4_database_->ResetStores(stores_to_reset);
+    UpdateListClientStates(GetStoreStateMap());
+
+    // The database is ready to process updates. Schedule them now.
+    v4_update_protocol_manager_->ScheduleNextUpdate(GetStoreStateMap());
+  }
+}
+
+void V4LocalDatabaseManager::DatabaseUpdated() {
+  if (enabled_) {
+    v4_update_protocol_manager_->ScheduleNextUpdate(GetStoreStateMap());
+
+    v4_database_->RecordFileSizeHistograms();
+    UpdateListClientStates(GetStoreStateMap());
+
+    base::PostTask(
+        FROM_HERE, {BrowserThread::UI},
+        base::BindOnce(
+            &SafeBrowsingDatabaseManager::NotifyDatabaseUpdateFinished, this));
+  }
+}
+
+void V4LocalDatabaseManager::DeleteUnusedStoreFiles() {
+  for (auto* const store_filename_to_delete : kStoreFileNamesToDelete) {
+    // Is the file marked for deletion also being used for a valid V4Store?
+    auto it = std::find_if(std::begin(list_infos_), std::end(list_infos_),
+                           [&store_filename_to_delete](ListInfo const& li) {
+                             return li.filename() == store_filename_to_delete;
+                           });
+    if (list_infos_.end() == it) {
+      const base::FilePath store_path =
+          base_path_.AppendASCII(store_filename_to_delete);
+      bool path_exists = base::PathExists(store_path);
+      base::UmaHistogramBoolean("SafeBrowsing.V4UnusedStoreFileExists" +
+                                    GetUmaSuffixForStore(store_path),
+                                path_exists);
+      if (!path_exists) {
+        continue;
+      }
+      task_runner_->PostTask(
+          FROM_HERE, base::BindOnce(base::IgnoreResult(&base::DeleteFile),
+                                    store_path, false /* recursive */));
+    } else {
+      NOTREACHED() << "Trying to delete a store file that's in use: "
+                   << store_filename_to_delete;
+    }
+  }
+}
+
+void V4LocalDatabaseManager::GetArtificialPrefixMatches(
+    const std::unique_ptr<PendingCheck>& check) {
+  if (artificially_marked_store_and_hash_prefixes_.empty()) {
+    return;
+  }
+  for (const auto& full_hash : check->full_hashes) {
+    for (const StoreAndHashPrefix& artificial_store_and_hash_prefix :
+         artificially_marked_store_and_hash_prefixes_) {
+      FullHash artificial_full_hash =
+          artificial_store_and_hash_prefix.hash_prefix;
+      DCHECK_EQ(crypto::kSHA256Length, artificial_full_hash.size());
+      if (artificial_full_hash == full_hash) {
+        (check->artificial_full_hash_to_store_and_hash_prefixes)[full_hash] = {
+            artificial_store_and_hash_prefix};
+      }
+    }
+  }
+}
+
+bool V4LocalDatabaseManager::GetPrefixMatches(
+    const std::unique_ptr<PendingCheck>& check) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK(enabled_);
+
+  check->full_hash_to_store_and_hash_prefixes.clear();
+  for (const auto& full_hash : check->full_hashes) {
+    StoreAndHashPrefixes matched_store_and_hash_prefixes;
+    v4_database_->GetStoresMatchingFullHash(full_hash, check->stores_to_check,
+                                            &matched_store_and_hash_prefixes);
+    if (!matched_store_and_hash_prefixes.empty()) {
+      (check->full_hash_to_store_and_hash_prefixes)[full_hash] =
+          matched_store_and_hash_prefixes;
+    }
+  }
+
+  return !check->full_hash_to_store_and_hash_prefixes.empty();
+}
+
+RealTimeUrlLookupService*
+V4LocalDatabaseManager::GetRealTimeUrlLookupService() {
+  return rt_url_lookup_service_.get();
+}
+
+void V4LocalDatabaseManager::GetSeverestThreatTypeAndMetadata(
+    const std::vector<FullHashInfo>& full_hash_infos,
+    const std::vector<FullHash>& full_hashes,
+    std::vector<SBThreatType>* full_hash_threat_types,
+    SBThreatType* most_severe_threat_type,
+    ThreatMetadata* metadata,
+    FullHash* matching_full_hash) {
+  ThreatSeverity most_severe_yet = kLeastSeverity;
+  for (const FullHashInfo& fhi : full_hash_infos) {
+    ThreatSeverity severity = GetThreatSeverity(fhi.list_id);
+    SBThreatType threat_type = GetSBThreatTypeForList(fhi.list_id);
+
+    const auto& it =
+        std::find(full_hashes.begin(), full_hashes.end(), fhi.full_hash);
+    DCHECK(it != full_hashes.end());
+    (*full_hash_threat_types)[it - full_hashes.begin()] = threat_type;
+
+    if (severity < most_severe_yet) {
+      most_severe_yet = severity;
+      *most_severe_threat_type = threat_type;
+      *metadata = fhi.metadata;
+      *matching_full_hash = fhi.full_hash;
+    }
+  }
+}
+
+StoresToCheck V4LocalDatabaseManager::GetStoresForFullHashRequests() {
+  StoresToCheck stores_for_full_hash;
+  for (const auto& info : list_infos_) {
+    stores_for_full_hash.insert(info.list_id());
+  }
+  return stores_for_full_hash;
+}
+
+std::unique_ptr<StoreStateMap> V4LocalDatabaseManager::GetStoreStateMap() {
+  return v4_database_->GetStoreStateMap();
+}
+
+// Returns the SBThreatType corresponding to a given SafeBrowsing list.
+SBThreatType V4LocalDatabaseManager::GetSBThreatTypeForList(
+    const ListIdentifier& list_id) {
+  auto it = std::find_if(
+      std::begin(list_infos_), std::end(list_infos_),
+      [&list_id](ListInfo const& li) { return li.list_id() == list_id; });
+  DCHECK(list_infos_.end() != it);
+  DCHECK_NE(SB_THREAT_TYPE_SAFE, it->sb_threat_type());
+  DCHECK_NE(SB_THREAT_TYPE_UNUSED, it->sb_threat_type());
+  return it->sb_threat_type();
+}
+
+AsyncMatch V4LocalDatabaseManager::HandleWhitelistCheck(
+    std::unique_ptr<PendingCheck> check) {
+  // We don't bother queuing whitelist checks since the DB will
+  // normally be available already -- whitelists are used after page load,
+  // and navigations are blocked until the DB is ready and dequeues checks.
+  // The caller should have already checked that the DB is ready.
+  DCHECK(v4_database_);
+
+  if (!GetPrefixMatches(check)) {
+    return AsyncMatch::NO_MATCH;
+  }
+
+  // Look for any full-length hash in the matches. If there is one,
+  // there's no need for a full-hash check. This saves bandwidth for
+  // very popular sites since they'll have full-length hashes locally.
+  // These loops will have exactly 1 entry most of the time.
+  for (const auto& entry : check->full_hash_to_store_and_hash_prefixes) {
+    for (const auto& store_and_prefix : entry.second) {
+      if (store_and_prefix.hash_prefix.size() == kMaxHashPrefixLength) {
+        return AsyncMatch::MATCH;
+      }
+    }
+  }
+
+  ScheduleFullHashCheck(std::move(check));
+  return AsyncMatch::ASYNC;
+}
+
+bool V4LocalDatabaseManager::HandleCheck(std::unique_ptr<PendingCheck> check) {
+  if (!v4_database_) {
+    queued_checks_.push_back(std::move(check));
+    return false;
+  }
+
+  GetPrefixMatches(check);
+  GetArtificialPrefixMatches(check);
+  if (check->full_hash_to_store_and_hash_prefixes.empty() &&
+      check->artificial_full_hash_to_store_and_hash_prefixes.empty()) {
+    return true;
+  }
+
+  ScheduleFullHashCheck(std::move(check));
+  return false;
+}
+
+void V4LocalDatabaseManager::PopulateArtificialDatabase() {
+  for (const auto& switch_and_threat_type : GetSwitchAndThreatTypes()) {
+    const std::string raw_artificial_urls =
+        base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+            switch_and_threat_type.first);
+    base::StringTokenizer tokenizer(raw_artificial_urls, ",");
+    while (tokenizer.GetNext()) {
+      ListIdentifier artificial_list_id(GetCurrentPlatformType(), URL,
+                                        switch_and_threat_type.second);
+      FullHash full_hash =
+          V4ProtocolManagerUtil::GetFullHash(GURL(tokenizer.token()));
+      artificially_marked_store_and_hash_prefixes_.emplace_back(
+          artificial_list_id, full_hash);
+    }
+  }
+}
+
+void V4LocalDatabaseManager::ScheduleFullHashCheck(
+    std::unique_ptr<PendingCheck> check) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  // Add check to pending_checks_ before scheduling PerformFullHashCheck so that
+  // even if the client calls CancelCheck before PerformFullHashCheck gets
+  // called, the check can be found in pending_checks_.
+  pending_checks_.insert(check.get());
+
+  // If the full hash matches one from the artificial list, don't send the
+  // request to the server.
+  if (!check->artificial_full_hash_to_store_and_hash_prefixes.empty()) {
+    std::vector<FullHashInfo> full_hash_infos;
+    for (const auto& entry :
+         check->artificial_full_hash_to_store_and_hash_prefixes) {
+      for (const auto& store_and_prefix : entry.second) {
+        ListIdentifier list_id = store_and_prefix.list_id;
+        base::Time next = base::Time::Now() + base::TimeDelta::FromMinutes(
+                                                  kFullHashExpiryTimeInMinutes);
+        full_hash_infos.emplace_back(entry.first, list_id, next);
+      }
+    }
+    base::PostTask(FROM_HERE, {BrowserThread::IO},
+                   base::BindOnce(&V4LocalDatabaseManager::OnFullHashResponse,
+                                  weak_factory_.GetWeakPtr(), std::move(check),
+                                  full_hash_infos));
+  } else {
+    // Post on the IO thread to enforce async behavior.
+    base::PostTask(
+        FROM_HERE, {BrowserThread::IO},
+        base::BindOnce(&V4LocalDatabaseManager::PerformFullHashCheck,
+                       weak_factory_.GetWeakPtr(), std::move(check)));
+  }
+}
+
+bool V4LocalDatabaseManager::HandleHashSynchronously(
+    const FullHash& hash,
+    const StoresToCheck& stores_to_check) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  std::set<FullHash> hashes{hash};
+  std::unique_ptr<PendingCheck> check = std::make_unique<PendingCheck>(
+      nullptr, ClientCallbackType::CHECK_OTHER, stores_to_check, hashes);
+
+  return GetPrefixMatches(check);
+}
+
+bool V4LocalDatabaseManager::HandleUrlSynchronously(
+    const GURL& url,
+    const StoresToCheck& stores_to_check) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  std::unique_ptr<PendingCheck> check = std::make_unique<PendingCheck>(
+      nullptr, ClientCallbackType::CHECK_OTHER, stores_to_check,
+      std::vector<GURL>(1, url));
+
+  return GetPrefixMatches(check);
+}
+
+void V4LocalDatabaseManager::OnFullHashResponse(
+    std::unique_ptr<PendingCheck> check,
+    const std::vector<FullHashInfo>& full_hash_infos) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  if (!enabled_) {
+    DCHECK(pending_checks_.empty());
+    return;
+  }
+
+  const auto it = pending_checks_.find(check.get());
+  if (it == pending_checks_.end()) {
+    // The check has since been cancelled.
+    return;
+  }
+
+  // Find out the most severe threat, if any, to report to the client.
+  GetSeverestThreatTypeAndMetadata(
+      full_hash_infos, check->full_hashes, &check->full_hash_threat_types,
+      &check->most_severe_threat_type, &check->url_metadata,
+      &check->matching_full_hash);
+  pending_checks_.erase(it);
+  RespondToClient(std::move(check));
+}
+
+void V4LocalDatabaseManager::PerformFullHashCheck(
+    std::unique_ptr<PendingCheck> check) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  DCHECK(enabled_);
+  DCHECK(!check->full_hash_to_store_and_hash_prefixes.empty());
+
+  FullHashToStoreAndHashPrefixesMap full_hash_to_store_and_hash_prefixes =
+      check->full_hash_to_store_and_hash_prefixes;
+  v4_get_hash_protocol_manager_->GetFullHashes(
+      full_hash_to_store_and_hash_prefixes, list_client_states_,
+      base::BindOnce(&V4LocalDatabaseManager::OnFullHashResponse,
+                     weak_factory_.GetWeakPtr(), std::move(check)));
+}
+
+void V4LocalDatabaseManager::ProcessQueuedChecks() {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  // Steal the queue to protect against reentrant CancelCheck() calls.
+  QueuedChecks checks;
+  checks.swap(queued_checks_);
+
+  for (auto& it : checks) {
+    if (!GetPrefixMatches(it)) {
+      RespondToClient(std::move(it));
+    } else {
+      pending_checks_.insert(it.get());
+      PerformFullHashCheck(std::move(it));
+    }
+  }
+}
+
+void V4LocalDatabaseManager::RespondSafeToQueuedChecks() {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  // Steal the queue to protect against reentrant CancelCheck() calls.
+  QueuedChecks checks;
+  checks.swap(queued_checks_);
+
+  for (std::unique_ptr<PendingCheck>& it : checks) {
+    RespondToClient(std::move(it));
+  }
+}
+
+void V4LocalDatabaseManager::RespondToClient(
+    std::unique_ptr<PendingCheck> check) {
+  DCHECK(check);
+
+  switch (check->client_callback_type) {
+    case ClientCallbackType::CHECK_BROWSE_URL:
+    case ClientCallbackType::CHECK_URL_FOR_SUBRESOURCE_FILTER:
+      DCHECK_EQ(1u, check->urls.size());
+      check->client->OnCheckBrowseUrlResult(
+          check->urls[0], check->most_severe_threat_type, check->url_metadata);
+      break;
+
+    case ClientCallbackType::CHECK_DOWNLOAD_URLS:
+      check->client->OnCheckDownloadUrlResult(check->urls,
+                                              check->most_severe_threat_type);
+      break;
+
+    case ClientCallbackType::CHECK_HIGH_CONFIDENCE_ALLOWLIST: {
+      DCHECK_EQ(1u, check->urls.size());
+      bool did_match_allowlist = check->most_severe_threat_type ==
+                                 SB_THREAT_TYPE_HIGH_CONFIDENCE_ALLOWLIST;
+      DCHECK(did_match_allowlist ||
+             check->most_severe_threat_type == SB_THREAT_TYPE_SAFE);
+      check->client->OnCheckUrlForHighConfidenceAllowlist(did_match_allowlist);
+      break;
+    }
+
+    case ClientCallbackType::CHECK_RESOURCE_URL:
+      DCHECK_EQ(1u, check->urls.size());
+      check->client->OnCheckResourceUrlResult(check->urls[0],
+                                              check->most_severe_threat_type,
+                                              check->matching_full_hash);
+      break;
+
+    case ClientCallbackType::CHECK_CSD_WHITELIST: {
+      DCHECK_EQ(1u, check->urls.size());
+      bool did_match_allowlist =
+          check->most_severe_threat_type == SB_THREAT_TYPE_CSD_WHITELIST;
+      DCHECK(did_match_allowlist ||
+             check->most_severe_threat_type == SB_THREAT_TYPE_SAFE);
+      check->client->OnCheckWhitelistUrlResult(did_match_allowlist);
+      break;
+    }
+
+    case ClientCallbackType::CHECK_EXTENSION_IDS: {
+      DCHECK_EQ(check->full_hash_threat_types.size(),
+                check->full_hashes.size());
+      std::set<FullHash> unsafe_extension_ids;
+      for (size_t i = 0; i < check->full_hash_threat_types.size(); i++) {
+        if (check->full_hash_threat_types[i] == SB_THREAT_TYPE_EXTENSION) {
+          unsafe_extension_ids.insert(check->full_hashes[i]);
+        }
+      }
+      check->client->OnCheckExtensionsResult(unsafe_extension_ids);
+      break;
+    }
+    case ClientCallbackType::CHECK_OTHER:
+      NOTREACHED() << "Unexpected client_callback_type encountered";
+  }
+}
+
+void V4LocalDatabaseManager::SetupDatabase() {
+  DCHECK(!base_path_.empty());
+  DCHECK(!list_infos_.empty());
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  // Do not create the database on the IO thread since this may be an expensive
+  // operation. Instead, do that on the task_runner and when the new database
+  // has been created, swap it out on the IO thread.
+  NewDatabaseReadyCallback db_ready_callback =
+      base::BindOnce(&V4LocalDatabaseManager::DatabaseReadyForChecks,
+                     weak_factory_.GetWeakPtr());
+  V4Database::Create(task_runner_, base_path_, list_infos_,
+                     std::move(db_ready_callback));
+}
+
+void V4LocalDatabaseManager::SetupRealTimeUrlLookupService(
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  rt_url_lookup_service_ =
+      std::make_unique<RealTimeUrlLookupService>(url_loader_factory);
+}
+
+void V4LocalDatabaseManager::SetupUpdateProtocolManager(
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    const V4ProtocolConfig& config) {
+  V4UpdateCallback update_callback =
+      base::BindRepeating(&V4LocalDatabaseManager::UpdateRequestCompleted,
+                          weak_factory_.GetWeakPtr());
+
+  v4_update_protocol_manager_ = V4UpdateProtocolManager::Create(
+      url_loader_factory, config, update_callback,
+      extended_reporting_level_callback_);
+}
+
+void V4LocalDatabaseManager::UpdateRequestCompleted(
+    std::unique_ptr<ParsedServerResponse> parsed_server_response) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  v4_database_->ApplyUpdate(std::move(parsed_server_response),
+                            db_updated_callback_);
+}
+
+bool V4LocalDatabaseManager::AreAllStoresAvailableNow(
+    const StoresToCheck& stores_to_check) const {
+  StoreAvailabilityResult result = StoreAvailabilityResult::AVAILABLE;
+  if (!enabled_) {
+    result = StoreAvailabilityResult::NOT_ENABLED;
+  } else if (!v4_database_) {
+    result = StoreAvailabilityResult::DATABASE_UNAVAILABLE;
+  } else if (!v4_database_->AreAllStoresAvailable(stores_to_check)) {
+    result = StoreAvailabilityResult::STORE_UNAVAILABLE;
+  }
+
+  UMA_HISTOGRAM_ENUMERATION(
+      "SafeBrowsing.V4LocalDatabaseManager.AreAllStoresAvailableNow", result,
+      StoreAvailabilityResult::COUNT);
+  return (result == StoreAvailabilityResult::AVAILABLE);
+}
+
+bool V4LocalDatabaseManager::AreAnyStoresAvailableNow(
+    const StoresToCheck& stores_to_check) const {
+  return enabled_ && v4_database_ &&
+         v4_database_->AreAnyStoresAvailable(stores_to_check);
+}
+
+void V4LocalDatabaseManager::UpdateListClientStates(
+    const std::unique_ptr<StoreStateMap>& store_state_map) {
+  list_client_states_.clear();
+  V4ProtocolManagerUtil::GetListClientStatesFromStoreStateMap(
+      store_state_map, &list_client_states_);
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/db/v4_local_database_manager.h b/components/safe_browsing/core/db/v4_local_database_manager.h
new file mode 100644
index 0000000..8ff3bedd
--- /dev/null
+++ b/components/safe_browsing/core/db/v4_local_database_manager.h
@@ -0,0 +1,396 @@
+// 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 COMPONENTS_SAFE_BROWSING_CORE_DB_V4_LOCAL_DATABASE_MANAGER_H_
+#define COMPONENTS_SAFE_BROWSING_CORE_DB_V4_LOCAL_DATABASE_MANAGER_H_
+
+// A class that provides the interface between the SafeBrowsing protocol manager
+// and database that holds the downloaded updates.
+
+#include <memory>
+#include <unordered_set>
+
+#include "base/memory/weak_ptr.h"
+#include "base/sequenced_task_runner.h"
+#include "components/safe_browsing/core/db/database_manager.h"
+#include "components/safe_browsing/core/db/hit_report.h"
+#include "components/safe_browsing/core/db/v4_database.h"
+#include "components/safe_browsing/core/db/v4_get_hash_protocol_manager.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/core/db/v4_update_protocol_manager.h"
+#include "components/safe_browsing/core/proto/webui.pb.h"
+#include "components/safe_browsing/core/realtime/url_lookup_service.h"
+#include "url/gurl.h"
+
+namespace safe_browsing {
+
+typedef unsigned ThreatSeverity;
+
+// Manages the local, on-disk database of updates downloaded from the
+// SafeBrowsing service and interfaces with the protocol manager.
+class V4LocalDatabaseManager : public SafeBrowsingDatabaseManager {
+ public:
+  // Create and return an instance of V4LocalDatabaseManager, if Finch trial
+  // allows it; nullptr otherwise.
+  static scoped_refptr<V4LocalDatabaseManager> Create(
+      const base::FilePath& base_path,
+      ExtendedReportingLevelCallback extended_reporting_level_callback);
+
+  // Populates the protobuf with the database data.
+  void CollectDatabaseManagerInfo(
+      DatabaseManagerInfo* v4_database_info,
+      FullHashCacheInfo* full_hash_cache_info) const;
+
+  // Return an instance of the V4LocalDatabaseManager object
+  static const V4LocalDatabaseManager* current_local_database_manager() {
+    return current_local_database_manager_;
+  }
+
+  //
+  // SafeBrowsingDatabaseManager implementation
+  //
+
+  void CancelCheck(Client* client) override;
+  bool CanCheckResourceType(content::ResourceType resource_type) const override;
+  bool CanCheckUrl(const GURL& url) const override;
+  bool ChecksAreAlwaysAsync() const override;
+  bool CheckBrowseUrl(const GURL& url,
+                      const SBThreatTypeSet& threat_types,
+                      Client* client) override;
+  AsyncMatch CheckCsdWhitelistUrl(const GURL& url, Client* client) override;
+  bool CheckDownloadUrl(const std::vector<GURL>& url_chain,
+                        Client* client) override;
+  // TODO(vakh): |CheckExtensionIDs| in the base class accepts a set of
+  // std::strings but the overriding method in this class accepts a set of
+  // FullHash objects. Since FullHash is currently std::string, it compiles,
+  // but this difference should be eliminated.
+  bool CheckExtensionIDs(const std::set<FullHash>& extension_ids,
+                         Client* client) override;
+  bool CheckResourceUrl(const GURL& url, Client* client) override;
+  AsyncMatch CheckUrlForHighConfidenceAllowlist(const GURL& url,
+                                                Client* client) override;
+  bool CheckUrlForSubresourceFilter(const GURL& url, Client* client) override;
+  bool MatchDownloadWhitelistString(const std::string& str) override;
+  bool MatchDownloadWhitelistUrl(const GURL& url) override;
+  bool MatchMalwareIP(const std::string& ip_address) override;
+  safe_browsing::ThreatSource GetThreatSource() const override;
+  bool IsDownloadProtectionEnabled() const override;
+  bool IsSupported() const override;
+
+  RealTimeUrlLookupService* GetRealTimeUrlLookupService() override;
+
+  void StartOnIOThread(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      const V4ProtocolConfig& config) override;
+  void StopOnIOThread(bool shutdown) override;
+
+  // The stores/lists to always get full hashes for, regardless of which store
+  // the hash prefix matched. We request all lists since it makes the full hash
+  // cache management simpler and we expect very few lists to have overlap for
+  // the same hash prefix anyway.
+  StoresToCheck GetStoresForFullHashRequests() override;
+
+  std::unique_ptr<StoreStateMap> GetStoreStateMap() override;
+
+  //
+  // End: SafeBrowsingDatabaseManager implementation
+  //
+
+ protected:
+  // Construct V4LocalDatabaseManager.
+  // Must be initialized by calling StartOnIOThread() before using.
+  V4LocalDatabaseManager(
+      const base::FilePath& base_path,
+      ExtendedReportingLevelCallback extended_reporting_level_callback,
+      scoped_refptr<base::SequencedTaskRunner> task_runner_for_tests);
+
+  ~V4LocalDatabaseManager() override;
+
+  enum class ClientCallbackType : int {
+    // This represents the case when we're trying to determine if a URL is
+    // unsafe from the following perspectives: Malware, Phishing, UwS.
+    CHECK_BROWSE_URL,
+
+    // This represents the case when we're trying to determine if any of the
+    // URLs in a vector of URLs is unsafe for downloading binaries.
+    CHECK_DOWNLOAD_URLS,
+
+    // This represents the case when we're trying to determine if a URL is an
+    // unsafe resource.
+    CHECK_RESOURCE_URL,
+
+    // This represents the case when we're trying to determine if a Chrome
+    // extension is a unsafe.
+    CHECK_EXTENSION_IDS,
+
+    // This respresents the case when we're trying to determine if a URL belongs
+    // to the list where subresource filter should be active.
+    CHECK_URL_FOR_SUBRESOURCE_FILTER,
+
+    // This respresents the case when we're trying to determine if a URL is
+    // part of the CSD whitelist.
+    CHECK_CSD_WHITELIST,
+
+    // TODO(vakh): Explain this.
+    CHECK_HIGH_CONFIDENCE_ALLOWLIST,
+
+    // This represents the other cases when a check is being performed
+    // synchronously so a client callback isn't required. For instance, when
+    // trying to determing if an IP address is unsafe due to hosting Malware.
+    CHECK_OTHER,
+  };
+
+  // The information we need to process a URL safety reputation request and
+  // respond to the SafeBrowsing client that asked for it.
+  struct PendingCheck {
+    PendingCheck(Client* client,
+                 ClientCallbackType client_callback_type,
+                 const StoresToCheck& stores_to_check,
+                 const std::vector<GURL>& urls);
+
+    PendingCheck(Client* client,
+                 ClientCallbackType client_callback_type,
+                 const StoresToCheck& stores_to_check,
+                 const std::set<FullHash>& full_hashes);
+
+    ~PendingCheck();
+
+    // The SafeBrowsing client that's waiting for the safe/unsafe verdict.
+    Client* client;
+
+    // Determines which funtion from the |client| needs to be called once we
+    // know whether the URL in |url| is safe or unsafe.
+    const ClientCallbackType client_callback_type;
+
+    // The most severe threat verdict for the URLs/hashes being checked.
+    SBThreatType most_severe_threat_type;
+
+    // When the check was sent to the SafeBrowsing service. Used to record the
+    // time it takes to get the uncached full hashes from the service (or a
+    // cached full hash response).
+    base::TimeTicks full_hash_check_start;
+
+    // The SafeBrowsing lists to check hash prefixes in.
+    const StoresToCheck stores_to_check;
+
+    // The URLs that are being checked for being unsafe. The size of exactly
+    // one of |full_hashes| and |urls| should be greater than 0.
+    const std::vector<GURL> urls;
+
+    // The full hashes that are being checked for being safe.
+    std::vector<FullHash> full_hashes;
+
+    // The most severe SBThreatType for each full hash in |full_hashes|. The
+    // length of |full_hash_threat_type| must always match |full_hashes|.
+    std::vector<SBThreatType> full_hash_threat_types;
+
+    // List of full hashes of urls we are checking and corresponding store and
+    // hash prefixes that match it in the local database.
+    FullHashToStoreAndHashPrefixesMap full_hash_to_store_and_hash_prefixes;
+
+    // List of full hashes of urls we are checking and corresponding store and
+    // hash prefixes that match it in the artificial database.
+    FullHashToStoreAndHashPrefixesMap
+        artificial_full_hash_to_store_and_hash_prefixes;
+
+    // The metadata associated with the full hash of the severest match found
+    // for that URL.
+    ThreatMetadata url_metadata;
+
+    // The full hash that matched for a blacklisted resource URL. Used only for
+    // |CheckResourceUrl| case.
+    FullHash matching_full_hash;
+  };
+
+  typedef std::vector<std::unique_ptr<PendingCheck>> QueuedChecks;
+
+ private:
+  friend class V4LocalDatabaseManagerTest;
+  FRIEND_TEST_ALL_PREFIXES(V4LocalDatabaseManagerTest,
+                           TestGetSeverestThreatTypeAndMetadata);
+  FRIEND_TEST_ALL_PREFIXES(V4LocalDatabaseManagerTest, NotificationOnUpdate);
+
+  // The checks awaiting a full hash response from SafeBrowsing service.
+  typedef std::unordered_set<const PendingCheck*> PendingChecks;
+
+  // Called when all the stores managed by the database have been read from
+  // disk after startup and the database is ready for checking resource
+  // reputation.
+  void DatabaseReadyForChecks(std::unique_ptr<V4Database> v4_database);
+
+  // Called when all the stores managed by the database have been verified for
+  // checksum correctness after startup and the database is ready for applying
+  // updates.
+  void DatabaseReadyForUpdates(
+      const std::vector<ListIdentifier>& stores_to_reset);
+
+  // Called when the database has been updated and schedules the next update.
+  void DatabaseUpdated();
+
+  // Delete any *.store files from disk that are no longer used.
+  void DeleteUnusedStoreFiles();
+
+  // Matches the full_hashes for a |check| with the hashes stored in
+  // |artificially_marked_store_and_hash_prefixes_|. For each full hash match,
+  // it populates |full_hash_to_store_and_hash_prefixes| with the matched hash
+  // prefix and store.
+  void GetArtificialPrefixMatches(const std::unique_ptr<PendingCheck>& check);
+
+  // Identifies the prefixes and the store they matched in, for a given |check|.
+  // Returns true if one or more hash prefix matches are found; false otherwise.
+  bool GetPrefixMatches(const std::unique_ptr<PendingCheck>& check);
+
+  // Goes over the |full_hash_infos| and stores the most severe SBThreatType in
+  // |most_severe_threat_type|, the corresponding metadata in |metadata|, and
+  // the matching full hash in |matching_full_hash|. Also, updates in
+  // |full_hash_threat_types|, the threat type for each full hash in
+  // |full_hashes|.
+  void GetSeverestThreatTypeAndMetadata(
+      const std::vector<FullHashInfo>& full_hash_infos,
+      const std::vector<FullHash>& full_hashes,
+      std::vector<SBThreatType>* full_hash_threat_types,
+      SBThreatType* most_severe_threat_type,
+      ThreatMetadata* metadata,
+      FullHash* matching_full_hash);
+
+  // Returns the SBThreatType for a given ListIdentifier.
+  SBThreatType GetSBThreatTypeForList(const ListIdentifier& list_id);
+
+  // Queues the check for async response if the database isn't ready yet.
+  // If the database is ready, checks the database for prefix matches and
+  // returns true immediately if there's no match. If a match is found, it
+  // schedules a task to perform full hash check and returns false.
+  bool HandleCheck(std::unique_ptr<PendingCheck> check);
+
+  // Like HandleCheck, but for whitelists that have both full-hashes and
+  // partial hashes in the DB. Returns MATCH, NO_MATCH, or ASYNC.
+  AsyncMatch HandleWhitelistCheck(std::unique_ptr<PendingCheck> check);
+
+  // Computes the hashes of URLs that have artificially been marked as unsafe
+  // using any of the following command line flags: "mark_as_phishing",
+  // "mark_as_malware", "mark_as_uws".
+  void PopulateArtificialDatabase();
+
+  // Schedules a full-hash check for a given set of prefixes.
+  void ScheduleFullHashCheck(std::unique_ptr<PendingCheck> check);
+
+  // Checks |stores_to_check| in database synchronously for hash prefixes
+  // matching |hash|. Returns true if there's a match; false otherwise. This is
+  // used for lists that have full hash information in the database.
+  bool HandleHashSynchronously(const FullHash& hash,
+                               const StoresToCheck& stores_to_check);
+
+  // Checks |stores_to_check| in database synchronously for hash prefixes
+  // matching the full hashes for |url|. See |HandleHashSynchronously| for
+  // details.
+  bool HandleUrlSynchronously(const GURL& url,
+                              const StoresToCheck& stores_to_check);
+
+  // Called when the |v4_get_hash_protocol_manager_| has the full hash response
+  // available for the URL that we requested. It determines the severest
+  // threat type and responds to the |client| with that information.
+  void OnFullHashResponse(std::unique_ptr<PendingCheck> pending_check,
+                          const std::vector<FullHashInfo>& full_hash_infos);
+
+  // Performs the full hash checking of the URL in |check|.
+  virtual void PerformFullHashCheck(std::unique_ptr<PendingCheck> check);
+
+  // When the database is ready to use, process the checks that were queued
+  // while the database was loading from disk.
+  void ProcessQueuedChecks();
+
+  // Called on StopOnIOThread, it responds to the clients that are waiting for
+  // the database to become available with the verdict as SAFE.
+  void RespondSafeToQueuedChecks();
+
+  // Calls the appopriate method on the |client| object, based on the contents
+  // of |pending_check|.
+  void RespondToClient(std::unique_ptr<PendingCheck> pending_check);
+
+  // Instantiates and initializes |v4_database_| on the task runner. Sets up the
+  // callback for |DatabaseReady| when the database is ready for use.
+  void SetupDatabase();
+
+  // Instantiates and initializes |rt_url_lookup_service_|.
+  void SetupRealTimeUrlLookupService(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
+
+  // Instantiates and initializes |v4_update_protocol_manager_|.
+  void SetupUpdateProtocolManager(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      const V4ProtocolConfig& config);
+
+  // Updates the |list_client_states_| with the state information in
+  // |store_state_map|.
+  void UpdateListClientStates(
+      const std::unique_ptr<StoreStateMap>& store_state_map);
+
+  // The callback called each time the protocol manager downloads updates
+  // successfully.
+  void UpdateRequestCompleted(
+      std::unique_ptr<ParsedServerResponse> parsed_server_response);
+
+  // Return true if we're enabled and have loaded real data for all of
+  // these stores.
+  bool AreAllStoresAvailableNow(const StoresToCheck& stores_to_check) const;
+
+  // Return true if we're enabled and have loaded real data for any of
+  // these stores.
+  bool AreAnyStoresAvailableNow(const StoresToCheck& stores_to_check) const;
+
+  // Stores full hashes of URLs that have been artificially marked as unsafe.
+  StoreAndHashPrefixes artificially_marked_store_and_hash_prefixes_;
+
+  // The base directory under which to create the files that contain hashes.
+  const base::FilePath base_path_;
+
+  // Instance of the V4LocalDatabaseManager object
+  static const V4LocalDatabaseManager* current_local_database_manager_;
+
+  // Called when the V4Database has finished applying the latest update and is
+  // ready to process next update.
+  DatabaseUpdatedCallback db_updated_callback_;
+
+  // Callback to get the current extended reporting level. Needed by the update
+  // manager.
+  ExtendedReportingLevelCallback extended_reporting_level_callback_;
+
+  // The client_state of each list currently being synced. This is updated each
+  // time a database update completes, and used to send list client_state
+  // information in the full hash request.
+  std::vector<std::string> list_client_states_;
+
+  // The list of stores to manage (for hash prefixes and full hashes). Each
+  // element contains the identifier for the store, the corresponding
+  // SBThreatType, whether to fetch hash prefixes for that store, and the
+  // name of the file on disk that would contain the prefixes, if applicable.
+  ListInfos list_infos_;
+
+  // The checks awaiting for a full hash response from the SafeBrowsing service.
+  PendingChecks pending_checks_;
+
+  // The checks that need to be scheduled when the database becomes ready for
+  // use.
+  QueuedChecks queued_checks_;
+
+  std::unique_ptr<RealTimeUrlLookupService> rt_url_lookup_service_;
+
+  // The sequenced task runner for running safe browsing database operations.
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
+
+  // The database that manages the stores containing the hash prefix updates.
+  // All writes to this variable must happen on the IO thread only.
+  std::unique_ptr<V4Database> v4_database_;
+
+  // The protocol manager that downloads the hash prefix updates.
+  std::unique_ptr<V4UpdateProtocolManager> v4_update_protocol_manager_;
+
+  base::WeakPtrFactory<V4LocalDatabaseManager> weak_factory_{this};
+
+  DISALLOW_COPY_AND_ASSIGN(V4LocalDatabaseManager);
+};  // class V4LocalDatabaseManager
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_DB_V4_LOCAL_DATABASE_MANAGER_H_
diff --git a/components/safe_browsing/core/db/v4_local_database_manager_unittest.cc b/components/safe_browsing/core/db/v4_local_database_manager_unittest.cc
new file mode 100644
index 0000000..e5961ff
--- /dev/null
+++ b/components/safe_browsing/core/db/v4_local_database_manager_unittest.cc
@@ -0,0 +1,1456 @@
+// 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 "components/safe_browsing/core/db/v4_local_database_manager.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/ref_counted.h"
+#include "base/run_loop.h"
+#include "base/sequenced_task_runner.h"
+#include "base/strings/string_tokenizer.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/safe_browsing/core/db/v4_database.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/core/db/v4_test_util.h"
+#include "components/safe_browsing/core/features.h"
+#include "content/public/test/browser_task_environment.h"
+#include "crypto/sha2.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "testing/platform_test.h"
+
+namespace safe_browsing {
+
+namespace {
+
+typedef std::vector<FullHashInfo> FullHashInfos;
+
+// Utility function for populating hashes.
+FullHash HashForUrl(const GURL& url) {
+  std::vector<FullHash> full_hashes;
+  V4ProtocolManagerUtil::UrlToFullHashes(url, &full_hashes);
+  // ASSERT_GE(full_hashes.size(), 1u);
+  return full_hashes[0];
+}
+
+// Use this if you want GetFullHashes() to always return prescribed results.
+class FakeGetHashProtocolManager : public V4GetHashProtocolManager {
+ public:
+  FakeGetHashProtocolManager(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      const StoresToCheck& stores_to_check,
+      const V4ProtocolConfig& config,
+      const FullHashInfos& full_hash_infos)
+      : V4GetHashProtocolManager(url_loader_factory, stores_to_check, config),
+        full_hash_infos_(full_hash_infos) {}
+
+  void GetFullHashes(const FullHashToStoreAndHashPrefixesMap,
+                     const std::vector<std::string>&,
+                     FullHashCallback callback) override {
+    // Async, since the real manager might use a fetcher.
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::BindOnce(std::move(callback), full_hash_infos_));
+  }
+
+ private:
+  FullHashInfos full_hash_infos_;
+};
+
+class FakeGetHashProtocolManagerFactory
+    : public V4GetHashProtocolManagerFactory {
+ public:
+  FakeGetHashProtocolManagerFactory(const FullHashInfos& full_hash_infos)
+      : full_hash_infos_(full_hash_infos) {}
+
+  std::unique_ptr<V4GetHashProtocolManager> CreateProtocolManager(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      const StoresToCheck& stores_to_check,
+      const V4ProtocolConfig& config) override {
+    return std::make_unique<FakeGetHashProtocolManager>(
+        url_loader_factory, stores_to_check, config, full_hash_infos_);
+  }
+
+ private:
+  FullHashInfos full_hash_infos_;
+};
+
+// Use FakeGetHashProtocolManagerFactory in scope, then reset.
+// You should make sure the DatabaseManager is created _after_ this.
+class ScopedFakeGetHashProtocolManagerFactory {
+ public:
+  ScopedFakeGetHashProtocolManagerFactory(
+      const FullHashInfos& full_hash_infos) {
+    V4GetHashProtocolManager::RegisterFactory(
+        std::make_unique<FakeGetHashProtocolManagerFactory>(full_hash_infos));
+  }
+  ~ScopedFakeGetHashProtocolManagerFactory() {
+    V4GetHashProtocolManager::RegisterFactory(nullptr);
+  }
+};
+
+}  // namespace
+
+class FakeV4Database : public V4Database {
+ public:
+  static void Create(
+      const scoped_refptr<base::SequencedTaskRunner>& db_task_runner,
+      std::unique_ptr<StoreMap> store_map,
+      const StoreAndHashPrefixes& store_and_hash_prefixes,
+      NewDatabaseReadyCallback new_db_callback,
+      bool stores_available) {
+    // Mimics V4Database::Create
+    const scoped_refptr<base::SingleThreadTaskRunner>& callback_task_runner =
+        base::ThreadTaskRunnerHandle::Get();
+    db_task_runner->PostTask(
+        FROM_HERE,
+        base::BindOnce(&FakeV4Database::CreateOnTaskRunner, db_task_runner,
+                       std::move(store_map), store_and_hash_prefixes,
+                       callback_task_runner, std::move(new_db_callback),
+                       stores_available));
+  }
+
+  // V4Database implementation
+  void GetStoresMatchingFullHash(
+      const FullHash& full_hash,
+      const StoresToCheck& stores_to_check,
+      StoreAndHashPrefixes* store_and_hash_prefixes) override {
+    store_and_hash_prefixes->clear();
+    for (const StoreAndHashPrefix& stored_sahp : store_and_hash_prefixes_) {
+      if (stores_to_check.count(stored_sahp.list_id) == 0)
+        continue;
+      const PrefixSize& prefix_size = stored_sahp.hash_prefix.size();
+      if (!full_hash.compare(0, prefix_size, stored_sahp.hash_prefix)) {
+        store_and_hash_prefixes->push_back(stored_sahp);
+      }
+    }
+  }
+
+  bool AreAllStoresAvailable(
+      const StoresToCheck& stores_to_check) const override {
+    return stores_available_;
+  }
+
+  bool AreAnyStoresAvailable(
+      const StoresToCheck& stores_to_check) const override {
+    return stores_available_;
+  }
+
+ private:
+  static void CreateOnTaskRunner(
+      const scoped_refptr<base::SequencedTaskRunner>& db_task_runner,
+      std::unique_ptr<StoreMap> store_map,
+      const StoreAndHashPrefixes& store_and_hash_prefixes,
+      const scoped_refptr<base::SingleThreadTaskRunner>& callback_task_runner,
+      NewDatabaseReadyCallback new_db_callback,
+      bool stores_available) {
+    // Mimics the semantics of V4Database::CreateOnTaskRunner
+    std::unique_ptr<FakeV4Database> fake_v4_database(
+        new FakeV4Database(db_task_runner, std::move(store_map),
+                           store_and_hash_prefixes, stores_available));
+    callback_task_runner->PostTask(FROM_HERE,
+                                   base::BindOnce(std::move(new_db_callback),
+                                                  std::move(fake_v4_database)));
+  }
+
+  FakeV4Database(const scoped_refptr<base::SequencedTaskRunner>& db_task_runner,
+                 std::unique_ptr<StoreMap> store_map,
+                 const StoreAndHashPrefixes& store_and_hash_prefixes,
+                 bool stores_available)
+      : V4Database(db_task_runner, std::move(store_map)),
+        store_and_hash_prefixes_(store_and_hash_prefixes),
+        stores_available_(stores_available) {}
+
+  const StoreAndHashPrefixes store_and_hash_prefixes_;
+  const bool stores_available_;
+};
+
+// TODO(nparker): This might be simpler with a mock and EXPECT calls.
+// That would also catch unexpected calls.
+class TestClient : public SafeBrowsingDatabaseManager::Client {
+ public:
+  TestClient(SBThreatType sb_threat_type,
+             const GURL& url,
+             V4LocalDatabaseManager* manager_to_cancel = nullptr)
+      : expected_sb_threat_type_(sb_threat_type),
+        expected_urls_(1, url),
+        manager_to_cancel_(manager_to_cancel) {}
+
+  TestClient(SBThreatType sb_threat_type, const std::vector<GURL>& url_chain)
+      : expected_sb_threat_type_(sb_threat_type), expected_urls_(url_chain) {}
+
+  void OnCheckBrowseUrlResult(const GURL& url,
+                              SBThreatType threat_type,
+                              const ThreatMetadata& metadata) override {
+    ASSERT_EQ(expected_urls_[0], url);
+    ASSERT_EQ(expected_sb_threat_type_, threat_type);
+    on_check_browse_url_result_called_ = true;
+    if (manager_to_cancel_) {
+      manager_to_cancel_->CancelCheck(this);
+    }
+  }
+
+  void OnCheckResourceUrlResult(const GURL& url,
+                                SBThreatType threat_type,
+                                const std::string& threat_hash) override {
+    ASSERT_EQ(expected_urls_[0], url);
+    ASSERT_EQ(expected_sb_threat_type_, threat_type);
+    ASSERT_EQ(threat_type == SB_THREAT_TYPE_SAFE, threat_hash.empty());
+    on_check_resource_url_result_called_ = true;
+  }
+
+  void OnCheckDownloadUrlResult(const std::vector<GURL>& url_chain,
+                                SBThreatType threat_type) override {
+    ASSERT_EQ(expected_urls_, url_chain);
+    ASSERT_EQ(expected_sb_threat_type_, threat_type);
+    on_check_download_urls_result_called_ = true;
+  }
+
+  std::vector<GURL>* mutable_expected_urls() { return &expected_urls_; }
+
+  bool on_check_browse_url_result_called() {
+    return on_check_browse_url_result_called_;
+  }
+  bool on_check_download_urls_result_called() {
+    return on_check_download_urls_result_called_;
+  }
+  bool on_check_resource_url_result_called() {
+    return on_check_resource_url_result_called_;
+  }
+
+ private:
+  const SBThreatType expected_sb_threat_type_;
+  std::vector<GURL> expected_urls_;
+  bool on_check_browse_url_result_called_ = false;
+  bool on_check_download_urls_result_called_ = false;
+  bool on_check_resource_url_result_called_ = false;
+  V4LocalDatabaseManager* manager_to_cancel_;
+};
+
+class TestAllowlistClient : public SafeBrowsingDatabaseManager::Client {
+ public:
+  // |match_expected| specifies whether a full hash match is expected.
+  // |expected_sb_threat_type| identifies which callback method to expect to get
+  // called.
+  explicit TestAllowlistClient(bool match_expected,
+                               SBThreatType expected_sb_threat_type)
+      : expected_sb_threat_type_(expected_sb_threat_type),
+        match_expected_(match_expected) {}
+
+  void OnCheckWhitelistUrlResult(bool is_allowlisted) override {
+    EXPECT_EQ(match_expected_, is_allowlisted);
+    EXPECT_EQ(SB_THREAT_TYPE_CSD_WHITELIST, expected_sb_threat_type_);
+    callback_called_ = true;
+  }
+
+  void OnCheckUrlForHighConfidenceAllowlist(bool is_allowlisted) override {
+    EXPECT_EQ(match_expected_, is_allowlisted);
+    EXPECT_EQ(SB_THREAT_TYPE_HIGH_CONFIDENCE_ALLOWLIST,
+              expected_sb_threat_type_);
+    callback_called_ = true;
+  }
+
+  bool callback_called() { return callback_called_; }
+
+ private:
+  const SBThreatType expected_sb_threat_type_;
+  const bool match_expected_;
+  bool callback_called_ = false;
+};
+
+class TestExtensionClient : public SafeBrowsingDatabaseManager::Client {
+ public:
+  TestExtensionClient(const std::set<FullHash>& expected_bad_crxs)
+      : expected_bad_crxs_(expected_bad_crxs),
+        on_check_extensions_result_called_(false) {}
+
+  void OnCheckExtensionsResult(const std::set<FullHash>& bad_crxs) override {
+    EXPECT_EQ(expected_bad_crxs_, bad_crxs);
+    on_check_extensions_result_called_ = true;
+  }
+
+  bool on_check_extensions_result_called() {
+    return on_check_extensions_result_called_;
+  }
+
+ private:
+  const std::set<FullHash> expected_bad_crxs_;
+  bool on_check_extensions_result_called_;
+};
+
+class FakeV4LocalDatabaseManager : public V4LocalDatabaseManager {
+ public:
+  FakeV4LocalDatabaseManager(
+      const base::FilePath& base_path,
+      ExtendedReportingLevelCallback extended_reporting_level_callback,
+      scoped_refptr<base::SequencedTaskRunner> task_runner)
+      : V4LocalDatabaseManager(base_path,
+                               extended_reporting_level_callback,
+                               task_runner),
+        perform_full_hash_check_called_(false) {}
+
+  // V4LocalDatabaseManager impl:
+  void PerformFullHashCheck(std::unique_ptr<PendingCheck> check) override {
+    perform_full_hash_check_called_ = true;
+  }
+
+  static bool PerformFullHashCheckCalled(
+      scoped_refptr<safe_browsing::V4LocalDatabaseManager>& v4_ldbm) {
+    FakeV4LocalDatabaseManager* fake =
+        static_cast<FakeV4LocalDatabaseManager*>(v4_ldbm.get());
+    return fake->perform_full_hash_check_called_;
+  }
+
+ private:
+  ~FakeV4LocalDatabaseManager() override {}
+
+  bool perform_full_hash_check_called_;
+};
+
+class V4LocalDatabaseManagerTest : public PlatformTest {
+ public:
+  V4LocalDatabaseManagerTest() : task_runner_(new base::TestSimpleTaskRunner) {}
+
+  void SetUp() override {
+    PlatformTest::SetUp();
+
+    test_shared_loader_factory_ =
+        base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+            &test_url_loader_factory_);
+
+    ASSERT_TRUE(base_dir_.CreateUniqueTempDir());
+    DVLOG(1) << "base_dir_: " << base_dir_.GetPath().value();
+
+    extended_reporting_level_ = SBER_LEVEL_OFF;
+    erl_callback_ = base::BindRepeating(
+        &V4LocalDatabaseManagerTest::GetExtendedReportingLevel,
+        base::Unretained(this));
+
+    v4_local_database_manager_ =
+        base::WrapRefCounted(new V4LocalDatabaseManager(
+            base_dir_.GetPath(), erl_callback_, task_runner_));
+
+    StartLocalDatabaseManager();
+  }
+
+  void TearDown() override {
+    StopLocalDatabaseManager();
+
+    PlatformTest::TearDown();
+  }
+
+  void ForceDisableLocalDatabaseManager() {
+    v4_local_database_manager_->enabled_ = false;
+  }
+
+  void ForceEnableLocalDatabaseManager() {
+    v4_local_database_manager_->enabled_ = true;
+  }
+
+  const V4LocalDatabaseManager::QueuedChecks& GetQueuedChecks() {
+    return v4_local_database_manager_->queued_checks_;
+  }
+
+  ExtendedReportingLevel GetExtendedReportingLevel() {
+    return extended_reporting_level_;
+  }
+
+  void PopulateArtificialDatabase() {
+    v4_local_database_manager_->PopulateArtificialDatabase();
+  }
+
+  void ReplaceV4Database(const StoreAndHashPrefixes& store_and_hash_prefixes,
+                         bool stores_available = false) {
+    // Disable the V4LocalDatabaseManager first so that if the callback to
+    // verify checksum has been scheduled, then it doesn't do anything when it
+    // is called back.
+    ForceDisableLocalDatabaseManager();
+    // Wait to make sure that the callback gets executed if it has already been
+    // scheduled.
+    WaitForTasksOnTaskRunner();
+    // Re-enable the V4LocalDatabaseManager otherwise the checks won't work and
+    // the fake database won't be set either.
+    ForceEnableLocalDatabaseManager();
+
+    NewDatabaseReadyCallback db_ready_callback =
+        base::BindOnce(&V4LocalDatabaseManager::DatabaseReadyForChecks,
+                       base::Unretained(v4_local_database_manager_.get()));
+    FakeV4Database::Create(task_runner_, std::make_unique<StoreMap>(),
+                           store_and_hash_prefixes,
+                           std::move(db_ready_callback), stores_available);
+    WaitForTasksOnTaskRunner();
+  }
+
+  void ResetLocalDatabaseManager() {
+    StopLocalDatabaseManager();
+    v4_local_database_manager_ =
+        base::WrapRefCounted(new V4LocalDatabaseManager(
+            base_dir_.GetPath(), erl_callback_, task_runner_));
+    StartLocalDatabaseManager();
+  }
+
+  void ResetV4Database() {
+    V4Database::Destroy(std::move(v4_local_database_manager_->v4_database_));
+  }
+
+  void StartLocalDatabaseManager() {
+    v4_local_database_manager_->StartOnIOThread(test_shared_loader_factory_,
+                                                GetTestV4ProtocolConfig());
+  }
+
+  void StopLocalDatabaseManager() {
+    if (v4_local_database_manager_) {
+      v4_local_database_manager_->StopOnIOThread(true);
+    }
+
+    // Force destruction of the database.
+    WaitForTasksOnTaskRunner();
+  }
+
+  void WaitForTasksOnTaskRunner() {
+    // Wait for tasks on the task runner so we're sure that the
+    // V4LocalDatabaseManager has read the data from disk.
+    task_runner_->RunPendingTasks();
+    base::RunLoop().RunUntilIdle();
+  }
+
+  // For those tests that need the fake manager
+  void SetupFakeManager() {
+    // StopLocalDatabaseManager before resetting it because that's what
+    // ~V4LocalDatabaseManager expects.
+    StopLocalDatabaseManager();
+    v4_local_database_manager_ =
+        base::WrapRefCounted(new FakeV4LocalDatabaseManager(
+            base_dir_.GetPath(), erl_callback_, task_runner_));
+    StartLocalDatabaseManager();
+    WaitForTasksOnTaskRunner();
+  }
+
+  const SBThreatTypeSet usual_threat_types_ = CreateSBThreatTypeSet(
+      {SB_THREAT_TYPE_URL_PHISHING, SB_THREAT_TYPE_URL_MALWARE,
+       SB_THREAT_TYPE_URL_UNWANTED});
+
+  network::TestURLLoaderFactory test_url_loader_factory_;
+  scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
+  base::ScopedTempDir base_dir_;
+  ExtendedReportingLevel extended_reporting_level_;
+  ExtendedReportingLevelCallback erl_callback_;
+  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+  content::BrowserTaskEnvironment task_environment_;
+  scoped_refptr<V4LocalDatabaseManager> v4_local_database_manager_;
+};
+
+TEST_F(V4LocalDatabaseManagerTest, TestGetThreatSource) {
+  WaitForTasksOnTaskRunner();
+  EXPECT_EQ(ThreatSource::LOCAL_PVER4,
+            v4_local_database_manager_->GetThreatSource());
+}
+
+TEST_F(V4LocalDatabaseManagerTest, TestIsSupported) {
+  WaitForTasksOnTaskRunner();
+  EXPECT_TRUE(v4_local_database_manager_->IsSupported());
+}
+
+TEST_F(V4LocalDatabaseManagerTest, TestCanCheckUrl) {
+  WaitForTasksOnTaskRunner();
+  EXPECT_TRUE(
+      v4_local_database_manager_->CanCheckUrl(GURL("http://example.com/a/")));
+  EXPECT_TRUE(
+      v4_local_database_manager_->CanCheckUrl(GURL("https://example.com/a/")));
+  EXPECT_TRUE(
+      v4_local_database_manager_->CanCheckUrl(GURL("ftp://example.com/a/")));
+  EXPECT_FALSE(
+      v4_local_database_manager_->CanCheckUrl(GURL("adp://example.com/a/")));
+}
+
+TEST_F(V4LocalDatabaseManagerTest,
+       TestCheckBrowseUrlWithEmptyStoresReturnsNoMatch) {
+  WaitForTasksOnTaskRunner();
+  // Both the stores are empty right now so CheckBrowseUrl should return true.
+  EXPECT_TRUE(v4_local_database_manager_->CheckBrowseUrl(
+      GURL("http://example.com/a/"), usual_threat_types_, nullptr));
+}
+
+TEST_F(V4LocalDatabaseManagerTest, TestCheckBrowseUrlWithFakeDbReturnsMatch) {
+  WaitForTasksOnTaskRunner();
+
+  std::string url_bad_no_scheme("example.com/bad/");
+  FullHash bad_full_hash(crypto::SHA256HashString(url_bad_no_scheme));
+  const HashPrefix bad_hash_prefix(bad_full_hash.substr(0, 5));
+  StoreAndHashPrefixes store_and_hash_prefixes;
+  store_and_hash_prefixes.emplace_back(GetUrlMalwareId(), bad_hash_prefix);
+  ReplaceV4Database(store_and_hash_prefixes);
+
+  const GURL url_bad("https://" + url_bad_no_scheme);
+  EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(
+      url_bad, usual_threat_types_, nullptr));
+
+  // Wait for PerformFullHashCheck to complete.
+  WaitForTasksOnTaskRunner();
+}
+
+TEST_F(V4LocalDatabaseManagerTest, TestCheckCsdWhitelistWithPrefixMatch) {
+  // Setup to receive full-hash misses. We won't make URL requests.
+  ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({}));
+  ResetLocalDatabaseManager();
+  WaitForTasksOnTaskRunner();
+
+  std::string url_safe_no_scheme("example.com/safe/");
+  FullHash safe_full_hash(crypto::SHA256HashString(url_safe_no_scheme));
+  const HashPrefix safe_hash_prefix(safe_full_hash.substr(0, 5));
+  StoreAndHashPrefixes store_and_hash_prefixes;
+  store_and_hash_prefixes.emplace_back(GetUrlCsdWhitelistId(),
+                                       safe_hash_prefix);
+  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true);
+
+  TestAllowlistClient client(
+      /* match_expected= */ false,
+      /* expected_sb_threat_type= */ SB_THREAT_TYPE_CSD_WHITELIST);
+  const GURL url_check("https://" + url_safe_no_scheme);
+  EXPECT_EQ(AsyncMatch::ASYNC, v4_local_database_manager_->CheckCsdWhitelistUrl(
+                                   url_check, &client));
+
+  EXPECT_FALSE(client.callback_called());
+
+  // Wait for PerformFullHashCheck to complete.
+  WaitForTasksOnTaskRunner();
+  EXPECT_TRUE(client.callback_called());
+}
+
+// This is like CsdWhitelistWithPrefixMatch, but we also verify the
+// full-hash-match results in an appropriate callback value.
+TEST_F(V4LocalDatabaseManagerTest,
+       TestCheckCsdWhitelistWithPrefixTheFullMatch) {
+  std::string url_safe_no_scheme("example.com/safe/");
+  FullHash safe_full_hash(crypto::SHA256HashString(url_safe_no_scheme));
+
+  // Setup to receive full-hash hit. We won't make URL requests.
+  FullHashInfos infos(
+      {{safe_full_hash, GetUrlCsdWhitelistId(), base::Time::Now()}});
+  ScopedFakeGetHashProtocolManagerFactory pin(infos);
+  ResetLocalDatabaseManager();
+  WaitForTasksOnTaskRunner();
+
+  const HashPrefix safe_hash_prefix(safe_full_hash.substr(0, 5));
+  StoreAndHashPrefixes store_and_hash_prefixes;
+  store_and_hash_prefixes.emplace_back(GetUrlCsdWhitelistId(),
+                                       safe_hash_prefix);
+  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true);
+
+  TestAllowlistClient client(
+      /* match_expected= */ true,
+      /* expected_sb_threat_type= */ SB_THREAT_TYPE_CSD_WHITELIST);
+  const GURL url_check("https://" + url_safe_no_scheme);
+  EXPECT_EQ(AsyncMatch::ASYNC, v4_local_database_manager_->CheckCsdWhitelistUrl(
+                                   url_check, &client));
+
+  EXPECT_FALSE(client.callback_called());
+
+  // Wait for PerformFullHashCheck to complete.
+  WaitForTasksOnTaskRunner();
+  EXPECT_TRUE(client.callback_called());
+}
+
+TEST_F(V4LocalDatabaseManagerTest, TestCheckCsdWhitelistWithFullMatch) {
+  // Setup to receive full-hash misses. We won't make URL requests.
+  ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({}));
+  ResetLocalDatabaseManager();
+  WaitForTasksOnTaskRunner();
+
+  std::string url_safe_no_scheme("example.com/safe/");
+  FullHash safe_full_hash(crypto::SHA256HashString(url_safe_no_scheme));
+  StoreAndHashPrefixes store_and_hash_prefixes;
+  store_and_hash_prefixes.emplace_back(GetUrlCsdWhitelistId(), safe_full_hash);
+  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true);
+
+  TestAllowlistClient client(
+      /* match_expected= */ false,
+      /* expected_sb_threat_type= */ SB_THREAT_TYPE_CSD_WHITELIST);
+  const GURL url_check("https://" + url_safe_no_scheme);
+  EXPECT_EQ(AsyncMatch::MATCH, v4_local_database_manager_->CheckCsdWhitelistUrl(
+                                   url_check, &client));
+
+  WaitForTasksOnTaskRunner();
+  EXPECT_FALSE(client.callback_called());
+}
+
+TEST_F(V4LocalDatabaseManagerTest, TestCheckCsdWhitelistWithNoMatch) {
+  // Setup to receive full-hash misses. We won't make URL requests.
+  ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({}));
+  ResetLocalDatabaseManager();
+  WaitForTasksOnTaskRunner();
+
+  // Add a full hash that won't match the URL we check.
+  std::string url_safe_no_scheme("example.com/safe/");
+  FullHash safe_full_hash(crypto::SHA256HashString(url_safe_no_scheme));
+  StoreAndHashPrefixes store_and_hash_prefixes;
+  store_and_hash_prefixes.emplace_back(GetUrlMalwareId(), safe_full_hash);
+  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true);
+
+  TestAllowlistClient client(
+      /* match_expected= */ true,
+      /* expected_sb_threat_type= */ SB_THREAT_TYPE_CSD_WHITELIST);
+  const GURL url_check("https://other.com/");
+  EXPECT_EQ(
+      AsyncMatch::NO_MATCH,
+      v4_local_database_manager_->CheckCsdWhitelistUrl(url_check, &client));
+
+  WaitForTasksOnTaskRunner();
+  EXPECT_FALSE(client.callback_called());
+}
+
+// When allowlist is unavailable, all URLS should be allowed.
+TEST_F(V4LocalDatabaseManagerTest, TestCheckCsdWhitelistUnavailable) {
+  // Setup to receive full-hash misses. We won't make URL requests.
+  ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({}));
+  ResetLocalDatabaseManager();
+  WaitForTasksOnTaskRunner();
+
+  StoreAndHashPrefixes store_and_hash_prefixes;
+  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ false);
+
+  TestAllowlistClient client(
+      /* match_expected= */ false,
+      /* expected_sb_threat_type= */ SB_THREAT_TYPE_CSD_WHITELIST);
+  const GURL url_check("https://other.com/");
+  EXPECT_EQ(AsyncMatch::MATCH, v4_local_database_manager_->CheckCsdWhitelistUrl(
+                                   url_check, &client));
+
+  WaitForTasksOnTaskRunner();
+  EXPECT_FALSE(client.callback_called());
+}
+
+TEST_F(V4LocalDatabaseManagerTest,
+       TestCheckBrowseUrlReturnsNoMatchWhenDisabled) {
+  WaitForTasksOnTaskRunner();
+
+  // The same URL returns |false| in the previous test because
+  // v4_local_database_manager_ is enabled.
+  ForceDisableLocalDatabaseManager();
+
+  EXPECT_TRUE(v4_local_database_manager_->CheckBrowseUrl(
+      GURL("http://example.com/a/"), usual_threat_types_, nullptr));
+}
+
+// Hash prefix matches on the high confidence allowlist, but full hash match
+// fails.
+TEST_F(V4LocalDatabaseManagerTest,
+       TestCheckUrlForHCAllowlistWithPrefixMatchButNoFullHashMatch) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures({safe_browsing::kRealTimeUrlLookupEnabled}, {});
+
+  std::string url_safe_no_scheme("example.com/safe/");
+  FullHash safe_full_hash(crypto::SHA256HashString(url_safe_no_scheme));
+
+  // Setup to receive full-hash misses. We won't make URL requests.
+  ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({}));
+  ResetLocalDatabaseManager();
+  WaitForTasksOnTaskRunner();
+
+  // Setup to match hash prefix in the local database.
+  const HashPrefix safe_hash_prefix(safe_full_hash.substr(0, 5));
+  StoreAndHashPrefixes store_and_hash_prefixes;
+  store_and_hash_prefixes.emplace_back(GetUrlHighConfidenceAllowlistId(),
+                                       safe_hash_prefix);
+  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true);
+
+  // Setup the allowlist client to verify the callback.
+  TestAllowlistClient client(
+      /* match_expected= */ false,
+      /* expected_sb_threat_type= */ SB_THREAT_TYPE_HIGH_CONFIDENCE_ALLOWLIST);
+
+  // Lookup the high confidence allowlist.
+  const GURL url_check("https://" + url_safe_no_scheme);
+  EXPECT_EQ(AsyncMatch::ASYNC,
+            v4_local_database_manager_->CheckUrlForHighConfidenceAllowlist(
+                url_check, &client));
+
+  EXPECT_FALSE(client.callback_called());
+
+  // Wait for PerformFullHashCheck to complete.
+  WaitForTasksOnTaskRunner();
+  EXPECT_TRUE(client.callback_called());
+}
+
+// Hash prefix matches on the high confidence allowlist, and subsequently the
+// full hash also matches.
+TEST_F(V4LocalDatabaseManagerTest,
+       TestCheckUrlForHCAllowlistWithPrefixMatchAndFullHashMatch) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures({safe_browsing::kRealTimeUrlLookupEnabled}, {});
+
+  std::string url_safe_no_scheme("example.com/safe/");
+  FullHash safe_full_hash(crypto::SHA256HashString(url_safe_no_scheme));
+
+  // Setup to receive full-hash hit. We won't make URL requests.
+  FullHashInfos infos(
+      {{safe_full_hash, GetUrlHighConfidenceAllowlistId(), base::Time::Now()}});
+  ScopedFakeGetHashProtocolManagerFactory pin(infos);
+  ResetLocalDatabaseManager();
+  WaitForTasksOnTaskRunner();
+
+  // Setup to match hash prefix in the local database.
+  const HashPrefix safe_hash_prefix(safe_full_hash.substr(0, 5));
+  StoreAndHashPrefixes store_and_hash_prefixes;
+  store_and_hash_prefixes.emplace_back(GetUrlHighConfidenceAllowlistId(),
+                                       safe_hash_prefix);
+  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true);
+
+  // Setup the allowlist client to verify the callback.
+  TestAllowlistClient client(
+      /* match_expected= */ true,
+      /* expected_sb_threat_type= */ SB_THREAT_TYPE_HIGH_CONFIDENCE_ALLOWLIST);
+
+  // Lookup the high confidence allowlist.
+  const GURL url_check("https://" + url_safe_no_scheme);
+  EXPECT_EQ(AsyncMatch::ASYNC,
+            v4_local_database_manager_->CheckUrlForHighConfidenceAllowlist(
+                url_check, &client));
+
+  EXPECT_FALSE(client.callback_called());
+
+  // Wait for PerformFullHashCheck to complete.
+  WaitForTasksOnTaskRunner();
+  EXPECT_TRUE(client.callback_called());
+}
+
+// Full hash match on the high confidence allowlist. Returns |MATCH|
+// synchronously and callback isn't called.
+TEST_F(V4LocalDatabaseManagerTest,
+       TestCheckUrlForHCAllowlistWithLocalFullHashMatch) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures({safe_browsing::kRealTimeUrlLookupEnabled}, {});
+
+  std::string url_safe_no_scheme("example.com/safe/");
+  FullHash safe_full_hash(crypto::SHA256HashString(url_safe_no_scheme));
+
+  // Setup to receive full-hash misses. We won't make URL requests.
+  ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({}));
+  ResetLocalDatabaseManager();
+  WaitForTasksOnTaskRunner();
+
+  // Setup to match full hash in the local database.
+  StoreAndHashPrefixes store_and_hash_prefixes;
+  store_and_hash_prefixes.emplace_back(GetUrlHighConfidenceAllowlistId(),
+                                       safe_full_hash);
+  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true);
+
+  // Setup the allowlist client to verify the callback isn't called.
+  TestAllowlistClient client(
+      /* match_expected= */ false,
+      /* expected_sb_threat_type= */ SB_THREAT_TYPE_HIGH_CONFIDENCE_ALLOWLIST);
+  const GURL url_check("https://" + url_safe_no_scheme);
+  EXPECT_EQ(AsyncMatch::MATCH,
+            v4_local_database_manager_->CheckUrlForHighConfidenceAllowlist(
+                url_check, &client));
+
+  WaitForTasksOnTaskRunner();
+  EXPECT_FALSE(client.callback_called());
+}
+
+// Hash prefix has no match on the high confidence allowlist. Returns |NO_MATCH|
+// synchronously and callback isn't called.
+TEST_F(V4LocalDatabaseManagerTest, TestCheckUrlForHCAllowlistWithNoMatch) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures({safe_browsing::kRealTimeUrlLookupEnabled}, {});
+
+  std::string url_safe_no_scheme("example.com/safe/");
+  FullHash safe_full_hash(crypto::SHA256HashString(url_safe_no_scheme));
+
+  // Setup to receive full-hash misses. We won't make URL requests.
+  ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({}));
+  ResetLocalDatabaseManager();
+  WaitForTasksOnTaskRunner();
+
+  // Add a full hash that won't match the URL we check.
+  StoreAndHashPrefixes store_and_hash_prefixes;
+  store_and_hash_prefixes.emplace_back(GetUrlMalwareId(), safe_full_hash);
+  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true);
+
+  // Setup the allowlist client to verify the callback isn't called.
+  TestAllowlistClient client(
+      /* match_expected= */ false,
+      /* expected_sb_threat_type= */ SB_THREAT_TYPE_HIGH_CONFIDENCE_ALLOWLIST);
+  const GURL url_check("https://example.com/other/");
+  EXPECT_EQ(AsyncMatch::NO_MATCH,
+            v4_local_database_manager_->CheckUrlForHighConfidenceAllowlist(
+                url_check, &client));
+
+  WaitForTasksOnTaskRunner();
+  EXPECT_FALSE(client.callback_called());
+}
+
+// When allowlist is unavailable, all URLS should be considered MATCH.
+TEST_F(V4LocalDatabaseManagerTest, TestCheckUrlForHCAllowlistUnavailable) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures({safe_browsing::kRealTimeUrlLookupEnabled}, {});
+
+  // Setup to receive full-hash misses. We won't make URL requests.
+  ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({}));
+  ResetLocalDatabaseManager();
+  WaitForTasksOnTaskRunner();
+
+  // Setup local database as unavailable.
+  StoreAndHashPrefixes store_and_hash_prefixes;
+  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ false);
+
+  // Setup the allowlist client to verify the callback isn't called.
+  TestAllowlistClient client(
+      /* match_expected= */ false,
+      /* expected_sb_threat_type= */ SB_THREAT_TYPE_HIGH_CONFIDENCE_ALLOWLIST);
+
+  const GURL url_check("https://example.com/safe");
+  EXPECT_EQ(AsyncMatch::MATCH,
+            v4_local_database_manager_->CheckUrlForHighConfidenceAllowlist(
+                url_check, &client));
+
+  WaitForTasksOnTaskRunner();
+  EXPECT_FALSE(client.callback_called());
+}
+
+TEST_F(V4LocalDatabaseManagerTest, TestGetSeverestThreatTypeAndMetadata) {
+  WaitForTasksOnTaskRunner();
+
+  FullHash fh_malware("Malware");
+  FullHashInfo fhi_malware(fh_malware, GetUrlMalwareId(), base::Time::Now());
+  fhi_malware.metadata.population_id = "malware_popid";
+
+  FullHash fh_api("api");
+  FullHashInfo fhi_api(fh_api, GetChromeUrlApiId(), base::Time::Now());
+  fhi_api.metadata.population_id = "api_popid";
+
+  FullHash fh_example("example");
+  std::vector<FullHashInfo> fhis({fhi_malware, fhi_api});
+  std::vector<FullHash> full_hashes({fh_malware, fh_example, fh_api});
+
+  std::vector<SBThreatType> full_hash_threat_types(full_hashes.size(),
+                                                   SB_THREAT_TYPE_SAFE);
+  SBThreatType result_threat_type;
+  ThreatMetadata metadata;
+  FullHash matching_full_hash;
+
+  const std::vector<SBThreatType> expected_full_hash_threat_types(
+      {SB_THREAT_TYPE_URL_MALWARE, SB_THREAT_TYPE_SAFE,
+       SB_THREAT_TYPE_API_ABUSE});
+
+  v4_local_database_manager_->GetSeverestThreatTypeAndMetadata(
+      fhis, full_hashes, &full_hash_threat_types, &result_threat_type,
+      &metadata, &matching_full_hash);
+  EXPECT_EQ(expected_full_hash_threat_types, full_hash_threat_types);
+
+  EXPECT_EQ(SB_THREAT_TYPE_URL_MALWARE, result_threat_type);
+  EXPECT_EQ("malware_popid", metadata.population_id);
+  EXPECT_EQ(fh_malware, matching_full_hash);
+
+  // Reversing the list has no effect.
+  std::reverse(std::begin(fhis), std::end(fhis));
+  full_hash_threat_types.assign(full_hashes.size(), SB_THREAT_TYPE_SAFE);
+
+  v4_local_database_manager_->GetSeverestThreatTypeAndMetadata(
+      fhis, full_hashes, &full_hash_threat_types, &result_threat_type,
+      &metadata, &matching_full_hash);
+  EXPECT_EQ(expected_full_hash_threat_types, full_hash_threat_types);
+  EXPECT_EQ(SB_THREAT_TYPE_URL_MALWARE, result_threat_type);
+  EXPECT_EQ("malware_popid", metadata.population_id);
+  EXPECT_EQ(fh_malware, matching_full_hash);
+}
+
+TEST_F(V4LocalDatabaseManagerTest, TestChecksAreQueued) {
+  const GURL url("https://www.example.com/");
+  TestClient client(SB_THREAT_TYPE_SAFE, url);
+  EXPECT_TRUE(GetQueuedChecks().empty());
+  v4_local_database_manager_->CheckBrowseUrl(url, usual_threat_types_, &client);
+  // The database is unavailable so the check should get queued.
+  EXPECT_EQ(1ul, GetQueuedChecks().size());
+
+  // The following function waits for the DB to load.
+  WaitForTasksOnTaskRunner();
+  EXPECT_TRUE(GetQueuedChecks().empty());
+
+  ResetV4Database();
+  v4_local_database_manager_->CheckBrowseUrl(url, usual_threat_types_, &client);
+  // The database is unavailable so the check should get queued.
+  EXPECT_EQ(1ul, GetQueuedChecks().size());
+
+  StopLocalDatabaseManager();
+  EXPECT_TRUE(GetQueuedChecks().empty());
+}
+
+// Verify that a window where checks cannot be cancelled is closed.
+TEST_F(V4LocalDatabaseManagerTest, CancelPending) {
+  // Setup to receive full-hash misses.
+  ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({}));
+
+  // Reset the database manager so it picks up the replacement protocol manager.
+  ResetLocalDatabaseManager();
+  WaitForTasksOnTaskRunner();
+
+  // Put a match in the db that will cause a protocol-manager request.
+  std::string url_bad_no_scheme("example.com/bad/");
+  FullHash bad_full_hash(crypto::SHA256HashString(url_bad_no_scheme));
+  const HashPrefix bad_hash_prefix(bad_full_hash.substr(0, 5));
+  StoreAndHashPrefixes store_and_hash_prefixes;
+  store_and_hash_prefixes.emplace_back(GetUrlMalwareId(), bad_hash_prefix);
+  ReplaceV4Database(store_and_hash_prefixes);
+
+  const GURL url_bad("https://" + url_bad_no_scheme);
+  // Test that a request flows through to the callback.
+  {
+    TestClient client(SB_THREAT_TYPE_SAFE, url_bad);
+    EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(
+        url_bad, usual_threat_types_, &client));
+    EXPECT_FALSE(client.on_check_browse_url_result_called());
+    WaitForTasksOnTaskRunner();
+    EXPECT_TRUE(client.on_check_browse_url_result_called());
+  }
+
+  // Test that cancel prevents the callback from being called.
+  {
+    TestClient client(SB_THREAT_TYPE_SAFE, url_bad);
+    EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(
+        url_bad, usual_threat_types_, &client));
+    v4_local_database_manager_->CancelCheck(&client);
+    EXPECT_FALSE(client.on_check_browse_url_result_called());
+    WaitForTasksOnTaskRunner();
+    EXPECT_FALSE(client.on_check_browse_url_result_called());
+  }
+}
+
+// When the database load flushes the queued requests, make sure that
+// CancelCheck() is not fatal in the client callback.
+TEST_F(V4LocalDatabaseManagerTest, CancelQueued) {
+  const GURL url("http://example.com/a/");
+
+  TestClient client1(SB_THREAT_TYPE_SAFE, url,
+                     v4_local_database_manager_.get());
+  TestClient client2(SB_THREAT_TYPE_SAFE, url);
+  EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(
+      url, usual_threat_types_, &client1));
+  EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(
+      url, usual_threat_types_, &client2));
+  EXPECT_EQ(2ul, GetQueuedChecks().size());
+  EXPECT_FALSE(client1.on_check_browse_url_result_called());
+  EXPECT_FALSE(client2.on_check_browse_url_result_called());
+  WaitForTasksOnTaskRunner();
+  EXPECT_TRUE(client1.on_check_browse_url_result_called());
+  EXPECT_TRUE(client2.on_check_browse_url_result_called());
+}
+
+// This test is somewhat similar to TestCheckBrowseUrlWithFakeDbReturnsMatch but
+// it uses a fake V4LocalDatabaseManager to assert that PerformFullHashCheck is
+// called async.
+TEST_F(V4LocalDatabaseManagerTest, PerformFullHashCheckCalledAsync) {
+  SetupFakeManager();
+
+  std::string url_bad_no_scheme("example.com/bad/");
+  FullHash bad_full_hash(crypto::SHA256HashString(url_bad_no_scheme));
+  const HashPrefix bad_hash_prefix(bad_full_hash.substr(0, 5));
+  StoreAndHashPrefixes store_and_hash_prefixes;
+  store_and_hash_prefixes.emplace_back(GetUrlMalwareId(), bad_hash_prefix);
+  ReplaceV4Database(store_and_hash_prefixes);
+
+  const GURL url_bad("https://" + url_bad_no_scheme);
+  // The fake database returns a matched hash prefix.
+  EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(
+      url_bad, usual_threat_types_, nullptr));
+
+  EXPECT_FALSE(FakeV4LocalDatabaseManager::PerformFullHashCheckCalled(
+      v4_local_database_manager_));
+
+  // Wait for PerformFullHashCheck to complete.
+  WaitForTasksOnTaskRunner();
+
+  EXPECT_TRUE(FakeV4LocalDatabaseManager::PerformFullHashCheckCalled(
+      v4_local_database_manager_));
+}
+
+TEST_F(V4LocalDatabaseManagerTest, UsingWeakPtrDropsCallback) {
+  SetupFakeManager();
+
+  std::string url_bad_no_scheme("example.com/bad/");
+  FullHash bad_full_hash(crypto::SHA256HashString(url_bad_no_scheme));
+  const HashPrefix bad_hash_prefix(bad_full_hash.substr(0, 5));
+  StoreAndHashPrefixes store_and_hash_prefixes;
+  store_and_hash_prefixes.emplace_back(GetUrlMalwareId(), bad_hash_prefix);
+  ReplaceV4Database(store_and_hash_prefixes);
+
+  const GURL url_bad("https://" + url_bad_no_scheme);
+  EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(
+      url_bad, usual_threat_types_, nullptr));
+  v4_local_database_manager_->StopOnIOThread(true);
+
+  // Release the V4LocalDatabaseManager object right away before the callback
+  // gets called. When the callback gets called, without using a weak-ptr
+  // factory, this leads to a use after free. However, using the weak-ptr means
+  // that the callback is simply dropped.
+  v4_local_database_manager_ = nullptr;
+
+  // Wait for the tasks scheduled by StopOnIOThread to complete.
+  WaitForTasksOnTaskRunner();
+}
+
+TEST_F(V4LocalDatabaseManagerTest, TestMatchDownloadWhitelistString) {
+  SetupFakeManager();
+  const std::string good_cert = "Good Cert";
+  const std::string other_cert = "Other Cert";
+  FullHash good_hash(crypto::SHA256HashString(good_cert));
+
+  StoreAndHashPrefixes store_and_hash_prefixes;
+  store_and_hash_prefixes.emplace_back(GetCertCsdDownloadWhitelistId(),
+                                       good_hash);
+
+  ReplaceV4Database(store_and_hash_prefixes, false /* not available */);
+  // Verify it defaults to false when DB is not available.
+  EXPECT_FALSE(
+      v4_local_database_manager_->MatchDownloadWhitelistString(good_cert));
+
+  ReplaceV4Database(store_and_hash_prefixes, true /* available */);
+  // Not whitelisted.
+  EXPECT_FALSE(
+      v4_local_database_manager_->MatchDownloadWhitelistString(other_cert));
+  // Whitelisted.
+  EXPECT_TRUE(
+      v4_local_database_manager_->MatchDownloadWhitelistString(good_cert));
+
+  EXPECT_FALSE(FakeV4LocalDatabaseManager::PerformFullHashCheckCalled(
+      v4_local_database_manager_));
+}
+
+TEST_F(V4LocalDatabaseManagerTest, TestMatchDownloadWhitelistUrl) {
+  SetupFakeManager();
+  GURL good_url("http://safe.com");
+  GURL other_url("http://iffy.com");
+
+  StoreAndHashPrefixes store_and_hash_prefixes;
+  store_and_hash_prefixes.emplace_back(GetUrlCsdDownloadWhitelistId(),
+                                       HashForUrl(good_url));
+
+  ReplaceV4Database(store_and_hash_prefixes, false /* not available */);
+  // Verify it defaults to false when DB is not available.
+  EXPECT_FALSE(v4_local_database_manager_->MatchDownloadWhitelistUrl(good_url));
+
+  ReplaceV4Database(store_and_hash_prefixes, true /* available */);
+  // Not whitelisted.
+  EXPECT_FALSE(
+      v4_local_database_manager_->MatchDownloadWhitelistUrl(other_url));
+  // Whitelisted.
+  EXPECT_TRUE(v4_local_database_manager_->MatchDownloadWhitelistUrl(good_url));
+
+  EXPECT_FALSE(FakeV4LocalDatabaseManager::PerformFullHashCheckCalled(
+      v4_local_database_manager_));
+}
+
+TEST_F(V4LocalDatabaseManagerTest, TestMatchMalwareIP) {
+  SetupFakeManager();
+
+  // >>> hashlib.sha1(socket.inet_pton(socket.AF_INET6,
+  // '::ffff:192.168.1.2')).digest() + chr(128)
+  // '\xb3\xe0z\xafAv#h\x9a\xcf<\xf3ee\x94\xda\xf6y\xb1\xad\x80'
+  StoreAndHashPrefixes store_and_hash_prefixes;
+  store_and_hash_prefixes.emplace_back(GetIpMalwareId(),
+                                       FullHash("\xB3\xE0z\xAF"
+                                                "Av#h\x9A\xCF<\xF3"
+                                                "ee\x94\xDA\xF6y\xB1\xAD\x80"));
+  ReplaceV4Database(store_and_hash_prefixes);
+
+  EXPECT_FALSE(v4_local_database_manager_->MatchMalwareIP(""));
+  // Not blacklisted.
+  EXPECT_FALSE(v4_local_database_manager_->MatchMalwareIP("192.168.1.1"));
+  // Blacklisted.
+  EXPECT_TRUE(v4_local_database_manager_->MatchMalwareIP("192.168.1.2"));
+
+  EXPECT_FALSE(FakeV4LocalDatabaseManager::PerformFullHashCheckCalled(
+      v4_local_database_manager_));
+}
+
+// This verifies the fix for race in http://crbug.com/660293
+TEST_F(V4LocalDatabaseManagerTest, TestCheckBrowseUrlWithSameClientAndCancel) {
+  ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({}));
+  // Reset the database manager so it picks up the replacement protocol manager.
+  ResetLocalDatabaseManager();
+  WaitForTasksOnTaskRunner();
+
+  StoreAndHashPrefixes store_and_hash_prefixes;
+  store_and_hash_prefixes.emplace_back(GetUrlMalwareId(),
+                                       HashPrefix("sن\340\t\006_"));
+  ReplaceV4Database(store_and_hash_prefixes);
+
+  GURL first_url("http://example.com/a");
+  GURL second_url("http://example.com/");
+  TestClient client(SB_THREAT_TYPE_SAFE, first_url);
+  // The fake database returns a matched hash prefix.
+  EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(
+      first_url, usual_threat_types_, &client));
+
+  // That check gets queued. Now, let's cancel the check. After this, we should
+  // not receive a call for |OnCheckBrowseUrlResult| with |first_url|.
+  v4_local_database_manager_->CancelCheck(&client);
+
+  // Now, re-use that client but for |second_url|.
+  client.mutable_expected_urls()->assign(1, second_url);
+  EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(
+      second_url, usual_threat_types_, &client));
+
+  // Wait for PerformFullHashCheck to complete.
+  WaitForTasksOnTaskRunner();
+  // |on_check_browse_url_result_called_| is true only if OnCheckBrowseUrlResult
+  // gets called with the |url| equal to |expected_url|, which is |second_url|
+  // in
+  // this test.
+  EXPECT_TRUE(client.on_check_browse_url_result_called());
+}
+
+TEST_F(V4LocalDatabaseManagerTest, TestCheckResourceUrl) {
+  // Setup to receive full-hash misses.
+  ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({}));
+
+  // Reset the database manager so it picks up the replacement protocol manager.
+  ResetLocalDatabaseManager();
+  WaitForTasksOnTaskRunner();
+
+  std::string url_bad_no_scheme("example.com/bad/");
+  FullHash bad_full_hash(crypto::SHA256HashString(url_bad_no_scheme));
+  const HashPrefix bad_hash_prefix(bad_full_hash.substr(0, 5));
+  StoreAndHashPrefixes store_and_hash_prefixes;
+  store_and_hash_prefixes.emplace_back(GetChromeUrlClientIncidentId(),
+                                       bad_hash_prefix);
+  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true);
+
+  const GURL url_bad("https://" + url_bad_no_scheme);
+  TestClient client(SB_THREAT_TYPE_SAFE, url_bad);
+  EXPECT_FALSE(v4_local_database_manager_->CheckResourceUrl(url_bad, &client));
+  EXPECT_FALSE(client.on_check_resource_url_result_called());
+  WaitForTasksOnTaskRunner();
+  EXPECT_TRUE(client.on_check_resource_url_result_called());
+}
+
+TEST_F(V4LocalDatabaseManagerTest, TestSubresourceFilterCallback) {
+  // Setup to receive full-hash misses.
+  ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({}));
+
+  // Reset the database manager so it picks up the replacement protocol manager.
+  ResetLocalDatabaseManager();
+  WaitForTasksOnTaskRunner();
+
+  std::string url_bad_no_scheme("example.com/bad/");
+  FullHash bad_full_hash(crypto::SHA256HashString(url_bad_no_scheme));
+  const HashPrefix bad_hash_prefix(bad_full_hash.substr(0, 5));
+
+  // Put a match in the db that will cause a protocol-manager request.
+  StoreAndHashPrefixes store_and_hash_prefixes;
+  store_and_hash_prefixes.emplace_back(GetUrlSubresourceFilterId(),
+                                       bad_hash_prefix);
+  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true);
+
+  const GURL url_bad("https://" + url_bad_no_scheme);
+  // Test that a request flows through to the callback.
+  {
+    TestClient client(SB_THREAT_TYPE_SAFE, url_bad);
+    EXPECT_FALSE(v4_local_database_manager_->CheckUrlForSubresourceFilter(
+        url_bad, &client));
+    EXPECT_FALSE(client.on_check_browse_url_result_called());
+    WaitForTasksOnTaskRunner();
+    EXPECT_TRUE(client.on_check_browse_url_result_called());
+  }
+}
+
+TEST_F(V4LocalDatabaseManagerTest, TestCheckResourceUrlReturnsBad) {
+  // Setup to receive full-hash hit.
+  std::string url_bad_no_scheme("example.com/bad/");
+  FullHash bad_full_hash(crypto::SHA256HashString(url_bad_no_scheme));
+  FullHashInfo fhi(bad_full_hash, GetChromeUrlClientIncidentId(), base::Time());
+  ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({fhi}));
+
+  // Reset the database manager so it picks up the replacement protocol manager.
+  ResetLocalDatabaseManager();
+  WaitForTasksOnTaskRunner();
+
+  // Put a match in the db that will cause a protocol-manager request.
+  const HashPrefix bad_hash_prefix(bad_full_hash.substr(0, 5));
+  StoreAndHashPrefixes store_and_hash_prefixes;
+  store_and_hash_prefixes.emplace_back(GetChromeUrlClientIncidentId(),
+                                       bad_hash_prefix);
+  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true);
+
+  const GURL url_bad("https://" + url_bad_no_scheme);
+  TestClient client(SB_THREAT_TYPE_BLACKLISTED_RESOURCE, url_bad);
+  EXPECT_FALSE(v4_local_database_manager_->CheckResourceUrl(url_bad, &client));
+  EXPECT_FALSE(client.on_check_resource_url_result_called());
+  WaitForTasksOnTaskRunner();
+  EXPECT_TRUE(client.on_check_resource_url_result_called());
+}
+
+TEST_F(V4LocalDatabaseManagerTest, TestCheckExtensionIDsNothingBlacklisted) {
+  // Setup to receive full-hash misses.
+  ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({}));
+
+  // Reset the database manager so it picks up the replacement protocol manager.
+  ResetLocalDatabaseManager();
+  WaitForTasksOnTaskRunner();
+
+  // bad_extension_id is in the local DB but the full hash won't match.
+  const FullHash bad_extension_id("aaaabbbbccccdddd"),
+      good_extension_id("ddddccccbbbbaaaa");
+
+  // Put a match in the db that will cause a protocol-manager request.
+  StoreAndHashPrefixes store_and_hash_prefixes;
+  store_and_hash_prefixes.emplace_back(GetChromeExtMalwareId(),
+                                       bad_extension_id);
+  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true);
+
+  const std::set<FullHash> expected_bad_crxs({});
+  const std::set<FullHash> extension_ids({good_extension_id, bad_extension_id});
+  TestExtensionClient client(expected_bad_crxs);
+  EXPECT_FALSE(
+      v4_local_database_manager_->CheckExtensionIDs(extension_ids, &client));
+  EXPECT_FALSE(client.on_check_extensions_result_called());
+  WaitForTasksOnTaskRunner();
+  EXPECT_TRUE(client.on_check_extensions_result_called());
+}
+
+TEST_F(V4LocalDatabaseManagerTest, TestCheckExtensionIDsOneIsBlacklisted) {
+  // bad_extension_id is in the local DB and the full hash will match.
+  const FullHash bad_extension_id("aaaabbbbccccdddd"),
+      good_extension_id("ddddccccbbbbaaaa");
+  FullHashInfo fhi(bad_extension_id, GetChromeExtMalwareId(), base::Time());
+
+  // Setup to receive full-hash hit.
+  ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({fhi}));
+
+  // Reset the database manager so it picks up the replacement protocol manager.
+  ResetLocalDatabaseManager();
+  WaitForTasksOnTaskRunner();
+
+  // Put a match in the db that will cause a protocol-manager request.
+  StoreAndHashPrefixes store_and_hash_prefixes;
+  store_and_hash_prefixes.emplace_back(GetChromeExtMalwareId(),
+                                       bad_extension_id);
+  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true);
+
+  const std::set<FullHash> expected_bad_crxs({bad_extension_id});
+  const std::set<FullHash> extension_ids({good_extension_id, bad_extension_id});
+  TestExtensionClient client(expected_bad_crxs);
+  EXPECT_FALSE(
+      v4_local_database_manager_->CheckExtensionIDs(extension_ids, &client));
+  EXPECT_FALSE(client.on_check_extensions_result_called());
+  WaitForTasksOnTaskRunner();
+  EXPECT_TRUE(client.on_check_extensions_result_called());
+}
+
+TEST_F(V4LocalDatabaseManagerTest, TestCheckDownloadUrlNothingBlacklisted) {
+  // Setup to receive full-hash misses.
+  ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({}));
+
+  // Reset the database manager so it picks up the replacement protocol manager.
+  ResetLocalDatabaseManager();
+  WaitForTasksOnTaskRunner();
+
+  // Put a match in the db that will cause a protocol-manager request.
+  std::string url_bad_no_scheme("example.com/bad/");
+  FullHash bad_full_hash(crypto::SHA256HashString(url_bad_no_scheme));
+  const HashPrefix bad_hash_prefix(bad_full_hash.substr(0, 5));
+  StoreAndHashPrefixes store_and_hash_prefixes;
+  store_and_hash_prefixes.emplace_back(GetUrlMalBinId(), bad_hash_prefix);
+  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true);
+
+  const GURL url_bad("https://" + url_bad_no_scheme),
+      url_good("https://example.com/good/");
+  const std::vector<GURL> url_chain({url_good, url_bad});
+
+  TestClient client(SB_THREAT_TYPE_SAFE, url_chain);
+  EXPECT_FALSE(
+      v4_local_database_manager_->CheckDownloadUrl(url_chain, &client));
+  EXPECT_FALSE(client.on_check_download_urls_result_called());
+  WaitForTasksOnTaskRunner();
+  EXPECT_TRUE(client.on_check_download_urls_result_called());
+}
+
+TEST_F(V4LocalDatabaseManagerTest, TestCheckDownloadUrlWithOneBlacklisted) {
+  // Setup to receive full-hash hit.
+  std::string url_bad_no_scheme("example.com/bad/");
+  FullHash bad_full_hash(crypto::SHA256HashString(url_bad_no_scheme));
+  FullHashInfo fhi(bad_full_hash, GetUrlMalBinId(), base::Time());
+  ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({fhi}));
+
+  // Reset the database manager so it picks up the replacement protocol manager.
+  ResetLocalDatabaseManager();
+  WaitForTasksOnTaskRunner();
+
+  const GURL url_bad("https://" + url_bad_no_scheme),
+      url_good("https://example.com/good/");
+  const std::vector<GURL> url_chain({url_good, url_bad});
+
+  // Put a match in the db that will cause a protocol-manager request.
+  const HashPrefix bad_hash_prefix(bad_full_hash.substr(0, 5));
+  StoreAndHashPrefixes store_and_hash_prefixes;
+  store_and_hash_prefixes.emplace_back(GetUrlMalBinId(), bad_hash_prefix);
+  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true);
+
+  TestClient client(SB_THREAT_TYPE_URL_BINARY_MALWARE, url_chain);
+  EXPECT_FALSE(
+      v4_local_database_manager_->CheckDownloadUrl(url_chain, &client));
+  EXPECT_FALSE(client.on_check_download_urls_result_called());
+  WaitForTasksOnTaskRunner();
+  EXPECT_TRUE(client.on_check_download_urls_result_called());
+}
+
+TEST_F(V4LocalDatabaseManagerTest, DeleteUnusedStoreFileDoesNotExist) {
+  auto store_file_path = base_dir_.GetPath().AppendASCII("AnyIpMalware.store");
+  ASSERT_FALSE(base::PathExists(store_file_path));
+
+  // Reset the database manager so that DeleteUnusedStoreFiles is called.
+  ResetLocalDatabaseManager();
+  WaitForTasksOnTaskRunner();
+  ASSERT_FALSE(base::PathExists(store_file_path));
+}
+
+TEST_F(V4LocalDatabaseManagerTest, DeleteUnusedStoreFileSuccess) {
+  auto store_file_path = base_dir_.GetPath().AppendASCII("AnyIpMalware.store");
+  ASSERT_FALSE(base::PathExists(store_file_path));
+
+  // Now write an empty file.
+  base::WriteFile(store_file_path, "", 0);
+  ASSERT_TRUE(base::PathExists(store_file_path));
+
+  // Reset the database manager so that DeleteUnusedStoreFiles is called.
+  ResetLocalDatabaseManager();
+  WaitForTasksOnTaskRunner();
+  ASSERT_FALSE(base::PathExists(store_file_path));
+}
+
+TEST_F(V4LocalDatabaseManagerTest, DeleteUnusedStoreFileRandomFileNotDeleted) {
+  auto random_store_file_path = base_dir_.GetPath().AppendASCII("random.store");
+  ASSERT_FALSE(base::PathExists(random_store_file_path));
+
+  // Now write an empty file.
+  base::WriteFile(random_store_file_path, "", 0);
+  ASSERT_TRUE(base::PathExists(random_store_file_path));
+
+  // Reset the database manager so that DeleteUnusedStoreFiles is called.
+  ResetLocalDatabaseManager();
+  WaitForTasksOnTaskRunner();
+  ASSERT_TRUE(base::PathExists(random_store_file_path));
+
+  // Cleanup
+  base::DeleteFile(random_store_file_path, false /* recursive */);
+}
+
+TEST_F(V4LocalDatabaseManagerTest, NotificationOnUpdate) {
+  base::RunLoop run_loop;
+  auto callback_subscription =
+      v4_local_database_manager_->RegisterDatabaseUpdatedCallback(
+          run_loop.QuitClosure());
+
+  // Creates and associates a V4Database instance.
+  StoreAndHashPrefixes store_and_hash_prefixes;
+  ReplaceV4Database(store_and_hash_prefixes);
+
+  v4_local_database_manager_->DatabaseUpdated();
+
+  run_loop.Run();
+}
+
+TEST_F(V4LocalDatabaseManagerTest, FlagOneUrlAsPhishing) {
+  SetupFakeManager();
+  base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+      "mark_as_phishing", "https://example.com/1/");
+  PopulateArtificialDatabase();
+
+  const GURL url_bad("https://example.com/1/");
+  EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(
+      url_bad, usual_threat_types_, nullptr));
+  // PerformFullHashCheck will not be called if there is a match within the
+  // artificial database
+  EXPECT_FALSE(FakeV4LocalDatabaseManager::PerformFullHashCheckCalled(
+      v4_local_database_manager_));
+
+  const GURL url_good("https://other.example.com");
+  EXPECT_TRUE(v4_local_database_manager_->CheckBrowseUrl(
+      url_good, usual_threat_types_, nullptr));
+
+  StopLocalDatabaseManager();
+}
+
+TEST_F(V4LocalDatabaseManagerTest, FlagOneUrlAsMalware) {
+  SetupFakeManager();
+  base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+      "mark_as_malware", "https://example.com/1/");
+  PopulateArtificialDatabase();
+
+  const GURL url_bad("https://example.com/1/");
+  EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(
+      url_bad, usual_threat_types_, nullptr));
+  // PerformFullHashCheck will not be called if there is a match within the
+  // artificial database
+  EXPECT_FALSE(FakeV4LocalDatabaseManager::PerformFullHashCheckCalled(
+      v4_local_database_manager_));
+
+  const GURL url_good("https://other.example.com");
+  EXPECT_TRUE(v4_local_database_manager_->CheckBrowseUrl(
+      url_good, usual_threat_types_, nullptr));
+
+  StopLocalDatabaseManager();
+}
+
+TEST_F(V4LocalDatabaseManagerTest, FlagOneUrlAsUWS) {
+  SetupFakeManager();
+  base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+      "mark_as_uws", "https://example.com/1/");
+  PopulateArtificialDatabase();
+
+  const GURL url_bad("https://example.com/1/");
+  EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(
+      url_bad, usual_threat_types_, nullptr));
+  // PerformFullHashCheck will not be called if there is a match within the
+  // artificial database
+  EXPECT_FALSE(FakeV4LocalDatabaseManager::PerformFullHashCheckCalled(
+      v4_local_database_manager_));
+
+  const GURL url_good("https://other.example.com");
+  EXPECT_TRUE(v4_local_database_manager_->CheckBrowseUrl(
+      url_good, usual_threat_types_, nullptr));
+
+  StopLocalDatabaseManager();
+}
+
+TEST_F(V4LocalDatabaseManagerTest, FlagMultipleUrls) {
+  SetupFakeManager();
+  base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+      "mark_as_phishing", "https://example.com/1/");
+  base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+      "mark_as_malware", "https://2.example.com");
+  base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+      "mark_as_uws", "https://example.test.com");
+  PopulateArtificialDatabase();
+
+  const GURL url_phishing("https://example.com/1/");
+  EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(
+      url_phishing, usual_threat_types_, nullptr));
+  const GURL url_malware("https://2.example.com");
+  EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(
+      url_malware, usual_threat_types_, nullptr));
+  const GURL url_uws("https://example.test.com");
+  EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(
+      url_uws, usual_threat_types_, nullptr));
+  // PerformFullHashCheck will not be called if there is a match within the
+  // artificial database
+  EXPECT_FALSE(FakeV4LocalDatabaseManager::PerformFullHashCheckCalled(
+      v4_local_database_manager_));
+
+  const GURL url_good("https://other.example.com");
+  EXPECT_TRUE(v4_local_database_manager_->CheckBrowseUrl(
+      url_good, usual_threat_types_, nullptr));
+
+  StopLocalDatabaseManager();
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/db/v4_protocol_manager_util.cc b/components/safe_browsing/core/db/v4_protocol_manager_util.cc
new file mode 100644
index 0000000..b4048d0
--- /dev/null
+++ b/components/safe_browsing/core/db/v4_protocol_manager_util.cc
@@ -0,0 +1,684 @@
+// 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 "components/safe_browsing/core/db/v4_protocol_manager_util.h"
+
+#include "base/base64.h"
+#include "base/hash/hash.h"
+#include "base/hash/sha1.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/rand_util.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "build/build_config.h"
+#include "components/version_info/version_info.h"
+#include "crypto/sha2.h"
+#include "google_apis/google_api_keys.h"
+#include "net/base/escape.h"
+#include "net/base/ip_address.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_request_headers.h"
+#include "url/url_util.h"
+
+using base::Time;
+using base::TimeDelta;
+
+namespace safe_browsing {
+
+// Can be overriden by tests.
+const char* g_sbv4_url_prefix_for_testing = nullptr;
+
+const char kSbV4UrlPrefix[] = "https://safebrowsing.googleapis.com/v4";
+
+const base::FilePath::CharType kStoreSuffix[] = FILE_PATH_LITERAL(".store");
+
+namespace {
+
+// The default URL prefix where browser reports safe browsing hits and malware
+// details.
+const char kSbReportsURLPrefix[] =
+    "https://safebrowsing.google.com/safebrowsing";
+
+std::string Unescape(const std::string& url) {
+  std::string unescaped_str(url);
+  const int kMaxLoopIterations = 1024;
+  size_t old_size = 0;
+  int loop_var = 0;
+  do {
+    old_size = unescaped_str.size();
+    unescaped_str = net::UnescapeBinaryURLComponent(unescaped_str);
+  } while (old_size != unescaped_str.size() &&
+           ++loop_var <= kMaxLoopIterations);
+
+  return unescaped_str;
+}
+
+std::string Escape(const std::string& url) {
+  std::string escaped_str;
+  // The escaped string is larger so allocate double the length to reduce the
+  // chance of the string being grown.
+  escaped_str.reserve(url.length() * 2);
+  const char* kHexString = "0123456789ABCDEF";
+  for (size_t i = 0; i < url.length(); i++) {
+    unsigned char c = static_cast<unsigned char>(url[i]);
+    if (c <= ' ' || c > '~' || c == '#' || c == '%') {
+      escaped_str += '%';
+      escaped_str += kHexString[c >> 4];
+      escaped_str += kHexString[c & 0xf];
+    } else {
+      escaped_str += c;
+    }
+  }
+
+  return escaped_str;
+}
+
+}  // namespace
+
+V4ProtocolConfig GetV4ProtocolConfig(const std::string& client_name,
+                                     bool disable_auto_update) {
+  return V4ProtocolConfig(client_name, disable_auto_update,
+                          google_apis::GetAPIKey(),
+                          version_info::GetVersionNumber());
+}
+
+void SetSbV4UrlPrefixForTesting(const char* url_prefix) {
+  g_sbv4_url_prefix_for_testing = url_prefix;
+}
+
+std::string GetReportUrl(const V4ProtocolConfig& config,
+                         const std::string& method,
+                         const ExtendedReportingLevel* reporting_level) {
+  std::string url = base::StringPrintf(
+      "%s/%s?client=%s&appver=%s&pver=4.0", kSbReportsURLPrefix, method.c_str(),
+      config.client_name.c_str(), config.version.c_str());
+  std::string api_key = google_apis::GetAPIKey();
+  if (!api_key.empty()) {
+    base::StringAppendF(&url, "&key=%s",
+                        net::EscapeQueryParamValue(api_key, true).c_str());
+  }
+  if (reporting_level)
+    url.append(base::StringPrintf("&ext=%d", *reporting_level));
+  return url;
+}
+
+std::ostream& operator<<(std::ostream& os, const ListIdentifier& id) {
+  os << "{hash: " << id.hash() << "; platform_type: " << id.platform_type()
+     << "; threat_entry_type: " << id.threat_entry_type()
+     << "; threat_type: " << id.threat_type() << "}";
+  return os;
+}
+
+PlatformType GetCurrentPlatformType() {
+#if defined(OS_WIN)
+  return WINDOWS_PLATFORM;
+#elif defined(OS_LINUX)
+  return LINUX_PLATFORM;
+#elif defined(OS_MACOSX)
+  return OSX_PLATFORM;
+#else
+  // TODO(crbug.com/1030487): This file is, in fact, intended to be compiled on
+  // Android, the comment below is obsolete. We should be able to return
+  // ANDROID_PLATFORM here.
+  //
+  // This should ideally never compile but it is getting compiled on Android.
+  // See: https://bugs.chromium.org/p/chromium/issues/detail?id=621647
+  // TODO(vakh): Once that bug is fixed, this should be removed. If we leave
+  // the platform_type empty, the server won't recognize the request and
+  // return an error response which will pollute our UMA metrics.
+  return LINUX_PLATFORM;
+#endif
+}
+
+ListIdentifier GetCertCsdDownloadWhitelistId() {
+  return ListIdentifier(GetCurrentPlatformType(), CERT, CSD_DOWNLOAD_WHITELIST);
+}
+
+ListIdentifier GetChromeExtMalwareId() {
+  return ListIdentifier(CHROME_PLATFORM, CHROME_EXTENSION, MALWARE_THREAT);
+}
+
+ListIdentifier GetChromeUrlApiId() {
+  // TODO(crbug.com/1030487): This special case for Android will no longer be
+  // needed once GetCurrentPlatformType() returns ANDROID_PLATFORM on Android.
+#if defined(OS_ANDROID)
+  return ListIdentifier(ANDROID_PLATFORM, URL, API_ABUSE);
+#else
+  return ListIdentifier(GetCurrentPlatformType(), URL, API_ABUSE);
+#endif
+}
+
+ListIdentifier GetChromeUrlClientIncidentId() {
+  return ListIdentifier(CHROME_PLATFORM, URL, CLIENT_INCIDENT);
+}
+
+ListIdentifier GetIpMalwareId() {
+  return ListIdentifier(GetCurrentPlatformType(), IP_RANGE, MALWARE_THREAT);
+}
+
+ListIdentifier GetUrlBillingId() {
+  return ListIdentifier(GetCurrentPlatformType(), URL, BILLING);
+}
+
+ListIdentifier GetUrlCsdDownloadWhitelistId() {
+  return ListIdentifier(GetCurrentPlatformType(), URL, CSD_DOWNLOAD_WHITELIST);
+}
+
+ListIdentifier GetUrlCsdWhitelistId() {
+  return ListIdentifier(GetCurrentPlatformType(), URL, CSD_WHITELIST);
+}
+
+ListIdentifier GetUrlHighConfidenceAllowlistId() {
+  return ListIdentifier(GetCurrentPlatformType(), URL,
+                        HIGH_CONFIDENCE_ALLOWLIST);
+}
+
+ListIdentifier GetUrlMalwareId() {
+  return ListIdentifier(GetCurrentPlatformType(), URL, MALWARE_THREAT);
+}
+
+ListIdentifier GetUrlMalBinId() {
+  return ListIdentifier(GetCurrentPlatformType(), URL, MALICIOUS_BINARY);
+}
+
+ListIdentifier GetUrlSocEngId() {
+  return ListIdentifier(GetCurrentPlatformType(), URL, SOCIAL_ENGINEERING);
+}
+
+ListIdentifier GetUrlSubresourceFilterId() {
+  return ListIdentifier(GetCurrentPlatformType(), URL, SUBRESOURCE_FILTER);
+}
+
+ListIdentifier GetUrlSuspiciousSiteId() {
+  return ListIdentifier(GetCurrentPlatformType(), URL, SUSPICIOUS);
+}
+
+ListIdentifier GetUrlUwsId() {
+  return ListIdentifier(GetCurrentPlatformType(), URL, UNWANTED_SOFTWARE);
+}
+
+std::string GetUmaSuffixForStore(const base::FilePath& file_path) {
+  DCHECK_EQ(kStoreSuffix, file_path.BaseName().Extension());
+  return base::StringPrintf(
+      ".%" PRFilePath, file_path.BaseName().RemoveExtension().value().c_str());
+}
+
+StoreAndHashPrefix::StoreAndHashPrefix(ListIdentifier list_id,
+                                       const HashPrefix& hash_prefix)
+    : list_id(list_id), hash_prefix(hash_prefix) {}
+
+StoreAndHashPrefix::~StoreAndHashPrefix() {}
+
+bool StoreAndHashPrefix::operator==(const StoreAndHashPrefix& other) const {
+  return list_id == other.list_id && hash_prefix == other.hash_prefix;
+}
+
+bool StoreAndHashPrefix::operator!=(const StoreAndHashPrefix& other) const {
+  return !operator==(other);
+}
+
+size_t StoreAndHashPrefix::hash() const {
+  std::size_t first = list_id.hash();
+  std::size_t second = std::hash<std::string>()(hash_prefix);
+
+  return base::HashInts(first, second);
+}
+
+bool SBThreatTypeSetIsValidForCheckBrowseUrl(const SBThreatTypeSet& set) {
+  for (SBThreatType type : set) {
+    switch (type) {
+      case SB_THREAT_TYPE_URL_PHISHING:
+      case SB_THREAT_TYPE_URL_MALWARE:
+      case SB_THREAT_TYPE_URL_UNWANTED:
+      case SB_THREAT_TYPE_SUSPICIOUS_SITE:
+      case SB_THREAT_TYPE_BILLING:
+        break;
+
+      default:
+        return false;
+    }
+  }
+  return true;
+}
+
+bool ListIdentifier::operator==(const ListIdentifier& other) const {
+  return platform_type_ == other.platform_type_ &&
+         threat_entry_type_ == other.threat_entry_type_ &&
+         threat_type_ == other.threat_type_;
+}
+
+bool ListIdentifier::operator!=(const ListIdentifier& other) const {
+  return !operator==(other);
+}
+
+size_t ListIdentifier::hash() const {
+  std::size_t first = std::hash<unsigned int>()(platform_type_);
+  std::size_t second = std::hash<unsigned int>()(threat_entry_type_);
+  std::size_t third = std::hash<unsigned int>()(threat_type_);
+
+  std::size_t interim = base::HashInts(first, second);
+  return base::HashInts(interim, third);
+}
+
+ListIdentifier::ListIdentifier(PlatformType platform_type,
+                               ThreatEntryType threat_entry_type,
+                               ThreatType threat_type)
+    : platform_type_(platform_type),
+      threat_entry_type_(threat_entry_type),
+      threat_type_(threat_type) {
+  DCHECK(PlatformType_IsValid(platform_type));
+  DCHECK(ThreatEntryType_IsValid(threat_entry_type));
+  DCHECK(ThreatType_IsValid(threat_type));
+}
+
+ListIdentifier::ListIdentifier(const ListUpdateResponse& response)
+    : ListIdentifier(response.platform_type(),
+                     response.threat_entry_type(),
+                     response.threat_type()) {}
+
+V4ProtocolConfig::V4ProtocolConfig(const std::string& client_name,
+                                   bool disable_auto_update,
+                                   const std::string& key_param,
+                                   const std::string& version)
+    : client_name(client_name),
+      disable_auto_update(disable_auto_update),
+      key_param(key_param),
+      version(version) {}
+
+V4ProtocolConfig::V4ProtocolConfig(const V4ProtocolConfig& other) = default;
+
+V4ProtocolConfig::~V4ProtocolConfig() {}
+
+// static
+base::TimeDelta V4ProtocolManagerUtil::GetNextBackOffInterval(
+    size_t* error_count,
+    size_t* multiplier) {
+  DCHECK(multiplier && error_count);
+  (*error_count)++;
+  if (*error_count > 1 && *error_count < 9) {
+    // With error count 9 and above we will hit the 24 hour max interval.
+    // Cap the multiplier here to prevent integer overflow errors.
+    *multiplier *= 2;
+  }
+  base::TimeDelta next =
+      base::TimeDelta::FromMinutes(*multiplier * (1 + base::RandDouble()) * 15);
+  base::TimeDelta day = base::TimeDelta::FromHours(24);
+  return next < day ? next : day;
+}
+
+// static
+void V4ProtocolManagerUtil::RecordHttpResponseOrErrorCode(
+    const char* metric_name,
+    int net_error,
+    int response_code) {
+  base::UmaHistogramSparse(metric_name,
+                           net_error == net::OK ? response_code : net_error);
+}
+
+// static
+void V4ProtocolManagerUtil::GetRequestUrlAndHeaders(
+    const std::string& request_base64,
+    const std::string& method_name,
+    const V4ProtocolConfig& config,
+    GURL* gurl,
+    net::HttpRequestHeaders* headers) {
+  const char* url_prefix = g_sbv4_url_prefix_for_testing
+                               ? g_sbv4_url_prefix_for_testing
+                               : kSbV4UrlPrefix;
+  *gurl = GURL(
+      ComposeUrl(url_prefix, method_name, request_base64, config.key_param));
+  UpdateHeaders(headers);
+}
+
+// static
+std::string V4ProtocolManagerUtil::ComposeUrl(const std::string& prefix,
+                                              const std::string& method,
+                                              const std::string& request_base64,
+                                              const std::string& key_param) {
+  DCHECK(!prefix.empty() && !method.empty());
+  std::string url = base::StringPrintf(
+      "%s/%s?$req=%s&$ct=application/x-protobuf", prefix.c_str(),
+      method.c_str(), request_base64.c_str());
+  if (!key_param.empty()) {
+    base::StringAppendF(&url, "&key=%s",
+                        net::EscapeQueryParamValue(key_param, true).c_str());
+  }
+  return url;
+}
+
+// static
+void V4ProtocolManagerUtil::UpdateHeaders(net::HttpRequestHeaders* headers) {
+  // NOTE(vakh): The following header informs the envelope server (which sits in
+  // front of Google's stubby server) that the received GET request should be
+  // interpreted as a POST.
+  headers->SetHeaderIfMissing("X-HTTP-Method-Override", "POST");
+}
+
+// static
+void V4ProtocolManagerUtil::UrlToFullHashes(
+    const GURL& url,
+    std::vector<FullHash>* full_hashes) {
+  std::string canon_host, canon_path, canon_query;
+  CanonicalizeUrl(url, &canon_host, &canon_path, &canon_query);
+
+  std::vector<std::string> hosts;
+  if (url.HostIsIPAddress()) {
+    hosts.push_back(url.host());
+  } else {
+    GenerateHostVariantsToCheck(canon_host, &hosts);
+  }
+
+  std::vector<std::string> paths;
+  GeneratePathVariantsToCheck(canon_path, canon_query, &paths);
+  for (const std::string& host : hosts) {
+    for (const std::string& path : paths) {
+      full_hashes->push_back(crypto::SHA256HashString(host + path));
+    }
+  }
+}
+
+// static
+bool V4ProtocolManagerUtil::FullHashToHashPrefix(const FullHash& full_hash,
+                                                 PrefixSize prefix_size,
+                                                 HashPrefix* hash_prefix) {
+  if (full_hash.size() < prefix_size) {
+    return false;
+  }
+  *hash_prefix = full_hash.substr(0, prefix_size);
+  return true;
+}
+
+// static
+bool V4ProtocolManagerUtil::FullHashToSmallestHashPrefix(
+    const FullHash& full_hash,
+    HashPrefix* hash_prefix) {
+  return FullHashToHashPrefix(full_hash, kMinHashPrefixLength, hash_prefix);
+}
+
+// static
+bool V4ProtocolManagerUtil::FullHashMatchesHashPrefix(
+    const FullHash& full_hash,
+    const HashPrefix& hash_prefix) {
+  return full_hash.compare(0, hash_prefix.length(), hash_prefix) == 0;
+}
+
+// static
+void V4ProtocolManagerUtil::GenerateHostsToCheck(
+    const GURL& url,
+    std::vector<std::string>* hosts) {
+  std::string canon_host;
+  CanonicalizeUrl(url, &canon_host, nullptr, nullptr);
+  GenerateHostVariantsToCheck(canon_host, hosts);
+}
+
+// static
+void V4ProtocolManagerUtil::GeneratePathsToCheck(
+    const GURL& url,
+    std::vector<std::string>* paths) {
+  std::string canon_path;
+  std::string canon_query;
+  CanonicalizeUrl(url, nullptr, &canon_path, &canon_query);
+  GeneratePathVariantsToCheck(canon_path, canon_query, paths);
+}
+
+// static
+void V4ProtocolManagerUtil::GeneratePatternsToCheck(
+    const GURL& url,
+    std::vector<std::string>* urls) {
+  std::string canon_host;
+  std::string canon_path;
+  std::string canon_query;
+  CanonicalizeUrl(url, &canon_host, &canon_path, &canon_query);
+
+  std::vector<std::string> hosts, paths;
+  GenerateHostVariantsToCheck(canon_host, &hosts);
+  GeneratePathVariantsToCheck(canon_path, canon_query, &paths);
+  for (size_t h = 0; h < hosts.size(); ++h) {
+    for (size_t p = 0; p < paths.size(); ++p) {
+      urls->push_back(hosts[h] + paths[p]);
+    }
+  }
+}
+
+// static
+FullHash V4ProtocolManagerUtil::GetFullHash(const GURL& url) {
+  std::string host;
+  std::string path;
+  CanonicalizeUrl(url, &host, &path, nullptr);
+
+  return crypto::SHA256HashString(host + path);
+}
+
+// static
+void V4ProtocolManagerUtil::CanonicalizeUrl(const GURL& url,
+                                            std::string* canonicalized_hostname,
+                                            std::string* canonicalized_path,
+                                            std::string* canonicalized_query) {
+  DCHECK(url.is_valid());
+
+  // We only canonicalize "normal" URLs.
+  if (!url.IsStandard())
+    return;
+
+  // Following canonicalization steps are excluded since url parsing takes care
+  // of those :-
+  // 1. Remove any tab (0x09), CR (0x0d), and LF (0x0a) chars from url.
+  //    (Exclude escaped version of these chars).
+  // 2. Normalize hostname to 4 dot-seperated decimal values.
+  // 3. Lowercase hostname.
+  // 4. Resolve path sequences "/../" and "/./".
+
+  // That leaves us with the following :-
+  // 1. Remove fragment in URL.
+  GURL url_without_fragment;
+  GURL::Replacements f_replacements;
+  f_replacements.ClearRef();
+  f_replacements.ClearUsername();
+  f_replacements.ClearPassword();
+  url_without_fragment = url.ReplaceComponents(f_replacements);
+
+  // 2. Do URL unescaping until no more hex encoded characters exist.
+  std::string url_unescaped_str(Unescape(url_without_fragment.spec()));
+  url::Parsed parsed;
+  url::ParseStandardURL(url_unescaped_str.data(), url_unescaped_str.length(),
+                        &parsed);
+
+  // 3. In hostname, remove all leading and trailing dots.
+  base::StringPiece host;
+  if (parsed.host.len > 0)
+    host.set(url_unescaped_str.data() + parsed.host.begin, parsed.host.len);
+
+  base::StringPiece host_without_end_dots =
+      base::TrimString(host, ".", base::TrimPositions::TRIM_ALL);
+
+  // 4. In hostname, replace consecutive dots with a single dot.
+  std::string host_without_consecutive_dots(
+      RemoveConsecutiveChars(host_without_end_dots, '.'));
+
+  // 5. In path, replace runs of consecutive slashes with a single slash.
+  base::StringPiece path;
+  if (parsed.path.len > 0)
+    path.set(url_unescaped_str.data() + parsed.path.begin, parsed.path.len);
+  std::string path_without_consecutive_slash(RemoveConsecutiveChars(path, '/'));
+
+  url::Replacements<char> hp_replacements;
+  hp_replacements.SetHost(
+      host_without_consecutive_dots.data(),
+      url::Component(0, host_without_consecutive_dots.length()));
+  hp_replacements.SetPath(
+      path_without_consecutive_slash.data(),
+      url::Component(0, path_without_consecutive_slash.length()));
+
+  std::string url_unescaped_with_can_hostpath;
+  url::StdStringCanonOutput output(&url_unescaped_with_can_hostpath);
+  url::Parsed temp_parsed;
+  url::ReplaceComponents(url_unescaped_str.data(), url_unescaped_str.length(),
+                         parsed, hp_replacements, nullptr, &output,
+                         &temp_parsed);
+  output.Complete();
+
+  // 6. Step needed to revert escaping done in url::ReplaceComponents.
+  url_unescaped_with_can_hostpath = Unescape(url_unescaped_with_can_hostpath);
+
+  // 7. After performing all above steps, percent-escape all chars in url which
+  // are <= ASCII 32, >= 127, #, %. Escapes must be uppercase hex characters.
+  std::string escaped_canon_url_str(Escape(url_unescaped_with_can_hostpath));
+  url::Parsed final_parsed;
+  url::ParseStandardURL(escaped_canon_url_str.data(),
+                        escaped_canon_url_str.length(), &final_parsed);
+
+  if (canonicalized_hostname && final_parsed.host.len > 0) {
+    *canonicalized_hostname = escaped_canon_url_str.substr(
+        final_parsed.host.begin, final_parsed.host.len);
+  }
+  if (canonicalized_path && final_parsed.path.len > 0) {
+    *canonicalized_path = escaped_canon_url_str.substr(final_parsed.path.begin,
+                                                       final_parsed.path.len);
+  }
+  if (canonicalized_query && final_parsed.query.len > 0) {
+    *canonicalized_query = escaped_canon_url_str.substr(
+        final_parsed.query.begin, final_parsed.query.len);
+  }
+}
+
+// static
+std::string V4ProtocolManagerUtil::RemoveConsecutiveChars(base::StringPiece str,
+                                                          const char c) {
+  std::string output;
+  // Output is at most the length of the original string.
+  output.reserve(str.size());
+
+  size_t i = 0;
+  while (i < str.size()) {
+    output.append(1, str[i++]);
+    if (str[i - 1] == c) {
+      while (i < str.size() && str[i] == c) {
+        i++;
+      }
+    }
+  }
+
+  return output;
+}
+
+// static
+void V4ProtocolManagerUtil::GenerateHostVariantsToCheck(
+    const std::string& host,
+    std::vector<std::string>* hosts) {
+  hosts->clear();
+
+  if (host.empty())
+    return;
+
+  // Per the Safe Browsing Protocol v2 spec, we try the host, and also up to 4
+  // hostnames formed by starting with the last 5 components and successively
+  // removing the leading component.  The last component isn't examined alone,
+  // since it's the TLD or a subcomponent thereof.
+  //
+  // Note that we don't need to be clever about stopping at the "real" eTLD --
+  // the data on the server side has been filtered to ensure it will not
+  // blacklist a whole TLD, and it's not significantly slower on our side to
+  // just check too much.
+  //
+  // Also note that because we have a simple blacklist, not some sort of complex
+  // whitelist-in-blacklist or vice versa, it doesn't matter what order we check
+  // these in.
+  const size_t kMaxHostsToCheck = 4;
+  bool skipped_last_component = false;
+  for (std::string::const_reverse_iterator i(host.rbegin());
+       i != host.rend() && hosts->size() < kMaxHostsToCheck; ++i) {
+    if (*i == '.') {
+      if (skipped_last_component)
+        hosts->push_back(std::string(i.base(), host.end()));
+      else
+        skipped_last_component = true;
+    }
+  }
+  hosts->push_back(host);
+}
+
+// static
+void V4ProtocolManagerUtil::GeneratePathVariantsToCheck(
+    const std::string& path,
+    const std::string& query,
+    std::vector<std::string>* paths) {
+  paths->clear();
+
+  if (path.empty())
+    return;
+
+  // Per the Safe Browsing Protocol v2 spec, we try the exact path with/without
+  // the query parameters, and also up to 4 paths formed by starting at the root
+  // and adding more path components.
+  //
+  // As with the hosts above, it doesn't matter what order we check these in.
+  const size_t kMaxPathsToCheck = 4;
+  for (std::string::const_iterator i(path.begin());
+       i != path.end() && paths->size() < kMaxPathsToCheck; ++i) {
+    if (*i == '/')
+      paths->push_back(std::string(path.begin(), i + 1));
+  }
+
+  if (!paths->empty() && paths->back() != path)
+    paths->push_back(path);
+
+  if (!query.empty())
+    paths->push_back(path + "?" + query);
+}
+
+// static
+void V4ProtocolManagerUtil::SetClientInfoFromConfig(
+    ClientInfo* client_info,
+    const V4ProtocolConfig& config) {
+  DCHECK(client_info);
+  client_info->set_client_id(config.client_name);
+  client_info->set_client_version(config.version);
+}
+
+// static
+bool V4ProtocolManagerUtil::GetIPV6AddressFromString(
+    const std::string& ip_address,
+    net::IPAddress* address) {
+  DCHECK(address);
+  if (!address->AssignFromIPLiteral(ip_address))
+    return false;
+  if (address->IsIPv4())
+    *address = net::ConvertIPv4ToIPv4MappedIPv6(*address);
+  return address->IsIPv6();
+}
+
+// static
+bool V4ProtocolManagerUtil::IPAddressToEncodedIPV6Hash(
+    const std::string& ip_address,
+    FullHash* hashed_encoded_ip) {
+  net::IPAddress address;
+  if (!GetIPV6AddressFromString(ip_address, &address)) {
+    return false;
+  }
+  std::string packed_ip = net::IPAddressToPackedString(address);
+  if (packed_ip.empty()) {
+    return false;
+  }
+
+  const std::string hash = base::SHA1HashString(packed_ip);
+  DCHECK_EQ(20u, hash.size());
+  hashed_encoded_ip->resize(hash.size() + 1);
+  hashed_encoded_ip->replace(0, hash.size(), hash);
+  (*hashed_encoded_ip)[hash.size()] = static_cast<unsigned char>(128);
+  return true;
+}
+
+// static
+void V4ProtocolManagerUtil::GetListClientStatesFromStoreStateMap(
+    const std::unique_ptr<StoreStateMap>& store_state_map,
+    std::vector<std::string>* list_client_states) {
+  std::transform(
+      store_state_map->begin(), store_state_map->end(),
+      std::back_inserter(*list_client_states),
+      [](const std::map<ListIdentifier, std::string>::value_type& pair) {
+        return pair.second;
+      });
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/db/v4_protocol_manager_util.h b/components/safe_browsing/core/db/v4_protocol_manager_util.h
new file mode 100644
index 0000000..cc43f2f
--- /dev/null
+++ b/components/safe_browsing/core/db/v4_protocol_manager_util.h
@@ -0,0 +1,468 @@
+// 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 COMPONENTS_SAFE_BROWSING_CORE_DB_V4_PROTOCOL_MANAGER_UTIL_H_
+#define COMPONENTS_SAFE_BROWSING_CORE_DB_V4_PROTOCOL_MANAGER_UTIL_H_
+
+// A class that implements the stateless methods used by the GetHashUpdate and
+// GetFullHash stubby calls made by Chrome using the SafeBrowsing V4 protocol.
+
+#include <functional>
+#include <initializer_list>
+#include <memory>
+#include <ostream>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#include "base/containers/flat_set.h"
+#include "base/gtest_prod_util.h"
+#include "base/strings/string_piece.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/db/safebrowsing.pb.h"
+#include "url/gurl.h"
+
+namespace net {
+class HttpRequestHeaders;
+class IPAddress;
+}  // namespace net
+
+namespace safe_browsing {
+
+
+// The size of the hash prefix, in bytes. It should be between 4 to 32 (full
+// hash).
+using PrefixSize = size_t;
+
+// The minimum expected size (in bytes) of a hash-prefix.
+const PrefixSize kMinHashPrefixLength = 4;
+
+// The maximum expected size (in bytes) of a hash-prefix. This represents the
+// length of a SHA256 hash.
+const PrefixSize kMaxHashPrefixLength = 32;
+
+// A hash prefix sent by the SafeBrowsing PVer4 service.
+using HashPrefix = std::string;
+
+// A full SHA256 hash.
+using FullHash = HashPrefix;
+
+using ListUpdateRequest = FetchThreatListUpdatesRequest::ListUpdateRequest;
+using ListUpdateResponse = FetchThreatListUpdatesResponse::ListUpdateResponse;
+
+void SetSbV4UrlPrefixForTesting(const char* url_prefix);
+
+// Config passed to the constructor of a V4 protocol manager.
+struct V4ProtocolConfig {
+  // The safe browsing client name sent in each request.
+  std::string client_name;
+
+  // Disable auto-updates using a command line switch.
+  bool disable_auto_update;
+
+  // The Google API key.
+  std::string key_param;
+
+  // Current product version sent in each request.
+  std::string version;
+
+  V4ProtocolConfig(const std::string& client_name,
+                   bool disable_auto_update,
+                   const std::string& key_param,
+                   const std::string& version);
+  V4ProtocolConfig(const V4ProtocolConfig& other);
+  ~V4ProtocolConfig();
+
+ private:
+  V4ProtocolConfig() = delete;
+};
+
+// Get the v4 protocol config struct with a given client name, and ability to
+// enable/disable database auto update.
+V4ProtocolConfig GetV4ProtocolConfig(const std::string& client_name,
+                                     bool disable_auto_update);
+
+// Returns the URL to use for sending threat reports and other Safe Browsing
+// hits back to Safe Browsing service.
+std::string GetReportUrl(
+    const V4ProtocolConfig& config,
+    const std::string& method,
+    const ExtendedReportingLevel* reporting_level = nullptr);
+
+// Different types of threats that SafeBrowsing protects against. This is the
+// type that's returned to the clients of SafeBrowsing in Chromium.
+// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.safe_browsing
+// GENERATED_JAVA_PREFIX_TO_STRIP: SB_THREAT_TYPE_
+enum SBThreatType {
+  // This type can be used for lists that can be checked synchronously so a
+  // client callback isn't required, or for whitelists.
+  SB_THREAT_TYPE_UNUSED,
+
+  // No threat at all.
+  SB_THREAT_TYPE_SAFE,
+
+  // The URL is being used for phishing.
+  SB_THREAT_TYPE_URL_PHISHING,
+
+  // The URL hosts malware.
+  SB_THREAT_TYPE_URL_MALWARE,
+
+  // The URL hosts unwanted programs.
+  SB_THREAT_TYPE_URL_UNWANTED,
+
+  // The download URL is malware.
+  SB_THREAT_TYPE_URL_BINARY_MALWARE,
+
+  // Url detected by the client-side phishing model.  Note that unlike the
+  // above values, this does not correspond to a downloaded list.
+  SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING,
+
+  // The Chrome extension or app (given by its ID) is malware.
+  SB_THREAT_TYPE_EXTENSION,
+
+  // Url detected by the client-side malware IP list. This IP list is part
+  // of the client side detection model.
+  SB_THREAT_TYPE_URL_CLIENT_SIDE_MALWARE,
+
+  // Url leads to a blacklisted resource script. Note that no warnings should be
+  // shown on this threat type, but an incident report might be sent.
+  SB_THREAT_TYPE_BLACKLISTED_RESOURCE,
+
+  // Url abuses a permission API.
+  SB_THREAT_TYPE_API_ABUSE,
+
+  // Activation patterns for the Subresource Filter.
+  SB_THREAT_TYPE_SUBRESOURCE_FILTER,
+
+  // CSD Phishing whitelist.  This "threat" means a URL matched the whitelist.
+  SB_THREAT_TYPE_CSD_WHITELIST,
+
+  // DEPRECATED. Url detected by password protection service.
+  DEPRECATED_SB_THREAT_TYPE_URL_PASSWORD_PROTECTION_PHISHING,
+
+  // Saved password reuse detected on low reputation page,
+  SB_THREAT_TYPE_SAVED_PASSWORD_REUSE,
+
+  // Chrome signed in and syncing gaia password reuse detected on low reputation
+  // page,
+  SB_THREAT_TYPE_SIGNED_IN_SYNC_PASSWORD_REUSE,
+
+  // Chrome signed in non syncing gaia password reuse detected on low reputation
+  // page,
+  SB_THREAT_TYPE_SIGNED_IN_NON_SYNC_PASSWORD_REUSE,
+
+  // A Google ad that caused a blocked autoredirect was collected
+  SB_THREAT_TYPE_BLOCKED_AD_REDIRECT,
+
+  // A sample of an ad was collected
+  SB_THREAT_TYPE_AD_SAMPLE,
+
+  // A report of Google ad that caused a blocked popup was collected.
+  SB_THREAT_TYPE_BLOCKED_AD_POPUP,
+
+  // The page loaded a resource from the Suspicious Site list.
+  SB_THREAT_TYPE_SUSPICIOUS_SITE,
+
+  // Enterprise password reuse detected on low reputation page.
+  SB_THREAT_TYPE_ENTERPRISE_PASSWORD_REUSE,
+
+  // Potential billing detected.
+  SB_THREAT_TYPE_BILLING,
+
+  // Off-market APK file downloaded, which could be potentially dangerous.
+  SB_THREAT_TYPE_APK_DOWNLOAD,
+
+  // Match found in the local high-confidence allowlist.
+  SB_THREAT_TYPE_HIGH_CONFIDENCE_ALLOWLIST,
+};
+
+using SBThreatTypeSet = base::flat_set<SBThreatType>;
+
+// Return true if |set| only contains types that are valid for CheckBrowseUrl().
+// Intended for use in DCHECK().
+bool SBThreatTypeSetIsValidForCheckBrowseUrl(const SBThreatTypeSet& set);
+
+// Shorthand for creating an SBThreatTypeSet from a list of SBThreatTypes. Use
+// like CreateSBThreatTypeSet({SB_THREAT_TYPE_URL_PHISHING,
+//                             SB_THREAT_TYPE_URL_MALWARE})
+inline SBThreatTypeSet CreateSBThreatTypeSet(
+    std::initializer_list<SBThreatType> set) {
+  return SBThreatTypeSet(set);
+}
+
+// The information required to uniquely identify each list the client is
+// interested in maintaining and downloading from the SafeBrowsing servers.
+// For example, for digests of Malware binaries on Windows:
+// platform_type = WINDOWS,
+// threat_entry_type = EXECUTABLE,
+// threat_type = MALWARE
+class ListIdentifier {
+ public:
+  ListIdentifier(PlatformType platform_type,
+                 ThreatEntryType threat_entry_type,
+                 ThreatType threat_type);
+  explicit ListIdentifier(const ListUpdateResponse&);
+
+  bool operator==(const ListIdentifier& other) const;
+  bool operator!=(const ListIdentifier& other) const;
+  size_t hash() const;
+
+  PlatformType platform_type() const { return platform_type_; }
+  ThreatEntryType threat_entry_type() const { return threat_entry_type_; }
+  ThreatType threat_type() const { return threat_type_; }
+
+ private:
+  PlatformType platform_type_;
+  ThreatEntryType threat_entry_type_;
+  ThreatType threat_type_;
+
+  ListIdentifier() = delete;
+};
+
+std::ostream& operator<<(std::ostream& os, const ListIdentifier& id);
+
+PlatformType GetCurrentPlatformType();
+ListIdentifier GetCertCsdDownloadWhitelistId();
+ListIdentifier GetChromeExtMalwareId();
+ListIdentifier GetChromeUrlApiId();
+ListIdentifier GetChromeUrlClientIncidentId();
+ListIdentifier GetIpMalwareId();
+ListIdentifier GetUrlBillingId();
+ListIdentifier GetUrlCsdDownloadWhitelistId();
+ListIdentifier GetUrlCsdWhitelistId();
+ListIdentifier GetUrlHighConfidenceAllowlistId();
+ListIdentifier GetUrlMalBinId();
+ListIdentifier GetUrlMalwareId();
+ListIdentifier GetUrlSocEngId();
+ListIdentifier GetUrlSubresourceFilterId();
+ListIdentifier GetUrlSuspiciousSiteId();
+ListIdentifier GetUrlUwsId();
+
+// Returns the basename of the store file, without the ".store" extension.
+std::string GetUmaSuffixForStore(const base::FilePath& file_path);
+
+// Represents the state of each store.
+using StoreStateMap = std::unordered_map<ListIdentifier, std::string>;
+
+// Sever response, parsed in vector form.
+using ParsedServerResponse = std::vector<std::unique_ptr<ListUpdateResponse>>;
+
+// Holds the hash prefix and the store that it matched in.
+struct StoreAndHashPrefix {
+ public:
+  ListIdentifier list_id;
+  HashPrefix hash_prefix;
+
+  StoreAndHashPrefix(ListIdentifier list_id, const HashPrefix& hash_prefix);
+  ~StoreAndHashPrefix();
+
+  bool operator==(const StoreAndHashPrefix& other) const;
+  bool operator!=(const StoreAndHashPrefix& other) const;
+  size_t hash() const;
+
+ private:
+  StoreAndHashPrefix() = delete;
+};
+
+// Used to track the hash prefix and the store in which a full hash's prefix
+// matched.
+using StoreAndHashPrefixes = std::vector<StoreAndHashPrefix>;
+
+// Enumerate failures for histogramming purposes.  DO NOT CHANGE THE
+// ORDERING OF THESE VALUES.
+enum V4OperationResult {
+  // 200 response code means that the server recognized the request.
+  STATUS_200 = 0,
+
+  // Subset of successful responses where the response body wasn't parsable.
+  PARSE_ERROR = 1,
+
+  // Operation request failed (network error).
+  NETWORK_ERROR = 2,
+
+  // Operation request returned HTTP result code other than 200.
+  HTTP_ERROR = 3,
+
+  // Operation attempted during error backoff, no request sent.
+  BACKOFF_ERROR = 4,
+
+  // Operation attempted before min wait duration elapsed, no request sent.
+  MIN_WAIT_DURATION_ERROR = 5,
+
+  // Identical operation already pending.
+  ALREADY_PENDING_ERROR = 6,
+
+  // Memory space for histograms is determined by the max.  ALWAYS
+  // ADD NEW VALUES BEFORE THIS ONE.
+  OPERATION_RESULT_MAX = 7
+};
+
+// A class that provides static methods related to the Pver4 protocol.
+class V4ProtocolManagerUtil {
+ public:
+  // Canonicalizes url as per Google Safe Browsing Specification.
+  // See: https://developers.google.com/safe-browsing/v4/urls-hashing
+  static void CanonicalizeUrl(const GURL& url,
+                              std::string* canonicalized_hostname,
+                              std::string* canonicalized_path,
+                              std::string* canonicalized_query);
+
+  // This method returns the host suffix combinations from the hostname in the
+  // URL, as described here:
+  // https://developers.google.com/safe-browsing/v4/urls-hashing
+  static void GenerateHostVariantsToCheck(const std::string& host,
+                                          std::vector<std::string>* hosts);
+
+  // This method returns the path prefix combinations from the path in the
+  // URL, as described here:
+  // https://developers.google.com/safe-browsing/v4/urls-hashing
+  static void GeneratePathVariantsToCheck(const std::string& path,
+                                          const std::string& query,
+                                          std::vector<std::string>* paths);
+
+  // Given a URL, returns all the patterns we need to check.
+  static void GeneratePatternsToCheck(const GURL& url,
+                                      std::vector<std::string>* urls);
+
+  // Returns a FullHash for the basic host+path pattern for a given URL after
+  // canonicalization. Not intended for general use.
+  static FullHash GetFullHash(const GURL& url);
+
+  // Generates a Pver4 request URL and sets the appropriate header values.
+  // |request_base64| is the serialized request protocol buffer encoded in
+  // base 64.
+  // |method_name| is the name of the method to call, as specified in the proto,
+  // |config| is an instance of V4ProtocolConfig that stores the client config,
+  // |gurl| is set to the value of the PVer4 request URL,
+  // |headers| is populated with the appropriate header values.
+  static void GetRequestUrlAndHeaders(const std::string& request_base64,
+                                      const std::string& method_name,
+                                      const V4ProtocolConfig& config,
+                                      GURL* gurl,
+                                      net::HttpRequestHeaders* headers);
+
+  // Worker function for calculating the backoff times.
+  // |multiplier| is doubled for each consecutive error after the
+  // first, and |error_count| is incremented with each call.
+  // Backoff interval is MIN(((2^(n-1))*15 minutes) * (RAND + 1), 24 hours)
+  // where n is the number of consecutive errors.
+  static base::TimeDelta GetNextBackOffInterval(size_t* error_count,
+                                                size_t* multiplier);
+
+  // Record HTTP response code when there's no error in fetching an HTTP
+  // request, and the error code, when there is.
+  // |metric_name| is the name of the UMA metric to record the response code or
+  // error code against, |net_error| represents the net error code of the HTTP
+  // request, and |response code| represents the HTTP response code received
+  // from the server.
+  static void RecordHttpResponseOrErrorCode(const char* metric_name,
+                                            int net_error,
+                                            int response_code);
+
+  // Generate the set of FullHashes to check for |url|.
+  static void UrlToFullHashes(const GURL& url,
+                              std::vector<FullHash>* full_hashes);
+
+  static bool FullHashToHashPrefix(const FullHash& full_hash,
+                                   PrefixSize prefix_size,
+                                   HashPrefix* hash_prefix);
+
+  static bool FullHashToSmallestHashPrefix(const FullHash& full_hash,
+                                           HashPrefix* hash_prefix);
+
+  static bool FullHashMatchesHashPrefix(const FullHash& full_hash,
+                                        const HashPrefix& hash_prefix);
+
+  static void SetClientInfoFromConfig(ClientInfo* client_info,
+                                      const V4ProtocolConfig& config);
+
+  static bool GetIPV6AddressFromString(const std::string& ip_address,
+                                       net::IPAddress* address);
+
+  // Converts a IPV4 or IPV6 address in |ip_address| to the SHA1 hash of the
+  // corresponding packed IPV6 address in |hashed_encoded_ip|, and adds an
+  // extra byte containing the value 128 at the end. This is done to match the
+  // server implementation for calculating the hash prefix of an IP address.
+  static bool IPAddressToEncodedIPV6Hash(const std::string& ip_address,
+                                         FullHash* hashed_encoded_ip);
+
+  // Stores the client state values for each of the lists in |store_state_map|
+  // into |list_client_states|.
+  static void GetListClientStatesFromStoreStateMap(
+      const std::unique_ptr<StoreStateMap>& store_state_map,
+      std::vector<std::string>* list_client_states);
+
+ private:
+  V4ProtocolManagerUtil() {}
+
+  FRIEND_TEST_ALL_PREFIXES(V4ProtocolManagerUtilTest, TestBackOffLogic);
+  FRIEND_TEST_ALL_PREFIXES(V4ProtocolManagerUtilTest,
+                           TestGetRequestUrlAndUpdateHeaders);
+  FRIEND_TEST_ALL_PREFIXES(V4ProtocolManagerUtilTest, UrlParsing);
+  FRIEND_TEST_ALL_PREFIXES(V4ProtocolManagerUtilTest, CanonicalizeUrl);
+
+  // Composes a URL using |prefix|, |method| (e.g.: encodedFullHashes).
+  // |request_base64|, |client_id|, |version| and |key_param|. |prefix|
+  // should contain the entire url prefix including scheme, host and path.
+  static std::string ComposeUrl(const std::string& prefix,
+                                const std::string& method,
+                                const std::string& request_base64,
+                                const std::string& key_param);
+
+  // Sets the HTTP headers expected by a standard PVer4 request.
+  static void UpdateHeaders(net::HttpRequestHeaders* headers);
+
+  // Given a URL, returns all the hosts we need to check.  They are returned
+  // in order of size (i.e. b.c is first, then a.b.c).
+  static void GenerateHostsToCheck(const GURL& url,
+                                   std::vector<std::string>* hosts);
+
+  // Given a URL, returns all the paths we need to check.
+  static void GeneratePathsToCheck(const GURL& url,
+                                   std::vector<std::string>* paths);
+
+  static std::string RemoveConsecutiveChars(base::StringPiece str,
+                                            const char c);
+
+  DISALLOW_COPY_AND_ASSIGN(V4ProtocolManagerUtil);
+};
+
+using StoresToCheck = std::unordered_set<ListIdentifier>;
+
+}  // namespace safe_browsing
+
+namespace std {
+
+template <>
+struct hash<safe_browsing::PlatformType> {
+  std::size_t operator()(const safe_browsing::PlatformType& p) const {
+    return std::hash<unsigned int>()(p);
+  }
+};
+
+template <>
+struct hash<safe_browsing::ThreatEntryType> {
+  std::size_t operator()(const safe_browsing::ThreatEntryType& tet) const {
+    return std::hash<unsigned int>()(tet);
+  }
+};
+
+template <>
+struct hash<safe_browsing::ThreatType> {
+  std::size_t operator()(const safe_browsing::ThreatType& tt) const {
+    return std::hash<unsigned int>()(tt);
+  }
+};
+
+template <>
+struct hash<safe_browsing::ListIdentifier> {
+  std::size_t operator()(const safe_browsing::ListIdentifier& id) const {
+    return id.hash();
+  }
+};
+
+}  // namespace std
+
+#endif  // COMPONENTS_SAFE_BROWSING_CORE_DB_V4_PROTOCOL_MANAGER_UTIL_H_
diff --git a/components/safe_browsing/core/db/v4_protocol_manager_util_unittest.cc b/components/safe_browsing/core/db/v4_protocol_manager_util_unittest.cc
new file mode 100644
index 0000000..7b6cbb1
--- /dev/null
+++ b/components/safe_browsing/core/db/v4_protocol_manager_util_unittest.cc
@@ -0,0 +1,287 @@
+// 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 "components/safe_browsing/core/db/v4_protocol_manager_util.h"
+
+#include <vector>
+
+#include "base/base64.h"
+#include "base/stl_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/time/time.h"
+#include "components/safe_browsing/core/db/v4_test_util.h"
+#include "net/base/escape.h"
+#include "net/http/http_request_headers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::Time;
+using base::TimeDelta;
+
+namespace safe_browsing {
+
+class V4ProtocolManagerUtilTest : public testing::Test {};
+
+TEST_F(V4ProtocolManagerUtilTest, TestBackOffLogic) {
+  size_t error_count = 0, back_off_multiplier = 1;
+
+  // 1 error.
+  base::TimeDelta next = V4ProtocolManagerUtil::GetNextBackOffInterval(
+      &error_count, &back_off_multiplier);
+  EXPECT_EQ(1U, error_count);
+  EXPECT_EQ(1U, back_off_multiplier);
+  EXPECT_LE(TimeDelta::FromMinutes(15), next);
+  EXPECT_GE(TimeDelta::FromMinutes(30), next);
+
+  // 2 errors.
+  next = V4ProtocolManagerUtil::GetNextBackOffInterval(&error_count,
+                                                       &back_off_multiplier);
+  EXPECT_EQ(2U, error_count);
+  EXPECT_EQ(2U, back_off_multiplier);
+  EXPECT_LE(TimeDelta::FromMinutes(30), next);
+  EXPECT_GE(TimeDelta::FromMinutes(60), next);
+
+  // 3 errors.
+  next = V4ProtocolManagerUtil::GetNextBackOffInterval(&error_count,
+                                                       &back_off_multiplier);
+  EXPECT_EQ(3U, error_count);
+  EXPECT_EQ(4U, back_off_multiplier);
+  EXPECT_LE(TimeDelta::FromMinutes(60), next);
+  EXPECT_GE(TimeDelta::FromMinutes(120), next);
+
+  // 4 errors.
+  next = V4ProtocolManagerUtil::GetNextBackOffInterval(&error_count,
+                                                       &back_off_multiplier);
+  EXPECT_EQ(4U, error_count);
+  EXPECT_EQ(8U, back_off_multiplier);
+  EXPECT_LE(TimeDelta::FromMinutes(120), next);
+  EXPECT_GE(TimeDelta::FromMinutes(240), next);
+
+  // 5 errors.
+  next = V4ProtocolManagerUtil::GetNextBackOffInterval(&error_count,
+                                                       &back_off_multiplier);
+  EXPECT_EQ(5U, error_count);
+  EXPECT_EQ(16U, back_off_multiplier);
+  EXPECT_LE(TimeDelta::FromMinutes(240), next);
+  EXPECT_GE(TimeDelta::FromMinutes(480), next);
+
+  // 6 errors.
+  next = V4ProtocolManagerUtil::GetNextBackOffInterval(&error_count,
+                                                       &back_off_multiplier);
+  EXPECT_EQ(6U, error_count);
+  EXPECT_EQ(32U, back_off_multiplier);
+  EXPECT_LE(TimeDelta::FromMinutes(480), next);
+  EXPECT_GE(TimeDelta::FromMinutes(960), next);
+
+  // 7 errors.
+  next = V4ProtocolManagerUtil::GetNextBackOffInterval(&error_count,
+                                                       &back_off_multiplier);
+  EXPECT_EQ(7U, error_count);
+  EXPECT_EQ(64U, back_off_multiplier);
+  EXPECT_LE(TimeDelta::FromMinutes(960), next);
+  EXPECT_GE(TimeDelta::FromMinutes(1920), next);
+
+  // 8 errors, reached max backoff.
+  next = V4ProtocolManagerUtil::GetNextBackOffInterval(&error_count,
+                                                       &back_off_multiplier);
+  EXPECT_EQ(8U, error_count);
+  EXPECT_EQ(128U, back_off_multiplier);
+  EXPECT_EQ(TimeDelta::FromHours(24), next);
+
+  // 9 errors, reached max backoff and multiplier capped.
+  next = V4ProtocolManagerUtil::GetNextBackOffInterval(&error_count,
+                                                       &back_off_multiplier);
+  EXPECT_EQ(9U, error_count);
+  EXPECT_EQ(128U, back_off_multiplier);
+  EXPECT_EQ(TimeDelta::FromHours(24), next);
+}
+
+TEST_F(V4ProtocolManagerUtilTest, TestGetRequestUrlAndUpdateHeaders) {
+  net::HttpRequestHeaders headers;
+  GURL gurl;
+  V4ProtocolManagerUtil::GetRequestUrlAndHeaders("request_base64", "someMethod",
+                                                 GetTestV4ProtocolConfig(),
+                                                 &gurl, &headers);
+  std::string expectedUrl =
+      "https://safebrowsing.googleapis.com/v4/someMethod?"
+      "$req=request_base64&$ct=application/x-protobuf&key=test_key_param";
+  EXPECT_EQ(expectedUrl, gurl.spec());
+  std::string header_value;
+  EXPECT_TRUE(headers.GetHeader("X-HTTP-Method-Override", &header_value));
+  EXPECT_EQ("POST", header_value);
+}
+
+// Tests that we generate the required host/path combinations for testing
+// according to the Safe Browsing spec.
+// See: https://developers.google.com/safe-browsing/v4/urls-hashing
+TEST_F(V4ProtocolManagerUtilTest, UrlParsing) {
+  std::vector<std::string> hosts, paths;
+
+  GURL url("http://a.b.c/1/2.html?param=1");
+  V4ProtocolManagerUtil::GenerateHostsToCheck(url, &hosts);
+  V4ProtocolManagerUtil::GeneratePathsToCheck(url, &paths);
+  EXPECT_EQ(hosts.size(), static_cast<size_t>(2));
+  EXPECT_EQ(paths.size(), static_cast<size_t>(4));
+  EXPECT_EQ(hosts[0], "b.c");
+  EXPECT_EQ(hosts[1], "a.b.c");
+
+  EXPECT_TRUE(base::Contains(paths, "/1/2.html?param=1"));
+  EXPECT_TRUE(base::Contains(paths, "/1/2.html"));
+  EXPECT_TRUE(base::Contains(paths, "/1/"));
+  EXPECT_TRUE(base::Contains(paths, "/"));
+
+  url = GURL("http://a.b.c.d.e.f.g/1.html");
+  V4ProtocolManagerUtil::GenerateHostsToCheck(url, &hosts);
+  V4ProtocolManagerUtil::GeneratePathsToCheck(url, &paths);
+  EXPECT_EQ(hosts.size(), static_cast<size_t>(5));
+  EXPECT_EQ(paths.size(), static_cast<size_t>(2));
+  EXPECT_EQ(hosts[0], "f.g");
+  EXPECT_EQ(hosts[1], "e.f.g");
+  EXPECT_EQ(hosts[2], "d.e.f.g");
+  EXPECT_EQ(hosts[3], "c.d.e.f.g");
+  EXPECT_EQ(hosts[4], "a.b.c.d.e.f.g");
+  EXPECT_TRUE(base::Contains(paths, "/1.html"));
+  EXPECT_TRUE(base::Contains(paths, "/"));
+
+  url = GURL("http://a.b/saw-cgi/eBayISAPI.dll/");
+  V4ProtocolManagerUtil::GeneratePathsToCheck(url, &paths);
+  EXPECT_EQ(paths.size(), static_cast<size_t>(3));
+  EXPECT_TRUE(base::Contains(paths, "/saw-cgi/eBayISAPI.dll/"));
+  EXPECT_TRUE(base::Contains(paths, "/saw-cgi/"));
+  EXPECT_TRUE(base::Contains(paths, "/"));
+}
+
+// Tests the url canonicalization according to the Safe Browsing spec.
+// See: https://developers.google.com/safe-browsing/v4/urls-hashing
+TEST_F(V4ProtocolManagerUtilTest, CanonicalizeUrl) {
+  struct {
+    const char* input_url;
+    const char* expected_canonicalized_hostname;
+    const char* expected_canonicalized_path;
+    const char* expected_canonicalized_query;
+  } tests[] = {
+      {"http://host/%25%32%35", "host", "/%25", ""},
+      {"http://host/%25%32%35%25%32%35", "host", "/%25%25", ""},
+      {"http://host/%2525252525252525", "host", "/%25", ""},
+      {"http://host/asdf%25%32%35asd", "host", "/asdf%25asd", ""},
+      {"http://host/%%%25%32%35asd%%", "host", "/%25%25%25asd%25%25", ""},
+      {"http://host/%%%25%32%35asd%%", "host", "/%25%25%25asd%25%25", ""},
+      {"http://www.google.com/", "www.google.com", "/", ""},
+      {"http://%31%36%38%2e%31%38%38%2e%39%39%2e%32%36/%2E%73%65%63%75%72%65/"
+       "%77"
+       "%77%77%2E%65%62%61%79%2E%63%6F%6D/",
+       "168.188.99.26", "/.secure/www.ebay.com/", ""},
+      {"http://195.127.0.11/uploads/%20%20%20%20/.verify/"
+       ".eBaysecure=updateuserd"
+       "ataxplimnbqmn-xplmvalidateinfoswqpcmlx=hgplmcx/",
+       "195.127.0.11",
+       "/uploads/%20%20%20%20/.verify/"
+       ".eBaysecure=updateuserdataxplimnbqmn-xplmv"
+       "alidateinfoswqpcmlx=hgplmcx/",
+       ""},
+      {"http://host.com/%257Ea%2521b%2540c%2523d%2524e%25f%255E00%252611%252A"
+       "22%252833%252944_55%252B",
+       "host.com", "/~a!b@c%23d$e%25f^00&11*22(33)44_55+", ""},
+      {"http://3279880203/blah", "195.127.0.11", "/blah", ""},
+      {"http://www.google.com/blah/..", "www.google.com", "/", ""},
+      {"http://www.google.com/blah#fraq", "www.google.com", "/blah", ""},
+      {"http://www.GOOgle.com/", "www.google.com", "/", ""},
+      {"http://www.google.com.../", "www.google.com", "/", ""},
+      {"http://www.google.com/q?", "www.google.com", "/q", ""},
+      {"http://www.google.com/q?r?", "www.google.com", "/q", "r?"},
+      {"http://www.google.com/q?r?s", "www.google.com", "/q", "r?s"},
+      {"http://evil.com/foo#bar#baz", "evil.com", "/foo", ""},
+      {"http://evil.com/foo;", "evil.com", "/foo;", ""},
+      {"http://evil.com/foo?bar;", "evil.com", "/foo", "bar;"},
+      {"http://notrailingslash.com", "notrailingslash.com", "/", ""},
+      {"http://www.gotaport.com:1234/", "www.gotaport.com", "/", ""},
+      {"  http://www.google.com/  ", "www.google.com", "/", ""},
+      {"http:// leadingspace.com/", "%20leadingspace.com", "/", ""},
+      {"http://%20leadingspace.com/", "%20leadingspace.com", "/", ""},
+      {"https://www.securesite.com/", "www.securesite.com", "/", ""},
+      {"http://host.com/ab%23cd", "host.com", "/ab%23cd", ""},
+      {"http://host%3e.com//twoslashes?more//slashes", "host>.com",
+       "/twoslashes", "more//slashes"},
+      {"http://host.com/abc?val=xyz#anything", "host.com", "/abc", "val=xyz"},
+      {"http://abc:def@host.com/xyz", "host.com", "/xyz", ""},
+      {"http://host%3e.com/abc/%2e%2e%2fdef", "host>.com", "/def", ""},
+      {"http://.......host...com.....//abc/////def%2F%2F%2Fxyz", "host.com",
+       "/abc/def/xyz", ""},
+      {"ftp://host.com/foo?bar", "host.com", "/foo", "bar"},
+      {"data:text/html;charset=utf-8,%0D%0A", "", "", ""},
+      {"javascript:alert()", "", "", ""},
+      {"mailto:abc@example.com", "", "", ""},
+  };
+  for (size_t i = 0; i < base::size(tests); ++i) {
+    SCOPED_TRACE(base::StringPrintf("Test: %s", tests[i].input_url));
+    GURL url(tests[i].input_url);
+
+    std::string canonicalized_hostname;
+    std::string canonicalized_path;
+    std::string canonicalized_query;
+    V4ProtocolManagerUtil::CanonicalizeUrl(url, &canonicalized_hostname,
+                                           &canonicalized_path,
+                                           &canonicalized_query);
+
+    EXPECT_EQ(tests[i].expected_canonicalized_hostname, canonicalized_hostname);
+    EXPECT_EQ(tests[i].expected_canonicalized_path, canonicalized_path);
+    EXPECT_EQ(tests[i].expected_canonicalized_query, canonicalized_query);
+  }
+}
+
+TEST_F(V4ProtocolManagerUtilTest, TestIPAddressToEncodedIPV6) {
+  // To verify the test values, here's the python code:
+  // >> import socket, hashlib, binascii
+  // >> hashlib.sha1(socket.inet_pton(socket.AF_INET6, input)).digest() +
+  // chr(128)
+  // For example:
+  // >>> hashlib.sha1(socket.inet_pton(socket.AF_INET6,
+  // '::ffff:192.168.1.1')).digest() + chr(128)
+  // 'X\xf8\xa1\x17I\xe6Pl\xfd\xdb\xbb\xa0\x0c\x02\x9d#\n|\xe7\xcd\x80'
+  std::vector<std::tuple<bool, std::string, std::string>> test_cases = {
+      std::make_tuple(false, "", ""),
+      std::make_tuple(
+          true, "192.168.1.1",
+          "X\xF8\xA1\x17I\xE6Pl\xFD\xDB\xBB\xA0\f\x2\x9D#\n|\xE7\xCD\x80"),
+      std::make_tuple(
+          true,
+          "::", "\xE1)\xF2|Q\x3\xBC\\\xC4K\xCD\xF0\xA1^\x16\rDPf\xFF\x80")};
+  for (size_t i = 0; i < test_cases.size(); i++) {
+    DVLOG(1) << "Running case: " << i;
+    bool success = std::get<0>(test_cases[i]);
+    const auto& input = std::get<1>(test_cases[i]);
+    const auto& expected_output = std::get<2>(test_cases[i]);
+    std::string encoded_ip;
+    ASSERT_EQ(success, V4ProtocolManagerUtil::IPAddressToEncodedIPV6Hash(
+                           input, &encoded_ip));
+    if (success) {
+      ASSERT_EQ(expected_output, encoded_ip);
+    }
+  }
+}
+
+TEST_F(V4ProtocolManagerUtilTest, TestFullHashToHashPrefix) {
+  const std::string full_hash = "abcdefgh";
+  std::vector<std::tuple<bool, std::string, PrefixSize, std::string>>
+      test_cases = {
+          std::make_tuple(true, "", 0, ""),
+          std::make_tuple(false, "", kMinHashPrefixLength, ""),
+          std::make_tuple(true, "a", 1, full_hash),
+          std::make_tuple(true, "abcd", kMinHashPrefixLength, full_hash),
+          std::make_tuple(true, "abcde", kMinHashPrefixLength + 1, full_hash)};
+  for (size_t i = 0; i < test_cases.size(); i++) {
+    DVLOG(1) << "Running case: " << i;
+    bool success = std::get<0>(test_cases[i]);
+    const auto& expected_prefix = std::get<1>(test_cases[i]);
+    const PrefixSize& prefix_size = std::get<2>(test_cases[i]);
+    const auto& input_full_hash = std::get<3>(test_cases[i]);
+    std::string prefix;
+    ASSERT_EQ(success, V4ProtocolManagerUtil::FullHashToHashPrefix(
+                           input_full_hash, prefix_size, &prefix));
+    if (success) {
+      ASSERT_EQ(expected_prefix, prefix);
+    }
+  }
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/db/v4_rice.cc b/components/safe_browsing/core/db/v4_rice.cc
new file mode 100644
index 0000000..ebc31055
--- /dev/null
+++ b/components/safe_browsing/core/db/v4_rice.cc
@@ -0,0 +1,296 @@
+// 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 <algorithm>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/numerics/safe_math.h"
+#include "base/strings/stringprintf.h"
+#include "build/build_config.h"
+#include "components/safe_browsing/core/db/v4_rice.h"
+
+#if defined(OS_WIN)
+#include <winsock2.h>
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+#include <arpa/inet.h>
+#endif
+
+using ::google::protobuf::RepeatedField;
+using ::google::protobuf::int32;
+using ::google::protobuf::int64;
+
+#if !defined(ARCH_CPU_LITTLE_ENDIAN) || (ARCH_CPU_LITTLE_ENDIAN != 1)
+#error The code below assumes little-endianness.
+#endif
+
+namespace safe_browsing {
+
+namespace {
+
+const int kBitsPerByte = 8;
+const unsigned int kMaxBitIndex = kBitsPerByte * sizeof(uint32_t);
+
+}  // namespace
+
+// static
+V4DecodeResult V4RiceDecoder::ValidateInput(const int32 rice_parameter,
+                                            const int32 num_entries,
+                                            const std::string& encoded_data) {
+  if (num_entries < 0) {
+    NOTREACHED();
+    return NUM_ENTRIES_NEGATIVE_FAILURE;
+  }
+
+  if (num_entries == 0) {
+    return DECODE_SUCCESS;
+  }
+
+  if (rice_parameter <= 0) {
+    NOTREACHED();
+    return RICE_PARAMETER_NON_POSITIVE_FAILURE;
+  }
+
+  if (encoded_data.empty()) {
+    NOTREACHED();
+    return ENCODED_DATA_UNEXPECTED_EMPTY_FAILURE;
+  }
+
+  return DECODE_SUCCESS;
+}
+
+// static
+V4DecodeResult V4RiceDecoder::DecodeIntegers(const int64 first_value,
+                                             const int32 rice_parameter,
+                                             const int32 num_entries,
+                                             const std::string& encoded_data,
+                                             RepeatedField<int32>* out) {
+  DCHECK(out);
+
+  V4DecodeResult result =
+      ValidateInput(rice_parameter, num_entries, encoded_data);
+  if (result != DECODE_SUCCESS) {
+    return result;
+  }
+
+  out->Reserve(num_entries + 1);
+  base::CheckedNumeric<int32> last_value(first_value);
+  out->Add(last_value.ValueOrDie());
+  if (num_entries == 0) {
+    return DECODE_SUCCESS;
+  }
+
+  V4RiceDecoder decoder(rice_parameter, num_entries, encoded_data);
+  while (decoder.HasAnotherValue()) {
+    uint32_t offset;
+    result = decoder.GetNextValue(&offset);
+    if (result != DECODE_SUCCESS) {
+      return result;
+    }
+
+    last_value += offset;
+    if (!last_value.IsValid()) {
+      NOTREACHED();
+      return DECODED_INTEGER_OVERFLOW_FAILURE;
+    }
+
+    out->Add(last_value.ValueOrDie());
+  }
+
+  return DECODE_SUCCESS;
+}
+
+// static
+V4DecodeResult V4RiceDecoder::DecodePrefixes(const int64 first_value,
+                                             const int32 rice_parameter,
+                                             const int32 num_entries,
+                                             const std::string& encoded_data,
+                                             std::vector<uint32_t>* out) {
+  DCHECK(out);
+
+  V4DecodeResult result =
+      ValidateInput(rice_parameter, num_entries, encoded_data);
+  if (result != DECODE_SUCCESS) {
+    return result;
+  }
+  out->reserve((num_entries + 1));
+
+  base::CheckedNumeric<uint32_t> last_value(first_value);
+  out->push_back(htonl(last_value.ValueOrDie()));
+
+  if (num_entries > 0) {
+    V4RiceDecoder decoder(rice_parameter, num_entries, encoded_data);
+    while (decoder.HasAnotherValue()) {
+      uint32_t offset;
+      result = decoder.GetNextValue(&offset);
+      if (result != DECODE_SUCCESS) {
+        return result;
+      }
+
+      last_value += offset;
+      if (!last_value.IsValid()) {
+        NOTREACHED();
+        return DECODED_INTEGER_OVERFLOW_FAILURE;
+      }
+
+      // This flipping is done so that the decoded uint32 is interpreted
+      // correcly as a string of 4 bytes.
+      out->push_back(htonl(last_value.ValueOrDie()));
+    }
+  }
+
+  // Flipping the bytes, as done above, destroys the sort order. Sort the
+  // values back.
+  std::sort(out->begin(), out->end());
+
+  // This flipping is done so that when the vector is interpreted as a string,
+  // the bytes are in the correct order.
+  for (size_t i = 0; i < out->size(); i++) {
+    (*out)[i] = ntohl((*out)[i]);
+  }
+
+  return DECODE_SUCCESS;
+}
+
+V4RiceDecoder::V4RiceDecoder(const int rice_parameter,
+                             const int num_entries,
+                             const std::string& encoded_data)
+    : rice_parameter_(rice_parameter),
+      num_entries_(num_entries),
+      data_(encoded_data),
+      current_word_(0) {
+  DCHECK_LE(0, num_entries_);
+  DCHECK_LE(2u, rice_parameter_);
+  DCHECK_GE(28u, rice_parameter_);
+
+  data_byte_index_ = 0;
+  current_word_bit_index_ = kMaxBitIndex;
+}
+
+V4RiceDecoder::~V4RiceDecoder() {}
+
+bool V4RiceDecoder::HasAnotherValue() const {
+  return num_entries_ > 0;
+}
+
+V4DecodeResult V4RiceDecoder::GetNextValue(uint32_t* value) {
+  if (!HasAnotherValue()) {
+    return DECODE_NO_MORE_ENTRIES_FAILURE;
+  }
+
+  V4DecodeResult result;
+  uint32_t q = 0;
+  uint32_t bit;
+  do {
+    result = GetNextBits(1, &bit);
+    if (result != DECODE_SUCCESS) {
+      return result;
+    }
+    q += bit;
+  } while (bit);
+  uint32_t r = 0;
+  result = GetNextBits(rice_parameter_, &r);
+  if (result != DECODE_SUCCESS) {
+    return result;
+  }
+
+  *value = (q << rice_parameter_) + r;
+  num_entries_--;
+  return DECODE_SUCCESS;
+}
+
+V4DecodeResult V4RiceDecoder::GetNextWord(uint32_t* word) {
+  if (data_byte_index_ >= data_.size()) {
+    return DECODE_RAN_OUT_OF_BITS_FAILURE;
+  }
+
+  const size_t mask = 0xFF;
+  *word = (data_[data_byte_index_] & mask);
+  data_byte_index_++;
+  current_word_bit_index_ = 0;
+
+  if (data_byte_index_ < data_.size()) {
+    *word |= ((data_[data_byte_index_] & mask) << 8);
+    data_byte_index_++;
+
+    if (data_byte_index_ < data_.size()) {
+      *word |= ((data_[data_byte_index_] & mask) << 16);
+      data_byte_index_++;
+
+      if (data_byte_index_ < data_.size()) {
+        *word |= ((data_[data_byte_index_] & mask) << 24);
+        data_byte_index_++;
+      }
+    }
+  }
+
+  return DECODE_SUCCESS;
+}
+
+V4DecodeResult V4RiceDecoder::GetNextBits(unsigned int num_requested_bits,
+                                          uint32_t* x) {
+  if (num_requested_bits > kMaxBitIndex) {
+    NOTREACHED();
+    return DECODE_REQUESTED_TOO_MANY_BITS_FAILURE;
+  }
+
+  if (current_word_bit_index_ == kMaxBitIndex) {
+    V4DecodeResult result = GetNextWord(&current_word_);
+    if (result != DECODE_SUCCESS) {
+      return result;
+    }
+  }
+
+  unsigned int num_bits_left_in_current_word =
+      kMaxBitIndex - current_word_bit_index_;
+  if (num_bits_left_in_current_word >= num_requested_bits) {
+    // All the bits that we need are in |current_word_|.
+    *x = GetBitsFromCurrentWord(num_requested_bits);
+  } else {
+    // |current_word_| contains fewer bits than we need so read the remaining
+    // bits from |current_word_| into |lower|, and then call GetNextBits on the
+    // remaining number of bits, which will read in a new word into
+    // |current_word_|.
+    uint32_t lower = GetBitsFromCurrentWord(num_bits_left_in_current_word);
+
+    unsigned int num_bits_from_next_word =
+        num_requested_bits - num_bits_left_in_current_word;
+    uint32_t upper;
+    V4DecodeResult result = GetNextBits(num_bits_from_next_word, &upper);
+    if (result != DECODE_SUCCESS) {
+      return result;
+    }
+    *x = (upper << num_bits_left_in_current_word) | lower;
+  }
+  return DECODE_SUCCESS;
+}
+
+uint32_t V4RiceDecoder::GetBitsFromCurrentWord(
+    unsigned int num_requested_bits) {
+  uint32_t mask = 0xFFFFFFFF >> (kMaxBitIndex - num_requested_bits);
+  uint32_t x = current_word_ & mask;
+  current_word_ = current_word_ >> num_requested_bits;
+  current_word_bit_index_ += num_requested_bits;
+  return x;
+}
+
+std::string V4RiceDecoder::DebugString() const {
+  // Calculates the total number of bits that we have read from the buffer,
+  // excluding those that have been read into current_word_ but not yet
+  // consumed byt GetNextBits().
+  unsigned bits_read = (data_byte_index_ - sizeof(uint32_t)) * kBitsPerByte +
+                       current_word_bit_index_;
+  return base::StringPrintf(
+      "bits_read: %x; current_word_: %x; data_byte_index_; %x, "
+      "current_word_bit_index_: %x; rice_parameter_: %x",
+      bits_read, current_word_, data_byte_index_, current_word_bit_index_,
+      rice_parameter_);
+}
+
+std::ostream& operator<<(std::ostream& os, const V4RiceDecoder& rice_decoder) {
+  os << rice_decoder.DebugString();
+  return os;
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/db/v4_rice.h b/components/safe_browsing/core/db/v4_rice.h
new file mode 100644
index 0000000..4ea0572
--- /dev/null
+++ b/components/safe_browsing/core/db/v4_rice.h
@@ -0,0 +1,164 @@
+// 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.
+
+// Rice-Golomb decoder for blacklist updates.
+// Details at: https://en.wikipedia.org/wiki/Golomb_coding
+
+#ifndef COMPONENTS_SAFE_BROWSING_CORE_DB_V4_RICE_H_
+#define COMPONENTS_SAFE_BROWSING_CORE_DB_V4_RICE_H_
+
+#include <ostream>
+#include <string>
+#include <vector>
+#include "base/gtest_prod_util.h"
+#include "third_party/protobuf/src/google/protobuf/repeated_field.h"
+
+namespace safe_browsing {
+
+// Enumerate different failure events while decoding the Rice-encoded string
+// sent by the server for histogramming purposes. DO NOT CHANGE THE ORDERING OF
+// THESE VALUES.
+enum V4DecodeResult {
+  // No error.
+  DECODE_SUCCESS = 0,
+
+  // Exceeded the number of entries to expect.
+  DECODE_NO_MORE_ENTRIES_FAILURE = 1,
+
+  // Requested to decode >32 bits.
+  DECODE_REQUESTED_TOO_MANY_BITS_FAILURE = 2,
+
+  // All bits had already been read and interpreted in the encoded string.
+  DECODE_RAN_OUT_OF_BITS_FAILURE = 3,
+
+  // The num_entries argument to DecodePrefixes or DecodeIntegers was negative.
+  NUM_ENTRIES_NEGATIVE_FAILURE = 4,
+
+  // Rice-encoding parameter was non-positive when the number of encoded entries
+  // was > 0.
+  RICE_PARAMETER_NON_POSITIVE_FAILURE = 5,
+
+  // |encoded_data| was empty when the number of encoded entries was > 0.
+  ENCODED_DATA_UNEXPECTED_EMPTY_FAILURE = 6,
+
+  // decoded value had an integer overflow, which is unexpected.
+  DECODED_INTEGER_OVERFLOW_FAILURE = 7,
+
+  // Memory space for histograms is determined by the max.  ALWAYS
+  // ADD NEW VALUES BEFORE THIS ONE.
+  DECODE_RESULT_MAX
+};
+
+class V4RiceDecoder {
+ public:
+  // Decodes the Rice-encoded string in |encoded_data| as a list of integers
+  // and stores them in |out|. |rice_parameter| is the exponent of 2 for
+  // calculating 'M', |first_value| is the first value in the output sequence,
+  // |num_entries| is the number of subsequent encoded entries. Each decoded
+  // value is a positive offset from the previous value.
+  // So, for instance, if the unencoded sequence is: [3, 7, 25], then
+  // |first_value| = 3, |num_entries| = 2 and decoding the |encoded_data| will
+  // produce the offsets: [4, 18].
+  static V4DecodeResult DecodeIntegers(
+      const ::google::protobuf::int64 first_value,
+      const ::google::protobuf::int32 rice_parameter,
+      const ::google::protobuf::int32 num_entries,
+      const std::string& encoded_data,
+      ::google::protobuf::RepeatedField<::google::protobuf::int32>* out);
+
+  // Decodes the Rice-encoded string in |encoded_data| as a string of 4-byte
+  // hash prefixes and stores them in |out|. The rest of the arguments are the
+  // same as for |DecodeIntegers|.
+  // Important: |out| is only meant to be used as a concatenated list of sorted
+  // 4-byte hash prefixes, not as a vector of uint32_t values.
+  // This method does the following:
+  // 1. Rice-decode the |encoded_data| as a list of uint32_t values.
+  // 2. Flip the endianness (on little-endian machines) of each of these
+  //    values. This is done because when a hash prefix is represented as a
+  //    uint32_t, the bytes get reordered. This generates the hash prefix that
+  //    the server would have sent in the absence of Rice-encoding.
+  // 3. Sort the resulting list of uint32_t values.
+  // 4. Flip the endianness once again since the uint32_t are expected to be
+  //    consumed as a concatenated list of 4-byte hash prefixes, when merging
+  //    the
+  //    update with the existing state.
+  static V4DecodeResult DecodePrefixes(
+      const ::google::protobuf::int64 first_value,
+      const ::google::protobuf::int32 rice_parameter,
+      const ::google::protobuf::int32 num_entries,
+      const std::string& encoded_data,
+      std::vector<uint32_t>* out);
+
+  virtual ~V4RiceDecoder();
+
+  std::string DebugString() const;
+
+ private:
+  FRIEND_TEST_ALL_PREFIXES(V4RiceTest, TestDecoderGetNextWordWithNoData);
+  FRIEND_TEST_ALL_PREFIXES(V4RiceTest, TestDecoderGetNextBitsWithNoData);
+  FRIEND_TEST_ALL_PREFIXES(V4RiceTest, TestDecoderGetNextValueWithNoData);
+  FRIEND_TEST_ALL_PREFIXES(V4RiceTest, TestDecoderGetNextValueWithNoEntries);
+  friend class V4RiceTest;
+
+  // Validate some of the parameters passed to the decode methods.
+  static V4DecodeResult ValidateInput(
+      const ::google::protobuf::int32 rice_parameter,
+      const ::google::protobuf::int32 num_entries,
+      const std::string& encoded_data);
+
+  // The |rice_parameter| is the exponent of 2 for calculating 'M',
+  // |num_entries| is the number of encoded entries in the |encoded_data| and
+  // |encoded_data| is the Rice-encoded string to decode.
+  V4RiceDecoder(const ::google::protobuf::int32 rice_parameter,
+                const ::google::protobuf::int32 num_entries,
+                const std::string& encoded_data);
+
+  // Returns true until |num_entries| entries have been decoded.
+  bool HasAnotherValue() const;
+
+  // Populates |value| with the next 32-bit unsigned integer decoded from
+  // |encoded_data|.
+  V4DecodeResult GetNextValue(uint32_t* value);
+
+  // Reads in up to 32 bits from |encoded_data| into |word|, from which
+  // subsequent GetNextBits() calls read bits.
+  V4DecodeResult GetNextWord(uint32_t* word);
+
+  // Reads |num_requested_bits| into |x| from |current_word_| and advances it
+  // if needed by calling GetNextWord().
+  V4DecodeResult GetNextBits(unsigned int num_requested_bits, uint32_t* x);
+
+  // Reads |num_requested_bits| from |current_word_|.
+  uint32_t GetBitsFromCurrentWord(unsigned int num_requested_bits);
+
+  // The Rice parameter, which is the exponent of two for calculating 'M'. 'M'
+  // is used as the base to calculate the quotient and remainder in the
+  // algorithm.
+  const unsigned int rice_parameter_;
+
+  // The number of entries encoded in the data stream.
+  ::google::protobuf::int32 num_entries_;
+
+  // The Rice-encoded string.
+  const std::string data_;
+
+  // Represents how many total bytes have we read from |data_| into
+  // |current_word_|.
+  unsigned int data_byte_index_;
+
+  // Represents the number of bits that we have read from |current_word_|. When
+  // this becomes 32, which is the size of |current_word_|, a new
+  // |current_word_| needs to be read from |data_|.
+  unsigned int current_word_bit_index_;
+
+  // The 32-bit value read from |data_|. All bit reading operations operate on
+  // |current_word_|.
+  uint32_t current_word_;
+};
+
+std::ostream& operator<<(std::ostream& os, const V4RiceDecoder& rice_decoder);
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CORE_DB_V4_RICE_H_
diff --git a/components/safe_browsing/core/db/v4_rice_unittest.cc b/components/safe_browsing/core/db/v4_rice_unittest.cc
new file mode 100644
index 0000000..60bcab9
--- /dev/null
+++ b/components/safe_browsing/core/db/v4_rice_unittest.cc
@@ -0,0 +1,277 @@
+// 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 "components/safe_browsing/core/db/v4_rice.h"
+#include "base/logging.h"
+#include "testing/platform_test.h"
+
+using ::google::protobuf::RepeatedField;
+using ::google::protobuf::int32;
+
+namespace safe_browsing {
+
+class V4RiceTest : public PlatformTest {
+ public:
+  V4RiceTest() {}
+
+  struct RiceDecodingTestInfo {
+    uint32_t rice_parameter;
+    std::vector<uint32_t> expected_values;
+    std::string encoded_string;
+
+    RiceDecodingTestInfo(const uint32_t in_rice_parameter,
+                         const std::vector<uint32_t>& in_expected_values,
+                         const std::string& in_encoded_string) {
+      rice_parameter = in_rice_parameter;
+      expected_values = in_expected_values;
+      encoded_string = in_encoded_string;
+    }
+  };
+
+  void VerifyRiceDecoding(const RiceDecodingTestInfo& test_info) {
+    const uint32_t num_entries = test_info.expected_values.size();
+    V4RiceDecoder decoder(test_info.rice_parameter, num_entries,
+                          test_info.encoded_string);
+    uint32_t word;
+    for (const auto& expected : test_info.expected_values) {
+      EXPECT_EQ(DECODE_SUCCESS, decoder.GetNextValue(&word));
+      EXPECT_EQ(expected, word);
+    }
+    ASSERT_FALSE(decoder.HasAnotherValue());
+  }
+};
+
+TEST_F(V4RiceTest, TestDecoderGetNextWordWithNoData) {
+  uint32_t word;
+  V4RiceDecoder decoder(5, 1, "");
+  EXPECT_EQ(DECODE_RAN_OUT_OF_BITS_FAILURE, decoder.GetNextWord(&word));
+}
+
+TEST_F(V4RiceTest, TestDecoderGetNextBitsWithNoData) {
+  uint32_t word;
+  V4RiceDecoder decoder(5, 1, "");
+  EXPECT_EQ(DECODE_RAN_OUT_OF_BITS_FAILURE, decoder.GetNextBits(1, &word));
+}
+
+TEST_F(V4RiceTest, TestDecoderGetNextValueWithNoData) {
+  uint32_t word;
+  V4RiceDecoder decoder(5, 1, "");
+  EXPECT_EQ(DECODE_RAN_OUT_OF_BITS_FAILURE, decoder.GetNextValue(&word));
+}
+
+TEST_F(V4RiceTest, TestDecoderGetNextValueWithNoEntries) {
+  uint32_t word;
+  V4RiceDecoder decoder(28, 0, "\xbf\xa8");
+  ASSERT_FALSE(decoder.HasAnotherValue());
+  EXPECT_EQ(DECODE_NO_MORE_ENTRIES_FAILURE, decoder.GetNextValue(&word));
+}
+
+TEST_F(V4RiceTest, TestDecoderGetNextValueWithInterestingValues) {
+  // These values are interesting because they match the unit test
+  // values used within Google to this test this code in other
+  // components, such as the SafeBrowsing service itself.
+
+  std::vector<RiceDecodingTestInfo> test_inputs = {
+      RiceDecodingTestInfo(2, {15, 9}, "\xf7\x2"),
+      RiceDecodingTestInfo(
+          28, {1777762129, 2093280223, 924369848},
+          "\xbf\xa8\x3f\xfb\xfc\xfb\x5e\x27\xe6\xc3\x1d\xc6\x38"),
+      RiceDecodingTestInfo(
+          28, {62763050, 1046523781, 192522171, 1800511020, 4442775, 582142548},
+          "\x54\x60\x7b\xe7\x0a\x5f\xc1\xdc\xee\x69\xde"
+          "\xfe\x58\x3c\xa3\xd6\xa5\xf2\x10\x8c\x4a\x59"
+          "\x56\x00"),
+      RiceDecodingTestInfo(
+          28,
+          {26067715, 344823336, 8420095, 399843890, 95029378, 731622412,
+           35811335, 1047558127, 1117722715, 78698892},
+          "\x06\x86\x1b\x23\x14\xcb\x46\xf2\xaf\x07\x08\xc9\x88\x54\x1f\x41\x04"
+          "\xd5\x1a\x03\xeb\xe6\x3a\x80\x13\x91\x7b\xbf\x83\xf3\xb7\x85\xf1\x29"
+          "\x18\xb3\x61\x09"),
+      RiceDecodingTestInfo(
+          27,
+          {225846818, 328287420, 166748623, 29117720, 552397365, 350353215,
+           558267528, 4738273, 567093445, 28563065, 55077698, 73091685,
+           339246010, 98242620, 38060941, 63917830, 206319759, 137700744},
+          "\x89\x98\xd8\x75\xbc\x44\x91\xeb\x39\x0c\x3e\x30\x9a\x78\xf3\x6a\xd4"
+          "\xd9\xb1\x9f\xfb\x70\x3e\x44\x3e\xa3\x08\x67\x42\xc2\x2b\x46\x69\x8e"
+          "\x3c\xeb\xd9\x10\x5a\x43\x9a\x32\xa5\x2d\x4e\x77\x0f\x87\x78\x20\xb6"
+          "\xab\x71\x98\x48\x0c\x9e\x9e\xd7\x23\x0c\x13\x43\x2c\xa9\x01"),
+      RiceDecodingTestInfo(
+          28,
+          {339784008, 263128563, 63871877, 69723256, 826001074, 797300228,
+           671166008, 207712688},
+          std::string("\x21\xc5\x02\x91\xf9\x82\xd7\x57\xb8\xe9\x3c\xf0\xc8\x4f"
+                      "\xe8\x64\x8d\x77\x62\x04\xd6\x85\x3f\x1c\x97\x00\x04\x1b"
+                      "\x17\xc6",
+                      30)),
+      RiceDecodingTestInfo(
+          28,
+          {471820069, 196333855, 855579133, 122737976, 203433838, 85354544,
+           1307949392, 165938578, 195134475, 553930435, 49231136},
+          "\x95\x9c\x7d\xb0\x8f\xe8\xd9\xbd\xfe\x8c\x7f\x81\x53\x0d\x75\xdc\x4e"
+          "\x40\x18\x0c\x9a\x45\x3d\xa8\xdc\xfa\x26\x59\x40\x9e\x16\x08\x43\x77"
+          "\xc3\x4e\x04\x01\xa4\xe6\x5d\x00"),
+      RiceDecodingTestInfo(
+          27,
+          {87336845, 129291033, 30906211, 433549264, 30899891, 53207875,
+           11959529, 354827862, 82919275, 489637251, 53561020, 336722992,
+           408117728, 204506246, 188216092, 9047110, 479817359, 230317256},
+          "\x1a\x4f\x69\x2a\x63\x9a\xf6\xc6\x2e\xaf\x73\xd0\x6f\xd7\x31\xeb\x77"
+          "\x1d\x43\xe3\x2b\x93\xce\x67\x8b\x59\xf9\x98\xd4\xda\x4f\x3c\x6f\xb0"
+          "\xe8\xa5\x78\x8d\x62\x36\x18\xfe\x08\x1e\x78\xd8\x14\x32\x24\x84\x61"
+          "\x1c\xf3\x37\x63\xc4\xa0\x88\x7b\x74\xcb\x64\xc8\x5c\xba\x05"),
+      RiceDecodingTestInfo(
+          28,
+          {297968956, 19709657, 259702329, 76998112, 1023176123, 29296013,
+           1602741145, 393745181, 177326295, 55225536, 75194472},
+          "\xf1\x94\x0a\x87\x6c\x5f\x96\x90\xe3\xab\xf7\xc0\xcb\x2d\xe9\x76\xdb"
+          "\xf8\x59\x63\xc1\x6f\x7c\x99\xe3\x87\x5f\xc7\x04\xde\xb9\x46\x8e\x54"
+          "\xc0\xac\x4a\x03\x0d\x6c\x8f\x00"),
+      RiceDecodingTestInfo(
+          28,
+          {532220688, 780594691, 436816483, 163436269, 573044456, 1069604,
+           39629436, 211410997, 227714491, 381562898, 75610008, 196754597,
+           40310339, 15204118, 99010842},
+          "\x41\x2c\xe4\xfe\x06\xdc\x0d\xbd\x31\xa5\x04\xd5\x6e\xdd\x9b\x43\xb7"
+          "\x3f\x11\x24\x52\x10\x80\x4f\x96\x4b\xd4\x80\x67\xb2\xdd\x52\xc9\x4e"
+          "\x02\xc6\xd7\x60\xde\x06\x92\x52\x1e\xdd\x35\x64\x71\x26\x2c\xfe\xcf"
+          "\x81\x46\xb2\x79\x01"),
+      RiceDecodingTestInfo(
+          28,
+          {219354713, 389598618, 750263679, 554684211, 87381124, 4523497,
+           287633354, 801308671, 424169435, 372520475, 277287849},
+          "\xb2\x2c\x26\x3a\xcd\x66\x9c\xdb\x5f\x07\x2e\x6f\xe6\xf9\x21\x10\x52"
+          "\xd5\x94\xf4\x82\x22\x48\xf9\x9d\x24\xf6\xff\x2f\xfc\x6d\x3f\x21\x65"
+          "\x1b\x36\x34\x56\xea\xc4\x21\x00"),
+  };
+
+  for (size_t i = 0; i < test_inputs.size(); i++) {
+    DVLOG(1) << "Running test case: " << i;
+    VerifyRiceDecoding(test_inputs[i]);
+  }
+}
+
+#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
+// This test hits a NOTREACHED so it is a release mode only test.
+TEST_F(V4RiceTest, TestDecoderIntegersWithNoData) {
+  RepeatedField<int32> out;
+  EXPECT_EQ(ENCODED_DATA_UNEXPECTED_EMPTY_FAILURE,
+            V4RiceDecoder::DecodeIntegers(3, 5, 1, "", &out));
+}
+#endif
+
+#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
+// This test hits a NOTREACHED so it is a release mode only test.
+TEST_F(V4RiceTest, TestDecoderIntegersWithNegativeNumEntries) {
+  RepeatedField<int32> out;
+  EXPECT_EQ(NUM_ENTRIES_NEGATIVE_FAILURE,
+            V4RiceDecoder::DecodeIntegers(3, 5, -1, "", &out));
+}
+#endif
+
+#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
+// This test hits a NOTREACHED so it is a release mode only test.
+TEST_F(V4RiceTest, TestDecoderIntegersWithNonPositiveRiceParameter) {
+  RepeatedField<int32> out;
+  EXPECT_EQ(RICE_PARAMETER_NON_POSITIVE_FAILURE,
+            V4RiceDecoder::DecodeIntegers(3, 0, 1, "a", &out));
+
+  EXPECT_EQ(RICE_PARAMETER_NON_POSITIVE_FAILURE,
+            V4RiceDecoder::DecodeIntegers(3, -1, 1, "a", &out));
+}
+#endif
+
+#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
+// This test hits a NOTREACHED so it is a release mode only test.
+TEST_F(V4RiceTest, TestDecoderIntegersWithOverflowValues) {
+  RepeatedField<int32> out;
+  EXPECT_EQ(DECODED_INTEGER_OVERFLOW_FAILURE,
+            V4RiceDecoder::DecodeIntegers(
+                5, 28, 3,
+                "\xbf\xa8\x3f\xfb\xfc\xfb\x5e\x27\xe6\xc3\x1d\xc6\x38", &out));
+}
+#endif
+
+TEST_F(V4RiceTest, TestDecoderIntegersWithOneValue) {
+  RepeatedField<int32> out;
+  EXPECT_EQ(DECODE_SUCCESS, V4RiceDecoder::DecodeIntegers(3, 2, 0, "", &out));
+  EXPECT_EQ(1, out.size());
+  EXPECT_EQ(3, out.Get(0));
+}
+
+TEST_F(V4RiceTest, TestDecoderIntegersWithMultipleValues) {
+  RepeatedField<int32> out;
+  EXPECT_EQ(DECODE_SUCCESS,
+            V4RiceDecoder::DecodeIntegers(5, 2, 2, "\xf7\x2", &out));
+  EXPECT_EQ(3, out.size());
+  EXPECT_EQ(5, out.Get(0));
+  EXPECT_EQ(20, out.Get(1));
+  EXPECT_EQ(29, out.Get(2));
+}
+
+#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
+// This test hits a NOTREACHED so it is a release mode only test.
+TEST_F(V4RiceTest, TestDecoderPrefixesWithNoData) {
+  std::vector<uint32_t> out;
+  EXPECT_EQ(ENCODED_DATA_UNEXPECTED_EMPTY_FAILURE,
+            V4RiceDecoder::DecodePrefixes(3, 5, 1, "", &out));
+}
+#endif
+
+#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
+// This test hits a NOTREACHED so it is a release mode only test.
+TEST_F(V4RiceTest, TestDecoderPrefixesWithNegativeNumEntries) {
+  std::vector<uint32_t> out;
+  EXPECT_EQ(NUM_ENTRIES_NEGATIVE_FAILURE,
+            V4RiceDecoder::DecodePrefixes(3, 5, -1, "", &out));
+}
+#endif
+
+#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
+// This test hits a NOTREACHED so it is a release mode only test.
+TEST_F(V4RiceTest, TestDecoderPrefixesWithNonPositiveRiceParameter) {
+  std::vector<uint32_t> out;
+  EXPECT_EQ(RICE_PARAMETER_NON_POSITIVE_FAILURE,
+            V4RiceDecoder::DecodePrefixes(3, 0, 1, "a", &out));
+
+  EXPECT_EQ(RICE_PARAMETER_NON_POSITIVE_FAILURE,
+            V4RiceDecoder::DecodePrefixes(3, -1, 1, "a", &out));
+}
+#endif
+
+TEST_F(V4RiceTest, TestDecoderPrefixesWithOneValue) {
+  std::vector<uint32_t> out;
+  EXPECT_TRUE(out.empty());
+  EXPECT_EQ(DECODE_SUCCESS,
+            V4RiceDecoder::DecodePrefixes(0x69F67F51u, 2, 0, "", &out));
+  EXPECT_EQ(1u, out.size());
+  EXPECT_EQ(0x69F67F51u, out[0]);
+}
+
+TEST_F(V4RiceTest, TestDecoderPrefixesWithMultipleValues) {
+  std::vector<uint32_t> out;
+  EXPECT_EQ(DECODE_SUCCESS,
+            V4RiceDecoder::DecodePrefixes(
+                5, 28, 3, "\xbf\xa8\x3f\xfb\xf\xf\x5e\x27\xe6\xc3\x1d\xc6\x38",
+                &out));
+  std::vector<uint32_t> expected = {5, 0xad934c0cu, 0x6ff67f56u, 0x81316fceu};
+  EXPECT_EQ(expected.size(), out.size());
+  for (unsigned i = 0; i < expected.size(); i++) {
+    EXPECT_EQ(expected[i], out[i]);
+  }
+}
+
+#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
+// This test hits a NOTREACHED so it is a release mode only test.
+TEST_F(V4RiceTest, TestDecoderPrefixesWithOverflowValues) {
+  std::vector<uint32_t> out;
+  EXPECT_EQ(DECODED_INTEGER_OVERFLOW_FAILURE,
+            V4RiceDecoder::DecodePrefixes(
+                5, 28, 3,
+                "\xbf\xa8\x3f\xfb\xfc\xfb\x5e\x27\xe6\xc3\x1d\xc6\x38", &out));
+}
+#endif
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/db/v4_store.cc b/components/safe_browsing/core/db/v4_store.cc
new file mode 100644
index 0000000..ad3054ce
--- /dev/null
+++ b/components/safe_browsing/core/db/v4_store.cc
@@ -0,0 +1,833 @@
+// 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 "components/safe_browsing/core/db/v4_store.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "base/base64.h"
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/stl_util.h"
+#include "base/strings/stringprintf.h"
+#include "components/safe_browsing/core/db/prefix_iterator.h"
+#include "components/safe_browsing/core/db/v4_rice.h"
+#include "components/safe_browsing/core/db/v4_store.pb.h"
+#include "components/safe_browsing/core/proto/webui.pb.h"
+#include "crypto/secure_hash.h"
+#include "crypto/sha2.h"
+
+using base::TimeTicks;
+
+namespace safe_browsing {
+
+namespace {
+
+// UMA related strings.
+// Part 1: Represent the overall operation being performed.
+const char kProcessFullUpdate[] = "SafeBrowsing.V4ProcessFullUpdate";
+const char kProcessPartialUpdate[] = "SafeBrowsing.V4ProcessPartialUpdate";
+const char kReadFromDisk[] = "SafeBrowsing.V4ReadFromDisk";
+// Part 2: Represent the sub-operation being performed as part of the larger
+// operation from part 1.
+const char kApplyUpdate[] = ".ApplyUpdate";
+const char kDecodeAdditions[] = ".DecodeAdditions";
+const char kDecodeRemovals[] = ".DecodeRemovals";
+// Part 3: Represent the unit of value being measured and logged.
+const char kResult[] = ".Result";
+// Part 4 (optional): Represent the name of the list for which the metric is
+// being logged. For instance, ".UrlSoceng".
+// UMA metric names for this file are generated by appending one value each,
+// in order, from parts [1, 2, and 3], or [1, 2, 3, and 4]. For example:
+// SafeBrowsing.V4ProcessPartialUpdate.ApplyUpdate.Result, or
+// SafeBrowsing.V4ProcessPartialUpdate.ApplyUpdate.Result.UrlSoceng
+
+const uint32_t kFileMagic = 0x600D71FE;
+const uint32_t kFileVersion = 9;
+
+// Set a common sense limit on the store file size we try to read.
+// The maximum store file size, as of today, is about 6MB.
+constexpr size_t kMaxStoreSizeBytes = 50 * 1000 * 1000;
+
+void RecordEnumWithAndWithoutSuffix(const std::string& metric,
+                                    int32_t value,
+                                    int32_t maximum,
+                                    const base::FilePath& file_path) {
+  // The histograms below are an expansion of the UMA_HISTOGRAM_ENUMERATION
+  // macro adapted to allow for a dynamically suffixed histogram name.
+  // Note: The factory creates and owns the histogram.
+  base::HistogramBase* histogram = base::LinearHistogram::FactoryGet(
+      metric + kResult, 1, maximum, maximum + 1,
+      base::HistogramBase::kUmaTargetedHistogramFlag);
+  if (histogram) {
+    histogram->Add(value);
+  }
+
+  std::string suffix = GetUmaSuffixForStore(file_path);
+  base::HistogramBase* histogram_suffix = base::LinearHistogram::FactoryGet(
+      metric + kResult + suffix, 1, maximum, maximum + 1,
+      base::HistogramBase::kUmaTargetedHistogramFlag);
+  if (histogram_suffix) {
+    histogram_suffix->Add(value);
+  }
+}
+
+void RecordBooleanWithAndWithoutSuffix(const std::string& metric,
+                                       bool value,
+                                       const base::FilePath& file_path) {
+  // The histograms below are an expansion of the UMA_HISTOGRAM_BOOLEAN
+  // macro adapted to allow for a dynamically suffixed histogram name.
+  // Note: The factory creates and owns the histogram.
+  base::HistogramBase* histogram = base::BooleanHistogram::FactoryGet(
+      metric, base::HistogramBase::kUmaTargetedHistogramFlag);
+  if (histogram) {
+    histogram->Add(value);
+  }
+
+  std::string suffix = GetUmaSuffixForStore(file_path);
+  base::HistogramBase* histogram_suffix = base::BooleanHistogram::FactoryGet(
+      metric + suffix, base::HistogramBase::kUmaTargetedHistogramFlag);
+  if (histogram_suffix) {
+    histogram_suffix->Add(value);
+  }
+}
+
+void RecordApplyUpdateResult(const std::string& base_metric,
+                             ApplyUpdateResult result,
+                             const base::FilePath& file_path) {
+  RecordEnumWithAndWithoutSuffix(base_metric + kApplyUpdate, result,
+                                 APPLY_UPDATE_RESULT_MAX, file_path);
+}
+
+void RecordDecodeAdditionsResult(const std::string& base_metric,
+                                 V4DecodeResult result,
+                                 const base::FilePath& file_path) {
+  RecordEnumWithAndWithoutSuffix(base_metric + kDecodeAdditions, result,
+                                 DECODE_RESULT_MAX, file_path);
+}
+
+void RecordDecodeRemovalsResult(const std::string& base_metric,
+                                V4DecodeResult result,
+                                const base::FilePath& file_path) {
+  RecordEnumWithAndWithoutSuffix(base_metric + kDecodeRemovals, result,
+                                 DECODE_RESULT_MAX, file_path);
+}
+
+void RecordStoreReadResult(StoreReadResult result) {
+  UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.V4StoreRead.Result", result,
+                            STORE_READ_RESULT_MAX);
+}
+
+void RecordStoreWriteResult(StoreWriteResult result) {
+  UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.V4StoreWrite.Result", result,
+                            STORE_WRITE_RESULT_MAX);
+}
+
+// Returns the name of the temporary file used to buffer data for
+// |filename|.
+const base::FilePath TemporaryFileForFilename(const base::FilePath& filename) {
+  return base::FilePath(filename.value() + FILE_PATH_LITERAL("_new"));
+}
+
+}  // namespace
+
+using ::google::protobuf::RepeatedField;
+using ::google::protobuf::RepeatedPtrField;
+using ::google::protobuf::int32;
+
+std::ostream& operator<<(std::ostream& os, const V4Store& store) {
+  os << store.DebugString();
+  return os;
+}
+
+std::unique_ptr<V4Store> V4StoreFactory::CreateV4Store(
+    const scoped_refptr<base::SequencedTaskRunner>& task_runner,
+    const base::FilePath& store_path) {
+  auto new_store = std::make_unique<V4Store>(task_runner, store_path);
+  new_store->Initialize();
+  return new_store;
+}
+
+void V4Store::Initialize() {
+  // If a state already exists, don't re-initilize.
+  DCHECK(state_.empty());
+
+  StoreReadResult store_read_result = ReadFromDisk();
+  has_valid_data_ = (store_read_result == READ_SUCCESS);
+  RecordStoreReadResult(store_read_result);
+}
+
+bool V4Store::HasValidData() const {
+  RecordBooleanWithAndWithoutSuffix("SafeBrowsing.V4Store.IsStoreValid",
+                                    has_valid_data_, store_path_);
+  return has_valid_data_;
+}
+
+V4Store::V4Store(const scoped_refptr<base::SequencedTaskRunner>& task_runner,
+                 const base::FilePath& store_path,
+                 const int64_t old_file_size)
+    : file_size_(old_file_size),
+      has_valid_data_(false),
+      store_path_(store_path),
+      task_runner_(task_runner) {}
+
+V4Store::~V4Store() {
+  DCHECK(task_runner_->RunsTasksInCurrentSequence());
+}
+
+std::string V4Store::DebugString() const {
+  std::string state_base64;
+  base::Base64Encode(state_, &state_base64);
+
+  return base::StringPrintf("path: %" PRFilePath "; state: %s",
+                            store_path_.value().c_str(), state_base64.c_str());
+}
+
+// static
+void V4Store::Destroy(std::unique_ptr<V4Store> v4_store) {
+  V4Store* v4_store_raw = v4_store.release();
+  if (v4_store_raw) {
+    v4_store_raw->task_runner_->DeleteSoon(FROM_HERE, v4_store_raw);
+  }
+}
+
+void V4Store::Reset() {
+  expected_checksum_.clear();
+  hash_prefix_map_.clear();
+  state_ = "";
+}
+
+ApplyUpdateResult V4Store::ProcessPartialUpdateAndWriteToDisk(
+    const std::string& metric,
+    const HashPrefixMap& hash_prefix_map_old,
+    std::unique_ptr<ListUpdateResponse> response) {
+  DCHECK(response->has_response_type());
+  DCHECK_EQ(ListUpdateResponse::PARTIAL_UPDATE, response->response_type());
+
+  ApplyUpdateResult result = ProcessUpdate(
+      metric, hash_prefix_map_old, response, false /* delay_checksum check */);
+  if (result == APPLY_UPDATE_SUCCESS) {
+    Checksum checksum = response->checksum();
+    response.reset();
+    RecordStoreWriteResult(WriteToDisk(checksum));
+  }
+  return result;
+}
+
+ApplyUpdateResult V4Store::ProcessFullUpdateAndWriteToDisk(
+    const std::string& metric,
+    std::unique_ptr<ListUpdateResponse> response) {
+  ApplyUpdateResult result =
+      ProcessFullUpdate(metric, response, false /* delay_checksum check */);
+  if (result == APPLY_UPDATE_SUCCESS) {
+    Checksum checksum = response->checksum();
+    response.reset();
+    RecordStoreWriteResult(WriteToDisk(checksum));
+  }
+  return result;
+}
+
+ApplyUpdateResult V4Store::ProcessFullUpdate(
+    const std::string& metric,
+    const std::unique_ptr<ListUpdateResponse>& response,
+    bool delay_checksum_check) {
+  DCHECK(response->has_response_type());
+  DCHECK_EQ(ListUpdateResponse::FULL_UPDATE, response->response_type());
+  // TODO(vakh): For a full update, we don't need to process the update in
+  // lexographical order to store it, but we do need to do that for calculating
+  // checksum. It might save some CPU cycles to store the full update as-is and
+  // walk the list of hash prefixes in lexographical order only for checksum
+  // calculation.
+  return ProcessUpdate(metric, HashPrefixMap(), response, delay_checksum_check);
+}
+
+ApplyUpdateResult V4Store::ProcessUpdate(
+    const std::string& metric,
+    const HashPrefixMap& hash_prefix_map_old,
+    const std::unique_ptr<ListUpdateResponse>& response,
+    bool delay_checksum_check) {
+  const RepeatedField<int32>* raw_removals = nullptr;
+  RepeatedField<int32> rice_removals;
+  size_t removals_size = response->removals_size();
+  DCHECK_LE(removals_size, 1u);
+  if (removals_size == 1) {
+    const ThreatEntrySet& removal = response->removals().Get(0);
+    const CompressionType compression_type = removal.compression_type();
+    if (compression_type == RAW) {
+      raw_removals = &removal.raw_indices().indices();
+    } else if (compression_type == RICE) {
+      DCHECK(removal.has_rice_indices());
+
+      const RiceDeltaEncoding& rice_indices = removal.rice_indices();
+      V4DecodeResult decode_result = V4RiceDecoder::DecodeIntegers(
+          rice_indices.first_value(), rice_indices.rice_parameter(),
+          rice_indices.num_entries(), rice_indices.encoded_data(),
+          &rice_removals);
+
+      RecordDecodeRemovalsResult(metric, decode_result, store_path_);
+      if (decode_result != DECODE_SUCCESS) {
+        return RICE_DECODING_FAILURE;
+      }
+      raw_removals = &rice_removals;
+    } else {
+      NOTREACHED() << "Unexpected compression_type type: " << compression_type;
+      return UNEXPECTED_COMPRESSION_TYPE_REMOVALS_FAILURE;
+    }
+  }
+
+  HashPrefixMap hash_prefix_map;
+  ApplyUpdateResult apply_update_result = UpdateHashPrefixMapFromAdditions(
+      metric, response->additions(), &hash_prefix_map);
+  if (apply_update_result != APPLY_UPDATE_SUCCESS) {
+    return apply_update_result;
+  }
+
+  std::string expected_checksum;
+  if (response->has_checksum() && response->checksum().has_sha256()) {
+    expected_checksum = response->checksum().sha256();
+  }
+
+  if (delay_checksum_check) {
+    DCHECK(hash_prefix_map_old.empty());
+    DCHECK(!raw_removals);
+    // We delay the checksum check at startup to be able to load the DB
+    // quickly. In this case, the |hash_prefix_map_old| should be empty, so just
+    // copy over the |hash_prefix_map|.
+    hash_prefix_map_ = hash_prefix_map;
+
+    // Calculate the checksum asynchronously later and if it doesn't match,
+    // reset the store.
+    expected_checksum_ = expected_checksum;
+  } else {
+    apply_update_result = MergeUpdate(hash_prefix_map_old, hash_prefix_map,
+                                      raw_removals, expected_checksum);
+    if (apply_update_result != APPLY_UPDATE_SUCCESS) {
+      return apply_update_result;
+    }
+  }
+
+  state_ = response->new_client_state();
+  return APPLY_UPDATE_SUCCESS;
+}
+
+void V4Store::ApplyUpdate(
+    std::unique_ptr<ListUpdateResponse> response,
+    const scoped_refptr<base::SingleThreadTaskRunner>& callback_task_runner,
+    UpdatedStoreReadyCallback callback) {
+  std::unique_ptr<V4Store> new_store(
+      new V4Store(task_runner_, store_path_, file_size_));
+  ApplyUpdateResult apply_update_result;
+  std::string metric;
+  if (response->response_type() == ListUpdateResponse::PARTIAL_UPDATE) {
+    metric = kProcessPartialUpdate;
+    apply_update_result = new_store->ProcessPartialUpdateAndWriteToDisk(
+        metric, hash_prefix_map_, std::move(response));
+  } else if (response->response_type() == ListUpdateResponse::FULL_UPDATE) {
+    metric = kProcessFullUpdate;
+    apply_update_result =
+        new_store->ProcessFullUpdateAndWriteToDisk(metric, std::move(response));
+  } else {
+    apply_update_result = UNEXPECTED_RESPONSE_TYPE_FAILURE;
+    NOTREACHED() << "Failure: Unexpected response type: "
+                 << response->response_type();
+  }
+
+  if (apply_update_result == APPLY_UPDATE_SUCCESS) {
+    new_store->has_valid_data_ = true;
+    new_store->last_apply_update_result_ = apply_update_result;
+    new_store->last_apply_update_time_millis_ = base::Time::Now();
+    new_store->checks_attempted_ = checks_attempted_;
+  } else {
+    new_store.reset();
+    DLOG(WARNING) << "Failure: ApplyUpdate: reason: " << apply_update_result
+                  << "; store: " << *this;
+  }
+
+  // Record the state of the update to be shown in the Safe Browsing page.
+  last_apply_update_result_ = apply_update_result;
+
+  RecordApplyUpdateResult(metric, apply_update_result, store_path_);
+
+  // Posting the task should be the last thing to do in this function.
+  // Otherwise, the posted task can end up running in parallel. If that
+  // happens, the old store will get destoyed and can lead to use-after-free in
+  // this function.
+  callback_task_runner->PostTask(
+      FROM_HERE, base::BindOnce(std::move(callback), std::move(new_store)));
+}
+
+ApplyUpdateResult V4Store::UpdateHashPrefixMapFromAdditions(
+    const std::string& metric,
+    const RepeatedPtrField<ThreatEntrySet>& additions,
+    HashPrefixMap* additions_map) {
+  for (const auto& addition : additions) {
+    ApplyUpdateResult apply_update_result = APPLY_UPDATE_SUCCESS;
+    const CompressionType compression_type = addition.compression_type();
+    if (compression_type == RAW) {
+      DCHECK(addition.has_raw_hashes());
+      DCHECK(addition.raw_hashes().has_raw_hashes());
+
+      apply_update_result =
+          AddUnlumpedHashes(addition.raw_hashes().prefix_size(),
+                            addition.raw_hashes().raw_hashes(), additions_map);
+    } else if (compression_type == RICE) {
+      DCHECK(addition.has_rice_hashes());
+
+      const RiceDeltaEncoding& rice_hashes = addition.rice_hashes();
+      std::vector<uint32_t> raw_hashes;
+      V4DecodeResult decode_result = V4RiceDecoder::DecodePrefixes(
+          rice_hashes.first_value(), rice_hashes.rice_parameter(),
+          rice_hashes.num_entries(), rice_hashes.encoded_data(), &raw_hashes);
+      RecordDecodeAdditionsResult(metric, decode_result, store_path_);
+      if (decode_result != DECODE_SUCCESS) {
+        return RICE_DECODING_FAILURE;
+      } else {
+        char* raw_hashes_start = reinterpret_cast<char*>(raw_hashes.data());
+        size_t raw_hashes_size = sizeof(uint32_t) * raw_hashes.size();
+
+        // Rice-Golomb encoding is used to send compressed compressed 4-byte
+        // hash prefixes. Hash prefixes longer than 4 bytes will not be
+        // compressed, and will be served in raw format instead.
+        // Source: https://developers.google.com/safe-browsing/v4/compression
+        const PrefixSize kPrefixSize = 4;
+        apply_update_result = AddUnlumpedHashes(kPrefixSize, raw_hashes_start,
+                                                raw_hashes_size, additions_map);
+      }
+    } else {
+      NOTREACHED() << "Unexpected compression_type type: " << compression_type;
+      return UNEXPECTED_COMPRESSION_TYPE_ADDITIONS_FAILURE;
+    }
+
+    if (apply_update_result != APPLY_UPDATE_SUCCESS) {
+      // If there was an error in updating the map, discard the update entirely.
+      return apply_update_result;
+    }
+  }
+
+  return APPLY_UPDATE_SUCCESS;
+}
+
+// static
+ApplyUpdateResult V4Store::AddUnlumpedHashes(PrefixSize prefix_size,
+                                             const std::string& raw_hashes,
+                                             HashPrefixMap* additions_map) {
+  return AddUnlumpedHashes(prefix_size, raw_hashes.data(), raw_hashes.size(),
+                           additions_map);
+}
+
+// static
+ApplyUpdateResult V4Store::AddUnlumpedHashes(PrefixSize prefix_size,
+                                             const char* raw_hashes_begin,
+                                             const size_t raw_hashes_length,
+                                             HashPrefixMap* additions_map) {
+  if (prefix_size < kMinHashPrefixLength) {
+    NOTREACHED();
+    return PREFIX_SIZE_TOO_SMALL_FAILURE;
+  }
+  if (prefix_size > kMaxHashPrefixLength) {
+    NOTREACHED();
+    return PREFIX_SIZE_TOO_LARGE_FAILURE;
+  }
+  if (raw_hashes_length % prefix_size != 0) {
+    return ADDITIONS_SIZE_UNEXPECTED_FAILURE;
+  }
+
+  // TODO(vakh): Figure out a way to avoid the following copy operation.
+  (*additions_map)[prefix_size] =
+      std::string(raw_hashes_begin, raw_hashes_begin + raw_hashes_length);
+  return APPLY_UPDATE_SUCCESS;
+}
+
+// static
+bool V4Store::GetNextSmallestUnmergedPrefix(
+    const HashPrefixMap& hash_prefix_map,
+    const IteratorMap& iterator_map,
+    HashPrefix* smallest_hash_prefix) {
+  HashPrefix current_hash_prefix;
+  bool has_unmerged = false;
+
+  for (const auto& iterator_pair : iterator_map) {
+    PrefixSize prefix_size = iterator_pair.first;
+    HashPrefixes::const_iterator start = iterator_pair.second;
+
+    const HashPrefixes& hash_prefixes = hash_prefix_map.at(prefix_size);
+    PrefixSize distance = std::distance(start, hash_prefixes.end());
+    CHECK_EQ(0u, distance % prefix_size);
+    if (prefix_size <= distance) {
+      current_hash_prefix = HashPrefix(start, start + prefix_size);
+      if (!has_unmerged || *smallest_hash_prefix > current_hash_prefix) {
+        has_unmerged = true;
+        smallest_hash_prefix->swap(current_hash_prefix);
+      }
+    }
+  }
+  return has_unmerged;
+}
+
+// static
+void V4Store::InitializeIteratorMap(const HashPrefixMap& hash_prefix_map,
+                                    IteratorMap* iterator_map) {
+  for (const auto& map_pair : hash_prefix_map) {
+    (*iterator_map)[map_pair.first] = map_pair.second.begin();
+  }
+}
+
+// static
+void V4Store::ReserveSpaceInPrefixMap(const HashPrefixMap& other_prefixes_map,
+                                      HashPrefixMap* prefix_map_to_update) {
+  for (const auto& pair : other_prefixes_map) {
+    PrefixSize prefix_size = pair.first;
+    size_t prefix_length_to_add = pair.second.length();
+
+    const HashPrefixes& existing_prefixes =
+        (*prefix_map_to_update)[prefix_size];
+    size_t existing_capacity = existing_prefixes.capacity();
+
+    (*prefix_map_to_update)[prefix_size].reserve(existing_capacity +
+                                                 prefix_length_to_add);
+  }
+}
+
+ApplyUpdateResult V4Store::MergeUpdate(const HashPrefixMap& old_prefixes_map,
+                                       const HashPrefixMap& additions_map,
+                                       const RepeatedField<int32>* raw_removals,
+                                       const std::string& expected_checksum) {
+  DCHECK(task_runner_->RunsTasksInCurrentSequence());
+  DCHECK(hash_prefix_map_.empty());
+
+  bool calculate_checksum = !expected_checksum.empty();
+  if (calculate_checksum &&
+      (expected_checksum.size() != crypto::kSHA256Length)) {
+    return CHECKSUM_MISMATCH_FAILURE;
+  }
+
+  hash_prefix_map_.clear();
+  ReserveSpaceInPrefixMap(old_prefixes_map, &hash_prefix_map_);
+  ReserveSpaceInPrefixMap(additions_map, &hash_prefix_map_);
+
+  IteratorMap old_iterator_map;
+  HashPrefix next_smallest_prefix_old;
+  InitializeIteratorMap(old_prefixes_map, &old_iterator_map);
+  bool old_has_unmerged = GetNextSmallestUnmergedPrefix(
+      old_prefixes_map, old_iterator_map, &next_smallest_prefix_old);
+
+  IteratorMap additions_iterator_map;
+  HashPrefix next_smallest_prefix_additions;
+  InitializeIteratorMap(additions_map, &additions_iterator_map);
+  bool additions_has_unmerged = GetNextSmallestUnmergedPrefix(
+      additions_map, additions_iterator_map, &next_smallest_prefix_additions);
+
+  // Classical merge sort.
+  // The two constructs to merge are maps: old_prefixes_map, additions_map.
+  // At least one of the maps still has elements that need to be merged into the
+  // new store.
+
+  std::unique_ptr<crypto::SecureHash> checksum_ctx(
+      crypto::SecureHash::Create(crypto::SecureHash::SHA256));
+
+  // Keep track of the number of elements picked from the old map. This is used
+  // to determine which elements to drop based on the raw_removals. Note that
+  // picked is not the same as merged. A picked element isn't merged if its
+  // index is on the raw_removals list.
+  int total_picked_from_old = 0;
+  const int* removals_iter = raw_removals ? raw_removals->begin() : nullptr;
+  while (old_has_unmerged || additions_has_unmerged) {
+    // If the same hash prefix appears in the existing store and the additions
+    // list, something is clearly wrong. Discard the update.
+    if (old_has_unmerged && additions_has_unmerged &&
+        next_smallest_prefix_old == next_smallest_prefix_additions) {
+      return ADDITIONS_HAS_EXISTING_PREFIX_FAILURE;
+    }
+
+    // Select which map to pick the next hash prefix from to keep the result in
+    // lexographically sorted order.
+    bool pick_from_old =
+        old_has_unmerged &&
+        (!additions_has_unmerged ||
+         (next_smallest_prefix_old < next_smallest_prefix_additions));
+
+    PrefixSize next_smallest_prefix_size;
+    if (pick_from_old) {
+      next_smallest_prefix_size = next_smallest_prefix_old.size();
+
+      // Update the iterator map, which means that we have merged one hash
+      // prefix of size |next_smallest_prefix_size| from the old store.
+      old_iterator_map[next_smallest_prefix_size] += next_smallest_prefix_size;
+
+      if (!raw_removals || removals_iter == raw_removals->end() ||
+          *removals_iter != total_picked_from_old) {
+        // Append the smallest hash to the appropriate list.
+        hash_prefix_map_[next_smallest_prefix_size] += next_smallest_prefix_old;
+
+        if (calculate_checksum) {
+          checksum_ctx->Update(next_smallest_prefix_old.data(),
+                               next_smallest_prefix_size);
+        }
+      } else {
+        // Element not added to new map. Move the removals iterator forward.
+        removals_iter++;
+      }
+
+      total_picked_from_old++;
+
+      // Find the next smallest unmerged element in the old store's map.
+      old_has_unmerged = GetNextSmallestUnmergedPrefix(
+          old_prefixes_map, old_iterator_map, &next_smallest_prefix_old);
+    } else {
+      next_smallest_prefix_size = next_smallest_prefix_additions.size();
+
+      // Append the smallest hash to the appropriate list.
+      hash_prefix_map_[next_smallest_prefix_size] +=
+          next_smallest_prefix_additions;
+
+      if (calculate_checksum) {
+        checksum_ctx->Update(next_smallest_prefix_additions.data(),
+                             next_smallest_prefix_size);
+      }
+
+      // Update the iterator map, which means that we have merged one hash
+      // prefix of size |next_smallest_prefix_size| from the update.
+      additions_iterator_map[next_smallest_prefix_size] +=
+          next_smallest_prefix_size;
+
+      // Find the next smallest unmerged element in the additions map.
+      additions_has_unmerged =
+          GetNextSmallestUnmergedPrefix(additions_map, additions_iterator_map,
+                                        &next_smallest_prefix_additions);
+    }
+  }
+
+  if (raw_removals && removals_iter != raw_removals->end()) {
+    return REMOVALS_INDEX_TOO_LARGE_FAILURE;
+  }
+
+  if (calculate_checksum) {
+    char checksum[crypto::kSHA256Length];
+    checksum_ctx->Finish(checksum, sizeof(checksum));
+    for (size_t i = 0; i < crypto::kSHA256Length; i++) {
+      if (checksum[i] != expected_checksum[i]) {
+#if DCHECK_IS_ON()
+        std::string checksum_b64, expected_checksum_b64;
+        base::Base64Encode(base::StringPiece(checksum, base::size(checksum)),
+                           &checksum_b64);
+        base::Base64Encode(expected_checksum, &expected_checksum_b64);
+        DVLOG(1) << "Failure: Checksum mismatch: calculated: " << checksum_b64
+                 << "; expected: " << expected_checksum_b64
+                 << "; store: " << *this;
+#endif
+        return CHECKSUM_MISMATCH_FAILURE;
+      }
+    }
+  }
+
+  return APPLY_UPDATE_SUCCESS;
+}
+
+StoreReadResult V4Store::ReadFromDisk() {
+  DCHECK(task_runner_->RunsTasksInCurrentSequence());
+
+  V4StoreFileFormat file_format;
+  int64_t file_size;
+  {
+    // A temporary scope to make sure that |contents| get destroyed as soon as
+    // we are doing using it.
+    std::string contents;
+    if (!base::ReadFileToStringWithMaxSize(store_path_, &contents,
+                                           kMaxStoreSizeBytes)) {
+      return FILE_UNREADABLE_FAILURE;
+    }
+
+    if (contents.empty()) {
+      return FILE_EMPTY_FAILURE;
+    }
+
+    if (!file_format.ParseFromString(contents)) {
+      return PROTO_PARSING_FAILURE;
+    }
+    file_size = static_cast<int64_t>(contents.size());
+  }
+
+  if (file_format.magic_number() != kFileMagic) {
+    return UNEXPECTED_MAGIC_NUMBER_FAILURE;
+  }
+
+  if (file_format.version_number() != kFileVersion) {
+    return FILE_VERSION_INCOMPATIBLE_FAILURE;
+  }
+
+  if (!file_format.has_list_update_response()) {
+    return HASH_PREFIX_INFO_MISSING_FAILURE;
+  }
+
+  std::unique_ptr<ListUpdateResponse> response(new ListUpdateResponse);
+  response->Swap(file_format.mutable_list_update_response());
+  ApplyUpdateResult apply_update_result = ProcessFullUpdate(
+      kReadFromDisk, response, true /* delay_checksum check */);
+  RecordApplyUpdateResult(kReadFromDisk, apply_update_result, store_path_);
+  last_apply_update_result_ = apply_update_result;
+  if (apply_update_result != APPLY_UPDATE_SUCCESS) {
+    hash_prefix_map_.clear();
+    return HASH_PREFIX_MAP_GENERATION_FAILURE;
+  }
+
+  // Update |file_size_| now because we parsed the file correctly.
+  file_size_ = file_size;
+
+  return READ_SUCCESS;
+}
+
+StoreWriteResult V4Store::WriteToDisk(const Checksum& checksum) {
+  V4StoreFileFormat file_format;
+  ListUpdateResponse* lur = file_format.mutable_list_update_response();
+  *(lur->mutable_checksum()) = checksum;
+  lur->set_new_client_state(state_);
+  lur->set_response_type(ListUpdateResponse::FULL_UPDATE);
+  for (const auto& entry : hash_prefix_map_) {
+    ThreatEntrySet* additions = lur->add_additions();
+    // TODO(vakh): Write RICE encoded hash prefixes on disk. Not doing so
+    // currently since it takes a long time to decode them on startup, which
+    // blocks resource load. See: http://crbug.com/654819
+    additions->set_compression_type(RAW);
+    additions->mutable_raw_hashes()->set_prefix_size(entry.first);
+    additions->mutable_raw_hashes()->set_raw_hashes(entry.second);
+  }
+
+  // Attempt writing to a temporary file first and at the end, swap the files.
+  const base::FilePath new_filename = TemporaryFileForFilename(store_path_);
+
+  file_format.set_magic_number(kFileMagic);
+  file_format.set_version_number(kFileVersion);
+  std::string file_format_string;
+  file_format.SerializeToString(&file_format_string);
+  size_t written = base::WriteFile(new_filename, file_format_string.data(),
+                                   file_format_string.size());
+
+  if (file_format_string.size() != written) {
+    base::DeleteFile(new_filename, /*recursive=*/false);
+    return UNEXPECTED_BYTES_WRITTEN_FAILURE;
+  }
+
+  if (!base::Move(new_filename, store_path_)) {
+    base::DeleteFile(new_filename, /*recursive=*/false);
+    return UNABLE_TO_RENAME_FAILURE;
+  }
+
+  // Update |file_size_| now because we wrote the file correctly.
+  file_size_ = static_cast<int64_t>(written);
+
+  return WRITE_SUCCESS;
+}
+
+HashPrefix V4Store::GetMatchingHashPrefix(const FullHash& full_hash) {
+  return GetMatchingHashPrefix(base::StringPiece(full_hash));
+}
+
+HashPrefix V4Store::GetMatchingHashPrefix(base::StringPiece full_hash) {
+  // It should never be the case that more than one hash prefixes match a given
+  // full hash. However, if that happens, this method returns any one of them.
+  // It does not guarantee which one of those will be returned.
+  DCHECK(full_hash.size() == 32u || full_hash.size() == 21u);
+  checks_attempted_++;
+  for (const auto& pair : hash_prefix_map_) {
+    const PrefixSize& prefix_size = pair.first;
+    base::StringPiece hash_prefix = full_hash.substr(0, prefix_size);
+    if (HashPrefixMatches(hash_prefix, pair.second, prefix_size))
+      return hash_prefix.as_string();
+  }
+  return HashPrefix();
+}
+
+bool V4Store::HashPrefixMatches(base::StringPiece prefix,
+                                const HashPrefixes& prefixes,
+                                const PrefixSize& size) {
+  return std::binary_search(
+      PrefixIterator(prefixes, 0, size),
+      PrefixIterator(prefixes, prefixes.size() / size, size), prefix);
+}
+
+bool V4Store::VerifyChecksum() {
+  DCHECK(task_runner_->RunsTasksInCurrentSequence());
+
+  if (expected_checksum_.empty()) {
+    // Nothing to check here folks!
+    // TODO(vakh): Do not allow empty checksums.
+    return true;
+  }
+
+  IteratorMap iterator_map;
+  HashPrefix next_smallest_prefix;
+  InitializeIteratorMap(hash_prefix_map_, &iterator_map);
+  CHECK_EQ(hash_prefix_map_.size(), iterator_map.size());
+  bool has_unmerged = GetNextSmallestUnmergedPrefix(
+      hash_prefix_map_, iterator_map, &next_smallest_prefix);
+
+  std::unique_ptr<crypto::SecureHash> checksum_ctx(
+      crypto::SecureHash::Create(crypto::SecureHash::SHA256));
+  while (has_unmerged) {
+    PrefixSize next_smallest_prefix_size = next_smallest_prefix.size();
+
+    // Update the iterator map, which means that we have read one hash
+    // prefix of size |next_smallest_prefix_size| from hash_prefix_map_.
+    iterator_map[next_smallest_prefix_size] += next_smallest_prefix_size;
+
+    checksum_ctx->Update(next_smallest_prefix.data(),
+                         next_smallest_prefix_size);
+
+    // Find the next smallest unmerged element in the map.
+    has_unmerged = GetNextSmallestUnmergedPrefix(hash_prefix_map_, iterator_map,
+                                                 &next_smallest_prefix);
+  }
+
+  char checksum[crypto::kSHA256Length];
+  checksum_ctx->Finish(checksum, sizeof(checksum));
+  for (size_t i = 0; i < crypto::kSHA256Length; i++) {
+    if (checksum[i] != expected_checksum_[i]) {
+      RecordApplyUpdateResult(kReadFromDisk, CHECKSUM_MISMATCH_FAILURE,
+                              store_path_);
+#if DCHECK_IS_ON()
+      std::string checksum_b64, expected_checksum_b64;
+      base::Base64Encode(base::StringPiece(checksum, base::size(checksum)),
+                         &checksum_b64);
+      base::Base64Encode(expected_checksum_, &expected_checksum_b64);
+      DVLOG(1) << "Failure: Checksum mismatch: calculated: " << checksum_b64
+               << "; expected: " << expected_checksum_b64
+               << "; store: " << *this;
+#endif
+      return false;
+    }
+  }
+  return true;
+}
+
+int64_t V4Store::RecordAndReturnFileSize(const std::string& base_metric) {
+  std::string suffix = GetUmaSuffixForStore(store_path_);
+  // Histogram properties as in UMA_HISTOGRAM_COUNTS_1M macro.
+  base::HistogramBase* histogram = base::Histogram::FactoryGet(
+      base_metric + suffix, 1, 1000000, 50,
+      base::HistogramBase::kUmaTargetedHistogramFlag);
+  if (histogram) {
+    const int64_t file_size_kilobytes = file_size_ / 1024;
+    histogram->Add(file_size_kilobytes);
+  }
+  return file_size_;
+}
+
+void V4Store::CollectStoreInfo(
+    DatabaseManagerInfo::DatabaseInfo::StoreInfo* store_info,
+    const std::string& base_metric) {
+  store_info->set_file_name(GetUmaSuffixForStore(store_path_)
+                                .substr(1));  // Strip the '.' off the front
+  store_info->set_file_size_bytes(file_size_);
+  store_info->set_update_status(static_cast<int>(last_apply_update_result_));
+  store_info->set_checks_attempted(checks_attempted_);
+  if (last_apply_update_time_millis_.ToJavaTime()) {
+    store_info->set_last_apply_update_time_millis(
+        last_apply_update_time_millis_.ToJavaTime());
+  }
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/db/v4_store.h b/components/safe_browsing/core/db/v4_store.h
new file mode 100644
index 0000000..86e1f46
--- /dev/null
+++ b/components/safe_browsing/core/db/v4_store.h
@@ -0,0 +1,450 @@
+// 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.
+
+// Design doc: go/design-doc-v4store
+
+#ifndef COMPONENTS_SAFE_BROWSING_CORE_DB_V4_STORE_H_
+#define COMPONENTS_SAFE_BROWSING_CORE_DB_V4_STORE_H_
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/sequenced_task_runner.h"
+#include "base/single_thread_task_runner.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/core/proto/webui.pb.h"
+
+namespace safe_browsing {
+
+class V4Store;
+
+using UpdatedStoreReadyCallback =
+    base::OnceCallback<void(std::unique_ptr<V4Store> new_store)>;
+
+// The sorted list of hash prefixes.
+using HashPrefixes = std::string;
+
+// Stores the list of sorted hash prefixes, by size.
+// For instance: {4: ["abcd", "bcde", "cdef", "gggg"], 5: ["fffff"]}
+using HashPrefixMap = std::unordered_map<PrefixSize, HashPrefixes>;
+
+// Stores the iterator to the last element merged from the HashPrefixMap for a
+// given prefix size.
+// For instance: {4:iter(3), 5:iter(1)} means that we have already merged
+// 3 hash prefixes of length 4, and 1 hash prefix of length 5.
+using IteratorMap =
+    std::unordered_map<PrefixSize, HashPrefixes::const_iterator>;
+
+// Enumerate different failure events while parsing the file read from disk for
+// histogramming purposes.  DO NOT CHANGE THE ORDERING OF THESE VALUES.
+enum StoreReadResult {
+  // No errors.
+  READ_SUCCESS = 0,
+
+  // Reserved for errors in parsing this enum.
+  UNEXPECTED_READ_FAILURE = 1,
+
+  // The contents of the file could not be read.
+  FILE_UNREADABLE_FAILURE = 2,
+
+  // The file was found to be empty.
+  FILE_EMPTY_FAILURE = 3,
+
+  // The contents of the file could not be interpreted as a valid
+  // V4StoreFileFormat proto.
+  PROTO_PARSING_FAILURE = 4,
+
+  // The magic number didn't match. We're most likely trying to read a file
+  // that doesn't contain hash prefixes.
+  UNEXPECTED_MAGIC_NUMBER_FAILURE = 5,
+
+  // The version of the file is different from expected and Chromium doesn't
+  // know how to interpret this version of the file.
+  FILE_VERSION_INCOMPATIBLE_FAILURE = 6,
+
+  // The rest of the file could not be parsed as a ListUpdateResponse protobuf.
+  // This can happen if the machine crashed before the file was fully written to
+  // disk or if there was disk corruption.
+  HASH_PREFIX_INFO_MISSING_FAILURE = 7,
+
+  // Unable to generate the hash prefix map from the updates on disk.
+  HASH_PREFIX_MAP_GENERATION_FAILURE = 8,
+
+  // Memory space for histograms is determined by the max.  ALWAYS
+  // ADD NEW VALUES BEFORE THIS ONE.
+  STORE_READ_RESULT_MAX
+};
+
+// Enumerate different failure events while writing the file to disk after
+// applying updates for histogramming purposes.
+// DO NOT CHANGE THE ORDERING OF THESE VALUES.
+enum StoreWriteResult {
+  // No errors.
+  WRITE_SUCCESS = 0,
+
+  // Reserved for errors in parsing this enum.
+  UNEXPECTED_WRITE_FAILURE = 1,
+
+  // The proto being written to disk wasn't a FULL_UPDATE proto.
+  INVALID_RESPONSE_TYPE_FAILURE = 2,
+
+  // Number of bytes written to disk was different from the size of the proto.
+  UNEXPECTED_BYTES_WRITTEN_FAILURE = 3,
+
+  // Renaming the temporary file to store file failed.
+  UNABLE_TO_RENAME_FAILURE = 4,
+
+  // Memory space for histograms is determined by the max.  ALWAYS
+  // ADD NEW VALUES BEFORE THIS ONE.
+  STORE_WRITE_RESULT_MAX
+};
+
+// Enumerate different events while applying the update fetched fom the server
+// for histogramming purposes.
+// DO NOT CHANGE THE ORDERING OF THESE VALUES.
+enum ApplyUpdateResult {
+  // No errors.
+  APPLY_UPDATE_SUCCESS = 0,
+
+  // Reserved for errors in parsing this enum.
+  UNEXPECTED_APPLY_UPDATE_FAILURE = 1,
+
+  // Prefix size smaller than 4 (which is the lowest expected).
+  PREFIX_SIZE_TOO_SMALL_FAILURE = 2,
+
+  // Prefix size larger than 32 (length of a full SHA256 hash).
+  PREFIX_SIZE_TOO_LARGE_FAILURE = 3,
+
+  // The number of bytes in additions isn't a multiple of prefix size.
+  ADDITIONS_SIZE_UNEXPECTED_FAILURE = 4,
+
+  // The update received from the server contains a prefix that's already
+  // present in the map.
+  ADDITIONS_HAS_EXISTING_PREFIX_FAILURE = 5,
+
+  // The server sent a response_type that the client did not expect.
+  UNEXPECTED_RESPONSE_TYPE_FAILURE = 6,
+
+  // One of more index(es) in removals field of the response is greater than
+  // the number of hash prefixes currently in the (old) store.
+  REMOVALS_INDEX_TOO_LARGE_FAILURE = 7,
+
+  // Failed to decode the Rice-encoded additions/removals field.
+  RICE_DECODING_FAILURE = 8,
+
+  // Compression type other than RAW and RICE for additions.
+  UNEXPECTED_COMPRESSION_TYPE_ADDITIONS_FAILURE = 9,
+
+  // Compression type other than RAW and RICE for removals.
+  UNEXPECTED_COMPRESSION_TYPE_REMOVALS_FAILURE = 10,
+
+  // The state of the store did not match the expected checksum sent by the
+  // server.
+  CHECKSUM_MISMATCH_FAILURE = 11,
+
+  // Memory space for histograms is determined by the max.  ALWAYS
+  // ADD NEW VALUES BEFORE THIS ONE.
+  APPLY_UPDATE_RESULT_MAX
+};
+
+// Factory for creating V4Store. Tests implement this factory to create fake
+// stores for testing.
+class V4StoreFactory {
+ public:
+  virtual ~V4StoreFactory() {}
+
+  virtual std::unique_ptr<V4Store> CreateV4Store(
+      const scoped_refptr<base::SequencedTaskRunner>& task_runner,
+      const base::FilePath& store_path);
+};
+
+class V4Store {
+ public:
+  // The |task_runner| is used to ensure that the operations in this file are
+  // performed on the correct thread. |store_path| specifies the location on
+  // disk for this file. The constructor doesn't read the store file from disk.
+  // If the store is being created to apply an update to the old store, then
+  // |old_file_size| is the size of the existing file on disk for this store;
+  // 0 otherwise. This is needed so that we can correctly report the size of
+  // store file on disk, even if writing the new file fails after successfully
+  // applying an update.
+  V4Store(const scoped_refptr<base::SequencedTaskRunner>& task_runner,
+          const base::FilePath& store_path,
+          int64_t old_file_size = 0);
+  virtual ~V4Store();
+
+  // Schedules the destruction of the V4Store object pointed to by |v4_store|,
+  // on the task runner.
+  static void Destroy(std::unique_ptr<V4Store> v4_store);
+
+  // If a hash prefix in this store matches |full_hash|, returns that hash
+  // prefix; otherwise returns an empty hash prefix.
+  virtual HashPrefix GetMatchingHashPrefix(const FullHash& full_hash);
+
+  // True if this store has valid contents, either from a successful read
+  // from disk or a full update.  This does not mean the checksum was verified.
+  virtual bool HasValidData() const;
+
+  const std::string& state() const { return state_; }
+
+  const base::FilePath& store_path() const { return store_path_; }
+
+  void ApplyUpdate(std::unique_ptr<ListUpdateResponse> response,
+                   const scoped_refptr<base::SingleThreadTaskRunner>& runner,
+                   UpdatedStoreReadyCallback callback);
+
+  // Records (in kilobytes) and returns the size of the file on disk for this
+  // store using |base_metric| as prefix and the filename as suffix.
+  int64_t RecordAndReturnFileSize(const std::string& base_metric);
+
+  std::string DebugString() const;
+
+  // Reads the store file from disk and populates the in-memory representation
+  // of the hash prefixes.
+  void Initialize();
+
+  // Reset internal state.
+  void Reset();
+
+  // Scheduled after reading the store file from disk on startup. When run, it
+  // ensures that the checksum of the hash prefixes in lexicographical sorted
+  // order matches the expected value in |expected_checksum_|. Returns true if
+  // it matches; false otherwise. Checksum verification can take a long time,
+  // so it is performed outside of the hotpath of loading SafeBrowsing database,
+  // which blocks resource loads.
+  bool VerifyChecksum();
+
+  // Populates the DatabaseInfo message.
+  void CollectStoreInfo(
+      DatabaseManagerInfo::DatabaseInfo::StoreInfo* store_info,
+      const std::string& base_metric);
+
+ protected:
+  HashPrefixMap hash_prefix_map_;
+
+ private:
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestReadFromEmptyFile);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestReadFromAbsentFile);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestReadFromInvalidContentsFile);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestReadFromUnexpectedMagicFile);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestReadFromLowVersionFile);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestReadFromNoHashPrefixInfoFile);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestReadFromNoHashPrefixesFile);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestWriteNoResponseType);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestWritePartialResponseType);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestWriteFullResponseType);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestReadFromFileWithUnknownProto);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest,
+                           TestAddUnlumpedHashesWithInvalidAddition);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestAddUnlumpedHashes);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestAddUnlumpedHashesWithEmptyString);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest,
+                           TestGetNextSmallestUnmergedPrefixWithEmptyPrefixMap);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestGetNextSmallestUnmergedPrefix);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestMergeUpdatesWithSameSizesInEachMap);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest,
+                           TestMergeUpdatesWithDifferentSizesInEachMap);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestMergeUpdatesOldMapRunsOutFirst);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest,
+                           TestMergeUpdatesAdditionsMapRunsOutFirst);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest,
+                           TestMergeUpdatesFailsForRepeatedHashPrefix);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest,
+                           TestMergeUpdatesFailsWhenRemovalsIndexTooLarge);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestMergeUpdatesRemovesOnlyElement);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestMergeUpdatesRemovesFirstElement);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestMergeUpdatesRemovesMiddleElement);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestMergeUpdatesRemovesLastElement);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest,
+                           TestMergeUpdatesRemovesWhenOldHasDifferentSizes);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest,
+                           TestMergeUpdatesRemovesMultipleAcrossDifferentSizes);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest,
+                           TestReadFullResponseWithValidHashPrefixMap);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest,
+                           TestReadFullResponseWithInvalidHashPrefixMap);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestHashPrefixExistsAtTheBeginning);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestHashPrefixExistsInTheMiddle);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestHashPrefixExistsAtTheEnd);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest,
+                           TestHashPrefixExistsAtTheBeginningOfEven);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestHashPrefixExistsAtTheEndOfEven);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest,
+                           TestHashPrefixDoesNotExistInConcatenatedList);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestFullHashExistsInMapWithSingleSize);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest,
+                           TestFullHashExistsInMapWithDifferentSizes);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest,
+                           TestHashPrefixExistsInMapWithSingleSize);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest,
+                           TestHashPrefixExistsInMapWithDifferentSizes);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest,
+                           TestHashPrefixDoesNotExistInMapWithDifferentSizes);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, GetMatchingHashPrefixSize32Or21);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest,
+                           TestAdditionsWithRiceEncodingFailsWithInvalidInput);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestAdditionsWithRiceEncodingSucceeds);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestRemovalsWithRiceEncodingSucceeds);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestMergeUpdatesFailsChecksum);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestChecksumErrorOnStartup);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, WriteToDiskFails);
+  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, FullUpdateFailsChecksumSynchronously);
+  FRIEND_TEST_ALL_PREFIXES(V4StorePerftest, StressTest);
+
+  friend class V4StoreTest;
+  friend class V4StoreFuzzer;
+
+  // If |prefix_size| is within expected range, and |raw_hashes_length| is a
+  // multiple of prefix_size, then it sets the string of length
+  // |raw_hashes_length| starting at |raw_hashes_begin| as the value at key
+  // |prefix_size| in |additions_map|
+  static ApplyUpdateResult AddUnlumpedHashes(PrefixSize prefix_size,
+                                             const char* raw_hashes_begin,
+                                             const size_t raw_hashes_length,
+                                             HashPrefixMap* additions_map);
+
+  // An overloaded version of AddUnlumpedHashes that allows passing in a
+  // std::string object.
+  static ApplyUpdateResult AddUnlumpedHashes(PrefixSize prefix_size,
+                                             const std::string& raw_hashes,
+                                             HashPrefixMap* additions_map);
+
+  // Get the next unmerged hash prefix in dictionary order from
+  // |hash_prefix_map|. |iterator_map| is used to determine which hash prefixes
+  // have been merged already. Returns true if there are any unmerged hash
+  // prefixes in the list.
+  static bool GetNextSmallestUnmergedPrefix(
+      const HashPrefixMap& hash_prefix_map,
+      const IteratorMap& iterator_map,
+      HashPrefix* smallest_hash_prefix);
+
+  // Returns true if |hash_prefix| with PrefixSize |size| exists in |prefixes|.
+  // This small method is exposed in the header so it can be tested separately.
+  static bool HashPrefixMatches(base::StringPiece prefix,
+                                const HashPrefixes& prefixes,
+                                const PrefixSize& size);
+
+  // For each key in |hash_prefix_map|, sets the iterator at that key
+  // |iterator_map| to hash_prefix_map[key].begin().
+  static void InitializeIteratorMap(const HashPrefixMap& hash_prefix_map,
+                                    IteratorMap* iterator_map);
+
+  // Reserve the appropriate string size so that the string size of the merged
+  // list is exact. This ignores the space that would otherwise be released by
+  // deletions specified in the update because it is non-trivial to calculate
+  // those deletions upfront. This isn't so bad since deletions are supposed to
+  // be small and infrequent.
+  static void ReserveSpaceInPrefixMap(const HashPrefixMap& other_prefixes_map,
+                                      HashPrefixMap* prefix_map_to_update);
+
+  // Same as the public GetMatchingHashPrefix method, but takes a StringPiece,
+  // for performance reasons.
+  HashPrefix GetMatchingHashPrefix(base::StringPiece full_hash);
+
+  // Merges the prefix map from the old store (|old_hash_prefix_map|) and the
+  // update (additions_map) to populate the prefix map for the current store.
+  // The indices in the |raw_removals| list, which may be NULL, are not merged.
+  // The SHA256 checksum of the final list of hash prefixes, in
+  // lexicographically sorted order, must match |expected_checksum| (if it's not
+  // empty).
+  ApplyUpdateResult MergeUpdate(
+      const HashPrefixMap& old_hash_prefix_map,
+      const HashPrefixMap& additions_map,
+      const ::google::protobuf::RepeatedField<::google::protobuf::int32>*
+          raw_removals,
+      const std::string& expected_checksum);
+
+  // Processes the FULL_UPDATE |response| from the server, and writes the
+  // merged V4Store to disk. If processing the |response| succeeds, it returns
+  // APPLY_UPDATE_SUCCESS. The UMA metrics for all interesting sub-operations
+  // use the prefix |metric|.
+  // This method is only called when we receive a FULL_UPDATE from the server.
+  ApplyUpdateResult ProcessFullUpdateAndWriteToDisk(
+      const std::string& metric,
+      std::unique_ptr<ListUpdateResponse> response);
+
+  // Processes a FULL_UPDATE |response| and updates the V4Store. If processing
+  // the |response| succeeds, it returns APPLY_UPDATE_SUCCESS.
+  // This method is called when we receive a FULL_UPDATE from the server, and
+  // when we read a store file from disk on startup. The UMA metrics for all
+  // interesting sub-operations use the prefix |metric|. Delays the checksum
+  // check if |delay_checksum_check| is true.
+  ApplyUpdateResult ProcessFullUpdate(
+      const std::string& metric,
+      const std::unique_ptr<ListUpdateResponse>& response,
+      bool delay_checksum_check);
+
+  // Merges the hash prefixes in |hash_prefix_map_old| and |response|, updates
+  // the |hash_prefix_map_| and |state_| in the V4Store, and writes the merged
+  // store to disk. If processing succeeds, it returns APPLY_UPDATE_SUCCESS.
+  // This method is only called when we receive a PARTIAL_UPDATE from the
+  // server. The UMA metrics for all interesting sub-operations use the prefix
+  // |metric|.
+  ApplyUpdateResult ProcessPartialUpdateAndWriteToDisk(
+      const std::string& metric,
+      const HashPrefixMap& hash_prefix_map_old,
+      std::unique_ptr<ListUpdateResponse> response);
+
+  // Merges the hash prefixes in |hash_prefix_map_old| and |response|, and
+  // updates the |hash_prefix_map_| and |state_| in the V4Store. If processing
+  // succeeds, it returns APPLY_UPDATE_SUCCESS. The UMA metrics for all
+  // interesting sub-operations use the prefix |metric|. Delays the checksum
+  // check if |delay_checksum_check| is true.
+  ApplyUpdateResult ProcessUpdate(
+      const std::string& metric,
+      const HashPrefixMap& hash_prefix_map_old,
+      const std::unique_ptr<ListUpdateResponse>& response,
+      bool delay_checksum_check);
+
+  // Reads the state of the store from the file on disk and returns the reason
+  // for the failure or reports success.
+  StoreReadResult ReadFromDisk();
+
+  // Updates the |additions_map| with the additions received in the partial
+  // update from the server. The UMA metrics for all interesting sub-operations
+  // use the prefix |metric|.
+  ApplyUpdateResult UpdateHashPrefixMapFromAdditions(
+      const std::string& metric,
+      const ::google::protobuf::RepeatedPtrField<ThreatEntrySet>& additions,
+      HashPrefixMap* additions_map);
+
+  // Writes the hash_prefix_map_ to disk as a V4StoreFileFormat proto.
+  // |checksum| is used to set the |checksum| field in the final proto.
+  StoreWriteResult WriteToDisk(const Checksum& checksum);
+
+  // Records the status of the update being applied to the database.
+  ApplyUpdateResult last_apply_update_result_ = APPLY_UPDATE_RESULT_MAX;
+
+  // Records the time when the store was last updated.
+  base::Time last_apply_update_time_millis_;
+
+  // The checksum value as read from the disk, until it is verified. Once
+  // verified, it is cleared.
+  std::string expected_checksum_;
+
+  // The size of the file on disk for this store.
+  int64_t file_size_;
+
+  // True if the file was successfully read+parsed or was populated from
+  // a full update.
+  bool has_valid_data_;
+
+  // Records the number of times we have looked up the store.
+  size_t checks_attempted_ = 0;
+
+  // The state of the store as returned by the PVer4 server in the last applied
+  // update response.
+  std::string state_;
+  const base::FilePath store_path_;
+  const scoped_refptr<base::SequencedTaskRunner> task_runner_;
+};
+
+std::ostream& operator<<(std::ostream& os, const V4Store& store);
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CORE_DB_V4_STORE_H_
diff --git a/components/safe_browsing/db/v4_store.proto b/components/safe_browsing/core/db/v4_store.proto
similarity index 100%
rename from components/safe_browsing/db/v4_store.proto
rename to components/safe_browsing/core/db/v4_store.proto
diff --git a/components/safe_browsing/core/db/v4_store_fuzzer.cc b/components/safe_browsing/core/db/v4_store_fuzzer.cc
new file mode 100644
index 0000000..01ceb18
--- /dev/null
+++ b/components/safe_browsing/core/db/v4_store_fuzzer.cc
@@ -0,0 +1,127 @@
+// Copyright 2019 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 <stdint.h>
+#include <memory>
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/test/test_simple_task_runner.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/core/db/v4_store.h"
+#include "components/safe_browsing/core/db/v4_test_util.h"
+
+namespace safe_browsing {
+
+const PrefixSize kMinHashPrefixLengthForFuzzing = kMinHashPrefixLength;
+const PrefixSize kMaxHashPrefixLengthForFuzzing = 8;
+
+class V4StoreFuzzer {
+ public:
+  static int FuzzMergeUpdate(const uint8_t* data, size_t size) {
+    // |prefix_map_old| represents the existing state of the |V4Store|.
+    HashPrefixMap prefix_map_old;
+    // |prefix_map_additions| represents the update being applied.
+    HashPrefixMap prefix_map_additions;
+
+    // Pass 1:
+    // Add a prefix_size->[prefixes] pair in |prefix_map_old|.
+    PopulateHashPrefixMap(&data, &size, &prefix_map_old);
+    // Add a prefix_size->[prefixes] pair in |prefix_map_additions|.
+    PopulateHashPrefixMap(&data, &size, &prefix_map_additions);
+
+    // Pass 2:
+    // Add a prefix_size->[prefixes] pair in |prefix_map_old|.
+    // If the prefix_size is the same as that added in |prefix_map_old| during
+    // Pass 1, the older list of prefixes is lost.
+    PopulateHashPrefixMap(&data, &size, &prefix_map_old);
+    // Add a prefix_size->[prefixes] pair in |prefix_map_additions|.
+    // If the prefix_size is the same as that added in |prefix_map_additions|
+    // during Pass 1, the older list of prefixes is lost.
+    PopulateHashPrefixMap(&data, &size, &prefix_map_additions);
+
+    auto store = std::make_unique<TestV4Store>(
+        base::MakeRefCounted<base::TestSimpleTaskRunner>(), base::FilePath());
+    // Assume no removals.
+    google::protobuf::RepeatedField<google::protobuf::int32> raw_removals;
+    // Empty checksum indicates that the checksum calculation should be skipped.
+    std::string empty_checksum;
+    store->MergeUpdate(prefix_map_old, prefix_map_additions, &raw_removals,
+                       empty_checksum);
+#ifndef NDEBUG
+    DisplayHashPrefixMapDetails(store->hash_prefix_map_);
+#endif
+
+    return 0;
+  }
+
+ private:
+  // Add a prefix_size->[prefixes] pair in |hash_prefix_map|.
+  // Ensures that length of [prefixes] is a multiple of prefix_size.
+  // If the map already contains a pair with key prefix_size, the existing value
+  // is discarded.
+  // Here's a summary of how the input is parsed:
+  // * First uint8_t is the |prefix_size| to be added.
+  // * Next uint8_t is the length of the list of prefixes.
+  //  * It is adjusted to be no greater than the remaining size of |data|.
+  //  * It is called as |prefixes_list_size|.
+  // * Next |prefixes_list_size| bytes are added to |hash_prefix_map|
+  //   as a list of prefixes of size |prefix_size|.
+  static void PopulateHashPrefixMap(const uint8_t** data,
+                                    size_t* size,
+                                    HashPrefixMap* hash_prefix_map) {
+    uint8_t datum;
+    if (!GetDatum(data, size, &datum))
+      return;
+
+    // Prefix size is defined to be between |kMinHashPrefixLength| and
+    // |kMaxHashPrefixLength| but we are going to limit them to smaller sizes so
+    // that we have a higher chance of actually populating the
+    // |hash_prefix_map| for smaller inputs.
+    PrefixSize prefix_size = kMinHashPrefixLengthForFuzzing +
+                             (datum % (kMaxHashPrefixLengthForFuzzing -
+                                       kMinHashPrefixLengthForFuzzing + 1));
+
+    if (!GetDatum(data, size, &datum))
+      return;
+    size_t prefixes_list_size = datum;
+    // This |prefixes_list_size| is the length of the list of prefixes to be
+    // added. It can't be larger than the remaining buffer.
+    if (*size < prefixes_list_size) {
+      prefixes_list_size = *size;
+    }
+    std::string prefixes(*data, *data + prefixes_list_size);
+    *size -= prefixes_list_size;
+    *data += prefixes_list_size;
+    V4Store::AddUnlumpedHashes(prefix_size, prefixes, hash_prefix_map);
+#ifndef NDEBUG
+    DisplayHashPrefixMapDetails(*hash_prefix_map);
+#endif
+  }
+
+  static bool GetDatum(const uint8_t** data, size_t* size, uint8_t* datum) {
+    if (*size == 0)
+      return false;
+    *datum = *data[0];
+    (*data)++;
+    (*size)--;
+    return true;
+  }
+
+  static void DisplayHashPrefixMapDetails(
+      const HashPrefixMap& hash_prefix_map) {
+    for (const auto& pair : hash_prefix_map) {
+      PrefixSize prefix_size = pair.first;
+      size_t prefixes_length = pair.second.length();
+      DVLOG(5) << __FUNCTION__ << " : " << prefix_size << " : "
+               << prefixes_length;
+    }
+  }
+};
+
+}  // namespace safe_browsing
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  return safe_browsing::V4StoreFuzzer::FuzzMergeUpdate(data, size);
+}
diff --git a/components/safe_browsing/core/db/v4_store_perftest.cc b/components/safe_browsing/core/db/v4_store_perftest.cc
new file mode 100644
index 0000000..2e1f916
--- /dev/null
+++ b/components/safe_browsing/core/db/v4_store_perftest.cc
@@ -0,0 +1,83 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/numerics/checked_math.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/timer/elapsed_timer.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/core/db/v4_test_util.h"
+#include "crypto/sha2.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/perf/perf_result_reporter.h"
+
+namespace safe_browsing {
+
+namespace {
+
+constexpr char kMetricPrefixV4Store[] = "V4Store.";
+constexpr char kMetricGetMatchingHashPrefixMs[] = "get_matching_hash_prefix";
+
+perf_test::PerfResultReporter SetUpV4StoreReporter(const std::string& story) {
+  perf_test::PerfResultReporter reporter(kMetricPrefixV4Store, story);
+  reporter.RegisterImportantMetric(kMetricGetMatchingHashPrefixMs, "ms");
+  return reporter;
+}
+
+}  // namespace
+
+class V4StorePerftest : public testing::Test {};
+
+TEST_F(V4StorePerftest, StressTest) {
+// Debug builds can be quite slow. Use a smaller number of prefixes to test.
+#if defined(NDEBUG)
+  const size_t kNumPrefixes = 2000000;
+#else
+  const size_t kNumPrefixes = 20000;
+#endif
+
+  static_assert(kMaxHashPrefixLength == crypto::kSHA256Length,
+                "SHA256 produces a valid FullHash");
+  CHECK(base::IsValidForType<size_t>(
+      base::CheckMul(kNumPrefixes, kMaxHashPrefixLength)));
+
+  // Keep the full hashes as one big string to avoid tons of allocations /
+  // deallocations in the test.
+  std::string full_hashes(kNumPrefixes * kMaxHashPrefixLength, 0);
+  base::StringPiece full_hashes_piece = base::StringPiece(full_hashes);
+  std::vector<std::string> prefixes;
+  for (size_t i = 0; i < kNumPrefixes; i++) {
+    size_t index = i * kMaxHashPrefixLength;
+    crypto::SHA256HashString(base::StringPrintf("%zu", i), &full_hashes[index],
+                             kMaxHashPrefixLength);
+    prefixes.push_back(full_hashes.substr(index, kMinHashPrefixLength));
+  }
+
+  auto store = std::make_unique<TestV4Store>(
+      base::MakeRefCounted<base::TestSimpleTaskRunner>(), base::FilePath());
+  store->SetPrefixes(std::move(prefixes), kMinHashPrefixLength);
+
+  size_t matches = 0;
+  auto reporter = SetUpV4StoreReporter("stress_test");
+  base::ElapsedTimer timer;
+  for (size_t i = 0; i < kNumPrefixes; i++) {
+    size_t index = i * kMaxHashPrefixLength;
+    base::StringPiece full_hash =
+        full_hashes_piece.substr(index, kMaxHashPrefixLength);
+    matches += !store->GetMatchingHashPrefix(full_hash).empty();
+  }
+  reporter.AddResult(kMetricGetMatchingHashPrefixMs,
+                     timer.Elapsed().InMillisecondsF());
+
+  EXPECT_EQ(kNumPrefixes, matches);
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/db/v4_store_unittest.cc b/components/safe_browsing/core/db/v4_store_unittest.cc
new file mode 100644
index 0000000..52a2781
--- /dev/null
+++ b/components/safe_browsing/core/db/v4_store_unittest.cc
@@ -0,0 +1,887 @@
+// 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 "components/safe_browsing/core/db/v4_store.h"
+
+#include "base/base64.h"
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/run_loop.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/time/time.h"
+#include "components/safe_browsing/core/db/v4_store.pb.h"
+#include "content/public/test/browser_task_environment.h"
+#include "crypto/sha2.h"
+#include "testing/platform_test.h"
+
+namespace safe_browsing {
+
+using ::google::protobuf::RepeatedField;
+using ::google::protobuf::RepeatedPtrField;
+using ::google::protobuf::int32;
+
+class V4StoreTest : public PlatformTest {
+ public:
+  V4StoreTest() : task_runner_(new base::TestSimpleTaskRunner) {}
+
+  void SetUp() override {
+    PlatformTest::SetUp();
+
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+    store_path_ = temp_dir_.GetPath().AppendASCII("V4StoreTest.store");
+    DVLOG(1) << "store_path_: " << store_path_.value();
+  }
+
+  void TearDown() override {
+    base::DeleteFile(store_path_, false);
+    PlatformTest::TearDown();
+  }
+
+  void WriteFileFormatProtoToFile(uint32_t magic,
+                                  uint32_t version = 0,
+                                  ListUpdateResponse* response = nullptr) {
+    V4StoreFileFormat file_format;
+    file_format.set_magic_number(magic);
+    file_format.set_version_number(version);
+    if (response != nullptr) {
+      ListUpdateResponse* list_update_response =
+          file_format.mutable_list_update_response();
+      *list_update_response = *response;
+    }
+
+    std::string file_format_string;
+    file_format.SerializeToString(&file_format_string);
+    base::WriteFile(store_path_, file_format_string.data(),
+                    file_format_string.size());
+  }
+
+  void UpdatedStoreReady(bool* called_back,
+                         bool expect_store,
+                         std::unique_ptr<V4Store> store) {
+    *called_back = true;
+    if (expect_store) {
+      ASSERT_TRUE(store);
+      EXPECT_EQ(2u, store->hash_prefix_map_.size());
+      EXPECT_EQ("22222", store->hash_prefix_map_[5]);
+      EXPECT_EQ("abcd", store->hash_prefix_map_[4]);
+    } else {
+      ASSERT_FALSE(store);
+    }
+
+    updated_store_ = std::move(store);
+  }
+
+  base::ScopedTempDir temp_dir_;
+  base::FilePath store_path_;
+  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+  content::BrowserTaskEnvironment task_environment_;
+  std::unique_ptr<V4Store> updated_store_;
+};
+
+TEST_F(V4StoreTest, TestReadFromEmptyFile) {
+  base::CloseFile(base::OpenFile(store_path_, "wb+"));
+
+  V4Store store(task_runner_, store_path_);
+  EXPECT_EQ(FILE_EMPTY_FAILURE, store.ReadFromDisk());
+  EXPECT_FALSE(store.HasValidData());
+}
+
+TEST_F(V4StoreTest, TestReadFromAbsentFile) {
+  EXPECT_EQ(FILE_UNREADABLE_FAILURE,
+            V4Store(task_runner_, store_path_).ReadFromDisk());
+}
+
+TEST_F(V4StoreTest, TestReadFromInvalidContentsFile) {
+  const char kInvalidContents[] = "Chromium";
+  base::WriteFile(store_path_, kInvalidContents, strlen(kInvalidContents));
+  EXPECT_EQ(PROTO_PARSING_FAILURE,
+            V4Store(task_runner_, store_path_).ReadFromDisk());
+}
+
+TEST_F(V4StoreTest, TestReadFromFileWithUnknownProto) {
+  Checksum checksum;
+  checksum.set_sha256("checksum");
+  std::string checksum_string;
+  checksum.SerializeToString(&checksum_string);
+  base::WriteFile(store_path_, checksum_string.data(), checksum_string.size());
+
+  // Even though we wrote a completely different proto to file, the proto
+  // parsing method does not fail. This shows the importance of a magic number.
+  EXPECT_EQ(UNEXPECTED_MAGIC_NUMBER_FAILURE,
+            V4Store(task_runner_, store_path_).ReadFromDisk());
+}
+
+TEST_F(V4StoreTest, TestReadFromUnexpectedMagicFile) {
+  WriteFileFormatProtoToFile(111);
+  EXPECT_EQ(UNEXPECTED_MAGIC_NUMBER_FAILURE,
+            V4Store(task_runner_, store_path_).ReadFromDisk());
+}
+
+TEST_F(V4StoreTest, TestReadFromLowVersionFile) {
+  WriteFileFormatProtoToFile(0x600D71FE, 2);
+  EXPECT_EQ(FILE_VERSION_INCOMPATIBLE_FAILURE,
+            V4Store(task_runner_, store_path_).ReadFromDisk());
+}
+
+TEST_F(V4StoreTest, TestReadFromNoHashPrefixInfoFile) {
+  WriteFileFormatProtoToFile(0x600D71FE, 9);
+  EXPECT_EQ(HASH_PREFIX_INFO_MISSING_FAILURE,
+            V4Store(task_runner_, store_path_).ReadFromDisk());
+}
+
+TEST_F(V4StoreTest, TestReadFromNoHashPrefixesFile) {
+  ListUpdateResponse list_update_response;
+  list_update_response.set_platform_type(LINUX_PLATFORM);
+  list_update_response.set_response_type(ListUpdateResponse::FULL_UPDATE);
+  WriteFileFormatProtoToFile(0x600D71FE, 9, &list_update_response);
+  V4Store store(task_runner_, store_path_);
+  EXPECT_EQ(READ_SUCCESS, store.ReadFromDisk());
+  EXPECT_TRUE(store.hash_prefix_map_.empty());
+  EXPECT_EQ(14, store.file_size_);
+  EXPECT_FALSE(store.HasValidData());
+}
+
+TEST_F(V4StoreTest, TestAddUnlumpedHashesWithInvalidAddition) {
+  HashPrefixMap prefix_map;
+  EXPECT_EQ(ADDITIONS_SIZE_UNEXPECTED_FAILURE,
+            V4Store::AddUnlumpedHashes(5, "a", &prefix_map));
+  EXPECT_TRUE(prefix_map.empty());
+}
+
+TEST_F(V4StoreTest, TestAddUnlumpedHashesWithEmptyString) {
+  HashPrefixMap prefix_map;
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            V4Store::AddUnlumpedHashes(5, "", &prefix_map));
+  EXPECT_TRUE(prefix_map[5].empty());
+}
+
+TEST_F(V4StoreTest, TestAddUnlumpedHashes) {
+  HashPrefixMap prefix_map;
+  PrefixSize prefix_size = 5;
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            V4Store::AddUnlumpedHashes(prefix_size, "abcde5432100000-----",
+                                       &prefix_map));
+  EXPECT_EQ(1u, prefix_map.size());
+  HashPrefixes hash_prefixes = prefix_map.at(prefix_size);
+  EXPECT_EQ(4 * prefix_size, hash_prefixes.size());
+  EXPECT_EQ("abcde5432100000-----", hash_prefixes);
+
+  prefix_size = 4;
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            V4Store::AddUnlumpedHashes(prefix_size, "abcde5432100000-----",
+                                       &prefix_map));
+  EXPECT_EQ(2u, prefix_map.size());
+  hash_prefixes = prefix_map.at(prefix_size);
+  EXPECT_EQ(5 * prefix_size, hash_prefixes.size());
+  EXPECT_EQ("abcde5432100000-----", hash_prefixes);
+}
+
+TEST_F(V4StoreTest, TestGetNextSmallestUnmergedPrefixWithEmptyPrefixMap) {
+  HashPrefixMap prefix_map;
+  IteratorMap iterator_map;
+  V4Store::InitializeIteratorMap(prefix_map, &iterator_map);
+
+  HashPrefix prefix;
+  EXPECT_FALSE(V4Store::GetNextSmallestUnmergedPrefix(prefix_map, iterator_map,
+                                                      &prefix));
+}
+
+TEST_F(V4StoreTest, TestGetNextSmallestUnmergedPrefix) {
+  HashPrefixMap prefix_map;
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            V4Store::AddUnlumpedHashes(5, "-----0000054321abcde", &prefix_map));
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            V4Store::AddUnlumpedHashes(4, "*****0000054321abcde", &prefix_map));
+  IteratorMap iterator_map;
+  V4Store::InitializeIteratorMap(prefix_map, &iterator_map);
+
+  HashPrefix prefix;
+  EXPECT_TRUE(V4Store::GetNextSmallestUnmergedPrefix(prefix_map, iterator_map,
+                                                     &prefix));
+  EXPECT_EQ("****", prefix);
+}
+
+TEST_F(V4StoreTest, TestMergeUpdatesWithSameSizesInEachMap) {
+  HashPrefixMap prefix_map_old;
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            V4Store::AddUnlumpedHashes(4, "abcdefgh", &prefix_map_old));
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            V4Store::AddUnlumpedHashes(5, "54321abcde", &prefix_map_old));
+  HashPrefixMap prefix_map_additions;
+  EXPECT_EQ(
+      APPLY_UPDATE_SUCCESS,
+      V4Store::AddUnlumpedHashes(4, "----1111bbbb", &prefix_map_additions));
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            V4Store::AddUnlumpedHashes(5, "22222bcdef", &prefix_map_additions));
+
+  V4Store store(task_runner_, store_path_);
+  // Proof of checksum validity using python:
+  // >>> import hashlib
+  // >>> m = hashlib.sha256()
+  // >>> m.update("----11112222254321abcdabcdebbbbbcdefefgh")
+  // >>> m.digest()
+  // "\xbc\xb3\xedk\xe3x\xd1(\xa9\xedz7]"
+  // "x\x18\xbdn]\xa5\xa8R\xf7\xab\xcf\xc1\xa3\xa3\xc5Z,\xa6o"
+  std::string expected_checksum = std::string(
+      "\xBC\xB3\xEDk\xE3x\xD1(\xA9\xEDz7]x\x18\xBDn]"
+      "\xA5\xA8R\xF7\xAB\xCF\xC1\xA3\xA3\xC5Z,\xA6o",
+      crypto::kSHA256Length);
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            store.MergeUpdate(prefix_map_old, prefix_map_additions, nullptr,
+                              expected_checksum));
+
+  const HashPrefixMap& prefix_map = store.hash_prefix_map_;
+  EXPECT_EQ(2u, prefix_map.size());
+
+  PrefixSize prefix_size = 4;
+  HashPrefixes hash_prefixes = prefix_map.at(prefix_size);
+  EXPECT_EQ(5 * prefix_size, hash_prefixes.size());
+  EXPECT_EQ("----", hash_prefixes.substr(0 * prefix_size, prefix_size));
+  EXPECT_EQ("1111", hash_prefixes.substr(1 * prefix_size, prefix_size));
+  EXPECT_EQ("abcd", hash_prefixes.substr(2 * prefix_size, prefix_size));
+  EXPECT_EQ("bbbb", hash_prefixes.substr(3 * prefix_size, prefix_size));
+  EXPECT_EQ("efgh", hash_prefixes.substr(4 * prefix_size, prefix_size));
+
+  prefix_size = 5;
+  hash_prefixes = prefix_map.at(prefix_size);
+  EXPECT_EQ(4 * prefix_size, hash_prefixes.size());
+  EXPECT_EQ("22222", hash_prefixes.substr(0 * prefix_size, prefix_size));
+  EXPECT_EQ("54321", hash_prefixes.substr(1 * prefix_size, prefix_size));
+  EXPECT_EQ("abcde", hash_prefixes.substr(2 * prefix_size, prefix_size));
+  EXPECT_EQ("bcdef", hash_prefixes.substr(3 * prefix_size, prefix_size));
+}
+
+TEST_F(V4StoreTest, TestMergeUpdatesWithDifferentSizesInEachMap) {
+  HashPrefixMap prefix_map_old;
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            V4Store::AddUnlumpedHashes(4, "1111abcdefgh", &prefix_map_old));
+  HashPrefixMap prefix_map_additions;
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            V4Store::AddUnlumpedHashes(5, "22222bcdef", &prefix_map_additions));
+
+  V4Store store(task_runner_, store_path_);
+  std::string expected_checksum = std::string(
+      "\xA5\x8B\xCAsD\xC7\xF9\xCE\xD2\xF4\x4="
+      "\xB2\"\x82\x1A\xC1\xB8\x1F\x10\r\v\x9A\x93\xFD\xE1\xB8"
+      "B\x1Eh\xF7\xB4",
+      crypto::kSHA256Length);
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            store.MergeUpdate(prefix_map_old, prefix_map_additions, nullptr,
+                              expected_checksum));
+
+  const HashPrefixMap& prefix_map = store.hash_prefix_map_;
+  EXPECT_EQ(2u, prefix_map.size());
+
+  PrefixSize prefix_size = 4;
+  HashPrefixes hash_prefixes = prefix_map.at(prefix_size);
+  EXPECT_EQ(3 * prefix_size, hash_prefixes.size());
+  EXPECT_EQ("1111abcdefgh", hash_prefixes);
+
+  prefix_size = 5;
+  hash_prefixes = prefix_map.at(prefix_size);
+  EXPECT_EQ(2 * prefix_size, hash_prefixes.size());
+  EXPECT_EQ("22222bcdef", hash_prefixes);
+}
+
+TEST_F(V4StoreTest, TestMergeUpdatesOldMapRunsOutFirst) {
+  HashPrefixMap prefix_map_old;
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            V4Store::AddUnlumpedHashes(4, "00001111", &prefix_map_old));
+  HashPrefixMap prefix_map_additions;
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            V4Store::AddUnlumpedHashes(4, "2222", &prefix_map_additions));
+
+  V4Store store(task_runner_, store_path_);
+  std::string expected_checksum = std::string(
+      "\x84\x92\xET\xED\xF7\x97"
+      "C\xCE}\xFF"
+      "E\x1\xAB-\b>\xDB\x95\b\xD8H\xD5\x1D\xF9]8x\xA4\xD4\xC2\xFA",
+      crypto::kSHA256Length);
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            store.MergeUpdate(prefix_map_old, prefix_map_additions, nullptr,
+                              expected_checksum));
+
+  const HashPrefixMap& prefix_map = store.hash_prefix_map_;
+  EXPECT_EQ(1u, prefix_map.size());
+
+  PrefixSize prefix_size = 4;
+  HashPrefixes hash_prefixes = prefix_map.at(prefix_size);
+  EXPECT_EQ(3 * prefix_size, hash_prefixes.size());
+  EXPECT_EQ("0000", hash_prefixes.substr(0 * prefix_size, prefix_size));
+  EXPECT_EQ("1111", hash_prefixes.substr(1 * prefix_size, prefix_size));
+  EXPECT_EQ("2222", hash_prefixes.substr(2 * prefix_size, prefix_size));
+}
+
+TEST_F(V4StoreTest, TestMergeUpdatesAdditionsMapRunsOutFirst) {
+  HashPrefixMap prefix_map_old;
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            V4Store::AddUnlumpedHashes(4, "2222", &prefix_map_old));
+  HashPrefixMap prefix_map_additions;
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            V4Store::AddUnlumpedHashes(4, "00001111", &prefix_map_additions));
+
+  V4Store store(task_runner_, store_path_);
+  std::string expected_checksum = std::string(
+      "\x84\x92\xET\xED\xF7\x97"
+      "C\xCE}\xFF"
+      "E\x1\xAB-\b>\xDB\x95\b\xD8H\xD5\x1D\xF9]8x\xA4\xD4\xC2\xFA",
+      crypto::kSHA256Length);
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            store.MergeUpdate(prefix_map_old, prefix_map_additions, nullptr,
+                              expected_checksum));
+
+  const HashPrefixMap& prefix_map = store.hash_prefix_map_;
+  EXPECT_EQ(1u, prefix_map.size());
+
+  PrefixSize prefix_size = 4;
+  HashPrefixes hash_prefixes = prefix_map.at(prefix_size);
+  EXPECT_EQ(3 * prefix_size, hash_prefixes.size());
+  EXPECT_EQ("0000", hash_prefixes.substr(0 * prefix_size, prefix_size));
+  EXPECT_EQ("1111", hash_prefixes.substr(1 * prefix_size, prefix_size));
+  EXPECT_EQ("2222", hash_prefixes.substr(2 * prefix_size, prefix_size));
+}
+
+TEST_F(V4StoreTest, TestMergeUpdatesFailsForRepeatedHashPrefix) {
+  HashPrefixMap prefix_map_old;
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            V4Store::AddUnlumpedHashes(4, "2222", &prefix_map_old));
+  HashPrefixMap prefix_map_additions;
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            V4Store::AddUnlumpedHashes(4, "2222", &prefix_map_additions));
+
+  V4Store store(task_runner_, store_path_);
+  std::string expected_checksum;
+  EXPECT_EQ(ADDITIONS_HAS_EXISTING_PREFIX_FAILURE,
+            store.MergeUpdate(prefix_map_old, prefix_map_additions, nullptr,
+                              expected_checksum));
+}
+
+TEST_F(V4StoreTest, TestMergeUpdatesFailsWhenRemovalsIndexTooLarge) {
+  HashPrefixMap prefix_map_old;
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            V4Store::AddUnlumpedHashes(4, "2222", &prefix_map_old));
+  HashPrefixMap prefix_map_additions;
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            V4Store::AddUnlumpedHashes(4, "11113333", &prefix_map_additions));
+
+  // Even though the merged map could have size 3 without removals, the
+  // removals index should only count the entries in the old map.
+  V4Store store(task_runner_, store_path_);
+  RepeatedField<int32> raw_removals;
+  // old_store: ["2222"]
+  raw_removals.Add(1);
+  std::string expected_checksum;
+  EXPECT_EQ(REMOVALS_INDEX_TOO_LARGE_FAILURE,
+            store.MergeUpdate(prefix_map_old, prefix_map_additions,
+                              &raw_removals, expected_checksum));
+}
+
+TEST_F(V4StoreTest, TestMergeUpdatesRemovesOnlyElement) {
+  HashPrefixMap prefix_map_old;
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            V4Store::AddUnlumpedHashes(4, "2222", &prefix_map_old));
+  HashPrefixMap prefix_map_additions;
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            V4Store::AddUnlumpedHashes(5, "1111133333", &prefix_map_additions));
+
+  V4Store store(task_runner_, store_path_);
+  RepeatedField<int32> raw_removals;
+  // old_store: ["2222"]
+  raw_removals.Add(0);  // Removes "2222"
+  std::string expected_checksum = std::string(
+      "\xE6\xB0\x1\x12\x89\x83\xF0/"
+      "\xE7\xD2\xE6\xDC\x16\xB9\x8C+\xA2\xB3\x9E\x89<,\x88"
+      "B3\xA5\xB1"
+      "D\x9E\x9E'\x14",
+      crypto::kSHA256Length);
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            store.MergeUpdate(prefix_map_old, prefix_map_additions,
+                              &raw_removals, expected_checksum));
+
+  const HashPrefixMap& prefix_map = store.hash_prefix_map_;
+  // The size is 2 since we reserve space anyway.
+  EXPECT_EQ(2u, prefix_map.size());
+  EXPECT_TRUE(prefix_map.at(4).empty());
+  EXPECT_EQ("1111133333", prefix_map.at(5));
+}
+
+TEST_F(V4StoreTest, TestMergeUpdatesRemovesFirstElement) {
+  HashPrefixMap prefix_map_old;
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            V4Store::AddUnlumpedHashes(4, "22224444", &prefix_map_old));
+  HashPrefixMap prefix_map_additions;
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            V4Store::AddUnlumpedHashes(5, "1111133333", &prefix_map_additions));
+
+  V4Store store(task_runner_, store_path_);
+  RepeatedField<int32> raw_removals;
+  // old_store: ["2222", "4444"]
+  raw_removals.Add(0);  // Removes "2222"
+  std::string expected_checksum = std::string(
+      "\x9D\xF3\xF2\x82\0\x1E{\xDF\xCD\xC0V\xBE\xD6<\x85"
+      "D7=\xB5v\xAD\b1\xC9\xB3"
+      "A\xAC"
+      "b\xF1lf\xA4",
+      crypto::kSHA256Length);
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            store.MergeUpdate(prefix_map_old, prefix_map_additions,
+                              &raw_removals, expected_checksum));
+
+  const HashPrefixMap& prefix_map = store.hash_prefix_map_;
+  // The size is 2 since we reserve space anyway.
+  EXPECT_EQ(2u, prefix_map.size());
+  EXPECT_EQ("4444", prefix_map.at(4));
+  EXPECT_EQ("1111133333", prefix_map.at(5));
+}
+
+TEST_F(V4StoreTest, TestMergeUpdatesRemovesMiddleElement) {
+  HashPrefixMap prefix_map_old;
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            V4Store::AddUnlumpedHashes(4, "222233334444", &prefix_map_old));
+  HashPrefixMap prefix_map_additions;
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            V4Store::AddUnlumpedHashes(5, "1111133333", &prefix_map_additions));
+
+  V4Store store(task_runner_, store_path_);
+  RepeatedField<int32> raw_removals;
+  // old_store: ["2222", "3333", 4444"]
+  raw_removals.Add(1);  // Removes "3333"
+  std::string expected_checksum = std::string(
+      "\xFA-A\x15{\x17\0>\xAE"
+      "8\xACigR\xD1\x93<\xB2\xC9\xB5\x81\xC0\xFB\xBB\x2\f\xAFpN\xEA"
+      "44",
+      crypto::kSHA256Length);
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            store.MergeUpdate(prefix_map_old, prefix_map_additions,
+                              &raw_removals, expected_checksum));
+
+  const HashPrefixMap& prefix_map = store.hash_prefix_map_;
+  // The size is 2 since we reserve space anyway.
+  EXPECT_EQ(2u, prefix_map.size());
+  EXPECT_EQ("22224444", prefix_map.at(4));
+  EXPECT_EQ("1111133333", prefix_map.at(5));
+}
+
+TEST_F(V4StoreTest, TestMergeUpdatesRemovesLastElement) {
+  HashPrefixMap prefix_map_old;
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            V4Store::AddUnlumpedHashes(4, "222233334444", &prefix_map_old));
+  HashPrefixMap prefix_map_additions;
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            V4Store::AddUnlumpedHashes(5, "1111133333", &prefix_map_additions));
+
+  V4Store store(task_runner_, store_path_);
+  RepeatedField<int32> raw_removals;
+  // old_store: ["2222", "3333", 4444"]
+  raw_removals.Add(2);  // Removes "4444"
+  std::string expected_checksum = std::string(
+      "a\xE1\xAD\x96\xFE\xA6"
+      "A\xCA~7W\xF6z\xD8\n\xCA?\x96\x8A\x17U\x5\v\r\x88]\n\xB2JX\xC4S",
+      crypto::kSHA256Length);
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            store.MergeUpdate(prefix_map_old, prefix_map_additions,
+                              &raw_removals, expected_checksum));
+
+  const HashPrefixMap& prefix_map = store.hash_prefix_map_;
+  // The size is 2 since we reserve space anyway.
+  EXPECT_EQ(2u, prefix_map.size());
+  EXPECT_EQ("22223333", prefix_map.at(4));
+  EXPECT_EQ("1111133333", prefix_map.at(5));
+}
+
+TEST_F(V4StoreTest, TestMergeUpdatesRemovesWhenOldHasDifferentSizes) {
+  HashPrefixMap prefix_map_old;
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            V4Store::AddUnlumpedHashes(4, "222233334444", &prefix_map_old));
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            V4Store::AddUnlumpedHashes(5, "aaaaabbbbb", &prefix_map_old));
+  HashPrefixMap prefix_map_additions;
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            V4Store::AddUnlumpedHashes(5, "1111133333", &prefix_map_additions));
+
+  V4Store store(task_runner_, store_path_);
+  RepeatedField<int32> raw_removals;
+  // old_store: ["2222", "3333", 4444", "aaaaa", "bbbbb"]
+  raw_removals.Add(3);  // Removes "aaaaa"
+  std::string expected_checksum = std::string(
+      "\xA7OG\x9D\x83.\x9D-f\x8A\xE\x8B\r&\x19"
+      "6\xE3\xF0\xEFTi\xA7\x5\xEA\xF7"
+      "ej,\xA8\x9D\xAD\x91",
+      crypto::kSHA256Length);
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            store.MergeUpdate(prefix_map_old, prefix_map_additions,
+                              &raw_removals, expected_checksum));
+
+  const HashPrefixMap& prefix_map = store.hash_prefix_map_;
+  // The size is 2 since we reserve space anyway.
+  EXPECT_EQ(2u, prefix_map.size());
+  EXPECT_EQ("222233334444", prefix_map.at(4));
+  EXPECT_EQ("1111133333bbbbb", prefix_map.at(5));
+}
+
+TEST_F(V4StoreTest, TestMergeUpdatesRemovesMultipleAcrossDifferentSizes) {
+  HashPrefixMap prefix_map_old;
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            V4Store::AddUnlumpedHashes(4, "22223333aaaa", &prefix_map_old));
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            V4Store::AddUnlumpedHashes(5, "3333344444bbbbb", &prefix_map_old));
+  HashPrefixMap prefix_map_additions;
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            V4Store::AddUnlumpedHashes(5, "11111", &prefix_map_additions));
+
+  V4Store store(task_runner_, store_path_);
+  RepeatedField<int32> raw_removals;
+  // old_store: ["2222", "3333", "33333", "44444", "aaaa", "bbbbb"]
+  raw_removals.Add(1);  // Removes "3333"
+  raw_removals.Add(3);  // Removes "44444"
+  std::string expected_checksum = std::string(
+      "!D\xB7&L\xA7&G0\x85\xB4"
+      "E\xDD\x10\"\x9A\xCA\xF1"
+      "3^\x83w\xBBL\x19n\xAD\xBDM\x9D"
+      "b\x9F",
+      crypto::kSHA256Length);
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            store.MergeUpdate(prefix_map_old, prefix_map_additions,
+                              &raw_removals, expected_checksum));
+
+  const HashPrefixMap& prefix_map = store.hash_prefix_map_;
+  // The size is 2 since we reserve space anyway.
+  EXPECT_EQ(2u, prefix_map.size());
+  EXPECT_EQ("2222aaaa", prefix_map.at(4));
+  EXPECT_EQ("1111133333bbbbb", prefix_map.at(5));
+}
+
+TEST_F(V4StoreTest, TestReadFullResponseWithValidHashPrefixMap) {
+  V4Store write_store(task_runner_, store_path_);
+  write_store.hash_prefix_map_[4] = "00000abc";
+  write_store.hash_prefix_map_[5] = "00000abcde";
+  write_store.state_ = "test_client_state";
+  EXPECT_FALSE(base::PathExists(write_store.store_path_));
+  EXPECT_EQ(WRITE_SUCCESS, write_store.WriteToDisk(Checksum()));
+  EXPECT_TRUE(base::PathExists(write_store.store_path_));
+
+  V4Store read_store(task_runner_, store_path_);
+  EXPECT_EQ(READ_SUCCESS, read_store.ReadFromDisk());
+  EXPECT_EQ("test_client_state", read_store.state_);
+  ASSERT_EQ(2u, read_store.hash_prefix_map_.size());
+  EXPECT_EQ("00000abc", read_store.hash_prefix_map_[4]);
+  EXPECT_EQ("00000abcde", read_store.hash_prefix_map_[5]);
+  EXPECT_EQ(71, read_store.file_size_);
+}
+
+// This tests fails to read the prefix map from the disk because the file on
+// disk is invalid. The hash prefixes string is 6 bytes long, but the prefix
+// size is 5 so the parser isn't able to split the hash prefixes list
+// completely.
+TEST_F(V4StoreTest, TestReadFullResponseWithInvalidHashPrefixMap) {
+  V4Store write_store(task_runner_, store_path_);
+  write_store.hash_prefix_map_[5] = "abcdef";
+  write_store.state_ = "test_client_state";
+  EXPECT_FALSE(base::PathExists(write_store.store_path_));
+  EXPECT_EQ(WRITE_SUCCESS, write_store.WriteToDisk(Checksum()));
+  EXPECT_TRUE(base::PathExists(write_store.store_path_));
+
+  V4Store read_store(task_runner_, store_path_);
+  EXPECT_EQ(HASH_PREFIX_MAP_GENERATION_FAILURE, read_store.ReadFromDisk());
+  EXPECT_TRUE(read_store.state_.empty());
+  EXPECT_TRUE(read_store.hash_prefix_map_.empty());
+  EXPECT_EQ(0, read_store.file_size_);
+}
+
+TEST_F(V4StoreTest, TestHashPrefixExistsAtTheBeginning) {
+  HashPrefixes hash_prefixes = "abcdebbbbbccccc";
+  HashPrefix hash_prefix = "abcde";
+  EXPECT_TRUE(V4Store::HashPrefixMatches(hash_prefix, hash_prefixes, 5));
+}
+
+TEST_F(V4StoreTest, TestHashPrefixExistsInTheMiddle) {
+  HashPrefixes hash_prefixes = "abcdebbbbbccccc";
+  HashPrefix hash_prefix = "bbbbb";
+  EXPECT_TRUE(V4Store::HashPrefixMatches(hash_prefix, hash_prefixes, 5));
+}
+
+TEST_F(V4StoreTest, TestHashPrefixExistsAtTheEnd) {
+  HashPrefixes hash_prefixes = "abcdebbbbbccccc";
+  HashPrefix hash_prefix = "ccccc";
+  EXPECT_TRUE(V4Store::HashPrefixMatches(hash_prefix, hash_prefixes, 5));
+}
+
+TEST_F(V4StoreTest, TestHashPrefixExistsAtTheBeginningOfEven) {
+  HashPrefixes hash_prefixes = "abcdebbbbb";
+  HashPrefix hash_prefix = "abcde";
+  EXPECT_TRUE(V4Store::HashPrefixMatches(hash_prefix, hash_prefixes, 5));
+}
+
+TEST_F(V4StoreTest, TestHashPrefixExistsAtTheEndOfEven) {
+  HashPrefixes hash_prefixes = "abcdebbbbb";
+  HashPrefix hash_prefix = "bbbbb";
+  EXPECT_TRUE(V4Store::HashPrefixMatches(hash_prefix, hash_prefixes, 5));
+}
+
+TEST_F(V4StoreTest, TestHashPrefixDoesNotExistInConcatenatedList) {
+  HashPrefixes hash_prefixes = "abcdebbbbb";
+  HashPrefix hash_prefix = "bbbbc";
+  EXPECT_FALSE(V4Store::HashPrefixMatches(hash_prefix, hash_prefixes, 5));
+}
+
+TEST_F(V4StoreTest, TestFullHashExistsInMapWithSingleSize) {
+  V4Store store(task_runner_, store_path_);
+  store.hash_prefix_map_[32] =
+      "0111222233334444555566667777888811112222333344445555666677778888";
+  FullHash full_hash = "11112222333344445555666677778888";
+  EXPECT_EQ("11112222333344445555666677778888",
+            store.GetMatchingHashPrefix(full_hash));
+}
+
+TEST_F(V4StoreTest, TestFullHashExistsInMapWithDifferentSizes) {
+  V4Store store(task_runner_, store_path_);
+  store.hash_prefix_map_[4] = "22223333aaaa";
+  store.hash_prefix_map_[32] = "11112222333344445555666677778888";
+  FullHash full_hash = "11112222333344445555666677778888";
+  EXPECT_EQ("11112222333344445555666677778888",
+            store.GetMatchingHashPrefix(full_hash));
+}
+
+TEST_F(V4StoreTest, TestHashPrefixExistsInMapWithSingleSize) {
+  V4Store store(task_runner_, store_path_);
+  store.hash_prefix_map_[4] = "22223333aaaa";
+  FullHash full_hash = "22222222222222222222222222222222";
+  EXPECT_EQ("2222", store.GetMatchingHashPrefix(full_hash));
+}
+
+TEST_F(V4StoreTest, TestHashPrefixExistsInMapWithDifferentSizes) {
+  V4Store store(task_runner_, store_path_);
+  store.hash_prefix_map_[4] = "22223333aaaa";
+  store.hash_prefix_map_[5] = "11111hhhhh";
+  FullHash full_hash = "22222222222222222222222222222222";
+  EXPECT_EQ("2222", store.GetMatchingHashPrefix(full_hash));
+}
+
+TEST_F(V4StoreTest, TestHashPrefixDoesNotExistInMapWithDifferentSizes) {
+  V4Store store(task_runner_, store_path_);
+  store.hash_prefix_map_[4] = "3333aaaa";
+  store.hash_prefix_map_[5] = "11111hhhhh";
+  FullHash full_hash = "22222222222222222222222222222222";
+  EXPECT_TRUE(store.GetMatchingHashPrefix(full_hash).empty());
+}
+
+TEST_F(V4StoreTest, GetMatchingHashPrefixSize32Or21) {
+  HashPrefix prefix = "0123";
+  V4Store store(task_runner_, store_path_);
+  store.hash_prefix_map_[4] = prefix;
+
+  FullHash full_hash_21 = "0123456789ABCDEF01234";
+  EXPECT_EQ(prefix, store.GetMatchingHashPrefix(full_hash_21));
+  FullHash full_hash_32 = "0123456789ABCDEF0123456789ABCDEF";
+  EXPECT_EQ(prefix, store.GetMatchingHashPrefix(full_hash_32));
+#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
+  // This hits a DCHECK so it is release mode only.
+  FullHash full_hash_22 = "0123456789ABCDEF012345";
+  EXPECT_EQ(prefix, store.GetMatchingHashPrefix(full_hash_22));
+#endif
+}
+
+#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
+// This test hits a NOTREACHED so it is a release mode only test.
+TEST_F(V4StoreTest, TestAdditionsWithRiceEncodingFailsWithInvalidInput) {
+  RepeatedPtrField<ThreatEntrySet> additions;
+  ThreatEntrySet* addition = additions.Add();
+  addition->set_compression_type(RICE);
+  addition->mutable_rice_hashes()->set_num_entries(-1);
+  HashPrefixMap additions_map;
+  EXPECT_EQ(RICE_DECODING_FAILURE,
+            V4Store(task_runner_, store_path_)
+                .UpdateHashPrefixMapFromAdditions("V4Metric", additions,
+                                                  &additions_map));
+}
+#endif
+
+TEST_F(V4StoreTest, TestAdditionsWithRiceEncodingSucceeds) {
+  RepeatedPtrField<ThreatEntrySet> additions;
+  ThreatEntrySet* addition = additions.Add();
+  addition->set_compression_type(RICE);
+  RiceDeltaEncoding* rice_hashes = addition->mutable_rice_hashes();
+  rice_hashes->set_first_value(5);
+  rice_hashes->set_num_entries(3);
+  rice_hashes->set_rice_parameter(28);
+  // The following value is hand-crafted by getting inspiration from:
+  // https://goto.google.com/testlargenumbersriceencoded
+  // The value listed at that place fails the "integer overflow" check so I
+  // modified it until the decoder parsed it successfully.
+  rice_hashes->set_encoded_data(
+      "\xbf\xa8\x3f\xfb\xf\xf\x5e\x27\xe6\xc3\x1d\xc6\x38");
+  HashPrefixMap additions_map;
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            V4Store(task_runner_, store_path_)
+                .UpdateHashPrefixMapFromAdditions("V4Metric", additions,
+                                                  &additions_map));
+  EXPECT_EQ(1u, additions_map.size());
+  EXPECT_EQ(std::string("\x5\0\0\0\fL\x93\xADV\x7F\xF6o\xCEo1\x81", 16),
+            additions_map[4]);
+}
+
+TEST_F(V4StoreTest, TestRemovalsWithRiceEncodingSucceeds) {
+  HashPrefixMap prefix_map_old;
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            V4Store::AddUnlumpedHashes(4, "1111abcdefgh", &prefix_map_old));
+  HashPrefixMap prefix_map_additions;
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            V4Store::AddUnlumpedHashes(5, "22222bcdef", &prefix_map_additions));
+
+  V4Store store(task_runner_, store_path_);
+  std::string expected_checksum = std::string(
+      "\xA5\x8B\xCAsD\xC7\xF9\xCE\xD2\xF4\x4="
+      "\xB2\"\x82\x1A\xC1\xB8\x1F\x10\r\v\x9A\x93\xFD\xE1\xB8"
+      "B\x1Eh\xF7\xB4",
+      crypto::kSHA256Length);
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            store.MergeUpdate(prefix_map_old, prefix_map_additions, nullptr,
+                              expected_checksum));
+  EXPECT_FALSE(store.HasValidData());  // Never actually read from disk.
+
+  // At this point, the store map looks like this:
+  // 4: 1111abcdefgh
+  // 5: 22222bcdef
+  // sorted: 1111, 22222, abcd, bcdef, efgh
+  // We'll now try to delete hashes at indexes 0, 3 and 4 in the sorted list.
+
+  std::unique_ptr<ListUpdateResponse> lur(new ListUpdateResponse);
+  lur->set_response_type(ListUpdateResponse::PARTIAL_UPDATE);
+  ThreatEntrySet* removal = lur->add_removals();
+  removal->set_compression_type(RICE);
+  RiceDeltaEncoding* rice_indices = removal->mutable_rice_indices();
+  rice_indices->set_first_value(0);
+  rice_indices->set_num_entries(2);
+  rice_indices->set_rice_parameter(2);
+  rice_indices->set_encoded_data("\x16");
+
+  bool called_back = false;
+  UpdatedStoreReadyCallback store_ready_callback =
+      base::BindOnce(&V4StoreTest::UpdatedStoreReady, base::Unretained(this),
+                     &called_back, true /* expect_store */);
+  EXPECT_FALSE(base::PathExists(store.store_path_));
+  store.ApplyUpdate(std::move(lur), task_runner_,
+                    std::move(store_ready_callback));
+  EXPECT_TRUE(base::PathExists(store.store_path_));
+
+  task_runner_->RunPendingTasks();
+  base::RunLoop().RunUntilIdle();
+
+  // This ensures that the callback was called.
+  EXPECT_TRUE(called_back);
+  // ApplyUpdate was successful, so we have valid data.
+  ASSERT_TRUE(updated_store_);
+  EXPECT_TRUE(updated_store_->HasValidData());
+}
+
+TEST_F(V4StoreTest, TestMergeUpdatesFailsChecksum) {
+  // Proof of checksum mismatch using python:
+  // >>> import hashlib
+  // >>> m = hashlib.sha256()
+  // >>> m.update("2222")
+  // >>> m.digest()
+  // "\xed\xee)\xf8\x82T;\x95f
+  // \xb2m\x0e\xe0\xe7\xe9P9\x9b\x1cB"\xf5\xde\x05\xe0d%\xb4\xc9\x95\xe9"
+
+  HashPrefixMap prefix_map_old;
+  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
+            V4Store::AddUnlumpedHashes(4, "2222", &prefix_map_old));
+  EXPECT_EQ(CHECKSUM_MISMATCH_FAILURE,
+            V4Store(task_runner_, store_path_)
+                .MergeUpdate(prefix_map_old, HashPrefixMap(), nullptr, "aawc"));
+}
+
+TEST_F(V4StoreTest, TestChecksumErrorOnStartup) {
+  // First the case of checksum not matching after reading from disk.
+  ListUpdateResponse list_update_response;
+  list_update_response.set_new_client_state("test_client_state");
+  list_update_response.set_platform_type(LINUX_PLATFORM);
+  list_update_response.set_response_type(ListUpdateResponse::FULL_UPDATE);
+  list_update_response.mutable_checksum()->set_sha256(
+      std::string(crypto::kSHA256Length, 0));
+  WriteFileFormatProtoToFile(0x600D71FE, 9, &list_update_response);
+  V4Store store(task_runner_, store_path_);
+  EXPECT_TRUE(store.expected_checksum_.empty());
+  EXPECT_EQ(READ_SUCCESS, store.ReadFromDisk());
+  EXPECT_TRUE(!store.expected_checksum_.empty());
+  EXPECT_EQ(69, store.file_size_);
+  EXPECT_EQ("test_client_state", store.state());
+
+  EXPECT_FALSE(store.VerifyChecksum());
+
+  // Now the case of checksum matching after reading from disk.
+  // Proof of checksum mismatch using python:
+  // >>> import hashlib
+  // >>> m = hashlib.sha256()
+  // >>> m.update("abcde")
+  // >>> import base64
+  // >>> encoded = base64.b64encode(m.digest())
+  // >>> encoded
+  // 'NrvlDtloQdEEQ7y2cNZVTwo0t2G+Z+ycSorSwMRMpCw='
+  std::string expected_checksum;
+  base::Base64Decode("NrvlDtloQdEEQ7y2cNZVTwo0t2G+Z+ycSorSwMRMpCw=",
+                     &expected_checksum);
+  ThreatEntrySet* additions = list_update_response.add_additions();
+  additions->set_compression_type(RAW);
+  additions->mutable_raw_hashes()->set_prefix_size(5);
+  additions->mutable_raw_hashes()->set_raw_hashes("abcde");
+  list_update_response.mutable_checksum()->set_sha256(expected_checksum);
+  WriteFileFormatProtoToFile(0x600D71FE, 9, &list_update_response);
+  V4Store another_store(task_runner_, store_path_);
+  EXPECT_TRUE(another_store.expected_checksum_.empty());
+
+  EXPECT_EQ(READ_SUCCESS, another_store.ReadFromDisk());
+  EXPECT_TRUE(!another_store.expected_checksum_.empty());
+  EXPECT_EQ("test_client_state", another_store.state());
+  EXPECT_EQ(69, store.file_size_);
+
+  EXPECT_TRUE(another_store.VerifyChecksum());
+}
+
+TEST_F(V4StoreTest, WriteToDiskFails) {
+  // Pass the directory name as file name so that when the code tries to rename
+  // the temp store file to |store_path_| it fails.
+  EXPECT_EQ(UNABLE_TO_RENAME_FAILURE,
+            V4Store(task_runner_, temp_dir_.GetPath()).WriteToDisk(Checksum()));
+
+  // Give a location that isn't writable, even for the tmp file.
+  base::FilePath non_writable_dir =
+      temp_dir_.GetPath()
+          .Append(FILE_PATH_LITERAL("nonexistent_dir"))
+          .Append(FILE_PATH_LITERAL("some.store"));
+  EXPECT_EQ(UNEXPECTED_BYTES_WRITTEN_FAILURE,
+            V4Store(task_runner_, non_writable_dir).WriteToDisk(Checksum()));
+}
+
+TEST_F(V4StoreTest, FullUpdateFailsChecksumSynchronously) {
+  V4Store store(task_runner_, store_path_);
+  bool called_back = false;
+  UpdatedStoreReadyCallback store_ready_callback =
+      base::BindOnce(&V4StoreTest::UpdatedStoreReady, base::Unretained(this),
+                     &called_back, false /* expect_store */);
+  EXPECT_FALSE(base::PathExists(store.store_path_));
+  EXPECT_FALSE(store.HasValidData());  // Never actually read from disk.
+
+  // Now create a response with invalid checksum.
+  std::unique_ptr<ListUpdateResponse> lur(new ListUpdateResponse);
+  lur->set_response_type(ListUpdateResponse::FULL_UPDATE);
+  lur->mutable_checksum()->set_sha256(std::string(crypto::kSHA256Length, 0));
+  store.ApplyUpdate(std::move(lur), task_runner_,
+                    std::move(store_ready_callback));
+  // The update should fail synchronously and not create a store file.
+  EXPECT_FALSE(base::PathExists(store.store_path_));
+
+  // Run everything on the task runner to ensure there are no pending tasks.
+  task_runner_->RunPendingTasks();
+  base::RunLoop().RunUntilIdle();
+
+  // This ensures that the callback was called.
+  EXPECT_TRUE(called_back);
+  // Ensure that the file is still not created.
+  EXPECT_FALSE(base::PathExists(store.store_path_));
+  EXPECT_FALSE(updated_store_);
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/db/v4_test_util.cc b/components/safe_browsing/core/db/v4_test_util.cc
new file mode 100644
index 0000000..00ec5e1
--- /dev/null
+++ b/components/safe_browsing/core/db/v4_test_util.cc
@@ -0,0 +1,148 @@
+// 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 "components/safe_browsing/core/db/v4_test_util.h"
+
+#include <algorithm>
+#include <string>
+#include <utility>
+
+#include "base/strings/strcat.h"
+#include "components/safe_browsing/core/db/util.h"
+#include "crypto/sha2.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+
+namespace safe_browsing {
+
+namespace {
+
+const char kClient[] = "unittest";
+const char kAppVer[] = "1.0";
+const char kKeyParam[] = "test_key_param";
+
+}  // namespace
+
+V4ProtocolConfig GetTestV4ProtocolConfig(bool disable_auto_update) {
+  return V4ProtocolConfig(kClient, disable_auto_update, kKeyParam, kAppVer);
+}
+
+std::ostream& operator<<(std::ostream& os, const ThreatMetadata& meta) {
+  os << "{threat_pattern_type=" << static_cast<int>(meta.threat_pattern_type)
+     << ", api_permissions=[";
+  for (auto p : meta.api_permissions)
+    os << p << ",";
+  os << "subresource_filter_match=[";
+  for (auto t : meta.subresource_filter_match)
+    os << static_cast<int>(t.first) << ":" << static_cast<int>(t.second) << ",";
+  return os << "], population_id=" << meta.population_id << "}";
+}
+
+TestV4Store::TestV4Store(
+    const scoped_refptr<base::SequencedTaskRunner>& task_runner,
+    const base::FilePath& store_path)
+    : V4Store(task_runner, store_path) {}
+
+TestV4Store::~TestV4Store() = default;
+
+bool TestV4Store::HasValidData() const {
+  return true;
+}
+
+void TestV4Store::MarkPrefixAsBad(HashPrefix prefix) {
+  auto& vec = mock_prefixes_[prefix.size()];
+  vec.insert(std::upper_bound(vec.begin(), vec.end(), prefix), prefix);
+  hash_prefix_map_[prefix.size()] = base::StrCat(vec);
+}
+
+void TestV4Store::SetPrefixes(std::vector<HashPrefix> prefixes,
+                              PrefixSize size) {
+  std::sort(prefixes.begin(), prefixes.end());
+  mock_prefixes_[size] = prefixes;
+  hash_prefix_map_[size] = base::StrCat(prefixes);
+}
+
+TestV4Database::TestV4Database(
+    const scoped_refptr<base::SequencedTaskRunner>& db_task_runner,
+    std::unique_ptr<StoreMap> store_map)
+    : V4Database(db_task_runner, std::move(store_map)) {}
+
+void TestV4Database::MarkPrefixAsBad(ListIdentifier list_id,
+                                     HashPrefix prefix) {
+  V4Store* base_store = store_map_->at(list_id).get();
+  TestV4Store* test_store = static_cast<TestV4Store*>(base_store);
+  test_store->MarkPrefixAsBad(prefix);
+}
+
+TestV4StoreFactory::TestV4StoreFactory() = default;
+
+TestV4StoreFactory::~TestV4StoreFactory() = default;
+
+std::unique_ptr<V4Store> TestV4StoreFactory::CreateV4Store(
+    const scoped_refptr<base::SequencedTaskRunner>& task_runner,
+    const base::FilePath& store_path) {
+  auto new_store = std::make_unique<TestV4Store>(task_runner, store_path);
+  new_store->Initialize();
+  return std::move(new_store);
+}
+
+TestV4DatabaseFactory::TestV4DatabaseFactory() = default;
+
+TestV4DatabaseFactory::~TestV4DatabaseFactory() = default;
+
+std::unique_ptr<V4Database> TestV4DatabaseFactory::Create(
+    const scoped_refptr<base::SequencedTaskRunner>& db_task_runner,
+    std::unique_ptr<StoreMap> store_map) {
+  auto v4_db =
+      std::make_unique<TestV4Database>(db_task_runner, std::move(store_map));
+  v4_db_ = v4_db.get();
+  return std::move(v4_db);
+}
+
+void TestV4DatabaseFactory::MarkPrefixAsBad(ListIdentifier list_id,
+                                            HashPrefix prefix) {
+  v4_db_->MarkPrefixAsBad(list_id, prefix);
+}
+
+TestV4GetHashProtocolManager::TestV4GetHashProtocolManager(
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    const StoresToCheck& stores_to_check,
+    const V4ProtocolConfig& config)
+    : V4GetHashProtocolManager(url_loader_factory, stores_to_check, config) {}
+
+void TestV4GetHashProtocolManager::AddToFullHashCache(FullHashInfo fhi) {
+  full_hash_cache_[fhi.full_hash].full_hash_infos.push_back(fhi);
+}
+
+TestV4GetHashProtocolManagerFactory::TestV4GetHashProtocolManagerFactory() =
+    default;
+
+TestV4GetHashProtocolManagerFactory::~TestV4GetHashProtocolManagerFactory() =
+    default;
+
+std::unique_ptr<V4GetHashProtocolManager>
+TestV4GetHashProtocolManagerFactory::CreateProtocolManager(
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    const StoresToCheck& stores_to_check,
+    const V4ProtocolConfig& config) {
+  auto pm = std::make_unique<TestV4GetHashProtocolManager>(
+      url_loader_factory, stores_to_check, config);
+  pm_ = pm.get();
+  return std::move(pm);
+}
+
+FullHashInfo GetFullHashInfo(const GURL& url, const ListIdentifier& list_id) {
+  return FullHashInfo(V4ProtocolManagerUtil::GetFullHash(url), list_id,
+                      base::Time::Now() + base::TimeDelta::FromMinutes(5));
+}
+
+FullHashInfo GetFullHashInfoWithMetadata(
+    const GURL& url,
+    const ListIdentifier& list_id,
+    const ThreatMetadata& threat_metadata) {
+  FullHashInfo fhi = GetFullHashInfo(url, list_id);
+  fhi.metadata = threat_metadata;
+  return fhi;
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/db/v4_test_util.h b/components/safe_browsing/core/db/v4_test_util.h
new file mode 100644
index 0000000..d59145c
--- /dev/null
+++ b/components/safe_browsing/core/db/v4_test_util.h
@@ -0,0 +1,124 @@
+// 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 COMPONENTS_SAFE_BROWSING_CORE_DB_V4_TEST_UTIL_H_
+#define COMPONENTS_SAFE_BROWSING_CORE_DB_V4_TEST_UTIL_H_
+
+// Contains classes and methods useful for tests.
+
+#include <map>
+#include <memory>
+#include <ostream>
+#include <vector>
+
+#include "components/safe_browsing/core/db/v4_database.h"
+#include "components/safe_browsing/core/db/v4_get_hash_protocol_manager.h"
+
+namespace safe_browsing {
+
+struct ThreatMetadata;
+struct V4ProtocolConfig;
+
+V4ProtocolConfig GetTestV4ProtocolConfig(bool disable_auto_update = false);
+
+std::ostream& operator<<(std::ostream& os, const ThreatMetadata& meta);
+
+class TestV4Store : public V4Store {
+ public:
+  TestV4Store(const scoped_refptr<base::SequencedTaskRunner>& task_runner,
+              const base::FilePath& store_path);
+  ~TestV4Store() override;
+
+  bool HasValidData() const override;
+
+  void MarkPrefixAsBad(HashPrefix prefix);
+
+  // |prefixes| does not need to be sorted.
+  void SetPrefixes(std::vector<HashPrefix> prefixes, PrefixSize size);
+
+ private:
+  // Holds mock prefixes from calls to MarkPrefixAsBad / SetPrefixes. Stored as
+  // a vector for simplicity.
+  std::map<PrefixSize, std::vector<HashPrefix>> mock_prefixes_;
+};
+
+class TestV4StoreFactory : public V4StoreFactory {
+ public:
+  TestV4StoreFactory();
+  ~TestV4StoreFactory() override;
+
+  std::unique_ptr<V4Store> CreateV4Store(
+      const scoped_refptr<base::SequencedTaskRunner>& task_runner,
+      const base::FilePath& store_path) override;
+};
+
+class TestV4Database : public V4Database {
+ public:
+  TestV4Database(const scoped_refptr<base::SequencedTaskRunner>& db_task_runner,
+                 std::unique_ptr<StoreMap> store_map);
+
+  void MarkPrefixAsBad(ListIdentifier list_id, HashPrefix prefix);
+};
+
+class TestV4DatabaseFactory : public V4DatabaseFactory {
+ public:
+  TestV4DatabaseFactory();
+  ~TestV4DatabaseFactory() override;
+
+  std::unique_ptr<V4Database> Create(
+      const scoped_refptr<base::SequencedTaskRunner>& db_task_runner,
+      std::unique_ptr<StoreMap> store_map) override;
+
+  void MarkPrefixAsBad(ListIdentifier list_id, HashPrefix prefix);
+
+ private:
+  // Owned by V4LocalDatabaseManager. The following usage is expected: each
+  // test in the test fixture instantiates a new SafebrowsingService instance,
+  // which instantiates a new V4LocalDatabaseManager, which instantiates a new
+  // V4Database using this method so use-after-free isn't possible.
+  TestV4Database* v4_db_ = nullptr;
+};
+
+class TestV4GetHashProtocolManager : public V4GetHashProtocolManager {
+ public:
+  TestV4GetHashProtocolManager(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      const StoresToCheck& stores_to_check,
+      const V4ProtocolConfig& config);
+
+  void AddToFullHashCache(FullHashInfo fhi);
+};
+
+class TestV4GetHashProtocolManagerFactory
+    : public V4GetHashProtocolManagerFactory {
+ public:
+  TestV4GetHashProtocolManagerFactory();
+  ~TestV4GetHashProtocolManagerFactory() override;
+
+  std::unique_ptr<V4GetHashProtocolManager> CreateProtocolManager(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      const StoresToCheck& stores_to_check,
+      const V4ProtocolConfig& config) override;
+
+  void AddToFullHashCache(FullHashInfo fhi) { pm_->AddToFullHashCache(fhi); }
+
+ private:
+  // Owned by the SafeBrowsingService.
+  TestV4GetHashProtocolManager* pm_ = nullptr;
+};
+
+// Returns FullHashInfo object for the basic host+path pattern for a given URL
+// after canonicalization.
+FullHashInfo GetFullHashInfo(const GURL& url, const ListIdentifier& list_id);
+
+// Returns a FullHashInfo info for the basic host+path pattern for a given URL
+// after canonicalization. Also adds metadata information to the FullHashInfo
+// object.
+FullHashInfo GetFullHashInfoWithMetadata(const GURL& url,
+                                         const ListIdentifier& list_id,
+                                         const ThreatMetadata& threat_metadata);
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CORE_DB_V4_TEST_UTIL_H_
diff --git a/components/safe_browsing/core/db/v4_update_protocol_manager.cc b/components/safe_browsing/core/db/v4_update_protocol_manager.cc
new file mode 100644
index 0000000..e4a6f6a
--- /dev/null
+++ b/components/safe_browsing/core/db/v4_update_protocol_manager.cc
@@ -0,0 +1,452 @@
+// 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 "components/safe_browsing/core/db/v4_update_protocol_manager.h"
+
+#include <utility>
+
+#include "base/base64url.h"
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/rand_util.h"
+#include "base/timer/timer.h"
+#include "components/safe_browsing/buildflags.h"
+#include "components/safe_browsing/core/db/safebrowsing.pb.h"
+#include "net/base/load_flags.h"
+#include "net/http/http_response_headers.h"
+#include "net/http/http_status_code.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/simple_url_loader.h"
+
+using base::Time;
+using base::TimeDelta;
+
+namespace {
+
+// Enumerate parsing failures for histogramming purposes.  DO NOT CHANGE
+// THE ORDERING OF THESE VALUES.
+enum ParseResultType {
+  // Error parsing the protocol buffer from a string.
+  PARSE_FROM_STRING_ERROR = 0,
+
+  // No platform_type set in the response.
+  NO_PLATFORM_TYPE_ERROR = 1,
+
+  // No threat_entry_type set in the response.
+  NO_THREAT_ENTRY_TYPE_ERROR = 2,
+
+  // No threat_type set in the response.
+  NO_THREAT_TYPE_ERROR = 3,
+
+  // No state set in the response for one or more lists.
+  NO_STATE_ERROR = 4,
+
+  // Memory space for histograms is determined by the max.  ALWAYS
+  // ADD NEW VALUES BEFORE THIS ONE.
+  PARSE_RESULT_TYPE_MAX = 5
+};
+
+// Record parsing errors of an update result.
+void RecordParseUpdateResult(ParseResultType result_type) {
+  UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.V4Update.Parse.Result", result_type,
+                            PARSE_RESULT_TYPE_MAX);
+}
+
+void RecordUpdateResult(safe_browsing::V4OperationResult result) {
+  UMA_HISTOGRAM_ENUMERATION(
+      "SafeBrowsing.V4Update.Result", result,
+      safe_browsing::V4OperationResult::OPERATION_RESULT_MAX);
+}
+
+}  // namespace
+
+namespace safe_browsing {
+
+// Minimum time, in seconds, from start up before we must issue an update query.
+static const int kV4TimerStartIntervalSecMin = 60;
+
+// Maximum time, in seconds, from start up before we must issue an update query.
+static const int kV4TimerStartIntervalSecMax = 300;
+
+// Maximum time, in seconds, to wait for a response to an update request.
+static const int kV4TimerUpdateWaitSecMax = 15 * 60;  // 15 minutes
+
+ChromeClientInfo::SafeBrowsingReportingPopulation GetReportingLevelProtoValue(
+    ExtendedReportingLevel reporting_level) {
+  switch (reporting_level) {
+    case SBER_LEVEL_OFF:
+      return ChromeClientInfo::OPT_OUT;
+    case SBER_LEVEL_LEGACY:
+      return ChromeClientInfo::EXTENDED;
+    case SBER_LEVEL_SCOUT:
+      return ChromeClientInfo::SCOUT;
+    default:
+      NOTREACHED() << "Unexpected reporting_level!";
+      return ChromeClientInfo::UNSPECIFIED;
+  }
+}
+
+// The default V4UpdateProtocolManagerFactory.
+class V4UpdateProtocolManagerFactoryImpl
+    : public V4UpdateProtocolManagerFactory {
+ public:
+  V4UpdateProtocolManagerFactoryImpl() {}
+  ~V4UpdateProtocolManagerFactoryImpl() override {}
+  std::unique_ptr<V4UpdateProtocolManager> CreateProtocolManager(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      const V4ProtocolConfig& config,
+      V4UpdateCallback update_callback,
+      ExtendedReportingLevelCallback extended_reporting_level_callback)
+      override {
+    return base::WrapUnique(
+        new V4UpdateProtocolManager(url_loader_factory, config, update_callback,
+                                    extended_reporting_level_callback));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(V4UpdateProtocolManagerFactoryImpl);
+};
+
+// V4UpdateProtocolManager implementation --------------------------------
+
+// static
+V4UpdateProtocolManagerFactory* V4UpdateProtocolManager::factory_ = nullptr;
+
+// static
+std::unique_ptr<V4UpdateProtocolManager> V4UpdateProtocolManager::Create(
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    const V4ProtocolConfig& config,
+    V4UpdateCallback update_callback,
+    ExtendedReportingLevelCallback extended_reporting_level_callback) {
+  if (!factory_) {
+    factory_ = new V4UpdateProtocolManagerFactoryImpl();
+  }
+  return factory_->CreateProtocolManager(url_loader_factory, config,
+                                         update_callback,
+                                         extended_reporting_level_callback);
+}
+
+void V4UpdateProtocolManager::ResetUpdateErrors() {
+  update_error_count_ = 0;
+  update_back_off_mult_ = 1;
+}
+
+V4UpdateProtocolManager::V4UpdateProtocolManager(
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    const V4ProtocolConfig& config,
+    V4UpdateCallback update_callback,
+    ExtendedReportingLevelCallback extended_reporting_level_callback)
+    : update_error_count_(0),
+      update_back_off_mult_(1),
+      next_update_interval_(base::TimeDelta::FromSeconds(
+          base::RandInt(kV4TimerStartIntervalSecMin,
+                        kV4TimerStartIntervalSecMax))),
+      config_(config),
+      url_loader_factory_(url_loader_factory),
+      update_callback_(update_callback),
+      extended_reporting_level_callback_(extended_reporting_level_callback) {
+  // Do not auto-schedule updates. Let the owner (V4LocalDatabaseManager) do it
+  // when it is ready to process updates.
+}
+
+V4UpdateProtocolManager::~V4UpdateProtocolManager() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+bool V4UpdateProtocolManager::IsUpdateScheduled() const {
+  return update_timer_.IsRunning();
+}
+
+void V4UpdateProtocolManager::ScheduleNextUpdate(
+    std::unique_ptr<StoreStateMap> store_state_map) {
+  store_state_map_ = std::move(store_state_map);
+  ScheduleNextUpdateWithBackoff(false);
+}
+
+void V4UpdateProtocolManager::ScheduleNextUpdateWithBackoff(bool back_off) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (config_.disable_auto_update) {
+    DCHECK(!IsUpdateScheduled());
+    return;
+  }
+
+  // Reschedule with the new update.
+  base::TimeDelta next_update_interval = GetNextUpdateInterval(back_off);
+  ScheduleNextUpdateAfterInterval(next_update_interval);
+}
+
+// According to section 5 of the SafeBrowsing protocol specification, we must
+// back off after a certain number of errors.
+base::TimeDelta V4UpdateProtocolManager::GetNextUpdateInterval(bool back_off) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(next_update_interval_ > base::TimeDelta());
+
+  base::TimeDelta next = next_update_interval_;
+  if (back_off) {
+    next = V4ProtocolManagerUtil::GetNextBackOffInterval(
+        &update_error_count_, &update_back_off_mult_);
+  }
+
+  if (!last_response_time_.is_null()) {
+    // The callback spent some time updating the database, including disk I/O.
+    // Do not wait that extra time.
+    base::TimeDelta callback_time = Time::Now() - last_response_time_;
+    if (callback_time < next) {
+      next -= callback_time;
+    } else {
+      // If the callback took too long, schedule the next update with no delay.
+      next = base::TimeDelta();
+    }
+  }
+  DVLOG(1) << "V4UpdateProtocolManager::GetNextUpdateInterval: "
+           << "next_interval: " << next;
+  return next;
+}
+
+void V4UpdateProtocolManager::ScheduleNextUpdateAfterInterval(
+    base::TimeDelta interval) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(interval >= base::TimeDelta());
+
+  // Unschedule any current timer.
+  update_timer_.Stop();
+  update_timer_.Start(FROM_HERE, interval, this,
+                      &V4UpdateProtocolManager::IssueUpdateRequest);
+}
+
+std::string V4UpdateProtocolManager::GetBase64SerializedUpdateRequestProto() {
+  DCHECK(!store_state_map_->empty());
+  // Build the request. Client info and client states are not added to the
+  // request protocol buffer. Client info is passed as params in the url.
+  FetchThreatListUpdatesRequest request;
+  for (const auto& entry : *store_state_map_) {
+    const auto& list_to_update = entry.first;
+    const auto& state = entry.second;
+    ListUpdateRequest* list_update_request = request.add_list_update_requests();
+    list_update_request->set_platform_type(list_to_update.platform_type());
+    list_update_request->set_threat_entry_type(
+        list_to_update.threat_entry_type());
+    list_update_request->set_threat_type(list_to_update.threat_type());
+
+    if (!state.empty()) {
+      list_update_request->set_state(state);
+    }
+
+    list_update_request->mutable_constraints()->add_supported_compressions(RAW);
+    list_update_request->mutable_constraints()->add_supported_compressions(
+        RICE);
+  }
+
+  if (!extended_reporting_level_callback_.is_null()) {
+    request.mutable_chrome_client_info()->set_reporting_population(
+        GetReportingLevelProtoValue(extended_reporting_level_callback_.Run()));
+  }
+
+  V4ProtocolManagerUtil::SetClientInfoFromConfig(request.mutable_client(),
+                                                 config_);
+
+  // Serialize and Base64 encode.
+  std::string req_data, req_base64;
+  request.SerializeToString(&req_data);
+  base::Base64UrlEncode(req_data, base::Base64UrlEncodePolicy::INCLUDE_PADDING,
+                        &req_base64);
+  return req_base64;
+}
+
+bool V4UpdateProtocolManager::ParseUpdateResponse(
+    const std::string& data,
+    ParsedServerResponse* parsed_server_response) {
+  FetchThreatListUpdatesResponse response;
+
+  if (!response.ParseFromString(data)) {
+    RecordParseUpdateResult(PARSE_FROM_STRING_ERROR);
+    return false;
+  }
+
+  if (response.has_minimum_wait_duration()) {
+    // Seconds resolution is good enough so we ignore the nanos field.
+    int64_t minimum_wait_duration_seconds =
+        response.minimum_wait_duration().seconds();
+
+    // Do not let the next_update_interval_ to be too low.
+    if (minimum_wait_duration_seconds < kV4TimerStartIntervalSecMin) {
+      minimum_wait_duration_seconds = kV4TimerStartIntervalSecMin;
+    }
+    next_update_interval_ =
+        base::TimeDelta::FromSeconds(minimum_wait_duration_seconds);
+  }
+
+  for (ListUpdateResponse& list_update_response :
+       *response.mutable_list_update_responses()) {
+    if (!list_update_response.has_platform_type()) {
+      RecordParseUpdateResult(NO_PLATFORM_TYPE_ERROR);
+    } else if (!list_update_response.has_threat_entry_type()) {
+      RecordParseUpdateResult(NO_THREAT_ENTRY_TYPE_ERROR);
+    } else if (!list_update_response.has_threat_type()) {
+      RecordParseUpdateResult(NO_THREAT_TYPE_ERROR);
+    } else if (!list_update_response.has_new_client_state()) {
+      RecordParseUpdateResult(NO_STATE_ERROR);
+    } else {
+      std::unique_ptr<ListUpdateResponse> add(new ListUpdateResponse);
+      add->Swap(&list_update_response);
+      parsed_server_response->push_back(std::move(add));
+    }
+  }
+  return true;
+}
+
+void V4UpdateProtocolManager::IssueUpdateRequest() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // If an update request is already pending, record and return silently.
+  if (request_) {
+    RecordUpdateResult(V4OperationResult::ALREADY_PENDING_ERROR);
+    return;
+  }
+
+  net::NetworkTrafficAnnotationTag traffic_annotation =
+      net::DefineNetworkTrafficAnnotation("safe_browsing_v4_update", R"(
+        semantics {
+          sender: "Safe Browsing"
+          description:
+            "Safe Browsing issues a request to Google every 30 minutes or so "
+            "to get the latest database of hashes of bad URLs."
+          trigger:
+            "On a timer, approximately every 30 minutes."
+          data:
+             "The state of the local DB is sent so the server can send just "
+             "the changes. This doesn't include any user data."
+          destination: GOOGLE_OWNED_SERVICE
+        }
+        policy {
+          cookies_allowed: YES
+          cookies_store: "Safe Browsing cookie store"
+          setting:
+            "Users can disable Safe Browsing by unchecking 'Protect you and "
+            "your device from dangerous sites' in Chromium settings under "
+            "Privacy. The feature is enabled by default."
+          chrome_policy {
+            SafeBrowsingEnabled {
+              policy_options {mode: MANDATORY}
+              SafeBrowsingEnabled: false
+            }
+          }
+        })");
+  auto resource_request = std::make_unique<network::ResourceRequest>();
+  std::string req_base64 = GetBase64SerializedUpdateRequestProto();
+  GetUpdateUrlAndHeaders(req_base64, &resource_request->url,
+                         &resource_request->headers);
+  resource_request->load_flags = net::LOAD_DISABLE_CACHE;
+  std::unique_ptr<network::SimpleURLLoader> loader =
+      network::SimpleURLLoader::Create(std::move(resource_request),
+                                       traffic_annotation);
+  loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+      url_loader_factory_.get(),
+      base::BindOnce(&V4UpdateProtocolManager::OnURLLoaderComplete,
+                     base::Unretained(this)));
+
+  request_ = std::move(loader);
+
+  // Begin the update request timeout.
+  timeout_timer_.Start(FROM_HERE,
+                       TimeDelta::FromSeconds(kV4TimerUpdateWaitSecMax), this,
+                       &V4UpdateProtocolManager::HandleTimeout);
+}
+
+void V4UpdateProtocolManager::HandleTimeout() {
+  request_.reset();
+  ScheduleNextUpdateWithBackoff(true);
+}
+
+// SafeBrowsing request responses are handled here.
+void V4UpdateProtocolManager::OnURLLoaderComplete(
+    std::unique_ptr<std::string> response_body) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  int response_code = 0;
+  if (request_->ResponseInfo() && request_->ResponseInfo()->headers)
+    response_code = request_->ResponseInfo()->headers->response_code();
+
+  std::string data;
+  if (response_body)
+    data = *response_body;
+
+  OnURLLoaderCompleteInternal(request_->NetError(), response_code, data);
+}
+
+void V4UpdateProtocolManager::OnURLLoaderCompleteInternal(
+    int net_error,
+    int response_code,
+    const std::string& data) {
+  timeout_timer_.Stop();
+
+  last_response_code_ = response_code;
+  V4ProtocolManagerUtil::RecordHttpResponseOrErrorCode(
+      "SafeBrowsing.V4Update.Network.Result", net_error, last_response_code_);
+
+  last_response_time_ = Time::Now();
+
+  std::unique_ptr<ParsedServerResponse> parsed_server_response(
+      new ParsedServerResponse);
+  if (net_error == net::OK && last_response_code_ == net::HTTP_OK) {
+    RecordUpdateResult(V4OperationResult::STATUS_200);
+    ResetUpdateErrors();
+    if (!ParseUpdateResponse(data, parsed_server_response.get())) {
+      parsed_server_response->clear();
+      RecordUpdateResult(V4OperationResult::PARSE_ERROR);
+    }
+    request_.reset();
+
+    UMA_HISTOGRAM_COUNTS_1M("SafeBrowsing.V4Update.ResponseSizeKB",
+                            data.size() / 1024);
+
+    // The caller should update its state now, based on parsed_server_response.
+    // The callback must call ScheduleNextUpdate() at the end to resume
+    // downloading updates.
+    update_callback_.Run(std::move(parsed_server_response));
+  } else {
+    DVLOG(1) << "SafeBrowsing GetEncodedUpdates request for: "
+             << request_->GetFinalURL() << " failed with error: " << net_error
+             << " and response code: " << last_response_code_;
+
+    if (net_error != net::OK) {
+      RecordUpdateResult(V4OperationResult::NETWORK_ERROR);
+    } else {
+      RecordUpdateResult(V4OperationResult::HTTP_ERROR);
+    }
+    // TODO(vakh): Figure out whether it is just a network error vs backoff vs
+    // another condition and RecordUpdateResult more accurately.
+
+    request_.reset();
+    ScheduleNextUpdateWithBackoff(true);
+  }
+}
+
+void V4UpdateProtocolManager::GetUpdateUrlAndHeaders(
+    const std::string& req_base64,
+    GURL* gurl,
+    net::HttpRequestHeaders* headers) const {
+  V4ProtocolManagerUtil::GetRequestUrlAndHeaders(
+      req_base64, "threatListUpdates:fetch", config_, gurl, headers);
+}
+
+void V4UpdateProtocolManager::CollectUpdateInfo(
+    DatabaseManagerInfo::UpdateInfo* update_info) {
+  if (last_response_code_)
+    update_info->set_network_status_code(last_response_code_);
+
+  if (last_response_time_.ToJavaTime()) {
+    update_info->set_last_update_time_millis(last_response_time_.ToJavaTime());
+
+    // We should only find the next update if the last_response is valid.
+    base::Time next_update = last_response_time_ + next_update_interval_;
+    if (next_update.ToJavaTime())
+      update_info->set_next_update_time_millis(next_update.ToJavaTime());
+  }
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/db/v4_update_protocol_manager.h b/components/safe_browsing/core/db/v4_update_protocol_manager.h
new file mode 100644
index 0000000..e1cc1bc7
--- /dev/null
+++ b/components/safe_browsing/core/db/v4_update_protocol_manager.h
@@ -0,0 +1,223 @@
+// 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 COMPONENTS_SAFE_BROWSING_CORE_DB_V4_UPDATE_PROTOCOL_MANAGER_H_
+#define COMPONENTS_SAFE_BROWSING_CORE_DB_V4_UPDATE_PROTOCOL_MANAGER_H_
+
+// A class that implements Chrome's interface with the SafeBrowsing V4 update
+// protocol.
+//
+// The V4UpdateProtocolManager handles formatting and making requests of, and
+// handling responses from, Google's SafeBrowsing servers. The purpose of this
+// class is to get hash prefixes from the SB server for the given set of lists.
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/sequence_checker.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/db/safebrowsing.pb.h"
+#include "components/safe_browsing/core/db/util.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/core/proto/webui.pb.h"
+
+class GURL;
+
+namespace network {
+class SimpleURLLoader;
+class SharedURLLoaderFactory;
+}  // namespace network
+
+namespace safe_browsing {
+
+class V4UpdateProtocolManagerFactory;
+
+// V4UpdateCallback is invoked when a scheduled update completes.
+// Parameters:
+//   - The vector of update response protobufs received from the server for
+//     each list type.
+using V4UpdateCallback =
+    base::RepeatingCallback<void(std::unique_ptr<ParsedServerResponse>)>;
+
+using ExtendedReportingLevelCallback =
+    base::RepeatingCallback<ExtendedReportingLevel()>;
+
+class V4UpdateProtocolManager {
+ public:
+  ~V4UpdateProtocolManager();
+
+  // Makes the passed |factory| the factory used to instantiate
+  // a V4UpdateProtocolManager. Useful for tests.
+  static void RegisterFactory(V4UpdateProtocolManagerFactory* factory) {
+    factory_ = factory;
+  }
+
+  // Create an instance of the safe browsing v4 protocol manager.
+  static std::unique_ptr<V4UpdateProtocolManager> Create(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      const V4ProtocolConfig& config,
+      V4UpdateCallback update_callback,
+      ExtendedReportingLevelCallback extended_reporting_level_callback);
+
+  void OnURLLoaderComplete(std::unique_ptr<std::string> response_body);
+
+  // Schedule the next update without backoff.
+  void ScheduleNextUpdate(std::unique_ptr<StoreStateMap> store_state_map);
+
+  // Populates the UpdateInfo message.
+  void CollectUpdateInfo(DatabaseManagerInfo::UpdateInfo* database_info);
+
+ protected:
+  // Constructs a V4UpdateProtocolManager that issues network requests using
+  // |url_loader_factory|. It schedules updates to get the hash prefixes for
+  // SafeBrowsing lists, and invoke |callback| when the results are retrieved.
+  // The callback may be invoked synchronously.
+  V4UpdateProtocolManager(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      const V4ProtocolConfig& config,
+      V4UpdateCallback update_callback,
+      ExtendedReportingLevelCallback extended_reporting_level_callback);
+
+ private:
+  FRIEND_TEST_ALL_PREFIXES(V4UpdateProtocolManagerTest,
+                           TestGetUpdatesErrorHandlingNetwork);
+  FRIEND_TEST_ALL_PREFIXES(V4UpdateProtocolManagerTest,
+                           TestGetUpdatesErrorHandlingResponseCode);
+  FRIEND_TEST_ALL_PREFIXES(V4UpdateProtocolManagerTest, TestGetUpdatesNoError);
+  FRIEND_TEST_ALL_PREFIXES(V4UpdateProtocolManagerTest,
+                           TestGetUpdatesWithOneBackoff);
+  FRIEND_TEST_ALL_PREFIXES(V4UpdateProtocolManagerTest,
+                           TestBase64EncodingUsesUrlEncoding);
+  FRIEND_TEST_ALL_PREFIXES(V4UpdateProtocolManagerTest, TestDisableAutoUpdates);
+  FRIEND_TEST_ALL_PREFIXES(V4UpdateProtocolManagerTest,
+                           TestGetUpdatesHasTimeout);
+  FRIEND_TEST_ALL_PREFIXES(V4UpdateProtocolManagerTest,
+                           TestExtendedReportingLevelIncluded);
+  friend class V4UpdateProtocolManagerFactoryImpl;
+
+  void OnURLLoaderCompleteInternal(int net_error,
+                                   int response_code,
+                                   const std::string& data);
+
+  // Fills a FetchThreatListUpdatesRequest protocol buffer for a request.
+  // Returns the serialized and base64 URL encoded request as a string.
+  std::string GetBase64SerializedUpdateRequestProto();
+
+  // Records the network response code of the last update
+  int last_response_code_ = 0;
+
+  // The method to populate |gurl| with the URL to be sent to the server.
+  // |request_base64| is the base64 encoded form of an instance of the protobuf
+  // FetchThreatListUpdatesRequest. Also sets the appropriate header values for
+  // sending PVer4 requests in |headers|.
+  void GetUpdateUrlAndHeaders(const std::string& request_base64,
+                              GURL* gurl,
+                              net::HttpRequestHeaders* headers) const;
+
+  // Parses the base64 encoded response received from the server as a
+  // FetchThreatListUpdatesResponse protobuf and returns each of the
+  // ListUpdateResponse protobufs contained in it as a vector.
+  // Returns true if parsing is successful, false otherwise.
+  bool ParseUpdateResponse(const std::string& data_base64,
+                           ParsedServerResponse* parsed_server_response);
+
+  // Resets the update error counter and multiplier.
+  void ResetUpdateErrors();
+
+  // Called when update request times out. Cancels the existing request and
+  // re-sends the update request.
+  void HandleTimeout();
+
+  // Updates internal update and backoff state for each update response error,
+  // assuming that the current time is |now|.
+  void HandleUpdateError(const base::Time& now);
+
+  // Generates the URL for the update request and issues the request for the
+  // lists passed to the constructor.
+  void IssueUpdateRequest();
+
+  // Returns whether another update is currently scheduled.
+  bool IsUpdateScheduled() const;
+
+  // Schedule the next update with backoff specified.
+  void ScheduleNextUpdateWithBackoff(bool back_off);
+
+  // Schedule the next update, after the given interval.
+  void ScheduleNextUpdateAfterInterval(base::TimeDelta interval);
+
+  // Get the next update interval, considering whether we are in backoff.
+  base::TimeDelta GetNextUpdateInterval(bool back_off);
+
+  // The factory that controls the creation of V4UpdateProtocolManager.
+  // This is used by tests.
+  static V4UpdateProtocolManagerFactory* factory_;
+
+  // The last known state of the lists.
+  // Updated after every successful update of the database.
+  std::unique_ptr<StoreStateMap> store_state_map_;
+
+  // The number of HTTP response errors since the the last successful HTTP
+  // response, used for request backoff timing.
+  size_t update_error_count_;
+
+  // Multiplier for the backoff error after the second.
+  size_t update_back_off_mult_;
+
+  // The time delta after which the next update request may be sent.
+  // It is set to a random interval between 60 and 300 seconds at start.
+  // The server can set it by setting the minimum_wait_duration.
+  base::TimeDelta next_update_interval_;
+
+  // The config of the client making Pver4 requests.
+  const V4ProtocolConfig config_;
+
+  // The URLLoaderFactory we use to issue network requests.
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
+
+  // The callback that's called when GetUpdates completes.
+  V4UpdateCallback update_callback_;
+
+  // The pending update request. The request must be canceled when the object is
+  // destroyed.
+  std::unique_ptr<network::SimpleURLLoader> request_;
+
+  // Timer to setup the next update request.
+  base::OneShotTimer update_timer_;
+
+  base::Time last_response_time_;
+
+  // Used to interrupt and re-schedule update requests that take too long to
+  // complete.
+  base::OneShotTimer timeout_timer_;
+
+  ExtendedReportingLevelCallback extended_reporting_level_callback_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  DISALLOW_COPY_AND_ASSIGN(V4UpdateProtocolManager);
+};
+
+// Interface of a factory to create V4UpdateProtocolManager.  Useful for tests.
+class V4UpdateProtocolManagerFactory {
+ public:
+  V4UpdateProtocolManagerFactory() {}
+  virtual ~V4UpdateProtocolManagerFactory() {}
+  virtual std::unique_ptr<V4UpdateProtocolManager> CreateProtocolManager(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      const V4ProtocolConfig& config,
+      V4UpdateCallback update_callback,
+      ExtendedReportingLevelCallback extended_reporting_level_callback) = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(V4UpdateProtocolManagerFactory);
+};
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CORE_DB_V4_UPDATE_PROTOCOL_MANAGER_H_
diff --git a/components/safe_browsing/core/db/v4_update_protocol_manager_unittest.cc b/components/safe_browsing/core/db/v4_update_protocol_manager_unittest.cc
new file mode 100644
index 0000000..0e14462
--- /dev/null
+++ b/components/safe_browsing/core/db/v4_update_protocol_manager_unittest.cc
@@ -0,0 +1,389 @@
+// 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 "components/safe_browsing/core/db/v4_update_protocol_manager.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/base64.h"
+#include "base/bind.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "components/safe_browsing/buildflags.h"
+#include "components/safe_browsing/core/db/safebrowsing.pb.h"
+#include "components/safe_browsing/core/db/util.h"
+#include "components/safe_browsing/core/db/v4_test_util.h"
+#include "net/base/escape.h"
+#include "net/base/load_flags.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_status_code.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+#if !BUILDFLAG(FULL_SAFE_BROWSING)
+#include "base/system/sys_info.h"
+#endif
+
+using base::Time;
+using base::TimeDelta;
+
+namespace safe_browsing {
+
+class V4UpdateProtocolManagerTest : public PlatformTest {
+  void SetUp() override {
+    PlatformTest::SetUp();
+
+    SetupStoreStates();
+    test_shared_loader_factory_ =
+        base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+            &test_url_loader_factory_);
+  }
+
+ protected:
+  void ValidateGetUpdatesResults(
+      const std::vector<ListUpdateResponse>& expected_lurs,
+      std::unique_ptr<ParsedServerResponse> parsed_server_response) {
+    // The callback should never be called if expect_callback_to_be_called_ is
+    // false.
+    EXPECT_TRUE(expect_callback_to_be_called_);
+    ASSERT_EQ(expected_lurs.size(), parsed_server_response->size());
+
+    for (unsigned int i = 0; i < parsed_server_response->size(); ++i) {
+      const ListUpdateResponse& expected = expected_lurs[i];
+      const std::unique_ptr<ListUpdateResponse>& actual =
+          (*parsed_server_response)[i];
+      EXPECT_EQ(expected.platform_type(), actual->platform_type());
+      EXPECT_EQ(expected.response_type(), actual->response_type());
+      EXPECT_EQ(expected.threat_entry_type(), actual->threat_entry_type());
+      EXPECT_EQ(expected.threat_type(), actual->threat_type());
+      EXPECT_EQ(expected.new_client_state(), actual->new_client_state());
+
+      // TODO(vakh): Test more fields from the proto.
+    }
+  }
+
+  ExtendedReportingLevel GetExtendedReportingLevel(ExtendedReportingLevel erl) {
+    return erl;
+  }
+
+  std::unique_ptr<V4UpdateProtocolManager> CreateProtocolManager(
+      const std::vector<ListUpdateResponse>& expected_lurs,
+      bool disable_auto_update = false,
+      ExtendedReportingLevel erl = SBER_LEVEL_OFF) {
+    return V4UpdateProtocolManager::Create(
+        test_shared_loader_factory_,
+        GetTestV4ProtocolConfig(disable_auto_update),
+        base::BindRepeating(
+            &V4UpdateProtocolManagerTest::ValidateGetUpdatesResults,
+            base::Unretained(this), expected_lurs),
+        base::BindRepeating(
+            &V4UpdateProtocolManagerTest::GetExtendedReportingLevel,
+            base::Unretained(this), erl));
+  }
+
+  void SetupStoreStates() {
+    store_state_map_ = std::make_unique<StoreStateMap>();
+
+    ListIdentifier win_url_malware(WINDOWS_PLATFORM, URL, MALWARE_THREAT);
+    store_state_map_->insert({win_url_malware, "initial_state_1"});
+
+    ListIdentifier win_url_uws(WINDOWS_PLATFORM, URL, UNWANTED_SOFTWARE);
+    store_state_map_->insert({win_url_uws, "initial_state_2"});
+
+    ListIdentifier win_exe_uws(WINDOWS_PLATFORM, EXECUTABLE, UNWANTED_SOFTWARE);
+    store_state_map_->insert({win_exe_uws, "initial_state_3"});
+  }
+
+  void SetupExpectedListUpdateResponse(
+      std::vector<ListUpdateResponse>* expected_lurs) {
+    ListUpdateResponse lur;
+    lur.set_platform_type(WINDOWS_PLATFORM);
+    lur.set_response_type(ListUpdateResponse::PARTIAL_UPDATE);
+    lur.set_threat_entry_type(URL);
+    lur.set_threat_type(MALWARE_THREAT);
+    lur.set_new_client_state("new_state_1");
+    expected_lurs->push_back(lur);
+
+    lur.set_platform_type(WINDOWS_PLATFORM);
+    lur.set_response_type(ListUpdateResponse::PARTIAL_UPDATE);
+    lur.set_threat_entry_type(URL);
+    lur.set_threat_type(UNWANTED_SOFTWARE);
+    lur.set_new_client_state("new_state_2");
+    expected_lurs->push_back(lur);
+
+    lur.set_platform_type(WINDOWS_PLATFORM);
+    lur.set_response_type(ListUpdateResponse::FULL_UPDATE);
+    lur.set_threat_entry_type(EXECUTABLE);
+    lur.set_threat_type(MALWARE_THREAT);
+    lur.set_new_client_state("new_state_3");
+    expected_lurs->push_back(lur);
+  }
+
+  std::string GetExpectedV4UpdateResponse(
+      const std::vector<ListUpdateResponse>& expected_lurs) const {
+    FetchThreatListUpdatesResponse response;
+
+    for (const auto& expected_lur : expected_lurs) {
+      ListUpdateResponse* lur = response.add_list_update_responses();
+      lur->set_new_client_state(expected_lur.new_client_state());
+      lur->set_platform_type(expected_lur.platform_type());
+      lur->set_response_type(expected_lur.response_type());
+      lur->set_threat_entry_type(expected_lur.threat_entry_type());
+      lur->set_threat_type(expected_lur.threat_type());
+    }
+
+    // Serialize.
+    std::string res_data;
+    response.SerializeToString(&res_data);
+
+    return res_data;
+  }
+
+  bool expect_callback_to_be_called_;
+  std::unique_ptr<StoreStateMap> store_state_map_;
+  network::TestURLLoaderFactory test_url_loader_factory_;
+  scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
+};
+
+TEST_F(V4UpdateProtocolManagerTest, TestGetUpdatesErrorHandlingNetwork) {
+  scoped_refptr<base::TestSimpleTaskRunner> runner(
+      new base::TestSimpleTaskRunner());
+  base::ThreadTaskRunnerHandle runner_handler(runner);
+  const std::vector<ListUpdateResponse> expected_lurs;
+  std::unique_ptr<V4UpdateProtocolManager> pm(
+      CreateProtocolManager(expected_lurs));
+  runner->ClearPendingTasks();
+
+  // Initial state. No errors.
+  EXPECT_EQ(0ul, pm->update_error_count_);
+  EXPECT_EQ(1ul, pm->update_back_off_mult_);
+  expect_callback_to_be_called_ = false;
+  pm->store_state_map_ = std::move(store_state_map_);
+  pm->IssueUpdateRequest();
+
+  EXPECT_FALSE(pm->IsUpdateScheduled());
+
+  // Failed request status should result in error.
+  pm->OnURLLoaderCompleteInternal(net::ERR_CONNECTION_RESET, 0, std::string());
+
+  // Should have recorded one error, but back off multiplier is unchanged.
+  EXPECT_EQ(1ul, pm->update_error_count_);
+  EXPECT_EQ(1ul, pm->update_back_off_mult_);
+  EXPECT_TRUE(pm->IsUpdateScheduled());
+}
+
+TEST_F(V4UpdateProtocolManagerTest, TestGetUpdatesErrorHandlingResponseCode) {
+  scoped_refptr<base::TestSimpleTaskRunner> runner(
+      new base::TestSimpleTaskRunner());
+  base::ThreadTaskRunnerHandle runner_handler(runner);
+  const std::vector<ListUpdateResponse> expected_lurs;
+  std::unique_ptr<V4UpdateProtocolManager> pm(
+      CreateProtocolManager(expected_lurs));
+  runner->ClearPendingTasks();
+
+  // Initial state. No errors.
+  EXPECT_EQ(0ul, pm->update_error_count_);
+  EXPECT_EQ(1ul, pm->update_back_off_mult_);
+  expect_callback_to_be_called_ = false;
+  pm->store_state_map_ = std::move(store_state_map_);
+  pm->IssueUpdateRequest();
+
+  EXPECT_FALSE(pm->IsUpdateScheduled());
+
+  // Response code of anything other than 200 should result in error.
+  pm->OnURLLoaderCompleteInternal(net::HTTP_NO_CONTENT, 0, std::string());
+
+  // Should have recorded one error, but back off multiplier is unchanged.
+  EXPECT_EQ(1ul, pm->update_error_count_);
+  EXPECT_EQ(1ul, pm->update_back_off_mult_);
+  EXPECT_TRUE(pm->IsUpdateScheduled());
+}
+
+TEST_F(V4UpdateProtocolManagerTest, TestGetUpdatesNoError) {
+  scoped_refptr<base::TestSimpleTaskRunner> runner(
+      new base::TestSimpleTaskRunner());
+  base::ThreadTaskRunnerHandle runner_handler(runner);
+  std::vector<ListUpdateResponse> expected_lurs;
+  SetupExpectedListUpdateResponse(&expected_lurs);
+  std::unique_ptr<V4UpdateProtocolManager> pm(
+      CreateProtocolManager(expected_lurs));
+  runner->ClearPendingTasks();
+
+  // Initial state. No errors.
+  EXPECT_EQ(0ul, pm->update_error_count_);
+  EXPECT_EQ(1ul, pm->update_back_off_mult_);
+  expect_callback_to_be_called_ = true;
+  pm->store_state_map_ = std::move(store_state_map_);
+  pm->IssueUpdateRequest();
+
+  EXPECT_FALSE(pm->IsUpdateScheduled());
+
+  pm->OnURLLoaderCompleteInternal(net::OK, net::HTTP_OK,
+                                  GetExpectedV4UpdateResponse(expected_lurs));
+
+  // No error, back off multiplier is unchanged.
+  EXPECT_EQ(0ul, pm->update_error_count_);
+  EXPECT_EQ(1ul, pm->update_back_off_mult_);
+  EXPECT_FALSE(pm->IsUpdateScheduled());
+}
+
+TEST_F(V4UpdateProtocolManagerTest, TestGetUpdatesWithOneBackoff) {
+  scoped_refptr<base::TestSimpleTaskRunner> runner(
+      new base::TestSimpleTaskRunner());
+  base::ThreadTaskRunnerHandle runner_handler(runner);
+  std::vector<ListUpdateResponse> expected_lurs;
+  SetupExpectedListUpdateResponse(&expected_lurs);
+  std::unique_ptr<V4UpdateProtocolManager> pm(
+      CreateProtocolManager(expected_lurs));
+  runner->ClearPendingTasks();
+
+  // Initial state. No errors.
+  EXPECT_EQ(0ul, pm->update_error_count_);
+  EXPECT_EQ(1ul, pm->update_back_off_mult_);
+  expect_callback_to_be_called_ = false;
+  pm->store_state_map_ = std::move(store_state_map_);
+  pm->IssueUpdateRequest();
+
+  EXPECT_FALSE(pm->IsUpdateScheduled());
+
+  // Response code of anything other than 200 should result in error.
+  pm->OnURLLoaderCompleteInternal(net::HTTP_NO_CONTENT, 0, std::string());
+
+  // Should have recorded one error, but back off multiplier is unchanged.
+  EXPECT_EQ(1ul, pm->update_error_count_);
+  EXPECT_EQ(1ul, pm->update_back_off_mult_);
+  EXPECT_TRUE(pm->IsUpdateScheduled());
+
+  // Retry, now no backoff.
+  expect_callback_to_be_called_ = true;
+  // Call RunPendingTasks to ensure that the request is sent after backoff.
+  runner->RunPendingTasks();
+
+  pm->OnURLLoaderCompleteInternal(net::OK, net::HTTP_OK,
+                                  GetExpectedV4UpdateResponse(expected_lurs));
+
+  // No error, back off multiplier is unchanged.
+  EXPECT_EQ(0ul, pm->update_error_count_);
+  EXPECT_EQ(1ul, pm->update_back_off_mult_);
+  EXPECT_FALSE(pm->IsUpdateScheduled());
+}
+
+TEST_F(V4UpdateProtocolManagerTest, TestBase64EncodingUsesUrlEncoding) {
+  // NOTE(vakh): I handpicked this value for state by generating random strings
+  // and picked the one that leads to a '-' in the base64 url encoded request
+  // output.
+  store_state_map_->clear();
+  (*store_state_map_)[ListIdentifier(LINUX_PLATFORM, URL, MALWARE_THREAT)] =
+      "h8xfYqY>:R";
+  std::unique_ptr<V4UpdateProtocolManager> pm(
+      CreateProtocolManager(std::vector<ListUpdateResponse>({})));
+  pm->store_state_map_ = std::move(store_state_map_);
+
+  std::string encoded_request_with_minus =
+      pm->GetBase64SerializedUpdateRequestProto();
+
+  std::string expected =
+      "Cg8KCHVuaXR0ZXN0EgMxLjAaGAgBEAIaCmg4eGZZcVk-OlIiBCABIAIoASICCAE=";
+#if !BUILDFLAG(FULL_SAFE_BROWSING)
+  if (base::SysInfo::IsLowEndDevice()) {
+    expected =
+        "Cg8KCHVuaXR0ZXN0EgMxLjAaGwgBEAIaCmg4eGZZcVk-OlIiBxCAICABIAIoASICCAE=";
+  }
+#endif
+
+  EXPECT_EQ(expected, encoded_request_with_minus);
+
+  // TODO(vakh): Add a similar test for underscore for completeness, although
+  // the '-' case is sufficient to prove that we are using URL encoding.
+}
+
+TEST_F(V4UpdateProtocolManagerTest, TestDisableAutoUpdates) {
+  scoped_refptr<base::TestSimpleTaskRunner> runner(
+      new base::TestSimpleTaskRunner());
+  base::ThreadTaskRunnerHandle runner_handler(runner);
+  std::unique_ptr<V4UpdateProtocolManager> pm(CreateProtocolManager(
+      std::vector<ListUpdateResponse>(), true /* disable_auto_update */));
+
+  // Initial state. No errors.
+  pm->ScheduleNextUpdate(std::move(store_state_map_));
+  EXPECT_FALSE(pm->IsUpdateScheduled());
+
+  runner->RunPendingTasks();
+  EXPECT_FALSE(pm->IsUpdateScheduled());
+
+  DCHECK(!pm->request_);
+}
+
+TEST_F(V4UpdateProtocolManagerTest, TestGetUpdatesHasTimeout) {
+  scoped_refptr<base::TestSimpleTaskRunner> runner(
+      new base::TestSimpleTaskRunner());
+  base::ThreadTaskRunnerHandle runner_handler(runner);
+  std::vector<ListUpdateResponse> expected_lurs;
+  SetupExpectedListUpdateResponse(&expected_lurs);
+  std::unique_ptr<V4UpdateProtocolManager> pm(
+      CreateProtocolManager(expected_lurs));
+  runner->ClearPendingTasks();
+
+  // Initial state. No errors.
+  EXPECT_EQ(0ul, pm->update_error_count_);
+  EXPECT_EQ(1ul, pm->update_back_off_mult_);
+  expect_callback_to_be_called_ = true;
+  pm->store_state_map_ = std::move(store_state_map_);
+  pm->IssueUpdateRequest();
+
+  // Don't set anything on the loader. Let it time out.
+  runner->RunPendingTasks();
+
+  // Now wait for the next request to be scheduled.
+  runner->RunPendingTasks();
+
+  // Should have recorded one error, but back off multiplier is unchanged.
+  EXPECT_EQ(1ul, pm->update_error_count_);
+  EXPECT_EQ(1ul, pm->update_back_off_mult_);
+
+  // There should be another fetcher now.
+  pm->OnURLLoaderCompleteInternal(net::OK, net::HTTP_OK,
+                                  GetExpectedV4UpdateResponse(expected_lurs));
+
+  // No error, back off multiplier is unchanged.
+  EXPECT_EQ(0ul, pm->update_error_count_);
+  EXPECT_EQ(1ul, pm->update_back_off_mult_);
+}
+
+TEST_F(V4UpdateProtocolManagerTest, TestExtendedReportingLevelIncluded) {
+  store_state_map_->clear();
+  (*store_state_map_)[ListIdentifier(LINUX_PLATFORM, URL, MALWARE_THREAT)] =
+      "state";
+  std::string base = "Cg8KCHVuaXR0ZXN0EgMxLjAaEwgBEAIaBXN0YXRlIgQgASACKAEiAgg";
+#if !BUILDFLAG(FULL_SAFE_BROWSING)
+  if (base::SysInfo::IsLowEndDevice()) {
+    base = "Cg8KCHVuaXR0ZXN0EgMxLjAaFggBEAIaBXN0YXRlIgcQgCAgASACKAEiAgg";
+  }
+#endif
+
+  std::unique_ptr<V4UpdateProtocolManager> pm_with_off(CreateProtocolManager(
+      std::vector<ListUpdateResponse>({}), false, SBER_LEVEL_OFF));
+  pm_with_off->store_state_map_ = std::move(store_state_map_);
+  EXPECT_EQ(base + "B", pm_with_off->GetBase64SerializedUpdateRequestProto());
+
+  std::unique_ptr<V4UpdateProtocolManager> pm_with_legacy(CreateProtocolManager(
+      std::vector<ListUpdateResponse>({}), false, SBER_LEVEL_LEGACY));
+  pm_with_legacy->store_state_map_ = std::move(pm_with_off->store_state_map_);
+  EXPECT_EQ(base + "C",
+            pm_with_legacy->GetBase64SerializedUpdateRequestProto());
+
+  std::unique_ptr<V4UpdateProtocolManager> pm_with_scout(CreateProtocolManager(
+      std::vector<ListUpdateResponse>({}), false, SBER_LEVEL_SCOUT));
+  pm_with_scout->store_state_map_ = std::move(pm_with_legacy->store_state_map_);
+  EXPECT_EQ(base + "D", pm_with_scout->GetBase64SerializedUpdateRequestProto());
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/features.cc b/components/safe_browsing/core/features.cc
new file mode 100644
index 0000000..1b2e9b3d
--- /dev/null
+++ b/components/safe_browsing/core/features.cc
@@ -0,0 +1,162 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/core/features.h"
+
+#include <stddef.h>
+#include <algorithm>
+#include <utility>
+#include <vector>
+#include "base/feature_list.h"
+#include "base/metrics/field_trial_params.h"
+#include "components/safe_browsing/buildflags.h"
+
+#include "base/macros.h"
+#include "base/values.h"
+namespace safe_browsing {
+// Please define any new SafeBrowsing related features in this file, and add
+// them to the ExperimentalFeaturesList below to start displaying their status
+// on the chrome://safe-browsing page.
+const base::Feature kAdPopupTriggerFeature{"SafeBrowsingAdPopupTrigger",
+                                           base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kAdRedirectTriggerFeature{
+    "SafeBrowsingAdRedirectTrigger", base::FEATURE_DISABLED_BY_DEFAULT};
+
+// Controls various parameters related to occasionally collecting ad samples,
+// for example to control how often collection should occur.
+const base::Feature kAdSamplerTriggerFeature{"SafeBrowsingAdSamplerTrigger",
+                                             base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kCaptureInlineJavascriptForGoogleAds{
+    "CaptureInlineJavascriptForGoogleAds", base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kCaptureSafetyNetId{"SafeBrowsingCaptureSafetyNetId",
+                                        base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kCommittedSBInterstitials{
+    "SafeBrowsingCommittedInterstitials", base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kContentComplianceEnabled{
+    "ContentComplianceEnabled", base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kMalwareScanEnabled{"MalwareScanEnabled",
+                                        base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kPasswordProtectionForSavedPasswords{
+    "SafeBrowsingPasswordProtectionForSavedPasswords",
+    base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kPasswordProtectionShowDomainsForSavedPasswords{
+    "SafeBrowsingPasswordProtectionShowDomainsForSavedPasswords",
+    base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kPasswordProtectionForSignedInUsers{
+    "SafeBrowsingPasswordProtectionForSignedInUsers",
+    base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kRealTimeUrlLookupEnabled{
+    "SafeBrowsingRealTimeUrlLookupEnabled", base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kSendOnFocusPing {
+  "SafeBrowsingSendOnFocusPing",
+#if BUILDFLAG(FULL_SAFE_BROWSING)
+      base::FEATURE_ENABLED_BY_DEFAULT
+};
+#else
+      base::FEATURE_DISABLED_BY_DEFAULT
+};
+#endif
+
+const base::Feature kSendPasswordReusePing {
+  "SafeBrowsingSendPasswordReusePing",
+#if BUILDFLAG(FULL_SAFE_BROWSING)
+      base::FEATURE_ENABLED_BY_DEFAULT
+};
+#else
+      base::FEATURE_DISABLED_BY_DEFAULT
+};
+#endif
+
+const base::Feature kSendSampledPingsForAllowlistDomains{
+    "SafeBrowsingSendSampledPingsForAllowlistDomain",
+    base::FEATURE_DISABLED_BY_DEFAULT};
+
+constexpr base::FeatureParam<bool> kShouldFillOldPhishGuardProto{
+    &kPasswordProtectionForSignedInUsers, "DeprecateOldProto", false};
+
+const base::Feature kSuspiciousSiteTriggerQuotaFeature{
+    "SafeBrowsingSuspiciousSiteTriggerQuota", base::FEATURE_ENABLED_BY_DEFAULT};
+
+const base::Feature kThreatDomDetailsTagAndAttributeFeature{
+    "ThreatDomDetailsTagAttributes", base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kTriggerThrottlerDailyQuotaFeature{
+    "SafeBrowsingTriggerThrottlerDailyQuota",
+    base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kUseLocalBlacklistsV2{"SafeBrowsingUseLocalBlacklistsV2",
+                                          base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kUseNewDownloadWarnings{"UseNewDownloadWarnings",
+                                            base::FEATURE_DISABLED_BY_DEFAULT};
+
+namespace {
+// List of Safe Browsing features. Boolean value for each list member should be
+// set to true if the experiment state should be listed on
+// chrome://safe-browsing.
+constexpr struct {
+  const base::Feature* feature;
+  // True if the feature's state should be listed on chrome://safe-browsing.
+  bool show_state;
+} kExperimentalFeatures[]{
+    {&kAdPopupTriggerFeature, true},
+    {&kAdRedirectTriggerFeature, true},
+    {&kAdSamplerTriggerFeature, false},
+    {&kCaptureInlineJavascriptForGoogleAds, true},
+    {&kCaptureSafetyNetId, true},
+    {&kCommittedSBInterstitials, true},
+    {&kContentComplianceEnabled, true},
+    {&kMalwareScanEnabled, true},
+    {&kPasswordProtectionForSavedPasswords, true},
+    {&kPasswordProtectionShowDomainsForSavedPasswords, true},
+    {&kPasswordProtectionForSignedInUsers, true},
+    {&kRealTimeUrlLookupEnabled, true},
+    {&kSendOnFocusPing, true},
+    {&kSendPasswordReusePing, true},
+    {&kSendSampledPingsForAllowlistDomains, false},
+    {&kSuspiciousSiteTriggerQuotaFeature, true},
+    {&kThreatDomDetailsTagAndAttributeFeature, false},
+    {&kTriggerThrottlerDailyQuotaFeature, false},
+    {&kUseLocalBlacklistsV2, true},
+};
+
+// Adds the name and the enabled/disabled status of a given feature.
+void AddFeatureAndAvailability(const base::Feature* exp_feature,
+                               base::ListValue* param_list) {
+  param_list->Append(base::Value(exp_feature->name));
+  if (base::FeatureList::IsEnabled(*exp_feature)) {
+    param_list->Append(base::Value("Enabled"));
+  } else {
+    param_list->Append(base::Value("Disabled"));
+  }
+}
+}  // namespace
+
+// Returns the list of the experimental features that are enabled or disabled,
+// as part of currently running Safe Browsing experiments.
+base::ListValue GetFeatureStatusList() {
+  base::ListValue param_list;
+  for (const auto& feature_status : kExperimentalFeatures) {
+    if (feature_status.show_state)
+      AddFeatureAndAvailability(feature_status.feature, &param_list);
+  }
+  return param_list;
+}
+
+bool GetShouldFillOldPhishGuardProto() {
+  return kShouldFillOldPhishGuardProto.Get();
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/features.h b/components/safe_browsing/core/features.h
new file mode 100644
index 0000000..61279aa7
--- /dev/null
+++ b/components/safe_browsing/core/features.h
@@ -0,0 +1,118 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CORE_FEATURES_H_
+#define COMPONENTS_SAFE_BROWSING_CORE_FEATURES_H_
+
+#include <stddef.h>
+#include <algorithm>
+#include <utility>
+#include <vector>
+
+#include "base/feature_list.h"
+#include "base/macros.h"
+#include "base/values.h"
+namespace base {
+class ListValue;
+}  // namespace base
+
+namespace safe_browsing {
+// Features list
+// Controls whether we send RIND reports when a popup originating from a Google
+// Ad is blocked.
+extern const base::Feature kAdPopupTriggerFeature;
+
+// Controls whether we send RIND reports when a redirect caused by a Google Ad
+// is blocked.
+extern const base::Feature kAdRedirectTriggerFeature;
+
+extern const base::Feature kAdSamplerTriggerFeature;
+
+// Controls whether we sample inline JavaScript for ads in RIND
+// reports.
+extern const base::Feature kCaptureInlineJavascriptForGoogleAds;
+
+// Controls whether we try to get the SafetyNet ID of the device for use when
+// a SBER user downloads an APK file.
+extern const base::Feature kCaptureSafetyNetId;
+
+// Controls if safe browsing interstitials are implemented as committed
+// navigations instead of overlays.
+extern const base::Feature kCommittedSBInterstitials;
+
+// Controls whether to do deep scanning for DLP. If both this feature and
+// the enterprise policies are enabled, the downloaded and uploaded files are
+// sent for scanning.
+extern const base::Feature kContentComplianceEnabled;
+
+// Controls whether to do deep scanning for malware. If both this feature and
+// the enterprise policies are enabled, the downloaded and uploaded files are
+// sent for scanning.
+extern const base::Feature kMalwareScanEnabled;
+
+// Controls whether the user has forcibly enabled AP download protection. This
+// flag will enable AP downloads protections even for users not enrolled in
+// APP.
+extern const base::Feature kForceUseAPDownloadProtection;
+
+// Enable password protection for non-Google accounts.
+extern const base::Feature kPasswordProtectionForSavedPasswords;
+
+// Enable whether or not to show a list of domains the saved password was used
+// on the modal warning dialog during password protection. This is only checked
+// if the |kPasswordProtectionForSavedPasswords| experiment is on.
+extern const base::Feature kPasswordProtectionShowDomainsForSavedPasswords;
+
+// Enable GAIA password protection for signed-in users.
+extern const base::Feature kPasswordProtectionForSignedInUsers;
+
+// Controls whether Chrome sends on focus ping.
+extern const base::Feature kSendOnFocusPing;
+
+// Controls whether Chrome sends password reuse ping.
+extern const base::Feature kSendPasswordReusePing;
+
+// Controls the daily quota for the suspicious site trigger.
+extern const base::Feature kSuspiciousSiteTriggerQuotaFeature;
+
+// Controls whether the real time URL lookup is enabled.
+extern const base::Feature kRealTimeUrlLookupEnabled;
+
+// Controls whether to send sample pings of allowlist domains on
+// the allowlist to Safe Browsing.
+extern const base::Feature kSendSampledPingsForAllowlistDomains;
+
+// Specifies which non-resource HTML Elements to collect based on their tag and
+// attributes. It's a single param containing a comma-separated list of pairs.
+// For example: "tag1,id,tag1,height,tag2,foo" - this will collect elements with
+// tag "tag1" that have attribute "id" or "height" set, and elements of tag
+// "tag2" if they have attribute "foo" set. All tag names and attributes should
+// be lower case.
+extern const base::Feature kThreatDomDetailsTagAndAttributeFeature;
+
+// Controls the daily quota for data collection triggers. It's a single param
+// containing a comma-separated list of pairs. The format of the param is
+// "T1,Q1,T2,Q2,...Tn,Qn", where Tx is a TriggerType and Qx is how many reports
+// that trigger is allowed to send per day.
+// TODO(crbug.com/744869): This param should be deprecated after ad sampler
+// launch in favour of having a unique quota feature and param per trigger.
+// Having a single shared feature makes it impossible to run multiple trigger
+// trials simultaneously.
+extern const base::Feature kTriggerThrottlerDailyQuotaFeature;
+
+// Controls whether Chrome on Android uses locally cached blacklists.
+extern const base::Feature kUseLocalBlacklistsV2;
+
+// Controls whether Chrome uses new download warning UX.
+extern const base::Feature kUseNewDownloadWarnings;
+
+base::ListValue GetFeatureStatusList();
+
+// Returns whether or not to stop filling in the SyncAccountType and
+// ReusedPasswordType enums. This is used in the
+// |kPasswordProtectionForSignedInUsers| experiment.
+bool GetShouldFillOldPhishGuardProto();
+
+}  // namespace safe_browsing
+#endif  // COMPONENTS_SAFE_BROWSING_CORE_FEATURES_H_
diff --git a/components/safe_browsing/core/ping_manager.cc b/components/safe_browsing/core/ping_manager.cc
new file mode 100644
index 0000000..664a267
--- /dev/null
+++ b/components/safe_browsing/core/ping_manager.cc
@@ -0,0 +1,222 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/core/ping_manager.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
+#include "google_apis/google_api_keys.h"
+#include "net/base/escape.h"
+#include "net/base/load_flags.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "services/network/public/cpp/simple_url_loader.h"
+#include "url/gurl.h"
+
+namespace {
+
+const net::NetworkTrafficAnnotationTag kTrafficAnnotation =
+    net::DefineNetworkTrafficAnnotation("safe_browsing_extended_reporting",
+                                        R"(
+      semantics {
+        sender: "Safe Browsing Extended Reporting"
+        description:
+          "When a user is opted in to automatically reporting 'possible "
+          "security incidents to Google,' and they reach a bad page that's "
+          "flagged by Safe Browsing, Chrome will send a report to Google "
+          "with information about the threat. This helps Safe Browsing learn "
+          "where threats originate and thus protect more users."
+        trigger:
+          "When a red interstitial is shown, and the user is opted-in."
+        data:
+          "The report includes the URL and referrer chain of the page. If the "
+          "warning is triggered by a subresource on a partially loaded page, "
+          "the report will include the URL and referrer chain of sub frames "
+          "and resources loaded into the page.  It may also include a subset "
+          "of headers for resources loaded, and some Google ad identifiers to "
+          "help block malicious ads."
+        destination: GOOGLE_OWNED_SERVICE
+      }
+      policy {
+        cookies_allowed: YES
+        cookies_store: "Safe Browsing Cookie Store"
+        setting:
+          "Users can control this feature via the 'Automatically report "
+          "details of possible security incidents to Google' setting under "
+          "'Privacy'. The feature is disabled by default."
+        chrome_policy {
+          SafeBrowsingExtendedReportingOptInAllowed {
+            policy_options {mode: MANDATORY}
+            SafeBrowsingExtendedReportingOptInAllowed: false
+          }
+        }
+      })");
+
+}  // namespace
+
+namespace safe_browsing {
+
+// SafeBrowsingPingManager implementation ----------------------------------
+
+// static
+std::unique_ptr<PingManager> PingManager::Create(
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    const V4ProtocolConfig& config) {
+  return base::WrapUnique(new PingManager(url_loader_factory, config));
+}
+
+PingManager::PingManager(
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    const V4ProtocolConfig& config)
+    : config_(config), url_loader_factory_(url_loader_factory) {}
+
+PingManager::~PingManager() {}
+
+// All SafeBrowsing request responses are handled here.
+void PingManager::OnURLLoaderComplete(
+    network::SimpleURLLoader* source,
+    std::unique_ptr<std::string> response_body) {
+  auto it = safebrowsing_reports_.find(source);
+  DCHECK(it != safebrowsing_reports_.end());
+  safebrowsing_reports_.erase(it);
+}
+
+// Sends a SafeBrowsing "hit" report.
+void PingManager::ReportSafeBrowsingHit(
+    const safe_browsing::HitReport& hit_report) {
+  auto resource_request = std::make_unique<network::ResourceRequest>();
+  GURL report_url = SafeBrowsingHitUrl(hit_report);
+  resource_request->url = report_url;
+  resource_request->load_flags = net::LOAD_DISABLE_CACHE;
+  if (!hit_report.post_data.empty())
+    resource_request->method = "POST";
+
+  auto report_ptr = network::SimpleURLLoader::Create(
+      std::move(resource_request), kTrafficAnnotation);
+
+  if (!hit_report.post_data.empty())
+    report_ptr->AttachStringForUpload(hit_report.post_data, "text/plain");
+
+  report_ptr->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+      url_loader_factory_.get(),
+      base::BindOnce(&PingManager::OnURLLoaderComplete, base::Unretained(this),
+                     report_ptr.get()));
+  safebrowsing_reports_.insert(std::move(report_ptr));
+}
+
+// Sends threat details for users who opt-in.
+void PingManager::ReportThreatDetails(const std::string& report) {
+  GURL report_url = ThreatDetailsUrl();
+
+  auto resource_request = std::make_unique<network::ResourceRequest>();
+  resource_request->url = report_url;
+  resource_request->load_flags = net::LOAD_DISABLE_CACHE;
+  resource_request->method = "POST";
+
+  auto loader = network::SimpleURLLoader::Create(std::move(resource_request),
+                                                 kTrafficAnnotation);
+
+  loader->AttachStringForUpload(report, "application/octet-stream");
+
+  loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+      url_loader_factory_.get(),
+      base::BindOnce(&PingManager::OnURLLoaderComplete, base::Unretained(this),
+                     loader.get()));
+  safebrowsing_reports_.insert(std::move(loader));
+}
+
+GURL PingManager::SafeBrowsingHitUrl(
+    const safe_browsing::HitReport& hit_report) const {
+  DCHECK(hit_report.threat_type == SB_THREAT_TYPE_URL_MALWARE ||
+         hit_report.threat_type == SB_THREAT_TYPE_URL_PHISHING ||
+         hit_report.threat_type == SB_THREAT_TYPE_URL_UNWANTED ||
+         hit_report.threat_type == SB_THREAT_TYPE_URL_BINARY_MALWARE ||
+         hit_report.threat_type == SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING ||
+         hit_report.threat_type == SB_THREAT_TYPE_URL_CLIENT_SIDE_MALWARE);
+  std::string url =
+      GetReportUrl(config_, "report", &hit_report.extended_reporting_level);
+  std::string threat_list = "none";
+  switch (hit_report.threat_type) {
+    case SB_THREAT_TYPE_URL_MALWARE:
+      threat_list = "malblhit";
+      break;
+    case SB_THREAT_TYPE_URL_PHISHING:
+      threat_list = "phishblhit";
+      break;
+    case SB_THREAT_TYPE_URL_UNWANTED:
+      threat_list = "uwsblhit";
+      break;
+    case SB_THREAT_TYPE_URL_BINARY_MALWARE:
+      threat_list = "binurlhit";
+      break;
+    case SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING:
+      threat_list = "phishcsdhit";
+      break;
+    case SB_THREAT_TYPE_URL_CLIENT_SIDE_MALWARE:
+      threat_list = "malcsdhit";
+      break;
+    default:
+      NOTREACHED();
+  }
+
+  std::string threat_source = "none";
+  switch (hit_report.threat_source) {
+    case safe_browsing::ThreatSource::DATA_SAVER:
+      threat_source = "ds";
+      break;
+    case safe_browsing::ThreatSource::REMOTE:
+      threat_source = "rem";
+      break;
+    case safe_browsing::ThreatSource::LOCAL_PVER3:
+      threat_source = "l3";
+      break;
+    case safe_browsing::ThreatSource::LOCAL_PVER4:
+      threat_source = "l4";
+      break;
+    case safe_browsing::ThreatSource::CLIENT_SIDE_DETECTION:
+      threat_source = "csd";
+      break;
+    case safe_browsing::ThreatSource::PASSWORD_PROTECTION_SERVICE:
+      threat_source = "pps";
+      break;
+    case safe_browsing::ThreatSource::UNKNOWN:
+      NOTREACHED();
+  }
+
+  // Add user_population component only if it's not empty.
+  std::string user_population_comp;
+  if (!hit_report.population_id.empty()) {
+    // Population_id should be URL-safe, but escape it and size-limit it
+    // anyway since it came from outside Chrome.
+    std::string up_str =
+        net::EscapeQueryParamValue(hit_report.population_id, true);
+    if (up_str.size() > 512) {
+      DCHECK(false) << "population_id is too long: " << up_str;
+      up_str = "UP_STRING_TOO_LONG";
+    }
+
+    user_population_comp = "&up=" + up_str;
+  }
+
+  return GURL(base::StringPrintf(
+      "%s&evts=%s&evtd=%s&evtr=%s&evhr=%s&evtb=%d&src=%s&m=%d%s", url.c_str(),
+      threat_list.c_str(),
+      net::EscapeQueryParamValue(hit_report.malicious_url.spec(), true).c_str(),
+      net::EscapeQueryParamValue(hit_report.page_url.spec(), true).c_str(),
+      net::EscapeQueryParamValue(hit_report.referrer_url.spec(), true).c_str(),
+      hit_report.is_subresource, threat_source.c_str(),
+      hit_report.is_metrics_reporting_active, user_population_comp.c_str()));
+}
+
+GURL PingManager::ThreatDetailsUrl() const {
+  std::string url = GetReportUrl(config_, "clientreport/malware");
+  return GURL(url);
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/ping_manager.h b/components/safe_browsing/core/ping_manager.h
new file mode 100644
index 0000000..3f4bf07
--- /dev/null
+++ b/components/safe_browsing/core/ping_manager.h
@@ -0,0 +1,88 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CORE_PING_MANAGER_H_
+#define COMPONENTS_SAFE_BROWSING_CORE_PING_MANAGER_H_
+
+// A class that reports basic safebrowsing statistics to Google's SafeBrowsing
+// servers.
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/containers/unique_ptr_adapters.h"
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "components/safe_browsing/core/db/hit_report.h"
+#include "components/safe_browsing/core/db/util.h"
+#include "content/public/browser/permission_type.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "url/gurl.h"
+
+namespace network {
+class SimpleURLLoader;
+}  // namespace network
+
+namespace safe_browsing {
+
+class PingManager {
+ public:
+  virtual ~PingManager();
+
+  // Create an instance of the safe browsing ping manager.
+  static std::unique_ptr<PingManager> Create(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      const V4ProtocolConfig& config);
+
+  void OnURLLoaderComplete(network::SimpleURLLoader* source,
+                           std::unique_ptr<std::string> response_body);
+
+  // Report to Google when a SafeBrowsing warning is shown to the user.
+  // |hit_report.threat_type| should be one of the types known by
+  // SafeBrowsingtHitUrl.
+  void ReportSafeBrowsingHit(const safe_browsing::HitReport& hit_report);
+
+  // Users can opt-in on the SafeBrowsing interstitial to send detailed
+  // threat reports. |report| is the serialized report.
+  void ReportThreatDetails(const std::string& report);
+
+ protected:
+  friend class PingManagerTest;
+  // Constructs a PingManager that issues network requests
+  // using |url_loader_factory|.
+  PingManager(scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+              const V4ProtocolConfig& config);
+
+ private:
+  FRIEND_TEST_ALL_PREFIXES(PingManagerTest, TestSafeBrowsingHitUrl);
+  FRIEND_TEST_ALL_PREFIXES(PingManagerTest, TestThreatDetailsUrl);
+  FRIEND_TEST_ALL_PREFIXES(PingManagerTest, TestReportThreatDetails);
+  FRIEND_TEST_ALL_PREFIXES(PingManagerTest, TestReportSafeBrowsingHit);
+
+  const V4ProtocolConfig config_;
+
+  using Reports = std::set<std::unique_ptr<network::SimpleURLLoader>,
+                           base::UniquePtrComparator>;
+
+  // Generates URL for reporting safe browsing hits.
+  GURL SafeBrowsingHitUrl(const safe_browsing::HitReport& hit_report) const;
+
+  // Generates URL for reporting threat details for users who opt-in.
+  GURL ThreatDetailsUrl() const;
+
+  // The URLLoaderFactory we use to issue network requests.
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
+
+  // Track outstanding SafeBrowsing report fetchers for clean up.
+  // We add both "hit" and "detail" fetchers in this set.
+  Reports safebrowsing_reports_;
+
+  DISALLOW_COPY_AND_ASSIGN(PingManager);
+};
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CORE_PING_MANAGER_H_
diff --git a/components/safe_browsing/core/ping_manager_unittest.cc b/components/safe_browsing/core/ping_manager_unittest.cc
new file mode 100644
index 0000000..be1a504b
--- /dev/null
+++ b/components/safe_browsing/core/ping_manager_unittest.cc
@@ -0,0 +1,190 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "components/safe_browsing/core/ping_manager.h"
+#include "base/base64.h"
+#include "base/logging.h"
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "components/safe_browsing/core/db/v4_test_util.h"
+#include "google_apis/google_api_keys.h"
+#include "net/base/escape.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::Time;
+using base::TimeDelta;
+using safe_browsing::HitReport;
+using safe_browsing::ThreatSource;
+
+namespace safe_browsing {
+
+class PingManagerTest : public testing::Test {
+ public:
+  PingManagerTest() {}
+
+ protected:
+  void SetUp() override {
+    std::string key = google_apis::GetAPIKey();
+    if (!key.empty()) {
+      key_param_ = base::StringPrintf(
+          "&key=%s", net::EscapeQueryParamValue(key, true).c_str());
+    }
+
+    ping_manager_.reset(
+        new PingManager(nullptr, safe_browsing::GetTestV4ProtocolConfig()));
+  }
+
+  PingManager* ping_manager() { return ping_manager_.get(); }
+
+  std::string key_param_;
+  std::unique_ptr<PingManager> ping_manager_;
+};
+
+TEST_F(PingManagerTest, TestSafeBrowsingHitUrl) {
+  HitReport base_hp;
+  base_hp.malicious_url = GURL("http://malicious.url.com");
+  base_hp.page_url = GURL("http://page.url.com");
+  base_hp.referrer_url = GURL("http://referrer.url.com");
+
+  {
+    HitReport hp(base_hp);
+    hp.threat_type = SB_THREAT_TYPE_URL_MALWARE;
+    hp.threat_source = ThreatSource::LOCAL_PVER3;
+    hp.is_subresource = true;
+    hp.extended_reporting_level = SBER_LEVEL_LEGACY;
+    hp.is_metrics_reporting_active = true;
+
+    EXPECT_EQ(
+        "https://safebrowsing.google.com/safebrowsing/report?client=unittest&"
+        "appver=1.0&pver=4.0" +
+            key_param_ +
+            "&ext=1&evts=malblhit&evtd=http%3A%2F%2Fmalicious.url.com%2F&"
+            "evtr=http%3A%2F%2Fpage.url.com%2F&evhr=http%3A%2F%2Freferrer."
+            "url.com%2F&evtb=1&src=l3&m=1",
+        ping_manager()->SafeBrowsingHitUrl(hp).spec());
+  }
+
+  {
+    HitReport hp(base_hp);
+    hp.threat_type = SB_THREAT_TYPE_URL_PHISHING;
+    hp.threat_source = ThreatSource::DATA_SAVER;
+    hp.is_subresource = false;
+    hp.extended_reporting_level = SBER_LEVEL_LEGACY;
+    hp.is_metrics_reporting_active = true;
+    EXPECT_EQ(
+        "https://safebrowsing.google.com/safebrowsing/report?client=unittest&"
+        "appver=1.0&pver=4.0" +
+            key_param_ +
+            "&ext=1&evts=phishblhit&"
+            "evtd=http%3A%2F%2Fmalicious.url.com%2F&"
+            "evtr=http%3A%2F%2Fpage.url.com%2F&evhr=http%3A%2F%2Freferrer."
+            "url.com%2F&evtb=0&src=ds&m=1",
+        ping_manager()->SafeBrowsingHitUrl(hp).spec());
+  }
+
+  {
+    HitReport hp(base_hp);
+    hp.threat_type = SB_THREAT_TYPE_URL_PHISHING;
+    hp.threat_source = ThreatSource::DATA_SAVER;
+    hp.is_subresource = false;
+    hp.extended_reporting_level = SBER_LEVEL_SCOUT;
+    hp.is_metrics_reporting_active = true;
+    EXPECT_EQ(
+        "https://safebrowsing.google.com/safebrowsing/report?client=unittest&"
+        "appver=1.0&pver=4.0" +
+            key_param_ +
+            "&ext=2&evts=phishblhit&"
+            "evtd=http%3A%2F%2Fmalicious.url.com%2F&"
+            "evtr=http%3A%2F%2Fpage.url.com%2F&evhr=http%3A%2F%2Freferrer."
+            "url.com%2F&evtb=0&src=ds&m=1",
+        ping_manager()->SafeBrowsingHitUrl(hp).spec());
+  }
+
+  {
+    HitReport hp(base_hp);
+    hp.threat_type = SB_THREAT_TYPE_URL_BINARY_MALWARE;
+    hp.threat_source = ThreatSource::REMOTE;
+    hp.extended_reporting_level = SBER_LEVEL_OFF;
+    hp.is_metrics_reporting_active = true;
+    hp.is_subresource = false;
+    EXPECT_EQ(
+        "https://safebrowsing.google.com/safebrowsing/report?client=unittest&"
+        "appver=1.0&pver=4.0" +
+            key_param_ +
+            "&ext=0&evts=binurlhit&"
+            "evtd=http%3A%2F%2Fmalicious.url.com%2F&"
+            "evtr=http%3A%2F%2Fpage.url.com%2F&evhr=http%3A%2F%2Freferrer."
+            "url.com%2F&evtb=0&src=rem&m=1",
+        ping_manager()->SafeBrowsingHitUrl(hp).spec());
+  }
+
+  {
+    HitReport hp(base_hp);
+    hp.threat_type = SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING;
+    hp.threat_source = ThreatSource::LOCAL_PVER4;
+    hp.extended_reporting_level = SBER_LEVEL_OFF;
+    hp.is_metrics_reporting_active = false;
+    hp.is_subresource = false;
+    EXPECT_EQ(
+        "https://safebrowsing.google.com/safebrowsing/report?client=unittest&"
+        "appver=1.0&pver=4.0" +
+            key_param_ +
+            "&ext=0&evts=phishcsdhit&"
+            "evtd=http%3A%2F%2Fmalicious.url.com%2F&"
+            "evtr=http%3A%2F%2Fpage.url.com%2F&evhr=http%3A%2F%2Freferrer."
+            "url.com%2F&evtb=0&src=l4&m=0",
+        ping_manager()->SafeBrowsingHitUrl(hp).spec());
+  }
+
+  {
+    HitReport hp(base_hp);
+    hp.threat_type = SB_THREAT_TYPE_URL_CLIENT_SIDE_MALWARE;
+    hp.threat_source = ThreatSource::LOCAL_PVER4;
+    hp.extended_reporting_level = SBER_LEVEL_OFF;
+    hp.is_metrics_reporting_active = false;
+    hp.is_subresource = true;
+    EXPECT_EQ(
+        "https://safebrowsing.google.com/safebrowsing/report?client=unittest&"
+        "appver=1.0&pver=4.0" +
+            key_param_ +
+            "&ext=0&evts=malcsdhit&"
+            "evtd=http%3A%2F%2Fmalicious.url.com%2F&"
+            "evtr=http%3A%2F%2Fpage.url.com%2F&evhr=http%3A%2F%2Freferrer."
+            "url.com%2F&evtb=1&src=l4&m=0",
+        ping_manager()->SafeBrowsingHitUrl(hp).spec());
+  }
+
+  // Same as above, but add population_id
+  {
+    HitReport hp(base_hp);
+    hp.threat_type = SB_THREAT_TYPE_URL_CLIENT_SIDE_MALWARE;
+    hp.threat_source = ThreatSource::LOCAL_PVER4;
+    hp.extended_reporting_level = SBER_LEVEL_OFF;
+    hp.is_metrics_reporting_active = false;
+    hp.is_subresource = true;
+    hp.population_id = "foo bar";
+    EXPECT_EQ(
+        "https://safebrowsing.google.com/safebrowsing/report?client=unittest&"
+        "appver=1.0&pver=4.0" +
+            key_param_ +
+            "&ext=0&evts=malcsdhit&"
+            "evtd=http%3A%2F%2Fmalicious.url.com%2F&"
+            "evtr=http%3A%2F%2Fpage.url.com%2F&evhr=http%3A%2F%2Freferrer."
+            "url.com%2F&evtb=1&src=l4&m=0&up=foo+bar",
+        ping_manager()->SafeBrowsingHitUrl(hp).spec());
+  }
+}
+
+TEST_F(PingManagerTest, TestThreatDetailsUrl) {
+  EXPECT_EQ(
+      "https://safebrowsing.google.com/safebrowsing/clientreport/malware?"
+      "client=unittest&appver=1.0&pver=4.0" +
+          key_param_,
+      ping_manager()->ThreatDetailsUrl().spec());
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/proto/PRESUBMIT.py b/components/safe_browsing/core/proto/PRESUBMIT.py
new file mode 100644
index 0000000..1839ab7f6
--- /dev/null
+++ b/components/safe_browsing/core/proto/PRESUBMIT.py
@@ -0,0 +1,31 @@
+# Copyright 2018 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.
+
+def CheckChangeOnUpload(input_api, output_api):
+  results = []
+
+  # Warn if the proto file is not modified without also modifying
+  # the WebUI and extension API idl file.
+  proto_path = 'components/safe_browsing/core/proto/csd.proto'
+  web_ui_path = 'components/safe_browsing/content/web_ui/safe_browsing_ui.cc'
+  idl_path = 'chrome/common/extensions/api/safe_browsing_private.idl'
+
+  if proto_path in input_api.change.LocalPaths():
+    if web_ui_path not in input_api.change.LocalPaths():
+      results.append(
+          output_api.PresubmitPromptWarning(
+              'You modified the one or more of the CSD protos in: \n'
+              '  ' + proto_path + '\n'
+              'without changing the WebUI in: \n'
+              '  ' + web_ui_path + '\n')
+      )
+    if idl_path not in input_api.change.LocalPaths():
+      results.append(
+          output_api.PresubmitPromptWarning(
+              'You modified the one or more of the CSD protos in: \n'
+              '  ' + proto_path + '\n'
+              'without changing the API definition in: \n'
+              '  ' + idl_path + '\n')
+      )
+  return results
diff --git a/components/safe_browsing/proto/csd.proto b/components/safe_browsing/core/proto/csd.proto
similarity index 100%
rename from components/safe_browsing/proto/csd.proto
rename to components/safe_browsing/core/proto/csd.proto
diff --git a/components/safe_browsing/proto/realtimeapi.proto b/components/safe_browsing/core/proto/realtimeapi.proto
similarity index 100%
rename from components/safe_browsing/proto/realtimeapi.proto
rename to components/safe_browsing/core/proto/realtimeapi.proto
diff --git a/components/safe_browsing/proto/webprotect.proto b/components/safe_browsing/core/proto/webprotect.proto
similarity index 100%
rename from components/safe_browsing/proto/webprotect.proto
rename to components/safe_browsing/core/proto/webprotect.proto
diff --git a/components/safe_browsing/proto/webui.proto b/components/safe_browsing/core/proto/webui.proto
similarity index 100%
rename from components/safe_browsing/proto/webui.proto
rename to components/safe_browsing/core/proto/webui.proto
diff --git a/components/safe_browsing/core/realtime/BUILD.gn b/components/safe_browsing/core/realtime/BUILD.gn
new file mode 100644
index 0000000..65485963
--- /dev/null
+++ b/components/safe_browsing/core/realtime/BUILD.gn
@@ -0,0 +1,60 @@
+# Copyright 2019 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.
+
+static_library("policy_engine") {
+  sources = [
+    "policy_engine.cc",
+    "policy_engine.h",
+  ]
+
+  deps = [
+    "//base:base",
+    "//components/prefs",
+    "//components/safe_browsing/core:features",
+    "//components/safe_browsing/core:realtimeapi_proto",
+    "//components/safe_browsing/core/common:safe_browsing_prefs",
+    "//components/unified_consent",
+    "//components/user_prefs",
+    "//content/public/browser",
+    "//content/public/common:resource_type_header",
+  ]
+}
+
+static_library("url_lookup_service") {
+  sources = [
+    "url_lookup_service.cc",
+    "url_lookup_service.h",
+  ]
+
+  deps = [
+    ":policy_engine",
+    "//base:base",
+    "//components/safe_browsing/core:realtimeapi_proto",
+    "//components/safe_browsing/core/db:v4_protocol_manager_util",
+    "//content/public/browser",
+    "//url:url",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "policy_engine_unittest.cc",
+    "url_lookup_service_unittest.cc",
+  ]
+  deps = [
+    ":policy_engine",
+    ":url_lookup_service",
+    "//base/test:test_support",
+    "//components/safe_browsing/core:features",
+    "//components/safe_browsing/core/common:safe_browsing_prefs",
+    "//components/sync_preferences:test_support",
+    "//components/unified_consent",
+    "//components/user_prefs",
+    "//content/test:test_support",
+    "//services/network:test_support",
+    "//services/network/public/cpp:cpp",
+    "//testing/gtest",
+  ]
+}
diff --git a/components/safe_browsing/core/realtime/policy_engine.cc b/components/safe_browsing/core/realtime/policy_engine.cc
new file mode 100644
index 0000000..a5808d8
--- /dev/null
+++ b/components/safe_browsing/core/realtime/policy_engine.cc
@@ -0,0 +1,79 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/core/realtime/policy_engine.h"
+
+#include "base/feature_list.h"
+#include "base/metrics/histogram_macros.h"
+#include "build/build_config.h"
+#include "components/prefs/pref_service.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/features.h"
+#include "components/unified_consent/pref_names.h"
+#include "components/user_prefs/user_prefs.h"
+#include "content/public/browser/browser_context.h"
+
+#if defined(OS_ANDROID)
+#include "base/metrics/field_trial_params.h"
+#include "base/system/sys_info.h"
+#endif
+
+namespace safe_browsing {
+
+#if defined(OS_ANDROID)
+const int kDefaultMemoryThresholdMb = 4096;
+#endif
+
+// static
+bool RealTimePolicyEngine::IsUrlLookupEnabled() {
+  if (!base::FeatureList::IsEnabled(kRealTimeUrlLookupEnabled))
+    return false;
+#if defined(OS_ANDROID)
+  // On Android, performs real time URL lookup only if
+  // |kRealTimeUrlLookupEnabled| is enabled, and system memory is larger than
+  // threshold.
+  int memory_threshold_mb = base::GetFieldTrialParamByFeatureAsInt(
+      kRealTimeUrlLookupEnabled, kRealTimeUrlLookupMemoryThresholdMb,
+      kDefaultMemoryThresholdMb);
+  return base::SysInfo::AmountOfPhysicalMemoryMB() >= memory_threshold_mb;
+#else
+  return true;
+#endif
+}
+
+// static
+bool RealTimePolicyEngine::IsUserOptedIn(
+    content::BrowserContext* browser_context) {
+  PrefService* pref_service = user_prefs::UserPrefs::Get(browser_context);
+  return pref_service->GetBoolean(
+      unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled);
+}
+
+// static
+bool RealTimePolicyEngine::IsEnabledByPolicy(
+    content::BrowserContext* browser_context) {
+  return false;
+}
+
+// static
+bool RealTimePolicyEngine::CanPerformFullURLLookup(
+    content::BrowserContext* browser_context) {
+  if (browser_context->IsOffTheRecord())
+    return false;
+
+  if (IsEnabledByPolicy(browser_context))
+    return true;
+
+  return IsUrlLookupEnabled() && IsUserOptedIn(browser_context);
+}
+
+// static
+bool RealTimePolicyEngine::CanPerformFullURLLookupForResourceType(
+    content::ResourceType resource_type) {
+  UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.RT.ResourceTypes.Requested",
+                            resource_type);
+  return resource_type == content::ResourceType::kMainFrame;
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/realtime/policy_engine.h b/components/safe_browsing/core/realtime/policy_engine.h
new file mode 100644
index 0000000..f9ce8874
--- /dev/null
+++ b/components/safe_browsing/core/realtime/policy_engine.h
@@ -0,0 +1,58 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CORE_REALTIME_POLICY_ENGINE_H_
+#define COMPONENTS_SAFE_BROWSING_CORE_REALTIME_POLICY_ENGINE_H_
+
+#include "build/build_config.h"
+#include "content/public/common/resource_type.h"
+
+namespace content {
+class BrowserContext;
+}
+
+namespace safe_browsing {
+
+#if defined(OS_ANDROID)
+// A parameter controlled by finch experiment.
+// On Android, performs real time URL lookup only if |kRealTimeUrlLookupEnabled|
+// is enabled, and system memory is larger than threshold.
+const char kRealTimeUrlLookupMemoryThresholdMb[] =
+    "SafeBrowsingRealTimeUrlLookupMemoryThresholdMb";
+#endif
+
+// This class implements the logic to decide whether the real time lookup
+// feature is enabled for a given user/profile.
+class RealTimePolicyEngine {
+ public:
+  RealTimePolicyEngine() = delete;
+  ~RealTimePolicyEngine() = delete;
+
+  // Return true if full URL lookups are enabled for |resource_type|.
+  static bool CanPerformFullURLLookupForResourceType(
+      content::ResourceType resource_type);
+
+  // Return true if the feature to enable full URL lookups is enabled and the
+  // allowlist fetch is enabled for the profile represented by
+  // |browser_context|.
+  static bool CanPerformFullURLLookup(content::BrowserContext* browser_context);
+
+  friend class SafeBrowsingService;
+
+ private:
+  // Is the feature to perform real-time URL lookup enabled?
+  static bool IsUrlLookupEnabled();
+
+  // Is user opted-in to the feature?
+  static bool IsUserOptedIn(content::BrowserContext* browser_context);
+
+  // Is the feature enabled due to enterprise policy?
+  static bool IsEnabledByPolicy(content::BrowserContext* browser_context);
+
+  friend class RealTimePolicyEngineTest;
+};  // class RealTimePolicyEngine
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CORE_REALTIME_POLICY_ENGINE_H_
diff --git a/components/safe_browsing/core/realtime/policy_engine_unittest.cc b/components/safe_browsing/core/realtime/policy_engine_unittest.cc
new file mode 100644
index 0000000..ad496ae
--- /dev/null
+++ b/components/safe_browsing/core/realtime/policy_engine_unittest.cc
@@ -0,0 +1,148 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/core/realtime/policy_engine.h"
+
+#include "base/test/scoped_feature_list.h"
+#include "build/build_config.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/features.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "components/unified_consent/pref_names.h"
+#include "components/unified_consent/unified_consent_service.h"
+#include "components/user_prefs/user_prefs.h"
+#include "content/public/test/browser_task_environment.h"
+#include "content/public/test/test_browser_context.h"
+#include "testing/platform_test.h"
+
+#if defined(OS_ANDROID)
+#include "base/strings/string_number_conversions.h"
+#include "base/system/sys_info.h"
+#endif
+
+namespace safe_browsing {
+
+class RealTimePolicyEngineTest : public PlatformTest {
+ public:
+  void SetUp() override {
+    user_prefs::UserPrefs::Set(&test_context_, &pref_service_);
+    RegisterProfilePrefs(pref_service_.registry());
+    unified_consent::UnifiedConsentService::RegisterPrefs(
+        pref_service_.registry());
+  }
+
+  bool IsUserOptedIn() {
+    return RealTimePolicyEngine::IsUserOptedIn(&test_context_);
+  }
+
+  bool CanPerformFullURLLookup() {
+    return RealTimePolicyEngine::CanPerformFullURLLookup(&test_context_);
+  }
+
+  content::BrowserTaskEnvironment task_environment_;
+  content::TestBrowserContext test_context_;
+  sync_preferences::TestingPrefServiceSyncable pref_service_;
+};
+
+#if defined(OS_ANDROID)
+// Real time URL check on Android is controlled by system memory size, the
+// following tests test that logic.
+TEST_F(RealTimePolicyEngineTest, TestCanPerformFullURLLookup_LargeMemorySize) {
+  base::test::ScopedFeatureList feature_list;
+  int system_memory_size = base::SysInfo::AmountOfPhysicalMemoryMB();
+  int memory_size_threshold = system_memory_size - 1;
+  feature_list.InitWithFeaturesAndParameters(
+      /* enabled_features */ {{kRealTimeUrlLookupEnabled,
+                               {{kRealTimeUrlLookupMemoryThresholdMb,
+                                 base::NumberToString(
+                                     memory_size_threshold)}}}},
+      /* disabled_features */ {});
+  pref_service_.SetUserPref(
+      unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled,
+      std::make_unique<base::Value>(true));
+  EXPECT_TRUE(CanPerformFullURLLookup());
+}
+
+TEST_F(RealTimePolicyEngineTest, TestCanPerformFullURLLookup_SmallMemorySize) {
+  base::test::ScopedFeatureList feature_list;
+  int system_memory_size = base::SysInfo::AmountOfPhysicalMemoryMB();
+  int memory_size_threshold = system_memory_size + 1;
+  feature_list.InitWithFeaturesAndParameters(
+      /* enabled_features */ {{kRealTimeUrlLookupEnabled,
+                               {{kRealTimeUrlLookupMemoryThresholdMb,
+                                 base::NumberToString(
+                                     memory_size_threshold)}}}},
+      /* disabled_features */ {});
+  pref_service_.SetUserPref(
+      unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled,
+      std::make_unique<base::Value>(true));
+  EXPECT_FALSE(CanPerformFullURLLookup());
+}
+
+TEST_F(RealTimePolicyEngineTest,
+       TestCanPerformFullURLLookup_DisabledUrlLookupWithLargeMemorySize) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeaturesAndParameters(
+      /* enabled_features */ {},
+      /* disabled_features */ {kRealTimeUrlLookupEnabled});
+  EXPECT_FALSE(CanPerformFullURLLookup());
+}
+#endif  // defined(OS_ANDROID)
+
+TEST_F(RealTimePolicyEngineTest, TestCanPerformFullURLLookup_EnabledByPolicy) {
+  base::test::ScopedFeatureList feature_list;
+  pref_service_.SetManagedPref(prefs::kSafeBrowsingRealTimeLookupEnabled,
+                               std::make_unique<base::Value>(true));
+  // Verifies that setting the pref still doesn't enable the feature.
+  // See crbug.com/1030815 for details.
+  EXPECT_FALSE(CanPerformFullURLLookup());
+}
+
+TEST_F(RealTimePolicyEngineTest,
+       TestCanPerformFullURLLookup_DisabledUrlLookup) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndDisableFeature(kRealTimeUrlLookupEnabled);
+  EXPECT_FALSE(CanPerformFullURLLookup());
+}
+
+TEST_F(RealTimePolicyEngineTest,
+       TestCanPerformFullURLLookup_DisabledOffTheRecord) {
+  base::test::ScopedFeatureList feature_list;
+  pref_service_.SetManagedPref(prefs::kSafeBrowsingRealTimeLookupEnabled,
+                               std::make_unique<base::Value>(true));
+  test_context_.set_is_off_the_record(true);
+  EXPECT_FALSE(CanPerformFullURLLookup());
+}
+
+TEST_F(RealTimePolicyEngineTest,
+       TestCanPerformFullURLLookup_DisabledUserOptin) {
+  ASSERT_FALSE(IsUserOptedIn());
+}
+
+TEST_F(RealTimePolicyEngineTest, TestCanPerformFullURLLookup_EnabledUserOptin) {
+  pref_service_.SetUserPref(
+      unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled,
+      std::make_unique<base::Value>(true));
+  ASSERT_TRUE(IsUserOptedIn());
+}
+
+TEST_F(RealTimePolicyEngineTest,
+       TestCanPerformFullURLLookup_EnabledMainFrameOnly) {
+  for (int i = 0; i <= static_cast<int>(content::ResourceType::kMaxValue);
+       i++) {
+    content::ResourceType resource_type = static_cast<content::ResourceType>(i);
+    bool enabled = RealTimePolicyEngine::CanPerformFullURLLookupForResourceType(
+        resource_type);
+    switch (resource_type) {
+      case content::ResourceType::kMainFrame:
+        EXPECT_TRUE(enabled);
+        break;
+      default:
+        EXPECT_FALSE(enabled);
+        break;
+    }
+  }
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/realtime/url_lookup_service.cc b/components/safe_browsing/core/realtime/url_lookup_service.cc
new file mode 100644
index 0000000..4f79983
--- /dev/null
+++ b/components/safe_browsing/core/realtime/url_lookup_service.cc
@@ -0,0 +1,270 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/core/realtime/url_lookup_service.h"
+
+#include "base/base64url.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/string_piece.h"
+#include "base/time/time.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
+#include "content/public/browser/browser_thread.h"
+#include "net/base/ip_address.h"
+#include "net/base/load_flags.h"
+#include "net/base/url_util.h"
+#include "net/http/http_status_code.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/simple_url_loader.h"
+
+namespace safe_browsing {
+
+namespace {
+
+const char kRealTimeLookupUrlPrefix[] =
+    "https://safebrowsing.google.com/safebrowsing/clientreport/realtime";
+
+const size_t kMaxFailuresToEnforceBackoff = 3;
+
+const size_t kMinBackOffResetDurationInSeconds = 5 * 60;   //  5 minutes.
+const size_t kMaxBackOffResetDurationInSeconds = 30 * 60;  // 30 minutes.
+
+const size_t kURLLookupTimeoutDurationInSeconds = 10;  // 10 seconds.
+
+// Fragements, usernames and passwords are removed, becuase fragments are only
+// used for local navigations and usernames/passwords are too privacy sensitive.
+GURL SanitizeURL(const GURL& url) {
+  GURL::Replacements replacements;
+  replacements.ClearRef();
+  replacements.ClearUsername();
+  replacements.ClearPassword();
+  return url.ReplaceComponents(replacements);
+}
+
+}  // namespace
+
+RealTimeUrlLookupService::RealTimeUrlLookupService(
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
+    : url_loader_factory_(url_loader_factory) {}
+
+void RealTimeUrlLookupService::StartLookup(
+    const GURL& url,
+    RTLookupRequestCallback request_callback,
+    RTLookupResponseCallback response_callback) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  DCHECK(url.is_valid());
+
+  std::unique_ptr<RTLookupRequest> request = FillRequestProto(url);
+
+  std::string req_data;
+  request->SerializeToString(&req_data);
+  net::NetworkTrafficAnnotationTag traffic_annotation =
+      net::DefineNetworkTrafficAnnotation("safe_browsing_realtime_url_lookup",
+                                          R"(
+        semantics {
+          sender: "Safe Browsing"
+          description:
+            "When Safe Browsing can't detect that a URL is safe based on its "
+            "local database, it sends the top-level URL to Google to verify it "
+            "before showing a warning to the user."
+          trigger:
+            "When a main frame URL fails to match the local hash-prefix "
+            "database of known safe URLs and a valid result from a prior "
+            "lookup is not already cached, this will be sent."
+          data: "The main frame URL that did not match the local safelist."
+          destination: GOOGLE_OWNED_SERVICE
+        }
+        policy {
+          cookies_allowed: YES
+          cookies_store: "Safe Browsing cookie store"
+          setting:
+            "Users can disable Safe Browsing real time URL checks by "
+            "unchecking 'Protect you and your device from dangerous sites' in "
+            "Chromium settings under Privacy, or by unchecking 'Make searches "
+            "and browsing better (Sends URLs of pages you visit to Google)' in "
+            "Chromium settings under Privacy."
+          chrome_policy {
+            SafeBrowsingRealTimeLookupEnabled {
+              policy_options {mode: MANDATORY}
+              SafeBrowsingRealTimeLookupEnabled: false
+            }
+          }
+        })");
+
+  auto resource_request = std::make_unique<network::ResourceRequest>();
+  resource_request->url = GURL(kRealTimeLookupUrlPrefix);
+  resource_request->load_flags = net::LOAD_DISABLE_CACHE;
+  resource_request->method = "POST";
+
+  std::unique_ptr<network::SimpleURLLoader> owned_loader =
+      network::SimpleURLLoader::Create(std::move(resource_request),
+                                       traffic_annotation);
+  network::SimpleURLLoader* loader = owned_loader.get();
+  owned_loader->AttachStringForUpload(req_data, "application/octet-stream");
+  owned_loader->SetTimeoutDuration(
+      base::TimeDelta::FromSeconds(kURLLookupTimeoutDurationInSeconds));
+  owned_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+      url_loader_factory_.get(),
+      base::BindOnce(&RealTimeUrlLookupService::OnURLLoaderComplete,
+                     GetWeakPtr(), loader, base::TimeTicks::Now()));
+
+  pending_requests_[owned_loader.release()] = std::move(response_callback);
+
+  std::move(request_callback).Run(std::move(request));
+}
+
+RealTimeUrlLookupService::~RealTimeUrlLookupService() {
+  for (auto& pending : pending_requests_) {
+    // An empty response is treated as safe.
+    auto response = std::make_unique<RTLookupResponse>();
+    std::move(pending.second).Run(std::move(response));
+    delete pending.first;
+  }
+  pending_requests_.clear();
+}
+
+void RealTimeUrlLookupService::OnURLLoaderComplete(
+    network::SimpleURLLoader* url_loader,
+    base::TimeTicks request_start_time,
+    std::unique_ptr<std::string> response_body) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+  auto it = pending_requests_.find(url_loader);
+  DCHECK(it != pending_requests_.end()) << "Request not found";
+
+  UMA_HISTOGRAM_TIMES("SafeBrowsing.RT.Network.Time",
+                      base::TimeTicks::Now() - request_start_time);
+
+  int net_error = url_loader->NetError();
+  int response_code = 0;
+  if (url_loader->ResponseInfo() && url_loader->ResponseInfo()->headers)
+    response_code = url_loader->ResponseInfo()->headers->response_code();
+  V4ProtocolManagerUtil::RecordHttpResponseOrErrorCode(
+      "SafeBrowsing.RT.Network.Result", net_error, response_code);
+
+  auto response = std::make_unique<RTLookupResponse>();
+  bool success = (net_error == net::OK) && (response_code == net::HTTP_OK) &&
+                 response->ParseFromString(*response_body);
+  success ? HandleLookupSuccess() : HandleLookupError();
+
+  std::move(it->second).Run(std::move(response));
+  delete it->first;
+  pending_requests_.erase(it);
+}
+
+bool RealTimeUrlLookupService::CanCheckUrl(const GURL& url) const {
+  if (!url.SchemeIsHTTPOrHTTPS()) {
+    return false;
+  }
+
+  if (net::IsLocalhost(url)) {
+    // Includes: "//localhost/", "//localhost.localdomain/", "//127.0.0.1/"
+    return false;
+  }
+
+  net::IPAddress ip_address;
+  if (url.HostIsIPAddress() && ip_address.AssignFromIPLiteral(url.host()) &&
+      !ip_address.IsPubliclyRoutable()) {
+    // Includes: "//192.168.1.1/", "//172.16.2.2/", "//10.1.1.1/"
+    return false;
+  }
+
+  return true;
+}
+
+std::unique_ptr<RTLookupRequest> RealTimeUrlLookupService::FillRequestProto(
+    const GURL& url) {
+  auto request = std::make_unique<RTLookupRequest>();
+  request->set_url(SanitizeURL(url).spec());
+  request->set_lookup_type(RTLookupRequest::NAVIGATION);
+  // TODO(crbug.com/1017499): Set ChromeUserPopulation.
+  return request;
+}
+
+size_t RealTimeUrlLookupService::GetBackoffDurationInSeconds() const {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  return did_successful_lookup_since_last_backoff_
+             ? kMinBackOffResetDurationInSeconds
+             : std::min(kMaxBackOffResetDurationInSeconds,
+                        2 * next_backoff_duration_secs_);
+}
+
+void RealTimeUrlLookupService::HandleLookupError() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  consecutive_failures_++;
+
+  // Any successful lookup clears both |consecutive_failures_| as well as
+  // |did_successful_lookup_since_last_backoff_|.
+  // On a failure, the following happens:
+  // 1) if |consecutive_failures_| < |kMaxFailuresToEnforceBackoff|:
+  //    Do nothing more.
+  // 2) if already in the backoff mode:
+  //    Do nothing more. This can happen if we had some outstanding real time
+  //    requests in flight when we entered the backoff mode.
+  // 3) if |did_successful_lookup_since_last_backoff_| is true:
+  //    Enter backoff mode for |kMinBackOffResetDurationInSeconds| seconds.
+  // 4) if |did_successful_lookup_since_last_backoff_| is false:
+  //    This indicates that we've had |kMaxFailuresToEnforceBackoff| since
+  //    exiting the last backoff with no successful lookups since so do an
+  //    exponential backoff.
+
+  if (consecutive_failures_ < kMaxFailuresToEnforceBackoff)
+    return;
+
+  if (IsInBackoffMode()) {
+    return;
+  }
+
+  // Enter backoff mode, calculate duration.
+  next_backoff_duration_secs_ = GetBackoffDurationInSeconds();
+  backoff_timer_.Start(
+      FROM_HERE, base::TimeDelta::FromSeconds(next_backoff_duration_secs_),
+      this, &RealTimeUrlLookupService::ResetFailures);
+  did_successful_lookup_since_last_backoff_ = false;
+}
+
+void RealTimeUrlLookupService::HandleLookupSuccess() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  ResetFailures();
+
+  // |did_successful_lookup_since_last_backoff_| is set to true only when we
+  // complete a lookup successfully.
+  did_successful_lookup_since_last_backoff_ = true;
+}
+
+bool RealTimeUrlLookupService::IsInBackoffMode() const {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  return backoff_timer_.IsRunning();
+}
+
+void RealTimeUrlLookupService::ResetFailures() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  consecutive_failures_ = 0;
+  backoff_timer_.Stop();
+}
+
+// static
+SBThreatType RealTimeUrlLookupService::GetSBThreatTypeForRTThreatType(
+    RTLookupResponse::ThreatInfo::ThreatType rt_threat_type) {
+  switch (rt_threat_type) {
+    case RTLookupResponse::ThreatInfo::WEB_MALWARE:
+      return SB_THREAT_TYPE_URL_MALWARE;
+    case RTLookupResponse::ThreatInfo::SOCIAL_ENGINEERING:
+      return SB_THREAT_TYPE_URL_PHISHING;
+    case RTLookupResponse::ThreatInfo::UNWANTED_SOFTWARE:
+      return SB_THREAT_TYPE_URL_UNWANTED;
+    case RTLookupResponse::ThreatInfo::UNCLEAR_BILLING:
+      return SB_THREAT_TYPE_BILLING;
+    case RTLookupResponse::ThreatInfo::THREAT_TYPE_UNSPECIFIED:
+      NOTREACHED() << "Unexpected RTLookupResponse::ThreatType encountered";
+      return SB_THREAT_TYPE_SAFE;
+  }
+}
+
+base::WeakPtr<RealTimeUrlLookupService> RealTimeUrlLookupService::GetWeakPtr() {
+  return weak_factory_.GetWeakPtr();
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/realtime/url_lookup_service.h b/components/safe_browsing/core/realtime/url_lookup_service.h
new file mode 100644
index 0000000..342cf9d
--- /dev/null
+++ b/components/safe_browsing/core/realtime/url_lookup_service.h
@@ -0,0 +1,129 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CORE_REALTIME_URL_LOOKUP_SERVICE_H_
+#define COMPONENTS_SAFE_BROWSING_CORE_REALTIME_URL_LOOKUP_SERVICE_H_
+
+#include <memory>
+#include <string>
+
+#include "base/callback.h"
+#include "base/containers/flat_map.h"
+#include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/core/proto/realtimeapi.pb.h"
+#include "url/gurl.h"
+
+namespace network {
+class SimpleURLLoader;
+class SharedURLLoaderFactory;
+}  // namespace network
+
+namespace safe_browsing {
+
+using RTLookupRequestCallback =
+    base::OnceCallback<void(std::unique_ptr<RTLookupRequest>)>;
+
+using RTLookupResponseCallback =
+    base::OnceCallback<void(std::unique_ptr<RTLookupResponse>)>;
+
+// This class implements the logic to decide whether the real time lookup
+// feature is enabled for a given user/profile.
+class RealTimeUrlLookupService {
+ public:
+  explicit RealTimeUrlLookupService(
+      scoped_refptr<network::SharedURLLoaderFactory>);
+  ~RealTimeUrlLookupService();
+
+  // Returns true if |url|'s scheme can be checked.
+  bool CanCheckUrl(const GURL& url) const;
+
+  // Returns true if the real time lookups are currently in backoff mode due to
+  // too many prior errors. If this happens, the checking falls back to
+  // local hash-based method.
+  bool IsInBackoffMode() const;
+
+  // Start the full URL lookup for |url|, call |request_callback| on the same
+  // thread when request is sent, call |response_callback| on the same thread
+  // when response is received.
+  void StartLookup(const GURL& url,
+                   RTLookupRequestCallback request_callback,
+                   RTLookupResponseCallback response_callback);
+
+  // Returns the SBThreatType for a given
+  // RTLookupResponse::ThreatInfo::ThreatType
+  static SBThreatType GetSBThreatTypeForRTThreatType(
+      RTLookupResponse::ThreatInfo::ThreatType rt_threat_type);
+
+ private:
+  using PendingRTLookupRequests =
+      base::flat_map<network::SimpleURLLoader*, RTLookupResponseCallback>;
+
+  // Returns the duration of the next backoff. Starts at
+  // |kMinBackOffResetDurationInSeconds| and increases exponentially until it
+  // reaches |kMaxBackOffResetDurationInSeconds|.
+  size_t GetBackoffDurationInSeconds() const;
+
+  // Called when the request to remote endpoint fails. May initiate or extend
+  // backoff.
+  void HandleLookupError();
+
+  // Called when the request to remote endpoint succeeds. Resets error count and
+  // ends backoff.
+  void HandleLookupSuccess();
+
+  // Resets the error count and ends backoff mode. Functionally same as
+  // |HandleLookupSuccess| for now.
+  void ResetFailures();
+
+  // Called when the response from the real-time lookup remote endpoint is
+  // received. |url_loader| is the unowned loader that was used to send the
+  // request. |request_start_time| is the time when the request was sent.
+  // |response_body| is the response received.
+  void OnURLLoaderComplete(network::SimpleURLLoader* url_loader,
+                           base::TimeTicks request_start_time,
+                           std::unique_ptr<std::string> response_body);
+
+  std::unique_ptr<RTLookupRequest> FillRequestProto(const GURL& url);
+
+  // Helper function to return a weak pointer.
+  base::WeakPtr<RealTimeUrlLookupService> GetWeakPtr();
+
+  PendingRTLookupRequests pending_requests_;
+
+  // Count of consecutive failures to complete URL lookup requests. When it
+  // reaches |kMaxFailuresToEnforceBackoff|, we enter the backoff mode. It gets
+  // reset when we complete a lookup successfully or when the backoff reset
+  // timer fires.
+  size_t consecutive_failures_ = 0;
+
+  // If true, represents that one or more real time lookups did complete
+  // successfully since the last backoff or Chrome never entered the breakoff;
+  // if false and Chrome re-enters backoff period, the backoff duration is
+  // increased exponentially (capped at |kMaxBackOffResetDurationInSeconds|).
+  bool did_successful_lookup_since_last_backoff_ = true;
+
+  // The current duration of backoff. Increases exponentially until it reaches
+  // |kMaxBackOffResetDurationInSeconds|.
+  size_t next_backoff_duration_secs_ = 0;
+
+  // If this timer is running, backoff is in effect.
+  base::OneShotTimer backoff_timer_;
+
+  // The URLLoaderFactory we use to issue network requests.
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
+
+  friend class RealTimeUrlLookupServiceTest;
+
+  base::WeakPtrFactory<RealTimeUrlLookupService> weak_factory_{this};
+
+  DISALLOW_COPY_AND_ASSIGN(RealTimeUrlLookupService);
+
+};  // class RealTimeUrlLookupService
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CORE_REALTIME_URL_LOOKUP_SERVICE_H_
diff --git a/components/safe_browsing/core/realtime/url_lookup_service_unittest.cc b/components/safe_browsing/core/realtime/url_lookup_service_unittest.cc
new file mode 100644
index 0000000..6afbffc8
--- /dev/null
+++ b/components/safe_browsing/core/realtime/url_lookup_service_unittest.cc
@@ -0,0 +1,378 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/core/realtime/url_lookup_service.h"
+
+#include "base/test/task_environment.h"
+#include "content/public/test/browser_task_environment.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "testing/platform_test.h"
+
+namespace safe_browsing {
+
+class RealTimeUrlLookupServiceTest : public PlatformTest {
+ public:
+  RealTimeUrlLookupServiceTest()
+      : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
+  void SetUp() override {
+    PlatformTest::SetUp();
+
+    test_shared_loader_factory_ =
+        base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+            &test_url_loader_factory_);
+
+    rt_service_ =
+        std::make_unique<RealTimeUrlLookupService>(test_shared_loader_factory_);
+  }
+
+  bool CanCheckUrl(const GURL& url) { return rt_service_->CanCheckUrl(url); }
+  void HandleLookupError() { rt_service_->HandleLookupError(); }
+  void HandleLookupSuccess() { rt_service_->HandleLookupSuccess(); }
+  bool IsInBackoffMode() { return rt_service_->IsInBackoffMode(); }
+  std::unique_ptr<RTLookupRequest> FillRequestProto(const GURL& url) {
+    return rt_service_->FillRequestProto(url);
+  }
+
+  network::TestURLLoaderFactory test_url_loader_factory_;
+  scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
+  std::unique_ptr<RealTimeUrlLookupService> rt_service_;
+  content::BrowserTaskEnvironment task_environment_;
+};
+
+TEST_F(RealTimeUrlLookupServiceTest, TestFillRequestProto) {
+  struct SanitizeUrlCase {
+    const char* url;
+    const char* expected_url;
+  } sanitize_url_cases[] = {
+      {"http://example.com/", "http://example.com/"},
+      {"http://user:pass@example.com/", "http://example.com/"},
+      {"http://%123:bar@example.com/", "http://example.com/"},
+      {"http://example.com#123", "http://example.com/"}};
+  for (size_t i = 0; i < base::size(sanitize_url_cases); i++) {
+    GURL url(sanitize_url_cases[i].url);
+    auto result = FillRequestProto(url);
+    EXPECT_EQ(sanitize_url_cases[i].expected_url, result->url());
+    EXPECT_EQ(RTLookupRequest::NAVIGATION, result->lookup_type());
+  }
+}
+
+TEST_F(RealTimeUrlLookupServiceTest, TestBackoffAndTimerReset) {
+  // Not in backoff at the beginning.
+  ASSERT_FALSE(IsInBackoffMode());
+
+  // Failure 1: No backoff.
+  HandleLookupError();
+  EXPECT_FALSE(IsInBackoffMode());
+
+  // Failure 2: No backoff.
+  HandleLookupError();
+  EXPECT_FALSE(IsInBackoffMode());
+
+  // Failure 3: Entered backoff.
+  HandleLookupError();
+  EXPECT_TRUE(IsInBackoffMode());
+
+  // Backoff not reset after 1 second.
+  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
+  EXPECT_TRUE(IsInBackoffMode());
+
+  // Backoff not reset after 299 seconds.
+  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(298));
+  EXPECT_TRUE(IsInBackoffMode());
+
+  // Backoff should have been reset after 300 seconds.
+  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
+  EXPECT_FALSE(IsInBackoffMode());
+}
+
+TEST_F(RealTimeUrlLookupServiceTest, TestBackoffAndLookupSuccessReset) {
+  // Not in backoff at the beginning.
+  ASSERT_FALSE(IsInBackoffMode());
+
+  // Failure 1: No backoff.
+  HandleLookupError();
+  EXPECT_FALSE(IsInBackoffMode());
+
+  // Lookup success resets the backoff counter.
+  HandleLookupSuccess();
+  EXPECT_FALSE(IsInBackoffMode());
+
+  // Failure 1: No backoff.
+  HandleLookupError();
+  EXPECT_FALSE(IsInBackoffMode());
+
+  // Failure 2: No backoff.
+  HandleLookupError();
+  EXPECT_FALSE(IsInBackoffMode());
+
+  // Lookup success resets the backoff counter.
+  HandleLookupSuccess();
+  EXPECT_FALSE(IsInBackoffMode());
+
+  // Failure 1: No backoff.
+  HandleLookupError();
+  EXPECT_FALSE(IsInBackoffMode());
+
+  // Failure 2: No backoff.
+  HandleLookupError();
+  EXPECT_FALSE(IsInBackoffMode());
+
+  // Failure 3: Entered backoff.
+  HandleLookupError();
+  EXPECT_TRUE(IsInBackoffMode());
+
+  // Lookup success resets the backoff counter.
+  HandleLookupSuccess();
+  EXPECT_FALSE(IsInBackoffMode());
+}
+
+TEST_F(RealTimeUrlLookupServiceTest, TestExponentialBackoff) {
+  ///////////////////////////////
+  // Initial backoff: 300 seconds
+  ///////////////////////////////
+
+  // Not in backoff at the beginning.
+  ASSERT_FALSE(IsInBackoffMode());
+
+  // Failure 1: No backoff.
+  HandleLookupError();
+  EXPECT_FALSE(IsInBackoffMode());
+
+  // Failure 2: No backoff.
+  HandleLookupError();
+  EXPECT_FALSE(IsInBackoffMode());
+
+  // Failure 3: Entered backoff.
+  HandleLookupError();
+  EXPECT_TRUE(IsInBackoffMode());
+
+  // Backoff not reset after 1 second.
+  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
+  EXPECT_TRUE(IsInBackoffMode());
+
+  // Backoff not reset after 299 seconds.
+  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(298));
+  EXPECT_TRUE(IsInBackoffMode());
+
+  // Backoff should have been reset after 300 seconds.
+  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
+  EXPECT_FALSE(IsInBackoffMode());
+
+  /////////////////////////////////////
+  // Exponential backoff 1: 600 seconds
+  /////////////////////////////////////
+
+  HandleLookupError();
+  EXPECT_FALSE(IsInBackoffMode());
+  HandleLookupError();
+  EXPECT_FALSE(IsInBackoffMode());
+  HandleLookupError();
+  EXPECT_TRUE(IsInBackoffMode());
+
+  // Backoff not reset after 1 second.
+  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
+  EXPECT_TRUE(IsInBackoffMode());
+
+  // Backoff not reset after 599 seconds.
+  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(598));
+  EXPECT_TRUE(IsInBackoffMode());
+
+  // Backoff should have been reset after 600 seconds.
+  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
+  EXPECT_FALSE(IsInBackoffMode());
+
+  //////////////////////////////////////
+  // Exponential backoff 2: 1200 seconds
+  //////////////////////////////////////
+
+  HandleLookupError();
+  EXPECT_FALSE(IsInBackoffMode());
+  HandleLookupError();
+  EXPECT_FALSE(IsInBackoffMode());
+  HandleLookupError();
+  EXPECT_TRUE(IsInBackoffMode());
+
+  // Backoff not reset after 1 second.
+  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
+  EXPECT_TRUE(IsInBackoffMode());
+
+  // Backoff not reset after 1199 seconds.
+  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1198));
+  EXPECT_TRUE(IsInBackoffMode());
+
+  // Backoff should have been reset after 1200 seconds.
+  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
+  EXPECT_FALSE(IsInBackoffMode());
+
+  ///////////////////////////////////////////////////
+  // Exponential backoff 3: 1800 seconds (30 minutes)
+  ///////////////////////////////////////////////////
+
+  HandleLookupError();
+  EXPECT_FALSE(IsInBackoffMode());
+  HandleLookupError();
+  EXPECT_FALSE(IsInBackoffMode());
+  HandleLookupError();
+  EXPECT_TRUE(IsInBackoffMode());
+
+  // Backoff not reset after 1 second.
+  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
+  EXPECT_TRUE(IsInBackoffMode());
+
+  // Backoff not reset after 1799 seconds.
+  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1798));
+  EXPECT_TRUE(IsInBackoffMode());
+
+  // Backoff should have been reset after 1800 seconds.
+  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
+  EXPECT_FALSE(IsInBackoffMode());
+
+  ///////////////////////////////////////////////////
+  // Exponential backoff 4: 1800 seconds (30 minutes)
+  ///////////////////////////////////////////////////
+
+  HandleLookupError();
+  EXPECT_FALSE(IsInBackoffMode());
+  HandleLookupError();
+  EXPECT_FALSE(IsInBackoffMode());
+  HandleLookupError();
+  EXPECT_TRUE(IsInBackoffMode());
+
+  // Backoff not reset after 1 second.
+  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
+  EXPECT_TRUE(IsInBackoffMode());
+
+  // Backoff not reset after 1799 seconds.
+  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1798));
+  EXPECT_TRUE(IsInBackoffMode());
+
+  // Backoff should have been reset after 1800 seconds.
+  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
+  EXPECT_FALSE(IsInBackoffMode());
+}
+
+TEST_F(RealTimeUrlLookupServiceTest, TestExponentialBackoffWithResetOnSuccess) {
+  ///////////////////////////////
+  // Initial backoff: 300 seconds
+  ///////////////////////////////
+
+  // Not in backoff at the beginning.
+  ASSERT_FALSE(IsInBackoffMode());
+
+  // Failure 1: No backoff.
+  HandleLookupError();
+  EXPECT_FALSE(IsInBackoffMode());
+
+  // Failure 2: No backoff.
+  HandleLookupError();
+  EXPECT_FALSE(IsInBackoffMode());
+
+  // Failure 3: Entered backoff.
+  HandleLookupError();
+  EXPECT_TRUE(IsInBackoffMode());
+
+  // Backoff not reset after 1 second.
+  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
+  EXPECT_TRUE(IsInBackoffMode());
+
+  // Backoff not reset after 299 seconds.
+  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(298));
+  EXPECT_TRUE(IsInBackoffMode());
+
+  // Backoff should have been reset after 300 seconds.
+  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
+  EXPECT_FALSE(IsInBackoffMode());
+
+  /////////////////////////////////////
+  // Exponential backoff 1: 600 seconds
+  /////////////////////////////////////
+
+  HandleLookupError();
+  EXPECT_FALSE(IsInBackoffMode());
+  HandleLookupError();
+  EXPECT_FALSE(IsInBackoffMode());
+  HandleLookupError();
+  EXPECT_TRUE(IsInBackoffMode());
+
+  // Backoff not reset after 1 second.
+  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
+  EXPECT_TRUE(IsInBackoffMode());
+
+  // Backoff not reset after 599 seconds.
+  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(598));
+  EXPECT_TRUE(IsInBackoffMode());
+
+  // Backoff should have been reset after 600 seconds.
+  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
+  EXPECT_FALSE(IsInBackoffMode());
+
+  // The next lookup is a success. This should reset the backoff duration to
+  // |kMinBackOffResetDurationInSeconds|
+  HandleLookupSuccess();
+
+  // Failure 1: No backoff.
+  HandleLookupError();
+  EXPECT_FALSE(IsInBackoffMode());
+
+  // Failure 2: No backoff.
+  HandleLookupError();
+  EXPECT_FALSE(IsInBackoffMode());
+
+  // Failure 3: Entered backoff.
+  HandleLookupError();
+  EXPECT_TRUE(IsInBackoffMode());
+
+  // Backoff not reset after 1 second.
+  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
+  EXPECT_TRUE(IsInBackoffMode());
+
+  // Backoff not reset after 299 seconds.
+  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(298));
+  EXPECT_TRUE(IsInBackoffMode());
+
+  // Backoff should have been reset after 300 seconds.
+  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
+  EXPECT_FALSE(IsInBackoffMode());
+}
+
+TEST_F(RealTimeUrlLookupServiceTest, TestGetSBThreatTypeForRTThreatType) {
+  EXPECT_EQ(SB_THREAT_TYPE_URL_MALWARE,
+            RealTimeUrlLookupService::GetSBThreatTypeForRTThreatType(
+                RTLookupResponse::ThreatInfo::WEB_MALWARE));
+  EXPECT_EQ(SB_THREAT_TYPE_URL_PHISHING,
+            RealTimeUrlLookupService::GetSBThreatTypeForRTThreatType(
+                RTLookupResponse::ThreatInfo::SOCIAL_ENGINEERING));
+  EXPECT_EQ(SB_THREAT_TYPE_URL_UNWANTED,
+            RealTimeUrlLookupService::GetSBThreatTypeForRTThreatType(
+                RTLookupResponse::ThreatInfo::UNWANTED_SOFTWARE));
+  EXPECT_EQ(SB_THREAT_TYPE_BILLING,
+            RealTimeUrlLookupService::GetSBThreatTypeForRTThreatType(
+                RTLookupResponse::ThreatInfo::UNCLEAR_BILLING));
+}
+
+TEST_F(RealTimeUrlLookupServiceTest, TestCanCheckUrl) {
+  struct CanCheckUrlCases {
+    const char* url;
+    bool can_check;
+  } can_check_url_cases[] = {{"ftp://example.test/path", false},
+                             {"http://localhost/path", false},
+                             {"http://localhost.localdomain/path", false},
+                             {"http://127.0.0.1/path", false},
+                             {"http://127.0.0.1:2222/path", false},
+                             {"http://192.168.1.1/path", false},
+                             {"http://172.16.2.2/path", false},
+                             {"http://10.1.1.1/path", false},
+                             {"http://10.1.1.1.1/path", true},
+                             {"http://example.test/path", true},
+                             {"https://example.test/path", true}};
+  for (size_t i = 0; i < base::size(can_check_url_cases); i++) {
+    GURL url(can_check_url_cases[i].url);
+    bool expected_can_check = can_check_url_cases[i].can_check;
+    EXPECT_EQ(expected_can_check, CanCheckUrl(url));
+  }
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/safe_browsing_service_interface.cc b/components/safe_browsing/core/safe_browsing_service_interface.cc
new file mode 100644
index 0000000..aa14e52
--- /dev/null
+++ b/components/safe_browsing/core/safe_browsing_service_interface.cc
@@ -0,0 +1,17 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/core/safe_browsing_service_interface.h"
+
+namespace safe_browsing {
+
+SafeBrowsingServiceInterface*
+SafeBrowsingServiceInterface::CreateSafeBrowsingService() {
+  return factory_ ? factory_->CreateSafeBrowsingService() : nullptr;
+}
+
+// static
+SafeBrowsingServiceFactory* SafeBrowsingServiceInterface::factory_ = nullptr;
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/safe_browsing_service_interface.h b/components/safe_browsing/core/safe_browsing_service_interface.h
new file mode 100644
index 0000000..d0bceb3
--- /dev/null
+++ b/components/safe_browsing/core/safe_browsing_service_interface.h
@@ -0,0 +1,67 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CORE_SAFE_BROWSING_SERVICE_INTERFACE_H_
+#define COMPONENTS_SAFE_BROWSING_CORE_SAFE_BROWSING_SERVICE_INTERFACE_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace safe_browsing {
+
+class SafeBrowsingServiceFactory;
+
+// This interface will provide methods for checking the safety of URLs and
+// downloads with Safe Browsing.
+class SafeBrowsingServiceInterface
+    : public base::RefCountedThreadSafe<
+          SafeBrowsingServiceInterface,
+          content::BrowserThread::DeleteOnUIThread> {
+ public:
+  // Makes the passed |factory| the factory used to instantiate
+  // a SafeBrowsingServiceInterface. Useful for tests.
+  static void RegisterFactory(SafeBrowsingServiceFactory* factory) {
+    factory_ = factory;
+  }
+
+  static bool HasFactory() { return (factory_ != nullptr); }
+
+  // Create an instance of the safe browsing service.
+  static SafeBrowsingServiceInterface* CreateSafeBrowsingService();
+
+ protected:
+  SafeBrowsingServiceInterface() {}
+  virtual ~SafeBrowsingServiceInterface() {}
+
+ private:
+  friend struct content::BrowserThread::DeleteOnThread<
+      content::BrowserThread::UI>;
+  friend class base::DeleteHelper<SafeBrowsingServiceInterface>;
+
+  // The factory used to instantiate a SafeBrowsingServiceInterface object.
+  // Useful for tests, so they can provide their own implementation of
+  // SafeBrowsingServiceInterface.
+  static SafeBrowsingServiceFactory* factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(SafeBrowsingServiceInterface);
+};
+
+// Factory for creating SafeBrowsingServiceInterface.  Useful for tests.
+class SafeBrowsingServiceFactory {
+ public:
+  SafeBrowsingServiceFactory() {}
+  virtual ~SafeBrowsingServiceFactory() {}
+
+  // TODO(crbug/925153): Once callers of this function are no longer downcasting
+  // it to the SafeBrowsingService, we can make this a scoped_refptr.
+  virtual SafeBrowsingServiceInterface* CreateSafeBrowsingService() = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SafeBrowsingServiceFactory);
+};
+
+}  // namespace safe_browsing
+
+#endif  //  COMPONENTS_SAFE_BROWSING_CORE_SAFE_BROWSING_SERVICE_INTERFACE_H_
diff --git a/components/safe_browsing/core/triggers/BUILD.gn b/components/safe_browsing/core/triggers/BUILD.gn
new file mode 100644
index 0000000..2bfb90df
--- /dev/null
+++ b/components/safe_browsing/core/triggers/BUILD.gn
@@ -0,0 +1,63 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/features.gni")
+
+source_set("triggers") {
+  sources = [
+    "trigger_manager.cc",
+    "trigger_manager.h",
+  ]
+  public_deps = [
+    "//components/security_interstitials/content:security_interstitial_page",
+    "//components/security_interstitials/core:core",
+    "//content/public/browser:browser",
+  ]
+  deps = [
+    ":trigger_throttler",
+    "//base:base",
+    "//components/prefs:prefs",
+    "//components/safe_browsing/content",
+    "//components/safe_browsing/content/browser",
+    "//components/safe_browsing/core:features",
+    "//components/safe_browsing/core/browser:browser",
+    "//components/safe_browsing/core/browser:referrer_chain_provider",
+    "//net:net",
+  ]
+}
+
+source_set("trigger_throttler") {
+  sources = [
+    "trigger_throttler.cc",
+    "trigger_throttler.h",
+  ]
+  deps = [
+    "//base:base",
+    "//components/prefs:prefs",
+    "//components/safe_browsing/core:features",
+    "//components/safe_browsing/core/common:safe_browsing_prefs",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "trigger_manager_unittest.cc",
+    "trigger_throttler_unittest.cc",
+  ]
+  deps = [
+    ":trigger_throttler",
+    ":triggers",
+    "//base",
+    "//base/test:test_support",
+    "//components/prefs:test_support",
+    "//components/safe_browsing/content/browser",
+    "//components/safe_browsing/core:features",
+    "//components/safe_browsing/core/browser:browser",
+    "//content/public/browser:browser",
+    "//content/test:test_support",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+}
diff --git a/components/safe_browsing/triggers/DEPS b/components/safe_browsing/core/triggers/DEPS
similarity index 100%
rename from components/safe_browsing/triggers/DEPS
rename to components/safe_browsing/core/triggers/DEPS
diff --git a/components/safe_browsing/triggers/OWNERS b/components/safe_browsing/core/triggers/OWNERS
similarity index 100%
rename from components/safe_browsing/triggers/OWNERS
rename to components/safe_browsing/core/triggers/OWNERS
diff --git a/components/safe_browsing/core/triggers/trigger_manager.cc b/components/safe_browsing/core/triggers/trigger_manager.cc
new file mode 100644
index 0000000..2165416
--- /dev/null
+++ b/components/safe_browsing/core/triggers/trigger_manager.cc
@@ -0,0 +1,257 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/core/triggers/trigger_manager.h"
+
+#include "base/bind.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/prefs/pref_service.h"
+#include "components/safe_browsing/content/base_ui_manager.h"
+#include "components/safe_browsing/content/browser/threat_details.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/features.h"
+#include "components/security_interstitials/content/unsafe_resource.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+
+namespace safe_browsing {
+
+namespace {
+
+bool TriggerNeedsOptInForCollection(const TriggerType trigger_type) {
+  switch (trigger_type) {
+    case TriggerType::SECURITY_INTERSTITIAL:
+      // For security interstitials, users can change the opt-in while the
+      // trigger runs, so collection can begin without opt-in.
+      return false;
+    case TriggerType::AD_POPUP:
+    case TriggerType::AD_REDIRECT:
+    case TriggerType::AD_SAMPLE:
+      // Ad samples happen in the background so the user must already be opted
+      // in before the trigger is allowed to run.
+      return true;
+    case TriggerType::GAIA_PASSWORD_REUSE:
+      // For Gaia password reuses, it is unlikely for users to change opt-in
+      // while the trigger runs, so we require opt-in for collection to avoid
+      // overheads.
+      return true;
+    case TriggerType::SUSPICIOUS_SITE:
+      // Suspicious site collection happens in the background so the user must
+      // already be opted in before the trigger is allowed to run.
+      return true;
+    case TriggerType::APK_DOWNLOAD:
+      // APK download collection happens in the background so the user must
+      // already be opted in before the trigger is allowed to run.
+      return true;
+  }
+  // By default, require opt-in for all triggers.
+  return true;
+}
+
+bool CanSendReport(const SBErrorOptions& error_display_options,
+                   const TriggerType trigger_type) {
+  // Reports are only sent for non-incoginito users who are allowed to modify
+  // the Extended Reporting setting and have opted-in to Extended Reporting.
+  return !error_display_options.is_off_the_record &&
+         error_display_options.is_extended_reporting_opt_in_allowed &&
+         error_display_options.is_extended_reporting_enabled;
+}
+
+}  // namespace
+
+DataCollectorsContainer::DataCollectorsContainer() {}
+DataCollectorsContainer::~DataCollectorsContainer() {}
+
+TriggerManager::TriggerManager(BaseUIManager* ui_manager,
+                               ReferrerChainProvider* referrer_chain_provider,
+                               PrefService* local_state_prefs)
+    : ui_manager_(ui_manager),
+      referrer_chain_provider_(referrer_chain_provider),
+      trigger_throttler_(new TriggerThrottler(local_state_prefs)) {}
+
+TriggerManager::~TriggerManager() {}
+
+void TriggerManager::set_trigger_throttler(TriggerThrottler* throttler) {
+  trigger_throttler_.reset(throttler);
+}
+
+// static
+SBErrorOptions TriggerManager::GetSBErrorDisplayOptions(
+    const PrefService& pref_service,
+    content::WebContents* web_contents) {
+  return SBErrorOptions(/*is_main_frame_load_blocked=*/false,
+                        IsExtendedReportingOptInAllowed(pref_service),
+                        web_contents->GetBrowserContext()->IsOffTheRecord(),
+                        IsExtendedReportingEnabled(pref_service),
+                        IsExtendedReportingPolicyManaged(pref_service),
+                        /*is_proceed_anyway_disabled=*/false,
+                        /*should_open_links_in_new_tab=*/false,
+                        /*show_back_to_safety_button=*/true,
+                        /*help_center_article_link=*/std::string());
+}
+
+bool TriggerManager::CanStartDataCollection(
+    const SBErrorOptions& error_display_options,
+    const TriggerType trigger_type) {
+  TriggerManagerReason unused_reason;
+  return CanStartDataCollectionWithReason(error_display_options, trigger_type,
+                                          &unused_reason);
+}
+
+bool TriggerManager::CanStartDataCollectionWithReason(
+    const SBErrorOptions& error_display_options,
+    const TriggerType trigger_type,
+    TriggerManagerReason* out_reason) {
+  *out_reason = TriggerManagerReason::NO_REASON;
+
+  // Some triggers require that the user be opted-in to extended reporting in
+  // order to run, while others can run without opt-in (eg: because users are
+  // prompted for opt-in as part of the trigger).
+  bool optin_required_check_ok =
+      !TriggerNeedsOptInForCollection(trigger_type) ||
+      error_display_options.is_extended_reporting_enabled;
+  // We start data collection as long as user is not incognito and is able to
+  // change the Extended Reporting opt-in, and the |trigger_type| has available
+  // quota. For some triggers we also require extended reporting opt-in in
+  // order to start data collection.
+  if (!error_display_options.is_off_the_record &&
+      error_display_options.is_extended_reporting_opt_in_allowed &&
+      optin_required_check_ok) {
+    bool quota_ok = trigger_throttler_->TriggerCanFire(trigger_type);
+    if (!quota_ok)
+      *out_reason = TriggerManagerReason::DAILY_QUOTA_EXCEEDED;
+    return quota_ok;
+  } else {
+    *out_reason = TriggerManagerReason::USER_PREFERENCES;
+    return false;
+  }
+}
+
+bool TriggerManager::StartCollectingThreatDetails(
+    const TriggerType trigger_type,
+    content::WebContents* web_contents,
+    const security_interstitials::UnsafeResource& resource,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    history::HistoryService* history_service,
+    const SBErrorOptions& error_display_options) {
+  TriggerManagerReason unused_reason;
+  return StartCollectingThreatDetailsWithReason(
+      trigger_type, web_contents, resource, url_loader_factory, history_service,
+      error_display_options, &unused_reason);
+}
+
+bool TriggerManager::StartCollectingThreatDetailsWithReason(
+    const TriggerType trigger_type,
+    content::WebContents* web_contents,
+    const security_interstitials::UnsafeResource& resource,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    history::HistoryService* history_service,
+    const SBErrorOptions& error_display_options,
+    TriggerManagerReason* reason) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  if (!CanStartDataCollectionWithReason(error_display_options, trigger_type,
+                                        reason))
+    return false;
+
+  // Ensure we're not already collecting ThreatDetails on this tab. Create an
+  // entry in the map for this |web_contents| if it's not there already.
+  DataCollectorsContainer* collectors = &data_collectors_map_[web_contents];
+  if (collectors->threat_details != nullptr)
+    return false;
+
+  bool should_trim_threat_details = (trigger_type == TriggerType::AD_POPUP ||
+                                     trigger_type == TriggerType::AD_SAMPLE ||
+                                     trigger_type == TriggerType::AD_REDIRECT);
+  collectors->threat_details = ThreatDetails::NewThreatDetails(
+      ui_manager_, web_contents, resource, url_loader_factory, history_service,
+      referrer_chain_provider_, should_trim_threat_details,
+      base::BindOnce(&TriggerManager::ThreatDetailsDone,
+                     weak_factory_.GetWeakPtr()));
+  return true;
+}
+
+bool TriggerManager::FinishCollectingThreatDetails(
+    const TriggerType trigger_type,
+    content::WebContents* web_contents,
+    const base::TimeDelta& delay,
+    bool did_proceed,
+    int num_visits,
+    const SBErrorOptions& error_display_options) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  // Make sure there's a ThreatDetails collector running on this tab.
+  if (!base::Contains(data_collectors_map_, web_contents))
+    return false;
+  DataCollectorsContainer* collectors = &data_collectors_map_[web_contents];
+  if (collectors->threat_details == nullptr)
+    return false;
+
+  // Determine whether a report should be sent.
+  bool should_send_report = CanSendReport(error_display_options, trigger_type);
+
+  if (should_send_report) {
+    // Find the data collector and tell it to finish collecting data. We expect
+    // it to notify us when it's finished so we can clean up references to it.
+
+    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+        FROM_HERE,
+        base::BindOnce(&ThreatDetails::FinishCollection,
+                       collectors->threat_details->GetWeakPtr(), did_proceed,
+                       num_visits),
+        delay);
+
+    // Record that this trigger fired and collected data.
+    trigger_throttler_->TriggerFired(trigger_type);
+  } else {
+    // We aren't telling ThreatDetails to finish the report so we should clean
+    // up our map ourselves.
+    ThreatDetailsDone(web_contents);
+  }
+
+  return should_send_report;
+}
+
+void TriggerManager::ThreatDetailsDone(content::WebContents* web_contents) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  // Clean up the ThreatDetailsdata collector on the specified tab.
+  if (!base::Contains(data_collectors_map_, web_contents))
+    return;
+
+  DataCollectorsContainer* collectors = &data_collectors_map_[web_contents];
+  collectors->threat_details = nullptr;
+}
+
+void TriggerManager::WebContentsDestroyed(content::WebContents* web_contents) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  if (!base::Contains(data_collectors_map_, web_contents))
+    return;
+  data_collectors_map_.erase(web_contents);
+}
+
+TriggerManagerWebContentsHelper::TriggerManagerWebContentsHelper(
+    content::WebContents* web_contents,
+    TriggerManager* trigger_manager)
+    : content::WebContentsObserver(web_contents),
+      trigger_manager_(trigger_manager) {}
+
+TriggerManagerWebContentsHelper::~TriggerManagerWebContentsHelper() {}
+
+void TriggerManagerWebContentsHelper::CreateForWebContents(
+    content::WebContents* web_contents,
+    TriggerManager* trigger_manager) {
+  DCHECK(web_contents);
+  if (!FromWebContents(web_contents)) {
+    web_contents->SetUserData(
+        UserDataKey(), base::WrapUnique(new TriggerManagerWebContentsHelper(
+                           web_contents, trigger_manager)));
+  }
+}
+
+void TriggerManagerWebContentsHelper::WebContentsDestroyed() {
+  trigger_manager_->WebContentsDestroyed(web_contents());
+}
+
+WEB_CONTENTS_USER_DATA_KEY_IMPL(TriggerManagerWebContentsHelper)
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/triggers/trigger_manager.h b/components/safe_browsing/core/triggers/trigger_manager.h
new file mode 100644
index 0000000..ae1560f
--- /dev/null
+++ b/components/safe_browsing/core/triggers/trigger_manager.h
@@ -0,0 +1,226 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CORE_TRIGGERS_TRIGGER_MANAGER_H_
+#define COMPONENTS_SAFE_BROWSING_CORE_TRIGGERS_TRIGGER_MANAGER_H_
+
+#include <unordered_map>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "components/safe_browsing/core/browser/referrer_chain_provider.h"
+#include "components/safe_browsing/core/triggers/trigger_throttler.h"
+#include "components/security_interstitials/content/unsafe_resource.h"
+#include "components/security_interstitials/core/base_safe_browsing_error_ui.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_contents_user_data.h"
+
+class PrefService;
+
+namespace history {
+class HistoryService;
+}
+
+namespace network {
+class SharedURLLoaderFactory;
+}
+
+namespace safe_browsing {
+
+class BaseUIManager;
+class ThreatDetails;
+
+// A wrapper around different kinds of data collectors that can be active on a
+// given browser tab. Any given field can be null or empty if the associated
+// data is not being collected.
+struct DataCollectorsContainer {
+ public:
+  DataCollectorsContainer();
+  ~DataCollectorsContainer();
+
+  // Note: new data collection types should be added below as additional fields.
+
+  // Collects ThreatDetails which contains resource URLs and partial DOM.
+  std::unique_ptr<ThreatDetails> threat_details;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DataCollectorsContainer);
+};
+
+// Stores the data collectors that are active on each WebContents (ie: browser
+// tab).
+using DataCollectorsMap =
+    std::unordered_map<content::WebContents*, DataCollectorsContainer>;
+
+using SBErrorOptions =
+    security_interstitials::BaseSafeBrowsingErrorUI::SBErrorDisplayOptions;
+
+// The reasons that trigger manager fails to create or finish a report.
+// These values are written to logs. New enum values can be added, but
+// existing enums must never be renumbered or deleted and reused.
+enum class TriggerManagerReason {
+  // Default value, used when there is no failure.
+  NO_REASON = 0,
+  // User preferences do not allow the report to be started or finished.
+  USER_PREFERENCES = 1,
+  // A report is already started on this tab, so no new report is started.
+  REPORT_ALREADY_STARTED = 2,
+  // There is no report to finish on this tab.
+  NO_REPORT_TO_FINISH = 3,
+  // No report is started because the user has exceeded their daily quota.
+  DAILY_QUOTA_EXCEEDED = 4,
+  // New reasons must be added before kMaxValue and the value of kMaxValue
+  // updated.
+  kMaxValue = DAILY_QUOTA_EXCEEDED
+};
+
+// This class manages SafeBrowsing data-reporting triggers. Triggers are
+// activated for users opted-in to Extended Reporting and when security-related
+// data collection is required.
+//
+// The TriggerManager has two main responsibilities: 1) ensuring triggers only
+// run when appropriate, by honouring user opt-ins and incognito state, and 2)
+// tracking how often triggers fire and throttling them when necessary.
+class TriggerManager {
+ public:
+  TriggerManager(BaseUIManager* ui_manager,
+                 ReferrerChainProvider* referrer_chain_provider,
+                 PrefService* local_state_prefs);
+  virtual ~TriggerManager();
+
+  // Returns a SBErrorDisplayOptions struct containing user state that is
+  // relevant for TriggerManager to decide whether to start/finish data
+  // collection. Looks at incognito state from |web_contents|, and opt-ins from
+  // |pref_service|. Only the fields needed by TriggerManager will be set.
+  static SBErrorOptions GetSBErrorDisplayOptions(
+      const PrefService& pref_service,
+      content::WebContents* web_contents);
+
+  // Returns whether data collection can be started for the |trigger_type| based
+  // on the settings specified in |error_display_options| as well as quota.
+  // If false is returned, |out_reason| will be specify the reason.
+  bool CanStartDataCollectionWithReason(
+      const SBErrorOptions& error_display_options,
+      const TriggerType trigger_type,
+      TriggerManagerReason* out_reason);
+
+  // Simplified signature for |CanStartDataCollectionWithReason| for callers
+  // that don't care about the reason.
+  bool CanStartDataCollection(const SBErrorOptions& error_display_options,
+                              const TriggerType trigger_type);
+
+  // Begins collecting a ThreatDetails report on the specified |web_contents|.
+  // |resource| is the unsafe resource that cause the collection to occur.
+  // |url_loader_factory| is used to retrieve data from the HTTP cache.
+  // |history_service| is used to get data about redirects.
+  // |error_display_options| contains the current state of relevant user
+  // preferences. We use this object for interop with WebView, in Chrome it
+  // should be created by TriggerManager::GetSBErrorDisplayOptions().
+  // Returns true if the collection began, or false if it didn't.
+  // If false is returned, |out_reason| is set to the reason the report didn't
+  // start.
+  virtual bool StartCollectingThreatDetailsWithReason(
+      TriggerType trigger_type,
+      content::WebContents* web_contents,
+      const security_interstitials::UnsafeResource& resource,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      history::HistoryService* history_service,
+      const SBErrorOptions& error_display_options,
+      TriggerManagerReason* out_reason);
+
+  // Simplified signature for |StartCollectingThreatDetailsWithReason| for
+  // callers that don't care about the reason.
+  virtual bool StartCollectingThreatDetails(
+      TriggerType trigger_type,
+      content::WebContents* web_contents,
+      const security_interstitials::UnsafeResource& resource,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      history::HistoryService* history_service,
+      const SBErrorOptions& error_display_options);
+
+  // Completes the collection of a ThreatDetails report on the specified
+  // |web_contents| and sends the report. |delay| can be used to wait a period
+  // of time before finishing the report. |did_proceed| indicates whether the
+  // user proceeded through the security interstitial associated with this
+  // report. |num_visits| is how many times the user has visited the site
+  // before. |error_display_options| contains the current state of relevant user
+  // preferences. We use this object for interop with WebView, in Chrome it
+  // should be created by TriggerManager::GetSBErrorDisplayOptions().
+  // Returns true if the report was completed and sent, or false otherwise (eg:
+  // the user was not opted-in to extended reporting after collection began).
+  virtual bool FinishCollectingThreatDetails(
+      TriggerType trigger_type,
+      content::WebContents* web_contents,
+      const base::TimeDelta& delay,
+      bool did_proceed,
+      int num_visits,
+      const SBErrorOptions& error_display_options);
+
+  // Called when a ThreatDetails report finishes for the specified
+  // |web_contents|.
+  void ThreatDetailsDone(content::WebContents* web_contents);
+
+  // Called when the specified |web_contents| is being destroyed. Used to clean
+  // up our map.
+  void WebContentsDestroyed(content::WebContents* web_contents);
+
+ private:
+  friend class TriggerManagerTest;
+
+  // For testing only - allows injecting a mock Throttler.
+  void set_trigger_throttler(TriggerThrottler* throttler);
+
+  // The UI manager is used to send reports to Google. Not owned.
+  // TODO(lpz): we may only need a the PingManager here.
+  BaseUIManager* ui_manager_;
+
+  // The Referrer Chain Provider is used to retrieve the referrer chain for
+  // reports that require it. Not owned.
+  ReferrerChainProvider* referrer_chain_provider_;
+
+  // Map of the data collectors running on each tabs. New keys are added the
+  // first time any trigger tries to collect data on a tab and are removed when
+  // the tab is destroyed. The values can be null if a trigger has finished on
+  // a tab but the tab remains open.
+  DataCollectorsMap data_collectors_map_;
+
+  // Keeps track of how often triggers fire and throttles them when needed.
+  std::unique_ptr<TriggerThrottler> trigger_throttler_;
+
+  base::WeakPtrFactory<TriggerManager> weak_factory_{this};
+  // WeakPtrFactory should be last, don't add any members below it.
+  DISALLOW_COPY_AND_ASSIGN(TriggerManager);
+};
+
+// A helper class that listens for events happening on a WebContents and can
+// notify TriggerManager of any that are relevant.
+class TriggerManagerWebContentsHelper
+    : public content::WebContentsObserver,
+      public content::WebContentsUserData<TriggerManagerWebContentsHelper> {
+ public:
+  ~TriggerManagerWebContentsHelper() override;
+
+  // Creates a TriggerManagerWebContentsHelper and scopes its lifetime to the
+  // specified |web_contents|.
+  static void CreateForWebContents(content::WebContents* web_contents,
+                                   TriggerManager* trigger_manager);
+
+  // WebContentsObserver implementation.
+  void WebContentsDestroyed() override;
+
+ private:
+  friend class content::WebContentsUserData<TriggerManagerWebContentsHelper>;
+
+  TriggerManagerWebContentsHelper(content::WebContents* web_contents,
+                                  TriggerManager* trigger_manager);
+
+  // Trigger Manager will be notified of any relevant WebContents events.
+  TriggerManager* trigger_manager_;
+  WEB_CONTENTS_USER_DATA_KEY_DECL();
+};
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CORE_TRIGGERS_TRIGGER_MANAGER_H_
diff --git a/components/safe_browsing/core/triggers/trigger_manager_unittest.cc b/components/safe_browsing/core/triggers/trigger_manager_unittest.cc
new file mode 100644
index 0000000..bf0f9b0
--- /dev/null
+++ b/components/safe_browsing/core/triggers/trigger_manager_unittest.cc
@@ -0,0 +1,462 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/core/triggers/trigger_manager.h"
+
+#include "base/run_loop.h"
+#include "base/stl_util.h"
+#include "base/test/scoped_feature_list.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/safe_browsing/content/browser/threat_details.h"
+#include "components/safe_browsing/core/features.h"
+#include "components/safe_browsing/core/triggers/trigger_throttler.h"
+#include "content/public/test/browser_task_environment.h"
+#include "content/public/test/test_browser_context.h"
+#include "content/public/test/test_web_contents_factory.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::Key;
+using testing::Return;
+using testing::UnorderedElementsAre;
+
+namespace safe_browsing {
+
+// Mock ThreatDetails class that makes FinishCollection a no-op.
+class MockThreatDetails : public ThreatDetails {
+ public:
+  MockThreatDetails() {}
+  ~MockThreatDetails() override {}
+  MOCK_METHOD2(FinishCollection, void(bool did_proceed, int num_visits));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockThreatDetails);
+};
+
+class MockThreatDetailsFactory : public ThreatDetailsFactory {
+ public:
+  ~MockThreatDetailsFactory() override {}
+
+  std::unique_ptr<ThreatDetails> CreateThreatDetails(
+      BaseUIManager* ui_manager,
+      content::WebContents* web_contents,
+      const security_interstitials::UnsafeResource& unsafe_resource,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      history::HistoryService* history_service,
+      ReferrerChainProvider* referrer_chain_provider,
+      bool trim_to_ad_tags,
+      ThreatDetailsDoneCallback done_callback) override {
+    return std::make_unique<MockThreatDetails>();
+  }
+};
+
+class MockTriggerThrottler : public TriggerThrottler {
+ public:
+  MockTriggerThrottler() : TriggerThrottler(nullptr) {}
+  MOCK_CONST_METHOD1(TriggerCanFire, bool(TriggerType trigger_type));
+};
+
+class TriggerManagerTest : public ::testing::Test {
+ public:
+  TriggerManagerTest() : trigger_manager_(nullptr, nullptr, nullptr) {}
+  ~TriggerManagerTest() override {}
+
+  void SetUp() override {
+    ThreatDetails::RegisterFactory(&mock_threat_details_factory_);
+
+    // Register any prefs that are needed by the trigger manager. By default,
+    // enable Safe Browsing and Extended Reporting.
+    safe_browsing::RegisterProfilePrefs(pref_service_.registry());
+    SetPref(prefs::kSafeBrowsingExtendedReportingOptInAllowed, true);
+    SetPref(prefs::kSafeBrowsingScoutReportingEnabled, true);
+
+    MockTriggerThrottler* mock_throttler = new MockTriggerThrottler();
+    ON_CALL(*mock_throttler, TriggerCanFire(_)).WillByDefault(Return(true));
+    // Trigger Manager takes ownership of the mock throttler.
+    trigger_manager_.set_trigger_throttler(mock_throttler);
+  }
+
+  void SetPref(const std::string& pref, bool value) {
+    pref_service_.SetBoolean(pref, value);
+  }
+
+  void SetManagedPref(const std::string& pref, bool value) {
+    pref_service_.SetManagedPref(pref, std::make_unique<base::Value>(value));
+  }
+
+  bool GetPref(const std::string& pref) {
+    return pref_service_.GetBoolean(pref);
+  }
+
+  void SetTriggerHasQuota(const TriggerType trigger_type, bool has_quota) {
+    MockTriggerThrottler* mock_throttler = static_cast<MockTriggerThrottler*>(
+        trigger_manager_.trigger_throttler_.get());
+    EXPECT_CALL(*mock_throttler, TriggerCanFire(trigger_type))
+        .WillOnce(Return(has_quota));
+  }
+
+  content::WebContents* CreateWebContents() {
+    DCHECK(!browser_context_.IsOffTheRecord())
+        << "CreateWebContents() should not be called after "
+           "CreateIncognitoWebContents()";
+    return web_contents_factory_.CreateWebContents(&browser_context_);
+  }
+
+  content::WebContents* CreateIncognitoWebContents() {
+    browser_context_.set_is_off_the_record(true);
+    return web_contents_factory_.CreateWebContents(&browser_context_);
+  }
+
+  bool StartCollectingThreatDetails(const TriggerType trigger_type,
+                                    content::WebContents* web_contents) {
+    SBErrorOptions options =
+        TriggerManager::GetSBErrorDisplayOptions(pref_service_, web_contents);
+    return trigger_manager_.StartCollectingThreatDetails(
+        trigger_type, web_contents, security_interstitials::UnsafeResource(),
+        nullptr, nullptr, options);
+  }
+
+  bool FinishCollectingThreatDetails(const TriggerType trigger_type,
+                                     content::WebContents* web_contents,
+                                     bool expect_report_sent) {
+    if (expect_report_sent) {
+      MockThreatDetails* threat_details = static_cast<MockThreatDetails*>(
+          trigger_manager_.data_collectors_map_[web_contents]
+              .threat_details.get());
+      EXPECT_CALL(*threat_details, FinishCollection(_, _)).Times(1);
+    }
+    SBErrorOptions options =
+        TriggerManager::GetSBErrorDisplayOptions(pref_service_, web_contents);
+    bool result = trigger_manager_.FinishCollectingThreatDetails(
+        trigger_type, web_contents, base::TimeDelta(), false, 0, options);
+
+    // Invoke the callback if the report was to be sent.
+    if (expect_report_sent) {
+      // Allow the ThreatDetails to complete, then remove it.
+      base::RunLoop().RunUntilIdle();
+      trigger_manager_.ThreatDetailsDone(web_contents);
+    }
+
+    return result;
+  }
+
+  const DataCollectorsMap& data_collectors_map() {
+    return trigger_manager_.data_collectors_map_;
+  }
+
+ private:
+  TriggerManager trigger_manager_;
+  MockThreatDetailsFactory mock_threat_details_factory_;
+  content::BrowserTaskEnvironment task_environment_;
+  content::TestBrowserContext browser_context_;
+  content::TestWebContentsFactory web_contents_factory_;
+  TestingPrefServiceSimple pref_service_;
+  std::unique_ptr<base::test::ScopedFeatureList> feature_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(TriggerManagerTest);
+};
+
+TEST_F(TriggerManagerTest, StartAndFinishCollectingThreatDetails) {
+  // Basic workflow is to start and finish data collection with a single
+  // WebContents.
+  content::WebContents* web_contents1 = CreateWebContents();
+  EXPECT_TRUE(StartCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
+                                           web_contents1));
+  EXPECT_THAT(data_collectors_map(), UnorderedElementsAre(Key(web_contents1)));
+  EXPECT_NE(data_collectors_map().find(web_contents1),
+            data_collectors_map().end());
+  EXPECT_NE(nullptr, data_collectors_map().at(web_contents1).threat_details);
+  EXPECT_TRUE(FinishCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
+                                            web_contents1, true));
+  EXPECT_NE(data_collectors_map().find(web_contents1),
+            data_collectors_map().end());
+  EXPECT_EQ(nullptr, data_collectors_map().at(web_contents1).threat_details);
+
+  // More complex scenarios can happen, where collection happens on two
+  // WebContents at the same time, possibly starting and completing in different
+  // order.
+  content::WebContents* web_contents2 = CreateWebContents();
+  EXPECT_TRUE(StartCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
+                                           web_contents1));
+  EXPECT_TRUE(StartCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
+                                           web_contents2));
+  EXPECT_THAT(data_collectors_map(),
+              UnorderedElementsAre(Key(web_contents1), Key(web_contents2)));
+  EXPECT_NE(data_collectors_map().find(web_contents1),
+            data_collectors_map().end());
+  EXPECT_NE(nullptr, data_collectors_map().at(web_contents1).threat_details);
+  EXPECT_NE(data_collectors_map().find(web_contents2),
+            data_collectors_map().end());
+  EXPECT_NE(nullptr, data_collectors_map().at(web_contents2).threat_details);
+  EXPECT_TRUE(FinishCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
+                                            web_contents2, true));
+  EXPECT_NE(data_collectors_map().find(web_contents1),
+            data_collectors_map().end());
+  EXPECT_NE(nullptr, data_collectors_map().at(web_contents1).threat_details);
+  EXPECT_NE(data_collectors_map().find(web_contents2),
+            data_collectors_map().end());
+  EXPECT_EQ(nullptr, data_collectors_map().at(web_contents2).threat_details);
+  EXPECT_TRUE(FinishCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
+                                            web_contents1, true));
+  EXPECT_NE(data_collectors_map().find(web_contents1),
+            data_collectors_map().end());
+  EXPECT_EQ(nullptr, data_collectors_map().at(web_contents1).threat_details);
+  EXPECT_NE(data_collectors_map().find(web_contents2),
+            data_collectors_map().end());
+  EXPECT_EQ(nullptr, data_collectors_map().at(web_contents2).threat_details);
+
+  // Calling Start twice with the same WebContents is an error, and will return
+  // false the second time. But it can still be completed normally.
+  EXPECT_TRUE(StartCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
+                                           web_contents1));
+  EXPECT_NE(data_collectors_map().find(web_contents1),
+            data_collectors_map().end());
+  EXPECT_NE(nullptr, data_collectors_map().at(web_contents1).threat_details);
+  EXPECT_FALSE(StartCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
+                                            web_contents1));
+  EXPECT_NE(data_collectors_map().find(web_contents1),
+            data_collectors_map().end());
+  EXPECT_NE(nullptr, data_collectors_map().at(web_contents1).threat_details);
+  EXPECT_TRUE(FinishCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
+                                            web_contents1, true));
+  EXPECT_NE(data_collectors_map().find(web_contents1),
+            data_collectors_map().end());
+  EXPECT_EQ(nullptr, data_collectors_map().at(web_contents1).threat_details);
+
+  // Calling Finish twice with the same WebContents is an error, and will return
+  // false the second time. It's basically a no-op.
+  EXPECT_TRUE(StartCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
+                                           web_contents1));
+  EXPECT_NE(data_collectors_map().find(web_contents1),
+            data_collectors_map().end());
+  EXPECT_NE(nullptr, data_collectors_map().at(web_contents1).threat_details);
+  EXPECT_TRUE(FinishCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
+                                            web_contents1, true));
+  EXPECT_NE(data_collectors_map().find(web_contents1),
+            data_collectors_map().end());
+  EXPECT_EQ(nullptr, data_collectors_map().at(web_contents1).threat_details);
+  EXPECT_FALSE(FinishCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
+                                             web_contents1, false));
+  EXPECT_NE(data_collectors_map().find(web_contents1),
+            data_collectors_map().end());
+  EXPECT_EQ(nullptr, data_collectors_map().at(web_contents1).threat_details);
+}
+
+TEST_F(TriggerManagerTest, NoDataCollection_Incognito) {
+  // Data collection will not begin and no reports will be sent when incognito.
+  content::WebContents* web_contents = CreateIncognitoWebContents();
+  EXPECT_FALSE(StartCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
+                                            web_contents));
+  EXPECT_TRUE(data_collectors_map().empty());
+  EXPECT_FALSE(FinishCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
+                                             web_contents, false));
+  EXPECT_TRUE(data_collectors_map().empty());
+}
+
+TEST_F(TriggerManagerTest, NoDataCollection_SBEROptInDisallowed) {
+  // Data collection will not begin and no reports will be sent when the user is
+  // not allowed to opt-in to SBER.
+  SetPref(prefs::kSafeBrowsingExtendedReportingOptInAllowed, false);
+  content::WebContents* web_contents = CreateWebContents();
+  EXPECT_FALSE(StartCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
+                                            web_contents));
+  EXPECT_TRUE(data_collectors_map().empty());
+  EXPECT_FALSE(FinishCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
+                                             web_contents, false));
+  EXPECT_TRUE(data_collectors_map().empty());
+}
+
+TEST_F(TriggerManagerTest, NoDataCollection_IncognitoAndSBEROptInDisallowed) {
+  // Data collection will not begin and no reports will be sent when the user is
+  // not allowed to opt-in to SBER and is also incognito.
+  SetPref(prefs::kSafeBrowsingExtendedReportingOptInAllowed, false);
+  content::WebContents* web_contents = CreateIncognitoWebContents();
+  EXPECT_FALSE(StartCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
+                                            web_contents));
+  EXPECT_TRUE(data_collectors_map().empty());
+  EXPECT_FALSE(FinishCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
+                                             web_contents, false));
+  EXPECT_TRUE(data_collectors_map().empty());
+}
+
+TEST_F(TriggerManagerTest, UserOptedOutOfSBER_DataCollected_NoReportSent) {
+  // When the user is opted-out of SBER then data collection will begin but no
+  // report will be sent when data collection ends.
+  SetPref(prefs::kSafeBrowsingScoutReportingEnabled, false);
+  content::WebContents* web_contents = CreateWebContents();
+  EXPECT_TRUE(StartCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
+                                           web_contents));
+  EXPECT_THAT(data_collectors_map(), UnorderedElementsAre(Key(web_contents)));
+  EXPECT_FALSE(FinishCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
+                                             web_contents, false));
+  EXPECT_NE(data_collectors_map().find(web_contents),
+            data_collectors_map().end());
+  EXPECT_EQ(nullptr, data_collectors_map().at(web_contents).threat_details);
+}
+
+TEST_F(TriggerManagerTest, UserOptsOutOfSBER_DataCollected_NoReportSent) {
+  // If the user opts-out of Extended Reporting while data is being collected
+  // then no report is sent. Note that the test fixture opts the user into
+  // Extended Reporting by default.
+  content::WebContents* web_contents = CreateWebContents();
+  EXPECT_TRUE(StartCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
+                                           web_contents));
+  EXPECT_THAT(data_collectors_map(), UnorderedElementsAre(Key(web_contents)));
+
+  SetPref(prefs::kSafeBrowsingScoutReportingEnabled, false);
+
+  EXPECT_FALSE(FinishCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
+                                             web_contents, false));
+  EXPECT_NE(data_collectors_map().find(web_contents),
+            data_collectors_map().end());
+  EXPECT_EQ(nullptr, data_collectors_map().at(web_contents).threat_details);
+}
+
+TEST_F(TriggerManagerTest, UserOptsInToSBER_DataCollected_ReportSent) {
+  // When the user is opted-out of SBER then data collection will begin. If they
+  // opt-in to SBER while data collection is in progress then the report will
+  // also be sent.
+  SetPref(prefs::kSafeBrowsingScoutReportingEnabled, false);
+  content::WebContents* web_contents = CreateWebContents();
+  EXPECT_TRUE(StartCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
+                                           web_contents));
+  EXPECT_THAT(data_collectors_map(), UnorderedElementsAre(Key(web_contents)));
+
+  SetPref(prefs::kSafeBrowsingScoutReportingEnabled, true);
+
+  EXPECT_TRUE(FinishCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
+                                            web_contents, true));
+  EXPECT_NE(data_collectors_map().find(web_contents),
+            data_collectors_map().end());
+  EXPECT_EQ(nullptr, data_collectors_map().at(web_contents).threat_details);
+}
+
+TEST_F(TriggerManagerTest,
+       SBEROptInBecomesDisallowed_DataCollected_NoReportSent) {
+  // If the user loses the ability to opt-in to SBER in the middle of data
+  // collection then the report will not be sent.
+  content::WebContents* web_contents = CreateWebContents();
+  EXPECT_TRUE(StartCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
+                                           web_contents));
+  EXPECT_THAT(data_collectors_map(), UnorderedElementsAre(Key(web_contents)));
+
+  // Remove the ability to opt-in to SBER.
+  SetPref(prefs::kSafeBrowsingExtendedReportingOptInAllowed, false);
+
+  EXPECT_FALSE(FinishCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
+                                             web_contents, false));
+  EXPECT_NE(data_collectors_map().find(web_contents),
+            data_collectors_map().end());
+  EXPECT_EQ(nullptr, data_collectors_map().at(web_contents).threat_details);
+}
+
+TEST_F(TriggerManagerTest, NoCollectionWhenOutOfQuota) {
+  // Triggers are not allowed to collect data when they're out of quota, even if
+  // all other conditions are as expected.
+  content::WebContents* web_contents = CreateWebContents();
+
+  // Turn on the AD_SAMPLE trigger inside the throttler and confirm that it can
+  // fire normally.
+  SetTriggerHasQuota(TriggerType::AD_SAMPLE, true);
+  EXPECT_TRUE(
+      StartCollectingThreatDetails(TriggerType::AD_SAMPLE, web_contents));
+  EXPECT_THAT(data_collectors_map(), UnorderedElementsAre(Key(web_contents)));
+  EXPECT_TRUE(FinishCollectingThreatDetails(TriggerType::AD_SAMPLE,
+                                            web_contents, true));
+  EXPECT_NE(data_collectors_map().find(web_contents),
+            data_collectors_map().end());
+  EXPECT_EQ(nullptr, data_collectors_map().at(web_contents).threat_details);
+
+  // Turn off the AD_SAMPLE trigger inside the throttler, the trigger should no
+  // longer be able to fire.
+  SetTriggerHasQuota(TriggerType::AD_SAMPLE, false);
+  EXPECT_FALSE(
+      StartCollectingThreatDetails(TriggerType::AD_SAMPLE, web_contents));
+  EXPECT_NE(data_collectors_map().find(web_contents),
+            data_collectors_map().end());
+  EXPECT_EQ(nullptr, data_collectors_map().at(web_contents).threat_details);
+}
+
+TEST_F(TriggerManagerTest, NoCollectionWhenSBERDisabledByPolicy) {
+  // Confirm that disabling SBER through an enterprise policy does disable
+  // triggers.
+  content::WebContents* web_contents = CreateWebContents();
+
+  SetManagedPref(prefs::kSafeBrowsingScoutReportingEnabled, false);
+  EXPECT_FALSE(
+      StartCollectingThreatDetails(TriggerType::AD_SAMPLE, web_contents));
+  EXPECT_TRUE(data_collectors_map().empty());
+  EXPECT_FALSE(FinishCollectingThreatDetails(TriggerType::AD_SAMPLE,
+                                             web_contents, false));
+  EXPECT_TRUE(data_collectors_map().empty());
+}
+
+TEST_F(TriggerManagerTest, AdSamplerTrigger) {
+  // Check the conditions required for the Ad Sampler trigger to fire. It needs
+  // opt-in to start collecting data, scout opt-in, and quota.
+  content::WebContents* web_contents = CreateWebContents();
+
+  // The default setup in this test makes the trigger fire (all prefs enabled,
+  // all triggers have quota).
+  EXPECT_TRUE(
+      StartCollectingThreatDetails(TriggerType::AD_SAMPLE, web_contents));
+  EXPECT_THAT(data_collectors_map(), UnorderedElementsAre(Key(web_contents)));
+  EXPECT_TRUE(FinishCollectingThreatDetails(TriggerType::AD_SAMPLE,
+                                            web_contents, true));
+  EXPECT_NE(data_collectors_map().find(web_contents),
+            data_collectors_map().end());
+  EXPECT_EQ(nullptr, data_collectors_map().at(web_contents).threat_details);
+
+  // Disabling SBEROptInAllowed disables this trigger.
+  SetPref(prefs::kSafeBrowsingExtendedReportingOptInAllowed, false);
+  EXPECT_FALSE(
+      StartCollectingThreatDetails(TriggerType::AD_SAMPLE, web_contents));
+
+  // Confirm it can fire when we re-enable SBEROptInAllowed
+  SetPref(prefs::kSafeBrowsingExtendedReportingOptInAllowed, true);
+  EXPECT_TRUE(
+      StartCollectingThreatDetails(TriggerType::AD_SAMPLE, web_contents));
+  EXPECT_TRUE(FinishCollectingThreatDetails(TriggerType::AD_SAMPLE,
+                                            web_contents, true));
+
+  // Disabling Scout disables this trigger.
+  SetPref(prefs::kSafeBrowsingScoutReportingEnabled, false);
+  EXPECT_FALSE(
+      StartCollectingThreatDetails(TriggerType::AD_SAMPLE, web_contents));
+
+  // Confirm it can fire when we re-enable Scout and disable legacy SBER.
+  SetPref(prefs::kSafeBrowsingScoutReportingEnabled, true);
+  EXPECT_TRUE(
+      StartCollectingThreatDetails(TriggerType::AD_SAMPLE, web_contents));
+  EXPECT_TRUE(FinishCollectingThreatDetails(TriggerType::AD_SAMPLE,
+                                            web_contents, true));
+
+  // Finally, make sure the trigger can't fire if it has no quota.
+  SetTriggerHasQuota(TriggerType::AD_SAMPLE, false);
+  EXPECT_FALSE(
+      StartCollectingThreatDetails(TriggerType::AD_SAMPLE, web_contents));
+
+  // Confirm it can fire again when quota is available.
+  SetTriggerHasQuota(TriggerType::AD_SAMPLE, true);
+  EXPECT_TRUE(
+      StartCollectingThreatDetails(TriggerType::AD_SAMPLE, web_contents));
+  EXPECT_TRUE(FinishCollectingThreatDetails(TriggerType::AD_SAMPLE,
+                                            web_contents, true));
+}
+
+TEST_F(TriggerManagerTest, AdSamplerTrigger_Incognito) {
+  // Check the conditions required for the Ad Sampler trigger to fire. It needs
+  // opt-in to start collecting data, scout opt-in, and quota, and it can't fire
+  // in inconito (except when forced on by finch feature).
+  content::WebContents* web_contents = CreateIncognitoWebContents();
+
+  // The default setup in this test makes the trigger fire (all prefs enabled,
+  // all triggers have quota), but the incognito window prevents it from firing.
+  EXPECT_FALSE(
+      StartCollectingThreatDetails(TriggerType::AD_SAMPLE, web_contents));
+}
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/triggers/trigger_throttler.cc b/components/safe_browsing/core/triggers/trigger_throttler.cc
new file mode 100644
index 0000000..ddb04f7
--- /dev/null
+++ b/components/safe_browsing/core/triggers/trigger_throttler.cc
@@ -0,0 +1,312 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/core/triggers/trigger_throttler.h"
+
+#include "base/metrics/field_trial_params.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/time/default_clock.h"
+#include "base/time/time.h"
+#include "components/prefs/pref_service.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/features.h"
+
+namespace safe_browsing {
+const size_t kAdPopupTriggerDefaultQuota = 1;
+const size_t kAdRedirectTriggerDefaultQuota = 1;
+const size_t kAdSamplerTriggerDefaultQuota = 10;
+const size_t kSuspiciousSiteTriggerDefaultQuota = 5;
+const char kAdPopupTriggerQuotaParam[] = "ad_popup_trigger_quota";
+const char kAdRedirectTriggerQuotaParam[] = "ad_redirect_trigger_quota";
+const char kSuspiciousSiteTriggerQuotaParam[] = "suspicious_site_trigger_quota";
+const char kTriggerTypeAndQuotaParam[] = "trigger_type_and_quota_csv";
+
+namespace {
+const size_t kUnlimitedTriggerQuota = std::numeric_limits<size_t>::max();
+constexpr base::TimeDelta kOneDayTimeDelta = base::TimeDelta::FromDays(1);
+
+// Predicate used to search |trigger_type_and_quota_list_| by trigger type.
+class TriggerTypeIs {
+ public:
+  explicit TriggerTypeIs(const TriggerType type) : type_(type) {}
+  bool operator()(const TriggerTypeAndQuotaItem& trigger_type_and_quota) {
+    return type_ == trigger_type_and_quota.first;
+  }
+
+ private:
+  TriggerType type_;
+};
+
+void ParseTriggerTypeAndQuotaParam(
+    std::vector<TriggerTypeAndQuotaItem>* trigger_type_and_quota_list) {
+  DCHECK(trigger_type_and_quota_list);
+  trigger_type_and_quota_list->clear();
+
+  // First, handle the trigger-specific features.
+  int suspicious_site_quota = base::GetFieldTrialParamByFeatureAsInt(
+      kSuspiciousSiteTriggerQuotaFeature, kSuspiciousSiteTriggerQuotaParam,
+      kSuspiciousSiteTriggerDefaultQuota);
+  if (suspicious_site_quota > 0) {
+    trigger_type_and_quota_list->push_back(
+        std::make_pair(TriggerType::SUSPICIOUS_SITE, suspicious_site_quota));
+  }
+
+  int ad_popup_quota = base::GetFieldTrialParamByFeatureAsInt(
+      kAdPopupTriggerFeature, kAdPopupTriggerQuotaParam,
+      kAdPopupTriggerDefaultQuota);
+  if (ad_popup_quota > 0) {
+    trigger_type_and_quota_list->push_back(
+        std::make_pair(TriggerType::AD_POPUP, ad_popup_quota));
+  }
+
+  int ad_redirect_quota = base::GetFieldTrialParamByFeatureAsInt(
+      kAdRedirectTriggerFeature, kAdRedirectTriggerQuotaParam,
+      kAdRedirectTriggerDefaultQuota);
+  if (ad_redirect_quota > 0) {
+    trigger_type_and_quota_list->push_back(
+        std::make_pair(TriggerType::AD_REDIRECT, ad_redirect_quota));
+  }
+
+  // If the feature is disabled we just use the default list. Otherwise the list
+  // from the Finch param will be the one used.
+  if (!base::FeatureList::IsEnabled(kTriggerThrottlerDailyQuotaFeature)) {
+    return;
+  }
+
+  const std::string& trigger_and_quota_csv_param =
+      base::GetFieldTrialParamValueByFeature(kTriggerThrottlerDailyQuotaFeature,
+                                             kTriggerTypeAndQuotaParam);
+  if (trigger_and_quota_csv_param.empty()) {
+    return;
+  }
+
+  std::vector<std::string> split =
+      base::SplitString(trigger_and_quota_csv_param, ",", base::TRIM_WHITESPACE,
+                        base::SPLIT_WANT_NONEMPTY);
+  // If we don't have the right number of pairs in the csv then don't bother
+  // parsing further.
+  if (split.size() % 2 != 0) {
+    return;
+  }
+  for (size_t i = 0; i < split.size(); i += 2) {
+    // Make sure both the trigger type and quota are integers. Skip them if not.
+    int trigger_type_int = -1;
+    int quota_int = -1;
+    if (!base::StringToInt(split[i], &trigger_type_int) ||
+        !base::StringToInt(split[i + 1], &quota_int)) {
+      continue;
+    }
+    trigger_type_and_quota_list->push_back(
+        std::make_pair(static_cast<TriggerType>(trigger_type_int), quota_int));
+  }
+
+  std::sort(trigger_type_and_quota_list->begin(),
+            trigger_type_and_quota_list->end(),
+            [](const TriggerTypeAndQuotaItem& a,
+               const TriggerTypeAndQuotaItem& b) { return a.first < b.first; });
+}
+
+// Looks in |trigger_quota_list| for |trigger_type|. If found, sets |out_quota|
+// to the configured quota, and returns true. If not found, returns false.
+bool TryFindQuotaForTrigger(
+    const TriggerType trigger_type,
+    const std::vector<TriggerTypeAndQuotaItem>& trigger_quota_list,
+    size_t* out_quota) {
+  const auto& trigger_quota_iter =
+      std::find_if(trigger_quota_list.begin(), trigger_quota_list.end(),
+                   TriggerTypeIs(trigger_type));
+  if (trigger_quota_iter != trigger_quota_list.end()) {
+    *out_quota = trigger_quota_iter->second;
+    return true;
+  }
+  return false;
+}
+
+}  // namespace
+
+TriggerThrottler::TriggerThrottler(PrefService* local_state_prefs)
+    : local_state_prefs_(local_state_prefs),
+      clock_(base::DefaultClock::GetInstance()) {
+  ParseTriggerTypeAndQuotaParam(&trigger_type_and_quota_list_);
+  LoadTriggerEventsFromPref();
+}
+
+TriggerThrottler::~TriggerThrottler() {}
+
+void TriggerThrottler::SetClockForTesting(base::Clock* test_clock) {
+  clock_ = test_clock;
+}
+
+bool TriggerThrottler::TriggerCanFire(const TriggerType trigger_type) const {
+  // Lookup how many times this trigger is allowed to fire each day.
+  const size_t trigger_quota = GetDailyQuotaForTrigger(trigger_type);
+
+  // Some basic corner cases for triggers that always fire, or disabled
+  // triggers that never fire.
+  if (trigger_quota == kUnlimitedTriggerQuota)
+    return true;
+  if (trigger_quota == 0)
+    return false;
+
+  // Other triggers are capped, see how many times this trigger has already
+  // fired.
+  if (!base::Contains(trigger_events_, trigger_type))
+    return true;
+
+  const std::vector<base::Time>& timestamps = trigger_events_.at(trigger_type);
+  // More quota is available, so the trigger can fire again.
+  if (trigger_quota > timestamps.size())
+    return true;
+
+  // Otherwise, we have more events than quota, check which day they occurred
+  // on. Newest events are at the end of vector so we can simply look at the
+  // Nth-from-last entry (where N is the quota) to see if it happened within
+  // the current day or earlier.
+  base::Time min_timestamp = clock_->Now() - kOneDayTimeDelta;
+  const size_t pos = timestamps.size() - trigger_quota;
+  return timestamps[pos] < min_timestamp;
+}
+
+void TriggerThrottler::TriggerFired(const TriggerType trigger_type) {
+  // Lookup how many times this trigger is allowed to fire each day.
+  const size_t trigger_quota = GetDailyQuotaForTrigger(trigger_type);
+
+  // For triggers that always fire, don't bother tracking quota.
+  if (trigger_quota == kUnlimitedTriggerQuota)
+    return;
+
+  // Otherwise, record that the trigger fired.
+  std::vector<base::Time>* timestamps = &trigger_events_[trigger_type];
+  timestamps->push_back(clock_->Now());
+
+  // Clean up the trigger events map.
+  CleanupOldEvents();
+
+  // Update the pref
+  WriteTriggerEventsToPref();
+}
+
+void TriggerThrottler::CleanupOldEvents() {
+  for (const auto& map_iter : trigger_events_) {
+    const TriggerType trigger_type = map_iter.first;
+    const size_t trigger_quota = GetDailyQuotaForTrigger(trigger_type);
+    const std::vector<base::Time>& trigger_times = map_iter.second;
+
+    // Skip the cleanup if we have quota room, quotas should generally be small.
+    if (trigger_times.size() < trigger_quota)
+      return;
+
+    std::vector<base::Time> tmp_trigger_times;
+    base::Time min_timestamp = clock_->Now() - kOneDayTimeDelta;
+    // Go over the event times for this trigger and keep timestamps which are
+    // newer than |min_timestamp|. We put timestamps in a temp vector that will
+    // get swapped into the map in place of the existing vector.
+    for (const base::Time timestamp : trigger_times) {
+      if (timestamp > min_timestamp)
+        tmp_trigger_times.push_back(timestamp);
+    }
+
+    trigger_events_[trigger_type].swap(tmp_trigger_times);
+  }
+}
+
+void TriggerThrottler::LoadTriggerEventsFromPref() {
+  trigger_events_.clear();
+  if (!local_state_prefs_)
+    return;
+
+  const base::DictionaryValue* event_dict = local_state_prefs_->GetDictionary(
+      prefs::kSafeBrowsingTriggerEventTimestamps);
+  for (const auto& trigger_pair : event_dict->DictItems()) {
+    // Check that the first item in the pair is convertible to a trigger type
+    // and that the second item is a list.
+    int trigger_type_int;
+    if (!base::StringToInt(trigger_pair.first, &trigger_type_int) ||
+        trigger_type_int < static_cast<int>(TriggerType::kMinTriggerType) ||
+        trigger_type_int > static_cast<int>(TriggerType::kMaxTriggerType)) {
+      continue;
+    }
+    if (!trigger_pair.second.is_list())
+      continue;
+
+    const TriggerType trigger_type = static_cast<TriggerType>(trigger_type_int);
+    for (const auto& timestamp : trigger_pair.second.GetList()) {
+      if (timestamp.is_double())
+        trigger_events_[trigger_type].push_back(
+            base::Time::FromDoubleT(timestamp.GetDouble()));
+    }
+  }
+}
+
+void TriggerThrottler::WriteTriggerEventsToPref() {
+  if (!local_state_prefs_)
+    return;
+
+  base::DictionaryValue trigger_dict;
+  for (const auto& trigger_item : trigger_events_) {
+    base::Value* pref_timestamps = trigger_dict.SetKey(
+        base::NumberToString(static_cast<int>(trigger_item.first)),
+        base::Value(base::Value::Type::LIST));
+    for (const base::Time timestamp : trigger_item.second) {
+      pref_timestamps->Append(base::Value(timestamp.ToDoubleT()));
+    }
+  }
+
+  local_state_prefs_->Set(prefs::kSafeBrowsingTriggerEventTimestamps,
+                          trigger_dict);
+}
+
+size_t TriggerThrottler::GetDailyQuotaForTrigger(
+    const TriggerType trigger_type) const {
+  size_t quota_from_finch = 0;
+  switch (trigger_type) {
+    case TriggerType::SECURITY_INTERSTITIAL:
+    case TriggerType::GAIA_PASSWORD_REUSE:
+    case TriggerType::APK_DOWNLOAD:
+      return kUnlimitedTriggerQuota;
+    case TriggerType::AD_POPUP:
+      // Ad Popup reports are disabled unless they are configured through Finch.
+      if (TryFindQuotaForTrigger(trigger_type, trigger_type_and_quota_list_,
+                                 &quota_from_finch)) {
+        return quota_from_finch;
+      }
+      break;
+    case TriggerType::AD_REDIRECT:
+      // Ad Redirects are disabled unless they are configured through Finch.
+      if (TryFindQuotaForTrigger(trigger_type, trigger_type_and_quota_list_,
+                                 &quota_from_finch)) {
+        return quota_from_finch;
+      }
+      break;
+    case TriggerType::AD_SAMPLE:
+      // Ad Samples have a non-zero default quota, but it can be overwritten
+      // through Finch.
+      if (TryFindQuotaForTrigger(trigger_type, trigger_type_and_quota_list_,
+                                 &quota_from_finch)) {
+        return quota_from_finch;
+      } else {
+        return kAdSamplerTriggerDefaultQuota;
+      }
+
+      break;
+    case TriggerType::SUSPICIOUS_SITE:
+      // Suspicious Sites are disabled unless they are configured through Finch.
+      if (TryFindQuotaForTrigger(trigger_type, trigger_type_and_quota_list_,
+                                 &quota_from_finch)) {
+        return quota_from_finch;
+      }
+      break;
+  }
+  // By default, unhandled or unconfigured trigger types have no quota.
+  return 0;
+}
+
+void TriggerThrottler::ResetPrefsForTesting(PrefService* local_state_prefs) {
+  local_state_prefs_ = local_state_prefs;
+  LoadTriggerEventsFromPref();
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/triggers/trigger_throttler.h b/components/safe_browsing/core/triggers/trigger_throttler.h
new file mode 100644
index 0000000..93f2f80
--- /dev/null
+++ b/components/safe_browsing/core/triggers/trigger_throttler.h
@@ -0,0 +1,120 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CORE_TRIGGERS_TRIGGER_THROTTLER_H_
+#define COMPONENTS_SAFE_BROWSING_CORE_TRIGGERS_TRIGGER_THROTTLER_H_
+
+#include <memory>
+#include <unordered_map>
+#include <vector>
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/time/clock.h"
+
+class PrefService;
+
+namespace safe_browsing {
+// Default quota for ad sampler trigger.
+extern const size_t kAdSamplerTriggerDefaultQuota;
+
+// Default quota for suspicious site trigger.
+extern const size_t kSuspiciousSiteTriggerDefaultQuota;
+
+// Param name of the finch param containing the quota for the suspicious site
+// trigger.
+extern const char kSuspiciousSiteTriggerQuotaParam[];
+
+// Param name of the finch param containing the comma-separated list of trigger
+// types and daily quotas.
+// TODO(crbug.com/744869): This param should be deprecated after ad sampler
+// launch in favour of having a unique quota feature and param per trigger.
+// Having a single shared feature makes it impossible to run multiple trigger
+// trials simultaneously.
+extern const char kTriggerTypeAndQuotaParam[];
+
+enum class TriggerType {
+  SECURITY_INTERSTITIAL = 1,
+  AD_SAMPLE = 2,
+  GAIA_PASSWORD_REUSE = 3,
+  SUSPICIOUS_SITE = 4,
+  APK_DOWNLOAD = 5,
+  AD_POPUP = 6,
+  AD_REDIRECT = 7,
+  kMinTriggerType = SECURITY_INTERSTITIAL,
+  kMaxTriggerType = AD_REDIRECT,
+};
+
+struct TriggerTypeHash {
+  std::size_t operator()(TriggerType trigger_type) const {
+    return static_cast<std::size_t>(trigger_type);
+  }
+};
+
+// A map for storing a list of event timestamps for different trigger types.
+using TriggerTimestampMap =
+    std::unordered_map<TriggerType, std::vector<base::Time>, TriggerTypeHash>;
+
+// A pair containing a TriggerType and its associated daily report quota.
+using TriggerTypeAndQuotaItem = std::pair<TriggerType, int>;
+
+// TriggerThrottler keeps track of how often each type of trigger gets fired
+// and throttles them if they fire too often.
+class TriggerThrottler {
+ public:
+  TriggerThrottler(PrefService* local_state_prefs);
+  virtual ~TriggerThrottler();
+
+  // Check if the the specified |trigger_type| has quota available and is
+  // allowed to fire at this time.
+  virtual bool TriggerCanFire(TriggerType trigger_type) const;
+
+  // Called to notify the throttler that a trigger has just fired and quota
+  // should be updated.
+  void TriggerFired(TriggerType trigger_type);
+
+ protected:
+  void SetClockForTesting(base::Clock* test_clock);
+
+ private:
+  friend class TriggerThrottlerTest;
+  friend class TriggerThrottlerTestFinch;
+
+  // Called to periodically clean-up the list of event timestamps.
+  void CleanupOldEvents();
+
+  // Loads trigger events that have been stored in preferences and adds them
+  // to |trigger_events_|.
+  void LoadTriggerEventsFromPref();
+
+  // Updates preferences with current contents of |trigger_events_|.
+  void WriteTriggerEventsToPref();
+
+  // Returns the daily quota for the specified trigger.
+  size_t GetDailyQuotaForTrigger(const TriggerType trigger_type) const;
+
+  // Resets |local_state_prefs_|. For testing.
+  void ResetPrefsForTesting(PrefService* local_state_prefs);
+
+  // Pref service for accessing local state prefs (ie: unsynced, tied to the
+  // browser not to a profile). Used to persist quota.
+  PrefService* local_state_prefs_;
+
+  // Can be set for testing.
+  base::Clock* clock_;
+
+  // Stores each trigger type that fired along with the timestamps of when it
+  // fired.
+  TriggerTimestampMap trigger_events_;
+
+  // List of trigger types and their quotas, controlled by Finch feature
+  // |kTriggerThrottlerDailyQuotaFeature|.
+  std::vector<TriggerTypeAndQuotaItem> trigger_type_and_quota_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(TriggerThrottler);
+};
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CORE_TRIGGERS_TRIGGER_THROTTLER_H_
diff --git a/components/safe_browsing/core/triggers/trigger_throttler_unittest.cc b/components/safe_browsing/core/triggers/trigger_throttler_unittest.cc
new file mode 100644
index 0000000..3722a9d
--- /dev/null
+++ b/components/safe_browsing/core/triggers/trigger_throttler_unittest.cc
@@ -0,0 +1,313 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/core/triggers/trigger_throttler.h"
+
+#include "base/strings/stringprintf.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/test/simple_test_clock.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/features.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::ElementsAre;
+
+namespace safe_browsing {
+
+class TriggerThrottlerTest : public ::testing::Test {
+ public:
+  TriggerThrottlerTest() : trigger_throttler_(nullptr) {}
+
+  void SetUp() override {
+    safe_browsing::RegisterLocalStatePrefs(pref_service_.registry());
+    trigger_throttler_.ResetPrefsForTesting(&pref_service_);
+  }
+
+  void SetQuotaForTriggerType(TriggerType trigger_type, size_t max_quota) {
+    SetQuotaForTriggerType(&trigger_throttler_, trigger_type, max_quota);
+  }
+
+  void SetQuotaForTriggerType(TriggerThrottler* throttler,
+                              TriggerType trigger_type,
+                              size_t max_quota) {
+    throttler->trigger_type_and_quota_list_.push_back(
+        std::make_pair(trigger_type, max_quota));
+  }
+
+  TriggerThrottler* throttler() { return &trigger_throttler_; }
+
+  void SetTestClock(base::Clock* clock) {
+    trigger_throttler_.SetClockForTesting(clock);
+  }
+
+  std::vector<base::Time> GetEventTimestampsForTriggerType(
+      TriggerType trigger_type) {
+    return trigger_throttler_.trigger_events_[trigger_type];
+  }
+
+  PrefService* get_pref_service() { return &pref_service_; }
+
+ private:
+  TestingPrefServiceSimple pref_service_;
+  TriggerThrottler trigger_throttler_;
+};
+
+TEST_F(TriggerThrottlerTest, SecurityInterstitialsHaveUnlimitedQuota) {
+  // Make sure that security interstitials never run out of quota.
+  for (int i = 0; i < 1000; ++i) {
+    throttler()->TriggerFired(TriggerType::SECURITY_INTERSTITIAL);
+    EXPECT_TRUE(
+        throttler()->TriggerCanFire(TriggerType::SECURITY_INTERSTITIAL));
+  }
+}
+
+TEST_F(TriggerThrottlerTest, SecurityInterstitialQuotaCanNotBeOverwritten) {
+  // Make sure that security interstitials never run out of quota, even if we
+  // try to configure quota for this trigger type.
+  SetQuotaForTriggerType(TriggerType::SECURITY_INTERSTITIAL, 3);
+  for (int i = 0; i < 1000; ++i) {
+    throttler()->TriggerFired(TriggerType::SECURITY_INTERSTITIAL);
+    EXPECT_TRUE(
+        throttler()->TriggerCanFire(TriggerType::SECURITY_INTERSTITIAL));
+  }
+}
+
+TEST_F(TriggerThrottlerTest, TriggerQuotaSetToOne) {
+  // This is a corner case where we can exceed array bounds for triggers that
+  // have quota set to 1 report per day. This can happen when quota is 1 and
+  // exactly one event has fired. When deciding whether another event can fire,
+  // we look at the Nth-from-last event to check if it was recent or not - in
+  // this scenario, Nth-from-last is 1st-from-last (because quota is 1). An
+  // off-by-one error in this calculation can cause us to look at position 1
+  // instead of position 0 in the even list.
+  SetQuotaForTriggerType(TriggerType::AD_SAMPLE, 1);
+
+  // Fire the trigger, first event will be allowed.
+  EXPECT_TRUE(throttler()->TriggerCanFire(TriggerType::AD_SAMPLE));
+  throttler()->TriggerFired(TriggerType::AD_SAMPLE);
+
+  // Ensure that checking whether this trigger can fire again does not cause
+  // an error and also returns the expected result.
+  EXPECT_FALSE(throttler()->TriggerCanFire(TriggerType::AD_SAMPLE));
+}
+
+TEST_F(TriggerThrottlerTest, TriggerExceedsQuota) {
+  // Ensure that a trigger can't fire more than its quota allows.
+  SetQuotaForTriggerType(TriggerType::AD_SAMPLE, 2);
+
+  // First two triggers should work
+  EXPECT_TRUE(throttler()->TriggerCanFire(TriggerType::AD_SAMPLE));
+  throttler()->TriggerFired(TriggerType::AD_SAMPLE);
+  EXPECT_TRUE(throttler()->TriggerCanFire(TriggerType::AD_SAMPLE));
+  throttler()->TriggerFired(TriggerType::AD_SAMPLE);
+
+  // Third attempt will fail since we're out of quota.
+  EXPECT_FALSE(throttler()->TriggerCanFire(TriggerType::AD_SAMPLE));
+}
+
+TEST_F(TriggerThrottlerTest, TriggerQuotaResetsAfterOneDay) {
+  // Ensure that trigger events older than a day are cleaned up and triggers can
+  // resume firing.
+
+  // We initialize the test clock to several days ago and fire some events to
+  // use up quota. We then advance the clock by a day and ensure quota is
+  // available again.
+  base::SimpleTestClock test_clock;
+  test_clock.SetNow(base::Time::Now() - base::TimeDelta::FromDays(10));
+  base::Time base_ts = test_clock.Now();
+
+  SetTestClock(&test_clock);
+  SetQuotaForTriggerType(TriggerType::AD_SAMPLE, 2);
+
+  // First two triggers should work
+  EXPECT_TRUE(throttler()->TriggerCanFire(TriggerType::AD_SAMPLE));
+  throttler()->TriggerFired(TriggerType::AD_SAMPLE);
+  EXPECT_TRUE(throttler()->TriggerCanFire(TriggerType::AD_SAMPLE));
+  throttler()->TriggerFired(TriggerType::AD_SAMPLE);
+
+  // Third attempt will fail since we're out of quota.
+  EXPECT_FALSE(throttler()->TriggerCanFire(TriggerType::AD_SAMPLE));
+
+  // Also confirm that the throttler contains two event timestamps for the above
+  // two events - since we use a test clock, it doesn't move unless we tell it
+  // to.
+  EXPECT_THAT(GetEventTimestampsForTriggerType(TriggerType::AD_SAMPLE),
+              ElementsAre(base_ts, base_ts));
+
+  // Move the clock forward by 1 day (and a bit) and try the trigger again,
+  // quota should be available now.
+  test_clock.Advance(base::TimeDelta::FromDays(1) +
+                     base::TimeDelta::FromSeconds(1));
+  base::Time advanced_ts = test_clock.Now();
+  EXPECT_TRUE(throttler()->TriggerCanFire(TriggerType::AD_SAMPLE));
+
+  // The previous time stamps should remain in the throttler.
+  EXPECT_THAT(GetEventTimestampsForTriggerType(TriggerType::AD_SAMPLE),
+              ElementsAre(base_ts, base_ts));
+
+  // Firing the trigger will clean up the expired timestamps and insert the new
+  // timestamp.
+  throttler()->TriggerFired(TriggerType::AD_SAMPLE);
+  EXPECT_THAT(GetEventTimestampsForTriggerType(TriggerType::AD_SAMPLE),
+              ElementsAre(advanced_ts));
+}
+
+TEST_F(TriggerThrottlerTest, TriggerQuotaPersistence) {
+  // Test that trigger quota is persisted in prefs when triggers fire, and
+  // retrieved from prefs on startup.
+
+  // Set some low quotas for two triggers
+  SetQuotaForTriggerType(TriggerType::AD_SAMPLE, 3);
+  SetQuotaForTriggerType(TriggerType::SUSPICIOUS_SITE, 3);
+
+  // Ensure each trigger can fire.
+  EXPECT_TRUE(throttler()->TriggerCanFire(TriggerType::AD_SAMPLE));
+  EXPECT_TRUE(throttler()->TriggerCanFire(TriggerType::SUSPICIOUS_SITE));
+
+  // Fire each trigger twice to store some events.
+  throttler()->TriggerFired(TriggerType::AD_SAMPLE);
+  throttler()->TriggerFired(TriggerType::AD_SAMPLE);
+  throttler()->TriggerFired(TriggerType::AD_SAMPLE);
+  throttler()->TriggerFired(TriggerType::SUSPICIOUS_SITE);
+  throttler()->TriggerFired(TriggerType::SUSPICIOUS_SITE);
+
+  // The AD_SAMPLE trigger is now out of quota, while SUSPICIOUS_SITE can still
+  // fire one more time.
+  EXPECT_FALSE(throttler()->TriggerCanFire(TriggerType::AD_SAMPLE));
+  EXPECT_TRUE(throttler()->TriggerCanFire(TriggerType::SUSPICIOUS_SITE));
+
+  // Check the pref directly, it should reflect the events for each trigger.
+  PrefService* prefs = get_pref_service();
+  const base::DictionaryValue* event_dict =
+      prefs->GetDictionary(prefs::kSafeBrowsingTriggerEventTimestamps);
+
+  const std::string kAdSampleKey = "2";
+  const base::Value* ad_sample_events = event_dict->FindKey(kAdSampleKey);
+  EXPECT_EQ(3u, ad_sample_events->GetList().size());
+
+  const std::string kSuspiciousSiteKey = "4";
+  const base::Value* suspicious_site_events =
+      event_dict->FindKey(kSuspiciousSiteKey);
+  EXPECT_EQ(2u, suspicious_site_events->GetList().size());
+
+  // To simulate a new startup of the browser, we can create another throttler
+  // using the same quota configuration and pref store. It should read the
+  // events from prefs and and reflect the same status for each trigger.
+  TriggerThrottler throttler2(prefs);
+  SetQuotaForTriggerType(&throttler2, TriggerType::AD_SAMPLE, 3);
+  SetQuotaForTriggerType(&throttler2, TriggerType::SUSPICIOUS_SITE, 3);
+  EXPECT_FALSE(throttler2.TriggerCanFire(TriggerType::AD_SAMPLE));
+  EXPECT_TRUE(throttler2.TriggerCanFire(TriggerType::SUSPICIOUS_SITE));
+}
+
+class TriggerThrottlerTestFinch : public ::testing::Test {
+ public:
+  void SetupQuotaParams(const TriggerType trigger_type,
+                        const std::string& group_name,
+                        int quota,
+                        base::test::ScopedFeatureList* scoped_feature_list) {
+    const base::Feature* feature = nullptr;
+    std::string param_name = "";
+    GetFeatureAndParamForTrigger(trigger_type, &feature, &param_name);
+
+    base::FieldTrialParams feature_params;
+    feature_params[param_name] =
+        GetQuotaParamValueForTrigger(trigger_type, quota);
+    scoped_feature_list->InitAndEnableFeatureWithParameters(*feature,
+                                                            feature_params);
+  }
+
+  size_t GetDailyQuotaForTrigger(const TriggerThrottler& throttler,
+                                 const TriggerType trigger_type) {
+    return throttler.GetDailyQuotaForTrigger(trigger_type);
+  }
+
+ private:
+  void GetFeatureAndParamForTrigger(const TriggerType trigger_type,
+                                    const base::Feature** out_feature,
+                                    std::string* out_param) {
+    switch (trigger_type) {
+      case TriggerType::AD_SAMPLE:
+        *out_feature = &safe_browsing::kTriggerThrottlerDailyQuotaFeature;
+        *out_param = safe_browsing::kTriggerTypeAndQuotaParam;
+        break;
+
+      case TriggerType::SUSPICIOUS_SITE:
+        *out_feature = &safe_browsing::kSuspiciousSiteTriggerQuotaFeature;
+        *out_param = safe_browsing::kSuspiciousSiteTriggerQuotaParam;
+        break;
+
+      default:
+        NOTREACHED() << "Unhandled trigger type: "
+                     << static_cast<int>(trigger_type);
+    }
+  }
+
+  std::string GetQuotaParamValueForTrigger(const TriggerType trigger_type,
+                                           int quota) {
+    if (trigger_type == TriggerType::AD_SAMPLE)
+      return base::StringPrintf("%d,%d", trigger_type, quota);
+    else
+      return base::StringPrintf("%d", quota);
+  }
+};
+
+TEST_F(TriggerThrottlerTestFinch, ConfigureQuotaViaFinch) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  SetupQuotaParams(TriggerType::AD_SAMPLE, "Group_ConfigureQuotaViaFinch", 3,
+                   &scoped_feature_list);
+  // Make sure that setting the quota param via Finch params works as expected.
+
+  // The throttler has been configured (above) to allow ad samples to fire three
+  // times per day.
+  TriggerThrottler throttler(nullptr);
+
+  // First three triggers should work
+  EXPECT_TRUE(throttler.TriggerCanFire(TriggerType::AD_SAMPLE));
+  throttler.TriggerFired(TriggerType::AD_SAMPLE);
+  EXPECT_TRUE(throttler.TriggerCanFire(TriggerType::AD_SAMPLE));
+  throttler.TriggerFired(TriggerType::AD_SAMPLE);
+  EXPECT_TRUE(throttler.TriggerCanFire(TriggerType::AD_SAMPLE));
+  throttler.TriggerFired(TriggerType::AD_SAMPLE);
+
+  // Fourth attempt will fail since we're out of quota.
+  EXPECT_FALSE(throttler.TriggerCanFire(TriggerType::AD_SAMPLE));
+}
+
+TEST_F(TriggerThrottlerTestFinch, AdSamplerDefaultQuota) {
+  // Make sure that the ad sampler gets its own default quota when no finch
+  // config exists, but the quota can be overwritten through Finch.
+  TriggerThrottler throttler_default(nullptr);
+  EXPECT_EQ(kAdSamplerTriggerDefaultQuota,
+            GetDailyQuotaForTrigger(throttler_default, TriggerType::AD_SAMPLE));
+  EXPECT_TRUE(throttler_default.TriggerCanFire(TriggerType::AD_SAMPLE));
+
+  base::test::ScopedFeatureList scoped_feature_list;
+  SetupQuotaParams(TriggerType::AD_SAMPLE, "Group_AdSamplerDefaultQuota", 4,
+                   &scoped_feature_list);
+  TriggerThrottler throttler_finch(nullptr);
+  EXPECT_EQ(4u,
+            GetDailyQuotaForTrigger(throttler_finch, TriggerType::AD_SAMPLE));
+}
+
+TEST_F(TriggerThrottlerTestFinch, SuspiciousSiteTriggerDefaultQuota) {
+  // Ensure that suspicious site trigger is enabled with default quota.
+  TriggerThrottler throttler_default(nullptr);
+  EXPECT_EQ(
+      kSuspiciousSiteTriggerDefaultQuota,
+      GetDailyQuotaForTrigger(throttler_default, TriggerType::SUSPICIOUS_SITE));
+  EXPECT_TRUE(throttler_default.TriggerCanFire(TriggerType::SUSPICIOUS_SITE));
+
+  base::test::ScopedFeatureList scoped_feature_list;
+  SetupQuotaParams(TriggerType::SUSPICIOUS_SITE,
+                   "Group_SuspiciousSiteTriggerDefaultQuota", 7,
+                   &scoped_feature_list);
+  TriggerThrottler throttler_finch(nullptr);
+  EXPECT_EQ(7u, GetDailyQuotaForTrigger(throttler_finch,
+                                        TriggerType::SUSPICIOUS_SITE));
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/verdict_cache_manager.cc b/components/safe_browsing/core/verdict_cache_manager.cc
new file mode 100644
index 0000000..0e3155a
--- /dev/null
+++ b/components/safe_browsing/core/verdict_cache_manager.cc
@@ -0,0 +1,664 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/core/verdict_cache_manager.h"
+
+#include "base/base64.h"
+#include "base/optional.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/task/post_task.h"
+#include "components/history/core/browser/history_service_observer.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace safe_browsing {
+
+namespace {
+
+// Keys for storing password protection verdict into a DictionaryValue.
+const char kCacheCreationTime[] = "cache_creation_time";
+const char kVerdictProto[] = "verdict_proto";
+const char kRealTimeThreatInfoProto[] = "rt_threat_info_proto";
+const char kPasswordOnFocusCacheKey[] = "password_on_focus_cache_key";
+const char kRealTimeUrlCacheKey[] = "real_time_url_cache_key";
+
+// Given a URL of either http or https scheme, return its http://hostname.
+// e.g., "https://www.foo.com:80/bar/test.cgi" -> "http://www.foo.com".
+GURL GetHostNameWithHTTPScheme(const GURL& url) {
+  DCHECK(url.SchemeIsHTTPOrHTTPS());
+  std::string result(url::kHttpScheme);
+  result.append(url::kStandardSchemeSeparator).append(url.host());
+  return GURL(result);
+}
+
+// Convert a Proto object into a DictionaryValue.
+template <class T>
+std::unique_ptr<base::DictionaryValue> CreateDictionaryFromVerdict(
+    const T& verdict,
+    const base::Time& receive_time,
+    const char* proto_name) {
+  DCHECK(proto_name == kVerdictProto || proto_name == kRealTimeThreatInfoProto);
+  std::unique_ptr<base::DictionaryValue> result =
+      std::make_unique<base::DictionaryValue>();
+  result->SetInteger(kCacheCreationTime,
+                     static_cast<int>(receive_time.ToDoubleT()));
+  std::string serialized_proto(verdict.SerializeAsString());
+  // Performs a base64 encoding on the serialized proto.
+  base::Base64Encode(serialized_proto, &serialized_proto);
+  result->SetString(proto_name, serialized_proto);
+  return result;
+}
+
+// Generate path variants of the given URL.
+void GeneratePathVariantsWithoutQuery(const GURL& url,
+                                      std::vector<std::string>* paths) {
+  std::string canonical_path;
+  V4ProtocolManagerUtil::CanonicalizeUrl(url, nullptr, &canonical_path,
+                                         nullptr);
+  V4ProtocolManagerUtil::GeneratePathVariantsToCheck(canonical_path,
+                                                     std::string(), paths);
+}
+
+template <class T>
+bool ParseVerdictEntry(base::Value* verdict_entry,
+                       int* out_verdict_received_time,
+                       T* out_verdict,
+                       const char* proto_name) {
+  DCHECK(proto_name == kVerdictProto || proto_name == kRealTimeThreatInfoProto);
+
+  if (!verdict_entry || !verdict_entry->is_dict() || !out_verdict)
+    return false;
+  base::Value* cache_creation_time_value =
+      verdict_entry->FindKey(kCacheCreationTime);
+
+  if (!cache_creation_time_value || !cache_creation_time_value->is_int())
+    return false;
+  *out_verdict_received_time = cache_creation_time_value->GetInt();
+
+  base::Value* verdict_proto_value = verdict_entry->FindKey(proto_name);
+  if (!verdict_proto_value || !verdict_proto_value->is_string())
+    return false;
+  std::string serialized_proto = verdict_proto_value->GetString();
+
+  return base::Base64Decode(serialized_proto, &serialized_proto) &&
+         out_verdict->ParseFromString(serialized_proto);
+}
+
+// Return the path of the cache expression. e.g.:
+// "www.google.com"     -> ""
+// "www.google.com/abc" -> "/abc"
+// "foo.com/foo/bar/"  -> "/foo/bar/"
+std::string GetCacheExpressionPath(const std::string& cache_expression) {
+  DCHECK(!cache_expression.empty());
+  size_t first_slash_pos = cache_expression.find_first_of("/");
+  if (first_slash_pos == std::string::npos)
+    return "";
+  return cache_expression.substr(first_slash_pos);
+}
+
+// Returns the number of path segments in |cache_expression_path|.
+// For example, return 0 for "/", since there is no path after the leading
+// slash; return 3 for "/abc/def/gh.html".
+size_t GetPathDepth(const std::string& cache_expression_path) {
+  return base::SplitString(base::StringPiece(cache_expression_path), "/",
+                           base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY)
+      .size();
+}
+
+bool PathVariantsMatchCacheExpression(
+    const std::vector<std::string>& generated_paths,
+    const std::string& cache_expression_path) {
+  return base::Contains(generated_paths, cache_expression_path);
+}
+
+bool IsCacheExpired(int cache_creation_time, int cache_duration) {
+  // Note that we assume client's clock is accurate or almost accurate.
+  return base::Time::Now().ToDoubleT() >
+         static_cast<double>(cache_creation_time + cache_duration);
+}
+
+template <class T>
+size_t RemoveExpiredEntries(base::Value* verdict_dictionary,
+                            const char* proto_name) {
+  DCHECK(proto_name == kVerdictProto || proto_name == kRealTimeThreatInfoProto);
+  std::vector<std::string> expired_keys;
+  for (const auto& item : verdict_dictionary->DictItems()) {
+    int verdict_received_time;
+    T verdict;
+    if (!ParseVerdictEntry<T>(&item.second, &verdict_received_time, &verdict,
+                              proto_name) ||
+        IsCacheExpired(verdict_received_time, verdict.cache_duration_sec())) {
+      expired_keys.push_back(item.first);
+    }
+  }
+
+  for (const std::string& key : expired_keys)
+    verdict_dictionary->RemoveKey(key);
+
+  return expired_keys.size();
+}
+
+// Helper function to determine if the given origin matches content settings
+// map's patterns.
+bool OriginMatchPrimaryPattern(
+    const GURL& origin,
+    const ContentSettingsPattern& primary_pattern,
+    const ContentSettingsPattern& secondary_pattern_unused) {
+  return ContentSettingsPattern::FromURLNoWildcard(origin) == primary_pattern;
+}
+
+std::string GetKeyOfTypeFromTriggerType(
+    LoginReputationClientRequest::TriggerType trigger_type,
+    ReusedPasswordAccountType password_type) {
+  return trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE
+             ? kPasswordOnFocusCacheKey
+             : base::NumberToString(
+                   static_cast<std::underlying_type_t<
+                       ReusedPasswordAccountType::AccountType>>(
+                       password_type.account_type()));
+}
+
+template <class T>
+typename T::VerdictType GetMostMatchingCachedVerdict(
+    const GURL& url,
+    const std::string& type_key,
+    scoped_refptr<HostContentSettingsMap> content_settings,
+    const ContentSettingsType contents_setting_type,
+    const char* proto_name,
+    T* out_response) {
+  DCHECK(proto_name == kVerdictProto || proto_name == kRealTimeThreatInfoProto);
+
+  GURL hostname = GetHostNameWithHTTPScheme(url);
+  std::unique_ptr<base::DictionaryValue> cache_dictionary =
+      base::DictionaryValue::From(content_settings->GetWebsiteSetting(
+          hostname, GURL(), contents_setting_type, std::string(), nullptr));
+
+  if (!cache_dictionary || cache_dictionary->empty())
+    return T::VERDICT_TYPE_UNSPECIFIED;
+
+  base::Value* verdict_dictionary =
+      cache_dictionary->FindKeyOfType(type_key, base::Value::Type::DICTIONARY);
+  if (!verdict_dictionary) {
+    return T::VERDICT_TYPE_UNSPECIFIED;
+  }
+
+  std::vector<std::string> paths;
+  GeneratePathVariantsWithoutQuery(url, &paths);
+  int max_path_depth = -1;
+  typename T::VerdictType most_matching_verdict_type =
+      T::VERDICT_TYPE_UNSPECIFIED;
+  // For all the verdicts of the same origin, we key them by |cache_expression|.
+  // Its corresponding value is a DictionaryValue contains its creation time and
+  // the serialized verdict proto.
+  for (const auto& item : verdict_dictionary->DictItems()) {
+    int verdict_received_time;
+    T verdict;
+    // Ignore any entry that we cannot parse. These invalid entries will be
+    // cleaned up during shutdown.
+    if (!ParseVerdictEntry<T>(&item.second, &verdict_received_time, &verdict,
+                              proto_name))
+      continue;
+    // Since verdict content settings are keyed by origin, we only need to
+    // compare the path part of the cache_expression and the given url.
+    std::string cache_expression_path =
+        GetCacheExpressionPath(verdict.cache_expression());
+
+    // Finds the most specific match.
+    int path_depth = static_cast<int>(GetPathDepth(cache_expression_path));
+    if (path_depth > max_path_depth &&
+        PathVariantsMatchCacheExpression(paths, cache_expression_path)) {
+      max_path_depth = path_depth;
+      // If the most matching verdict is expired, set the result to
+      // VERDICT_TYPE_UNSPECIFIED.
+      most_matching_verdict_type =
+          IsCacheExpired(verdict_received_time, verdict.cache_duration_sec())
+              ? T::VERDICT_TYPE_UNSPECIFIED
+              : verdict.verdict_type();
+      out_response->CopyFrom(verdict);
+    }
+  }
+  return most_matching_verdict_type;
+}
+
+}  // namespace
+
+VerdictCacheManager::VerdictCacheManager(
+    history::HistoryService* history_service,
+    scoped_refptr<HostContentSettingsMap> content_settings)
+    : stored_verdict_count_password_on_focus_(base::nullopt),
+      stored_verdict_count_password_entry_(base::nullopt),
+      content_settings_(content_settings) {
+  if (history_service)
+    history_service_observer_.Add(history_service);
+}
+
+VerdictCacheManager::~VerdictCacheManager() {
+  CleanUpExpiredVerdicts();
+  history_service_observer_.RemoveAll();
+  weak_factory_.InvalidateWeakPtrs();
+}
+
+void VerdictCacheManager::CachePhishGuardVerdict(
+    const GURL& url,
+    LoginReputationClientRequest::TriggerType trigger_type,
+    ReusedPasswordAccountType password_type,
+    const LoginReputationClientResponse& verdict,
+    const base::Time& receive_time) {
+  DCHECK(content_settings_);
+  DCHECK(trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
+         trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
+
+  GURL hostname = GetHostNameWithHTTPScheme(url);
+
+  std::unique_ptr<base::DictionaryValue> cache_dictionary =
+      base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
+          hostname, GURL(), ContentSettingsType::PASSWORD_PROTECTION,
+          std::string(), nullptr));
+
+  if (!cache_dictionary)
+    cache_dictionary = std::make_unique<base::DictionaryValue>();
+
+  std::unique_ptr<base::DictionaryValue> verdict_entry(
+      CreateDictionaryFromVerdict<LoginReputationClientResponse>(
+          verdict, receive_time, kVerdictProto));
+
+  std::string type_key =
+      GetKeyOfTypeFromTriggerType(trigger_type, password_type);
+  base::Value* verdict_dictionary =
+      cache_dictionary->FindKeyOfType(type_key, base::Value::Type::DICTIONARY);
+  if (!verdict_dictionary) {
+    verdict_dictionary = cache_dictionary->SetKey(
+        type_key, base::Value(base::Value::Type::DICTIONARY));
+  }
+
+  // Increases stored verdict count if we haven't seen this cache expression
+  // before.
+  if (!verdict_dictionary->FindKey(verdict.cache_expression())) {
+    base::Optional<size_t>* stored_verdict_count =
+        trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE
+            ? &stored_verdict_count_password_on_focus_
+            : &stored_verdict_count_password_entry_;
+    *stored_verdict_count = GetStoredPhishGuardVerdictCount(trigger_type) + 1;
+  }
+
+  // If same cache_expression is already in this verdict_dictionary, we simply
+  // override it.
+  verdict_dictionary->SetKey(
+      verdict.cache_expression(),
+      base::Value::FromUniquePtrValue(std::move(verdict_entry)));
+  content_settings_->SetWebsiteSettingDefaultScope(
+      hostname, GURL(), ContentSettingsType::PASSWORD_PROTECTION, std::string(),
+      std::move(cache_dictionary));
+}
+
+LoginReputationClientResponse::VerdictType
+VerdictCacheManager::GetCachedPhishGuardVerdict(
+    const GURL& url,
+    LoginReputationClientRequest::TriggerType trigger_type,
+    ReusedPasswordAccountType password_type,
+    LoginReputationClientResponse* out_response) {
+  DCHECK(trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
+         trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
+
+  std::string type_key =
+      GetKeyOfTypeFromTriggerType(trigger_type, password_type);
+
+  return GetMostMatchingCachedVerdict<LoginReputationClientResponse>(
+      url, type_key, content_settings_,
+      ContentSettingsType::PASSWORD_PROTECTION, kVerdictProto, out_response);
+}
+
+size_t VerdictCacheManager::GetStoredPhishGuardVerdictCount(
+    LoginReputationClientRequest::TriggerType trigger_type) {
+  DCHECK(content_settings_);
+  DCHECK(trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
+         trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
+  base::Optional<size_t>* stored_verdict_count =
+      trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE
+          ? &stored_verdict_count_password_on_focus_
+          : &stored_verdict_count_password_entry_;
+  // If we have already computed this, return its value.
+  if (stored_verdict_count->has_value())
+    return stored_verdict_count->value();
+
+  ContentSettingsForOneType password_protection_settings;
+  content_settings_->GetSettingsForOneType(
+      ContentSettingsType::PASSWORD_PROTECTION, std::string(),
+      &password_protection_settings);
+  stored_verdict_count_password_on_focus_ = 0;
+  stored_verdict_count_password_entry_ = 0;
+  if (password_protection_settings.empty())
+    return 0;
+
+  for (const ContentSettingPatternSource& source :
+       password_protection_settings) {
+    std::unique_ptr<base::DictionaryValue> cache_dictionary =
+        base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
+            GURL(source.primary_pattern.ToString()), GURL(),
+            ContentSettingsType::PASSWORD_PROTECTION, std::string(), nullptr));
+    if (cache_dictionary.get() && !cache_dictionary->empty()) {
+      for (const auto& item : cache_dictionary->DictItems()) {
+        if (item.first == base::StringPiece(kPasswordOnFocusCacheKey)) {
+          stored_verdict_count_password_on_focus_.value() +=
+              item.second.DictSize();
+        } else {
+          stored_verdict_count_password_entry_.value() +=
+              item.second.DictSize();
+        }
+      }
+    }
+  }
+
+  return stored_verdict_count->value();
+}
+
+void VerdictCacheManager::CacheRealTimeUrlVerdict(
+    const GURL& url,
+    const RTLookupResponse& verdict,
+    const base::Time& receive_time) {
+  GURL hostname = GetHostNameWithHTTPScheme(url);
+  std::unique_ptr<base::DictionaryValue> cache_dictionary =
+      base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
+          hostname, GURL(), ContentSettingsType::SAFE_BROWSING_URL_CHECK_DATA,
+          std::string(), nullptr));
+
+  if (!cache_dictionary)
+    cache_dictionary = std::make_unique<base::DictionaryValue>();
+
+  base::Value* verdict_dictionary = cache_dictionary->FindKeyOfType(
+      kRealTimeUrlCacheKey, base::Value::Type::DICTIONARY);
+  if (!verdict_dictionary) {
+    verdict_dictionary = cache_dictionary->SetKey(
+        kRealTimeUrlCacheKey, base::Value(base::Value::Type::DICTIONARY));
+  }
+
+  std::vector<std::string> visited_cache_expressions;
+  for (const auto& threat_info : verdict.threat_info()) {
+    std::string cache_expression = threat_info.cache_expression();
+    // TODO(crbug.com/1033692): For the same cache_expression, threat_info is in
+    // decreasing order of severity. To avoid lower severity threat being
+    // overridden by higher one, only store threat info that is first seen for a
+    // cache expression.
+    if (base::Contains(visited_cache_expressions, cache_expression))
+      continue;
+    std::unique_ptr<base::DictionaryValue> threat_info_entry(
+        CreateDictionaryFromVerdict<RTLookupResponse::ThreatInfo>(
+            threat_info, receive_time, kRealTimeThreatInfoProto));
+    // Increases stored verdict count if we haven't seen this cache expression
+    // before.
+    if (!verdict_dictionary->FindKey(cache_expression)) {
+      stored_verdict_count_real_time_url_check_++;
+    }
+    verdict_dictionary->SetKey(
+        cache_expression,
+        base::Value::FromUniquePtrValue(std::move(threat_info_entry)));
+    visited_cache_expressions.push_back(cache_expression);
+  }
+  content_settings_->SetWebsiteSettingDefaultScope(
+      hostname, GURL(), ContentSettingsType::SAFE_BROWSING_URL_CHECK_DATA,
+      std::string(), std::move(cache_dictionary));
+}
+
+RTLookupResponse::ThreatInfo::VerdictType
+VerdictCacheManager::GetCachedRealTimeUrlVerdict(
+    const GURL& url,
+    RTLookupResponse::ThreatInfo* out_threat_info) {
+  return GetMostMatchingCachedVerdict<RTLookupResponse::ThreatInfo>(
+      url, kRealTimeUrlCacheKey, content_settings_,
+      ContentSettingsType::SAFE_BROWSING_URL_CHECK_DATA,
+      kRealTimeThreatInfoProto, out_threat_info);
+}
+
+void VerdictCacheManager::CleanUpExpiredVerdicts() {
+  DCHECK(content_settings_);
+
+  CleanUpExpiredPhishGuardVerdicts();
+  CleanUpExpiredRealTimeUrlCheckVerdicts();
+}
+
+void VerdictCacheManager::CleanUpExpiredPhishGuardVerdicts() {
+  if (GetStoredPhishGuardVerdictCount(
+          LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) <= 0 &&
+      GetStoredPhishGuardVerdictCount(
+          LoginReputationClientRequest::PASSWORD_REUSE_EVENT) <= 0)
+    return;
+
+  ContentSettingsForOneType password_protection_settings;
+  content_settings_->GetSettingsForOneType(
+      ContentSettingsType::PASSWORD_PROTECTION, std::string(),
+      &password_protection_settings);
+
+  for (const ContentSettingPatternSource& source :
+       password_protection_settings) {
+    GURL primary_pattern_url = GURL(source.primary_pattern.ToString());
+    // Find all verdicts associated with this origin.
+    std::unique_ptr<base::DictionaryValue> cache_dictionary =
+        base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
+            primary_pattern_url, GURL(),
+            ContentSettingsType::PASSWORD_PROTECTION, std::string(), nullptr));
+    bool has_expired_password_on_focus_entry = RemoveExpiredPhishGuardVerdicts(
+        LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+        cache_dictionary.get());
+    bool has_expired_password_reuse_entry = RemoveExpiredPhishGuardVerdicts(
+        LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+        cache_dictionary.get());
+
+    if (cache_dictionary->size() == 0u) {
+      content_settings_->ClearSettingsForOneTypeWithPredicate(
+          ContentSettingsType::PASSWORD_PROTECTION, base::Time(),
+          base::Time::Max(),
+          base::BindRepeating(&OriginMatchPrimaryPattern, primary_pattern_url));
+    } else if (has_expired_password_on_focus_entry ||
+               has_expired_password_reuse_entry) {
+      // Set the website setting of this origin with the updated
+      // |cache_dictionary|.
+      content_settings_->SetWebsiteSettingDefaultScope(
+          primary_pattern_url, GURL(), ContentSettingsType::PASSWORD_PROTECTION,
+          std::string(), std::move(cache_dictionary));
+    }
+  }
+}
+
+void VerdictCacheManager::CleanUpExpiredRealTimeUrlCheckVerdicts() {
+  ContentSettingsForOneType safe_browsing_url_check_data_settings;
+  content_settings_->GetSettingsForOneType(
+      ContentSettingsType::SAFE_BROWSING_URL_CHECK_DATA, std::string(),
+      &safe_browsing_url_check_data_settings);
+
+  for (const ContentSettingPatternSource& source :
+       safe_browsing_url_check_data_settings) {
+    GURL primary_pattern_url = GURL(source.primary_pattern.ToString());
+    // Find all verdicts associated with this origin.
+    std::unique_ptr<base::DictionaryValue> cache_dictionary =
+        base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
+            primary_pattern_url, GURL(),
+            ContentSettingsType::SAFE_BROWSING_URL_CHECK_DATA, std::string(),
+            nullptr));
+    bool has_expired_entry =
+        RemoveExpiredRealTimeUrlCheckVerdicts(cache_dictionary.get());
+
+    if (cache_dictionary->size() == 0u) {
+      content_settings_->ClearSettingsForOneTypeWithPredicate(
+          ContentSettingsType::SAFE_BROWSING_URL_CHECK_DATA, base::Time(),
+          base::Time::Max(),
+          base::BindRepeating(&OriginMatchPrimaryPattern, primary_pattern_url));
+    } else if (has_expired_entry) {
+      // Set the website setting of this origin with the updated
+      // |cache_dictionary|.
+      content_settings_->SetWebsiteSettingDefaultScope(
+          primary_pattern_url, GURL(),
+          ContentSettingsType::SAFE_BROWSING_URL_CHECK_DATA, std::string(),
+          std::move(cache_dictionary));
+    }
+  }
+}
+
+// Overridden from history::HistoryServiceObserver.
+void VerdictCacheManager::OnURLsDeleted(
+    history::HistoryService* history_service,
+    const history::DeletionInfo& deletion_info) {
+  base::PostTask(FROM_HERE, {content::BrowserThread::UI},
+                 base::BindRepeating(
+                     &VerdictCacheManager::RemoveContentSettingsOnURLsDeleted,
+                     GetWeakPtr(), deletion_info.IsAllHistory(),
+                     deletion_info.deleted_rows()));
+}
+
+// Overridden from history::HistoryServiceObserver.
+void VerdictCacheManager::HistoryServiceBeingDeleted(
+    history::HistoryService* history_service) {
+  history_service_observer_.Remove(history_service);
+}
+
+bool VerdictCacheManager::RemoveExpiredPhishGuardVerdicts(
+    LoginReputationClientRequest::TriggerType trigger_type,
+    base::DictionaryValue* cache_dictionary) {
+  DCHECK(trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
+         trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
+  if (!cache_dictionary || cache_dictionary->empty())
+    return false;
+
+  size_t verdicts_removed = 0;
+  std::vector<std::string> empty_keys;
+  for (auto item : cache_dictionary->DictItems()) {
+    if (trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE &&
+        item.first == std::string(kPasswordOnFocusCacheKey)) {
+      size_t removed_cnt = RemoveExpiredEntries<LoginReputationClientResponse>(
+          &item.second, kVerdictProto);
+      verdicts_removed += removed_cnt;
+      if (stored_verdict_count_password_on_focus_.has_value())
+        stored_verdict_count_password_on_focus_.value() -= removed_cnt;
+    } else {
+      size_t removed_cnt = RemoveExpiredEntries<LoginReputationClientResponse>(
+          &item.second, kVerdictProto);
+      verdicts_removed += removed_cnt;
+      if (stored_verdict_count_password_entry_.has_value())
+        stored_verdict_count_password_entry_.value() -= removed_cnt;
+    }
+
+    if (item.second.DictSize() == 0U)
+      empty_keys.push_back(item.first);
+  }
+  for (const auto& key : empty_keys)
+    cache_dictionary->RemoveKey(key);
+
+  return verdicts_removed > 0U;
+}
+
+bool VerdictCacheManager::RemoveExpiredRealTimeUrlCheckVerdicts(
+    base::DictionaryValue* cache_dictionary) {
+  if (!cache_dictionary || cache_dictionary->empty())
+    return false;
+
+  size_t verdicts_removed = 0;
+  std::vector<std::string> empty_keys;
+  for (auto item : cache_dictionary->DictItems()) {
+    size_t removed_cnt = RemoveExpiredEntries<RTLookupResponse::ThreatInfo>(
+        &item.second, kRealTimeThreatInfoProto);
+    verdicts_removed += removed_cnt;
+    stored_verdict_count_real_time_url_check_ -= removed_cnt;
+    if (item.second.DictSize() == 0U)
+      empty_keys.push_back(item.first);
+  }
+  for (const auto& key : empty_keys)
+    cache_dictionary->RemoveKey(key);
+
+  return verdicts_removed > 0U;
+}
+
+void VerdictCacheManager::RemoveContentSettingsOnURLsDeleted(
+    bool all_history,
+    const history::URLRows& deleted_rows) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  DCHECK(content_settings_);
+
+  if (all_history) {
+    content_settings_->ClearSettingsForOneType(
+        ContentSettingsType::PASSWORD_PROTECTION);
+    stored_verdict_count_password_on_focus_ = 0;
+    stored_verdict_count_password_entry_ = 0;
+    stored_verdict_count_real_time_url_check_ = 0;
+    content_settings_->ClearSettingsForOneType(
+        ContentSettingsType::SAFE_BROWSING_URL_CHECK_DATA);
+    return;
+  }
+
+  // For now, if a URL is deleted from history, we simply remove all the
+  // cached verdicts of the same origin. This is a pretty aggressive deletion.
+  // We might revisit this logic later to decide if we want to only delete the
+  // cached verdict whose cache expression matches this URL.
+  for (const history::URLRow& row : deleted_rows) {
+    if (!row.url().SchemeIsHTTPOrHTTPS())
+      continue;
+
+    GURL url_key = GetHostNameWithHTTPScheme(row.url());
+    stored_verdict_count_password_on_focus_ =
+        GetStoredPhishGuardVerdictCount(
+            LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) -
+        GetPhishGuardVerdictCountForURL(
+            url_key, LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE);
+    stored_verdict_count_password_entry_ =
+        GetStoredPhishGuardVerdictCount(
+            LoginReputationClientRequest::PASSWORD_REUSE_EVENT) -
+        GetPhishGuardVerdictCountForURL(
+            url_key, LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
+    stored_verdict_count_real_time_url_check_ -=
+        GetRealTimeUrlCheckVerdictCountForURL(url_key);
+    content_settings_->ClearSettingsForOneTypeWithPredicate(
+        ContentSettingsType::PASSWORD_PROTECTION, base::Time(),
+        base::Time::Max(),
+        base::BindRepeating(&OriginMatchPrimaryPattern, url_key));
+    content_settings_->ClearSettingsForOneTypeWithPredicate(
+        ContentSettingsType::SAFE_BROWSING_URL_CHECK_DATA, base::Time(),
+        base::Time::Max(),
+        base::BindRepeating(&OriginMatchPrimaryPattern, url_key));
+  }
+}
+
+size_t VerdictCacheManager::GetPhishGuardVerdictCountForURL(
+    const GURL& url,
+    LoginReputationClientRequest::TriggerType trigger_type) {
+  DCHECK(trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
+         trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
+  std::unique_ptr<base::DictionaryValue> cache_dictionary =
+      base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
+          url, GURL(), ContentSettingsType::PASSWORD_PROTECTION, std::string(),
+          nullptr));
+  if (!cache_dictionary || cache_dictionary->empty())
+    return 0;
+
+  int verdict_cnt = 0;
+  if (trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) {
+    base::Value* password_on_focus_dict = nullptr;
+    password_on_focus_dict =
+        cache_dictionary->FindKey(kPasswordOnFocusCacheKey);
+    verdict_cnt +=
+        password_on_focus_dict ? password_on_focus_dict->DictSize() : 0;
+  } else {
+    for (const auto& item : cache_dictionary->DictItems()) {
+      if (item.first == kPasswordOnFocusCacheKey)
+        continue;
+      verdict_cnt += item.second.DictSize();
+    }
+  }
+  return verdict_cnt;
+}
+
+size_t VerdictCacheManager::GetRealTimeUrlCheckVerdictCountForURL(
+    const GURL& url) {
+  std::unique_ptr<base::DictionaryValue> cache_dictionary =
+      base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
+          url, GURL(), ContentSettingsType::PASSWORD_PROTECTION, std::string(),
+          nullptr));
+  if (!cache_dictionary || cache_dictionary->empty())
+    return 0;
+  base::Value* verdict_dictionary =
+      cache_dictionary->FindKey(kRealTimeUrlCacheKey);
+  return verdict_dictionary ? verdict_dictionary->DictSize() : 0;
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/verdict_cache_manager.h b/components/safe_browsing/core/verdict_cache_manager.h
new file mode 100644
index 0000000..d7db56e
--- /dev/null
+++ b/components/safe_browsing/core/verdict_cache_manager.h
@@ -0,0 +1,147 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CORE_VERDICT_CACHE_MANAGER_H_
+#define COMPONENTS_SAFE_BROWSING_CORE_VERDICT_CACHE_MANAGER_H_
+
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/scoped_observer.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "components/content_settings/core/browser/host_content_settings_map.h"
+#include "components/history/core/browser/history_service.h"
+#include "components/history/core/browser/history_service_observer.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/realtimeapi.pb.h"
+#include "url/gurl.h"
+
+class HostContentSettingsMap;
+
+namespace safe_browsing {
+
+using ReusedPasswordAccountType =
+    LoginReputationClientRequest::PasswordReuseEvent::ReusedPasswordAccountType;
+
+class VerdictCacheManager : public history::HistoryServiceObserver {
+ public:
+  explicit VerdictCacheManager(
+      history::HistoryService* history_service,
+      scoped_refptr<HostContentSettingsMap> content_settings);
+  VerdictCacheManager(const VerdictCacheManager&) = delete;
+  VerdictCacheManager& operator=(const VerdictCacheManager&) = delete;
+  VerdictCacheManager(VerdictCacheManager&&) = delete;
+  VerdictCacheManager& operator=(const VerdictCacheManager&&) = delete;
+
+  ~VerdictCacheManager() override;
+
+  base::WeakPtr<VerdictCacheManager> GetWeakPtr() {
+    return weak_factory_.GetWeakPtr();
+  }
+
+  // Stores |verdict| in |content_settings_| based on its |trigger_type|, |url|,
+  // reused |password_type|, |verdict| and |receive_time|.
+  void CachePhishGuardVerdict(
+      const GURL& url,
+      LoginReputationClientRequest::TriggerType trigger_type,
+      ReusedPasswordAccountType password_type,
+      const LoginReputationClientResponse& verdict,
+      const base::Time& receive_time);
+
+  // Looks up |content_settings_| to find the cached verdict response. If
+  // verdict is not available or is expired, return VERDICT_TYPE_UNSPECIFIED.
+  // Can be called on any thread.
+  LoginReputationClientResponse::VerdictType GetCachedPhishGuardVerdict(
+      const GURL& url,
+      LoginReputationClientRequest::TriggerType trigger_type,
+      ReusedPasswordAccountType password_type,
+      LoginReputationClientResponse* out_response);
+
+  // Gets the total number of verdicts of the specified |trigger_type| we cached
+  // for this profile. This counts both expired and active verdicts.
+  size_t GetStoredPhishGuardVerdictCount(
+      LoginReputationClientRequest::TriggerType trigger_type);
+
+  // Stores |verdict| in |content_settings_| based on its |url|, |verdict| and
+  // |receive_time|.
+  void CacheRealTimeUrlVerdict(const GURL& url,
+                               const RTLookupResponse& verdict,
+                               const base::Time& receive_time);
+
+  // Looks up |content_settings_| to find the cached verdict response. If
+  // verdict is not available or is expired, return VERDICT_TYPE_UNSPECIFIED.
+  // Otherwise, the most matching theat info will be copied to out_threat_info.
+  // Can be called on any thread.
+  RTLookupResponse::ThreatInfo::VerdictType GetCachedRealTimeUrlVerdict(
+      const GURL& url,
+      RTLookupResponse::ThreatInfo* out_threat_info);
+
+  // Overridden from history::HistoryServiceObserver.
+  void OnURLsDeleted(history::HistoryService* history_service,
+                     const history::DeletionInfo& deletion_info) override;
+
+  void HistoryServiceBeingDeleted(
+      history::HistoryService* history_service) override;
+
+ private:
+  FRIEND_TEST_ALL_PREFIXES(VerdictCacheManagerTest, TestCleanUpExpiredVerdict);
+  FRIEND_TEST_ALL_PREFIXES(VerdictCacheManagerTest,
+                           TestCleanUpExpiredVerdictWithInvalidEntry);
+  FRIEND_TEST_ALL_PREFIXES(VerdictCacheManagerTest,
+                           TestRemoveCachedVerdictOnURLsDeleted);
+  FRIEND_TEST_ALL_PREFIXES(
+      VerdictCacheManagerTest,
+      TestRemoveRealTimeUrlCheckCachedVerdictOnURLsDeleted);
+
+  // Removes all the expired verdicts from cache.
+  void CleanUpExpiredVerdicts();
+  void CleanUpExpiredPhishGuardVerdicts();
+  void CleanUpExpiredRealTimeUrlCheckVerdicts();
+
+  // Helper method to remove content settings when URLs are deleted. If
+  // |all_history| is true, removes all cached verdicts. Otherwise it removes
+  // all verdicts associated with the deleted URLs in |deleted_rows|.
+  void RemoveContentSettingsOnURLsDeleted(bool all_history,
+                                          const history::URLRows& deleted_rows);
+  bool RemoveExpiredPhishGuardVerdicts(
+      LoginReputationClientRequest::TriggerType trigger_type,
+      base::DictionaryValue* cache_dictionary);
+  bool RemoveExpiredRealTimeUrlCheckVerdicts(
+      base::DictionaryValue* cache_dictionary);
+
+  size_t GetPhishGuardVerdictCountForURL(
+      const GURL& url,
+      LoginReputationClientRequest::TriggerType trigger_type);
+  // This method is only used for testing.
+  size_t GetRealTimeUrlCheckVerdictCountForURL(const GURL& url);
+
+  // This method is only used for testing.
+  int GetStoredRealTimeUrlCheckVerdictCount() {
+    return stored_verdict_count_real_time_url_check_;
+  }
+
+  // Number of verdict stored for this profile for password on focus pings.
+  base::Optional<size_t> stored_verdict_count_password_on_focus_;
+
+  // Number of verdict stored for this profile for protected password entry
+  // pings.
+  base::Optional<size_t> stored_verdict_count_password_entry_;
+
+  // Number of verdict stored for this profile for real time url check pings.
+  // This is only used for testing.
+  int stored_verdict_count_real_time_url_check_ = 0;
+
+  ScopedObserver<history::HistoryService, history::HistoryServiceObserver>
+      history_service_observer_{this};
+
+  // Content settings maps associated with this instance.
+  scoped_refptr<HostContentSettingsMap> content_settings_;
+
+  base::WeakPtrFactory<VerdictCacheManager> weak_factory_{this};
+};
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CORE_VERDICT_CACHE_MANAGER_H_
diff --git a/components/safe_browsing/core/verdict_cache_manager_unittest.cc b/components/safe_browsing/core/verdict_cache_manager_unittest.cc
new file mode 100644
index 0000000..9e6656c
--- /dev/null
+++ b/components/safe_browsing/core/verdict_cache_manager_unittest.cc
@@ -0,0 +1,549 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/core/verdict_cache_manager.h"
+
+#include "base/base64.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/values.h"
+#include "components/content_settings/core/browser/host_content_settings_map.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
+#include "components/safe_browsing/core/proto/realtimeapi.pb.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "content/public/test/browser_task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace safe_browsing {
+
+class VerdictCacheManagerTest : public ::testing::Test {
+ public:
+  VerdictCacheManagerTest() {}
+
+  void SetUp() override {
+    HostContentSettingsMap::RegisterProfilePrefs(test_pref_service_.registry());
+    content_setting_map_ = new HostContentSettingsMap(
+        &test_pref_service_, false /* is_off_the_record */,
+        false /* store_last_modified */,
+        false /* migrate_requesting_and_top_level_origin_settings */);
+    cache_manager_ = std::make_unique<VerdictCacheManager>(
+        nullptr, content_setting_map_.get());
+  }
+
+  void TearDown() override {
+    cache_manager_.reset();
+    content_setting_map_->ShutdownOnUIThread();
+  }
+
+  void CachePhishGuardVerdict(
+      const GURL& url,
+      LoginReputationClientRequest::TriggerType trigger,
+      ReusedPasswordAccountType password_type,
+      LoginReputationClientResponse::VerdictType verdict,
+      int cache_duration_sec,
+      const std::string& cache_expression,
+      const base::Time& verdict_received_time) {
+    ASSERT_FALSE(cache_expression.empty());
+    LoginReputationClientResponse response;
+    response.set_verdict_type(verdict);
+    response.set_cache_expression(cache_expression);
+    response.set_cache_duration_sec(cache_duration_sec);
+    cache_manager_->CachePhishGuardVerdict(url, trigger, password_type,
+                                           response, verdict_received_time);
+  }
+
+  void AddThreatInfoToResponse(
+      RTLookupResponse& response,
+      RTLookupResponse::ThreatInfo::VerdictType verdict_type,
+      RTLookupResponse::ThreatInfo::ThreatType threat_type,
+      int cache_duration_sec,
+      const std::string& cache_expression) {
+    RTLookupResponse::ThreatInfo* new_threat_info = response.add_threat_info();
+    new_threat_info->set_verdict_type(verdict_type);
+    new_threat_info->set_threat_type(threat_type);
+    new_threat_info->set_cache_duration_sec(cache_duration_sec);
+    new_threat_info->set_cache_expression(cache_expression);
+  }
+
+ protected:
+  std::unique_ptr<VerdictCacheManager> cache_manager_;
+  scoped_refptr<HostContentSettingsMap> content_setting_map_;
+
+ private:
+  content::BrowserTaskEnvironment task_environment_;
+  sync_preferences::TestingPrefServiceSyncable test_pref_service_;
+};
+
+TEST_F(VerdictCacheManagerTest, TestCanRetrieveCachedVerdict) {
+  GURL url("https://www.google.com/");
+  ReusedPasswordAccountType password_type;
+  password_type.set_account_type(ReusedPasswordAccountType::GSUITE);
+  password_type.set_is_account_syncing(true);
+  LoginReputationClientResponse cached_verdict;
+  cached_verdict.set_cache_expression("www.google.com/");
+  EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
+            cache_manager_->GetCachedPhishGuardVerdict(
+                url, LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+                password_type, &cached_verdict));
+
+  CachePhishGuardVerdict(url,
+                         LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+                         password_type, LoginReputationClientResponse::SAFE, 60,
+                         "www.google.com/", base::Time::Now());
+
+  EXPECT_EQ(LoginReputationClientResponse::SAFE,
+            cache_manager_->GetCachedPhishGuardVerdict(
+                url, LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+                password_type, &cached_verdict));
+}
+
+TEST_F(VerdictCacheManagerTest, TestCacheSplitByTriggerType) {
+  GURL url("https://www.google.com/");
+  ReusedPasswordAccountType password_type;
+  password_type.set_account_type(ReusedPasswordAccountType::GSUITE);
+  password_type.set_is_account_syncing(true);
+  LoginReputationClientResponse cached_verdict;
+  cached_verdict.set_cache_expression("www.google.com/");
+  EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
+            cache_manager_->GetCachedPhishGuardVerdict(
+                url, LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+                password_type, &cached_verdict));
+
+  CachePhishGuardVerdict(url,
+                         LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+                         password_type, LoginReputationClientResponse::SAFE, 60,
+                         "www.google.com/", base::Time::Now());
+
+  EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
+            cache_manager_->GetCachedPhishGuardVerdict(
+                url, LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+                password_type, &cached_verdict));
+}
+
+TEST_F(VerdictCacheManagerTest, TestCacheSplitByPasswordType) {
+  GURL url("https://www.google.com/");
+  ReusedPasswordAccountType password_type;
+  password_type.set_account_type(ReusedPasswordAccountType::GSUITE);
+  password_type.set_is_account_syncing(true);
+  LoginReputationClientResponse cached_verdict;
+  cached_verdict.set_cache_expression("www.google.com/");
+  EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
+            cache_manager_->GetCachedPhishGuardVerdict(
+                url, LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+                password_type, &cached_verdict));
+
+  password_type.set_account_type(
+      ReusedPasswordAccountType::NON_GAIA_ENTERPRISE);
+  CachePhishGuardVerdict(url,
+                         LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+                         password_type, LoginReputationClientResponse::SAFE, 60,
+                         "www.google.com/", base::Time::Now());
+  password_type.set_account_type(ReusedPasswordAccountType::GSUITE);
+  EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
+            cache_manager_->GetCachedPhishGuardVerdict(
+                url, LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+                password_type, &cached_verdict));
+}
+
+TEST_F(VerdictCacheManagerTest, TestGetStoredPhishGuardVerdictCount) {
+  GURL url("https://www.google.com/");
+
+  LoginReputationClientResponse cached_verdict;
+  cached_verdict.set_cache_expression("www.google.com/");
+  EXPECT_EQ(0u, cache_manager_->GetStoredPhishGuardVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+  ReusedPasswordAccountType password_type;
+  password_type.set_account_type(
+      ReusedPasswordAccountType::NON_GAIA_ENTERPRISE);
+  password_type.set_is_account_syncing(true);
+  CachePhishGuardVerdict(url,
+                         LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+                         password_type, LoginReputationClientResponse::SAFE, 60,
+                         "www.google.com/", base::Time::Now());
+
+  EXPECT_EQ(1u, cache_manager_->GetStoredPhishGuardVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+
+  CachePhishGuardVerdict(url,
+                         LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+                         password_type, LoginReputationClientResponse::SAFE, 60,
+                         "www.google.com/", base::Time::Now());
+
+  EXPECT_EQ(1u, cache_manager_->GetStoredPhishGuardVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+
+  CachePhishGuardVerdict(url,
+                         LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+                         password_type, LoginReputationClientResponse::SAFE, 60,
+                         "www.google.com/path", base::Time::Now());
+
+  EXPECT_EQ(2u, cache_manager_->GetStoredPhishGuardVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+}
+
+TEST_F(VerdictCacheManagerTest, TestParseInvalidVerdictEntry) {
+  // Directly save an invalid cache entry.
+  LoginReputationClientResponse verdict;
+  verdict.set_verdict_type(LoginReputationClientResponse::SAFE);
+  verdict.set_cache_expression("www.google.com/");
+  verdict.set_cache_duration_sec(60);
+
+  std::string verdict_serialized;
+  verdict.SerializeToString(&verdict_serialized);
+  base::Base64Encode(verdict_serialized, &verdict_serialized);
+
+  auto cache_dictionary = std::make_unique<base::DictionaryValue>();
+  auto* verdict_dictionary =
+      cache_dictionary->SetKey("2", base::Value(base::Value::Type::DICTIONARY));
+  auto* verdict_entry = verdict_dictionary->SetKey(
+      "www.google.com/", base::Value(base::Value::Type::DICTIONARY));
+  verdict_entry->SetStringKey("cache_creation_time", "invalid_time");
+  verdict_entry->SetStringKey("verdict_proto", verdict_serialized);
+
+  content_setting_map_->SetWebsiteSettingDefaultScope(
+      GURL("http://www.google.com/"), GURL(),
+      ContentSettingsType::PASSWORD_PROTECTION, std::string(),
+      std::move(cache_dictionary));
+
+  ReusedPasswordAccountType password_type;
+  password_type.set_account_type(ReusedPasswordAccountType::GSUITE);
+  password_type.set_is_account_syncing(true);
+
+  LoginReputationClientResponse cached_verdict;
+  EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
+            cache_manager_->GetCachedPhishGuardVerdict(
+                GURL("https://www.google.com/"),
+                LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+                password_type, &cached_verdict));
+}
+
+TEST_F(VerdictCacheManagerTest, TestRemoveCachedVerdictOnURLsDeleted) {
+  ASSERT_EQ(0u, cache_manager_->GetStoredPhishGuardVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+  ASSERT_EQ(0u, cache_manager_->GetStoredPhishGuardVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
+  // Prepare 5 verdicts. Three are for origin "http://foo.com", and the others
+  // are for "http://bar.com".
+  base::Time now = base::Time::Now();
+  ReusedPasswordAccountType password_type;
+  password_type.set_account_type(ReusedPasswordAccountType::GSUITE);
+  CachePhishGuardVerdict(
+      GURL("http://foo.com/abc/index.jsp"),
+      LoginReputationClientRequest::PASSWORD_REUSE_EVENT, password_type,
+      LoginReputationClientResponse::LOW_REPUTATION, 600, "foo.com/abc/", now);
+  password_type.set_account_type(
+      ReusedPasswordAccountType::NON_GAIA_ENTERPRISE);
+  CachePhishGuardVerdict(
+      GURL("http://foo.com/abc/index.jsp"),
+      LoginReputationClientRequest::PASSWORD_REUSE_EVENT, password_type,
+      LoginReputationClientResponse::LOW_REPUTATION, 600, "foo.com/abc/", now);
+  password_type.set_account_type(ReusedPasswordAccountType::GSUITE);
+  CachePhishGuardVerdict(GURL("http://bar.com/index.jsp"),
+                         LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+                         password_type, LoginReputationClientResponse::PHISHING,
+                         600, "bar.com", now);
+  ASSERT_EQ(3u, cache_manager_->GetStoredPhishGuardVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+
+  password_type.set_account_type(ReusedPasswordAccountType::UNKNOWN);
+  CachePhishGuardVerdict(
+      GURL("http://foo.com/abc/index.jsp"),
+      LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE, password_type,
+      LoginReputationClientResponse::LOW_REPUTATION, 600, "foo.com/abc/", now);
+  CachePhishGuardVerdict(GURL("http://bar.com/index.jsp"),
+                         LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+                         password_type, LoginReputationClientResponse::PHISHING,
+                         600, "bar.com", now);
+  ASSERT_EQ(2u, cache_manager_->GetStoredPhishGuardVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
+
+  // Delete a bar.com URL. Corresponding content setting keyed on
+  // origin "http://bar.com" should be removed,
+  history::URLRows deleted_urls;
+  deleted_urls.push_back(history::URLRow(GURL("http://bar.com")));
+
+  // Delete an arbitrary data URL, to ensure the service is robust against
+  // filtering only http/s URLs. See crbug.com/709758.
+  deleted_urls.push_back(history::URLRow(GURL("data:text/html, <p>hellow")));
+
+  cache_manager_->RemoveContentSettingsOnURLsDeleted(false /* all_history */,
+                                                     deleted_urls);
+  EXPECT_EQ(2u, cache_manager_->GetStoredPhishGuardVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+  EXPECT_EQ(1u, cache_manager_->GetStoredPhishGuardVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
+
+  LoginReputationClientResponse actual_verdict;
+  password_type.set_account_type(ReusedPasswordAccountType::GSUITE);
+  EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
+            cache_manager_->GetCachedPhishGuardVerdict(
+                GURL("http://bar.com"),
+                LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+                password_type, &actual_verdict));
+  password_type.set_account_type(ReusedPasswordAccountType::UNKNOWN);
+  EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
+            cache_manager_->GetCachedPhishGuardVerdict(
+                GURL("http://bar.com"),
+                LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+                password_type, &actual_verdict));
+
+  // If delete all history. All password protection content settings should be
+  // gone.
+  cache_manager_->RemoveContentSettingsOnURLsDeleted(true /* all_history */,
+                                                     history::URLRows());
+  EXPECT_EQ(0u, cache_manager_->GetStoredPhishGuardVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+  EXPECT_EQ(0u, cache_manager_->GetStoredPhishGuardVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
+}
+
+TEST_F(VerdictCacheManagerTest, TestCleanUpExpiredVerdict) {
+  // Prepare 4 verdicts for PASSWORD_REUSE_EVENT with SIGN_IN_PASSWORD type:
+  // (1) "foo.com/abc/" valid
+  // (2) "foo.com/def/" expired
+  // (3) "bar.com/abc/" expired
+  // (4) "bar.com/def/" expired
+  base::Time now = base::Time::Now();
+  ReusedPasswordAccountType password_type;
+  password_type.set_account_type(ReusedPasswordAccountType::GSUITE);
+  CachePhishGuardVerdict(
+      GURL("https://foo.com/abc/index.jsp"),
+      LoginReputationClientRequest::PASSWORD_REUSE_EVENT, password_type,
+      LoginReputationClientResponse::LOW_REPUTATION, 600, "foo.com/abc/", now);
+  CachePhishGuardVerdict(
+      GURL("https://foo.com/def/index.jsp"),
+      LoginReputationClientRequest::PASSWORD_REUSE_EVENT, password_type,
+      LoginReputationClientResponse::LOW_REPUTATION, 0, "foo.com/def/", now);
+  CachePhishGuardVerdict(GURL("https://bar.com/abc/index.jsp"),
+                         LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+                         password_type, LoginReputationClientResponse::PHISHING,
+                         0, "bar.com/abc/", now);
+  CachePhishGuardVerdict(GURL("https://bar.com/def/index.jsp"),
+                         LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+                         password_type, LoginReputationClientResponse::PHISHING,
+                         0, "bar.com/def/", now);
+  ASSERT_EQ(4u, cache_manager_->GetStoredPhishGuardVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+
+  // Prepare 2 verdicts for UNFAMILIAR_LOGIN_PAGE:
+  // (1) "bar.com/def/" valid
+  // (2) "bar.com/xyz/" expired
+  password_type.set_account_type(ReusedPasswordAccountType::UNKNOWN);
+  CachePhishGuardVerdict(GURL("https://bar.com/def/index.jsp"),
+                         LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+                         password_type, LoginReputationClientResponse::SAFE,
+                         600, "bar.com/def/", now);
+  CachePhishGuardVerdict(GURL("https://bar.com/xyz/index.jsp"),
+                         LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+                         password_type, LoginReputationClientResponse::PHISHING,
+                         0, "bar.com/xyz/", now);
+  ASSERT_EQ(2u, cache_manager_->GetStoredPhishGuardVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
+
+  RTLookupResponse response;
+  AddThreatInfoToResponse(response, RTLookupResponse::ThreatInfo::DANGEROUS,
+                          RTLookupResponse::ThreatInfo::SOCIAL_ENGINEERING, 0,
+                          "www.example.com/");
+  AddThreatInfoToResponse(response, RTLookupResponse::ThreatInfo::DANGEROUS,
+                          RTLookupResponse::ThreatInfo::UNWANTED_SOFTWARE, 60,
+                          "www.example.com/path");
+  cache_manager_->CacheRealTimeUrlVerdict(GURL("https://www.example.com/"),
+                                          response, base::Time::Now());
+  ASSERT_EQ(2, cache_manager_->GetStoredRealTimeUrlCheckVerdictCount());
+
+  cache_manager_->CleanUpExpiredVerdicts();
+
+  ASSERT_EQ(1u, cache_manager_->GetStoredPhishGuardVerdictCount(
+                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
+  ASSERT_EQ(1u, cache_manager_->GetStoredPhishGuardVerdictCount(
+                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
+  ASSERT_EQ(1, cache_manager_->GetStoredRealTimeUrlCheckVerdictCount());
+  LoginReputationClientResponse actual_verdict;
+  password_type.set_account_type(ReusedPasswordAccountType::GSUITE);
+  // Has cached PASSWORD_REUSE_EVENT verdict for foo.com/abc/.
+  EXPECT_EQ(LoginReputationClientResponse::LOW_REPUTATION,
+            cache_manager_->GetCachedPhishGuardVerdict(
+                GURL("https://foo.com/abc/test.jsp"),
+                LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+                password_type, &actual_verdict));
+  // No cached PASSWORD_REUSE_EVENT verdict for foo.com/def.
+  EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
+            cache_manager_->GetCachedPhishGuardVerdict(
+                GURL("https://foo.com/def/index.jsp"),
+                LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+                password_type, &actual_verdict));
+  // No cached PASSWORD_REUSE_EVENT verdict for bar.com/abc.
+  EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
+            cache_manager_->GetCachedPhishGuardVerdict(
+                GURL("https://bar.com/abc/index.jsp"),
+                LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+                password_type, &actual_verdict));
+  // No cached PASSWORD_REUSE_EVENT verdict for bar.com/def.
+  EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
+            cache_manager_->GetCachedPhishGuardVerdict(
+                GURL("https://bar.com/def/index.jsp"),
+                LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+                password_type, &actual_verdict));
+
+  // Has cached UNFAMILIAR_LOGIN_PAGE verdict for bar.com/def.
+  password_type.set_account_type(ReusedPasswordAccountType::UNKNOWN);
+  EXPECT_EQ(LoginReputationClientResponse::SAFE,
+            cache_manager_->GetCachedPhishGuardVerdict(
+                GURL("https://bar.com/def/index.jsp"),
+                LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+                password_type, &actual_verdict));
+
+  // No cached UNFAMILIAR_LOGIN_PAGE verdict for bar.com/xyz.
+  EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
+            cache_manager_->GetCachedPhishGuardVerdict(
+                GURL("https://bar.com/xyz/index.jsp"),
+                LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
+                password_type, &actual_verdict));
+}
+
+TEST_F(VerdictCacheManagerTest, TestCleanUpExpiredVerdictWithInvalidEntry) {
+  // Directly save an invalid cache entry.
+  LoginReputationClientResponse verdict;
+  verdict.set_verdict_type(LoginReputationClientResponse::SAFE);
+  verdict.set_cache_expression("www.google.com/");
+  verdict.set_cache_duration_sec(60);
+
+  std::string verdict_serialized;
+  verdict.SerializeToString(&verdict_serialized);
+  base::Base64Encode(verdict_serialized, &verdict_serialized);
+
+  auto cache_dictionary = std::make_unique<base::DictionaryValue>();
+  auto* verdict_dictionary =
+      cache_dictionary->SetKey("1", base::Value(base::Value::Type::DICTIONARY));
+  auto* verdict_entry = verdict_dictionary->SetKey(
+      "www.google.com/path", base::Value(base::Value::Type::DICTIONARY));
+  verdict_entry->SetStringKey("cache_creation_time", "invalid_time");
+  verdict_entry->SetStringKey("verdict_proto", verdict_serialized);
+
+  content_setting_map_->SetWebsiteSettingDefaultScope(
+      GURL("http://www.google.com/"), GURL(),
+      ContentSettingsType::PASSWORD_PROTECTION, std::string(),
+      std::move(cache_dictionary));
+
+  ReusedPasswordAccountType password_type;
+  password_type.set_account_type(ReusedPasswordAccountType::GSUITE);
+  // Save one valid entry
+  CachePhishGuardVerdict(GURL("https://www.google.com"),
+                         LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
+                         password_type, LoginReputationClientResponse::SAFE, 60,
+                         "www.google.com/", base::Time::Now());
+
+  // Verify we saved two entries under PasswordType PRIMARY_ACCOUNT_PASSWORD
+  EXPECT_EQ(2U,
+            content_setting_map_
+                ->GetWebsiteSetting(GURL("http://www.google.com/"), GURL(),
+                                    ContentSettingsType::PASSWORD_PROTECTION,
+                                    std::string(), nullptr)
+                ->FindDictKey("1")
+                ->DictSize());
+
+  cache_manager_->CleanUpExpiredVerdicts();
+
+  // One should have been cleaned up
+  EXPECT_EQ(1U,
+            content_setting_map_
+                ->GetWebsiteSetting(GURL("http://www.google.com/"), GURL(),
+                                    ContentSettingsType::PASSWORD_PROTECTION,
+                                    std::string(), nullptr)
+                ->FindDictKey("1")
+                ->DictSize());
+}
+
+TEST_F(VerdictCacheManagerTest, TestCanRetrieveCachedRealTimeUrlCheckVerdict) {
+  GURL url("https://www.example.com/path");
+
+  RTLookupResponse response;
+  AddThreatInfoToResponse(response, RTLookupResponse::ThreatInfo::SAFE,
+                          RTLookupResponse::ThreatInfo::THREAT_TYPE_UNSPECIFIED,
+                          60, "www.example.com/");
+  AddThreatInfoToResponse(response, RTLookupResponse::ThreatInfo::DANGEROUS,
+                          RTLookupResponse::ThreatInfo::SOCIAL_ENGINEERING, 60,
+                          "www.example.com/path");
+  cache_manager_->CacheRealTimeUrlVerdict(url, response, base::Time::Now());
+
+  RTLookupResponse::ThreatInfo out_verdict;
+  EXPECT_EQ(RTLookupResponse::ThreatInfo::DANGEROUS,
+            cache_manager_->GetCachedRealTimeUrlVerdict(url, &out_verdict));
+  EXPECT_EQ("www.example.com/path", out_verdict.cache_expression());
+  EXPECT_EQ(60, out_verdict.cache_duration_sec());
+  EXPECT_EQ(RTLookupResponse::ThreatInfo::SOCIAL_ENGINEERING,
+            out_verdict.threat_type());
+}
+
+TEST_F(VerdictCacheManagerTest,
+       TestCanRetrieveCachedRealTimeUrlCheckVerdictWithMultipleThreatInfos) {
+  GURL url1("https://www.example.com/");
+  GURL url2("https://www.example.com/path");
+
+  RTLookupResponse response;
+  AddThreatInfoToResponse(response, RTLookupResponse::ThreatInfo::DANGEROUS,
+                          RTLookupResponse::ThreatInfo::SOCIAL_ENGINEERING, 60,
+                          "www.example.com/");
+  AddThreatInfoToResponse(response, RTLookupResponse::ThreatInfo::DANGEROUS,
+                          RTLookupResponse::ThreatInfo::UNWANTED_SOFTWARE, 60,
+                          "www.example.com/");
+  AddThreatInfoToResponse(response, RTLookupResponse::ThreatInfo::DANGEROUS,
+                          RTLookupResponse::ThreatInfo::UNWANTED_SOFTWARE, 60,
+                          "www.example.com/path");
+  AddThreatInfoToResponse(response, RTLookupResponse::ThreatInfo::DANGEROUS,
+                          RTLookupResponse::ThreatInfo::UNCLEAR_BILLING, 60,
+                          "www.example.com/path");
+  cache_manager_->CacheRealTimeUrlVerdict(url2, response, base::Time::Now());
+
+  RTLookupResponse::ThreatInfo out_verdict;
+  EXPECT_EQ(RTLookupResponse::ThreatInfo::DANGEROUS,
+            cache_manager_->GetCachedRealTimeUrlVerdict(url1, &out_verdict));
+  EXPECT_EQ("www.example.com/", out_verdict.cache_expression());
+  EXPECT_EQ(RTLookupResponse::ThreatInfo::SOCIAL_ENGINEERING,
+            out_verdict.threat_type());
+
+  EXPECT_EQ(RTLookupResponse::ThreatInfo::DANGEROUS,
+            cache_manager_->GetCachedRealTimeUrlVerdict(url2, &out_verdict));
+  EXPECT_EQ("www.example.com/path", out_verdict.cache_expression());
+  EXPECT_EQ(RTLookupResponse::ThreatInfo::UNWANTED_SOFTWARE,
+            out_verdict.threat_type());
+}
+
+TEST_F(VerdictCacheManagerTest,
+       TestCannotRetrieveRealTimeUrlCheckExpiredVerdict) {
+  GURL url("https://www.example.com/path");
+
+  RTLookupResponse response;
+  AddThreatInfoToResponse(response, RTLookupResponse::ThreatInfo::DANGEROUS,
+                          RTLookupResponse::ThreatInfo::SOCIAL_ENGINEERING, 0,
+                          "www.example.com/path");
+  cache_manager_->CacheRealTimeUrlVerdict(url, response, base::Time::Now());
+
+  RTLookupResponse::ThreatInfo out_verdict;
+  EXPECT_EQ(RTLookupResponse::ThreatInfo::VERDICT_TYPE_UNSPECIFIED,
+            cache_manager_->GetCachedRealTimeUrlVerdict(url, &out_verdict));
+}
+
+TEST_F(VerdictCacheManagerTest,
+       TestRemoveRealTimeUrlCheckCachedVerdictOnURLsDeleted) {
+  GURL url("https://www.example.com/path");
+
+  RTLookupResponse response;
+  AddThreatInfoToResponse(response, RTLookupResponse::ThreatInfo::DANGEROUS,
+                          RTLookupResponse::ThreatInfo::SOCIAL_ENGINEERING, 60,
+                          "www.example.com/path");
+  cache_manager_->CacheRealTimeUrlVerdict(url, response, base::Time::Now());
+  RTLookupResponse::ThreatInfo out_verdict;
+  EXPECT_EQ(RTLookupResponse::ThreatInfo::DANGEROUS,
+            cache_manager_->GetCachedRealTimeUrlVerdict(url, &out_verdict));
+
+  history::URLRows deleted_urls;
+  deleted_urls.push_back(history::URLRow(GURL("https://www.example.com/path")));
+
+  cache_manager_->RemoveContentSettingsOnURLsDeleted(false /* all_history */,
+                                                     deleted_urls);
+  EXPECT_EQ(RTLookupResponse::ThreatInfo::VERDICT_TYPE_UNSPECIFIED,
+            cache_manager_->GetCachedRealTimeUrlVerdict(url, &out_verdict));
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/web_ui/BUILD.gn b/components/safe_browsing/core/web_ui/BUILD.gn
new file mode 100644
index 0000000..b7b6e7a
--- /dev/null
+++ b/components/safe_browsing/core/web_ui/BUILD.gn
@@ -0,0 +1,10 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+static_library("constants") {
+  sources = [
+    "constants.cc",
+    "constants.h",
+  ]
+}
diff --git a/components/safe_browsing/core/web_ui/constants.cc b/components/safe_browsing/core/web_ui/constants.cc
new file mode 100644
index 0000000..4e00e35
--- /dev/null
+++ b/components/safe_browsing/core/web_ui/constants.cc
@@ -0,0 +1,22 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/core/web_ui/constants.h"
+
+namespace safe_browsing {
+
+const char kChromeUISafeBrowsingURL[] = "chrome://safe-browsing/";
+const char kChromeUISafeBrowsingHost[] = "safe-browsing";
+const char kSbUnderConstruction[] =
+    "The safe browsing page is under construction.";
+const char kChromeUISafeBrowsingMatchBillingUrl[] =
+    "chrome://safe-browsing/match?type=billing";
+const char kChromeUISafeBrowsingMatchMalwareUrl[] =
+    "chrome://safe-browsing/match?type=malware";
+const char kChromeUISafeBrowsingMatchPhishingUrl[] =
+    "chrome://safe-browsing/match?type=phishing";
+const char kChromeUISafeBrowsingMatchUnwantedUrl[] =
+    "chrome://safe-browsing/match?type=unwanted";
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/web_ui/constants.h b/components/safe_browsing/core/web_ui/constants.h
new file mode 100644
index 0000000..95f3dc6
--- /dev/null
+++ b/components/safe_browsing/core/web_ui/constants.h
@@ -0,0 +1,20 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SAFE_BROWSING_CORE_WEB_UI_CONSTANTS_H_
+#define COMPONENTS_SAFE_BROWSING_CORE_WEB_UI_CONSTANTS_H_
+
+namespace safe_browsing {
+
+extern const char kChromeUISafeBrowsingURL[];
+extern const char kChromeUISafeBrowsingHost[];
+extern const char kSbUnderConstruction[];
+extern const char kChromeUISafeBrowsingMatchBillingUrl[];
+extern const char kChromeUISafeBrowsingMatchMalwareUrl[];
+extern const char kChromeUISafeBrowsingMatchPhishingUrl[];
+extern const char kChromeUISafeBrowsingMatchUnwantedUrl[];
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CORE_WEB_UI_CONSTANTS_H_
diff --git a/components/safe_browsing/db/BUILD.gn b/components/safe_browsing/db/BUILD.gn
deleted file mode 100644
index 6f1ac841..0000000
--- a/components/safe_browsing/db/BUILD.gn
+++ /dev/null
@@ -1,391 +0,0 @@
-# Copyright 2017 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//testing/libfuzzer/fuzzer_test.gni")
-import("//third_party/protobuf/proto_library.gni")
-
-proto_library("safebrowsing_proto") {
-  sources = [
-    "safebrowsing.proto",
-  ]
-}
-
-proto_library("v4_store_proto") {
-  sources = [
-    "v4_store.proto",
-  ]
-  link_deps = [ ":safebrowsing_proto" ]
-}
-
-proto_library("metadata_proto") {
-  sources = [
-    "metadata.proto",
-  ]
-}
-
-# This target is shared between the desktop and mobile versions.
-group("safe_browsing_db_shared") {
-  deps = [
-    ":database_manager",
-    ":hit_report",
-    ":safebrowsing_proto",
-    ":util",
-    "//components/safe_browsing/common:safe_browsing_prefs",
-  ]
-}
-
-# This target is for the desktop version.
-group("db") {
-  deps = [
-    ":safe_browsing_db_shared",
-    ":v4_local_database_manager",
-  ]
-}
-
-static_library("database_manager") {
-  sources = [
-    "database_manager.cc",
-    "database_manager.h",
-  ]
-  deps = [
-    ":hit_report",
-    ":util",
-    ":v4_get_hash_protocol_manager",
-    ":v4_protocol_manager_util",
-    "//base",
-    "//content/public/browser",
-    "//content/public/common",
-    "//net",
-    "//url",
-  ]
-
-  public_deps = [
-    ":safebrowsing_proto",
-  ]
-}
-
-static_library("hit_report") {
-  sources = [
-    "hit_report.cc",
-    "hit_report.h",
-  ]
-  public_deps = [
-    ":util",
-  ]
-  deps = [
-    "//components/metrics",
-    "//components/safe_browsing/common:safe_browsing_prefs",
-    "//url",
-  ]
-}
-
-static_library("test_database_manager") {
-  sources = [
-    "test_database_manager.cc",
-    "test_database_manager.h",
-  ]
-  deps = [
-    ":database_manager",
-    ":v4_protocol_manager_util",
-    "//base:base",
-    "//net",
-    "//services/network/public/cpp",
-  ]
-}
-
-static_library("util") {
-  sources = [
-    "util.cc",
-    "util.h",
-  ]
-  public_deps = [
-    ":v4_protocol_manager_util",
-    "//components/safe_browsing/common:safe_browsing_prefs",
-  ]
-  deps = [
-    "//base",
-    "//components/version_info:version_info",
-    "//crypto",
-    "//google_apis:google_apis",
-    "//net",
-    "//url",
-  ]
-  if (is_win) {
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]  # Conversion from size_t to 'type'.
-  }
-}
-
-static_library("v4_database") {
-  sources = [
-    "v4_database.cc",
-    "v4_database.h",
-  ]
-  public_deps = [
-    ":safebrowsing_proto",
-  ]
-  deps = [
-    ":v4_protocol_manager_util",
-    ":v4_store",
-    "//base",
-    "//components/safe_browsing:webui_proto",
-    "//content/public/browser",
-  ]
-}
-
-static_library("v4_get_hash_protocol_manager") {
-  sources = [
-    "v4_get_hash_protocol_manager.cc",
-    "v4_get_hash_protocol_manager.h",
-  ]
-  public_deps = [
-    ":safebrowsing_proto",
-  ]
-  deps = [
-    ":util",
-    ":v4_protocol_manager_util",
-    "//base",
-    "//components/safe_browsing:webui_proto",
-    "//content/public/browser",
-    "//net",
-    "//url",
-  ]
-}
-
-static_library("v4_local_database_manager") {
-  sources = [
-    "v4_local_database_manager.cc",
-    "v4_local_database_manager.h",
-  ]
-  deps = [
-    ":database_manager",
-    ":hit_report",
-    ":safebrowsing_proto",
-    ":v4_database",
-    ":v4_get_hash_protocol_manager",
-    ":v4_protocol_manager_util",
-    ":v4_update_protocol_manager",
-    "//base",
-    "//build:branding_buildflags",
-    "//components/safe_browsing:webui_proto",
-    "//components/safe_browsing/realtime:policy_engine",
-    "//components/safe_browsing/realtime:url_lookup_service",
-    "//content/public/browser",
-    "//crypto",
-    "//net",
-    "//url",
-  ]
-}
-
-source_set("v4_protocol_manager_util") {
-  sources = [
-    "v4_protocol_manager_util.cc",
-    "v4_protocol_manager_util.h",
-  ]
-  public_deps = [
-    ":safebrowsing_proto",
-  ]
-  deps = [
-    "//base",
-    "//components/safe_browsing/common:safe_browsing_prefs",
-    "//components/version_info:version_info",
-    "//google_apis:google_apis",
-    "//net",
-    "//url",
-  ]
-}
-
-source_set("prefix_iterator") {
-  sources = [
-    "prefix_iterator.cc",
-    "prefix_iterator.h",
-  ]
-  deps = [
-    ":v4_protocol_manager_util",
-    "//base",
-  ]
-}
-
-if (is_android) {
-  import("//build/config/android/rules.gni")
-  java_cpp_enum("sb_threat_values") {
-    sources = [
-      "v4_protocol_manager_util.h",
-    ]
-  }
-}
-
-source_set("v4_rice") {
-  sources = [
-    "v4_rice.cc",
-    "v4_rice.h",
-  ]
-  deps = [
-    "//base",
-    "//third_party/protobuf:protobuf_lite",
-  ]
-}
-
-source_set("v4_store") {
-  sources = [
-    "v4_store.cc",
-    "v4_store.h",
-  ]
-  public_deps = [
-    ":safebrowsing_proto",
-    ":v4_store_proto",
-  ]
-  deps = [
-    ":prefix_iterator",
-    ":v4_protocol_manager_util",
-    ":v4_rice",
-    "//base",
-    "//components/safe_browsing:webui_proto",
-    "//crypto",
-  ]
-}
-
-static_library("v4_test_util") {
-  testonly = true
-  sources = [
-    "v4_embedded_test_server_util.cc",
-    "v4_embedded_test_server_util.h",
-    "v4_test_util.cc",
-    "v4_test_util.h",
-  ]
-  deps = [
-    ":util",
-    ":v4_database",
-    ":v4_get_hash_protocol_manager",
-    ":v4_protocol_manager_util",
-    "//base",
-    "//net:test_support",
-    "//services/network/public/cpp",
-  ]
-}
-
-static_library("v4_update_protocol_manager") {
-  sources = [
-    "v4_update_protocol_manager.cc",
-    "v4_update_protocol_manager.h",
-  ]
-  deps = [
-    ":safebrowsing_proto",
-    ":util",
-    ":v4_protocol_manager_util",
-    "//base",
-    "//components/safe_browsing:buildflags",
-    "//components/safe_browsing:webui_proto",
-    "//components/safe_browsing/common:safe_browsing_prefs",
-    "//net",
-    "//services/network/public/cpp",
-    "//url",
-  ]
-}
-
-source_set("unit_tests_shared") {
-  testonly = true
-  sources = [
-    "allowlist_checker_client_unittest.cc",
-    "database_manager_unittest.cc",
-    "v4_get_hash_protocol_manager_unittest.cc",
-    "v4_protocol_manager_util_unittest.cc",
-  ]
-  deps = [
-    ":allowlist_checker_client",
-    ":database_manager",
-    ":safebrowsing_proto",
-    ":test_database_manager",
-    ":util",
-    ":v4_get_hash_protocol_manager",
-    ":v4_protocol_manager_util",
-    ":v4_test_util",
-    "//base",
-    "//content/public/browser",
-    "//content/test:test_support",
-    "//net",
-    "//net:test_support",
-    "//services/network:test_support",
-    "//testing/gtest",
-  ]
-  if (is_win) {
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]  # Conversion from size_t to 'type'.
-  }
-}
-
-source_set("unit_tests_desktop") {
-  testonly = true
-  sources = [
-    "v4_database_unittest.cc",
-    "v4_local_database_manager_unittest.cc",
-    "v4_rice_unittest.cc",
-    "v4_store_unittest.cc",
-    "v4_update_protocol_manager_unittest.cc",
-  ]
-  deps = [
-    ":unit_tests_shared",
-    ":util",
-    ":v4_database",
-    ":v4_local_database_manager",
-    ":v4_protocol_manager_util",
-    ":v4_rice",
-    ":v4_store",
-    ":v4_store_proto",
-    ":v4_test_util",
-    ":v4_update_protocol_manager",
-    "//base",
-    "//components/prefs:test_support",
-    "//components/safe_browsing:buildflags",
-    "//components/safe_browsing:features",
-    "//components/safe_browsing/common:safe_browsing_prefs",
-    "//content/test:test_support",
-    "//crypto",
-    "//net",
-    "//net:test_support",
-    "//services/network:test_support",
-    "//services/network/public/cpp",
-    "//testing/gtest",
-    "//url",
-  ]
-  if (is_win) {
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]  # Conversion from size_t to 'type'.
-  }
-}
-
-static_library("allowlist_checker_client") {
-  sources = [
-    "allowlist_checker_client.cc",
-    "allowlist_checker_client.h",
-  ]
-  deps = [
-    ":database_manager",
-    "//base:base",
-    "//content/public/browser",
-    "//url:url",
-  ]
-}
-
-fuzzer_test("v4_get_hash_protocol_manager_fuzzer") {
-  sources = [
-    "v4_get_hash_protocol_manager_fuzzer.cc",
-  ]
-  deps = [
-    ":safebrowsing_proto",
-    ":v4_get_hash_protocol_manager",
-  ]
-}
-
-fuzzer_test("v4_store_fuzzer") {
-  sources = [
-    "v4_store_fuzzer.cc",
-  ]
-  deps = [
-    ":v4_protocol_manager_util",
-    ":v4_store",
-    ":v4_test_util",
-    "//base/test:test_support",
-  ]
-}
diff --git a/components/safe_browsing/db/DEPS b/components/safe_browsing/db/DEPS
deleted file mode 100644
index 9d24dc7..0000000
--- a/components/safe_browsing/db/DEPS
+++ /dev/null
@@ -1,19 +0,0 @@
-include_rules = [
-  "+base",
-  "+components/safe_browsing/common/safe_browsing_prefs.h",
-  "+components/safe_browsing/features.h",
-  "+components/safe_browsing/proto/webui.pb.h",
-  "+components/safe_browsing/web_ui/webui.pb.h",
-  "+components/version_info",
-  "+content/public/browser",
-  "+content/public/common",
-  "+content/public/test",
-  "+crypto",
-  "+google_apis/google_api_keys.h",
-  "+net",
-  "+services/network/public",
-  "+services/network/test",
-  "+testing/gtest",
-  "+third_party/protobuf/src/google",
-  "+url",
-]
diff --git a/components/safe_browsing/db/allowlist_checker_client.cc b/components/safe_browsing/db/allowlist_checker_client.cc
deleted file mode 100644
index 8c2f439..0000000
--- a/components/safe_browsing/db/allowlist_checker_client.cc
+++ /dev/null
@@ -1,155 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/db/allowlist_checker_client.h"
-
-#include <memory>
-
-#include "base/bind.h"
-#include "content/public/browser/browser_thread.h"
-
-namespace safe_browsing {
-
-namespace {
-
-// Number of milliseconds to wait for the response from the Safe Browsing
-// database manager before proceeding with the timeout behavior.
-const int kLookupTimeoutMS = 5000;
-}  // namespace
-
-// static
-void AllowlistCheckerClient::StartCheckCsdWhitelist(
-    scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
-    const GURL& url,
-    base::OnceCallback<void(bool)> callback_for_result) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-
-  // On timeout or if the list is unavailable, report match.
-  const bool kDefaultDoesMatchAllowlist = true;
-
-  std::unique_ptr<AllowlistCheckerClient> client = GetAllowlistCheckerClient(
-      database_manager, url, &callback_for_result, kDefaultDoesMatchAllowlist);
-  if (!client) {
-    std::move(callback_for_result).Run(kDefaultDoesMatchAllowlist);
-    return;
-  }
-
-  AsyncMatch match = database_manager->CheckCsdWhitelistUrl(url, client.get());
-  InvokeCallbackOrRelease(match, std::move(client));
-}
-
-// static
-void AllowlistCheckerClient::StartCheckHighConfidenceAllowlist(
-    scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
-    const GURL& url,
-    base::OnceCallback<void(bool)> callback_for_result) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-
-  // On timeout or if the list is unavailable, report no match.
-  const bool kDefaultDoesMatchAllowlist = false;
-
-  std::unique_ptr<AllowlistCheckerClient> client = GetAllowlistCheckerClient(
-      database_manager, url, &callback_for_result, kDefaultDoesMatchAllowlist);
-  if (!client) {
-    std::move(callback_for_result).Run(kDefaultDoesMatchAllowlist);
-    return;
-  }
-
-  AsyncMatch match =
-      database_manager->CheckUrlForHighConfidenceAllowlist(url, client.get());
-  InvokeCallbackOrRelease(match, std::move(client));
-}
-
-// static
-std::unique_ptr<AllowlistCheckerClient>
-AllowlistCheckerClient::GetAllowlistCheckerClient(
-    scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
-    const GURL& url,
-    base::OnceCallback<void(bool)>* callback_for_result,
-    bool default_does_match_allowlist) {
-  if (!url.is_valid() || !database_manager ||
-      !database_manager->CanCheckUrl(url)) {
-    return nullptr;
-  }
-
-  // Make a client for each request. The caller could have several in
-  // flight at once.
-  return std::make_unique<AllowlistCheckerClient>(
-      std::move(*callback_for_result), database_manager,
-      default_does_match_allowlist);
-}
-
-// static
-void AllowlistCheckerClient::InvokeCallbackOrRelease(
-    AsyncMatch match,
-    std::unique_ptr<AllowlistCheckerClient> client) {
-  switch (match) {
-    case AsyncMatch::MATCH:
-      std::move(client->callback_for_result_)
-          .Run(true /* did_match_allowlist */);
-      break;
-    case AsyncMatch::NO_MATCH:
-      std::move(client->callback_for_result_)
-          .Run(false /* did_match_allowlist */);
-      break;
-    case AsyncMatch::ASYNC:
-      // Client is now self-owned. When it gets called back with the result,
-      // it will delete itself.
-      client.release();
-      break;
-  }
-}
-
-AllowlistCheckerClient::AllowlistCheckerClient(
-    base::OnceCallback<void(bool)> callback_for_result,
-    scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
-    bool default_does_match_allowlist)
-    : callback_for_result_(std::move(callback_for_result)),
-      database_manager_(database_manager),
-      default_does_match_allowlist_(default_does_match_allowlist) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-
-  // Set a timer to fail open, i.e. call it "whitelisted", if the full
-  // check takes too long.
-  auto timeout_callback = base::BindOnce(&AllowlistCheckerClient::OnTimeout,
-                                         weak_factory_.GetWeakPtr());
-  timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kLookupTimeoutMS),
-               std::move(timeout_callback));
-}
-
-AllowlistCheckerClient::~AllowlistCheckerClient() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-}
-
-// SafeBrowsingDatabaseMananger::Client impl
-void AllowlistCheckerClient::OnCheckWhitelistUrlResult(
-    bool did_match_allowlist) {
-  OnCheckUrlResult(did_match_allowlist);
-}
-
-void AllowlistCheckerClient::OnCheckUrlForHighConfidenceAllowlist(
-    bool did_match_allowlist) {
-  OnCheckUrlResult(did_match_allowlist);
-}
-
-void AllowlistCheckerClient::OnCheckUrlResult(bool did_match_allowlist) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-  timer_.Stop();
-
-  // The callback can only be invoked by other code paths if this object is not
-  // self-owned. Because this method is only invoked when we're self-owned, we
-  // know the callback must still be valid, and it must be safe to delete
-  // |this|.
-  DCHECK(callback_for_result_);
-  std::move(callback_for_result_).Run(did_match_allowlist);
-  delete this;
-}
-
-void AllowlistCheckerClient::OnTimeout() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-  database_manager_->CancelCheck(this);
-  OnCheckUrlResult(default_does_match_allowlist_);
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/db/allowlist_checker_client.h b/components/safe_browsing/db/allowlist_checker_client.h
deleted file mode 100644
index 0a32242..0000000
--- a/components/safe_browsing/db/allowlist_checker_client.h
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_DB_ALLOWLIST_CHECKER_CLIENT_H_
-#define COMPONENTS_SAFE_BROWSING_DB_ALLOWLIST_CHECKER_CLIENT_H_
-
-#include "base/callback.h"
-#include "base/memory/scoped_refptr.h"
-#include "base/memory/weak_ptr.h"
-#include "base/timer/timer.h"
-#include "components/safe_browsing/db/database_manager.h"
-#include "url/gurl.h"
-
-namespace safe_browsing {
-
-// This provides a simpler interface to
-// SafeBrowsingDatabaseManager::CheckCsdWhitelistUrl() for callers that
-// don't want to track their own clients.
-
-class AllowlistCheckerClient : public SafeBrowsingDatabaseManager::Client {
- public:
-  using BoolCallback = base::OnceCallback<void(bool /* is_whitelisted */)>;
-
-  // Static method to lookup |url| on the CSD allowlist. |callback| will be
-  // called when the lookup result is known, or on time out, or if the
-  // |database_manager| gets shut down, whichever happens first.
-  // Must be called on IO thread.
-  static void StartCheckCsdWhitelist(
-      scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
-      const GURL& url,
-      BoolCallback callback_for_result);
-
-  // Static method to lookup |url| on the high confidence allowlist. |callback|
-  // will be called when the lookup result is known, or on time out, or if the
-  // |database_manager| gets shut down, whichever happens first.
-  // Must be called on IO thread.
-  static void StartCheckHighConfidenceAllowlist(
-      scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
-      const GURL& url,
-      BoolCallback callback_for_result);
-
-  // public constructor for use with std::make_unique
-  AllowlistCheckerClient(
-      BoolCallback callback_for_result,
-      scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
-      bool default_does_match_allowlist);
-
-  ~AllowlistCheckerClient() override;
-
-  // SafeBrowsingDatabaseMananger::Client impl
-  void OnCheckWhitelistUrlResult(bool is_whitelisted) override;
-  void OnCheckUrlForHighConfidenceAllowlist(bool did_match_allowlist) override;
-
- private:
-  // Helper method to instantiate a AllowlistCheckerClient object.
-  static std::unique_ptr<AllowlistCheckerClient> GetAllowlistCheckerClient(
-      scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
-      const GURL& url,
-      base::OnceCallback<void(bool)>* callback_for_result,
-      bool default_does_match_allowlist);
-
-  // Invokes |callback_for_result_| if the allowlist lookup completed
-  // synchronously i.e if |match| is |MATCH| or |NO_MATCH|. If, however, |match|
-  // is |ASYNC|, it releases the ownership of |client| so that it can be deleted
-  // in |OnCheckUrlResult| later.
-  static void InvokeCallbackOrRelease(
-      AsyncMatch match,
-      std::unique_ptr<AllowlistCheckerClient> client);
-
-  AllowlistCheckerClient() = delete;
-
-  // Calls the |callback_for_result_| with the result of the lookup or timeout.
-  void OnCheckUrlResult(bool did_match_allowlist);
-
-  // Called when the call to CheckCsdWhitelistUrl times out.
-  void OnTimeout();
-
-  // For setting up timeout behavior.
-  base::OneShotTimer timer_;
-
-  // The method to call when the match result is known.
-  BoolCallback callback_for_result_;
-
-  scoped_refptr<SafeBrowsingDatabaseManager> database_manager_;
-
-  // Whether to report allowlist match in any of the following cases:
-  // a) On timeout, or
-  // b) If the list is unavailable.
-  bool default_does_match_allowlist_;
-
-  base::WeakPtrFactory<AllowlistCheckerClient> weak_factory_{this};
-};
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_DB_ALLOWLIST_CHECKER_CLIENT_H_
diff --git a/components/safe_browsing/db/allowlist_checker_client_unittest.cc b/components/safe_browsing/db/allowlist_checker_client_unittest.cc
deleted file mode 100644
index e273d58..0000000
--- a/components/safe_browsing/db/allowlist_checker_client_unittest.cc
+++ /dev/null
@@ -1,178 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-#include "components/safe_browsing/db/allowlist_checker_client.h"
-
-#include <memory>
-
-#include "base/bind.h"
-#include "base/run_loop.h"
-#include "base/test/mock_callback.h"
-#include "base/test/task_environment.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "components/safe_browsing/db/test_database_manager.h"
-#include "content/public/test/browser_task_environment.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace safe_browsing {
-
-using base::TimeDelta;
-using testing::_;
-using testing::Return;
-using testing::SaveArg;
-
-using BoolCallback = base::OnceCallback<void(bool /* did_match_allowlist */)>;
-using MockBoolCallback = testing::StrictMock<base::MockCallback<BoolCallback>>;
-
-namespace {
-class MockSafeBrowsingDatabaseManager : public TestSafeBrowsingDatabaseManager {
- public:
-  MockSafeBrowsingDatabaseManager() {}
-
-  MOCK_METHOD1(CancelCheck, void(SafeBrowsingDatabaseManager::Client*));
-
-  MOCK_METHOD2(CheckCsdWhitelistUrl,
-               AsyncMatch(const GURL&, SafeBrowsingDatabaseManager::Client*));
-
-  MOCK_METHOD2(CheckUrlForHighConfidenceAllowlist,
-               AsyncMatch(const GURL&, SafeBrowsingDatabaseManager::Client*));
-
- protected:
-  ~MockSafeBrowsingDatabaseManager() override {}
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockSafeBrowsingDatabaseManager);
-};
-}  // namespace
-
-class AllowlistCheckerClientTest : public testing::Test {
- public:
-  AllowlistCheckerClientTest()
-      : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME),
-        target_url_("https://example.test") {}
-
-  void SetUp() override {
-    database_manager_ = new MockSafeBrowsingDatabaseManager;
-  }
-
-  void TearDown() override {
-    database_manager_ = nullptr;
-    base::RunLoop().RunUntilIdle();
-
-    // Verify no callback is remaining.
-    EXPECT_TRUE(task_environment_.MainThreadIsIdle());
-  }
-
- protected:
-  content::BrowserTaskEnvironment task_environment_;
-
-  GURL target_url_;
-  scoped_refptr<MockSafeBrowsingDatabaseManager> database_manager_;
-};
-
-TEST_F(AllowlistCheckerClientTest, TestCsdListMatch) {
-  EXPECT_CALL(*database_manager_, CheckCsdWhitelistUrl(target_url_, _))
-      .WillOnce(Return(AsyncMatch::MATCH));
-  MockBoolCallback callback;
-  EXPECT_CALL(callback, Run(true /* did_match_allowlist */));
-  AllowlistCheckerClient::StartCheckCsdWhitelist(database_manager_, target_url_,
-                                                 callback.Get());
-}
-
-TEST_F(AllowlistCheckerClientTest, TestCsdListNoMatch) {
-  EXPECT_CALL(*database_manager_, CheckCsdWhitelistUrl(target_url_, _))
-      .WillOnce(Return(AsyncMatch::NO_MATCH));
-  MockBoolCallback callback;
-  EXPECT_CALL(callback, Run(false /* did_match_allowlist */));
-  AllowlistCheckerClient::StartCheckCsdWhitelist(database_manager_, target_url_,
-                                                 callback.Get());
-}
-
-TEST_F(AllowlistCheckerClientTest, TestCsdListAsyncNoMatch) {
-  SafeBrowsingDatabaseManager::Client* client;
-  EXPECT_CALL(*database_manager_, CheckCsdWhitelistUrl(target_url_, _))
-      .WillOnce(DoAll(SaveArg<1>(&client), Return(AsyncMatch::ASYNC)));
-
-  MockBoolCallback callback;
-  AllowlistCheckerClient::StartCheckCsdWhitelist(database_manager_, target_url_,
-                                                 callback.Get());
-  // Callback should not be called yet.
-
-  EXPECT_CALL(callback, Run(false /* did_match_allowlist */));
-  // The self-owned client deletes itself here.
-  client->OnCheckWhitelistUrlResult(false);
-}
-
-TEST_F(AllowlistCheckerClientTest, TestCsdListAsyncTimeout) {
-  SafeBrowsingDatabaseManager::Client* client;
-  EXPECT_CALL(*database_manager_, CheckCsdWhitelistUrl(target_url_, _))
-      .WillOnce(DoAll(SaveArg<1>(&client), Return(AsyncMatch::ASYNC)));
-  EXPECT_CALL(*database_manager_, CancelCheck(_)).Times(1);
-
-  MockBoolCallback callback;
-  AllowlistCheckerClient::StartCheckCsdWhitelist(database_manager_, target_url_,
-                                                 callback.Get());
-  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
-  // No callback yet.
-
-  EXPECT_CALL(callback, Run(true /* did_match_allowlist */));
-  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(5));
-}
-
-TEST_F(AllowlistCheckerClientTest, TestHighConfidenceListMatch) {
-  EXPECT_CALL(*database_manager_,
-              CheckUrlForHighConfidenceAllowlist(target_url_, _))
-      .WillOnce(Return(AsyncMatch::MATCH));
-
-  MockBoolCallback callback;
-  EXPECT_CALL(callback, Run(true /* did_match_allowlist */));
-  AllowlistCheckerClient::StartCheckHighConfidenceAllowlist(
-      database_manager_, target_url_, callback.Get());
-}
-
-TEST_F(AllowlistCheckerClientTest, TestHighConfidenceListNoMatch) {
-  EXPECT_CALL(*database_manager_,
-              CheckUrlForHighConfidenceAllowlist(target_url_, _))
-      .WillOnce(Return(AsyncMatch::NO_MATCH));
-
-  MockBoolCallback callback;
-  EXPECT_CALL(callback, Run(false /* did_match_allowlist */));
-  AllowlistCheckerClient::StartCheckHighConfidenceAllowlist(
-      database_manager_, target_url_, callback.Get());
-}
-
-TEST_F(AllowlistCheckerClientTest, TestHighConfidenceListAsyncNoMatch) {
-  SafeBrowsingDatabaseManager::Client* client;
-  EXPECT_CALL(*database_manager_,
-              CheckUrlForHighConfidenceAllowlist(target_url_, _))
-      .WillOnce(DoAll(SaveArg<1>(&client), Return(AsyncMatch::ASYNC)));
-
-  MockBoolCallback callback;
-  AllowlistCheckerClient::StartCheckHighConfidenceAllowlist(
-      database_manager_, target_url_, callback.Get());
-  // Callback should not be called yet.
-
-  EXPECT_CALL(callback, Run(false /* did_match_allowlist */));
-  // The self-owned client deletes itself here.
-  client->OnCheckWhitelistUrlResult(false);
-}
-
-TEST_F(AllowlistCheckerClientTest, TestHighConfidenceListAsyncTimeout) {
-  SafeBrowsingDatabaseManager::Client* client;
-  EXPECT_CALL(*database_manager_,
-              CheckUrlForHighConfidenceAllowlist(target_url_, _))
-      .WillOnce(DoAll(SaveArg<1>(&client), Return(AsyncMatch::ASYNC)));
-  EXPECT_CALL(*database_manager_, CancelCheck(_)).Times(1);
-
-  MockBoolCallback callback;
-  AllowlistCheckerClient::StartCheckHighConfidenceAllowlist(
-      database_manager_, target_url_, callback.Get());
-  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
-  // No callback yet.
-
-  EXPECT_CALL(callback, Run(false /* did_match_allowlist */));
-  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(5));
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/db/database_manager.cc b/components/safe_browsing/db/database_manager.cc
deleted file mode 100644
index de71c827..0000000
--- a/components/safe_browsing/db/database_manager.cc
+++ /dev/null
@@ -1,172 +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 "components/safe_browsing/db/database_manager.h"
-
-#include <memory>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/task/post_task.h"
-#include "components/safe_browsing/db/v4_get_hash_protocol_manager.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "url/gurl.h"
-
-using content::BrowserThread;
-
-namespace safe_browsing {
-
-SafeBrowsingDatabaseManager::SafeBrowsingDatabaseManager()
-    : base::RefCountedDeleteOnSequence<SafeBrowsingDatabaseManager>(
-          base::CreateSingleThreadTaskRunner({content::BrowserThread::IO})),
-      enabled_(false) {}
-
-SafeBrowsingDatabaseManager::~SafeBrowsingDatabaseManager() {
-  DCHECK(!v4_get_hash_protocol_manager_);
-}
-
-bool SafeBrowsingDatabaseManager::CancelApiCheck(Client* client) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  auto it = FindClientApiCheck(client);
-  if (it != api_checks_.end()) {
-    api_checks_.erase(it);
-    return true;
-  }
-  NOTREACHED();
-  return false;
-}
-
-bool SafeBrowsingDatabaseManager::CheckApiBlacklistUrl(const GURL& url,
-                                                       Client* client) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DCHECK(v4_get_hash_protocol_manager_);
-
-  // Make sure we can check this url and that the service is enabled.
-  if (!enabled_ ||
-      !(url.SchemeIs(url::kHttpScheme) || url.SchemeIs(url::kHttpsScheme))) {
-    return true;
-  }
-
-  // There can only be one in-progress check for the same client at a time.
-  DCHECK(FindClientApiCheck(client) == api_checks_.end());
-
-  std::unique_ptr<SafeBrowsingApiCheck> check(
-      new SafeBrowsingApiCheck(url, client));
-  api_checks_.insert(check.get());
-
-  std::vector<std::string> list_client_states;
-  V4ProtocolManagerUtil::GetListClientStatesFromStoreStateMap(
-      GetStoreStateMap(), &list_client_states);
-
-  v4_get_hash_protocol_manager_->GetFullHashesWithApis(
-      url, list_client_states,
-      base::BindOnce(&SafeBrowsingDatabaseManager::OnThreatMetadataResponse,
-                     base::Unretained(this), std::move(check)));
-
-  return false;
-}
-
-SafeBrowsingDatabaseManager::ApiCheckSet::iterator
-SafeBrowsingDatabaseManager::FindClientApiCheck(Client* client) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  for (auto it = api_checks_.begin(); it != api_checks_.end(); ++it) {
-    if ((*it)->client() == client) {
-      return it;
-    }
-  }
-  return api_checks_.end();
-}
-
-// Keep the list returned here in sync with GetStoreStateMap()
-StoresToCheck SafeBrowsingDatabaseManager::GetStoresForFullHashRequests() {
-  return StoresToCheck({GetChromeUrlApiId()});
-}
-
-// Keep the list returned here in sync with GetStoresForFullHashRequests()
-std::unique_ptr<StoreStateMap> SafeBrowsingDatabaseManager::GetStoreStateMap() {
-  // This implementation is currently used only for RemoteDatabaseManager which
-  // only requests full hashes for GetChromeUrlApiId() list that has no local
-  // storage so the client state is always empty.
-
-  auto store_state_map = std::make_unique<StoreStateMap>();
-  (*store_state_map)[GetChromeUrlApiId()] = "";
-  return store_state_map;
-}
-
-void SafeBrowsingDatabaseManager::OnThreatMetadataResponse(
-    std::unique_ptr<SafeBrowsingApiCheck> check,
-    const ThreatMetadata& md) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DCHECK(check);
-
-  // If the check is not in |api_checks_| then the request was cancelled by the
-  // client.
-  auto it = api_checks_.find(check.get());
-  if (it == api_checks_.end())
-    return;
-
-  check->client()->OnCheckApiBlacklistUrlResult(check->url(), md);
-  api_checks_.erase(it);
-}
-
-void SafeBrowsingDatabaseManager::StartOnIOThread(
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    const V4ProtocolConfig& config) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  v4_get_hash_protocol_manager_ = V4GetHashProtocolManager::Create(
-      url_loader_factory, GetStoresForFullHashRequests(), config);
-}
-
-// |shutdown| not used. Destroys the v4 protocol managers. This may be called
-// multiple times during the life of the DatabaseManager.
-// Must be called on IO thread.
-void SafeBrowsingDatabaseManager::StopOnIOThread(bool shutdown) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  // Delete pending checks, calling back any clients with empty metadata.
-  for (const SafeBrowsingApiCheck* check : api_checks_) {
-    if (check->client()) {
-      check->client()->OnCheckApiBlacklistUrlResult(check->url(),
-                                                    ThreatMetadata());
-    }
-  }
-
-  // This cancels all in-flight GetHash requests.
-  v4_get_hash_protocol_manager_.reset();
-}
-
-RealTimeUrlLookupService*
-SafeBrowsingDatabaseManager::GetRealTimeUrlLookupService() {
-  return nullptr;
-}
-
-std::unique_ptr<base::CallbackList<void()>::Subscription>
-SafeBrowsingDatabaseManager::RegisterDatabaseUpdatedCallback(
-    const OnDatabaseUpdated& cb) {
-  return update_complete_callback_list_.Add(cb);
-}
-
-void SafeBrowsingDatabaseManager::NotifyDatabaseUpdateFinished() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  update_complete_callback_list_.Notify();
-}
-
-std::string SafeBrowsingDatabaseManager::GetSafetyNetId() const {
-  NOTREACHED() << "Only implemented on Android";
-  return "";
-}
-
-SafeBrowsingDatabaseManager::SafeBrowsingApiCheck::SafeBrowsingApiCheck(
-    const GURL& url,
-    Client* client)
-    : url_(url), client_(client) {}
-
-SafeBrowsingDatabaseManager::SafeBrowsingApiCheck::~SafeBrowsingApiCheck() {}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/db/database_manager.h b/components/safe_browsing/db/database_manager.h
deleted file mode 100644
index fade8c5..0000000
--- a/components/safe_browsing/db/database_manager.h
+++ /dev/null
@@ -1,351 +0,0 @@
-// Copyright (c) 2012 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.
-//
-// The Safe Browsing service is responsible for downloading anti-phishing and
-// anti-malware tables and checking urls against them.
-
-#ifndef COMPONENTS_SAFE_BROWSING_DB_DATABASE_MANAGER_H_
-#define COMPONENTS_SAFE_BROWSING_DB_DATABASE_MANAGER_H_
-
-#include <memory>
-#include <set>
-#include <string>
-#include <unordered_set>
-#include <vector>
-
-#include "base/callback_list.h"
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted_delete_on_sequence.h"
-#include "components/safe_browsing/db/hit_report.h"
-#include "components/safe_browsing/db/util.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
-#include "content/public/common/resource_type.h"
-#include "url/gurl.h"
-
-namespace network {
-class SharedURLLoaderFactory;
-}  // namespace network
-
-namespace safe_browsing {
-
-class RealTimeUrlLookupService;
-
-// Value returned by some functions that check an allowlist and may or may not
-// have an immediate answer.
-enum class AsyncMatch : int {
-  // If a hash prefix on the allowlist matches any of the computed hashes for
-  // the URL. In this case, the callback method on the client is called back
-  // later with the result.
-  ASYNC,
-
-  // If a full hash on the allowlist matches any of the computed hashes for the
-  // URL. The callback function isn't called.
-  MATCH,
-
-  // If Safe Browsing isn't enabled, or the allowlist hasn't been sync'd yet, or
-  // when no hash prefix or full hash in the allowlist matches the computed
-  // hashes of the URL. The callback function isn't called.
-  NO_MATCH,
-
-  kMaxValue = NO_MATCH,
-};
-
-struct V4ProtocolConfig;
-class V4GetHashProtocolManager;
-
-// Base class to either the locally-managed or a remotely-managed database.
-class SafeBrowsingDatabaseManager
-    : public base::RefCountedDeleteOnSequence<SafeBrowsingDatabaseManager> {
- public:
-  // Callers requesting a result should derive from this class.
-  // The destructor should call db_manager->CancelCheck(client) if a
-  // request is still pending.
-  class Client {
-   public:
-    virtual ~Client() {}
-
-    // Called when the result of checking the API blacklist is known.
-    // TODO(kcarattini): Consider if we need |url| passed here, remove if not.
-    virtual void OnCheckApiBlacklistUrlResult(const GURL& url,
-                                              const ThreatMetadata& metadata) {}
-
-    // Called when the result of checking a browse URL is known or the result of
-    // checking the URL for subresource filter is known.
-    virtual void OnCheckBrowseUrlResult(const GURL& url,
-                                        SBThreatType threat_type,
-                                        const ThreatMetadata& metadata) {}
-
-    // Called when the result of checking a download URL is known.
-    virtual void OnCheckDownloadUrlResult(const std::vector<GURL>& url_chain,
-                                          SBThreatType threat_type) {}
-
-    // Called when the result of checking a set of extensions is known.
-    virtual void OnCheckExtensionsResult(const std::set<std::string>& threats) {
-    }
-
-    // Called when the result of checking the resource blacklist is known.
-    virtual void OnCheckResourceUrlResult(const GURL& url,
-                                          SBThreatType threat_type,
-                                          const std::string& threat_hash) {}
-
-    // Called when the result of checking a whitelist is known.
-    // Currently only used for CSD whitelist.
-    virtual void OnCheckWhitelistUrlResult(bool did_match_allowlist) {}
-
-    // Called when the result of checking the high-confidence allowlist is
-    // known.
-    virtual void OnCheckUrlForHighConfidenceAllowlist(
-        bool did_match_allowlist) {}
-  };
-
-  //
-  // Methods called by the client to cancel pending checks.
-  //
-
-  // Called on the IO thread to cancel a pending API check if the result is no
-  // longer needed. Returns true if the client was found and the check
-  // successfully cancelled.
-  virtual bool CancelApiCheck(Client* client);
-
-  // Called on the IO thread to cancel a pending check if the result is no
-  // longer needed.  Also called after the result has been handled. Api checks
-  // are handled separately. To cancel an API check use CancelApiCheck.
-  virtual void CancelCheck(Client* client) = 0;
-
-  //
-  // Methods to check whether the database manager supports a certain feature.
-  //
-
-  // Returns true if this resource type should be checked.
-  virtual bool CanCheckResourceType(
-      content::ResourceType resource_type) const = 0;
-
-  // Returns true if the url's scheme can be checked.
-  virtual bool CanCheckUrl(const GURL& url) const = 0;
-
-  // Returns true if checks are never done synchronously, and therefore
-  // always have some latency.
-  virtual bool ChecksAreAlwaysAsync() const = 0;
-
-  //
-  // Methods to check (possibly asynchronously) whether a given resource is
-  // safe. If the database manager can't determine it synchronously, the
-  // appropriate method on the |client| is called back when the reputation of
-  // the resource is known.
-  //
-
-  // Called on the IO thread to check if the given url has blacklisted APIs.
-  // |client| is called asynchronously with the result when it is ready. Callers
-  // should wait for results before calling this method a second time with the
-  // same client. This method has the same implementation for both the local and
-  // remote database managers since it pings Safe Browsing servers directly
-  // without accessing the database at all.  Returns true if we can
-  // synchronously determine that the url is safe. Otherwise it returns false,
-  // and |client| is called asynchronously with the result when it is ready.
-  virtual bool CheckApiBlacklistUrl(const GURL& url, Client* client);
-
-  // Check if the |url| matches any of the full-length hashes from the client-
-  // side phishing detection whitelist. The 3-state return value indicates
-  // the result or that |client| will get a callback later with the result.
-  virtual AsyncMatch CheckCsdWhitelistUrl(const GURL& url, Client* client) = 0;
-
-  // Called on the IO thread to check if the given url is safe or not.  If we
-  // can synchronously determine that the url is safe, CheckUrl returns true.
-  // Otherwise it returns false, and |client| is called asynchronously with the
-  // result when it is ready. The URL will only be checked for the threat types
-  // in |threat_types|.
-  virtual bool CheckBrowseUrl(const GURL& url,
-                              const SBThreatTypeSet& threat_types,
-                              Client* client) = 0;
-
-  // Check if the prefix for |url| is in safebrowsing download add lists.
-  // Result will be passed to callback in |client|.
-  virtual bool CheckDownloadUrl(const std::vector<GURL>& url_chain,
-                                Client* client) = 0;
-
-  // Check which prefixes in |extension_ids| are in the safebrowsing blacklist.
-  // Returns true if not, false if further checks need to be made in which case
-  // the result will be passed to |client|.
-  virtual bool CheckExtensionIDs(const std::set<std::string>& extension_ids,
-                                 Client* client) = 0;
-
-  // Check if |url| is in the resources blacklist. Returns true if not, false
-  // if further checks need to be made in which case the result will be passed
-  // to callback in |client|.
-  virtual bool CheckResourceUrl(const GURL& url, Client* client) = 0;
-
-  // Called on the IO thread to check if the given url belongs to a list the
-  // subresource cares about. If the url doesn't belong to any such list and the
-  // check can happen synchronously, returns true. Otherwise it returns false,
-  // and |client| is called asynchronously with the result when it is ready.
-  // Returns true if the list is not yet available.
-  virtual bool CheckUrlForSubresourceFilter(const GURL& url,
-                                            Client* client) = 0;
-
-  // Called on the IO thread to check whether |url| is safe by checking if it
-  // appears on a high-confidence allowlist. The 3-state return value indicates
-  // the result or that |client| will get a callback later with the result.
-  // The high confidence allowlist is a list of partial or full hashes of URLs
-  // that are expected to be safe so in the case of a match on this list, the
-  // realtime full URL Safe Browsing lookup isn't performed.
-  virtual AsyncMatch CheckUrlForHighConfidenceAllowlist(const GURL& url,
-                                                        Client* client) = 0;
-
-  //
-  // Match*(): Methods to synchronously check if various types are safe.
-  //
-
-  // Check if SHA-256 hash of |str| matches any of the full-length hashes from
-  // the download whitelist.  Returns true if there was a match and false
-  // otherwise. To make sure we are conservative we will return true if an error
-  // occurs.  This method must be called on the IO thread.
-  virtual bool MatchDownloadWhitelistString(const std::string& str) = 0;
-
-  // Check if the |url| matches any of the full-length hashes from the download
-  // whitelist.  Returns true if there was a match and false otherwise. To make
-  // sure we are conservative we will return true if an error occurs.  This
-  // method must be called on the IO thread.
-  virtual bool MatchDownloadWhitelistUrl(const GURL& url) = 0;
-
-  // Check if the given IP address (either IPv4 or IPv6) matches the malware
-  // IP blacklist.
-  virtual bool MatchMalwareIP(const std::string& ip_address) = 0;
-
-  //
-  // Methods to check the config of the DatabaseManager.
-  //
-
-  // Returns the lists that this DatabaseManager should get full hashes for.
-  virtual StoresToCheck GetStoresForFullHashRequests();
-
-  // Returns the client_state of each of the lists that this DatabaseManager
-  // syncs.
-  virtual std::unique_ptr<StoreStateMap> GetStoreStateMap();
-
-  // Returns the Safety Net ID of the device.
-  virtual std::string GetSafetyNetId() const;
-
-  // Returns the ThreatSource for this implementation.
-  virtual ThreatSource GetThreatSource() const = 0;
-
-  // Returns whether download protection is enabled.
-  virtual bool IsDownloadProtectionEnabled() const = 0;
-
-  // Returns true if URL-checking is supported on this build+device.
-  // If false, calls to CheckBrowseUrl may dcheck-fail.
-  virtual bool IsSupported() const = 0;
-
-  //
-  // Methods to indicate when to start or suspend the SafeBrowsing operations.
-  // These functions are always called on the IO thread.
-  //
-
-  // Called to initialize objects that are used on the io_thread, such as the
-  // v4 protocol manager.  This may be called multiple times during the life of
-  // the DatabaseManager. Must be called on IO thread. All subclasses should
-  // override this method, set enabled_ to true and call the base class method
-  // at the top of it.
-  virtual void StartOnIOThread(
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      const V4ProtocolConfig& config);
-
-  //
-  // Method to manage getting database updates of the DatabaseManager.
-  //
-
-  // Subscribe to receive callbacks when the database is updated, both initially
-  // when it's loaded from disk at startup, and then periodically. These
-  // callbacks will be on the UI thread.
-  using OnDatabaseUpdated = base::RepeatingClosure;
-  std::unique_ptr<base::CallbackList<void()>::Subscription>
-  RegisterDatabaseUpdatedCallback(const OnDatabaseUpdated& cb);
-
-  // Called to stop or shutdown operations on the io_thread. All subclasses
-  // should override this method, set enabled_ to false and call the base class
-  // method at the bottom of it.
-  virtual void StopOnIOThread(bool shutdown);
-
-  virtual RealTimeUrlLookupService* GetRealTimeUrlLookupService();
-
- protected:
-  // Bundled client info for an API abuse hash prefix check.
-  class SafeBrowsingApiCheck {
-   public:
-    SafeBrowsingApiCheck(const GURL& url, Client* client);
-    ~SafeBrowsingApiCheck();
-
-    const GURL& url() const { return url_; }
-    Client* client() const { return client_; }
-
-   private:
-    GURL url_;
-
-    // Not owned.
-    Client* client_;
-
-    DISALLOW_COPY_AND_ASSIGN(SafeBrowsingApiCheck);
-  };
-
-  SafeBrowsingDatabaseManager();
-
-  virtual ~SafeBrowsingDatabaseManager();
-
-  friend class base::RefCountedDeleteOnSequence<SafeBrowsingDatabaseManager>;
-  friend class base::DeleteHelper<SafeBrowsingDatabaseManager>;
-  friend class V4LocalDatabaseManager;
-
-  FRIEND_TEST_ALL_PREFIXES(SafeBrowsingDatabaseManagerTest,
-                           CheckApiBlacklistUrlPrefixes);
-  FRIEND_TEST_ALL_PREFIXES(SafeBrowsingDatabaseManagerTest,
-                           HandleGetHashesWithApisResults);
-  FRIEND_TEST_ALL_PREFIXES(SafeBrowsingDatabaseManagerTest,
-                           HandleGetHashesWithApisResultsNoMatch);
-  FRIEND_TEST_ALL_PREFIXES(SafeBrowsingDatabaseManagerTest,
-                           HandleGetHashesWithApisResultsMatches);
-  FRIEND_TEST_ALL_PREFIXES(SafeBrowsingDatabaseManagerTest, CancelApiCheck);
-  FRIEND_TEST_ALL_PREFIXES(SafeBrowsingDatabaseManagerTest, ResultsAreCached);
-  FRIEND_TEST_ALL_PREFIXES(SafeBrowsingDatabaseManagerTest,
-                           ResultsAreNotCachedOnNull);
-  FRIEND_TEST_ALL_PREFIXES(SafeBrowsingDatabaseManagerTest, GetCachedResults);
-  FRIEND_TEST_ALL_PREFIXES(SafeBrowsingDatabaseManagerTest,
-                           CachedResultsMerged);
-  FRIEND_TEST_ALL_PREFIXES(SafeBrowsingDatabaseManagerTest,
-                           CachedResultsAreEvicted);
-
-  // Called on the IO thread when the SafeBrowsingProtocolManager has received
-  // the full hash and api results for prefixes of the |url| argument in
-  // CheckApiBlacklistUrl.
-  void OnThreatMetadataResponse(std::unique_ptr<SafeBrowsingApiCheck> check,
-                                const ThreatMetadata& md);
-
-  typedef std::set<SafeBrowsingApiCheck*> ApiCheckSet;
-
-  // In-progress checks. This set owns the SafeBrowsingApiCheck pointers and is
-  // responsible for deleting them when removing from the set.
-  ApiCheckSet api_checks_;
-
-  // Whether the service is running. 'enabled_' is used by the
-  // SafeBrowsingDatabaseManager on the IO thread during normal operations.
-  bool enabled_;
-
-  // Make callbacks about the completion of database update process. This is
-  // currently used by the extension blacklist checker to disable any installed
-  // extensions that have been blacklisted since.
-  void NotifyDatabaseUpdateFinished();
-
-  // Created and destroyed via StartOnIOThread/StopOnIOThread.
-  std::unique_ptr<V4GetHashProtocolManager> v4_get_hash_protocol_manager_;
-
-  // A list of parties to be notified about database updates.
-  base::CallbackList<void()> update_complete_callback_list_;
-
- private:
-  // Returns an iterator to the pending API check with the given |client|.
-  ApiCheckSet::iterator FindClientApiCheck(Client* client);
-};  // class SafeBrowsingDatabaseManager
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_DB_DATABASE_MANAGER_H_
diff --git a/components/safe_browsing/db/database_manager_unittest.cc b/components/safe_browsing/db/database_manager_unittest.cc
deleted file mode 100644
index b7bbe94..0000000
--- a/components/safe_browsing/db/database_manager_unittest.cc
+++ /dev/null
@@ -1,153 +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.
-
-#include "components/safe_browsing/db/database_manager.h"
-
-#include <stddef.h>
-
-#include <set>
-#include <string>
-#include <vector>
-
-#include "base/base64.h"
-#include "base/location.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/run_loop.h"
-#include "base/single_thread_task_runner.h"
-#include "base/synchronization/waitable_event.h"
-#include "base/test/bind_test_util.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "components/safe_browsing/db/test_database_manager.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
-#include "components/safe_browsing/db/v4_test_util.h"
-#include "content/public/test/browser_task_environment.h"
-#include "crypto/sha2.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
-#include "services/network/test/test_url_loader_factory.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
-
-namespace safe_browsing {
-
-namespace {
-
-class TestClient : public SafeBrowsingDatabaseManager::Client {
- public:
-  TestClient() : callback_invoked_(false) {}
-  ~TestClient() override {}
-
-  void OnCheckApiBlacklistUrlResult(const GURL& url,
-                                    const ThreatMetadata& metadata) override {
-    blocked_permissions_ = metadata.api_permissions;
-    callback_invoked_ = true;
-    run_loop_.Quit();
-  }
-
-  const std::set<std::string>& GetBlockedPermissions() {
-    return blocked_permissions_;
-  }
-
-  void WaitForCallback() { run_loop_.Run(); }
-
-  bool callback_invoked() { return callback_invoked_; }
-
- private:
-  std::set<std::string> blocked_permissions_;
-  bool callback_invoked_;
-  base::RunLoop run_loop_;
-  DISALLOW_COPY_AND_ASSIGN(TestClient);
-};
-
-}  // namespace
-
-class SafeBrowsingDatabaseManagerTest : public testing::Test {
- protected:
-  void SetUp() override {
-    test_shared_loader_factory_ =
-        base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
-            &test_url_loader_factory_);
-
-    db_manager_ = new TestSafeBrowsingDatabaseManager();
-    db_manager_->StartOnIOThread(test_shared_loader_factory_,
-                                 GetTestV4ProtocolConfig());
-  }
-
-  void TearDown() override {
-    db_manager_->StopOnIOThread(false);
-    db_manager_ = nullptr;
-    base::RunLoop().RunUntilIdle();
-  }
-
-  std::string GetStockV4GetHashResponse() {
-    ListIdentifier list_id = GetChromeUrlApiId();
-    FullHash full_hash = crypto::SHA256HashString("example.com/");
-
-    FindFullHashesResponse response;
-    response.mutable_negative_cache_duration()->set_seconds(600);
-    ThreatMatch* m = response.add_matches();
-    m->set_platform_type(list_id.platform_type());
-    m->set_threat_entry_type(list_id.threat_entry_type());
-    m->set_threat_type(list_id.threat_type());
-    m->mutable_threat()->set_hash(full_hash);
-    m->mutable_cache_duration()->set_seconds(300);
-
-    ThreatEntryMetadata::MetadataEntry* e =
-        m->mutable_threat_entry_metadata()->add_entries();
-    e->set_key("permission");
-    e->set_value("GEOLOCATION");
-
-    std::string res_data;
-    response.SerializeToString(&res_data);
-    return res_data;
-  }
-
-  network::TestURLLoaderFactory test_url_loader_factory_;
-  scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
-  scoped_refptr<SafeBrowsingDatabaseManager> db_manager_;
-
- private:
-  content::BrowserTaskEnvironment task_environment_;
-};
-
-TEST_F(SafeBrowsingDatabaseManagerTest, CheckApiBlacklistUrlWrongScheme) {
-  EXPECT_TRUE(
-      db_manager_->CheckApiBlacklistUrl(GURL("file://example.txt"), nullptr));
-}
-
-TEST_F(SafeBrowsingDatabaseManagerTest, CancelApiCheck) {
-  TestClient client;
-  const GURL url("https://www.example.com/more");
-
-  EXPECT_FALSE(db_manager_->CheckApiBlacklistUrl(url, &client));
-  EXPECT_TRUE(db_manager_->CancelApiCheck(&client));
-
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_FALSE(client.callback_invoked());
-  EXPECT_EQ(0ul, client.GetBlockedPermissions().size());
-}
-
-TEST_F(SafeBrowsingDatabaseManagerTest, GetApiCheckResponse) {
-  TestClient client;
-  const GURL url("https://www.example.com/more");
-
-  GURL request_url;
-  test_url_loader_factory_.SetInterceptor(
-      base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
-        request_url = request.url;
-      }));
-
-  EXPECT_FALSE(db_manager_->CheckApiBlacklistUrl(url, &client));
-  test_url_loader_factory_.AddResponse(request_url.spec(),
-                                       GetStockV4GetHashResponse());
-  base::RunLoop().RunUntilIdle();
-
-  client.WaitForCallback();
-  ASSERT_EQ(1ul, client.GetBlockedPermissions().size());
-  EXPECT_EQ("GEOLOCATION", *(client.GetBlockedPermissions().begin()));
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/db/hit_report.cc b/components/safe_browsing/db/hit_report.cc
deleted file mode 100644
index 20c18729..0000000
--- a/components/safe_browsing/db/hit_report.cc
+++ /dev/null
@@ -1,15 +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 "components/safe_browsing/db/hit_report.h"
-
-namespace safe_browsing {
-
-HitReport::HitReport() {}
-
-HitReport::HitReport(const HitReport& other) = default;
-
-HitReport::~HitReport() {}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/db/hit_report.h b/components/safe_browsing/db/hit_report.h
deleted file mode 100644
index 87db01a..0000000
--- a/components/safe_browsing/db/hit_report.h
+++ /dev/null
@@ -1,55 +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.
-//
-// Datastructures that hold details of a Safe Browsing hit for reporting.
-
-#ifndef COMPONENTS_SAFE_BROWSING_DB_HIT_REPORT_H_
-#define COMPONENTS_SAFE_BROWSING_DB_HIT_REPORT_H_
-
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/db/util.h"
-#include "url/gurl.h"
-
-namespace safe_browsing {
-
-// What service classified this threat as unsafe.
-enum class ThreatSource {
-  UNKNOWN,
-  DATA_SAVER,             // From the Data Reduction service.
-  LOCAL_PVER3,            // From LocalSafeBrowsingDatabaseManager, protocol v3
-  LOCAL_PVER4,            // From V4LocalDatabaseManager, protocol v4
-  REMOTE,                 // From RemoteSafeBrowsingDatabaseManager
-  CLIENT_SIDE_DETECTION,  // From ClientSideDetectionHost
-  PASSWORD_PROTECTION_SERVICE,  // From PasswordProtectionService
-};
-
-// Data to report about the contents of a particular threat (malware, phishing,
-// unsafe download URL).  If post_data is non-empty, the request will be
-// sent as a POST instead of a GET.
-struct HitReport {
-  HitReport();
-  HitReport(const HitReport& other);
-  ~HitReport();
-
-  GURL malicious_url;
-  GURL page_url;
-  GURL referrer_url;
-
-  bool is_subresource;
-  SBThreatType threat_type;
-  ThreatSource threat_source;
-
-  // Opaque string used for tracking Pver4-based experiments.
-  // NOTE(vakh): Unused at the moment, but may be used later.
-  std::string population_id;
-
-  ExtendedReportingLevel extended_reporting_level;
-  bool is_metrics_reporting_active;
-
-  std::string post_data;
-};
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_DB_HIT_REPORT_H_
diff --git a/components/safe_browsing/db/prefix_iterator.cc b/components/safe_browsing/db/prefix_iterator.cc
deleted file mode 100644
index e7471ff..0000000
--- a/components/safe_browsing/db/prefix_iterator.cc
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/db/prefix_iterator.h"
-
-namespace safe_browsing {
-
-PrefixIterator::PrefixIterator(base::StringPiece prefixes,
-                               size_t index,
-                               size_t size)
-    : prefixes_(prefixes), index_(index), size_(size) {}
-
-PrefixIterator::PrefixIterator(const PrefixIterator& rhs)
-    : prefixes_(rhs.prefixes_), index_(rhs.index_), size_(rhs.size_) {}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/db/prefix_iterator.h b/components/safe_browsing/db/prefix_iterator.h
deleted file mode 100644
index 1f95b1e..0000000
--- a/components/safe_browsing/db/prefix_iterator.h
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_DB_PREFIX_ITERATOR_H_
-#define COMPONENTS_SAFE_BROWSING_DB_PREFIX_ITERATOR_H_
-
-#include <cstddef>
-#include <iterator>
-
-#include "base/strings/string_piece.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
-
-namespace safe_browsing {
-
-// The prefix iterator is used to binary search within a |HashPrefixes|. It is
-// essentially a random access iterator that steps |PrefixSize| steps within the
-// underlying buffer.
-class PrefixIterator
-    : public std::iterator<std::random_access_iterator_tag, base::StringPiece> {
- public:
-  using difference_type =
-      typename std::iterator<std::random_access_iterator_tag,
-                             base::StringPiece>::difference_type;
-
-  PrefixIterator(base::StringPiece prefixes, size_t index, size_t size);
-  PrefixIterator(const PrefixIterator& rhs);
-
-  base::StringPiece operator*() const { return GetPiece(index_); }
-  base::StringPiece operator[](const int& rhs) const {
-    return GetPiece(index_ + rhs);
-  }
-
-  PrefixIterator& operator=(const PrefixIterator& rhs) {
-    index_ = rhs.index_;
-    return *this;
-  }
-  PrefixIterator& operator+=(const int& rhs) {
-    index_ += rhs;
-    return *this;
-  }
-  PrefixIterator& operator-=(const int& rhs) {
-    index_ -= rhs;
-    return *this;
-  }
-  PrefixIterator& operator++() {
-    index_++;
-    return *this;
-  }
-  PrefixIterator& operator--() {
-    index_--;
-    return *this;
-  }
-
-  PrefixIterator operator+(const PrefixIterator& rhs) const {
-    return PrefixIterator(prefixes_, index_ + rhs.index_, size_);
-  }
-  difference_type operator-(const PrefixIterator& rhs) const {
-    return index_ - rhs.index_;
-  }
-  PrefixIterator operator+(const int& rhs) const {
-    return PrefixIterator(prefixes_, index_ + rhs, size_);
-  }
-  PrefixIterator operator-(const int& rhs) const {
-    return PrefixIterator(prefixes_, index_ - rhs, size_);
-  }
-
-  bool operator==(const PrefixIterator& rhs) const {
-    return index_ == rhs.index_;
-  }
-  bool operator!=(const PrefixIterator& rhs) const {
-    return index_ != rhs.index_;
-  }
-  bool operator>(const PrefixIterator& rhs) const {
-    return index_ > rhs.index_;
-  }
-  bool operator<(const PrefixIterator& rhs) const {
-    return index_ < rhs.index_;
-  }
-  bool operator>=(const PrefixIterator& rhs) const {
-    return index_ >= rhs.index_;
-  }
-  bool operator<=(const PrefixIterator& rhs) const {
-    return index_ <= rhs.index_;
-  }
-
- private:
-  base::StringPiece GetPiece(size_t index) const {
-    return prefixes_.substr(index * size_, size_);
-  }
-
-  base::StringPiece prefixes_;
-  size_t index_;
-  size_t size_;
-};
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_DB_PREFIX_ITERATOR_H_
diff --git a/components/safe_browsing/db/test_database_manager.cc b/components/safe_browsing/db/test_database_manager.cc
deleted file mode 100644
index 33a91b4..0000000
--- a/components/safe_browsing/db/test_database_manager.cc
+++ /dev/null
@@ -1,131 +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 "components/safe_browsing/db/test_database_manager.h"
-
-#include <set>
-#include <string>
-#include <vector>
-
-#include "base/logging.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-
-namespace safe_browsing {
-
-void TestSafeBrowsingDatabaseManager::CancelCheck(Client* client) {
-  NOTIMPLEMENTED();
-}
-
-bool TestSafeBrowsingDatabaseManager::CanCheckResourceType(
-    content::ResourceType resource_type) const {
-  NOTIMPLEMENTED();
-  return false;
-}
-
-bool TestSafeBrowsingDatabaseManager::CanCheckUrl(const GURL& url) const {
-  NOTIMPLEMENTED();
-  return (url != GURL("about:blank"));
-}
-
-bool TestSafeBrowsingDatabaseManager::ChecksAreAlwaysAsync() const {
-  NOTIMPLEMENTED();
-  return false;
-}
-
-bool TestSafeBrowsingDatabaseManager::CheckBrowseUrl(
-    const GURL& url,
-    const SBThreatTypeSet& threat_types,
-    Client* client) {
-  NOTIMPLEMENTED();
-  return true;
-}
-
-bool TestSafeBrowsingDatabaseManager::CheckDownloadUrl(
-    const std::vector<GURL>& url_chain,
-    Client* client) {
-  NOTIMPLEMENTED();
-  return true;
-}
-
-bool TestSafeBrowsingDatabaseManager::CheckExtensionIDs(
-    const std::set<std::string>& extension_ids,
-    Client* client) {
-  NOTIMPLEMENTED();
-  return true;
-}
-
-bool TestSafeBrowsingDatabaseManager::CheckResourceUrl(const GURL& url,
-                                                       Client* client) {
-  NOTIMPLEMENTED();
-  return true;
-}
-
-AsyncMatch TestSafeBrowsingDatabaseManager::CheckUrlForHighConfidenceAllowlist(
-    const GURL& url,
-    Client* client) {
-  NOTIMPLEMENTED();
-  return AsyncMatch::NO_MATCH;
-}
-
-bool TestSafeBrowsingDatabaseManager::CheckUrlForSubresourceFilter(
-    const GURL& url,
-    Client* client) {
-  NOTIMPLEMENTED();
-  return true;
-}
-
-AsyncMatch TestSafeBrowsingDatabaseManager::CheckCsdWhitelistUrl(
-    const GURL& url,
-    Client* client) {
-  NOTIMPLEMENTED();
-  return AsyncMatch::MATCH;
-}
-
-bool TestSafeBrowsingDatabaseManager::MatchDownloadWhitelistString(
-    const std::string& str) {
-  NOTIMPLEMENTED();
-  return true;
-}
-
-bool TestSafeBrowsingDatabaseManager::MatchDownloadWhitelistUrl(
-    const GURL& url) {
-  NOTIMPLEMENTED();
-  return true;
-}
-
-bool TestSafeBrowsingDatabaseManager::MatchMalwareIP(
-    const std::string& ip_address) {
-  NOTIMPLEMENTED();
-  return true;
-}
-
-safe_browsing::ThreatSource TestSafeBrowsingDatabaseManager::GetThreatSource()
-    const {
-  NOTIMPLEMENTED();
-  return safe_browsing::ThreatSource::UNKNOWN;
-}
-
-bool TestSafeBrowsingDatabaseManager::IsDownloadProtectionEnabled() const {
-  NOTIMPLEMENTED();
-  return false;
-}
-
-bool TestSafeBrowsingDatabaseManager::IsSupported() const {
-  NOTIMPLEMENTED();
-  return false;
-}
-
-void TestSafeBrowsingDatabaseManager::StartOnIOThread(
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    const V4ProtocolConfig& config) {
-  SafeBrowsingDatabaseManager::StartOnIOThread(url_loader_factory, config);
-  enabled_ = true;
-}
-
-void TestSafeBrowsingDatabaseManager::StopOnIOThread(bool shutdown) {
-  enabled_ = false;
-  SafeBrowsingDatabaseManager::StopOnIOThread(shutdown);
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/db/test_database_manager.h b/components/safe_browsing/db/test_database_manager.h
deleted file mode 100644
index b5c6379..0000000
--- a/components/safe_browsing/db/test_database_manager.h
+++ /dev/null
@@ -1,57 +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 COMPONENTS_SAFE_BROWSING_DB_TEST_DATABASE_MANAGER_H_
-#define COMPONENTS_SAFE_BROWSING_DB_TEST_DATABASE_MANAGER_H_
-
-#include <set>
-#include <string>
-#include <vector>
-
-#include "components/safe_browsing/db/database_manager.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
-
-namespace safe_browsing {
-
-// This is a non-pure-virtual implementation of the SafeBrowsingDatabaseManager
-// interface.  It's used in tests by overriding only the functions that get
-// called, and it'll complain if you call one that isn't overriden. The
-// non-abstract methods in the base class are not overridden.
-class TestSafeBrowsingDatabaseManager : public SafeBrowsingDatabaseManager {
- public:
-  // SafeBrowsingDatabaseManager implementation:
-  void CancelCheck(Client* client) override;
-  bool CanCheckResourceType(content::ResourceType resource_type) const override;
-  bool CanCheckUrl(const GURL& url) const override;
-  bool ChecksAreAlwaysAsync() const override;
-  bool CheckBrowseUrl(const GURL& url,
-                      const SBThreatTypeSet& threat_types,
-                      Client* client) override;
-  AsyncMatch CheckCsdWhitelistUrl(const GURL& url, Client* client) override;
-  bool CheckDownloadUrl(const std::vector<GURL>& url_chain,
-                        Client* client) override;
-  bool CheckExtensionIDs(const std::set<std::string>& extension_ids,
-                         Client* client) override;
-  bool CheckResourceUrl(const GURL& url, Client* client) override;
-  AsyncMatch CheckUrlForHighConfidenceAllowlist(const GURL& url,
-                                                Client* client) override;
-  bool CheckUrlForSubresourceFilter(const GURL& url, Client* client) override;
-  bool MatchDownloadWhitelistString(const std::string& str) override;
-  bool MatchDownloadWhitelistUrl(const GURL& url) override;
-  bool MatchMalwareIP(const std::string& ip_address) override;
-  safe_browsing::ThreatSource GetThreatSource() const override;
-  bool IsDownloadProtectionEnabled() const override;
-  bool IsSupported() const override;
-  void StartOnIOThread(
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      const V4ProtocolConfig& config) override;
-  void StopOnIOThread(bool shutdown) override;
-
- protected:
-  ~TestSafeBrowsingDatabaseManager() override {}
-};
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_DB_TEST_DATABASE_MANAGER_H_
diff --git a/components/safe_browsing/db/util.cc b/components/safe_browsing/db/util.cc
deleted file mode 100644
index d01cee5..0000000
--- a/components/safe_browsing/db/util.cc
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright (c) 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 "components/safe_browsing/db/util.h"
-
-#include <stddef.h>
-
-namespace safe_browsing {
-
-ThreatMetadata::ThreatMetadata()
-    : threat_pattern_type(ThreatPatternType::NONE) {}
-
-ThreatMetadata::ThreatMetadata(const ThreatMetadata& other) = default;
-
-ThreatMetadata::~ThreatMetadata() {}
-
-bool ThreatMetadata::operator==(const ThreatMetadata& other) const {
-  return threat_pattern_type == other.threat_pattern_type &&
-         api_permissions == other.api_permissions &&
-         subresource_filter_match == other.subresource_filter_match &&
-         population_id == other.population_id;
-}
-
-bool ThreatMetadata::operator!=(const ThreatMetadata& other) const {
-  return !operator==(other);
-}
-
-std::unique_ptr<base::trace_event::TracedValue> ThreatMetadata::ToTracedValue()
-    const {
-  auto value = std::make_unique<base::trace_event::TracedValue>();
-
-  value->SetInteger("threat_pattern_type",
-                    static_cast<int>(threat_pattern_type));
-
-  value->BeginArray("api_permissions");
-  for (const std::string& permission : api_permissions) {
-    value->AppendString(permission);
-  }
-  value->EndArray();
-
-  value->BeginDictionary("subresource_filter_match");
-  for (const auto& it : subresource_filter_match) {
-    value->BeginArray("match_metadata");
-    value->AppendInteger(static_cast<int>(it.first));
-    value->AppendInteger(static_cast<int>(it.second));
-    value->EndArray();
-  }
-  value->EndDictionary();
-
-  value->SetString("popuplation_id", population_id);
-  return value;
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/db/util.h b/components/safe_browsing/db/util.h
deleted file mode 100644
index 1a7490a2..0000000
--- a/components/safe_browsing/db/util.h
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright (c) 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.
-//
-// Utilities for the SafeBrowsing DB code.
-
-#ifndef COMPONENTS_SAFE_BROWSING_DB_UTIL_H_
-#define COMPONENTS_SAFE_BROWSING_DB_UTIL_H_
-
-#include <stdint.h>
-
-#include <cstring>
-#include <memory>
-#include <set>
-#include <string>
-
-#include "base/containers/flat_map.h"
-#include "base/strings/string_piece.h"
-#include "base/time/time.h"
-#include "base/trace_event/traced_value.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
-
-namespace safe_browsing {
-
-// Metadata that indicates what kind of URL match this is.
-enum class ThreatPatternType : int {
-  NONE = 0,                        // Pattern type didn't appear in the metadata
-  MALWARE_LANDING = 1,             // The match is a malware landing page
-  MALWARE_DISTRIBUTION = 2,        // The match is a malware distribution page
-  SOCIAL_ENGINEERING_ADS = 3,      // The match is a social engineering ads page
-  SOCIAL_ENGINEERING_LANDING = 4,  // The match is a social engineering landing
-                                   // page
-  PHISHING = 5,                    // The match is a phishing page
-  THREAT_PATTERN_TYPE_MAX_VALUE
-};
-
-enum class SubresourceFilterType : int { ABUSIVE = 0, BETTER_ADS = 1 };
-
-enum class SubresourceFilterLevel : int { WARN = 0, ENFORCE = 1 };
-
-using SubresourceFilterMatch =
-    base::flat_map<SubresourceFilterType, SubresourceFilterLevel>;
-
-// Metadata that was returned by a GetFullHash call. This is the parsed version
-// of the PB (from Pver3, or Pver4 local) or JSON (from Pver4 via GMSCore).
-// Some fields are only applicable to certain lists.
-//
-// When adding elements to this struct, make sure you update operator== and
-// ToTracedValue.
-struct ThreatMetadata {
-  ThreatMetadata();
-  ThreatMetadata(const ThreatMetadata& other);
-  ~ThreatMetadata();
-
-  bool operator==(const ThreatMetadata& other) const;
-  bool operator!=(const ThreatMetadata& other) const;
-
-  // Returns the metadata in a format tracing can support.
-  std::unique_ptr<base::trace_event::TracedValue> ToTracedValue() const;
-
-  // Type of blacklisted page. Used on malware and UwS lists.
-  // This will be NONE if it wasn't present in the reponse.
-  ThreatPatternType threat_pattern_type;
-
-  // Set of permissions blocked. Used with threat_type API_ABUSE.
-  // This will be empty if it wasn't present in the response.
-  std::set<std::string> api_permissions;
-
-  // Map of list sub-types related to the SUBRESOURCE_FILTER threat type.
-  // Used instead of ThreatPatternType to allow multiple types at the same time.
-  SubresourceFilterMatch subresource_filter_match;
-
-  // Opaque base64 string used for user-population experiments in pver4.
-  // This will be empty if it wasn't present in the response.
-  std::string population_id;
-};
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_DB_UTIL_H_
diff --git a/components/safe_browsing/db/v4_database.cc b/components/safe_browsing/db/v4_database.cc
deleted file mode 100644
index 30f7ef9..0000000
--- a/components/safe_browsing/db/v4_database.cc
+++ /dev/null
@@ -1,341 +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.
-
-#include "components/safe_browsing/db/v4_database.h"
-
-#include <memory>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/files/file_util.h"
-#include "base/lazy_instance.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/task_runner_util.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "components/safe_browsing/proto/webui.pb.h"
-#include "content/public/browser/browser_thread.h"
-
-using content::BrowserThread;
-using base::TimeTicks;
-
-namespace safe_browsing {
-
-namespace {
-
-const char kV4DatabaseSizeMetric[] = "SafeBrowsing.V4Database.Size";
-
-// The factory that controls the creation of the V4Database object.
-base::LazyInstance<std::unique_ptr<V4DatabaseFactory>>::Leaky g_db_factory =
-    LAZY_INSTANCE_INITIALIZER;
-
-// The factory that controls the creation of V4Store objects.
-base::LazyInstance<std::unique_ptr<V4StoreFactory>>::Leaky g_store_factory =
-    LAZY_INSTANCE_INITIALIZER;
-
-// Verifies the checksums on a collection of stores.
-// Returns the IDs of stores whose checksums failed to verify.
-std::vector<ListIdentifier> VerifyChecksums(
-    std::vector<std::pair<ListIdentifier, V4Store*>> stores) {
-  std::vector<ListIdentifier> stores_to_reset;
-  for (const auto& store_map_iter : stores) {
-    if (!store_map_iter.second->VerifyChecksum()) {
-      stores_to_reset.push_back(store_map_iter.first);
-    }
-  }
-  return stores_to_reset;
-}
-
-}  // namespace
-
-std::unique_ptr<V4Database> V4DatabaseFactory::Create(
-    const scoped_refptr<base::SequencedTaskRunner>& db_task_runner,
-    std::unique_ptr<StoreMap> store_map) {
-  // Not using MakeUnique since the constructor of V4Database is protected.
-  return std::unique_ptr<V4Database>(
-      new V4Database(db_task_runner, std::move(store_map)));
-}
-
-// static
-void V4Database::Create(
-    const scoped_refptr<base::SequencedTaskRunner>& db_task_runner,
-    const base::FilePath& base_path,
-    const ListInfos& list_infos,
-    NewDatabaseReadyCallback new_db_callback) {
-  DCHECK(base_path.IsAbsolute());
-  DCHECK(!list_infos.empty());
-
-  const scoped_refptr<base::SingleThreadTaskRunner> callback_task_runner =
-      base::ThreadTaskRunnerHandle::Get();
-  db_task_runner->PostTask(
-      FROM_HERE, base::BindOnce(&V4Database::CreateOnTaskRunner, db_task_runner,
-                                base_path, list_infos, callback_task_runner,
-                                std::move(new_db_callback)));
-}
-
-// static
-void V4Database::CreateOnTaskRunner(
-    const scoped_refptr<base::SequencedTaskRunner>& db_task_runner,
-    const base::FilePath& base_path,
-    const ListInfos& list_infos,
-    const scoped_refptr<base::SingleThreadTaskRunner>& callback_task_runner,
-    NewDatabaseReadyCallback new_db_callback) {
-  DCHECK(db_task_runner->RunsTasksInCurrentSequence());
-
-  if (!g_store_factory.Get())
-    g_store_factory.Get() = std::make_unique<V4StoreFactory>();
-
-  if (!base::CreateDirectory(base_path))
-    NOTREACHED();
-
-  std::unique_ptr<StoreMap> store_map = std::make_unique<StoreMap>();
-  for (const auto& it : list_infos) {
-    if (!it.fetch_updates()) {
-      // This list doesn't need to be fetched or stored on disk.
-      continue;
-    }
-
-    const base::FilePath store_path = base_path.AppendASCII(it.filename());
-    (*store_map)[it.list_id()] =
-        g_store_factory.Get()->CreateV4Store(db_task_runner, store_path);
-  }
-
-  if (!g_db_factory.Get())
-    g_db_factory.Get() = std::make_unique<V4DatabaseFactory>();
-
-  std::unique_ptr<V4Database> v4_database =
-      g_db_factory.Get()->Create(db_task_runner, std::move(store_map));
-
-  // Database is done loading, pass it to the new_db_callback on the caller's
-  // thread. This would unblock resource loads.
-  callback_task_runner->PostTask(
-      FROM_HERE,
-      base::BindOnce(std::move(new_db_callback), std::move(v4_database)));
-}
-
-// static
-void V4Database::RegisterDatabaseFactoryForTest(
-    std::unique_ptr<V4DatabaseFactory> factory) {
-  g_db_factory.Get() = std::move(factory);
-}
-
-// static
-void V4Database::RegisterStoreFactoryForTest(
-    std::unique_ptr<V4StoreFactory> factory) {
-  g_store_factory.Get() = std::move(factory);
-}
-
-V4Database::V4Database(
-    const scoped_refptr<base::SequencedTaskRunner>& db_task_runner,
-    std::unique_ptr<StoreMap> store_map)
-    : store_map_(std::move(store_map)),
-      db_task_runner_(db_task_runner),
-      pending_store_updates_(0) {
-  DCHECK(db_task_runner->RunsTasksInCurrentSequence());
-}
-
-// static
-void V4Database::Destroy(std::unique_ptr<V4Database> v4_database) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  V4Database* v4_database_raw = v4_database.release();
-  if (v4_database_raw) {
-    v4_database_raw->weak_factory_on_io_.InvalidateWeakPtrs();
-    v4_database_raw->db_task_runner_->DeleteSoon(FROM_HERE, v4_database_raw);
-  }
-}
-
-V4Database::~V4Database() {
-  DCHECK(db_task_runner_->RunsTasksInCurrentSequence());
-}
-
-void V4Database::ApplyUpdate(
-    std::unique_ptr<ParsedServerResponse> parsed_server_response,
-    DatabaseUpdatedCallback db_updated_callback) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DCHECK(!pending_store_updates_);
-  DCHECK(db_updated_callback_.is_null());
-
-  db_updated_callback_ = db_updated_callback;
-
-  // Post the V4Store update task on the task runner but get the callback on the
-  // current thread.
-  const scoped_refptr<base::SingleThreadTaskRunner> current_task_runner =
-      base::ThreadTaskRunnerHandle::Get();
-  for (std::unique_ptr<ListUpdateResponse>& response :
-       *parsed_server_response) {
-    ListIdentifier identifier(*response);
-    StoreMap::const_iterator iter = store_map_->find(identifier);
-    if (iter != store_map_->end()) {
-      const std::unique_ptr<V4Store>& old_store = iter->second;
-      if (old_store->state() != response->new_client_state()) {
-        // A different state implies there are updates to process.
-        pending_store_updates_++;
-        UpdatedStoreReadyCallback store_ready_callback =
-            base::BindOnce(&V4Database::UpdatedStoreReady,
-                           weak_factory_on_io_.GetWeakPtr(), identifier);
-        db_task_runner_->PostTask(
-            FROM_HERE, base::BindOnce(&V4Store::ApplyUpdate,
-                                      base::Unretained(old_store.get()),
-                                      std::move(response), current_task_runner,
-                                      std::move(store_ready_callback)));
-      }
-    } else {
-      NOTREACHED() << "Got update for unexpected identifier: " << identifier;
-    }
-  }
-
-  if (!pending_store_updates_) {
-    current_task_runner->PostTask(FROM_HERE, db_updated_callback_);
-    db_updated_callback_.Reset();
-  }
-}
-
-void V4Database::UpdatedStoreReady(ListIdentifier identifier,
-                                   std::unique_ptr<V4Store> new_store) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DCHECK(pending_store_updates_);
-  if (new_store) {
-    (*store_map_)[identifier].swap(new_store);
-    // |new_store| now is the store that needs to be destroyed on task runner.
-    V4Store::Destroy(std::move(new_store));
-  }
-
-  pending_store_updates_--;
-  if (!pending_store_updates_) {
-    db_updated_callback_.Run();
-    db_updated_callback_.Reset();
-  }
-}
-
-std::unique_ptr<StoreStateMap> V4Database::GetStoreStateMap() {
-  std::unique_ptr<StoreStateMap> store_state_map =
-      std::make_unique<StoreStateMap>();
-  for (const auto& store_map_iter : *store_map_) {
-    (*store_state_map)[store_map_iter.first] = store_map_iter.second->state();
-  }
-  return store_state_map;
-}
-
-bool V4Database::AreAnyStoresAvailable(
-    const StoresToCheck& stores_to_check) const {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  for (const ListIdentifier& identifier : stores_to_check) {
-    if (IsStoreAvailable(identifier))
-      return true;
-  }
-  return false;
-}
-
-bool V4Database::AreAllStoresAvailable(
-    const StoresToCheck& stores_to_check) const {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  for (const ListIdentifier& identifier : stores_to_check) {
-    if (!IsStoreAvailable(identifier))
-      return false;
-  }
-  return true;
-}
-
-void V4Database::GetStoresMatchingFullHash(
-    const FullHash& full_hash,
-    const StoresToCheck& stores_to_check,
-    StoreAndHashPrefixes* matched_store_and_hash_prefixes) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  matched_store_and_hash_prefixes->clear();
-  for (const ListIdentifier& identifier : stores_to_check) {
-    if (!IsStoreAvailable(identifier))
-      continue;
-    const auto& store_pair = store_map_->find(identifier);
-    DCHECK(store_pair != store_map_->end());
-    const std::unique_ptr<V4Store>& store = store_pair->second;
-    HashPrefix hash_prefix = store->GetMatchingHashPrefix(full_hash);
-    if (!hash_prefix.empty()) {
-      matched_store_and_hash_prefixes->emplace_back(identifier, hash_prefix);
-    }
-  }
-}
-
-void V4Database::ResetStores(
-    const std::vector<ListIdentifier>& stores_to_reset) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  for (const ListIdentifier& identifier : stores_to_reset) {
-    store_map_->at(identifier)->Reset();
-  }
-}
-
-void V4Database::VerifyChecksum(
-    DatabaseReadyForUpdatesCallback db_ready_for_updates_callback) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  // Make a threadsafe copy of store_map_ w/raw pointers that we can hand to
-  // the DB thread. The V4Stores ptrs are guaranteed to be valid because their
-  // deletion would be sequenced on the DB thread, after this posted task is
-  // serviced.
-  std::vector<std::pair<ListIdentifier, V4Store*>> stores;
-  for (const auto& next_store : *store_map_) {
-    stores.push_back(std::make_pair(next_store.first, next_store.second.get()));
-  }
-
-  base::PostTaskAndReplyWithResult(
-      db_task_runner_.get(), FROM_HERE,
-      base::BindOnce(&VerifyChecksums, stores),
-      base::BindOnce(&V4Database::OnChecksumVerified,
-                     weak_factory_on_io_.GetWeakPtr(),
-                     std::move(db_ready_for_updates_callback)));
-}
-
-void V4Database::OnChecksumVerified(
-    DatabaseReadyForUpdatesCallback db_ready_for_updates_callback,
-    const std::vector<ListIdentifier>& stores_to_reset) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  std::move(db_ready_for_updates_callback).Run(stores_to_reset);
-}
-
-bool V4Database::IsStoreAvailable(const ListIdentifier& identifier) const {
-  const auto& store_pair = store_map_->find(identifier);
-  return (store_pair != store_map_->end()) &&
-         store_pair->second->HasValidData();
-}
-
-void V4Database::RecordFileSizeHistograms() {
-  int64_t db_size = 0;
-  for (const auto& store_map_iter : *store_map_) {
-    const int64_t size =
-        store_map_iter.second->RecordAndReturnFileSize(kV4DatabaseSizeMetric);
-    db_size += size;
-  }
-  const int64_t db_size_kilobytes = static_cast<int64_t>(db_size / 1024);
-  UMA_HISTOGRAM_COUNTS_1M(kV4DatabaseSizeMetric, db_size_kilobytes);
-}
-
-void V4Database::CollectDatabaseInfo(
-    DatabaseManagerInfo::DatabaseInfo* database_info) {
-  // Records the database size in bytes.
-  int64_t db_size = 0;
-
-  for (const auto& store_map_iter : *store_map_) {
-    DatabaseManagerInfo::DatabaseInfo::StoreInfo* store_info =
-        database_info->add_store_info();
-    store_map_iter.second->CollectStoreInfo(store_info, kV4DatabaseSizeMetric);
-    db_size += store_info->file_size_bytes();
-  }
-
-  database_info->set_database_size_bytes(db_size);
-}
-
-ListInfo::ListInfo(const bool fetch_updates,
-                   const std::string& filename,
-                   const ListIdentifier& list_id,
-                   const SBThreatType sb_threat_type)
-    : fetch_updates_(fetch_updates),
-      filename_(filename),
-      list_id_(list_id),
-      sb_threat_type_(sb_threat_type) {
-  DCHECK(!fetch_updates_ || !filename_.empty());
-  DCHECK_NE(SB_THREAT_TYPE_SAFE, sb_threat_type_);
-}
-
-ListInfo::~ListInfo() {}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/db/v4_database.h b/components/safe_browsing/db/v4_database.h
deleted file mode 100644
index 6bb0ae6d..0000000
--- a/components/safe_browsing/db/v4_database.h
+++ /dev/null
@@ -1,245 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_DB_V4_DATABASE_H_
-#define COMPONENTS_SAFE_BROWSING_DB_V4_DATABASE_H_
-
-#include <memory>
-#include <string>
-#include <unordered_map>
-#include <vector>
-
-#include "base/callback.h"
-#include "base/files/file_path.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#include "base/sequenced_task_runner.h"
-#include "base/single_thread_task_runner.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
-#include "components/safe_browsing/db/v4_store.h"
-#include "components/safe_browsing/proto/webui.pb.h"
-
-class TestSafeBrowsingDatabaseHelper;
-
-namespace safe_browsing {
-
-class V4Database;
-
-// Scheduled when the database has been read from disk and is ready to process
-// resource reputation requests.
-using NewDatabaseReadyCallback =
-    base::OnceCallback<void(std::unique_ptr<V4Database>)>;
-
-// Scheduled when the checksum for all the stores in the database has been
-// verified to match the expected value. Stores for which the checksum did not
-// match are passed as the argument and need to be reset.
-using DatabaseReadyForUpdatesCallback =
-    base::OnceCallback<void(const std::vector<ListIdentifier>&)>;
-
-// This callback is scheduled once the database has finished processing the
-// update requests for all stores and is ready to process the next set of update
-// requests.
-using DatabaseUpdatedCallback = base::RepeatingClosure;
-
-// Maps the ListIdentifiers to their corresponding in-memory stores, which
-// contain the hash prefixes for that ListIdentifier as well as manage their
-// storage on disk.
-using StoreMap = std::unordered_map<ListIdentifier, std::unique_ptr<V4Store>>;
-
-// Associates metadata for a list with its ListIdentifier.
-class ListInfo {
- public:
-  ListInfo(const bool fetch_updates,
-           const std::string& filename,
-           const ListIdentifier& list_id,
-           const SBThreatType sb_threat_type);
-  ~ListInfo();
-
-  const ListIdentifier& list_id() const { return list_id_; }
-  const std::string& filename() const { return filename_; }
-  SBThreatType sb_threat_type() const { return sb_threat_type_; }
-  bool fetch_updates() const { return fetch_updates_; }
-
- private:
-  // Whether to fetch and store updates for this list.
-  bool fetch_updates_;
-
-  // The ASCII name of the file on disk. This file is created inside the
-  // user-data directory. For instance, the ListIdentifier could be for URL
-  // expressions for UwS on Windows platform, and the corresponding file on disk
-  // could be named: "UrlUws.store"
-  std::string filename_;
-
-  // The list being read from/written to the disk.
-  ListIdentifier list_id_;
-
-  // The threat type enum value for this store.
-  SBThreatType sb_threat_type_;
-
-  ListInfo() = delete;
-};
-
-using ListInfos = std::vector<ListInfo>;
-
-// Factory for creating V4Database. Tests implement this factory to create fake
-// databases for testing.
-class V4DatabaseFactory {
- public:
-  virtual ~V4DatabaseFactory() {}
-  virtual std::unique_ptr<V4Database> Create(
-      const scoped_refptr<base::SequencedTaskRunner>& db_task_runner,
-      std::unique_ptr<StoreMap> store_map);
-};
-
-// The on-disk databases are shared among all profiles, as it doesn't contain
-// user-specific data. This object is not thread-safe, i.e. all its methods
-// should be used on the same thread that it was created on, unless specified
-// otherwise.
-// The hash-prefixes of each type are managed by a V4Store (including saving to
-// and reading from disk).
-// The V4Database serves as a single place to manage all the V4Stores.
-class V4Database {
- public:
-  // Factory method to create a V4Database. It creates the database on the
-  // provided |db_task_runner| containing stores in |store_file_name_map|. When
-  // the database creation is complete, it runs the NewDatabaseReadyCallback on
-  // the same thread as it was called.
-  static void Create(
-      const scoped_refptr<base::SequencedTaskRunner>& db_task_runner,
-      const base::FilePath& base_path,
-      const ListInfos& list_infos,
-      NewDatabaseReadyCallback new_db_callback);
-
-  // Destroys the provided v4_database on its task_runner since this may be a
-  // long operation.
-  static void Destroy(std::unique_ptr<V4Database> v4_database);
-
-  virtual ~V4Database();
-
-  // Updates the stores with the response received from the SafeBrowsing service
-  // and calls the db_updated_callback when done.
-  void ApplyUpdate(std::unique_ptr<ParsedServerResponse> parsed_server_response,
-                   DatabaseUpdatedCallback db_updated_callback);
-
-  // Returns the current state of each of the stores being managed.
-  std::unique_ptr<StoreStateMap> GetStoreStateMap();
-
-  // Check if all the selected stores are available and populated.
-  // Returns false if any of |stores_to_check| don't have valid data.
-  // A store may be unavailble if either it hasn't yet gotten a proper
-  // full-update (just after install, or corrupted/missing file), or if it's
-  // not supported in this build (i.e. Chromium).
-  virtual bool AreAllStoresAvailable(
-      const StoresToCheck& stores_to_check) const;
-
-  // Check if any of the stores are available and populated.
-  // Returns false if all of |stores_to_check| don't have valid data.
-  virtual bool AreAnyStoresAvailable(
-      const StoresToCheck& stores_to_check) const;
-
-  // Searches for a hash prefix matching the |full_hash| in stores in the
-  // database, filtered by |stores_to_check|, and returns the identifier of the
-  // store along with the matching hash prefix in |matched_hash_prefix_map|.
-  virtual void GetStoresMatchingFullHash(
-      const FullHash& full_hash,
-      const StoresToCheck& stores_to_check,
-      StoreAndHashPrefixes* matched_store_and_full_hashes);
-
-  // Resets the stores in |stores_to_reset| to an empty state. This is done if
-  // the checksum doesn't match the expected value.
-  void ResetStores(const std::vector<ListIdentifier>& stores_to_reset);
-
-  // Schedules verification of the checksum of each store read from disk on task
-  // runner. If the checksum doesn't match, that store is passed to the
-  // |db_ready_for_updates_callback|. At the end,
-  // |db_ready_for_updates_callback| is scheduled (on the same thread as it was
-  // called) to indicate that the database updates can now be scheduled.
-  void VerifyChecksum(
-      DatabaseReadyForUpdatesCallback db_ready_for_updates_callback);
-
-  // Records the size of each of the stores managed by this database, along
-  // with the combined size of all the stores.
-  void RecordFileSizeHistograms();
-
-  // Populates the DatabaseInfo message of the safe_browsing_page proto.
-  void CollectDatabaseInfo(DatabaseManagerInfo::DatabaseInfo* database_info);
-
- protected:
-  V4Database(const scoped_refptr<base::SequencedTaskRunner>& db_task_runner,
-             std::unique_ptr<StoreMap> store_map);
-
-  // The collection of V4Stores, keyed by ListIdentifier.
-  // The map itself lives on the V4Database's parent thread, but its V4Store
-  // objects live on the db_task_runner_thread.
-  // TODO(vakh): Consider writing a container object which encapsulates or
-  // harmonizes thread affinity for the associative container and the data.
-  const std::unique_ptr<StoreMap> store_map_;
-
- private:
-  friend class ::TestSafeBrowsingDatabaseHelper;
-  friend class V4DatabaseFactory;
-  friend class V4EmbeddedTestServerBrowserTest;
-  friend class V4DatabaseTest;
-  friend class V4SafeBrowsingServiceTest;
-  FRIEND_TEST_ALL_PREFIXES(V4DatabaseTest, TestSetupDatabaseWithFakeStores);
-  FRIEND_TEST_ALL_PREFIXES(V4DatabaseTest,
-                           TestSetupDatabaseWithFakeStoresFailsReset);
-  FRIEND_TEST_ALL_PREFIXES(V4DatabaseTest, TestApplyUpdateWithNewStates);
-  FRIEND_TEST_ALL_PREFIXES(V4DatabaseTest, TestApplyUpdateWithNoNewState);
-  FRIEND_TEST_ALL_PREFIXES(V4DatabaseTest, TestApplyUpdateWithEmptyUpdate);
-  FRIEND_TEST_ALL_PREFIXES(V4DatabaseTest, TestApplyUpdateWithInvalidUpdate);
-  FRIEND_TEST_ALL_PREFIXES(V4DatabaseTest, TestSomeStoresMatchFullHash);
-
-  // Factory method to create a V4Database. When the database creation is
-  // complete, it calls the NewDatabaseReadyCallback on |callback_task_runner|.
-  static void CreateOnTaskRunner(
-      const scoped_refptr<base::SequencedTaskRunner>& db_task_runner,
-      const base::FilePath& base_path,
-      const ListInfos& list_infos,
-      const scoped_refptr<base::SingleThreadTaskRunner>& callback_task_runner,
-      NewDatabaseReadyCallback callback);
-
-  // Makes the passed |factory| the factory used to instantiate a V4Database.
-  // Only for tests.
-  static void RegisterDatabaseFactoryForTest(
-      std::unique_ptr<V4DatabaseFactory> factory);
-
-  // Makes the passed |factory| the factory used to instantiate a V4Store. Only
-  // for tests.
-  static void RegisterStoreFactoryForTest(
-      std::unique_ptr<V4StoreFactory> factory);
-
-  // Callback called when a new store has been created and is ready to be used.
-  // This method updates the store_map_ to point to the new store, which causes
-  // the old store to get deleted.
-  void UpdatedStoreReady(ListIdentifier identifier,
-                         std::unique_ptr<V4Store> store);
-
-  // See |VerifyChecksum|.
-  void OnChecksumVerified(
-      DatabaseReadyForUpdatesCallback db_ready_for_updates_callback,
-      const std::vector<ListIdentifier>& stores_to_reset);
-
-  bool IsStoreAvailable(const ListIdentifier& identifier) const;
-
-  const scoped_refptr<base::SequencedTaskRunner> db_task_runner_;
-
-  DatabaseUpdatedCallback db_updated_callback_;
-
-  // The number of stores for which the update request is pending. When this
-  // goes down to 0, that indicates that the database has updated all the stores
-  // that needed updating and is ready for the next update. It should only be
-  // accessed on the IO thread.
-  int pending_store_updates_;
-
-  // Only meant to be dereferenced and invalidated on the IO thread and hence
-  // named. For details, see the comment at the top of weak_ptr.h
-  base::WeakPtrFactory<V4Database> weak_factory_on_io_{this};
-
-  DISALLOW_COPY_AND_ASSIGN(V4Database);
-};
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_DB_V4_DATABASE_H_
diff --git a/components/safe_browsing/db/v4_database_unittest.cc b/components/safe_browsing/db/v4_database_unittest.cc
deleted file mode 100644
index 7d1628a3..0000000
--- a/components/safe_browsing/db/v4_database_unittest.cc
+++ /dev/null
@@ -1,548 +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.
-
-#include <unordered_map>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/debug/leak_annotations.h"
-#include "base/files/scoped_temp_dir.h"
-#include "base/run_loop.h"
-#include "base/test/test_simple_task_runner.h"
-#include "components/safe_browsing/db/v4_database.h"
-#include "components/safe_browsing/db/v4_store.h"
-#include "content/public/test/browser_task_environment.h"
-#include "testing/platform_test.h"
-
-namespace safe_browsing {
-
-class FakeV4Store : public V4Store {
- public:
-  FakeV4Store(const scoped_refptr<base::SequencedTaskRunner>& task_runner,
-              const base::FilePath& store_path,
-              const bool hash_prefix_matches)
-      : V4Store(
-            task_runner,
-            base::FilePath(store_path.value() + FILE_PATH_LITERAL(".store"))),
-        hash_prefix_should_match_(hash_prefix_matches) {}
-
-  HashPrefix GetMatchingHashPrefix(const FullHash& full_hash) override {
-    return hash_prefix_should_match_ ? full_hash : HashPrefix();
-  }
-
-  bool HasValidData() const override { return true; }
-
-  void set_hash_prefix_matches(bool hash_prefix_matches) {
-    hash_prefix_should_match_ = hash_prefix_matches;
-  }
-
- private:
-  bool hash_prefix_should_match_;
-};
-
-// This factory creates a "fake" store. It allows the caller to specify whether
-// the store has a hash prefix matching a full hash. This is used to test the
-// |GetStoresMatchingFullHash()| method in |V4Database|.
-class FakeV4StoreFactory : public V4StoreFactory {
- public:
-  explicit FakeV4StoreFactory(bool hash_prefix_matches)
-      : hash_prefix_should_match_(hash_prefix_matches) {}
-
-  std::unique_ptr<V4Store> CreateV4Store(
-      const scoped_refptr<base::SequencedTaskRunner>& task_runner,
-      const base::FilePath& store_path) override {
-    return std::make_unique<FakeV4Store>(task_runner, store_path,
-                                         hash_prefix_should_match_);
-  }
-
- private:
-  const bool hash_prefix_should_match_;
-};
-
-class V4DatabaseTest : public PlatformTest {
- public:
-  V4DatabaseTest()
-      : task_runner_(new base::TestSimpleTaskRunner),
-        linux_malware_id_(LINUX_PLATFORM, URL, MALWARE_THREAT),
-        win_malware_id_(WINDOWS_PLATFORM, URL, MALWARE_THREAT) {}
-
-  void SetUp() override {
-    PlatformTest::SetUp();
-
-    // Setup a database in a temporary directory.
-    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
-    database_dirname_ = temp_dir_.GetPath().AppendASCII("V4DatabaseTest");
-
-    created_but_not_called_back_ = false;
-    created_and_called_back_ = false;
-    verify_checksum_called_back_ = false;
-
-    callback_db_updated_ = base::BindRepeating(&V4DatabaseTest::DatabaseUpdated,
-                                               base::Unretained(this));
-
-    callback_db_ready_ = base::BindOnce(
-        &V4DatabaseTest::NewDatabaseReadyWithExpectedStorePathsAndIds,
-        base::Unretained(this));
-
-    SetupInfoMapAndExpectedState();
-  }
-
-  void TearDown() override {
-    V4Database::RegisterStoreFactoryForTest(nullptr);
-    PlatformTest::TearDown();
-  }
-
-  void RegisterFactory(bool hash_prefix_matches = true) {
-    V4Database::RegisterStoreFactoryForTest(
-        std::make_unique<FakeV4StoreFactory>(hash_prefix_matches));
-  }
-
-  void SetupInfoMapAndExpectedState() {
-    list_infos_.emplace_back(true, "win_url_malware", win_malware_id_,
-                             SB_THREAT_TYPE_URL_MALWARE);
-    expected_identifiers_.push_back(win_malware_id_);
-    expected_store_paths_.push_back(
-        database_dirname_.AppendASCII("win_url_malware.store"));
-
-    list_infos_.emplace_back(true, "linux_url_malware", linux_malware_id_,
-                             SB_THREAT_TYPE_URL_MALWARE);
-    expected_identifiers_.push_back(linux_malware_id_);
-    expected_store_paths_.push_back(
-        database_dirname_.AppendASCII("linux_url_malware.store"));
-  }
-
-  void DatabaseUpdated() {}
-
-  void NewDatabaseReadyWithExpectedStorePathsAndIds(
-      std::unique_ptr<V4Database> v4_database) {
-    ASSERT_TRUE(v4_database);
-    ASSERT_TRUE(v4_database->store_map_);
-
-    // The following check ensures that the callback was called asynchronously.
-    EXPECT_TRUE(created_but_not_called_back_);
-
-    ASSERT_EQ(expected_store_paths_.size(), v4_database->store_map_->size());
-    ASSERT_EQ(expected_identifiers_.size(), v4_database->store_map_->size());
-    for (size_t i = 0; i < expected_identifiers_.size(); i++) {
-      const auto& expected_identifier = expected_identifiers_[i];
-      const auto& store = (*v4_database->store_map_)[expected_identifier];
-      ASSERT_TRUE(store);
-      const auto& expected_store_path = expected_store_paths_[i];
-      EXPECT_EQ(expected_store_path, store->store_path());
-    }
-
-    EXPECT_FALSE(created_and_called_back_);
-    created_and_called_back_ = true;
-
-    v4_database_ = std::move(v4_database);
-  }
-
-  std::unique_ptr<ParsedServerResponse> CreateFakeServerResponse(
-      StoreStateMap store_state_map,
-      bool use_valid_response_type) {
-    auto parsed_server_response = std::make_unique<ParsedServerResponse>();
-    for (const auto& store_state_iter : store_state_map) {
-      ListIdentifier identifier = store_state_iter.first;
-      auto lur = std::make_unique<ListUpdateResponse>();
-      lur->set_platform_type(identifier.platform_type());
-      lur->set_threat_entry_type(identifier.threat_entry_type());
-      lur->set_threat_type(identifier.threat_type());
-      lur->set_new_client_state(store_state_iter.second);
-      if (use_valid_response_type) {
-        lur->set_response_type(ListUpdateResponse::FULL_UPDATE);
-      } else {
-        lur->set_response_type(ListUpdateResponse::RESPONSE_TYPE_UNSPECIFIED);
-      }
-      parsed_server_response->push_back(std::move(lur));
-    }
-    return parsed_server_response;
-  }
-
-  void VerifyExpectedStoresState(bool expect_new_stores) {
-    const StoreMap* new_store_map = v4_database_->store_map_.get();
-    std::unique_ptr<StoreStateMap> new_store_state_map =
-        v4_database_->GetStoreStateMap();
-    EXPECT_EQ(expected_store_state_map_.size(), new_store_map->size());
-    EXPECT_EQ(expected_store_state_map_.size(), new_store_state_map->size());
-    for (const auto& expected_iter : expected_store_state_map_) {
-      const ListIdentifier& identifier = expected_iter.first;
-      const std::string& state = expected_iter.second;
-      ASSERT_EQ(1u, new_store_map->count(identifier));
-      ASSERT_EQ(1u, new_store_state_map->count(identifier));
-
-      // Verify the expected state in the store map and the state map.
-      EXPECT_EQ(state, new_store_map->at(identifier)->state());
-      EXPECT_EQ(state, new_store_state_map->at(identifier));
-
-      if (expect_new_stores) {
-        // Verify that a new store was created.
-        EXPECT_NE(old_stores_map_.at(identifier),
-                  new_store_map->at(identifier).get());
-      } else {
-        // Verify that NO new store was created.
-        EXPECT_EQ(old_stores_map_.at(identifier),
-                  new_store_map->at(identifier).get());
-      }
-    }
-  }
-
-  void VerifyChecksumCallback(const std::vector<ListIdentifier>& stores) {
-    EXPECT_FALSE(verify_checksum_called_back_);
-    verify_checksum_called_back_ = true;
-  }
-
-  void WaitForTasksOnTaskRunner() {
-    task_runner_->RunPendingTasks();
-    base::RunLoop().RunUntilIdle();
-  }
-
-  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
-  std::unique_ptr<V4Database> v4_database_;
-  base::FilePath database_dirname_;
-  base::ScopedTempDir temp_dir_;
-  content::BrowserTaskEnvironment task_environment_;
-  bool created_but_not_called_back_;
-  bool created_and_called_back_;
-  bool verify_checksum_called_back_;
-  ListInfos list_infos_;
-  std::vector<ListIdentifier> expected_identifiers_;
-  std::vector<base::FilePath> expected_store_paths_;
-  DatabaseUpdatedCallback callback_db_updated_;
-  NewDatabaseReadyCallback callback_db_ready_;
-  StoreStateMap expected_store_state_map_;
-  std::unordered_map<ListIdentifier, V4Store*> old_stores_map_;
-  const ListIdentifier linux_malware_id_, win_malware_id_;
-};
-
-// Test to set up the database with fake stores.
-TEST_F(V4DatabaseTest, TestSetupDatabaseWithFakeStores) {
-  RegisterFactory();
-
-  V4Database::Create(task_runner_, database_dirname_, list_infos_,
-                     std::move(callback_db_ready_));
-  created_but_not_called_back_ = true;
-  WaitForTasksOnTaskRunner();
-  EXPECT_EQ(true, created_and_called_back_);
-}
-
-// Test to check database updates as expected.
-TEST_F(V4DatabaseTest, TestApplyUpdateWithNewStates) {
-  RegisterFactory();
-
-  V4Database::Create(task_runner_, database_dirname_, list_infos_,
-                     std::move(callback_db_ready_));
-  created_but_not_called_back_ = true;
-  WaitForTasksOnTaskRunner();
-
-  // The database has now been created. Time to try to update it.
-  EXPECT_TRUE(v4_database_);
-  const StoreMap* db_stores = v4_database_->store_map_.get();
-  EXPECT_EQ(expected_store_paths_.size(), db_stores->size());
-  for (const auto& store_iter : *db_stores) {
-    V4Store* store = store_iter.second.get();
-    expected_store_state_map_[store_iter.first] = store->state() + "_fake";
-    old_stores_map_[store_iter.first] = store;
-  }
-
-  v4_database_->ApplyUpdate(
-      CreateFakeServerResponse(expected_store_state_map_, true),
-      callback_db_updated_);
-
-  // Wait for the ApplyUpdate callback to get called.
-  WaitForTasksOnTaskRunner();
-
-  VerifyExpectedStoresState(true);
-
-  // Wait for the old stores to get destroyed on task runner.
-  WaitForTasksOnTaskRunner();
-}
-
-// Test to ensure no state updates leads to no store updates.
-TEST_F(V4DatabaseTest, TestApplyUpdateWithNoNewState) {
-  RegisterFactory();
-
-  V4Database::Create(task_runner_, database_dirname_, list_infos_,
-                     std::move(callback_db_ready_));
-  created_but_not_called_back_ = true;
-  WaitForTasksOnTaskRunner();
-
-  // The database has now been created. Time to try to update it.
-  EXPECT_TRUE(v4_database_);
-  const StoreMap* db_stores = v4_database_->store_map_.get();
-  EXPECT_EQ(expected_store_paths_.size(), db_stores->size());
-  for (const auto& store_iter : *db_stores) {
-    V4Store* store = store_iter.second.get();
-    expected_store_state_map_[store_iter.first] = store->state();
-    old_stores_map_[store_iter.first] = store;
-  }
-
-  v4_database_->ApplyUpdate(
-      CreateFakeServerResponse(expected_store_state_map_, true),
-      callback_db_updated_);
-
-  WaitForTasksOnTaskRunner();
-
-  VerifyExpectedStoresState(false);
-}
-
-// Test to ensure no updates leads to no store updates.
-TEST_F(V4DatabaseTest, TestApplyUpdateWithEmptyUpdate) {
-  RegisterFactory();
-
-  V4Database::Create(task_runner_, database_dirname_, list_infos_,
-                     std::move(callback_db_ready_));
-  created_but_not_called_back_ = true;
-  WaitForTasksOnTaskRunner();
-
-  // The database has now been created. Time to try to update it.
-  EXPECT_TRUE(v4_database_);
-  const StoreMap* db_stores = v4_database_->store_map_.get();
-  EXPECT_EQ(expected_store_paths_.size(), db_stores->size());
-  for (const auto& store_iter : *db_stores) {
-    V4Store* store = store_iter.second.get();
-    expected_store_state_map_[store_iter.first] = store->state();
-    old_stores_map_[store_iter.first] = store;
-  }
-
-  auto parsed_server_response = std::make_unique<ParsedServerResponse>();
-  v4_database_->ApplyUpdate(std::move(parsed_server_response),
-                            callback_db_updated_);
-
-  WaitForTasksOnTaskRunner();
-
-  VerifyExpectedStoresState(false);
-}
-
-// Test to ensure invalid update leads to no store changes.
-TEST_F(V4DatabaseTest, TestApplyUpdateWithInvalidUpdate) {
-  RegisterFactory();
-
-  V4Database::Create(task_runner_, database_dirname_, list_infos_,
-                     std::move(callback_db_ready_));
-  created_but_not_called_back_ = true;
-  WaitForTasksOnTaskRunner();
-
-  // The database has now been created. Time to try to update it.
-  EXPECT_TRUE(v4_database_);
-  const StoreMap* db_stores = v4_database_->store_map_.get();
-  EXPECT_EQ(expected_store_paths_.size(), db_stores->size());
-  for (const auto& store_iter : *db_stores) {
-    V4Store* store = store_iter.second.get();
-    expected_store_state_map_[store_iter.first] = store->state();
-    old_stores_map_[store_iter.first] = store;
-  }
-
-  v4_database_->ApplyUpdate(
-      CreateFakeServerResponse(expected_store_state_map_, false),
-      callback_db_updated_);
-  WaitForTasksOnTaskRunner();
-
-  VerifyExpectedStoresState(false);
-}
-
-// Test to ensure the case that all stores match a given full hash.
-TEST_F(V4DatabaseTest, TestAllStoresMatchFullHash) {
-  bool hash_prefix_matches = true;
-  RegisterFactory(hash_prefix_matches);
-
-  V4Database::Create(task_runner_, database_dirname_, list_infos_,
-                     std::move(callback_db_ready_));
-  created_but_not_called_back_ = true;
-  WaitForTasksOnTaskRunner();
-  EXPECT_EQ(true, created_and_called_back_);
-
-  StoresToCheck stores_to_check({linux_malware_id_, win_malware_id_});
-  StoreAndHashPrefixes store_and_hash_prefixes;
-  v4_database_->GetStoresMatchingFullHash("anything", stores_to_check,
-                                          &store_and_hash_prefixes);
-  EXPECT_EQ(2u, store_and_hash_prefixes.size());
-  StoresToCheck stores_found;
-  for (const auto& it : store_and_hash_prefixes) {
-    stores_found.insert(it.list_id);
-  }
-  EXPECT_EQ(stores_to_check, stores_found);
-}
-
-// Test to ensure the case that no stores match a given full hash.
-TEST_F(V4DatabaseTest, TestNoStoreMatchesFullHash) {
-  bool hash_prefix_matches = false;
-  RegisterFactory(hash_prefix_matches);
-
-  V4Database::Create(task_runner_, database_dirname_, list_infos_,
-                     std::move(callback_db_ready_));
-  created_but_not_called_back_ = true;
-  WaitForTasksOnTaskRunner();
-  EXPECT_EQ(true, created_and_called_back_);
-
-  StoreAndHashPrefixes store_and_hash_prefixes;
-  v4_database_->GetStoresMatchingFullHash(
-      "anything", StoresToCheck({linux_malware_id_, win_malware_id_}),
-      &store_and_hash_prefixes);
-  EXPECT_TRUE(store_and_hash_prefixes.empty());
-}
-
-// Test to ensure the case that some stores match a given full hash.
-TEST_F(V4DatabaseTest, TestSomeStoresMatchFullHash) {
-  // Setup stores to not match the full hash.
-  bool hash_prefix_matches = false;
-  RegisterFactory(hash_prefix_matches);
-
-  V4Database::Create(task_runner_, database_dirname_, list_infos_,
-                     std::move(callback_db_ready_));
-  created_but_not_called_back_ = true;
-  WaitForTasksOnTaskRunner();
-  EXPECT_EQ(true, created_and_called_back_);
-
-  // Set the store corresponding to linux_malware_id_ to match the full hash.
-  FakeV4Store* store = static_cast<FakeV4Store*>(
-      v4_database_->store_map_->at(win_malware_id_).get());
-  store->set_hash_prefix_matches(true);
-
-  StoreAndHashPrefixes store_and_hash_prefixes;
-  v4_database_->GetStoresMatchingFullHash(
-      "anything", StoresToCheck({linux_malware_id_, win_malware_id_}),
-      &store_and_hash_prefixes);
-  EXPECT_EQ(1u, store_and_hash_prefixes.size());
-  EXPECT_EQ(store_and_hash_prefixes.begin()->list_id, win_malware_id_);
-  EXPECT_FALSE(store_and_hash_prefixes.begin()->hash_prefix.empty());
-}
-
-// Test to ensure the case that only some stores are reported to match a given
-// full hash because of StoresToCheck.
-TEST_F(V4DatabaseTest, TestSomeStoresMatchFullHashBecauseOfStoresToMatch) {
-  // Setup all stores to match the full hash.
-  bool hash_prefix_matches = true;
-  RegisterFactory(hash_prefix_matches);
-
-  V4Database::Create(task_runner_, database_dirname_, list_infos_,
-                     std::move(callback_db_ready_));
-  created_but_not_called_back_ = true;
-  WaitForTasksOnTaskRunner();
-  EXPECT_EQ(true, created_and_called_back_);
-
-  // Don't add win_malware_id_ to the StoresToCheck.
-  StoreAndHashPrefixes store_and_hash_prefixes;
-  v4_database_->GetStoresMatchingFullHash(
-      "anything", StoresToCheck({linux_malware_id_}), &store_and_hash_prefixes);
-  EXPECT_EQ(1u, store_and_hash_prefixes.size());
-  EXPECT_EQ(store_and_hash_prefixes.begin()->list_id, linux_malware_id_);
-  EXPECT_FALSE(store_and_hash_prefixes.begin()->hash_prefix.empty());
-}
-
-TEST_F(V4DatabaseTest, VerifyChecksumCalledAsync) {
-  bool hash_prefix_matches = true;
-  RegisterFactory(hash_prefix_matches);
-
-  V4Database::Create(task_runner_, database_dirname_, list_infos_,
-                     std::move(callback_db_ready_));
-  created_but_not_called_back_ = true;
-  WaitForTasksOnTaskRunner();
-  EXPECT_EQ(true, created_and_called_back_);
-
-  // verify_checksum_called_back_ set to false in the constructor.
-  EXPECT_FALSE(verify_checksum_called_back_);
-  // Now call VerifyChecksum and pass the callback that sets
-  // verify_checksum_called_back_ to true.
-  v4_database_->VerifyChecksum(base::BindOnce(
-      &V4DatabaseTest::VerifyChecksumCallback, base::Unretained(this)));
-  // verify_checksum_called_back_ should still be false since the checksum
-  // verification is async.
-  EXPECT_FALSE(verify_checksum_called_back_);
-  WaitForTasksOnTaskRunner();
-  EXPECT_TRUE(verify_checksum_called_back_);
-}
-
-TEST_F(V4DatabaseTest, VerifyChecksumCancelled) {
-  bool hash_prefix_matches = true;
-  RegisterFactory(hash_prefix_matches);
-
-  V4Database::Create(task_runner_, database_dirname_, list_infos_,
-                     std::move(callback_db_ready_));
-  created_but_not_called_back_ = true;
-  WaitForTasksOnTaskRunner();
-  EXPECT_EQ(true, created_and_called_back_);
-
-  EXPECT_FALSE(verify_checksum_called_back_);
-  v4_database_->VerifyChecksum(base::BindOnce(
-      &V4DatabaseTest::VerifyChecksumCallback, base::Unretained(this)));
-  EXPECT_FALSE(verify_checksum_called_back_);
-  // Destroy database.
-  V4Database::Destroy(std::move(v4_database_));
-  WaitForTasksOnTaskRunner();
-  // Callback should not be called since database is destroyed.
-  EXPECT_FALSE(verify_checksum_called_back_);
-}
-
-// Test that we can properly check for unsupported stores
-TEST_F(V4DatabaseTest, TestStoresAvailable) {
-  bool hash_prefix_matches = false;
-  RegisterFactory(hash_prefix_matches);
-
-  V4Database::Create(task_runner_, database_dirname_, list_infos_,
-                     std::move(callback_db_ready_));
-  created_but_not_called_back_ = true;
-  WaitForTasksOnTaskRunner();
-  EXPECT_EQ(true, created_and_called_back_);
-
-  // Doesn't exist in out list
-  const ListIdentifier bogus_id(LINUX_PLATFORM, CHROME_EXTENSION,
-                                CSD_WHITELIST);
-
-  EXPECT_TRUE(v4_database_->AreAllStoresAvailable(
-      StoresToCheck({linux_malware_id_, win_malware_id_})));
-  EXPECT_TRUE(v4_database_->AreAnyStoresAvailable(
-      StoresToCheck({linux_malware_id_, win_malware_id_})));
-
-  EXPECT_TRUE(
-      v4_database_->AreAllStoresAvailable(StoresToCheck({linux_malware_id_})));
-  EXPECT_TRUE(
-      v4_database_->AreAnyStoresAvailable(StoresToCheck({linux_malware_id_})));
-
-  EXPECT_FALSE(v4_database_->AreAllStoresAvailable(
-      StoresToCheck({linux_malware_id_, bogus_id})));
-  EXPECT_TRUE(v4_database_->AreAnyStoresAvailable(
-      StoresToCheck({linux_malware_id_, bogus_id})));
-
-  EXPECT_FALSE(v4_database_->AreAllStoresAvailable(StoresToCheck({bogus_id})));
-}
-
-// Test to ensure that the callback to the database is dropped when the database
-// gets destroyed. See http://crbug.com/683147#c5 for more details.
-TEST_F(V4DatabaseTest, UsingWeakPtrDropsCallback) {
-  RegisterFactory();
-
-  // Step 1: Create the database.
-  V4Database::Create(task_runner_, database_dirname_, list_infos_,
-                     std::move(callback_db_ready_));
-  created_but_not_called_back_ = true;
-  WaitForTasksOnTaskRunner();
-
-  // Step 2: Try to update the database. This posts V4Store::ApplyUpdate on the
-  // task runner.
-  auto parsed_server_response = std::make_unique<ParsedServerResponse>();
-  auto lur = std::make_unique<ListUpdateResponse>();
-  lur->set_platform_type(linux_malware_id_.platform_type());
-  lur->set_threat_entry_type(linux_malware_id_.threat_entry_type());
-  lur->set_threat_type(linux_malware_id_.threat_type());
-  lur->set_new_client_state("new_state");
-  lur->set_response_type(ListUpdateResponse::FULL_UPDATE);
-  parsed_server_response->push_back(std::move(lur));
-
-  // We pass |null_callback| as the second argument to |ApplyUpdate| since we
-  // expect it to not get called. This callback method is called from
-  // V4Database::UpdatedStoreReady but we expect that call to get dropped.
-  v4_database_->ApplyUpdate(std::move(parsed_server_response),
-                            base::NullCallback());
-
-  // Step 3: Before V4Store::ApplyUpdate gets executed on the task runner,
-  // destroy the database. This posts ~V4Database() on the task runner.
-  V4Database::Destroy(std::move(v4_database_));
-
-  // Step 4: Wait for the task runner to go to completion. The test should
-  // finish to completion and the |null_callback| should not get called.
-  WaitForTasksOnTaskRunner();
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/db/v4_embedded_test_server_util.cc b/components/safe_browsing/db/v4_embedded_test_server_util.cc
deleted file mode 100644
index d555520..0000000
--- a/components/safe_browsing/db/v4_embedded_test_server_util.cc
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/db/v4_embedded_test_server_util.h"
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/base64.h"
-#include "base/base64url.h"
-#include "base/bind.h"
-#include "base/logging.h"
-#include "components/safe_browsing/db/util.h"
-#include "components/safe_browsing/db/v4_test_util.h"
-#include "net/base/url_util.h"
-#include "net/test/embedded_test_server/embedded_test_server.h"
-#include "net/test/embedded_test_server/http_request.h"
-#include "net/test/embedded_test_server/http_response.h"
-#include "net/test/embedded_test_server/request_handler_util.h"
-
-namespace safe_browsing {
-
-namespace {
-
-// This method parses a request URL and returns a vector of HashPrefixes that
-// were being requested. It does this by:
-// 1. Finding the "req" query param.
-// 2. Base64 decoding it.
-// 3. Parsing the FindFullHashesRequest from the decoded string.
-std::vector<HashPrefix> GetPrefixesForRequest(const GURL& url) {
-  // Find the "req" query param.
-  std::string req;
-  bool success = net::GetValueForKeyInQuery(url, "$req", &req);
-  DCHECK(success) << "Requests to fullHashes:find should include the req param";
-
-  // Base64 decode it.
-  std::string decoded_output;
-  success = base::Base64UrlDecode(
-      req, base::Base64UrlDecodePolicy::REQUIRE_PADDING, &decoded_output);
-  DCHECK(success);
-
-  // Parse the FindFullHashRequest from the decoded output.
-  FindFullHashesRequest full_hash_req;
-  success = full_hash_req.ParseFromString(decoded_output);
-  DCHECK(success);
-
-  // Extract HashPrefixes from the request proto.
-  const ThreatInfo& info = full_hash_req.threat_info();
-  std::vector<HashPrefix> prefixes;
-  for (int i = 0; i < info.threat_entries_size(); ++i) {
-    prefixes.push_back(info.threat_entries(i).hash());
-  }
-  return prefixes;
-}
-
-// This function listens for requests to /v4/fullHashes:find, and responds with
-// predetermined responses.
-std::unique_ptr<net::test_server::HttpResponse> HandleFullHashRequest(
-    const std::map<GURL, ThreatMatch>& response_map,
-    const std::map<GURL, base::TimeDelta>& delay_map,
-    const net::test_server::HttpRequest& request) {
-  if (!(net::test_server::ShouldHandle(request, "/v4/fullHashes:find")))
-    return nullptr;
-  FindFullHashesResponse find_full_hashes_response;
-  find_full_hashes_response.mutable_negative_cache_duration()->set_seconds(600);
-
-  // Mock a response based on |response_map| and the prefixes scraped from the
-  // request URL.
-  //
-  // This loops through all prefixes requested, and finds all of the full hashes
-  // that match the prefix.
-  std::vector<HashPrefix> request_prefixes =
-      GetPrefixesForRequest(request.GetURL());
-  const base::TimeDelta* delay = nullptr;
-  for (const HashPrefix& prefix : request_prefixes) {
-    for (const auto& response : response_map) {
-      FullHash full_hash = V4ProtocolManagerUtil::GetFullHash(response.first);
-      if (V4ProtocolManagerUtil::FullHashMatchesHashPrefix(full_hash, prefix)) {
-        ThreatMatch* match = find_full_hashes_response.add_matches();
-        *match = response.second;
-        auto it = delay_map.find(response.first);
-        if (it != delay_map.end()) {
-          delay = &(it->second);
-        }
-      }
-    }
-  }
-
-  std::string serialized_response;
-  find_full_hashes_response.SerializeToString(&serialized_response);
-
-  auto http_response =
-      (delay ? std::make_unique<net::test_server::DelayedHttpResponse>(*delay)
-             : std::make_unique<net::test_server::BasicHttpResponse>());
-  http_response->set_content(serialized_response);
-  return http_response;
-}
-
-}  // namespace
-
-void StartRedirectingV4RequestsForTesting(
-    const std::map<GURL, ThreatMatch>& response_map,
-    net::test_server::EmbeddedTestServer* embedded_test_server,
-    const std::map<GURL, base::TimeDelta>& delay_map) {
-  // Static so accessing the underlying buffer won't cause use-after-free.
-  static std::string url_prefix;
-  url_prefix = embedded_test_server->GetURL("/v4").spec();
-  SetSbV4UrlPrefixForTesting(url_prefix.c_str());
-  embedded_test_server->RegisterRequestHandler(
-      base::BindRepeating(&HandleFullHashRequest, response_map, delay_map));
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/db/v4_embedded_test_server_util.h b/components/safe_browsing/db/v4_embedded_test_server_util.h
deleted file mode 100644
index 14183a8..0000000
--- a/components/safe_browsing/db/v4_embedded_test_server_util.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_DB_V4_EMBEDDED_TEST_SERVER_UTIL_H_
-#define COMPONENTS_SAFE_BROWSING_DB_V4_EMBEDDED_TEST_SERVER_UTIL_H_
-
-#include <map>
-
-#include "base/time/time.h"
-#include "components/safe_browsing/db/safebrowsing.pb.h"
-#include "url/gurl.h"
-
-namespace net {
-namespace test_server {
-class EmbeddedTestServer;
-}
-}  // namespace net
-
-namespace safe_browsing {
-
-// This method does three things:
-// 1. Rewrites the global V4 server URL prefix to point to the test server.
-// 2. Registers the FullHash request handler with the server.
-// 3. (Optionally) associates some delay with the resulting http response.
-void StartRedirectingV4RequestsForTesting(
-    const std::map<GURL, ThreatMatch>& response_map,
-    net::test_server::EmbeddedTestServer* embedded_test_server,
-    const std::map<GURL, base::TimeDelta>& delay_map =
-        std::map<GURL, base::TimeDelta>());
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_DB_V4_EMBEDDED_TEST_SERVER_UTIL_H_
diff --git a/components/safe_browsing/db/v4_get_hash_protocol_manager.cc b/components/safe_browsing/db/v4_get_hash_protocol_manager.cc
deleted file mode 100644
index 23f0f5dd..0000000
--- a/components/safe_browsing/db/v4_get_hash_protocol_manager.cc
+++ /dev/null
@@ -1,872 +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.
-
-#include "components/safe_browsing/db/v4_get_hash_protocol_manager.h"
-
-#include <utility>
-
-#include "base/base64url.h"
-#include "base/bind.h"
-#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/stl_util.h"
-#include "base/strings/string_split.h"
-#include "base/timer/timer.h"
-#include "base/trace_event/trace_event.h"
-#include "base/trace_event/traced_value.h"
-#include "content/public/browser/browser_thread.h"
-#include "net/base/load_flags.h"
-#include "net/http/http_response_headers.h"
-#include "net/http/http_status_code.h"
-#include "net/traffic_annotation/network_traffic_annotation.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "services/network/public/cpp/simple_url_loader.h"
-
-using base::Time;
-using base::TimeDelta;
-using content::BrowserThread;
-
-namespace {
-
-// Record a GetHash result.
-void RecordGetHashResult(safe_browsing::V4OperationResult result) {
-  UMA_HISTOGRAM_ENUMERATION(
-      "SafeBrowsing.V4GetHash.Result", result,
-      safe_browsing::V4OperationResult::OPERATION_RESULT_MAX);
-}
-
-// Enumerate parsing failures for histogramming purposes.  DO NOT CHANGE
-// THE ORDERING OF THESE VALUES.
-enum ParseResultType {
-  // Error parsing the protocol buffer from a string.
-  PARSE_FROM_STRING_ERROR = 0,
-
-  // A match in the response had an unexpected THREAT_ENTRY_TYPE.
-  UNEXPECTED_THREAT_ENTRY_TYPE_ERROR = 1,
-
-  // A match in the response had an unexpected THREAT_TYPE.
-  UNEXPECTED_THREAT_TYPE_ERROR = 2,
-
-  // A match in the response had an unexpected PLATFORM_TYPE.
-  UNEXPECTED_PLATFORM_TYPE_ERROR = 3,
-
-  // A match in the response contained no metadata where metadata was
-  // expected.
-  NO_METADATA_ERROR = 4,
-
-  // A match in the response contained a ThreatType that was inconsistent
-  // with the other matches.
-  INCONSISTENT_THREAT_TYPE_ERROR = 5,
-
-  // A match in the response contained a metadata, but the metadata is invalid.
-  UNEXPECTED_METADATA_VALUE_ERROR = 6,
-
-  // A match in the response had no information in the threat field.
-  NO_THREAT_ERROR = 7,
-
-  // Memory space for histograms is determined by the max.  ALWAYS
-  // ADD NEW VALUES BEFORE THIS ONE.
-  PARSE_RESULT_TYPE_MAX = 8,
-};
-
-// Record parsing errors of a GetHash result.
-void RecordParseGetHashResult(ParseResultType result_type) {
-  UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.V4GetHash.Parse.Result", result_type,
-                            PARSE_RESULT_TYPE_MAX);
-}
-
-// Enumerate full hash cache hits/misses for histogramming purposes.
-// DO NOT CHANGE THE ORDERING OF THESE VALUES.
-enum V4FullHashCacheResultType {
-  // Full hashes for which there is no cache hit.
-  FULL_HASH_CACHE_MISS = 0,
-
-  // Full hashes with a cache hit.
-  FULL_HASH_CACHE_HIT = 1,
-
-  // Full hashes with a negative cache hit.
-  FULL_HASH_NEGATIVE_CACHE_HIT = 2,
-
-  // Memory space for histograms is determined by the max. ALWAYS
-  // ADD NEW VALUES BEFORE THIS ONE.
-  FULL_HASH_CACHE_RESULT_MAX
-};
-
-// Record a full hash cache hit result.
-void RecordV4FullHashCacheResult(V4FullHashCacheResultType result_type) {
-  UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.V4GetHash.CacheHit.Result",
-                            result_type, FULL_HASH_CACHE_RESULT_MAX);
-}
-
-// Enumerate GetHash hits/misses for histogramming purposes. DO NOT CHANGE THE
-// ORDERING OF THESE VALUES.
-enum V4GetHashCheckResultType {
-  // Successful responses which returned no full hashes.
-  GET_HASH_CHECK_EMPTY = 0,
-
-  // Successful responses for which one or more of the full hashes matched.
-  GET_HASH_CHECK_HIT = 1,
-
-  // Successful responses which weren't empty but have no matches.
-  GET_HASH_CHECK_MISS = 2,
-
-  // Memory space for histograms is determined by the max. ALWAYS
-  // ADD NEW VALUES BEFORE THIS ONE.
-  GET_HASH_CHECK_RESULT_MAX
-};
-
-// Record a GetHash hit result.
-void RecordV4GetHashCheckResult(V4GetHashCheckResultType result_type) {
-  UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.V4GetHash.Check.Result", result_type,
-                            GET_HASH_CHECK_RESULT_MAX);
-}
-
-const char kPermission[] = "permission";
-const char kPhaPatternType[] = "pha_pattern_type";
-const char kMalwareThreatType[] = "malware_threat_type";
-const char kSePatternType[] = "se_pattern_type";
-const char kLanding[] = "LANDING";
-const char kDistribution[] = "DISTRIBUTION";
-const char kSocialEngineeringAds[] = "SOCIAL_ENGINEERING_ADS";
-const char kSocialEngineeringLanding[] = "SOCIAL_ENGINEERING_LANDING";
-const char kPhishing[] = "PHISHING";
-
-}  // namespace
-
-namespace safe_browsing {
-
-// The default V4GetHashProtocolManagerFactory.
-class V4GetHashProtocolManagerFactoryImpl
-    : public V4GetHashProtocolManagerFactory {
- public:
-  V4GetHashProtocolManagerFactoryImpl() {}
-  ~V4GetHashProtocolManagerFactoryImpl() override {}
-  std::unique_ptr<V4GetHashProtocolManager> CreateProtocolManager(
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      const StoresToCheck& stores_to_check,
-      const V4ProtocolConfig& config) override {
-    return base::WrapUnique(new V4GetHashProtocolManager(
-        url_loader_factory, stores_to_check, config));
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(V4GetHashProtocolManagerFactoryImpl);
-};
-
-// ----------------------------------------------------------------
-
-CachedHashPrefixInfo::CachedHashPrefixInfo() {}
-
-CachedHashPrefixInfo::CachedHashPrefixInfo(const CachedHashPrefixInfo& other) =
-    default;
-
-CachedHashPrefixInfo::~CachedHashPrefixInfo() {}
-
-// ----------------------------------------------------------------
-
-FullHashCallbackInfo::FullHashCallbackInfo() {}
-
-FullHashCallbackInfo::FullHashCallbackInfo(
-    const std::vector<FullHashInfo>& cached_full_hash_infos,
-    const std::vector<HashPrefix>& prefixes_requested,
-    std::unique_ptr<network::SimpleURLLoader> loader,
-    const FullHashToStoreAndHashPrefixesMap&
-        full_hash_to_store_and_hash_prefixes,
-    FullHashCallback callback,
-    const base::Time& network_start_time)
-    : cached_full_hash_infos(cached_full_hash_infos),
-      callback(std::move(callback)),
-      loader(std::move(loader)),
-      full_hash_to_store_and_hash_prefixes(
-          full_hash_to_store_and_hash_prefixes),
-      network_start_time(network_start_time),
-      prefixes_requested(prefixes_requested) {}
-
-FullHashCallbackInfo::~FullHashCallbackInfo() {}
-
-// ----------------------------------------------------------------
-
-FullHashInfo::FullHashInfo(const FullHash& full_hash,
-                           const ListIdentifier& list_id,
-                           const base::Time& positive_expiry)
-    : full_hash(full_hash),
-      list_id(list_id),
-      positive_expiry(positive_expiry) {}
-
-FullHashInfo::FullHashInfo(const FullHashInfo& other) = default;
-
-FullHashInfo::~FullHashInfo() {}
-
-bool FullHashInfo::operator==(const FullHashInfo& other) const {
-  return full_hash == other.full_hash && list_id == other.list_id &&
-         positive_expiry == other.positive_expiry && metadata == other.metadata;
-}
-
-bool FullHashInfo::operator!=(const FullHashInfo& other) const {
-  return !operator==(other);
-}
-
-// V4GetHashProtocolManager implementation --------------------------------
-
-// static
-V4GetHashProtocolManagerFactory* V4GetHashProtocolManager::factory_ = nullptr;
-
-// static
-std::unique_ptr<V4GetHashProtocolManager> V4GetHashProtocolManager::Create(
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    const StoresToCheck& stores_to_check,
-    const V4ProtocolConfig& config) {
-  if (!factory_)
-    factory_ = new V4GetHashProtocolManagerFactoryImpl();
-  return factory_->CreateProtocolManager(url_loader_factory, stores_to_check,
-                                         config);
-}
-
-// static
-void V4GetHashProtocolManager::RegisterFactory(
-    std::unique_ptr<V4GetHashProtocolManagerFactory> factory) {
-  delete factory_;
-  factory_ = factory.release();
-}
-
-V4GetHashProtocolManager::V4GetHashProtocolManager(
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    const StoresToCheck& stores_to_check,
-    const V4ProtocolConfig& config)
-    : gethash_error_count_(0),
-      gethash_back_off_mult_(1),
-      next_gethash_time_(Time::FromDoubleT(0)),
-      config_(config),
-      url_loader_factory_(url_loader_factory),
-      clock_(base::DefaultClock::GetInstance()) {
-  DCHECK(!stores_to_check.empty());
-  std::set<PlatformType> platform_types;
-  std::set<ThreatEntryType> threat_entry_types;
-  std::set<ThreatType> threat_types;
-  for (const ListIdentifier& store : stores_to_check) {
-    platform_types.insert(store.platform_type());
-    threat_entry_types.insert(store.threat_entry_type());
-    threat_types.insert(store.threat_type());
-  }
-  platform_types_.assign(platform_types.begin(), platform_types.end());
-  threat_entry_types_.assign(threat_entry_types.begin(),
-                             threat_entry_types.end());
-  threat_types_.assign(threat_types.begin(), threat_types.end());
-}
-
-V4GetHashProtocolManager::~V4GetHashProtocolManager() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-}
-
-void V4GetHashProtocolManager::ClearCache() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  full_hash_cache_.clear();
-}
-
-void V4GetHashProtocolManager::GetFullHashes(
-    const FullHashToStoreAndHashPrefixesMap
-        full_hash_to_store_and_hash_prefixes,
-    const std::vector<std::string>& list_client_states,
-    FullHashCallback callback) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(!full_hash_to_store_and_hash_prefixes.empty());
-
-  std::vector<HashPrefix> prefixes_to_request;
-  std::vector<FullHashInfo> cached_full_hash_infos;
-  GetFullHashCachedResults(full_hash_to_store_and_hash_prefixes, Time::Now(),
-                           &prefixes_to_request, &cached_full_hash_infos);
-
-  if (prefixes_to_request.empty()) {
-    // 100% cache hits (positive or negative) so we can call the callback right
-    // away.
-    std::move(callback).Run(cached_full_hash_infos);
-    return;
-  }
-
-  // We need to wait the minimum waiting duration, and if we are in backoff,
-  // we need to check if we're past the next allowed time. If we are, we can
-  // proceed with the request. If not, we are required to return empty results
-  // (i.e. just use the results from cache and potentially report an unsafe
-  // resource as safe).
-  if (clock_->Now() <= next_gethash_time_) {
-    if (gethash_error_count_) {
-      RecordGetHashResult(V4OperationResult::BACKOFF_ERROR);
-    } else {
-      RecordGetHashResult(V4OperationResult::MIN_WAIT_DURATION_ERROR);
-    }
-    std::move(callback).Run(cached_full_hash_infos);
-    return;
-  }
-
-  net::NetworkTrafficAnnotationTag traffic_annotation =
-      net::DefineNetworkTrafficAnnotation("safe_browsing_v4_get_hash", R"(
-        semantics {
-          sender: "Safe Browsing"
-          description:
-            "When Safe Browsing detects that a URL might be dangerous based on "
-            "its local database, it sends a partial hash of that URL to Google "
-            "to verify it before showing a warning to the user. This partial "
-            "hash does not expose the URL to Google."
-          trigger:
-            "When a resource URL matches the local hash-prefix database of "
-            "potential threats (malware, phishing etc), and the full-hash "
-            "result is not already cached, this will be sent."
-          data:
-             "The 32-bit hash prefix of any potentially bad URLs. The URLs "
-             "themselves are not sent."
-          destination: GOOGLE_OWNED_SERVICE
-        }
-        policy {
-          cookies_allowed: YES
-          cookies_store: "Safe Browsing cookie store"
-          setting:
-            "Users can disable Safe Browsing by unchecking 'Protect you and "
-            "your device from dangerous sites' in Chromium settings under "
-            "Privacy. The feature is enabled by default."
-          chrome_policy {
-            SafeBrowsingEnabled {
-              policy_options {mode: MANDATORY}
-              SafeBrowsingEnabled: false
-            }
-          }
-        })");
-  auto resource_request = std::make_unique<network::ResourceRequest>();
-  std::string req_base64 =
-      GetHashRequest(prefixes_to_request, list_client_states);
-  GetHashUrlAndHeaders(req_base64, &resource_request->url,
-                       &resource_request->headers);
-
-  resource_request->load_flags = net::LOAD_DISABLE_CACHE;
-  std::unique_ptr<network::SimpleURLLoader> owned_loader =
-      network::SimpleURLLoader::Create(std::move(resource_request),
-                                       traffic_annotation);
-  network::SimpleURLLoader* loader = owned_loader.get();
-  owned_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
-      url_loader_factory_.get(),
-      base::BindOnce(&V4GetHashProtocolManager::OnURLLoaderComplete,
-                     base::Unretained(this), loader));
-
-  pending_hash_requests_[loader].reset(new FullHashCallbackInfo(
-      cached_full_hash_infos, prefixes_to_request, std::move(owned_loader),
-      full_hash_to_store_and_hash_prefixes, std::move(callback),
-      clock_->Now()));
-  UMA_HISTOGRAM_COUNTS_100("SafeBrowsing.V4GetHash.CountOfPrefixes",
-                           prefixes_to_request.size());
-}
-
-void V4GetHashProtocolManager::GetFullHashesWithApis(
-    const GURL& url,
-    const std::vector<std::string>& list_client_states,
-    ThreatMetadataForApiCallback api_callback) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DCHECK(url.SchemeIs(url::kHttpScheme) || url.SchemeIs(url::kHttpsScheme));
-
-  std::vector<FullHash> full_hashes;
-  V4ProtocolManagerUtil::UrlToFullHashes(url.GetOrigin(), &full_hashes);
-
-  FullHashToStoreAndHashPrefixesMap full_hash_to_store_and_hash_prefixes;
-  for (const FullHash& full_hash : full_hashes) {
-    HashPrefix prefix;
-    bool result =
-        V4ProtocolManagerUtil::FullHashToSmallestHashPrefix(full_hash, &prefix);
-    DCHECK(result);
-    full_hash_to_store_and_hash_prefixes[full_hash].emplace_back(
-        GetChromeUrlApiId(), prefix);
-  }
-
-  GetFullHashes(full_hash_to_store_and_hash_prefixes, list_client_states,
-                base::BindOnce(&V4GetHashProtocolManager::OnFullHashForApi,
-                               base::Unretained(this), std::move(api_callback),
-                               full_hashes));
-}
-
-void V4GetHashProtocolManager::GetFullHashCachedResults(
-    const FullHashToStoreAndHashPrefixesMap&
-        full_hash_to_store_and_hash_prefixes,
-    const Time& now,
-    std::vector<HashPrefix>* prefixes_to_request,
-    std::vector<FullHashInfo>* cached_full_hash_infos) {
-  DCHECK(!full_hash_to_store_and_hash_prefixes.empty());
-  DCHECK(prefixes_to_request->empty());
-  DCHECK(cached_full_hash_infos->empty());
-
-  // Caching behavior is documented here:
-  // https://developers.google.com/safe-browsing/v4/caching#about-caching
-  //
-  // The cache operates as follows:
-  // Lookup:
-  //     Case 1: The prefix is in the cache.
-  //         Case a: The full hash is in the cache.
-  //             Case i : The positive full hash result has not expired.
-  //                      The result is unsafe and we do not need to send a new
-  //                      request.
-  //             Case ii: The positive full hash result has expired.
-  //                      We need to send a request for full hashes.
-  //         Case b: The full hash is not in the cache.
-  //             Case i : The negative cache entry has not expired.
-  //                      The result is still safe and we do not need to send a
-  //                      new request.
-  //             Case ii: The negative cache entry has expired.
-  //                      We need to send a request for full hashes.
-  //     Case 2: The prefix is not in the cache.
-  //             We need to send a request for full hashes.
-  //
-  // Note on eviction:
-  //   CachedHashPrefixInfo entries can be removed from the cache only when
-  //   the negative cache expire time and the cache expire time of all full
-  //   hash results for that prefix have expired.
-  //   Individual full hash results can be removed from the prefix's
-  //   cache entry if they expire AND their expire time is after the negative
-  //   cache expire time.
-
-  std::unordered_set<HashPrefix> unique_prefixes_to_request;
-  for (const auto& it : full_hash_to_store_and_hash_prefixes) {
-    const FullHash& full_hash = it.first;
-    const StoreAndHashPrefixes& matched = it.second;
-    for (const StoreAndHashPrefix& matched_it : matched) {
-      const ListIdentifier& list_id = matched_it.list_id;
-      const HashPrefix& prefix = matched_it.hash_prefix;
-      auto prefix_entry = full_hash_cache_.find(prefix);
-      if (prefix_entry != full_hash_cache_.end()) {
-        // Case 1.
-        const CachedHashPrefixInfo& cached_prefix_info = prefix_entry->second;
-        bool found_full_hash = false;
-        for (const FullHashInfo& full_hash_info :
-             cached_prefix_info.full_hash_infos) {
-          if (full_hash_info.full_hash == full_hash &&
-              full_hash_info.list_id == list_id) {
-            // Case a.
-            found_full_hash = true;
-            number_of_hits_++;
-            if (full_hash_info.positive_expiry > now) {
-              // Case i.
-              cached_full_hash_infos->push_back(full_hash_info);
-              RecordV4FullHashCacheResult(FULL_HASH_CACHE_HIT);
-            } else {
-              // Case ii.
-              unique_prefixes_to_request.insert(prefix);
-              RecordV4FullHashCacheResult(FULL_HASH_CACHE_MISS);
-            }
-            break;
-          }
-        }
-
-        if (!found_full_hash) {
-          // Case b.
-          if (cached_prefix_info.negative_expiry > now) {
-            // Case i.
-            RecordV4FullHashCacheResult(FULL_HASH_NEGATIVE_CACHE_HIT);
-          } else {
-            // Case ii.
-            unique_prefixes_to_request.insert(prefix);
-            RecordV4FullHashCacheResult(FULL_HASH_CACHE_MISS);
-          }
-        }
-      } else {
-        // Case 2.
-        unique_prefixes_to_request.insert(prefix);
-        RecordV4FullHashCacheResult(FULL_HASH_CACHE_MISS);
-      }
-    }
-  }
-
-  prefixes_to_request->insert(prefixes_to_request->begin(),
-                              unique_prefixes_to_request.begin(),
-                              unique_prefixes_to_request.end());
-}
-
-std::string V4GetHashProtocolManager::GetHashRequest(
-    const std::vector<HashPrefix>& prefixes_to_request,
-    const std::vector<std::string>& list_client_states) {
-  DCHECK(!prefixes_to_request.empty());
-
-  FindFullHashesRequest req;
-
-  V4ProtocolManagerUtil::SetClientInfoFromConfig(req.mutable_client(), config_);
-
-  for (const auto& client_state : list_client_states) {
-    req.add_client_states(client_state);
-  }
-
-  ThreatInfo* info = req.mutable_threat_info();
-  for (const PlatformType p : platform_types_) {
-    info->add_platform_types(p);
-  }
-  for (const ThreatEntryType tet : threat_entry_types_) {
-    info->add_threat_entry_types(tet);
-  }
-  for (const ThreatType tt : threat_types_) {
-    info->add_threat_types(tt);
-  }
-  for (const HashPrefix& prefix : prefixes_to_request) {
-    info->add_threat_entries()->set_hash(prefix);
-  }
-
-  // Serialize and Base64 encode.
-  std::string req_data, req_base64;
-  req.SerializeToString(&req_data);
-  base::Base64UrlEncode(req_data, base::Base64UrlEncodePolicy::INCLUDE_PADDING,
-                        &req_base64);
-  return req_base64;
-}
-
-void V4GetHashProtocolManager::GetHashUrlAndHeaders(
-    const std::string& req_base64,
-    GURL* gurl,
-    net::HttpRequestHeaders* headers) const {
-  V4ProtocolManagerUtil::GetRequestUrlAndHeaders(req_base64, "fullHashes:find",
-                                                 config_, gurl, headers);
-}
-
-void V4GetHashProtocolManager::HandleGetHashError(const Time& now) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  TimeDelta next = V4ProtocolManagerUtil::GetNextBackOffInterval(
-      &gethash_error_count_, &gethash_back_off_mult_);
-  next_gethash_time_ = now + next;
-}
-
-void V4GetHashProtocolManager::OnFullHashForApi(
-    ThreatMetadataForApiCallback api_callback,
-    const std::vector<FullHash>& full_hashes,
-    const std::vector<FullHashInfo>& full_hash_infos) {
-  ThreatMetadata md;
-  for (const FullHashInfo& full_hash_info : full_hash_infos) {
-    DCHECK_EQ(GetChromeUrlApiId(), full_hash_info.list_id);
-    DCHECK(base::Contains(full_hashes, full_hash_info.full_hash));
-    md.api_permissions.insert(full_hash_info.metadata.api_permissions.begin(),
-                              full_hash_info.metadata.api_permissions.end());
-  }
-
-  std::move(api_callback).Run(md);
-}
-
-bool V4GetHashProtocolManager::ParseHashResponse(
-    const std::string& response_data,
-    std::vector<FullHashInfo>* full_hash_infos,
-    Time* negative_cache_expire) {
-  FindFullHashesResponse response;
-
-  if (!response.ParseFromString(response_data)) {
-    RecordParseGetHashResult(PARSE_FROM_STRING_ERROR);
-    return false;
-  }
-
-  // negative_cache_duration should always be set.
-  DCHECK(response.has_negative_cache_duration());
-
-  // Seconds resolution is good enough so we ignore the nanos field.
-  *negative_cache_expire =
-      clock_->Now() +
-      TimeDelta::FromSeconds(response.negative_cache_duration().seconds());
-
-  if (response.has_minimum_wait_duration()) {
-    // Seconds resolution is good enough so we ignore the nanos field.
-    next_gethash_time_ =
-        clock_->Now() +
-        TimeDelta::FromSeconds(response.minimum_wait_duration().seconds());
-  }
-
-  for (const ThreatMatch& match : response.matches()) {
-    if (!match.has_platform_type()) {
-      RecordParseGetHashResult(UNEXPECTED_PLATFORM_TYPE_ERROR);
-      return false;
-    }
-    if (!match.has_threat_entry_type()) {
-      RecordParseGetHashResult(UNEXPECTED_THREAT_ENTRY_TYPE_ERROR);
-      return false;
-    }
-    if (!match.has_threat_type()) {
-      RecordParseGetHashResult(UNEXPECTED_THREAT_TYPE_ERROR);
-      return false;
-    }
-    if (!match.has_threat()) {
-      RecordParseGetHashResult(NO_THREAT_ERROR);
-      return false;
-    }
-
-    ListIdentifier list_id(match.platform_type(), match.threat_entry_type(),
-                           match.threat_type());
-    if (!base::Contains(platform_types_, list_id.platform_type()) ||
-        !base::Contains(threat_entry_types_, list_id.threat_entry_type()) ||
-        !base::Contains(threat_types_, list_id.threat_type())) {
-      // The server may send a ThreatMatch response for lists that we didn't ask
-      // for so ignore those ThreatMatch responses.
-      continue;
-    }
-
-    base::Time positive_expiry;
-    if (match.has_cache_duration()) {
-      // Seconds resolution is good enough so we ignore the nanos field.
-      positive_expiry = clock_->Now() + TimeDelta::FromSeconds(
-                                            match.cache_duration().seconds());
-    } else {
-      positive_expiry = clock_->Now() - base::TimeDelta::FromSeconds(1);
-    }
-    FullHashInfo full_hash_info(match.threat().hash(), list_id,
-                                positive_expiry);
-    ParseMetadata(match, &full_hash_info.metadata);
-    TRACE_EVENT2("safe_browsing", "V4GetHashProtocolManager::ParseHashResponse",
-                 "threat_type", full_hash_info.list_id.threat_type(),
-                 "metadata", full_hash_info.metadata.ToTracedValue());
-    full_hash_infos->push_back(full_hash_info);
-  }
-  return true;
-}
-
-// static
-void V4GetHashProtocolManager::ParseMetadata(const ThreatMatch& match,
-                                             ThreatMetadata* metadata) {
-  // Different threat types will handle the metadata differently.
-  if (match.threat_type() == API_ABUSE) {
-    if (!match.has_platform_type()) {
-      RecordParseGetHashResult(UNEXPECTED_PLATFORM_TYPE_ERROR);
-      return;
-    }
-
-    if (!match.has_threat_entry_metadata()) {
-      RecordParseGetHashResult(NO_METADATA_ERROR);
-      return;
-    }
-    // For API Abuse, store a list of the returned permissions.
-    for (const ThreatEntryMetadata::MetadataEntry& m :
-         match.threat_entry_metadata().entries()) {
-      if (m.key() != kPermission) {
-        RecordParseGetHashResult(UNEXPECTED_METADATA_VALUE_ERROR);
-        return;
-      }
-      metadata->api_permissions.insert(m.value());
-    }
-  } else if (match.threat_type() == MALWARE_THREAT ||
-             match.threat_type() == POTENTIALLY_HARMFUL_APPLICATION) {
-    for (const ThreatEntryMetadata::MetadataEntry& m :
-         match.threat_entry_metadata().entries()) {
-      if (m.key() == kPhaPatternType || m.key() == kMalwareThreatType) {
-        if (m.value() == kLanding) {
-          metadata->threat_pattern_type = ThreatPatternType::MALWARE_LANDING;
-          break;
-        } else if (m.value() == kDistribution) {
-          metadata->threat_pattern_type =
-              ThreatPatternType::MALWARE_DISTRIBUTION;
-          break;
-        } else {
-          RecordParseGetHashResult(UNEXPECTED_METADATA_VALUE_ERROR);
-          return;
-        }
-      }
-    }
-  } else if (match.threat_type() == SOCIAL_ENGINEERING) {
-    for (const ThreatEntryMetadata::MetadataEntry& m :
-         match.threat_entry_metadata().entries()) {
-      if (m.key() == kSePatternType) {
-        if (m.value() == kSocialEngineeringAds) {
-          metadata->threat_pattern_type =
-              ThreatPatternType::SOCIAL_ENGINEERING_ADS;
-          break;
-        } else if (m.value() == kSocialEngineeringLanding) {
-          metadata->threat_pattern_type =
-              ThreatPatternType::SOCIAL_ENGINEERING_LANDING;
-          break;
-        } else if (m.value() == kPhishing) {
-          metadata->threat_pattern_type = ThreatPatternType::PHISHING;
-          break;
-        } else {
-          RecordParseGetHashResult(UNEXPECTED_METADATA_VALUE_ERROR);
-          return;
-        }
-      }
-    }
-  } else if (match.threat_type() == SUBRESOURCE_FILTER) {
-    for (const ThreatEntryMetadata::MetadataEntry& m :
-         match.threat_entry_metadata().entries()) {
-      // Anything other than "warn" is interpreted as enforce, which should be
-      // more common (and therefore leaves us open to shorten it in the future).
-      auto get_enforcement = [](const std::string& value) {
-        return value == "warn" ? SubresourceFilterLevel::WARN
-                               : SubresourceFilterLevel::ENFORCE;
-      };
-      if (m.key() == "sf_absv") {
-        metadata->subresource_filter_match[SubresourceFilterType::ABUSIVE] =
-            get_enforcement(m.value());
-      } else if (m.key() == "sf_bas") {
-        metadata->subresource_filter_match[SubresourceFilterType::BETTER_ADS] =
-            get_enforcement(m.value());
-      }
-    }
-  } else if (match.has_threat_entry_metadata() &&
-             match.threat_entry_metadata().entries_size() > 1) {
-    RecordParseGetHashResult(UNEXPECTED_THREAT_TYPE_ERROR);
-  }
-}
-
-void V4GetHashProtocolManager::ResetGetHashErrors() {
-  gethash_error_count_ = 0;
-  gethash_back_off_mult_ = 1;
-  next_gethash_time_ = base::Time();
-}
-
-void V4GetHashProtocolManager::SetClockForTests(base::Clock* clock) {
-  clock_ = clock;
-}
-
-void V4GetHashProtocolManager::UpdateCache(
-    const std::vector<HashPrefix>& prefixes_requested,
-    const std::vector<FullHashInfo>& full_hash_infos,
-    const Time& negative_cache_expire) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  // If negative_cache_expire is null, don't cache the results since it's not
-  // clear till what time they should be considered valid.
-  if (negative_cache_expire.is_null()) {
-    return;
-  }
-
-  for (const HashPrefix& prefix : prefixes_requested) {
-    // Create or reset the cached result for this prefix.
-    CachedHashPrefixInfo& chpi = full_hash_cache_[prefix];
-    chpi.full_hash_infos.clear();
-    chpi.negative_expiry = negative_cache_expire;
-
-    for (const FullHashInfo& full_hash_info : full_hash_infos) {
-      if (V4ProtocolManagerUtil::FullHashMatchesHashPrefix(
-              full_hash_info.full_hash, prefix)) {
-        chpi.full_hash_infos.push_back(full_hash_info);
-      }
-    }
-  }
-}
-
-void V4GetHashProtocolManager::MergeResults(
-    const FullHashToStoreAndHashPrefixesMap&
-        full_hash_to_store_and_hash_prefixes,
-    const std::vector<FullHashInfo>& full_hash_infos,
-    std::vector<FullHashInfo>* merged_full_hash_infos) {
-  bool get_hash_hit = false;
-  for (const FullHashInfo& fhi : full_hash_infos) {
-    auto it = full_hash_to_store_and_hash_prefixes.find(fhi.full_hash);
-    if (full_hash_to_store_and_hash_prefixes.end() != it) {
-      for (const StoreAndHashPrefix& sahp : it->second) {
-        if (fhi.list_id == sahp.list_id) {
-          merged_full_hash_infos->push_back(fhi);
-          get_hash_hit = true;
-          break;
-        }
-      }
-    }
-  }
-
-  if (get_hash_hit) {
-    RecordV4GetHashCheckResult(GET_HASH_CHECK_HIT);
-  } else if (full_hash_infos.empty()) {
-    RecordV4GetHashCheckResult(GET_HASH_CHECK_EMPTY);
-  } else {
-    RecordV4GetHashCheckResult(GET_HASH_CHECK_MISS);
-  }
-}
-
-// SafeBrowsing request responses are handled here.
-void V4GetHashProtocolManager::OnURLLoaderComplete(
-    network::SimpleURLLoader* url_loader,
-    std::unique_ptr<std::string> response_body) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  int response_code = 0;
-  if (url_loader->ResponseInfo() && url_loader->ResponseInfo()->headers)
-    response_code = url_loader->ResponseInfo()->headers->response_code();
-
-  std::string data;
-  if (response_body)
-    data = *response_body;
-
-  OnURLLoaderCompleteInternal(url_loader, url_loader->NetError(), response_code,
-                              data);
-}
-
-void V4GetHashProtocolManager::OnURLLoaderCompleteInternal(
-    network::SimpleURLLoader* url_loader,
-    int net_error,
-    int response_code,
-    const std::string& data) {
-  auto it = pending_hash_requests_.find(url_loader);
-  DCHECK(it != pending_hash_requests_.end()) << "Request not found";
-  V4ProtocolManagerUtil::RecordHttpResponseOrErrorCode(
-      "SafeBrowsing.V4GetHash.Network.Result", net_error, response_code);
-
-  std::vector<FullHashInfo> full_hash_infos;
-  Time negative_cache_expire;
-  if (net_error == net::OK && response_code == net::HTTP_OK) {
-    RecordGetHashResult(V4OperationResult::STATUS_200);
-    ResetGetHashErrors();
-    if (!ParseHashResponse(data, &full_hash_infos, &negative_cache_expire)) {
-      full_hash_infos.clear();
-      RecordGetHashResult(V4OperationResult::PARSE_ERROR);
-    }
-  } else {
-    HandleGetHashError(clock_->Now());
-
-    DVLOG(1) << "SafeBrowsing GetEncodedFullHashes request for: "
-             << url_loader->GetFinalURL() << " failed with error: " << net_error
-             << " and response code: " << response_code;
-
-    if (net_error != net::OK) {
-      RecordGetHashResult(V4OperationResult::NETWORK_ERROR);
-    } else {
-      RecordGetHashResult(V4OperationResult::HTTP_ERROR);
-    }
-  }
-
-  const std::unique_ptr<FullHashCallbackInfo>& fhci = it->second;
-  UMA_HISTOGRAM_LONG_TIMES("SafeBrowsing.V4GetHash.Network.Time",
-                           clock_->Now() - fhci->network_start_time);
-  UpdateCache(fhci->prefixes_requested, full_hash_infos, negative_cache_expire);
-  MergeResults(fhci->full_hash_to_store_and_hash_prefixes, full_hash_infos,
-               &fhci->cached_full_hash_infos);
-
-  std::move(fhci->callback).Run(fhci->cached_full_hash_infos);
-
-  pending_hash_requests_.erase(it);
-}
-
-void V4GetHashProtocolManager::CollectFullHashCacheInfo(
-    FullHashCacheInfo* full_hash_cache_info) {
-  full_hash_cache_info->set_number_of_hits(number_of_hits_);
-
-  for (const auto& it : full_hash_cache_) {
-    FullHashCacheInfo::FullHashCache* full_hash_cache =
-        full_hash_cache_info->add_full_hash_cache();
-    full_hash_cache->set_hash_prefix(it.first);
-    full_hash_cache->mutable_cached_hash_prefix_info()->set_negative_expiry(
-        it.second.negative_expiry.ToJavaTime());
-
-    for (const auto& full_hash_infos_it : it.second.full_hash_infos) {
-      FullHashCacheInfo::FullHashCache::CachedHashPrefixInfo::FullHashInfo*
-          full_hash_info = full_hash_cache->mutable_cached_hash_prefix_info()
-                               ->add_full_hash_info();
-      full_hash_info->set_positive_expiry(
-          full_hash_infos_it.positive_expiry.ToJavaTime());
-      full_hash_info->set_full_hash(full_hash_infos_it.full_hash);
-
-      full_hash_info->mutable_list_identifier()->set_platform_type(
-          static_cast<int>(full_hash_infos_it.list_id.platform_type()));
-      full_hash_info->mutable_list_identifier()->set_threat_entry_type(
-          static_cast<int>(full_hash_infos_it.list_id.threat_entry_type()));
-      full_hash_info->mutable_list_identifier()->set_threat_type(
-          static_cast<int>(full_hash_infos_it.list_id.threat_type()));
-    }
-  }
-}
-
-#ifndef DEBUG
-std::ostream& operator<<(std::ostream& os, const FullHashInfo& fhi) {
-  os << "{full_hash: " << fhi.full_hash << "; list_id: " << fhi.list_id
-     << "; positive_expiry: " << fhi.positive_expiry
-     << "; metadata.api_permissions.size(): "
-     << fhi.metadata.api_permissions.size() << "}";
-  return os;
-}
-#endif
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/db/v4_get_hash_protocol_manager.h b/components/safe_browsing/db/v4_get_hash_protocol_manager.h
deleted file mode 100644
index 83889a8..0000000
--- a/components/safe_browsing/db/v4_get_hash_protocol_manager.h
+++ /dev/null
@@ -1,383 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_DB_V4_GET_HASH_PROTOCOL_MANAGER_H_
-#define COMPONENTS_SAFE_BROWSING_DB_V4_GET_HASH_PROTOCOL_MANAGER_H_
-
-// A class that implements Chrome's interface with the SafeBrowsing V4 protocol.
-//
-// The V4GetHashProtocolManager handles formatting and making requests of, and
-// handling responses from, Google's SafeBrowsing servers. The purpose of this
-// class is to get full hash matches from the SB server for the given set of
-// hash prefixes.
-//
-// Design doc: go/design-doc-v4-full-hash-manager
-
-#include <memory>
-#include <string>
-#include <unordered_map>
-#include <utility>
-#include <vector>
-
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
-#include "base/sequence_checker.h"
-#include "base/time/default_clock.h"
-#include "base/time/time.h"
-#include "base/timer/timer.h"
-#include "components/safe_browsing/db/safebrowsing.pb.h"
-#include "components/safe_browsing/db/util.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
-#include "components/safe_browsing/proto/webui.pb.h"
-
-class GURL;
-
-namespace network {
-class SimpleURLLoader;
-class SharedURLLoaderFactory;
-}  // namespace network
-
-namespace safe_browsing {
-
-class V4GetHashProtocolManagerFuzzer;
-
-// The matching hash prefixes and corresponding stores, for each full hash
-// generated for a given URL.
-typedef std::unordered_map<FullHash, StoreAndHashPrefixes>
-    FullHashToStoreAndHashPrefixesMap;
-
-// ----------------------------------------------------------------
-
-// All information about a particular full hash i.e. negative TTL, store for
-// which it is valid, and metadata associated with that store.
-struct FullHashInfo {
- public:
-  FullHash full_hash;
-
-  // The list for which this full hash is applicable.
-  ListIdentifier list_id;
-
-  // The expiration time of the full hash for a particular store.
-  base::Time positive_expiry;
-
-  // Any metadata for this full hash for a particular store.
-  ThreatMetadata metadata;
-
-  FullHashInfo(const FullHash& full_hash,
-               const ListIdentifier& list_id,
-               const base::Time& positive_expiry);
-  FullHashInfo(const FullHashInfo& other);
-  ~FullHashInfo();
-
-  bool operator==(const FullHashInfo& other) const;
-  bool operator!=(const FullHashInfo& other) const;
-
- private:
-  FullHashInfo();
-};
-
-// Caches individual response from GETHASH response.
-struct CachedHashPrefixInfo {
-  // The negative TTL for the hash prefix that leads to this
-  // CachedHashPrefixInfo. The client should not send any more requests for that
-  // hash prefix until this time.
-  base::Time negative_expiry;
-
-  // The list of all full hashes (and related info) that start with a
-  // particular hash prefix and are known to be unsafe.
-  std::vector<FullHashInfo> full_hash_infos;
-
-  CachedHashPrefixInfo();
-  CachedHashPrefixInfo(const CachedHashPrefixInfo& other);
-  ~CachedHashPrefixInfo();
-};
-
-// Cached full hashes received from the server for the corresponding hash
-// prefixes.
-typedef std::unordered_map<HashPrefix, CachedHashPrefixInfo> FullHashCache;
-
-// FullHashCallback is invoked when GetFullHashes completes. The parameter is
-// the vector of full hash results. If empty, indicates that there were no
-// matches, and that the resource is safe.
-using FullHashCallback =
-    base::OnceCallback<void(const std::vector<FullHashInfo>&)>;
-
-// Information needed to update the cache and call the callback to post the
-// results.
-struct FullHashCallbackInfo {
-  FullHashCallbackInfo();
-  FullHashCallbackInfo(const std::vector<FullHashInfo>& cached_full_hash_infos,
-                       const std::vector<HashPrefix>& prefixes_requested,
-                       std::unique_ptr<network::SimpleURLLoader> loader,
-                       const FullHashToStoreAndHashPrefixesMap&
-                           full_hash_to_store_and_hash_prefixes,
-                       FullHashCallback callback,
-                       const base::Time& network_start_time);
-  ~FullHashCallbackInfo();
-
-  // The FullHashInfo objects retrieved from cache. These are merged with the
-  // results received from the server before invoking the callback.
-  std::vector<FullHashInfo> cached_full_hash_infos;
-
-  // The callback method to call after collecting the full hashes for given
-  // hash prefixes.
-  FullHashCallback callback;
-
-  // The loader that will return the response from the server. This is stored
-  // here as a unique pointer to be able to reason about its lifetime easily.
-  std::unique_ptr<network::SimpleURLLoader> loader;
-
-  // The generated full hashes and the corresponding prefixes and the stores in
-  // which to look for a full hash match.
-  FullHashToStoreAndHashPrefixesMap full_hash_to_store_and_hash_prefixes;
-
-  // Used to measure how long did it take to fetch the full hash response from
-  // the server.
-  base::Time network_start_time;
-
-  // The prefixes that were requested from the server.
-  std::vector<HashPrefix> prefixes_requested;
-};
-
-// ----------------------------------------------------------------
-
-class V4GetHashProtocolManagerFactory;
-
-class V4GetHashProtocolManager {
- public:
-  // Invoked when GetFullHashesWithApis completes.
-  // Parameters:
-  //   - The API threat metadata for the given URL.
-  using ThreatMetadataForApiCallback =
-      base::OnceCallback<void(const ThreatMetadata& md)>;
-
-  virtual ~V4GetHashProtocolManager();
-
-  // Create an instance of the safe browsing v4 protocol manager.
-  static std::unique_ptr<V4GetHashProtocolManager> Create(
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      const StoresToCheck& stores_to_check,
-      const V4ProtocolConfig& config);
-
-  // Makes the passed |factory| the factory used to instantiate
-  // a V4GetHashProtocolManager. Useful for tests.
-  static void RegisterFactory(
-      std::unique_ptr<V4GetHashProtocolManagerFactory> factory);
-
-  // Empties the cache.
-  void ClearCache();
-
-  // Retrieve the full hash for a set of prefixes, and invoke the callback
-  // argument when the results are retrieved. The callback may be invoked
-  // synchronously. |list_client_states| is needed for reporting the current
-  // state of the lists on the client; it does not affect the response from the
-  // server.
-  virtual void GetFullHashes(const FullHashToStoreAndHashPrefixesMap
-                                 full_hash_to_matching_hash_prefixes,
-                             const std::vector<std::string>& list_client_states,
-                             FullHashCallback callback);
-
-  // Retrieve the full hash and API metadata for the origin of |url|, and invoke
-  // the callback argument when the results are retrieved. The callback may be
-  // invoked synchronously.
-  virtual void GetFullHashesWithApis(
-      const GURL& url,
-      const std::vector<std::string>& list_client_states,
-      ThreatMetadataForApiCallback api_callback);
-
-  // Callback when the request completes
-  void OnURLLoaderComplete(network::SimpleURLLoader* url_loader,
-                           std::unique_ptr<std::string> response_body);
-
-  // Populates the protobuf with the FullHashCache data.
-  void CollectFullHashCacheInfo(FullHashCacheInfo* full_hash_cache_info);
-
- protected:
-  // Constructs a V4GetHashProtocolManager that issues network requests using
-  // |url_loader_factory|.
-  V4GetHashProtocolManager(
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      const StoresToCheck& stores_to_check,
-      const V4ProtocolConfig& config);
-
- private:
-  FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest, TestGetHashRequest);
-  FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest, TestParseHashResponse);
-  FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest,
-                           TestParseHashResponseWrongThreatEntryType);
-  FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest,
-                           TestParseHashThreatPatternType);
-  FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest,
-                           TestParseSubresourceFilterMetadata);
-  FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest,
-                           TestParseHashResponseNonPermissionMetadata);
-  FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest,
-                           TestParseHashResponseInconsistentThreatTypes);
-  FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest,
-                           TestGetHashErrorHandlingOK);
-  FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest,
-                           TestResultsNotCachedForNegativeCacheDuration);
-  FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest,
-                           TestGetHashErrorHandlingNetwork);
-  FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest,
-                           TestGetHashErrorHandlingResponseCode);
-  FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest,
-                           TestGetHashErrorHandlingParallelRequests);
-  FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest, GetCachedResults);
-  FRIEND_TEST_ALL_PREFIXES(V4GetHashProtocolManagerTest, TestUpdatesAreMerged);
-  friend class V4GetHashProtocolManagerTest;
-  friend class V4GetHashProtocolManagerFuzzer;
-  friend class V4GetHashProtocolManagerFactoryImpl;
-
-  FullHashCache* full_hash_cache_for_tests() { return &full_hash_cache_; }
-
-  void OnURLLoaderCompleteInternal(network::SimpleURLLoader* url_loader,
-                                   int net_error,
-                                   int response_code,
-                                   const std::string& data);
-
-  // Looks up the cached results for full hashes in
-  // |full_hash_to_store_and_hash_prefixes|. Fills |prefixes_to_request| with
-  // the prefixes that need to be requested. Fills |cached_full_hash_infos|
-  // with the cached results.
-  // Note: It is valid for both |prefixes_to_request| and
-  // |cached_full_hash_infos| to be empty after this function finishes.
-  void GetFullHashCachedResults(
-      const FullHashToStoreAndHashPrefixesMap&
-          full_hash_to_store_and_hash_prefixes,
-      const base::Time& now,
-      std::vector<HashPrefix>* prefixes_to_request,
-      std::vector<FullHashInfo>* cached_full_hash_infos);
-
-  // Fills a FindFullHashesRequest protocol buffer for a request.
-  // Returns the serialized and base 64 encoded request as a string.
-  // |prefixes_to_request| is the list of hash prefixes to get full hashes for.
-  // |list_client_states| is the client_state of each of the lists being synced.
-  std::string GetHashRequest(
-      const std::vector<HashPrefix>& prefixes_to_request,
-      const std::vector<std::string>& list_client_states);
-
-  void GetHashUrlAndHeaders(const std::string& request_base64,
-                            GURL* gurl,
-                            net::HttpRequestHeaders* headers) const;
-
-  // Updates internal state for each GetHash response error, assuming that
-  // the current time is |now|.
-  void HandleGetHashError(const base::Time& now);
-
-  // Merges the results from the cache and the results from the server. The
-  // response from the server may include information for full hashes from
-  // stores other than those required by this client so it filters out those
-  // results that the client did not ask for.
-  void MergeResults(const FullHashToStoreAndHashPrefixesMap&
-                        full_hash_to_store_and_hash_prefixes,
-                    const std::vector<FullHashInfo>& full_hash_infos,
-                    std::vector<FullHashInfo>* merged_full_hash_infos);
-
-  // Calls |api_callback| with an object of ThreatMetadata that contains
-  // permission API metadata for full hashes in those |full_hash_infos| that
-  // have a full hash in |full_hashes|.
-  void OnFullHashForApi(ThreatMetadataForApiCallback api_callback,
-                        const std::vector<FullHash>& full_hashes,
-                        const std::vector<FullHashInfo>& full_hash_infos);
-
-  // Parses a FindFullHashesResponse protocol buffer and fills the results in
-  // |full_hash_infos| and |negative_cache_expire|. |response_data| is a
-  // serialized FindFullHashes protocol buffer. |negative_cache_expire| is the
-  // cache expiry time of the hash prefixes that were requested. Returns true if
-  // parsing is successful; false otherwise.
-  bool ParseHashResponse(const std::string& response_data,
-                         std::vector<FullHashInfo>* full_hash_infos,
-                         base::Time* negative_cache_expire);
-
-  // Parses the store specific |metadata| information from |match|. Logs errors
-  // to UMA if the metadata information was not parsed correctly or was
-  // inconsistent with what's expected from that corresponding store.
-  static void ParseMetadata(const ThreatMatch& match, ThreatMetadata* metadata);
-
-  // Resets the gethash error counter and multiplier.
-  void ResetGetHashErrors();
-
-  // Overrides the clock used to check the time.
-  void SetClockForTests(base::Clock* clock);
-
-  // Updates the state of the full hash cache upon receiving a valid response
-  // from the server.
-  void UpdateCache(const std::vector<HashPrefix>& prefixes_requested,
-                   const std::vector<FullHashInfo>& full_hash_infos,
-                   const base::Time& negative_cache_expire);
-
- protected:
-  // A cache of full hash results.
-  FullHashCache full_hash_cache_;
-
- private:
-  // Map of GetHash requests to parameters which created it.
-  using PendingHashRequests =
-      std::unordered_map<const network::SimpleURLLoader*,
-                         std::unique_ptr<FullHashCallbackInfo>>;
-
-  // The factory that controls the creation of V4GetHashProtocolManager.
-  // This is used by tests.
-  static V4GetHashProtocolManagerFactory* factory_;
-
-  // The number of HTTP response errors since the the last successful HTTP
-  // response, used for request backoff timing.
-  size_t gethash_error_count_;
-
-  // Multiplier for the backoff error after the second.
-  size_t gethash_back_off_mult_;
-
-  PendingHashRequests pending_hash_requests_;
-
-  // For v4, the next gethash time is set to the backoff time is the last
-  // response was an error, or the minimum wait time if the last response was
-  // successful.
-  base::Time next_gethash_time_;
-
-  // The config of the client making Pver4 requests.
-  const V4ProtocolConfig config_;
-
-  // The URLLoaderFactory we use to issue network requests.
-  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
-
-  // Records number of cache hits since the beginning of this session.
-  int number_of_hits_ = 0;
-
-  // The clock used to vend times.
-  base::Clock* clock_;
-
-  // The following sets represent the combination of lists that we would always
-  // request from the server, irrespective of which list we found the hash
-  // prefix match in.
-  std::vector<PlatformType> platform_types_;
-  std::vector<ThreatEntryType> threat_entry_types_;
-  std::vector<ThreatType> threat_types_;
-
-  SEQUENCE_CHECKER(sequence_checker_);
-
-  DISALLOW_COPY_AND_ASSIGN(V4GetHashProtocolManager);
-};
-
-// Interface of a factory to create V4GetHashProtocolManager.  Useful for tests.
-class V4GetHashProtocolManagerFactory {
- public:
-  V4GetHashProtocolManagerFactory() {}
-  virtual ~V4GetHashProtocolManagerFactory() {}
-  virtual std::unique_ptr<V4GetHashProtocolManager> CreateProtocolManager(
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      const StoresToCheck& stores_to_check,
-      const V4ProtocolConfig& config) = 0;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(V4GetHashProtocolManagerFactory);
-};
-
-#ifndef NDEBUG
-std::ostream& operator<<(std::ostream& os, const FullHashInfo& id);
-#endif
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_DB_V4_GET_HASH_PROTOCOL_MANAGER_H_
diff --git a/components/safe_browsing/db/v4_get_hash_protocol_manager_fuzzer.cc b/components/safe_browsing/db/v4_get_hash_protocol_manager_fuzzer.cc
deleted file mode 100644
index c5b951c..0000000
--- a/components/safe_browsing/db/v4_get_hash_protocol_manager_fuzzer.cc
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/db/v4_get_hash_protocol_manager.h"
-
-#include <string>
-
-#include "components/safe_browsing/db/safebrowsing.pb.h"
-
-namespace safe_browsing {
-
-class V4GetHashProtocolManagerFuzzer {
- public:
-  static int FuzzMetadata(const uint8_t* data, size_t size) {
-    FindFullHashesResponse response;
-    std::string input(reinterpret_cast<const char*>(data), size);
-    if (!response.ParseFromString(input))
-      return 0;
-    safe_browsing::ThreatMetadata metadata;
-    for (const ThreatMatch& match : response.matches()) {
-      V4GetHashProtocolManager::ParseMetadata(match, &metadata);
-    }
-    return 0;
-  }
-};
-
-}  // namespace safe_browsing
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  return safe_browsing::V4GetHashProtocolManagerFuzzer::FuzzMetadata(data,
-                                                                     size);
-}
diff --git a/components/safe_browsing/db/v4_get_hash_protocol_manager_unittest.cc b/components/safe_browsing/db/v4_get_hash_protocol_manager_unittest.cc
deleted file mode 100644
index c6a168d..0000000
--- a/components/safe_browsing/db/v4_get_hash_protocol_manager_unittest.cc
+++ /dev/null
@@ -1,942 +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.
-
-#include "components/safe_browsing/db/v4_get_hash_protocol_manager.h"
-
-#include <memory>
-#include <vector>
-
-#include "base/base64.h"
-#include "base/bind.h"
-#include "base/run_loop.h"
-#include "base/stl_util.h"
-#include "base/strings/stringprintf.h"
-#include "base/test/metrics/histogram_tester.h"
-#include "base/test/simple_test_clock.h"
-#include "base/time/time.h"
-#include "build/build_config.h"
-#include "components/safe_browsing/db/safebrowsing.pb.h"
-#include "components/safe_browsing/db/util.h"
-#include "components/safe_browsing/db/v4_test_util.h"
-#include "content/public/test/browser_task_environment.h"
-#include "net/base/escape.h"
-#include "net/base/load_flags.h"
-#include "net/base/net_errors.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
-#include "services/network/test/test_url_loader_factory.h"
-#include "testing/platform_test.h"
-
-using base::Time;
-using base::TimeDelta;
-
-namespace safe_browsing {
-
-namespace {
-
-struct KeyValue {
-  std::string key;
-  std::string value;
-
-  explicit KeyValue(const std::string key, const std::string value)
-      : key(key), value(value) {}
-  explicit KeyValue(const KeyValue& other) = default;
-
- private:
-  KeyValue();
-};
-
-struct ResponseInfo {
-  FullHash full_hash;
-  ListIdentifier list_id;
-  std::vector<KeyValue> key_values;
-
-  explicit ResponseInfo(FullHash full_hash, ListIdentifier list_id)
-      : full_hash(full_hash), list_id(list_id) {}
-  explicit ResponseInfo(const ResponseInfo& other)
-      : full_hash(other.full_hash),
-        list_id(other.list_id),
-        key_values(other.key_values) {}
-
- private:
-  ResponseInfo();
-};
-
-}  // namespace
-
-class V4GetHashProtocolManagerTest : public PlatformTest {
- public:
-  void SetUp() override {
-    PlatformTest::SetUp();
-    callback_called_ = false;
-    test_shared_loader_factory_ =
-        base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
-            &test_url_loader_factory_);
-  }
-
-  std::unique_ptr<V4GetHashProtocolManager> CreateProtocolManager() {
-    StoresToCheck stores_to_check(
-        {GetUrlMalwareId(), GetChromeUrlApiId(),
-         ListIdentifier(CHROME_PLATFORM, URL, SOCIAL_ENGINEERING),
-         ListIdentifier(CHROME_PLATFORM, URL, POTENTIALLY_HARMFUL_APPLICATION),
-         ListIdentifier(CHROME_PLATFORM, URL, SUBRESOURCE_FILTER)});
-    return V4GetHashProtocolManager::Create(test_shared_loader_factory_,
-                                            stores_to_check,
-                                            GetTestV4ProtocolConfig());
-  }
-
-  static void SetupFetcherToReturnResponse(V4GetHashProtocolManager* pm,
-                                           int net_error,
-                                           int response_code,
-                                           const std::string& data) {
-    CHECK_EQ(pm->pending_hash_requests_.size(), 1u);
-    pm->OnURLLoaderCompleteInternal(
-        pm->pending_hash_requests_.begin()->second->loader.get(), net_error,
-        response_code, data);
-  }
-
-  static void SetupFullHashFetcherToReturnResponse(V4GetHashProtocolManager* pm,
-                                                   const FullHash& full_hash,
-                                                   int net_error,
-                                                   int response_code,
-                                                   const std::string& data) {
-    network::SimpleURLLoader* url_loader = nullptr;
-    for (const auto& pending_request : pm->pending_hash_requests_) {
-      if (pending_request.second->full_hash_to_store_and_hash_prefixes.count(
-              full_hash)) {
-        EXPECT_EQ(nullptr, url_loader);
-        url_loader = pending_request.second->loader.get();
-      }
-    }
-    ASSERT_TRUE(url_loader);
-
-    pm->OnURLLoaderCompleteInternal(url_loader, net_error, response_code, data);
-  }
-
-  static void SetupFetcherToReturnOKResponse(
-      V4GetHashProtocolManager* pm,
-      const std::vector<ResponseInfo>& infos) {
-    SetupFetcherToReturnResponse(pm, net::OK, 200, GetV4HashResponse(infos));
-  }
-
-  static std::vector<ResponseInfo> GetStockV4HashResponseInfos() {
-    ResponseInfo info(FullHash("Everything's shiny, Cap'n."),
-                      GetChromeUrlApiId());
-    info.key_values.emplace_back("permission", "NOTIFICATIONS");
-    std::vector<ResponseInfo> infos;
-    infos.push_back(info);
-    return infos;
-  }
-
-  static std::string GetStockV4HashResponse() {
-    return GetV4HashResponse(GetStockV4HashResponseInfos());
-  }
-
-  void SetTestClock(base::Time now, V4GetHashProtocolManager* pm) {
-    clock_.SetNow(now);
-    pm->SetClockForTests(&clock_);
-  }
-
-  void ValidateGetV4ApiResults(const ThreatMetadata& expected_md,
-                               const ThreatMetadata& actual_md) {
-    EXPECT_EQ(expected_md, actual_md);
-    callback_called_ = true;
-  }
-
-  void ValidateGetV4HashResults(
-      const std::vector<FullHashInfo>& expected_results,
-      const std::vector<FullHashInfo>& actual_results) {
-    EXPECT_EQ(expected_results.size(), actual_results.size());
-    for (size_t i = 0; i < actual_results.size(); i++) {
-      EXPECT_TRUE(expected_results[i] == actual_results[i]);
-    }
-    callback_called_ = true;
-  }
-
-  bool callback_called() const { return callback_called_; }
-  void reset_callback_called() { callback_called_ = false; }
-
- private:
-  static std::string GetV4HashResponse(
-      std::vector<ResponseInfo> response_infos) {
-    FindFullHashesResponse res;
-    res.mutable_negative_cache_duration()->set_seconds(600);
-    for (const ResponseInfo& info : response_infos) {
-      ThreatMatch* m = res.add_matches();
-      m->set_platform_type(info.list_id.platform_type());
-      m->set_threat_entry_type(info.list_id.threat_entry_type());
-      m->set_threat_type(info.list_id.threat_type());
-      m->mutable_cache_duration()->set_seconds(300);
-      m->mutable_threat()->set_hash(info.full_hash);
-
-      for (const KeyValue& key_value : info.key_values) {
-        ThreatEntryMetadata::MetadataEntry* e =
-            m->mutable_threat_entry_metadata()->add_entries();
-        e->set_key(key_value.key);
-        e->set_value(key_value.value);
-      }
-    }
-
-    // Serialize.
-    std::string res_data;
-    res.SerializeToString(&res_data);
-
-    return res_data;
-  }
-
-  bool callback_called_;
-  base::SimpleTestClock clock_;
-  network::TestURLLoaderFactory test_url_loader_factory_;
-  scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
-  content::BrowserTaskEnvironment task_environment_;
-};
-
-TEST_F(V4GetHashProtocolManagerTest, TestGetHashErrorHandlingNetwork) {
-  std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager());
-
-  FullHashToStoreAndHashPrefixesMap matched_locally;
-  matched_locally[FullHash("AHashFull")].emplace_back(GetUrlSocEngId(),
-                                                      HashPrefix("AHash"));
-  std::vector<FullHashInfo> expected_results;
-  pm->GetFullHashes(
-      matched_locally, {},
-      base::BindOnce(&V4GetHashProtocolManagerTest::ValidateGetV4HashResults,
-                     base::Unretained(this), expected_results));
-
-  // Failed request status should result in error.
-  SetupFetcherToReturnResponse(pm.get(), net::ERR_CONNECTION_RESET, 200,
-                               GetStockV4HashResponse());
-
-  // Should have recorded one error, but back off multiplier is unchanged.
-  EXPECT_EQ(1ul, pm->gethash_error_count_);
-  EXPECT_EQ(1ul, pm->gethash_back_off_mult_);
-  EXPECT_TRUE(callback_called());
-}
-
-TEST_F(V4GetHashProtocolManagerTest, TestGetHashErrorHandlingResponseCode) {
-  std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager());
-
-  FullHashToStoreAndHashPrefixesMap matched_locally;
-  matched_locally[FullHash("AHashFull")].emplace_back(GetUrlSocEngId(),
-                                                      HashPrefix("AHash"));
-  std::vector<FullHashInfo> expected_results;
-  pm->GetFullHashes(
-      matched_locally, {},
-      base::BindOnce(&V4GetHashProtocolManagerTest::ValidateGetV4HashResults,
-                     base::Unretained(this), expected_results));
-
-  // Response code of anything other than 200 should result in error.
-  SetupFetcherToReturnResponse(pm.get(), net::OK, 204,
-                               GetStockV4HashResponse());
-
-  // Should have recorded one error, but back off multiplier is unchanged.
-  EXPECT_EQ(1ul, pm->gethash_error_count_);
-  EXPECT_EQ(1ul, pm->gethash_back_off_mult_);
-  EXPECT_TRUE(callback_called());
-}
-
-TEST_F(V4GetHashProtocolManagerTest, TestGetHashErrorHandlingParallelRequests) {
-  std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager());
-  std::vector<FullHashInfo> empty_results;
-  base::HistogramTester histogram_tester;
-
-  FullHashToStoreAndHashPrefixesMap matched_locally1;
-  matched_locally1[FullHash("AHash1Full")].emplace_back(GetUrlSocEngId(),
-                                                        HashPrefix("AHash1"));
-  pm->GetFullHashes(matched_locally1, {},
-                    base::BindRepeating(
-                        &V4GetHashProtocolManagerTest::ValidateGetV4HashResults,
-                        base::Unretained(this), empty_results));
-
-  FullHashToStoreAndHashPrefixesMap matched_locally2;
-  matched_locally2[FullHash("AHash2Full")].emplace_back(GetUrlSocEngId(),
-                                                        HashPrefix("AHash2"));
-  pm->GetFullHashes(matched_locally2, {},
-                    base::BindRepeating(
-                        &V4GetHashProtocolManagerTest::ValidateGetV4HashResults,
-                        base::Unretained(this), empty_results));
-
-  // Fail the first request.
-  SetupFullHashFetcherToReturnResponse(pm.get(), FullHash("AHash1Full"),
-                                       net::ERR_CONNECTION_RESET, 200,
-                                       GetStockV4HashResponse());
-
-  // Should have recorded one error, but back off multiplier is unchanged.
-  EXPECT_EQ(1ul, pm->gethash_error_count_);
-  EXPECT_EQ(1ul, pm->gethash_back_off_mult_);
-  EXPECT_TRUE(callback_called());
-  histogram_tester.ExpectUniqueSample("SafeBrowsing.V4GetHash.Result",
-                                      V4OperationResult::NETWORK_ERROR, 1);
-
-  reset_callback_called();
-
-  // Comple the second request successfully.
-  SetupFullHashFetcherToReturnResponse(pm.get(), FullHash("AHash2Full"),
-                                       net::OK, 200, GetStockV4HashResponse());
-
-  // Error counters are reset.
-  EXPECT_EQ(0ul, pm->gethash_error_count_);
-  EXPECT_EQ(1ul, pm->gethash_back_off_mult_);
-  EXPECT_TRUE(callback_called());
-  histogram_tester.ExpectBucketCount("SafeBrowsing.V4GetHash.Result",
-                                     V4OperationResult::STATUS_200, 1);
-
-  reset_callback_called();
-
-  // Start the third request.
-  FullHashToStoreAndHashPrefixesMap matched_locally3;
-  matched_locally3[FullHash("AHash3Full")].emplace_back(GetUrlSocEngId(),
-                                                        HashPrefix("AHash3"));
-  pm->GetFullHashes(matched_locally3, {},
-                    base::BindRepeating(
-                        &V4GetHashProtocolManagerTest::ValidateGetV4HashResults,
-                        base::Unretained(this), empty_results));
-
-  // The request is not failed right away.
-  EXPECT_FALSE(callback_called());
-  // The request is not reported as MIN_WAIT_DURATION_ERROR.
-  histogram_tester.ExpectBucketCount("SafeBrowsing.V4GetHash.Result",
-                                     V4OperationResult::MIN_WAIT_DURATION_ERROR,
-                                     0);
-}
-
-TEST_F(V4GetHashProtocolManagerTest, TestGetHashErrorHandlingOK) {
-  std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager());
-
-  base::Time now = base::Time::UnixEpoch();
-  SetTestClock(now, pm.get());
-
-  HashPrefix prefix("Everything");
-  FullHash full_hash("Everything's shiny, Cap'n.");
-  FullHashToStoreAndHashPrefixesMap matched_locally;
-  matched_locally[full_hash].push_back(
-      StoreAndHashPrefix(GetChromeUrlApiId(), prefix));
-  std::vector<FullHashInfo> expected_results;
-  FullHashInfo fhi(full_hash, GetChromeUrlApiId(),
-                   now + base::TimeDelta::FromSeconds(300));
-  fhi.metadata.api_permissions.insert("NOTIFICATIONS");
-  expected_results.push_back(fhi);
-
-  pm->GetFullHashes(
-      matched_locally, {},
-      base::BindOnce(&V4GetHashProtocolManagerTest::ValidateGetV4HashResults,
-                     base::Unretained(this), expected_results));
-
-  SetupFetcherToReturnOKResponse(pm.get(), GetStockV4HashResponseInfos());
-
-  // No error, back off multiplier is unchanged.
-  EXPECT_EQ(0ul, pm->gethash_error_count_);
-  EXPECT_EQ(1ul, pm->gethash_back_off_mult_);
-
-  // Verify the state of the cache.
-  const FullHashCache* cache = pm->full_hash_cache_for_tests();
-  // Check the cache.
-  ASSERT_EQ(1u, cache->size());
-  EXPECT_EQ(1u, cache->count(prefix));
-  const CachedHashPrefixInfo& cached_result = cache->at(prefix);
-  EXPECT_EQ(cached_result.negative_expiry,
-            now + base::TimeDelta::FromSeconds(600));
-  ASSERT_EQ(1u, cached_result.full_hash_infos.size());
-  EXPECT_EQ(FullHash("Everything's shiny, Cap'n."),
-            cached_result.full_hash_infos[0].full_hash);
-  EXPECT_TRUE(callback_called());
-}
-
-TEST_F(V4GetHashProtocolManagerTest,
-       TestResultsNotCachedForNegativeCacheDuration) {
-  std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager());
-
-  HashPrefix prefix("Everything");
-  std::vector<HashPrefix> prefixes_requested({prefix});
-  base::Time negative_cache_expire;
-  FullHash full_hash("Everything's shiny, Cap'n.");
-  std::vector<FullHashInfo> fhis;
-  fhis.emplace_back(full_hash, GetChromeUrlApiId(), base::Time::UnixEpoch());
-
-  pm->UpdateCache(prefixes_requested, fhis, negative_cache_expire);
-
-  // Verify the state of the cache.
-  const FullHashCache* cache = pm->full_hash_cache_for_tests();
-  // Check the cache.
-  EXPECT_EQ(0u, cache->size());
-}
-
-TEST_F(V4GetHashProtocolManagerTest, TestGetHashRequest) {
-  FindFullHashesRequest req;
-  ThreatInfo* info = req.mutable_threat_info();
-
-  const std::set<PlatformType> platform_types = {
-    GetCurrentPlatformType(),
-    CHROME_PLATFORM,
-  // TODO(crbug.com/1030487): This special case for Android will no longer be
-  // needed once GetCurrentPlatformType() returns ANDROID_PLATFORM on Android.
-#if defined(OS_ANDROID)
-    ANDROID_PLATFORM,
-#endif
-  };
-
-  for (const PlatformType& p : platform_types) {
-    info->add_platform_types(p);
-  }
-
-  info->add_threat_entry_types(URL);
-
-  for (const ThreatType& tt : std::set<ThreatType>{
-           MALWARE_THREAT, SOCIAL_ENGINEERING, POTENTIALLY_HARMFUL_APPLICATION,
-           API_ABUSE, SUBRESOURCE_FILTER}) {
-    info->add_threat_types(tt);
-  }
-
-  HashPrefix one = "hashone";
-  HashPrefix two = "hashtwo";
-  info->add_threat_entries()->set_hash(one);
-  info->add_threat_entries()->set_hash(two);
-
-  std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager());
-  req.mutable_client()->set_client_id(pm->config_.client_name);
-  req.mutable_client()->set_client_version(pm->config_.version);
-
-  std::vector<std::string> client_states = {"client_state_1", "client_state_2"};
-  for (const auto& client_state : client_states) {
-    req.add_client_states(client_state);
-  }
-
-  // Serialize and Base64 encode.
-  std::string req_data, req_base64;
-  req.SerializeToString(&req_data);
-  base::Base64Encode(req_data, &req_base64);
-
-  std::vector<HashPrefix> prefixes_to_request = {one, two};
-  EXPECT_EQ(req_base64, pm->GetHashRequest(prefixes_to_request, client_states));
-}
-
-TEST_F(V4GetHashProtocolManagerTest, TestParseHashResponse) {
-  std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager());
-
-  base::Time now = base::Time::UnixEpoch();
-  SetTestClock(now, pm.get());
-
-  FullHash full_hash("Everything's shiny, Cap'n.");
-  FindFullHashesResponse res;
-  res.mutable_negative_cache_duration()->set_seconds(600);
-  res.mutable_minimum_wait_duration()->set_seconds(400);
-  ThreatMatch* m = res.add_matches();
-  m->set_threat_type(API_ABUSE);
-  // TODO(crbug.com/1030487): This special case for Android will no longer be
-  // needed once GetCurrentPlatformType() returns ANDROID_PLATFORM on Android.
-#if defined(OS_ANDROID)
-  m->set_platform_type(ANDROID_PLATFORM);
-#else
-  m->set_platform_type(GetCurrentPlatformType());
-#endif
-  m->set_threat_entry_type(URL);
-  m->mutable_cache_duration()->set_seconds(300);
-  m->mutable_threat()->set_hash(full_hash);
-  ThreatEntryMetadata::MetadataEntry* e =
-      m->mutable_threat_entry_metadata()->add_entries();
-  e->set_key("permission");
-  e->set_value("NOTIFICATIONS");
-  // Add another ThreatMatch for a list we don't track. This response should
-  // get dropped.
-  m = res.add_matches();
-  m->set_threat_type(THREAT_TYPE_UNSPECIFIED);
-  m->set_platform_type(CHROME_PLATFORM);
-  m->set_threat_entry_type(URL);
-  m->mutable_cache_duration()->set_seconds(300);
-  m->mutable_threat()->set_hash(full_hash);
-
-  // Serialize.
-  std::string res_data;
-  res.SerializeToString(&res_data);
-
-  std::vector<FullHashInfo> full_hash_infos;
-  base::Time cache_expire;
-  EXPECT_TRUE(pm->ParseHashResponse(res_data, &full_hash_infos, &cache_expire));
-
-  EXPECT_EQ(now + base::TimeDelta::FromSeconds(600), cache_expire);
-  // Even though the server responded with two ThreatMatch responses, one
-  // should have been dropped.
-  ASSERT_EQ(1ul, full_hash_infos.size());
-  const FullHashInfo& fhi = full_hash_infos[0];
-  EXPECT_EQ(full_hash, fhi.full_hash);
-  EXPECT_EQ(GetChromeUrlApiId(), fhi.list_id);
-  EXPECT_EQ(1ul, fhi.metadata.api_permissions.size());
-  EXPECT_EQ(1ul, fhi.metadata.api_permissions.count("NOTIFICATIONS"));
-  EXPECT_EQ(now + base::TimeDelta::FromSeconds(300), fhi.positive_expiry);
-  EXPECT_EQ(now + base::TimeDelta::FromSeconds(400), pm->next_gethash_time_);
-}
-
-// Adds an entry with an ignored ThreatEntryType.
-TEST_F(V4GetHashProtocolManagerTest,
-       TestParseHashResponseWrongThreatEntryType) {
-  std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager());
-
-  base::Time now = base::Time::UnixEpoch();
-  SetTestClock(now, pm.get());
-
-  FindFullHashesResponse res;
-  res.mutable_negative_cache_duration()->set_seconds(600);
-  res.add_matches()->set_threat_entry_type(EXECUTABLE);
-
-  // Serialize.
-  std::string res_data;
-  res.SerializeToString(&res_data);
-
-  std::vector<FullHashInfo> full_hash_infos;
-  base::Time cache_expire;
-  EXPECT_FALSE(
-      pm->ParseHashResponse(res_data, &full_hash_infos, &cache_expire));
-
-  EXPECT_EQ(now + base::TimeDelta::FromSeconds(600), cache_expire);
-  // There should be no hash results.
-  EXPECT_EQ(0ul, full_hash_infos.size());
-}
-
-// Adds entries with a ThreatPatternType metadata.
-TEST_F(V4GetHashProtocolManagerTest, TestParseHashThreatPatternType) {
-  std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager());
-
-  base::Time now = base::Time::UnixEpoch();
-  SetTestClock(now, pm.get());
-
-  {
-    // Test social engineering pattern type.
-    FindFullHashesResponse se_res;
-    se_res.mutable_negative_cache_duration()->set_seconds(600);
-    ThreatMatch* se = se_res.add_matches();
-    se->set_threat_type(SOCIAL_ENGINEERING);
-    se->set_platform_type(CHROME_PLATFORM);
-    se->set_threat_entry_type(URL);
-    FullHash full_hash("Everything's shiny, Cap'n.");
-    se->mutable_threat()->set_hash(full_hash);
-    ThreatEntryMetadata::MetadataEntry* se_meta =
-        se->mutable_threat_entry_metadata()->add_entries();
-    se_meta->set_key("se_pattern_type");
-    se_meta->set_value("SOCIAL_ENGINEERING_LANDING");
-
-    std::string se_data;
-    se_res.SerializeToString(&se_data);
-
-    std::vector<FullHashInfo> full_hash_infos;
-    base::Time cache_expire;
-    EXPECT_TRUE(
-        pm->ParseHashResponse(se_data, &full_hash_infos, &cache_expire));
-    EXPECT_EQ(now + base::TimeDelta::FromSeconds(600), cache_expire);
-
-    // Ensure that the threat remains valid since we found a full hash match,
-    // even though the metadata information could not be parsed correctly.
-    ASSERT_EQ(1ul, full_hash_infos.size());
-    const FullHashInfo& fhi = full_hash_infos[0];
-    EXPECT_EQ(full_hash, fhi.full_hash);
-    const ListIdentifier list_id(CHROME_PLATFORM, URL, SOCIAL_ENGINEERING);
-    EXPECT_EQ(list_id, fhi.list_id);
-    EXPECT_EQ(ThreatPatternType::SOCIAL_ENGINEERING_LANDING,
-              fhi.metadata.threat_pattern_type);
-  }
-
-  {
-    // Test potentially harmful application pattern type.
-    FindFullHashesResponse pha_res;
-    pha_res.mutable_negative_cache_duration()->set_seconds(600);
-    ThreatMatch* pha = pha_res.add_matches();
-    pha->set_threat_type(POTENTIALLY_HARMFUL_APPLICATION);
-    pha->set_threat_entry_type(URL);
-    pha->set_platform_type(CHROME_PLATFORM);
-    FullHash full_hash("Not to fret.");
-    pha->mutable_threat()->set_hash(full_hash);
-    ThreatEntryMetadata::MetadataEntry* pha_meta =
-        pha->mutable_threat_entry_metadata()->add_entries();
-    pha_meta->set_key("pha_pattern_type");
-    pha_meta->set_value("LANDING");
-
-    std::string pha_data;
-    pha_res.SerializeToString(&pha_data);
-    std::vector<FullHashInfo> full_hash_infos;
-    base::Time cache_expire;
-    EXPECT_TRUE(
-        pm->ParseHashResponse(pha_data, &full_hash_infos, &cache_expire));
-    EXPECT_EQ(now + base::TimeDelta::FromSeconds(600), cache_expire);
-
-    ASSERT_EQ(1ul, full_hash_infos.size());
-    const FullHashInfo& fhi = full_hash_infos[0];
-    EXPECT_EQ(full_hash, fhi.full_hash);
-    const ListIdentifier list_id(CHROME_PLATFORM, URL,
-                                 POTENTIALLY_HARMFUL_APPLICATION);
-    EXPECT_EQ(list_id, fhi.list_id);
-    EXPECT_EQ(ThreatPatternType::MALWARE_LANDING,
-              fhi.metadata.threat_pattern_type);
-  }
-
-  {
-    // Test invalid pattern type.
-    FullHash full_hash("Not to fret.");
-    FindFullHashesResponse invalid_res;
-    invalid_res.mutable_negative_cache_duration()->set_seconds(600);
-    ThreatMatch* invalid = invalid_res.add_matches();
-    invalid->set_threat_type(POTENTIALLY_HARMFUL_APPLICATION);
-    invalid->set_threat_entry_type(URL);
-    invalid->set_platform_type(CHROME_PLATFORM);
-    invalid->mutable_threat()->set_hash(full_hash);
-    ThreatEntryMetadata::MetadataEntry* invalid_meta =
-        invalid->mutable_threat_entry_metadata()->add_entries();
-    invalid_meta->set_key("pha_pattern_type");
-    invalid_meta->set_value("INVALIDE_VALUE");
-
-    std::string invalid_data;
-    invalid_res.SerializeToString(&invalid_data);
-    std::vector<FullHashInfo> full_hash_infos;
-    base::Time cache_expire;
-    EXPECT_TRUE(
-        pm->ParseHashResponse(invalid_data, &full_hash_infos, &cache_expire));
-
-    // Ensure that the threat remains valid since we found a full hash match,
-    // even though the metadata information could not be parsed correctly.
-    ASSERT_EQ(1ul, full_hash_infos.size());
-    const auto& fhi = full_hash_infos[0];
-    EXPECT_EQ(full_hash, fhi.full_hash);
-    EXPECT_EQ(
-        ListIdentifier(CHROME_PLATFORM, URL, POTENTIALLY_HARMFUL_APPLICATION),
-        fhi.list_id);
-    EXPECT_EQ(ThreatPatternType::NONE, fhi.metadata.threat_pattern_type);
-  }
-}
-
-TEST_F(V4GetHashProtocolManagerTest, TestParseSubresourceFilterMetadata) {
-  typedef SubresourceFilterLevel Level;
-  typedef SubresourceFilterType Type;
-  const struct {
-    const char* abusive_type;
-    const char* bas_type;
-    SubresourceFilterMatch expected_match;
-  } test_cases[] = {
-      {"warn",
-       "enforce",
-       {{Type::ABUSIVE, Level::WARN}, {Type::BETTER_ADS, Level::ENFORCE}}},
-      {nullptr, "warn", {{Type::BETTER_ADS, Level::WARN}}},
-      {"asdf",
-       "",
-       {{Type::ABUSIVE, Level::ENFORCE}, {Type::BETTER_ADS, Level::ENFORCE}}},
-      {"warn", nullptr, {{Type::ABUSIVE, Level::WARN}}},
-      {nullptr, nullptr, {}},
-      {"",
-       "",
-       {{Type::ABUSIVE, Level::ENFORCE}, {Type::BETTER_ADS, Level::ENFORCE}}},
-  };
-
-  for (const auto& test_case : test_cases) {
-    SCOPED_TRACE(testing::Message() << "abusive: " << test_case.abusive_type
-                                    << " better ads: " << test_case.bas_type);
-    std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager());
-
-    base::Time now = base::Time::UnixEpoch();
-    SetTestClock(now, pm.get());
-    FindFullHashesResponse sf_res;
-    sf_res.mutable_negative_cache_duration()->set_seconds(600);
-    ThreatMatch* sf = sf_res.add_matches();
-    sf->set_threat_type(SUBRESOURCE_FILTER);
-    sf->set_platform_type(CHROME_PLATFORM);
-    sf->set_threat_entry_type(URL);
-    FullHash full_hash("Everything's shiny, Cap'n.");
-    sf->mutable_threat()->set_hash(full_hash);
-
-    // sf_absv.
-    if (test_case.abusive_type != nullptr) {
-      ThreatEntryMetadata::MetadataEntry* sf_absv =
-          sf->mutable_threat_entry_metadata()->add_entries();
-      sf_absv->set_key("sf_absv");
-      sf_absv->set_value(test_case.abusive_type);
-    }
-
-    // sf_bas
-    if (test_case.bas_type != nullptr) {
-      ThreatEntryMetadata::MetadataEntry* sf_bas =
-          sf->mutable_threat_entry_metadata()->add_entries();
-      sf_bas->set_key("sf_bas");
-      sf_bas->set_value(test_case.bas_type);
-    }
-
-    std::string sf_data;
-    sf_res.SerializeToString(&sf_data);
-
-    std::vector<FullHashInfo> full_hash_infos;
-    base::Time cache_expire;
-    EXPECT_TRUE(
-        pm->ParseHashResponse(sf_data, &full_hash_infos, &cache_expire));
-    EXPECT_EQ(now + base::TimeDelta::FromSeconds(600), cache_expire);
-
-    ASSERT_EQ(1ul, full_hash_infos.size());
-    const FullHashInfo& fhi = full_hash_infos[0];
-    EXPECT_EQ(full_hash, fhi.full_hash);
-    const ListIdentifier list_id(CHROME_PLATFORM, URL, SUBRESOURCE_FILTER);
-    EXPECT_EQ(list_id, fhi.list_id);
-    EXPECT_EQ(test_case.expected_match, fhi.metadata.subresource_filter_match);
-  }
-}
-
-// Adds metadata with a key value that is not "permission".
-TEST_F(V4GetHashProtocolManagerTest,
-       TestParseHashResponseNonPermissionMetadata) {
-  std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager());
-
-  base::Time now = base::Time::UnixEpoch();
-  SetTestClock(now, pm.get());
-
-  FullHash full_hash("Not to fret.");
-  FindFullHashesResponse res;
-  res.mutable_negative_cache_duration()->set_seconds(600);
-  ThreatMatch* m = res.add_matches();
-  m->set_threat_type(API_ABUSE);
-  // TODO(crbug.com/1030487): This special case for Android will no longer be
-  // needed once GetCurrentPlatformType() returns ANDROID_PLATFORM on Android.
-#if defined(OS_ANDROID)
-  m->set_platform_type(ANDROID_PLATFORM);
-#else
-  m->set_platform_type(GetCurrentPlatformType());
-#endif
-  m->set_threat_entry_type(URL);
-  m->mutable_threat()->set_hash(full_hash);
-  ThreatEntryMetadata::MetadataEntry* e =
-      m->mutable_threat_entry_metadata()->add_entries();
-  e->set_key("notpermission");
-  e->set_value("NOTGEOLOCATION");
-
-  // Serialize.
-  std::string res_data;
-  res.SerializeToString(&res_data);
-
-  std::vector<FullHashInfo> full_hash_infos;
-  base::Time cache_expire;
-  EXPECT_TRUE(pm->ParseHashResponse(res_data, &full_hash_infos, &cache_expire));
-
-  EXPECT_EQ(now + base::TimeDelta::FromSeconds(600), cache_expire);
-  ASSERT_EQ(1ul, full_hash_infos.size());
-  const auto& fhi = full_hash_infos[0];
-  EXPECT_EQ(full_hash, fhi.full_hash);
-  EXPECT_EQ(GetChromeUrlApiId(), fhi.list_id);
-  EXPECT_TRUE(fhi.metadata.api_permissions.empty());
-}
-
-TEST_F(V4GetHashProtocolManagerTest,
-       TestParseHashResponseInconsistentThreatTypes) {
-  std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager());
-
-  FindFullHashesResponse res;
-  res.mutable_negative_cache_duration()->set_seconds(600);
-  ThreatMatch* m1 = res.add_matches();
-  m1->set_threat_type(API_ABUSE);
-  m1->set_platform_type(CHROME_PLATFORM);
-  m1->set_threat_entry_type(URL);
-  m1->mutable_threat()->set_hash(FullHash("Everything's shiny, Cap'n."));
-  m1->mutable_threat_entry_metadata()->add_entries();
-  ThreatMatch* m2 = res.add_matches();
-  m2->set_threat_type(MALWARE_THREAT);
-  m2->set_threat_entry_type(URL);
-  m2->mutable_threat()->set_hash(FullHash("Not to fret."));
-
-  // Serialize.
-  std::string res_data;
-  res.SerializeToString(&res_data);
-
-  std::vector<FullHashInfo> full_hash_infos;
-  base::Time cache_expire;
-  EXPECT_FALSE(
-      pm->ParseHashResponse(res_data, &full_hash_infos, &cache_expire));
-}
-
-// Checks that results are looked up correctly in the cache.
-TEST_F(V4GetHashProtocolManagerTest, GetCachedResults) {
-  base::Time now = base::Time::UnixEpoch();
-  FullHash full_hash("example");
-  HashPrefix prefix("exam");
-  FullHashToStoreAndHashPrefixesMap matched_locally;
-  matched_locally[full_hash].emplace_back(GetUrlMalwareId(), prefix);
-  std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager());
-  FullHashCache* cache = pm->full_hash_cache_for_tests();
-
-  {
-    std::vector<HashPrefix> prefixes_to_request;
-    std::vector<FullHashInfo> cached_full_hash_infos;
-    cache->clear();
-
-    // Test with an empty cache. (Case: 2)
-    pm->GetFullHashCachedResults(matched_locally, now, &prefixes_to_request,
-                                 &cached_full_hash_infos);
-    EXPECT_TRUE(cache->empty());
-    ASSERT_EQ(1ul, prefixes_to_request.size());
-    EXPECT_EQ(prefix, prefixes_to_request[0]);
-    EXPECT_TRUE(cached_full_hash_infos.empty());
-  }
-
-  {
-    std::vector<HashPrefix> prefixes_to_request;
-    std::vector<FullHashInfo> cached_full_hash_infos;
-    cache->clear();
-
-    // Prefix has a cache entry but full hash is not there. (Case: 1-b-i)
-    CachedHashPrefixInfo* entry = &(*cache)[prefix];
-    entry->negative_expiry = now + base::TimeDelta::FromMinutes(5);
-    pm->GetFullHashCachedResults(matched_locally, now, &prefixes_to_request,
-                                 &cached_full_hash_infos);
-    EXPECT_TRUE(prefixes_to_request.empty());
-    EXPECT_TRUE(cached_full_hash_infos.empty());
-  }
-
-  {
-    std::vector<HashPrefix> prefixes_to_request;
-    std::vector<FullHashInfo> cached_full_hash_infos;
-    cache->clear();
-
-    // Expired negative cache entry. (Case: 1-b-ii)
-    CachedHashPrefixInfo* entry = &(*cache)[prefix];
-    entry->negative_expiry = now - base::TimeDelta::FromMinutes(5);
-    pm->GetFullHashCachedResults(matched_locally, now, &prefixes_to_request,
-                                 &cached_full_hash_infos);
-    ASSERT_EQ(1ul, prefixes_to_request.size());
-    EXPECT_EQ(prefix, prefixes_to_request[0]);
-    EXPECT_TRUE(cached_full_hash_infos.empty());
-  }
-
-  {
-    std::vector<HashPrefix> prefixes_to_request;
-    std::vector<FullHashInfo> cached_full_hash_infos;
-    cache->clear();
-
-    // Now put unexpired full hash in the cache. (Case: 1-a-i)
-    CachedHashPrefixInfo* entry = &(*cache)[prefix];
-    entry->negative_expiry = now + base::TimeDelta::FromMinutes(5);
-    entry->full_hash_infos.emplace_back(full_hash, GetUrlMalwareId(),
-                                        now + base::TimeDelta::FromMinutes(3));
-    pm->GetFullHashCachedResults(matched_locally, now, &prefixes_to_request,
-                                 &cached_full_hash_infos);
-    EXPECT_TRUE(prefixes_to_request.empty());
-    ASSERT_EQ(1ul, cached_full_hash_infos.size());
-    EXPECT_EQ(full_hash, cached_full_hash_infos[0].full_hash);
-  }
-
-  {
-    std::vector<HashPrefix> prefixes_to_request;
-    std::vector<FullHashInfo> cached_full_hash_infos;
-    cache->clear();
-
-    // Expire the full hash in the cache. (Case: 1-a-ii)
-    CachedHashPrefixInfo* entry = &(*cache)[prefix];
-    entry->negative_expiry = now + base::TimeDelta::FromMinutes(5);
-    entry->full_hash_infos.emplace_back(full_hash, GetUrlMalwareId(),
-                                        now - base::TimeDelta::FromMinutes(3));
-    pm->GetFullHashCachedResults(matched_locally, now, &prefixes_to_request,
-                                 &cached_full_hash_infos);
-    ASSERT_EQ(1ul, prefixes_to_request.size());
-    EXPECT_EQ(prefix, prefixes_to_request[0]);
-    EXPECT_TRUE(cached_full_hash_infos.empty());
-  }
-}
-
-TEST_F(V4GetHashProtocolManagerTest, TestUpdatesAreMerged) {
-  // We'll add one of the requested FullHashInfo objects into the cache, and
-  // inject the other one as a response from the server. The result should
-  // include both FullHashInfo objects.
-
-  std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager());
-  HashPrefix prefix_1("exam");
-  FullHash full_hash_1("example");
-  HashPrefix prefix_2("Everything");
-  FullHash full_hash_2("Everything's shiny, Cap'n.");
-
-  base::Time now = Time::Now();
-  SetTestClock(now, pm.get());
-
-  FullHashCache* cache = pm->full_hash_cache_for_tests();
-  CachedHashPrefixInfo* entry = &(*cache)[prefix_1];
-  entry->negative_expiry = now + base::TimeDelta::FromMinutes(100);
-  // Put one unexpired full hash in the cache for a store we'll look in.
-  entry->full_hash_infos.emplace_back(full_hash_1, GetUrlMalwareId(),
-                                      now + base::TimeDelta::FromSeconds(200));
-  // Put one unexpired full hash in the cache for a store we'll not look in.
-  entry->full_hash_infos.emplace_back(full_hash_1, GetUrlSocEngId(),
-                                      now + base::TimeDelta::FromSeconds(200));
-
-  // Request full hash information from two stores.
-  FullHashToStoreAndHashPrefixesMap matched_locally;
-  matched_locally[full_hash_1].push_back(
-      StoreAndHashPrefix(GetUrlMalwareId(), prefix_1));
-  matched_locally[full_hash_2].push_back(
-      StoreAndHashPrefix(GetChromeUrlApiId(), prefix_2));
-
-  // Expect full hash information from both stores.
-  std::vector<FullHashInfo> expected_results;
-  expected_results.emplace_back(full_hash_1, GetUrlMalwareId(),
-                                now + base::TimeDelta::FromSeconds(200));
-  expected_results.emplace_back(full_hash_2, GetChromeUrlApiId(),
-                                now + base::TimeDelta::FromSeconds(300));
-  expected_results[1].metadata.api_permissions.insert("NOTIFICATIONS");
-
-  pm->GetFullHashes(
-      matched_locally, {},
-      base::BindOnce(&V4GetHashProtocolManagerTest::ValidateGetV4HashResults,
-                     base::Unretained(this), expected_results));
-
-  SetupFetcherToReturnOKResponse(pm.get(), GetStockV4HashResponseInfos());
-
-  // No error, back off multiplier is unchanged.
-  EXPECT_EQ(0ul, pm->gethash_error_count_);
-  EXPECT_EQ(1ul, pm->gethash_back_off_mult_);
-
-  // Verify the state of the cache.
-  ASSERT_EQ(2u, cache->size());
-  const CachedHashPrefixInfo& cached_result_1 = cache->at(prefix_1);
-  EXPECT_EQ(cached_result_1.negative_expiry,
-            now + base::TimeDelta::FromMinutes(100));
-  ASSERT_EQ(2u, cached_result_1.full_hash_infos.size());
-  EXPECT_EQ(full_hash_1, cached_result_1.full_hash_infos[0].full_hash);
-  EXPECT_EQ(GetUrlMalwareId(), cached_result_1.full_hash_infos[0].list_id);
-
-  const CachedHashPrefixInfo& cached_result_2 = cache->at(prefix_2);
-  EXPECT_EQ(cached_result_2.negative_expiry,
-            now + base::TimeDelta::FromSeconds(600));
-  ASSERT_EQ(1u, cached_result_2.full_hash_infos.size());
-  EXPECT_EQ(full_hash_2, cached_result_2.full_hash_infos[0].full_hash);
-  EXPECT_EQ(GetChromeUrlApiId(), cached_result_2.full_hash_infos[0].list_id);
-  EXPECT_TRUE(callback_called());
-}
-
-// The server responds back with full hash information containing metadata
-// information for one of the full hashes for the URL in test.
-TEST_F(V4GetHashProtocolManagerTest, TestGetFullHashesWithApisMergesMetadata) {
-  const GURL url("https://www.example.com/more");
-  ThreatMetadata expected_md;
-  expected_md.api_permissions.insert("NOTIFICATIONS");
-  expected_md.api_permissions.insert("AUDIO_CAPTURE");
-  std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager());
-  pm->GetFullHashesWithApis(
-      url, {} /* list_client_states */,
-      base::BindOnce(&V4GetHashProtocolManagerTest::ValidateGetV4ApiResults,
-                     base::Unretained(this), expected_md));
-
-  // The following two random looking strings value are two of the full hashes
-  // produced by UrlToFullHashes in v4_protocol_manager_util.h for the URL:
-  // "https://www.example.com"
-  std::vector<ResponseInfo> infos;
-  FullHash full_hash;
-  base::Base64Decode("1ZzJ0/7NjPkg6t0DAS8L5Jf7jA48Pn7opQcP4UXYeXc=",
-                     &full_hash);
-  ResponseInfo info(full_hash, GetChromeUrlApiId());
-  info.key_values.emplace_back("permission", "NOTIFICATIONS");
-  infos.push_back(info);
-
-  base::Base64Decode("c9mG4AkGXxgsELy2pF2z1u2pSY-JMGVK8mU_ipOM2AE=",
-                     &full_hash);
-  info = ResponseInfo(full_hash, GetChromeUrlApiId());
-  info.key_values.emplace_back("permission", "AUDIO_CAPTURE");
-  infos.push_back(info);
-
-  full_hash = FullHash("Everything's shiny, Cap'n.");
-  info = ResponseInfo(full_hash, GetChromeUrlApiId());
-  info.key_values.emplace_back("permission", "GEOLOCATION");
-  infos.push_back(info);
-  SetupFetcherToReturnOKResponse(pm.get(), infos);
-
-  EXPECT_TRUE(callback_called());
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/db/v4_local_database_manager.cc b/components/safe_browsing/db/v4_local_database_manager.cc
deleted file mode 100644
index 3f06bea..0000000
--- a/components/safe_browsing/db/v4_local_database_manager.cc
+++ /dev/null
@@ -1,1062 +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.
-
-#include "components/safe_browsing/db/v4_local_database_manager.h"
-
-#include <utility>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/callback.h"
-#include "base/files/file_util.h"
-#include "base/memory/ref_counted.h"
-#include "base/metrics/histogram_functions.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/strings/string_tokenizer.h"
-#include "base/task/post_task.h"
-#include "build/branding_buildflags.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
-#include "components/safe_browsing/realtime/policy_engine.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
-#include "crypto/sha2.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-
-using content::BrowserThread;
-
-namespace safe_browsing {
-
-namespace {
-
-using CommandLineSwitchAndThreatType = std::pair<std::string, ThreatType>;
-
-// The expiration time of the full hash stored in the artificial database.
-const int64_t kFullHashExpiryTimeInMinutes = 60;
-
-const ThreatSeverity kLeastSeverity =
-    std::numeric_limits<ThreatSeverity>::max();
-
-// The list of the name of any store files that are no longer used and can be
-// safely deleted from the disk. There's no overlap allowed between the files
-// on this list and the list returned by GetListInfos().
-const char* const kStoreFileNamesToDelete[] = {
-    "AnyIpMalware.store", "ChromeFilenameClientIncident.store",
-    "UrlSuspiciousSiteId.store"};
-
-ListInfos GetListInfos() {
-// NOTE(vakh): When adding a store here, add the corresponding store-specific
-// histograms also.
-// The first argument to ListInfo specifies whether to sync hash prefixes for
-// that list. This can be false for two reasons:
-// - The server doesn't support that list yet. Once the server adds support
-//   for it, it can be changed to true.
-// - The list doesn't have hash prefixes to match. All requests lead to full
-//   hash checks. For instance: GetChromeUrlApiId()
-
-#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
-  const bool kSyncOnlyOnChromeBuilds = true;
-#else
-  const bool kSyncOnlyOnChromeBuilds = false;
-#endif
-  const bool kSyncAlways = true;
-  const bool kSyncNever = false;
-  return ListInfos({
-      ListInfo(kSyncAlways, "IpMalware.store", GetIpMalwareId(),
-               SB_THREAT_TYPE_UNUSED),
-      ListInfo(kSyncAlways, "UrlSoceng.store", GetUrlSocEngId(),
-               SB_THREAT_TYPE_URL_PHISHING),
-      ListInfo(kSyncAlways, "UrlMalware.store", GetUrlMalwareId(),
-               SB_THREAT_TYPE_URL_MALWARE),
-      ListInfo(kSyncAlways, "UrlUws.store", GetUrlUwsId(),
-               SB_THREAT_TYPE_URL_UNWANTED),
-      ListInfo(kSyncAlways, "UrlMalBin.store", GetUrlMalBinId(),
-               SB_THREAT_TYPE_URL_BINARY_MALWARE),
-      ListInfo(kSyncAlways, "ChromeExtMalware.store", GetChromeExtMalwareId(),
-               SB_THREAT_TYPE_EXTENSION),
-      ListInfo(kSyncOnlyOnChromeBuilds, "CertCsdDownloadWhitelist.store",
-               GetCertCsdDownloadWhitelistId(), SB_THREAT_TYPE_UNUSED),
-      ListInfo(kSyncOnlyOnChromeBuilds, "ChromeUrlClientIncident.store",
-               GetChromeUrlClientIncidentId(),
-               SB_THREAT_TYPE_BLACKLISTED_RESOURCE),
-      ListInfo(kSyncAlways, "UrlBilling.store", GetUrlBillingId(),
-               SB_THREAT_TYPE_BILLING),
-      ListInfo(kSyncOnlyOnChromeBuilds, "UrlCsdDownloadWhitelist.store",
-               GetUrlCsdDownloadWhitelistId(), SB_THREAT_TYPE_UNUSED),
-      ListInfo(kSyncOnlyOnChromeBuilds, "UrlCsdWhitelist.store",
-               GetUrlCsdWhitelistId(), SB_THREAT_TYPE_CSD_WHITELIST),
-      ListInfo(kSyncOnlyOnChromeBuilds, "UrlSubresourceFilter.store",
-               GetUrlSubresourceFilterId(), SB_THREAT_TYPE_SUBRESOURCE_FILTER),
-      ListInfo(kSyncOnlyOnChromeBuilds, "UrlSuspiciousSite.store",
-               GetUrlSuspiciousSiteId(), SB_THREAT_TYPE_SUSPICIOUS_SITE),
-      ListInfo(kSyncNever, "", GetChromeUrlApiId(), SB_THREAT_TYPE_API_ABUSE),
-      ListInfo(kSyncOnlyOnChromeBuilds, "UrlHighConfidenceAllowlist.store",
-               GetUrlHighConfidenceAllowlistId(),
-               SB_THREAT_TYPE_HIGH_CONFIDENCE_ALLOWLIST),
-  });
-  // NOTE(vakh): IMPORTANT: Please make sure that the server already supports
-  // any list before adding it to this list otherwise the prefix updates break
-  // for all Canary users.
-}
-
-std::vector<CommandLineSwitchAndThreatType> GetSwitchAndThreatTypes() {
-  static const std::vector<CommandLineSwitchAndThreatType>
-      command_line_switch_and_threat_type = {
-          {"mark_as_phishing", SOCIAL_ENGINEERING},
-          {"mark_as_malware", MALWARE_THREAT},
-          {"mark_as_uws", UNWANTED_SOFTWARE}};
-  return command_line_switch_and_threat_type;
-}
-
-// Returns the severity information about a given SafeBrowsing list. The lowest
-// value is 0, which represents the most severe list.
-ThreatSeverity GetThreatSeverity(const ListIdentifier& list_id) {
-  switch (list_id.threat_type()) {
-    case MALWARE_THREAT:
-    case SOCIAL_ENGINEERING:
-    case MALICIOUS_BINARY:
-      return 0;
-    case UNWANTED_SOFTWARE:
-      return 1;
-    case API_ABUSE:
-    case CLIENT_INCIDENT:
-    case SUBRESOURCE_FILTER:
-      return 2;
-    case CSD_WHITELIST:
-    case HIGH_CONFIDENCE_ALLOWLIST:
-      return 3;
-    case SUSPICIOUS:
-      return 4;
-    case BILLING:
-      return 15;
-    default:
-      NOTREACHED() << "Unexpected ThreatType encountered: "
-                   << list_id.threat_type();
-      return kLeastSeverity;
-  }
-}
-
-// This is only valid for types that are passed to GetBrowseUrl().
-ListIdentifier GetUrlIdFromSBThreatType(SBThreatType sb_threat_type) {
-  switch (sb_threat_type) {
-    case SB_THREAT_TYPE_URL_MALWARE:
-      return GetUrlMalwareId();
-
-    case SB_THREAT_TYPE_URL_PHISHING:
-      return GetUrlSocEngId();
-
-    case SB_THREAT_TYPE_URL_UNWANTED:
-      return GetUrlUwsId();
-
-    case SB_THREAT_TYPE_SUSPICIOUS_SITE:
-      return GetUrlSuspiciousSiteId();
-
-    case SB_THREAT_TYPE_BILLING:
-      return GetUrlBillingId();
-
-    default:
-      NOTREACHED();
-      // Compiler requires a return statement here.
-      return GetUrlMalwareId();
-  }
-}
-
-StoresToCheck CreateStoresToCheckFromSBThreatTypeSet(
-    const SBThreatTypeSet& threat_types) {
-  StoresToCheck stores_to_check;
-  for (SBThreatType sb_threat_type : threat_types) {
-    stores_to_check.insert(GetUrlIdFromSBThreatType(sb_threat_type));
-  }
-  return stores_to_check;
-}
-
-// These values are persisted to logs. Entries should not be renumbered and
-// numeric values should never be reused.
-enum StoreAvailabilityResult {
-  // Unknown availability. This is unexpected.
-  UNKNOWN = 0,
-
-  // The local database is not enabled.
-  NOT_ENABLED = 1,
-
-  // The database is still being loaded.
-  DATABASE_UNAVAILABLE = 2,
-
-  // The requested store is unavailable.
-  STORE_UNAVAILABLE = 3,
-
-  // The store is available.
-  AVAILABLE = 4,
-  COUNT,
-};
-
-}  // namespace
-
-V4LocalDatabaseManager::PendingCheck::PendingCheck(
-    Client* client,
-    ClientCallbackType client_callback_type,
-    const StoresToCheck& stores_to_check,
-    const std::vector<GURL>& urls)
-    : client(client),
-      client_callback_type(client_callback_type),
-      most_severe_threat_type(SB_THREAT_TYPE_SAFE),
-      stores_to_check(stores_to_check),
-      urls(urls) {
-  for (const auto& url : urls) {
-    V4ProtocolManagerUtil::UrlToFullHashes(url, &full_hashes);
-  }
-  full_hash_threat_types.assign(full_hashes.size(), SB_THREAT_TYPE_SAFE);
-}
-
-V4LocalDatabaseManager::PendingCheck::PendingCheck(
-    Client* client,
-    ClientCallbackType client_callback_type,
-    const StoresToCheck& stores_to_check,
-    const std::set<FullHash>& full_hashes_set)
-    : client(client),
-      client_callback_type(client_callback_type),
-      most_severe_threat_type(SB_THREAT_TYPE_SAFE),
-      stores_to_check(stores_to_check) {
-  full_hashes.assign(full_hashes_set.begin(), full_hashes_set.end());
-  DCHECK(full_hashes.size());
-  full_hash_threat_types.assign(full_hashes.size(), SB_THREAT_TYPE_SAFE);
-}
-
-V4LocalDatabaseManager::PendingCheck::~PendingCheck() {}
-
-// static
-const V4LocalDatabaseManager*
-    V4LocalDatabaseManager::current_local_database_manager_;
-
-// static
-scoped_refptr<V4LocalDatabaseManager> V4LocalDatabaseManager::Create(
-    const base::FilePath& base_path,
-    ExtendedReportingLevelCallback extended_reporting_level_callback) {
-  return base::WrapRefCounted(new V4LocalDatabaseManager(
-      base_path, extended_reporting_level_callback, nullptr));
-}
-
-void V4LocalDatabaseManager::CollectDatabaseManagerInfo(
-    DatabaseManagerInfo* database_manager_info,
-    FullHashCacheInfo* full_hash_cache_info) const {
-  if (v4_update_protocol_manager_) {
-    v4_update_protocol_manager_->CollectUpdateInfo(
-        database_manager_info->mutable_update_info());
-  }
-  if (v4_database_) {
-    v4_database_->CollectDatabaseInfo(
-        database_manager_info->mutable_database_info());
-  }
-  if (v4_get_hash_protocol_manager_) {
-    v4_get_hash_protocol_manager_->CollectFullHashCacheInfo(
-        full_hash_cache_info);
-  }
-}
-
-V4LocalDatabaseManager::V4LocalDatabaseManager(
-    const base::FilePath& base_path,
-    ExtendedReportingLevelCallback extended_reporting_level_callback,
-    scoped_refptr<base::SequencedTaskRunner> task_runner_for_tests)
-    : base_path_(base_path),
-      extended_reporting_level_callback_(extended_reporting_level_callback),
-      list_infos_(GetListInfos()),
-      task_runner_(task_runner_for_tests
-                       ? task_runner_for_tests
-                       : base::CreateSequencedTaskRunner(
-                             {base::ThreadPool(), base::MayBlock(),
-                              base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})) {
-  DCHECK(!base_path_.empty());
-  DCHECK(!list_infos_.empty());
-
-  DeleteUnusedStoreFiles();
-
-  DVLOG(1) << "V4LocalDatabaseManager::V4LocalDatabaseManager: "
-           << "base_path_: " << base_path_.AsUTF8Unsafe();
-}
-
-V4LocalDatabaseManager::~V4LocalDatabaseManager() {
-  DCHECK(!enabled_);
-}
-
-//
-// Start: SafeBrowsingDatabaseManager implementation
-//
-
-void V4LocalDatabaseManager::CancelCheck(Client* client) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DCHECK(enabled_);
-
-  auto pending_it = std::find_if(
-      std::begin(pending_checks_), std::end(pending_checks_),
-      [client](const PendingCheck* check) { return check->client == client; });
-  if (pending_it != pending_checks_.end()) {
-    pending_checks_.erase(pending_it);
-  }
-
-  auto queued_it =
-      std::find_if(std::begin(queued_checks_), std::end(queued_checks_),
-                   [&client](const std::unique_ptr<PendingCheck>& check) {
-                     return check->client == client;
-                   });
-  if (queued_it != queued_checks_.end()) {
-    queued_checks_.erase(queued_it);
-  }
-}
-
-bool V4LocalDatabaseManager::CanCheckResourceType(
-    content::ResourceType resource_type) const {
-  // We check all types since most checks are fast.
-  return true;
-}
-
-bool V4LocalDatabaseManager::CanCheckUrl(const GURL& url) const {
-  return url.SchemeIsHTTPOrHTTPS() || url.SchemeIs(url::kFtpScheme) ||
-         url.SchemeIsWSOrWSS();
-}
-
-bool V4LocalDatabaseManager::ChecksAreAlwaysAsync() const {
-  return false;
-}
-
-bool V4LocalDatabaseManager::CheckBrowseUrl(const GURL& url,
-                                            const SBThreatTypeSet& threat_types,
-                                            Client* client) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DCHECK(!threat_types.empty());
-  DCHECK(SBThreatTypeSetIsValidForCheckBrowseUrl(threat_types));
-
-  if (!enabled_ || !CanCheckUrl(url)) {
-    return true;
-  }
-
-  std::unique_ptr<PendingCheck> check = std::make_unique<PendingCheck>(
-      client, ClientCallbackType::CHECK_BROWSE_URL,
-      CreateStoresToCheckFromSBThreatTypeSet(threat_types),
-      std::vector<GURL>(1, url));
-
-  bool safe_synchronously = HandleCheck(std::move(check));
-  UMA_HISTOGRAM_BOOLEAN("SafeBrowsing.CheckBrowseUrl.HasLocalMatch",
-                        !safe_synchronously);
-  return safe_synchronously;
-}
-
-bool V4LocalDatabaseManager::CheckDownloadUrl(
-    const std::vector<GURL>& url_chain,
-    Client* client) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  if (!enabled_ || url_chain.empty()) {
-    return true;
-  }
-
-  std::unique_ptr<PendingCheck> check = std::make_unique<PendingCheck>(
-      client, ClientCallbackType::CHECK_DOWNLOAD_URLS,
-      StoresToCheck({GetUrlMalBinId()}), url_chain);
-
-  return HandleCheck(std::move(check));
-}
-
-bool V4LocalDatabaseManager::CheckExtensionIDs(
-    const std::set<FullHash>& extension_ids,
-    Client* client) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  if (!enabled_) {
-    return true;
-  }
-
-  std::unique_ptr<PendingCheck> check = std::make_unique<PendingCheck>(
-      client, ClientCallbackType::CHECK_EXTENSION_IDS,
-      StoresToCheck({GetChromeExtMalwareId()}), extension_ids);
-
-  return HandleCheck(std::move(check));
-}
-
-bool V4LocalDatabaseManager::CheckResourceUrl(const GURL& url, Client* client) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  StoresToCheck stores_to_check({GetChromeUrlClientIncidentId()});
-
-  if (!CanCheckUrl(url) || !AreAllStoresAvailableNow(stores_to_check)) {
-    // Fail open: Mark resource as safe immediately.
-    // TODO(nparker): This should queue the request if the DB isn't yet
-    // loaded, and later decide if this store is available.
-    // Currently this is the only store that requires full-hash-checks
-    // AND isn't supported on Chromium, so it's unique.
-    return true;
-  }
-
-  std::unique_ptr<PendingCheck> check = std::make_unique<PendingCheck>(
-      client, ClientCallbackType::CHECK_RESOURCE_URL, stores_to_check,
-      std::vector<GURL>(1, url));
-
-  return HandleCheck(std::move(check));
-}
-
-AsyncMatch V4LocalDatabaseManager::CheckUrlForHighConfidenceAllowlist(
-    const GURL& url,
-    Client* client) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  StoresToCheck stores_to_check({GetUrlHighConfidenceAllowlistId()});
-  if (!enabled_ || !CanCheckUrl(url) ||
-      !AreAllStoresAvailableNow(stores_to_check)) {
-    // NOTE(vakh): If Safe Browsing isn't enabled yet, or if the URL isn't a
-    // navigation URL, or if the allowlist isn't ready yet, return MATCH.
-    // The full URL check won't be performed, but hash-based check will still
-    // be done.
-    return AsyncMatch::MATCH;
-  }
-
-  std::unique_ptr<PendingCheck> check = std::make_unique<PendingCheck>(
-      client, ClientCallbackType::CHECK_HIGH_CONFIDENCE_ALLOWLIST,
-      stores_to_check, std::vector<GURL>(1, url));
-
-  return HandleWhitelistCheck(std::move(check));
-}
-
-bool V4LocalDatabaseManager::CheckUrlForSubresourceFilter(const GURL& url,
-                                                          Client* client) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  StoresToCheck stores_to_check(
-      {GetUrlSocEngId(), GetUrlSubresourceFilterId()});
-  if (!AreAnyStoresAvailableNow(stores_to_check) || !CanCheckUrl(url)) {
-    return true;
-  }
-
-  std::unique_ptr<PendingCheck> check = std::make_unique<PendingCheck>(
-      client, ClientCallbackType::CHECK_URL_FOR_SUBRESOURCE_FILTER,
-      stores_to_check, std::vector<GURL>(1, url));
-
-  return HandleCheck(std::move(check));
-}
-
-AsyncMatch V4LocalDatabaseManager::CheckCsdWhitelistUrl(const GURL& url,
-                                                        Client* client) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  StoresToCheck stores_to_check({GetUrlCsdWhitelistId()});
-  if (!AreAllStoresAvailableNow(stores_to_check) || !CanCheckUrl(url)) {
-    // Fail open: Whitelist everything. Otherwise we may run the
-    // CSD phishing/malware detector on popular domains and generate
-    // undue load on the client and server, or send Password Reputation
-    // requests on popular sites. This has the effect of disabling
-    // CSD phishing/malware detection and password reputation service
-    // until the store is first synced and/or loaded from disk.
-    return AsyncMatch::MATCH;
-  }
-
-  std::unique_ptr<PendingCheck> check = std::make_unique<PendingCheck>(
-      client, ClientCallbackType::CHECK_CSD_WHITELIST, stores_to_check,
-      std::vector<GURL>(1, url));
-
-  return HandleWhitelistCheck(std::move(check));
-}
-
-bool V4LocalDatabaseManager::MatchDownloadWhitelistString(
-    const std::string& str) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  StoresToCheck stores_to_check({GetCertCsdDownloadWhitelistId()});
-  if (!AreAllStoresAvailableNow(stores_to_check)) {
-    // Fail close: Whitelist nothing. This may generate download-protection
-    // pings for whitelisted binaries, but that's fine.
-    return false;
-  }
-
-  return HandleHashSynchronously(crypto::SHA256HashString(str),
-                                 stores_to_check);
-}
-
-bool V4LocalDatabaseManager::MatchDownloadWhitelistUrl(const GURL& url) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  StoresToCheck stores_to_check({GetUrlCsdDownloadWhitelistId()});
-
-  if (!AreAllStoresAvailableNow(stores_to_check) || !CanCheckUrl(url)) {
-    // Fail close: Whitelist nothing. This may generate download-protection
-    // pings for whitelisted domains, but that's fine.
-    return false;
-  }
-
-  return HandleUrlSynchronously(url, stores_to_check);
-}
-
-bool V4LocalDatabaseManager::MatchMalwareIP(const std::string& ip_address) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  if (!enabled_ || !v4_database_) {
-    return false;
-  }
-
-  FullHash hashed_encoded_ip;
-  if (!V4ProtocolManagerUtil::IPAddressToEncodedIPV6Hash(ip_address,
-                                                         &hashed_encoded_ip)) {
-    return false;
-  }
-
-  return HandleHashSynchronously(hashed_encoded_ip,
-                                 StoresToCheck({GetIpMalwareId()}));
-}
-
-ThreatSource V4LocalDatabaseManager::GetThreatSource() const {
-  return ThreatSource::LOCAL_PVER4;
-}
-
-bool V4LocalDatabaseManager::IsDownloadProtectionEnabled() const {
-  // TODO(vakh): Investigate the possibility of using a command line switch for
-  // this instead.
-  return true;
-}
-
-bool V4LocalDatabaseManager::IsSupported() const {
-  return true;
-}
-
-void V4LocalDatabaseManager::StartOnIOThread(
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    const V4ProtocolConfig& config) {
-  SafeBrowsingDatabaseManager::StartOnIOThread(url_loader_factory, config);
-
-  db_updated_callback_ = base::BindRepeating(
-      &V4LocalDatabaseManager::DatabaseUpdated, weak_factory_.GetWeakPtr());
-
-  SetupRealTimeUrlLookupService(url_loader_factory);
-  SetupUpdateProtocolManager(url_loader_factory, config);
-  SetupDatabase();
-
-  enabled_ = true;
-
-  current_local_database_manager_ = this;
-}
-
-void V4LocalDatabaseManager::StopOnIOThread(bool shutdown) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  enabled_ = false;
-
-  current_local_database_manager_ = nullptr;
-
-  pending_checks_.clear();
-
-  RespondSafeToQueuedChecks();
-
-  // Delete the V4Database. Any pending writes to disk are completed.
-  // This operation happens on the task_runner on which v4_database_ operates
-  // and doesn't block the IO thread.
-  V4Database::Destroy(std::move(v4_database_));
-
-  rt_url_lookup_service_.reset();
-
-  // Delete the V4UpdateProtocolManager.
-  // This cancels any in-flight update request.
-  v4_update_protocol_manager_.reset();
-
-  db_updated_callback_.Reset();
-
-  SafeBrowsingDatabaseManager::StopOnIOThread(shutdown);
-}
-
-//
-// End: SafeBrowsingDatabaseManager implementation
-//
-
-void V4LocalDatabaseManager::DatabaseReadyForChecks(
-    std::unique_ptr<V4Database> v4_database) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  // The following check is needed because it is possible that by the time the
-  // database is ready, StopOnIOThread has been called.
-  if (enabled_) {
-    V4Database::Destroy(std::move(v4_database_));
-    v4_database_ = std::move(v4_database);
-
-    v4_database_->RecordFileSizeHistograms();
-
-    PopulateArtificialDatabase();
-
-    // The consistency of the stores read from the disk needs to verified. Post
-    // that task on the task runner. It calls |DatabaseReadyForUpdates|
-    // callback with the stores to reset, if any, and then we can schedule the
-    // database updates.
-    v4_database_->VerifyChecksum(
-        base::BindOnce(&V4LocalDatabaseManager::DatabaseReadyForUpdates,
-                       weak_factory_.GetWeakPtr()));
-
-    ProcessQueuedChecks();
-  } else {
-    // Schedule the deletion of v4_database off IO thread.
-    V4Database::Destroy(std::move(v4_database));
-  }
-}
-
-void V4LocalDatabaseManager::DatabaseReadyForUpdates(
-    const std::vector<ListIdentifier>& stores_to_reset) {
-  if (enabled_) {
-    v4_database_->ResetStores(stores_to_reset);
-    UpdateListClientStates(GetStoreStateMap());
-
-    // The database is ready to process updates. Schedule them now.
-    v4_update_protocol_manager_->ScheduleNextUpdate(GetStoreStateMap());
-  }
-}
-
-void V4LocalDatabaseManager::DatabaseUpdated() {
-  if (enabled_) {
-    v4_update_protocol_manager_->ScheduleNextUpdate(GetStoreStateMap());
-
-    v4_database_->RecordFileSizeHistograms();
-    UpdateListClientStates(GetStoreStateMap());
-
-    base::PostTask(
-        FROM_HERE, {BrowserThread::UI},
-        base::BindOnce(
-            &SafeBrowsingDatabaseManager::NotifyDatabaseUpdateFinished, this));
-  }
-}
-
-void V4LocalDatabaseManager::DeleteUnusedStoreFiles() {
-  for (auto* const store_filename_to_delete : kStoreFileNamesToDelete) {
-    // Is the file marked for deletion also being used for a valid V4Store?
-    auto it = std::find_if(std::begin(list_infos_), std::end(list_infos_),
-                           [&store_filename_to_delete](ListInfo const& li) {
-                             return li.filename() == store_filename_to_delete;
-                           });
-    if (list_infos_.end() == it) {
-      const base::FilePath store_path =
-          base_path_.AppendASCII(store_filename_to_delete);
-      bool path_exists = base::PathExists(store_path);
-      base::UmaHistogramBoolean("SafeBrowsing.V4UnusedStoreFileExists" +
-                                    GetUmaSuffixForStore(store_path),
-                                path_exists);
-      if (!path_exists) {
-        continue;
-      }
-      task_runner_->PostTask(
-          FROM_HERE, base::BindOnce(base::IgnoreResult(&base::DeleteFile),
-                                    store_path, false /* recursive */));
-    } else {
-      NOTREACHED() << "Trying to delete a store file that's in use: "
-                   << store_filename_to_delete;
-    }
-  }
-}
-
-void V4LocalDatabaseManager::GetArtificialPrefixMatches(
-    const std::unique_ptr<PendingCheck>& check) {
-  if (artificially_marked_store_and_hash_prefixes_.empty()) {
-    return;
-  }
-  for (const auto& full_hash : check->full_hashes) {
-    for (const StoreAndHashPrefix& artificial_store_and_hash_prefix :
-         artificially_marked_store_and_hash_prefixes_) {
-      FullHash artificial_full_hash =
-          artificial_store_and_hash_prefix.hash_prefix;
-      DCHECK_EQ(crypto::kSHA256Length, artificial_full_hash.size());
-      if (artificial_full_hash == full_hash) {
-        (check->artificial_full_hash_to_store_and_hash_prefixes)[full_hash] = {
-            artificial_store_and_hash_prefix};
-      }
-    }
-  }
-}
-
-bool V4LocalDatabaseManager::GetPrefixMatches(
-    const std::unique_ptr<PendingCheck>& check) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DCHECK(enabled_);
-
-  check->full_hash_to_store_and_hash_prefixes.clear();
-  for (const auto& full_hash : check->full_hashes) {
-    StoreAndHashPrefixes matched_store_and_hash_prefixes;
-    v4_database_->GetStoresMatchingFullHash(full_hash, check->stores_to_check,
-                                            &matched_store_and_hash_prefixes);
-    if (!matched_store_and_hash_prefixes.empty()) {
-      (check->full_hash_to_store_and_hash_prefixes)[full_hash] =
-          matched_store_and_hash_prefixes;
-    }
-  }
-
-  return !check->full_hash_to_store_and_hash_prefixes.empty();
-}
-
-RealTimeUrlLookupService*
-V4LocalDatabaseManager::GetRealTimeUrlLookupService() {
-  return rt_url_lookup_service_.get();
-}
-
-void V4LocalDatabaseManager::GetSeverestThreatTypeAndMetadata(
-    const std::vector<FullHashInfo>& full_hash_infos,
-    const std::vector<FullHash>& full_hashes,
-    std::vector<SBThreatType>* full_hash_threat_types,
-    SBThreatType* most_severe_threat_type,
-    ThreatMetadata* metadata,
-    FullHash* matching_full_hash) {
-  ThreatSeverity most_severe_yet = kLeastSeverity;
-  for (const FullHashInfo& fhi : full_hash_infos) {
-    ThreatSeverity severity = GetThreatSeverity(fhi.list_id);
-    SBThreatType threat_type = GetSBThreatTypeForList(fhi.list_id);
-
-    const auto& it =
-        std::find(full_hashes.begin(), full_hashes.end(), fhi.full_hash);
-    DCHECK(it != full_hashes.end());
-    (*full_hash_threat_types)[it - full_hashes.begin()] = threat_type;
-
-    if (severity < most_severe_yet) {
-      most_severe_yet = severity;
-      *most_severe_threat_type = threat_type;
-      *metadata = fhi.metadata;
-      *matching_full_hash = fhi.full_hash;
-    }
-  }
-}
-
-StoresToCheck V4LocalDatabaseManager::GetStoresForFullHashRequests() {
-  StoresToCheck stores_for_full_hash;
-  for (const auto& info : list_infos_) {
-    stores_for_full_hash.insert(info.list_id());
-  }
-  return stores_for_full_hash;
-}
-
-std::unique_ptr<StoreStateMap> V4LocalDatabaseManager::GetStoreStateMap() {
-  return v4_database_->GetStoreStateMap();
-}
-
-// Returns the SBThreatType corresponding to a given SafeBrowsing list.
-SBThreatType V4LocalDatabaseManager::GetSBThreatTypeForList(
-    const ListIdentifier& list_id) {
-  auto it = std::find_if(
-      std::begin(list_infos_), std::end(list_infos_),
-      [&list_id](ListInfo const& li) { return li.list_id() == list_id; });
-  DCHECK(list_infos_.end() != it);
-  DCHECK_NE(SB_THREAT_TYPE_SAFE, it->sb_threat_type());
-  DCHECK_NE(SB_THREAT_TYPE_UNUSED, it->sb_threat_type());
-  return it->sb_threat_type();
-}
-
-AsyncMatch V4LocalDatabaseManager::HandleWhitelistCheck(
-    std::unique_ptr<PendingCheck> check) {
-  // We don't bother queuing whitelist checks since the DB will
-  // normally be available already -- whitelists are used after page load,
-  // and navigations are blocked until the DB is ready and dequeues checks.
-  // The caller should have already checked that the DB is ready.
-  DCHECK(v4_database_);
-
-  if (!GetPrefixMatches(check)) {
-    return AsyncMatch::NO_MATCH;
-  }
-
-  // Look for any full-length hash in the matches. If there is one,
-  // there's no need for a full-hash check. This saves bandwidth for
-  // very popular sites since they'll have full-length hashes locally.
-  // These loops will have exactly 1 entry most of the time.
-  for (const auto& entry : check->full_hash_to_store_and_hash_prefixes) {
-    for (const auto& store_and_prefix : entry.second) {
-      if (store_and_prefix.hash_prefix.size() == kMaxHashPrefixLength) {
-        return AsyncMatch::MATCH;
-      }
-    }
-  }
-
-  ScheduleFullHashCheck(std::move(check));
-  return AsyncMatch::ASYNC;
-}
-
-bool V4LocalDatabaseManager::HandleCheck(std::unique_ptr<PendingCheck> check) {
-  if (!v4_database_) {
-    queued_checks_.push_back(std::move(check));
-    return false;
-  }
-
-  GetPrefixMatches(check);
-  GetArtificialPrefixMatches(check);
-  if (check->full_hash_to_store_and_hash_prefixes.empty() &&
-      check->artificial_full_hash_to_store_and_hash_prefixes.empty()) {
-    return true;
-  }
-
-  ScheduleFullHashCheck(std::move(check));
-  return false;
-}
-
-void V4LocalDatabaseManager::PopulateArtificialDatabase() {
-  for (const auto& switch_and_threat_type : GetSwitchAndThreatTypes()) {
-    const std::string raw_artificial_urls =
-        base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
-            switch_and_threat_type.first);
-    base::StringTokenizer tokenizer(raw_artificial_urls, ",");
-    while (tokenizer.GetNext()) {
-      ListIdentifier artificial_list_id(GetCurrentPlatformType(), URL,
-                                        switch_and_threat_type.second);
-      FullHash full_hash =
-          V4ProtocolManagerUtil::GetFullHash(GURL(tokenizer.token()));
-      artificially_marked_store_and_hash_prefixes_.emplace_back(
-          artificial_list_id, full_hash);
-    }
-  }
-}
-
-void V4LocalDatabaseManager::ScheduleFullHashCheck(
-    std::unique_ptr<PendingCheck> check) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  // Add check to pending_checks_ before scheduling PerformFullHashCheck so that
-  // even if the client calls CancelCheck before PerformFullHashCheck gets
-  // called, the check can be found in pending_checks_.
-  pending_checks_.insert(check.get());
-
-  // If the full hash matches one from the artificial list, don't send the
-  // request to the server.
-  if (!check->artificial_full_hash_to_store_and_hash_prefixes.empty()) {
-    std::vector<FullHashInfo> full_hash_infos;
-    for (const auto& entry :
-         check->artificial_full_hash_to_store_and_hash_prefixes) {
-      for (const auto& store_and_prefix : entry.second) {
-        ListIdentifier list_id = store_and_prefix.list_id;
-        base::Time next = base::Time::Now() + base::TimeDelta::FromMinutes(
-                                                  kFullHashExpiryTimeInMinutes);
-        full_hash_infos.emplace_back(entry.first, list_id, next);
-      }
-    }
-    base::PostTask(FROM_HERE, {BrowserThread::IO},
-                   base::BindOnce(&V4LocalDatabaseManager::OnFullHashResponse,
-                                  weak_factory_.GetWeakPtr(), std::move(check),
-                                  full_hash_infos));
-  } else {
-    // Post on the IO thread to enforce async behavior.
-    base::PostTask(
-        FROM_HERE, {BrowserThread::IO},
-        base::BindOnce(&V4LocalDatabaseManager::PerformFullHashCheck,
-                       weak_factory_.GetWeakPtr(), std::move(check)));
-  }
-}
-
-bool V4LocalDatabaseManager::HandleHashSynchronously(
-    const FullHash& hash,
-    const StoresToCheck& stores_to_check) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  std::set<FullHash> hashes{hash};
-  std::unique_ptr<PendingCheck> check = std::make_unique<PendingCheck>(
-      nullptr, ClientCallbackType::CHECK_OTHER, stores_to_check, hashes);
-
-  return GetPrefixMatches(check);
-}
-
-bool V4LocalDatabaseManager::HandleUrlSynchronously(
-    const GURL& url,
-    const StoresToCheck& stores_to_check) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  std::unique_ptr<PendingCheck> check = std::make_unique<PendingCheck>(
-      nullptr, ClientCallbackType::CHECK_OTHER, stores_to_check,
-      std::vector<GURL>(1, url));
-
-  return GetPrefixMatches(check);
-}
-
-void V4LocalDatabaseManager::OnFullHashResponse(
-    std::unique_ptr<PendingCheck> check,
-    const std::vector<FullHashInfo>& full_hash_infos) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  if (!enabled_) {
-    DCHECK(pending_checks_.empty());
-    return;
-  }
-
-  const auto it = pending_checks_.find(check.get());
-  if (it == pending_checks_.end()) {
-    // The check has since been cancelled.
-    return;
-  }
-
-  // Find out the most severe threat, if any, to report to the client.
-  GetSeverestThreatTypeAndMetadata(
-      full_hash_infos, check->full_hashes, &check->full_hash_threat_types,
-      &check->most_severe_threat_type, &check->url_metadata,
-      &check->matching_full_hash);
-  pending_checks_.erase(it);
-  RespondToClient(std::move(check));
-}
-
-void V4LocalDatabaseManager::PerformFullHashCheck(
-    std::unique_ptr<PendingCheck> check) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  DCHECK(enabled_);
-  DCHECK(!check->full_hash_to_store_and_hash_prefixes.empty());
-
-  FullHashToStoreAndHashPrefixesMap full_hash_to_store_and_hash_prefixes =
-      check->full_hash_to_store_and_hash_prefixes;
-  v4_get_hash_protocol_manager_->GetFullHashes(
-      full_hash_to_store_and_hash_prefixes, list_client_states_,
-      base::BindOnce(&V4LocalDatabaseManager::OnFullHashResponse,
-                     weak_factory_.GetWeakPtr(), std::move(check)));
-}
-
-void V4LocalDatabaseManager::ProcessQueuedChecks() {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  // Steal the queue to protect against reentrant CancelCheck() calls.
-  QueuedChecks checks;
-  checks.swap(queued_checks_);
-
-  for (auto& it : checks) {
-    if (!GetPrefixMatches(it)) {
-      RespondToClient(std::move(it));
-    } else {
-      pending_checks_.insert(it.get());
-      PerformFullHashCheck(std::move(it));
-    }
-  }
-}
-
-void V4LocalDatabaseManager::RespondSafeToQueuedChecks() {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  // Steal the queue to protect against reentrant CancelCheck() calls.
-  QueuedChecks checks;
-  checks.swap(queued_checks_);
-
-  for (std::unique_ptr<PendingCheck>& it : checks) {
-    RespondToClient(std::move(it));
-  }
-}
-
-void V4LocalDatabaseManager::RespondToClient(
-    std::unique_ptr<PendingCheck> check) {
-  DCHECK(check);
-
-  switch (check->client_callback_type) {
-    case ClientCallbackType::CHECK_BROWSE_URL:
-    case ClientCallbackType::CHECK_URL_FOR_SUBRESOURCE_FILTER:
-      DCHECK_EQ(1u, check->urls.size());
-      check->client->OnCheckBrowseUrlResult(
-          check->urls[0], check->most_severe_threat_type, check->url_metadata);
-      break;
-
-    case ClientCallbackType::CHECK_DOWNLOAD_URLS:
-      check->client->OnCheckDownloadUrlResult(check->urls,
-                                              check->most_severe_threat_type);
-      break;
-
-    case ClientCallbackType::CHECK_HIGH_CONFIDENCE_ALLOWLIST: {
-      DCHECK_EQ(1u, check->urls.size());
-      bool did_match_allowlist = check->most_severe_threat_type ==
-                                 SB_THREAT_TYPE_HIGH_CONFIDENCE_ALLOWLIST;
-      DCHECK(did_match_allowlist ||
-             check->most_severe_threat_type == SB_THREAT_TYPE_SAFE);
-      check->client->OnCheckUrlForHighConfidenceAllowlist(did_match_allowlist);
-      break;
-    }
-
-    case ClientCallbackType::CHECK_RESOURCE_URL:
-      DCHECK_EQ(1u, check->urls.size());
-      check->client->OnCheckResourceUrlResult(check->urls[0],
-                                              check->most_severe_threat_type,
-                                              check->matching_full_hash);
-      break;
-
-    case ClientCallbackType::CHECK_CSD_WHITELIST: {
-      DCHECK_EQ(1u, check->urls.size());
-      bool did_match_allowlist =
-          check->most_severe_threat_type == SB_THREAT_TYPE_CSD_WHITELIST;
-      DCHECK(did_match_allowlist ||
-             check->most_severe_threat_type == SB_THREAT_TYPE_SAFE);
-      check->client->OnCheckWhitelistUrlResult(did_match_allowlist);
-      break;
-    }
-
-    case ClientCallbackType::CHECK_EXTENSION_IDS: {
-      DCHECK_EQ(check->full_hash_threat_types.size(),
-                check->full_hashes.size());
-      std::set<FullHash> unsafe_extension_ids;
-      for (size_t i = 0; i < check->full_hash_threat_types.size(); i++) {
-        if (check->full_hash_threat_types[i] == SB_THREAT_TYPE_EXTENSION) {
-          unsafe_extension_ids.insert(check->full_hashes[i]);
-        }
-      }
-      check->client->OnCheckExtensionsResult(unsafe_extension_ids);
-      break;
-    }
-    case ClientCallbackType::CHECK_OTHER:
-      NOTREACHED() << "Unexpected client_callback_type encountered";
-  }
-}
-
-void V4LocalDatabaseManager::SetupDatabase() {
-  DCHECK(!base_path_.empty());
-  DCHECK(!list_infos_.empty());
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  // Do not create the database on the IO thread since this may be an expensive
-  // operation. Instead, do that on the task_runner and when the new database
-  // has been created, swap it out on the IO thread.
-  NewDatabaseReadyCallback db_ready_callback =
-      base::BindOnce(&V4LocalDatabaseManager::DatabaseReadyForChecks,
-                     weak_factory_.GetWeakPtr());
-  V4Database::Create(task_runner_, base_path_, list_infos_,
-                     std::move(db_ready_callback));
-}
-
-void V4LocalDatabaseManager::SetupRealTimeUrlLookupService(
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  rt_url_lookup_service_ =
-      std::make_unique<RealTimeUrlLookupService>(url_loader_factory);
-}
-
-void V4LocalDatabaseManager::SetupUpdateProtocolManager(
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    const V4ProtocolConfig& config) {
-  V4UpdateCallback update_callback =
-      base::BindRepeating(&V4LocalDatabaseManager::UpdateRequestCompleted,
-                          weak_factory_.GetWeakPtr());
-
-  v4_update_protocol_manager_ = V4UpdateProtocolManager::Create(
-      url_loader_factory, config, update_callback,
-      extended_reporting_level_callback_);
-}
-
-void V4LocalDatabaseManager::UpdateRequestCompleted(
-    std::unique_ptr<ParsedServerResponse> parsed_server_response) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  v4_database_->ApplyUpdate(std::move(parsed_server_response),
-                            db_updated_callback_);
-}
-
-bool V4LocalDatabaseManager::AreAllStoresAvailableNow(
-    const StoresToCheck& stores_to_check) const {
-  StoreAvailabilityResult result = StoreAvailabilityResult::AVAILABLE;
-  if (!enabled_) {
-    result = StoreAvailabilityResult::NOT_ENABLED;
-  } else if (!v4_database_) {
-    result = StoreAvailabilityResult::DATABASE_UNAVAILABLE;
-  } else if (!v4_database_->AreAllStoresAvailable(stores_to_check)) {
-    result = StoreAvailabilityResult::STORE_UNAVAILABLE;
-  }
-
-  UMA_HISTOGRAM_ENUMERATION(
-      "SafeBrowsing.V4LocalDatabaseManager.AreAllStoresAvailableNow", result,
-      StoreAvailabilityResult::COUNT);
-  return (result == StoreAvailabilityResult::AVAILABLE);
-}
-
-bool V4LocalDatabaseManager::AreAnyStoresAvailableNow(
-    const StoresToCheck& stores_to_check) const {
-  return enabled_ && v4_database_ &&
-         v4_database_->AreAnyStoresAvailable(stores_to_check);
-}
-
-void V4LocalDatabaseManager::UpdateListClientStates(
-    const std::unique_ptr<StoreStateMap>& store_state_map) {
-  list_client_states_.clear();
-  V4ProtocolManagerUtil::GetListClientStatesFromStoreStateMap(
-      store_state_map, &list_client_states_);
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/db/v4_local_database_manager.h b/components/safe_browsing/db/v4_local_database_manager.h
deleted file mode 100644
index 98c2370..0000000
--- a/components/safe_browsing/db/v4_local_database_manager.h
+++ /dev/null
@@ -1,396 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_DB_V4_LOCAL_DATABASE_MANAGER_H_
-#define COMPONENTS_SAFE_BROWSING_DB_V4_LOCAL_DATABASE_MANAGER_H_
-
-// A class that provides the interface between the SafeBrowsing protocol manager
-// and database that holds the downloaded updates.
-
-#include <memory>
-#include <unordered_set>
-
-#include "base/memory/weak_ptr.h"
-#include "base/sequenced_task_runner.h"
-#include "components/safe_browsing/db/database_manager.h"
-#include "components/safe_browsing/db/hit_report.h"
-#include "components/safe_browsing/db/v4_database.h"
-#include "components/safe_browsing/db/v4_get_hash_protocol_manager.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
-#include "components/safe_browsing/db/v4_update_protocol_manager.h"
-#include "components/safe_browsing/proto/webui.pb.h"
-#include "components/safe_browsing/realtime/url_lookup_service.h"
-#include "url/gurl.h"
-
-namespace safe_browsing {
-
-typedef unsigned ThreatSeverity;
-
-// Manages the local, on-disk database of updates downloaded from the
-// SafeBrowsing service and interfaces with the protocol manager.
-class V4LocalDatabaseManager : public SafeBrowsingDatabaseManager {
- public:
-  // Create and return an instance of V4LocalDatabaseManager, if Finch trial
-  // allows it; nullptr otherwise.
-  static scoped_refptr<V4LocalDatabaseManager> Create(
-      const base::FilePath& base_path,
-      ExtendedReportingLevelCallback extended_reporting_level_callback);
-
-  // Populates the protobuf with the database data.
-  void CollectDatabaseManagerInfo(
-      DatabaseManagerInfo* v4_database_info,
-      FullHashCacheInfo* full_hash_cache_info) const;
-
-  // Return an instance of the V4LocalDatabaseManager object
-  static const V4LocalDatabaseManager* current_local_database_manager() {
-    return current_local_database_manager_;
-  }
-
-  //
-  // SafeBrowsingDatabaseManager implementation
-  //
-
-  void CancelCheck(Client* client) override;
-  bool CanCheckResourceType(content::ResourceType resource_type) const override;
-  bool CanCheckUrl(const GURL& url) const override;
-  bool ChecksAreAlwaysAsync() const override;
-  bool CheckBrowseUrl(const GURL& url,
-                      const SBThreatTypeSet& threat_types,
-                      Client* client) override;
-  AsyncMatch CheckCsdWhitelistUrl(const GURL& url, Client* client) override;
-  bool CheckDownloadUrl(const std::vector<GURL>& url_chain,
-                        Client* client) override;
-  // TODO(vakh): |CheckExtensionIDs| in the base class accepts a set of
-  // std::strings but the overriding method in this class accepts a set of
-  // FullHash objects. Since FullHash is currently std::string, it compiles,
-  // but this difference should be eliminated.
-  bool CheckExtensionIDs(const std::set<FullHash>& extension_ids,
-                         Client* client) override;
-  bool CheckResourceUrl(const GURL& url, Client* client) override;
-  AsyncMatch CheckUrlForHighConfidenceAllowlist(const GURL& url,
-                                                Client* client) override;
-  bool CheckUrlForSubresourceFilter(const GURL& url, Client* client) override;
-  bool MatchDownloadWhitelistString(const std::string& str) override;
-  bool MatchDownloadWhitelistUrl(const GURL& url) override;
-  bool MatchMalwareIP(const std::string& ip_address) override;
-  safe_browsing::ThreatSource GetThreatSource() const override;
-  bool IsDownloadProtectionEnabled() const override;
-  bool IsSupported() const override;
-
-  RealTimeUrlLookupService* GetRealTimeUrlLookupService() override;
-
-  void StartOnIOThread(
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      const V4ProtocolConfig& config) override;
-  void StopOnIOThread(bool shutdown) override;
-
-  // The stores/lists to always get full hashes for, regardless of which store
-  // the hash prefix matched. We request all lists since it makes the full hash
-  // cache management simpler and we expect very few lists to have overlap for
-  // the same hash prefix anyway.
-  StoresToCheck GetStoresForFullHashRequests() override;
-
-  std::unique_ptr<StoreStateMap> GetStoreStateMap() override;
-
-  //
-  // End: SafeBrowsingDatabaseManager implementation
-  //
-
- protected:
-  // Construct V4LocalDatabaseManager.
-  // Must be initialized by calling StartOnIOThread() before using.
-  V4LocalDatabaseManager(
-      const base::FilePath& base_path,
-      ExtendedReportingLevelCallback extended_reporting_level_callback,
-      scoped_refptr<base::SequencedTaskRunner> task_runner_for_tests);
-
-  ~V4LocalDatabaseManager() override;
-
-  enum class ClientCallbackType : int {
-    // This represents the case when we're trying to determine if a URL is
-    // unsafe from the following perspectives: Malware, Phishing, UwS.
-    CHECK_BROWSE_URL,
-
-    // This represents the case when we're trying to determine if any of the
-    // URLs in a vector of URLs is unsafe for downloading binaries.
-    CHECK_DOWNLOAD_URLS,
-
-    // This represents the case when we're trying to determine if a URL is an
-    // unsafe resource.
-    CHECK_RESOURCE_URL,
-
-    // This represents the case when we're trying to determine if a Chrome
-    // extension is a unsafe.
-    CHECK_EXTENSION_IDS,
-
-    // This respresents the case when we're trying to determine if a URL belongs
-    // to the list where subresource filter should be active.
-    CHECK_URL_FOR_SUBRESOURCE_FILTER,
-
-    // This respresents the case when we're trying to determine if a URL is
-    // part of the CSD whitelist.
-    CHECK_CSD_WHITELIST,
-
-    // TODO(vakh): Explain this.
-    CHECK_HIGH_CONFIDENCE_ALLOWLIST,
-
-    // This represents the other cases when a check is being performed
-    // synchronously so a client callback isn't required. For instance, when
-    // trying to determing if an IP address is unsafe due to hosting Malware.
-    CHECK_OTHER,
-  };
-
-  // The information we need to process a URL safety reputation request and
-  // respond to the SafeBrowsing client that asked for it.
-  struct PendingCheck {
-    PendingCheck(Client* client,
-                 ClientCallbackType client_callback_type,
-                 const StoresToCheck& stores_to_check,
-                 const std::vector<GURL>& urls);
-
-    PendingCheck(Client* client,
-                 ClientCallbackType client_callback_type,
-                 const StoresToCheck& stores_to_check,
-                 const std::set<FullHash>& full_hashes);
-
-    ~PendingCheck();
-
-    // The SafeBrowsing client that's waiting for the safe/unsafe verdict.
-    Client* client;
-
-    // Determines which funtion from the |client| needs to be called once we
-    // know whether the URL in |url| is safe or unsafe.
-    const ClientCallbackType client_callback_type;
-
-    // The most severe threat verdict for the URLs/hashes being checked.
-    SBThreatType most_severe_threat_type;
-
-    // When the check was sent to the SafeBrowsing service. Used to record the
-    // time it takes to get the uncached full hashes from the service (or a
-    // cached full hash response).
-    base::TimeTicks full_hash_check_start;
-
-    // The SafeBrowsing lists to check hash prefixes in.
-    const StoresToCheck stores_to_check;
-
-    // The URLs that are being checked for being unsafe. The size of exactly
-    // one of |full_hashes| and |urls| should be greater than 0.
-    const std::vector<GURL> urls;
-
-    // The full hashes that are being checked for being safe.
-    std::vector<FullHash> full_hashes;
-
-    // The most severe SBThreatType for each full hash in |full_hashes|. The
-    // length of |full_hash_threat_type| must always match |full_hashes|.
-    std::vector<SBThreatType> full_hash_threat_types;
-
-    // List of full hashes of urls we are checking and corresponding store and
-    // hash prefixes that match it in the local database.
-    FullHashToStoreAndHashPrefixesMap full_hash_to_store_and_hash_prefixes;
-
-    // List of full hashes of urls we are checking and corresponding store and
-    // hash prefixes that match it in the artificial database.
-    FullHashToStoreAndHashPrefixesMap
-        artificial_full_hash_to_store_and_hash_prefixes;
-
-    // The metadata associated with the full hash of the severest match found
-    // for that URL.
-    ThreatMetadata url_metadata;
-
-    // The full hash that matched for a blacklisted resource URL. Used only for
-    // |CheckResourceUrl| case.
-    FullHash matching_full_hash;
-  };
-
-  typedef std::vector<std::unique_ptr<PendingCheck>> QueuedChecks;
-
- private:
-  friend class V4LocalDatabaseManagerTest;
-  FRIEND_TEST_ALL_PREFIXES(V4LocalDatabaseManagerTest,
-                           TestGetSeverestThreatTypeAndMetadata);
-  FRIEND_TEST_ALL_PREFIXES(V4LocalDatabaseManagerTest, NotificationOnUpdate);
-
-  // The checks awaiting a full hash response from SafeBrowsing service.
-  typedef std::unordered_set<const PendingCheck*> PendingChecks;
-
-  // Called when all the stores managed by the database have been read from
-  // disk after startup and the database is ready for checking resource
-  // reputation.
-  void DatabaseReadyForChecks(std::unique_ptr<V4Database> v4_database);
-
-  // Called when all the stores managed by the database have been verified for
-  // checksum correctness after startup and the database is ready for applying
-  // updates.
-  void DatabaseReadyForUpdates(
-      const std::vector<ListIdentifier>& stores_to_reset);
-
-  // Called when the database has been updated and schedules the next update.
-  void DatabaseUpdated();
-
-  // Delete any *.store files from disk that are no longer used.
-  void DeleteUnusedStoreFiles();
-
-  // Matches the full_hashes for a |check| with the hashes stored in
-  // |artificially_marked_store_and_hash_prefixes_|. For each full hash match,
-  // it populates |full_hash_to_store_and_hash_prefixes| with the matched hash
-  // prefix and store.
-  void GetArtificialPrefixMatches(const std::unique_ptr<PendingCheck>& check);
-
-  // Identifies the prefixes and the store they matched in, for a given |check|.
-  // Returns true if one or more hash prefix matches are found; false otherwise.
-  bool GetPrefixMatches(const std::unique_ptr<PendingCheck>& check);
-
-  // Goes over the |full_hash_infos| and stores the most severe SBThreatType in
-  // |most_severe_threat_type|, the corresponding metadata in |metadata|, and
-  // the matching full hash in |matching_full_hash|. Also, updates in
-  // |full_hash_threat_types|, the threat type for each full hash in
-  // |full_hashes|.
-  void GetSeverestThreatTypeAndMetadata(
-      const std::vector<FullHashInfo>& full_hash_infos,
-      const std::vector<FullHash>& full_hashes,
-      std::vector<SBThreatType>* full_hash_threat_types,
-      SBThreatType* most_severe_threat_type,
-      ThreatMetadata* metadata,
-      FullHash* matching_full_hash);
-
-  // Returns the SBThreatType for a given ListIdentifier.
-  SBThreatType GetSBThreatTypeForList(const ListIdentifier& list_id);
-
-  // Queues the check for async response if the database isn't ready yet.
-  // If the database is ready, checks the database for prefix matches and
-  // returns true immediately if there's no match. If a match is found, it
-  // schedules a task to perform full hash check and returns false.
-  bool HandleCheck(std::unique_ptr<PendingCheck> check);
-
-  // Like HandleCheck, but for whitelists that have both full-hashes and
-  // partial hashes in the DB. Returns MATCH, NO_MATCH, or ASYNC.
-  AsyncMatch HandleWhitelistCheck(std::unique_ptr<PendingCheck> check);
-
-  // Computes the hashes of URLs that have artificially been marked as unsafe
-  // using any of the following command line flags: "mark_as_phishing",
-  // "mark_as_malware", "mark_as_uws".
-  void PopulateArtificialDatabase();
-
-  // Schedules a full-hash check for a given set of prefixes.
-  void ScheduleFullHashCheck(std::unique_ptr<PendingCheck> check);
-
-  // Checks |stores_to_check| in database synchronously for hash prefixes
-  // matching |hash|. Returns true if there's a match; false otherwise. This is
-  // used for lists that have full hash information in the database.
-  bool HandleHashSynchronously(const FullHash& hash,
-                               const StoresToCheck& stores_to_check);
-
-  // Checks |stores_to_check| in database synchronously for hash prefixes
-  // matching the full hashes for |url|. See |HandleHashSynchronously| for
-  // details.
-  bool HandleUrlSynchronously(const GURL& url,
-                              const StoresToCheck& stores_to_check);
-
-  // Called when the |v4_get_hash_protocol_manager_| has the full hash response
-  // available for the URL that we requested. It determines the severest
-  // threat type and responds to the |client| with that information.
-  void OnFullHashResponse(std::unique_ptr<PendingCheck> pending_check,
-                          const std::vector<FullHashInfo>& full_hash_infos);
-
-  // Performs the full hash checking of the URL in |check|.
-  virtual void PerformFullHashCheck(std::unique_ptr<PendingCheck> check);
-
-  // When the database is ready to use, process the checks that were queued
-  // while the database was loading from disk.
-  void ProcessQueuedChecks();
-
-  // Called on StopOnIOThread, it responds to the clients that are waiting for
-  // the database to become available with the verdict as SAFE.
-  void RespondSafeToQueuedChecks();
-
-  // Calls the appopriate method on the |client| object, based on the contents
-  // of |pending_check|.
-  void RespondToClient(std::unique_ptr<PendingCheck> pending_check);
-
-  // Instantiates and initializes |v4_database_| on the task runner. Sets up the
-  // callback for |DatabaseReady| when the database is ready for use.
-  void SetupDatabase();
-
-  // Instantiates and initializes |rt_url_lookup_service_|.
-  void SetupRealTimeUrlLookupService(
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
-
-  // Instantiates and initializes |v4_update_protocol_manager_|.
-  void SetupUpdateProtocolManager(
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      const V4ProtocolConfig& config);
-
-  // Updates the |list_client_states_| with the state information in
-  // |store_state_map|.
-  void UpdateListClientStates(
-      const std::unique_ptr<StoreStateMap>& store_state_map);
-
-  // The callback called each time the protocol manager downloads updates
-  // successfully.
-  void UpdateRequestCompleted(
-      std::unique_ptr<ParsedServerResponse> parsed_server_response);
-
-  // Return true if we're enabled and have loaded real data for all of
-  // these stores.
-  bool AreAllStoresAvailableNow(const StoresToCheck& stores_to_check) const;
-
-  // Return true if we're enabled and have loaded real data for any of
-  // these stores.
-  bool AreAnyStoresAvailableNow(const StoresToCheck& stores_to_check) const;
-
-  // Stores full hashes of URLs that have been artificially marked as unsafe.
-  StoreAndHashPrefixes artificially_marked_store_and_hash_prefixes_;
-
-  // The base directory under which to create the files that contain hashes.
-  const base::FilePath base_path_;
-
-  // Instance of the V4LocalDatabaseManager object
-  static const V4LocalDatabaseManager* current_local_database_manager_;
-
-  // Called when the V4Database has finished applying the latest update and is
-  // ready to process next update.
-  DatabaseUpdatedCallback db_updated_callback_;
-
-  // Callback to get the current extended reporting level. Needed by the update
-  // manager.
-  ExtendedReportingLevelCallback extended_reporting_level_callback_;
-
-  // The client_state of each list currently being synced. This is updated each
-  // time a database update completes, and used to send list client_state
-  // information in the full hash request.
-  std::vector<std::string> list_client_states_;
-
-  // The list of stores to manage (for hash prefixes and full hashes). Each
-  // element contains the identifier for the store, the corresponding
-  // SBThreatType, whether to fetch hash prefixes for that store, and the
-  // name of the file on disk that would contain the prefixes, if applicable.
-  ListInfos list_infos_;
-
-  // The checks awaiting for a full hash response from the SafeBrowsing service.
-  PendingChecks pending_checks_;
-
-  // The checks that need to be scheduled when the database becomes ready for
-  // use.
-  QueuedChecks queued_checks_;
-
-  std::unique_ptr<RealTimeUrlLookupService> rt_url_lookup_service_;
-
-  // The sequenced task runner for running safe browsing database operations.
-  scoped_refptr<base::SequencedTaskRunner> task_runner_;
-
-  // The database that manages the stores containing the hash prefix updates.
-  // All writes to this variable must happen on the IO thread only.
-  std::unique_ptr<V4Database> v4_database_;
-
-  // The protocol manager that downloads the hash prefix updates.
-  std::unique_ptr<V4UpdateProtocolManager> v4_update_protocol_manager_;
-
-  base::WeakPtrFactory<V4LocalDatabaseManager> weak_factory_{this};
-
-  DISALLOW_COPY_AND_ASSIGN(V4LocalDatabaseManager);
-};  // class V4LocalDatabaseManager
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_DB_V4_LOCAL_DATABASE_MANAGER_H_
diff --git a/components/safe_browsing/db/v4_local_database_manager_unittest.cc b/components/safe_browsing/db/v4_local_database_manager_unittest.cc
deleted file mode 100644
index 7aa21de..0000000
--- a/components/safe_browsing/db/v4_local_database_manager_unittest.cc
+++ /dev/null
@@ -1,1456 +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.
-
-#include "components/safe_browsing/db/v4_local_database_manager.h"
-
-#include <utility>
-
-#include "base/bind.h"
-#include "base/command_line.h"
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/files/scoped_temp_dir.h"
-#include "base/memory/ref_counted.h"
-#include "base/run_loop.h"
-#include "base/sequenced_task_runner.h"
-#include "base/strings/string_tokenizer.h"
-#include "base/test/scoped_feature_list.h"
-#include "base/test/test_simple_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "components/safe_browsing/db/v4_database.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
-#include "components/safe_browsing/db/v4_test_util.h"
-#include "components/safe_browsing/features.h"
-#include "content/public/test/browser_task_environment.h"
-#include "crypto/sha2.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
-#include "services/network/test/test_url_loader_factory.h"
-#include "testing/platform_test.h"
-
-namespace safe_browsing {
-
-namespace {
-
-typedef std::vector<FullHashInfo> FullHashInfos;
-
-// Utility function for populating hashes.
-FullHash HashForUrl(const GURL& url) {
-  std::vector<FullHash> full_hashes;
-  V4ProtocolManagerUtil::UrlToFullHashes(url, &full_hashes);
-  // ASSERT_GE(full_hashes.size(), 1u);
-  return full_hashes[0];
-}
-
-// Use this if you want GetFullHashes() to always return prescribed results.
-class FakeGetHashProtocolManager : public V4GetHashProtocolManager {
- public:
-  FakeGetHashProtocolManager(
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      const StoresToCheck& stores_to_check,
-      const V4ProtocolConfig& config,
-      const FullHashInfos& full_hash_infos)
-      : V4GetHashProtocolManager(url_loader_factory, stores_to_check, config),
-        full_hash_infos_(full_hash_infos) {}
-
-  void GetFullHashes(const FullHashToStoreAndHashPrefixesMap,
-                     const std::vector<std::string>&,
-                     FullHashCallback callback) override {
-    // Async, since the real manager might use a fetcher.
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(std::move(callback), full_hash_infos_));
-  }
-
- private:
-  FullHashInfos full_hash_infos_;
-};
-
-class FakeGetHashProtocolManagerFactory
-    : public V4GetHashProtocolManagerFactory {
- public:
-  FakeGetHashProtocolManagerFactory(const FullHashInfos& full_hash_infos)
-      : full_hash_infos_(full_hash_infos) {}
-
-  std::unique_ptr<V4GetHashProtocolManager> CreateProtocolManager(
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      const StoresToCheck& stores_to_check,
-      const V4ProtocolConfig& config) override {
-    return std::make_unique<FakeGetHashProtocolManager>(
-        url_loader_factory, stores_to_check, config, full_hash_infos_);
-  }
-
- private:
-  FullHashInfos full_hash_infos_;
-};
-
-// Use FakeGetHashProtocolManagerFactory in scope, then reset.
-// You should make sure the DatabaseManager is created _after_ this.
-class ScopedFakeGetHashProtocolManagerFactory {
- public:
-  ScopedFakeGetHashProtocolManagerFactory(
-      const FullHashInfos& full_hash_infos) {
-    V4GetHashProtocolManager::RegisterFactory(
-        std::make_unique<FakeGetHashProtocolManagerFactory>(full_hash_infos));
-  }
-  ~ScopedFakeGetHashProtocolManagerFactory() {
-    V4GetHashProtocolManager::RegisterFactory(nullptr);
-  }
-};
-
-}  // namespace
-
-class FakeV4Database : public V4Database {
- public:
-  static void Create(
-      const scoped_refptr<base::SequencedTaskRunner>& db_task_runner,
-      std::unique_ptr<StoreMap> store_map,
-      const StoreAndHashPrefixes& store_and_hash_prefixes,
-      NewDatabaseReadyCallback new_db_callback,
-      bool stores_available) {
-    // Mimics V4Database::Create
-    const scoped_refptr<base::SingleThreadTaskRunner>& callback_task_runner =
-        base::ThreadTaskRunnerHandle::Get();
-    db_task_runner->PostTask(
-        FROM_HERE,
-        base::BindOnce(&FakeV4Database::CreateOnTaskRunner, db_task_runner,
-                       std::move(store_map), store_and_hash_prefixes,
-                       callback_task_runner, std::move(new_db_callback),
-                       stores_available));
-  }
-
-  // V4Database implementation
-  void GetStoresMatchingFullHash(
-      const FullHash& full_hash,
-      const StoresToCheck& stores_to_check,
-      StoreAndHashPrefixes* store_and_hash_prefixes) override {
-    store_and_hash_prefixes->clear();
-    for (const StoreAndHashPrefix& stored_sahp : store_and_hash_prefixes_) {
-      if (stores_to_check.count(stored_sahp.list_id) == 0)
-        continue;
-      const PrefixSize& prefix_size = stored_sahp.hash_prefix.size();
-      if (!full_hash.compare(0, prefix_size, stored_sahp.hash_prefix)) {
-        store_and_hash_prefixes->push_back(stored_sahp);
-      }
-    }
-  }
-
-  bool AreAllStoresAvailable(
-      const StoresToCheck& stores_to_check) const override {
-    return stores_available_;
-  }
-
-  bool AreAnyStoresAvailable(
-      const StoresToCheck& stores_to_check) const override {
-    return stores_available_;
-  }
-
- private:
-  static void CreateOnTaskRunner(
-      const scoped_refptr<base::SequencedTaskRunner>& db_task_runner,
-      std::unique_ptr<StoreMap> store_map,
-      const StoreAndHashPrefixes& store_and_hash_prefixes,
-      const scoped_refptr<base::SingleThreadTaskRunner>& callback_task_runner,
-      NewDatabaseReadyCallback new_db_callback,
-      bool stores_available) {
-    // Mimics the semantics of V4Database::CreateOnTaskRunner
-    std::unique_ptr<FakeV4Database> fake_v4_database(
-        new FakeV4Database(db_task_runner, std::move(store_map),
-                           store_and_hash_prefixes, stores_available));
-    callback_task_runner->PostTask(FROM_HERE,
-                                   base::BindOnce(std::move(new_db_callback),
-                                                  std::move(fake_v4_database)));
-  }
-
-  FakeV4Database(const scoped_refptr<base::SequencedTaskRunner>& db_task_runner,
-                 std::unique_ptr<StoreMap> store_map,
-                 const StoreAndHashPrefixes& store_and_hash_prefixes,
-                 bool stores_available)
-      : V4Database(db_task_runner, std::move(store_map)),
-        store_and_hash_prefixes_(store_and_hash_prefixes),
-        stores_available_(stores_available) {}
-
-  const StoreAndHashPrefixes store_and_hash_prefixes_;
-  const bool stores_available_;
-};
-
-// TODO(nparker): This might be simpler with a mock and EXPECT calls.
-// That would also catch unexpected calls.
-class TestClient : public SafeBrowsingDatabaseManager::Client {
- public:
-  TestClient(SBThreatType sb_threat_type,
-             const GURL& url,
-             V4LocalDatabaseManager* manager_to_cancel = nullptr)
-      : expected_sb_threat_type_(sb_threat_type),
-        expected_urls_(1, url),
-        manager_to_cancel_(manager_to_cancel) {}
-
-  TestClient(SBThreatType sb_threat_type, const std::vector<GURL>& url_chain)
-      : expected_sb_threat_type_(sb_threat_type), expected_urls_(url_chain) {}
-
-  void OnCheckBrowseUrlResult(const GURL& url,
-                              SBThreatType threat_type,
-                              const ThreatMetadata& metadata) override {
-    ASSERT_EQ(expected_urls_[0], url);
-    ASSERT_EQ(expected_sb_threat_type_, threat_type);
-    on_check_browse_url_result_called_ = true;
-    if (manager_to_cancel_) {
-      manager_to_cancel_->CancelCheck(this);
-    }
-  }
-
-  void OnCheckResourceUrlResult(const GURL& url,
-                                SBThreatType threat_type,
-                                const std::string& threat_hash) override {
-    ASSERT_EQ(expected_urls_[0], url);
-    ASSERT_EQ(expected_sb_threat_type_, threat_type);
-    ASSERT_EQ(threat_type == SB_THREAT_TYPE_SAFE, threat_hash.empty());
-    on_check_resource_url_result_called_ = true;
-  }
-
-  void OnCheckDownloadUrlResult(const std::vector<GURL>& url_chain,
-                                SBThreatType threat_type) override {
-    ASSERT_EQ(expected_urls_, url_chain);
-    ASSERT_EQ(expected_sb_threat_type_, threat_type);
-    on_check_download_urls_result_called_ = true;
-  }
-
-  std::vector<GURL>* mutable_expected_urls() { return &expected_urls_; }
-
-  bool on_check_browse_url_result_called() {
-    return on_check_browse_url_result_called_;
-  }
-  bool on_check_download_urls_result_called() {
-    return on_check_download_urls_result_called_;
-  }
-  bool on_check_resource_url_result_called() {
-    return on_check_resource_url_result_called_;
-  }
-
- private:
-  const SBThreatType expected_sb_threat_type_;
-  std::vector<GURL> expected_urls_;
-  bool on_check_browse_url_result_called_ = false;
-  bool on_check_download_urls_result_called_ = false;
-  bool on_check_resource_url_result_called_ = false;
-  V4LocalDatabaseManager* manager_to_cancel_;
-};
-
-class TestAllowlistClient : public SafeBrowsingDatabaseManager::Client {
- public:
-  // |match_expected| specifies whether a full hash match is expected.
-  // |expected_sb_threat_type| identifies which callback method to expect to get
-  // called.
-  explicit TestAllowlistClient(bool match_expected,
-                               SBThreatType expected_sb_threat_type)
-      : expected_sb_threat_type_(expected_sb_threat_type),
-        match_expected_(match_expected) {}
-
-  void OnCheckWhitelistUrlResult(bool is_allowlisted) override {
-    EXPECT_EQ(match_expected_, is_allowlisted);
-    EXPECT_EQ(SB_THREAT_TYPE_CSD_WHITELIST, expected_sb_threat_type_);
-    callback_called_ = true;
-  }
-
-  void OnCheckUrlForHighConfidenceAllowlist(bool is_allowlisted) override {
-    EXPECT_EQ(match_expected_, is_allowlisted);
-    EXPECT_EQ(SB_THREAT_TYPE_HIGH_CONFIDENCE_ALLOWLIST,
-              expected_sb_threat_type_);
-    callback_called_ = true;
-  }
-
-  bool callback_called() { return callback_called_; }
-
- private:
-  const SBThreatType expected_sb_threat_type_;
-  const bool match_expected_;
-  bool callback_called_ = false;
-};
-
-class TestExtensionClient : public SafeBrowsingDatabaseManager::Client {
- public:
-  TestExtensionClient(const std::set<FullHash>& expected_bad_crxs)
-      : expected_bad_crxs_(expected_bad_crxs),
-        on_check_extensions_result_called_(false) {}
-
-  void OnCheckExtensionsResult(const std::set<FullHash>& bad_crxs) override {
-    EXPECT_EQ(expected_bad_crxs_, bad_crxs);
-    on_check_extensions_result_called_ = true;
-  }
-
-  bool on_check_extensions_result_called() {
-    return on_check_extensions_result_called_;
-  }
-
- private:
-  const std::set<FullHash> expected_bad_crxs_;
-  bool on_check_extensions_result_called_;
-};
-
-class FakeV4LocalDatabaseManager : public V4LocalDatabaseManager {
- public:
-  FakeV4LocalDatabaseManager(
-      const base::FilePath& base_path,
-      ExtendedReportingLevelCallback extended_reporting_level_callback,
-      scoped_refptr<base::SequencedTaskRunner> task_runner)
-      : V4LocalDatabaseManager(base_path,
-                               extended_reporting_level_callback,
-                               task_runner),
-        perform_full_hash_check_called_(false) {}
-
-  // V4LocalDatabaseManager impl:
-  void PerformFullHashCheck(std::unique_ptr<PendingCheck> check) override {
-    perform_full_hash_check_called_ = true;
-  }
-
-  static bool PerformFullHashCheckCalled(
-      scoped_refptr<safe_browsing::V4LocalDatabaseManager>& v4_ldbm) {
-    FakeV4LocalDatabaseManager* fake =
-        static_cast<FakeV4LocalDatabaseManager*>(v4_ldbm.get());
-    return fake->perform_full_hash_check_called_;
-  }
-
- private:
-  ~FakeV4LocalDatabaseManager() override {}
-
-  bool perform_full_hash_check_called_;
-};
-
-class V4LocalDatabaseManagerTest : public PlatformTest {
- public:
-  V4LocalDatabaseManagerTest() : task_runner_(new base::TestSimpleTaskRunner) {}
-
-  void SetUp() override {
-    PlatformTest::SetUp();
-
-    test_shared_loader_factory_ =
-        base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
-            &test_url_loader_factory_);
-
-    ASSERT_TRUE(base_dir_.CreateUniqueTempDir());
-    DVLOG(1) << "base_dir_: " << base_dir_.GetPath().value();
-
-    extended_reporting_level_ = SBER_LEVEL_OFF;
-    erl_callback_ = base::BindRepeating(
-        &V4LocalDatabaseManagerTest::GetExtendedReportingLevel,
-        base::Unretained(this));
-
-    v4_local_database_manager_ =
-        base::WrapRefCounted(new V4LocalDatabaseManager(
-            base_dir_.GetPath(), erl_callback_, task_runner_));
-
-    StartLocalDatabaseManager();
-  }
-
-  void TearDown() override {
-    StopLocalDatabaseManager();
-
-    PlatformTest::TearDown();
-  }
-
-  void ForceDisableLocalDatabaseManager() {
-    v4_local_database_manager_->enabled_ = false;
-  }
-
-  void ForceEnableLocalDatabaseManager() {
-    v4_local_database_manager_->enabled_ = true;
-  }
-
-  const V4LocalDatabaseManager::QueuedChecks& GetQueuedChecks() {
-    return v4_local_database_manager_->queued_checks_;
-  }
-
-  ExtendedReportingLevel GetExtendedReportingLevel() {
-    return extended_reporting_level_;
-  }
-
-  void PopulateArtificialDatabase() {
-    v4_local_database_manager_->PopulateArtificialDatabase();
-  }
-
-  void ReplaceV4Database(const StoreAndHashPrefixes& store_and_hash_prefixes,
-                         bool stores_available = false) {
-    // Disable the V4LocalDatabaseManager first so that if the callback to
-    // verify checksum has been scheduled, then it doesn't do anything when it
-    // is called back.
-    ForceDisableLocalDatabaseManager();
-    // Wait to make sure that the callback gets executed if it has already been
-    // scheduled.
-    WaitForTasksOnTaskRunner();
-    // Re-enable the V4LocalDatabaseManager otherwise the checks won't work and
-    // the fake database won't be set either.
-    ForceEnableLocalDatabaseManager();
-
-    NewDatabaseReadyCallback db_ready_callback =
-        base::BindOnce(&V4LocalDatabaseManager::DatabaseReadyForChecks,
-                       base::Unretained(v4_local_database_manager_.get()));
-    FakeV4Database::Create(task_runner_, std::make_unique<StoreMap>(),
-                           store_and_hash_prefixes,
-                           std::move(db_ready_callback), stores_available);
-    WaitForTasksOnTaskRunner();
-  }
-
-  void ResetLocalDatabaseManager() {
-    StopLocalDatabaseManager();
-    v4_local_database_manager_ =
-        base::WrapRefCounted(new V4LocalDatabaseManager(
-            base_dir_.GetPath(), erl_callback_, task_runner_));
-    StartLocalDatabaseManager();
-  }
-
-  void ResetV4Database() {
-    V4Database::Destroy(std::move(v4_local_database_manager_->v4_database_));
-  }
-
-  void StartLocalDatabaseManager() {
-    v4_local_database_manager_->StartOnIOThread(test_shared_loader_factory_,
-                                                GetTestV4ProtocolConfig());
-  }
-
-  void StopLocalDatabaseManager() {
-    if (v4_local_database_manager_) {
-      v4_local_database_manager_->StopOnIOThread(true);
-    }
-
-    // Force destruction of the database.
-    WaitForTasksOnTaskRunner();
-  }
-
-  void WaitForTasksOnTaskRunner() {
-    // Wait for tasks on the task runner so we're sure that the
-    // V4LocalDatabaseManager has read the data from disk.
-    task_runner_->RunPendingTasks();
-    base::RunLoop().RunUntilIdle();
-  }
-
-  // For those tests that need the fake manager
-  void SetupFakeManager() {
-    // StopLocalDatabaseManager before resetting it because that's what
-    // ~V4LocalDatabaseManager expects.
-    StopLocalDatabaseManager();
-    v4_local_database_manager_ =
-        base::WrapRefCounted(new FakeV4LocalDatabaseManager(
-            base_dir_.GetPath(), erl_callback_, task_runner_));
-    StartLocalDatabaseManager();
-    WaitForTasksOnTaskRunner();
-  }
-
-  const SBThreatTypeSet usual_threat_types_ = CreateSBThreatTypeSet(
-      {SB_THREAT_TYPE_URL_PHISHING, SB_THREAT_TYPE_URL_MALWARE,
-       SB_THREAT_TYPE_URL_UNWANTED});
-
-  network::TestURLLoaderFactory test_url_loader_factory_;
-  scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
-  base::ScopedTempDir base_dir_;
-  ExtendedReportingLevel extended_reporting_level_;
-  ExtendedReportingLevelCallback erl_callback_;
-  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
-  content::BrowserTaskEnvironment task_environment_;
-  scoped_refptr<V4LocalDatabaseManager> v4_local_database_manager_;
-};
-
-TEST_F(V4LocalDatabaseManagerTest, TestGetThreatSource) {
-  WaitForTasksOnTaskRunner();
-  EXPECT_EQ(ThreatSource::LOCAL_PVER4,
-            v4_local_database_manager_->GetThreatSource());
-}
-
-TEST_F(V4LocalDatabaseManagerTest, TestIsSupported) {
-  WaitForTasksOnTaskRunner();
-  EXPECT_TRUE(v4_local_database_manager_->IsSupported());
-}
-
-TEST_F(V4LocalDatabaseManagerTest, TestCanCheckUrl) {
-  WaitForTasksOnTaskRunner();
-  EXPECT_TRUE(
-      v4_local_database_manager_->CanCheckUrl(GURL("http://example.com/a/")));
-  EXPECT_TRUE(
-      v4_local_database_manager_->CanCheckUrl(GURL("https://example.com/a/")));
-  EXPECT_TRUE(
-      v4_local_database_manager_->CanCheckUrl(GURL("ftp://example.com/a/")));
-  EXPECT_FALSE(
-      v4_local_database_manager_->CanCheckUrl(GURL("adp://example.com/a/")));
-}
-
-TEST_F(V4LocalDatabaseManagerTest,
-       TestCheckBrowseUrlWithEmptyStoresReturnsNoMatch) {
-  WaitForTasksOnTaskRunner();
-  // Both the stores are empty right now so CheckBrowseUrl should return true.
-  EXPECT_TRUE(v4_local_database_manager_->CheckBrowseUrl(
-      GURL("http://example.com/a/"), usual_threat_types_, nullptr));
-}
-
-TEST_F(V4LocalDatabaseManagerTest, TestCheckBrowseUrlWithFakeDbReturnsMatch) {
-  WaitForTasksOnTaskRunner();
-
-  std::string url_bad_no_scheme("example.com/bad/");
-  FullHash bad_full_hash(crypto::SHA256HashString(url_bad_no_scheme));
-  const HashPrefix bad_hash_prefix(bad_full_hash.substr(0, 5));
-  StoreAndHashPrefixes store_and_hash_prefixes;
-  store_and_hash_prefixes.emplace_back(GetUrlMalwareId(), bad_hash_prefix);
-  ReplaceV4Database(store_and_hash_prefixes);
-
-  const GURL url_bad("https://" + url_bad_no_scheme);
-  EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(
-      url_bad, usual_threat_types_, nullptr));
-
-  // Wait for PerformFullHashCheck to complete.
-  WaitForTasksOnTaskRunner();
-}
-
-TEST_F(V4LocalDatabaseManagerTest, TestCheckCsdWhitelistWithPrefixMatch) {
-  // Setup to receive full-hash misses. We won't make URL requests.
-  ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({}));
-  ResetLocalDatabaseManager();
-  WaitForTasksOnTaskRunner();
-
-  std::string url_safe_no_scheme("example.com/safe/");
-  FullHash safe_full_hash(crypto::SHA256HashString(url_safe_no_scheme));
-  const HashPrefix safe_hash_prefix(safe_full_hash.substr(0, 5));
-  StoreAndHashPrefixes store_and_hash_prefixes;
-  store_and_hash_prefixes.emplace_back(GetUrlCsdWhitelistId(),
-                                       safe_hash_prefix);
-  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true);
-
-  TestAllowlistClient client(
-      /* match_expected= */ false,
-      /* expected_sb_threat_type= */ SB_THREAT_TYPE_CSD_WHITELIST);
-  const GURL url_check("https://" + url_safe_no_scheme);
-  EXPECT_EQ(AsyncMatch::ASYNC, v4_local_database_manager_->CheckCsdWhitelistUrl(
-                                   url_check, &client));
-
-  EXPECT_FALSE(client.callback_called());
-
-  // Wait for PerformFullHashCheck to complete.
-  WaitForTasksOnTaskRunner();
-  EXPECT_TRUE(client.callback_called());
-}
-
-// This is like CsdWhitelistWithPrefixMatch, but we also verify the
-// full-hash-match results in an appropriate callback value.
-TEST_F(V4LocalDatabaseManagerTest,
-       TestCheckCsdWhitelistWithPrefixTheFullMatch) {
-  std::string url_safe_no_scheme("example.com/safe/");
-  FullHash safe_full_hash(crypto::SHA256HashString(url_safe_no_scheme));
-
-  // Setup to receive full-hash hit. We won't make URL requests.
-  FullHashInfos infos(
-      {{safe_full_hash, GetUrlCsdWhitelistId(), base::Time::Now()}});
-  ScopedFakeGetHashProtocolManagerFactory pin(infos);
-  ResetLocalDatabaseManager();
-  WaitForTasksOnTaskRunner();
-
-  const HashPrefix safe_hash_prefix(safe_full_hash.substr(0, 5));
-  StoreAndHashPrefixes store_and_hash_prefixes;
-  store_and_hash_prefixes.emplace_back(GetUrlCsdWhitelistId(),
-                                       safe_hash_prefix);
-  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true);
-
-  TestAllowlistClient client(
-      /* match_expected= */ true,
-      /* expected_sb_threat_type= */ SB_THREAT_TYPE_CSD_WHITELIST);
-  const GURL url_check("https://" + url_safe_no_scheme);
-  EXPECT_EQ(AsyncMatch::ASYNC, v4_local_database_manager_->CheckCsdWhitelistUrl(
-                                   url_check, &client));
-
-  EXPECT_FALSE(client.callback_called());
-
-  // Wait for PerformFullHashCheck to complete.
-  WaitForTasksOnTaskRunner();
-  EXPECT_TRUE(client.callback_called());
-}
-
-TEST_F(V4LocalDatabaseManagerTest, TestCheckCsdWhitelistWithFullMatch) {
-  // Setup to receive full-hash misses. We won't make URL requests.
-  ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({}));
-  ResetLocalDatabaseManager();
-  WaitForTasksOnTaskRunner();
-
-  std::string url_safe_no_scheme("example.com/safe/");
-  FullHash safe_full_hash(crypto::SHA256HashString(url_safe_no_scheme));
-  StoreAndHashPrefixes store_and_hash_prefixes;
-  store_and_hash_prefixes.emplace_back(GetUrlCsdWhitelistId(), safe_full_hash);
-  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true);
-
-  TestAllowlistClient client(
-      /* match_expected= */ false,
-      /* expected_sb_threat_type= */ SB_THREAT_TYPE_CSD_WHITELIST);
-  const GURL url_check("https://" + url_safe_no_scheme);
-  EXPECT_EQ(AsyncMatch::MATCH, v4_local_database_manager_->CheckCsdWhitelistUrl(
-                                   url_check, &client));
-
-  WaitForTasksOnTaskRunner();
-  EXPECT_FALSE(client.callback_called());
-}
-
-TEST_F(V4LocalDatabaseManagerTest, TestCheckCsdWhitelistWithNoMatch) {
-  // Setup to receive full-hash misses. We won't make URL requests.
-  ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({}));
-  ResetLocalDatabaseManager();
-  WaitForTasksOnTaskRunner();
-
-  // Add a full hash that won't match the URL we check.
-  std::string url_safe_no_scheme("example.com/safe/");
-  FullHash safe_full_hash(crypto::SHA256HashString(url_safe_no_scheme));
-  StoreAndHashPrefixes store_and_hash_prefixes;
-  store_and_hash_prefixes.emplace_back(GetUrlMalwareId(), safe_full_hash);
-  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true);
-
-  TestAllowlistClient client(
-      /* match_expected= */ true,
-      /* expected_sb_threat_type= */ SB_THREAT_TYPE_CSD_WHITELIST);
-  const GURL url_check("https://other.com/");
-  EXPECT_EQ(
-      AsyncMatch::NO_MATCH,
-      v4_local_database_manager_->CheckCsdWhitelistUrl(url_check, &client));
-
-  WaitForTasksOnTaskRunner();
-  EXPECT_FALSE(client.callback_called());
-}
-
-// When allowlist is unavailable, all URLS should be allowed.
-TEST_F(V4LocalDatabaseManagerTest, TestCheckCsdWhitelistUnavailable) {
-  // Setup to receive full-hash misses. We won't make URL requests.
-  ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({}));
-  ResetLocalDatabaseManager();
-  WaitForTasksOnTaskRunner();
-
-  StoreAndHashPrefixes store_and_hash_prefixes;
-  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ false);
-
-  TestAllowlistClient client(
-      /* match_expected= */ false,
-      /* expected_sb_threat_type= */ SB_THREAT_TYPE_CSD_WHITELIST);
-  const GURL url_check("https://other.com/");
-  EXPECT_EQ(AsyncMatch::MATCH, v4_local_database_manager_->CheckCsdWhitelistUrl(
-                                   url_check, &client));
-
-  WaitForTasksOnTaskRunner();
-  EXPECT_FALSE(client.callback_called());
-}
-
-TEST_F(V4LocalDatabaseManagerTest,
-       TestCheckBrowseUrlReturnsNoMatchWhenDisabled) {
-  WaitForTasksOnTaskRunner();
-
-  // The same URL returns |false| in the previous test because
-  // v4_local_database_manager_ is enabled.
-  ForceDisableLocalDatabaseManager();
-
-  EXPECT_TRUE(v4_local_database_manager_->CheckBrowseUrl(
-      GURL("http://example.com/a/"), usual_threat_types_, nullptr));
-}
-
-// Hash prefix matches on the high confidence allowlist, but full hash match
-// fails.
-TEST_F(V4LocalDatabaseManagerTest,
-       TestCheckUrlForHCAllowlistWithPrefixMatchButNoFullHashMatch) {
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitWithFeatures({safe_browsing::kRealTimeUrlLookupEnabled}, {});
-
-  std::string url_safe_no_scheme("example.com/safe/");
-  FullHash safe_full_hash(crypto::SHA256HashString(url_safe_no_scheme));
-
-  // Setup to receive full-hash misses. We won't make URL requests.
-  ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({}));
-  ResetLocalDatabaseManager();
-  WaitForTasksOnTaskRunner();
-
-  // Setup to match hash prefix in the local database.
-  const HashPrefix safe_hash_prefix(safe_full_hash.substr(0, 5));
-  StoreAndHashPrefixes store_and_hash_prefixes;
-  store_and_hash_prefixes.emplace_back(GetUrlHighConfidenceAllowlistId(),
-                                       safe_hash_prefix);
-  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true);
-
-  // Setup the allowlist client to verify the callback.
-  TestAllowlistClient client(
-      /* match_expected= */ false,
-      /* expected_sb_threat_type= */ SB_THREAT_TYPE_HIGH_CONFIDENCE_ALLOWLIST);
-
-  // Lookup the high confidence allowlist.
-  const GURL url_check("https://" + url_safe_no_scheme);
-  EXPECT_EQ(AsyncMatch::ASYNC,
-            v4_local_database_manager_->CheckUrlForHighConfidenceAllowlist(
-                url_check, &client));
-
-  EXPECT_FALSE(client.callback_called());
-
-  // Wait for PerformFullHashCheck to complete.
-  WaitForTasksOnTaskRunner();
-  EXPECT_TRUE(client.callback_called());
-}
-
-// Hash prefix matches on the high confidence allowlist, and subsequently the
-// full hash also matches.
-TEST_F(V4LocalDatabaseManagerTest,
-       TestCheckUrlForHCAllowlistWithPrefixMatchAndFullHashMatch) {
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitWithFeatures({safe_browsing::kRealTimeUrlLookupEnabled}, {});
-
-  std::string url_safe_no_scheme("example.com/safe/");
-  FullHash safe_full_hash(crypto::SHA256HashString(url_safe_no_scheme));
-
-  // Setup to receive full-hash hit. We won't make URL requests.
-  FullHashInfos infos(
-      {{safe_full_hash, GetUrlHighConfidenceAllowlistId(), base::Time::Now()}});
-  ScopedFakeGetHashProtocolManagerFactory pin(infos);
-  ResetLocalDatabaseManager();
-  WaitForTasksOnTaskRunner();
-
-  // Setup to match hash prefix in the local database.
-  const HashPrefix safe_hash_prefix(safe_full_hash.substr(0, 5));
-  StoreAndHashPrefixes store_and_hash_prefixes;
-  store_and_hash_prefixes.emplace_back(GetUrlHighConfidenceAllowlistId(),
-                                       safe_hash_prefix);
-  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true);
-
-  // Setup the allowlist client to verify the callback.
-  TestAllowlistClient client(
-      /* match_expected= */ true,
-      /* expected_sb_threat_type= */ SB_THREAT_TYPE_HIGH_CONFIDENCE_ALLOWLIST);
-
-  // Lookup the high confidence allowlist.
-  const GURL url_check("https://" + url_safe_no_scheme);
-  EXPECT_EQ(AsyncMatch::ASYNC,
-            v4_local_database_manager_->CheckUrlForHighConfidenceAllowlist(
-                url_check, &client));
-
-  EXPECT_FALSE(client.callback_called());
-
-  // Wait for PerformFullHashCheck to complete.
-  WaitForTasksOnTaskRunner();
-  EXPECT_TRUE(client.callback_called());
-}
-
-// Full hash match on the high confidence allowlist. Returns |MATCH|
-// synchronously and callback isn't called.
-TEST_F(V4LocalDatabaseManagerTest,
-       TestCheckUrlForHCAllowlistWithLocalFullHashMatch) {
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitWithFeatures({safe_browsing::kRealTimeUrlLookupEnabled}, {});
-
-  std::string url_safe_no_scheme("example.com/safe/");
-  FullHash safe_full_hash(crypto::SHA256HashString(url_safe_no_scheme));
-
-  // Setup to receive full-hash misses. We won't make URL requests.
-  ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({}));
-  ResetLocalDatabaseManager();
-  WaitForTasksOnTaskRunner();
-
-  // Setup to match full hash in the local database.
-  StoreAndHashPrefixes store_and_hash_prefixes;
-  store_and_hash_prefixes.emplace_back(GetUrlHighConfidenceAllowlistId(),
-                                       safe_full_hash);
-  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true);
-
-  // Setup the allowlist client to verify the callback isn't called.
-  TestAllowlistClient client(
-      /* match_expected= */ false,
-      /* expected_sb_threat_type= */ SB_THREAT_TYPE_HIGH_CONFIDENCE_ALLOWLIST);
-  const GURL url_check("https://" + url_safe_no_scheme);
-  EXPECT_EQ(AsyncMatch::MATCH,
-            v4_local_database_manager_->CheckUrlForHighConfidenceAllowlist(
-                url_check, &client));
-
-  WaitForTasksOnTaskRunner();
-  EXPECT_FALSE(client.callback_called());
-}
-
-// Hash prefix has no match on the high confidence allowlist. Returns |NO_MATCH|
-// synchronously and callback isn't called.
-TEST_F(V4LocalDatabaseManagerTest, TestCheckUrlForHCAllowlistWithNoMatch) {
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitWithFeatures({safe_browsing::kRealTimeUrlLookupEnabled}, {});
-
-  std::string url_safe_no_scheme("example.com/safe/");
-  FullHash safe_full_hash(crypto::SHA256HashString(url_safe_no_scheme));
-
-  // Setup to receive full-hash misses. We won't make URL requests.
-  ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({}));
-  ResetLocalDatabaseManager();
-  WaitForTasksOnTaskRunner();
-
-  // Add a full hash that won't match the URL we check.
-  StoreAndHashPrefixes store_and_hash_prefixes;
-  store_and_hash_prefixes.emplace_back(GetUrlMalwareId(), safe_full_hash);
-  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true);
-
-  // Setup the allowlist client to verify the callback isn't called.
-  TestAllowlistClient client(
-      /* match_expected= */ false,
-      /* expected_sb_threat_type= */ SB_THREAT_TYPE_HIGH_CONFIDENCE_ALLOWLIST);
-  const GURL url_check("https://example.com/other/");
-  EXPECT_EQ(AsyncMatch::NO_MATCH,
-            v4_local_database_manager_->CheckUrlForHighConfidenceAllowlist(
-                url_check, &client));
-
-  WaitForTasksOnTaskRunner();
-  EXPECT_FALSE(client.callback_called());
-}
-
-// When allowlist is unavailable, all URLS should be considered MATCH.
-TEST_F(V4LocalDatabaseManagerTest, TestCheckUrlForHCAllowlistUnavailable) {
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitWithFeatures({safe_browsing::kRealTimeUrlLookupEnabled}, {});
-
-  // Setup to receive full-hash misses. We won't make URL requests.
-  ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({}));
-  ResetLocalDatabaseManager();
-  WaitForTasksOnTaskRunner();
-
-  // Setup local database as unavailable.
-  StoreAndHashPrefixes store_and_hash_prefixes;
-  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ false);
-
-  // Setup the allowlist client to verify the callback isn't called.
-  TestAllowlistClient client(
-      /* match_expected= */ false,
-      /* expected_sb_threat_type= */ SB_THREAT_TYPE_HIGH_CONFIDENCE_ALLOWLIST);
-
-  const GURL url_check("https://example.com/safe");
-  EXPECT_EQ(AsyncMatch::MATCH,
-            v4_local_database_manager_->CheckUrlForHighConfidenceAllowlist(
-                url_check, &client));
-
-  WaitForTasksOnTaskRunner();
-  EXPECT_FALSE(client.callback_called());
-}
-
-TEST_F(V4LocalDatabaseManagerTest, TestGetSeverestThreatTypeAndMetadata) {
-  WaitForTasksOnTaskRunner();
-
-  FullHash fh_malware("Malware");
-  FullHashInfo fhi_malware(fh_malware, GetUrlMalwareId(), base::Time::Now());
-  fhi_malware.metadata.population_id = "malware_popid";
-
-  FullHash fh_api("api");
-  FullHashInfo fhi_api(fh_api, GetChromeUrlApiId(), base::Time::Now());
-  fhi_api.metadata.population_id = "api_popid";
-
-  FullHash fh_example("example");
-  std::vector<FullHashInfo> fhis({fhi_malware, fhi_api});
-  std::vector<FullHash> full_hashes({fh_malware, fh_example, fh_api});
-
-  std::vector<SBThreatType> full_hash_threat_types(full_hashes.size(),
-                                                   SB_THREAT_TYPE_SAFE);
-  SBThreatType result_threat_type;
-  ThreatMetadata metadata;
-  FullHash matching_full_hash;
-
-  const std::vector<SBThreatType> expected_full_hash_threat_types(
-      {SB_THREAT_TYPE_URL_MALWARE, SB_THREAT_TYPE_SAFE,
-       SB_THREAT_TYPE_API_ABUSE});
-
-  v4_local_database_manager_->GetSeverestThreatTypeAndMetadata(
-      fhis, full_hashes, &full_hash_threat_types, &result_threat_type,
-      &metadata, &matching_full_hash);
-  EXPECT_EQ(expected_full_hash_threat_types, full_hash_threat_types);
-
-  EXPECT_EQ(SB_THREAT_TYPE_URL_MALWARE, result_threat_type);
-  EXPECT_EQ("malware_popid", metadata.population_id);
-  EXPECT_EQ(fh_malware, matching_full_hash);
-
-  // Reversing the list has no effect.
-  std::reverse(std::begin(fhis), std::end(fhis));
-  full_hash_threat_types.assign(full_hashes.size(), SB_THREAT_TYPE_SAFE);
-
-  v4_local_database_manager_->GetSeverestThreatTypeAndMetadata(
-      fhis, full_hashes, &full_hash_threat_types, &result_threat_type,
-      &metadata, &matching_full_hash);
-  EXPECT_EQ(expected_full_hash_threat_types, full_hash_threat_types);
-  EXPECT_EQ(SB_THREAT_TYPE_URL_MALWARE, result_threat_type);
-  EXPECT_EQ("malware_popid", metadata.population_id);
-  EXPECT_EQ(fh_malware, matching_full_hash);
-}
-
-TEST_F(V4LocalDatabaseManagerTest, TestChecksAreQueued) {
-  const GURL url("https://www.example.com/");
-  TestClient client(SB_THREAT_TYPE_SAFE, url);
-  EXPECT_TRUE(GetQueuedChecks().empty());
-  v4_local_database_manager_->CheckBrowseUrl(url, usual_threat_types_, &client);
-  // The database is unavailable so the check should get queued.
-  EXPECT_EQ(1ul, GetQueuedChecks().size());
-
-  // The following function waits for the DB to load.
-  WaitForTasksOnTaskRunner();
-  EXPECT_TRUE(GetQueuedChecks().empty());
-
-  ResetV4Database();
-  v4_local_database_manager_->CheckBrowseUrl(url, usual_threat_types_, &client);
-  // The database is unavailable so the check should get queued.
-  EXPECT_EQ(1ul, GetQueuedChecks().size());
-
-  StopLocalDatabaseManager();
-  EXPECT_TRUE(GetQueuedChecks().empty());
-}
-
-// Verify that a window where checks cannot be cancelled is closed.
-TEST_F(V4LocalDatabaseManagerTest, CancelPending) {
-  // Setup to receive full-hash misses.
-  ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({}));
-
-  // Reset the database manager so it picks up the replacement protocol manager.
-  ResetLocalDatabaseManager();
-  WaitForTasksOnTaskRunner();
-
-  // Put a match in the db that will cause a protocol-manager request.
-  std::string url_bad_no_scheme("example.com/bad/");
-  FullHash bad_full_hash(crypto::SHA256HashString(url_bad_no_scheme));
-  const HashPrefix bad_hash_prefix(bad_full_hash.substr(0, 5));
-  StoreAndHashPrefixes store_and_hash_prefixes;
-  store_and_hash_prefixes.emplace_back(GetUrlMalwareId(), bad_hash_prefix);
-  ReplaceV4Database(store_and_hash_prefixes);
-
-  const GURL url_bad("https://" + url_bad_no_scheme);
-  // Test that a request flows through to the callback.
-  {
-    TestClient client(SB_THREAT_TYPE_SAFE, url_bad);
-    EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(
-        url_bad, usual_threat_types_, &client));
-    EXPECT_FALSE(client.on_check_browse_url_result_called());
-    WaitForTasksOnTaskRunner();
-    EXPECT_TRUE(client.on_check_browse_url_result_called());
-  }
-
-  // Test that cancel prevents the callback from being called.
-  {
-    TestClient client(SB_THREAT_TYPE_SAFE, url_bad);
-    EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(
-        url_bad, usual_threat_types_, &client));
-    v4_local_database_manager_->CancelCheck(&client);
-    EXPECT_FALSE(client.on_check_browse_url_result_called());
-    WaitForTasksOnTaskRunner();
-    EXPECT_FALSE(client.on_check_browse_url_result_called());
-  }
-}
-
-// When the database load flushes the queued requests, make sure that
-// CancelCheck() is not fatal in the client callback.
-TEST_F(V4LocalDatabaseManagerTest, CancelQueued) {
-  const GURL url("http://example.com/a/");
-
-  TestClient client1(SB_THREAT_TYPE_SAFE, url,
-                     v4_local_database_manager_.get());
-  TestClient client2(SB_THREAT_TYPE_SAFE, url);
-  EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(
-      url, usual_threat_types_, &client1));
-  EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(
-      url, usual_threat_types_, &client2));
-  EXPECT_EQ(2ul, GetQueuedChecks().size());
-  EXPECT_FALSE(client1.on_check_browse_url_result_called());
-  EXPECT_FALSE(client2.on_check_browse_url_result_called());
-  WaitForTasksOnTaskRunner();
-  EXPECT_TRUE(client1.on_check_browse_url_result_called());
-  EXPECT_TRUE(client2.on_check_browse_url_result_called());
-}
-
-// This test is somewhat similar to TestCheckBrowseUrlWithFakeDbReturnsMatch but
-// it uses a fake V4LocalDatabaseManager to assert that PerformFullHashCheck is
-// called async.
-TEST_F(V4LocalDatabaseManagerTest, PerformFullHashCheckCalledAsync) {
-  SetupFakeManager();
-
-  std::string url_bad_no_scheme("example.com/bad/");
-  FullHash bad_full_hash(crypto::SHA256HashString(url_bad_no_scheme));
-  const HashPrefix bad_hash_prefix(bad_full_hash.substr(0, 5));
-  StoreAndHashPrefixes store_and_hash_prefixes;
-  store_and_hash_prefixes.emplace_back(GetUrlMalwareId(), bad_hash_prefix);
-  ReplaceV4Database(store_and_hash_prefixes);
-
-  const GURL url_bad("https://" + url_bad_no_scheme);
-  // The fake database returns a matched hash prefix.
-  EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(
-      url_bad, usual_threat_types_, nullptr));
-
-  EXPECT_FALSE(FakeV4LocalDatabaseManager::PerformFullHashCheckCalled(
-      v4_local_database_manager_));
-
-  // Wait for PerformFullHashCheck to complete.
-  WaitForTasksOnTaskRunner();
-
-  EXPECT_TRUE(FakeV4LocalDatabaseManager::PerformFullHashCheckCalled(
-      v4_local_database_manager_));
-}
-
-TEST_F(V4LocalDatabaseManagerTest, UsingWeakPtrDropsCallback) {
-  SetupFakeManager();
-
-  std::string url_bad_no_scheme("example.com/bad/");
-  FullHash bad_full_hash(crypto::SHA256HashString(url_bad_no_scheme));
-  const HashPrefix bad_hash_prefix(bad_full_hash.substr(0, 5));
-  StoreAndHashPrefixes store_and_hash_prefixes;
-  store_and_hash_prefixes.emplace_back(GetUrlMalwareId(), bad_hash_prefix);
-  ReplaceV4Database(store_and_hash_prefixes);
-
-  const GURL url_bad("https://" + url_bad_no_scheme);
-  EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(
-      url_bad, usual_threat_types_, nullptr));
-  v4_local_database_manager_->StopOnIOThread(true);
-
-  // Release the V4LocalDatabaseManager object right away before the callback
-  // gets called. When the callback gets called, without using a weak-ptr
-  // factory, this leads to a use after free. However, using the weak-ptr means
-  // that the callback is simply dropped.
-  v4_local_database_manager_ = nullptr;
-
-  // Wait for the tasks scheduled by StopOnIOThread to complete.
-  WaitForTasksOnTaskRunner();
-}
-
-TEST_F(V4LocalDatabaseManagerTest, TestMatchDownloadWhitelistString) {
-  SetupFakeManager();
-  const std::string good_cert = "Good Cert";
-  const std::string other_cert = "Other Cert";
-  FullHash good_hash(crypto::SHA256HashString(good_cert));
-
-  StoreAndHashPrefixes store_and_hash_prefixes;
-  store_and_hash_prefixes.emplace_back(GetCertCsdDownloadWhitelistId(),
-                                       good_hash);
-
-  ReplaceV4Database(store_and_hash_prefixes, false /* not available */);
-  // Verify it defaults to false when DB is not available.
-  EXPECT_FALSE(
-      v4_local_database_manager_->MatchDownloadWhitelistString(good_cert));
-
-  ReplaceV4Database(store_and_hash_prefixes, true /* available */);
-  // Not whitelisted.
-  EXPECT_FALSE(
-      v4_local_database_manager_->MatchDownloadWhitelistString(other_cert));
-  // Whitelisted.
-  EXPECT_TRUE(
-      v4_local_database_manager_->MatchDownloadWhitelistString(good_cert));
-
-  EXPECT_FALSE(FakeV4LocalDatabaseManager::PerformFullHashCheckCalled(
-      v4_local_database_manager_));
-}
-
-TEST_F(V4LocalDatabaseManagerTest, TestMatchDownloadWhitelistUrl) {
-  SetupFakeManager();
-  GURL good_url("http://safe.com");
-  GURL other_url("http://iffy.com");
-
-  StoreAndHashPrefixes store_and_hash_prefixes;
-  store_and_hash_prefixes.emplace_back(GetUrlCsdDownloadWhitelistId(),
-                                       HashForUrl(good_url));
-
-  ReplaceV4Database(store_and_hash_prefixes, false /* not available */);
-  // Verify it defaults to false when DB is not available.
-  EXPECT_FALSE(v4_local_database_manager_->MatchDownloadWhitelistUrl(good_url));
-
-  ReplaceV4Database(store_and_hash_prefixes, true /* available */);
-  // Not whitelisted.
-  EXPECT_FALSE(
-      v4_local_database_manager_->MatchDownloadWhitelistUrl(other_url));
-  // Whitelisted.
-  EXPECT_TRUE(v4_local_database_manager_->MatchDownloadWhitelistUrl(good_url));
-
-  EXPECT_FALSE(FakeV4LocalDatabaseManager::PerformFullHashCheckCalled(
-      v4_local_database_manager_));
-}
-
-TEST_F(V4LocalDatabaseManagerTest, TestMatchMalwareIP) {
-  SetupFakeManager();
-
-  // >>> hashlib.sha1(socket.inet_pton(socket.AF_INET6,
-  // '::ffff:192.168.1.2')).digest() + chr(128)
-  // '\xb3\xe0z\xafAv#h\x9a\xcf<\xf3ee\x94\xda\xf6y\xb1\xad\x80'
-  StoreAndHashPrefixes store_and_hash_prefixes;
-  store_and_hash_prefixes.emplace_back(GetIpMalwareId(),
-                                       FullHash("\xB3\xE0z\xAF"
-                                                "Av#h\x9A\xCF<\xF3"
-                                                "ee\x94\xDA\xF6y\xB1\xAD\x80"));
-  ReplaceV4Database(store_and_hash_prefixes);
-
-  EXPECT_FALSE(v4_local_database_manager_->MatchMalwareIP(""));
-  // Not blacklisted.
-  EXPECT_FALSE(v4_local_database_manager_->MatchMalwareIP("192.168.1.1"));
-  // Blacklisted.
-  EXPECT_TRUE(v4_local_database_manager_->MatchMalwareIP("192.168.1.2"));
-
-  EXPECT_FALSE(FakeV4LocalDatabaseManager::PerformFullHashCheckCalled(
-      v4_local_database_manager_));
-}
-
-// This verifies the fix for race in http://crbug.com/660293
-TEST_F(V4LocalDatabaseManagerTest, TestCheckBrowseUrlWithSameClientAndCancel) {
-  ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({}));
-  // Reset the database manager so it picks up the replacement protocol manager.
-  ResetLocalDatabaseManager();
-  WaitForTasksOnTaskRunner();
-
-  StoreAndHashPrefixes store_and_hash_prefixes;
-  store_and_hash_prefixes.emplace_back(GetUrlMalwareId(),
-                                       HashPrefix("sن\340\t\006_"));
-  ReplaceV4Database(store_and_hash_prefixes);
-
-  GURL first_url("http://example.com/a");
-  GURL second_url("http://example.com/");
-  TestClient client(SB_THREAT_TYPE_SAFE, first_url);
-  // The fake database returns a matched hash prefix.
-  EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(
-      first_url, usual_threat_types_, &client));
-
-  // That check gets queued. Now, let's cancel the check. After this, we should
-  // not receive a call for |OnCheckBrowseUrlResult| with |first_url|.
-  v4_local_database_manager_->CancelCheck(&client);
-
-  // Now, re-use that client but for |second_url|.
-  client.mutable_expected_urls()->assign(1, second_url);
-  EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(
-      second_url, usual_threat_types_, &client));
-
-  // Wait for PerformFullHashCheck to complete.
-  WaitForTasksOnTaskRunner();
-  // |on_check_browse_url_result_called_| is true only if OnCheckBrowseUrlResult
-  // gets called with the |url| equal to |expected_url|, which is |second_url|
-  // in
-  // this test.
-  EXPECT_TRUE(client.on_check_browse_url_result_called());
-}
-
-TEST_F(V4LocalDatabaseManagerTest, TestCheckResourceUrl) {
-  // Setup to receive full-hash misses.
-  ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({}));
-
-  // Reset the database manager so it picks up the replacement protocol manager.
-  ResetLocalDatabaseManager();
-  WaitForTasksOnTaskRunner();
-
-  std::string url_bad_no_scheme("example.com/bad/");
-  FullHash bad_full_hash(crypto::SHA256HashString(url_bad_no_scheme));
-  const HashPrefix bad_hash_prefix(bad_full_hash.substr(0, 5));
-  StoreAndHashPrefixes store_and_hash_prefixes;
-  store_and_hash_prefixes.emplace_back(GetChromeUrlClientIncidentId(),
-                                       bad_hash_prefix);
-  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true);
-
-  const GURL url_bad("https://" + url_bad_no_scheme);
-  TestClient client(SB_THREAT_TYPE_SAFE, url_bad);
-  EXPECT_FALSE(v4_local_database_manager_->CheckResourceUrl(url_bad, &client));
-  EXPECT_FALSE(client.on_check_resource_url_result_called());
-  WaitForTasksOnTaskRunner();
-  EXPECT_TRUE(client.on_check_resource_url_result_called());
-}
-
-TEST_F(V4LocalDatabaseManagerTest, TestSubresourceFilterCallback) {
-  // Setup to receive full-hash misses.
-  ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({}));
-
-  // Reset the database manager so it picks up the replacement protocol manager.
-  ResetLocalDatabaseManager();
-  WaitForTasksOnTaskRunner();
-
-  std::string url_bad_no_scheme("example.com/bad/");
-  FullHash bad_full_hash(crypto::SHA256HashString(url_bad_no_scheme));
-  const HashPrefix bad_hash_prefix(bad_full_hash.substr(0, 5));
-
-  // Put a match in the db that will cause a protocol-manager request.
-  StoreAndHashPrefixes store_and_hash_prefixes;
-  store_and_hash_prefixes.emplace_back(GetUrlSubresourceFilterId(),
-                                       bad_hash_prefix);
-  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true);
-
-  const GURL url_bad("https://" + url_bad_no_scheme);
-  // Test that a request flows through to the callback.
-  {
-    TestClient client(SB_THREAT_TYPE_SAFE, url_bad);
-    EXPECT_FALSE(v4_local_database_manager_->CheckUrlForSubresourceFilter(
-        url_bad, &client));
-    EXPECT_FALSE(client.on_check_browse_url_result_called());
-    WaitForTasksOnTaskRunner();
-    EXPECT_TRUE(client.on_check_browse_url_result_called());
-  }
-}
-
-TEST_F(V4LocalDatabaseManagerTest, TestCheckResourceUrlReturnsBad) {
-  // Setup to receive full-hash hit.
-  std::string url_bad_no_scheme("example.com/bad/");
-  FullHash bad_full_hash(crypto::SHA256HashString(url_bad_no_scheme));
-  FullHashInfo fhi(bad_full_hash, GetChromeUrlClientIncidentId(), base::Time());
-  ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({fhi}));
-
-  // Reset the database manager so it picks up the replacement protocol manager.
-  ResetLocalDatabaseManager();
-  WaitForTasksOnTaskRunner();
-
-  // Put a match in the db that will cause a protocol-manager request.
-  const HashPrefix bad_hash_prefix(bad_full_hash.substr(0, 5));
-  StoreAndHashPrefixes store_and_hash_prefixes;
-  store_and_hash_prefixes.emplace_back(GetChromeUrlClientIncidentId(),
-                                       bad_hash_prefix);
-  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true);
-
-  const GURL url_bad("https://" + url_bad_no_scheme);
-  TestClient client(SB_THREAT_TYPE_BLACKLISTED_RESOURCE, url_bad);
-  EXPECT_FALSE(v4_local_database_manager_->CheckResourceUrl(url_bad, &client));
-  EXPECT_FALSE(client.on_check_resource_url_result_called());
-  WaitForTasksOnTaskRunner();
-  EXPECT_TRUE(client.on_check_resource_url_result_called());
-}
-
-TEST_F(V4LocalDatabaseManagerTest, TestCheckExtensionIDsNothingBlacklisted) {
-  // Setup to receive full-hash misses.
-  ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({}));
-
-  // Reset the database manager so it picks up the replacement protocol manager.
-  ResetLocalDatabaseManager();
-  WaitForTasksOnTaskRunner();
-
-  // bad_extension_id is in the local DB but the full hash won't match.
-  const FullHash bad_extension_id("aaaabbbbccccdddd"),
-      good_extension_id("ddddccccbbbbaaaa");
-
-  // Put a match in the db that will cause a protocol-manager request.
-  StoreAndHashPrefixes store_and_hash_prefixes;
-  store_and_hash_prefixes.emplace_back(GetChromeExtMalwareId(),
-                                       bad_extension_id);
-  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true);
-
-  const std::set<FullHash> expected_bad_crxs({});
-  const std::set<FullHash> extension_ids({good_extension_id, bad_extension_id});
-  TestExtensionClient client(expected_bad_crxs);
-  EXPECT_FALSE(
-      v4_local_database_manager_->CheckExtensionIDs(extension_ids, &client));
-  EXPECT_FALSE(client.on_check_extensions_result_called());
-  WaitForTasksOnTaskRunner();
-  EXPECT_TRUE(client.on_check_extensions_result_called());
-}
-
-TEST_F(V4LocalDatabaseManagerTest, TestCheckExtensionIDsOneIsBlacklisted) {
-  // bad_extension_id is in the local DB and the full hash will match.
-  const FullHash bad_extension_id("aaaabbbbccccdddd"),
-      good_extension_id("ddddccccbbbbaaaa");
-  FullHashInfo fhi(bad_extension_id, GetChromeExtMalwareId(), base::Time());
-
-  // Setup to receive full-hash hit.
-  ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({fhi}));
-
-  // Reset the database manager so it picks up the replacement protocol manager.
-  ResetLocalDatabaseManager();
-  WaitForTasksOnTaskRunner();
-
-  // Put a match in the db that will cause a protocol-manager request.
-  StoreAndHashPrefixes store_and_hash_prefixes;
-  store_and_hash_prefixes.emplace_back(GetChromeExtMalwareId(),
-                                       bad_extension_id);
-  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true);
-
-  const std::set<FullHash> expected_bad_crxs({bad_extension_id});
-  const std::set<FullHash> extension_ids({good_extension_id, bad_extension_id});
-  TestExtensionClient client(expected_bad_crxs);
-  EXPECT_FALSE(
-      v4_local_database_manager_->CheckExtensionIDs(extension_ids, &client));
-  EXPECT_FALSE(client.on_check_extensions_result_called());
-  WaitForTasksOnTaskRunner();
-  EXPECT_TRUE(client.on_check_extensions_result_called());
-}
-
-TEST_F(V4LocalDatabaseManagerTest, TestCheckDownloadUrlNothingBlacklisted) {
-  // Setup to receive full-hash misses.
-  ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({}));
-
-  // Reset the database manager so it picks up the replacement protocol manager.
-  ResetLocalDatabaseManager();
-  WaitForTasksOnTaskRunner();
-
-  // Put a match in the db that will cause a protocol-manager request.
-  std::string url_bad_no_scheme("example.com/bad/");
-  FullHash bad_full_hash(crypto::SHA256HashString(url_bad_no_scheme));
-  const HashPrefix bad_hash_prefix(bad_full_hash.substr(0, 5));
-  StoreAndHashPrefixes store_and_hash_prefixes;
-  store_and_hash_prefixes.emplace_back(GetUrlMalBinId(), bad_hash_prefix);
-  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true);
-
-  const GURL url_bad("https://" + url_bad_no_scheme),
-      url_good("https://example.com/good/");
-  const std::vector<GURL> url_chain({url_good, url_bad});
-
-  TestClient client(SB_THREAT_TYPE_SAFE, url_chain);
-  EXPECT_FALSE(
-      v4_local_database_manager_->CheckDownloadUrl(url_chain, &client));
-  EXPECT_FALSE(client.on_check_download_urls_result_called());
-  WaitForTasksOnTaskRunner();
-  EXPECT_TRUE(client.on_check_download_urls_result_called());
-}
-
-TEST_F(V4LocalDatabaseManagerTest, TestCheckDownloadUrlWithOneBlacklisted) {
-  // Setup to receive full-hash hit.
-  std::string url_bad_no_scheme("example.com/bad/");
-  FullHash bad_full_hash(crypto::SHA256HashString(url_bad_no_scheme));
-  FullHashInfo fhi(bad_full_hash, GetUrlMalBinId(), base::Time());
-  ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({fhi}));
-
-  // Reset the database manager so it picks up the replacement protocol manager.
-  ResetLocalDatabaseManager();
-  WaitForTasksOnTaskRunner();
-
-  const GURL url_bad("https://" + url_bad_no_scheme),
-      url_good("https://example.com/good/");
-  const std::vector<GURL> url_chain({url_good, url_bad});
-
-  // Put a match in the db that will cause a protocol-manager request.
-  const HashPrefix bad_hash_prefix(bad_full_hash.substr(0, 5));
-  StoreAndHashPrefixes store_and_hash_prefixes;
-  store_and_hash_prefixes.emplace_back(GetUrlMalBinId(), bad_hash_prefix);
-  ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true);
-
-  TestClient client(SB_THREAT_TYPE_URL_BINARY_MALWARE, url_chain);
-  EXPECT_FALSE(
-      v4_local_database_manager_->CheckDownloadUrl(url_chain, &client));
-  EXPECT_FALSE(client.on_check_download_urls_result_called());
-  WaitForTasksOnTaskRunner();
-  EXPECT_TRUE(client.on_check_download_urls_result_called());
-}
-
-TEST_F(V4LocalDatabaseManagerTest, DeleteUnusedStoreFileDoesNotExist) {
-  auto store_file_path = base_dir_.GetPath().AppendASCII("AnyIpMalware.store");
-  ASSERT_FALSE(base::PathExists(store_file_path));
-
-  // Reset the database manager so that DeleteUnusedStoreFiles is called.
-  ResetLocalDatabaseManager();
-  WaitForTasksOnTaskRunner();
-  ASSERT_FALSE(base::PathExists(store_file_path));
-}
-
-TEST_F(V4LocalDatabaseManagerTest, DeleteUnusedStoreFileSuccess) {
-  auto store_file_path = base_dir_.GetPath().AppendASCII("AnyIpMalware.store");
-  ASSERT_FALSE(base::PathExists(store_file_path));
-
-  // Now write an empty file.
-  base::WriteFile(store_file_path, "", 0);
-  ASSERT_TRUE(base::PathExists(store_file_path));
-
-  // Reset the database manager so that DeleteUnusedStoreFiles is called.
-  ResetLocalDatabaseManager();
-  WaitForTasksOnTaskRunner();
-  ASSERT_FALSE(base::PathExists(store_file_path));
-}
-
-TEST_F(V4LocalDatabaseManagerTest, DeleteUnusedStoreFileRandomFileNotDeleted) {
-  auto random_store_file_path = base_dir_.GetPath().AppendASCII("random.store");
-  ASSERT_FALSE(base::PathExists(random_store_file_path));
-
-  // Now write an empty file.
-  base::WriteFile(random_store_file_path, "", 0);
-  ASSERT_TRUE(base::PathExists(random_store_file_path));
-
-  // Reset the database manager so that DeleteUnusedStoreFiles is called.
-  ResetLocalDatabaseManager();
-  WaitForTasksOnTaskRunner();
-  ASSERT_TRUE(base::PathExists(random_store_file_path));
-
-  // Cleanup
-  base::DeleteFile(random_store_file_path, false /* recursive */);
-}
-
-TEST_F(V4LocalDatabaseManagerTest, NotificationOnUpdate) {
-  base::RunLoop run_loop;
-  auto callback_subscription =
-      v4_local_database_manager_->RegisterDatabaseUpdatedCallback(
-          run_loop.QuitClosure());
-
-  // Creates and associates a V4Database instance.
-  StoreAndHashPrefixes store_and_hash_prefixes;
-  ReplaceV4Database(store_and_hash_prefixes);
-
-  v4_local_database_manager_->DatabaseUpdated();
-
-  run_loop.Run();
-}
-
-TEST_F(V4LocalDatabaseManagerTest, FlagOneUrlAsPhishing) {
-  SetupFakeManager();
-  base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
-      "mark_as_phishing", "https://example.com/1/");
-  PopulateArtificialDatabase();
-
-  const GURL url_bad("https://example.com/1/");
-  EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(
-      url_bad, usual_threat_types_, nullptr));
-  // PerformFullHashCheck will not be called if there is a match within the
-  // artificial database
-  EXPECT_FALSE(FakeV4LocalDatabaseManager::PerformFullHashCheckCalled(
-      v4_local_database_manager_));
-
-  const GURL url_good("https://other.example.com");
-  EXPECT_TRUE(v4_local_database_manager_->CheckBrowseUrl(
-      url_good, usual_threat_types_, nullptr));
-
-  StopLocalDatabaseManager();
-}
-
-TEST_F(V4LocalDatabaseManagerTest, FlagOneUrlAsMalware) {
-  SetupFakeManager();
-  base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
-      "mark_as_malware", "https://example.com/1/");
-  PopulateArtificialDatabase();
-
-  const GURL url_bad("https://example.com/1/");
-  EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(
-      url_bad, usual_threat_types_, nullptr));
-  // PerformFullHashCheck will not be called if there is a match within the
-  // artificial database
-  EXPECT_FALSE(FakeV4LocalDatabaseManager::PerformFullHashCheckCalled(
-      v4_local_database_manager_));
-
-  const GURL url_good("https://other.example.com");
-  EXPECT_TRUE(v4_local_database_manager_->CheckBrowseUrl(
-      url_good, usual_threat_types_, nullptr));
-
-  StopLocalDatabaseManager();
-}
-
-TEST_F(V4LocalDatabaseManagerTest, FlagOneUrlAsUWS) {
-  SetupFakeManager();
-  base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
-      "mark_as_uws", "https://example.com/1/");
-  PopulateArtificialDatabase();
-
-  const GURL url_bad("https://example.com/1/");
-  EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(
-      url_bad, usual_threat_types_, nullptr));
-  // PerformFullHashCheck will not be called if there is a match within the
-  // artificial database
-  EXPECT_FALSE(FakeV4LocalDatabaseManager::PerformFullHashCheckCalled(
-      v4_local_database_manager_));
-
-  const GURL url_good("https://other.example.com");
-  EXPECT_TRUE(v4_local_database_manager_->CheckBrowseUrl(
-      url_good, usual_threat_types_, nullptr));
-
-  StopLocalDatabaseManager();
-}
-
-TEST_F(V4LocalDatabaseManagerTest, FlagMultipleUrls) {
-  SetupFakeManager();
-  base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
-      "mark_as_phishing", "https://example.com/1/");
-  base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
-      "mark_as_malware", "https://2.example.com");
-  base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
-      "mark_as_uws", "https://example.test.com");
-  PopulateArtificialDatabase();
-
-  const GURL url_phishing("https://example.com/1/");
-  EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(
-      url_phishing, usual_threat_types_, nullptr));
-  const GURL url_malware("https://2.example.com");
-  EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(
-      url_malware, usual_threat_types_, nullptr));
-  const GURL url_uws("https://example.test.com");
-  EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl(
-      url_uws, usual_threat_types_, nullptr));
-  // PerformFullHashCheck will not be called if there is a match within the
-  // artificial database
-  EXPECT_FALSE(FakeV4LocalDatabaseManager::PerformFullHashCheckCalled(
-      v4_local_database_manager_));
-
-  const GURL url_good("https://other.example.com");
-  EXPECT_TRUE(v4_local_database_manager_->CheckBrowseUrl(
-      url_good, usual_threat_types_, nullptr));
-
-  StopLocalDatabaseManager();
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/db/v4_protocol_manager_util.cc b/components/safe_browsing/db/v4_protocol_manager_util.cc
deleted file mode 100644
index 24808e9..0000000
--- a/components/safe_browsing/db/v4_protocol_manager_util.cc
+++ /dev/null
@@ -1,684 +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.
-
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
-
-#include "base/base64.h"
-#include "base/hash/hash.h"
-#include "base/hash/sha1.h"
-#include "base/metrics/histogram_functions.h"
-#include "base/rand_util.h"
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
-#include "build/build_config.h"
-#include "components/version_info/version_info.h"
-#include "crypto/sha2.h"
-#include "google_apis/google_api_keys.h"
-#include "net/base/escape.h"
-#include "net/base/ip_address.h"
-#include "net/base/net_errors.h"
-#include "net/http/http_request_headers.h"
-#include "url/url_util.h"
-
-using base::Time;
-using base::TimeDelta;
-
-namespace safe_browsing {
-
-// Can be overriden by tests.
-const char* g_sbv4_url_prefix_for_testing = nullptr;
-
-const char kSbV4UrlPrefix[] = "https://safebrowsing.googleapis.com/v4";
-
-const base::FilePath::CharType kStoreSuffix[] = FILE_PATH_LITERAL(".store");
-
-namespace {
-
-// The default URL prefix where browser reports safe browsing hits and malware
-// details.
-const char kSbReportsURLPrefix[] =
-    "https://safebrowsing.google.com/safebrowsing";
-
-std::string Unescape(const std::string& url) {
-  std::string unescaped_str(url);
-  const int kMaxLoopIterations = 1024;
-  size_t old_size = 0;
-  int loop_var = 0;
-  do {
-    old_size = unescaped_str.size();
-    unescaped_str = net::UnescapeBinaryURLComponent(unescaped_str);
-  } while (old_size != unescaped_str.size() &&
-           ++loop_var <= kMaxLoopIterations);
-
-  return unescaped_str;
-}
-
-std::string Escape(const std::string& url) {
-  std::string escaped_str;
-  // The escaped string is larger so allocate double the length to reduce the
-  // chance of the string being grown.
-  escaped_str.reserve(url.length() * 2);
-  const char* kHexString = "0123456789ABCDEF";
-  for (size_t i = 0; i < url.length(); i++) {
-    unsigned char c = static_cast<unsigned char>(url[i]);
-    if (c <= ' ' || c > '~' || c == '#' || c == '%') {
-      escaped_str += '%';
-      escaped_str += kHexString[c >> 4];
-      escaped_str += kHexString[c & 0xf];
-    } else {
-      escaped_str += c;
-    }
-  }
-
-  return escaped_str;
-}
-
-}  // namespace
-
-V4ProtocolConfig GetV4ProtocolConfig(const std::string& client_name,
-                                     bool disable_auto_update) {
-  return V4ProtocolConfig(client_name, disable_auto_update,
-                          google_apis::GetAPIKey(),
-                          version_info::GetVersionNumber());
-}
-
-void SetSbV4UrlPrefixForTesting(const char* url_prefix) {
-  g_sbv4_url_prefix_for_testing = url_prefix;
-}
-
-std::string GetReportUrl(const V4ProtocolConfig& config,
-                         const std::string& method,
-                         const ExtendedReportingLevel* reporting_level) {
-  std::string url = base::StringPrintf(
-      "%s/%s?client=%s&appver=%s&pver=4.0", kSbReportsURLPrefix, method.c_str(),
-      config.client_name.c_str(), config.version.c_str());
-  std::string api_key = google_apis::GetAPIKey();
-  if (!api_key.empty()) {
-    base::StringAppendF(&url, "&key=%s",
-                        net::EscapeQueryParamValue(api_key, true).c_str());
-  }
-  if (reporting_level)
-    url.append(base::StringPrintf("&ext=%d", *reporting_level));
-  return url;
-}
-
-std::ostream& operator<<(std::ostream& os, const ListIdentifier& id) {
-  os << "{hash: " << id.hash() << "; platform_type: " << id.platform_type()
-     << "; threat_entry_type: " << id.threat_entry_type()
-     << "; threat_type: " << id.threat_type() << "}";
-  return os;
-}
-
-PlatformType GetCurrentPlatformType() {
-#if defined(OS_WIN)
-  return WINDOWS_PLATFORM;
-#elif defined(OS_LINUX)
-  return LINUX_PLATFORM;
-#elif defined(OS_MACOSX)
-  return OSX_PLATFORM;
-#else
-  // TODO(crbug.com/1030487): This file is, in fact, intended to be compiled on
-  // Android, the comment below is obsolete. We should be able to return
-  // ANDROID_PLATFORM here.
-  //
-  // This should ideally never compile but it is getting compiled on Android.
-  // See: https://bugs.chromium.org/p/chromium/issues/detail?id=621647
-  // TODO(vakh): Once that bug is fixed, this should be removed. If we leave
-  // the platform_type empty, the server won't recognize the request and
-  // return an error response which will pollute our UMA metrics.
-  return LINUX_PLATFORM;
-#endif
-}
-
-ListIdentifier GetCertCsdDownloadWhitelistId() {
-  return ListIdentifier(GetCurrentPlatformType(), CERT, CSD_DOWNLOAD_WHITELIST);
-}
-
-ListIdentifier GetChromeExtMalwareId() {
-  return ListIdentifier(CHROME_PLATFORM, CHROME_EXTENSION, MALWARE_THREAT);
-}
-
-ListIdentifier GetChromeUrlApiId() {
-  // TODO(crbug.com/1030487): This special case for Android will no longer be
-  // needed once GetCurrentPlatformType() returns ANDROID_PLATFORM on Android.
-#if defined(OS_ANDROID)
-  return ListIdentifier(ANDROID_PLATFORM, URL, API_ABUSE);
-#else
-  return ListIdentifier(GetCurrentPlatformType(), URL, API_ABUSE);
-#endif
-}
-
-ListIdentifier GetChromeUrlClientIncidentId() {
-  return ListIdentifier(CHROME_PLATFORM, URL, CLIENT_INCIDENT);
-}
-
-ListIdentifier GetIpMalwareId() {
-  return ListIdentifier(GetCurrentPlatformType(), IP_RANGE, MALWARE_THREAT);
-}
-
-ListIdentifier GetUrlBillingId() {
-  return ListIdentifier(GetCurrentPlatformType(), URL, BILLING);
-}
-
-ListIdentifier GetUrlCsdDownloadWhitelistId() {
-  return ListIdentifier(GetCurrentPlatformType(), URL, CSD_DOWNLOAD_WHITELIST);
-}
-
-ListIdentifier GetUrlCsdWhitelistId() {
-  return ListIdentifier(GetCurrentPlatformType(), URL, CSD_WHITELIST);
-}
-
-ListIdentifier GetUrlHighConfidenceAllowlistId() {
-  return ListIdentifier(GetCurrentPlatformType(), URL,
-                        HIGH_CONFIDENCE_ALLOWLIST);
-}
-
-ListIdentifier GetUrlMalwareId() {
-  return ListIdentifier(GetCurrentPlatformType(), URL, MALWARE_THREAT);
-}
-
-ListIdentifier GetUrlMalBinId() {
-  return ListIdentifier(GetCurrentPlatformType(), URL, MALICIOUS_BINARY);
-}
-
-ListIdentifier GetUrlSocEngId() {
-  return ListIdentifier(GetCurrentPlatformType(), URL, SOCIAL_ENGINEERING);
-}
-
-ListIdentifier GetUrlSubresourceFilterId() {
-  return ListIdentifier(GetCurrentPlatformType(), URL, SUBRESOURCE_FILTER);
-}
-
-ListIdentifier GetUrlSuspiciousSiteId() {
-  return ListIdentifier(GetCurrentPlatformType(), URL, SUSPICIOUS);
-}
-
-ListIdentifier GetUrlUwsId() {
-  return ListIdentifier(GetCurrentPlatformType(), URL, UNWANTED_SOFTWARE);
-}
-
-std::string GetUmaSuffixForStore(const base::FilePath& file_path) {
-  DCHECK_EQ(kStoreSuffix, file_path.BaseName().Extension());
-  return base::StringPrintf(
-      ".%" PRFilePath, file_path.BaseName().RemoveExtension().value().c_str());
-}
-
-StoreAndHashPrefix::StoreAndHashPrefix(ListIdentifier list_id,
-                                       const HashPrefix& hash_prefix)
-    : list_id(list_id), hash_prefix(hash_prefix) {}
-
-StoreAndHashPrefix::~StoreAndHashPrefix() {}
-
-bool StoreAndHashPrefix::operator==(const StoreAndHashPrefix& other) const {
-  return list_id == other.list_id && hash_prefix == other.hash_prefix;
-}
-
-bool StoreAndHashPrefix::operator!=(const StoreAndHashPrefix& other) const {
-  return !operator==(other);
-}
-
-size_t StoreAndHashPrefix::hash() const {
-  std::size_t first = list_id.hash();
-  std::size_t second = std::hash<std::string>()(hash_prefix);
-
-  return base::HashInts(first, second);
-}
-
-bool SBThreatTypeSetIsValidForCheckBrowseUrl(const SBThreatTypeSet& set) {
-  for (SBThreatType type : set) {
-    switch (type) {
-      case SB_THREAT_TYPE_URL_PHISHING:
-      case SB_THREAT_TYPE_URL_MALWARE:
-      case SB_THREAT_TYPE_URL_UNWANTED:
-      case SB_THREAT_TYPE_SUSPICIOUS_SITE:
-      case SB_THREAT_TYPE_BILLING:
-        break;
-
-      default:
-        return false;
-    }
-  }
-  return true;
-}
-
-bool ListIdentifier::operator==(const ListIdentifier& other) const {
-  return platform_type_ == other.platform_type_ &&
-         threat_entry_type_ == other.threat_entry_type_ &&
-         threat_type_ == other.threat_type_;
-}
-
-bool ListIdentifier::operator!=(const ListIdentifier& other) const {
-  return !operator==(other);
-}
-
-size_t ListIdentifier::hash() const {
-  std::size_t first = std::hash<unsigned int>()(platform_type_);
-  std::size_t second = std::hash<unsigned int>()(threat_entry_type_);
-  std::size_t third = std::hash<unsigned int>()(threat_type_);
-
-  std::size_t interim = base::HashInts(first, second);
-  return base::HashInts(interim, third);
-}
-
-ListIdentifier::ListIdentifier(PlatformType platform_type,
-                               ThreatEntryType threat_entry_type,
-                               ThreatType threat_type)
-    : platform_type_(platform_type),
-      threat_entry_type_(threat_entry_type),
-      threat_type_(threat_type) {
-  DCHECK(PlatformType_IsValid(platform_type));
-  DCHECK(ThreatEntryType_IsValid(threat_entry_type));
-  DCHECK(ThreatType_IsValid(threat_type));
-}
-
-ListIdentifier::ListIdentifier(const ListUpdateResponse& response)
-    : ListIdentifier(response.platform_type(),
-                     response.threat_entry_type(),
-                     response.threat_type()) {}
-
-V4ProtocolConfig::V4ProtocolConfig(const std::string& client_name,
-                                   bool disable_auto_update,
-                                   const std::string& key_param,
-                                   const std::string& version)
-    : client_name(client_name),
-      disable_auto_update(disable_auto_update),
-      key_param(key_param),
-      version(version) {}
-
-V4ProtocolConfig::V4ProtocolConfig(const V4ProtocolConfig& other) = default;
-
-V4ProtocolConfig::~V4ProtocolConfig() {}
-
-// static
-base::TimeDelta V4ProtocolManagerUtil::GetNextBackOffInterval(
-    size_t* error_count,
-    size_t* multiplier) {
-  DCHECK(multiplier && error_count);
-  (*error_count)++;
-  if (*error_count > 1 && *error_count < 9) {
-    // With error count 9 and above we will hit the 24 hour max interval.
-    // Cap the multiplier here to prevent integer overflow errors.
-    *multiplier *= 2;
-  }
-  base::TimeDelta next =
-      base::TimeDelta::FromMinutes(*multiplier * (1 + base::RandDouble()) * 15);
-  base::TimeDelta day = base::TimeDelta::FromHours(24);
-  return next < day ? next : day;
-}
-
-// static
-void V4ProtocolManagerUtil::RecordHttpResponseOrErrorCode(
-    const char* metric_name,
-    int net_error,
-    int response_code) {
-  base::UmaHistogramSparse(metric_name,
-                           net_error == net::OK ? response_code : net_error);
-}
-
-// static
-void V4ProtocolManagerUtil::GetRequestUrlAndHeaders(
-    const std::string& request_base64,
-    const std::string& method_name,
-    const V4ProtocolConfig& config,
-    GURL* gurl,
-    net::HttpRequestHeaders* headers) {
-  const char* url_prefix = g_sbv4_url_prefix_for_testing
-                               ? g_sbv4_url_prefix_for_testing
-                               : kSbV4UrlPrefix;
-  *gurl = GURL(
-      ComposeUrl(url_prefix, method_name, request_base64, config.key_param));
-  UpdateHeaders(headers);
-}
-
-// static
-std::string V4ProtocolManagerUtil::ComposeUrl(const std::string& prefix,
-                                              const std::string& method,
-                                              const std::string& request_base64,
-                                              const std::string& key_param) {
-  DCHECK(!prefix.empty() && !method.empty());
-  std::string url = base::StringPrintf(
-      "%s/%s?$req=%s&$ct=application/x-protobuf", prefix.c_str(),
-      method.c_str(), request_base64.c_str());
-  if (!key_param.empty()) {
-    base::StringAppendF(&url, "&key=%s",
-                        net::EscapeQueryParamValue(key_param, true).c_str());
-  }
-  return url;
-}
-
-// static
-void V4ProtocolManagerUtil::UpdateHeaders(net::HttpRequestHeaders* headers) {
-  // NOTE(vakh): The following header informs the envelope server (which sits in
-  // front of Google's stubby server) that the received GET request should be
-  // interpreted as a POST.
-  headers->SetHeaderIfMissing("X-HTTP-Method-Override", "POST");
-}
-
-// static
-void V4ProtocolManagerUtil::UrlToFullHashes(
-    const GURL& url,
-    std::vector<FullHash>* full_hashes) {
-  std::string canon_host, canon_path, canon_query;
-  CanonicalizeUrl(url, &canon_host, &canon_path, &canon_query);
-
-  std::vector<std::string> hosts;
-  if (url.HostIsIPAddress()) {
-    hosts.push_back(url.host());
-  } else {
-    GenerateHostVariantsToCheck(canon_host, &hosts);
-  }
-
-  std::vector<std::string> paths;
-  GeneratePathVariantsToCheck(canon_path, canon_query, &paths);
-  for (const std::string& host : hosts) {
-    for (const std::string& path : paths) {
-      full_hashes->push_back(crypto::SHA256HashString(host + path));
-    }
-  }
-}
-
-// static
-bool V4ProtocolManagerUtil::FullHashToHashPrefix(const FullHash& full_hash,
-                                                 PrefixSize prefix_size,
-                                                 HashPrefix* hash_prefix) {
-  if (full_hash.size() < prefix_size) {
-    return false;
-  }
-  *hash_prefix = full_hash.substr(0, prefix_size);
-  return true;
-}
-
-// static
-bool V4ProtocolManagerUtil::FullHashToSmallestHashPrefix(
-    const FullHash& full_hash,
-    HashPrefix* hash_prefix) {
-  return FullHashToHashPrefix(full_hash, kMinHashPrefixLength, hash_prefix);
-}
-
-// static
-bool V4ProtocolManagerUtil::FullHashMatchesHashPrefix(
-    const FullHash& full_hash,
-    const HashPrefix& hash_prefix) {
-  return full_hash.compare(0, hash_prefix.length(), hash_prefix) == 0;
-}
-
-// static
-void V4ProtocolManagerUtil::GenerateHostsToCheck(
-    const GURL& url,
-    std::vector<std::string>* hosts) {
-  std::string canon_host;
-  CanonicalizeUrl(url, &canon_host, nullptr, nullptr);
-  GenerateHostVariantsToCheck(canon_host, hosts);
-}
-
-// static
-void V4ProtocolManagerUtil::GeneratePathsToCheck(
-    const GURL& url,
-    std::vector<std::string>* paths) {
-  std::string canon_path;
-  std::string canon_query;
-  CanonicalizeUrl(url, nullptr, &canon_path, &canon_query);
-  GeneratePathVariantsToCheck(canon_path, canon_query, paths);
-}
-
-// static
-void V4ProtocolManagerUtil::GeneratePatternsToCheck(
-    const GURL& url,
-    std::vector<std::string>* urls) {
-  std::string canon_host;
-  std::string canon_path;
-  std::string canon_query;
-  CanonicalizeUrl(url, &canon_host, &canon_path, &canon_query);
-
-  std::vector<std::string> hosts, paths;
-  GenerateHostVariantsToCheck(canon_host, &hosts);
-  GeneratePathVariantsToCheck(canon_path, canon_query, &paths);
-  for (size_t h = 0; h < hosts.size(); ++h) {
-    for (size_t p = 0; p < paths.size(); ++p) {
-      urls->push_back(hosts[h] + paths[p]);
-    }
-  }
-}
-
-// static
-FullHash V4ProtocolManagerUtil::GetFullHash(const GURL& url) {
-  std::string host;
-  std::string path;
-  CanonicalizeUrl(url, &host, &path, nullptr);
-
-  return crypto::SHA256HashString(host + path);
-}
-
-// static
-void V4ProtocolManagerUtil::CanonicalizeUrl(const GURL& url,
-                                            std::string* canonicalized_hostname,
-                                            std::string* canonicalized_path,
-                                            std::string* canonicalized_query) {
-  DCHECK(url.is_valid());
-
-  // We only canonicalize "normal" URLs.
-  if (!url.IsStandard())
-    return;
-
-  // Following canonicalization steps are excluded since url parsing takes care
-  // of those :-
-  // 1. Remove any tab (0x09), CR (0x0d), and LF (0x0a) chars from url.
-  //    (Exclude escaped version of these chars).
-  // 2. Normalize hostname to 4 dot-seperated decimal values.
-  // 3. Lowercase hostname.
-  // 4. Resolve path sequences "/../" and "/./".
-
-  // That leaves us with the following :-
-  // 1. Remove fragment in URL.
-  GURL url_without_fragment;
-  GURL::Replacements f_replacements;
-  f_replacements.ClearRef();
-  f_replacements.ClearUsername();
-  f_replacements.ClearPassword();
-  url_without_fragment = url.ReplaceComponents(f_replacements);
-
-  // 2. Do URL unescaping until no more hex encoded characters exist.
-  std::string url_unescaped_str(Unescape(url_without_fragment.spec()));
-  url::Parsed parsed;
-  url::ParseStandardURL(url_unescaped_str.data(), url_unescaped_str.length(),
-                        &parsed);
-
-  // 3. In hostname, remove all leading and trailing dots.
-  base::StringPiece host;
-  if (parsed.host.len > 0)
-    host.set(url_unescaped_str.data() + parsed.host.begin, parsed.host.len);
-
-  base::StringPiece host_without_end_dots =
-      base::TrimString(host, ".", base::TrimPositions::TRIM_ALL);
-
-  // 4. In hostname, replace consecutive dots with a single dot.
-  std::string host_without_consecutive_dots(
-      RemoveConsecutiveChars(host_without_end_dots, '.'));
-
-  // 5. In path, replace runs of consecutive slashes with a single slash.
-  base::StringPiece path;
-  if (parsed.path.len > 0)
-    path.set(url_unescaped_str.data() + parsed.path.begin, parsed.path.len);
-  std::string path_without_consecutive_slash(RemoveConsecutiveChars(path, '/'));
-
-  url::Replacements<char> hp_replacements;
-  hp_replacements.SetHost(
-      host_without_consecutive_dots.data(),
-      url::Component(0, host_without_consecutive_dots.length()));
-  hp_replacements.SetPath(
-      path_without_consecutive_slash.data(),
-      url::Component(0, path_without_consecutive_slash.length()));
-
-  std::string url_unescaped_with_can_hostpath;
-  url::StdStringCanonOutput output(&url_unescaped_with_can_hostpath);
-  url::Parsed temp_parsed;
-  url::ReplaceComponents(url_unescaped_str.data(), url_unescaped_str.length(),
-                         parsed, hp_replacements, nullptr, &output,
-                         &temp_parsed);
-  output.Complete();
-
-  // 6. Step needed to revert escaping done in url::ReplaceComponents.
-  url_unescaped_with_can_hostpath = Unescape(url_unescaped_with_can_hostpath);
-
-  // 7. After performing all above steps, percent-escape all chars in url which
-  // are <= ASCII 32, >= 127, #, %. Escapes must be uppercase hex characters.
-  std::string escaped_canon_url_str(Escape(url_unescaped_with_can_hostpath));
-  url::Parsed final_parsed;
-  url::ParseStandardURL(escaped_canon_url_str.data(),
-                        escaped_canon_url_str.length(), &final_parsed);
-
-  if (canonicalized_hostname && final_parsed.host.len > 0) {
-    *canonicalized_hostname = escaped_canon_url_str.substr(
-        final_parsed.host.begin, final_parsed.host.len);
-  }
-  if (canonicalized_path && final_parsed.path.len > 0) {
-    *canonicalized_path = escaped_canon_url_str.substr(final_parsed.path.begin,
-                                                       final_parsed.path.len);
-  }
-  if (canonicalized_query && final_parsed.query.len > 0) {
-    *canonicalized_query = escaped_canon_url_str.substr(
-        final_parsed.query.begin, final_parsed.query.len);
-  }
-}
-
-// static
-std::string V4ProtocolManagerUtil::RemoveConsecutiveChars(base::StringPiece str,
-                                                          const char c) {
-  std::string output;
-  // Output is at most the length of the original string.
-  output.reserve(str.size());
-
-  size_t i = 0;
-  while (i < str.size()) {
-    output.append(1, str[i++]);
-    if (str[i - 1] == c) {
-      while (i < str.size() && str[i] == c) {
-        i++;
-      }
-    }
-  }
-
-  return output;
-}
-
-// static
-void V4ProtocolManagerUtil::GenerateHostVariantsToCheck(
-    const std::string& host,
-    std::vector<std::string>* hosts) {
-  hosts->clear();
-
-  if (host.empty())
-    return;
-
-  // Per the Safe Browsing Protocol v2 spec, we try the host, and also up to 4
-  // hostnames formed by starting with the last 5 components and successively
-  // removing the leading component.  The last component isn't examined alone,
-  // since it's the TLD or a subcomponent thereof.
-  //
-  // Note that we don't need to be clever about stopping at the "real" eTLD --
-  // the data on the server side has been filtered to ensure it will not
-  // blacklist a whole TLD, and it's not significantly slower on our side to
-  // just check too much.
-  //
-  // Also note that because we have a simple blacklist, not some sort of complex
-  // whitelist-in-blacklist or vice versa, it doesn't matter what order we check
-  // these in.
-  const size_t kMaxHostsToCheck = 4;
-  bool skipped_last_component = false;
-  for (std::string::const_reverse_iterator i(host.rbegin());
-       i != host.rend() && hosts->size() < kMaxHostsToCheck; ++i) {
-    if (*i == '.') {
-      if (skipped_last_component)
-        hosts->push_back(std::string(i.base(), host.end()));
-      else
-        skipped_last_component = true;
-    }
-  }
-  hosts->push_back(host);
-}
-
-// static
-void V4ProtocolManagerUtil::GeneratePathVariantsToCheck(
-    const std::string& path,
-    const std::string& query,
-    std::vector<std::string>* paths) {
-  paths->clear();
-
-  if (path.empty())
-    return;
-
-  // Per the Safe Browsing Protocol v2 spec, we try the exact path with/without
-  // the query parameters, and also up to 4 paths formed by starting at the root
-  // and adding more path components.
-  //
-  // As with the hosts above, it doesn't matter what order we check these in.
-  const size_t kMaxPathsToCheck = 4;
-  for (std::string::const_iterator i(path.begin());
-       i != path.end() && paths->size() < kMaxPathsToCheck; ++i) {
-    if (*i == '/')
-      paths->push_back(std::string(path.begin(), i + 1));
-  }
-
-  if (!paths->empty() && paths->back() != path)
-    paths->push_back(path);
-
-  if (!query.empty())
-    paths->push_back(path + "?" + query);
-}
-
-// static
-void V4ProtocolManagerUtil::SetClientInfoFromConfig(
-    ClientInfo* client_info,
-    const V4ProtocolConfig& config) {
-  DCHECK(client_info);
-  client_info->set_client_id(config.client_name);
-  client_info->set_client_version(config.version);
-}
-
-// static
-bool V4ProtocolManagerUtil::GetIPV6AddressFromString(
-    const std::string& ip_address,
-    net::IPAddress* address) {
-  DCHECK(address);
-  if (!address->AssignFromIPLiteral(ip_address))
-    return false;
-  if (address->IsIPv4())
-    *address = net::ConvertIPv4ToIPv4MappedIPv6(*address);
-  return address->IsIPv6();
-}
-
-// static
-bool V4ProtocolManagerUtil::IPAddressToEncodedIPV6Hash(
-    const std::string& ip_address,
-    FullHash* hashed_encoded_ip) {
-  net::IPAddress address;
-  if (!GetIPV6AddressFromString(ip_address, &address)) {
-    return false;
-  }
-  std::string packed_ip = net::IPAddressToPackedString(address);
-  if (packed_ip.empty()) {
-    return false;
-  }
-
-  const std::string hash = base::SHA1HashString(packed_ip);
-  DCHECK_EQ(20u, hash.size());
-  hashed_encoded_ip->resize(hash.size() + 1);
-  hashed_encoded_ip->replace(0, hash.size(), hash);
-  (*hashed_encoded_ip)[hash.size()] = static_cast<unsigned char>(128);
-  return true;
-}
-
-// static
-void V4ProtocolManagerUtil::GetListClientStatesFromStoreStateMap(
-    const std::unique_ptr<StoreStateMap>& store_state_map,
-    std::vector<std::string>* list_client_states) {
-  std::transform(
-      store_state_map->begin(), store_state_map->end(),
-      std::back_inserter(*list_client_states),
-      [](const std::map<ListIdentifier, std::string>::value_type& pair) {
-        return pair.second;
-      });
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/db/v4_protocol_manager_util.h b/components/safe_browsing/db/v4_protocol_manager_util.h
deleted file mode 100644
index 1bb74cc..0000000
--- a/components/safe_browsing/db/v4_protocol_manager_util.h
+++ /dev/null
@@ -1,468 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_DB_V4_PROTOCOL_MANAGER_UTIL_H_
-#define COMPONENTS_SAFE_BROWSING_DB_V4_PROTOCOL_MANAGER_UTIL_H_
-
-// A class that implements the stateless methods used by the GetHashUpdate and
-// GetFullHash stubby calls made by Chrome using the SafeBrowsing V4 protocol.
-
-#include <functional>
-#include <initializer_list>
-#include <memory>
-#include <ostream>
-#include <string>
-#include <unordered_map>
-#include <unordered_set>
-#include <vector>
-
-#include "base/containers/flat_set.h"
-#include "base/gtest_prod_util.h"
-#include "base/strings/string_piece.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/db/safebrowsing.pb.h"
-#include "url/gurl.h"
-
-namespace net {
-class HttpRequestHeaders;
-class IPAddress;
-}  // namespace net
-
-namespace safe_browsing {
-
-
-// The size of the hash prefix, in bytes. It should be between 4 to 32 (full
-// hash).
-using PrefixSize = size_t;
-
-// The minimum expected size (in bytes) of a hash-prefix.
-const PrefixSize kMinHashPrefixLength = 4;
-
-// The maximum expected size (in bytes) of a hash-prefix. This represents the
-// length of a SHA256 hash.
-const PrefixSize kMaxHashPrefixLength = 32;
-
-// A hash prefix sent by the SafeBrowsing PVer4 service.
-using HashPrefix = std::string;
-
-// A full SHA256 hash.
-using FullHash = HashPrefix;
-
-using ListUpdateRequest = FetchThreatListUpdatesRequest::ListUpdateRequest;
-using ListUpdateResponse = FetchThreatListUpdatesResponse::ListUpdateResponse;
-
-void SetSbV4UrlPrefixForTesting(const char* url_prefix);
-
-// Config passed to the constructor of a V4 protocol manager.
-struct V4ProtocolConfig {
-  // The safe browsing client name sent in each request.
-  std::string client_name;
-
-  // Disable auto-updates using a command line switch.
-  bool disable_auto_update;
-
-  // The Google API key.
-  std::string key_param;
-
-  // Current product version sent in each request.
-  std::string version;
-
-  V4ProtocolConfig(const std::string& client_name,
-                   bool disable_auto_update,
-                   const std::string& key_param,
-                   const std::string& version);
-  V4ProtocolConfig(const V4ProtocolConfig& other);
-  ~V4ProtocolConfig();
-
- private:
-  V4ProtocolConfig() = delete;
-};
-
-// Get the v4 protocol config struct with a given client name, and ability to
-// enable/disable database auto update.
-V4ProtocolConfig GetV4ProtocolConfig(const std::string& client_name,
-                                     bool disable_auto_update);
-
-// Returns the URL to use for sending threat reports and other Safe Browsing
-// hits back to Safe Browsing service.
-std::string GetReportUrl(
-    const V4ProtocolConfig& config,
-    const std::string& method,
-    const ExtendedReportingLevel* reporting_level = nullptr);
-
-// Different types of threats that SafeBrowsing protects against. This is the
-// type that's returned to the clients of SafeBrowsing in Chromium.
-// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.safe_browsing
-// GENERATED_JAVA_PREFIX_TO_STRIP: SB_THREAT_TYPE_
-enum SBThreatType {
-  // This type can be used for lists that can be checked synchronously so a
-  // client callback isn't required, or for whitelists.
-  SB_THREAT_TYPE_UNUSED,
-
-  // No threat at all.
-  SB_THREAT_TYPE_SAFE,
-
-  // The URL is being used for phishing.
-  SB_THREAT_TYPE_URL_PHISHING,
-
-  // The URL hosts malware.
-  SB_THREAT_TYPE_URL_MALWARE,
-
-  // The URL hosts unwanted programs.
-  SB_THREAT_TYPE_URL_UNWANTED,
-
-  // The download URL is malware.
-  SB_THREAT_TYPE_URL_BINARY_MALWARE,
-
-  // Url detected by the client-side phishing model.  Note that unlike the
-  // above values, this does not correspond to a downloaded list.
-  SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING,
-
-  // The Chrome extension or app (given by its ID) is malware.
-  SB_THREAT_TYPE_EXTENSION,
-
-  // Url detected by the client-side malware IP list. This IP list is part
-  // of the client side detection model.
-  SB_THREAT_TYPE_URL_CLIENT_SIDE_MALWARE,
-
-  // Url leads to a blacklisted resource script. Note that no warnings should be
-  // shown on this threat type, but an incident report might be sent.
-  SB_THREAT_TYPE_BLACKLISTED_RESOURCE,
-
-  // Url abuses a permission API.
-  SB_THREAT_TYPE_API_ABUSE,
-
-  // Activation patterns for the Subresource Filter.
-  SB_THREAT_TYPE_SUBRESOURCE_FILTER,
-
-  // CSD Phishing whitelist.  This "threat" means a URL matched the whitelist.
-  SB_THREAT_TYPE_CSD_WHITELIST,
-
-  // DEPRECATED. Url detected by password protection service.
-  DEPRECATED_SB_THREAT_TYPE_URL_PASSWORD_PROTECTION_PHISHING,
-
-  // Saved password reuse detected on low reputation page,
-  SB_THREAT_TYPE_SAVED_PASSWORD_REUSE,
-
-  // Chrome signed in and syncing gaia password reuse detected on low reputation
-  // page,
-  SB_THREAT_TYPE_SIGNED_IN_SYNC_PASSWORD_REUSE,
-
-  // Chrome signed in non syncing gaia password reuse detected on low reputation
-  // page,
-  SB_THREAT_TYPE_SIGNED_IN_NON_SYNC_PASSWORD_REUSE,
-
-  // A Google ad that caused a blocked autoredirect was collected
-  SB_THREAT_TYPE_BLOCKED_AD_REDIRECT,
-
-  // A sample of an ad was collected
-  SB_THREAT_TYPE_AD_SAMPLE,
-
-  // A report of Google ad that caused a blocked popup was collected.
-  SB_THREAT_TYPE_BLOCKED_AD_POPUP,
-
-  // The page loaded a resource from the Suspicious Site list.
-  SB_THREAT_TYPE_SUSPICIOUS_SITE,
-
-  // Enterprise password reuse detected on low reputation page.
-  SB_THREAT_TYPE_ENTERPRISE_PASSWORD_REUSE,
-
-  // Potential billing detected.
-  SB_THREAT_TYPE_BILLING,
-
-  // Off-market APK file downloaded, which could be potentially dangerous.
-  SB_THREAT_TYPE_APK_DOWNLOAD,
-
-  // Match found in the local high-confidence allowlist.
-  SB_THREAT_TYPE_HIGH_CONFIDENCE_ALLOWLIST,
-};
-
-using SBThreatTypeSet = base::flat_set<SBThreatType>;
-
-// Return true if |set| only contains types that are valid for CheckBrowseUrl().
-// Intended for use in DCHECK().
-bool SBThreatTypeSetIsValidForCheckBrowseUrl(const SBThreatTypeSet& set);
-
-// Shorthand for creating an SBThreatTypeSet from a list of SBThreatTypes. Use
-// like CreateSBThreatTypeSet({SB_THREAT_TYPE_URL_PHISHING,
-//                             SB_THREAT_TYPE_URL_MALWARE})
-inline SBThreatTypeSet CreateSBThreatTypeSet(
-    std::initializer_list<SBThreatType> set) {
-  return SBThreatTypeSet(set);
-}
-
-// The information required to uniquely identify each list the client is
-// interested in maintaining and downloading from the SafeBrowsing servers.
-// For example, for digests of Malware binaries on Windows:
-// platform_type = WINDOWS,
-// threat_entry_type = EXECUTABLE,
-// threat_type = MALWARE
-class ListIdentifier {
- public:
-  ListIdentifier(PlatformType platform_type,
-                 ThreatEntryType threat_entry_type,
-                 ThreatType threat_type);
-  explicit ListIdentifier(const ListUpdateResponse&);
-
-  bool operator==(const ListIdentifier& other) const;
-  bool operator!=(const ListIdentifier& other) const;
-  size_t hash() const;
-
-  PlatformType platform_type() const { return platform_type_; }
-  ThreatEntryType threat_entry_type() const { return threat_entry_type_; }
-  ThreatType threat_type() const { return threat_type_; }
-
- private:
-  PlatformType platform_type_;
-  ThreatEntryType threat_entry_type_;
-  ThreatType threat_type_;
-
-  ListIdentifier() = delete;
-};
-
-std::ostream& operator<<(std::ostream& os, const ListIdentifier& id);
-
-PlatformType GetCurrentPlatformType();
-ListIdentifier GetCertCsdDownloadWhitelistId();
-ListIdentifier GetChromeExtMalwareId();
-ListIdentifier GetChromeUrlApiId();
-ListIdentifier GetChromeUrlClientIncidentId();
-ListIdentifier GetIpMalwareId();
-ListIdentifier GetUrlBillingId();
-ListIdentifier GetUrlCsdDownloadWhitelistId();
-ListIdentifier GetUrlCsdWhitelistId();
-ListIdentifier GetUrlHighConfidenceAllowlistId();
-ListIdentifier GetUrlMalBinId();
-ListIdentifier GetUrlMalwareId();
-ListIdentifier GetUrlSocEngId();
-ListIdentifier GetUrlSubresourceFilterId();
-ListIdentifier GetUrlSuspiciousSiteId();
-ListIdentifier GetUrlUwsId();
-
-// Returns the basename of the store file, without the ".store" extension.
-std::string GetUmaSuffixForStore(const base::FilePath& file_path);
-
-// Represents the state of each store.
-using StoreStateMap = std::unordered_map<ListIdentifier, std::string>;
-
-// Sever response, parsed in vector form.
-using ParsedServerResponse = std::vector<std::unique_ptr<ListUpdateResponse>>;
-
-// Holds the hash prefix and the store that it matched in.
-struct StoreAndHashPrefix {
- public:
-  ListIdentifier list_id;
-  HashPrefix hash_prefix;
-
-  StoreAndHashPrefix(ListIdentifier list_id, const HashPrefix& hash_prefix);
-  ~StoreAndHashPrefix();
-
-  bool operator==(const StoreAndHashPrefix& other) const;
-  bool operator!=(const StoreAndHashPrefix& other) const;
-  size_t hash() const;
-
- private:
-  StoreAndHashPrefix() = delete;
-};
-
-// Used to track the hash prefix and the store in which a full hash's prefix
-// matched.
-using StoreAndHashPrefixes = std::vector<StoreAndHashPrefix>;
-
-// Enumerate failures for histogramming purposes.  DO NOT CHANGE THE
-// ORDERING OF THESE VALUES.
-enum V4OperationResult {
-  // 200 response code means that the server recognized the request.
-  STATUS_200 = 0,
-
-  // Subset of successful responses where the response body wasn't parsable.
-  PARSE_ERROR = 1,
-
-  // Operation request failed (network error).
-  NETWORK_ERROR = 2,
-
-  // Operation request returned HTTP result code other than 200.
-  HTTP_ERROR = 3,
-
-  // Operation attempted during error backoff, no request sent.
-  BACKOFF_ERROR = 4,
-
-  // Operation attempted before min wait duration elapsed, no request sent.
-  MIN_WAIT_DURATION_ERROR = 5,
-
-  // Identical operation already pending.
-  ALREADY_PENDING_ERROR = 6,
-
-  // Memory space for histograms is determined by the max.  ALWAYS
-  // ADD NEW VALUES BEFORE THIS ONE.
-  OPERATION_RESULT_MAX = 7
-};
-
-// A class that provides static methods related to the Pver4 protocol.
-class V4ProtocolManagerUtil {
- public:
-  // Canonicalizes url as per Google Safe Browsing Specification.
-  // See: https://developers.google.com/safe-browsing/v4/urls-hashing
-  static void CanonicalizeUrl(const GURL& url,
-                              std::string* canonicalized_hostname,
-                              std::string* canonicalized_path,
-                              std::string* canonicalized_query);
-
-  // This method returns the host suffix combinations from the hostname in the
-  // URL, as described here:
-  // https://developers.google.com/safe-browsing/v4/urls-hashing
-  static void GenerateHostVariantsToCheck(const std::string& host,
-                                          std::vector<std::string>* hosts);
-
-  // This method returns the path prefix combinations from the path in the
-  // URL, as described here:
-  // https://developers.google.com/safe-browsing/v4/urls-hashing
-  static void GeneratePathVariantsToCheck(const std::string& path,
-                                          const std::string& query,
-                                          std::vector<std::string>* paths);
-
-  // Given a URL, returns all the patterns we need to check.
-  static void GeneratePatternsToCheck(const GURL& url,
-                                      std::vector<std::string>* urls);
-
-  // Returns a FullHash for the basic host+path pattern for a given URL after
-  // canonicalization. Not intended for general use.
-  static FullHash GetFullHash(const GURL& url);
-
-  // Generates a Pver4 request URL and sets the appropriate header values.
-  // |request_base64| is the serialized request protocol buffer encoded in
-  // base 64.
-  // |method_name| is the name of the method to call, as specified in the proto,
-  // |config| is an instance of V4ProtocolConfig that stores the client config,
-  // |gurl| is set to the value of the PVer4 request URL,
-  // |headers| is populated with the appropriate header values.
-  static void GetRequestUrlAndHeaders(const std::string& request_base64,
-                                      const std::string& method_name,
-                                      const V4ProtocolConfig& config,
-                                      GURL* gurl,
-                                      net::HttpRequestHeaders* headers);
-
-  // Worker function for calculating the backoff times.
-  // |multiplier| is doubled for each consecutive error after the
-  // first, and |error_count| is incremented with each call.
-  // Backoff interval is MIN(((2^(n-1))*15 minutes) * (RAND + 1), 24 hours)
-  // where n is the number of consecutive errors.
-  static base::TimeDelta GetNextBackOffInterval(size_t* error_count,
-                                                size_t* multiplier);
-
-  // Record HTTP response code when there's no error in fetching an HTTP
-  // request, and the error code, when there is.
-  // |metric_name| is the name of the UMA metric to record the response code or
-  // error code against, |net_error| represents the net error code of the HTTP
-  // request, and |response code| represents the HTTP response code received
-  // from the server.
-  static void RecordHttpResponseOrErrorCode(const char* metric_name,
-                                            int net_error,
-                                            int response_code);
-
-  // Generate the set of FullHashes to check for |url|.
-  static void UrlToFullHashes(const GURL& url,
-                              std::vector<FullHash>* full_hashes);
-
-  static bool FullHashToHashPrefix(const FullHash& full_hash,
-                                   PrefixSize prefix_size,
-                                   HashPrefix* hash_prefix);
-
-  static bool FullHashToSmallestHashPrefix(const FullHash& full_hash,
-                                           HashPrefix* hash_prefix);
-
-  static bool FullHashMatchesHashPrefix(const FullHash& full_hash,
-                                        const HashPrefix& hash_prefix);
-
-  static void SetClientInfoFromConfig(ClientInfo* client_info,
-                                      const V4ProtocolConfig& config);
-
-  static bool GetIPV6AddressFromString(const std::string& ip_address,
-                                       net::IPAddress* address);
-
-  // Converts a IPV4 or IPV6 address in |ip_address| to the SHA1 hash of the
-  // corresponding packed IPV6 address in |hashed_encoded_ip|, and adds an
-  // extra byte containing the value 128 at the end. This is done to match the
-  // server implementation for calculating the hash prefix of an IP address.
-  static bool IPAddressToEncodedIPV6Hash(const std::string& ip_address,
-                                         FullHash* hashed_encoded_ip);
-
-  // Stores the client state values for each of the lists in |store_state_map|
-  // into |list_client_states|.
-  static void GetListClientStatesFromStoreStateMap(
-      const std::unique_ptr<StoreStateMap>& store_state_map,
-      std::vector<std::string>* list_client_states);
-
- private:
-  V4ProtocolManagerUtil() {}
-
-  FRIEND_TEST_ALL_PREFIXES(V4ProtocolManagerUtilTest, TestBackOffLogic);
-  FRIEND_TEST_ALL_PREFIXES(V4ProtocolManagerUtilTest,
-                           TestGetRequestUrlAndUpdateHeaders);
-  FRIEND_TEST_ALL_PREFIXES(V4ProtocolManagerUtilTest, UrlParsing);
-  FRIEND_TEST_ALL_PREFIXES(V4ProtocolManagerUtilTest, CanonicalizeUrl);
-
-  // Composes a URL using |prefix|, |method| (e.g.: encodedFullHashes).
-  // |request_base64|, |client_id|, |version| and |key_param|. |prefix|
-  // should contain the entire url prefix including scheme, host and path.
-  static std::string ComposeUrl(const std::string& prefix,
-                                const std::string& method,
-                                const std::string& request_base64,
-                                const std::string& key_param);
-
-  // Sets the HTTP headers expected by a standard PVer4 request.
-  static void UpdateHeaders(net::HttpRequestHeaders* headers);
-
-  // Given a URL, returns all the hosts we need to check.  They are returned
-  // in order of size (i.e. b.c is first, then a.b.c).
-  static void GenerateHostsToCheck(const GURL& url,
-                                   std::vector<std::string>* hosts);
-
-  // Given a URL, returns all the paths we need to check.
-  static void GeneratePathsToCheck(const GURL& url,
-                                   std::vector<std::string>* paths);
-
-  static std::string RemoveConsecutiveChars(base::StringPiece str,
-                                            const char c);
-
-  DISALLOW_COPY_AND_ASSIGN(V4ProtocolManagerUtil);
-};
-
-using StoresToCheck = std::unordered_set<ListIdentifier>;
-
-}  // namespace safe_browsing
-
-namespace std {
-
-template <>
-struct hash<safe_browsing::PlatformType> {
-  std::size_t operator()(const safe_browsing::PlatformType& p) const {
-    return std::hash<unsigned int>()(p);
-  }
-};
-
-template <>
-struct hash<safe_browsing::ThreatEntryType> {
-  std::size_t operator()(const safe_browsing::ThreatEntryType& tet) const {
-    return std::hash<unsigned int>()(tet);
-  }
-};
-
-template <>
-struct hash<safe_browsing::ThreatType> {
-  std::size_t operator()(const safe_browsing::ThreatType& tt) const {
-    return std::hash<unsigned int>()(tt);
-  }
-};
-
-template <>
-struct hash<safe_browsing::ListIdentifier> {
-  std::size_t operator()(const safe_browsing::ListIdentifier& id) const {
-    return id.hash();
-  }
-};
-
-}  // namespace std
-
-#endif  // COMPONENTS_SAFE_BROWSING_DB_V4_PROTOCOL_MANAGER_UTIL_H_
diff --git a/components/safe_browsing/db/v4_protocol_manager_util_unittest.cc b/components/safe_browsing/db/v4_protocol_manager_util_unittest.cc
deleted file mode 100644
index a69357c..0000000
--- a/components/safe_browsing/db/v4_protocol_manager_util_unittest.cc
+++ /dev/null
@@ -1,287 +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.
-
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
-
-#include <vector>
-
-#include "base/base64.h"
-#include "base/stl_util.h"
-#include "base/strings/stringprintf.h"
-#include "base/time/time.h"
-#include "components/safe_browsing/db/v4_test_util.h"
-#include "net/base/escape.h"
-#include "net/http/http_request_headers.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using base::Time;
-using base::TimeDelta;
-
-namespace safe_browsing {
-
-class V4ProtocolManagerUtilTest : public testing::Test {};
-
-TEST_F(V4ProtocolManagerUtilTest, TestBackOffLogic) {
-  size_t error_count = 0, back_off_multiplier = 1;
-
-  // 1 error.
-  base::TimeDelta next = V4ProtocolManagerUtil::GetNextBackOffInterval(
-      &error_count, &back_off_multiplier);
-  EXPECT_EQ(1U, error_count);
-  EXPECT_EQ(1U, back_off_multiplier);
-  EXPECT_LE(TimeDelta::FromMinutes(15), next);
-  EXPECT_GE(TimeDelta::FromMinutes(30), next);
-
-  // 2 errors.
-  next = V4ProtocolManagerUtil::GetNextBackOffInterval(&error_count,
-                                                       &back_off_multiplier);
-  EXPECT_EQ(2U, error_count);
-  EXPECT_EQ(2U, back_off_multiplier);
-  EXPECT_LE(TimeDelta::FromMinutes(30), next);
-  EXPECT_GE(TimeDelta::FromMinutes(60), next);
-
-  // 3 errors.
-  next = V4ProtocolManagerUtil::GetNextBackOffInterval(&error_count,
-                                                       &back_off_multiplier);
-  EXPECT_EQ(3U, error_count);
-  EXPECT_EQ(4U, back_off_multiplier);
-  EXPECT_LE(TimeDelta::FromMinutes(60), next);
-  EXPECT_GE(TimeDelta::FromMinutes(120), next);
-
-  // 4 errors.
-  next = V4ProtocolManagerUtil::GetNextBackOffInterval(&error_count,
-                                                       &back_off_multiplier);
-  EXPECT_EQ(4U, error_count);
-  EXPECT_EQ(8U, back_off_multiplier);
-  EXPECT_LE(TimeDelta::FromMinutes(120), next);
-  EXPECT_GE(TimeDelta::FromMinutes(240), next);
-
-  // 5 errors.
-  next = V4ProtocolManagerUtil::GetNextBackOffInterval(&error_count,
-                                                       &back_off_multiplier);
-  EXPECT_EQ(5U, error_count);
-  EXPECT_EQ(16U, back_off_multiplier);
-  EXPECT_LE(TimeDelta::FromMinutes(240), next);
-  EXPECT_GE(TimeDelta::FromMinutes(480), next);
-
-  // 6 errors.
-  next = V4ProtocolManagerUtil::GetNextBackOffInterval(&error_count,
-                                                       &back_off_multiplier);
-  EXPECT_EQ(6U, error_count);
-  EXPECT_EQ(32U, back_off_multiplier);
-  EXPECT_LE(TimeDelta::FromMinutes(480), next);
-  EXPECT_GE(TimeDelta::FromMinutes(960), next);
-
-  // 7 errors.
-  next = V4ProtocolManagerUtil::GetNextBackOffInterval(&error_count,
-                                                       &back_off_multiplier);
-  EXPECT_EQ(7U, error_count);
-  EXPECT_EQ(64U, back_off_multiplier);
-  EXPECT_LE(TimeDelta::FromMinutes(960), next);
-  EXPECT_GE(TimeDelta::FromMinutes(1920), next);
-
-  // 8 errors, reached max backoff.
-  next = V4ProtocolManagerUtil::GetNextBackOffInterval(&error_count,
-                                                       &back_off_multiplier);
-  EXPECT_EQ(8U, error_count);
-  EXPECT_EQ(128U, back_off_multiplier);
-  EXPECT_EQ(TimeDelta::FromHours(24), next);
-
-  // 9 errors, reached max backoff and multiplier capped.
-  next = V4ProtocolManagerUtil::GetNextBackOffInterval(&error_count,
-                                                       &back_off_multiplier);
-  EXPECT_EQ(9U, error_count);
-  EXPECT_EQ(128U, back_off_multiplier);
-  EXPECT_EQ(TimeDelta::FromHours(24), next);
-}
-
-TEST_F(V4ProtocolManagerUtilTest, TestGetRequestUrlAndUpdateHeaders) {
-  net::HttpRequestHeaders headers;
-  GURL gurl;
-  V4ProtocolManagerUtil::GetRequestUrlAndHeaders("request_base64", "someMethod",
-                                                 GetTestV4ProtocolConfig(),
-                                                 &gurl, &headers);
-  std::string expectedUrl =
-      "https://safebrowsing.googleapis.com/v4/someMethod?"
-      "$req=request_base64&$ct=application/x-protobuf&key=test_key_param";
-  EXPECT_EQ(expectedUrl, gurl.spec());
-  std::string header_value;
-  EXPECT_TRUE(headers.GetHeader("X-HTTP-Method-Override", &header_value));
-  EXPECT_EQ("POST", header_value);
-}
-
-// Tests that we generate the required host/path combinations for testing
-// according to the Safe Browsing spec.
-// See: https://developers.google.com/safe-browsing/v4/urls-hashing
-TEST_F(V4ProtocolManagerUtilTest, UrlParsing) {
-  std::vector<std::string> hosts, paths;
-
-  GURL url("http://a.b.c/1/2.html?param=1");
-  V4ProtocolManagerUtil::GenerateHostsToCheck(url, &hosts);
-  V4ProtocolManagerUtil::GeneratePathsToCheck(url, &paths);
-  EXPECT_EQ(hosts.size(), static_cast<size_t>(2));
-  EXPECT_EQ(paths.size(), static_cast<size_t>(4));
-  EXPECT_EQ(hosts[0], "b.c");
-  EXPECT_EQ(hosts[1], "a.b.c");
-
-  EXPECT_TRUE(base::Contains(paths, "/1/2.html?param=1"));
-  EXPECT_TRUE(base::Contains(paths, "/1/2.html"));
-  EXPECT_TRUE(base::Contains(paths, "/1/"));
-  EXPECT_TRUE(base::Contains(paths, "/"));
-
-  url = GURL("http://a.b.c.d.e.f.g/1.html");
-  V4ProtocolManagerUtil::GenerateHostsToCheck(url, &hosts);
-  V4ProtocolManagerUtil::GeneratePathsToCheck(url, &paths);
-  EXPECT_EQ(hosts.size(), static_cast<size_t>(5));
-  EXPECT_EQ(paths.size(), static_cast<size_t>(2));
-  EXPECT_EQ(hosts[0], "f.g");
-  EXPECT_EQ(hosts[1], "e.f.g");
-  EXPECT_EQ(hosts[2], "d.e.f.g");
-  EXPECT_EQ(hosts[3], "c.d.e.f.g");
-  EXPECT_EQ(hosts[4], "a.b.c.d.e.f.g");
-  EXPECT_TRUE(base::Contains(paths, "/1.html"));
-  EXPECT_TRUE(base::Contains(paths, "/"));
-
-  url = GURL("http://a.b/saw-cgi/eBayISAPI.dll/");
-  V4ProtocolManagerUtil::GeneratePathsToCheck(url, &paths);
-  EXPECT_EQ(paths.size(), static_cast<size_t>(3));
-  EXPECT_TRUE(base::Contains(paths, "/saw-cgi/eBayISAPI.dll/"));
-  EXPECT_TRUE(base::Contains(paths, "/saw-cgi/"));
-  EXPECT_TRUE(base::Contains(paths, "/"));
-}
-
-// Tests the url canonicalization according to the Safe Browsing spec.
-// See: https://developers.google.com/safe-browsing/v4/urls-hashing
-TEST_F(V4ProtocolManagerUtilTest, CanonicalizeUrl) {
-  struct {
-    const char* input_url;
-    const char* expected_canonicalized_hostname;
-    const char* expected_canonicalized_path;
-    const char* expected_canonicalized_query;
-  } tests[] = {
-      {"http://host/%25%32%35", "host", "/%25", ""},
-      {"http://host/%25%32%35%25%32%35", "host", "/%25%25", ""},
-      {"http://host/%2525252525252525", "host", "/%25", ""},
-      {"http://host/asdf%25%32%35asd", "host", "/asdf%25asd", ""},
-      {"http://host/%%%25%32%35asd%%", "host", "/%25%25%25asd%25%25", ""},
-      {"http://host/%%%25%32%35asd%%", "host", "/%25%25%25asd%25%25", ""},
-      {"http://www.google.com/", "www.google.com", "/", ""},
-      {"http://%31%36%38%2e%31%38%38%2e%39%39%2e%32%36/%2E%73%65%63%75%72%65/"
-       "%77"
-       "%77%77%2E%65%62%61%79%2E%63%6F%6D/",
-       "168.188.99.26", "/.secure/www.ebay.com/", ""},
-      {"http://195.127.0.11/uploads/%20%20%20%20/.verify/"
-       ".eBaysecure=updateuserd"
-       "ataxplimnbqmn-xplmvalidateinfoswqpcmlx=hgplmcx/",
-       "195.127.0.11",
-       "/uploads/%20%20%20%20/.verify/"
-       ".eBaysecure=updateuserdataxplimnbqmn-xplmv"
-       "alidateinfoswqpcmlx=hgplmcx/",
-       ""},
-      {"http://host.com/%257Ea%2521b%2540c%2523d%2524e%25f%255E00%252611%252A"
-       "22%252833%252944_55%252B",
-       "host.com", "/~a!b@c%23d$e%25f^00&11*22(33)44_55+", ""},
-      {"http://3279880203/blah", "195.127.0.11", "/blah", ""},
-      {"http://www.google.com/blah/..", "www.google.com", "/", ""},
-      {"http://www.google.com/blah#fraq", "www.google.com", "/blah", ""},
-      {"http://www.GOOgle.com/", "www.google.com", "/", ""},
-      {"http://www.google.com.../", "www.google.com", "/", ""},
-      {"http://www.google.com/q?", "www.google.com", "/q", ""},
-      {"http://www.google.com/q?r?", "www.google.com", "/q", "r?"},
-      {"http://www.google.com/q?r?s", "www.google.com", "/q", "r?s"},
-      {"http://evil.com/foo#bar#baz", "evil.com", "/foo", ""},
-      {"http://evil.com/foo;", "evil.com", "/foo;", ""},
-      {"http://evil.com/foo?bar;", "evil.com", "/foo", "bar;"},
-      {"http://notrailingslash.com", "notrailingslash.com", "/", ""},
-      {"http://www.gotaport.com:1234/", "www.gotaport.com", "/", ""},
-      {"  http://www.google.com/  ", "www.google.com", "/", ""},
-      {"http:// leadingspace.com/", "%20leadingspace.com", "/", ""},
-      {"http://%20leadingspace.com/", "%20leadingspace.com", "/", ""},
-      {"https://www.securesite.com/", "www.securesite.com", "/", ""},
-      {"http://host.com/ab%23cd", "host.com", "/ab%23cd", ""},
-      {"http://host%3e.com//twoslashes?more//slashes", "host>.com",
-       "/twoslashes", "more//slashes"},
-      {"http://host.com/abc?val=xyz#anything", "host.com", "/abc", "val=xyz"},
-      {"http://abc:def@host.com/xyz", "host.com", "/xyz", ""},
-      {"http://host%3e.com/abc/%2e%2e%2fdef", "host>.com", "/def", ""},
-      {"http://.......host...com.....//abc/////def%2F%2F%2Fxyz", "host.com",
-       "/abc/def/xyz", ""},
-      {"ftp://host.com/foo?bar", "host.com", "/foo", "bar"},
-      {"data:text/html;charset=utf-8,%0D%0A", "", "", ""},
-      {"javascript:alert()", "", "", ""},
-      {"mailto:abc@example.com", "", "", ""},
-  };
-  for (size_t i = 0; i < base::size(tests); ++i) {
-    SCOPED_TRACE(base::StringPrintf("Test: %s", tests[i].input_url));
-    GURL url(tests[i].input_url);
-
-    std::string canonicalized_hostname;
-    std::string canonicalized_path;
-    std::string canonicalized_query;
-    V4ProtocolManagerUtil::CanonicalizeUrl(url, &canonicalized_hostname,
-                                           &canonicalized_path,
-                                           &canonicalized_query);
-
-    EXPECT_EQ(tests[i].expected_canonicalized_hostname, canonicalized_hostname);
-    EXPECT_EQ(tests[i].expected_canonicalized_path, canonicalized_path);
-    EXPECT_EQ(tests[i].expected_canonicalized_query, canonicalized_query);
-  }
-}
-
-TEST_F(V4ProtocolManagerUtilTest, TestIPAddressToEncodedIPV6) {
-  // To verify the test values, here's the python code:
-  // >> import socket, hashlib, binascii
-  // >> hashlib.sha1(socket.inet_pton(socket.AF_INET6, input)).digest() +
-  // chr(128)
-  // For example:
-  // >>> hashlib.sha1(socket.inet_pton(socket.AF_INET6,
-  // '::ffff:192.168.1.1')).digest() + chr(128)
-  // 'X\xf8\xa1\x17I\xe6Pl\xfd\xdb\xbb\xa0\x0c\x02\x9d#\n|\xe7\xcd\x80'
-  std::vector<std::tuple<bool, std::string, std::string>> test_cases = {
-      std::make_tuple(false, "", ""),
-      std::make_tuple(
-          true, "192.168.1.1",
-          "X\xF8\xA1\x17I\xE6Pl\xFD\xDB\xBB\xA0\f\x2\x9D#\n|\xE7\xCD\x80"),
-      std::make_tuple(
-          true,
-          "::", "\xE1)\xF2|Q\x3\xBC\\\xC4K\xCD\xF0\xA1^\x16\rDPf\xFF\x80")};
-  for (size_t i = 0; i < test_cases.size(); i++) {
-    DVLOG(1) << "Running case: " << i;
-    bool success = std::get<0>(test_cases[i]);
-    const auto& input = std::get<1>(test_cases[i]);
-    const auto& expected_output = std::get<2>(test_cases[i]);
-    std::string encoded_ip;
-    ASSERT_EQ(success, V4ProtocolManagerUtil::IPAddressToEncodedIPV6Hash(
-                           input, &encoded_ip));
-    if (success) {
-      ASSERT_EQ(expected_output, encoded_ip);
-    }
-  }
-}
-
-TEST_F(V4ProtocolManagerUtilTest, TestFullHashToHashPrefix) {
-  const std::string full_hash = "abcdefgh";
-  std::vector<std::tuple<bool, std::string, PrefixSize, std::string>>
-      test_cases = {
-          std::make_tuple(true, "", 0, ""),
-          std::make_tuple(false, "", kMinHashPrefixLength, ""),
-          std::make_tuple(true, "a", 1, full_hash),
-          std::make_tuple(true, "abcd", kMinHashPrefixLength, full_hash),
-          std::make_tuple(true, "abcde", kMinHashPrefixLength + 1, full_hash)};
-  for (size_t i = 0; i < test_cases.size(); i++) {
-    DVLOG(1) << "Running case: " << i;
-    bool success = std::get<0>(test_cases[i]);
-    const auto& expected_prefix = std::get<1>(test_cases[i]);
-    const PrefixSize& prefix_size = std::get<2>(test_cases[i]);
-    const auto& input_full_hash = std::get<3>(test_cases[i]);
-    std::string prefix;
-    ASSERT_EQ(success, V4ProtocolManagerUtil::FullHashToHashPrefix(
-                           input_full_hash, prefix_size, &prefix));
-    if (success) {
-      ASSERT_EQ(expected_prefix, prefix);
-    }
-  }
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/db/v4_rice.cc b/components/safe_browsing/db/v4_rice.cc
deleted file mode 100644
index 547a4020..0000000
--- a/components/safe_browsing/db/v4_rice.cc
+++ /dev/null
@@ -1,296 +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.
-
-#include <algorithm>
-#include <vector>
-
-#include "base/logging.h"
-#include "base/numerics/safe_math.h"
-#include "base/strings/stringprintf.h"
-#include "build/build_config.h"
-#include "components/safe_browsing/db/v4_rice.h"
-
-#if defined(OS_WIN)
-#include <winsock2.h>
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
-#include <arpa/inet.h>
-#endif
-
-using ::google::protobuf::RepeatedField;
-using ::google::protobuf::int32;
-using ::google::protobuf::int64;
-
-#if !defined(ARCH_CPU_LITTLE_ENDIAN) || (ARCH_CPU_LITTLE_ENDIAN != 1)
-#error The code below assumes little-endianness.
-#endif
-
-namespace safe_browsing {
-
-namespace {
-
-const int kBitsPerByte = 8;
-const unsigned int kMaxBitIndex = kBitsPerByte * sizeof(uint32_t);
-
-}  // namespace
-
-// static
-V4DecodeResult V4RiceDecoder::ValidateInput(const int32 rice_parameter,
-                                            const int32 num_entries,
-                                            const std::string& encoded_data) {
-  if (num_entries < 0) {
-    NOTREACHED();
-    return NUM_ENTRIES_NEGATIVE_FAILURE;
-  }
-
-  if (num_entries == 0) {
-    return DECODE_SUCCESS;
-  }
-
-  if (rice_parameter <= 0) {
-    NOTREACHED();
-    return RICE_PARAMETER_NON_POSITIVE_FAILURE;
-  }
-
-  if (encoded_data.empty()) {
-    NOTREACHED();
-    return ENCODED_DATA_UNEXPECTED_EMPTY_FAILURE;
-  }
-
-  return DECODE_SUCCESS;
-}
-
-// static
-V4DecodeResult V4RiceDecoder::DecodeIntegers(const int64 first_value,
-                                             const int32 rice_parameter,
-                                             const int32 num_entries,
-                                             const std::string& encoded_data,
-                                             RepeatedField<int32>* out) {
-  DCHECK(out);
-
-  V4DecodeResult result =
-      ValidateInput(rice_parameter, num_entries, encoded_data);
-  if (result != DECODE_SUCCESS) {
-    return result;
-  }
-
-  out->Reserve(num_entries + 1);
-  base::CheckedNumeric<int32> last_value(first_value);
-  out->Add(last_value.ValueOrDie());
-  if (num_entries == 0) {
-    return DECODE_SUCCESS;
-  }
-
-  V4RiceDecoder decoder(rice_parameter, num_entries, encoded_data);
-  while (decoder.HasAnotherValue()) {
-    uint32_t offset;
-    result = decoder.GetNextValue(&offset);
-    if (result != DECODE_SUCCESS) {
-      return result;
-    }
-
-    last_value += offset;
-    if (!last_value.IsValid()) {
-      NOTREACHED();
-      return DECODED_INTEGER_OVERFLOW_FAILURE;
-    }
-
-    out->Add(last_value.ValueOrDie());
-  }
-
-  return DECODE_SUCCESS;
-}
-
-// static
-V4DecodeResult V4RiceDecoder::DecodePrefixes(const int64 first_value,
-                                             const int32 rice_parameter,
-                                             const int32 num_entries,
-                                             const std::string& encoded_data,
-                                             std::vector<uint32_t>* out) {
-  DCHECK(out);
-
-  V4DecodeResult result =
-      ValidateInput(rice_parameter, num_entries, encoded_data);
-  if (result != DECODE_SUCCESS) {
-    return result;
-  }
-  out->reserve((num_entries + 1));
-
-  base::CheckedNumeric<uint32_t> last_value(first_value);
-  out->push_back(htonl(last_value.ValueOrDie()));
-
-  if (num_entries > 0) {
-    V4RiceDecoder decoder(rice_parameter, num_entries, encoded_data);
-    while (decoder.HasAnotherValue()) {
-      uint32_t offset;
-      result = decoder.GetNextValue(&offset);
-      if (result != DECODE_SUCCESS) {
-        return result;
-      }
-
-      last_value += offset;
-      if (!last_value.IsValid()) {
-        NOTREACHED();
-        return DECODED_INTEGER_OVERFLOW_FAILURE;
-      }
-
-      // This flipping is done so that the decoded uint32 is interpreted
-      // correcly as a string of 4 bytes.
-      out->push_back(htonl(last_value.ValueOrDie()));
-    }
-  }
-
-  // Flipping the bytes, as done above, destroys the sort order. Sort the
-  // values back.
-  std::sort(out->begin(), out->end());
-
-  // This flipping is done so that when the vector is interpreted as a string,
-  // the bytes are in the correct order.
-  for (size_t i = 0; i < out->size(); i++) {
-    (*out)[i] = ntohl((*out)[i]);
-  }
-
-  return DECODE_SUCCESS;
-}
-
-V4RiceDecoder::V4RiceDecoder(const int rice_parameter,
-                             const int num_entries,
-                             const std::string& encoded_data)
-    : rice_parameter_(rice_parameter),
-      num_entries_(num_entries),
-      data_(encoded_data),
-      current_word_(0) {
-  DCHECK_LE(0, num_entries_);
-  DCHECK_LE(2u, rice_parameter_);
-  DCHECK_GE(28u, rice_parameter_);
-
-  data_byte_index_ = 0;
-  current_word_bit_index_ = kMaxBitIndex;
-}
-
-V4RiceDecoder::~V4RiceDecoder() {}
-
-bool V4RiceDecoder::HasAnotherValue() const {
-  return num_entries_ > 0;
-}
-
-V4DecodeResult V4RiceDecoder::GetNextValue(uint32_t* value) {
-  if (!HasAnotherValue()) {
-    return DECODE_NO_MORE_ENTRIES_FAILURE;
-  }
-
-  V4DecodeResult result;
-  uint32_t q = 0;
-  uint32_t bit;
-  do {
-    result = GetNextBits(1, &bit);
-    if (result != DECODE_SUCCESS) {
-      return result;
-    }
-    q += bit;
-  } while (bit);
-  uint32_t r = 0;
-  result = GetNextBits(rice_parameter_, &r);
-  if (result != DECODE_SUCCESS) {
-    return result;
-  }
-
-  *value = (q << rice_parameter_) + r;
-  num_entries_--;
-  return DECODE_SUCCESS;
-}
-
-V4DecodeResult V4RiceDecoder::GetNextWord(uint32_t* word) {
-  if (data_byte_index_ >= data_.size()) {
-    return DECODE_RAN_OUT_OF_BITS_FAILURE;
-  }
-
-  const size_t mask = 0xFF;
-  *word = (data_[data_byte_index_] & mask);
-  data_byte_index_++;
-  current_word_bit_index_ = 0;
-
-  if (data_byte_index_ < data_.size()) {
-    *word |= ((data_[data_byte_index_] & mask) << 8);
-    data_byte_index_++;
-
-    if (data_byte_index_ < data_.size()) {
-      *word |= ((data_[data_byte_index_] & mask) << 16);
-      data_byte_index_++;
-
-      if (data_byte_index_ < data_.size()) {
-        *word |= ((data_[data_byte_index_] & mask) << 24);
-        data_byte_index_++;
-      }
-    }
-  }
-
-  return DECODE_SUCCESS;
-}
-
-V4DecodeResult V4RiceDecoder::GetNextBits(unsigned int num_requested_bits,
-                                          uint32_t* x) {
-  if (num_requested_bits > kMaxBitIndex) {
-    NOTREACHED();
-    return DECODE_REQUESTED_TOO_MANY_BITS_FAILURE;
-  }
-
-  if (current_word_bit_index_ == kMaxBitIndex) {
-    V4DecodeResult result = GetNextWord(&current_word_);
-    if (result != DECODE_SUCCESS) {
-      return result;
-    }
-  }
-
-  unsigned int num_bits_left_in_current_word =
-      kMaxBitIndex - current_word_bit_index_;
-  if (num_bits_left_in_current_word >= num_requested_bits) {
-    // All the bits that we need are in |current_word_|.
-    *x = GetBitsFromCurrentWord(num_requested_bits);
-  } else {
-    // |current_word_| contains fewer bits than we need so read the remaining
-    // bits from |current_word_| into |lower|, and then call GetNextBits on the
-    // remaining number of bits, which will read in a new word into
-    // |current_word_|.
-    uint32_t lower = GetBitsFromCurrentWord(num_bits_left_in_current_word);
-
-    unsigned int num_bits_from_next_word =
-        num_requested_bits - num_bits_left_in_current_word;
-    uint32_t upper;
-    V4DecodeResult result = GetNextBits(num_bits_from_next_word, &upper);
-    if (result != DECODE_SUCCESS) {
-      return result;
-    }
-    *x = (upper << num_bits_left_in_current_word) | lower;
-  }
-  return DECODE_SUCCESS;
-}
-
-uint32_t V4RiceDecoder::GetBitsFromCurrentWord(
-    unsigned int num_requested_bits) {
-  uint32_t mask = 0xFFFFFFFF >> (kMaxBitIndex - num_requested_bits);
-  uint32_t x = current_word_ & mask;
-  current_word_ = current_word_ >> num_requested_bits;
-  current_word_bit_index_ += num_requested_bits;
-  return x;
-}
-
-std::string V4RiceDecoder::DebugString() const {
-  // Calculates the total number of bits that we have read from the buffer,
-  // excluding those that have been read into current_word_ but not yet
-  // consumed byt GetNextBits().
-  unsigned bits_read = (data_byte_index_ - sizeof(uint32_t)) * kBitsPerByte +
-                       current_word_bit_index_;
-  return base::StringPrintf(
-      "bits_read: %x; current_word_: %x; data_byte_index_; %x, "
-      "current_word_bit_index_: %x; rice_parameter_: %x",
-      bits_read, current_word_, data_byte_index_, current_word_bit_index_,
-      rice_parameter_);
-}
-
-std::ostream& operator<<(std::ostream& os, const V4RiceDecoder& rice_decoder) {
-  os << rice_decoder.DebugString();
-  return os;
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/db/v4_rice.h b/components/safe_browsing/db/v4_rice.h
deleted file mode 100644
index 8d3f816..0000000
--- a/components/safe_browsing/db/v4_rice.h
+++ /dev/null
@@ -1,164 +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.
-
-// Rice-Golomb decoder for blacklist updates.
-// Details at: https://en.wikipedia.org/wiki/Golomb_coding
-
-#ifndef COMPONENTS_SAFE_BROWSING_DB_V4_RICE_H_
-#define COMPONENTS_SAFE_BROWSING_DB_V4_RICE_H_
-
-#include <ostream>
-#include <string>
-#include <vector>
-#include "base/gtest_prod_util.h"
-#include "third_party/protobuf/src/google/protobuf/repeated_field.h"
-
-namespace safe_browsing {
-
-// Enumerate different failure events while decoding the Rice-encoded string
-// sent by the server for histogramming purposes. DO NOT CHANGE THE ORDERING OF
-// THESE VALUES.
-enum V4DecodeResult {
-  // No error.
-  DECODE_SUCCESS = 0,
-
-  // Exceeded the number of entries to expect.
-  DECODE_NO_MORE_ENTRIES_FAILURE = 1,
-
-  // Requested to decode >32 bits.
-  DECODE_REQUESTED_TOO_MANY_BITS_FAILURE = 2,
-
-  // All bits had already been read and interpreted in the encoded string.
-  DECODE_RAN_OUT_OF_BITS_FAILURE = 3,
-
-  // The num_entries argument to DecodePrefixes or DecodeIntegers was negative.
-  NUM_ENTRIES_NEGATIVE_FAILURE = 4,
-
-  // Rice-encoding parameter was non-positive when the number of encoded entries
-  // was > 0.
-  RICE_PARAMETER_NON_POSITIVE_FAILURE = 5,
-
-  // |encoded_data| was empty when the number of encoded entries was > 0.
-  ENCODED_DATA_UNEXPECTED_EMPTY_FAILURE = 6,
-
-  // decoded value had an integer overflow, which is unexpected.
-  DECODED_INTEGER_OVERFLOW_FAILURE = 7,
-
-  // Memory space for histograms is determined by the max.  ALWAYS
-  // ADD NEW VALUES BEFORE THIS ONE.
-  DECODE_RESULT_MAX
-};
-
-class V4RiceDecoder {
- public:
-  // Decodes the Rice-encoded string in |encoded_data| as a list of integers
-  // and stores them in |out|. |rice_parameter| is the exponent of 2 for
-  // calculating 'M', |first_value| is the first value in the output sequence,
-  // |num_entries| is the number of subsequent encoded entries. Each decoded
-  // value is a positive offset from the previous value.
-  // So, for instance, if the unencoded sequence is: [3, 7, 25], then
-  // |first_value| = 3, |num_entries| = 2 and decoding the |encoded_data| will
-  // produce the offsets: [4, 18].
-  static V4DecodeResult DecodeIntegers(
-      const ::google::protobuf::int64 first_value,
-      const ::google::protobuf::int32 rice_parameter,
-      const ::google::protobuf::int32 num_entries,
-      const std::string& encoded_data,
-      ::google::protobuf::RepeatedField<::google::protobuf::int32>* out);
-
-  // Decodes the Rice-encoded string in |encoded_data| as a string of 4-byte
-  // hash prefixes and stores them in |out|. The rest of the arguments are the
-  // same as for |DecodeIntegers|.
-  // Important: |out| is only meant to be used as a concatenated list of sorted
-  // 4-byte hash prefixes, not as a vector of uint32_t values.
-  // This method does the following:
-  // 1. Rice-decode the |encoded_data| as a list of uint32_t values.
-  // 2. Flip the endianness (on little-endian machines) of each of these
-  //    values. This is done because when a hash prefix is represented as a
-  //    uint32_t, the bytes get reordered. This generates the hash prefix that
-  //    the server would have sent in the absence of Rice-encoding.
-  // 3. Sort the resulting list of uint32_t values.
-  // 4. Flip the endianness once again since the uint32_t are expected to be
-  //    consumed as a concatenated list of 4-byte hash prefixes, when merging
-  //    the
-  //    update with the existing state.
-  static V4DecodeResult DecodePrefixes(
-      const ::google::protobuf::int64 first_value,
-      const ::google::protobuf::int32 rice_parameter,
-      const ::google::protobuf::int32 num_entries,
-      const std::string& encoded_data,
-      std::vector<uint32_t>* out);
-
-  virtual ~V4RiceDecoder();
-
-  std::string DebugString() const;
-
- private:
-  FRIEND_TEST_ALL_PREFIXES(V4RiceTest, TestDecoderGetNextWordWithNoData);
-  FRIEND_TEST_ALL_PREFIXES(V4RiceTest, TestDecoderGetNextBitsWithNoData);
-  FRIEND_TEST_ALL_PREFIXES(V4RiceTest, TestDecoderGetNextValueWithNoData);
-  FRIEND_TEST_ALL_PREFIXES(V4RiceTest, TestDecoderGetNextValueWithNoEntries);
-  friend class V4RiceTest;
-
-  // Validate some of the parameters passed to the decode methods.
-  static V4DecodeResult ValidateInput(
-      const ::google::protobuf::int32 rice_parameter,
-      const ::google::protobuf::int32 num_entries,
-      const std::string& encoded_data);
-
-  // The |rice_parameter| is the exponent of 2 for calculating 'M',
-  // |num_entries| is the number of encoded entries in the |encoded_data| and
-  // |encoded_data| is the Rice-encoded string to decode.
-  V4RiceDecoder(const ::google::protobuf::int32 rice_parameter,
-                const ::google::protobuf::int32 num_entries,
-                const std::string& encoded_data);
-
-  // Returns true until |num_entries| entries have been decoded.
-  bool HasAnotherValue() const;
-
-  // Populates |value| with the next 32-bit unsigned integer decoded from
-  // |encoded_data|.
-  V4DecodeResult GetNextValue(uint32_t* value);
-
-  // Reads in up to 32 bits from |encoded_data| into |word|, from which
-  // subsequent GetNextBits() calls read bits.
-  V4DecodeResult GetNextWord(uint32_t* word);
-
-  // Reads |num_requested_bits| into |x| from |current_word_| and advances it
-  // if needed by calling GetNextWord().
-  V4DecodeResult GetNextBits(unsigned int num_requested_bits, uint32_t* x);
-
-  // Reads |num_requested_bits| from |current_word_|.
-  uint32_t GetBitsFromCurrentWord(unsigned int num_requested_bits);
-
-  // The Rice parameter, which is the exponent of two for calculating 'M'. 'M'
-  // is used as the base to calculate the quotient and remainder in the
-  // algorithm.
-  const unsigned int rice_parameter_;
-
-  // The number of entries encoded in the data stream.
-  ::google::protobuf::int32 num_entries_;
-
-  // The Rice-encoded string.
-  const std::string data_;
-
-  // Represents how many total bytes have we read from |data_| into
-  // |current_word_|.
-  unsigned int data_byte_index_;
-
-  // Represents the number of bits that we have read from |current_word_|. When
-  // this becomes 32, which is the size of |current_word_|, a new
-  // |current_word_| needs to be read from |data_|.
-  unsigned int current_word_bit_index_;
-
-  // The 32-bit value read from |data_|. All bit reading operations operate on
-  // |current_word_|.
-  uint32_t current_word_;
-};
-
-std::ostream& operator<<(std::ostream& os, const V4RiceDecoder& rice_decoder);
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_DB_V4_RICE_H_
diff --git a/components/safe_browsing/db/v4_rice_unittest.cc b/components/safe_browsing/db/v4_rice_unittest.cc
deleted file mode 100644
index cfc369c..0000000
--- a/components/safe_browsing/db/v4_rice_unittest.cc
+++ /dev/null
@@ -1,277 +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.
-
-#include "components/safe_browsing/db/v4_rice.h"
-#include "base/logging.h"
-#include "testing/platform_test.h"
-
-using ::google::protobuf::RepeatedField;
-using ::google::protobuf::int32;
-
-namespace safe_browsing {
-
-class V4RiceTest : public PlatformTest {
- public:
-  V4RiceTest() {}
-
-  struct RiceDecodingTestInfo {
-    uint32_t rice_parameter;
-    std::vector<uint32_t> expected_values;
-    std::string encoded_string;
-
-    RiceDecodingTestInfo(const uint32_t in_rice_parameter,
-                         const std::vector<uint32_t>& in_expected_values,
-                         const std::string& in_encoded_string) {
-      rice_parameter = in_rice_parameter;
-      expected_values = in_expected_values;
-      encoded_string = in_encoded_string;
-    }
-  };
-
-  void VerifyRiceDecoding(const RiceDecodingTestInfo& test_info) {
-    const uint32_t num_entries = test_info.expected_values.size();
-    V4RiceDecoder decoder(test_info.rice_parameter, num_entries,
-                          test_info.encoded_string);
-    uint32_t word;
-    for (const auto& expected : test_info.expected_values) {
-      EXPECT_EQ(DECODE_SUCCESS, decoder.GetNextValue(&word));
-      EXPECT_EQ(expected, word);
-    }
-    ASSERT_FALSE(decoder.HasAnotherValue());
-  }
-};
-
-TEST_F(V4RiceTest, TestDecoderGetNextWordWithNoData) {
-  uint32_t word;
-  V4RiceDecoder decoder(5, 1, "");
-  EXPECT_EQ(DECODE_RAN_OUT_OF_BITS_FAILURE, decoder.GetNextWord(&word));
-}
-
-TEST_F(V4RiceTest, TestDecoderGetNextBitsWithNoData) {
-  uint32_t word;
-  V4RiceDecoder decoder(5, 1, "");
-  EXPECT_EQ(DECODE_RAN_OUT_OF_BITS_FAILURE, decoder.GetNextBits(1, &word));
-}
-
-TEST_F(V4RiceTest, TestDecoderGetNextValueWithNoData) {
-  uint32_t word;
-  V4RiceDecoder decoder(5, 1, "");
-  EXPECT_EQ(DECODE_RAN_OUT_OF_BITS_FAILURE, decoder.GetNextValue(&word));
-}
-
-TEST_F(V4RiceTest, TestDecoderGetNextValueWithNoEntries) {
-  uint32_t word;
-  V4RiceDecoder decoder(28, 0, "\xbf\xa8");
-  ASSERT_FALSE(decoder.HasAnotherValue());
-  EXPECT_EQ(DECODE_NO_MORE_ENTRIES_FAILURE, decoder.GetNextValue(&word));
-}
-
-TEST_F(V4RiceTest, TestDecoderGetNextValueWithInterestingValues) {
-  // These values are interesting because they match the unit test
-  // values used within Google to this test this code in other
-  // components, such as the SafeBrowsing service itself.
-
-  std::vector<RiceDecodingTestInfo> test_inputs = {
-      RiceDecodingTestInfo(2, {15, 9}, "\xf7\x2"),
-      RiceDecodingTestInfo(
-          28, {1777762129, 2093280223, 924369848},
-          "\xbf\xa8\x3f\xfb\xfc\xfb\x5e\x27\xe6\xc3\x1d\xc6\x38"),
-      RiceDecodingTestInfo(
-          28, {62763050, 1046523781, 192522171, 1800511020, 4442775, 582142548},
-          "\x54\x60\x7b\xe7\x0a\x5f\xc1\xdc\xee\x69\xde"
-          "\xfe\x58\x3c\xa3\xd6\xa5\xf2\x10\x8c\x4a\x59"
-          "\x56\x00"),
-      RiceDecodingTestInfo(
-          28,
-          {26067715, 344823336, 8420095, 399843890, 95029378, 731622412,
-           35811335, 1047558127, 1117722715, 78698892},
-          "\x06\x86\x1b\x23\x14\xcb\x46\xf2\xaf\x07\x08\xc9\x88\x54\x1f\x41\x04"
-          "\xd5\x1a\x03\xeb\xe6\x3a\x80\x13\x91\x7b\xbf\x83\xf3\xb7\x85\xf1\x29"
-          "\x18\xb3\x61\x09"),
-      RiceDecodingTestInfo(
-          27,
-          {225846818, 328287420, 166748623, 29117720, 552397365, 350353215,
-           558267528, 4738273, 567093445, 28563065, 55077698, 73091685,
-           339246010, 98242620, 38060941, 63917830, 206319759, 137700744},
-          "\x89\x98\xd8\x75\xbc\x44\x91\xeb\x39\x0c\x3e\x30\x9a\x78\xf3\x6a\xd4"
-          "\xd9\xb1\x9f\xfb\x70\x3e\x44\x3e\xa3\x08\x67\x42\xc2\x2b\x46\x69\x8e"
-          "\x3c\xeb\xd9\x10\x5a\x43\x9a\x32\xa5\x2d\x4e\x77\x0f\x87\x78\x20\xb6"
-          "\xab\x71\x98\x48\x0c\x9e\x9e\xd7\x23\x0c\x13\x43\x2c\xa9\x01"),
-      RiceDecodingTestInfo(
-          28,
-          {339784008, 263128563, 63871877, 69723256, 826001074, 797300228,
-           671166008, 207712688},
-          std::string("\x21\xc5\x02\x91\xf9\x82\xd7\x57\xb8\xe9\x3c\xf0\xc8\x4f"
-                      "\xe8\x64\x8d\x77\x62\x04\xd6\x85\x3f\x1c\x97\x00\x04\x1b"
-                      "\x17\xc6",
-                      30)),
-      RiceDecodingTestInfo(
-          28,
-          {471820069, 196333855, 855579133, 122737976, 203433838, 85354544,
-           1307949392, 165938578, 195134475, 553930435, 49231136},
-          "\x95\x9c\x7d\xb0\x8f\xe8\xd9\xbd\xfe\x8c\x7f\x81\x53\x0d\x75\xdc\x4e"
-          "\x40\x18\x0c\x9a\x45\x3d\xa8\xdc\xfa\x26\x59\x40\x9e\x16\x08\x43\x77"
-          "\xc3\x4e\x04\x01\xa4\xe6\x5d\x00"),
-      RiceDecodingTestInfo(
-          27,
-          {87336845, 129291033, 30906211, 433549264, 30899891, 53207875,
-           11959529, 354827862, 82919275, 489637251, 53561020, 336722992,
-           408117728, 204506246, 188216092, 9047110, 479817359, 230317256},
-          "\x1a\x4f\x69\x2a\x63\x9a\xf6\xc6\x2e\xaf\x73\xd0\x6f\xd7\x31\xeb\x77"
-          "\x1d\x43\xe3\x2b\x93\xce\x67\x8b\x59\xf9\x98\xd4\xda\x4f\x3c\x6f\xb0"
-          "\xe8\xa5\x78\x8d\x62\x36\x18\xfe\x08\x1e\x78\xd8\x14\x32\x24\x84\x61"
-          "\x1c\xf3\x37\x63\xc4\xa0\x88\x7b\x74\xcb\x64\xc8\x5c\xba\x05"),
-      RiceDecodingTestInfo(
-          28,
-          {297968956, 19709657, 259702329, 76998112, 1023176123, 29296013,
-           1602741145, 393745181, 177326295, 55225536, 75194472},
-          "\xf1\x94\x0a\x87\x6c\x5f\x96\x90\xe3\xab\xf7\xc0\xcb\x2d\xe9\x76\xdb"
-          "\xf8\x59\x63\xc1\x6f\x7c\x99\xe3\x87\x5f\xc7\x04\xde\xb9\x46\x8e\x54"
-          "\xc0\xac\x4a\x03\x0d\x6c\x8f\x00"),
-      RiceDecodingTestInfo(
-          28,
-          {532220688, 780594691, 436816483, 163436269, 573044456, 1069604,
-           39629436, 211410997, 227714491, 381562898, 75610008, 196754597,
-           40310339, 15204118, 99010842},
-          "\x41\x2c\xe4\xfe\x06\xdc\x0d\xbd\x31\xa5\x04\xd5\x6e\xdd\x9b\x43\xb7"
-          "\x3f\x11\x24\x52\x10\x80\x4f\x96\x4b\xd4\x80\x67\xb2\xdd\x52\xc9\x4e"
-          "\x02\xc6\xd7\x60\xde\x06\x92\x52\x1e\xdd\x35\x64\x71\x26\x2c\xfe\xcf"
-          "\x81\x46\xb2\x79\x01"),
-      RiceDecodingTestInfo(
-          28,
-          {219354713, 389598618, 750263679, 554684211, 87381124, 4523497,
-           287633354, 801308671, 424169435, 372520475, 277287849},
-          "\xb2\x2c\x26\x3a\xcd\x66\x9c\xdb\x5f\x07\x2e\x6f\xe6\xf9\x21\x10\x52"
-          "\xd5\x94\xf4\x82\x22\x48\xf9\x9d\x24\xf6\xff\x2f\xfc\x6d\x3f\x21\x65"
-          "\x1b\x36\x34\x56\xea\xc4\x21\x00"),
-  };
-
-  for (size_t i = 0; i < test_inputs.size(); i++) {
-    DVLOG(1) << "Running test case: " << i;
-    VerifyRiceDecoding(test_inputs[i]);
-  }
-}
-
-#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
-// This test hits a NOTREACHED so it is a release mode only test.
-TEST_F(V4RiceTest, TestDecoderIntegersWithNoData) {
-  RepeatedField<int32> out;
-  EXPECT_EQ(ENCODED_DATA_UNEXPECTED_EMPTY_FAILURE,
-            V4RiceDecoder::DecodeIntegers(3, 5, 1, "", &out));
-}
-#endif
-
-#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
-// This test hits a NOTREACHED so it is a release mode only test.
-TEST_F(V4RiceTest, TestDecoderIntegersWithNegativeNumEntries) {
-  RepeatedField<int32> out;
-  EXPECT_EQ(NUM_ENTRIES_NEGATIVE_FAILURE,
-            V4RiceDecoder::DecodeIntegers(3, 5, -1, "", &out));
-}
-#endif
-
-#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
-// This test hits a NOTREACHED so it is a release mode only test.
-TEST_F(V4RiceTest, TestDecoderIntegersWithNonPositiveRiceParameter) {
-  RepeatedField<int32> out;
-  EXPECT_EQ(RICE_PARAMETER_NON_POSITIVE_FAILURE,
-            V4RiceDecoder::DecodeIntegers(3, 0, 1, "a", &out));
-
-  EXPECT_EQ(RICE_PARAMETER_NON_POSITIVE_FAILURE,
-            V4RiceDecoder::DecodeIntegers(3, -1, 1, "a", &out));
-}
-#endif
-
-#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
-// This test hits a NOTREACHED so it is a release mode only test.
-TEST_F(V4RiceTest, TestDecoderIntegersWithOverflowValues) {
-  RepeatedField<int32> out;
-  EXPECT_EQ(DECODED_INTEGER_OVERFLOW_FAILURE,
-            V4RiceDecoder::DecodeIntegers(
-                5, 28, 3,
-                "\xbf\xa8\x3f\xfb\xfc\xfb\x5e\x27\xe6\xc3\x1d\xc6\x38", &out));
-}
-#endif
-
-TEST_F(V4RiceTest, TestDecoderIntegersWithOneValue) {
-  RepeatedField<int32> out;
-  EXPECT_EQ(DECODE_SUCCESS, V4RiceDecoder::DecodeIntegers(3, 2, 0, "", &out));
-  EXPECT_EQ(1, out.size());
-  EXPECT_EQ(3, out.Get(0));
-}
-
-TEST_F(V4RiceTest, TestDecoderIntegersWithMultipleValues) {
-  RepeatedField<int32> out;
-  EXPECT_EQ(DECODE_SUCCESS,
-            V4RiceDecoder::DecodeIntegers(5, 2, 2, "\xf7\x2", &out));
-  EXPECT_EQ(3, out.size());
-  EXPECT_EQ(5, out.Get(0));
-  EXPECT_EQ(20, out.Get(1));
-  EXPECT_EQ(29, out.Get(2));
-}
-
-#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
-// This test hits a NOTREACHED so it is a release mode only test.
-TEST_F(V4RiceTest, TestDecoderPrefixesWithNoData) {
-  std::vector<uint32_t> out;
-  EXPECT_EQ(ENCODED_DATA_UNEXPECTED_EMPTY_FAILURE,
-            V4RiceDecoder::DecodePrefixes(3, 5, 1, "", &out));
-}
-#endif
-
-#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
-// This test hits a NOTREACHED so it is a release mode only test.
-TEST_F(V4RiceTest, TestDecoderPrefixesWithNegativeNumEntries) {
-  std::vector<uint32_t> out;
-  EXPECT_EQ(NUM_ENTRIES_NEGATIVE_FAILURE,
-            V4RiceDecoder::DecodePrefixes(3, 5, -1, "", &out));
-}
-#endif
-
-#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
-// This test hits a NOTREACHED so it is a release mode only test.
-TEST_F(V4RiceTest, TestDecoderPrefixesWithNonPositiveRiceParameter) {
-  std::vector<uint32_t> out;
-  EXPECT_EQ(RICE_PARAMETER_NON_POSITIVE_FAILURE,
-            V4RiceDecoder::DecodePrefixes(3, 0, 1, "a", &out));
-
-  EXPECT_EQ(RICE_PARAMETER_NON_POSITIVE_FAILURE,
-            V4RiceDecoder::DecodePrefixes(3, -1, 1, "a", &out));
-}
-#endif
-
-TEST_F(V4RiceTest, TestDecoderPrefixesWithOneValue) {
-  std::vector<uint32_t> out;
-  EXPECT_TRUE(out.empty());
-  EXPECT_EQ(DECODE_SUCCESS,
-            V4RiceDecoder::DecodePrefixes(0x69F67F51u, 2, 0, "", &out));
-  EXPECT_EQ(1u, out.size());
-  EXPECT_EQ(0x69F67F51u, out[0]);
-}
-
-TEST_F(V4RiceTest, TestDecoderPrefixesWithMultipleValues) {
-  std::vector<uint32_t> out;
-  EXPECT_EQ(DECODE_SUCCESS,
-            V4RiceDecoder::DecodePrefixes(
-                5, 28, 3, "\xbf\xa8\x3f\xfb\xf\xf\x5e\x27\xe6\xc3\x1d\xc6\x38",
-                &out));
-  std::vector<uint32_t> expected = {5, 0xad934c0cu, 0x6ff67f56u, 0x81316fceu};
-  EXPECT_EQ(expected.size(), out.size());
-  for (unsigned i = 0; i < expected.size(); i++) {
-    EXPECT_EQ(expected[i], out[i]);
-  }
-}
-
-#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
-// This test hits a NOTREACHED so it is a release mode only test.
-TEST_F(V4RiceTest, TestDecoderPrefixesWithOverflowValues) {
-  std::vector<uint32_t> out;
-  EXPECT_EQ(DECODED_INTEGER_OVERFLOW_FAILURE,
-            V4RiceDecoder::DecodePrefixes(
-                5, 28, 3,
-                "\xbf\xa8\x3f\xfb\xfc\xfb\x5e\x27\xe6\xc3\x1d\xc6\x38", &out));
-}
-#endif
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/db/v4_store.cc b/components/safe_browsing/db/v4_store.cc
deleted file mode 100644
index 87180d8..0000000
--- a/components/safe_browsing/db/v4_store.cc
+++ /dev/null
@@ -1,833 +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.
-
-#include "components/safe_browsing/db/v4_store.h"
-
-#include <algorithm>
-#include <utility>
-
-#include "base/base64.h"
-#include "base/bind.h"
-#include "base/files/file_util.h"
-#include "base/metrics/histogram_functions.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/stl_util.h"
-#include "base/strings/stringprintf.h"
-#include "components/safe_browsing/db/prefix_iterator.h"
-#include "components/safe_browsing/db/v4_rice.h"
-#include "components/safe_browsing/db/v4_store.pb.h"
-#include "components/safe_browsing/proto/webui.pb.h"
-#include "crypto/secure_hash.h"
-#include "crypto/sha2.h"
-
-using base::TimeTicks;
-
-namespace safe_browsing {
-
-namespace {
-
-// UMA related strings.
-// Part 1: Represent the overall operation being performed.
-const char kProcessFullUpdate[] = "SafeBrowsing.V4ProcessFullUpdate";
-const char kProcessPartialUpdate[] = "SafeBrowsing.V4ProcessPartialUpdate";
-const char kReadFromDisk[] = "SafeBrowsing.V4ReadFromDisk";
-// Part 2: Represent the sub-operation being performed as part of the larger
-// operation from part 1.
-const char kApplyUpdate[] = ".ApplyUpdate";
-const char kDecodeAdditions[] = ".DecodeAdditions";
-const char kDecodeRemovals[] = ".DecodeRemovals";
-// Part 3: Represent the unit of value being measured and logged.
-const char kResult[] = ".Result";
-// Part 4 (optional): Represent the name of the list for which the metric is
-// being logged. For instance, ".UrlSoceng".
-// UMA metric names for this file are generated by appending one value each,
-// in order, from parts [1, 2, and 3], or [1, 2, 3, and 4]. For example:
-// SafeBrowsing.V4ProcessPartialUpdate.ApplyUpdate.Result, or
-// SafeBrowsing.V4ProcessPartialUpdate.ApplyUpdate.Result.UrlSoceng
-
-const uint32_t kFileMagic = 0x600D71FE;
-const uint32_t kFileVersion = 9;
-
-// Set a common sense limit on the store file size we try to read.
-// The maximum store file size, as of today, is about 6MB.
-constexpr size_t kMaxStoreSizeBytes = 50 * 1000 * 1000;
-
-void RecordEnumWithAndWithoutSuffix(const std::string& metric,
-                                    int32_t value,
-                                    int32_t maximum,
-                                    const base::FilePath& file_path) {
-  // The histograms below are an expansion of the UMA_HISTOGRAM_ENUMERATION
-  // macro adapted to allow for a dynamically suffixed histogram name.
-  // Note: The factory creates and owns the histogram.
-  base::HistogramBase* histogram = base::LinearHistogram::FactoryGet(
-      metric + kResult, 1, maximum, maximum + 1,
-      base::HistogramBase::kUmaTargetedHistogramFlag);
-  if (histogram) {
-    histogram->Add(value);
-  }
-
-  std::string suffix = GetUmaSuffixForStore(file_path);
-  base::HistogramBase* histogram_suffix = base::LinearHistogram::FactoryGet(
-      metric + kResult + suffix, 1, maximum, maximum + 1,
-      base::HistogramBase::kUmaTargetedHistogramFlag);
-  if (histogram_suffix) {
-    histogram_suffix->Add(value);
-  }
-}
-
-void RecordBooleanWithAndWithoutSuffix(const std::string& metric,
-                                       bool value,
-                                       const base::FilePath& file_path) {
-  // The histograms below are an expansion of the UMA_HISTOGRAM_BOOLEAN
-  // macro adapted to allow for a dynamically suffixed histogram name.
-  // Note: The factory creates and owns the histogram.
-  base::HistogramBase* histogram = base::BooleanHistogram::FactoryGet(
-      metric, base::HistogramBase::kUmaTargetedHistogramFlag);
-  if (histogram) {
-    histogram->Add(value);
-  }
-
-  std::string suffix = GetUmaSuffixForStore(file_path);
-  base::HistogramBase* histogram_suffix = base::BooleanHistogram::FactoryGet(
-      metric + suffix, base::HistogramBase::kUmaTargetedHistogramFlag);
-  if (histogram_suffix) {
-    histogram_suffix->Add(value);
-  }
-}
-
-void RecordApplyUpdateResult(const std::string& base_metric,
-                             ApplyUpdateResult result,
-                             const base::FilePath& file_path) {
-  RecordEnumWithAndWithoutSuffix(base_metric + kApplyUpdate, result,
-                                 APPLY_UPDATE_RESULT_MAX, file_path);
-}
-
-void RecordDecodeAdditionsResult(const std::string& base_metric,
-                                 V4DecodeResult result,
-                                 const base::FilePath& file_path) {
-  RecordEnumWithAndWithoutSuffix(base_metric + kDecodeAdditions, result,
-                                 DECODE_RESULT_MAX, file_path);
-}
-
-void RecordDecodeRemovalsResult(const std::string& base_metric,
-                                V4DecodeResult result,
-                                const base::FilePath& file_path) {
-  RecordEnumWithAndWithoutSuffix(base_metric + kDecodeRemovals, result,
-                                 DECODE_RESULT_MAX, file_path);
-}
-
-void RecordStoreReadResult(StoreReadResult result) {
-  UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.V4StoreRead.Result", result,
-                            STORE_READ_RESULT_MAX);
-}
-
-void RecordStoreWriteResult(StoreWriteResult result) {
-  UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.V4StoreWrite.Result", result,
-                            STORE_WRITE_RESULT_MAX);
-}
-
-// Returns the name of the temporary file used to buffer data for
-// |filename|.
-const base::FilePath TemporaryFileForFilename(const base::FilePath& filename) {
-  return base::FilePath(filename.value() + FILE_PATH_LITERAL("_new"));
-}
-
-}  // namespace
-
-using ::google::protobuf::RepeatedField;
-using ::google::protobuf::RepeatedPtrField;
-using ::google::protobuf::int32;
-
-std::ostream& operator<<(std::ostream& os, const V4Store& store) {
-  os << store.DebugString();
-  return os;
-}
-
-std::unique_ptr<V4Store> V4StoreFactory::CreateV4Store(
-    const scoped_refptr<base::SequencedTaskRunner>& task_runner,
-    const base::FilePath& store_path) {
-  auto new_store = std::make_unique<V4Store>(task_runner, store_path);
-  new_store->Initialize();
-  return new_store;
-}
-
-void V4Store::Initialize() {
-  // If a state already exists, don't re-initilize.
-  DCHECK(state_.empty());
-
-  StoreReadResult store_read_result = ReadFromDisk();
-  has_valid_data_ = (store_read_result == READ_SUCCESS);
-  RecordStoreReadResult(store_read_result);
-}
-
-bool V4Store::HasValidData() const {
-  RecordBooleanWithAndWithoutSuffix("SafeBrowsing.V4Store.IsStoreValid",
-                                    has_valid_data_, store_path_);
-  return has_valid_data_;
-}
-
-V4Store::V4Store(const scoped_refptr<base::SequencedTaskRunner>& task_runner,
-                 const base::FilePath& store_path,
-                 const int64_t old_file_size)
-    : file_size_(old_file_size),
-      has_valid_data_(false),
-      store_path_(store_path),
-      task_runner_(task_runner) {}
-
-V4Store::~V4Store() {
-  DCHECK(task_runner_->RunsTasksInCurrentSequence());
-}
-
-std::string V4Store::DebugString() const {
-  std::string state_base64;
-  base::Base64Encode(state_, &state_base64);
-
-  return base::StringPrintf("path: %" PRFilePath "; state: %s",
-                            store_path_.value().c_str(), state_base64.c_str());
-}
-
-// static
-void V4Store::Destroy(std::unique_ptr<V4Store> v4_store) {
-  V4Store* v4_store_raw = v4_store.release();
-  if (v4_store_raw) {
-    v4_store_raw->task_runner_->DeleteSoon(FROM_HERE, v4_store_raw);
-  }
-}
-
-void V4Store::Reset() {
-  expected_checksum_.clear();
-  hash_prefix_map_.clear();
-  state_ = "";
-}
-
-ApplyUpdateResult V4Store::ProcessPartialUpdateAndWriteToDisk(
-    const std::string& metric,
-    const HashPrefixMap& hash_prefix_map_old,
-    std::unique_ptr<ListUpdateResponse> response) {
-  DCHECK(response->has_response_type());
-  DCHECK_EQ(ListUpdateResponse::PARTIAL_UPDATE, response->response_type());
-
-  ApplyUpdateResult result = ProcessUpdate(
-      metric, hash_prefix_map_old, response, false /* delay_checksum check */);
-  if (result == APPLY_UPDATE_SUCCESS) {
-    Checksum checksum = response->checksum();
-    response.reset();
-    RecordStoreWriteResult(WriteToDisk(checksum));
-  }
-  return result;
-}
-
-ApplyUpdateResult V4Store::ProcessFullUpdateAndWriteToDisk(
-    const std::string& metric,
-    std::unique_ptr<ListUpdateResponse> response) {
-  ApplyUpdateResult result =
-      ProcessFullUpdate(metric, response, false /* delay_checksum check */);
-  if (result == APPLY_UPDATE_SUCCESS) {
-    Checksum checksum = response->checksum();
-    response.reset();
-    RecordStoreWriteResult(WriteToDisk(checksum));
-  }
-  return result;
-}
-
-ApplyUpdateResult V4Store::ProcessFullUpdate(
-    const std::string& metric,
-    const std::unique_ptr<ListUpdateResponse>& response,
-    bool delay_checksum_check) {
-  DCHECK(response->has_response_type());
-  DCHECK_EQ(ListUpdateResponse::FULL_UPDATE, response->response_type());
-  // TODO(vakh): For a full update, we don't need to process the update in
-  // lexographical order to store it, but we do need to do that for calculating
-  // checksum. It might save some CPU cycles to store the full update as-is and
-  // walk the list of hash prefixes in lexographical order only for checksum
-  // calculation.
-  return ProcessUpdate(metric, HashPrefixMap(), response, delay_checksum_check);
-}
-
-ApplyUpdateResult V4Store::ProcessUpdate(
-    const std::string& metric,
-    const HashPrefixMap& hash_prefix_map_old,
-    const std::unique_ptr<ListUpdateResponse>& response,
-    bool delay_checksum_check) {
-  const RepeatedField<int32>* raw_removals = nullptr;
-  RepeatedField<int32> rice_removals;
-  size_t removals_size = response->removals_size();
-  DCHECK_LE(removals_size, 1u);
-  if (removals_size == 1) {
-    const ThreatEntrySet& removal = response->removals().Get(0);
-    const CompressionType compression_type = removal.compression_type();
-    if (compression_type == RAW) {
-      raw_removals = &removal.raw_indices().indices();
-    } else if (compression_type == RICE) {
-      DCHECK(removal.has_rice_indices());
-
-      const RiceDeltaEncoding& rice_indices = removal.rice_indices();
-      V4DecodeResult decode_result = V4RiceDecoder::DecodeIntegers(
-          rice_indices.first_value(), rice_indices.rice_parameter(),
-          rice_indices.num_entries(), rice_indices.encoded_data(),
-          &rice_removals);
-
-      RecordDecodeRemovalsResult(metric, decode_result, store_path_);
-      if (decode_result != DECODE_SUCCESS) {
-        return RICE_DECODING_FAILURE;
-      }
-      raw_removals = &rice_removals;
-    } else {
-      NOTREACHED() << "Unexpected compression_type type: " << compression_type;
-      return UNEXPECTED_COMPRESSION_TYPE_REMOVALS_FAILURE;
-    }
-  }
-
-  HashPrefixMap hash_prefix_map;
-  ApplyUpdateResult apply_update_result = UpdateHashPrefixMapFromAdditions(
-      metric, response->additions(), &hash_prefix_map);
-  if (apply_update_result != APPLY_UPDATE_SUCCESS) {
-    return apply_update_result;
-  }
-
-  std::string expected_checksum;
-  if (response->has_checksum() && response->checksum().has_sha256()) {
-    expected_checksum = response->checksum().sha256();
-  }
-
-  if (delay_checksum_check) {
-    DCHECK(hash_prefix_map_old.empty());
-    DCHECK(!raw_removals);
-    // We delay the checksum check at startup to be able to load the DB
-    // quickly. In this case, the |hash_prefix_map_old| should be empty, so just
-    // copy over the |hash_prefix_map|.
-    hash_prefix_map_ = hash_prefix_map;
-
-    // Calculate the checksum asynchronously later and if it doesn't match,
-    // reset the store.
-    expected_checksum_ = expected_checksum;
-  } else {
-    apply_update_result = MergeUpdate(hash_prefix_map_old, hash_prefix_map,
-                                      raw_removals, expected_checksum);
-    if (apply_update_result != APPLY_UPDATE_SUCCESS) {
-      return apply_update_result;
-    }
-  }
-
-  state_ = response->new_client_state();
-  return APPLY_UPDATE_SUCCESS;
-}
-
-void V4Store::ApplyUpdate(
-    std::unique_ptr<ListUpdateResponse> response,
-    const scoped_refptr<base::SingleThreadTaskRunner>& callback_task_runner,
-    UpdatedStoreReadyCallback callback) {
-  std::unique_ptr<V4Store> new_store(
-      new V4Store(task_runner_, store_path_, file_size_));
-  ApplyUpdateResult apply_update_result;
-  std::string metric;
-  if (response->response_type() == ListUpdateResponse::PARTIAL_UPDATE) {
-    metric = kProcessPartialUpdate;
-    apply_update_result = new_store->ProcessPartialUpdateAndWriteToDisk(
-        metric, hash_prefix_map_, std::move(response));
-  } else if (response->response_type() == ListUpdateResponse::FULL_UPDATE) {
-    metric = kProcessFullUpdate;
-    apply_update_result =
-        new_store->ProcessFullUpdateAndWriteToDisk(metric, std::move(response));
-  } else {
-    apply_update_result = UNEXPECTED_RESPONSE_TYPE_FAILURE;
-    NOTREACHED() << "Failure: Unexpected response type: "
-                 << response->response_type();
-  }
-
-  if (apply_update_result == APPLY_UPDATE_SUCCESS) {
-    new_store->has_valid_data_ = true;
-    new_store->last_apply_update_result_ = apply_update_result;
-    new_store->last_apply_update_time_millis_ = base::Time::Now();
-    new_store->checks_attempted_ = checks_attempted_;
-  } else {
-    new_store.reset();
-    DLOG(WARNING) << "Failure: ApplyUpdate: reason: " << apply_update_result
-                  << "; store: " << *this;
-  }
-
-  // Record the state of the update to be shown in the Safe Browsing page.
-  last_apply_update_result_ = apply_update_result;
-
-  RecordApplyUpdateResult(metric, apply_update_result, store_path_);
-
-  // Posting the task should be the last thing to do in this function.
-  // Otherwise, the posted task can end up running in parallel. If that
-  // happens, the old store will get destoyed and can lead to use-after-free in
-  // this function.
-  callback_task_runner->PostTask(
-      FROM_HERE, base::BindOnce(std::move(callback), std::move(new_store)));
-}
-
-ApplyUpdateResult V4Store::UpdateHashPrefixMapFromAdditions(
-    const std::string& metric,
-    const RepeatedPtrField<ThreatEntrySet>& additions,
-    HashPrefixMap* additions_map) {
-  for (const auto& addition : additions) {
-    ApplyUpdateResult apply_update_result = APPLY_UPDATE_SUCCESS;
-    const CompressionType compression_type = addition.compression_type();
-    if (compression_type == RAW) {
-      DCHECK(addition.has_raw_hashes());
-      DCHECK(addition.raw_hashes().has_raw_hashes());
-
-      apply_update_result =
-          AddUnlumpedHashes(addition.raw_hashes().prefix_size(),
-                            addition.raw_hashes().raw_hashes(), additions_map);
-    } else if (compression_type == RICE) {
-      DCHECK(addition.has_rice_hashes());
-
-      const RiceDeltaEncoding& rice_hashes = addition.rice_hashes();
-      std::vector<uint32_t> raw_hashes;
-      V4DecodeResult decode_result = V4RiceDecoder::DecodePrefixes(
-          rice_hashes.first_value(), rice_hashes.rice_parameter(),
-          rice_hashes.num_entries(), rice_hashes.encoded_data(), &raw_hashes);
-      RecordDecodeAdditionsResult(metric, decode_result, store_path_);
-      if (decode_result != DECODE_SUCCESS) {
-        return RICE_DECODING_FAILURE;
-      } else {
-        char* raw_hashes_start = reinterpret_cast<char*>(raw_hashes.data());
-        size_t raw_hashes_size = sizeof(uint32_t) * raw_hashes.size();
-
-        // Rice-Golomb encoding is used to send compressed compressed 4-byte
-        // hash prefixes. Hash prefixes longer than 4 bytes will not be
-        // compressed, and will be served in raw format instead.
-        // Source: https://developers.google.com/safe-browsing/v4/compression
-        const PrefixSize kPrefixSize = 4;
-        apply_update_result = AddUnlumpedHashes(kPrefixSize, raw_hashes_start,
-                                                raw_hashes_size, additions_map);
-      }
-    } else {
-      NOTREACHED() << "Unexpected compression_type type: " << compression_type;
-      return UNEXPECTED_COMPRESSION_TYPE_ADDITIONS_FAILURE;
-    }
-
-    if (apply_update_result != APPLY_UPDATE_SUCCESS) {
-      // If there was an error in updating the map, discard the update entirely.
-      return apply_update_result;
-    }
-  }
-
-  return APPLY_UPDATE_SUCCESS;
-}
-
-// static
-ApplyUpdateResult V4Store::AddUnlumpedHashes(PrefixSize prefix_size,
-                                             const std::string& raw_hashes,
-                                             HashPrefixMap* additions_map) {
-  return AddUnlumpedHashes(prefix_size, raw_hashes.data(), raw_hashes.size(),
-                           additions_map);
-}
-
-// static
-ApplyUpdateResult V4Store::AddUnlumpedHashes(PrefixSize prefix_size,
-                                             const char* raw_hashes_begin,
-                                             const size_t raw_hashes_length,
-                                             HashPrefixMap* additions_map) {
-  if (prefix_size < kMinHashPrefixLength) {
-    NOTREACHED();
-    return PREFIX_SIZE_TOO_SMALL_FAILURE;
-  }
-  if (prefix_size > kMaxHashPrefixLength) {
-    NOTREACHED();
-    return PREFIX_SIZE_TOO_LARGE_FAILURE;
-  }
-  if (raw_hashes_length % prefix_size != 0) {
-    return ADDITIONS_SIZE_UNEXPECTED_FAILURE;
-  }
-
-  // TODO(vakh): Figure out a way to avoid the following copy operation.
-  (*additions_map)[prefix_size] =
-      std::string(raw_hashes_begin, raw_hashes_begin + raw_hashes_length);
-  return APPLY_UPDATE_SUCCESS;
-}
-
-// static
-bool V4Store::GetNextSmallestUnmergedPrefix(
-    const HashPrefixMap& hash_prefix_map,
-    const IteratorMap& iterator_map,
-    HashPrefix* smallest_hash_prefix) {
-  HashPrefix current_hash_prefix;
-  bool has_unmerged = false;
-
-  for (const auto& iterator_pair : iterator_map) {
-    PrefixSize prefix_size = iterator_pair.first;
-    HashPrefixes::const_iterator start = iterator_pair.second;
-
-    const HashPrefixes& hash_prefixes = hash_prefix_map.at(prefix_size);
-    PrefixSize distance = std::distance(start, hash_prefixes.end());
-    CHECK_EQ(0u, distance % prefix_size);
-    if (prefix_size <= distance) {
-      current_hash_prefix = HashPrefix(start, start + prefix_size);
-      if (!has_unmerged || *smallest_hash_prefix > current_hash_prefix) {
-        has_unmerged = true;
-        smallest_hash_prefix->swap(current_hash_prefix);
-      }
-    }
-  }
-  return has_unmerged;
-}
-
-// static
-void V4Store::InitializeIteratorMap(const HashPrefixMap& hash_prefix_map,
-                                    IteratorMap* iterator_map) {
-  for (const auto& map_pair : hash_prefix_map) {
-    (*iterator_map)[map_pair.first] = map_pair.second.begin();
-  }
-}
-
-// static
-void V4Store::ReserveSpaceInPrefixMap(const HashPrefixMap& other_prefixes_map,
-                                      HashPrefixMap* prefix_map_to_update) {
-  for (const auto& pair : other_prefixes_map) {
-    PrefixSize prefix_size = pair.first;
-    size_t prefix_length_to_add = pair.second.length();
-
-    const HashPrefixes& existing_prefixes =
-        (*prefix_map_to_update)[prefix_size];
-    size_t existing_capacity = existing_prefixes.capacity();
-
-    (*prefix_map_to_update)[prefix_size].reserve(existing_capacity +
-                                                 prefix_length_to_add);
-  }
-}
-
-ApplyUpdateResult V4Store::MergeUpdate(const HashPrefixMap& old_prefixes_map,
-                                       const HashPrefixMap& additions_map,
-                                       const RepeatedField<int32>* raw_removals,
-                                       const std::string& expected_checksum) {
-  DCHECK(task_runner_->RunsTasksInCurrentSequence());
-  DCHECK(hash_prefix_map_.empty());
-
-  bool calculate_checksum = !expected_checksum.empty();
-  if (calculate_checksum &&
-      (expected_checksum.size() != crypto::kSHA256Length)) {
-    return CHECKSUM_MISMATCH_FAILURE;
-  }
-
-  hash_prefix_map_.clear();
-  ReserveSpaceInPrefixMap(old_prefixes_map, &hash_prefix_map_);
-  ReserveSpaceInPrefixMap(additions_map, &hash_prefix_map_);
-
-  IteratorMap old_iterator_map;
-  HashPrefix next_smallest_prefix_old;
-  InitializeIteratorMap(old_prefixes_map, &old_iterator_map);
-  bool old_has_unmerged = GetNextSmallestUnmergedPrefix(
-      old_prefixes_map, old_iterator_map, &next_smallest_prefix_old);
-
-  IteratorMap additions_iterator_map;
-  HashPrefix next_smallest_prefix_additions;
-  InitializeIteratorMap(additions_map, &additions_iterator_map);
-  bool additions_has_unmerged = GetNextSmallestUnmergedPrefix(
-      additions_map, additions_iterator_map, &next_smallest_prefix_additions);
-
-  // Classical merge sort.
-  // The two constructs to merge are maps: old_prefixes_map, additions_map.
-  // At least one of the maps still has elements that need to be merged into the
-  // new store.
-
-  std::unique_ptr<crypto::SecureHash> checksum_ctx(
-      crypto::SecureHash::Create(crypto::SecureHash::SHA256));
-
-  // Keep track of the number of elements picked from the old map. This is used
-  // to determine which elements to drop based on the raw_removals. Note that
-  // picked is not the same as merged. A picked element isn't merged if its
-  // index is on the raw_removals list.
-  int total_picked_from_old = 0;
-  const int* removals_iter = raw_removals ? raw_removals->begin() : nullptr;
-  while (old_has_unmerged || additions_has_unmerged) {
-    // If the same hash prefix appears in the existing store and the additions
-    // list, something is clearly wrong. Discard the update.
-    if (old_has_unmerged && additions_has_unmerged &&
-        next_smallest_prefix_old == next_smallest_prefix_additions) {
-      return ADDITIONS_HAS_EXISTING_PREFIX_FAILURE;
-    }
-
-    // Select which map to pick the next hash prefix from to keep the result in
-    // lexographically sorted order.
-    bool pick_from_old =
-        old_has_unmerged &&
-        (!additions_has_unmerged ||
-         (next_smallest_prefix_old < next_smallest_prefix_additions));
-
-    PrefixSize next_smallest_prefix_size;
-    if (pick_from_old) {
-      next_smallest_prefix_size = next_smallest_prefix_old.size();
-
-      // Update the iterator map, which means that we have merged one hash
-      // prefix of size |next_smallest_prefix_size| from the old store.
-      old_iterator_map[next_smallest_prefix_size] += next_smallest_prefix_size;
-
-      if (!raw_removals || removals_iter == raw_removals->end() ||
-          *removals_iter != total_picked_from_old) {
-        // Append the smallest hash to the appropriate list.
-        hash_prefix_map_[next_smallest_prefix_size] += next_smallest_prefix_old;
-
-        if (calculate_checksum) {
-          checksum_ctx->Update(next_smallest_prefix_old.data(),
-                               next_smallest_prefix_size);
-        }
-      } else {
-        // Element not added to new map. Move the removals iterator forward.
-        removals_iter++;
-      }
-
-      total_picked_from_old++;
-
-      // Find the next smallest unmerged element in the old store's map.
-      old_has_unmerged = GetNextSmallestUnmergedPrefix(
-          old_prefixes_map, old_iterator_map, &next_smallest_prefix_old);
-    } else {
-      next_smallest_prefix_size = next_smallest_prefix_additions.size();
-
-      // Append the smallest hash to the appropriate list.
-      hash_prefix_map_[next_smallest_prefix_size] +=
-          next_smallest_prefix_additions;
-
-      if (calculate_checksum) {
-        checksum_ctx->Update(next_smallest_prefix_additions.data(),
-                             next_smallest_prefix_size);
-      }
-
-      // Update the iterator map, which means that we have merged one hash
-      // prefix of size |next_smallest_prefix_size| from the update.
-      additions_iterator_map[next_smallest_prefix_size] +=
-          next_smallest_prefix_size;
-
-      // Find the next smallest unmerged element in the additions map.
-      additions_has_unmerged =
-          GetNextSmallestUnmergedPrefix(additions_map, additions_iterator_map,
-                                        &next_smallest_prefix_additions);
-    }
-  }
-
-  if (raw_removals && removals_iter != raw_removals->end()) {
-    return REMOVALS_INDEX_TOO_LARGE_FAILURE;
-  }
-
-  if (calculate_checksum) {
-    char checksum[crypto::kSHA256Length];
-    checksum_ctx->Finish(checksum, sizeof(checksum));
-    for (size_t i = 0; i < crypto::kSHA256Length; i++) {
-      if (checksum[i] != expected_checksum[i]) {
-#if DCHECK_IS_ON()
-        std::string checksum_b64, expected_checksum_b64;
-        base::Base64Encode(base::StringPiece(checksum, base::size(checksum)),
-                           &checksum_b64);
-        base::Base64Encode(expected_checksum, &expected_checksum_b64);
-        DVLOG(1) << "Failure: Checksum mismatch: calculated: " << checksum_b64
-                 << "; expected: " << expected_checksum_b64
-                 << "; store: " << *this;
-#endif
-        return CHECKSUM_MISMATCH_FAILURE;
-      }
-    }
-  }
-
-  return APPLY_UPDATE_SUCCESS;
-}
-
-StoreReadResult V4Store::ReadFromDisk() {
-  DCHECK(task_runner_->RunsTasksInCurrentSequence());
-
-  V4StoreFileFormat file_format;
-  int64_t file_size;
-  {
-    // A temporary scope to make sure that |contents| get destroyed as soon as
-    // we are doing using it.
-    std::string contents;
-    if (!base::ReadFileToStringWithMaxSize(store_path_, &contents,
-                                           kMaxStoreSizeBytes)) {
-      return FILE_UNREADABLE_FAILURE;
-    }
-
-    if (contents.empty()) {
-      return FILE_EMPTY_FAILURE;
-    }
-
-    if (!file_format.ParseFromString(contents)) {
-      return PROTO_PARSING_FAILURE;
-    }
-    file_size = static_cast<int64_t>(contents.size());
-  }
-
-  if (file_format.magic_number() != kFileMagic) {
-    return UNEXPECTED_MAGIC_NUMBER_FAILURE;
-  }
-
-  if (file_format.version_number() != kFileVersion) {
-    return FILE_VERSION_INCOMPATIBLE_FAILURE;
-  }
-
-  if (!file_format.has_list_update_response()) {
-    return HASH_PREFIX_INFO_MISSING_FAILURE;
-  }
-
-  std::unique_ptr<ListUpdateResponse> response(new ListUpdateResponse);
-  response->Swap(file_format.mutable_list_update_response());
-  ApplyUpdateResult apply_update_result = ProcessFullUpdate(
-      kReadFromDisk, response, true /* delay_checksum check */);
-  RecordApplyUpdateResult(kReadFromDisk, apply_update_result, store_path_);
-  last_apply_update_result_ = apply_update_result;
-  if (apply_update_result != APPLY_UPDATE_SUCCESS) {
-    hash_prefix_map_.clear();
-    return HASH_PREFIX_MAP_GENERATION_FAILURE;
-  }
-
-  // Update |file_size_| now because we parsed the file correctly.
-  file_size_ = file_size;
-
-  return READ_SUCCESS;
-}
-
-StoreWriteResult V4Store::WriteToDisk(const Checksum& checksum) {
-  V4StoreFileFormat file_format;
-  ListUpdateResponse* lur = file_format.mutable_list_update_response();
-  *(lur->mutable_checksum()) = checksum;
-  lur->set_new_client_state(state_);
-  lur->set_response_type(ListUpdateResponse::FULL_UPDATE);
-  for (const auto& entry : hash_prefix_map_) {
-    ThreatEntrySet* additions = lur->add_additions();
-    // TODO(vakh): Write RICE encoded hash prefixes on disk. Not doing so
-    // currently since it takes a long time to decode them on startup, which
-    // blocks resource load. See: http://crbug.com/654819
-    additions->set_compression_type(RAW);
-    additions->mutable_raw_hashes()->set_prefix_size(entry.first);
-    additions->mutable_raw_hashes()->set_raw_hashes(entry.second);
-  }
-
-  // Attempt writing to a temporary file first and at the end, swap the files.
-  const base::FilePath new_filename = TemporaryFileForFilename(store_path_);
-
-  file_format.set_magic_number(kFileMagic);
-  file_format.set_version_number(kFileVersion);
-  std::string file_format_string;
-  file_format.SerializeToString(&file_format_string);
-  size_t written = base::WriteFile(new_filename, file_format_string.data(),
-                                   file_format_string.size());
-
-  if (file_format_string.size() != written) {
-    base::DeleteFile(new_filename, /*recursive=*/false);
-    return UNEXPECTED_BYTES_WRITTEN_FAILURE;
-  }
-
-  if (!base::Move(new_filename, store_path_)) {
-    base::DeleteFile(new_filename, /*recursive=*/false);
-    return UNABLE_TO_RENAME_FAILURE;
-  }
-
-  // Update |file_size_| now because we wrote the file correctly.
-  file_size_ = static_cast<int64_t>(written);
-
-  return WRITE_SUCCESS;
-}
-
-HashPrefix V4Store::GetMatchingHashPrefix(const FullHash& full_hash) {
-  return GetMatchingHashPrefix(base::StringPiece(full_hash));
-}
-
-HashPrefix V4Store::GetMatchingHashPrefix(base::StringPiece full_hash) {
-  // It should never be the case that more than one hash prefixes match a given
-  // full hash. However, if that happens, this method returns any one of them.
-  // It does not guarantee which one of those will be returned.
-  DCHECK(full_hash.size() == 32u || full_hash.size() == 21u);
-  checks_attempted_++;
-  for (const auto& pair : hash_prefix_map_) {
-    const PrefixSize& prefix_size = pair.first;
-    base::StringPiece hash_prefix = full_hash.substr(0, prefix_size);
-    if (HashPrefixMatches(hash_prefix, pair.second, prefix_size))
-      return hash_prefix.as_string();
-  }
-  return HashPrefix();
-}
-
-bool V4Store::HashPrefixMatches(base::StringPiece prefix,
-                                const HashPrefixes& prefixes,
-                                const PrefixSize& size) {
-  return std::binary_search(
-      PrefixIterator(prefixes, 0, size),
-      PrefixIterator(prefixes, prefixes.size() / size, size), prefix);
-}
-
-bool V4Store::VerifyChecksum() {
-  DCHECK(task_runner_->RunsTasksInCurrentSequence());
-
-  if (expected_checksum_.empty()) {
-    // Nothing to check here folks!
-    // TODO(vakh): Do not allow empty checksums.
-    return true;
-  }
-
-  IteratorMap iterator_map;
-  HashPrefix next_smallest_prefix;
-  InitializeIteratorMap(hash_prefix_map_, &iterator_map);
-  CHECK_EQ(hash_prefix_map_.size(), iterator_map.size());
-  bool has_unmerged = GetNextSmallestUnmergedPrefix(
-      hash_prefix_map_, iterator_map, &next_smallest_prefix);
-
-  std::unique_ptr<crypto::SecureHash> checksum_ctx(
-      crypto::SecureHash::Create(crypto::SecureHash::SHA256));
-  while (has_unmerged) {
-    PrefixSize next_smallest_prefix_size = next_smallest_prefix.size();
-
-    // Update the iterator map, which means that we have read one hash
-    // prefix of size |next_smallest_prefix_size| from hash_prefix_map_.
-    iterator_map[next_smallest_prefix_size] += next_smallest_prefix_size;
-
-    checksum_ctx->Update(next_smallest_prefix.data(),
-                         next_smallest_prefix_size);
-
-    // Find the next smallest unmerged element in the map.
-    has_unmerged = GetNextSmallestUnmergedPrefix(hash_prefix_map_, iterator_map,
-                                                 &next_smallest_prefix);
-  }
-
-  char checksum[crypto::kSHA256Length];
-  checksum_ctx->Finish(checksum, sizeof(checksum));
-  for (size_t i = 0; i < crypto::kSHA256Length; i++) {
-    if (checksum[i] != expected_checksum_[i]) {
-      RecordApplyUpdateResult(kReadFromDisk, CHECKSUM_MISMATCH_FAILURE,
-                              store_path_);
-#if DCHECK_IS_ON()
-      std::string checksum_b64, expected_checksum_b64;
-      base::Base64Encode(base::StringPiece(checksum, base::size(checksum)),
-                         &checksum_b64);
-      base::Base64Encode(expected_checksum_, &expected_checksum_b64);
-      DVLOG(1) << "Failure: Checksum mismatch: calculated: " << checksum_b64
-               << "; expected: " << expected_checksum_b64
-               << "; store: " << *this;
-#endif
-      return false;
-    }
-  }
-  return true;
-}
-
-int64_t V4Store::RecordAndReturnFileSize(const std::string& base_metric) {
-  std::string suffix = GetUmaSuffixForStore(store_path_);
-  // Histogram properties as in UMA_HISTOGRAM_COUNTS_1M macro.
-  base::HistogramBase* histogram = base::Histogram::FactoryGet(
-      base_metric + suffix, 1, 1000000, 50,
-      base::HistogramBase::kUmaTargetedHistogramFlag);
-  if (histogram) {
-    const int64_t file_size_kilobytes = file_size_ / 1024;
-    histogram->Add(file_size_kilobytes);
-  }
-  return file_size_;
-}
-
-void V4Store::CollectStoreInfo(
-    DatabaseManagerInfo::DatabaseInfo::StoreInfo* store_info,
-    const std::string& base_metric) {
-  store_info->set_file_name(GetUmaSuffixForStore(store_path_)
-                                .substr(1));  // Strip the '.' off the front
-  store_info->set_file_size_bytes(file_size_);
-  store_info->set_update_status(static_cast<int>(last_apply_update_result_));
-  store_info->set_checks_attempted(checks_attempted_);
-  if (last_apply_update_time_millis_.ToJavaTime()) {
-    store_info->set_last_apply_update_time_millis(
-        last_apply_update_time_millis_.ToJavaTime());
-  }
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/db/v4_store.h b/components/safe_browsing/db/v4_store.h
deleted file mode 100644
index 21977cb8..0000000
--- a/components/safe_browsing/db/v4_store.h
+++ /dev/null
@@ -1,450 +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.
-
-// Design doc: go/design-doc-v4store
-
-#ifndef COMPONENTS_SAFE_BROWSING_DB_V4_STORE_H_
-#define COMPONENTS_SAFE_BROWSING_DB_V4_STORE_H_
-
-#include <memory>
-#include <string>
-#include <unordered_map>
-
-#include "base/files/file_path.h"
-#include "base/memory/ref_counted.h"
-#include "base/sequenced_task_runner.h"
-#include "base/single_thread_task_runner.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
-#include "components/safe_browsing/proto/webui.pb.h"
-
-namespace safe_browsing {
-
-class V4Store;
-
-using UpdatedStoreReadyCallback =
-    base::OnceCallback<void(std::unique_ptr<V4Store> new_store)>;
-
-// The sorted list of hash prefixes.
-using HashPrefixes = std::string;
-
-// Stores the list of sorted hash prefixes, by size.
-// For instance: {4: ["abcd", "bcde", "cdef", "gggg"], 5: ["fffff"]}
-using HashPrefixMap = std::unordered_map<PrefixSize, HashPrefixes>;
-
-// Stores the iterator to the last element merged from the HashPrefixMap for a
-// given prefix size.
-// For instance: {4:iter(3), 5:iter(1)} means that we have already merged
-// 3 hash prefixes of length 4, and 1 hash prefix of length 5.
-using IteratorMap =
-    std::unordered_map<PrefixSize, HashPrefixes::const_iterator>;
-
-// Enumerate different failure events while parsing the file read from disk for
-// histogramming purposes.  DO NOT CHANGE THE ORDERING OF THESE VALUES.
-enum StoreReadResult {
-  // No errors.
-  READ_SUCCESS = 0,
-
-  // Reserved for errors in parsing this enum.
-  UNEXPECTED_READ_FAILURE = 1,
-
-  // The contents of the file could not be read.
-  FILE_UNREADABLE_FAILURE = 2,
-
-  // The file was found to be empty.
-  FILE_EMPTY_FAILURE = 3,
-
-  // The contents of the file could not be interpreted as a valid
-  // V4StoreFileFormat proto.
-  PROTO_PARSING_FAILURE = 4,
-
-  // The magic number didn't match. We're most likely trying to read a file
-  // that doesn't contain hash prefixes.
-  UNEXPECTED_MAGIC_NUMBER_FAILURE = 5,
-
-  // The version of the file is different from expected and Chromium doesn't
-  // know how to interpret this version of the file.
-  FILE_VERSION_INCOMPATIBLE_FAILURE = 6,
-
-  // The rest of the file could not be parsed as a ListUpdateResponse protobuf.
-  // This can happen if the machine crashed before the file was fully written to
-  // disk or if there was disk corruption.
-  HASH_PREFIX_INFO_MISSING_FAILURE = 7,
-
-  // Unable to generate the hash prefix map from the updates on disk.
-  HASH_PREFIX_MAP_GENERATION_FAILURE = 8,
-
-  // Memory space for histograms is determined by the max.  ALWAYS
-  // ADD NEW VALUES BEFORE THIS ONE.
-  STORE_READ_RESULT_MAX
-};
-
-// Enumerate different failure events while writing the file to disk after
-// applying updates for histogramming purposes.
-// DO NOT CHANGE THE ORDERING OF THESE VALUES.
-enum StoreWriteResult {
-  // No errors.
-  WRITE_SUCCESS = 0,
-
-  // Reserved for errors in parsing this enum.
-  UNEXPECTED_WRITE_FAILURE = 1,
-
-  // The proto being written to disk wasn't a FULL_UPDATE proto.
-  INVALID_RESPONSE_TYPE_FAILURE = 2,
-
-  // Number of bytes written to disk was different from the size of the proto.
-  UNEXPECTED_BYTES_WRITTEN_FAILURE = 3,
-
-  // Renaming the temporary file to store file failed.
-  UNABLE_TO_RENAME_FAILURE = 4,
-
-  // Memory space for histograms is determined by the max.  ALWAYS
-  // ADD NEW VALUES BEFORE THIS ONE.
-  STORE_WRITE_RESULT_MAX
-};
-
-// Enumerate different events while applying the update fetched fom the server
-// for histogramming purposes.
-// DO NOT CHANGE THE ORDERING OF THESE VALUES.
-enum ApplyUpdateResult {
-  // No errors.
-  APPLY_UPDATE_SUCCESS = 0,
-
-  // Reserved for errors in parsing this enum.
-  UNEXPECTED_APPLY_UPDATE_FAILURE = 1,
-
-  // Prefix size smaller than 4 (which is the lowest expected).
-  PREFIX_SIZE_TOO_SMALL_FAILURE = 2,
-
-  // Prefix size larger than 32 (length of a full SHA256 hash).
-  PREFIX_SIZE_TOO_LARGE_FAILURE = 3,
-
-  // The number of bytes in additions isn't a multiple of prefix size.
-  ADDITIONS_SIZE_UNEXPECTED_FAILURE = 4,
-
-  // The update received from the server contains a prefix that's already
-  // present in the map.
-  ADDITIONS_HAS_EXISTING_PREFIX_FAILURE = 5,
-
-  // The server sent a response_type that the client did not expect.
-  UNEXPECTED_RESPONSE_TYPE_FAILURE = 6,
-
-  // One of more index(es) in removals field of the response is greater than
-  // the number of hash prefixes currently in the (old) store.
-  REMOVALS_INDEX_TOO_LARGE_FAILURE = 7,
-
-  // Failed to decode the Rice-encoded additions/removals field.
-  RICE_DECODING_FAILURE = 8,
-
-  // Compression type other than RAW and RICE for additions.
-  UNEXPECTED_COMPRESSION_TYPE_ADDITIONS_FAILURE = 9,
-
-  // Compression type other than RAW and RICE for removals.
-  UNEXPECTED_COMPRESSION_TYPE_REMOVALS_FAILURE = 10,
-
-  // The state of the store did not match the expected checksum sent by the
-  // server.
-  CHECKSUM_MISMATCH_FAILURE = 11,
-
-  // Memory space for histograms is determined by the max.  ALWAYS
-  // ADD NEW VALUES BEFORE THIS ONE.
-  APPLY_UPDATE_RESULT_MAX
-};
-
-// Factory for creating V4Store. Tests implement this factory to create fake
-// stores for testing.
-class V4StoreFactory {
- public:
-  virtual ~V4StoreFactory() {}
-
-  virtual std::unique_ptr<V4Store> CreateV4Store(
-      const scoped_refptr<base::SequencedTaskRunner>& task_runner,
-      const base::FilePath& store_path);
-};
-
-class V4Store {
- public:
-  // The |task_runner| is used to ensure that the operations in this file are
-  // performed on the correct thread. |store_path| specifies the location on
-  // disk for this file. The constructor doesn't read the store file from disk.
-  // If the store is being created to apply an update to the old store, then
-  // |old_file_size| is the size of the existing file on disk for this store;
-  // 0 otherwise. This is needed so that we can correctly report the size of
-  // store file on disk, even if writing the new file fails after successfully
-  // applying an update.
-  V4Store(const scoped_refptr<base::SequencedTaskRunner>& task_runner,
-          const base::FilePath& store_path,
-          int64_t old_file_size = 0);
-  virtual ~V4Store();
-
-  // Schedules the destruction of the V4Store object pointed to by |v4_store|,
-  // on the task runner.
-  static void Destroy(std::unique_ptr<V4Store> v4_store);
-
-  // If a hash prefix in this store matches |full_hash|, returns that hash
-  // prefix; otherwise returns an empty hash prefix.
-  virtual HashPrefix GetMatchingHashPrefix(const FullHash& full_hash);
-
-  // True if this store has valid contents, either from a successful read
-  // from disk or a full update.  This does not mean the checksum was verified.
-  virtual bool HasValidData() const;
-
-  const std::string& state() const { return state_; }
-
-  const base::FilePath& store_path() const { return store_path_; }
-
-  void ApplyUpdate(std::unique_ptr<ListUpdateResponse> response,
-                   const scoped_refptr<base::SingleThreadTaskRunner>& runner,
-                   UpdatedStoreReadyCallback callback);
-
-  // Records (in kilobytes) and returns the size of the file on disk for this
-  // store using |base_metric| as prefix and the filename as suffix.
-  int64_t RecordAndReturnFileSize(const std::string& base_metric);
-
-  std::string DebugString() const;
-
-  // Reads the store file from disk and populates the in-memory representation
-  // of the hash prefixes.
-  void Initialize();
-
-  // Reset internal state.
-  void Reset();
-
-  // Scheduled after reading the store file from disk on startup. When run, it
-  // ensures that the checksum of the hash prefixes in lexicographical sorted
-  // order matches the expected value in |expected_checksum_|. Returns true if
-  // it matches; false otherwise. Checksum verification can take a long time,
-  // so it is performed outside of the hotpath of loading SafeBrowsing database,
-  // which blocks resource loads.
-  bool VerifyChecksum();
-
-  // Populates the DatabaseInfo message.
-  void CollectStoreInfo(
-      DatabaseManagerInfo::DatabaseInfo::StoreInfo* store_info,
-      const std::string& base_metric);
-
- protected:
-  HashPrefixMap hash_prefix_map_;
-
- private:
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestReadFromEmptyFile);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestReadFromAbsentFile);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestReadFromInvalidContentsFile);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestReadFromUnexpectedMagicFile);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestReadFromLowVersionFile);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestReadFromNoHashPrefixInfoFile);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestReadFromNoHashPrefixesFile);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestWriteNoResponseType);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestWritePartialResponseType);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestWriteFullResponseType);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestReadFromFileWithUnknownProto);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest,
-                           TestAddUnlumpedHashesWithInvalidAddition);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestAddUnlumpedHashes);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestAddUnlumpedHashesWithEmptyString);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest,
-                           TestGetNextSmallestUnmergedPrefixWithEmptyPrefixMap);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestGetNextSmallestUnmergedPrefix);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestMergeUpdatesWithSameSizesInEachMap);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest,
-                           TestMergeUpdatesWithDifferentSizesInEachMap);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestMergeUpdatesOldMapRunsOutFirst);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest,
-                           TestMergeUpdatesAdditionsMapRunsOutFirst);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest,
-                           TestMergeUpdatesFailsForRepeatedHashPrefix);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest,
-                           TestMergeUpdatesFailsWhenRemovalsIndexTooLarge);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestMergeUpdatesRemovesOnlyElement);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestMergeUpdatesRemovesFirstElement);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestMergeUpdatesRemovesMiddleElement);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestMergeUpdatesRemovesLastElement);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest,
-                           TestMergeUpdatesRemovesWhenOldHasDifferentSizes);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest,
-                           TestMergeUpdatesRemovesMultipleAcrossDifferentSizes);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest,
-                           TestReadFullResponseWithValidHashPrefixMap);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest,
-                           TestReadFullResponseWithInvalidHashPrefixMap);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestHashPrefixExistsAtTheBeginning);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestHashPrefixExistsInTheMiddle);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestHashPrefixExistsAtTheEnd);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest,
-                           TestHashPrefixExistsAtTheBeginningOfEven);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestHashPrefixExistsAtTheEndOfEven);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest,
-                           TestHashPrefixDoesNotExistInConcatenatedList);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestFullHashExistsInMapWithSingleSize);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest,
-                           TestFullHashExistsInMapWithDifferentSizes);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest,
-                           TestHashPrefixExistsInMapWithSingleSize);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest,
-                           TestHashPrefixExistsInMapWithDifferentSizes);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest,
-                           TestHashPrefixDoesNotExistInMapWithDifferentSizes);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, GetMatchingHashPrefixSize32Or21);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest,
-                           TestAdditionsWithRiceEncodingFailsWithInvalidInput);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestAdditionsWithRiceEncodingSucceeds);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestRemovalsWithRiceEncodingSucceeds);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestMergeUpdatesFailsChecksum);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, TestChecksumErrorOnStartup);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, WriteToDiskFails);
-  FRIEND_TEST_ALL_PREFIXES(V4StoreTest, FullUpdateFailsChecksumSynchronously);
-  FRIEND_TEST_ALL_PREFIXES(V4StorePerftest, StressTest);
-
-  friend class V4StoreTest;
-  friend class V4StoreFuzzer;
-
-  // If |prefix_size| is within expected range, and |raw_hashes_length| is a
-  // multiple of prefix_size, then it sets the string of length
-  // |raw_hashes_length| starting at |raw_hashes_begin| as the value at key
-  // |prefix_size| in |additions_map|
-  static ApplyUpdateResult AddUnlumpedHashes(PrefixSize prefix_size,
-                                             const char* raw_hashes_begin,
-                                             const size_t raw_hashes_length,
-                                             HashPrefixMap* additions_map);
-
-  // An overloaded version of AddUnlumpedHashes that allows passing in a
-  // std::string object.
-  static ApplyUpdateResult AddUnlumpedHashes(PrefixSize prefix_size,
-                                             const std::string& raw_hashes,
-                                             HashPrefixMap* additions_map);
-
-  // Get the next unmerged hash prefix in dictionary order from
-  // |hash_prefix_map|. |iterator_map| is used to determine which hash prefixes
-  // have been merged already. Returns true if there are any unmerged hash
-  // prefixes in the list.
-  static bool GetNextSmallestUnmergedPrefix(
-      const HashPrefixMap& hash_prefix_map,
-      const IteratorMap& iterator_map,
-      HashPrefix* smallest_hash_prefix);
-
-  // Returns true if |hash_prefix| with PrefixSize |size| exists in |prefixes|.
-  // This small method is exposed in the header so it can be tested separately.
-  static bool HashPrefixMatches(base::StringPiece prefix,
-                                const HashPrefixes& prefixes,
-                                const PrefixSize& size);
-
-  // For each key in |hash_prefix_map|, sets the iterator at that key
-  // |iterator_map| to hash_prefix_map[key].begin().
-  static void InitializeIteratorMap(const HashPrefixMap& hash_prefix_map,
-                                    IteratorMap* iterator_map);
-
-  // Reserve the appropriate string size so that the string size of the merged
-  // list is exact. This ignores the space that would otherwise be released by
-  // deletions specified in the update because it is non-trivial to calculate
-  // those deletions upfront. This isn't so bad since deletions are supposed to
-  // be small and infrequent.
-  static void ReserveSpaceInPrefixMap(const HashPrefixMap& other_prefixes_map,
-                                      HashPrefixMap* prefix_map_to_update);
-
-  // Same as the public GetMatchingHashPrefix method, but takes a StringPiece,
-  // for performance reasons.
-  HashPrefix GetMatchingHashPrefix(base::StringPiece full_hash);
-
-  // Merges the prefix map from the old store (|old_hash_prefix_map|) and the
-  // update (additions_map) to populate the prefix map for the current store.
-  // The indices in the |raw_removals| list, which may be NULL, are not merged.
-  // The SHA256 checksum of the final list of hash prefixes, in
-  // lexicographically sorted order, must match |expected_checksum| (if it's not
-  // empty).
-  ApplyUpdateResult MergeUpdate(
-      const HashPrefixMap& old_hash_prefix_map,
-      const HashPrefixMap& additions_map,
-      const ::google::protobuf::RepeatedField<::google::protobuf::int32>*
-          raw_removals,
-      const std::string& expected_checksum);
-
-  // Processes the FULL_UPDATE |response| from the server, and writes the
-  // merged V4Store to disk. If processing the |response| succeeds, it returns
-  // APPLY_UPDATE_SUCCESS. The UMA metrics for all interesting sub-operations
-  // use the prefix |metric|.
-  // This method is only called when we receive a FULL_UPDATE from the server.
-  ApplyUpdateResult ProcessFullUpdateAndWriteToDisk(
-      const std::string& metric,
-      std::unique_ptr<ListUpdateResponse> response);
-
-  // Processes a FULL_UPDATE |response| and updates the V4Store. If processing
-  // the |response| succeeds, it returns APPLY_UPDATE_SUCCESS.
-  // This method is called when we receive a FULL_UPDATE from the server, and
-  // when we read a store file from disk on startup. The UMA metrics for all
-  // interesting sub-operations use the prefix |metric|. Delays the checksum
-  // check if |delay_checksum_check| is true.
-  ApplyUpdateResult ProcessFullUpdate(
-      const std::string& metric,
-      const std::unique_ptr<ListUpdateResponse>& response,
-      bool delay_checksum_check);
-
-  // Merges the hash prefixes in |hash_prefix_map_old| and |response|, updates
-  // the |hash_prefix_map_| and |state_| in the V4Store, and writes the merged
-  // store to disk. If processing succeeds, it returns APPLY_UPDATE_SUCCESS.
-  // This method is only called when we receive a PARTIAL_UPDATE from the
-  // server. The UMA metrics for all interesting sub-operations use the prefix
-  // |metric|.
-  ApplyUpdateResult ProcessPartialUpdateAndWriteToDisk(
-      const std::string& metric,
-      const HashPrefixMap& hash_prefix_map_old,
-      std::unique_ptr<ListUpdateResponse> response);
-
-  // Merges the hash prefixes in |hash_prefix_map_old| and |response|, and
-  // updates the |hash_prefix_map_| and |state_| in the V4Store. If processing
-  // succeeds, it returns APPLY_UPDATE_SUCCESS. The UMA metrics for all
-  // interesting sub-operations use the prefix |metric|. Delays the checksum
-  // check if |delay_checksum_check| is true.
-  ApplyUpdateResult ProcessUpdate(
-      const std::string& metric,
-      const HashPrefixMap& hash_prefix_map_old,
-      const std::unique_ptr<ListUpdateResponse>& response,
-      bool delay_checksum_check);
-
-  // Reads the state of the store from the file on disk and returns the reason
-  // for the failure or reports success.
-  StoreReadResult ReadFromDisk();
-
-  // Updates the |additions_map| with the additions received in the partial
-  // update from the server. The UMA metrics for all interesting sub-operations
-  // use the prefix |metric|.
-  ApplyUpdateResult UpdateHashPrefixMapFromAdditions(
-      const std::string& metric,
-      const ::google::protobuf::RepeatedPtrField<ThreatEntrySet>& additions,
-      HashPrefixMap* additions_map);
-
-  // Writes the hash_prefix_map_ to disk as a V4StoreFileFormat proto.
-  // |checksum| is used to set the |checksum| field in the final proto.
-  StoreWriteResult WriteToDisk(const Checksum& checksum);
-
-  // Records the status of the update being applied to the database.
-  ApplyUpdateResult last_apply_update_result_ = APPLY_UPDATE_RESULT_MAX;
-
-  // Records the time when the store was last updated.
-  base::Time last_apply_update_time_millis_;
-
-  // The checksum value as read from the disk, until it is verified. Once
-  // verified, it is cleared.
-  std::string expected_checksum_;
-
-  // The size of the file on disk for this store.
-  int64_t file_size_;
-
-  // True if the file was successfully read+parsed or was populated from
-  // a full update.
-  bool has_valid_data_;
-
-  // Records the number of times we have looked up the store.
-  size_t checks_attempted_ = 0;
-
-  // The state of the store as returned by the PVer4 server in the last applied
-  // update response.
-  std::string state_;
-  const base::FilePath store_path_;
-  const scoped_refptr<base::SequencedTaskRunner> task_runner_;
-};
-
-std::ostream& operator<<(std::ostream& os, const V4Store& store);
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_DB_V4_STORE_H_
diff --git a/components/safe_browsing/db/v4_store_fuzzer.cc b/components/safe_browsing/db/v4_store_fuzzer.cc
deleted file mode 100644
index 6d4f3fd..0000000
--- a/components/safe_browsing/db/v4_store_fuzzer.cc
+++ /dev/null
@@ -1,127 +0,0 @@
-// Copyright 2019 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 <stdint.h>
-#include <memory>
-#include <string>
-
-#include "base/files/file_path.h"
-#include "base/test/test_simple_task_runner.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
-#include "components/safe_browsing/db/v4_store.h"
-#include "components/safe_browsing/db/v4_test_util.h"
-
-namespace safe_browsing {
-
-const PrefixSize kMinHashPrefixLengthForFuzzing = kMinHashPrefixLength;
-const PrefixSize kMaxHashPrefixLengthForFuzzing = 8;
-
-class V4StoreFuzzer {
- public:
-  static int FuzzMergeUpdate(const uint8_t* data, size_t size) {
-    // |prefix_map_old| represents the existing state of the |V4Store|.
-    HashPrefixMap prefix_map_old;
-    // |prefix_map_additions| represents the update being applied.
-    HashPrefixMap prefix_map_additions;
-
-    // Pass 1:
-    // Add a prefix_size->[prefixes] pair in |prefix_map_old|.
-    PopulateHashPrefixMap(&data, &size, &prefix_map_old);
-    // Add a prefix_size->[prefixes] pair in |prefix_map_additions|.
-    PopulateHashPrefixMap(&data, &size, &prefix_map_additions);
-
-    // Pass 2:
-    // Add a prefix_size->[prefixes] pair in |prefix_map_old|.
-    // If the prefix_size is the same as that added in |prefix_map_old| during
-    // Pass 1, the older list of prefixes is lost.
-    PopulateHashPrefixMap(&data, &size, &prefix_map_old);
-    // Add a prefix_size->[prefixes] pair in |prefix_map_additions|.
-    // If the prefix_size is the same as that added in |prefix_map_additions|
-    // during Pass 1, the older list of prefixes is lost.
-    PopulateHashPrefixMap(&data, &size, &prefix_map_additions);
-
-    auto store = std::make_unique<TestV4Store>(
-        base::MakeRefCounted<base::TestSimpleTaskRunner>(), base::FilePath());
-    // Assume no removals.
-    google::protobuf::RepeatedField<google::protobuf::int32> raw_removals;
-    // Empty checksum indicates that the checksum calculation should be skipped.
-    std::string empty_checksum;
-    store->MergeUpdate(prefix_map_old, prefix_map_additions, &raw_removals,
-                       empty_checksum);
-#ifndef NDEBUG
-    DisplayHashPrefixMapDetails(store->hash_prefix_map_);
-#endif
-
-    return 0;
-  }
-
- private:
-  // Add a prefix_size->[prefixes] pair in |hash_prefix_map|.
-  // Ensures that length of [prefixes] is a multiple of prefix_size.
-  // If the map already contains a pair with key prefix_size, the existing value
-  // is discarded.
-  // Here's a summary of how the input is parsed:
-  // * First uint8_t is the |prefix_size| to be added.
-  // * Next uint8_t is the length of the list of prefixes.
-  //  * It is adjusted to be no greater than the remaining size of |data|.
-  //  * It is called as |prefixes_list_size|.
-  // * Next |prefixes_list_size| bytes are added to |hash_prefix_map|
-  //   as a list of prefixes of size |prefix_size|.
-  static void PopulateHashPrefixMap(const uint8_t** data,
-                                    size_t* size,
-                                    HashPrefixMap* hash_prefix_map) {
-    uint8_t datum;
-    if (!GetDatum(data, size, &datum))
-      return;
-
-    // Prefix size is defined to be between |kMinHashPrefixLength| and
-    // |kMaxHashPrefixLength| but we are going to limit them to smaller sizes so
-    // that we have a higher chance of actually populating the
-    // |hash_prefix_map| for smaller inputs.
-    PrefixSize prefix_size = kMinHashPrefixLengthForFuzzing +
-                             (datum % (kMaxHashPrefixLengthForFuzzing -
-                                       kMinHashPrefixLengthForFuzzing + 1));
-
-    if (!GetDatum(data, size, &datum))
-      return;
-    size_t prefixes_list_size = datum;
-    // This |prefixes_list_size| is the length of the list of prefixes to be
-    // added. It can't be larger than the remaining buffer.
-    if (*size < prefixes_list_size) {
-      prefixes_list_size = *size;
-    }
-    std::string prefixes(*data, *data + prefixes_list_size);
-    *size -= prefixes_list_size;
-    *data += prefixes_list_size;
-    V4Store::AddUnlumpedHashes(prefix_size, prefixes, hash_prefix_map);
-#ifndef NDEBUG
-    DisplayHashPrefixMapDetails(*hash_prefix_map);
-#endif
-  }
-
-  static bool GetDatum(const uint8_t** data, size_t* size, uint8_t* datum) {
-    if (*size == 0)
-      return false;
-    *datum = *data[0];
-    (*data)++;
-    (*size)--;
-    return true;
-  }
-
-  static void DisplayHashPrefixMapDetails(
-      const HashPrefixMap& hash_prefix_map) {
-    for (const auto& pair : hash_prefix_map) {
-      PrefixSize prefix_size = pair.first;
-      size_t prefixes_length = pair.second.length();
-      DVLOG(5) << __FUNCTION__ << " : " << prefix_size << " : "
-               << prefixes_length;
-    }
-  }
-};
-
-}  // namespace safe_browsing
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  return safe_browsing::V4StoreFuzzer::FuzzMergeUpdate(data, size);
-}
diff --git a/components/safe_browsing/db/v4_store_perftest.cc b/components/safe_browsing/db/v4_store_perftest.cc
deleted file mode 100644
index 3282b16..0000000
--- a/components/safe_browsing/db/v4_store_perftest.cc
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <memory>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/numerics/checked_math.h"
-#include "base/strings/stringprintf.h"
-#include "base/test/test_simple_task_runner.h"
-#include "base/timer/elapsed_timer.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
-#include "components/safe_browsing/db/v4_test_util.h"
-#include "crypto/sha2.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/perf/perf_result_reporter.h"
-
-namespace safe_browsing {
-
-namespace {
-
-constexpr char kMetricPrefixV4Store[] = "V4Store.";
-constexpr char kMetricGetMatchingHashPrefixMs[] = "get_matching_hash_prefix";
-
-perf_test::PerfResultReporter SetUpV4StoreReporter(const std::string& story) {
-  perf_test::PerfResultReporter reporter(kMetricPrefixV4Store, story);
-  reporter.RegisterImportantMetric(kMetricGetMatchingHashPrefixMs, "ms");
-  return reporter;
-}
-
-}  // namespace
-
-class V4StorePerftest : public testing::Test {};
-
-TEST_F(V4StorePerftest, StressTest) {
-// Debug builds can be quite slow. Use a smaller number of prefixes to test.
-#if defined(NDEBUG)
-  const size_t kNumPrefixes = 2000000;
-#else
-  const size_t kNumPrefixes = 20000;
-#endif
-
-  static_assert(kMaxHashPrefixLength == crypto::kSHA256Length,
-                "SHA256 produces a valid FullHash");
-  CHECK(base::IsValidForType<size_t>(
-      base::CheckMul(kNumPrefixes, kMaxHashPrefixLength)));
-
-  // Keep the full hashes as one big string to avoid tons of allocations /
-  // deallocations in the test.
-  std::string full_hashes(kNumPrefixes * kMaxHashPrefixLength, 0);
-  base::StringPiece full_hashes_piece = base::StringPiece(full_hashes);
-  std::vector<std::string> prefixes;
-  for (size_t i = 0; i < kNumPrefixes; i++) {
-    size_t index = i * kMaxHashPrefixLength;
-    crypto::SHA256HashString(base::StringPrintf("%zu", i), &full_hashes[index],
-                             kMaxHashPrefixLength);
-    prefixes.push_back(full_hashes.substr(index, kMinHashPrefixLength));
-  }
-
-  auto store = std::make_unique<TestV4Store>(
-      base::MakeRefCounted<base::TestSimpleTaskRunner>(), base::FilePath());
-  store->SetPrefixes(std::move(prefixes), kMinHashPrefixLength);
-
-  size_t matches = 0;
-  auto reporter = SetUpV4StoreReporter("stress_test");
-  base::ElapsedTimer timer;
-  for (size_t i = 0; i < kNumPrefixes; i++) {
-    size_t index = i * kMaxHashPrefixLength;
-    base::StringPiece full_hash =
-        full_hashes_piece.substr(index, kMaxHashPrefixLength);
-    matches += !store->GetMatchingHashPrefix(full_hash).empty();
-  }
-  reporter.AddResult(kMetricGetMatchingHashPrefixMs,
-                     timer.Elapsed().InMillisecondsF());
-
-  EXPECT_EQ(kNumPrefixes, matches);
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/db/v4_store_unittest.cc b/components/safe_browsing/db/v4_store_unittest.cc
deleted file mode 100644
index ba3a72f..0000000
--- a/components/safe_browsing/db/v4_store_unittest.cc
+++ /dev/null
@@ -1,887 +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.
-
-#include "components/safe_browsing/db/v4_store.h"
-
-#include "base/base64.h"
-#include "base/bind.h"
-#include "base/files/file_util.h"
-#include "base/files/scoped_temp_dir.h"
-#include "base/run_loop.h"
-#include "base/test/test_simple_task_runner.h"
-#include "base/time/time.h"
-#include "components/safe_browsing/db/v4_store.pb.h"
-#include "content/public/test/browser_task_environment.h"
-#include "crypto/sha2.h"
-#include "testing/platform_test.h"
-
-namespace safe_browsing {
-
-using ::google::protobuf::RepeatedField;
-using ::google::protobuf::RepeatedPtrField;
-using ::google::protobuf::int32;
-
-class V4StoreTest : public PlatformTest {
- public:
-  V4StoreTest() : task_runner_(new base::TestSimpleTaskRunner) {}
-
-  void SetUp() override {
-    PlatformTest::SetUp();
-
-    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
-    store_path_ = temp_dir_.GetPath().AppendASCII("V4StoreTest.store");
-    DVLOG(1) << "store_path_: " << store_path_.value();
-  }
-
-  void TearDown() override {
-    base::DeleteFile(store_path_, false);
-    PlatformTest::TearDown();
-  }
-
-  void WriteFileFormatProtoToFile(uint32_t magic,
-                                  uint32_t version = 0,
-                                  ListUpdateResponse* response = nullptr) {
-    V4StoreFileFormat file_format;
-    file_format.set_magic_number(magic);
-    file_format.set_version_number(version);
-    if (response != nullptr) {
-      ListUpdateResponse* list_update_response =
-          file_format.mutable_list_update_response();
-      *list_update_response = *response;
-    }
-
-    std::string file_format_string;
-    file_format.SerializeToString(&file_format_string);
-    base::WriteFile(store_path_, file_format_string.data(),
-                    file_format_string.size());
-  }
-
-  void UpdatedStoreReady(bool* called_back,
-                         bool expect_store,
-                         std::unique_ptr<V4Store> store) {
-    *called_back = true;
-    if (expect_store) {
-      ASSERT_TRUE(store);
-      EXPECT_EQ(2u, store->hash_prefix_map_.size());
-      EXPECT_EQ("22222", store->hash_prefix_map_[5]);
-      EXPECT_EQ("abcd", store->hash_prefix_map_[4]);
-    } else {
-      ASSERT_FALSE(store);
-    }
-
-    updated_store_ = std::move(store);
-  }
-
-  base::ScopedTempDir temp_dir_;
-  base::FilePath store_path_;
-  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
-  content::BrowserTaskEnvironment task_environment_;
-  std::unique_ptr<V4Store> updated_store_;
-};
-
-TEST_F(V4StoreTest, TestReadFromEmptyFile) {
-  base::CloseFile(base::OpenFile(store_path_, "wb+"));
-
-  V4Store store(task_runner_, store_path_);
-  EXPECT_EQ(FILE_EMPTY_FAILURE, store.ReadFromDisk());
-  EXPECT_FALSE(store.HasValidData());
-}
-
-TEST_F(V4StoreTest, TestReadFromAbsentFile) {
-  EXPECT_EQ(FILE_UNREADABLE_FAILURE,
-            V4Store(task_runner_, store_path_).ReadFromDisk());
-}
-
-TEST_F(V4StoreTest, TestReadFromInvalidContentsFile) {
-  const char kInvalidContents[] = "Chromium";
-  base::WriteFile(store_path_, kInvalidContents, strlen(kInvalidContents));
-  EXPECT_EQ(PROTO_PARSING_FAILURE,
-            V4Store(task_runner_, store_path_).ReadFromDisk());
-}
-
-TEST_F(V4StoreTest, TestReadFromFileWithUnknownProto) {
-  Checksum checksum;
-  checksum.set_sha256("checksum");
-  std::string checksum_string;
-  checksum.SerializeToString(&checksum_string);
-  base::WriteFile(store_path_, checksum_string.data(), checksum_string.size());
-
-  // Even though we wrote a completely different proto to file, the proto
-  // parsing method does not fail. This shows the importance of a magic number.
-  EXPECT_EQ(UNEXPECTED_MAGIC_NUMBER_FAILURE,
-            V4Store(task_runner_, store_path_).ReadFromDisk());
-}
-
-TEST_F(V4StoreTest, TestReadFromUnexpectedMagicFile) {
-  WriteFileFormatProtoToFile(111);
-  EXPECT_EQ(UNEXPECTED_MAGIC_NUMBER_FAILURE,
-            V4Store(task_runner_, store_path_).ReadFromDisk());
-}
-
-TEST_F(V4StoreTest, TestReadFromLowVersionFile) {
-  WriteFileFormatProtoToFile(0x600D71FE, 2);
-  EXPECT_EQ(FILE_VERSION_INCOMPATIBLE_FAILURE,
-            V4Store(task_runner_, store_path_).ReadFromDisk());
-}
-
-TEST_F(V4StoreTest, TestReadFromNoHashPrefixInfoFile) {
-  WriteFileFormatProtoToFile(0x600D71FE, 9);
-  EXPECT_EQ(HASH_PREFIX_INFO_MISSING_FAILURE,
-            V4Store(task_runner_, store_path_).ReadFromDisk());
-}
-
-TEST_F(V4StoreTest, TestReadFromNoHashPrefixesFile) {
-  ListUpdateResponse list_update_response;
-  list_update_response.set_platform_type(LINUX_PLATFORM);
-  list_update_response.set_response_type(ListUpdateResponse::FULL_UPDATE);
-  WriteFileFormatProtoToFile(0x600D71FE, 9, &list_update_response);
-  V4Store store(task_runner_, store_path_);
-  EXPECT_EQ(READ_SUCCESS, store.ReadFromDisk());
-  EXPECT_TRUE(store.hash_prefix_map_.empty());
-  EXPECT_EQ(14, store.file_size_);
-  EXPECT_FALSE(store.HasValidData());
-}
-
-TEST_F(V4StoreTest, TestAddUnlumpedHashesWithInvalidAddition) {
-  HashPrefixMap prefix_map;
-  EXPECT_EQ(ADDITIONS_SIZE_UNEXPECTED_FAILURE,
-            V4Store::AddUnlumpedHashes(5, "a", &prefix_map));
-  EXPECT_TRUE(prefix_map.empty());
-}
-
-TEST_F(V4StoreTest, TestAddUnlumpedHashesWithEmptyString) {
-  HashPrefixMap prefix_map;
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            V4Store::AddUnlumpedHashes(5, "", &prefix_map));
-  EXPECT_TRUE(prefix_map[5].empty());
-}
-
-TEST_F(V4StoreTest, TestAddUnlumpedHashes) {
-  HashPrefixMap prefix_map;
-  PrefixSize prefix_size = 5;
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            V4Store::AddUnlumpedHashes(prefix_size, "abcde5432100000-----",
-                                       &prefix_map));
-  EXPECT_EQ(1u, prefix_map.size());
-  HashPrefixes hash_prefixes = prefix_map.at(prefix_size);
-  EXPECT_EQ(4 * prefix_size, hash_prefixes.size());
-  EXPECT_EQ("abcde5432100000-----", hash_prefixes);
-
-  prefix_size = 4;
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            V4Store::AddUnlumpedHashes(prefix_size, "abcde5432100000-----",
-                                       &prefix_map));
-  EXPECT_EQ(2u, prefix_map.size());
-  hash_prefixes = prefix_map.at(prefix_size);
-  EXPECT_EQ(5 * prefix_size, hash_prefixes.size());
-  EXPECT_EQ("abcde5432100000-----", hash_prefixes);
-}
-
-TEST_F(V4StoreTest, TestGetNextSmallestUnmergedPrefixWithEmptyPrefixMap) {
-  HashPrefixMap prefix_map;
-  IteratorMap iterator_map;
-  V4Store::InitializeIteratorMap(prefix_map, &iterator_map);
-
-  HashPrefix prefix;
-  EXPECT_FALSE(V4Store::GetNextSmallestUnmergedPrefix(prefix_map, iterator_map,
-                                                      &prefix));
-}
-
-TEST_F(V4StoreTest, TestGetNextSmallestUnmergedPrefix) {
-  HashPrefixMap prefix_map;
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            V4Store::AddUnlumpedHashes(5, "-----0000054321abcde", &prefix_map));
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            V4Store::AddUnlumpedHashes(4, "*****0000054321abcde", &prefix_map));
-  IteratorMap iterator_map;
-  V4Store::InitializeIteratorMap(prefix_map, &iterator_map);
-
-  HashPrefix prefix;
-  EXPECT_TRUE(V4Store::GetNextSmallestUnmergedPrefix(prefix_map, iterator_map,
-                                                     &prefix));
-  EXPECT_EQ("****", prefix);
-}
-
-TEST_F(V4StoreTest, TestMergeUpdatesWithSameSizesInEachMap) {
-  HashPrefixMap prefix_map_old;
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            V4Store::AddUnlumpedHashes(4, "abcdefgh", &prefix_map_old));
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            V4Store::AddUnlumpedHashes(5, "54321abcde", &prefix_map_old));
-  HashPrefixMap prefix_map_additions;
-  EXPECT_EQ(
-      APPLY_UPDATE_SUCCESS,
-      V4Store::AddUnlumpedHashes(4, "----1111bbbb", &prefix_map_additions));
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            V4Store::AddUnlumpedHashes(5, "22222bcdef", &prefix_map_additions));
-
-  V4Store store(task_runner_, store_path_);
-  // Proof of checksum validity using python:
-  // >>> import hashlib
-  // >>> m = hashlib.sha256()
-  // >>> m.update("----11112222254321abcdabcdebbbbbcdefefgh")
-  // >>> m.digest()
-  // "\xbc\xb3\xedk\xe3x\xd1(\xa9\xedz7]"
-  // "x\x18\xbdn]\xa5\xa8R\xf7\xab\xcf\xc1\xa3\xa3\xc5Z,\xa6o"
-  std::string expected_checksum = std::string(
-      "\xBC\xB3\xEDk\xE3x\xD1(\xA9\xEDz7]x\x18\xBDn]"
-      "\xA5\xA8R\xF7\xAB\xCF\xC1\xA3\xA3\xC5Z,\xA6o",
-      crypto::kSHA256Length);
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            store.MergeUpdate(prefix_map_old, prefix_map_additions, nullptr,
-                              expected_checksum));
-
-  const HashPrefixMap& prefix_map = store.hash_prefix_map_;
-  EXPECT_EQ(2u, prefix_map.size());
-
-  PrefixSize prefix_size = 4;
-  HashPrefixes hash_prefixes = prefix_map.at(prefix_size);
-  EXPECT_EQ(5 * prefix_size, hash_prefixes.size());
-  EXPECT_EQ("----", hash_prefixes.substr(0 * prefix_size, prefix_size));
-  EXPECT_EQ("1111", hash_prefixes.substr(1 * prefix_size, prefix_size));
-  EXPECT_EQ("abcd", hash_prefixes.substr(2 * prefix_size, prefix_size));
-  EXPECT_EQ("bbbb", hash_prefixes.substr(3 * prefix_size, prefix_size));
-  EXPECT_EQ("efgh", hash_prefixes.substr(4 * prefix_size, prefix_size));
-
-  prefix_size = 5;
-  hash_prefixes = prefix_map.at(prefix_size);
-  EXPECT_EQ(4 * prefix_size, hash_prefixes.size());
-  EXPECT_EQ("22222", hash_prefixes.substr(0 * prefix_size, prefix_size));
-  EXPECT_EQ("54321", hash_prefixes.substr(1 * prefix_size, prefix_size));
-  EXPECT_EQ("abcde", hash_prefixes.substr(2 * prefix_size, prefix_size));
-  EXPECT_EQ("bcdef", hash_prefixes.substr(3 * prefix_size, prefix_size));
-}
-
-TEST_F(V4StoreTest, TestMergeUpdatesWithDifferentSizesInEachMap) {
-  HashPrefixMap prefix_map_old;
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            V4Store::AddUnlumpedHashes(4, "1111abcdefgh", &prefix_map_old));
-  HashPrefixMap prefix_map_additions;
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            V4Store::AddUnlumpedHashes(5, "22222bcdef", &prefix_map_additions));
-
-  V4Store store(task_runner_, store_path_);
-  std::string expected_checksum = std::string(
-      "\xA5\x8B\xCAsD\xC7\xF9\xCE\xD2\xF4\x4="
-      "\xB2\"\x82\x1A\xC1\xB8\x1F\x10\r\v\x9A\x93\xFD\xE1\xB8"
-      "B\x1Eh\xF7\xB4",
-      crypto::kSHA256Length);
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            store.MergeUpdate(prefix_map_old, prefix_map_additions, nullptr,
-                              expected_checksum));
-
-  const HashPrefixMap& prefix_map = store.hash_prefix_map_;
-  EXPECT_EQ(2u, prefix_map.size());
-
-  PrefixSize prefix_size = 4;
-  HashPrefixes hash_prefixes = prefix_map.at(prefix_size);
-  EXPECT_EQ(3 * prefix_size, hash_prefixes.size());
-  EXPECT_EQ("1111abcdefgh", hash_prefixes);
-
-  prefix_size = 5;
-  hash_prefixes = prefix_map.at(prefix_size);
-  EXPECT_EQ(2 * prefix_size, hash_prefixes.size());
-  EXPECT_EQ("22222bcdef", hash_prefixes);
-}
-
-TEST_F(V4StoreTest, TestMergeUpdatesOldMapRunsOutFirst) {
-  HashPrefixMap prefix_map_old;
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            V4Store::AddUnlumpedHashes(4, "00001111", &prefix_map_old));
-  HashPrefixMap prefix_map_additions;
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            V4Store::AddUnlumpedHashes(4, "2222", &prefix_map_additions));
-
-  V4Store store(task_runner_, store_path_);
-  std::string expected_checksum = std::string(
-      "\x84\x92\xET\xED\xF7\x97"
-      "C\xCE}\xFF"
-      "E\x1\xAB-\b>\xDB\x95\b\xD8H\xD5\x1D\xF9]8x\xA4\xD4\xC2\xFA",
-      crypto::kSHA256Length);
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            store.MergeUpdate(prefix_map_old, prefix_map_additions, nullptr,
-                              expected_checksum));
-
-  const HashPrefixMap& prefix_map = store.hash_prefix_map_;
-  EXPECT_EQ(1u, prefix_map.size());
-
-  PrefixSize prefix_size = 4;
-  HashPrefixes hash_prefixes = prefix_map.at(prefix_size);
-  EXPECT_EQ(3 * prefix_size, hash_prefixes.size());
-  EXPECT_EQ("0000", hash_prefixes.substr(0 * prefix_size, prefix_size));
-  EXPECT_EQ("1111", hash_prefixes.substr(1 * prefix_size, prefix_size));
-  EXPECT_EQ("2222", hash_prefixes.substr(2 * prefix_size, prefix_size));
-}
-
-TEST_F(V4StoreTest, TestMergeUpdatesAdditionsMapRunsOutFirst) {
-  HashPrefixMap prefix_map_old;
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            V4Store::AddUnlumpedHashes(4, "2222", &prefix_map_old));
-  HashPrefixMap prefix_map_additions;
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            V4Store::AddUnlumpedHashes(4, "00001111", &prefix_map_additions));
-
-  V4Store store(task_runner_, store_path_);
-  std::string expected_checksum = std::string(
-      "\x84\x92\xET\xED\xF7\x97"
-      "C\xCE}\xFF"
-      "E\x1\xAB-\b>\xDB\x95\b\xD8H\xD5\x1D\xF9]8x\xA4\xD4\xC2\xFA",
-      crypto::kSHA256Length);
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            store.MergeUpdate(prefix_map_old, prefix_map_additions, nullptr,
-                              expected_checksum));
-
-  const HashPrefixMap& prefix_map = store.hash_prefix_map_;
-  EXPECT_EQ(1u, prefix_map.size());
-
-  PrefixSize prefix_size = 4;
-  HashPrefixes hash_prefixes = prefix_map.at(prefix_size);
-  EXPECT_EQ(3 * prefix_size, hash_prefixes.size());
-  EXPECT_EQ("0000", hash_prefixes.substr(0 * prefix_size, prefix_size));
-  EXPECT_EQ("1111", hash_prefixes.substr(1 * prefix_size, prefix_size));
-  EXPECT_EQ("2222", hash_prefixes.substr(2 * prefix_size, prefix_size));
-}
-
-TEST_F(V4StoreTest, TestMergeUpdatesFailsForRepeatedHashPrefix) {
-  HashPrefixMap prefix_map_old;
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            V4Store::AddUnlumpedHashes(4, "2222", &prefix_map_old));
-  HashPrefixMap prefix_map_additions;
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            V4Store::AddUnlumpedHashes(4, "2222", &prefix_map_additions));
-
-  V4Store store(task_runner_, store_path_);
-  std::string expected_checksum;
-  EXPECT_EQ(ADDITIONS_HAS_EXISTING_PREFIX_FAILURE,
-            store.MergeUpdate(prefix_map_old, prefix_map_additions, nullptr,
-                              expected_checksum));
-}
-
-TEST_F(V4StoreTest, TestMergeUpdatesFailsWhenRemovalsIndexTooLarge) {
-  HashPrefixMap prefix_map_old;
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            V4Store::AddUnlumpedHashes(4, "2222", &prefix_map_old));
-  HashPrefixMap prefix_map_additions;
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            V4Store::AddUnlumpedHashes(4, "11113333", &prefix_map_additions));
-
-  // Even though the merged map could have size 3 without removals, the
-  // removals index should only count the entries in the old map.
-  V4Store store(task_runner_, store_path_);
-  RepeatedField<int32> raw_removals;
-  // old_store: ["2222"]
-  raw_removals.Add(1);
-  std::string expected_checksum;
-  EXPECT_EQ(REMOVALS_INDEX_TOO_LARGE_FAILURE,
-            store.MergeUpdate(prefix_map_old, prefix_map_additions,
-                              &raw_removals, expected_checksum));
-}
-
-TEST_F(V4StoreTest, TestMergeUpdatesRemovesOnlyElement) {
-  HashPrefixMap prefix_map_old;
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            V4Store::AddUnlumpedHashes(4, "2222", &prefix_map_old));
-  HashPrefixMap prefix_map_additions;
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            V4Store::AddUnlumpedHashes(5, "1111133333", &prefix_map_additions));
-
-  V4Store store(task_runner_, store_path_);
-  RepeatedField<int32> raw_removals;
-  // old_store: ["2222"]
-  raw_removals.Add(0);  // Removes "2222"
-  std::string expected_checksum = std::string(
-      "\xE6\xB0\x1\x12\x89\x83\xF0/"
-      "\xE7\xD2\xE6\xDC\x16\xB9\x8C+\xA2\xB3\x9E\x89<,\x88"
-      "B3\xA5\xB1"
-      "D\x9E\x9E'\x14",
-      crypto::kSHA256Length);
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            store.MergeUpdate(prefix_map_old, prefix_map_additions,
-                              &raw_removals, expected_checksum));
-
-  const HashPrefixMap& prefix_map = store.hash_prefix_map_;
-  // The size is 2 since we reserve space anyway.
-  EXPECT_EQ(2u, prefix_map.size());
-  EXPECT_TRUE(prefix_map.at(4).empty());
-  EXPECT_EQ("1111133333", prefix_map.at(5));
-}
-
-TEST_F(V4StoreTest, TestMergeUpdatesRemovesFirstElement) {
-  HashPrefixMap prefix_map_old;
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            V4Store::AddUnlumpedHashes(4, "22224444", &prefix_map_old));
-  HashPrefixMap prefix_map_additions;
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            V4Store::AddUnlumpedHashes(5, "1111133333", &prefix_map_additions));
-
-  V4Store store(task_runner_, store_path_);
-  RepeatedField<int32> raw_removals;
-  // old_store: ["2222", "4444"]
-  raw_removals.Add(0);  // Removes "2222"
-  std::string expected_checksum = std::string(
-      "\x9D\xF3\xF2\x82\0\x1E{\xDF\xCD\xC0V\xBE\xD6<\x85"
-      "D7=\xB5v\xAD\b1\xC9\xB3"
-      "A\xAC"
-      "b\xF1lf\xA4",
-      crypto::kSHA256Length);
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            store.MergeUpdate(prefix_map_old, prefix_map_additions,
-                              &raw_removals, expected_checksum));
-
-  const HashPrefixMap& prefix_map = store.hash_prefix_map_;
-  // The size is 2 since we reserve space anyway.
-  EXPECT_EQ(2u, prefix_map.size());
-  EXPECT_EQ("4444", prefix_map.at(4));
-  EXPECT_EQ("1111133333", prefix_map.at(5));
-}
-
-TEST_F(V4StoreTest, TestMergeUpdatesRemovesMiddleElement) {
-  HashPrefixMap prefix_map_old;
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            V4Store::AddUnlumpedHashes(4, "222233334444", &prefix_map_old));
-  HashPrefixMap prefix_map_additions;
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            V4Store::AddUnlumpedHashes(5, "1111133333", &prefix_map_additions));
-
-  V4Store store(task_runner_, store_path_);
-  RepeatedField<int32> raw_removals;
-  // old_store: ["2222", "3333", 4444"]
-  raw_removals.Add(1);  // Removes "3333"
-  std::string expected_checksum = std::string(
-      "\xFA-A\x15{\x17\0>\xAE"
-      "8\xACigR\xD1\x93<\xB2\xC9\xB5\x81\xC0\xFB\xBB\x2\f\xAFpN\xEA"
-      "44",
-      crypto::kSHA256Length);
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            store.MergeUpdate(prefix_map_old, prefix_map_additions,
-                              &raw_removals, expected_checksum));
-
-  const HashPrefixMap& prefix_map = store.hash_prefix_map_;
-  // The size is 2 since we reserve space anyway.
-  EXPECT_EQ(2u, prefix_map.size());
-  EXPECT_EQ("22224444", prefix_map.at(4));
-  EXPECT_EQ("1111133333", prefix_map.at(5));
-}
-
-TEST_F(V4StoreTest, TestMergeUpdatesRemovesLastElement) {
-  HashPrefixMap prefix_map_old;
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            V4Store::AddUnlumpedHashes(4, "222233334444", &prefix_map_old));
-  HashPrefixMap prefix_map_additions;
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            V4Store::AddUnlumpedHashes(5, "1111133333", &prefix_map_additions));
-
-  V4Store store(task_runner_, store_path_);
-  RepeatedField<int32> raw_removals;
-  // old_store: ["2222", "3333", 4444"]
-  raw_removals.Add(2);  // Removes "4444"
-  std::string expected_checksum = std::string(
-      "a\xE1\xAD\x96\xFE\xA6"
-      "A\xCA~7W\xF6z\xD8\n\xCA?\x96\x8A\x17U\x5\v\r\x88]\n\xB2JX\xC4S",
-      crypto::kSHA256Length);
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            store.MergeUpdate(prefix_map_old, prefix_map_additions,
-                              &raw_removals, expected_checksum));
-
-  const HashPrefixMap& prefix_map = store.hash_prefix_map_;
-  // The size is 2 since we reserve space anyway.
-  EXPECT_EQ(2u, prefix_map.size());
-  EXPECT_EQ("22223333", prefix_map.at(4));
-  EXPECT_EQ("1111133333", prefix_map.at(5));
-}
-
-TEST_F(V4StoreTest, TestMergeUpdatesRemovesWhenOldHasDifferentSizes) {
-  HashPrefixMap prefix_map_old;
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            V4Store::AddUnlumpedHashes(4, "222233334444", &prefix_map_old));
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            V4Store::AddUnlumpedHashes(5, "aaaaabbbbb", &prefix_map_old));
-  HashPrefixMap prefix_map_additions;
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            V4Store::AddUnlumpedHashes(5, "1111133333", &prefix_map_additions));
-
-  V4Store store(task_runner_, store_path_);
-  RepeatedField<int32> raw_removals;
-  // old_store: ["2222", "3333", 4444", "aaaaa", "bbbbb"]
-  raw_removals.Add(3);  // Removes "aaaaa"
-  std::string expected_checksum = std::string(
-      "\xA7OG\x9D\x83.\x9D-f\x8A\xE\x8B\r&\x19"
-      "6\xE3\xF0\xEFTi\xA7\x5\xEA\xF7"
-      "ej,\xA8\x9D\xAD\x91",
-      crypto::kSHA256Length);
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            store.MergeUpdate(prefix_map_old, prefix_map_additions,
-                              &raw_removals, expected_checksum));
-
-  const HashPrefixMap& prefix_map = store.hash_prefix_map_;
-  // The size is 2 since we reserve space anyway.
-  EXPECT_EQ(2u, prefix_map.size());
-  EXPECT_EQ("222233334444", prefix_map.at(4));
-  EXPECT_EQ("1111133333bbbbb", prefix_map.at(5));
-}
-
-TEST_F(V4StoreTest, TestMergeUpdatesRemovesMultipleAcrossDifferentSizes) {
-  HashPrefixMap prefix_map_old;
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            V4Store::AddUnlumpedHashes(4, "22223333aaaa", &prefix_map_old));
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            V4Store::AddUnlumpedHashes(5, "3333344444bbbbb", &prefix_map_old));
-  HashPrefixMap prefix_map_additions;
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            V4Store::AddUnlumpedHashes(5, "11111", &prefix_map_additions));
-
-  V4Store store(task_runner_, store_path_);
-  RepeatedField<int32> raw_removals;
-  // old_store: ["2222", "3333", "33333", "44444", "aaaa", "bbbbb"]
-  raw_removals.Add(1);  // Removes "3333"
-  raw_removals.Add(3);  // Removes "44444"
-  std::string expected_checksum = std::string(
-      "!D\xB7&L\xA7&G0\x85\xB4"
-      "E\xDD\x10\"\x9A\xCA\xF1"
-      "3^\x83w\xBBL\x19n\xAD\xBDM\x9D"
-      "b\x9F",
-      crypto::kSHA256Length);
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            store.MergeUpdate(prefix_map_old, prefix_map_additions,
-                              &raw_removals, expected_checksum));
-
-  const HashPrefixMap& prefix_map = store.hash_prefix_map_;
-  // The size is 2 since we reserve space anyway.
-  EXPECT_EQ(2u, prefix_map.size());
-  EXPECT_EQ("2222aaaa", prefix_map.at(4));
-  EXPECT_EQ("1111133333bbbbb", prefix_map.at(5));
-}
-
-TEST_F(V4StoreTest, TestReadFullResponseWithValidHashPrefixMap) {
-  V4Store write_store(task_runner_, store_path_);
-  write_store.hash_prefix_map_[4] = "00000abc";
-  write_store.hash_prefix_map_[5] = "00000abcde";
-  write_store.state_ = "test_client_state";
-  EXPECT_FALSE(base::PathExists(write_store.store_path_));
-  EXPECT_EQ(WRITE_SUCCESS, write_store.WriteToDisk(Checksum()));
-  EXPECT_TRUE(base::PathExists(write_store.store_path_));
-
-  V4Store read_store(task_runner_, store_path_);
-  EXPECT_EQ(READ_SUCCESS, read_store.ReadFromDisk());
-  EXPECT_EQ("test_client_state", read_store.state_);
-  ASSERT_EQ(2u, read_store.hash_prefix_map_.size());
-  EXPECT_EQ("00000abc", read_store.hash_prefix_map_[4]);
-  EXPECT_EQ("00000abcde", read_store.hash_prefix_map_[5]);
-  EXPECT_EQ(71, read_store.file_size_);
-}
-
-// This tests fails to read the prefix map from the disk because the file on
-// disk is invalid. The hash prefixes string is 6 bytes long, but the prefix
-// size is 5 so the parser isn't able to split the hash prefixes list
-// completely.
-TEST_F(V4StoreTest, TestReadFullResponseWithInvalidHashPrefixMap) {
-  V4Store write_store(task_runner_, store_path_);
-  write_store.hash_prefix_map_[5] = "abcdef";
-  write_store.state_ = "test_client_state";
-  EXPECT_FALSE(base::PathExists(write_store.store_path_));
-  EXPECT_EQ(WRITE_SUCCESS, write_store.WriteToDisk(Checksum()));
-  EXPECT_TRUE(base::PathExists(write_store.store_path_));
-
-  V4Store read_store(task_runner_, store_path_);
-  EXPECT_EQ(HASH_PREFIX_MAP_GENERATION_FAILURE, read_store.ReadFromDisk());
-  EXPECT_TRUE(read_store.state_.empty());
-  EXPECT_TRUE(read_store.hash_prefix_map_.empty());
-  EXPECT_EQ(0, read_store.file_size_);
-}
-
-TEST_F(V4StoreTest, TestHashPrefixExistsAtTheBeginning) {
-  HashPrefixes hash_prefixes = "abcdebbbbbccccc";
-  HashPrefix hash_prefix = "abcde";
-  EXPECT_TRUE(V4Store::HashPrefixMatches(hash_prefix, hash_prefixes, 5));
-}
-
-TEST_F(V4StoreTest, TestHashPrefixExistsInTheMiddle) {
-  HashPrefixes hash_prefixes = "abcdebbbbbccccc";
-  HashPrefix hash_prefix = "bbbbb";
-  EXPECT_TRUE(V4Store::HashPrefixMatches(hash_prefix, hash_prefixes, 5));
-}
-
-TEST_F(V4StoreTest, TestHashPrefixExistsAtTheEnd) {
-  HashPrefixes hash_prefixes = "abcdebbbbbccccc";
-  HashPrefix hash_prefix = "ccccc";
-  EXPECT_TRUE(V4Store::HashPrefixMatches(hash_prefix, hash_prefixes, 5));
-}
-
-TEST_F(V4StoreTest, TestHashPrefixExistsAtTheBeginningOfEven) {
-  HashPrefixes hash_prefixes = "abcdebbbbb";
-  HashPrefix hash_prefix = "abcde";
-  EXPECT_TRUE(V4Store::HashPrefixMatches(hash_prefix, hash_prefixes, 5));
-}
-
-TEST_F(V4StoreTest, TestHashPrefixExistsAtTheEndOfEven) {
-  HashPrefixes hash_prefixes = "abcdebbbbb";
-  HashPrefix hash_prefix = "bbbbb";
-  EXPECT_TRUE(V4Store::HashPrefixMatches(hash_prefix, hash_prefixes, 5));
-}
-
-TEST_F(V4StoreTest, TestHashPrefixDoesNotExistInConcatenatedList) {
-  HashPrefixes hash_prefixes = "abcdebbbbb";
-  HashPrefix hash_prefix = "bbbbc";
-  EXPECT_FALSE(V4Store::HashPrefixMatches(hash_prefix, hash_prefixes, 5));
-}
-
-TEST_F(V4StoreTest, TestFullHashExistsInMapWithSingleSize) {
-  V4Store store(task_runner_, store_path_);
-  store.hash_prefix_map_[32] =
-      "0111222233334444555566667777888811112222333344445555666677778888";
-  FullHash full_hash = "11112222333344445555666677778888";
-  EXPECT_EQ("11112222333344445555666677778888",
-            store.GetMatchingHashPrefix(full_hash));
-}
-
-TEST_F(V4StoreTest, TestFullHashExistsInMapWithDifferentSizes) {
-  V4Store store(task_runner_, store_path_);
-  store.hash_prefix_map_[4] = "22223333aaaa";
-  store.hash_prefix_map_[32] = "11112222333344445555666677778888";
-  FullHash full_hash = "11112222333344445555666677778888";
-  EXPECT_EQ("11112222333344445555666677778888",
-            store.GetMatchingHashPrefix(full_hash));
-}
-
-TEST_F(V4StoreTest, TestHashPrefixExistsInMapWithSingleSize) {
-  V4Store store(task_runner_, store_path_);
-  store.hash_prefix_map_[4] = "22223333aaaa";
-  FullHash full_hash = "22222222222222222222222222222222";
-  EXPECT_EQ("2222", store.GetMatchingHashPrefix(full_hash));
-}
-
-TEST_F(V4StoreTest, TestHashPrefixExistsInMapWithDifferentSizes) {
-  V4Store store(task_runner_, store_path_);
-  store.hash_prefix_map_[4] = "22223333aaaa";
-  store.hash_prefix_map_[5] = "11111hhhhh";
-  FullHash full_hash = "22222222222222222222222222222222";
-  EXPECT_EQ("2222", store.GetMatchingHashPrefix(full_hash));
-}
-
-TEST_F(V4StoreTest, TestHashPrefixDoesNotExistInMapWithDifferentSizes) {
-  V4Store store(task_runner_, store_path_);
-  store.hash_prefix_map_[4] = "3333aaaa";
-  store.hash_prefix_map_[5] = "11111hhhhh";
-  FullHash full_hash = "22222222222222222222222222222222";
-  EXPECT_TRUE(store.GetMatchingHashPrefix(full_hash).empty());
-}
-
-TEST_F(V4StoreTest, GetMatchingHashPrefixSize32Or21) {
-  HashPrefix prefix = "0123";
-  V4Store store(task_runner_, store_path_);
-  store.hash_prefix_map_[4] = prefix;
-
-  FullHash full_hash_21 = "0123456789ABCDEF01234";
-  EXPECT_EQ(prefix, store.GetMatchingHashPrefix(full_hash_21));
-  FullHash full_hash_32 = "0123456789ABCDEF0123456789ABCDEF";
-  EXPECT_EQ(prefix, store.GetMatchingHashPrefix(full_hash_32));
-#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
-  // This hits a DCHECK so it is release mode only.
-  FullHash full_hash_22 = "0123456789ABCDEF012345";
-  EXPECT_EQ(prefix, store.GetMatchingHashPrefix(full_hash_22));
-#endif
-}
-
-#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
-// This test hits a NOTREACHED so it is a release mode only test.
-TEST_F(V4StoreTest, TestAdditionsWithRiceEncodingFailsWithInvalidInput) {
-  RepeatedPtrField<ThreatEntrySet> additions;
-  ThreatEntrySet* addition = additions.Add();
-  addition->set_compression_type(RICE);
-  addition->mutable_rice_hashes()->set_num_entries(-1);
-  HashPrefixMap additions_map;
-  EXPECT_EQ(RICE_DECODING_FAILURE,
-            V4Store(task_runner_, store_path_)
-                .UpdateHashPrefixMapFromAdditions("V4Metric", additions,
-                                                  &additions_map));
-}
-#endif
-
-TEST_F(V4StoreTest, TestAdditionsWithRiceEncodingSucceeds) {
-  RepeatedPtrField<ThreatEntrySet> additions;
-  ThreatEntrySet* addition = additions.Add();
-  addition->set_compression_type(RICE);
-  RiceDeltaEncoding* rice_hashes = addition->mutable_rice_hashes();
-  rice_hashes->set_first_value(5);
-  rice_hashes->set_num_entries(3);
-  rice_hashes->set_rice_parameter(28);
-  // The following value is hand-crafted by getting inspiration from:
-  // https://goto.google.com/testlargenumbersriceencoded
-  // The value listed at that place fails the "integer overflow" check so I
-  // modified it until the decoder parsed it successfully.
-  rice_hashes->set_encoded_data(
-      "\xbf\xa8\x3f\xfb\xf\xf\x5e\x27\xe6\xc3\x1d\xc6\x38");
-  HashPrefixMap additions_map;
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            V4Store(task_runner_, store_path_)
-                .UpdateHashPrefixMapFromAdditions("V4Metric", additions,
-                                                  &additions_map));
-  EXPECT_EQ(1u, additions_map.size());
-  EXPECT_EQ(std::string("\x5\0\0\0\fL\x93\xADV\x7F\xF6o\xCEo1\x81", 16),
-            additions_map[4]);
-}
-
-TEST_F(V4StoreTest, TestRemovalsWithRiceEncodingSucceeds) {
-  HashPrefixMap prefix_map_old;
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            V4Store::AddUnlumpedHashes(4, "1111abcdefgh", &prefix_map_old));
-  HashPrefixMap prefix_map_additions;
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            V4Store::AddUnlumpedHashes(5, "22222bcdef", &prefix_map_additions));
-
-  V4Store store(task_runner_, store_path_);
-  std::string expected_checksum = std::string(
-      "\xA5\x8B\xCAsD\xC7\xF9\xCE\xD2\xF4\x4="
-      "\xB2\"\x82\x1A\xC1\xB8\x1F\x10\r\v\x9A\x93\xFD\xE1\xB8"
-      "B\x1Eh\xF7\xB4",
-      crypto::kSHA256Length);
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            store.MergeUpdate(prefix_map_old, prefix_map_additions, nullptr,
-                              expected_checksum));
-  EXPECT_FALSE(store.HasValidData());  // Never actually read from disk.
-
-  // At this point, the store map looks like this:
-  // 4: 1111abcdefgh
-  // 5: 22222bcdef
-  // sorted: 1111, 22222, abcd, bcdef, efgh
-  // We'll now try to delete hashes at indexes 0, 3 and 4 in the sorted list.
-
-  std::unique_ptr<ListUpdateResponse> lur(new ListUpdateResponse);
-  lur->set_response_type(ListUpdateResponse::PARTIAL_UPDATE);
-  ThreatEntrySet* removal = lur->add_removals();
-  removal->set_compression_type(RICE);
-  RiceDeltaEncoding* rice_indices = removal->mutable_rice_indices();
-  rice_indices->set_first_value(0);
-  rice_indices->set_num_entries(2);
-  rice_indices->set_rice_parameter(2);
-  rice_indices->set_encoded_data("\x16");
-
-  bool called_back = false;
-  UpdatedStoreReadyCallback store_ready_callback =
-      base::BindOnce(&V4StoreTest::UpdatedStoreReady, base::Unretained(this),
-                     &called_back, true /* expect_store */);
-  EXPECT_FALSE(base::PathExists(store.store_path_));
-  store.ApplyUpdate(std::move(lur), task_runner_,
-                    std::move(store_ready_callback));
-  EXPECT_TRUE(base::PathExists(store.store_path_));
-
-  task_runner_->RunPendingTasks();
-  base::RunLoop().RunUntilIdle();
-
-  // This ensures that the callback was called.
-  EXPECT_TRUE(called_back);
-  // ApplyUpdate was successful, so we have valid data.
-  ASSERT_TRUE(updated_store_);
-  EXPECT_TRUE(updated_store_->HasValidData());
-}
-
-TEST_F(V4StoreTest, TestMergeUpdatesFailsChecksum) {
-  // Proof of checksum mismatch using python:
-  // >>> import hashlib
-  // >>> m = hashlib.sha256()
-  // >>> m.update("2222")
-  // >>> m.digest()
-  // "\xed\xee)\xf8\x82T;\x95f
-  // \xb2m\x0e\xe0\xe7\xe9P9\x9b\x1cB"\xf5\xde\x05\xe0d%\xb4\xc9\x95\xe9"
-
-  HashPrefixMap prefix_map_old;
-  EXPECT_EQ(APPLY_UPDATE_SUCCESS,
-            V4Store::AddUnlumpedHashes(4, "2222", &prefix_map_old));
-  EXPECT_EQ(CHECKSUM_MISMATCH_FAILURE,
-            V4Store(task_runner_, store_path_)
-                .MergeUpdate(prefix_map_old, HashPrefixMap(), nullptr, "aawc"));
-}
-
-TEST_F(V4StoreTest, TestChecksumErrorOnStartup) {
-  // First the case of checksum not matching after reading from disk.
-  ListUpdateResponse list_update_response;
-  list_update_response.set_new_client_state("test_client_state");
-  list_update_response.set_platform_type(LINUX_PLATFORM);
-  list_update_response.set_response_type(ListUpdateResponse::FULL_UPDATE);
-  list_update_response.mutable_checksum()->set_sha256(
-      std::string(crypto::kSHA256Length, 0));
-  WriteFileFormatProtoToFile(0x600D71FE, 9, &list_update_response);
-  V4Store store(task_runner_, store_path_);
-  EXPECT_TRUE(store.expected_checksum_.empty());
-  EXPECT_EQ(READ_SUCCESS, store.ReadFromDisk());
-  EXPECT_TRUE(!store.expected_checksum_.empty());
-  EXPECT_EQ(69, store.file_size_);
-  EXPECT_EQ("test_client_state", store.state());
-
-  EXPECT_FALSE(store.VerifyChecksum());
-
-  // Now the case of checksum matching after reading from disk.
-  // Proof of checksum mismatch using python:
-  // >>> import hashlib
-  // >>> m = hashlib.sha256()
-  // >>> m.update("abcde")
-  // >>> import base64
-  // >>> encoded = base64.b64encode(m.digest())
-  // >>> encoded
-  // 'NrvlDtloQdEEQ7y2cNZVTwo0t2G+Z+ycSorSwMRMpCw='
-  std::string expected_checksum;
-  base::Base64Decode("NrvlDtloQdEEQ7y2cNZVTwo0t2G+Z+ycSorSwMRMpCw=",
-                     &expected_checksum);
-  ThreatEntrySet* additions = list_update_response.add_additions();
-  additions->set_compression_type(RAW);
-  additions->mutable_raw_hashes()->set_prefix_size(5);
-  additions->mutable_raw_hashes()->set_raw_hashes("abcde");
-  list_update_response.mutable_checksum()->set_sha256(expected_checksum);
-  WriteFileFormatProtoToFile(0x600D71FE, 9, &list_update_response);
-  V4Store another_store(task_runner_, store_path_);
-  EXPECT_TRUE(another_store.expected_checksum_.empty());
-
-  EXPECT_EQ(READ_SUCCESS, another_store.ReadFromDisk());
-  EXPECT_TRUE(!another_store.expected_checksum_.empty());
-  EXPECT_EQ("test_client_state", another_store.state());
-  EXPECT_EQ(69, store.file_size_);
-
-  EXPECT_TRUE(another_store.VerifyChecksum());
-}
-
-TEST_F(V4StoreTest, WriteToDiskFails) {
-  // Pass the directory name as file name so that when the code tries to rename
-  // the temp store file to |store_path_| it fails.
-  EXPECT_EQ(UNABLE_TO_RENAME_FAILURE,
-            V4Store(task_runner_, temp_dir_.GetPath()).WriteToDisk(Checksum()));
-
-  // Give a location that isn't writable, even for the tmp file.
-  base::FilePath non_writable_dir =
-      temp_dir_.GetPath()
-          .Append(FILE_PATH_LITERAL("nonexistent_dir"))
-          .Append(FILE_PATH_LITERAL("some.store"));
-  EXPECT_EQ(UNEXPECTED_BYTES_WRITTEN_FAILURE,
-            V4Store(task_runner_, non_writable_dir).WriteToDisk(Checksum()));
-}
-
-TEST_F(V4StoreTest, FullUpdateFailsChecksumSynchronously) {
-  V4Store store(task_runner_, store_path_);
-  bool called_back = false;
-  UpdatedStoreReadyCallback store_ready_callback =
-      base::BindOnce(&V4StoreTest::UpdatedStoreReady, base::Unretained(this),
-                     &called_back, false /* expect_store */);
-  EXPECT_FALSE(base::PathExists(store.store_path_));
-  EXPECT_FALSE(store.HasValidData());  // Never actually read from disk.
-
-  // Now create a response with invalid checksum.
-  std::unique_ptr<ListUpdateResponse> lur(new ListUpdateResponse);
-  lur->set_response_type(ListUpdateResponse::FULL_UPDATE);
-  lur->mutable_checksum()->set_sha256(std::string(crypto::kSHA256Length, 0));
-  store.ApplyUpdate(std::move(lur), task_runner_,
-                    std::move(store_ready_callback));
-  // The update should fail synchronously and not create a store file.
-  EXPECT_FALSE(base::PathExists(store.store_path_));
-
-  // Run everything on the task runner to ensure there are no pending tasks.
-  task_runner_->RunPendingTasks();
-  base::RunLoop().RunUntilIdle();
-
-  // This ensures that the callback was called.
-  EXPECT_TRUE(called_back);
-  // Ensure that the file is still not created.
-  EXPECT_FALSE(base::PathExists(store.store_path_));
-  EXPECT_FALSE(updated_store_);
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/db/v4_test_util.cc b/components/safe_browsing/db/v4_test_util.cc
deleted file mode 100644
index 87aa0096..0000000
--- a/components/safe_browsing/db/v4_test_util.cc
+++ /dev/null
@@ -1,148 +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.
-
-#include "components/safe_browsing/db/v4_test_util.h"
-
-#include <algorithm>
-#include <string>
-#include <utility>
-
-#include "base/strings/strcat.h"
-#include "components/safe_browsing/db/util.h"
-#include "crypto/sha2.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-
-namespace safe_browsing {
-
-namespace {
-
-const char kClient[] = "unittest";
-const char kAppVer[] = "1.0";
-const char kKeyParam[] = "test_key_param";
-
-}  // namespace
-
-V4ProtocolConfig GetTestV4ProtocolConfig(bool disable_auto_update) {
-  return V4ProtocolConfig(kClient, disable_auto_update, kKeyParam, kAppVer);
-}
-
-std::ostream& operator<<(std::ostream& os, const ThreatMetadata& meta) {
-  os << "{threat_pattern_type=" << static_cast<int>(meta.threat_pattern_type)
-     << ", api_permissions=[";
-  for (auto p : meta.api_permissions)
-    os << p << ",";
-  os << "subresource_filter_match=[";
-  for (auto t : meta.subresource_filter_match)
-    os << static_cast<int>(t.first) << ":" << static_cast<int>(t.second) << ",";
-  return os << "], population_id=" << meta.population_id << "}";
-}
-
-TestV4Store::TestV4Store(
-    const scoped_refptr<base::SequencedTaskRunner>& task_runner,
-    const base::FilePath& store_path)
-    : V4Store(task_runner, store_path) {}
-
-TestV4Store::~TestV4Store() = default;
-
-bool TestV4Store::HasValidData() const {
-  return true;
-}
-
-void TestV4Store::MarkPrefixAsBad(HashPrefix prefix) {
-  auto& vec = mock_prefixes_[prefix.size()];
-  vec.insert(std::upper_bound(vec.begin(), vec.end(), prefix), prefix);
-  hash_prefix_map_[prefix.size()] = base::StrCat(vec);
-}
-
-void TestV4Store::SetPrefixes(std::vector<HashPrefix> prefixes,
-                              PrefixSize size) {
-  std::sort(prefixes.begin(), prefixes.end());
-  mock_prefixes_[size] = prefixes;
-  hash_prefix_map_[size] = base::StrCat(prefixes);
-}
-
-TestV4Database::TestV4Database(
-    const scoped_refptr<base::SequencedTaskRunner>& db_task_runner,
-    std::unique_ptr<StoreMap> store_map)
-    : V4Database(db_task_runner, std::move(store_map)) {}
-
-void TestV4Database::MarkPrefixAsBad(ListIdentifier list_id,
-                                     HashPrefix prefix) {
-  V4Store* base_store = store_map_->at(list_id).get();
-  TestV4Store* test_store = static_cast<TestV4Store*>(base_store);
-  test_store->MarkPrefixAsBad(prefix);
-}
-
-TestV4StoreFactory::TestV4StoreFactory() = default;
-
-TestV4StoreFactory::~TestV4StoreFactory() = default;
-
-std::unique_ptr<V4Store> TestV4StoreFactory::CreateV4Store(
-    const scoped_refptr<base::SequencedTaskRunner>& task_runner,
-    const base::FilePath& store_path) {
-  auto new_store = std::make_unique<TestV4Store>(task_runner, store_path);
-  new_store->Initialize();
-  return std::move(new_store);
-}
-
-TestV4DatabaseFactory::TestV4DatabaseFactory() = default;
-
-TestV4DatabaseFactory::~TestV4DatabaseFactory() = default;
-
-std::unique_ptr<V4Database> TestV4DatabaseFactory::Create(
-    const scoped_refptr<base::SequencedTaskRunner>& db_task_runner,
-    std::unique_ptr<StoreMap> store_map) {
-  auto v4_db =
-      std::make_unique<TestV4Database>(db_task_runner, std::move(store_map));
-  v4_db_ = v4_db.get();
-  return std::move(v4_db);
-}
-
-void TestV4DatabaseFactory::MarkPrefixAsBad(ListIdentifier list_id,
-                                            HashPrefix prefix) {
-  v4_db_->MarkPrefixAsBad(list_id, prefix);
-}
-
-TestV4GetHashProtocolManager::TestV4GetHashProtocolManager(
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    const StoresToCheck& stores_to_check,
-    const V4ProtocolConfig& config)
-    : V4GetHashProtocolManager(url_loader_factory, stores_to_check, config) {}
-
-void TestV4GetHashProtocolManager::AddToFullHashCache(FullHashInfo fhi) {
-  full_hash_cache_[fhi.full_hash].full_hash_infos.push_back(fhi);
-}
-
-TestV4GetHashProtocolManagerFactory::TestV4GetHashProtocolManagerFactory() =
-    default;
-
-TestV4GetHashProtocolManagerFactory::~TestV4GetHashProtocolManagerFactory() =
-    default;
-
-std::unique_ptr<V4GetHashProtocolManager>
-TestV4GetHashProtocolManagerFactory::CreateProtocolManager(
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    const StoresToCheck& stores_to_check,
-    const V4ProtocolConfig& config) {
-  auto pm = std::make_unique<TestV4GetHashProtocolManager>(
-      url_loader_factory, stores_to_check, config);
-  pm_ = pm.get();
-  return std::move(pm);
-}
-
-FullHashInfo GetFullHashInfo(const GURL& url, const ListIdentifier& list_id) {
-  return FullHashInfo(V4ProtocolManagerUtil::GetFullHash(url), list_id,
-                      base::Time::Now() + base::TimeDelta::FromMinutes(5));
-}
-
-FullHashInfo GetFullHashInfoWithMetadata(
-    const GURL& url,
-    const ListIdentifier& list_id,
-    const ThreatMetadata& threat_metadata) {
-  FullHashInfo fhi = GetFullHashInfo(url, list_id);
-  fhi.metadata = threat_metadata;
-  return fhi;
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/db/v4_test_util.h b/components/safe_browsing/db/v4_test_util.h
deleted file mode 100644
index 6ca5a00..0000000
--- a/components/safe_browsing/db/v4_test_util.h
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_DB_V4_TEST_UTIL_H_
-#define COMPONENTS_SAFE_BROWSING_DB_V4_TEST_UTIL_H_
-
-// Contains classes and methods useful for tests.
-
-#include <map>
-#include <memory>
-#include <ostream>
-#include <vector>
-
-#include "components/safe_browsing/db/v4_database.h"
-#include "components/safe_browsing/db/v4_get_hash_protocol_manager.h"
-
-namespace safe_browsing {
-
-struct ThreatMetadata;
-struct V4ProtocolConfig;
-
-V4ProtocolConfig GetTestV4ProtocolConfig(bool disable_auto_update = false);
-
-std::ostream& operator<<(std::ostream& os, const ThreatMetadata& meta);
-
-class TestV4Store : public V4Store {
- public:
-  TestV4Store(const scoped_refptr<base::SequencedTaskRunner>& task_runner,
-              const base::FilePath& store_path);
-  ~TestV4Store() override;
-
-  bool HasValidData() const override;
-
-  void MarkPrefixAsBad(HashPrefix prefix);
-
-  // |prefixes| does not need to be sorted.
-  void SetPrefixes(std::vector<HashPrefix> prefixes, PrefixSize size);
-
- private:
-  // Holds mock prefixes from calls to MarkPrefixAsBad / SetPrefixes. Stored as
-  // a vector for simplicity.
-  std::map<PrefixSize, std::vector<HashPrefix>> mock_prefixes_;
-};
-
-class TestV4StoreFactory : public V4StoreFactory {
- public:
-  TestV4StoreFactory();
-  ~TestV4StoreFactory() override;
-
-  std::unique_ptr<V4Store> CreateV4Store(
-      const scoped_refptr<base::SequencedTaskRunner>& task_runner,
-      const base::FilePath& store_path) override;
-};
-
-class TestV4Database : public V4Database {
- public:
-  TestV4Database(const scoped_refptr<base::SequencedTaskRunner>& db_task_runner,
-                 std::unique_ptr<StoreMap> store_map);
-
-  void MarkPrefixAsBad(ListIdentifier list_id, HashPrefix prefix);
-};
-
-class TestV4DatabaseFactory : public V4DatabaseFactory {
- public:
-  TestV4DatabaseFactory();
-  ~TestV4DatabaseFactory() override;
-
-  std::unique_ptr<V4Database> Create(
-      const scoped_refptr<base::SequencedTaskRunner>& db_task_runner,
-      std::unique_ptr<StoreMap> store_map) override;
-
-  void MarkPrefixAsBad(ListIdentifier list_id, HashPrefix prefix);
-
- private:
-  // Owned by V4LocalDatabaseManager. The following usage is expected: each
-  // test in the test fixture instantiates a new SafebrowsingService instance,
-  // which instantiates a new V4LocalDatabaseManager, which instantiates a new
-  // V4Database using this method so use-after-free isn't possible.
-  TestV4Database* v4_db_ = nullptr;
-};
-
-class TestV4GetHashProtocolManager : public V4GetHashProtocolManager {
- public:
-  TestV4GetHashProtocolManager(
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      const StoresToCheck& stores_to_check,
-      const V4ProtocolConfig& config);
-
-  void AddToFullHashCache(FullHashInfo fhi);
-};
-
-class TestV4GetHashProtocolManagerFactory
-    : public V4GetHashProtocolManagerFactory {
- public:
-  TestV4GetHashProtocolManagerFactory();
-  ~TestV4GetHashProtocolManagerFactory() override;
-
-  std::unique_ptr<V4GetHashProtocolManager> CreateProtocolManager(
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      const StoresToCheck& stores_to_check,
-      const V4ProtocolConfig& config) override;
-
-  void AddToFullHashCache(FullHashInfo fhi) { pm_->AddToFullHashCache(fhi); }
-
- private:
-  // Owned by the SafeBrowsingService.
-  TestV4GetHashProtocolManager* pm_ = nullptr;
-};
-
-// Returns FullHashInfo object for the basic host+path pattern for a given URL
-// after canonicalization.
-FullHashInfo GetFullHashInfo(const GURL& url, const ListIdentifier& list_id);
-
-// Returns a FullHashInfo info for the basic host+path pattern for a given URL
-// after canonicalization. Also adds metadata information to the FullHashInfo
-// object.
-FullHashInfo GetFullHashInfoWithMetadata(const GURL& url,
-                                         const ListIdentifier& list_id,
-                                         const ThreatMetadata& threat_metadata);
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_DB_V4_TEST_UTIL_H_
diff --git a/components/safe_browsing/db/v4_update_protocol_manager.cc b/components/safe_browsing/db/v4_update_protocol_manager.cc
deleted file mode 100644
index 2e2058e6..0000000
--- a/components/safe_browsing/db/v4_update_protocol_manager.cc
+++ /dev/null
@@ -1,451 +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.
-
-#include "components/safe_browsing/db/v4_update_protocol_manager.h"
-
-#include <utility>
-
-#include "base/base64url.h"
-#include "base/bind.h"
-#include "base/macros.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/rand_util.h"
-#include "base/timer/timer.h"
-#include "components/safe_browsing/buildflags.h"
-#include "components/safe_browsing/db/safebrowsing.pb.h"
-#include "net/base/load_flags.h"
-#include "net/http/http_response_headers.h"
-#include "net/http/http_status_code.h"
-#include "net/traffic_annotation/network_traffic_annotation.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "services/network/public/cpp/simple_url_loader.h"
-
-using base::Time;
-using base::TimeDelta;
-
-namespace {
-
-// Enumerate parsing failures for histogramming purposes.  DO NOT CHANGE
-// THE ORDERING OF THESE VALUES.
-enum ParseResultType {
-  // Error parsing the protocol buffer from a string.
-  PARSE_FROM_STRING_ERROR = 0,
-
-  // No platform_type set in the response.
-  NO_PLATFORM_TYPE_ERROR = 1,
-
-  // No threat_entry_type set in the response.
-  NO_THREAT_ENTRY_TYPE_ERROR = 2,
-
-  // No threat_type set in the response.
-  NO_THREAT_TYPE_ERROR = 3,
-
-  // No state set in the response for one or more lists.
-  NO_STATE_ERROR = 4,
-
-  // Memory space for histograms is determined by the max.  ALWAYS
-  // ADD NEW VALUES BEFORE THIS ONE.
-  PARSE_RESULT_TYPE_MAX = 5
-};
-
-// Record parsing errors of an update result.
-void RecordParseUpdateResult(ParseResultType result_type) {
-  UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.V4Update.Parse.Result", result_type,
-                            PARSE_RESULT_TYPE_MAX);
-}
-
-void RecordUpdateResult(safe_browsing::V4OperationResult result) {
-  UMA_HISTOGRAM_ENUMERATION(
-      "SafeBrowsing.V4Update.Result", result,
-      safe_browsing::V4OperationResult::OPERATION_RESULT_MAX);
-}
-
-}  // namespace
-
-namespace safe_browsing {
-
-// Minimum time, in seconds, from start up before we must issue an update query.
-static const int kV4TimerStartIntervalSecMin = 60;
-
-// Maximum time, in seconds, from start up before we must issue an update query.
-static const int kV4TimerStartIntervalSecMax = 300;
-
-// Maximum time, in seconds, to wait for a response to an update request.
-static const int kV4TimerUpdateWaitSecMax = 15 * 60;  // 15 minutes
-
-ChromeClientInfo::SafeBrowsingReportingPopulation GetReportingLevelProtoValue(
-    ExtendedReportingLevel reporting_level) {
-  switch (reporting_level) {
-    case SBER_LEVEL_OFF:
-      return ChromeClientInfo::OPT_OUT;
-    case SBER_LEVEL_LEGACY:
-      return ChromeClientInfo::EXTENDED;
-    case SBER_LEVEL_SCOUT:
-      return ChromeClientInfo::SCOUT;
-    default:
-      NOTREACHED() << "Unexpected reporting_level!";
-      return ChromeClientInfo::UNSPECIFIED;
-  }
-}
-
-// The default V4UpdateProtocolManagerFactory.
-class V4UpdateProtocolManagerFactoryImpl
-    : public V4UpdateProtocolManagerFactory {
- public:
-  V4UpdateProtocolManagerFactoryImpl() {}
-  ~V4UpdateProtocolManagerFactoryImpl() override {}
-  std::unique_ptr<V4UpdateProtocolManager> CreateProtocolManager(
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      const V4ProtocolConfig& config,
-      V4UpdateCallback update_callback,
-      ExtendedReportingLevelCallback extended_reporting_level_callback)
-      override {
-    return std::unique_ptr<V4UpdateProtocolManager>(
-        new V4UpdateProtocolManager(url_loader_factory, config, update_callback,
-                                    extended_reporting_level_callback));
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(V4UpdateProtocolManagerFactoryImpl);
-};
-
-// V4UpdateProtocolManager implementation --------------------------------
-
-// static
-V4UpdateProtocolManagerFactory* V4UpdateProtocolManager::factory_ = nullptr;
-
-// static
-std::unique_ptr<V4UpdateProtocolManager> V4UpdateProtocolManager::Create(
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    const V4ProtocolConfig& config,
-    V4UpdateCallback update_callback,
-    ExtendedReportingLevelCallback extended_reporting_level_callback) {
-  if (!factory_) {
-    factory_ = new V4UpdateProtocolManagerFactoryImpl();
-  }
-  return factory_->CreateProtocolManager(url_loader_factory, config,
-                                         update_callback,
-                                         extended_reporting_level_callback);
-}
-
-void V4UpdateProtocolManager::ResetUpdateErrors() {
-  update_error_count_ = 0;
-  update_back_off_mult_ = 1;
-}
-
-V4UpdateProtocolManager::V4UpdateProtocolManager(
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    const V4ProtocolConfig& config,
-    V4UpdateCallback update_callback,
-    ExtendedReportingLevelCallback extended_reporting_level_callback)
-    : update_error_count_(0),
-      update_back_off_mult_(1),
-      next_update_interval_(base::TimeDelta::FromSeconds(
-          base::RandInt(kV4TimerStartIntervalSecMin,
-                        kV4TimerStartIntervalSecMax))),
-      config_(config),
-      url_loader_factory_(url_loader_factory),
-      update_callback_(update_callback),
-      extended_reporting_level_callback_(extended_reporting_level_callback) {
-  // Do not auto-schedule updates. Let the owner (V4LocalDatabaseManager) do it
-  // when it is ready to process updates.
-}
-
-V4UpdateProtocolManager::~V4UpdateProtocolManager() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-}
-
-bool V4UpdateProtocolManager::IsUpdateScheduled() const {
-  return update_timer_.IsRunning();
-}
-
-void V4UpdateProtocolManager::ScheduleNextUpdate(
-    std::unique_ptr<StoreStateMap> store_state_map) {
-  store_state_map_ = std::move(store_state_map);
-  ScheduleNextUpdateWithBackoff(false);
-}
-
-void V4UpdateProtocolManager::ScheduleNextUpdateWithBackoff(bool back_off) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  if (config_.disable_auto_update) {
-    DCHECK(!IsUpdateScheduled());
-    return;
-  }
-
-  // Reschedule with the new update.
-  base::TimeDelta next_update_interval = GetNextUpdateInterval(back_off);
-  ScheduleNextUpdateAfterInterval(next_update_interval);
-}
-
-// According to section 5 of the SafeBrowsing protocol specification, we must
-// back off after a certain number of errors.
-base::TimeDelta V4UpdateProtocolManager::GetNextUpdateInterval(bool back_off) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(next_update_interval_ > base::TimeDelta());
-
-  base::TimeDelta next = next_update_interval_;
-  if (back_off) {
-    next = V4ProtocolManagerUtil::GetNextBackOffInterval(
-        &update_error_count_, &update_back_off_mult_);
-  }
-
-  if (!last_response_time_.is_null()) {
-    // The callback spent some time updating the database, including disk I/O.
-    // Do not wait that extra time.
-    base::TimeDelta callback_time = Time::Now() - last_response_time_;
-    if (callback_time < next) {
-      next -= callback_time;
-    } else {
-      // If the callback took too long, schedule the next update with no delay.
-      next = base::TimeDelta();
-    }
-  }
-  DVLOG(1) << "V4UpdateProtocolManager::GetNextUpdateInterval: "
-           << "next_interval: " << next;
-  return next;
-}
-
-void V4UpdateProtocolManager::ScheduleNextUpdateAfterInterval(
-    base::TimeDelta interval) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(interval >= base::TimeDelta());
-
-  // Unschedule any current timer.
-  update_timer_.Stop();
-  update_timer_.Start(FROM_HERE, interval, this,
-                      &V4UpdateProtocolManager::IssueUpdateRequest);
-}
-
-std::string V4UpdateProtocolManager::GetBase64SerializedUpdateRequestProto() {
-  DCHECK(!store_state_map_->empty());
-  // Build the request. Client info and client states are not added to the
-  // request protocol buffer. Client info is passed as params in the url.
-  FetchThreatListUpdatesRequest request;
-  for (const auto& entry : *store_state_map_) {
-    const auto& list_to_update = entry.first;
-    const auto& state = entry.second;
-    ListUpdateRequest* list_update_request = request.add_list_update_requests();
-    list_update_request->set_platform_type(list_to_update.platform_type());
-    list_update_request->set_threat_entry_type(
-        list_to_update.threat_entry_type());
-    list_update_request->set_threat_type(list_to_update.threat_type());
-
-    if (!state.empty()) {
-      list_update_request->set_state(state);
-    }
-
-    list_update_request->mutable_constraints()->add_supported_compressions(RAW);
-    list_update_request->mutable_constraints()->add_supported_compressions(
-        RICE);
-  }
-
-  if (!extended_reporting_level_callback_.is_null()) {
-    request.mutable_chrome_client_info()->set_reporting_population(
-        GetReportingLevelProtoValue(extended_reporting_level_callback_.Run()));
-  }
-
-  V4ProtocolManagerUtil::SetClientInfoFromConfig(request.mutable_client(),
-                                                 config_);
-
-  // Serialize and Base64 encode.
-  std::string req_data, req_base64;
-  request.SerializeToString(&req_data);
-  base::Base64UrlEncode(req_data, base::Base64UrlEncodePolicy::INCLUDE_PADDING,
-                        &req_base64);
-  return req_base64;
-}
-
-bool V4UpdateProtocolManager::ParseUpdateResponse(
-    const std::string& data,
-    ParsedServerResponse* parsed_server_response) {
-  FetchThreatListUpdatesResponse response;
-
-  if (!response.ParseFromString(data)) {
-    RecordParseUpdateResult(PARSE_FROM_STRING_ERROR);
-    return false;
-  }
-
-  if (response.has_minimum_wait_duration()) {
-    // Seconds resolution is good enough so we ignore the nanos field.
-    int64_t minimum_wait_duration_seconds =
-        response.minimum_wait_duration().seconds();
-
-    // Do not let the next_update_interval_ to be too low.
-    if (minimum_wait_duration_seconds < kV4TimerStartIntervalSecMin) {
-      minimum_wait_duration_seconds = kV4TimerStartIntervalSecMin;
-    }
-    next_update_interval_ =
-        base::TimeDelta::FromSeconds(minimum_wait_duration_seconds);
-  }
-
-  for (ListUpdateResponse& list_update_response :
-       *response.mutable_list_update_responses()) {
-    if (!list_update_response.has_platform_type()) {
-      RecordParseUpdateResult(NO_PLATFORM_TYPE_ERROR);
-    } else if (!list_update_response.has_threat_entry_type()) {
-      RecordParseUpdateResult(NO_THREAT_ENTRY_TYPE_ERROR);
-    } else if (!list_update_response.has_threat_type()) {
-      RecordParseUpdateResult(NO_THREAT_TYPE_ERROR);
-    } else if (!list_update_response.has_new_client_state()) {
-      RecordParseUpdateResult(NO_STATE_ERROR);
-    } else {
-      std::unique_ptr<ListUpdateResponse> add(new ListUpdateResponse);
-      add->Swap(&list_update_response);
-      parsed_server_response->push_back(std::move(add));
-    }
-  }
-  return true;
-}
-
-void V4UpdateProtocolManager::IssueUpdateRequest() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  // If an update request is already pending, record and return silently.
-  if (request_) {
-    RecordUpdateResult(V4OperationResult::ALREADY_PENDING_ERROR);
-    return;
-  }
-
-  net::NetworkTrafficAnnotationTag traffic_annotation =
-      net::DefineNetworkTrafficAnnotation("safe_browsing_v4_update", R"(
-        semantics {
-          sender: "Safe Browsing"
-          description:
-            "Safe Browsing issues a request to Google every 30 minutes or so "
-            "to get the latest database of hashes of bad URLs."
-          trigger:
-            "On a timer, approximately every 30 minutes."
-          data:
-             "The state of the local DB is sent so the server can send just "
-             "the changes. This doesn't include any user data."
-          destination: GOOGLE_OWNED_SERVICE
-        }
-        policy {
-          cookies_allowed: YES
-          cookies_store: "Safe Browsing cookie store"
-          setting:
-            "Users can disable Safe Browsing by unchecking 'Protect you and "
-            "your device from dangerous sites' in Chromium settings under "
-            "Privacy. The feature is enabled by default."
-          chrome_policy {
-            SafeBrowsingEnabled {
-              policy_options {mode: MANDATORY}
-              SafeBrowsingEnabled: false
-            }
-          }
-        })");
-  auto resource_request = std::make_unique<network::ResourceRequest>();
-  std::string req_base64 = GetBase64SerializedUpdateRequestProto();
-  GetUpdateUrlAndHeaders(req_base64, &resource_request->url,
-                         &resource_request->headers);
-  resource_request->load_flags = net::LOAD_DISABLE_CACHE;
-  std::unique_ptr<network::SimpleURLLoader> loader =
-      network::SimpleURLLoader::Create(std::move(resource_request),
-                                       traffic_annotation);
-  loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
-      url_loader_factory_.get(),
-      base::BindOnce(&V4UpdateProtocolManager::OnURLLoaderComplete,
-                     base::Unretained(this)));
-
-  request_ = std::move(loader);
-
-  // Begin the update request timeout.
-  timeout_timer_.Start(FROM_HERE,
-                       TimeDelta::FromSeconds(kV4TimerUpdateWaitSecMax), this,
-                       &V4UpdateProtocolManager::HandleTimeout);
-}
-
-void V4UpdateProtocolManager::HandleTimeout() {
-  request_.reset();
-  ScheduleNextUpdateWithBackoff(true);
-}
-
-// SafeBrowsing request responses are handled here.
-void V4UpdateProtocolManager::OnURLLoaderComplete(
-    std::unique_ptr<std::string> response_body) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  int response_code = 0;
-  if (request_->ResponseInfo() && request_->ResponseInfo()->headers)
-    response_code = request_->ResponseInfo()->headers->response_code();
-
-  std::string data;
-  if (response_body)
-    data = *response_body;
-
-  OnURLLoaderCompleteInternal(request_->NetError(), response_code, data);
-}
-
-void V4UpdateProtocolManager::OnURLLoaderCompleteInternal(
-    int net_error,
-    int response_code,
-    const std::string& data) {
-  timeout_timer_.Stop();
-
-  last_response_code_ = response_code;
-  V4ProtocolManagerUtil::RecordHttpResponseOrErrorCode(
-      "SafeBrowsing.V4Update.Network.Result", net_error, last_response_code_);
-
-  last_response_time_ = Time::Now();
-
-  std::unique_ptr<ParsedServerResponse> parsed_server_response(
-      new ParsedServerResponse);
-  if (net_error == net::OK && last_response_code_ == net::HTTP_OK) {
-    RecordUpdateResult(V4OperationResult::STATUS_200);
-    ResetUpdateErrors();
-    if (!ParseUpdateResponse(data, parsed_server_response.get())) {
-      parsed_server_response->clear();
-      RecordUpdateResult(V4OperationResult::PARSE_ERROR);
-    }
-    request_.reset();
-
-    UMA_HISTOGRAM_COUNTS_1M("SafeBrowsing.V4Update.ResponseSizeKB",
-                            data.size() / 1024);
-
-    // The caller should update its state now, based on parsed_server_response.
-    // The callback must call ScheduleNextUpdate() at the end to resume
-    // downloading updates.
-    update_callback_.Run(std::move(parsed_server_response));
-  } else {
-    DVLOG(1) << "SafeBrowsing GetEncodedUpdates request for: "
-             << request_->GetFinalURL() << " failed with error: " << net_error
-             << " and response code: " << last_response_code_;
-
-    if (net_error != net::OK) {
-      RecordUpdateResult(V4OperationResult::NETWORK_ERROR);
-    } else {
-      RecordUpdateResult(V4OperationResult::HTTP_ERROR);
-    }
-    // TODO(vakh): Figure out whether it is just a network error vs backoff vs
-    // another condition and RecordUpdateResult more accurately.
-
-    request_.reset();
-    ScheduleNextUpdateWithBackoff(true);
-  }
-}
-
-void V4UpdateProtocolManager::GetUpdateUrlAndHeaders(
-    const std::string& req_base64,
-    GURL* gurl,
-    net::HttpRequestHeaders* headers) const {
-  V4ProtocolManagerUtil::GetRequestUrlAndHeaders(
-      req_base64, "threatListUpdates:fetch", config_, gurl, headers);
-}
-
-void V4UpdateProtocolManager::CollectUpdateInfo(
-    DatabaseManagerInfo::UpdateInfo* update_info) {
-  if (last_response_code_)
-    update_info->set_network_status_code(last_response_code_);
-
-  if (last_response_time_.ToJavaTime()) {
-    update_info->set_last_update_time_millis(last_response_time_.ToJavaTime());
-
-    // We should only find the next update if the last_response is valid.
-    base::Time next_update = last_response_time_ + next_update_interval_;
-    if (next_update.ToJavaTime())
-      update_info->set_next_update_time_millis(next_update.ToJavaTime());
-  }
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/db/v4_update_protocol_manager.h b/components/safe_browsing/db/v4_update_protocol_manager.h
deleted file mode 100644
index 0657859..0000000
--- a/components/safe_browsing/db/v4_update_protocol_manager.h
+++ /dev/null
@@ -1,223 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_DB_V4_UPDATE_PROTOCOL_MANAGER_H_
-#define COMPONENTS_SAFE_BROWSING_DB_V4_UPDATE_PROTOCOL_MANAGER_H_
-
-// A class that implements Chrome's interface with the SafeBrowsing V4 update
-// protocol.
-//
-// The V4UpdateProtocolManager handles formatting and making requests of, and
-// handling responses from, Google's SafeBrowsing servers. The purpose of this
-// class is to get hash prefixes from the SB server for the given set of lists.
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
-#include "base/sequence_checker.h"
-#include "base/time/time.h"
-#include "base/timer/timer.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/db/safebrowsing.pb.h"
-#include "components/safe_browsing/db/util.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
-#include "components/safe_browsing/proto/webui.pb.h"
-
-class GURL;
-
-namespace network {
-class SimpleURLLoader;
-class SharedURLLoaderFactory;
-}  // namespace network
-
-namespace safe_browsing {
-
-class V4UpdateProtocolManagerFactory;
-
-// V4UpdateCallback is invoked when a scheduled update completes.
-// Parameters:
-//   - The vector of update response protobufs received from the server for
-//     each list type.
-using V4UpdateCallback =
-    base::RepeatingCallback<void(std::unique_ptr<ParsedServerResponse>)>;
-
-using ExtendedReportingLevelCallback =
-    base::RepeatingCallback<ExtendedReportingLevel()>;
-
-class V4UpdateProtocolManager {
- public:
-  ~V4UpdateProtocolManager();
-
-  // Makes the passed |factory| the factory used to instantiate
-  // a V4UpdateProtocolManager. Useful for tests.
-  static void RegisterFactory(V4UpdateProtocolManagerFactory* factory) {
-    factory_ = factory;
-  }
-
-  // Create an instance of the safe browsing v4 protocol manager.
-  static std::unique_ptr<V4UpdateProtocolManager> Create(
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      const V4ProtocolConfig& config,
-      V4UpdateCallback update_callback,
-      ExtendedReportingLevelCallback extended_reporting_level_callback);
-
-  void OnURLLoaderComplete(std::unique_ptr<std::string> response_body);
-
-  // Schedule the next update without backoff.
-  void ScheduleNextUpdate(std::unique_ptr<StoreStateMap> store_state_map);
-
-  // Populates the UpdateInfo message.
-  void CollectUpdateInfo(DatabaseManagerInfo::UpdateInfo* database_info);
-
- protected:
-  // Constructs a V4UpdateProtocolManager that issues network requests using
-  // |url_loader_factory|. It schedules updates to get the hash prefixes for
-  // SafeBrowsing lists, and invoke |callback| when the results are retrieved.
-  // The callback may be invoked synchronously.
-  V4UpdateProtocolManager(
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      const V4ProtocolConfig& config,
-      V4UpdateCallback update_callback,
-      ExtendedReportingLevelCallback extended_reporting_level_callback);
-
- private:
-  FRIEND_TEST_ALL_PREFIXES(V4UpdateProtocolManagerTest,
-                           TestGetUpdatesErrorHandlingNetwork);
-  FRIEND_TEST_ALL_PREFIXES(V4UpdateProtocolManagerTest,
-                           TestGetUpdatesErrorHandlingResponseCode);
-  FRIEND_TEST_ALL_PREFIXES(V4UpdateProtocolManagerTest, TestGetUpdatesNoError);
-  FRIEND_TEST_ALL_PREFIXES(V4UpdateProtocolManagerTest,
-                           TestGetUpdatesWithOneBackoff);
-  FRIEND_TEST_ALL_PREFIXES(V4UpdateProtocolManagerTest,
-                           TestBase64EncodingUsesUrlEncoding);
-  FRIEND_TEST_ALL_PREFIXES(V4UpdateProtocolManagerTest, TestDisableAutoUpdates);
-  FRIEND_TEST_ALL_PREFIXES(V4UpdateProtocolManagerTest,
-                           TestGetUpdatesHasTimeout);
-  FRIEND_TEST_ALL_PREFIXES(V4UpdateProtocolManagerTest,
-                           TestExtendedReportingLevelIncluded);
-  friend class V4UpdateProtocolManagerFactoryImpl;
-
-  void OnURLLoaderCompleteInternal(int net_error,
-                                   int response_code,
-                                   const std::string& data);
-
-  // Fills a FetchThreatListUpdatesRequest protocol buffer for a request.
-  // Returns the serialized and base64 URL encoded request as a string.
-  std::string GetBase64SerializedUpdateRequestProto();
-
-  // Records the network response code of the last update
-  int last_response_code_ = 0;
-
-  // The method to populate |gurl| with the URL to be sent to the server.
-  // |request_base64| is the base64 encoded form of an instance of the protobuf
-  // FetchThreatListUpdatesRequest. Also sets the appropriate header values for
-  // sending PVer4 requests in |headers|.
-  void GetUpdateUrlAndHeaders(const std::string& request_base64,
-                              GURL* gurl,
-                              net::HttpRequestHeaders* headers) const;
-
-  // Parses the base64 encoded response received from the server as a
-  // FetchThreatListUpdatesResponse protobuf and returns each of the
-  // ListUpdateResponse protobufs contained in it as a vector.
-  // Returns true if parsing is successful, false otherwise.
-  bool ParseUpdateResponse(const std::string& data_base64,
-                           ParsedServerResponse* parsed_server_response);
-
-  // Resets the update error counter and multiplier.
-  void ResetUpdateErrors();
-
-  // Called when update request times out. Cancels the existing request and
-  // re-sends the update request.
-  void HandleTimeout();
-
-  // Updates internal update and backoff state for each update response error,
-  // assuming that the current time is |now|.
-  void HandleUpdateError(const base::Time& now);
-
-  // Generates the URL for the update request and issues the request for the
-  // lists passed to the constructor.
-  void IssueUpdateRequest();
-
-  // Returns whether another update is currently scheduled.
-  bool IsUpdateScheduled() const;
-
-  // Schedule the next update with backoff specified.
-  void ScheduleNextUpdateWithBackoff(bool back_off);
-
-  // Schedule the next update, after the given interval.
-  void ScheduleNextUpdateAfterInterval(base::TimeDelta interval);
-
-  // Get the next update interval, considering whether we are in backoff.
-  base::TimeDelta GetNextUpdateInterval(bool back_off);
-
-  // The factory that controls the creation of V4UpdateProtocolManager.
-  // This is used by tests.
-  static V4UpdateProtocolManagerFactory* factory_;
-
-  // The last known state of the lists.
-  // Updated after every successful update of the database.
-  std::unique_ptr<StoreStateMap> store_state_map_;
-
-  // The number of HTTP response errors since the the last successful HTTP
-  // response, used for request backoff timing.
-  size_t update_error_count_;
-
-  // Multiplier for the backoff error after the second.
-  size_t update_back_off_mult_;
-
-  // The time delta after which the next update request may be sent.
-  // It is set to a random interval between 60 and 300 seconds at start.
-  // The server can set it by setting the minimum_wait_duration.
-  base::TimeDelta next_update_interval_;
-
-  // The config of the client making Pver4 requests.
-  const V4ProtocolConfig config_;
-
-  // The URLLoaderFactory we use to issue network requests.
-  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
-
-  // The callback that's called when GetUpdates completes.
-  V4UpdateCallback update_callback_;
-
-  // The pending update request. The request must be canceled when the object is
-  // destroyed.
-  std::unique_ptr<network::SimpleURLLoader> request_;
-
-  // Timer to setup the next update request.
-  base::OneShotTimer update_timer_;
-
-  base::Time last_response_time_;
-
-  // Used to interrupt and re-schedule update requests that take too long to
-  // complete.
-  base::OneShotTimer timeout_timer_;
-
-  ExtendedReportingLevelCallback extended_reporting_level_callback_;
-
-  SEQUENCE_CHECKER(sequence_checker_);
-
-  DISALLOW_COPY_AND_ASSIGN(V4UpdateProtocolManager);
-};
-
-// Interface of a factory to create V4UpdateProtocolManager.  Useful for tests.
-class V4UpdateProtocolManagerFactory {
- public:
-  V4UpdateProtocolManagerFactory() {}
-  virtual ~V4UpdateProtocolManagerFactory() {}
-  virtual std::unique_ptr<V4UpdateProtocolManager> CreateProtocolManager(
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      const V4ProtocolConfig& config,
-      V4UpdateCallback update_callback,
-      ExtendedReportingLevelCallback extended_reporting_level_callback) = 0;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(V4UpdateProtocolManagerFactory);
-};
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_DB_V4_UPDATE_PROTOCOL_MANAGER_H_
diff --git a/components/safe_browsing/db/v4_update_protocol_manager_unittest.cc b/components/safe_browsing/db/v4_update_protocol_manager_unittest.cc
deleted file mode 100644
index ee9412c..0000000
--- a/components/safe_browsing/db/v4_update_protocol_manager_unittest.cc
+++ /dev/null
@@ -1,389 +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.
-
-#include "components/safe_browsing/db/v4_update_protocol_manager.h"
-
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "base/base64.h"
-#include "base/bind.h"
-#include "base/strings/stringprintf.h"
-#include "base/test/test_simple_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/time/time.h"
-#include "components/safe_browsing/buildflags.h"
-#include "components/safe_browsing/db/safebrowsing.pb.h"
-#include "components/safe_browsing/db/util.h"
-#include "components/safe_browsing/db/v4_test_util.h"
-#include "net/base/escape.h"
-#include "net/base/load_flags.h"
-#include "net/base/net_errors.h"
-#include "net/http/http_status_code.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
-#include "services/network/test/test_url_loader_factory.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/platform_test.h"
-
-#if !BUILDFLAG(FULL_SAFE_BROWSING)
-#include "base/system/sys_info.h"
-#endif
-
-using base::Time;
-using base::TimeDelta;
-
-namespace safe_browsing {
-
-class V4UpdateProtocolManagerTest : public PlatformTest {
-  void SetUp() override {
-    PlatformTest::SetUp();
-
-    SetupStoreStates();
-    test_shared_loader_factory_ =
-        base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
-            &test_url_loader_factory_);
-  }
-
- protected:
-  void ValidateGetUpdatesResults(
-      const std::vector<ListUpdateResponse>& expected_lurs,
-      std::unique_ptr<ParsedServerResponse> parsed_server_response) {
-    // The callback should never be called if expect_callback_to_be_called_ is
-    // false.
-    EXPECT_TRUE(expect_callback_to_be_called_);
-    ASSERT_EQ(expected_lurs.size(), parsed_server_response->size());
-
-    for (unsigned int i = 0; i < parsed_server_response->size(); ++i) {
-      const ListUpdateResponse& expected = expected_lurs[i];
-      const std::unique_ptr<ListUpdateResponse>& actual =
-          (*parsed_server_response)[i];
-      EXPECT_EQ(expected.platform_type(), actual->platform_type());
-      EXPECT_EQ(expected.response_type(), actual->response_type());
-      EXPECT_EQ(expected.threat_entry_type(), actual->threat_entry_type());
-      EXPECT_EQ(expected.threat_type(), actual->threat_type());
-      EXPECT_EQ(expected.new_client_state(), actual->new_client_state());
-
-      // TODO(vakh): Test more fields from the proto.
-    }
-  }
-
-  ExtendedReportingLevel GetExtendedReportingLevel(ExtendedReportingLevel erl) {
-    return erl;
-  }
-
-  std::unique_ptr<V4UpdateProtocolManager> CreateProtocolManager(
-      const std::vector<ListUpdateResponse>& expected_lurs,
-      bool disable_auto_update = false,
-      ExtendedReportingLevel erl = SBER_LEVEL_OFF) {
-    return V4UpdateProtocolManager::Create(
-        test_shared_loader_factory_,
-        GetTestV4ProtocolConfig(disable_auto_update),
-        base::BindRepeating(
-            &V4UpdateProtocolManagerTest::ValidateGetUpdatesResults,
-            base::Unretained(this), expected_lurs),
-        base::BindRepeating(
-            &V4UpdateProtocolManagerTest::GetExtendedReportingLevel,
-            base::Unretained(this), erl));
-  }
-
-  void SetupStoreStates() {
-    store_state_map_ = std::make_unique<StoreStateMap>();
-
-    ListIdentifier win_url_malware(WINDOWS_PLATFORM, URL, MALWARE_THREAT);
-    store_state_map_->insert({win_url_malware, "initial_state_1"});
-
-    ListIdentifier win_url_uws(WINDOWS_PLATFORM, URL, UNWANTED_SOFTWARE);
-    store_state_map_->insert({win_url_uws, "initial_state_2"});
-
-    ListIdentifier win_exe_uws(WINDOWS_PLATFORM, EXECUTABLE, UNWANTED_SOFTWARE);
-    store_state_map_->insert({win_exe_uws, "initial_state_3"});
-  }
-
-  void SetupExpectedListUpdateResponse(
-      std::vector<ListUpdateResponse>* expected_lurs) {
-    ListUpdateResponse lur;
-    lur.set_platform_type(WINDOWS_PLATFORM);
-    lur.set_response_type(ListUpdateResponse::PARTIAL_UPDATE);
-    lur.set_threat_entry_type(URL);
-    lur.set_threat_type(MALWARE_THREAT);
-    lur.set_new_client_state("new_state_1");
-    expected_lurs->push_back(lur);
-
-    lur.set_platform_type(WINDOWS_PLATFORM);
-    lur.set_response_type(ListUpdateResponse::PARTIAL_UPDATE);
-    lur.set_threat_entry_type(URL);
-    lur.set_threat_type(UNWANTED_SOFTWARE);
-    lur.set_new_client_state("new_state_2");
-    expected_lurs->push_back(lur);
-
-    lur.set_platform_type(WINDOWS_PLATFORM);
-    lur.set_response_type(ListUpdateResponse::FULL_UPDATE);
-    lur.set_threat_entry_type(EXECUTABLE);
-    lur.set_threat_type(MALWARE_THREAT);
-    lur.set_new_client_state("new_state_3");
-    expected_lurs->push_back(lur);
-  }
-
-  std::string GetExpectedV4UpdateResponse(
-      const std::vector<ListUpdateResponse>& expected_lurs) const {
-    FetchThreatListUpdatesResponse response;
-
-    for (const auto& expected_lur : expected_lurs) {
-      ListUpdateResponse* lur = response.add_list_update_responses();
-      lur->set_new_client_state(expected_lur.new_client_state());
-      lur->set_platform_type(expected_lur.platform_type());
-      lur->set_response_type(expected_lur.response_type());
-      lur->set_threat_entry_type(expected_lur.threat_entry_type());
-      lur->set_threat_type(expected_lur.threat_type());
-    }
-
-    // Serialize.
-    std::string res_data;
-    response.SerializeToString(&res_data);
-
-    return res_data;
-  }
-
-  bool expect_callback_to_be_called_;
-  std::unique_ptr<StoreStateMap> store_state_map_;
-  network::TestURLLoaderFactory test_url_loader_factory_;
-  scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
-};
-
-TEST_F(V4UpdateProtocolManagerTest, TestGetUpdatesErrorHandlingNetwork) {
-  scoped_refptr<base::TestSimpleTaskRunner> runner(
-      new base::TestSimpleTaskRunner());
-  base::ThreadTaskRunnerHandle runner_handler(runner);
-  const std::vector<ListUpdateResponse> expected_lurs;
-  std::unique_ptr<V4UpdateProtocolManager> pm(
-      CreateProtocolManager(expected_lurs));
-  runner->ClearPendingTasks();
-
-  // Initial state. No errors.
-  EXPECT_EQ(0ul, pm->update_error_count_);
-  EXPECT_EQ(1ul, pm->update_back_off_mult_);
-  expect_callback_to_be_called_ = false;
-  pm->store_state_map_ = std::move(store_state_map_);
-  pm->IssueUpdateRequest();
-
-  EXPECT_FALSE(pm->IsUpdateScheduled());
-
-  // Failed request status should result in error.
-  pm->OnURLLoaderCompleteInternal(net::ERR_CONNECTION_RESET, 0, std::string());
-
-  // Should have recorded one error, but back off multiplier is unchanged.
-  EXPECT_EQ(1ul, pm->update_error_count_);
-  EXPECT_EQ(1ul, pm->update_back_off_mult_);
-  EXPECT_TRUE(pm->IsUpdateScheduled());
-}
-
-TEST_F(V4UpdateProtocolManagerTest, TestGetUpdatesErrorHandlingResponseCode) {
-  scoped_refptr<base::TestSimpleTaskRunner> runner(
-      new base::TestSimpleTaskRunner());
-  base::ThreadTaskRunnerHandle runner_handler(runner);
-  const std::vector<ListUpdateResponse> expected_lurs;
-  std::unique_ptr<V4UpdateProtocolManager> pm(
-      CreateProtocolManager(expected_lurs));
-  runner->ClearPendingTasks();
-
-  // Initial state. No errors.
-  EXPECT_EQ(0ul, pm->update_error_count_);
-  EXPECT_EQ(1ul, pm->update_back_off_mult_);
-  expect_callback_to_be_called_ = false;
-  pm->store_state_map_ = std::move(store_state_map_);
-  pm->IssueUpdateRequest();
-
-  EXPECT_FALSE(pm->IsUpdateScheduled());
-
-  // Response code of anything other than 200 should result in error.
-  pm->OnURLLoaderCompleteInternal(net::HTTP_NO_CONTENT, 0, std::string());
-
-  // Should have recorded one error, but back off multiplier is unchanged.
-  EXPECT_EQ(1ul, pm->update_error_count_);
-  EXPECT_EQ(1ul, pm->update_back_off_mult_);
-  EXPECT_TRUE(pm->IsUpdateScheduled());
-}
-
-TEST_F(V4UpdateProtocolManagerTest, TestGetUpdatesNoError) {
-  scoped_refptr<base::TestSimpleTaskRunner> runner(
-      new base::TestSimpleTaskRunner());
-  base::ThreadTaskRunnerHandle runner_handler(runner);
-  std::vector<ListUpdateResponse> expected_lurs;
-  SetupExpectedListUpdateResponse(&expected_lurs);
-  std::unique_ptr<V4UpdateProtocolManager> pm(
-      CreateProtocolManager(expected_lurs));
-  runner->ClearPendingTasks();
-
-  // Initial state. No errors.
-  EXPECT_EQ(0ul, pm->update_error_count_);
-  EXPECT_EQ(1ul, pm->update_back_off_mult_);
-  expect_callback_to_be_called_ = true;
-  pm->store_state_map_ = std::move(store_state_map_);
-  pm->IssueUpdateRequest();
-
-  EXPECT_FALSE(pm->IsUpdateScheduled());
-
-  pm->OnURLLoaderCompleteInternal(net::OK, net::HTTP_OK,
-                                  GetExpectedV4UpdateResponse(expected_lurs));
-
-  // No error, back off multiplier is unchanged.
-  EXPECT_EQ(0ul, pm->update_error_count_);
-  EXPECT_EQ(1ul, pm->update_back_off_mult_);
-  EXPECT_FALSE(pm->IsUpdateScheduled());
-}
-
-TEST_F(V4UpdateProtocolManagerTest, TestGetUpdatesWithOneBackoff) {
-  scoped_refptr<base::TestSimpleTaskRunner> runner(
-      new base::TestSimpleTaskRunner());
-  base::ThreadTaskRunnerHandle runner_handler(runner);
-  std::vector<ListUpdateResponse> expected_lurs;
-  SetupExpectedListUpdateResponse(&expected_lurs);
-  std::unique_ptr<V4UpdateProtocolManager> pm(
-      CreateProtocolManager(expected_lurs));
-  runner->ClearPendingTasks();
-
-  // Initial state. No errors.
-  EXPECT_EQ(0ul, pm->update_error_count_);
-  EXPECT_EQ(1ul, pm->update_back_off_mult_);
-  expect_callback_to_be_called_ = false;
-  pm->store_state_map_ = std::move(store_state_map_);
-  pm->IssueUpdateRequest();
-
-  EXPECT_FALSE(pm->IsUpdateScheduled());
-
-  // Response code of anything other than 200 should result in error.
-  pm->OnURLLoaderCompleteInternal(net::HTTP_NO_CONTENT, 0, std::string());
-
-  // Should have recorded one error, but back off multiplier is unchanged.
-  EXPECT_EQ(1ul, pm->update_error_count_);
-  EXPECT_EQ(1ul, pm->update_back_off_mult_);
-  EXPECT_TRUE(pm->IsUpdateScheduled());
-
-  // Retry, now no backoff.
-  expect_callback_to_be_called_ = true;
-  // Call RunPendingTasks to ensure that the request is sent after backoff.
-  runner->RunPendingTasks();
-
-  pm->OnURLLoaderCompleteInternal(net::OK, net::HTTP_OK,
-                                  GetExpectedV4UpdateResponse(expected_lurs));
-
-  // No error, back off multiplier is unchanged.
-  EXPECT_EQ(0ul, pm->update_error_count_);
-  EXPECT_EQ(1ul, pm->update_back_off_mult_);
-  EXPECT_FALSE(pm->IsUpdateScheduled());
-}
-
-TEST_F(V4UpdateProtocolManagerTest, TestBase64EncodingUsesUrlEncoding) {
-  // NOTE(vakh): I handpicked this value for state by generating random strings
-  // and picked the one that leads to a '-' in the base64 url encoded request
-  // output.
-  store_state_map_->clear();
-  (*store_state_map_)[ListIdentifier(LINUX_PLATFORM, URL, MALWARE_THREAT)] =
-      "h8xfYqY>:R";
-  std::unique_ptr<V4UpdateProtocolManager> pm(
-      CreateProtocolManager(std::vector<ListUpdateResponse>({})));
-  pm->store_state_map_ = std::move(store_state_map_);
-
-  std::string encoded_request_with_minus =
-      pm->GetBase64SerializedUpdateRequestProto();
-
-  std::string expected =
-      "Cg8KCHVuaXR0ZXN0EgMxLjAaGAgBEAIaCmg4eGZZcVk-OlIiBCABIAIoASICCAE=";
-#if !BUILDFLAG(FULL_SAFE_BROWSING)
-  if (base::SysInfo::IsLowEndDevice()) {
-    expected =
-        "Cg8KCHVuaXR0ZXN0EgMxLjAaGwgBEAIaCmg4eGZZcVk-OlIiBxCAICABIAIoASICCAE=";
-  }
-#endif
-
-  EXPECT_EQ(expected, encoded_request_with_minus);
-
-  // TODO(vakh): Add a similar test for underscore for completeness, although
-  // the '-' case is sufficient to prove that we are using URL encoding.
-}
-
-TEST_F(V4UpdateProtocolManagerTest, TestDisableAutoUpdates) {
-  scoped_refptr<base::TestSimpleTaskRunner> runner(
-      new base::TestSimpleTaskRunner());
-  base::ThreadTaskRunnerHandle runner_handler(runner);
-  std::unique_ptr<V4UpdateProtocolManager> pm(CreateProtocolManager(
-      std::vector<ListUpdateResponse>(), true /* disable_auto_update */));
-
-  // Initial state. No errors.
-  pm->ScheduleNextUpdate(std::move(store_state_map_));
-  EXPECT_FALSE(pm->IsUpdateScheduled());
-
-  runner->RunPendingTasks();
-  EXPECT_FALSE(pm->IsUpdateScheduled());
-
-  DCHECK(!pm->request_);
-}
-
-TEST_F(V4UpdateProtocolManagerTest, TestGetUpdatesHasTimeout) {
-  scoped_refptr<base::TestSimpleTaskRunner> runner(
-      new base::TestSimpleTaskRunner());
-  base::ThreadTaskRunnerHandle runner_handler(runner);
-  std::vector<ListUpdateResponse> expected_lurs;
-  SetupExpectedListUpdateResponse(&expected_lurs);
-  std::unique_ptr<V4UpdateProtocolManager> pm(
-      CreateProtocolManager(expected_lurs));
-  runner->ClearPendingTasks();
-
-  // Initial state. No errors.
-  EXPECT_EQ(0ul, pm->update_error_count_);
-  EXPECT_EQ(1ul, pm->update_back_off_mult_);
-  expect_callback_to_be_called_ = true;
-  pm->store_state_map_ = std::move(store_state_map_);
-  pm->IssueUpdateRequest();
-
-  // Don't set anything on the loader. Let it time out.
-  runner->RunPendingTasks();
-
-  // Now wait for the next request to be scheduled.
-  runner->RunPendingTasks();
-
-  // Should have recorded one error, but back off multiplier is unchanged.
-  EXPECT_EQ(1ul, pm->update_error_count_);
-  EXPECT_EQ(1ul, pm->update_back_off_mult_);
-
-  // There should be another fetcher now.
-  pm->OnURLLoaderCompleteInternal(net::OK, net::HTTP_OK,
-                                  GetExpectedV4UpdateResponse(expected_lurs));
-
-  // No error, back off multiplier is unchanged.
-  EXPECT_EQ(0ul, pm->update_error_count_);
-  EXPECT_EQ(1ul, pm->update_back_off_mult_);
-}
-
-TEST_F(V4UpdateProtocolManagerTest, TestExtendedReportingLevelIncluded) {
-  store_state_map_->clear();
-  (*store_state_map_)[ListIdentifier(LINUX_PLATFORM, URL, MALWARE_THREAT)] =
-      "state";
-  std::string base = "Cg8KCHVuaXR0ZXN0EgMxLjAaEwgBEAIaBXN0YXRlIgQgASACKAEiAgg";
-#if !BUILDFLAG(FULL_SAFE_BROWSING)
-  if (base::SysInfo::IsLowEndDevice()) {
-    base = "Cg8KCHVuaXR0ZXN0EgMxLjAaFggBEAIaBXN0YXRlIgcQgCAgASACKAEiAgg";
-  }
-#endif
-
-  std::unique_ptr<V4UpdateProtocolManager> pm_with_off(CreateProtocolManager(
-      std::vector<ListUpdateResponse>({}), false, SBER_LEVEL_OFF));
-  pm_with_off->store_state_map_ = std::move(store_state_map_);
-  EXPECT_EQ(base + "B", pm_with_off->GetBase64SerializedUpdateRequestProto());
-
-  std::unique_ptr<V4UpdateProtocolManager> pm_with_legacy(CreateProtocolManager(
-      std::vector<ListUpdateResponse>({}), false, SBER_LEVEL_LEGACY));
-  pm_with_legacy->store_state_map_ = std::move(pm_with_off->store_state_map_);
-  EXPECT_EQ(base + "C",
-            pm_with_legacy->GetBase64SerializedUpdateRequestProto());
-
-  std::unique_ptr<V4UpdateProtocolManager> pm_with_scout(CreateProtocolManager(
-      std::vector<ListUpdateResponse>({}), false, SBER_LEVEL_SCOUT));
-  pm_with_scout->store_state_map_ = std::move(pm_with_legacy->store_state_map_);
-  EXPECT_EQ(base + "D", pm_with_scout->GetBase64SerializedUpdateRequestProto());
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/features.cc b/components/safe_browsing/features.cc
deleted file mode 100644
index 2f1d92e..0000000
--- a/components/safe_browsing/features.cc
+++ /dev/null
@@ -1,162 +0,0 @@
-// Copyright (c) 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/features.h"
-
-#include <stddef.h>
-#include <algorithm>
-#include <utility>
-#include <vector>
-#include "base/feature_list.h"
-#include "base/metrics/field_trial_params.h"
-#include "components/safe_browsing/buildflags.h"
-
-#include "base/macros.h"
-#include "base/values.h"
-namespace safe_browsing {
-// Please define any new SafeBrowsing related features in this file, and add
-// them to the ExperimentalFeaturesList below to start displaying their status
-// on the chrome://safe-browsing page.
-const base::Feature kAdPopupTriggerFeature{"SafeBrowsingAdPopupTrigger",
-                                           base::FEATURE_DISABLED_BY_DEFAULT};
-
-const base::Feature kAdRedirectTriggerFeature{
-    "SafeBrowsingAdRedirectTrigger", base::FEATURE_DISABLED_BY_DEFAULT};
-
-// Controls various parameters related to occasionally collecting ad samples,
-// for example to control how often collection should occur.
-const base::Feature kAdSamplerTriggerFeature{"SafeBrowsingAdSamplerTrigger",
-                                             base::FEATURE_DISABLED_BY_DEFAULT};
-
-const base::Feature kCaptureInlineJavascriptForGoogleAds{
-    "CaptureInlineJavascriptForGoogleAds", base::FEATURE_DISABLED_BY_DEFAULT};
-
-const base::Feature kCaptureSafetyNetId{"SafeBrowsingCaptureSafetyNetId",
-                                        base::FEATURE_DISABLED_BY_DEFAULT};
-
-const base::Feature kCommittedSBInterstitials{
-    "SafeBrowsingCommittedInterstitials", base::FEATURE_DISABLED_BY_DEFAULT};
-
-const base::Feature kContentComplianceEnabled{
-    "ContentComplianceEnabled", base::FEATURE_DISABLED_BY_DEFAULT};
-
-const base::Feature kMalwareScanEnabled{"MalwareScanEnabled",
-                                        base::FEATURE_DISABLED_BY_DEFAULT};
-
-const base::Feature kPasswordProtectionForSavedPasswords{
-    "SafeBrowsingPasswordProtectionForSavedPasswords",
-    base::FEATURE_DISABLED_BY_DEFAULT};
-
-const base::Feature kPasswordProtectionShowDomainsForSavedPasswords{
-    "SafeBrowsingPasswordProtectionShowDomainsForSavedPasswords",
-    base::FEATURE_DISABLED_BY_DEFAULT};
-
-const base::Feature kPasswordProtectionForSignedInUsers{
-    "SafeBrowsingPasswordProtectionForSignedInUsers",
-    base::FEATURE_DISABLED_BY_DEFAULT};
-
-const base::Feature kRealTimeUrlLookupEnabled{
-    "SafeBrowsingRealTimeUrlLookupEnabled", base::FEATURE_DISABLED_BY_DEFAULT};
-
-const base::Feature kSendOnFocusPing {
-  "SafeBrowsingSendOnFocusPing",
-#if BUILDFLAG(FULL_SAFE_BROWSING)
-      base::FEATURE_ENABLED_BY_DEFAULT
-};
-#else
-      base::FEATURE_DISABLED_BY_DEFAULT
-};
-#endif
-
-const base::Feature kSendPasswordReusePing {
-  "SafeBrowsingSendPasswordReusePing",
-#if BUILDFLAG(FULL_SAFE_BROWSING)
-      base::FEATURE_ENABLED_BY_DEFAULT
-};
-#else
-      base::FEATURE_DISABLED_BY_DEFAULT
-};
-#endif
-
-const base::Feature kSendSampledPingsForAllowlistDomains{
-    "SafeBrowsingSendSampledPingsForAllowlistDomain",
-    base::FEATURE_DISABLED_BY_DEFAULT};
-
-constexpr base::FeatureParam<bool> kShouldFillOldPhishGuardProto{
-    &kPasswordProtectionForSignedInUsers, "DeprecateOldProto", false};
-
-const base::Feature kSuspiciousSiteTriggerQuotaFeature{
-    "SafeBrowsingSuspiciousSiteTriggerQuota", base::FEATURE_ENABLED_BY_DEFAULT};
-
-const base::Feature kThreatDomDetailsTagAndAttributeFeature{
-    "ThreatDomDetailsTagAttributes", base::FEATURE_DISABLED_BY_DEFAULT};
-
-const base::Feature kTriggerThrottlerDailyQuotaFeature{
-    "SafeBrowsingTriggerThrottlerDailyQuota",
-    base::FEATURE_DISABLED_BY_DEFAULT};
-
-const base::Feature kUseLocalBlacklistsV2{"SafeBrowsingUseLocalBlacklistsV2",
-                                          base::FEATURE_DISABLED_BY_DEFAULT};
-
-const base::Feature kUseNewDownloadWarnings{"UseNewDownloadWarnings",
-                                            base::FEATURE_DISABLED_BY_DEFAULT};
-
-namespace {
-// List of Safe Browsing features. Boolean value for each list member should be
-// set to true if the experiment state should be listed on
-// chrome://safe-browsing.
-constexpr struct {
-  const base::Feature* feature;
-  // True if the feature's state should be listed on chrome://safe-browsing.
-  bool show_state;
-} kExperimentalFeatures[]{
-    {&kAdPopupTriggerFeature, true},
-    {&kAdRedirectTriggerFeature, true},
-    {&kAdSamplerTriggerFeature, false},
-    {&kCaptureInlineJavascriptForGoogleAds, true},
-    {&kCaptureSafetyNetId, true},
-    {&kCommittedSBInterstitials, true},
-    {&kContentComplianceEnabled, true},
-    {&kMalwareScanEnabled, true},
-    {&kPasswordProtectionForSavedPasswords, true},
-    {&kPasswordProtectionShowDomainsForSavedPasswords, true},
-    {&kPasswordProtectionForSignedInUsers, true},
-    {&kRealTimeUrlLookupEnabled, true},
-    {&kSendOnFocusPing, true},
-    {&kSendPasswordReusePing, true},
-    {&kSendSampledPingsForAllowlistDomains, false},
-    {&kSuspiciousSiteTriggerQuotaFeature, true},
-    {&kThreatDomDetailsTagAndAttributeFeature, false},
-    {&kTriggerThrottlerDailyQuotaFeature, false},
-    {&kUseLocalBlacklistsV2, true},
-};
-
-// Adds the name and the enabled/disabled status of a given feature.
-void AddFeatureAndAvailability(const base::Feature* exp_feature,
-                               base::ListValue* param_list) {
-  param_list->Append(base::Value(exp_feature->name));
-  if (base::FeatureList::IsEnabled(*exp_feature)) {
-    param_list->Append(base::Value("Enabled"));
-  } else {
-    param_list->Append(base::Value("Disabled"));
-  }
-}
-}  // namespace
-
-// Returns the list of the experimental features that are enabled or disabled,
-// as part of currently running Safe Browsing experiments.
-base::ListValue GetFeatureStatusList() {
-  base::ListValue param_list;
-  for (const auto& feature_status : kExperimentalFeatures) {
-    if (feature_status.show_state)
-      AddFeatureAndAvailability(feature_status.feature, &param_list);
-  }
-  return param_list;
-}
-
-bool GetShouldFillOldPhishGuardProto() {
-  return kShouldFillOldPhishGuardProto.Get();
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/features.h b/components/safe_browsing/features.h
deleted file mode 100644
index eb9ba21..0000000
--- a/components/safe_browsing/features.h
+++ /dev/null
@@ -1,118 +0,0 @@
-// Copyright (c) 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_FEATURES_H_
-#define COMPONENTS_SAFE_BROWSING_FEATURES_H_
-
-#include <stddef.h>
-#include <algorithm>
-#include <utility>
-#include <vector>
-
-#include "base/feature_list.h"
-#include "base/macros.h"
-#include "base/values.h"
-namespace base {
-class ListValue;
-}  // namespace base
-
-namespace safe_browsing {
-// Features list
-// Controls whether we send RIND reports when a popup originating from a Google
-// Ad is blocked.
-extern const base::Feature kAdPopupTriggerFeature;
-
-// Controls whether we send RIND reports when a redirect caused by a Google Ad
-// is blocked.
-extern const base::Feature kAdRedirectTriggerFeature;
-
-extern const base::Feature kAdSamplerTriggerFeature;
-
-// Controls whether we sample inline JavaScript for ads in RIND
-// reports.
-extern const base::Feature kCaptureInlineJavascriptForGoogleAds;
-
-// Controls whether we try to get the SafetyNet ID of the device for use when
-// a SBER user downloads an APK file.
-extern const base::Feature kCaptureSafetyNetId;
-
-// Controls if safe browsing interstitials are implemented as committed
-// navigations instead of overlays.
-extern const base::Feature kCommittedSBInterstitials;
-
-// Controls whether to do deep scanning for DLP. If both this feature and
-// the enterprise policies are enabled, the downloaded and uploaded files are
-// sent for scanning.
-extern const base::Feature kContentComplianceEnabled;
-
-// Controls whether to do deep scanning for malware. If both this feature and
-// the enterprise policies are enabled, the downloaded and uploaded files are
-// sent for scanning.
-extern const base::Feature kMalwareScanEnabled;
-
-// Controls whether the user has forcibly enabled AP download protection. This
-// flag will enable AP downloads protections even for users not enrolled in
-// APP.
-extern const base::Feature kForceUseAPDownloadProtection;
-
-// Enable password protection for non-Google accounts.
-extern const base::Feature kPasswordProtectionForSavedPasswords;
-
-// Enable whether or not to show a list of domains the saved password was used
-// on the modal warning dialog during password protection. This is only checked
-// if the |kPasswordProtectionForSavedPasswords| experiment is on.
-extern const base::Feature kPasswordProtectionShowDomainsForSavedPasswords;
-
-// Enable GAIA password protection for signed-in users.
-extern const base::Feature kPasswordProtectionForSignedInUsers;
-
-// Controls whether Chrome sends on focus ping.
-extern const base::Feature kSendOnFocusPing;
-
-// Controls whether Chrome sends password reuse ping.
-extern const base::Feature kSendPasswordReusePing;
-
-// Controls the daily quota for the suspicious site trigger.
-extern const base::Feature kSuspiciousSiteTriggerQuotaFeature;
-
-// Controls whether the real time URL lookup is enabled.
-extern const base::Feature kRealTimeUrlLookupEnabled;
-
-// Controls whether to send sample pings of allowlist domains on
-// the allowlist to Safe Browsing.
-extern const base::Feature kSendSampledPingsForAllowlistDomains;
-
-// Specifies which non-resource HTML Elements to collect based on their tag and
-// attributes. It's a single param containing a comma-separated list of pairs.
-// For example: "tag1,id,tag1,height,tag2,foo" - this will collect elements with
-// tag "tag1" that have attribute "id" or "height" set, and elements of tag
-// "tag2" if they have attribute "foo" set. All tag names and attributes should
-// be lower case.
-extern const base::Feature kThreatDomDetailsTagAndAttributeFeature;
-
-// Controls the daily quota for data collection triggers. It's a single param
-// containing a comma-separated list of pairs. The format of the param is
-// "T1,Q1,T2,Q2,...Tn,Qn", where Tx is a TriggerType and Qx is how many reports
-// that trigger is allowed to send per day.
-// TODO(crbug.com/744869): This param should be deprecated after ad sampler
-// launch in favour of having a unique quota feature and param per trigger.
-// Having a single shared feature makes it impossible to run multiple trigger
-// trials simultaneously.
-extern const base::Feature kTriggerThrottlerDailyQuotaFeature;
-
-// Controls whether Chrome on Android uses locally cached blacklists.
-extern const base::Feature kUseLocalBlacklistsV2;
-
-// Controls whether Chrome uses new download warning UX.
-extern const base::Feature kUseNewDownloadWarnings;
-
-base::ListValue GetFeatureStatusList();
-
-// Returns whether or not to stop filling in the SyncAccountType and
-// ReusedPasswordType enums. This is used in the
-// |kPasswordProtectionForSignedInUsers| experiment.
-bool GetShouldFillOldPhishGuardProto();
-
-}  // namespace safe_browsing
-#endif  // COMPONENTS_SAFE_BROWSING_FEATURES_H_
diff --git a/components/safe_browsing/password_protection/BUILD.gn b/components/safe_browsing/password_protection/BUILD.gn
deleted file mode 100644
index 6da106c..0000000
--- a/components/safe_browsing/password_protection/BUILD.gn
+++ /dev/null
@@ -1,120 +0,0 @@
-# Copyright 2017 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//components/safe_browsing/buildflags.gni")
-import("//extensions/buildflags/buildflags.gni")
-
-source_set("password_protection") {
-  if (safe_browsing_mode == 1 || safe_browsing_mode == 2) {
-    sources = [
-      "password_protection_navigation_throttle.cc",
-      "password_protection_navigation_throttle.h",
-      "password_protection_request.cc",
-      "password_protection_request.h",
-      "password_protection_service.cc",
-      "password_protection_service.h",
-      "visual_utils.cc",
-      "visual_utils.h",
-    ]
-
-    public_deps = [
-      "//google_apis:google_apis",
-    ]
-
-    deps = [
-      ":password_protection_metrics_util",
-      "//base:base",
-      "//components/content_settings/core/browser:browser",
-      "//components/history/core/browser:browser",
-      "//components/password_manager/core/browser:browser",
-      "//components/safe_browsing:csd_proto",
-      "//components/safe_browsing:features",
-      "//components/safe_browsing/browser:referrer_chain_provider",
-      "//components/safe_browsing/common:common",
-      "//components/safe_browsing/common:interfaces",
-      "//components/safe_browsing/common:safe_browsing_prefs",
-      "//components/safe_browsing/db:allowlist_checker_client",
-      "//components/safe_browsing/db:database_manager",
-      "//components/safe_browsing/db:v4_protocol_manager_util",
-      "//components/safe_browsing/web_ui:web_ui",
-      "//components/sessions",
-      "//components/signin/public/base",
-      "//components/signin/public/identity_manager",
-      "//content/public/browser:browser",
-      "//net:net",
-      "//third_party/protobuf:protobuf_lite",
-    ]
-  }
-  if (safe_browsing_mode == 1) {
-    deps += [ "//components/zoom" ]
-  }
-}
-
-source_set("password_protection_metrics_util") {
-  sources = [
-    "metrics_util.cc",
-    "metrics_util.h",
-  ]
-
-  deps = [
-    "//base",
-    "//components/safe_browsing:csd_proto",
-    "//net:net",
-    "//ui/gfx/geometry:geometry",
-  ]
-}
-
-source_set("password_protection_unittest") {
-  testonly = true
-  if (safe_browsing_mode == 1) {
-    sources = [
-      "password_protection_service_unittest.cc",
-      "visual_utils_unittest.cc",
-    ]
-    deps = [
-      ":mock_password_protection",
-      ":password_protection",
-      ":password_protection_metrics_util",
-      "//base",
-      "//base/test:test_support",
-      "//components/content_settings/core/browser:browser",
-      "//components/history/core/browser:browser",
-      "//components/password_manager/core/browser:browser",
-      "//components/safe_browsing:csd_proto",
-      "//components/safe_browsing:features",
-      "//components/safe_browsing:verdict_cache_manager",
-      "//components/safe_browsing/common:interfaces",
-      "//components/safe_browsing/db:test_database_manager",
-      "//components/signin/public/base",
-      "//components/signin/public/identity_manager",
-      "//components/sync_preferences:test_support",
-      "//content/test:test_support",
-      "//net:test_support",
-      "//services/network:test_support",
-      "//testing/gmock",
-      "//testing/gtest",
-      "//third_party/protobuf:protobuf_lite",
-    ]
-  }
-}
-
-source_set("mock_password_protection") {
-  testonly = true
-  if (safe_browsing_mode == 1) {
-    sources = [
-      "mock_password_protection_service.cc",
-      "mock_password_protection_service.h",
-    ]
-    deps = [
-      ":password_protection",
-      "//base",
-      "//components/content_settings/core/browser:browser",
-      "//components/safe_browsing/db:database_manager",
-      "//net:test_support",
-      "//services/network/public/cpp:cpp",
-      "//testing/gmock",
-      "//testing/gtest",
-    ]
-  }
-}
diff --git a/components/safe_browsing/password_protection/metrics_util.cc b/components/safe_browsing/password_protection/metrics_util.cc
deleted file mode 100644
index 58a3cc7..0000000
--- a/components/safe_browsing/password_protection/metrics_util.cc
+++ /dev/null
@@ -1,392 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/password_protection/metrics_util.h"
-
-#include "base/metrics/histogram_functions.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/time/time.h"
-#include "net/http/http_status_code.h"
-
-namespace safe_browsing {
-
-const char kAnyPasswordEntryRequestOutcomeHistogram[] =
-    "PasswordProtection.RequestOutcome.AnyPasswordEntry";
-const char kAnyPasswordEntryVerdictHistogram[] =
-    "PasswordProtection.Verdict.AnyPasswordEntry";
-const char kEnterprisePasswordEntryRequestOutcomeHistogram[] =
-    "PasswordProtection.RequestOutcome.NonGaiaEnterprisePasswordEntry";
-const char kEnterprisePasswordEntryVerdictHistogram[] =
-    "PasswordProtection.Verdict.NonGaiaEnterprisePasswordEntry";
-const char kEnterprisePasswordPageInfoHistogram[] =
-    "PasswordProtection.PageInfoAction.NonGaiaEnterprisePasswordEntry";
-const char kEnterprisePasswordInterstitialHistogram[] =
-    "PasswordProtection.InterstitialAction.NonGaiaEnterprisePasswordEntry";
-const char kEnterprisePasswordWarningDialogHistogram[] =
-    "PasswordProtection.ModalWarningDialogAction."
-    "NonGaiaEnterprisePasswordEntry";
-const char kGmailSyncPasswordEntryRequestOutcomeHistogram[] =
-    "PasswordProtection.RequestOutcome.GmailSyncPasswordEntry";
-const char kGmailNonSyncPasswordEntryRequestOutcomeHistogram[] =
-    "PasswordProtection.RequestOutcome.GmailNonSyncPasswordEntry";
-const char kGSuiteSyncPasswordEntryRequestOutcomeHistogram[] =
-    "PasswordProtection.RequestOutcome.GSuiteSyncPasswordEntry";
-const char kGSuiteNonSyncPasswordEntryRequestOutcomeHistogram[] =
-    "PasswordProtection.RequestOutcome.GSuiteNonSyncPasswordEntry";
-const char kSavedPasswordEntryRequestOutcomeHistogram[] =
-    "PasswordProtection.RequestOutcome.SavedPasswordEntry";
-const char kUnknownPrimaryAccountPasswordEntryVerdictHistogram[] =
-    "PasswordProtection.Verdict.UnknownPrimaryPasswordEntry";
-const char kUnknownNonPrimaryAccountPasswordEntryVerdictHistogram[] =
-    "PasswordProtection.Verdict.UnknownNonPrimaryPasswordEntry";
-const char kGSuiteSyncPasswordEntryVerdictHistogram[] =
-    "PasswordProtection.Verdict.GSuiteSyncPasswordEntry";
-const char kGSuiteNonSyncPasswordEntryVerdictHistogram[] =
-    "PasswordProtection.Verdict.GSuiteNonSyncPasswordEntry";
-const char kGmailSyncPasswordEntryVerdictHistogram[] =
-    "PasswordProtection.Verdict.GmailSyncPasswordEntry";
-const char kGmailNonSyncPasswordEntryVerdictHistogram[] =
-    "PasswordProtection.Verdict.GmailNonSyncPasswordEntry";
-const char kSavedPasswordEntryVerdictHistogram[] =
-    "PasswordProtection.Verdict.SavedPasswordEntry";
-const char kGmailNonSyncPasswordInterstitialHistogram[] =
-    "PasswordProtection.InterstitialAction.GmailNonSyncPasswordEntry";
-const char kGmailSyncPasswordPageInfoHistogram[] =
-    "PasswordProtection.PageInfoAction.GmailSyncPasswordEntry";
-const char kGmailNonSyncPasswordPageInfoHistogram[] =
-    "PasswordProtection.PageInfoAction.GmailNonSyncPasswordEntry";
-const char kSavedPasswordPageInfoHistogram[] =
-    "PasswordProtection.PageInfoAction.SavedPasswordEntry";
-const char kGmailSyncPasswordWarningDialogHistogram[] =
-    "PasswordProtection.ModalWarningDialogAction.GmailSyncPasswordEntry";
-const char kGmailNonSyncPasswordWarningDialogHistogram[] =
-    "PasswordProtection.ModalWarningDialogAction.GmailNonSyncPasswordEntry";
-const char kNonSyncPasswordInterstitialHistogram[] =
-    "PasswordProtection.InterstitialAction.NonSyncPasswordEntry";
-const char kNonSyncPasswordPageInfoHistogram[] =
-    "PasswordProtection.PageInfoAction.NonSyncPasswordEntry";
-const char kGSuiteSyncPasswordInterstitialHistogram[] =
-    "PasswordProtection.InterstitialAction.GSuiteSyncPasswordEntry";
-const char kGSuiteNonSyncPasswordInterstitialHistogram[] =
-    "PasswordProtection.InterstitialAction.GSuiteNonSyncPasswordEntry";
-const char kGSuiteSyncPasswordPageInfoHistogram[] =
-    "PasswordProtection.PageInfoAction.GSuiteSyncPasswordEntry";
-const char kGSuiteNonSyncPasswordPageInfoHistogram[] =
-    "PasswordProtection.PageInfoAction.GSuiteNonSyncPasswordEntry";
-const char kGSuiteSyncPasswordWarningDialogHistogram[] =
-    "PasswordProtection.ModalWarningDialogAction.GSuiteSyncPasswordEntry";
-const char kGSuiteNonSyncPasswordWarningDialogHistogram[] =
-    "PasswordProtection.ModalWarningDialogAction.GSuiteNonSyncPasswordEntry";
-const char kSavedPasswordWarningDialogHistogram[] =
-    "PasswordProtection.ModalWarningDialogAction.SavedPasswordEntry";
-const char kNonSyncPasswordWarningDialogHistogram[] =
-    "PasswordProtection.ModalWarningDialogAction.NonSyncPasswordEntry";
-const char kPasswordOnFocusRequestOutcomeHistogram[] =
-    "PasswordProtection.RequestOutcome.PasswordFieldOnFocus";
-const char kPasswordOnFocusVerdictHistogram[] =
-    "PasswordProtection.Verdict.PasswordFieldOnFocus";
-const char kNonSyncPasswordEntryRequestOutcomeHistogram[] =
-    "PasswordProtection.RequestOutcome.NonSyncPasswordEntry";
-const char kSyncPasswordEntryRequestOutcomeHistogram[] =
-    "PasswordProtection.RequestOutcome.SyncPasswordEntry";
-const char kSyncPasswordEntryVerdictHistogram[] =
-    "PasswordProtection.Verdict.SyncPasswordEntry";
-const char kNonSyncPasswordEntryVerdictHistogram[] =
-    "PasswordProtection.Verdict.NonSyncPasswordEntry";
-const char kSyncPasswordChromeSettingsHistogram[] =
-    "PasswordProtection.ChromeSettingsAction.SyncPasswordEntry";
-const char kSyncPasswordInterstitialHistogram[] =
-    "PasswordProtection.InterstitialAction.SyncPasswordEntry";
-const char kSyncPasswordPageInfoHistogram[] =
-    "PasswordProtection.PageInfoAction.SyncPasswordEntry";
-const char kSyncPasswordWarningDialogHistogram[] =
-    "PasswordProtection.ModalWarningDialogAction.SyncPasswordEntry";
-const char kEnterprisePasswordAlertHistogram[] =
-    "PasswordProtection.PasswordAlertModeOutcome."
-    "NonGaiaEnterprisePasswordEntry";
-const char kGsuiteSyncPasswordAlertHistogram[] =
-    "PasswordProtection.PasswordAlertModeOutcome.GSuiteSyncPasswordEntry";
-const char kGsuiteNonSyncPasswordAlertHistogram[] =
-    "PasswordProtection.PasswordAlertModeOutcome.GSuiteNonSyncPasswordEntry";
-
-void LogPasswordEntryRequestOutcome(
-    RequestOutcome outcome,
-    ReusedPasswordAccountType password_account_type) {
-  UMA_HISTOGRAM_ENUMERATION(kAnyPasswordEntryRequestOutcomeHistogram, outcome);
-
-  bool is_gsuite_user =
-      password_account_type.account_type() == ReusedPasswordAccountType::GSUITE;
-  bool is_gmail_user =
-      password_account_type.account_type() == ReusedPasswordAccountType::GMAIL;
-  bool is_primary_account_password = password_account_type.is_account_syncing();
-  if (is_primary_account_password) {
-    base::UmaHistogramEnumeration(
-        is_gsuite_user ? kGSuiteSyncPasswordEntryRequestOutcomeHistogram
-                       : kGmailSyncPasswordEntryRequestOutcomeHistogram,
-        outcome);
-    UMA_HISTOGRAM_ENUMERATION(kSyncPasswordEntryRequestOutcomeHistogram,
-                              outcome);
-  } else if (password_account_type.account_type() ==
-             ReusedPasswordAccountType::NON_GAIA_ENTERPRISE) {
-    UMA_HISTOGRAM_ENUMERATION(kEnterprisePasswordEntryRequestOutcomeHistogram,
-                              outcome);
-  } else if (password_account_type.account_type() ==
-             ReusedPasswordAccountType::SAVED_PASSWORD) {
-    UMA_HISTOGRAM_ENUMERATION(kSavedPasswordEntryRequestOutcomeHistogram,
-                              outcome);
-  } else if (is_gsuite_user || is_gmail_user) {
-    base::UmaHistogramEnumeration(
-        is_gsuite_user ? kGSuiteNonSyncPasswordEntryRequestOutcomeHistogram
-                       : kGmailNonSyncPasswordEntryRequestOutcomeHistogram,
-        outcome);
-    UMA_HISTOGRAM_ENUMERATION(kNonSyncPasswordEntryRequestOutcomeHistogram,
-                              outcome);
-  }
-}
-
-void LogPasswordOnFocusRequestOutcome(RequestOutcome outcome) {
-  UMA_HISTOGRAM_ENUMERATION(kPasswordOnFocusRequestOutcomeHistogram, outcome);
-}
-
-void LogPasswordAlertModeOutcome(
-    RequestOutcome outcome,
-    ReusedPasswordAccountType password_account_type) {
-  DCHECK_NE(ReusedPasswordAccountType::GMAIL,
-            password_account_type.account_type());
-  if (password_account_type.account_type() ==
-      ReusedPasswordAccountType::NON_GAIA_ENTERPRISE) {
-    UMA_HISTOGRAM_ENUMERATION(kEnterprisePasswordAlertHistogram, outcome);
-  } else {
-    if (password_account_type.is_account_syncing()) {
-      UMA_HISTOGRAM_ENUMERATION(kGsuiteSyncPasswordAlertHistogram, outcome);
-    } else {
-      UMA_HISTOGRAM_ENUMERATION(kGsuiteNonSyncPasswordAlertHistogram, outcome);
-    }
-  }
-}
-
-void LogNoPingingReason(LoginReputationClientRequest::TriggerType trigger_type,
-                        RequestOutcome reason,
-                        ReusedPasswordAccountType password_account_type) {
-  DCHECK(trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
-         trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
-
-  if (trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) {
-    UMA_HISTOGRAM_ENUMERATION(kPasswordOnFocusRequestOutcomeHistogram, reason);
-  } else {
-    LogPasswordEntryRequestOutcome(reason, password_account_type);
-  }
-}
-
-void LogPasswordProtectionVerdict(
-    LoginReputationClientRequest::TriggerType trigger_type,
-    ReusedPasswordAccountType password_account_type,
-    VerdictType verdict_type) {
-  bool is_gsuite_user =
-      password_account_type.account_type() == ReusedPasswordAccountType::GSUITE;
-  bool is_gmail_user =
-      password_account_type.account_type() == ReusedPasswordAccountType::GMAIL;
-  bool is_account_syncing = password_account_type.is_account_syncing();
-
-  switch (trigger_type) {
-    case LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE:
-      UMA_HISTOGRAM_ENUMERATION(
-          kPasswordOnFocusVerdictHistogram, verdict_type,
-          (LoginReputationClientResponse_VerdictType_VerdictType_MAX + 1));
-      break;
-    case LoginReputationClientRequest::PASSWORD_REUSE_EVENT:
-      UMA_HISTOGRAM_ENUMERATION(
-          kAnyPasswordEntryVerdictHistogram, verdict_type,
-          (LoginReputationClientResponse_VerdictType_VerdictType_MAX + 1));
-      if (is_account_syncing) {
-        if (password_account_type.account_type() ==
-            ReusedPasswordAccountType::UNKNOWN) {
-          UMA_HISTOGRAM_ENUMERATION(
-              kUnknownPrimaryAccountPasswordEntryVerdictHistogram, verdict_type,
-              (LoginReputationClientResponse_VerdictType_VerdictType_MAX + 1));
-        }
-        UMA_HISTOGRAM_ENUMERATION(
-            kSyncPasswordEntryVerdictHistogram, verdict_type,
-            (LoginReputationClientResponse_VerdictType_VerdictType_MAX + 1));
-      } else if (is_gsuite_user || is_gmail_user) {
-        UMA_HISTOGRAM_ENUMERATION(
-            kNonSyncPasswordEntryVerdictHistogram, verdict_type,
-            (LoginReputationClientResponse_VerdictType_VerdictType_MAX + 1));
-      }
-      if (is_gsuite_user) {
-        if (is_account_syncing) {
-          UMA_HISTOGRAM_ENUMERATION(
-              kGSuiteSyncPasswordEntryVerdictHistogram, verdict_type,
-              (LoginReputationClientResponse_VerdictType_VerdictType_MAX + 1));
-        } else {
-          UMA_HISTOGRAM_ENUMERATION(
-              kGSuiteNonSyncPasswordEntryVerdictHistogram, verdict_type,
-              (LoginReputationClientResponse_VerdictType_VerdictType_MAX + 1));
-        }
-      } else if (is_gmail_user) {
-        if (is_account_syncing) {
-          UMA_HISTOGRAM_ENUMERATION(
-              kGmailSyncPasswordEntryVerdictHistogram, verdict_type,
-              (LoginReputationClientResponse_VerdictType_VerdictType_MAX + 1));
-        } else {
-          UMA_HISTOGRAM_ENUMERATION(
-              kGmailNonSyncPasswordEntryVerdictHistogram, verdict_type,
-              (LoginReputationClientResponse_VerdictType_VerdictType_MAX + 1));
-        }
-      } else if (password_account_type.account_type() ==
-                 ReusedPasswordAccountType::NON_GAIA_ENTERPRISE) {
-        UMA_HISTOGRAM_ENUMERATION(
-            kEnterprisePasswordEntryVerdictHistogram, verdict_type,
-            (LoginReputationClientResponse_VerdictType_VerdictType_MAX + 1));
-      } else if (password_account_type.account_type() ==
-                 ReusedPasswordAccountType::SAVED_PASSWORD) {
-        UMA_HISTOGRAM_ENUMERATION(
-            kSavedPasswordEntryVerdictHistogram, verdict_type,
-            (LoginReputationClientResponse_VerdictType_VerdictType_MAX + 1));
-      } else {
-        UMA_HISTOGRAM_ENUMERATION(
-            kUnknownNonPrimaryAccountPasswordEntryVerdictHistogram,
-            verdict_type,
-            (LoginReputationClientResponse_VerdictType_VerdictType_MAX + 1));
-      }
-      break;
-    default:
-      NOTREACHED();
-  }
-}
-
-void LogSyncAccountType(SyncAccountType sync_account_type) {
-  UMA_HISTOGRAM_ENUMERATION(
-      "PasswordProtection.PasswordReuseSyncAccountType", sync_account_type,
-      LoginReputationClientRequest::PasswordReuseEvent::SyncAccountType_MAX +
-          1);
-}
-
-void LogPasswordProtectionNetworkResponseAndDuration(
-    int response_code,
-    const base::TimeTicks& request_start_time) {
-  base::UmaHistogramSparse(
-      "PasswordProtection.PasswordProtectionResponseOrErrorCode",
-      response_code);
-  if (response_code == net::HTTP_OK) {
-    UMA_HISTOGRAM_TIMES("PasswordProtection.RequestNetworkDuration",
-                        base::TimeTicks::Now() - request_start_time);
-  }
-}
-
-void LogPasswordProtectionSampleReportSent() {
-  base::UmaHistogramBoolean("PasswordProtection.SampleReportSent", true);
-}
-
-void LogWarningAction(WarningUIType ui_type,
-                      WarningAction action,
-                      ReusedPasswordAccountType password_account_type) {
-  // |password_type| can be unknown if user directly navigates to
-  // chrome://reset-password page. In this case, do not record user action.
-  if (password_account_type.account_type() ==
-          ReusedPasswordAccountType::UNKNOWN &&
-      ui_type == WarningUIType::INTERSTITIAL) {
-    return;
-  }
-
-  bool is_gsuite_user =
-      password_account_type.account_type() == ReusedPasswordAccountType::GSUITE;
-  bool is_primary_account_password = password_account_type.is_account_syncing();
-  switch (ui_type) {
-    case WarningUIType::PAGE_INFO:
-      if (is_primary_account_password) {
-        UMA_HISTOGRAM_ENUMERATION(kSyncPasswordPageInfoHistogram, action);
-        if (is_gsuite_user) {
-          UMA_HISTOGRAM_ENUMERATION(kGSuiteSyncPasswordPageInfoHistogram,
-                                    action);
-        } else {
-          UMA_HISTOGRAM_ENUMERATION(kGmailSyncPasswordPageInfoHistogram,
-                                    action);
-        }
-      } else if (password_account_type.account_type() ==
-                 ReusedPasswordAccountType::NON_GAIA_ENTERPRISE) {
-        UMA_HISTOGRAM_ENUMERATION(kEnterprisePasswordPageInfoHistogram, action);
-      } else if (password_account_type.account_type() ==
-                 ReusedPasswordAccountType::SAVED_PASSWORD) {
-        UMA_HISTOGRAM_ENUMERATION(kSavedPasswordPageInfoHistogram, action);
-      } else {
-        UMA_HISTOGRAM_ENUMERATION(kNonSyncPasswordPageInfoHistogram, action);
-        if (is_gsuite_user) {
-          UMA_HISTOGRAM_ENUMERATION(kGSuiteNonSyncPasswordPageInfoHistogram,
-                                    action);
-        } else {
-          UMA_HISTOGRAM_ENUMERATION(kGmailNonSyncPasswordPageInfoHistogram,
-                                    action);
-        }
-      }
-      break;
-    case WarningUIType::MODAL_DIALOG:
-      if (is_primary_account_password) {
-        UMA_HISTOGRAM_ENUMERATION(kSyncPasswordWarningDialogHistogram, action);
-        if (is_gsuite_user) {
-          UMA_HISTOGRAM_ENUMERATION(kGSuiteSyncPasswordWarningDialogHistogram,
-                                    action);
-        } else {
-          UMA_HISTOGRAM_ENUMERATION(kGmailSyncPasswordWarningDialogHistogram,
-                                    action);
-        }
-      } else if (password_account_type.account_type() ==
-                 ReusedPasswordAccountType::NON_GAIA_ENTERPRISE) {
-        UMA_HISTOGRAM_ENUMERATION(kEnterprisePasswordWarningDialogHistogram,
-                                  action);
-      } else if (password_account_type.account_type() ==
-                 ReusedPasswordAccountType::SAVED_PASSWORD) {
-        UMA_HISTOGRAM_ENUMERATION(kSavedPasswordWarningDialogHistogram, action);
-      } else {
-        UMA_HISTOGRAM_ENUMERATION(kNonSyncPasswordWarningDialogHistogram,
-                                  action);
-        if (is_gsuite_user) {
-          UMA_HISTOGRAM_ENUMERATION(
-              kGSuiteNonSyncPasswordWarningDialogHistogram, action);
-        } else {
-          UMA_HISTOGRAM_ENUMERATION(kGmailNonSyncPasswordWarningDialogHistogram,
-                                    action);
-        }
-      }
-      break;
-    case WarningUIType::CHROME_SETTINGS:
-      DCHECK(is_primary_account_password);
-      UMA_HISTOGRAM_ENUMERATION(kSyncPasswordChromeSettingsHistogram, action);
-      break;
-    case WarningUIType::INTERSTITIAL:
-      if (is_primary_account_password) {
-        UMA_HISTOGRAM_ENUMERATION(kSyncPasswordInterstitialHistogram, action);
-        if (is_gsuite_user) {
-          UMA_HISTOGRAM_ENUMERATION(kGSuiteSyncPasswordInterstitialHistogram,
-                                    action);
-        }
-      } else if (password_account_type.account_type() ==
-                 ReusedPasswordAccountType::NON_GAIA_ENTERPRISE) {
-        UMA_HISTOGRAM_ENUMERATION(kEnterprisePasswordInterstitialHistogram,
-                                  action);
-      } else {
-        UMA_HISTOGRAM_ENUMERATION(kNonSyncPasswordInterstitialHistogram,
-                                  action);
-        if (is_gsuite_user) {
-          UMA_HISTOGRAM_ENUMERATION(kGSuiteNonSyncPasswordInterstitialHistogram,
-                                    action);
-        } else {
-          UMA_HISTOGRAM_ENUMERATION(kGmailNonSyncPasswordInterstitialHistogram,
-                                    action);
-        }
-      }
-      break;
-    case WarningUIType::NOT_USED:
-      NOTREACHED();
-      break;
-  }
-}
-
-void LogNumberOfReuseBeforeSyncPasswordChange(size_t reuse_count) {
-  UMA_HISTOGRAM_COUNTS_100(
-      "PasswordProtection.GaiaPasswordReusesBeforeGaiaPasswordChanged",
-      reuse_count);
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/password_protection/metrics_util.h b/components/safe_browsing/password_protection/metrics_util.h
deleted file mode 100644
index f3965283..0000000
--- a/components/safe_browsing/password_protection/metrics_util.h
+++ /dev/null
@@ -1,209 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_PASSWORD_PROTECTION_METRICS_UTIL_H_
-#define COMPONENTS_SAFE_BROWSING_PASSWORD_PROTECTION_METRICS_UTIL_H_
-
-#include "base/macros.h"
-#include "components/safe_browsing/proto/csd.pb.h"
-#include "ui/gfx/geometry/size.h"
-
-namespace base {
-class TimeTicks;
-}
-
-namespace safe_browsing {
-
-// UMA metrics
-extern const char kAnyPasswordEntryRequestOutcomeHistogram[];
-extern const char kAnyPasswordEntryVerdictHistogram[];
-extern const char kEnterprisePasswordEntryRequestOutcomeHistogram[];
-extern const char kEnterprisePasswordEntryVerdictHistogram[];
-extern const char kEnterprisePasswordInterstitialHistogram[];
-extern const char kEnterprisePasswordPageInfoHistogram[];
-extern const char kEnterprisePasswordWarningDialogHistogram[];
-extern const char kGmailNonSyncPasswordInterstitialHistogram[];
-extern const char kGmailSyncPasswordPageInfoHistogram[];
-extern const char kGmailNonSyncPasswordPageInfoHistogram[];
-extern const char kGmailSyncPasswordWarningDialogHistogram[];
-extern const char kGmailNonSyncPasswordWarningDialogHistogram[];
-extern const char kNonSyncPasswordInterstitialHistogram[];
-extern const char kNonSyncPasswordPageInfoHistogram[];
-extern const char kGmailSyncPasswordEntryRequestOutcomeHistogram[];
-extern const char kGmailNonSyncPasswordEntryRequestOutcomeHistogram[];
-extern const char kGSuiteNonSyncPasswordEntryRequestOutcomeHistogram[];
-extern const char kGSuiteSyncPasswordEntryVerdictHistogram[];
-
-extern const char kGSuiteSyncPasswordEntryRequestOutcomeHistogram[];
-extern const char kGSuiteNonSyncPasswordEntryVerdictHistogram[];
-extern const char kGmailSyncPasswordEntryVerdictHistogram[];
-extern const char kGmailNonSyncPasswordEntryVerdictHistogram[];
-extern const char kGSuiteSyncPasswordInterstitialHistogram[];
-extern const char kGSuiteNonSyncPasswordInterstitialHistogram[];
-extern const char kGSuiteSyncPasswordPageInfoHistogram[];
-extern const char kGSuiteNonSyncPasswordPageInfoHistogram[];
-extern const char kGSuiteSyncPasswordWarningDialogHistogram[];
-extern const char kGSuiteNonSyncPasswordWarningDialogHistogram[];
-extern const char kPasswordOnFocusRequestOutcomeHistogram[];
-extern const char kPasswordOnFocusVerdictHistogram[];
-extern const char kNonSyncPasswordEntryRequestOutcomeHistogram[];
-extern const char kProtectedPasswordEntryRequestOutcomeHistogram[];
-extern const char kNonSyncPasswordEntryVerdictHistogram[];
-extern const char kSyncPasswordChromeSettingsHistogram[];
-extern const char kSyncPasswordEntryRequestOutcomeHistogram[];
-extern const char kSyncPasswordEntryVerdictHistogram[];
-extern const char kSyncPasswordInterstitialHistogram[];
-extern const char kSyncPasswordPageInfoHistogram[];
-extern const char kSyncPasswordWarningDialogHistogram[];
-extern const char kEnterprisePasswordAlertHistogram[];
-extern const char kGsuiteSyncPasswordAlertHistogram[];
-extern const char kGsuiteNonSyncPasswordAlertHistogram[];
-
-using ReusedPasswordAccountType =
-    LoginReputationClientRequest::PasswordReuseEvent::ReusedPasswordAccountType;
-using SyncAccountType =
-    LoginReputationClientRequest::PasswordReuseEvent::SyncAccountType;
-using VerdictType = LoginReputationClientResponse::VerdictType;
-
-// The outcome of the request. These values are used for UMA.
-// DO NOT CHANGE THE ORDERING OF THESE VALUES.
-enum class RequestOutcome {
-  // Request outcome unknown.
-  UNKNOWN = 0,
-  // Request successfully sent.
-  SUCCEEDED = 1,
-  // Request canceled.
-  CANCELED = 2,
-  // Request timeout.
-  TIMEDOUT = 3,
-  // No request sent because URL matches whitelist.
-  MATCHED_WHITELIST = 4,
-  // No request sent because response already cached.
-  RESPONSE_ALREADY_CACHED = 5,
-  DEPRECATED_NO_EXTENDED_REPORTING = 6,
-  // No request sent because user is in incognito mode.
-  DISABLED_DUE_TO_INCOGNITO = 7,
-  // No request sent because request is malformed.
-  REQUEST_MALFORMED = 8,
-  // Net error.
-  FETCH_FAILED = 9,
-  // Response received but malformed.
-  RESPONSE_MALFORMED = 10,
-  // No request sent since password protection service is no longer available.
-  SERVICE_DESTROYED = 11,
-  // No request sent because pinging feature is disable.
-  DISABLED_DUE_TO_FEATURE_DISABLED = 12,
-  // No request sent because the user is not extended reporting user.
-  DISABLED_DUE_TO_USER_POPULATION = 13,
-  // No request sent because the reputation of the URL is not computable.
-  URL_NOT_VALID_FOR_REPUTATION_COMPUTING = 14,
-  // No request sent because URL matches enterprise whitelist.
-  MATCHED_ENTERPRISE_WHITELIST = 15,
-  // No request sent because URL matches enterprise change password URL.
-  MATCHED_ENTERPRISE_CHANGE_PASSWORD_URL = 16,
-  // No request sent because URL matches enterprise login URL.
-  MATCHED_ENTERPRISE_LOGIN_URL = 17,
-  // No request sent if the admin configures password protection to
-  // warn on ALL password reuses (rather than just phishing sites).
-  PASSWORD_ALERT_MODE = 18,
-  // No request sent if the admin turns off password protection.
-  TURNED_OFF_BY_ADMIN = 19,
-  // No request sent because Safe Browsing is disabled.
-  SAFE_BROWSING_DISABLED = 20,
-  // No request sent because user is not signed-in.
-  USER_NOT_SIGNED_IN = 21,
-  kMaxValue = USER_NOT_SIGNED_IN,
-};
-
-// Enum values indicates if a password protection warning is shown or
-// represents user's action on warnings. These values are used for UMA.
-// DO NOT CHANGE THE ORDERING OF THESE VALUES.
-enum class WarningAction {
-  // Warning shows up.
-  SHOWN = 0,
-
-  // User clicks on "Change Password" button.
-  CHANGE_PASSWORD = 1,
-
-  // User clicks on "Ignore" button.
-  IGNORE_WARNING = 2,
-
-  // Dialog closed in reaction to change of user state.
-  CLOSE = 3,
-
-  // User explicitly mark the site as legitimate.
-  MARK_AS_LEGITIMATE = 4,
-
-  kMaxValue = MARK_AS_LEGITIMATE,
-};
-
-// Type of password protection warning UI.
-enum class WarningUIType {
-  NOT_USED = 0,
-  // Page in bubble.
-  PAGE_INFO = 1,
-  // Modal warning dialog.
-  MODAL_DIALOG = 2,
-  // chrome://settings page.
-  CHROME_SETTINGS = 3,
-  // chrome://reset-password interstitial.
-  INTERSTITIAL = 4
-};
-
-// Logs the |outcome| to several UMA metrics, depending on the value
-// of |password_type| and |sync_account_type|.
-void LogPasswordEntryRequestOutcome(
-    RequestOutcome outcome,
-    ReusedPasswordAccountType password_account_type);
-
-// Logs the |outcome| to several UMA metrics for password on focus pings.
-void LogPasswordOnFocusRequestOutcome(RequestOutcome outcome);
-
-// Logs the |outcome| to several UMA metrics for password alert mode.
-void LogPasswordAlertModeOutcome(
-    RequestOutcome outcome,
-    ReusedPasswordAccountType password_account_type);
-
-// Logs password protection verdict based on |trigger_type|, |password_type|,
-// and |sync_account_type|.
-void LogPasswordProtectionVerdict(
-    LoginReputationClientRequest::TriggerType trigger_type,
-    ReusedPasswordAccountType password_account_type,
-    VerdictType verdict_type);
-
-// Logs |reason| for why there's no ping sent out.
-void LogNoPingingReason(LoginReputationClientRequest::TriggerType trigger_type,
-                        RequestOutcome reason,
-                        ReusedPasswordAccountType password_account_type);
-
-// Logs the type of sync account.
-void LogSyncAccountType(SyncAccountType sync_account_type);
-
-// Logs the network response and duration of a password protection ping.
-void LogPasswordProtectionNetworkResponseAndDuration(
-    int response_code,
-    const base::TimeTicks& request_start_time);
-
-// Logs when a sample ping of allowlist URLs is sent to Safe Browsing.
-void LogPasswordProtectionSampleReportSent();
-
-// Records user action on warnings to corresponding UMA histograms.
-void LogWarningAction(WarningUIType ui_type,
-                      WarningAction action,
-                      ReusedPasswordAccountType password_account_type);
-
-// Logs the number of verdict migrated to the new caching structure.
-void LogNumberOfVerdictMigrated(size_t verdicts_migrated);
-
-// Logs how many time user reused their sync password before they change it.
-void LogNumberOfReuseBeforeSyncPasswordChange(size_t reuse_count);
-
-// Logs the size of referrer chain by |verdict_type|.
-void LogReferrerChainSize(
-    LoginReputationClientResponse::VerdictType verdict_type,
-    int referrer_chain_size);
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_PASSWORD_PROTECTION_METRICS_UTIL_H_
diff --git a/components/safe_browsing/password_protection/mock_password_protection_service.cc b/components/safe_browsing/password_protection/mock_password_protection_service.cc
deleted file mode 100644
index 4e2f1f5c..0000000
--- a/components/safe_browsing/password_protection/mock_password_protection_service.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/password_protection/mock_password_protection_service.h"
-
-#include "components/content_settings/core/browser/host_content_settings_map.h"
-#include "components/safe_browsing/db/database_manager.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-
-namespace safe_browsing {
-
-MockPasswordProtectionService::MockPasswordProtectionService()
-    : PasswordProtectionService(nullptr, nullptr, nullptr) {}
-
-MockPasswordProtectionService::MockPasswordProtectionService(
-    const scoped_refptr<SafeBrowsingDatabaseManager>& database_manager,
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    history::HistoryService* history_service)
-    : PasswordProtectionService(database_manager,
-                                url_loader_factory,
-                                history_service) {}
-
-MockPasswordProtectionService::~MockPasswordProtectionService() {}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/password_protection/mock_password_protection_service.h b/components/safe_browsing/password_protection/mock_password_protection_service.h
deleted file mode 100644
index 7983836..0000000
--- a/components/safe_browsing/password_protection/mock_password_protection_service.h
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_PASSWORD_PROTECTION_MOCK_PASSWORD_PROTECTION_SERVICE_H_
-#define COMPONENTS_SAFE_BROWSING_PASSWORD_PROTECTION_MOCK_PASSWORD_PROTECTION_SERVICE_H_
-
-#include "base/macros.h"
-#include "components/safe_browsing/password_protection/password_protection_service.h"
-#include "testing/gmock/include/gmock/gmock.h"
-
-namespace safe_browsing {
-
-class MockPasswordProtectionService : public PasswordProtectionService {
- public:
-  MockPasswordProtectionService();
-  MockPasswordProtectionService(
-      const scoped_refptr<SafeBrowsingDatabaseManager>& database_manager,
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      history::HistoryService* history_service);
-  ~MockPasswordProtectionService() override;
-
-  // safe_browsing::PasswordProtectionService
-  MOCK_CONST_METHOD0(GetSyncAccountType,
-                     safe_browsing::LoginReputationClientRequest::
-                         PasswordReuseEvent::SyncAccountType());
-  MOCK_CONST_METHOD0(GetBrowserPolicyConnector,
-                     const policy::BrowserPolicyConnector*());
-  MOCK_CONST_METHOD0(GetCurrentContentAreaSize, gfx::Size());
-  MOCK_CONST_METHOD0(GetAccountInfo, AccountInfo());
-  MOCK_CONST_METHOD0(IsPrimaryAccountSyncing, bool());
-  MOCK_CONST_METHOD0(IsPrimaryAccountSignedIn, bool());
-  MOCK_CONST_METHOD0(IsPrimaryAccountGmail, bool());
-  MOCK_CONST_METHOD1(GetPasswordProtectionWarningTriggerPref,
-                     PasswordProtectionTrigger(ReusedPasswordAccountType));
-  MOCK_CONST_METHOD1(GetSignedInNonSyncAccount,
-                     AccountInfo(const std::string&));
-  MOCK_CONST_METHOD1(IsOtherGaiaAccountGmail, bool(const std::string&));
-  MOCK_CONST_METHOD2(IsURLWhitelistedForPasswordEntry,
-                     bool(const GURL&, RequestOutcome*));
-
-  MOCK_METHOD0(CanSendSamplePing, bool());
-  MOCK_METHOD0(IsExtendedReporting, bool());
-  MOCK_METHOD0(IsIncognito, bool());
-  MOCK_METHOD0(IsHistorySyncEnabled, bool());
-  MOCK_METHOD0(IsUnderAdvancedProtection, bool());
-  MOCK_METHOD0(ReportPasswordChanged, void());
-  MOCK_METHOD1(UserClickedThroughSBInterstitial, bool(content::WebContents*));
-  MOCK_METHOD1(MaybeLogPasswordReuseDetectedEvent, void(content::WebContents*));
-  MOCK_METHOD1(SanitizeReferrerChain, void(ReferrerChain*));
-  MOCK_METHOD2(ShowInterstitial,
-               void(content::WebContents*, ReusedPasswordAccountType));
-  MOCK_METHOD2(PersistPhishedSavedPasswordCredential,
-               void(const std::string&, const std::vector<std::string>&));
-  MOCK_METHOD3(IsPingingEnabled,
-               bool(LoginReputationClientRequest::TriggerType,
-                    ReusedPasswordAccountType,
-                    RequestOutcome*));
-  MOCK_METHOD5(ShowModalWarning,
-               void(content::WebContents*,
-                    RequestOutcome,
-                    LoginReputationClientResponse::VerdictType,
-                    const std::string&,
-                    ReusedPasswordAccountType));
-  MOCK_METHOD4(
-      MaybeReportPasswordReuseDetected,
-      void(content::WebContents*, const std::string&, PasswordType, bool));
-  MOCK_METHOD3(UpdateSecurityState,
-               void(safe_browsing::SBThreatType,
-                    ReusedPasswordAccountType,
-                    content::WebContents*));
-  MOCK_METHOD2(RemoveUnhandledSyncPasswordReuseOnURLsDeleted,
-               void(bool, const history::URLRows&));
-  MOCK_METHOD3(FillReferrerChain,
-               void(const GURL&,
-                    SessionID,
-                    LoginReputationClientRequest::Frame*));
-  MOCK_METHOD4(MaybeLogPasswordReuseLookupEvent,
-               void(content::WebContents*,
-                    RequestOutcome,
-                    PasswordType,
-                    const safe_browsing::LoginReputationClientResponse*));
-  MOCK_METHOD3(CanShowInterstitial,
-               bool(RequestOutcome, ReusedPasswordAccountType, const GURL&));
-  MOCK_METHOD5(MaybeStartPasswordFieldOnFocusRequest,
-               void(content::WebContents*,
-                    const GURL&,
-                    const GURL&,
-                    const GURL&,
-                    const std::string&));
-  MOCK_METHOD6(MaybeStartProtectedPasswordEntryRequest,
-               void(content::WebContents*,
-                    const GURL&,
-                    const std::string&,
-                    PasswordType,
-                    const std::vector<std::string>&,
-                    bool));
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockPasswordProtectionService);
-};
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_PASSWORD_PROTECTION_MOCK_PASSWORD_PROTECTION_SERVICE_H_
diff --git a/components/safe_browsing/password_protection/password_protection_navigation_throttle.cc b/components/safe_browsing/password_protection/password_protection_navigation_throttle.cc
deleted file mode 100644
index eab309b7..0000000
--- a/components/safe_browsing/password_protection/password_protection_navigation_throttle.cc
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/password_protection/password_protection_navigation_throttle.h"
-
-#include "components/safe_browsing/password_protection/password_protection_request.h"
-#include "content/public/browser/navigation_handle.h"
-
-namespace safe_browsing {
-PasswordProtectionNavigationThrottle::PasswordProtectionNavigationThrottle(
-    content::NavigationHandle* navigation_handle,
-    scoped_refptr<PasswordProtectionRequest> request,
-    bool is_warning_showing)
-    : content::NavigationThrottle(navigation_handle),
-      request_(request),
-      is_warning_showing_(is_warning_showing) {
-  // Only call AddThrottle() if there is no modal warning showing. If there's a
-  // modal dialog, PPNavigationThrottle will simply cancel this navigation
-  // immediately, therefore no need to keep track of it.
-  if (!is_warning_showing_)
-    request_->AddThrottle(this);
-}
-
-PasswordProtectionNavigationThrottle::~PasswordProtectionNavigationThrottle() {
-  if (request_)
-    request_->RemoveThrottle(this);
-}
-
-content::NavigationThrottle::ThrottleCheckResult
-PasswordProtectionNavigationThrottle::WillStartRequest() {
-  if (is_warning_showing_)
-    return content::NavigationThrottle::CANCEL;
-  return content::NavigationThrottle::DEFER;
-}
-
-content::NavigationThrottle::ThrottleCheckResult
-PasswordProtectionNavigationThrottle::WillRedirectRequest() {
-  if (is_warning_showing_)
-    return content::NavigationThrottle::CANCEL;
-  return content::NavigationThrottle::DEFER;
-}
-
-const char* PasswordProtectionNavigationThrottle::GetNameForLogging() {
-  return "PasswordProtectionNavigationThrottle";
-}
-
-void PasswordProtectionNavigationThrottle::ResumeNavigation() {
-  Resume();
-}
-
-void PasswordProtectionNavigationThrottle::CancelNavigation(
-    content::NavigationThrottle::ThrottleCheckResult result) {
-  CancelDeferredNavigation(result);
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/password_protection/password_protection_navigation_throttle.h b/components/safe_browsing/password_protection/password_protection_navigation_throttle.h
deleted file mode 100644
index 16d4ebb..0000000
--- a/components/safe_browsing/password_protection/password_protection_navigation_throttle.h
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_PASSWORD_PROTECTION_PASSWORD_PROTECTION_NAVIGATION_THROTTLE_H_
-#define COMPONENTS_SAFE_BROWSING_PASSWORD_PROTECTION_PASSWORD_PROTECTION_NAVIGATION_THROTTLE_H_
-
-#include "base/memory/ref_counted.h"
-#include "content/public/browser/navigation_throttle.h"
-
-namespace content {
-class NavigationHandle;
-}  // namespace content
-
-namespace safe_browsing {
-class PasswordProtectionRequest;
-
-// PasswordProtectionNavigationThrottle defers or cancel navigation under the
-// following condition:
-// (1) if a navigation starts when there is a on-going sync password reuse ping,
-//     this throttle defers this navigation. When the verdict comes back, if the
-//     verdict results in showing a modal warning dialog, the deferred
-//     navigation will be canceled; otherwise, the deferred navigation will be
-//     resumed.
-// (2) if a navigation starts when there is a modal warning showing, this
-//     throttle simply cancels this navigation.
-class PasswordProtectionNavigationThrottle
-    : public content::NavigationThrottle {
- public:
-  PasswordProtectionNavigationThrottle(
-      content::NavigationHandle* navigation_handle,
-      scoped_refptr<PasswordProtectionRequest> request,
-      bool is_warning_showing);
-  ~PasswordProtectionNavigationThrottle() override;
-
-  // content::NavigationThrottle:
-  content::NavigationThrottle::ThrottleCheckResult WillStartRequest() override;
-  content::NavigationThrottle::ThrottleCheckResult WillRedirectRequest()
-      override;
-  const char* GetNameForLogging() override;
-
-  void ResumeNavigation();
-  void CancelNavigation(
-      content::NavigationThrottle::ThrottleCheckResult result);
-
- private:
-  scoped_refptr<PasswordProtectionRequest> request_;
-  bool is_warning_showing_;
-  DISALLOW_COPY_AND_ASSIGN(PasswordProtectionNavigationThrottle);
-};
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_PASSWORD_PROTECTION_PASSWORD_PROTECTION_NAVIGATION_THROTTLE_H_
diff --git a/components/safe_browsing/password_protection/password_protection_request.cc b/components/safe_browsing/password_protection/password_protection_request.cc
deleted file mode 100644
index 029c08de..0000000
--- a/components/safe_browsing/password_protection/password_protection_request.cc
+++ /dev/null
@@ -1,599 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/password_protection/password_protection_request.h"
-
-#include <cstddef>
-#include <memory>
-
-#include "base/bind.h"
-#include "base/memory/weak_ptr.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/task/post_task.h"
-#include "base/time/time.h"
-#include "components/password_manager/core/browser/password_manager_metrics_util.h"
-#include "components/password_manager/core/browser/password_reuse_detector.h"
-#include "components/safe_browsing/common/safe_browsing.mojom.h"
-#include "components/safe_browsing/db/allowlist_checker_client.h"
-#include "components/safe_browsing/features.h"
-#include "components/safe_browsing/password_protection/metrics_util.h"
-#include "components/safe_browsing/password_protection/password_protection_navigation_throttle.h"
-#include "components/safe_browsing/password_protection/visual_utils.h"
-#include "components/safe_browsing/proto/csd.pb.h"
-#include "components/safe_browsing/web_ui/safe_browsing_ui.h"
-#include "components/zoom/zoom_controller.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/render_widget_host_view.h"
-#include "content/public/browser/web_contents.h"
-#include "net/base/escape.h"
-#include "net/base/load_flags.h"
-#include "net/base/url_util.h"
-#include "net/http/http_status_code.h"
-#include "net/traffic_annotation/network_traffic_annotation.h"
-#include "services/network/public/cpp/simple_url_loader.h"
-#include "services/service_manager/public/cpp/interface_provider.h"
-#include "url/origin.h"
-
-using content::BrowserThread;
-using content::WebContents;
-
-namespace safe_browsing {
-
-using ReusedPasswordAccountType =
-    LoginReputationClientRequest::PasswordReuseEvent::ReusedPasswordAccountType;
-
-namespace {
-
-// Cap on how many reused domains can be included in a report, to limit
-// the size of the report. UMA suggests 99.9% will have < 200 domains.
-const int kMaxReusedDomains = 200;
-
-#if BUILDFLAG(FULL_SAFE_BROWSING)
-// The maximum time to wait for DOM features to be collected, in milliseconds.
-const int kDomFeatureTimeoutMs = 3000;
-
-// Parameters chosen to ensure privacy is preserved by visual features.
-const int kMinWidthForVisualFeatures = 576;
-const int kMinHeightForVisualFeatures = 576;
-const float kMaxZoomForVisualFeatures = 2.0;
-
-std::unique_ptr<VisualFeatures> ExtractVisualFeatures(
-    const SkBitmap& screenshot) {
-  auto features = std::make_unique<VisualFeatures>();
-  visual_utils::GetHistogramForImage(screenshot,
-                                     features->mutable_color_histogram());
-  visual_utils::GetBlurredImage(screenshot, features->mutable_image());
-  return features;
-}
-#endif
-
-}  // namespace
-
-PasswordProtectionRequest::PasswordProtectionRequest(
-    WebContents* web_contents,
-    const GURL& main_frame_url,
-    const GURL& password_form_action,
-    const GURL& password_form_frame_url,
-    const std::string& username,
-    PasswordType password_type,
-    const std::vector<std::string>& matching_domains,
-    LoginReputationClientRequest::TriggerType type,
-    bool password_field_exists,
-    PasswordProtectionService* pps,
-    int request_timeout_in_ms)
-    : content::WebContentsObserver(web_contents),
-      web_contents_(web_contents),
-      main_frame_url_(main_frame_url),
-      password_form_action_(password_form_action),
-      password_form_frame_url_(password_form_frame_url),
-      username_(username),
-      password_type_(password_type),
-      matching_domains_(matching_domains),
-      trigger_type_(type),
-      password_field_exists_(password_field_exists),
-      password_protection_service_(pps),
-      request_timeout_in_ms_(request_timeout_in_ms),
-      request_proto_(std::make_unique<LoginReputationClientRequest>()),
-      is_modal_warning_showing_(false) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-  DCHECK(trigger_type_ == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
-         trigger_type_ == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
-  DCHECK(trigger_type_ != LoginReputationClientRequest::PASSWORD_REUSE_EVENT ||
-         password_type_ != PasswordType::SAVED_PASSWORD ||
-         matching_domains_.size() > 0);
-
-  request_proto_->set_trigger_type(trigger_type_);
-}
-
-PasswordProtectionRequest::~PasswordProtectionRequest() {
-  weakptr_factory_.InvalidateWeakPtrs();
-}
-
-void PasswordProtectionRequest::Start() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  CheckWhitelist();
-}
-
-void PasswordProtectionRequest::CheckWhitelist() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-  // In order to send pings for about:blank, we skip the whitelist check for
-  // URLs with unsupported schemes.
-  if (!password_protection_service_->database_manager()->CanCheckUrl(
-          main_frame_url_)) {
-    OnWhitelistCheckDone(false);
-    return;
-  }
-
-  // Start a task on the IO thread to check the whitelist. It may
-  // callback immediately on the IO thread or take some time if a full-hash-
-  // check is required.
-  auto result_callback =
-      base::BindOnce(&OnWhitelistCheckDoneOnIO, GetWeakPtr());
-  tracker_.PostTask(
-      base::CreateSingleThreadTaskRunner({BrowserThread::IO}).get(), FROM_HERE,
-      base::BindOnce(&AllowlistCheckerClient::StartCheckCsdWhitelist,
-                     password_protection_service_->database_manager(),
-                     main_frame_url_, std::move(result_callback)));
-}
-
-// static
-void PasswordProtectionRequest::OnWhitelistCheckDoneOnIO(
-    base::WeakPtr<PasswordProtectionRequest> weak_request,
-    bool match_whitelist) {
-  // Don't access weak_request on IO thread. Move it back to UI thread first.
-  base::PostTask(
-      FROM_HERE, {BrowserThread::UI},
-      base::BindOnce(&PasswordProtectionRequest::OnWhitelistCheckDone,
-                     weak_request, match_whitelist));
-}
-
-void PasswordProtectionRequest::OnWhitelistCheckDone(bool match_whitelist) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (match_whitelist) {
-    if (password_protection_service_->CanSendSamplePing()) {
-      FillRequestProto(/*is_sampled_ping=*/true);
-    }
-    Finish(RequestOutcome::MATCHED_WHITELIST, nullptr);
-  } else {
-    // In case the request to Safe Browsing takes too long,
-    // we set a timer to cancel that request and return an "unspecified verdict"
-    // so that the navigation isn't blocked indefinitely.
-    StartTimeout();
-    CheckCachedVerdicts();
-  }
-}
-
-void PasswordProtectionRequest::CheckCachedVerdicts() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (!password_protection_service_) {
-    Finish(RequestOutcome::SERVICE_DESTROYED, nullptr);
-    return;
-  }
-
-  std::unique_ptr<LoginReputationClientResponse> cached_response =
-      std::make_unique<LoginReputationClientResponse>();
-  ReusedPasswordAccountType password_account_type =
-      password_protection_service_
-          ->GetPasswordProtectionReusedPasswordAccountType(password_type_,
-                                                           username_);
-
-  auto verdict = password_protection_service_->GetCachedVerdict(
-      main_frame_url_, trigger_type_, password_account_type,
-      cached_response.get());
-
-  if (verdict != LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED) {
-    set_request_outcome(RequestOutcome::RESPONSE_ALREADY_CACHED);
-    Finish(RequestOutcome::RESPONSE_ALREADY_CACHED, std::move(cached_response));
-  } else {
-    FillRequestProto(/*is_sampled_ping=*/false);
-  }
-}
-
-void PasswordProtectionRequest::FillRequestProto(bool is_sampled_ping) {
-  request_proto_->set_page_url(main_frame_url_.spec());
-  LoginReputationClientRequest::Frame* main_frame =
-      request_proto_->add_frames();
-  main_frame->set_url(main_frame_url_.spec());
-  main_frame->set_frame_index(0 /* main frame */);
-  password_protection_service_->FillReferrerChain(
-      main_frame_url_, SessionID::InvalidValue(), main_frame);
-
-  // If a sample ping is send, only the URL and referrer chain is sent in the
-  // request.
-  if (is_sampled_ping) {
-    LogPasswordProtectionSampleReportSent();
-    request_proto_->set_report_type(
-        LoginReputationClientRequest::SAMPLE_REPORT);
-    request_proto_->clear_trigger_type();
-    if (main_frame->referrer_chain_size() > 0) {
-      password_protection_service_->SanitizeReferrerChain(
-          main_frame->mutable_referrer_chain());
-    }
-    SendRequest();
-    return;
-  } else {
-    request_proto_->set_report_type(LoginReputationClientRequest::FULL_REPORT);
-  }
-
-  password_protection_service_->FillUserPopulation(trigger_type_,
-                                                   request_proto_.get());
-  request_proto_->set_stored_verdict_cnt(
-      password_protection_service_->GetStoredVerdictCount(trigger_type_));
-
-  bool clicked_through_interstitial =
-      password_protection_service_->UserClickedThroughSBInterstitial(
-          web_contents_);
-  request_proto_->set_clicked_through_interstitial(
-      clicked_through_interstitial);
-  request_proto_->set_content_type(web_contents_->GetContentsMimeType());
-
-#if BUILDFLAG(FULL_SAFE_BROWSING)
-  if (password_protection_service_->IsExtendedReporting() &&
-      !password_protection_service_->IsIncognito()) {
-    gfx::Size content_area_size =
-        password_protection_service_->GetCurrentContentAreaSize();
-    request_proto_->set_content_area_height(content_area_size.height());
-    request_proto_->set_content_area_width(content_area_size.width());
-  }
-#endif
-
-  switch (trigger_type_) {
-    case LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE: {
-      LoginReputationClientRequest::Frame::Form* password_form;
-      if (password_form_frame_url_ == main_frame_url_) {
-        main_frame->set_has_password_field(true);
-        password_form = main_frame->add_forms();
-      } else {
-        LoginReputationClientRequest::Frame* password_frame =
-            request_proto_->add_frames();
-        password_frame->set_url(password_form_frame_url_.spec());
-        password_frame->set_has_password_field(true);
-        password_form = password_frame->add_forms();
-      }
-      password_form->set_action_url(password_form_action_.spec());
-      break;
-    }
-    case LoginReputationClientRequest::PASSWORD_REUSE_EVENT: {
-      main_frame->set_has_password_field(password_field_exists_);
-      LoginReputationClientRequest::PasswordReuseEvent* reuse_event =
-          request_proto_->mutable_password_reuse_event();
-      bool matches_signin_password =
-          password_type_ == PasswordType::PRIMARY_ACCOUNT_PASSWORD;
-      reuse_event->set_is_chrome_signin_password(matches_signin_password);
-      reuse_event->set_reused_password_type(
-          password_protection_service_->GetPasswordProtectionReusedPasswordType(
-              password_type_));
-      if (matches_signin_password) {
-        reuse_event->set_sync_account_type(
-            password_protection_service_->GetSyncAccountType());
-        LogSyncAccountType(reuse_event->sync_account_type());
-      }
-
-      if ((password_protection_service_->IsExtendedReporting()) &&
-          !password_protection_service_->IsIncognito()) {
-        for (const auto& domain : matching_domains_) {
-          reuse_event->add_domains_matching_password(domain);
-          if (reuse_event->domains_matching_password_size() >=
-              kMaxReusedDomains)
-            break;
-        }
-      }
-      if (base::FeatureList::IsEnabled(
-              safe_browsing::kPasswordProtectionForSignedInUsers)) {
-        ReusedPasswordAccountType password_account_type_to_add =
-            password_protection_service_
-                ->GetPasswordProtectionReusedPasswordAccountType(password_type_,
-                                                                 username_);
-        *reuse_event->mutable_reused_password_account_type() =
-            password_account_type_to_add;
-      }
-      break;
-    }
-    default:
-      NOTREACHED();
-  }
-
-#if BUILDFLAG(FULL_SAFE_BROWSING)
-  // Get the page DOM features.
-  content::RenderFrameHost* rfh = web_contents_->GetMainFrame();
-  password_protection_service_->GetPhishingDetector(rfh->GetRemoteInterfaces(),
-                                                    &phishing_detector_);
-  dom_features_collection_complete_ = false;
-  phishing_detector_->StartPhishingDetection(
-      main_frame_url_,
-      base::BindRepeating(&PasswordProtectionRequest::OnGetDomFeatures,
-                          GetWeakPtr()));
-  base::PostDelayedTask(
-      FROM_HERE, {BrowserThread::UI},
-      base::BindOnce(&PasswordProtectionRequest::OnGetDomFeatureTimeout,
-                     GetWeakPtr()),
-      base::TimeDelta::FromMilliseconds(kDomFeatureTimeoutMs));
-  dom_feature_start_time_ = base::TimeTicks::Now();
-#else
-  SendRequest();
-#endif
-}
-
-#if BUILDFLAG(FULL_SAFE_BROWSING)
-void PasswordProtectionRequest::OnGetDomFeatures(
-    mojom::PhishingDetectorResult result,
-    const std::string& verdict) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (dom_features_collection_complete_)
-    return;
-
-  UMA_HISTOGRAM_ENUMERATION("PasswordProtection.RendererDomFeatureResult",
-                            result);
-
-  if (result != mojom::PhishingDetectorResult::SUCCESS &&
-      result != mojom::PhishingDetectorResult::INVALID_SCORE)
-    return;
-
-  dom_features_collection_complete_ = true;
-  ClientPhishingRequest dom_features_request;
-  if (dom_features_request.ParseFromString(verdict)) {
-    for (const ClientPhishingRequest::Feature& feature :
-         dom_features_request.feature_map()) {
-      DomFeatures::Feature* new_feature =
-          request_proto_->mutable_dom_features()->add_feature_map();
-      new_feature->set_name(feature.name());
-      new_feature->set_value(feature.value());
-    }
-
-    for (const ClientPhishingRequest::Feature& feature :
-         dom_features_request.non_model_feature_map()) {
-      DomFeatures::Feature* new_feature =
-          request_proto_->mutable_dom_features()->add_feature_map();
-      new_feature->set_name(feature.name());
-      new_feature->set_value(feature.value());
-    }
-
-    request_proto_->mutable_dom_features()->mutable_shingle_hashes()->Swap(
-        dom_features_request.mutable_shingle_hashes());
-    request_proto_->mutable_dom_features()->set_model_version(
-        dom_features_request.model_version());
-  }
-
-  UMA_HISTOGRAM_TIMES("PasswordProtection.DomFeatureExtractionDuration",
-                      base::TimeTicks::Now() - dom_feature_start_time_);
-
-  MaybeCollectVisualFeatures();
-}
-
-void PasswordProtectionRequest::OnGetDomFeatureTimeout() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (!dom_features_collection_complete_) {
-    dom_features_collection_complete_ = true;
-    MaybeCollectVisualFeatures();
-  }
-}
-
-void PasswordProtectionRequest::MaybeCollectVisualFeatures() {
-  // Once the DOM features are collected, either collect visual features, or go
-  // straight to sending the ping.
-  if (trigger_type_ == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE &&
-      password_protection_service_->IsExtendedReporting() &&
-      zoom::ZoomController::GetZoomLevelForWebContents(web_contents_) <=
-          kMaxZoomForVisualFeatures &&
-      request_proto_->content_area_width() >= kMinWidthForVisualFeatures &&
-      request_proto_->content_area_height() >= kMinHeightForVisualFeatures) {
-    CollectVisualFeatures();
-  } else {
-    SendRequest();
-  }
-}
-
-void PasswordProtectionRequest::CollectVisualFeatures() {
-  content::RenderWidgetHostView* view =
-      web_contents_ ? web_contents_->GetRenderWidgetHostView() : nullptr;
-
-  if (!view) {
-    SendRequest();
-    return;
-  }
-
-  visual_feature_start_time_ = base::TimeTicks::Now();
-
-  view->CopyFromSurface(
-      gfx::Rect(), gfx::Size(),
-      base::BindOnce(&PasswordProtectionRequest::OnScreenshotTaken,
-                     GetWeakPtr()));
-}
-
-void PasswordProtectionRequest::OnScreenshotTaken(const SkBitmap& screenshot) {
-  // Do the feature extraction on a worker thread, to avoid blocking the UI.
-  base::PostTaskAndReplyWithResult(
-      FROM_HERE,
-      {base::ThreadPool(), base::MayBlock(), base::TaskPriority::BEST_EFFORT,
-       base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
-      base::BindOnce(&ExtractVisualFeatures, screenshot),
-      base::BindOnce(&PasswordProtectionRequest::OnVisualFeatureCollectionDone,
-                     GetWeakPtr()));
-}
-
-void PasswordProtectionRequest::OnVisualFeatureCollectionDone(
-    std::unique_ptr<VisualFeatures> visual_features) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-  request_proto_->mutable_visual_features()->Swap(visual_features.get());
-
-  UMA_HISTOGRAM_TIMES("PasswordProtection.VisualFeatureExtractionDuration",
-                      base::TimeTicks::Now() - visual_feature_start_time_);
-
-  SendRequest();
-}
-#endif
-
-void PasswordProtectionRequest::SendRequest() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-  web_ui_token_ =
-      WebUIInfoSingleton::GetInstance()->AddToPGPings(*request_proto_);
-
-  std::string serialized_request;
-  if (!request_proto_->SerializeToString(&serialized_request)) {
-    Finish(RequestOutcome::REQUEST_MALFORMED, nullptr);
-    return;
-  }
-
-  net::NetworkTrafficAnnotationTag traffic_annotation =
-      net::DefineNetworkTrafficAnnotation("password_protection_request", R"(
-        semantics {
-          sender: "Safe Browsing"
-          description:
-            "When the user is about to log in to a new, uncommon site, Chrome "
-            "will send a request to Safe Browsing to determine if the page is "
-            "phishing. It'll then show a warning if the page poses a risk of "
-            "phishing."
-          trigger:
-            "When a user focuses on a password field on a page that they "
-            "haven't visited before and that isn't popular or known to be safe."
-          data:
-            "URL and referrer of the current page, password form action, and "
-            "iframe structure."
-          destination: GOOGLE_OWNED_SERVICE
-        }
-        policy {
-          cookies_allowed: YES
-          cookies_store: "Safe Browsing Cookie Store"
-          setting:
-            "Users can control this feature via 'Protect you and your device "
-            "from dangerous sites'. By default, this setting is enabled."
-            "Alternatively, you can turn it off via "
-            "'PasswordProtectionWarningTrigger' enterprise policy setting."
-          chrome_policy {
-            PasswordProtectionWarningTrigger {
-              policy_options {mode: MANDATORY}
-              PasswordProtectionWarningTrigger: 2
-            }
-          }
-        })");
-  auto resource_request = std::make_unique<network::ResourceRequest>();
-  resource_request->url =
-      PasswordProtectionService::GetPasswordProtectionRequestUrl();
-  resource_request->method = "POST";
-  resource_request->load_flags = net::LOAD_DISABLE_CACHE;
-  url_loader_ = network::SimpleURLLoader::Create(std::move(resource_request),
-                                                 traffic_annotation);
-  url_loader_->AttachStringForUpload(serialized_request,
-                                     "application/octet-stream");
-  request_start_time_ = base::TimeTicks::Now();
-  url_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
-      password_protection_service_->url_loader_factory().get(),
-      base::BindOnce(&PasswordProtectionRequest::OnURLLoaderComplete,
-                     base::Unretained(this)));
-}
-
-void PasswordProtectionRequest::StartTimeout() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-  // If request is not done withing 10 seconds, we cancel this request.
-  // The weak pointer used for the timeout will be invalidated (and
-  // hence would prevent the timeout) if the check completes on time and
-  // execution reaches Finish().
-  base::PostDelayedTask(
-      FROM_HERE, {BrowserThread::UI},
-      base::BindOnce(&PasswordProtectionRequest::Cancel, GetWeakPtr(), true),
-      base::TimeDelta::FromMilliseconds(request_timeout_in_ms_));
-}
-
-void PasswordProtectionRequest::OnURLLoaderComplete(
-    std::unique_ptr<std::string> response_body) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  int response_code = 0;
-  if (url_loader_->ResponseInfo() && url_loader_->ResponseInfo()->headers)
-    response_code = url_loader_->ResponseInfo()->headers->response_code();
-
-  const bool is_success = url_loader_->NetError() == net::OK;
-
-  LogPasswordProtectionNetworkResponseAndDuration(
-      is_success ? response_code : url_loader_->NetError(),
-      request_start_time_);
-
-  if (!is_success || net::HTTP_OK != response_code) {
-    Finish(RequestOutcome::FETCH_FAILED, nullptr);
-    return;
-  }
-
-  std::unique_ptr<LoginReputationClientResponse> response =
-      std::make_unique<LoginReputationClientResponse>();
-  DCHECK(response_body);
-  url_loader_.reset();  // We don't need it anymore.
-  if (response_body && response->ParseFromString(*response_body)) {
-    WebUIInfoSingleton::GetInstance()->AddToPGResponses(web_ui_token_,
-                                                        *response);
-    set_request_outcome(RequestOutcome::SUCCEEDED);
-    Finish(RequestOutcome::SUCCEEDED, std::move(response));
-  } else {
-    Finish(RequestOutcome::RESPONSE_MALFORMED, nullptr);
-  }
-}
-
-void PasswordProtectionRequest::Finish(
-    RequestOutcome outcome,
-    std::unique_ptr<LoginReputationClientResponse> response) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  tracker_.TryCancelAll();
-
-  // If the request is canceled, the PasswordProtectionService is already
-  // partially destroyed, and we won't be able to log accurate metrics.
-  if (outcome != RequestOutcome::CANCELED) {
-    ReusedPasswordAccountType password_account_type =
-        password_protection_service_
-            ->GetPasswordProtectionReusedPasswordAccountType(password_type_,
-                                                             username_);
-    if (trigger_type_ == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) {
-      LogPasswordOnFocusRequestOutcome(outcome);
-    } else {
-#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
-      LogPasswordEntryRequestOutcome(outcome, password_account_type);
-#endif
-
-#if defined(SYNC_PASSWORD_REUSE_WARNING_ENABLED)
-      if (password_type_ == PasswordType::PRIMARY_ACCOUNT_PASSWORD) {
-        password_protection_service_->MaybeLogPasswordReuseLookupEvent(
-            web_contents_, outcome, password_type_, response.get());
-      }
-#endif
-    }
-
-    if (outcome == RequestOutcome::SUCCEEDED && response) {
-      LogPasswordProtectionVerdict(trigger_type_, password_account_type,
-                                   response->verdict_type());
-    }
-  }
-  password_protection_service_->RequestFinished(this, outcome,
-                                                std::move(response));
-}
-
-void PasswordProtectionRequest::Cancel(bool timed_out) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  url_loader_.reset();
-  // If request is canceled because |password_protection_service_| is shutting
-  // down, ignore all these deferred navigations.
-  if (!timed_out) {
-    throttles_.clear();
-  }
-
-  Finish(timed_out ? RequestOutcome::TIMEDOUT : RequestOutcome::CANCELED,
-         nullptr);
-}
-
-void PasswordProtectionRequest::HandleDeferredNavigations() {
-  for (auto* throttle : throttles_) {
-    if (is_modal_warning_showing_)
-      throttle->CancelNavigation(content::NavigationThrottle::CANCEL);
-    else
-      throttle->ResumeNavigation();
-  }
-  throttles_.clear();
-}
-
-void PasswordProtectionRequest::WebContentsDestroyed() {
-  Cancel(/*timed_out=*/false);
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/password_protection/password_protection_request.h b/components/safe_browsing/password_protection/password_protection_request.h
deleted file mode 100644
index e7afcc14..0000000
--- a/components/safe_browsing/password_protection/password_protection_request.h
+++ /dev/null
@@ -1,284 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_PASSWORD_PROTECTION_PASSWORD_PROTECTION_REQUEST_H_
-#define COMPONENTS_SAFE_BROWSING_PASSWORD_PROTECTION_PASSWORD_PROTECTION_REQUEST_H_
-
-#include <vector>
-
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/task/cancelable_task_tracker.h"
-#include "base/time/time.h"
-#include "components/password_manager/core/browser/password_manager_metrics_util.h"
-#include "components/safe_browsing/buildflags.h"
-#include "components/safe_browsing/common/safe_browsing.mojom.h"
-#include "components/safe_browsing/password_protection/metrics_util.h"
-#include "components/safe_browsing/password_protection/password_protection_service.h"
-#include "components/safe_browsing/proto/csd.pb.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/web_contents_observer.h"
-#include "mojo/public/cpp/bindings/remote.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-
-class GURL;
-
-namespace network {
-class SimpleURLLoader;
-}
-
-namespace safe_browsing {
-
-class PasswordProtectionNavigationThrottle;
-
-using password_manager::metrics_util::PasswordType;
-
-// A request for checking if an unfamiliar login form or a password reuse event
-// is safe. PasswordProtectionRequest objects are owned by
-// PasswordProtectionService indicated by |password_protection_service_|.
-// PasswordProtectionService is RefCountedThreadSafe such that it can post task
-// safely between IO and UI threads. It can only be destroyed on UI thread.
-//
-// PasswordProtectionRequest flow:
-// Step| Thread |                    Task
-// (1) |   UI   | If incognito or !SBER, quit request.
-// (2) |   UI   | Add task to IO thread for whitelist checking.
-// (3) |   IO   | Check whitelist and return the result back to UI thread.
-// (4) |   UI   | If whitelisted, check verdict cache; else quit request.
-// (5) |   UI   | If verdict cached, quit request; else prepare request proto.
-// (6) |   UI   | Collect features related to the DOM of the page.
-// (7) |   UI   | If appropriate, compute visual features of the page.
-// (7) |   UI   | Start a timeout task, and send network request.
-// (8) |   UI   | On receiving response, handle response and finish.
-//     |        | On request timeout, cancel request.
-//     |        | On deletion of |password_protection_service_|, cancel request.
-class PasswordProtectionRequest : public base::RefCountedThreadSafe<
-                                      PasswordProtectionRequest,
-                                      content::BrowserThread::DeleteOnUIThread>,
-                                  public content::WebContentsObserver {
- public:
-  PasswordProtectionRequest(content::WebContents* web_contents,
-                            const GURL& main_frame_url,
-                            const GURL& password_form_action,
-                            const GURL& password_form_frame_url,
-                            const std::string& username,
-                            PasswordType password_type,
-                            const std::vector<std::string>& matching_origins,
-                            LoginReputationClientRequest::TriggerType type,
-                            bool password_field_exists,
-                            PasswordProtectionService* pps,
-                            int request_timeout_in_ms);
-
-  base::WeakPtr<PasswordProtectionRequest> GetWeakPtr() {
-    return weakptr_factory_.GetWeakPtr();
-  }
-
-  // Starts processing request by checking extended reporting and incognito
-  // conditions.
-  void Start();
-
-  // Cancels the current request. |timed_out| indicates if this cancellation is
-  // due to timeout. This function will call Finish() to destroy |this|.
-  void Cancel(bool timed_out);
-
-  // Processes the received response.
-  void OnURLLoaderComplete(std::unique_ptr<std::string> response_body);
-
-  GURL main_frame_url() const { return main_frame_url_; }
-
-  const LoginReputationClientRequest* request_proto() const {
-    return request_proto_.get();
-  }
-
-  content::WebContents* web_contents() const { return web_contents_; }
-
-  LoginReputationClientRequest::TriggerType trigger_type() const {
-    return trigger_type_;
-  }
-
-  const std::string username() const { return username_; }
-
-  PasswordType password_type() const { return password_type_; }
-
-  const std::vector<std::string> matching_domains() const& {
-    return matching_domains_;
-  }
-
-  bool is_modal_warning_showing() const { return is_modal_warning_showing_; }
-
-  void set_is_modal_warning_showing(bool is_warning_showing) {
-    is_modal_warning_showing_ = is_warning_showing;
-  }
-
-  RequestOutcome request_outcome() const { return request_outcome_; }
-
-  void set_request_outcome(RequestOutcome request_outcome) {
-    request_outcome_ = request_outcome;
-  }
-
-  // Keeps track of created navigation throttle.
-  void AddThrottle(PasswordProtectionNavigationThrottle* throttle) {
-    throttles_.insert(throttle);
-  }
-
-  void RemoveThrottle(PasswordProtectionNavigationThrottle* throttle) {
-    throttles_.erase(throttle);
-  }
-
-  // Cancels navigation if there is modal warning showing, resumes it otherwise.
-  void HandleDeferredNavigations();
-
-  // WebContentsObserver implementation
-  void WebContentsDestroyed() override;
-
- protected:
-  friend class base::RefCountedThreadSafe<PasswordProtectionRequest>;
-
- private:
-  friend struct content::BrowserThread::DeleteOnThread<
-      content::BrowserThread::UI>;
-  friend class base::DeleteHelper<PasswordProtectionRequest>;
-  friend class PasswordProtectionServiceTest;
-  friend class ChromePasswordProtectionServiceTest;
-  ~PasswordProtectionRequest() override;
-
-  // Start checking the whitelist.
-  void CheckWhitelist();
-
-  static void OnWhitelistCheckDoneOnIO(
-      base::WeakPtr<PasswordProtectionRequest> weak_request,
-      bool match_whitelist);
-
-  // If |main_frame_url_| matches whitelist, call Finish() immediately;
-  // otherwise call CheckCachedVerdicts().
-  void OnWhitelistCheckDone(bool match_whitelist);
-
-  // Looks up cached verdicts. If verdict is already cached, call SendRequest();
-  // otherwise call Finish().
-  void CheckCachedVerdicts();
-
-  // Fill |request_proto_| with appropriate values.
-  void FillRequestProto(bool is_sampled_ping);
-
-#if BUILDFLAG(FULL_SAFE_BROWSING)
-  // Collects visual features from the current login page.
-  void CollectVisualFeatures();
-
-  // Processes the screenshot of the login page into visual features.
-  void OnScreenshotTaken(const SkBitmap& bitmap);
-
-  // Called when the visual feature extraction is complete.
-  void OnVisualFeatureCollectionDone(
-      std::unique_ptr<VisualFeatures> visual_features);
-
-  // Called when the DOM feature extraction is complete.
-  void OnGetDomFeatures(mojom::PhishingDetectorResult result,
-                        const std::string& verdict);
-
-  // Called when the DOM feature extraction times out.
-  void OnGetDomFeatureTimeout();
-
-  // If appropriate, collects visual features, otherwise continues on to sending
-  // the request.
-  void MaybeCollectVisualFeatures();
-#endif
-
-  // Initiates network request to Safe Browsing backend.
-  void SendRequest();
-
-  // Start a timer to cancel the request if it takes too long.
-  void StartTimeout();
-
-  // |this| will be destroyed after calling this function.
-  void Finish(RequestOutcome outcome,
-              std::unique_ptr<LoginReputationClientResponse> response);
-
-  // WebContents of the password protection event.
-  content::WebContents* web_contents_;
-
-  // Main frame URL of the login form.
-  const GURL main_frame_url_;
-
-  // The action URL of the password form.
-  const GURL password_form_action_;
-
-  // Frame url of the detected password form.
-  const GURL password_form_frame_url_;
-
-  // The username of the reused password hash. The username can be an email or
-  // a username for a non-GAIA or saved-password reuse. No validation has been
-  // done on it.
-  const std::string username_;
-
-  // Type of the reused password.
-  const PasswordType password_type_;
-
-  // Domains from the Password Manager that match this password.
-  // Should be non-empty if |reused_password_type_| == SAVED_PASSWORD.
-  // Otherwise, may or may not be empty.
-  const std::vector<std::string> matching_domains_;
-
-  // If this request is for unfamiliar login page or for a password reuse event.
-  const LoginReputationClientRequest::TriggerType trigger_type_;
-
-  // If there is a password field on the page.
-  const bool password_field_exists_;
-
-  // When request is sent.
-  base::TimeTicks request_start_time_;
-
-  // SimpleURLLoader instance for sending request and receiving response.
-  std::unique_ptr<network::SimpleURLLoader> url_loader_;
-
-  // The PasswordProtectionService instance owns |this|.
-  // Can only be accessed on UI thread.
-  PasswordProtectionService* password_protection_service_;
-
-  // The outcome of the password protection request.
-  RequestOutcome request_outcome_;
-
-  // If we haven't receive response after this period of time, we cancel this
-  // request.
-  const int request_timeout_in_ms_;
-
-  std::unique_ptr<LoginReputationClientRequest> request_proto_;
-
-  // Needed for canceling tasks posted to different threads.
-  base::CancelableTaskTracker tracker_;
-
-  // Navigation throttles created for this |web_contents_| during |this|'s
-  // lifetime. These throttles are owned by their corresponding
-  // NavigationHandler instances.
-  std::set<PasswordProtectionNavigationThrottle*> throttles_;
-
-  // Whether there is a modal warning triggered by this request.
-  bool is_modal_warning_showing_;
-
-  // If a request is sent, this is the token returned by the WebUI.
-  int web_ui_token_;
-
-#if BUILDFLAG(FULL_SAFE_BROWSING)
-  // When we start extracting visual features.
-  base::TimeTicks visual_feature_start_time_;
-
-  // The Mojo pipe used for extracting DOM features from the renderer.
-  mojo::Remote<safe_browsing::mojom::PhishingDetector> phishing_detector_;
-
-  // When we start extracting DOM features. Used to compute the duration of DOM
-  // feature extraction, which is logged at
-  // PasswordProtection.DomFeatureExtractionDuration.
-  base::TimeTicks dom_feature_start_time_;
-
-  // Whether the DOM features collection is finished, either by timeout or by
-  // successfully gathering the features.
-  bool dom_features_collection_complete_;
-#endif
-
-  base::WeakPtrFactory<PasswordProtectionRequest> weakptr_factory_{this};
-  DISALLOW_COPY_AND_ASSIGN(PasswordProtectionRequest);
-};
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_PASSWORD_PROTECTION_PASSWORD_PROTECTION_REQUEST_H_
diff --git a/components/safe_browsing/password_protection/password_protection_service.cc b/components/safe_browsing/password_protection/password_protection_service.cc
deleted file mode 100644
index 14a12a6..0000000
--- a/components/safe_browsing/password_protection/password_protection_service.cc
+++ /dev/null
@@ -1,598 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/password_protection/password_protection_service.h"
-
-#include <stddef.h>
-
-#include <memory>
-#include <string>
-
-#include "base/base64.h"
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/stl_util.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
-#include "base/task/post_task.h"
-#include "components/content_settings/core/browser/host_content_settings_map.h"
-#include "components/password_manager/core/browser/password_manager_metrics_util.h"
-#include "components/password_manager/core/browser/password_reuse_detector.h"
-#include "components/safe_browsing/common/utils.h"
-#include "components/safe_browsing/db/database_manager.h"
-#include "components/safe_browsing/features.h"
-#include "components/safe_browsing/password_protection/password_protection_navigation_throttle.h"
-#include "components/safe_browsing/password_protection/password_protection_request.h"
-#include "components/zoom/zoom_controller.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/navigation_handle.h"
-#include "content/public/browser/web_contents.h"
-#include "google_apis/google_api_keys.h"
-#include "net/base/escape.h"
-#include "net/base/url_util.h"
-#include "third_party/blink/public/common/page/page_zoom.h"
-
-using content::BrowserThread;
-using content::WebContents;
-using history::HistoryService;
-using password_manager::metrics_util::PasswordType;
-
-namespace safe_browsing {
-
-using PasswordReuseEvent = LoginReputationClientRequest::PasswordReuseEvent;
-
-namespace {
-
-// Keys for storing password protection verdict into a DictionaryValue.
-const int kRequestTimeoutMs = 10000;
-const char kPasswordProtectionRequestUrl[] =
-    "https://sb-ssl.google.com/safebrowsing/clientreport/login";
-
-}  // namespace
-
-PasswordProtectionService::PasswordProtectionService(
-    const scoped_refptr<SafeBrowsingDatabaseManager>& database_manager,
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    HistoryService* history_service)
-    : database_manager_(database_manager),
-      url_loader_factory_(url_loader_factory) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (history_service)
-    history_service_observer_.Add(history_service);
-
-  common_spoofed_domains_ = {
-      "login.live.com"
-      "facebook.com",
-      "box.com",
-      "paypal.com",
-      "apple.com",
-      "yahoo.com",
-      "adobe.com",
-      "amazon.com",
-      "linkedin.com",
-      "att.com"};
-}
-
-PasswordProtectionService::~PasswordProtectionService() {
-  tracker_.TryCancelAll();
-  CancelPendingRequests();
-  history_service_observer_.RemoveAll();
-  weak_factory_.InvalidateWeakPtrs();
-}
-
-bool PasswordProtectionService::CanGetReputationOfURL(const GURL& url) {
-  if (!url.is_valid() || !url.SchemeIsHTTPOrHTTPS() || net::IsLocalhost(url))
-    return false;
-
-  const std::string hostname = url.HostNoBrackets();
-  return !net::IsHostnameNonUnique(hostname) &&
-         hostname.find('.') != std::string::npos;
-}
-
-#if defined(ON_FOCUS_PING_ENABLED)
-void PasswordProtectionService::MaybeStartPasswordFieldOnFocusRequest(
-    WebContents* web_contents,
-    const GURL& main_frame_url,
-    const GURL& password_form_action,
-    const GURL& password_form_frame_url,
-    const std::string& hosted_domain) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  RequestOutcome reason;
-  if (!base::FeatureList::IsEnabled(safe_browsing::kSendOnFocusPing)) {
-    return;
-  }
-  if (CanSendPing(LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
-                  main_frame_url,
-                  GetPasswordProtectionReusedPasswordAccountType(
-                      PasswordType::PASSWORD_TYPE_UNKNOWN,
-                      /*username=*/""),
-                  &reason)) {
-    StartRequest(web_contents, main_frame_url, password_form_action,
-                 password_form_frame_url, /* username */ "",
-                 PasswordType::PASSWORD_TYPE_UNKNOWN,
-                 {}, /* matching_domains: not used for this type */
-                 LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE, true);
-  }
-}
-#endif
-
-#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
-void PasswordProtectionService::MaybeStartProtectedPasswordEntryRequest(
-    WebContents* web_contents,
-    const GURL& main_frame_url,
-    const std::string& username,
-    PasswordType password_type,
-    const std::vector<std::string>& matching_domains,
-    bool password_field_exists) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (!base::FeatureList::IsEnabled(safe_browsing::kSendPasswordReusePing)) {
-    return;
-  }
-  ReusedPasswordAccountType reused_password_account_type =
-      GetPasswordProtectionReusedPasswordAccountType(password_type, username);
-  RequestOutcome reason;
-  // Need to populate |reason| to be passed into CanShowInterstitial.
-  bool can_send_ping =
-      CanSendPing(LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-                  main_frame_url, reused_password_account_type, &reason);
-  if (IsSupportedPasswordTypeForPinging(password_type)) {
-#if BUILDFLAG(FULL_SAFE_BROWSING)
-    // Collect metrics about typical page-zoom on login pages.
-    double zoom_level =
-        zoom::ZoomController::GetZoomLevelForWebContents(web_contents);
-    UMA_HISTOGRAM_COUNTS_1000(
-        "PasswordProtection.PageZoomFactor",
-        static_cast<int>(100 * blink::PageZoomLevelToZoomFactor(zoom_level)));
-#endif  // defined(FULL_SAFE_BROWSING)
-    if (can_send_ping) {
-      StartRequest(web_contents, main_frame_url, GURL(), GURL(), username,
-                   password_type, matching_domains,
-                   LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-                   password_field_exists);
-    } else {
-#if defined(SYNC_PASSWORD_REUSE_WARNING_ENABLED)
-      if (reused_password_account_type.is_account_syncing())
-        MaybeLogPasswordReuseLookupEvent(web_contents, reason, password_type,
-                                         nullptr);
-#endif  // defined(SYNC_PASSWORD_REUSE_WARNING_ENABLED)
-    }
-  }
-
-#if defined(SYNC_PASSWORD_REUSE_WARNING_ENABLED)
-  if (CanShowInterstitial(reason, reused_password_account_type,
-                          main_frame_url)) {
-    username_for_last_shown_warning_ = username;
-    reused_password_account_type_for_last_shown_warning_ =
-        reused_password_account_type;
-    ShowInterstitial(web_contents, reused_password_account_type);
-  }
-#endif  // defined(SYNC_PASSWORD_REUSE_WARNING_ENABLED)
-}
-#endif  // defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
-
-#if defined(SYNC_PASSWORD_REUSE_WARNING_ENABLED)
-bool PasswordProtectionService::ShouldShowModalWarning(
-    LoginReputationClientRequest::TriggerType trigger_type,
-    ReusedPasswordAccountType password_type,
-    LoginReputationClientResponse::VerdictType verdict_type) {
-  if (trigger_type != LoginReputationClientRequest::PASSWORD_REUSE_EVENT ||
-      !IsSupportedPasswordTypeForModalWarning(password_type)) {
-    return false;
-  }
-
-  return (verdict_type == LoginReputationClientResponse::PHISHING ||
-          verdict_type == LoginReputationClientResponse::LOW_REPUTATION) &&
-         IsWarningEnabled(password_type);
-}
-
-void PasswordProtectionService::RemoveWarningRequestsByWebContents(
-    content::WebContents* web_contents) {
-  for (auto it = warning_requests_.begin(); it != warning_requests_.end();) {
-    if (it->get()->web_contents() == web_contents)
-      it = warning_requests_.erase(it);
-    else
-      ++it;
-  }
-}
-
-bool PasswordProtectionService::IsModalWarningShowingInWebContents(
-    content::WebContents* web_contents) {
-  for (const auto& request : warning_requests_) {
-    if (request->web_contents() == web_contents)
-      return true;
-  }
-  return false;
-}
-#endif
-
-LoginReputationClientResponse::VerdictType
-PasswordProtectionService::GetCachedVerdict(
-    const GURL& url,
-    LoginReputationClientRequest::TriggerType trigger_type,
-    ReusedPasswordAccountType password_type,
-    LoginReputationClientResponse* out_response) {
-  return LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED;
-}
-
-void PasswordProtectionService::CacheVerdict(
-    const GURL& url,
-    LoginReputationClientRequest::TriggerType trigger_type,
-    ReusedPasswordAccountType password_type,
-    const LoginReputationClientResponse& verdict,
-    const base::Time& receive_time) {}
-
-void PasswordProtectionService::StartRequest(
-    WebContents* web_contents,
-    const GURL& main_frame_url,
-    const GURL& password_form_action,
-    const GURL& password_form_frame_url,
-    const std::string& username,
-    PasswordType password_type,
-    const std::vector<std::string>& matching_domains,
-    LoginReputationClientRequest::TriggerType trigger_type,
-    bool password_field_exists) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  scoped_refptr<PasswordProtectionRequest> request(
-      new PasswordProtectionRequest(
-          web_contents, main_frame_url, password_form_action,
-          password_form_frame_url, username, password_type, matching_domains,
-          trigger_type, password_field_exists, this, GetRequestTimeoutInMS()));
-  request->Start();
-  pending_requests_.insert(std::move(request));
-}
-
-bool PasswordProtectionService::CanSendPing(
-    LoginReputationClientRequest::TriggerType trigger_type,
-    const GURL& main_frame_url,
-    ReusedPasswordAccountType password_type,
-    RequestOutcome* reason) {
-  *reason = RequestOutcome::UNKNOWN;
-  bool is_pinging_enabled =
-      IsPingingEnabled(trigger_type, password_type, reason);
-  // Pinging is enabled for password_reuse trigger level; however we need to
-  // make sure *reason is set appropriately.
-  PasswordProtectionTrigger trigger_level =
-      GetPasswordProtectionWarningTriggerPref(password_type);
-  if (trigger_level == PASSWORD_REUSE) {
-    *reason = RequestOutcome::PASSWORD_ALERT_MODE;
-  }
-  if (is_pinging_enabled &&
-      !IsURLWhitelistedForPasswordEntry(main_frame_url, reason)) {
-    return true;
-  }
-  LogNoPingingReason(trigger_type, *reason, password_type);
-  return false;
-}
-
-void PasswordProtectionService::RequestFinished(
-    PasswordProtectionRequest* request,
-    RequestOutcome outcome,
-    std::unique_ptr<LoginReputationClientResponse> response) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK(request);
-
-  if (response) {
-    ReusedPasswordAccountType password_type =
-        GetPasswordProtectionReusedPasswordAccountType(request->password_type(),
-                                                       request->username());
-    if (outcome != RequestOutcome::RESPONSE_ALREADY_CACHED) {
-      CacheVerdict(request->main_frame_url(), request->trigger_type(),
-                   password_type, *response, base::Time::Now());
-    }
-    bool enable_warning_for_non_sync_users = base::FeatureList::IsEnabled(
-        safe_browsing::kPasswordProtectionForSignedInUsers);
-    if (!enable_warning_for_non_sync_users &&
-        request->password_type() == PasswordType::OTHER_GAIA_PASSWORD) {
-      return;
-    }
-
-    // If it's password alert mode and a Gsuite/enterprise account, we do not
-    // show a modal warning.
-    if (outcome == RequestOutcome::PASSWORD_ALERT_MODE &&
-        (password_type.account_type() == ReusedPasswordAccountType::GSUITE ||
-         password_type.account_type() ==
-             ReusedPasswordAccountType::NON_GAIA_ENTERPRISE)) {
-      return;
-    }
-
-#if defined(SYNC_PASSWORD_REUSE_WARNING_ENABLED)
-    if (ShouldShowModalWarning(request->trigger_type(), password_type,
-                               response->verdict_type())) {
-      username_for_last_shown_warning_ = request->username();
-      reused_password_account_type_for_last_shown_warning_ = password_type;
-      saved_passwords_matching_domains_ = request->matching_domains();
-      ShowModalWarning(request->web_contents(), request->request_outcome(),
-                       response->verdict_type(), response->verdict_token(),
-                       password_type);
-      request->set_is_modal_warning_showing(true);
-    }
-#endif
-  }
-
-  request->HandleDeferredNavigations();
-
-  // If the request is canceled, the PasswordProtectionService is already
-  // partially destroyed, and we won't be able to log accurate metrics.
-  if (outcome != RequestOutcome::CANCELED) {
-    auto verdict =
-        response ? response->verdict_type()
-                 : LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED;
-
-// Disabled on Android, because enterprise reporting extension is not supported.
-#if !defined(OS_ANDROID)
-    MaybeReportPasswordReuseDetected(
-        request->web_contents(), request->username(), request->password_type(),
-        verdict == LoginReputationClientResponse::PHISHING);
-#endif
-
-    // Persist a bit in CompromisedCredentials table when saved password is
-    // reused on a phishing or low reputation site.
-    auto is_unsafe_url =
-        verdict == LoginReputationClientResponse::PHISHING ||
-        verdict == LoginReputationClientResponse::LOW_REPUTATION;
-    if (is_unsafe_url) {
-      PersistPhishedSavedPasswordCredential(request->username(),
-                                            request->matching_domains());
-    }
-  }
-
-  // Remove request from |pending_requests_| list. If it triggers warning, add
-  // it into the !warning_reqeusts_| list.
-  for (auto it = pending_requests_.begin(); it != pending_requests_.end();
-       it++) {
-    if (it->get() == request) {
-      if (request->is_modal_warning_showing())
-        warning_requests_.insert(std::move(request));
-      pending_requests_.erase(it);
-      break;
-    }
-  }
-}
-
-void PasswordProtectionService::CancelPendingRequests() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  for (auto it = pending_requests_.begin(); it != pending_requests_.end();) {
-    PasswordProtectionRequest* request = it->get();
-    // These are the requests for whom we're still waiting for verdicts.
-    // We need to advance the iterator before we cancel because canceling
-    // the request will invalidate it when RequestFinished is called.
-    it++;
-    request->Cancel(false);
-  }
-  DCHECK(pending_requests_.empty());
-}
-
-int PasswordProtectionService::GetStoredVerdictCount(
-    LoginReputationClientRequest::TriggerType trigger_type) {
-  return -1;
-}
-
-scoped_refptr<SafeBrowsingDatabaseManager>
-PasswordProtectionService::database_manager() {
-  return database_manager_;
-}
-
-GURL PasswordProtectionService::GetPasswordProtectionRequestUrl() {
-  GURL url(kPasswordProtectionRequestUrl);
-  std::string api_key = google_apis::GetAPIKey();
-  DCHECK(!api_key.empty());
-  return url.Resolve("?key=" + net::EscapeQueryParamValue(api_key, true));
-}
-
-int PasswordProtectionService::GetRequestTimeoutInMS() {
-  return kRequestTimeoutMs;
-}
-
-void PasswordProtectionService::FillUserPopulation(
-    LoginReputationClientRequest::TriggerType trigger_type,
-    LoginReputationClientRequest* request_proto) {
-  ChromeUserPopulation* user_population = request_proto->mutable_population();
-  user_population->set_user_population(
-      IsExtendedReporting() ? ChromeUserPopulation::EXTENDED_REPORTING
-                            : ChromeUserPopulation::SAFE_BROWSING);
-  user_population->set_profile_management_status(
-      GetProfileManagementStatus(GetBrowserPolicyConnector()));
-  user_population->set_is_history_sync_enabled(IsHistorySyncEnabled());
-#if BUILDFLAG(FULL_SAFE_BROWSING)
-  user_population->set_is_under_advanced_protection(
-      IsUnderAdvancedProtection());
-#endif
-  user_population->set_is_incognito(IsIncognito());
-}
-
-void PasswordProtectionService::OnURLsDeleted(
-    history::HistoryService* history_service,
-    const history::DeletionInfo& deletion_info) {
-  base::PostTask(
-      FROM_HERE, {BrowserThread::UI},
-      base::BindRepeating(&PasswordProtectionService::
-                              RemoveUnhandledSyncPasswordReuseOnURLsDeleted,
-                          GetWeakPtr(), deletion_info.IsAllHistory(),
-                          deletion_info.deleted_rows()));
-}
-
-void PasswordProtectionService::HistoryServiceBeingDeleted(
-    history::HistoryService* history_service) {
-  history_service_observer_.RemoveAll();
-}
-
-std::unique_ptr<PasswordProtectionNavigationThrottle>
-PasswordProtectionService::MaybeCreateNavigationThrottle(
-    content::NavigationHandle* navigation_handle) {
-  if (!navigation_handle->IsRendererInitiated())
-    return nullptr;
-
-  content::WebContents* web_contents = navigation_handle->GetWebContents();
-  for (scoped_refptr<PasswordProtectionRequest> request : pending_requests_) {
-    if (request->web_contents() == web_contents &&
-        request->trigger_type() ==
-            safe_browsing::LoginReputationClientRequest::PASSWORD_REUSE_EVENT &&
-        IsSupportedPasswordTypeForModalWarning(
-            GetPasswordProtectionReusedPasswordAccountType(
-                request->password_type(), username_for_last_shown_warning()))) {
-      return std::make_unique<PasswordProtectionNavigationThrottle>(
-          navigation_handle, request, /*is_warning_showing=*/false);
-    }
-  }
-
-  for (scoped_refptr<PasswordProtectionRequest> request : warning_requests_) {
-    if (request->web_contents() == web_contents) {
-      return std::make_unique<PasswordProtectionNavigationThrottle>(
-          navigation_handle, request, /*is_warning_showing=*/true);
-    }
-  }
-  return nullptr;
-}
-
-bool PasswordProtectionService::IsWarningEnabled(
-    ReusedPasswordAccountType password_type) {
-  return GetPasswordProtectionWarningTriggerPref(password_type) ==
-         PHISHING_REUSE;
-}
-
-// static
-ReusedPasswordType
-PasswordProtectionService::GetPasswordProtectionReusedPasswordType(
-    password_manager::metrics_util::PasswordType password_type) {
-  switch (password_type) {
-    case PasswordType::SAVED_PASSWORD:
-      return PasswordReuseEvent::SAVED_PASSWORD;
-    case PasswordType::PRIMARY_ACCOUNT_PASSWORD:
-      return PasswordReuseEvent::SIGN_IN_PASSWORD;
-    case PasswordType::OTHER_GAIA_PASSWORD:
-      return PasswordReuseEvent::OTHER_GAIA_PASSWORD;
-    case PasswordType::ENTERPRISE_PASSWORD:
-      return PasswordReuseEvent::ENTERPRISE_PASSWORD;
-    case PasswordType::PASSWORD_TYPE_UNKNOWN:
-      return PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN;
-    case PasswordType::PASSWORD_TYPE_COUNT:
-      break;
-  }
-  NOTREACHED();
-  return PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN;
-}
-
-ReusedPasswordAccountType
-PasswordProtectionService::GetPasswordProtectionReusedPasswordAccountType(
-    password_manager::metrics_util::PasswordType password_type,
-    const std::string& username) const {
-  ReusedPasswordAccountType reused_password_account_type;
-  switch (password_type) {
-    case PasswordType::SAVED_PASSWORD:
-      reused_password_account_type.set_account_type(
-          ReusedPasswordAccountType::SAVED_PASSWORD);
-      return reused_password_account_type;
-    case PasswordType::ENTERPRISE_PASSWORD:
-      reused_password_account_type.set_account_type(
-          ReusedPasswordAccountType::NON_GAIA_ENTERPRISE);
-      return reused_password_account_type;
-    case PasswordType::PRIMARY_ACCOUNT_PASSWORD: {
-      reused_password_account_type.set_is_account_syncing(
-          IsPrimaryAccountSyncing());
-      if (!IsPrimaryAccountSignedIn()) {
-        reused_password_account_type.set_account_type(
-            ReusedPasswordAccountType::UNKNOWN);
-        return reused_password_account_type;
-      }
-      reused_password_account_type.set_account_type(
-          IsPrimaryAccountGmail() ? ReusedPasswordAccountType::GMAIL
-                                  : ReusedPasswordAccountType::GSUITE);
-      return reused_password_account_type;
-    }
-    case PasswordType::OTHER_GAIA_PASSWORD: {
-      AccountInfo account_info = GetSignedInNonSyncAccount(username);
-      if (account_info.account_id.empty()) {
-        reused_password_account_type.set_account_type(
-            ReusedPasswordAccountType::UNKNOWN);
-        return reused_password_account_type;
-      }
-      reused_password_account_type.set_account_type(
-          IsOtherGaiaAccountGmail(username)
-              ? ReusedPasswordAccountType::GMAIL
-              : ReusedPasswordAccountType::GSUITE);
-      return reused_password_account_type;
-    }
-    case PasswordType::PASSWORD_TYPE_UNKNOWN:
-    case PasswordType::PASSWORD_TYPE_COUNT:
-      reused_password_account_type.set_account_type(
-          ReusedPasswordAccountType::UNKNOWN);
-      return reused_password_account_type;
-  }
-  NOTREACHED();
-  return reused_password_account_type;
-}
-
-// static
-PasswordType
-PasswordProtectionService::ConvertReusedPasswordAccountTypeToPasswordType(
-    ReusedPasswordAccountType password_type) {
-  if (password_type.is_account_syncing()) {
-    return PasswordType::PRIMARY_ACCOUNT_PASSWORD;
-  } else if (password_type.account_type() ==
-             ReusedPasswordAccountType::NON_GAIA_ENTERPRISE) {
-    return PasswordType::ENTERPRISE_PASSWORD;
-  } else if (password_type.account_type() ==
-             ReusedPasswordAccountType::SAVED_PASSWORD) {
-    return PasswordType::SAVED_PASSWORD;
-  } else if (password_type.account_type() ==
-             ReusedPasswordAccountType::UNKNOWN) {
-    return PasswordType::PASSWORD_TYPE_UNKNOWN;
-  } else {
-    return PasswordType::OTHER_GAIA_PASSWORD;
-  }
-}
-
-bool PasswordProtectionService::IsSupportedPasswordTypeForPinging(
-    PasswordType password_type) const {
-  switch (password_type) {
-    case PasswordType::SAVED_PASSWORD:
-      return true;
-    case PasswordType::PRIMARY_ACCOUNT_PASSWORD:
-      return true;
-    case PasswordType::ENTERPRISE_PASSWORD:
-      return true;
-    case PasswordType::OTHER_GAIA_PASSWORD:
-      return base::FeatureList::IsEnabled(
-          safe_browsing::kPasswordProtectionForSignedInUsers);
-    case PasswordType::PASSWORD_TYPE_UNKNOWN:
-    case PasswordType::PASSWORD_TYPE_COUNT:
-      return false;
-  }
-  NOTREACHED();
-  return false;
-}
-
-bool PasswordProtectionService::IsSupportedPasswordTypeForModalWarning(
-    ReusedPasswordAccountType password_type) const {
-  if (password_type.account_type() ==
-      ReusedPasswordAccountType::NON_GAIA_ENTERPRISE)
-    return true;
-
-  if (password_type.account_type() ==
-          ReusedPasswordAccountType::SAVED_PASSWORD &&
-      base::FeatureList::IsEnabled(
-          safe_browsing::kPasswordProtectionForSavedPasswords))
-    return true;
-
-  if (password_type.account_type() != ReusedPasswordAccountType::GMAIL &&
-      password_type.account_type() != ReusedPasswordAccountType::GSUITE)
-    return false;
-
-  return password_type.is_account_syncing() ||
-         base::FeatureList::IsEnabled(
-             safe_browsing::kPasswordProtectionForSignedInUsers);
-}
-
-#if BUILDFLAG(FULL_SAFE_BROWSING)
-void PasswordProtectionService::GetPhishingDetector(
-    service_manager::InterfaceProvider* provider,
-    mojo::Remote<mojom::PhishingDetector>* phishing_detector) {
-  provider->GetInterface(phishing_detector->BindNewPipeAndPassReceiver());
-}
-#endif
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/password_protection/password_protection_service.h b/components/safe_browsing/password_protection/password_protection_service.h
deleted file mode 100644
index 4953737..0000000
--- a/components/safe_browsing/password_protection/password_protection_service.h
+++ /dev/null
@@ -1,496 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_PASSWORD_PROTECTION_PASSWORD_PROTECTION_SERVICE_H_
-#define COMPONENTS_SAFE_BROWSING_PASSWORD_PROTECTION_PASSWORD_PROTECTION_SERVICE_H_
-
-#include <set>
-#include <unordered_map>
-
-#include "base/callback.h"
-#include "base/feature_list.h"
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#include "base/scoped_observer.h"
-#include "base/task/cancelable_task_tracker.h"
-#include "base/values.h"
-#include "build/build_config.h"
-#include "components/history/core/browser/history_service.h"
-#include "components/history/core/browser/history_service_observer.h"
-#include "components/password_manager/core/browser/password_manager_metrics_util.h"
-#include "components/safe_browsing/browser/referrer_chain_provider.h"
-#include "components/safe_browsing/buildflags.h"
-#include "components/safe_browsing/common/safe_browsing.mojom.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
-#include "components/safe_browsing/password_protection/metrics_util.h"
-#include "components/safe_browsing/proto/csd.pb.h"
-#include "components/sessions/core/session_id.h"
-#include "components/signin/public/identity_manager/account_info.h"
-#include "mojo/public/cpp/bindings/remote.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "services/service_manager/public/cpp/interface_provider.h"
-#include "third_party/protobuf/src/google/protobuf/repeated_field.h"
-
-namespace content {
-class WebContents;
-class NavigationHandle;
-}
-
-namespace policy {
-class BrowserPolicyConnector;
-}
-
-class GURL;
-class HostContentSettingsMap;
-
-namespace safe_browsing {
-
-class PasswordProtectionNavigationThrottle;
-class PasswordProtectionRequest;
-class SafeBrowsingDatabaseManager;
-
-using ReusedPasswordAccountType =
-    LoginReputationClientRequest::PasswordReuseEvent::ReusedPasswordAccountType;
-using ReusedPasswordType =
-    LoginReputationClientRequest::PasswordReuseEvent::ReusedPasswordType;
-using password_manager::metrics_util::PasswordType;
-
-// Manage password protection pings and verdicts. There is one instance of this
-// class per profile. Therefore, every PasswordProtectionService instance is
-// associated with a unique HistoryService instance and a unique
-// HostContentSettingsMap instance.
-class PasswordProtectionService : public history::HistoryServiceObserver {
- public:
-  PasswordProtectionService(
-      const scoped_refptr<SafeBrowsingDatabaseManager>& database_manager,
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      history::HistoryService* history_service);
-
-  ~PasswordProtectionService() override;
-
-  base::WeakPtr<PasswordProtectionService> GetWeakPtr() {
-    return weak_factory_.GetWeakPtr();
-  }
-
-  // Looks up |settings| to find the cached verdict response. If verdict is not
-  // available or is expired, return VERDICT_TYPE_UNSPECIFIED. Can be called on
-  // any thread.
-  virtual LoginReputationClientResponse::VerdictType GetCachedVerdict(
-      const GURL& url,
-      LoginReputationClientRequest::TriggerType trigger_type,
-      ReusedPasswordAccountType password_type,
-      LoginReputationClientResponse* out_response);
-
-  // Stores |verdict| in |settings| based on its |trigger_type|, |url|,
-  // reused |password_type|, |verdict| and |receive_time|.
-  virtual void CacheVerdict(
-      const GURL& url,
-      LoginReputationClientRequest::TriggerType trigger_type,
-      ReusedPasswordAccountType password_type,
-      const LoginReputationClientResponse& verdict,
-      const base::Time& receive_time);
-
-  // Creates an instance of PasswordProtectionRequest and call Start() on that
-  // instance. This function also insert this request object in |requests_| for
-  // record keeping.
-  void StartRequest(content::WebContents* web_contents,
-                    const GURL& main_frame_url,
-                    const GURL& password_form_action,
-                    const GURL& password_form_frame_url,
-                    const std::string& username,
-                    PasswordType password_type,
-                    const std::vector<std::string>& matching_domains,
-                    LoginReputationClientRequest::TriggerType trigger_type,
-                    bool password_field_exists);
-
-#if defined(ON_FOCUS_PING_ENABLED)
-  virtual void MaybeStartPasswordFieldOnFocusRequest(
-      content::WebContents* web_contents,
-      const GURL& main_frame_url,
-      const GURL& password_form_action,
-      const GURL& password_form_frame_url,
-      const std::string& hosted_domain);
-#endif
-
-#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
-  virtual void MaybeStartProtectedPasswordEntryRequest(
-      content::WebContents* web_contents,
-      const GURL& main_frame_url,
-      const std::string& username,
-      PasswordType password_type,
-      const std::vector<std::string>& matching_domains,
-      bool password_field_exists);
-#endif
-
-#if defined(SYNC_PASSWORD_REUSE_WARNING_ENABLED)
-  // Records a Chrome Sync event that sync password reuse was detected.
-  virtual void MaybeLogPasswordReuseDetectedEvent(
-      content::WebContents* web_contents) = 0;
-
-  // If we want to show password reuse modal warning.
-  bool ShouldShowModalWarning(
-      LoginReputationClientRequest::TriggerType trigger_type,
-      ReusedPasswordAccountType password_type,
-      LoginReputationClientResponse::VerdictType verdict_type);
-
-  // Shows modal warning dialog on the current |web_contents| and pass the
-  // |verdict_token| to callback of this dialog.
-  virtual void ShowModalWarning(
-      content::WebContents* web_contents,
-      RequestOutcome outcome,
-      LoginReputationClientResponse::VerdictType verdict_type,
-      const std::string& verdict_token,
-      ReusedPasswordAccountType password_type) = 0;
-
-  // Shows chrome://reset-password interstitial.
-  virtual void ShowInterstitial(content::WebContents* web_contens,
-                                ReusedPasswordAccountType password_type) = 0;
-#endif
-
-// The following functions are disabled on Android, because enterprise reporting
-// extension is not supported.
-#if !defined(OS_ANDROID)
-  // Triggers the safeBrowsingPrivate.OnPolicySpecifiedPasswordReuseDetected.
-  virtual void MaybeReportPasswordReuseDetected(
-      content::WebContents* web_contents,
-      const std::string& username,
-      PasswordType password_type,
-      bool is_phishing_url) = 0;
-
-  // Called when a protected password change is detected. Must be called on
-  // UI thread.
-  virtual void ReportPasswordChanged() = 0;
-#endif
-
-#if defined(SYNC_PASSWORD_REUSE_WARNING_ENABLED)
-  virtual void UpdateSecurityState(safe_browsing::SBThreatType threat_type,
-                                   ReusedPasswordAccountType password_type,
-                                   content::WebContents* web_contents) = 0;
-#endif
-
-  scoped_refptr<SafeBrowsingDatabaseManager> database_manager();
-
-  // Safe Browsing backend cannot get a reliable reputation of a URL if
-  // (1) URL is not valid
-  // (2) URL doesn't have http or https scheme
-  // (3) It maps to a local host.
-  // (4) Its hostname is an IP Address in an IANA-reserved range.
-  // (5) Its hostname is a not-yet-assigned by ICANN gTLD.
-  // (6) Its hostname is a dotless domain.
-  static bool CanGetReputationOfURL(const GURL& url);
-
-  // If user has clicked through any Safe Browsing interstitial on this given
-  // |web_contents|.
-  virtual bool UserClickedThroughSBInterstitial(
-      content::WebContents* web_contents) = 0;
-
-  // Called when a new navigation is starting. Create throttle if there is a
-  // pending sync password reuse ping or if there is a modal warning dialog
-  // showing in the corresponding web contents.
-  std::unique_ptr<PasswordProtectionNavigationThrottle>
-  MaybeCreateNavigationThrottle(content::NavigationHandle* navigation_handle);
-
-  // Returns if the warning UI is enabled.
-  bool IsWarningEnabled(ReusedPasswordAccountType password_type);
-
-  // Returns the pref value of password protection warning trigger.
-  virtual PasswordProtectionTrigger GetPasswordProtectionWarningTriggerPref(
-      ReusedPasswordAccountType password_type) const = 0;
-
-  // If |url| matches Safe Browsing whitelist domains, password protection
-  // change password URL, or password protection login URLs in the enterprise
-  // policy.
-  virtual bool IsURLWhitelistedForPasswordEntry(
-      const GURL& url,
-      RequestOutcome* reason) const = 0;
-
-  // Persist the phished saved password credential in the "compromised
-  // credentials" table. Calls the password store to add a row for each domain
-  // where the phished saved password is used on.
-  virtual void PersistPhishedSavedPasswordCredential(
-      const std::string& username,
-      const std::vector<std::string>& matching_domains) = 0;
-
-  // Converts from password::metrics_util::PasswordType to
-  // LoginReputationClientRequest::PasswordReuseEvent::ReusedPasswordType.
-  static ReusedPasswordType GetPasswordProtectionReusedPasswordType(
-      PasswordType password_type);
-
-  // Converts from password_manager::metrics_util::PasswordType
-  // to PasswordReuseEvent::ReusedPasswordAccountType. |username| is only
-  // used if |password_type| is OTHER_GAIA_PASSWORD because it needs to be
-  // compared to the list of signed in accounts.
-  ReusedPasswordAccountType GetPasswordProtectionReusedPasswordAccountType(
-      PasswordType password_type,
-      const std::string& username) const;
-
-  // Converts from ReusedPasswordAccountType to
-  // password_manager::metrics_util::PasswordType.
-  static PasswordType ConvertReusedPasswordAccountTypeToPasswordType(
-      ReusedPasswordAccountType password_type);
-
-  // If we can send ping for this type of reused password.
-  bool IsSupportedPasswordTypeForPinging(PasswordType password_type) const;
-
-  // If we can show modal warning for this type of reused password.
-  bool IsSupportedPasswordTypeForModalWarning(
-      ReusedPasswordAccountType password_type) const;
-
-  const ReusedPasswordAccountType&
-  reused_password_account_type_for_last_shown_warning() const {
-    return reused_password_account_type_for_last_shown_warning_;
-  }
-#if defined(UNIT_TEST)
-  void set_reused_password_account_type_for_last_shown_warning(
-      ReusedPasswordAccountType
-          reused_password_account_type_for_last_shown_warning) {
-    reused_password_account_type_for_last_shown_warning_ =
-        reused_password_account_type_for_last_shown_warning;
-  }
-#endif
-
-  const std::string& username_for_last_shown_warning() const {
-    return username_for_last_shown_warning_;
-  }
-#if defined(UNIT_TEST)
-  void set_username_for_last_shown_warning(const std::string& username) {
-    username_for_last_shown_warning_ = username;
-  }
-#endif
-
-  const std::vector<std::string>& saved_passwords_matching_domains() const {
-    return saved_passwords_matching_domains_;
-  }
-#if defined(UNIT_TEST)
-  void set_saved_passwords_matching_domains(
-      const std::vector<std::string>& matching_domains) {
-    saved_passwords_matching_domains_ = matching_domains;
-  }
-#endif
-
-  virtual AccountInfo GetAccountInfo() const = 0;
-
- protected:
-  friend class PasswordProtectionRequest;
-
-  // Chrome can send password protection ping if it is allowed by for the
-  // |trigger_type| and if Safe Browsing can compute reputation of
-  // |main_frame_url| (e.g. Safe Browsing is not able to compute reputation of a
-  // private IP or a local host). Update |reason| if sending ping is not
-  // allowed. |password_type| is used for UMA metric recording.
-  bool CanSendPing(LoginReputationClientRequest::TriggerType trigger_type,
-                   const GURL& main_frame_url,
-                   ReusedPasswordAccountType password_type,
-                   RequestOutcome* reason);
-
-  // Called by a PasswordProtectionRequest instance when it finishes to remove
-  // itself from |requests_|.
-  virtual void RequestFinished(
-      PasswordProtectionRequest* request,
-      RequestOutcome outcome,
-      std::unique_ptr<LoginReputationClientResponse> response);
-
-  // Called by a PasswordProtectionRequest instance to check if a sample ping
-  // can be sent to Safe Browsing.
-  virtual bool CanSendSamplePing() = 0;
-
-  // Sanitize referrer chain by only keeping origin information of all URLs.
-  virtual void SanitizeReferrerChain(ReferrerChain* referrer_chain) = 0;
-
-  // Cancels all requests in |requests_|, empties it, and releases references to
-  // the requests.
-  void CancelPendingRequests();
-
-  // Gets the total number of verdicts of the specified |trigger_type| we cached
-  // for this profile. This counts both expired and active verdicts.
-  virtual int GetStoredVerdictCount(
-      LoginReputationClientRequest::TriggerType trigger_type);
-
-  // Gets an unowned |BrowserPolicyConnector| for the current platform.
-  virtual const policy::BrowserPolicyConnector* GetBrowserPolicyConnector()
-      const = 0;
-
-  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory() {
-    return url_loader_factory_;
-  }
-
-  // Returns the URL where PasswordProtectionRequest instances send requests.
-  static GURL GetPasswordProtectionRequestUrl();
-
-  // Gets the request timeout in milliseconds.
-  static int GetRequestTimeoutInMS();
-
-  // Obtains referrer chain of |event_url| and |event_tab_id| and adds this
-  // info into |frame|.
-  virtual void FillReferrerChain(
-      const GURL& event_url,
-      SessionID
-          event_tab_id,  // SessionID::InvalidValue() if tab not available.
-      LoginReputationClientRequest::Frame* frame) = 0;
-
-  void FillUserPopulation(
-      LoginReputationClientRequest::TriggerType trigger_type,
-      LoginReputationClientRequest* request_proto);
-
-  virtual bool IsExtendedReporting() = 0;
-
-  virtual bool IsIncognito() = 0;
-
-  virtual bool IsPingingEnabled(
-      LoginReputationClientRequest::TriggerType trigger_type,
-      ReusedPasswordAccountType password_type,
-      RequestOutcome* reason) = 0;
-
-  virtual bool IsHistorySyncEnabled() = 0;
-
-  // If primary account is syncing.
-  virtual bool IsPrimaryAccountSyncing() const = 0;
-
-  // If primary account is signed in.
-  virtual bool IsPrimaryAccountSignedIn() const = 0;
-
-  // If a domain is not defined for the primary account. This means the primary
-  // account is a Gmail account.
-  virtual bool IsPrimaryAccountGmail() const = 0;
-
-  // If the domain for the non sync account is equal to |kNoHostedDomainFound|,
-  // this means that the account is a Gmail account.
-  virtual bool IsOtherGaiaAccountGmail(const std::string& username) const = 0;
-
-  // Gets the account based off of the username from a list of signed in
-  // accounts.
-  virtual AccountInfo GetSignedInNonSyncAccount(
-      const std::string& username) const = 0;
-
-#if BUILDFLAG(FULL_SAFE_BROWSING)
-  virtual bool IsUnderAdvancedProtection() = 0;
-#endif
-
-#if defined(SYNC_PASSWORD_REUSE_WARNING_ENABLED)
-  // Records a Chrome Sync event for the result of the URL reputation lookup
-  // if the user enters their sync password on a website.
-  virtual void MaybeLogPasswordReuseLookupEvent(
-      content::WebContents* web_contents,
-      RequestOutcome,
-      PasswordType password_type,
-      const LoginReputationClientResponse*) = 0;
-
-  void RemoveWarningRequestsByWebContents(content::WebContents* web_contents);
-
-  bool IsModalWarningShowingInWebContents(content::WebContents* web_contents);
-
-  // Determines if we should show chrome://reset-password interstitial based on
-  // previous request outcome, the reused |password_type| and the
-  // |main_frame_url|.
-  virtual bool CanShowInterstitial(RequestOutcome reason,
-                                   ReusedPasswordAccountType password_type,
-                                   const GURL& main_frame_url) = 0;
-#endif
-
-  void CheckCsdWhitelistOnIOThread(const GURL& url, bool* check_result);
-
-  // Gets the type of sync account associated with current profile or
-  // |NOT_SIGNED_IN|.
-  virtual LoginReputationClientRequest::PasswordReuseEvent::SyncAccountType
-  GetSyncAccountType() const = 0;
-
-  const std::list<std::string>& common_spoofed_domains() const {
-    return common_spoofed_domains_;
-  }
-
- private:
-  friend class PasswordProtectionServiceTest;
-  friend class TestPasswordProtectionService;
-  friend class ChromePasswordProtectionServiceTest;
-  friend class ChromePasswordProtectionServiceBrowserTest;
-  FRIEND_TEST_ALL_PREFIXES(PasswordProtectionServiceTest,
-                           TestParseInvalidVerdictEntry);
-  FRIEND_TEST_ALL_PREFIXES(PasswordProtectionServiceTest,
-                           TestParseValidVerdictEntry);
-  FRIEND_TEST_ALL_PREFIXES(PasswordProtectionServiceTest,
-                           TestPathVariantsMatchCacheExpression);
-  FRIEND_TEST_ALL_PREFIXES(PasswordProtectionServiceTest,
-                           TestRemoveCachedVerdictOnURLsDeleted);
-  FRIEND_TEST_ALL_PREFIXES(PasswordProtectionServiceTest,
-                           TestCleanUpExpiredVerdict);
-
-  // Overridden from history::HistoryServiceObserver.
-  void OnURLsDeleted(history::HistoryService* history_service,
-                     const history::DeletionInfo& deletion_info) override;
-
-  void HistoryServiceBeingDeleted(
-      history::HistoryService* history_service) override;
-
-  // Posted to UI thread by OnURLsDeleted(...). This function remove the related
-  // entries in kSafeBrowsingUnhandledSyncPasswordReuses.
-  virtual void RemoveUnhandledSyncPasswordReuseOnURLsDeleted(
-      bool all_history,
-      const history::URLRows& deleted_rows) = 0;
-
-  static bool PathVariantsMatchCacheExpression(
-      const std::vector<std::string>& generated_paths,
-      const std::string& cache_expression_path);
-
-  void RecordNoPingingReason(
-      LoginReputationClientRequest::TriggerType trigger_type,
-      RequestOutcome reason,
-      PasswordType password_type);
-
-#if BUILDFLAG(FULL_SAFE_BROWSING)
-  // Get the content area size of current browsing window.
-  virtual gfx::Size GetCurrentContentAreaSize() const = 0;
-
-  // Binds the |phishing_detector| to the appropriate interface, as provided by
-  // |provider|.
-  virtual void GetPhishingDetector(
-      service_manager::InterfaceProvider* provider,
-      mojo::Remote<mojom::PhishingDetector>* phishing_detector);
-#endif
-
-  // The username of the account which password has been reused on. It is only
-  // set once a modal warning or interstitial is verified to be shown.
-  std::string username_for_last_shown_warning_ = "";
-
-  // The last ReusedPasswordAccountType that was shown a warning or
-  // interstitial.
-  ReusedPasswordAccountType
-      reused_password_account_type_for_last_shown_warning_;
-
-  std::vector<std::string> saved_passwords_matching_domains_;
-
-  scoped_refptr<SafeBrowsingDatabaseManager> database_manager_;
-
-  // The context we use to issue network requests. This request_context_getter
-  // is obtained from SafeBrowsingService so that we can use the Safe Browsing
-  // cookie store.
-  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
-
-  // Set of pending PasswordProtectionRequests that are still waiting for
-  // verdict.
-  std::set<scoped_refptr<PasswordProtectionRequest>> pending_requests_;
-
-  // Set of PasswordProtectionRequests that are triggering modal warnings.
-  std::set<scoped_refptr<PasswordProtectionRequest>> warning_requests_;
-
-  // List of most commonly spoofed domains to default to on the password warning
-  // dialog.
-  std::list<std::string> common_spoofed_domains_;
-
-  ScopedObserver<history::HistoryService, history::HistoryServiceObserver>
-      history_service_observer_{this};
-
-  // Weakptr can only cancel task if it is posted to the same thread. Therefore,
-  // we need CancelableTaskTracker to cancel tasks posted to IO thread.
-  base::CancelableTaskTracker tracker_;
-
-  base::WeakPtrFactory<PasswordProtectionService> weak_factory_{this};
-  DISALLOW_COPY_AND_ASSIGN(PasswordProtectionService);
-};
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_PASSWORD_PROTECTION_PASSWORD_PROTECTION_SERVICE_H_
diff --git a/components/safe_browsing/password_protection/password_protection_service_unittest.cc b/components/safe_browsing/password_protection/password_protection_service_unittest.cc
deleted file mode 100644
index 51051ce9..0000000
--- a/components/safe_browsing/password_protection/password_protection_service_unittest.cc
+++ /dev/null
@@ -1,1446 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-#include "components/safe_browsing/password_protection/password_protection_service.h"
-
-#include <memory>
-
-#include "base/bind.h"
-#include "base/run_loop.h"
-#include "base/single_thread_task_runner.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/test/metrics/histogram_tester.h"
-#include "base/test/null_task_runner.h"
-#include "base/test/scoped_feature_list.h"
-#include "base/time/time.h"
-#include "base/timer/timer.h"
-#include "build/build_config.h"
-#include "components/content_settings/core/browser/host_content_settings_map.h"
-#include "components/password_manager/core/browser/password_manager_metrics_util.h"
-#include "components/password_manager/core/browser/password_reuse_detector.h"
-#include "components/safe_browsing/common/safe_browsing.mojom-forward.h"
-#include "components/safe_browsing/common/safe_browsing.mojom.h"
-#include "components/safe_browsing/db/test_database_manager.h"
-#include "components/safe_browsing/features.h"
-#include "components/safe_browsing/password_protection/metrics_util.h"
-#include "components/safe_browsing/password_protection/mock_password_protection_service.h"
-#include "components/safe_browsing/password_protection/password_protection_navigation_throttle.h"
-#include "components/safe_browsing/password_protection/password_protection_request.h"
-#include "components/safe_browsing/proto/csd.pb.h"
-#include "components/safe_browsing/verdict_cache_manager.h"
-#include "components/signin/public/identity_manager/account_info.h"
-#include "components/sync_preferences/testing_pref_service_syncable.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/test/browser_task_environment.h"
-#include "content/public/test/test_browser_context.h"
-#include "content/public/test/test_renderer_host.h"
-#include "content/public/test/web_contents_tester.h"
-#include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "mojo/public/cpp/bindings/receiver.h"
-#include "mojo/public/cpp/system/message_pipe.h"
-#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
-#include "services/network/test/test_url_loader_factory.h"
-#include "services/service_manager/public/cpp/interface_provider.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using testing::_;
-using testing::AnyNumber;
-using testing::ElementsAre;
-using testing::IsEmpty;
-using testing::Return;
-
-namespace {
-
-const char kFormActionUrl[] = "https://form_action.com/";
-const char kPasswordFrameUrl[] = "https://password_frame.com/";
-const char kSavedDomain[] = "saved_domain.com";
-const char kSavedDomain2[] = "saved_domain2.com";
-const char kTargetUrl[] = "http://foo.com/";
-const char kUserName[] = "username";
-
-const unsigned int kMinute = 60;
-const unsigned int kDay = 24 * 60 * kMinute;
-
-}  // namespace
-
-namespace safe_browsing {
-
-using PasswordReuseEvent = LoginReputationClientRequest::PasswordReuseEvent;
-
-class MockSafeBrowsingDatabaseManager : public TestSafeBrowsingDatabaseManager {
- public:
-  MockSafeBrowsingDatabaseManager() {}
-
-  MOCK_METHOD2(CheckCsdWhitelistUrl,
-               AsyncMatch(const GURL&, SafeBrowsingDatabaseManager::Client*));
-
- protected:
-  ~MockSafeBrowsingDatabaseManager() override {}
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockSafeBrowsingDatabaseManager);
-};
-
-class TestPhishingDetector : public mojom::PhishingDetector {
- public:
-  TestPhishingDetector() : should_timeout_(false) {}
-  ~TestPhishingDetector() override {}
-
-  void Bind(mojo::ScopedMessagePipeHandle handle) {
-    receiver_.Bind(
-        mojo::PendingReceiver<mojom::PhishingDetector>(std::move(handle)));
-  }
-
-  void StartPhishingDetection(
-      const GURL& url,
-      StartPhishingDetectionCallback callback) override {
-    if (should_timeout_) {
-      deferred_callbacks_.push_back(std::move(callback));
-    } else {
-      ReturnFeatures(url, std::move(callback));
-    }
-  }
-  void ReturnFeatures(const GURL& url,
-                      StartPhishingDetectionCallback callback) {
-    ClientPhishingRequest verdict;
-    verdict.set_is_phishing(false);
-    verdict.set_client_score(0.1);
-    std::move(callback).Run(mojom::PhishingDetectorResult::SUCCESS,
-                            verdict.SerializeAsString());
-  }
-
-  void set_should_timeout(bool timeout) { should_timeout_ = timeout; }
-
- private:
-  bool should_timeout_;
-  std::vector<StartPhishingDetectionCallback> deferred_callbacks_;
-  mojo::Receiver<mojom::PhishingDetector> receiver_{this};
-
-  DISALLOW_COPY_AND_ASSIGN(TestPhishingDetector);
-};
-
-class TestPasswordProtectionService : public MockPasswordProtectionService {
- public:
-  TestPasswordProtectionService(
-      const scoped_refptr<SafeBrowsingDatabaseManager>& database_manager,
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      scoped_refptr<HostContentSettingsMap> content_setting_map)
-      : MockPasswordProtectionService(database_manager,
-                                      url_loader_factory,
-                                      nullptr),
-        cache_manager_(
-            std::make_unique<VerdictCacheManager>(nullptr,
-                                                  content_setting_map.get())) {}
-
-  void RequestFinished(
-      PasswordProtectionRequest* request,
-      RequestOutcome outcome,
-      std::unique_ptr<LoginReputationClientResponse> response) override {
-    latest_request_ = request;
-    latest_response_ = std::move(response);
-    run_loop_.Quit();
-  }
-
-  LoginReputationClientResponse* latest_response() {
-    return latest_response_.get();
-  }
-
-  void WaitForResponse() { run_loop_.Run(); }
-
-  ~TestPasswordProtectionService() override {}
-
-  size_t GetPendingRequestsCount() { return pending_requests_.size(); }
-
-  const LoginReputationClientRequest* GetLatestRequestProto() {
-    return latest_request_ ? latest_request_->request_proto() : nullptr;
-  }
-
-  void GetPhishingDetector(
-      service_manager::InterfaceProvider* provider,
-      mojo::Remote<mojom::PhishingDetector>* phishing_detector) override {
-    service_manager::InterfaceProvider::TestApi test_api(provider);
-    test_api.SetBinderForName(
-        mojom::PhishingDetector::Name_,
-        base::BindRepeating(&TestPhishingDetector::Bind,
-                            base::Unretained(&test_phishing_detector_)));
-    provider->GetInterface(phishing_detector->BindNewPipeAndPassReceiver());
-    test_api.ClearBinderForName(mojom::PhishingDetector::Name_);
-  }
-
-  void CacheVerdict(const GURL& url,
-                    LoginReputationClientRequest::TriggerType trigger_type,
-                    ReusedPasswordAccountType password_type,
-                    const LoginReputationClientResponse& verdict,
-                    const base::Time& receive_time) override {
-    if (!CanGetReputationOfURL(url) || IsIncognito())
-      return;
-
-    cache_manager_->CachePhishGuardVerdict(url, trigger_type, password_type,
-                                           verdict, receive_time);
-  }
-
-  LoginReputationClientResponse::VerdictType GetCachedVerdict(
-      const GURL& url,
-      LoginReputationClientRequest::TriggerType trigger_type,
-      ReusedPasswordAccountType password_type,
-      LoginReputationClientResponse* out_response) override {
-    if (!url.is_valid() || !CanGetReputationOfURL(url))
-      return LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED;
-
-    return cache_manager_->GetCachedPhishGuardVerdict(
-        url, trigger_type, password_type, out_response);
-  }
-
-  int GetStoredVerdictCount(
-      LoginReputationClientRequest::TriggerType trigger_type) override {
-    return cache_manager_->GetStoredPhishGuardVerdictCount(trigger_type);
-  }
-
-  void SetDomFeatureCollectionTimeout(bool should_timeout) {
-    test_phishing_detector_.set_should_timeout(should_timeout);
-  }
-
- private:
-  PasswordProtectionRequest* latest_request_;
-  base::RunLoop run_loop_;
-  std::unique_ptr<LoginReputationClientResponse> latest_response_;
-  TestPhishingDetector test_phishing_detector_;
-
-  // The TestPasswordProtectionService manages its own cache, rather than using
-  // the global one.
-  std::unique_ptr<VerdictCacheManager> cache_manager_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestPasswordProtectionService);
-};
-
-class MockPasswordProtectionNavigationThrottle
-    : public PasswordProtectionNavigationThrottle {
- public:
-  MockPasswordProtectionNavigationThrottle(
-      content::NavigationHandle* navigation_handle,
-      scoped_refptr<PasswordProtectionRequest> request,
-      bool is_warning_showing)
-      : PasswordProtectionNavigationThrottle(navigation_handle,
-                                             request,
-                                             is_warning_showing) {}
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockPasswordProtectionNavigationThrottle);
-};
-
-class PasswordProtectionServiceTest : public ::testing::TestWithParam<bool> {
- public:
-  PasswordProtectionServiceTest()
-      : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
-
-  LoginReputationClientResponse CreateVerdictProto(
-      LoginReputationClientResponse::VerdictType verdict,
-      int cache_duration_sec,
-      const std::string& cache_expression) {
-    LoginReputationClientResponse verdict_proto;
-    verdict_proto.set_verdict_type(verdict);
-    verdict_proto.set_cache_duration_sec(cache_duration_sec);
-    verdict_proto.set_cache_expression(cache_expression);
-    return verdict_proto;
-  }
-
-  void SetUp() override {
-    HostContentSettingsMap::RegisterProfilePrefs(test_pref_service_.registry());
-    content_setting_map_ = new HostContentSettingsMap(
-        &test_pref_service_, false /* is_off_the_record */,
-        false /* store_last_modified */,
-        false /* migrate_requesting_and_top_level_origin_settings */);
-    database_manager_ = new MockSafeBrowsingDatabaseManager();
-    password_protection_service_ =
-        std::make_unique<TestPasswordProtectionService>(
-            database_manager_,
-            base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
-                &test_url_loader_factory_),
-            content_setting_map_);
-    EXPECT_CALL(*password_protection_service_, IsExtendedReporting())
-        .WillRepeatedly(Return(GetParam()));
-    EXPECT_CALL(*password_protection_service_, IsIncognito())
-        .WillRepeatedly(Return(false));
-    EXPECT_CALL(*password_protection_service_,
-                IsURLWhitelistedForPasswordEntry(_, _))
-        .WillRepeatedly(Return(false));
-    EXPECT_CALL(*password_protection_service_,
-                GetPasswordProtectionWarningTriggerPref(_))
-        .WillRepeatedly(Return(PASSWORD_PROTECTION_OFF));
-    url_ = PasswordProtectionService::GetPasswordProtectionRequestUrl();
-  }
-
-  void TearDown() override {
-    password_protection_service_.reset();
-    content_setting_map_->ShutdownOnUIThread();
-  }
-
-  // Sets up |database_manager_| and |pending_requests_| as needed.
-  void InitializeAndStartPasswordOnFocusRequest(
-      bool match_whitelist,
-      int timeout_in_ms,
-      content::WebContents* web_contents) {
-    GURL target_url(kTargetUrl);
-    EXPECT_CALL(*database_manager_, CheckCsdWhitelistUrl(target_url, _))
-        .WillRepeatedly(
-            Return(match_whitelist ? AsyncMatch::MATCH : AsyncMatch::NO_MATCH));
-
-    request_ = new PasswordProtectionRequest(
-        web_contents, target_url, GURL(kFormActionUrl), GURL(kPasswordFrameUrl),
-        kUserName, PasswordType::PASSWORD_TYPE_UNKNOWN, {},
-        LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE, true,
-        password_protection_service_.get(), timeout_in_ms);
-    request_->Start();
-  }
-
-  void InitializeAndStartPasswordEntryRequest(
-      PasswordType type,
-      const std::vector<std::string>& matching_domains,
-      bool match_whitelist,
-      int timeout_in_ms,
-      content::WebContents* web_contents) {
-    GURL target_url(kTargetUrl);
-    EXPECT_CALL(*database_manager_, CheckCsdWhitelistUrl(target_url, _))
-        .WillRepeatedly(
-            Return(match_whitelist ? AsyncMatch::MATCH : AsyncMatch::NO_MATCH));
-
-    request_ = new PasswordProtectionRequest(
-        web_contents, target_url, GURL(), GURL(), kUserName, type,
-        matching_domains, LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-        true, password_protection_service_.get(), timeout_in_ms);
-    request_->Start();
-  }
-
-  void CacheVerdict(const GURL& url,
-                    LoginReputationClientRequest::TriggerType trigger,
-                    ReusedPasswordAccountType password_type,
-                    LoginReputationClientResponse::VerdictType verdict,
-                    int cache_duration_sec,
-                    const std::string& cache_expression,
-                    const base::Time& verdict_received_time) {
-    ASSERT_FALSE(cache_expression.empty());
-    LoginReputationClientResponse response(
-        CreateVerdictProto(verdict, cache_duration_sec, cache_expression));
-    password_protection_service_->CacheVerdict(url, trigger, password_type,
-                                               response, verdict_received_time);
-  }
-
-  void CacheInvalidVerdict(ReusedPasswordAccountType password_type) {
-    GURL invalid_hostname("http://invalid.com");
-    std::unique_ptr<base::DictionaryValue> verdict_dictionary =
-        base::DictionaryValue::From(content_setting_map_->GetWebsiteSetting(
-            invalid_hostname, GURL(), ContentSettingsType::PASSWORD_PROTECTION,
-            std::string(), nullptr));
-
-    if (!verdict_dictionary)
-      verdict_dictionary = std::make_unique<base::DictionaryValue>();
-
-    std::unique_ptr<base::DictionaryValue> invalid_verdict_entry =
-        std::make_unique<base::DictionaryValue>();
-    invalid_verdict_entry->SetString("invalid", "invalid_string");
-
-    std::unique_ptr<base::DictionaryValue> invalid_cache_expression_entry =
-        std::make_unique<base::DictionaryValue>();
-    invalid_cache_expression_entry->SetWithoutPathExpansion(
-        "invalid_cache_expression", std::move(invalid_verdict_entry));
-    verdict_dictionary->SetWithoutPathExpansion(
-        base::NumberToString(static_cast<std::underlying_type_t<PasswordType>>(
-            password_protection_service_
-                ->ConvertReusedPasswordAccountTypeToPasswordType(
-                    password_type))),
-        std::move(invalid_cache_expression_entry));
-    content_setting_map_->SetWebsiteSettingDefaultScope(
-        invalid_hostname, GURL(), ContentSettingsType::PASSWORD_PROTECTION,
-        std::string(), std::move(verdict_dictionary));
-  }
-
-  size_t GetStoredVerdictCount(LoginReputationClientRequest::TriggerType type) {
-    return password_protection_service_->GetStoredVerdictCount(type);
-  }
-
-  std::unique_ptr<content::WebContents> GetWebContents() {
-    return base::WrapUnique(content::WebContentsTester::CreateTestWebContents(
-        content::WebContents::CreateParams(&browser_context_)));
-  }
-
-  void VerifyContentAreaSizeCollection(
-      const LoginReputationClientRequest& request) {
-    bool should_report_content_size =
-        password_protection_service_->IsExtendedReporting() &&
-        !password_protection_service_->IsIncognito();
-    EXPECT_EQ(should_report_content_size, request.has_content_area_height());
-    EXPECT_EQ(should_report_content_size, request.has_content_area_width());
-  }
-
-  size_t GetNumberOfNavigationThrottles() {
-    return request_ ? request_->throttles_.size() : 0u;
-  }
-
- protected:
-  // |task_environment_| is needed here because this test involves both UI and
-  // IO threads.
-  content::BrowserTaskEnvironment task_environment_;
-  scoped_refptr<MockSafeBrowsingDatabaseManager> database_manager_;
-  sync_preferences::TestingPrefServiceSyncable test_pref_service_;
-  scoped_refptr<HostContentSettingsMap> content_setting_map_;
-  GURL url_;
-  network::TestURLLoaderFactory test_url_loader_factory_;
-  std::unique_ptr<TestPasswordProtectionService> password_protection_service_;
-  scoped_refptr<PasswordProtectionRequest> request_;
-  base::HistogramTester histograms_;
-  content::TestBrowserContext browser_context_;
-  content::RenderViewHostTestEnabler rvh_test_enabler_;
-};
-
-TEST_P(PasswordProtectionServiceTest, TestCachePasswordReuseVerdicts) {
-  ASSERT_EQ(0U, GetStoredVerdictCount(
-                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
-  EXPECT_CALL(*password_protection_service_, IsPrimaryAccountSignedIn())
-      .WillRepeatedly(Return(true));
-  // Assume each verdict has a TTL of 10 minutes.
-  // Cache a verdict for http://www.test.com/foo/index.html
-  ReusedPasswordAccountType reused_password_account_type;
-  reused_password_account_type.set_account_type(
-      ReusedPasswordAccountType::GSUITE);
-  reused_password_account_type.set_is_account_syncing(true);
-  CacheVerdict(GURL("http://www.test.com/foo/index.html"),
-               LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-               reused_password_account_type,
-               LoginReputationClientResponse::SAFE, 10 * kMinute,
-               "test.com/foo/", base::Time::Now());
-
-  EXPECT_EQ(1U, GetStoredVerdictCount(
-                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
-
-  // Cache another verdict with the some origin and cache_expression should
-  // override the cache.
-  CacheVerdict(GURL("http://www.test.com/foo/index2.html"),
-               LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-               reused_password_account_type,
-               LoginReputationClientResponse::PHISHING, 10 * kMinute,
-               "test.com/foo/", base::Time::Now());
-  EXPECT_EQ(1U, GetStoredVerdictCount(
-                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
-  LoginReputationClientResponse out_verdict;
-  EXPECT_EQ(LoginReputationClientResponse::PHISHING,
-            password_protection_service_->GetCachedVerdict(
-                GURL("http://www.test.com/foo/index2.html"),
-                LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-                reused_password_account_type, &out_verdict));
-
-  // Cache a password reuse verdict with a different password type but same
-  // origin and cache expression should add a new entry.
-  reused_password_account_type.set_account_type(
-      ReusedPasswordAccountType::NON_GAIA_ENTERPRISE);
-  CacheVerdict(GURL("http://www.test.com/foo/index2.html"),
-               LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-               reused_password_account_type,
-               LoginReputationClientResponse::PHISHING, 10 * kMinute,
-               "test.com/foo/", base::Time::Now());
-  EXPECT_EQ(2U, GetStoredVerdictCount(
-                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
-  EXPECT_EQ(LoginReputationClientResponse::PHISHING,
-            password_protection_service_->GetCachedVerdict(
-                GURL("http://www.test.com/foo/index2.html"),
-                LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-                reused_password_account_type, &out_verdict));
-
-  // Cache another verdict with the same origin but different cache_expression
-  // will not increase setting count, but will increase the number of verdicts
-  // in the given origin.
-  CacheVerdict(GURL("http://www.test.com/bar/index2.html"),
-               LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-               reused_password_account_type,
-               LoginReputationClientResponse::SAFE, 10 * kMinute,
-               "test.com/bar/", base::Time::Now());
-  EXPECT_EQ(3U, GetStoredVerdictCount(
-                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
-
-  // Now cache a UNFAMILIAR_LOGIN_PAGE verdict, stored verdict count for
-  // PASSWORD_REUSE_EVENT should be the same.
-  CacheVerdict(GURL("http://www.test.com/foobar/index3.html"),
-               LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
-               reused_password_account_type,
-               LoginReputationClientResponse::SAFE, 10 * kMinute,
-               "test.com/foobar/", base::Time::Now());
-  EXPECT_EQ(3U, GetStoredVerdictCount(
-                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
-  EXPECT_EQ(1U, GetStoredVerdictCount(
-                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
-}
-
-TEST_P(PasswordProtectionServiceTest, TestCachePasswordReuseVerdictsIncognito) {
-  EXPECT_CALL(*password_protection_service_, IsIncognito())
-      .WillRepeatedly(Return(true));
-  ASSERT_EQ(0U, GetStoredVerdictCount(
-                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
-
-  ReusedPasswordAccountType reused_password_account_type;
-  reused_password_account_type.set_account_type(
-      ReusedPasswordAccountType::GSUITE);
-  reused_password_account_type.set_is_account_syncing(true);
-  // No verdict will be cached for incognito profile.
-  CacheVerdict(GURL("http://www.test.com/foo/index.html"),
-               LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-               reused_password_account_type,
-               LoginReputationClientResponse::SAFE, 10 * kMinute,
-               "test.com/foo/", base::Time::Now());
-
-  EXPECT_EQ(0U, GetStoredVerdictCount(
-                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
-
-  // Try cache another verdict with the some origin and cache_expression.
-  // Verdict count should not increase.
-  CacheVerdict(GURL("http://www.test.com/foo/index2.html"),
-               LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-               reused_password_account_type,
-               LoginReputationClientResponse::PHISHING, 10 * kMinute,
-               "test.com/foo/", base::Time::Now());
-  EXPECT_EQ(0U, GetStoredVerdictCount(
-                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
-
-  // Now cache a UNFAMILIAR_LOGIN_PAGE verdict, verdict count should not
-  // increase.
-  CacheVerdict(GURL("http://www.test.com/foobar/index3.html"),
-               LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
-               reused_password_account_type,
-               LoginReputationClientResponse::SAFE, 10 * kMinute,
-               "test.com/foobar/", base::Time::Now());
-  EXPECT_EQ(0U, GetStoredVerdictCount(
-                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
-  EXPECT_EQ(0U, GetStoredVerdictCount(
-                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
-}
-
-TEST_P(PasswordProtectionServiceTest, TestCacheUnfamiliarLoginVerdicts) {
-  ASSERT_EQ(0U, GetStoredVerdictCount(
-                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
-  ReusedPasswordAccountType reused_password_account_type;
-  reused_password_account_type.set_account_type(
-      ReusedPasswordAccountType::UNKNOWN);
-  reused_password_account_type.set_is_account_syncing(true);
-  // Assume each verdict has a TTL of 10 minutes.
-  // Cache a verdict for http://www.test.com/foo/index.html
-  CacheVerdict(GURL("http://www.test.com/foo/index.html"),
-               LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
-               reused_password_account_type,
-               LoginReputationClientResponse::SAFE, 10 * kMinute,
-               "test.com/foo/", base::Time::Now());
-
-  EXPECT_EQ(1U, GetStoredVerdictCount(
-                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
-
-  // Cache another verdict with the same origin but different cache_expression
-  // will not increase setting count, but will increase the number of verdicts
-  // in the given origin.
-  CacheVerdict(GURL("http://www.test.com/bar/index2.html"),
-               LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
-               reused_password_account_type,
-               LoginReputationClientResponse::SAFE, 10 * kMinute,
-               "test.com/bar/", base::Time::Now());
-  EXPECT_EQ(2U, GetStoredVerdictCount(
-                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
-
-  // Now cache a PASSWORD_REUSE_EVENT verdict, stored verdict count for
-  // UNFAMILIAR_LOGIN_PAGE should be the same.
-  CacheVerdict(GURL("http://www.test.com/foobar/index3.html"),
-               LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-               reused_password_account_type,
-               LoginReputationClientResponse::SAFE, 10 * kMinute,
-               "test.com/foobar/", base::Time::Now());
-  EXPECT_EQ(2U, GetStoredVerdictCount(
-                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
-  EXPECT_EQ(1U, GetStoredVerdictCount(
-                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
-}
-
-TEST_P(PasswordProtectionServiceTest,
-       TestCacheUnfamiliarLoginVerdictsIncognito) {
-  EXPECT_CALL(*password_protection_service_, IsIncognito())
-      .WillRepeatedly(Return(true));
-  ASSERT_EQ(0U, GetStoredVerdictCount(
-                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
-
-  ReusedPasswordAccountType reused_password_account_type;
-  reused_password_account_type.set_account_type(
-      ReusedPasswordAccountType::UNKNOWN);
-  reused_password_account_type.set_is_account_syncing(true);
-  // No verdict will be cached for incognito profile.
-  CacheVerdict(GURL("http://www.test.com/foo/index.html"),
-               LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
-               reused_password_account_type,
-               LoginReputationClientResponse::SAFE, 10 * kMinute,
-               "test.com/foo/", base::Time::Now());
-
-  EXPECT_EQ(0U, GetStoredVerdictCount(
-                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
-
-  CacheVerdict(GURL("http://www.test.com/bar/index2.html"),
-               LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
-               reused_password_account_type,
-               LoginReputationClientResponse::SAFE, 10 * kMinute,
-               "test.com/bar/", base::Time::Now());
-  EXPECT_EQ(0U, GetStoredVerdictCount(
-                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
-
-  // Now cache a PASSWORD_REUSE_EVENT verdict. Verdict count should not
-  // increase.
-  reused_password_account_type.set_account_type(
-      ReusedPasswordAccountType::GSUITE);
-  reused_password_account_type.set_is_account_syncing(true);
-  CacheVerdict(GURL("http://www.test.com/foobar/index3.html"),
-               LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-               reused_password_account_type,
-               LoginReputationClientResponse::SAFE, 10 * kMinute,
-               "test.com/foobar/", base::Time::Now());
-  EXPECT_EQ(0U, GetStoredVerdictCount(
-                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
-  EXPECT_EQ(0U, GetStoredVerdictCount(
-                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
-}
-
-TEST_P(PasswordProtectionServiceTest, TestGetCachedVerdicts) {
-  ASSERT_EQ(0U, GetStoredVerdictCount(
-                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
-  ASSERT_EQ(0U, GetStoredVerdictCount(
-                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
-  ReusedPasswordAccountType reused_password_account_type;
-  reused_password_account_type.set_account_type(
-      ReusedPasswordAccountType::GSUITE);
-  reused_password_account_type.set_is_account_syncing(true);
-  // Prepare 4 verdicts of the same origin with different cache expressions,
-  // or password type, one is expired, one is not, one is of a different
-  // trigger type, and the other is with a different password type.
-  base::Time now = base::Time::Now();
-  CacheVerdict(GURL("http://test.com/login.html"),
-               LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-               reused_password_account_type,
-               LoginReputationClientResponse::SAFE, 10 * kMinute, "test.com/",
-               now);
-  CacheVerdict(
-      GURL("http://test.com/def/index.jsp"),
-      LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-      reused_password_account_type, LoginReputationClientResponse::PHISHING,
-      10 * kMinute, "test.com/def/",
-      base::Time::FromDoubleT(now.ToDoubleT() - kDay));  // Yesterday, expired.
-  reused_password_account_type.set_account_type(
-      ReusedPasswordAccountType::UNKNOWN);
-  CacheVerdict(GURL("http://test.com/bar/login.html"),
-               LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
-               reused_password_account_type,
-               LoginReputationClientResponse::PHISHING, 10 * kMinute,
-               "test.com/bar/", now);
-  reused_password_account_type.set_account_type(
-      ReusedPasswordAccountType::NON_GAIA_ENTERPRISE);
-  CacheVerdict(GURL("http://test.com/login.html"),
-               LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-               reused_password_account_type,
-               LoginReputationClientResponse::SAFE, 10 * kMinute, "test.com/",
-               now);
-
-  ASSERT_EQ(3U, GetStoredVerdictCount(
-                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
-  ASSERT_EQ(1U, GetStoredVerdictCount(
-                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
-
-  // Return VERDICT_TYPE_UNSPECIFIED if look up for a URL with unknown origin.
-  LoginReputationClientResponse actual_verdict;
-  reused_password_account_type.set_account_type(
-      ReusedPasswordAccountType::GSUITE);
-  EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
-            password_protection_service_->GetCachedVerdict(
-                GURL("http://www.unknown.com/"),
-                LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-                reused_password_account_type, &actual_verdict));
-  reused_password_account_type.set_account_type(
-      ReusedPasswordAccountType::NON_GAIA_ENTERPRISE);
-  EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
-            password_protection_service_->GetCachedVerdict(
-                GURL("http://www.unknown.com/"),
-                LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-                reused_password_account_type, &actual_verdict));
-
-  // Return SAFE if look up for a URL that matches "test.com" cache expression.
-  reused_password_account_type.set_account_type(
-      ReusedPasswordAccountType::GSUITE);
-  EXPECT_EQ(LoginReputationClientResponse::SAFE,
-            password_protection_service_->GetCachedVerdict(
-                GURL("http://test.com/xyz/foo.jsp"),
-                LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-                reused_password_account_type, &actual_verdict));
-  reused_password_account_type.set_account_type(
-      ReusedPasswordAccountType::NON_GAIA_ENTERPRISE);
-  EXPECT_EQ(LoginReputationClientResponse::SAFE,
-            password_protection_service_->GetCachedVerdict(
-                GURL("http://test.com/xyz/foo.jsp"),
-                LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-                reused_password_account_type, &actual_verdict));
-
-  // Return VERDICT_TYPE_UNSPECIFIED if look up for a URL whose variants match
-  // test.com/def, but the corresponding verdict is expired.
-  reused_password_account_type.set_account_type(
-      ReusedPasswordAccountType::GSUITE);
-  EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
-            password_protection_service_->GetCachedVerdict(
-                GURL("http://test.com/def/ghi/index.html"),
-                LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-                reused_password_account_type, &actual_verdict));
-
-  // Return PHISHING. Matches "test.com/bar/" cache expression.
-  reused_password_account_type.set_account_type(
-      ReusedPasswordAccountType::UNKNOWN);
-  EXPECT_EQ(LoginReputationClientResponse::PHISHING,
-            password_protection_service_->GetCachedVerdict(
-                GURL("http://test.com/bar/foo.jsp"),
-                LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
-                reused_password_account_type, &actual_verdict));
-
-  // Now cache SAFE verdict for the full path.
-  CacheVerdict(GURL("http://test.com/bar/foo.jsp"),
-               LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
-               reused_password_account_type,
-               LoginReputationClientResponse::SAFE, 10 * kMinute,
-               "test.com/bar/foo.jsp", now);
-
-  // Return SAFE now. Matches the full cache expression.
-  EXPECT_EQ(LoginReputationClientResponse::SAFE,
-            password_protection_service_->GetCachedVerdict(
-                GURL("http://test.com/bar/foo.jsp"),
-                LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
-                reused_password_account_type, &actual_verdict));
-}
-
-TEST_P(PasswordProtectionServiceTest, TestDoesNotCacheAboutBlank) {
-  ASSERT_EQ(0U, GetStoredVerdictCount(
-                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
-  ReusedPasswordAccountType reused_password_account_type;
-  reused_password_account_type.set_account_type(
-      ReusedPasswordAccountType::UNKNOWN);
-
-  // Should not actually cache, since about:blank is not valid for reputation
-  // computing.
-  CacheVerdict(
-      GURL("about:blank"), LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
-      reused_password_account_type, LoginReputationClientResponse::SAFE,
-      10 * kMinute, "about:blank", base::Time::Now());
-
-  EXPECT_EQ(0U, GetStoredVerdictCount(
-                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
-}
-
-TEST_P(PasswordProtectionServiceTest, VerifyCanGetReputationOfURL) {
-  // Invalid main frame URL.
-  EXPECT_FALSE(PasswordProtectionService::CanGetReputationOfURL(GURL()));
-
-  // Main frame URL scheme is not HTTP or HTTPS.
-  EXPECT_FALSE(PasswordProtectionService::CanGetReputationOfURL(
-      GURL("data:text/html, <p>hellow")));
-
-  // Main frame URL is a local host.
-  EXPECT_FALSE(PasswordProtectionService::CanGetReputationOfURL(
-      GURL("http://localhost:80")));
-  EXPECT_FALSE(PasswordProtectionService::CanGetReputationOfURL(
-      GURL("http://127.0.0.1")));
-
-  // Main frame URL is a private IP address or anything in an IANA-reserved
-  // range.
-  EXPECT_FALSE(PasswordProtectionService::CanGetReputationOfURL(
-      GURL("http://192.168.1.0/")));
-  EXPECT_FALSE(PasswordProtectionService::CanGetReputationOfURL(
-      GURL("http://10.0.1.0/")));
-  EXPECT_FALSE(PasswordProtectionService::CanGetReputationOfURL(
-      GURL("http://[FEED::BEEF]")));
-
-  // Main frame URL is a no-yet-assigned y ICANN gTLD.
-  EXPECT_FALSE(PasswordProtectionService::CanGetReputationOfURL(
-      GURL("http://intranet")));
-  EXPECT_FALSE(PasswordProtectionService::CanGetReputationOfURL(
-      GURL("http://host.intranet.example")));
-
-  // Main frame URL is a dotless domain.
-  EXPECT_FALSE(PasswordProtectionService::CanGetReputationOfURL(
-      GURL("http://go/example")));
-
-  // Main frame URL is anything else.
-  EXPECT_TRUE(PasswordProtectionService::CanGetReputationOfURL(
-      GURL("http://www.chromium.org")));
-}
-
-TEST_P(PasswordProtectionServiceTest, TestNoRequestSentForWhitelistedURL) {
-  histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogram, 0);
-  std::unique_ptr<content::WebContents> web_contents = GetWebContents();
-  content::WebContentsTester::For(web_contents.get())
-      ->SetLastCommittedURL(GURL("http://safe.com/"));
-  InitializeAndStartPasswordOnFocusRequest(true /* match whitelist */,
-                                           10000 /* timeout in ms */,
-                                           web_contents.get());
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(nullptr, password_protection_service_->latest_response());
-  EXPECT_THAT(
-      histograms_.GetAllSamples(kPasswordOnFocusRequestOutcomeHistogram),
-      ElementsAre(base::Bucket(4 /* MATCHED_WHITELIST */, 1)));
-}
-
-// crbug.com/1010007: crashes on win
-#if defined(OS_WIN)
-#define MAYBE_TestNoRequestSentIfVerdictAlreadyCached \
-  DISABLED_TestNoRequestSentIfVerdictAlreadyCached
-#else
-#define MAYBE_TestNoRequestSentIfVerdictAlreadyCached \
-  TestNoRequestSentIfVerdictAlreadyCached
-#endif
-TEST_P(PasswordProtectionServiceTest,
-       MAYBE_TestNoRequestSentIfVerdictAlreadyCached) {
-  histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogram, 0);
-  ReusedPasswordAccountType reused_password_account_type;
-  reused_password_account_type.set_account_type(
-      ReusedPasswordAccountType::UNKNOWN);
-  std::unique_ptr<content::WebContents> web_contents = GetWebContents();
-  CacheVerdict(GURL(kTargetUrl),
-               LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
-               reused_password_account_type,
-               LoginReputationClientResponse::LOW_REPUTATION, 10 * kMinute,
-               GURL(kTargetUrl).host().append("/"), base::Time::Now());
-  InitializeAndStartPasswordOnFocusRequest(/*match_whitelist=*/false,
-                                           /*timeout_in_ms=*/10000,
-                                           web_contents.get());
-  base::RunLoop().RunUntilIdle();
-  EXPECT_THAT(
-      histograms_.GetAllSamples(kPasswordOnFocusRequestOutcomeHistogram),
-      ElementsAre(base::Bucket(5 /* RESPONSE_ALREADY_CACHED */, 1)));
-  EXPECT_EQ(LoginReputationClientResponse::LOW_REPUTATION,
-            password_protection_service_->latest_response()->verdict_type());
-}
-
-TEST_P(PasswordProtectionServiceTest, TestResponseFetchFailed) {
-  histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogram, 0);
-  // Set up failed response.
-  network::URLLoaderCompletionStatus status(net::ERR_FAILED);
-  test_url_loader_factory_.AddResponse(
-      url_, network::mojom::URLResponseHead::New(), std::string(), status);
-  std::unique_ptr<content::WebContents> web_contents = GetWebContents();
-
-  InitializeAndStartPasswordOnFocusRequest(/*match_whitelist=*/false,
-                                           /*timeout_in_ms=*/10000,
-                                           web_contents.get());
-  password_protection_service_->WaitForResponse();
-  EXPECT_EQ(nullptr, password_protection_service_->latest_response());
-  EXPECT_THAT(
-      histograms_.GetAllSamples(kPasswordOnFocusRequestOutcomeHistogram),
-      ElementsAre(base::Bucket(9 /* FETCH_FAILED */, 1)));
-}
-
-TEST_P(PasswordProtectionServiceTest, TestMalformedResponse) {
-  histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogram, 0);
-  // Set up malformed response.
-  test_url_loader_factory_.AddResponse(url_.spec(), "invalid response");
-  std::unique_ptr<content::WebContents> web_contents = GetWebContents();
-
-  InitializeAndStartPasswordOnFocusRequest(/*match_whitelist=*/false,
-                                           /*timeout_in_ms=*/10000,
-                                           web_contents.get());
-  password_protection_service_->WaitForResponse();
-  EXPECT_EQ(nullptr, password_protection_service_->latest_response());
-  EXPECT_THAT(
-      histograms_.GetAllSamples(kPasswordOnFocusRequestOutcomeHistogram),
-      ElementsAre(base::Bucket(10 /* RESPONSE_MALFORMED */, 1)));
-}
-
-TEST_P(PasswordProtectionServiceTest, TestRequestTimedout) {
-  histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogram, 0);
-  std::unique_ptr<content::WebContents> web_contents = GetWebContents();
-  InitializeAndStartPasswordOnFocusRequest(/*match_whitelist=*/false,
-                                           /*timeout_in_ms=*/0,
-                                           web_contents.get());
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(nullptr, password_protection_service_->latest_response());
-  EXPECT_THAT(
-      histograms_.GetAllSamples(kPasswordOnFocusRequestOutcomeHistogram),
-      ElementsAre(base::Bucket(3 /* TIMEDOUT */, 1)));
-}
-
-TEST_P(PasswordProtectionServiceTest,
-       TestPasswordOnFocusRequestAndResponseSuccessfull) {
-  histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogram, 0);
-  // Set up valid response.
-  LoginReputationClientResponse expected_response =
-      CreateVerdictProto(LoginReputationClientResponse::PHISHING, 10 * kMinute,
-                         GURL(kTargetUrl).host());
-  test_url_loader_factory_.AddResponse(url_.spec(),
-                                       expected_response.SerializeAsString());
-  std::unique_ptr<content::WebContents> web_contents = GetWebContents();
-
-  InitializeAndStartPasswordOnFocusRequest(/*match_whitelist=*/false,
-                                           /*timeout_in_ms=*/10000,
-                                           web_contents.get());
-  password_protection_service_->WaitForResponse();
-  EXPECT_THAT(
-      histograms_.GetAllSamples(kPasswordOnFocusRequestOutcomeHistogram),
-      ElementsAre(base::Bucket(1 /* SUCCEEDED */, 1)));
-  EXPECT_THAT(histograms_.GetAllSamples(kPasswordOnFocusVerdictHistogram),
-              ElementsAre(base::Bucket(3 /* PHISHING */, 1)));
-  LoginReputationClientResponse* actual_response =
-      password_protection_service_->latest_response();
-  EXPECT_EQ(expected_response.verdict_type(), actual_response->verdict_type());
-  EXPECT_EQ(expected_response.cache_expression(),
-            actual_response->cache_expression());
-  EXPECT_EQ(expected_response.cache_duration_sec(),
-            actual_response->cache_duration_sec());
-}
-
-TEST_P(PasswordProtectionServiceTest,
-       TestProtectedPasswordEntryRequestAndResponseSuccessfull) {
-  histograms_.ExpectTotalCount(kAnyPasswordEntryRequestOutcomeHistogram, 0);
-  histograms_.ExpectTotalCount(kSyncPasswordEntryRequestOutcomeHistogram, 0);
-  histograms_.ExpectTotalCount(kNonSyncPasswordEntryRequestOutcomeHistogram, 0);
-  // Set up valid response.
-  LoginReputationClientResponse expected_response =
-      CreateVerdictProto(LoginReputationClientResponse::PHISHING, 10 * kMinute,
-                         GURL(kTargetUrl).host());
-  test_url_loader_factory_.AddResponse(url_.spec(),
-                                       expected_response.SerializeAsString());
-  std::unique_ptr<content::WebContents> web_contents = GetWebContents();
-
-  // Initiate a saved password entry request (w/ no sync password).
-  AccountInfo account_info;
-  account_info.account_id = CoreAccountId("account_id");
-  account_info.email = "email";
-  account_info.gaia = "gaia";
-  EXPECT_CALL(*password_protection_service_, GetSignedInNonSyncAccount(_))
-      .WillRepeatedly(Return(account_info));
-
-  InitializeAndStartPasswordEntryRequest(
-      PasswordType::OTHER_GAIA_PASSWORD, {"gmail.com"},
-      /*match_whitelist=*/false,
-      /*timeout_in_ms=*/10000, web_contents.get());
-  password_protection_service_->WaitForResponse();
-
-  // UMA: request outcomes
-  EXPECT_THAT(
-      histograms_.GetAllSamples(kAnyPasswordEntryRequestOutcomeHistogram),
-      ElementsAre(base::Bucket(1 /* SUCCEEDED */, 1)));
-  histograms_.ExpectTotalCount(kSyncPasswordEntryRequestOutcomeHistogram, 0);
-  EXPECT_THAT(
-      histograms_.GetAllSamples(kNonSyncPasswordEntryRequestOutcomeHistogram),
-      ElementsAre(base::Bucket(1 /* SUCCEEDED */, 1)));
-  EXPECT_THAT(histograms_.GetAllSamples(kNonSyncPasswordEntryVerdictHistogram),
-              ElementsAre(base::Bucket(3 /* PHISHING */, 1)));
-
-  // UMA: verdicts
-  EXPECT_THAT(histograms_.GetAllSamples(kAnyPasswordEntryVerdictHistogram),
-              ElementsAre(base::Bucket(3 /* PHISHING */, 1)));
-  histograms_.ExpectTotalCount(kSyncPasswordEntryVerdictHistogram, 0);
-  EXPECT_THAT(histograms_.GetAllSamples(kNonSyncPasswordEntryVerdictHistogram),
-              ElementsAre(base::Bucket(3 /* PHISHING */, 1)));
-}
-
-TEST_P(PasswordProtectionServiceTest,
-       TestSyncPasswordEntryRequestAndResponseSuccessfull) {
-  histograms_.ExpectTotalCount(kAnyPasswordEntryRequestOutcomeHistogram, 0);
-  histograms_.ExpectTotalCount(kSyncPasswordEntryRequestOutcomeHistogram, 0);
-  histograms_.ExpectTotalCount(kNonSyncPasswordEntryRequestOutcomeHistogram, 0);
-  // Set up valid response.
-  LoginReputationClientResponse expected_response =
-      CreateVerdictProto(LoginReputationClientResponse::PHISHING, 10 * kMinute,
-                         GURL(kTargetUrl).host());
-  test_url_loader_factory_.AddResponse(url_.spec(),
-                                       expected_response.SerializeAsString());
-  EXPECT_CALL(*password_protection_service_, IsPrimaryAccountSyncing())
-      .WillRepeatedly(Return(true));
-  EXPECT_CALL(*password_protection_service_, IsPrimaryAccountSignedIn())
-      .WillRepeatedly(Return(true));
-  // Initiate a sync password entry request (w/ no saved password).
-  std::unique_ptr<content::WebContents> web_contents = GetWebContents();
-  InitializeAndStartPasswordEntryRequest(
-      PasswordType::PRIMARY_ACCOUNT_PASSWORD, {},
-      /*match_whitelist=*/false,
-      /*timeout_in_ms=*/10000, web_contents.get());
-  password_protection_service_->WaitForResponse();
-
-  // UMA: request outcomes
-  EXPECT_THAT(
-      histograms_.GetAllSamples(kAnyPasswordEntryRequestOutcomeHistogram),
-      ElementsAre(base::Bucket(1 /* SUCCEEDED */, 1)));
-  EXPECT_THAT(
-      histograms_.GetAllSamples(kSyncPasswordEntryRequestOutcomeHistogram),
-      ElementsAre(base::Bucket(1 /* SUCCEEDED */, 1)));
-  histograms_.ExpectTotalCount(kNonSyncPasswordEntryRequestOutcomeHistogram, 0);
-
-  // UMA: verdicts
-  EXPECT_THAT(histograms_.GetAllSamples(kAnyPasswordEntryVerdictHistogram),
-              ElementsAre(base::Bucket(3 /* PHISHING */, 1)));
-  EXPECT_THAT(histograms_.GetAllSamples(kSyncPasswordEntryVerdictHistogram),
-              ElementsAre(base::Bucket(3 /* PHISHING */, 1)));
-  histograms_.ExpectTotalCount(kNonSyncPasswordEntryVerdictHistogram, 0);
-}
-
-TEST_P(PasswordProtectionServiceTest, TestTearDownWithPendingRequests) {
-  histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogram, 0);
-  GURL target_url(kTargetUrl);
-  EXPECT_CALL(*database_manager_, CheckCsdWhitelistUrl(target_url, _))
-      .WillRepeatedly(Return(AsyncMatch::NO_MATCH));
-  std::unique_ptr<content::WebContents> web_contents = GetWebContents();
-  password_protection_service_->StartRequest(
-      web_contents.get(), target_url, GURL("http://foo.com/submit"),
-      GURL("http://foo.com/frame"), "username", PasswordType::SAVED_PASSWORD,
-      {}, LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE, true);
-
-  // Destroy password_protection_service_ while there is one request pending.
-  password_protection_service_.reset();
-  base::RunLoop().RunUntilIdle();
-
-  // We should not log on TearDown, since that can dispatch calls to pure
-  // virtual methods.
-  EXPECT_THAT(
-      histograms_.GetAllSamples(kPasswordOnFocusRequestOutcomeHistogram),
-      IsEmpty());
-}
-
-TEST_P(PasswordProtectionServiceTest, VerifyPasswordOnFocusRequestProto) {
-  // Set up valid response.
-  LoginReputationClientResponse expected_response =
-      CreateVerdictProto(LoginReputationClientResponse::PHISHING, 10 * kMinute,
-                         GURL(kTargetUrl).host());
-  test_url_loader_factory_.AddResponse(url_.spec(),
-                                       expected_response.SerializeAsString());
-  std::unique_ptr<content::WebContents> web_contents = GetWebContents();
-
-  InitializeAndStartPasswordOnFocusRequest(/*match_whitelist=*/false,
-                                           /*timeout_in_ms=*/10000,
-                                           web_contents.get());
-  password_protection_service_->WaitForResponse();
-
-  const LoginReputationClientRequest* actual_request =
-      password_protection_service_->GetLatestRequestProto();
-  EXPECT_EQ(kTargetUrl, actual_request->page_url());
-  EXPECT_EQ(LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
-            actual_request->trigger_type());
-  ASSERT_EQ(2, actual_request->frames_size());
-  EXPECT_EQ(kTargetUrl, actual_request->frames(0).url());
-  EXPECT_EQ(kPasswordFrameUrl, actual_request->frames(1).url());
-  EXPECT_EQ(true, actual_request->frames(1).has_password_field());
-  ASSERT_EQ(1, actual_request->frames(1).forms_size());
-  EXPECT_EQ(kFormActionUrl, actual_request->frames(1).forms(0).action_url());
-  VerifyContentAreaSizeCollection(*actual_request);
-}
-
-TEST_P(PasswordProtectionServiceTest,
-       VerifyPasswordOnFocusRequestProtoForAllowlistMatch) {
-  // Set up valid response.
-  LoginReputationClientResponse expected_response =
-      CreateVerdictProto(LoginReputationClientResponse::PHISHING, 10 * kMinute,
-                         GURL(kTargetUrl).host());
-  test_url_loader_factory_.AddResponse(url_.spec(),
-                                       expected_response.SerializeAsString());
-  std::unique_ptr<content::WebContents> web_contents = GetWebContents();
-
-  EXPECT_CALL(*password_protection_service_, CanSendSamplePing())
-      .WillRepeatedly(Return(true));
-  InitializeAndStartPasswordOnFocusRequest(/*match_whitelist=*/true,
-                                           /*timeout_in_ms=*/10000,
-                                           web_contents.get());
-  password_protection_service_->WaitForResponse();
-
-  const LoginReputationClientRequest* actual_request =
-      password_protection_service_->GetLatestRequestProto();
-  EXPECT_EQ(kTargetUrl, actual_request->page_url());
-  ASSERT_EQ(1, actual_request->frames_size());
-  EXPECT_EQ(kTargetUrl, actual_request->frames(0).url());
-}
-
-TEST_P(PasswordProtectionServiceTest,
-       VerifySyncPasswordProtectionRequestProto) {
-  // Set up valid response.
-  LoginReputationClientResponse expected_response =
-      CreateVerdictProto(LoginReputationClientResponse::PHISHING, 10 * kMinute,
-                         GURL(kTargetUrl).host());
-  test_url_loader_factory_.AddResponse(url_.spec(),
-                                       expected_response.SerializeAsString());
-  std::unique_ptr<content::WebContents> web_contents = GetWebContents();
-
-  // Initialize request triggered by chrome sync password reuse.
-  InitializeAndStartPasswordEntryRequest(
-      PasswordType::PRIMARY_ACCOUNT_PASSWORD, {}, false /* match whitelist */,
-      100000 /* timeout in ms*/, web_contents.get());
-  password_protection_service_->WaitForResponse();
-
-  const LoginReputationClientRequest* actual_request =
-      password_protection_service_->GetLatestRequestProto();
-  EXPECT_EQ(kTargetUrl, actual_request->page_url());
-  EXPECT_EQ(LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-            actual_request->trigger_type());
-  EXPECT_EQ(1, actual_request->frames_size());
-  EXPECT_EQ(kTargetUrl, actual_request->frames(0).url());
-  EXPECT_TRUE(actual_request->frames(0).has_password_field());
-  ASSERT_TRUE(actual_request->has_password_reuse_event());
-  const auto& reuse_event = actual_request->password_reuse_event();
-  EXPECT_TRUE(reuse_event.is_chrome_signin_password());
-  EXPECT_EQ(0, reuse_event.domains_matching_password_size());
-  VerifyContentAreaSizeCollection(*actual_request);
-}
-
-TEST_P(PasswordProtectionServiceTest,
-       VerifyNonSyncPasswordProtectionRequestProto) {
-  // Set up valid response.
-  LoginReputationClientResponse expected_response =
-      CreateVerdictProto(LoginReputationClientResponse::PHISHING, 10 * kMinute,
-                         GURL(kTargetUrl).host());
-  test_url_loader_factory_.AddResponse(url_.spec(),
-                                       expected_response.SerializeAsString());
-  std::unique_ptr<content::WebContents> web_contents = GetWebContents();
-
-  // Initialize request triggered by saved password reuse.
-  InitializeAndStartPasswordEntryRequest(
-      PasswordType::SAVED_PASSWORD, {kSavedDomain, kSavedDomain2},
-      false /* match whitelist */, 100000 /* timeout in ms*/,
-      web_contents.get());
-  password_protection_service_->WaitForResponse();
-
-  const LoginReputationClientRequest* actual_request =
-      password_protection_service_->GetLatestRequestProto();
-  ASSERT_TRUE(actual_request->has_password_reuse_event());
-  const auto& reuse_event = actual_request->password_reuse_event();
-  EXPECT_FALSE(reuse_event.is_chrome_signin_password());
-
-  if (password_protection_service_->IsExtendedReporting() &&
-      !password_protection_service_->IsIncognito()) {
-    ASSERT_EQ(2, reuse_event.domains_matching_password_size());
-    EXPECT_EQ(kSavedDomain, reuse_event.domains_matching_password(0));
-    EXPECT_EQ(kSavedDomain2, reuse_event.domains_matching_password(1));
-  } else {
-    EXPECT_EQ(0, reuse_event.domains_matching_password_size());
-  }
-  VerifyContentAreaSizeCollection(*actual_request);
-}
-
-TEST_P(PasswordProtectionServiceTest, VerifyShouldShowModalWarning) {
-  EXPECT_CALL(*password_protection_service_,
-              GetPasswordProtectionWarningTriggerPref(_))
-      .WillRepeatedly(Return(PHISHING_REUSE));
-  EXPECT_CALL(*password_protection_service_, IsPrimaryAccountSignedIn())
-      .WillRepeatedly(Return(true));
-  AccountInfo account_info;
-  account_info.account_id = CoreAccountId("account_id");
-  account_info.email = "email";
-  account_info.gaia = "gaia";
-  EXPECT_CALL(*password_protection_service_, GetSignedInNonSyncAccount(_))
-      .WillRepeatedly(Return(account_info));
-
-  // Don't show modal warning if it is not a password reuse ping.
-  ReusedPasswordAccountType reused_password_account_type;
-  reused_password_account_type.set_account_type(
-      ReusedPasswordAccountType::UNKNOWN);
-  reused_password_account_type.set_is_account_syncing(true);
-  EXPECT_FALSE(password_protection_service_->ShouldShowModalWarning(
-      LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
-      reused_password_account_type, LoginReputationClientResponse::PHISHING));
-
-  // Don't show modal warning if it is a saved password reuse and the experiment
-  // isn't on.
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndDisableFeature(
-      safe_browsing::kPasswordProtectionForSignedInUsers);
-  reused_password_account_type.set_account_type(
-      ReusedPasswordAccountType::SAVED_PASSWORD);
-  EXPECT_FALSE(password_protection_service_->ShouldShowModalWarning(
-      LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-      reused_password_account_type, LoginReputationClientResponse::PHISHING));
-
-  {
-    base::test::ScopedFeatureList feature_list;
-    feature_list.InitAndEnableFeature(
-        safe_browsing::kPasswordProtectionForSavedPasswords);
-    EXPECT_TRUE(password_protection_service_->ShouldShowModalWarning(
-        LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-        reused_password_account_type, LoginReputationClientResponse::PHISHING));
-    EXPECT_TRUE(password_protection_service_->ShouldShowModalWarning(
-        LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-        reused_password_account_type,
-        LoginReputationClientResponse::LOW_REPUTATION));
-  }
-
-  {
-    base::test::ScopedFeatureList feature_list;
-    feature_list.InitAndDisableFeature(
-        safe_browsing::kPasswordProtectionForSignedInUsers);
-
-    // Don't show modal warning if non-sync gaia account experiment is not
-    // on.
-    reused_password_account_type.set_account_type(
-        ReusedPasswordAccountType::GMAIL);
-    reused_password_account_type.set_is_account_syncing(false);
-    EXPECT_FALSE(password_protection_service_->ShouldShowModalWarning(
-        LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-        reused_password_account_type, LoginReputationClientResponse::PHISHING));
-  }
-  {
-    base::test::ScopedFeatureList feature_list;
-    feature_list.InitAndEnableFeature(
-        safe_browsing::kPasswordProtectionForSignedInUsers);
-    // Show modal warning if non-sync gaia account experiment is on.
-    reused_password_account_type.set_account_type(
-        ReusedPasswordAccountType::GMAIL);
-    reused_password_account_type.set_is_account_syncing(false);
-    EXPECT_TRUE(password_protection_service_->ShouldShowModalWarning(
-        LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-        reused_password_account_type, LoginReputationClientResponse::PHISHING));
-  }
-
-  // Don't show modal warning if reused password type unknown.
-  reused_password_account_type.set_account_type(
-      ReusedPasswordAccountType::UNKNOWN);
-  EXPECT_FALSE(password_protection_service_->ShouldShowModalWarning(
-      LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-      reused_password_account_type, LoginReputationClientResponse::PHISHING));
-
-  reused_password_account_type.set_account_type(
-      ReusedPasswordAccountType::GMAIL);
-  reused_password_account_type.set_is_account_syncing(true);
-  EXPECT_TRUE(password_protection_service_->ShouldShowModalWarning(
-      LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-      reused_password_account_type, LoginReputationClientResponse::PHISHING));
-
-  // For a GSUITE account, don't show warning if password protection is set to
-  // off by enterprise policy.
-  reused_password_account_type.set_account_type(
-      ReusedPasswordAccountType::GSUITE);
-  EXPECT_CALL(*password_protection_service_,
-              GetPasswordProtectionWarningTriggerPref(_))
-      .WillRepeatedly(Return(PASSWORD_PROTECTION_OFF));
-  EXPECT_FALSE(password_protection_service_->ShouldShowModalWarning(
-      LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-      reused_password_account_type, LoginReputationClientResponse::PHISHING));
-
-  // For a GSUITE account, show warning if password protection is set to
-  // PHISHING_REUSE.
-  EXPECT_CALL(*password_protection_service_,
-              GetPasswordProtectionWarningTriggerPref(_))
-      .WillRepeatedly(Return(PHISHING_REUSE));
-  EXPECT_EQ(
-      PHISHING_REUSE,
-      password_protection_service_->GetPasswordProtectionWarningTriggerPref(
-          reused_password_account_type));
-  EXPECT_TRUE(password_protection_service_->ShouldShowModalWarning(
-      LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-      reused_password_account_type, LoginReputationClientResponse::PHISHING));
-
-  // Modal dialog warning is also shown on LOW_REPUTATION verdict.
-  EXPECT_TRUE(password_protection_service_->ShouldShowModalWarning(
-      LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-      reused_password_account_type,
-      LoginReputationClientResponse::LOW_REPUTATION));
-
-  // Modal dialog warning should not be shown for enterprise password reuse
-  // if it is turned off by policy.
-  EXPECT_CALL(*password_protection_service_,
-              GetPasswordProtectionWarningTriggerPref(_))
-      .WillRepeatedly(Return(PASSWORD_PROTECTION_OFF));
-  EXPECT_FALSE(password_protection_service_->ShouldShowModalWarning(
-      LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-      reused_password_account_type, LoginReputationClientResponse::PHISHING));
-
-  // Show modal warning for enterprise password reuse if the trigger is
-  // configured to PHISHING_REUSE.
-  reused_password_account_type.set_account_type(
-      ReusedPasswordAccountType::NON_GAIA_ENTERPRISE);
-  EXPECT_CALL(*password_protection_service_,
-              GetPasswordProtectionWarningTriggerPref(_))
-      .WillRepeatedly(Return(PHISHING_REUSE));
-  EXPECT_TRUE(password_protection_service_->ShouldShowModalWarning(
-      LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-      reused_password_account_type, LoginReputationClientResponse::PHISHING));
-}
-
-TEST_P(PasswordProtectionServiceTest, VerifyContentTypeIsPopulated) {
-  LoginReputationClientResponse response =
-      CreateVerdictProto(LoginReputationClientResponse::SAFE, 10 * kMinute,
-                         GURL(kTargetUrl).host());
-  test_url_loader_factory_.AddResponse(url_.spec(),
-                                       response.SerializeAsString());
-
-  std::unique_ptr<content::WebContents> web_contents = GetWebContents();
-
-  content::WebContentsTester::For(web_contents.get())
-      ->SetMainFrameMimeType("application/pdf");
-
-  InitializeAndStartPasswordOnFocusRequest(false /* match whitelist */,
-                                           10000 /* timeout in ms */,
-                                           web_contents.get());
-
-  password_protection_service_->WaitForResponse();
-
-  EXPECT_EQ(
-      "application/pdf",
-      password_protection_service_->GetLatestRequestProto()->content_type());
-}
-
-TEST_P(PasswordProtectionServiceTest, VerifyIsSupportedPasswordTypeForPinging) {
-  EXPECT_CALL(*password_protection_service_, IsPrimaryAccountSignedIn())
-      .WillRepeatedly(Return(true));
-  AccountInfo account_info;
-  account_info.account_id = CoreAccountId("account_id");
-  account_info.email = "email";
-  account_info.gaia = "gaia";
-  EXPECT_CALL(*password_protection_service_, GetSignedInNonSyncAccount(_))
-      .WillRepeatedly(Return(account_info));
-
-  EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging(
-      PasswordType::SAVED_PASSWORD));
-  EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging(
-      PasswordType::PRIMARY_ACCOUNT_PASSWORD));
-  EXPECT_FALSE(password_protection_service_->IsSupportedPasswordTypeForPinging(
-      PasswordType::OTHER_GAIA_PASSWORD));
-  EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging(
-      PasswordType::ENTERPRISE_PASSWORD));
-
-  EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging(
-      PasswordType::SAVED_PASSWORD));
-  EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging(
-      PasswordType::ENTERPRISE_PASSWORD));
-  {
-    base::test::ScopedFeatureList feature_list;
-    feature_list.InitAndDisableFeature(
-        safe_browsing::kPasswordProtectionForSignedInUsers);
-    // Only ping for signed in, non-syncing users if the experiment is on.
-    EXPECT_FALSE(
-        password_protection_service_->IsSupportedPasswordTypeForPinging(
-            PasswordType::OTHER_GAIA_PASSWORD));
-  }
-  {
-    base::test::ScopedFeatureList feature_list;
-    feature_list.InitAndEnableFeature(
-        safe_browsing::kPasswordProtectionForSignedInUsers);
-    EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging(
-        PasswordType::OTHER_GAIA_PASSWORD));
-  }
-}
-
-TEST_P(PasswordProtectionServiceTest, TestPingsForAboutBlank) {
-  histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogram, 0);
-  LoginReputationClientResponse expected_response =
-      CreateVerdictProto(LoginReputationClientResponse::PHISHING, 10 * kMinute,
-                         GURL("about:blank").host());
-  test_url_loader_factory_.AddResponse(url_.spec(),
-                                       expected_response.SerializeAsString());
-  std::unique_ptr<content::WebContents> web_contents = GetWebContents();
-  password_protection_service_->StartRequest(
-      web_contents.get(), GURL("about:blank"), GURL(), GURL(), "username",
-      PasswordType::SAVED_PASSWORD, {"example.com"},
-      LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE, true);
-  base::RunLoop().RunUntilIdle();
-  histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogram, 1);
-}
-
-TEST_P(PasswordProtectionServiceTest,
-       TestVisualFeaturesPopulatedInOnFocusPing) {
-  LoginReputationClientResponse expected_response =
-      CreateVerdictProto(LoginReputationClientResponse::PHISHING, 10 * kMinute,
-                         GURL("about:blank").host());
-  test_url_loader_factory_.AddResponse(url_.spec(),
-                                       expected_response.SerializeAsString());
-  EXPECT_CALL(*password_protection_service_, GetCurrentContentAreaSize())
-      .Times(AnyNumber())
-      .WillOnce(Return(gfx::Size(1000, 1000)));
-  std::unique_ptr<content::WebContents> web_contents = GetWebContents();
-  password_protection_service_->StartRequest(
-      web_contents.get(), GURL("about:blank"), GURL(), GURL(), kUserName,
-      PasswordType::SAVED_PASSWORD, {"example.com"},
-      LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE, true);
-  base::RunLoop().RunUntilIdle();
-
-  bool is_sber = GetParam();
-  if (is_sber) {
-    password_protection_service_->WaitForResponse();
-    ASSERT_NE(nullptr, password_protection_service_->GetLatestRequestProto());
-    EXPECT_TRUE(password_protection_service_->GetLatestRequestProto()
-                    ->has_visual_features());
-  }
-}
-
-TEST_P(PasswordProtectionServiceTest, TestDomFeaturesPopulated) {
-  LoginReputationClientResponse expected_response =
-      CreateVerdictProto(LoginReputationClientResponse::PHISHING, 10 * kMinute,
-                         GURL("about:blank").host());
-  test_url_loader_factory_.AddResponse(url_.spec(),
-                                       expected_response.SerializeAsString());
-  EXPECT_CALL(*password_protection_service_, GetCurrentContentAreaSize())
-      .Times(AnyNumber())
-      .WillOnce(Return(gfx::Size(1000, 1000)));
-  std::unique_ptr<content::WebContents> web_contents = GetWebContents();
-  password_protection_service_->StartRequest(
-      web_contents.get(), GURL("about:blank"), GURL(), GURL(), kUserName,
-      PasswordType::SAVED_PASSWORD, {"example.com"},
-      LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE, true);
-  base::RunLoop().RunUntilIdle();
-
-  password_protection_service_->WaitForResponse();
-  ASSERT_NE(nullptr, password_protection_service_->GetLatestRequestProto());
-  EXPECT_TRUE(password_protection_service_->GetLatestRequestProto()
-                  ->has_dom_features());
-}
-
-TEST_P(PasswordProtectionServiceTest, TestDomFeaturesTimeout) {
-  password_protection_service_->SetDomFeatureCollectionTimeout(true);
-  LoginReputationClientResponse expected_response =
-      CreateVerdictProto(LoginReputationClientResponse::PHISHING, 10 * kMinute,
-                         GURL("about:blank").host());
-  test_url_loader_factory_.AddResponse(url_.spec(),
-                                       expected_response.SerializeAsString());
-  EXPECT_CALL(*password_protection_service_, GetCurrentContentAreaSize())
-      .Times(AnyNumber())
-      .WillOnce(Return(gfx::Size(1000, 1000)));
-  std::unique_ptr<content::WebContents> web_contents = GetWebContents();
-  password_protection_service_->StartRequest(
-      web_contents.get(), GURL("about:blank"), GURL(), GURL(), kUserName,
-      PasswordType::SAVED_PASSWORD, {"example.com"},
-      LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE, true);
-  task_environment_.FastForwardUntilNoTasksRemain();
-
-  password_protection_service_->WaitForResponse();
-  ASSERT_NE(nullptr, password_protection_service_->GetLatestRequestProto());
-  EXPECT_FALSE(password_protection_service_->GetLatestRequestProto()
-                   ->has_dom_features());
-}
-
-TEST_P(PasswordProtectionServiceTest, TestRequestCancelOnTimeout) {
-  std::unique_ptr<content::WebContents> web_contents = GetWebContents();
-  InitializeAndStartPasswordOnFocusRequest(true /* match whitelist */,
-                                           10000 /* timeout in ms */,
-                                           web_contents.get());
-  auto throttle = std::make_unique<MockPasswordProtectionNavigationThrottle>(
-      nullptr, request_, false);
-  EXPECT_EQ(1U, GetNumberOfNavigationThrottles());
-  request_->Cancel(true /* timeout */);
-  EXPECT_EQ(1U, GetNumberOfNavigationThrottles());
-}
-
-TEST_P(PasswordProtectionServiceTest, TestRequestCancelNotOnTimeout) {
-  std::unique_ptr<content::WebContents> web_contents = GetWebContents();
-  InitializeAndStartPasswordOnFocusRequest(true /* match whitelist */,
-                                           10000 /* timeout in ms */,
-                                           web_contents.get());
-  auto throttle = std::make_unique<MockPasswordProtectionNavigationThrottle>(
-      nullptr, request_, false);
-  EXPECT_EQ(1U, GetNumberOfNavigationThrottles());
-  request_->Cancel(false /* timeout */);
-  EXPECT_EQ(0U, GetNumberOfNavigationThrottles());
-}
-
-TEST_P(PasswordProtectionServiceTest, TestWebContentsDestroyed) {
-  std::unique_ptr<content::WebContents> web_contents = GetWebContents();
-  InitializeAndStartPasswordOnFocusRequest(false /* match whitelist */,
-                                           10000 /* timeout in ms */,
-                                           web_contents.get());
-  web_contents.reset();
-  task_environment_.FastForwardUntilNoTasksRemain();
-}
-
-INSTANTIATE_TEST_SUITE_P(Regular,
-                         PasswordProtectionServiceTest,
-                         ::testing::Values(false));
-INSTANTIATE_TEST_SUITE_P(SBER,
-                         PasswordProtectionServiceTest,
-                         ::testing::Values(true));
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/password_protection/visual_utils.cc b/components/safe_browsing/password_protection/visual_utils.cc
deleted file mode 100644
index 7ccde23..0000000
--- a/components/safe_browsing/password_protection/visual_utils.cc
+++ /dev/null
@@ -1,176 +0,0 @@
-// Copyright 2018 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 <unordered_map>
-#include <vector>
-
-#include "components/safe_browsing/password_protection/visual_utils.h"
-
-#include "base/logging.h"
-#include "base/numerics/checked_math.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "third_party/skia/include/core/SkPixmap.h"
-
-namespace safe_browsing {
-namespace visual_utils {
-
-namespace {
-
-// WARNING: The following parameters are highly privacy and performance
-// sensitive. These should not be changed without thorough review.
-const int kPHashDownsampleWidth = 288;
-const int kPHashDownsampleHeight = 288;
-const int kPHashBlockSize = 6;
-
-}  // namespace
-
-// A QuantizedColor takes the highest 3 bits of R, G, and B, and concatenates
-// them.
-QuantizedColor SkColorToQuantizedColor(SkColor color) {
-  return (SkColorGetR(color) >> 5) << 6 | (SkColorGetG(color) >> 5) << 3 |
-         (SkColorGetB(color) >> 5);
-}
-
-int GetQuantizedR(QuantizedColor color) {
-  return color >> 6;
-}
-
-int GetQuantizedG(QuantizedColor color) {
-  return (color >> 3) & 7;
-}
-
-int GetQuantizedB(QuantizedColor color) {
-  return color & 7;
-}
-
-bool GetHistogramForImage(const SkBitmap& image,
-                          VisualFeatures::ColorHistogram* histogram) {
-  if (image.drawsNothing())
-    return false;
-
-  std::unordered_map<QuantizedColor, int> color_to_count;
-  std::unordered_map<QuantizedColor, double> color_to_total_x;
-  std::unordered_map<QuantizedColor, double> color_to_total_y;
-  for (int x = 0; x < image.width(); x++) {
-    for (int y = 0; y < image.height(); y++) {
-      QuantizedColor color = SkColorToQuantizedColor(image.getColor(x, y));
-      color_to_count[color]++;
-      color_to_total_x[color] += static_cast<float>(x) / image.width();
-      color_to_total_y[color] += static_cast<float>(y) / image.height();
-    }
-  }
-
-  int normalization_factor;
-  if (!base::CheckMul(image.width(), image.height())
-           .AssignIfValid(&normalization_factor))
-    return false;
-
-  for (const auto& entry : color_to_count) {
-    const QuantizedColor& color = entry.first;
-    int count = entry.second;
-
-    VisualFeatures::ColorHistogramBin* bin = histogram->add_bins();
-    bin->set_weight(static_cast<float>(count) / normalization_factor);
-    bin->set_centroid_x(color_to_total_x[color] / count);
-    bin->set_centroid_y(color_to_total_y[color] / count);
-    bin->set_quantized_r(GetQuantizedR(color));
-    bin->set_quantized_g(GetQuantizedG(color));
-    bin->set_quantized_b(GetQuantizedB(color));
-  }
-
-  return true;
-}
-
-bool GetBlurredImage(const SkBitmap& image,
-                     VisualFeatures::BlurredImage* blurred_image) {
-  if (image.drawsNothing())
-    return false;
-
-  // Use the Rec. 2020 color space, in case the user input is wide-gamut.
-  sk_sp<SkColorSpace> rec2020 = SkColorSpace::MakeRGB(
-      {2.22222f, 0.909672f, 0.0903276f, 0.222222f, 0.0812429f, 0, 0},
-      SkNamedGamut::kRec2020);
-
-  // We scale down twice, once with medium quality, then with a block mean
-  // average to be consistent with the backend.
-  // TODO(drubery): Investigate whether this is necessary for performance or
-  // not.
-  SkImageInfo downsampled_info =
-      SkImageInfo::Make(kPHashDownsampleWidth, kPHashDownsampleHeight,
-                        SkColorType::kRGBA_8888_SkColorType,
-                        SkAlphaType::kUnpremul_SkAlphaType, rec2020);
-  SkBitmap downsampled;
-  if (!downsampled.tryAllocPixels(downsampled_info))
-    return false;
-  image.pixmap().scalePixels(downsampled.pixmap(),
-                             SkFilterQuality::kMedium_SkFilterQuality);
-
-  std::unique_ptr<SkBitmap> blurred =
-      BlockMeanAverage(downsampled, kPHashBlockSize);
-
-  blurred_image->set_width(blurred->width());
-  blurred_image->set_height(blurred->height());
-  blurred_image->clear_data();
-
-  const uint32_t* rgba = blurred->getAddr32(0, 0);
-  for (int i = 0; i < blurred->width() * blurred->height(); i++) {
-    // Data is stored in BGR order.
-    *blurred_image->mutable_data() += static_cast<char>((rgba[i] >> 0) & 0xff);
-    *blurred_image->mutable_data() += static_cast<char>((rgba[i] >> 8) & 0xff);
-    *blurred_image->mutable_data() += static_cast<char>((rgba[i] >> 16) & 0xff);
-  }
-
-  return true;
-}
-
-std::unique_ptr<SkBitmap> BlockMeanAverage(const SkBitmap& image,
-                                           int block_size) {
-  // Compute the number of blocks in the target image, rounding up to account
-  // for partial blocks.
-  int num_blocks_high =
-      std::ceil(static_cast<float>(image.height()) / block_size);
-  int num_blocks_wide =
-      std::ceil(static_cast<float>(image.width()) / block_size);
-
-  SkImageInfo target_info = SkImageInfo::Make(
-      num_blocks_wide, num_blocks_high, SkColorType::kRGBA_8888_SkColorType,
-      SkAlphaType::kUnpremul_SkAlphaType, image.refColorSpace());
-  auto target = std::make_unique<SkBitmap>();
-  if (!target->tryAllocPixels(target_info))
-    return target;
-
-  for (int block_x = 0; block_x < num_blocks_wide; block_x++) {
-    for (int block_y = 0; block_y < num_blocks_high; block_y++) {
-      int r_total = 0, g_total = 0, b_total = 0, sample_count = 0;
-
-      // Compute boundary for the current block, taking into account the
-      // possibility of partial blocks near the edges.
-      int x_start = block_x * block_size;
-      int x_end = std::min(x_start + block_size, image.width());
-
-      int y_start = block_y * block_size;
-      int y_end = std::min(y_start + block_size, image.height());
-      for (int i = x_start; i < x_end; i++) {
-        for (int j = y_start; j < y_end; j++) {
-          r_total += SkColorGetR(image.getColor(i, j));
-          g_total += SkColorGetG(image.getColor(i, j));
-          b_total += SkColorGetB(image.getColor(i, j));
-          sample_count++;
-        }
-      }
-
-      int r_mean = r_total / sample_count;
-      int g_mean = g_total / sample_count;
-      int b_mean = b_total / sample_count;
-
-      *target->getAddr32(block_x, block_y) =
-          (255 << 24) | (b_mean << 16) | (g_mean << 8) | (r_mean << 0);
-    }
-  }
-
-  return target;
-}
-
-}  // namespace visual_utils
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/password_protection/visual_utils.h b/components/safe_browsing/password_protection/visual_utils.h
deleted file mode 100644
index 1026e65..0000000
--- a/components/safe_browsing/password_protection/visual_utils.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_PASSWORD_PROTECTION_VISUAL_UTILS_H_
-#define COMPONENTS_SAFE_BROWSING_PASSWORD_PROTECTION_VISUAL_UTILS_H_
-
-#include <string>
-
-#include "components/safe_browsing/proto/csd.pb.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-
-namespace safe_browsing {
-namespace visual_utils {
-
-using QuantizedColor = uint32_t;
-
-// Utility methods for working with QuantizedColors.
-QuantizedColor SkColorToQuantizedColor(SkColor color);
-int GetQuantizedR(QuantizedColor color);
-int GetQuantizedG(QuantizedColor color);
-int GetQuantizedB(QuantizedColor color);
-
-// Computes the color histogram for the image. This buckets the pixels according
-// to their QuantizedColor, then reports their weight and centroid.
-bool GetHistogramForImage(const SkBitmap& image,
-                          VisualFeatures::ColorHistogram* histogram);
-
-// Computes the BlurredImage for the given input image. This involves
-// downsampling the image to a certain fixed resolution, then blurring
-// by taking an average over fixed-size blocks of pixels.
-bool GetBlurredImage(const SkBitmap& image,
-                     VisualFeatures::BlurredImage* blurred_image);
-
-// Downsizes an image by averaging all the pixels in the source image that
-// contribute to the target image. Groups pixels into squares of size
-// |block_size|, potentially with partial blocks at the edge. The output
-// image has pixels the average of the pixels in each block.
-std::unique_ptr<SkBitmap> BlockMeanAverage(const SkBitmap& image,
-                                           int block_size);
-
-}  // namespace visual_utils
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_PASSWORD_PROTECTION_VISUAL_UTILS_H_
diff --git a/components/safe_browsing/password_protection/visual_utils_unittest.cc b/components/safe_browsing/password_protection/visual_utils_unittest.cc
deleted file mode 100644
index 97920ad..0000000
--- a/components/safe_browsing/password_protection/visual_utils_unittest.cc
+++ /dev/null
@@ -1,256 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/password_protection/visual_utils.h"
-
-#include "base/test/test_discardable_memory_allocator.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace safe_browsing {
-namespace visual_utils {
-
-namespace {
-
-// Pixel value constants, all in BGR order.
-const unsigned int kWhite = 0xffffffff;
-const unsigned int kBlack = 0xff000000;
-const unsigned int kRed = 0xff0000ff;
-const unsigned int kGreen = 0xff00ff00;
-const unsigned int kBlue = 0xffff0000;
-
-}  // namespace
-
-using ::testing::FloatEq;
-
-class VisualUtilsTest : public testing::Test {
- protected:
-  void SetUp() override {
-    base::DiscardableMemoryAllocator::SetInstance(&test_allocator_);
-
-    sk_sp<SkColorSpace> rec2020 = SkColorSpace::MakeRGB(
-        {2.22222f, 0.909672f, 0.0903276f, 0.222222f, 0.0812429f, 0, 0},
-        SkNamedGamut::kRec2020);
-    SkImageInfo bitmap_info =
-        SkImageInfo::Make(1000, 1000, SkColorType::kRGBA_8888_SkColorType,
-                          SkAlphaType::kUnpremul_SkAlphaType, rec2020);
-
-    ASSERT_TRUE(bitmap_.tryAllocPixels(bitmap_info));
-  }
-
-  void TearDown() override {
-    base::DiscardableMemoryAllocator::SetInstance(nullptr);
-  }
-
-  // A test bitmap to work with. Initialized to be 1000x1000 in the Rec 2020
-  // color space.
-  SkBitmap bitmap_;
-
- private:
-  // A DiscardableMemoryAllocator is needed for certain Skia operations.
-  base::TestDiscardableMemoryAllocator test_allocator_;
-};
-
-TEST_F(VisualUtilsTest, TestSkColorToQuantizedColor) {
-  // Test quantization
-  EXPECT_EQ(SkColorToQuantizedColor(SkColorSetRGB(0, 0, 31)), 0u);
-  EXPECT_EQ(SkColorToQuantizedColor(SkColorSetRGB(0, 0, 32)), 1u);
-  EXPECT_EQ(SkColorToQuantizedColor(SkColorSetRGB(0, 31, 0)), 0u);
-  EXPECT_EQ(SkColorToQuantizedColor(SkColorSetRGB(0, 32, 0)), 8u);
-  EXPECT_EQ(SkColorToQuantizedColor(SkColorSetRGB(31, 0, 0)), 0u);
-  EXPECT_EQ(SkColorToQuantizedColor(SkColorSetRGB(32, 0, 0)), 64u);
-
-  // Test composition of RGB quantized values
-  EXPECT_EQ(SkColorToQuantizedColor(SkColorSetRGB(0, 0, 0)), 0u);
-  EXPECT_EQ(SkColorToQuantizedColor(SkColorSetRGB(0, 0, 255)), 7u);
-  EXPECT_EQ(SkColorToQuantizedColor(SkColorSetRGB(0, 255, 255)), 63u);
-  EXPECT_EQ(SkColorToQuantizedColor(SkColorSetRGB(255, 255, 255)), 511u);
-}
-
-TEST_F(VisualUtilsTest, GetQuantizedR) {
-  EXPECT_EQ(GetQuantizedR(0), 0);
-  EXPECT_EQ(GetQuantizedR(64), 1);
-  EXPECT_EQ(GetQuantizedR(448), 7);
-}
-
-TEST_F(VisualUtilsTest, GetQuantizedG) {
-  EXPECT_EQ(GetQuantizedG(0), 0);
-  EXPECT_EQ(GetQuantizedG(8), 1);
-  EXPECT_EQ(GetQuantizedG(56), 7);
-}
-
-TEST_F(VisualUtilsTest, GetQuantizedB) {
-  EXPECT_EQ(GetQuantizedB(0), 0);
-  EXPECT_EQ(GetQuantizedB(1), 1);
-  EXPECT_EQ(GetQuantizedB(7), 7);
-}
-
-TEST_F(VisualUtilsTest, GetHistogramForImageWhite) {
-  VisualFeatures::ColorHistogram histogram;
-  SkBitmap bitmap;
-
-  // Draw white over half the image
-  for (int x = 0; x < 1000; x++)
-    for (int y = 0; y < 1000; y++)
-      *bitmap_.getAddr32(x, y) = kWhite;
-
-  ASSERT_TRUE(GetHistogramForImage(bitmap_, &histogram));
-  ASSERT_EQ(histogram.bins_size(), 1);
-  EXPECT_THAT(histogram.bins(0).centroid_x(),
-              FloatEq(0.4995));  // All pixels are the same color, so centroid_x
-                                 // is (0+1+...+999)/1000/1000 = 0.4995
-  EXPECT_THAT(histogram.bins(0).centroid_y(), FloatEq(0.4995));
-  EXPECT_EQ(histogram.bins(0).quantized_r(), 7);
-  EXPECT_EQ(histogram.bins(0).quantized_g(), 7);
-  EXPECT_EQ(histogram.bins(0).quantized_b(), 7);
-  EXPECT_THAT(histogram.bins(0).weight(), FloatEq(1.0));
-}
-
-TEST_F(VisualUtilsTest, GetHistogramForImageHalfWhiteHalfBlack) {
-  VisualFeatures::ColorHistogram histogram;
-
-  // Draw white over half the image
-  for (int x = 0; x < 1000; x++)
-    for (int y = 0; y < 500; y++)
-      *bitmap_.getAddr32(x, y) = kWhite;
-
-  // Draw black over half the image.
-  for (int x = 0; x < 1000; x++)
-    for (int y = 500; y < 1000; y++)
-      *bitmap_.getAddr32(x, y) = kBlack;
-
-  ASSERT_TRUE(GetHistogramForImage(bitmap_, &histogram));
-  ASSERT_EQ(histogram.bins_size(), 2);
-
-  EXPECT_THAT(histogram.bins(0).centroid_x(), FloatEq(0.4995));
-  EXPECT_THAT(histogram.bins(0).centroid_y(), FloatEq(0.7495));
-  EXPECT_EQ(histogram.bins(0).quantized_r(), 0);
-  EXPECT_EQ(histogram.bins(0).quantized_g(), 0);
-  EXPECT_EQ(histogram.bins(0).quantized_b(), 0);
-  EXPECT_THAT(histogram.bins(0).weight(), FloatEq(0.5));
-
-  EXPECT_THAT(histogram.bins(1).centroid_x(), FloatEq(0.4995));
-  EXPECT_THAT(histogram.bins(1).centroid_y(), FloatEq(0.2495));
-  EXPECT_EQ(histogram.bins(1).quantized_r(), 7);
-  EXPECT_EQ(histogram.bins(1).quantized_g(), 7);
-  EXPECT_EQ(histogram.bins(1).quantized_b(), 7);
-  EXPECT_THAT(histogram.bins(1).weight(), FloatEq(0.5));
-}
-
-TEST_F(VisualUtilsTest, BlurImageWhite) {
-  VisualFeatures::BlurredImage blurred;
-
-  // Draw white over the image
-  for (int x = 0; x < 1000; x++)
-    for (int y = 0; y < 1000; y++)
-      *bitmap_.getAddr32(x, y) = kWhite;
-
-  ASSERT_TRUE(GetBlurredImage(bitmap_, &blurred));
-  ASSERT_EQ(48, blurred.width());
-  ASSERT_EQ(48, blurred.height());
-  ASSERT_EQ(3u * 48u * 48u, blurred.data().size());
-  for (size_t i = 0; i < 48u * 48u; i++) {
-    EXPECT_EQ('\xff', blurred.data()[3 * i]);
-    EXPECT_EQ('\xff', blurred.data()[3 * i + 1]);
-    EXPECT_EQ('\xff', blurred.data()[3 * i + 2]);
-  }
-}
-
-TEST_F(VisualUtilsTest, BlurImageRed) {
-  VisualFeatures::BlurredImage blurred;
-
-  // Draw red over the image.
-  for (int x = 0; x < 1000; x++)
-    for (int y = 0; y < 1000; y++)
-      *bitmap_.getAddr32(x, y) = kRed;
-
-  ASSERT_TRUE(GetBlurredImage(bitmap_, &blurred));
-  ASSERT_EQ(48, blurred.width());
-  ASSERT_EQ(48, blurred.height());
-  ASSERT_EQ(3u * 48u * 48u, blurred.data().size());
-  for (size_t i = 0; i < 48u * 48u; i++) {
-    EXPECT_EQ('\xff', blurred.data()[3 * i]);
-    EXPECT_EQ('\x00', blurred.data()[3 * i + 1]);
-    EXPECT_EQ('\x00', blurred.data()[3 * i + 2]);
-  }
-}
-
-TEST_F(VisualUtilsTest, BlurImageHalfWhiteHalfBlack) {
-  VisualFeatures::BlurredImage blurred;
-
-  // Draw black over half the image.
-  for (int x = 0; x < 1000; x++)
-    for (int y = 0; y < 500; y++)
-      *bitmap_.getAddr32(x, y) = kBlack;
-
-  // Draw white over half the image
-  for (int x = 0; x < 1000; x++)
-    for (int y = 500; y < 1000; y++)
-      *bitmap_.getAddr32(x, y) = kWhite;
-
-  ASSERT_TRUE(GetBlurredImage(bitmap_, &blurred));
-  ASSERT_EQ(48, blurred.width());
-  ASSERT_EQ(48, blurred.height());
-  ASSERT_EQ(3u * 48u * 48u, blurred.data().size());
-  // The middle blocks may have been blurred to something between white and
-  // black, so only verify the first 22 and last 22 rows.
-  for (size_t i = 0; i < 22u * 48u; i++) {
-    EXPECT_EQ('\x00', blurred.data()[3 * i]);
-    EXPECT_EQ('\x00', blurred.data()[3 * i + 1]);
-    EXPECT_EQ('\x00', blurred.data()[3 * i + 2]);
-  }
-
-  for (size_t i = 26u * 48u; i < 48u * 48u; i++) {
-    EXPECT_EQ('\xff', blurred.data()[3 * i]);
-    EXPECT_EQ('\xff', blurred.data()[3 * i + 1]);
-    EXPECT_EQ('\xff', blurred.data()[3 * i + 2]);
-  }
-}
-
-TEST_F(VisualUtilsTest, BlockMeanAverageOneBlock) {
-  // Draw black over half the image.
-  for (int x = 0; x < 1000; x++)
-    for (int y = 0; y < 500; y++)
-      *bitmap_.getAddr32(x, y) = kBlack;
-
-  // Draw white over half the image
-  for (int x = 0; x < 1000; x++)
-    for (int y = 500; y < 1000; y++)
-      *bitmap_.getAddr32(x, y) = kWhite;
-
-  std::unique_ptr<SkBitmap> blocks = BlockMeanAverage(bitmap_, 1000);
-  ASSERT_EQ(1, blocks->width());
-  ASSERT_EQ(1, blocks->height());
-  EXPECT_EQ(blocks->getColor(0, 0), SkColorSetRGB(127, 127, 127));
-}
-
-TEST_F(VisualUtilsTest, BlockMeanAveragePartialBlocks) {
-  // Draw a white, red, green, and blue box with the expected block sizes.
-  for (int x = 0; x < 600; x++)
-    for (int y = 0; y < 600; y++)
-      *bitmap_.getAddr32(x, y) = kWhite;
-
-  for (int x = 600; x < 1000; x++)
-    for (int y = 0; y < 600; y++)
-      *bitmap_.getAddr32(x, y) = kRed;
-
-  for (int x = 0; x < 600; x++)
-    for (int y = 600; y < 1000; y++)
-      *bitmap_.getAddr32(x, y) = kGreen;
-
-  for (int x = 600; x < 1000; x++)
-    for (int y = 600; y < 1000; y++)
-      *bitmap_.getAddr32(x, y) = kBlue;
-
-  std::unique_ptr<SkBitmap> blocks = BlockMeanAverage(bitmap_, 600);
-  ASSERT_EQ(2, blocks->width());
-  ASSERT_EQ(2, blocks->height());
-  EXPECT_EQ(*blocks->getAddr32(0, 0), kWhite);
-  EXPECT_EQ(*blocks->getAddr32(1, 0), kRed);
-  EXPECT_EQ(*blocks->getAddr32(0, 1), kGreen);
-  EXPECT_EQ(*blocks->getAddr32(1, 1), kBlue);
-}
-
-}  // namespace visual_utils
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/ping_manager.cc b/components/safe_browsing/ping_manager.cc
deleted file mode 100644
index 129469b2..0000000
--- a/components/safe_browsing/ping_manager.cc
+++ /dev/null
@@ -1,222 +0,0 @@
-// Copyright (c) 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/ping_manager.h"
-
-#include <utility>
-
-#include "base/bind.h"
-#include "base/logging.h"
-#include "base/memory/ptr_util.h"
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
-#include "google_apis/google_api_keys.h"
-#include "net/base/escape.h"
-#include "net/base/load_flags.h"
-#include "net/traffic_annotation/network_traffic_annotation.h"
-#include "services/network/public/cpp/simple_url_loader.h"
-#include "url/gurl.h"
-
-namespace {
-
-const net::NetworkTrafficAnnotationTag kTrafficAnnotation =
-    net::DefineNetworkTrafficAnnotation("safe_browsing_extended_reporting",
-                                        R"(
-      semantics {
-        sender: "Safe Browsing Extended Reporting"
-        description:
-          "When a user is opted in to automatically reporting 'possible "
-          "security incidents to Google,' and they reach a bad page that's "
-          "flagged by Safe Browsing, Chrome will send a report to Google "
-          "with information about the threat. This helps Safe Browsing learn "
-          "where threats originate and thus protect more users."
-        trigger:
-          "When a red interstitial is shown, and the user is opted-in."
-        data:
-          "The report includes the URL and referrer chain of the page. If the "
-          "warning is triggered by a subresource on a partially loaded page, "
-          "the report will include the URL and referrer chain of sub frames "
-          "and resources loaded into the page.  It may also include a subset "
-          "of headers for resources loaded, and some Google ad identifiers to "
-          "help block malicious ads."
-        destination: GOOGLE_OWNED_SERVICE
-      }
-      policy {
-        cookies_allowed: YES
-        cookies_store: "Safe Browsing Cookie Store"
-        setting:
-          "Users can control this feature via the 'Automatically report "
-          "details of possible security incidents to Google' setting under "
-          "'Privacy'. The feature is disabled by default."
-        chrome_policy {
-          SafeBrowsingExtendedReportingOptInAllowed {
-            policy_options {mode: MANDATORY}
-            SafeBrowsingExtendedReportingOptInAllowed: false
-          }
-        }
-      })");
-
-}  // namespace
-
-namespace safe_browsing {
-
-// SafeBrowsingPingManager implementation ----------------------------------
-
-// static
-std::unique_ptr<PingManager> PingManager::Create(
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    const V4ProtocolConfig& config) {
-  return base::WrapUnique(new PingManager(url_loader_factory, config));
-}
-
-PingManager::PingManager(
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    const V4ProtocolConfig& config)
-    : config_(config), url_loader_factory_(url_loader_factory) {}
-
-PingManager::~PingManager() {}
-
-// All SafeBrowsing request responses are handled here.
-void PingManager::OnURLLoaderComplete(
-    network::SimpleURLLoader* source,
-    std::unique_ptr<std::string> response_body) {
-  auto it = safebrowsing_reports_.find(source);
-  DCHECK(it != safebrowsing_reports_.end());
-  safebrowsing_reports_.erase(it);
-}
-
-// Sends a SafeBrowsing "hit" report.
-void PingManager::ReportSafeBrowsingHit(
-    const safe_browsing::HitReport& hit_report) {
-  auto resource_request = std::make_unique<network::ResourceRequest>();
-  GURL report_url = SafeBrowsingHitUrl(hit_report);
-  resource_request->url = report_url;
-  resource_request->load_flags = net::LOAD_DISABLE_CACHE;
-  if (!hit_report.post_data.empty())
-    resource_request->method = "POST";
-
-  auto report_ptr = network::SimpleURLLoader::Create(
-      std::move(resource_request), kTrafficAnnotation);
-
-  if (!hit_report.post_data.empty())
-    report_ptr->AttachStringForUpload(hit_report.post_data, "text/plain");
-
-  report_ptr->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
-      url_loader_factory_.get(),
-      base::BindOnce(&PingManager::OnURLLoaderComplete, base::Unretained(this),
-                     report_ptr.get()));
-  safebrowsing_reports_.insert(std::move(report_ptr));
-}
-
-// Sends threat details for users who opt-in.
-void PingManager::ReportThreatDetails(const std::string& report) {
-  GURL report_url = ThreatDetailsUrl();
-
-  auto resource_request = std::make_unique<network::ResourceRequest>();
-  resource_request->url = report_url;
-  resource_request->load_flags = net::LOAD_DISABLE_CACHE;
-  resource_request->method = "POST";
-
-  auto loader = network::SimpleURLLoader::Create(std::move(resource_request),
-                                                 kTrafficAnnotation);
-
-  loader->AttachStringForUpload(report, "application/octet-stream");
-
-  loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
-      url_loader_factory_.get(),
-      base::BindOnce(&PingManager::OnURLLoaderComplete, base::Unretained(this),
-                     loader.get()));
-  safebrowsing_reports_.insert(std::move(loader));
-}
-
-GURL PingManager::SafeBrowsingHitUrl(
-    const safe_browsing::HitReport& hit_report) const {
-  DCHECK(hit_report.threat_type == SB_THREAT_TYPE_URL_MALWARE ||
-         hit_report.threat_type == SB_THREAT_TYPE_URL_PHISHING ||
-         hit_report.threat_type == SB_THREAT_TYPE_URL_UNWANTED ||
-         hit_report.threat_type == SB_THREAT_TYPE_URL_BINARY_MALWARE ||
-         hit_report.threat_type == SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING ||
-         hit_report.threat_type == SB_THREAT_TYPE_URL_CLIENT_SIDE_MALWARE);
-  std::string url =
-      GetReportUrl(config_, "report", &hit_report.extended_reporting_level);
-  std::string threat_list = "none";
-  switch (hit_report.threat_type) {
-    case SB_THREAT_TYPE_URL_MALWARE:
-      threat_list = "malblhit";
-      break;
-    case SB_THREAT_TYPE_URL_PHISHING:
-      threat_list = "phishblhit";
-      break;
-    case SB_THREAT_TYPE_URL_UNWANTED:
-      threat_list = "uwsblhit";
-      break;
-    case SB_THREAT_TYPE_URL_BINARY_MALWARE:
-      threat_list = "binurlhit";
-      break;
-    case SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING:
-      threat_list = "phishcsdhit";
-      break;
-    case SB_THREAT_TYPE_URL_CLIENT_SIDE_MALWARE:
-      threat_list = "malcsdhit";
-      break;
-    default:
-      NOTREACHED();
-  }
-
-  std::string threat_source = "none";
-  switch (hit_report.threat_source) {
-    case safe_browsing::ThreatSource::DATA_SAVER:
-      threat_source = "ds";
-      break;
-    case safe_browsing::ThreatSource::REMOTE:
-      threat_source = "rem";
-      break;
-    case safe_browsing::ThreatSource::LOCAL_PVER3:
-      threat_source = "l3";
-      break;
-    case safe_browsing::ThreatSource::LOCAL_PVER4:
-      threat_source = "l4";
-      break;
-    case safe_browsing::ThreatSource::CLIENT_SIDE_DETECTION:
-      threat_source = "csd";
-      break;
-    case safe_browsing::ThreatSource::PASSWORD_PROTECTION_SERVICE:
-      threat_source = "pps";
-      break;
-    case safe_browsing::ThreatSource::UNKNOWN:
-      NOTREACHED();
-  }
-
-  // Add user_population component only if it's not empty.
-  std::string user_population_comp;
-  if (!hit_report.population_id.empty()) {
-    // Population_id should be URL-safe, but escape it and size-limit it
-    // anyway since it came from outside Chrome.
-    std::string up_str =
-        net::EscapeQueryParamValue(hit_report.population_id, true);
-    if (up_str.size() > 512) {
-      DCHECK(false) << "population_id is too long: " << up_str;
-      up_str = "UP_STRING_TOO_LONG";
-    }
-
-    user_population_comp = "&up=" + up_str;
-  }
-
-  return GURL(base::StringPrintf(
-      "%s&evts=%s&evtd=%s&evtr=%s&evhr=%s&evtb=%d&src=%s&m=%d%s", url.c_str(),
-      threat_list.c_str(),
-      net::EscapeQueryParamValue(hit_report.malicious_url.spec(), true).c_str(),
-      net::EscapeQueryParamValue(hit_report.page_url.spec(), true).c_str(),
-      net::EscapeQueryParamValue(hit_report.referrer_url.spec(), true).c_str(),
-      hit_report.is_subresource, threat_source.c_str(),
-      hit_report.is_metrics_reporting_active, user_population_comp.c_str()));
-}
-
-GURL PingManager::ThreatDetailsUrl() const {
-  std::string url = GetReportUrl(config_, "clientreport/malware");
-  return GURL(url);
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/ping_manager.h b/components/safe_browsing/ping_manager.h
deleted file mode 100644
index b3c87a4..0000000
--- a/components/safe_browsing/ping_manager.h
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright (c) 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_PING_MANAGER_H_
-#define COMPONENTS_SAFE_BROWSING_PING_MANAGER_H_
-
-// A class that reports basic safebrowsing statistics to Google's SafeBrowsing
-// servers.
-#include <memory>
-#include <set>
-#include <string>
-#include <vector>
-
-#include "base/containers/unique_ptr_adapters.h"
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "components/safe_browsing/db/hit_report.h"
-#include "components/safe_browsing/db/util.h"
-#include "content/public/browser/permission_type.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "url/gurl.h"
-
-namespace network {
-class SimpleURLLoader;
-}  // namespace network
-
-namespace safe_browsing {
-
-class PingManager {
- public:
-  virtual ~PingManager();
-
-  // Create an instance of the safe browsing ping manager.
-  static std::unique_ptr<PingManager> Create(
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      const V4ProtocolConfig& config);
-
-  void OnURLLoaderComplete(network::SimpleURLLoader* source,
-                           std::unique_ptr<std::string> response_body);
-
-  // Report to Google when a SafeBrowsing warning is shown to the user.
-  // |hit_report.threat_type| should be one of the types known by
-  // SafeBrowsingtHitUrl.
-  void ReportSafeBrowsingHit(const safe_browsing::HitReport& hit_report);
-
-  // Users can opt-in on the SafeBrowsing interstitial to send detailed
-  // threat reports. |report| is the serialized report.
-  void ReportThreatDetails(const std::string& report);
-
- protected:
-  friend class PingManagerTest;
-  // Constructs a PingManager that issues network requests
-  // using |url_loader_factory|.
-  PingManager(scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-              const V4ProtocolConfig& config);
-
- private:
-  FRIEND_TEST_ALL_PREFIXES(PingManagerTest, TestSafeBrowsingHitUrl);
-  FRIEND_TEST_ALL_PREFIXES(PingManagerTest, TestThreatDetailsUrl);
-  FRIEND_TEST_ALL_PREFIXES(PingManagerTest, TestReportThreatDetails);
-  FRIEND_TEST_ALL_PREFIXES(PingManagerTest, TestReportSafeBrowsingHit);
-
-  const V4ProtocolConfig config_;
-
-  using Reports = std::set<std::unique_ptr<network::SimpleURLLoader>,
-                           base::UniquePtrComparator>;
-
-  // Generates URL for reporting safe browsing hits.
-  GURL SafeBrowsingHitUrl(const safe_browsing::HitReport& hit_report) const;
-
-  // Generates URL for reporting threat details for users who opt-in.
-  GURL ThreatDetailsUrl() const;
-
-  // The URLLoaderFactory we use to issue network requests.
-  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
-
-  // Track outstanding SafeBrowsing report fetchers for clean up.
-  // We add both "hit" and "detail" fetchers in this set.
-  Reports safebrowsing_reports_;
-
-  DISALLOW_COPY_AND_ASSIGN(PingManager);
-};
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_PING_MANAGER_H_
diff --git a/components/safe_browsing/ping_manager_unittest.cc b/components/safe_browsing/ping_manager_unittest.cc
deleted file mode 100644
index 9cd7034..0000000
--- a/components/safe_browsing/ping_manager_unittest.cc
+++ /dev/null
@@ -1,190 +0,0 @@
-// Copyright (c) 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-
-#include "components/safe_browsing/ping_manager.h"
-#include "base/base64.h"
-#include "base/logging.h"
-#include "base/run_loop.h"
-#include "base/strings/stringprintf.h"
-#include "base/time/time.h"
-#include "base/values.h"
-#include "components/safe_browsing/db/v4_test_util.h"
-#include "google_apis/google_api_keys.h"
-#include "net/base/escape.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using base::Time;
-using base::TimeDelta;
-using safe_browsing::HitReport;
-using safe_browsing::ThreatSource;
-
-namespace safe_browsing {
-
-class PingManagerTest : public testing::Test {
- public:
-  PingManagerTest() {}
-
- protected:
-  void SetUp() override {
-    std::string key = google_apis::GetAPIKey();
-    if (!key.empty()) {
-      key_param_ = base::StringPrintf(
-          "&key=%s", net::EscapeQueryParamValue(key, true).c_str());
-    }
-
-    ping_manager_.reset(
-        new PingManager(nullptr, safe_browsing::GetTestV4ProtocolConfig()));
-  }
-
-  PingManager* ping_manager() { return ping_manager_.get(); }
-
-  std::string key_param_;
-  std::unique_ptr<PingManager> ping_manager_;
-};
-
-TEST_F(PingManagerTest, TestSafeBrowsingHitUrl) {
-  HitReport base_hp;
-  base_hp.malicious_url = GURL("http://malicious.url.com");
-  base_hp.page_url = GURL("http://page.url.com");
-  base_hp.referrer_url = GURL("http://referrer.url.com");
-
-  {
-    HitReport hp(base_hp);
-    hp.threat_type = SB_THREAT_TYPE_URL_MALWARE;
-    hp.threat_source = ThreatSource::LOCAL_PVER3;
-    hp.is_subresource = true;
-    hp.extended_reporting_level = SBER_LEVEL_LEGACY;
-    hp.is_metrics_reporting_active = true;
-
-    EXPECT_EQ(
-        "https://safebrowsing.google.com/safebrowsing/report?client=unittest&"
-        "appver=1.0&pver=4.0" +
-            key_param_ +
-            "&ext=1&evts=malblhit&evtd=http%3A%2F%2Fmalicious.url.com%2F&"
-            "evtr=http%3A%2F%2Fpage.url.com%2F&evhr=http%3A%2F%2Freferrer."
-            "url.com%2F&evtb=1&src=l3&m=1",
-        ping_manager()->SafeBrowsingHitUrl(hp).spec());
-  }
-
-  {
-    HitReport hp(base_hp);
-    hp.threat_type = SB_THREAT_TYPE_URL_PHISHING;
-    hp.threat_source = ThreatSource::DATA_SAVER;
-    hp.is_subresource = false;
-    hp.extended_reporting_level = SBER_LEVEL_LEGACY;
-    hp.is_metrics_reporting_active = true;
-    EXPECT_EQ(
-        "https://safebrowsing.google.com/safebrowsing/report?client=unittest&"
-        "appver=1.0&pver=4.0" +
-            key_param_ +
-            "&ext=1&evts=phishblhit&"
-            "evtd=http%3A%2F%2Fmalicious.url.com%2F&"
-            "evtr=http%3A%2F%2Fpage.url.com%2F&evhr=http%3A%2F%2Freferrer."
-            "url.com%2F&evtb=0&src=ds&m=1",
-        ping_manager()->SafeBrowsingHitUrl(hp).spec());
-  }
-
-  {
-    HitReport hp(base_hp);
-    hp.threat_type = SB_THREAT_TYPE_URL_PHISHING;
-    hp.threat_source = ThreatSource::DATA_SAVER;
-    hp.is_subresource = false;
-    hp.extended_reporting_level = SBER_LEVEL_SCOUT;
-    hp.is_metrics_reporting_active = true;
-    EXPECT_EQ(
-        "https://safebrowsing.google.com/safebrowsing/report?client=unittest&"
-        "appver=1.0&pver=4.0" +
-            key_param_ +
-            "&ext=2&evts=phishblhit&"
-            "evtd=http%3A%2F%2Fmalicious.url.com%2F&"
-            "evtr=http%3A%2F%2Fpage.url.com%2F&evhr=http%3A%2F%2Freferrer."
-            "url.com%2F&evtb=0&src=ds&m=1",
-        ping_manager()->SafeBrowsingHitUrl(hp).spec());
-  }
-
-  {
-    HitReport hp(base_hp);
-    hp.threat_type = SB_THREAT_TYPE_URL_BINARY_MALWARE;
-    hp.threat_source = ThreatSource::REMOTE;
-    hp.extended_reporting_level = SBER_LEVEL_OFF;
-    hp.is_metrics_reporting_active = true;
-    hp.is_subresource = false;
-    EXPECT_EQ(
-        "https://safebrowsing.google.com/safebrowsing/report?client=unittest&"
-        "appver=1.0&pver=4.0" +
-            key_param_ +
-            "&ext=0&evts=binurlhit&"
-            "evtd=http%3A%2F%2Fmalicious.url.com%2F&"
-            "evtr=http%3A%2F%2Fpage.url.com%2F&evhr=http%3A%2F%2Freferrer."
-            "url.com%2F&evtb=0&src=rem&m=1",
-        ping_manager()->SafeBrowsingHitUrl(hp).spec());
-  }
-
-  {
-    HitReport hp(base_hp);
-    hp.threat_type = SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING;
-    hp.threat_source = ThreatSource::LOCAL_PVER4;
-    hp.extended_reporting_level = SBER_LEVEL_OFF;
-    hp.is_metrics_reporting_active = false;
-    hp.is_subresource = false;
-    EXPECT_EQ(
-        "https://safebrowsing.google.com/safebrowsing/report?client=unittest&"
-        "appver=1.0&pver=4.0" +
-            key_param_ +
-            "&ext=0&evts=phishcsdhit&"
-            "evtd=http%3A%2F%2Fmalicious.url.com%2F&"
-            "evtr=http%3A%2F%2Fpage.url.com%2F&evhr=http%3A%2F%2Freferrer."
-            "url.com%2F&evtb=0&src=l4&m=0",
-        ping_manager()->SafeBrowsingHitUrl(hp).spec());
-  }
-
-  {
-    HitReport hp(base_hp);
-    hp.threat_type = SB_THREAT_TYPE_URL_CLIENT_SIDE_MALWARE;
-    hp.threat_source = ThreatSource::LOCAL_PVER4;
-    hp.extended_reporting_level = SBER_LEVEL_OFF;
-    hp.is_metrics_reporting_active = false;
-    hp.is_subresource = true;
-    EXPECT_EQ(
-        "https://safebrowsing.google.com/safebrowsing/report?client=unittest&"
-        "appver=1.0&pver=4.0" +
-            key_param_ +
-            "&ext=0&evts=malcsdhit&"
-            "evtd=http%3A%2F%2Fmalicious.url.com%2F&"
-            "evtr=http%3A%2F%2Fpage.url.com%2F&evhr=http%3A%2F%2Freferrer."
-            "url.com%2F&evtb=1&src=l4&m=0",
-        ping_manager()->SafeBrowsingHitUrl(hp).spec());
-  }
-
-  // Same as above, but add population_id
-  {
-    HitReport hp(base_hp);
-    hp.threat_type = SB_THREAT_TYPE_URL_CLIENT_SIDE_MALWARE;
-    hp.threat_source = ThreatSource::LOCAL_PVER4;
-    hp.extended_reporting_level = SBER_LEVEL_OFF;
-    hp.is_metrics_reporting_active = false;
-    hp.is_subresource = true;
-    hp.population_id = "foo bar";
-    EXPECT_EQ(
-        "https://safebrowsing.google.com/safebrowsing/report?client=unittest&"
-        "appver=1.0&pver=4.0" +
-            key_param_ +
-            "&ext=0&evts=malcsdhit&"
-            "evtd=http%3A%2F%2Fmalicious.url.com%2F&"
-            "evtr=http%3A%2F%2Fpage.url.com%2F&evhr=http%3A%2F%2Freferrer."
-            "url.com%2F&evtb=1&src=l4&m=0&up=foo+bar",
-        ping_manager()->SafeBrowsingHitUrl(hp).spec());
-  }
-}
-
-TEST_F(PingManagerTest, TestThreatDetailsUrl) {
-  EXPECT_EQ(
-      "https://safebrowsing.google.com/safebrowsing/clientreport/malware?"
-      "client=unittest&appver=1.0&pver=4.0" +
-          key_param_,
-      ping_manager()->ThreatDetailsUrl().spec());
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/proto/PRESUBMIT.py b/components/safe_browsing/proto/PRESUBMIT.py
deleted file mode 100644
index 1b4c0c8..0000000
--- a/components/safe_browsing/proto/PRESUBMIT.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright 2018 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.
-
-def CheckChangeOnUpload(input_api, output_api):
-  results = []
-
-  # Warn if the proto file is not modified without also modifying
-  # the WebUI and extension API idl file.
-  proto_path = 'components/safe_browsing/proto/csd.proto'
-  web_ui_path = 'components/safe_browsing/web_ui/safe_browsing_ui.cc'
-  idl_path = 'chrome/common/extensions/api/safe_browsing_private.idl'
-
-  if proto_path in input_api.change.LocalPaths():
-    if web_ui_path not in input_api.change.LocalPaths():
-      results.append(
-          output_api.PresubmitPromptWarning(
-              'You modified the one or more of the CSD protos in: \n'
-              '  ' + proto_path + '\n'
-              'without changing the WebUI in: \n'
-              '  ' + web_ui_path + '\n')
-      )
-    if idl_path not in input_api.change.LocalPaths():
-      results.append(
-          output_api.PresubmitPromptWarning(
-              'You modified the one or more of the CSD protos in: \n'
-              '  ' + proto_path + '\n'
-              'without changing the API definition in: \n'
-              '  ' + idl_path + '\n')
-      )
-  return results
diff --git a/components/safe_browsing/realtime/BUILD.gn b/components/safe_browsing/realtime/BUILD.gn
deleted file mode 100644
index 6d2426681d..0000000
--- a/components/safe_browsing/realtime/BUILD.gn
+++ /dev/null
@@ -1,60 +0,0 @@
-# Copyright 2019 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.
-
-static_library("policy_engine") {
-  sources = [
-    "policy_engine.cc",
-    "policy_engine.h",
-  ]
-
-  deps = [
-    "//base:base",
-    "//components/prefs",
-    "//components/safe_browsing:features",
-    "//components/safe_browsing:realtimeapi_proto",
-    "//components/safe_browsing/common:safe_browsing_prefs",
-    "//components/unified_consent",
-    "//components/user_prefs",
-    "//content/public/browser",
-    "//content/public/common:resource_type_header",
-  ]
-}
-
-static_library("url_lookup_service") {
-  sources = [
-    "url_lookup_service.cc",
-    "url_lookup_service.h",
-  ]
-
-  deps = [
-    ":policy_engine",
-    "//base:base",
-    "//components/safe_browsing:realtimeapi_proto",
-    "//components/safe_browsing/db:v4_protocol_manager_util",
-    "//content/public/browser",
-    "//url:url",
-  ]
-}
-
-source_set("unit_tests") {
-  testonly = true
-  sources = [
-    "policy_engine_unittest.cc",
-    "url_lookup_service_unittest.cc",
-  ]
-  deps = [
-    ":policy_engine",
-    ":url_lookup_service",
-    "//base/test:test_support",
-    "//components/safe_browsing:features",
-    "//components/safe_browsing/common:safe_browsing_prefs",
-    "//components/sync_preferences:test_support",
-    "//components/unified_consent",
-    "//components/user_prefs",
-    "//content/test:test_support",
-    "//services/network:test_support",
-    "//services/network/public/cpp:cpp",
-    "//testing/gtest",
-  ]
-}
diff --git a/components/safe_browsing/realtime/policy_engine.cc b/components/safe_browsing/realtime/policy_engine.cc
deleted file mode 100644
index 55341cc..0000000
--- a/components/safe_browsing/realtime/policy_engine.cc
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/realtime/policy_engine.h"
-
-#include "base/feature_list.h"
-#include "base/metrics/histogram_macros.h"
-#include "build/build_config.h"
-#include "components/prefs/pref_service.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/features.h"
-#include "components/unified_consent/pref_names.h"
-#include "components/user_prefs/user_prefs.h"
-#include "content/public/browser/browser_context.h"
-
-#if defined(OS_ANDROID)
-#include "base/metrics/field_trial_params.h"
-#include "base/system/sys_info.h"
-#endif
-
-namespace safe_browsing {
-
-#if defined(OS_ANDROID)
-const int kDefaultMemoryThresholdMb = 4096;
-#endif
-
-// static
-bool RealTimePolicyEngine::IsUrlLookupEnabled() {
-  if (!base::FeatureList::IsEnabled(kRealTimeUrlLookupEnabled))
-    return false;
-#if defined(OS_ANDROID)
-  // On Android, performs real time URL lookup only if
-  // |kRealTimeUrlLookupEnabled| is enabled, and system memory is larger than
-  // threshold.
-  int memory_threshold_mb = base::GetFieldTrialParamByFeatureAsInt(
-      kRealTimeUrlLookupEnabled, kRealTimeUrlLookupMemoryThresholdMb,
-      kDefaultMemoryThresholdMb);
-  return base::SysInfo::AmountOfPhysicalMemoryMB() >= memory_threshold_mb;
-#else
-  return true;
-#endif
-}
-
-// static
-bool RealTimePolicyEngine::IsUserOptedIn(
-    content::BrowserContext* browser_context) {
-  PrefService* pref_service = user_prefs::UserPrefs::Get(browser_context);
-  return pref_service->GetBoolean(
-      unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled);
-}
-
-// static
-bool RealTimePolicyEngine::IsEnabledByPolicy(
-    content::BrowserContext* browser_context) {
-  return false;
-}
-
-// static
-bool RealTimePolicyEngine::CanPerformFullURLLookup(
-    content::BrowserContext* browser_context) {
-  if (browser_context->IsOffTheRecord())
-    return false;
-
-  if (IsEnabledByPolicy(browser_context))
-    return true;
-
-  return IsUrlLookupEnabled() && IsUserOptedIn(browser_context);
-}
-
-// static
-bool RealTimePolicyEngine::CanPerformFullURLLookupForResourceType(
-    content::ResourceType resource_type) {
-  UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.RT.ResourceTypes.Requested",
-                            resource_type);
-  return resource_type == content::ResourceType::kMainFrame;
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/realtime/policy_engine.h b/components/safe_browsing/realtime/policy_engine.h
deleted file mode 100644
index 8de5b6d..0000000
--- a/components/safe_browsing/realtime/policy_engine.h
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_REALTIME_POLICY_ENGINE_H_
-#define COMPONENTS_SAFE_BROWSING_REALTIME_POLICY_ENGINE_H_
-
-#include "build/build_config.h"
-#include "content/public/common/resource_type.h"
-
-namespace content {
-class BrowserContext;
-}
-
-namespace safe_browsing {
-
-#if defined(OS_ANDROID)
-// A parameter controlled by finch experiment.
-// On Android, performs real time URL lookup only if |kRealTimeUrlLookupEnabled|
-// is enabled, and system memory is larger than threshold.
-const char kRealTimeUrlLookupMemoryThresholdMb[] =
-    "SafeBrowsingRealTimeUrlLookupMemoryThresholdMb";
-#endif
-
-// This class implements the logic to decide whether the real time lookup
-// feature is enabled for a given user/profile.
-class RealTimePolicyEngine {
- public:
-  RealTimePolicyEngine() = delete;
-  ~RealTimePolicyEngine() = delete;
-
-  // Return true if full URL lookups are enabled for |resource_type|.
-  static bool CanPerformFullURLLookupForResourceType(
-      content::ResourceType resource_type);
-
-  // Return true if the feature to enable full URL lookups is enabled and the
-  // allowlist fetch is enabled for the profile represented by
-  // |browser_context|.
-  static bool CanPerformFullURLLookup(content::BrowserContext* browser_context);
-
-  friend class SafeBrowsingService;
-
- private:
-  // Is the feature to perform real-time URL lookup enabled?
-  static bool IsUrlLookupEnabled();
-
-  // Is user opted-in to the feature?
-  static bool IsUserOptedIn(content::BrowserContext* browser_context);
-
-  // Is the feature enabled due to enterprise policy?
-  static bool IsEnabledByPolicy(content::BrowserContext* browser_context);
-
-  friend class RealTimePolicyEngineTest;
-};  // class RealTimePolicyEngine
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_REALTIME_POLICY_ENGINE_H_
diff --git a/components/safe_browsing/realtime/policy_engine_unittest.cc b/components/safe_browsing/realtime/policy_engine_unittest.cc
deleted file mode 100644
index ad633d12..0000000
--- a/components/safe_browsing/realtime/policy_engine_unittest.cc
+++ /dev/null
@@ -1,148 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/realtime/policy_engine.h"
-
-#include "base/test/scoped_feature_list.h"
-#include "build/build_config.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/features.h"
-#include "components/sync_preferences/testing_pref_service_syncable.h"
-#include "components/unified_consent/pref_names.h"
-#include "components/unified_consent/unified_consent_service.h"
-#include "components/user_prefs/user_prefs.h"
-#include "content/public/test/browser_task_environment.h"
-#include "content/public/test/test_browser_context.h"
-#include "testing/platform_test.h"
-
-#if defined(OS_ANDROID)
-#include "base/strings/string_number_conversions.h"
-#include "base/system/sys_info.h"
-#endif
-
-namespace safe_browsing {
-
-class RealTimePolicyEngineTest : public PlatformTest {
- public:
-  void SetUp() override {
-    user_prefs::UserPrefs::Set(&test_context_, &pref_service_);
-    RegisterProfilePrefs(pref_service_.registry());
-    unified_consent::UnifiedConsentService::RegisterPrefs(
-        pref_service_.registry());
-  }
-
-  bool IsUserOptedIn() {
-    return RealTimePolicyEngine::IsUserOptedIn(&test_context_);
-  }
-
-  bool CanPerformFullURLLookup() {
-    return RealTimePolicyEngine::CanPerformFullURLLookup(&test_context_);
-  }
-
-  content::BrowserTaskEnvironment task_environment_;
-  content::TestBrowserContext test_context_;
-  sync_preferences::TestingPrefServiceSyncable pref_service_;
-};
-
-#if defined(OS_ANDROID)
-// Real time URL check on Android is controlled by system memory size, the
-// following tests test that logic.
-TEST_F(RealTimePolicyEngineTest, TestCanPerformFullURLLookup_LargeMemorySize) {
-  base::test::ScopedFeatureList feature_list;
-  int system_memory_size = base::SysInfo::AmountOfPhysicalMemoryMB();
-  int memory_size_threshold = system_memory_size - 1;
-  feature_list.InitWithFeaturesAndParameters(
-      /* enabled_features */ {{kRealTimeUrlLookupEnabled,
-                               {{kRealTimeUrlLookupMemoryThresholdMb,
-                                 base::NumberToString(
-                                     memory_size_threshold)}}}},
-      /* disabled_features */ {});
-  pref_service_.SetUserPref(
-      unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled,
-      std::make_unique<base::Value>(true));
-  EXPECT_TRUE(CanPerformFullURLLookup());
-}
-
-TEST_F(RealTimePolicyEngineTest, TestCanPerformFullURLLookup_SmallMemorySize) {
-  base::test::ScopedFeatureList feature_list;
-  int system_memory_size = base::SysInfo::AmountOfPhysicalMemoryMB();
-  int memory_size_threshold = system_memory_size + 1;
-  feature_list.InitWithFeaturesAndParameters(
-      /* enabled_features */ {{kRealTimeUrlLookupEnabled,
-                               {{kRealTimeUrlLookupMemoryThresholdMb,
-                                 base::NumberToString(
-                                     memory_size_threshold)}}}},
-      /* disabled_features */ {});
-  pref_service_.SetUserPref(
-      unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled,
-      std::make_unique<base::Value>(true));
-  EXPECT_FALSE(CanPerformFullURLLookup());
-}
-
-TEST_F(RealTimePolicyEngineTest,
-       TestCanPerformFullURLLookup_DisabledUrlLookupWithLargeMemorySize) {
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitWithFeaturesAndParameters(
-      /* enabled_features */ {},
-      /* disabled_features */ {kRealTimeUrlLookupEnabled});
-  EXPECT_FALSE(CanPerformFullURLLookup());
-}
-#endif  // defined(OS_ANDROID)
-
-TEST_F(RealTimePolicyEngineTest, TestCanPerformFullURLLookup_EnabledByPolicy) {
-  base::test::ScopedFeatureList feature_list;
-  pref_service_.SetManagedPref(prefs::kSafeBrowsingRealTimeLookupEnabled,
-                               std::make_unique<base::Value>(true));
-  // Verifies that setting the pref still doesn't enable the feature.
-  // See crbug.com/1030815 for details.
-  EXPECT_FALSE(CanPerformFullURLLookup());
-}
-
-TEST_F(RealTimePolicyEngineTest,
-       TestCanPerformFullURLLookup_DisabledUrlLookup) {
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndDisableFeature(kRealTimeUrlLookupEnabled);
-  EXPECT_FALSE(CanPerformFullURLLookup());
-}
-
-TEST_F(RealTimePolicyEngineTest,
-       TestCanPerformFullURLLookup_DisabledOffTheRecord) {
-  base::test::ScopedFeatureList feature_list;
-  pref_service_.SetManagedPref(prefs::kSafeBrowsingRealTimeLookupEnabled,
-                               std::make_unique<base::Value>(true));
-  test_context_.set_is_off_the_record(true);
-  EXPECT_FALSE(CanPerformFullURLLookup());
-}
-
-TEST_F(RealTimePolicyEngineTest,
-       TestCanPerformFullURLLookup_DisabledUserOptin) {
-  ASSERT_FALSE(IsUserOptedIn());
-}
-
-TEST_F(RealTimePolicyEngineTest, TestCanPerformFullURLLookup_EnabledUserOptin) {
-  pref_service_.SetUserPref(
-      unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled,
-      std::make_unique<base::Value>(true));
-  ASSERT_TRUE(IsUserOptedIn());
-}
-
-TEST_F(RealTimePolicyEngineTest,
-       TestCanPerformFullURLLookup_EnabledMainFrameOnly) {
-  for (int i = 0; i <= static_cast<int>(content::ResourceType::kMaxValue);
-       i++) {
-    content::ResourceType resource_type = static_cast<content::ResourceType>(i);
-    bool enabled = RealTimePolicyEngine::CanPerformFullURLLookupForResourceType(
-        resource_type);
-    switch (resource_type) {
-      case content::ResourceType::kMainFrame:
-        EXPECT_TRUE(enabled);
-        break;
-      default:
-        EXPECT_FALSE(enabled);
-        break;
-    }
-  }
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/realtime/url_lookup_service.cc b/components/safe_browsing/realtime/url_lookup_service.cc
deleted file mode 100644
index 308ff2140..0000000
--- a/components/safe_browsing/realtime/url_lookup_service.cc
+++ /dev/null
@@ -1,270 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/realtime/url_lookup_service.h"
-
-#include "base/base64url.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/strings/string_piece.h"
-#include "base/time/time.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
-#include "content/public/browser/browser_thread.h"
-#include "net/base/ip_address.h"
-#include "net/base/load_flags.h"
-#include "net/base/url_util.h"
-#include "net/http/http_status_code.h"
-#include "net/traffic_annotation/network_traffic_annotation.h"
-#include "services/network/public/cpp/resource_request.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "services/network/public/cpp/simple_url_loader.h"
-
-namespace safe_browsing {
-
-namespace {
-
-const char kRealTimeLookupUrlPrefix[] =
-    "https://safebrowsing.google.com/safebrowsing/clientreport/realtime";
-
-const size_t kMaxFailuresToEnforceBackoff = 3;
-
-const size_t kMinBackOffResetDurationInSeconds = 5 * 60;   //  5 minutes.
-const size_t kMaxBackOffResetDurationInSeconds = 30 * 60;  // 30 minutes.
-
-const size_t kURLLookupTimeoutDurationInSeconds = 10;  // 10 seconds.
-
-// Fragements, usernames and passwords are removed, becuase fragments are only
-// used for local navigations and usernames/passwords are too privacy sensitive.
-GURL SanitizeURL(const GURL& url) {
-  GURL::Replacements replacements;
-  replacements.ClearRef();
-  replacements.ClearUsername();
-  replacements.ClearPassword();
-  return url.ReplaceComponents(replacements);
-}
-
-}  // namespace
-
-RealTimeUrlLookupService::RealTimeUrlLookupService(
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
-    : url_loader_factory_(url_loader_factory) {}
-
-void RealTimeUrlLookupService::StartLookup(
-    const GURL& url,
-    RTLookupRequestCallback request_callback,
-    RTLookupResponseCallback response_callback) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-  DCHECK(url.is_valid());
-
-  std::unique_ptr<RTLookupRequest> request = FillRequestProto(url);
-
-  std::string req_data;
-  request->SerializeToString(&req_data);
-  net::NetworkTrafficAnnotationTag traffic_annotation =
-      net::DefineNetworkTrafficAnnotation("safe_browsing_realtime_url_lookup",
-                                          R"(
-        semantics {
-          sender: "Safe Browsing"
-          description:
-            "When Safe Browsing can't detect that a URL is safe based on its "
-            "local database, it sends the top-level URL to Google to verify it "
-            "before showing a warning to the user."
-          trigger:
-            "When a main frame URL fails to match the local hash-prefix "
-            "database of known safe URLs and a valid result from a prior "
-            "lookup is not already cached, this will be sent."
-          data: "The main frame URL that did not match the local safelist."
-          destination: GOOGLE_OWNED_SERVICE
-        }
-        policy {
-          cookies_allowed: YES
-          cookies_store: "Safe Browsing cookie store"
-          setting:
-            "Users can disable Safe Browsing real time URL checks by "
-            "unchecking 'Protect you and your device from dangerous sites' in "
-            "Chromium settings under Privacy, or by unchecking 'Make searches "
-            "and browsing better (Sends URLs of pages you visit to Google)' in "
-            "Chromium settings under Privacy."
-          chrome_policy {
-            SafeBrowsingRealTimeLookupEnabled {
-              policy_options {mode: MANDATORY}
-              SafeBrowsingRealTimeLookupEnabled: false
-            }
-          }
-        })");
-
-  auto resource_request = std::make_unique<network::ResourceRequest>();
-  resource_request->url = GURL(kRealTimeLookupUrlPrefix);
-  resource_request->load_flags = net::LOAD_DISABLE_CACHE;
-  resource_request->method = "POST";
-
-  std::unique_ptr<network::SimpleURLLoader> owned_loader =
-      network::SimpleURLLoader::Create(std::move(resource_request),
-                                       traffic_annotation);
-  network::SimpleURLLoader* loader = owned_loader.get();
-  owned_loader->AttachStringForUpload(req_data, "application/octet-stream");
-  owned_loader->SetTimeoutDuration(
-      base::TimeDelta::FromSeconds(kURLLookupTimeoutDurationInSeconds));
-  owned_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
-      url_loader_factory_.get(),
-      base::BindOnce(&RealTimeUrlLookupService::OnURLLoaderComplete,
-                     GetWeakPtr(), loader, base::TimeTicks::Now()));
-
-  pending_requests_[owned_loader.release()] = std::move(response_callback);
-
-  std::move(request_callback).Run(std::move(request));
-}
-
-RealTimeUrlLookupService::~RealTimeUrlLookupService() {
-  for (auto& pending : pending_requests_) {
-    // An empty response is treated as safe.
-    auto response = std::make_unique<RTLookupResponse>();
-    std::move(pending.second).Run(std::move(response));
-    delete pending.first;
-  }
-  pending_requests_.clear();
-}
-
-void RealTimeUrlLookupService::OnURLLoaderComplete(
-    network::SimpleURLLoader* url_loader,
-    base::TimeTicks request_start_time,
-    std::unique_ptr<std::string> response_body) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-
-  auto it = pending_requests_.find(url_loader);
-  DCHECK(it != pending_requests_.end()) << "Request not found";
-
-  UMA_HISTOGRAM_TIMES("SafeBrowsing.RT.Network.Time",
-                      base::TimeTicks::Now() - request_start_time);
-
-  int net_error = url_loader->NetError();
-  int response_code = 0;
-  if (url_loader->ResponseInfo() && url_loader->ResponseInfo()->headers)
-    response_code = url_loader->ResponseInfo()->headers->response_code();
-  V4ProtocolManagerUtil::RecordHttpResponseOrErrorCode(
-      "SafeBrowsing.RT.Network.Result", net_error, response_code);
-
-  auto response = std::make_unique<RTLookupResponse>();
-  bool success = (net_error == net::OK) && (response_code == net::HTTP_OK) &&
-                 response->ParseFromString(*response_body);
-  success ? HandleLookupSuccess() : HandleLookupError();
-
-  std::move(it->second).Run(std::move(response));
-  delete it->first;
-  pending_requests_.erase(it);
-}
-
-bool RealTimeUrlLookupService::CanCheckUrl(const GURL& url) const {
-  if (!url.SchemeIsHTTPOrHTTPS()) {
-    return false;
-  }
-
-  if (net::IsLocalhost(url)) {
-    // Includes: "//localhost/", "//localhost.localdomain/", "//127.0.0.1/"
-    return false;
-  }
-
-  net::IPAddress ip_address;
-  if (url.HostIsIPAddress() && ip_address.AssignFromIPLiteral(url.host()) &&
-      !ip_address.IsPubliclyRoutable()) {
-    // Includes: "//192.168.1.1/", "//172.16.2.2/", "//10.1.1.1/"
-    return false;
-  }
-
-  return true;
-}
-
-std::unique_ptr<RTLookupRequest> RealTimeUrlLookupService::FillRequestProto(
-    const GURL& url) {
-  auto request = std::make_unique<RTLookupRequest>();
-  request->set_url(SanitizeURL(url).spec());
-  request->set_lookup_type(RTLookupRequest::NAVIGATION);
-  // TODO(crbug.com/1017499): Set ChromeUserPopulation.
-  return request;
-}
-
-size_t RealTimeUrlLookupService::GetBackoffDurationInSeconds() const {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-  return did_successful_lookup_since_last_backoff_
-             ? kMinBackOffResetDurationInSeconds
-             : std::min(kMaxBackOffResetDurationInSeconds,
-                        2 * next_backoff_duration_secs_);
-}
-
-void RealTimeUrlLookupService::HandleLookupError() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-  consecutive_failures_++;
-
-  // Any successful lookup clears both |consecutive_failures_| as well as
-  // |did_successful_lookup_since_last_backoff_|.
-  // On a failure, the following happens:
-  // 1) if |consecutive_failures_| < |kMaxFailuresToEnforceBackoff|:
-  //    Do nothing more.
-  // 2) if already in the backoff mode:
-  //    Do nothing more. This can happen if we had some outstanding real time
-  //    requests in flight when we entered the backoff mode.
-  // 3) if |did_successful_lookup_since_last_backoff_| is true:
-  //    Enter backoff mode for |kMinBackOffResetDurationInSeconds| seconds.
-  // 4) if |did_successful_lookup_since_last_backoff_| is false:
-  //    This indicates that we've had |kMaxFailuresToEnforceBackoff| since
-  //    exiting the last backoff with no successful lookups since so do an
-  //    exponential backoff.
-
-  if (consecutive_failures_ < kMaxFailuresToEnforceBackoff)
-    return;
-
-  if (IsInBackoffMode()) {
-    return;
-  }
-
-  // Enter backoff mode, calculate duration.
-  next_backoff_duration_secs_ = GetBackoffDurationInSeconds();
-  backoff_timer_.Start(
-      FROM_HERE, base::TimeDelta::FromSeconds(next_backoff_duration_secs_),
-      this, &RealTimeUrlLookupService::ResetFailures);
-  did_successful_lookup_since_last_backoff_ = false;
-}
-
-void RealTimeUrlLookupService::HandleLookupSuccess() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-  ResetFailures();
-
-  // |did_successful_lookup_since_last_backoff_| is set to true only when we
-  // complete a lookup successfully.
-  did_successful_lookup_since_last_backoff_ = true;
-}
-
-bool RealTimeUrlLookupService::IsInBackoffMode() const {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-  return backoff_timer_.IsRunning();
-}
-
-void RealTimeUrlLookupService::ResetFailures() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-  consecutive_failures_ = 0;
-  backoff_timer_.Stop();
-}
-
-// static
-SBThreatType RealTimeUrlLookupService::GetSBThreatTypeForRTThreatType(
-    RTLookupResponse::ThreatInfo::ThreatType rt_threat_type) {
-  switch (rt_threat_type) {
-    case RTLookupResponse::ThreatInfo::WEB_MALWARE:
-      return SB_THREAT_TYPE_URL_MALWARE;
-    case RTLookupResponse::ThreatInfo::SOCIAL_ENGINEERING:
-      return SB_THREAT_TYPE_URL_PHISHING;
-    case RTLookupResponse::ThreatInfo::UNWANTED_SOFTWARE:
-      return SB_THREAT_TYPE_URL_UNWANTED;
-    case RTLookupResponse::ThreatInfo::UNCLEAR_BILLING:
-      return SB_THREAT_TYPE_BILLING;
-    case RTLookupResponse::ThreatInfo::THREAT_TYPE_UNSPECIFIED:
-      NOTREACHED() << "Unexpected RTLookupResponse::ThreatType encountered";
-      return SB_THREAT_TYPE_SAFE;
-  }
-}
-
-base::WeakPtr<RealTimeUrlLookupService> RealTimeUrlLookupService::GetWeakPtr() {
-  return weak_factory_.GetWeakPtr();
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/realtime/url_lookup_service.h b/components/safe_browsing/realtime/url_lookup_service.h
deleted file mode 100644
index 654230d..0000000
--- a/components/safe_browsing/realtime/url_lookup_service.h
+++ /dev/null
@@ -1,129 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_REALTIME_URL_LOOKUP_SERVICE_H_
-#define COMPONENTS_SAFE_BROWSING_REALTIME_URL_LOOKUP_SERVICE_H_
-
-#include <memory>
-#include <string>
-
-#include "base/callback.h"
-#include "base/containers/flat_map.h"
-#include "base/memory/weak_ptr.h"
-#include "base/time/time.h"
-#include "base/timer/timer.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
-#include "components/safe_browsing/proto/realtimeapi.pb.h"
-#include "url/gurl.h"
-
-namespace network {
-class SimpleURLLoader;
-class SharedURLLoaderFactory;
-}  // namespace network
-
-namespace safe_browsing {
-
-using RTLookupRequestCallback =
-    base::OnceCallback<void(std::unique_ptr<RTLookupRequest>)>;
-
-using RTLookupResponseCallback =
-    base::OnceCallback<void(std::unique_ptr<RTLookupResponse>)>;
-
-// This class implements the logic to decide whether the real time lookup
-// feature is enabled for a given user/profile.
-class RealTimeUrlLookupService {
- public:
-  explicit RealTimeUrlLookupService(
-      scoped_refptr<network::SharedURLLoaderFactory>);
-  ~RealTimeUrlLookupService();
-
-  // Returns true if |url|'s scheme can be checked.
-  bool CanCheckUrl(const GURL& url) const;
-
-  // Returns true if the real time lookups are currently in backoff mode due to
-  // too many prior errors. If this happens, the checking falls back to
-  // local hash-based method.
-  bool IsInBackoffMode() const;
-
-  // Start the full URL lookup for |url|, call |request_callback| on the same
-  // thread when request is sent, call |response_callback| on the same thread
-  // when response is received.
-  void StartLookup(const GURL& url,
-                   RTLookupRequestCallback request_callback,
-                   RTLookupResponseCallback response_callback);
-
-  // Returns the SBThreatType for a given
-  // RTLookupResponse::ThreatInfo::ThreatType
-  static SBThreatType GetSBThreatTypeForRTThreatType(
-      RTLookupResponse::ThreatInfo::ThreatType rt_threat_type);
-
- private:
-  using PendingRTLookupRequests =
-      base::flat_map<network::SimpleURLLoader*, RTLookupResponseCallback>;
-
-  // Returns the duration of the next backoff. Starts at
-  // |kMinBackOffResetDurationInSeconds| and increases exponentially until it
-  // reaches |kMaxBackOffResetDurationInSeconds|.
-  size_t GetBackoffDurationInSeconds() const;
-
-  // Called when the request to remote endpoint fails. May initiate or extend
-  // backoff.
-  void HandleLookupError();
-
-  // Called when the request to remote endpoint succeeds. Resets error count and
-  // ends backoff.
-  void HandleLookupSuccess();
-
-  // Resets the error count and ends backoff mode. Functionally same as
-  // |HandleLookupSuccess| for now.
-  void ResetFailures();
-
-  // Called when the response from the real-time lookup remote endpoint is
-  // received. |url_loader| is the unowned loader that was used to send the
-  // request. |request_start_time| is the time when the request was sent.
-  // |response_body| is the response received.
-  void OnURLLoaderComplete(network::SimpleURLLoader* url_loader,
-                           base::TimeTicks request_start_time,
-                           std::unique_ptr<std::string> response_body);
-
-  std::unique_ptr<RTLookupRequest> FillRequestProto(const GURL& url);
-
-  // Helper function to return a weak pointer.
-  base::WeakPtr<RealTimeUrlLookupService> GetWeakPtr();
-
-  PendingRTLookupRequests pending_requests_;
-
-  // Count of consecutive failures to complete URL lookup requests. When it
-  // reaches |kMaxFailuresToEnforceBackoff|, we enter the backoff mode. It gets
-  // reset when we complete a lookup successfully or when the backoff reset
-  // timer fires.
-  size_t consecutive_failures_ = 0;
-
-  // If true, represents that one or more real time lookups did complete
-  // successfully since the last backoff or Chrome never entered the breakoff;
-  // if false and Chrome re-enters backoff period, the backoff duration is
-  // increased exponentially (capped at |kMaxBackOffResetDurationInSeconds|).
-  bool did_successful_lookup_since_last_backoff_ = true;
-
-  // The current duration of backoff. Increases exponentially until it reaches
-  // |kMaxBackOffResetDurationInSeconds|.
-  size_t next_backoff_duration_secs_ = 0;
-
-  // If this timer is running, backoff is in effect.
-  base::OneShotTimer backoff_timer_;
-
-  // The URLLoaderFactory we use to issue network requests.
-  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
-
-  friend class RealTimeUrlLookupServiceTest;
-
-  base::WeakPtrFactory<RealTimeUrlLookupService> weak_factory_{this};
-
-  DISALLOW_COPY_AND_ASSIGN(RealTimeUrlLookupService);
-
-};  // class RealTimeUrlLookupService
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_REALTIME_URL_LOOKUP_SERVICE_H_
diff --git a/components/safe_browsing/realtime/url_lookup_service_unittest.cc b/components/safe_browsing/realtime/url_lookup_service_unittest.cc
deleted file mode 100644
index 6c5d1af..0000000
--- a/components/safe_browsing/realtime/url_lookup_service_unittest.cc
+++ /dev/null
@@ -1,378 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/realtime/url_lookup_service.h"
-
-#include "base/test/task_environment.h"
-#include "content/public/test/browser_task_environment.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
-#include "services/network/test/test_url_loader_factory.h"
-#include "testing/platform_test.h"
-
-namespace safe_browsing {
-
-class RealTimeUrlLookupServiceTest : public PlatformTest {
- public:
-  RealTimeUrlLookupServiceTest()
-      : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
-  void SetUp() override {
-    PlatformTest::SetUp();
-
-    test_shared_loader_factory_ =
-        base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
-            &test_url_loader_factory_);
-
-    rt_service_ =
-        std::make_unique<RealTimeUrlLookupService>(test_shared_loader_factory_);
-  }
-
-  bool CanCheckUrl(const GURL& url) { return rt_service_->CanCheckUrl(url); }
-  void HandleLookupError() { rt_service_->HandleLookupError(); }
-  void HandleLookupSuccess() { rt_service_->HandleLookupSuccess(); }
-  bool IsInBackoffMode() { return rt_service_->IsInBackoffMode(); }
-  std::unique_ptr<RTLookupRequest> FillRequestProto(const GURL& url) {
-    return rt_service_->FillRequestProto(url);
-  }
-
-  network::TestURLLoaderFactory test_url_loader_factory_;
-  scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
-  std::unique_ptr<RealTimeUrlLookupService> rt_service_;
-  content::BrowserTaskEnvironment task_environment_;
-};
-
-TEST_F(RealTimeUrlLookupServiceTest, TestFillRequestProto) {
-  struct SanitizeUrlCase {
-    const char* url;
-    const char* expected_url;
-  } sanitize_url_cases[] = {
-      {"http://example.com/", "http://example.com/"},
-      {"http://user:pass@example.com/", "http://example.com/"},
-      {"http://%123:bar@example.com/", "http://example.com/"},
-      {"http://example.com#123", "http://example.com/"}};
-  for (size_t i = 0; i < base::size(sanitize_url_cases); i++) {
-    GURL url(sanitize_url_cases[i].url);
-    auto result = FillRequestProto(url);
-    EXPECT_EQ(sanitize_url_cases[i].expected_url, result->url());
-    EXPECT_EQ(RTLookupRequest::NAVIGATION, result->lookup_type());
-  }
-}
-
-TEST_F(RealTimeUrlLookupServiceTest, TestBackoffAndTimerReset) {
-  // Not in backoff at the beginning.
-  ASSERT_FALSE(IsInBackoffMode());
-
-  // Failure 1: No backoff.
-  HandleLookupError();
-  EXPECT_FALSE(IsInBackoffMode());
-
-  // Failure 2: No backoff.
-  HandleLookupError();
-  EXPECT_FALSE(IsInBackoffMode());
-
-  // Failure 3: Entered backoff.
-  HandleLookupError();
-  EXPECT_TRUE(IsInBackoffMode());
-
-  // Backoff not reset after 1 second.
-  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
-  EXPECT_TRUE(IsInBackoffMode());
-
-  // Backoff not reset after 299 seconds.
-  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(298));
-  EXPECT_TRUE(IsInBackoffMode());
-
-  // Backoff should have been reset after 300 seconds.
-  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
-  EXPECT_FALSE(IsInBackoffMode());
-}
-
-TEST_F(RealTimeUrlLookupServiceTest, TestBackoffAndLookupSuccessReset) {
-  // Not in backoff at the beginning.
-  ASSERT_FALSE(IsInBackoffMode());
-
-  // Failure 1: No backoff.
-  HandleLookupError();
-  EXPECT_FALSE(IsInBackoffMode());
-
-  // Lookup success resets the backoff counter.
-  HandleLookupSuccess();
-  EXPECT_FALSE(IsInBackoffMode());
-
-  // Failure 1: No backoff.
-  HandleLookupError();
-  EXPECT_FALSE(IsInBackoffMode());
-
-  // Failure 2: No backoff.
-  HandleLookupError();
-  EXPECT_FALSE(IsInBackoffMode());
-
-  // Lookup success resets the backoff counter.
-  HandleLookupSuccess();
-  EXPECT_FALSE(IsInBackoffMode());
-
-  // Failure 1: No backoff.
-  HandleLookupError();
-  EXPECT_FALSE(IsInBackoffMode());
-
-  // Failure 2: No backoff.
-  HandleLookupError();
-  EXPECT_FALSE(IsInBackoffMode());
-
-  // Failure 3: Entered backoff.
-  HandleLookupError();
-  EXPECT_TRUE(IsInBackoffMode());
-
-  // Lookup success resets the backoff counter.
-  HandleLookupSuccess();
-  EXPECT_FALSE(IsInBackoffMode());
-}
-
-TEST_F(RealTimeUrlLookupServiceTest, TestExponentialBackoff) {
-  ///////////////////////////////
-  // Initial backoff: 300 seconds
-  ///////////////////////////////
-
-  // Not in backoff at the beginning.
-  ASSERT_FALSE(IsInBackoffMode());
-
-  // Failure 1: No backoff.
-  HandleLookupError();
-  EXPECT_FALSE(IsInBackoffMode());
-
-  // Failure 2: No backoff.
-  HandleLookupError();
-  EXPECT_FALSE(IsInBackoffMode());
-
-  // Failure 3: Entered backoff.
-  HandleLookupError();
-  EXPECT_TRUE(IsInBackoffMode());
-
-  // Backoff not reset after 1 second.
-  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
-  EXPECT_TRUE(IsInBackoffMode());
-
-  // Backoff not reset after 299 seconds.
-  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(298));
-  EXPECT_TRUE(IsInBackoffMode());
-
-  // Backoff should have been reset after 300 seconds.
-  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
-  EXPECT_FALSE(IsInBackoffMode());
-
-  /////////////////////////////////////
-  // Exponential backoff 1: 600 seconds
-  /////////////////////////////////////
-
-  HandleLookupError();
-  EXPECT_FALSE(IsInBackoffMode());
-  HandleLookupError();
-  EXPECT_FALSE(IsInBackoffMode());
-  HandleLookupError();
-  EXPECT_TRUE(IsInBackoffMode());
-
-  // Backoff not reset after 1 second.
-  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
-  EXPECT_TRUE(IsInBackoffMode());
-
-  // Backoff not reset after 599 seconds.
-  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(598));
-  EXPECT_TRUE(IsInBackoffMode());
-
-  // Backoff should have been reset after 600 seconds.
-  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
-  EXPECT_FALSE(IsInBackoffMode());
-
-  //////////////////////////////////////
-  // Exponential backoff 2: 1200 seconds
-  //////////////////////////////////////
-
-  HandleLookupError();
-  EXPECT_FALSE(IsInBackoffMode());
-  HandleLookupError();
-  EXPECT_FALSE(IsInBackoffMode());
-  HandleLookupError();
-  EXPECT_TRUE(IsInBackoffMode());
-
-  // Backoff not reset after 1 second.
-  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
-  EXPECT_TRUE(IsInBackoffMode());
-
-  // Backoff not reset after 1199 seconds.
-  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1198));
-  EXPECT_TRUE(IsInBackoffMode());
-
-  // Backoff should have been reset after 1200 seconds.
-  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
-  EXPECT_FALSE(IsInBackoffMode());
-
-  ///////////////////////////////////////////////////
-  // Exponential backoff 3: 1800 seconds (30 minutes)
-  ///////////////////////////////////////////////////
-
-  HandleLookupError();
-  EXPECT_FALSE(IsInBackoffMode());
-  HandleLookupError();
-  EXPECT_FALSE(IsInBackoffMode());
-  HandleLookupError();
-  EXPECT_TRUE(IsInBackoffMode());
-
-  // Backoff not reset after 1 second.
-  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
-  EXPECT_TRUE(IsInBackoffMode());
-
-  // Backoff not reset after 1799 seconds.
-  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1798));
-  EXPECT_TRUE(IsInBackoffMode());
-
-  // Backoff should have been reset after 1800 seconds.
-  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
-  EXPECT_FALSE(IsInBackoffMode());
-
-  ///////////////////////////////////////////////////
-  // Exponential backoff 4: 1800 seconds (30 minutes)
-  ///////////////////////////////////////////////////
-
-  HandleLookupError();
-  EXPECT_FALSE(IsInBackoffMode());
-  HandleLookupError();
-  EXPECT_FALSE(IsInBackoffMode());
-  HandleLookupError();
-  EXPECT_TRUE(IsInBackoffMode());
-
-  // Backoff not reset after 1 second.
-  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
-  EXPECT_TRUE(IsInBackoffMode());
-
-  // Backoff not reset after 1799 seconds.
-  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1798));
-  EXPECT_TRUE(IsInBackoffMode());
-
-  // Backoff should have been reset after 1800 seconds.
-  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
-  EXPECT_FALSE(IsInBackoffMode());
-}
-
-TEST_F(RealTimeUrlLookupServiceTest, TestExponentialBackoffWithResetOnSuccess) {
-  ///////////////////////////////
-  // Initial backoff: 300 seconds
-  ///////////////////////////////
-
-  // Not in backoff at the beginning.
-  ASSERT_FALSE(IsInBackoffMode());
-
-  // Failure 1: No backoff.
-  HandleLookupError();
-  EXPECT_FALSE(IsInBackoffMode());
-
-  // Failure 2: No backoff.
-  HandleLookupError();
-  EXPECT_FALSE(IsInBackoffMode());
-
-  // Failure 3: Entered backoff.
-  HandleLookupError();
-  EXPECT_TRUE(IsInBackoffMode());
-
-  // Backoff not reset after 1 second.
-  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
-  EXPECT_TRUE(IsInBackoffMode());
-
-  // Backoff not reset after 299 seconds.
-  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(298));
-  EXPECT_TRUE(IsInBackoffMode());
-
-  // Backoff should have been reset after 300 seconds.
-  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
-  EXPECT_FALSE(IsInBackoffMode());
-
-  /////////////////////////////////////
-  // Exponential backoff 1: 600 seconds
-  /////////////////////////////////////
-
-  HandleLookupError();
-  EXPECT_FALSE(IsInBackoffMode());
-  HandleLookupError();
-  EXPECT_FALSE(IsInBackoffMode());
-  HandleLookupError();
-  EXPECT_TRUE(IsInBackoffMode());
-
-  // Backoff not reset after 1 second.
-  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
-  EXPECT_TRUE(IsInBackoffMode());
-
-  // Backoff not reset after 599 seconds.
-  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(598));
-  EXPECT_TRUE(IsInBackoffMode());
-
-  // Backoff should have been reset after 600 seconds.
-  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
-  EXPECT_FALSE(IsInBackoffMode());
-
-  // The next lookup is a success. This should reset the backoff duration to
-  // |kMinBackOffResetDurationInSeconds|
-  HandleLookupSuccess();
-
-  // Failure 1: No backoff.
-  HandleLookupError();
-  EXPECT_FALSE(IsInBackoffMode());
-
-  // Failure 2: No backoff.
-  HandleLookupError();
-  EXPECT_FALSE(IsInBackoffMode());
-
-  // Failure 3: Entered backoff.
-  HandleLookupError();
-  EXPECT_TRUE(IsInBackoffMode());
-
-  // Backoff not reset after 1 second.
-  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
-  EXPECT_TRUE(IsInBackoffMode());
-
-  // Backoff not reset after 299 seconds.
-  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(298));
-  EXPECT_TRUE(IsInBackoffMode());
-
-  // Backoff should have been reset after 300 seconds.
-  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
-  EXPECT_FALSE(IsInBackoffMode());
-}
-
-TEST_F(RealTimeUrlLookupServiceTest, TestGetSBThreatTypeForRTThreatType) {
-  EXPECT_EQ(SB_THREAT_TYPE_URL_MALWARE,
-            RealTimeUrlLookupService::GetSBThreatTypeForRTThreatType(
-                RTLookupResponse::ThreatInfo::WEB_MALWARE));
-  EXPECT_EQ(SB_THREAT_TYPE_URL_PHISHING,
-            RealTimeUrlLookupService::GetSBThreatTypeForRTThreatType(
-                RTLookupResponse::ThreatInfo::SOCIAL_ENGINEERING));
-  EXPECT_EQ(SB_THREAT_TYPE_URL_UNWANTED,
-            RealTimeUrlLookupService::GetSBThreatTypeForRTThreatType(
-                RTLookupResponse::ThreatInfo::UNWANTED_SOFTWARE));
-  EXPECT_EQ(SB_THREAT_TYPE_BILLING,
-            RealTimeUrlLookupService::GetSBThreatTypeForRTThreatType(
-                RTLookupResponse::ThreatInfo::UNCLEAR_BILLING));
-}
-
-TEST_F(RealTimeUrlLookupServiceTest, TestCanCheckUrl) {
-  struct CanCheckUrlCases {
-    const char* url;
-    bool can_check;
-  } can_check_url_cases[] = {{"ftp://example.test/path", false},
-                             {"http://localhost/path", false},
-                             {"http://localhost.localdomain/path", false},
-                             {"http://127.0.0.1/path", false},
-                             {"http://127.0.0.1:2222/path", false},
-                             {"http://192.168.1.1/path", false},
-                             {"http://172.16.2.2/path", false},
-                             {"http://10.1.1.1/path", false},
-                             {"http://10.1.1.1.1/path", true},
-                             {"http://example.test/path", true},
-                             {"https://example.test/path", true}};
-  for (size_t i = 0; i < base::size(can_check_url_cases); i++) {
-    GURL url(can_check_url_cases[i].url);
-    bool expected_can_check = can_check_url_cases[i].can_check;
-    EXPECT_EQ(expected_can_check, CanCheckUrl(url));
-  }
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/renderer/BUILD.gn b/components/safe_browsing/renderer/BUILD.gn
deleted file mode 100644
index 7c600dd0..0000000
--- a/components/safe_browsing/renderer/BUILD.gn
+++ /dev/null
@@ -1,66 +0,0 @@
-# Copyright 2017 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//build/config/features.gni")
-import("//components/safe_browsing/buildflags.gni")
-
-source_set("renderer") {
-  if (safe_browsing_mode != 0) {
-    sources = [
-      "threat_dom_details.cc",
-      "threat_dom_details.h",
-    ]
-    deps = [
-      "//base",
-      "//components/safe_browsing:features",
-      "//components/safe_browsing/common:common",
-      "//content/public/renderer",
-      "//ipc",
-      "//third_party/blink/public:blink",
-      "//url/ipc:url_ipc",
-    ]
-  }
-}
-
-source_set("throttles") {
-  sources = [
-    "renderer_url_loader_throttle.cc",
-    "renderer_url_loader_throttle.h",
-    "websocket_sb_handshake_throttle.cc",
-    "websocket_sb_handshake_throttle.h",
-  ]
-
-  deps = [
-    "//base:base",
-    "//components/safe_browsing:features",
-    "//components/safe_browsing/common:common",
-    "//components/safe_browsing/common:interfaces",
-    "//content/public/common:common",
-    "//content/public/renderer:renderer",
-    "//ipc",
-    "//net",
-    "//services/service_manager/public/cpp:cpp",
-    "//third_party/blink/public:blink",
-    "//url:url",
-  ]
-}
-
-source_set("websocket_sb_handshake_throttle_unittest") {
-  testonly = true
-  sources = [
-    "websocket_sb_handshake_throttle_unittest.cc",
-  ]
-
-  deps = [
-    ":throttles",
-    "//base:base",
-    "//base/test:test_support",
-    "//components/safe_browsing/common:interfaces",
-    "//content/public/common",
-    "//ipc",
-    "//testing/gmock",
-    "//testing/gtest",
-    "//third_party/blink/public:blink",
-  ]
-}
diff --git a/components/safe_browsing/renderer/renderer_url_loader_throttle.cc b/components/safe_browsing/renderer/renderer_url_loader_throttle.cc
deleted file mode 100644
index f398e8c..0000000
--- a/components/safe_browsing/renderer/renderer_url_loader_throttle.cc
+++ /dev/null
@@ -1,211 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/renderer/renderer_url_loader_throttle.h"
-
-#include "base/bind.h"
-#include "base/logging.h"
-#include "base/trace_event/trace_event.h"
-#include "components/safe_browsing/common/safebrowsing_constants.h"
-#include "components/safe_browsing/common/utils.h"
-#include "net/url_request/redirect_info.h"
-#include "services/network/public/cpp/resource_request.h"
-
-namespace safe_browsing {
-
-RendererURLLoaderThrottle::RendererURLLoaderThrottle(
-    mojom::SafeBrowsing* safe_browsing,
-    int render_frame_id)
-    : safe_browsing_(safe_browsing), render_frame_id_(render_frame_id) {}
-
-RendererURLLoaderThrottle::~RendererURLLoaderThrottle() {
-  if (deferred_)
-    TRACE_EVENT_ASYNC_END0("safe_browsing", "Deferred", this);
-}
-
-void RendererURLLoaderThrottle::DetachFromCurrentSequence() {
-  // Create a new pipe to the SafeBrowsing interface that can be bound to a
-  // different sequence.
-  safe_browsing_->Clone(
-      safe_browsing_pending_remote_.InitWithNewPipeAndPassReceiver());
-  safe_browsing_ = nullptr;
-}
-
-void RendererURLLoaderThrottle::WillStartRequest(
-    network::ResourceRequest* request,
-    bool* defer) {
-  DCHECK_EQ(0u, pending_checks_);
-  DCHECK(!blocked_);
-  DCHECK(!url_checker_);
-
-  if (safe_browsing_pending_remote_.is_valid()) {
-    // Bind the pipe created in DetachFromCurrentSequence to the current
-    // sequence.
-    safe_browsing_remote_.Bind(std::move(safe_browsing_pending_remote_));
-    safe_browsing_ = safe_browsing_remote_.get();
-  }
-
-  original_url_ = request->url;
-  pending_checks_++;
-  // Use a weak pointer to self because |safe_browsing_| may not be owned by
-  // this object.
-  net::HttpRequestHeaders headers;
-  headers.CopyFrom(request->headers);
-  safe_browsing_->CreateCheckerAndCheck(
-      render_frame_id_, url_checker_.BindNewPipeAndPassReceiver(), request->url,
-      request->method, headers, request->load_flags,
-      static_cast<content::ResourceType>(request->resource_type),
-      request->has_user_gesture, request->originated_from_service_worker,
-      base::BindOnce(&RendererURLLoaderThrottle::OnCheckUrlResult,
-                     weak_factory_.GetWeakPtr()));
-  safe_browsing_ = nullptr;
-
-  url_checker_.set_disconnect_handler(base::BindOnce(
-      &RendererURLLoaderThrottle::OnMojoDisconnect, base::Unretained(this)));
-}
-
-void RendererURLLoaderThrottle::WillRedirectRequest(
-    net::RedirectInfo* redirect_info,
-    const network::mojom::URLResponseHead& /* response_head */,
-    bool* /* defer */,
-    std::vector<std::string>* /* to_be_removed_headers */,
-    net::HttpRequestHeaders* /* modified_headers */) {
-  // If |blocked_| is true, the resource load has been canceled and there
-  // shouldn't be such a notification.
-  DCHECK(!blocked_);
-
-  if (!url_checker_) {
-    DCHECK_EQ(0u, pending_checks_);
-    return;
-  }
-
-  pending_checks_++;
-  url_checker_->CheckUrl(
-      redirect_info->new_url, redirect_info->new_method,
-      base::BindOnce(&RendererURLLoaderThrottle::OnCheckUrlResult,
-                     base::Unretained(this)));
-}
-
-void RendererURLLoaderThrottle::WillProcessResponse(
-    const GURL& response_url,
-    network::mojom::URLResponseHead* response_head,
-    bool* defer) {
-  // If |blocked_| is true, the resource load has been canceled and there
-  // shouldn't be such a notification.
-  DCHECK(!blocked_);
-
-  if (pending_checks_ == 0)
-    return;
-
-  DCHECK(!deferred_);
-  deferred_ = true;
-  defer_start_time_ = base::TimeTicks::Now();
-  *defer = true;
-  TRACE_EVENT_ASYNC_BEGIN1("safe_browsing", "Deferred", this, "original_url",
-                           original_url_.spec());
-}
-
-void RendererURLLoaderThrottle::OnCompleteCheck(bool proceed,
-                                                bool showed_interstitial) {
-  OnCompleteCheckInternal(true /* slow_check */, proceed, showed_interstitial);
-}
-
-void RendererURLLoaderThrottle::OnCheckUrlResult(
-    mojo::PendingReceiver<mojom::UrlCheckNotifier> slow_check_notifier,
-    bool proceed,
-    bool showed_interstitial) {
-  // When this is the callback of safe_browsing_->CreateCheckerAndCheck(), it is
-  // possible that we get here after a check with |url_checker_| has completed
-  // and blocked the request.
-  if (blocked_ || !url_checker_)
-    return;
-
-  if (!slow_check_notifier.is_valid()) {
-    OnCompleteCheckInternal(false /* slow_check */, proceed,
-                            showed_interstitial);
-    return;
-  }
-
-  pending_slow_checks_++;
-  // Pending slow checks indicate that the resource may be unsafe. In that case,
-  // pause reading response body from network to minimize the chance of
-  // processing unsafe contents (e.g., writing unsafe contents into cache),
-  // until we get the results. According to the results, we may resume reading
-  // or cancel the resource load.
-  if (pending_slow_checks_ == 1)
-    delegate_->PauseReadingBodyFromNet();
-
-  if (!notifier_receivers_) {
-    notifier_receivers_ =
-        std::make_unique<mojo::ReceiverSet<mojom::UrlCheckNotifier>>();
-  }
-  notifier_receivers_->Add(this, std::move(slow_check_notifier));
-}
-
-void RendererURLLoaderThrottle::OnCompleteCheckInternal(
-    bool slow_check,
-    bool proceed,
-    bool showed_interstitial) {
-  DCHECK(!blocked_);
-  DCHECK(url_checker_);
-
-  DCHECK_LT(0u, pending_checks_);
-  pending_checks_--;
-
-  if (slow_check) {
-    DCHECK_LT(0u, pending_slow_checks_);
-    pending_slow_checks_--;
-  }
-
-  user_action_involved_ = user_action_involved_ || showed_interstitial;
-  // If the resource load is currently deferred and is going to exit that state
-  // (either being cancelled or resumed), record the total delay.
-  if (deferred_ && (!proceed || pending_checks_ == 0))
-    total_delay_ = base::TimeTicks::Now() - defer_start_time_;
-
-  if (proceed) {
-    if (pending_slow_checks_ == 0 && slow_check)
-      delegate_->ResumeReadingBodyFromNet();
-
-    if (pending_checks_ == 0 && deferred_) {
-      deferred_ = false;
-      TRACE_EVENT_ASYNC_END0("safe_browsing", "Deferred", this);
-      delegate_->Resume();
-    }
-  } else {
-    blocked_ = true;
-
-    url_checker_.reset();
-    notifier_receivers_.reset();
-    pending_checks_ = 0;
-    pending_slow_checks_ = 0;
-    delegate_->CancelWithError(GetNetErrorCodeForSafeBrowsing(),
-                               kCustomCancelReasonForURLLoader);
-  }
-}
-
-void RendererURLLoaderThrottle::OnMojoDisconnect() {
-  DCHECK(!blocked_);
-
-  // If a service-side disconnect happens, treat all URLs as if they are safe.
-  url_checker_.reset();
-  notifier_receivers_.reset();
-
-  pending_checks_ = 0;
-
-  if (pending_slow_checks_ > 0) {
-    pending_slow_checks_ = 0;
-    delegate_->ResumeReadingBodyFromNet();
-  }
-
-  if (deferred_) {
-    total_delay_ = base::TimeTicks::Now() - defer_start_time_;
-
-    deferred_ = false;
-    TRACE_EVENT_ASYNC_END0("safe_browsing", "Deferred", this);
-    delegate_->Resume();
-  }
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/renderer/renderer_url_loader_throttle.h b/components/safe_browsing/renderer/renderer_url_loader_throttle.h
deleted file mode 100644
index 421976a..0000000
--- a/components/safe_browsing/renderer/renderer_url_loader_throttle.h
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_RENDERER_RENDERER_URL_LOADER_THROTTLE_H_
-#define COMPONENTS_SAFE_BROWSING_RENDERER_RENDERER_URL_LOADER_THROTTLE_H_
-
-#include <memory>
-
-#include "base/memory/weak_ptr.h"
-#include "components/safe_browsing/common/safe_browsing.mojom.h"
-#include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "mojo/public/cpp/bindings/pending_remote.h"
-#include "mojo/public/cpp/bindings/receiver_set.h"
-#include "mojo/public/cpp/bindings/remote.h"
-#include "third_party/blink/public/common/loader/url_loader_throttle.h"
-#include "url/gurl.h"
-
-namespace safe_browsing {
-
-// RendererURLLoaderThrottle is used in renderer processes to query
-// SafeBrowsing and determine whether a URL and its redirect URLs are safe to
-// load. It defers response processing until all URL checks are completed;
-// cancels the load if any URLs turn out to be bad.
-class RendererURLLoaderThrottle : public blink::URLLoaderThrottle,
-                                  public mojom::UrlCheckNotifier {
- public:
-  // |safe_browsing| must stay alive until WillStartRequest() (if it is called)
-  // or the end of this object.
-  // |render_frame_id| is used for displaying SafeBrowsing UI when necessary.
-  RendererURLLoaderThrottle(mojom::SafeBrowsing* safe_browsing,
-                            int render_frame_id);
-  ~RendererURLLoaderThrottle() override;
-
- private:
-  // blink::URLLoaderThrottle implementation.
-  void DetachFromCurrentSequence() override;
-  void WillStartRequest(network::ResourceRequest* request,
-                        bool* defer) override;
-  void WillRedirectRequest(net::RedirectInfo* redirect_info,
-                           const network::mojom::URLResponseHead& response_head,
-                           bool* defer,
-                           std::vector<std::string>* to_be_removed_headers,
-                           net::HttpRequestHeaders* modified_headers) override;
-  void WillProcessResponse(const GURL& response_url,
-                           network::mojom::URLResponseHead* response_head,
-                           bool* defer) override;
-
-  // mojom::UrlCheckNotifier implementation.
-  void OnCompleteCheck(bool proceed, bool showed_interstitial) override;
-
-  void OnCheckUrlResult(
-      mojo::PendingReceiver<mojom::UrlCheckNotifier> slow_check_notifier,
-      bool proceed,
-      bool showed_interstitial);
-
-  // Called by the two methods above.
-  // |slow_check| indicates whether it reports the result of a slow check.
-  // (Please see comments in safe_browsing.mojom for what slow check means).
-  void OnCompleteCheckInternal(bool slow_check,
-                               bool proceed,
-                               bool showed_interstitial);
-
-  void OnMojoDisconnect();
-
-  mojom::SafeBrowsing* safe_browsing_;
-  const int render_frame_id_;
-
-  // These fields hold the connection to this instance's private connection to
-  // the Safe Browsing service if DetachFromCurrentThread has been called.
-  mojo::PendingRemote<mojom::SafeBrowsing> safe_browsing_pending_remote_;
-  mojo::Remote<mojom::SafeBrowsing> safe_browsing_remote_;
-
-  mojo::Remote<mojom::SafeBrowsingUrlChecker> url_checker_;
-
-  size_t pending_checks_ = 0;
-  size_t pending_slow_checks_ = 0;
-  bool blocked_ = false;
-
-  // The time when we started deferring the request.
-  base::TimeTicks defer_start_time_;
-  bool deferred_ = false;
-
-  // The total delay caused by SafeBrowsing deferring the resource load.
-  base::TimeDelta total_delay_;
-  // Whether the interstitial page has been shown and therefore user action has
-  // been involved.
-  bool user_action_involved_ = false;
-
-  std::unique_ptr<mojo::ReceiverSet<mojom::UrlCheckNotifier>>
-      notifier_receivers_;
-
-  GURL original_url_;
-
-  base::WeakPtrFactory<RendererURLLoaderThrottle> weak_factory_{this};
-};
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_RENDERER_RENDERER_URL_LOADER_THROTTLE_H_
diff --git a/components/safe_browsing/renderer/threat_dom_details.cc b/components/safe_browsing/renderer/threat_dom_details.cc
deleted file mode 100644
index 334eed79..0000000
--- a/components/safe_browsing/renderer/threat_dom_details.cc
+++ /dev/null
@@ -1,347 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/renderer/threat_dom_details.h"
-
-#include <algorithm>
-#include <map>
-#include <string>
-#include <unordered_set>
-
-#include "base/bind.h"
-#include "base/compiler_specific.h"
-#include "base/metrics/field_trial_params.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/strings/string_piece.h"
-#include "base/strings/string_split.h"
-#include "base/strings/stringprintf.h"
-#include "components/safe_browsing/features.h"
-#include "content/public/renderer/render_frame.h"
-#include "third_party/blink/public/platform/web_string.h"
-#include "third_party/blink/public/web/web_document.h"
-#include "third_party/blink/public/web/web_element.h"
-#include "third_party/blink/public/web/web_element_collection.h"
-#include "third_party/blink/public/web/web_frame.h"
-#include "third_party/blink/public/web/web_local_frame.h"
-
-namespace safe_browsing {
-
-// A map for keeping track of the identity of DOM Elements, used to generate
-// unique IDs for each element and lookup elements IDs by parent Element, to
-// maintain proper parent/child relationships.
-// They key is a WebNode from the DOM, which is basically a pointer so can be
-// copied into the map when inserting new elements.
-// The values are indices into the resource vector, and are used to retrieve IPC
-// messages generated by ThreatDOMDetails.
-using ElementToNodeMap = std::map<blink::WebNode, size_t>;
-
-// The name of the param containing the tags and attributes list.
-const char kTagAndAttributeParamName[] = "tag_attribute_csv";
-
-namespace {
-
-// Predicate used to search |tag_and_attributes_list_| by tag_name.
-class TagNameIs {
- public:
-  explicit TagNameIs(const std::string& tag) : tag_(tag) {}
-  bool operator()(const TagAndAttributesItem& tag_and_attribute) {
-    return tag_ == tag_and_attribute.tag_name;
-  }
-
- private:
-  std::string tag_;
-};
-
-void GetDefaultTagAndAttributeList(
-    std::vector<TagAndAttributesItem>* tag_and_attributes_list) {
-  tag_and_attributes_list->clear();
-  // These entries must be sorted by tag name.
-  bool should_capture_js =
-      base::FeatureList::IsEnabled(kCaptureInlineJavascriptForGoogleAds);
-  if (should_capture_js)
-    tag_and_attributes_list->push_back(TagAndAttributesItem("a", {"onclick"}));
-  // These entries must be sorted by tag name.
-  // These tags are related to identifying Google ads.
-  tag_and_attributes_list->push_back(
-      TagAndAttributesItem("div", {"data-google-query-id", "id"}));
-  tag_and_attributes_list->push_back(TagAndAttributesItem("iframe", {"id"}));
-  if (should_capture_js)
-    tag_and_attributes_list->push_back(
-        TagAndAttributesItem("img", {"onclick"}));
-}
-
-void ParseTagAndAttributeParams(
-    std::vector<TagAndAttributesItem>* tag_and_attributes_list) {
-  DCHECK(tag_and_attributes_list);
-  // If the feature is disabled we just use the default list. Otherwise the list
-  // from the Finch param will be the one used.
-  if (!base::FeatureList::IsEnabled(kThreatDomDetailsTagAndAttributeFeature)) {
-    GetDefaultTagAndAttributeList(tag_and_attributes_list);
-    return;
-  }
-  tag_and_attributes_list->clear();
-  const std::string& tag_attribute_csv_param =
-      base::GetFieldTrialParamValueByFeature(
-          kThreatDomDetailsTagAndAttributeFeature, kTagAndAttributeParamName);
-  if (tag_attribute_csv_param.empty()) {
-    return;
-  }
-
-  std::vector<std::string> split =
-      base::SplitString(tag_attribute_csv_param, ",", base::TRIM_WHITESPACE,
-                        base::SPLIT_WANT_NONEMPTY);
-  // If we don't have the right number of pairs in the csv then don't bother
-  // parsing further.
-  if (split.size() % 2 != 0) {
-    return;
-  }
-  for (size_t i = 0; i < split.size(); i += 2) {
-    const std::string& tag_name = split[i];
-    const std::string& attribute = split[i + 1];
-    auto item_iter =
-        std::find_if(tag_and_attributes_list->begin(),
-                     tag_and_attributes_list->end(), TagNameIs(tag_name));
-    if (item_iter == tag_and_attributes_list->end()) {
-      TagAndAttributesItem item;
-      item.tag_name = tag_name;
-      item.attributes.push_back(attribute);
-      tag_and_attributes_list->push_back(item);
-    } else {
-      item_iter->attributes.push_back(attribute);
-    }
-  }
-
-  std::sort(tag_and_attributes_list->begin(), tag_and_attributes_list->end(),
-            [](const TagAndAttributesItem& a, const TagAndAttributesItem& b) {
-              return a.tag_name < b.tag_name;
-            });
-}
-
-mojom::ThreatDOMDetailsNode* GetNodeForElement(
-    const blink::WebNode& element,
-    const safe_browsing::ElementToNodeMap& element_to_node_map,
-    std::vector<mojom::ThreatDOMDetailsNodePtr>* resources) {
-  DCHECK_GT(element_to_node_map.count(element), 0u);
-  size_t resource_index = element_to_node_map.at(element);
-  return (*resources)[resource_index].get();
-}
-
-std::string TruncateAttributeString(const std::string& input) {
-  if (input.length() <= ThreatDOMDetails::kMaxAttributeStringLength) {
-    return input;
-  }
-
-  std::string truncated;
-  base::TruncateUTF8ToByteSize(
-      input, ThreatDOMDetails::kMaxAttributeStringLength - 3, &truncated);
-  truncated.append("...");
-  return truncated;
-}
-
-// Handler for the various HTML elements that we extract URLs from.
-void HandleElement(
-    const blink::WebElement& element,
-    const std::vector<TagAndAttributesItem>& tag_and_attributes_list,
-    mojom::ThreatDOMDetailsNode* summary_node,
-    std::vector<mojom::ThreatDOMDetailsNodePtr>* resources,
-    safe_browsing::ElementToNodeMap* element_to_node_map) {
-  // Retrieve the link and resolve the link in case it's relative.
-  blink::WebURL full_url =
-      element.GetDocument().CompleteURL(element.GetAttribute("src"));
-
-  const GURL& child_url = GURL(full_url);
-  if (!child_url.is_empty() && child_url.is_valid()) {
-    summary_node->children.push_back(child_url);
-  }
-
-  mojom::ThreatDOMDetailsNodePtr child_node =
-      mojom::ThreatDOMDetailsNode::New();
-  child_node->url = child_url;
-  child_node->tag_name = element.TagName().Utf8();
-  child_node->parent = summary_node->url;
-  // The body of an iframe may be in a different renderer. Look up the routing
-  // ID of the local or remote frame and store it with the iframe node. If this
-  // element is not a frame then the result of the lookup will be null.
-  blink::WebFrame* subframe = blink::WebFrame::FromFrameOwnerElement(element);
-  if (subframe) {
-    child_node->child_frame_routing_id =
-        content::RenderFrame::GetRoutingIdForWebFrame(subframe);
-  }
-  if (base::FeatureList::IsEnabled(kCaptureInlineJavascriptForGoogleAds) &&
-      child_node->tag_name == "SCRIPT") {
-    child_node->inner_html = element.TextContent().Utf8();
-  }
-  // Populate the element's attributes, but only collect the ones that are
-  // configured in the finch study.
-  const auto& tag_attribute_iter = std::find_if(
-      tag_and_attributes_list.begin(), tag_and_attributes_list.end(),
-      TagNameIs(base::ToLowerASCII(child_node->tag_name)));
-  if (tag_attribute_iter != tag_and_attributes_list.end()) {
-    const std::vector<std::string> attributes_to_collect =
-        tag_attribute_iter->attributes;
-    for (const std::string& attribute : attributes_to_collect) {
-      blink::WebString attr_webstring = blink::WebString::FromASCII(attribute);
-      if (!element.HasAttribute(attr_webstring)) {
-        continue;
-      }
-      mojom::AttributeNameValuePtr attribute_name_value =
-          mojom::AttributeNameValue::New(
-              attribute, TruncateAttributeString(
-                             element.GetAttribute(attr_webstring).Ascii()));
-      child_node->attributes.push_back(std::move(attribute_name_value));
-      if (child_node->attributes.size() == ThreatDOMDetails::kMaxAttributes) {
-        break;
-      }
-    }
-  }
-
-  // Update the ID mapping. First generate the ID for the current node.
-  // Then, if its parent is available, set the current node's parent ID, and
-  // also update the parent's children with the current node's ID.
-  const int child_id = static_cast<int>(element_to_node_map->size()) + 1;
-  child_node->node_id = child_id;
-  blink::WebNode cur_parent_element = element.ParentNode();
-  while (!cur_parent_element.IsNull()) {
-    if (element_to_node_map->count(cur_parent_element) > 0) {
-      mojom::ThreatDOMDetailsNode* parent_node = GetNodeForElement(
-          cur_parent_element, *element_to_node_map, resources);
-      child_node->parent_node_id = parent_node->node_id;
-      parent_node->child_node_ids.push_back(child_id);
-
-      // TODO(lpz): Consider also updating the URL-level parent/child mapping
-      // here. Eg: child_node.parent=parent_node.url, and
-      // parent_node.children.push_back(child_url).
-      break;
-    } else {
-      // It's possible that the direct parent of this node wasn't handled, so it
-      // isn't represented in |element_to_node_map|. Try walking up the
-      // hierarchy to see if a parent further up was handled.
-      cur_parent_element = cur_parent_element.ParentNode();
-    }
-  }
-  // Add the child node to the list of resources.
-  resources->push_back(std::move(child_node));
-  // .. and remember which index it was inserted at so we can look it up later.
-  (*element_to_node_map)[element] = resources->size() - 1;
-}
-
-bool ShouldHandleElement(
-    const blink::WebElement& element,
-    const std::vector<TagAndAttributesItem>& tag_and_attributes_list) {
-  // Resources with a SRC are always handled.
-  if ((element.HasHTMLTagName("iframe") || element.HasHTMLTagName("frame") ||
-       element.HasHTMLTagName("embed") || element.HasHTMLTagName("script")) &&
-      element.HasAttribute("src")) {
-    return true;
-  }
-  if (base::FeatureList::IsEnabled(kCaptureInlineJavascriptForGoogleAds) &&
-      element.HasHTMLTagName("script")) {
-    return true;
-  }
-  std::string tag_name_lower = base::ToLowerASCII(element.TagName().Ascii());
-  const auto& tag_attribute_iter =
-      std::find_if(tag_and_attributes_list.begin(),
-                   tag_and_attributes_list.end(), TagNameIs(tag_name_lower));
-  if (tag_attribute_iter == tag_and_attributes_list.end()) {
-    return false;
-  }
-
-  const std::vector<std::string>& valid_attributes =
-      tag_attribute_iter->attributes;
-  for (const std::string& attribute : valid_attributes) {
-    if (element.HasAttribute(blink::WebString::FromASCII(attribute))) {
-      return true;
-    }
-  }
-  return false;
-}
-
-}  // namespace
-
-TagAndAttributesItem::TagAndAttributesItem() {}
-
-TagAndAttributesItem::TagAndAttributesItem(
-    const std::string& tag_name_param,
-    const std::vector<std::string>& attributes_param)
-    : tag_name(tag_name_param), attributes(attributes_param) {}
-
-TagAndAttributesItem::TagAndAttributesItem(const TagAndAttributesItem& item)
-    : tag_name(item.tag_name), attributes(item.attributes) {}
-
-TagAndAttributesItem::~TagAndAttributesItem() {}
-
-uint32_t ThreatDOMDetails::kMaxNodes = 500;
-uint32_t ThreatDOMDetails::kMaxAttributes = 100;
-uint32_t ThreatDOMDetails::kMaxAttributeStringLength = 100;
-
-// static
-ThreatDOMDetails* ThreatDOMDetails::Create(
-    content::RenderFrame* render_frame,
-    service_manager::BinderRegistry* registry) {
-  // Private constructor and public static Create() method to facilitate
-  // stubbing out this class for binary-size reduction purposes.
-  return new ThreatDOMDetails(render_frame, registry);
-}
-
-void ThreatDOMDetails::OnThreatReporterReceiver(
-    mojo::PendingReceiver<mojom::ThreatReporter> receiver) {
-  threat_reporter_receivers_.Add(this, std::move(receiver));
-}
-
-ThreatDOMDetails::ThreatDOMDetails(content::RenderFrame* render_frame,
-                                   service_manager::BinderRegistry* registry)
-    : content::RenderFrameObserver(render_frame) {
-  ParseTagAndAttributeParams(&tag_and_attributes_list_);
-  // Base::Unretained() is safe here because both the registry and the
-  // ThreatDOMDetails are scoped to the same render frame.
-  registry->AddInterface(base::BindRepeating(
-      &ThreatDOMDetails::OnThreatReporterReceiver, base::Unretained(this)));
-}
-
-ThreatDOMDetails::~ThreatDOMDetails() {}
-
-void ThreatDOMDetails::GetThreatDOMDetails(
-    GetThreatDOMDetailsCallback callback) {
-  std::vector<mojom::ThreatDOMDetailsNodePtr> resources;
-  ExtractResources(&resources);
-  // Notify the browser.
-  std::move(callback).Run(std::move(resources));
-}
-
-void ThreatDOMDetails::ExtractResources(
-    std::vector<mojom::ThreatDOMDetailsNodePtr>* resources) {
-  blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
-  if (!frame)
-    return;
-  mojom::ThreatDOMDetailsNodePtr details_node =
-      mojom::ThreatDOMDetailsNode::New();
-  blink::WebDocument document = frame->GetDocument();
-  details_node->url = GURL(document.Url());
-  if (document.IsNull()) {
-    // Nothing in this frame. Just report its URL.
-    resources->push_back(std::move(details_node));
-    return;
-  }
-
-  ElementToNodeMap element_to_node_map;
-  blink::WebElementCollection elements = document.All();
-  blink::WebElement element = elements.FirstItem();
-  for (; !element.IsNull(); element = elements.NextItem()) {
-    if (ShouldHandleElement(element, tag_and_attributes_list_)) {
-      HandleElement(element, tag_and_attributes_list_, details_node.get(),
-                    resources, &element_to_node_map);
-      if (resources->size() >= kMaxNodes) {
-        // We have reached kMaxNodes, exit early.
-        break;
-      }
-    }
-  }
-  resources->push_back(std::move(details_node));
-}
-
-void ThreatDOMDetails::OnDestruct() {
-  delete this;
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/renderer/threat_dom_details.h b/components/safe_browsing/renderer/threat_dom_details.h
deleted file mode 100644
index 3ae85a1..0000000
--- a/components/safe_browsing/renderer/threat_dom_details.h
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright (c) 2012 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.
-//
-// ThreatDOMDetails iterates over a document's frames and gathers
-// interesting URLs such as those of scripts and frames. When done, it sends
-// them to the ThreatDetails that requested them.
-
-#ifndef COMPONENTS_SAFE_BROWSING_RENDERER_THREAT_DOM_DETAILS_H_
-#define COMPONENTS_SAFE_BROWSING_RENDERER_THREAT_DOM_DETAILS_H_
-
-#include <vector>
-
-#include "base/compiler_specific.h"
-#include "base/feature_list.h"
-#include "components/safe_browsing/common/safe_browsing.mojom.h"
-#include "content/public/renderer/render_frame_observer.h"
-#include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "mojo/public/cpp/bindings/receiver_set.h"
-#include "services/service_manager/public/cpp/binder_registry.h"
-
-namespace safe_browsing {
-
-extern const char kTagAndAttributeParamName[];
-
-// Represents the tag name of an HTML Element and its associated attributes.
-// Used to determine which elements to collect. Populated from the param value
-// of |kThreatDomDetailsTagAndAttributeFeature|.
-class TagAndAttributesItem {
- public:
-  TagAndAttributesItem();
-  TagAndAttributesItem(const std::string& tag_name_param,
-                       const std::vector<std::string>& attributes_param);
-  TagAndAttributesItem(const TagAndAttributesItem& item);
-  ~TagAndAttributesItem();
-
-  std::string tag_name;
-  std::vector<std::string> attributes;
-};
-
-// There is one ThreatDOMDetails per RenderFrame.
-class ThreatDOMDetails : public content::RenderFrameObserver,
-                         public mojom::ThreatReporter {
- public:
-  // An upper limit on the number of nodes we collect. Not const for the test.
-  static uint32_t kMaxNodes;
-
-  // An upper limit on the number of attributes to collect per node. Not const
-  // for the test.
-  static uint32_t kMaxAttributes;
-
-  // An upper limit on the length of an attribute string.
-  static uint32_t kMaxAttributeStringLength;
-
-  static ThreatDOMDetails* Create(content::RenderFrame* render_frame,
-                                  service_manager::BinderRegistry* registry);
-  ~ThreatDOMDetails() override;
-
-  // Begins extracting resource urls for the page currently loaded in
-  // this object's RenderFrame.
-  // Exposed for testing.
-  void ExtractResources(std::vector<mojom::ThreatDOMDetailsNodePtr>* resources);
-
-  std::vector<TagAndAttributesItem> GetTagAndAttributesListForTest() {
-    return tag_and_attributes_list_;
-  }
-
- private:
-  // Creates a ThreatDOMDetails for the specified RenderFrame.
-  // The ThreatDOMDetails should be destroyed prior to destroying
-  // the RenderFrame.
-  ThreatDOMDetails(content::RenderFrame* render_frame,
-                   service_manager::BinderRegistry* registry);
-
-  void OnDestruct() override;
-
-  // safe_browsing::mojom::ThreatReporter:
-  void GetThreatDOMDetails(GetThreatDOMDetailsCallback callback) override;
-
-  void OnThreatReporterReceiver(
-      mojo::PendingReceiver<mojom::ThreatReporter> receiver);
-
-  mojo::ReceiverSet<mojom::ThreatReporter> threat_reporter_receivers_;
-
-  // A list of tag names and associates attributes, used to determine which
-  // elements need to be collected.
-  std::vector<TagAndAttributesItem> tag_and_attributes_list_;
-
-  DISALLOW_COPY_AND_ASSIGN(ThreatDOMDetails);
-};
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_RENDERER_THREAT_DOM_DETAILS_H_
diff --git a/components/safe_browsing/renderer/websocket_sb_handshake_throttle.cc b/components/safe_browsing/renderer/websocket_sb_handshake_throttle.cc
deleted file mode 100644
index 833afe1..0000000
--- a/components/safe_browsing/renderer/websocket_sb_handshake_throttle.cc
+++ /dev/null
@@ -1,117 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/renderer/websocket_sb_handshake_throttle.h"
-
-#include <utility>
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/logging.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/strings/stringprintf.h"
-#include "content/public/common/resource_type.h"
-#include "content/public/renderer/render_frame.h"
-#include "ipc/ipc_message.h"
-#include "net/http/http_request_headers.h"
-#include "third_party/blink/public/platform/web_string.h"
-#include "third_party/blink/public/platform/web_url.h"
-
-namespace safe_browsing {
-
-WebSocketSBHandshakeThrottle::WebSocketSBHandshakeThrottle(
-    mojom::SafeBrowsing* safe_browsing,
-    int render_frame_id)
-    : render_frame_id_(render_frame_id),
-      safe_browsing_(safe_browsing),
-      result_(Result::UNKNOWN) {}
-
-WebSocketSBHandshakeThrottle::~WebSocketSBHandshakeThrottle() {
-  // ThrottleHandshake() should always be called, but since that is done all the
-  // way over in Blink, just avoid logging if it is not called rather than
-  // DCHECK()ing.
-  if (start_time_.is_null())
-    return;
-  if (result_ == Result::UNKNOWN) {
-    result_ = Result::ABANDONED;
-    UMA_HISTOGRAM_TIMES("SafeBrowsing.WebSocket.Elapsed.Abandoned",
-                        base::TimeTicks::Now() - start_time_);
-  }
-}
-
-void WebSocketSBHandshakeThrottle::ThrottleHandshake(
-    const blink::WebURL& url,
-    blink::WebSocketHandshakeThrottle::OnCompletion completion_callback) {
-  DCHECK(!url_checker_);
-  DCHECK(!completion_callback_);
-  completion_callback_ = std::move(completion_callback);
-  url_ = url;
-  int load_flags = 0;
-  start_time_ = base::TimeTicks::Now();
-  safe_browsing_->CreateCheckerAndCheck(
-      render_frame_id_, url_checker_.BindNewPipeAndPassReceiver(), url, "GET",
-      net::HttpRequestHeaders(), load_flags,
-      content::ResourceType::kSubResource, false /* has_user_gesture */,
-      false /* originated_from_service_worker */,
-      base::BindOnce(&WebSocketSBHandshakeThrottle::OnCheckResult,
-                     weak_factory_.GetWeakPtr()));
-
-  // This use of base::Unretained() is safe because the handler will not be
-  // called after |url_checker_| is destroyed, and it is owned by this object.
-  url_checker_.set_disconnect_handler(base::BindOnce(
-      &WebSocketSBHandshakeThrottle::OnMojoDisconnect, base::Unretained(this)));
-}
-
-void WebSocketSBHandshakeThrottle::OnCompleteCheck(bool proceed,
-                                                   bool showed_interstitial) {
-  DCHECK(!start_time_.is_null());
-  base::TimeDelta elapsed = base::TimeTicks::Now() - start_time_;
-  if (proceed) {
-    result_ = Result::SAFE;
-    UMA_HISTOGRAM_TIMES("SafeBrowsing.WebSocket.Elapsed.Safe", elapsed);
-    std::move(completion_callback_).Run(base::nullopt);
-  } else {
-    // When the insterstitial is dismissed the page is navigated and this object
-    // is destroyed before reaching here.
-    result_ = Result::BLOCKED;
-    UMA_HISTOGRAM_TIMES("SafeBrowsing.WebSocket.Elapsed.Blocked", elapsed);
-    std::move(completion_callback_)
-        .Run(blink::WebString::FromUTF8(base::StringPrintf(
-            "WebSocket connection to %s failed safe browsing check",
-            url_.spec().c_str())));
-  }
-  // |this| is destroyed here.
-}
-
-void WebSocketSBHandshakeThrottle::OnCheckResult(
-    mojo::PendingReceiver<mojom::UrlCheckNotifier> slow_check_notifier,
-    bool proceed,
-    bool showed_interstitial) {
-  if (!slow_check_notifier.is_valid()) {
-    OnCompleteCheck(proceed, showed_interstitial);
-    return;
-  }
-
-  // TODO(yzshen): Notify the network service to pause processing response body.
-  if (!notifier_receiver_) {
-    notifier_receiver_ =
-        std::make_unique<mojo::Receiver<mojom::UrlCheckNotifier>>(this);
-  }
-  notifier_receiver_->Bind(std::move(slow_check_notifier));
-}
-
-void WebSocketSBHandshakeThrottle::OnMojoDisconnect() {
-  DCHECK_EQ(result_, Result::UNKNOWN);
-
-  url_checker_.reset();
-  notifier_receiver_.reset();
-
-  // Make the destructor record NOT_SUPPORTED in the result histogram.
-  result_ = Result::NOT_SUPPORTED;
-  // Don't record the time elapsed because it's unlikely to be meaningful.
-  std::move(completion_callback_).Run(base::nullopt);
-  // |this| is destroyed here.
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/renderer/websocket_sb_handshake_throttle.h b/components/safe_browsing/renderer/websocket_sb_handshake_throttle.h
deleted file mode 100644
index f30a3dab..0000000
--- a/components/safe_browsing/renderer/websocket_sb_handshake_throttle.h
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Implementation of SafeBrowsing for WebSockets. This code runs inside the
-// render process, calling the interface defined in safe_browsing.mojom to
-// communicate with the SafeBrowsing service.
-
-#ifndef COMPONENTS_SAFE_BROWSING_RENDERER_WEBSOCKET_SB_HANDSHAKE_THROTTLE_H_
-#define COMPONENTS_SAFE_BROWSING_RENDERER_WEBSOCKET_SB_HANDSHAKE_THROTTLE_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "base/time/time.h"
-#include "components/safe_browsing/common/safe_browsing.mojom.h"
-#include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "mojo/public/cpp/bindings/receiver.h"
-#include "mojo/public/cpp/bindings/remote.h"
-#include "third_party/blink/public/platform/websocket_handshake_throttle.h"
-#include "url/gurl.h"
-
-namespace safe_browsing {
-
-class WebSocketSBHandshakeThrottle : public blink::WebSocketHandshakeThrottle,
-                                     public mojom::UrlCheckNotifier {
- public:
-  WebSocketSBHandshakeThrottle(mojom::SafeBrowsing* safe_browsing,
-                               int render_frame_id);
-  ~WebSocketSBHandshakeThrottle() override;
-
-  void ThrottleHandshake(const blink::WebURL& url,
-                         blink::WebSocketHandshakeThrottle::OnCompletion
-                             completion_callback) override;
-
- private:
-  // These values are logged to UMA so do not renumber or reuse.
-  enum class Result {
-    UNKNOWN = 0,
-    SAFE = 1,
-    BLOCKED = 2,
-    ABANDONED = 3,
-    NOT_SUPPORTED = 4,
-    RESULT_COUNT
-  };
-
-  // mojom::UrlCheckNotifier implementation.
-  void OnCompleteCheck(bool proceed, bool showed_interstitial) override;
-
-  void OnCheckResult(
-      mojo::PendingReceiver<mojom::UrlCheckNotifier> slow_check_notifier,
-      bool proceed,
-      bool showed_interstitial);
-  void OnMojoDisconnect();
-
-  const int render_frame_id_;
-  GURL url_;
-  blink::WebSocketHandshakeThrottle::OnCompletion completion_callback_;
-  mojo::Remote<mojom::SafeBrowsingUrlChecker> url_checker_;
-  mojom::SafeBrowsing* safe_browsing_;
-  std::unique_ptr<mojo::Receiver<mojom::UrlCheckNotifier>> notifier_receiver_;
-  base::TimeTicks start_time_;
-  Result result_;
-
-  base::WeakPtrFactory<WebSocketSBHandshakeThrottle> weak_factory_{this};
-
-  DISALLOW_COPY_AND_ASSIGN(WebSocketSBHandshakeThrottle);
-};
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_RENDERER_WEBSOCKET_SB_HANDSHAKE_THROTTLE_H_
diff --git a/components/safe_browsing/renderer/websocket_sb_handshake_throttle_unittest.cc b/components/safe_browsing/renderer/websocket_sb_handshake_throttle_unittest.cc
deleted file mode 100644
index 781a4bff..0000000
--- a/components/safe_browsing/renderer/websocket_sb_handshake_throttle_unittest.cc
+++ /dev/null
@@ -1,195 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/renderer/websocket_sb_handshake_throttle.h"
-
-#include <utility>
-
-#include "base/callback.h"
-#include "base/run_loop.h"
-#include "base/test/task_environment.h"
-#include "components/safe_browsing/common/safe_browsing.mojom.h"
-#include "content/public/common/resource_type.h"
-#include "ipc/ipc_message.h"
-#include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "mojo/public/cpp/bindings/receiver.h"
-#include "mojo/public/cpp/bindings/remote.h"
-#include "net/http/http_request_headers.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/platform/web_string.h"
-#include "third_party/blink/public/platform/web_url.h"
-
-namespace safe_browsing {
-
-namespace {
-
-constexpr char kTestUrl[] = "wss://test/";
-
-class FakeSafeBrowsing : public mojom::SafeBrowsing {
- public:
-  FakeSafeBrowsing()
-      : render_frame_id_(),
-        load_flags_(-1),
-        resource_type_(),
-        has_user_gesture_(false),
-        originated_from_service_worker_(false) {}
-
-  void CreateCheckerAndCheck(
-      int32_t render_frame_id,
-      mojo::PendingReceiver<mojom::SafeBrowsingUrlChecker> receiver,
-      const GURL& url,
-      const std::string& method,
-      const net::HttpRequestHeaders& headers,
-      int32_t load_flags,
-      content::ResourceType resource_type,
-      bool has_user_gesture,
-      bool originated_from_service_worker,
-      CreateCheckerAndCheckCallback callback) override {
-    render_frame_id_ = render_frame_id;
-    receiver_ = std::move(receiver);
-    url_ = url;
-    method_ = method;
-    headers_ = headers;
-    load_flags_ = load_flags;
-    resource_type_ = resource_type;
-    has_user_gesture_ = has_user_gesture;
-    originated_from_service_worker_ = originated_from_service_worker;
-    callback_ = std::move(callback);
-    run_loop_.Quit();
-  }
-
-  void Clone(mojo::PendingReceiver<mojom::SafeBrowsing> receiver) override {
-    NOTREACHED();
-  }
-
-  void RunUntilCalled() { run_loop_.Run(); }
-
-  int32_t render_frame_id_;
-  mojo::PendingReceiver<mojom::SafeBrowsingUrlChecker> receiver_;
-  GURL url_;
-  std::string method_;
-  net::HttpRequestHeaders headers_;
-  int32_t load_flags_;
-  content::ResourceType resource_type_;
-  bool has_user_gesture_;
-  bool originated_from_service_worker_;
-  CreateCheckerAndCheckCallback callback_;
-  base::RunLoop run_loop_;
-};
-
-class FakeCallback {
- public:
-  enum Result { RESULT_NOT_CALLED, RESULT_SUCCESS, RESULT_ERROR };
-
-  FakeCallback() : result_(RESULT_NOT_CALLED) {}
-
-  void OnCompletion(const base::Optional<blink::WebString>& message) {
-    if (message) {
-      result_ = RESULT_ERROR;
-      message_ = *message;
-      run_loop_.Quit();
-      return;
-    }
-
-    result_ = RESULT_SUCCESS;
-    run_loop_.Quit();
-  }
-
-  void RunUntilCalled() { run_loop_.Run(); }
-
-  void RunUntilIdle() { base::RunLoop().RunUntilIdle(); }
-
-  Result result_;
-  blink::WebString message_;
-  base::RunLoop run_loop_;
-};
-
-class WebSocketSBHandshakeThrottleTest : public ::testing::Test {
- protected:
-  WebSocketSBHandshakeThrottleTest() : mojo_receiver_(&safe_browsing_) {
-    mojo_receiver_.Bind(safe_browsing_remote_.BindNewPipeAndPassReceiver());
-    throttle_ = std::make_unique<WebSocketSBHandshakeThrottle>(
-        safe_browsing_remote_.get(), MSG_ROUTING_NONE);
-  }
-
-  base::test::TaskEnvironment message_loop_;
-  FakeSafeBrowsing safe_browsing_;
-  mojo::Receiver<mojom::SafeBrowsing> mojo_receiver_;
-  mojo::Remote<mojom::SafeBrowsing> safe_browsing_remote_;
-  std::unique_ptr<WebSocketSBHandshakeThrottle> throttle_;
-  FakeCallback fake_callback_;
-};
-
-TEST_F(WebSocketSBHandshakeThrottleTest, Construction) {}
-
-TEST_F(WebSocketSBHandshakeThrottleTest, CheckArguments) {
-  throttle_->ThrottleHandshake(
-      GURL(kTestUrl), base::BindOnce(&FakeCallback::OnCompletion,
-                                     base::Unretained(&fake_callback_)));
-  safe_browsing_.RunUntilCalled();
-  EXPECT_EQ(MSG_ROUTING_NONE, safe_browsing_.render_frame_id_);
-  EXPECT_EQ(GURL(kTestUrl), safe_browsing_.url_);
-  EXPECT_EQ("GET", safe_browsing_.method_);
-  EXPECT_TRUE(safe_browsing_.headers_.GetHeaderVector().empty());
-  EXPECT_EQ(0, safe_browsing_.load_flags_);
-  EXPECT_EQ(content::ResourceType::kSubResource, safe_browsing_.resource_type_);
-  EXPECT_FALSE(safe_browsing_.has_user_gesture_);
-  EXPECT_FALSE(safe_browsing_.originated_from_service_worker_);
-  EXPECT_TRUE(safe_browsing_.callback_);
-}
-
-TEST_F(WebSocketSBHandshakeThrottleTest, Safe) {
-  throttle_->ThrottleHandshake(
-      GURL(kTestUrl), base::BindOnce(&FakeCallback::OnCompletion,
-                                     base::Unretained(&fake_callback_)));
-  safe_browsing_.RunUntilCalled();
-  std::move(safe_browsing_.callback_).Run(mojo::NullReceiver(), true, false);
-  fake_callback_.RunUntilCalled();
-  EXPECT_EQ(FakeCallback::RESULT_SUCCESS, fake_callback_.result_);
-}
-
-TEST_F(WebSocketSBHandshakeThrottleTest, Unsafe) {
-  throttle_->ThrottleHandshake(
-      GURL(kTestUrl), base::BindOnce(&FakeCallback::OnCompletion,
-                                     base::Unretained(&fake_callback_)));
-  safe_browsing_.RunUntilCalled();
-  std::move(safe_browsing_.callback_).Run(mojo::NullReceiver(), false, false);
-  fake_callback_.RunUntilCalled();
-  EXPECT_EQ(FakeCallback::RESULT_ERROR, fake_callback_.result_);
-  EXPECT_EQ(
-      blink::WebString(
-          "WebSocket connection to wss://test/ failed safe browsing check"),
-      fake_callback_.message_);
-}
-
-TEST_F(WebSocketSBHandshakeThrottleTest, SlowCheckNotifier) {
-  throttle_->ThrottleHandshake(
-      GURL(kTestUrl), base::BindOnce(&FakeCallback::OnCompletion,
-                                     base::Unretained(&fake_callback_)));
-  safe_browsing_.RunUntilCalled();
-
-  mojo::Remote<mojom::UrlCheckNotifier> slow_check_notifier;
-  std::move(safe_browsing_.callback_)
-      .Run(slow_check_notifier.BindNewPipeAndPassReceiver(), false, false);
-  fake_callback_.RunUntilIdle();
-  EXPECT_EQ(FakeCallback::RESULT_NOT_CALLED, fake_callback_.result_);
-
-  slow_check_notifier->OnCompleteCheck(true, false);
-  fake_callback_.RunUntilCalled();
-  EXPECT_EQ(FakeCallback::RESULT_SUCCESS, fake_callback_.result_);
-}
-
-TEST_F(WebSocketSBHandshakeThrottleTest, MojoServiceNotThere) {
-  mojo_receiver_.reset();
-  throttle_->ThrottleHandshake(
-      GURL(kTestUrl), base::BindOnce(&FakeCallback::OnCompletion,
-                                     base::Unretained(&fake_callback_)));
-  fake_callback_.RunUntilCalled();
-  EXPECT_EQ(FakeCallback::RESULT_SUCCESS, fake_callback_.result_);
-}
-
-}  // namespace
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/safe_browsing_controller_client.cc b/components/safe_browsing/safe_browsing_controller_client.cc
deleted file mode 100644
index 6d88892..0000000
--- a/components/safe_browsing/safe_browsing_controller_client.cc
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/safe_browsing_controller_client.h"
-
-#include "base/feature_list.h"
-#include "components/safe_browsing/features.h"
-#include "components/security_interstitials/core/metrics_helper.h"
-
-namespace safe_browsing {
-
-SafeBrowsingControllerClient::SafeBrowsingControllerClient(
-    content::WebContents* web_contents,
-    std::unique_ptr<security_interstitials::MetricsHelper> metrics_helper,
-    PrefService* prefs,
-    const std::string& app_locale,
-    const GURL& default_safe_page)
-    : security_interstitials::SecurityInterstitialControllerClient(
-          web_contents,
-          std::move(metrics_helper),
-          prefs,
-          app_locale,
-          default_safe_page) {}
-
-SafeBrowsingControllerClient::~SafeBrowsingControllerClient() {}
-
-void SafeBrowsingControllerClient::Proceed() {
-  if (!interstitial_page()) {
-    DCHECK(
-        base::FeatureList::IsEnabled(safe_browsing::kCommittedSBInterstitials));
-    // In this case, committed interstitials are enabled, the site has already
-    // been added to the whitelist, so reload will proceed.
-    Reload();
-    return;
-  }
-  security_interstitials::SecurityInterstitialControllerClient::Proceed();
-}
-
-void SafeBrowsingControllerClient::GoBack() {
-  if (!interstitial_page()) {
-    // In this case, committed interstitials are enabled, so we do a regular
-    // back navigation.
-    SecurityInterstitialControllerClient::GoBackAfterNavigationCommitted();
-    return;
-  }
-
-  SecurityInterstitialControllerClient::GoBack();
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/safe_browsing_controller_client.h b/components/safe_browsing/safe_browsing_controller_client.h
deleted file mode 100644
index 57b82c7..0000000
--- a/components/safe_browsing/safe_browsing_controller_client.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_SAFE_BROWSING_CONTROLLER_CLIENT_H_
-#define COMPONENTS_SAFE_BROWSING_SAFE_BROWSING_CONTROLLER_CLIENT_H_
-
-#include "base/macros.h"
-#include "components/security_interstitials/content/security_interstitial_controller_client.h"
-
-namespace content {
-class WebContents;
-}
-
-namespace security_interstitials {
-class MetricsHelper;
-}
-
-class PrefService;
-
-namespace safe_browsing {
-
-// Provides embedder-specific logic for the Safe Browsing interstitial page
-// controller.
-class SafeBrowsingControllerClient
-    : public security_interstitials::SecurityInterstitialControllerClient {
- public:
-  SafeBrowsingControllerClient(
-      content::WebContents* web_contents,
-      std::unique_ptr<security_interstitials::MetricsHelper> metrics_helper,
-      PrefService* prefs,
-      const std::string& app_locale,
-      const GURL& default_safe_page);
-  ~SafeBrowsingControllerClient() override;
-
-  void Proceed() override;
-
-  void GoBack() override;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(SafeBrowsingControllerClient);
-};
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_SAFE_BROWSING_CONTROLLER_CLIENT_H_
diff --git a/components/safe_browsing/safe_browsing_service_interface.cc b/components/safe_browsing/safe_browsing_service_interface.cc
deleted file mode 100644
index a16d3e6..0000000
--- a/components/safe_browsing/safe_browsing_service_interface.cc
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/safe_browsing_service_interface.h"
-
-namespace safe_browsing {
-
-SafeBrowsingServiceInterface*
-SafeBrowsingServiceInterface::CreateSafeBrowsingService() {
-  return factory_ ? factory_->CreateSafeBrowsingService() : nullptr;
-}
-
-// static
-SafeBrowsingServiceFactory* SafeBrowsingServiceInterface::factory_ = nullptr;
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/safe_browsing_service_interface.h b/components/safe_browsing/safe_browsing_service_interface.h
deleted file mode 100644
index 4f216ccb..0000000
--- a/components/safe_browsing/safe_browsing_service_interface.h
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_SAFE_BROWSING_SERVICE_INTERFACE_H_
-#define COMPONENTS_SAFE_BROWSING_SAFE_BROWSING_SERVICE_INTERFACE_H_
-
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "content/public/browser/browser_thread.h"
-
-namespace safe_browsing {
-
-class SafeBrowsingServiceFactory;
-
-// This interface will provide methods for checking the safety of URLs and
-// downloads with Safe Browsing.
-class SafeBrowsingServiceInterface
-    : public base::RefCountedThreadSafe<
-          SafeBrowsingServiceInterface,
-          content::BrowserThread::DeleteOnUIThread> {
- public:
-  // Makes the passed |factory| the factory used to instantiate
-  // a SafeBrowsingServiceInterface. Useful for tests.
-  static void RegisterFactory(SafeBrowsingServiceFactory* factory) {
-    factory_ = factory;
-  }
-
-  static bool HasFactory() { return (factory_ != nullptr); }
-
-  // Create an instance of the safe browsing service.
-  static SafeBrowsingServiceInterface* CreateSafeBrowsingService();
-
- protected:
-  SafeBrowsingServiceInterface() {}
-  virtual ~SafeBrowsingServiceInterface() {}
-
- private:
-  friend struct content::BrowserThread::DeleteOnThread<
-      content::BrowserThread::UI>;
-  friend class base::DeleteHelper<SafeBrowsingServiceInterface>;
-
-  // The factory used to instantiate a SafeBrowsingServiceInterface object.
-  // Useful for tests, so they can provide their own implementation of
-  // SafeBrowsingServiceInterface.
-  static SafeBrowsingServiceFactory* factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(SafeBrowsingServiceInterface);
-};
-
-// Factory for creating SafeBrowsingServiceInterface.  Useful for tests.
-class SafeBrowsingServiceFactory {
- public:
-  SafeBrowsingServiceFactory() {}
-  virtual ~SafeBrowsingServiceFactory() {}
-
-  // TODO(crbug/925153): Once callers of this function are no longer downcasting
-  // it to the SafeBrowsingService, we can make this a scoped_refptr.
-  virtual SafeBrowsingServiceInterface* CreateSafeBrowsingService() = 0;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(SafeBrowsingServiceFactory);
-};
-
-}  // namespace safe_browsing
-
-#endif  //  COMPONENTS_SAFE_BROWSING_SAFE_BROWSING_SERVICE_INTERFACE_H_
diff --git a/components/safe_browsing/triggers/BUILD.gn b/components/safe_browsing/triggers/BUILD.gn
deleted file mode 100644
index 1ee288e..0000000
--- a/components/safe_browsing/triggers/BUILD.gn
+++ /dev/null
@@ -1,144 +0,0 @@
-# Copyright 2017 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//build/config/features.gni")
-
-source_set("triggers") {
-  sources = [
-    "trigger_manager.cc",
-    "trigger_manager.h",
-  ]
-  public_deps = [
-    "//components/security_interstitials/content:security_interstitial_page",
-    "//components/security_interstitials/core:core",
-    "//content/public/browser:browser",
-  ]
-  deps = [
-    ":trigger_throttler",
-    "//base:base",
-    "//components/prefs:prefs",
-    "//components/safe_browsing:features",
-    "//components/safe_browsing:safe_browsing",
-    "//components/safe_browsing/browser:browser",
-    "//components/safe_browsing/browser:referrer_chain_provider",
-    "//net:net",
-  ]
-}
-
-source_set("trigger_throttler") {
-  sources = [
-    "trigger_throttler.cc",
-    "trigger_throttler.h",
-  ]
-  deps = [
-    "//base:base",
-    "//components/prefs:prefs",
-    "//components/safe_browsing:features",
-    "//components/safe_browsing/common:safe_browsing_prefs",
-  ]
-}
-
-source_set("trigger_util") {
-  sources = [
-    "trigger_util.cc",
-    "trigger_util.h",
-  ]
-  deps = [
-    ":triggers",
-    "//content/public/browser",
-  ]
-}
-
-source_set("ad_popup_trigger") {
-  sources = [
-    "ad_popup_trigger.cc",
-    "ad_popup_trigger.h",
-  ]
-  deps = [
-    ":trigger_throttler",
-    ":trigger_util",
-    ":triggers",
-    "//base:base",
-    "//components/safe_browsing:features",
-    "//content/public/browser",
-  ]
-}
-
-source_set("ad_redirect_trigger") {
-  sources = [
-    "ad_redirect_trigger.cc",
-    "ad_redirect_trigger.h",
-  ]
-  deps = [
-    ":trigger_throttler",
-    ":trigger_util",
-    ":triggers",
-    "//base:base",
-    "//components/safe_browsing:features",
-    "//content/public/browser",
-    "//content/public/common",
-  ]
-}
-
-source_set("ad_sampler_trigger") {
-  sources = [
-    "ad_sampler_trigger.cc",
-    "ad_sampler_trigger.h",
-  ]
-  deps = [
-    ":trigger_throttler",
-    ":trigger_util",
-    ":triggers",
-    "//base:base",
-    "//components/safe_browsing:features",
-    "//content/public/browser",
-  ]
-}
-
-source_set("suspicious_site_trigger") {
-  sources = [
-    "suspicious_site_trigger.cc",
-    "suspicious_site_trigger.h",
-  ]
-  deps = [
-    ":trigger_throttler",
-    ":triggers",
-    "//base:base",
-    "//components/history/core/browser:browser",
-    "//components/prefs:prefs",
-    "//components/safe_browsing:features",
-    "//content/public/browser",
-    "//net:net",
-  ]
-}
-
-source_set("unit_tests") {
-  testonly = true
-  sources = [
-    "ad_popup_trigger_unittest.cc",
-    "ad_sampler_trigger_unittest.cc",
-    "mock_trigger_manager.cc",
-    "mock_trigger_manager.h",
-    "suspicious_site_trigger_unittest.cc",
-    "trigger_manager_unittest.cc",
-    "trigger_throttler_unittest.cc",
-  ]
-  deps = [
-    ":ad_popup_trigger",
-    ":ad_sampler_trigger",
-    ":suspicious_site_trigger",
-    ":trigger_throttler",
-    ":triggers",
-    "//base",
-    "//base/test:test_support",
-    "//components/prefs:test_support",
-    "//components/safe_browsing:features",
-    "//components/safe_browsing/browser:browser",
-    "//components/subresource_filter/content/browser:test_support",
-    "//content/public/browser:browser",
-    "//content/test:test_support",
-    "//testing/gmock",
-    "//testing/gtest",
-  ]
-}
diff --git a/components/safe_browsing/triggers/ad_popup_trigger.cc b/components/safe_browsing/triggers/ad_popup_trigger.cc
deleted file mode 100644
index b4d1f6dc..0000000
--- a/components/safe_browsing/triggers/ad_popup_trigger.cc
+++ /dev/null
@@ -1,150 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/triggers/ad_popup_trigger.h"
-
-#include <string>
-
-#include "base/bind.h"
-#include "base/feature_list.h"
-#include "base/metrics/field_trial_params.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/rand_util.h"
-#include "base/single_thread_task_runner.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/task/post_task.h"
-#include "components/safe_browsing/features.h"
-#include "components/safe_browsing/triggers/trigger_manager.h"
-#include "components/safe_browsing/triggers/trigger_throttler.h"
-#include "components/safe_browsing/triggers/trigger_util.h"
-#include "components/security_interstitials/content/unsafe_resource.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/navigation_handle.h"
-#include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/render_process_host.h"
-#include "content/public/browser/web_contents.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "services/network/public/cpp/simple_url_loader.h"
-
-namespace safe_browsing {
-
-namespace {
-
-// Number of milliseconds to allow data collection to run before sending a
-// report (since this trigger runs in the background).
-const int64_t kAdPopupCollectionPeriodMilliseconds = 5000;
-
-// Range of number of milliseconds to wait after a page finished loading before
-// starting a report. Allows ads which load in the background to finish loading.
-const int64_t kMaxAdPopupCollectionStartDelayMilliseconds = 5000;
-const int64_t kMinAdPopupCollectionStartDelayMilliseconds = 500;
-
-void RecordAdPopupTriggerAction(AdPopupTriggerAction action) {
-  UMA_HISTOGRAM_ENUMERATION(kAdPopupTriggerActionMetricName, action);
-}
-
-}  // namespace
-
-// Metric for tracking what the Ad Popup trigger does on each navigation.
-const char kAdPopupTriggerActionMetricName[] =
-    "SafeBrowsing.Triggers.AdPopup.Action";
-
-AdPopupTrigger::AdPopupTrigger(
-    content::WebContents* web_contents,
-    TriggerManager* trigger_manager,
-    PrefService* prefs,
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    history::HistoryService* history_service)
-    : web_contents_(web_contents),
-      start_report_delay_ms_(
-          base::RandInt(kMinAdPopupCollectionStartDelayMilliseconds,
-                        kMaxAdPopupCollectionStartDelayMilliseconds)),
-      finish_report_delay_ms_(kAdPopupCollectionPeriodMilliseconds),
-      trigger_manager_(trigger_manager),
-      prefs_(prefs),
-      url_loader_factory_(url_loader_factory),
-      history_service_(history_service),
-      task_runner_(
-          base::CreateSingleThreadTaskRunner({content::BrowserThread::UI})) {}
-
-AdPopupTrigger::~AdPopupTrigger() {}
-
-// static
-void AdPopupTrigger::CreateForWebContents(
-    content::WebContents* web_contents,
-    TriggerManager* trigger_manager,
-    PrefService* prefs,
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    history::HistoryService* history_service) {
-  DCHECK(web_contents);
-  if (!FromWebContents(web_contents)) {
-    web_contents->SetUserData(UserDataKey(),
-                              base::WrapUnique(new AdPopupTrigger(
-                                  web_contents, trigger_manager, prefs,
-                                  url_loader_factory, history_service)));
-  }
-}
-
-void AdPopupTrigger::CreateAdPopupReport() {
-  SBErrorOptions error_options =
-      TriggerManager::GetSBErrorDisplayOptions(*prefs_, web_contents_);
-  security_interstitials::UnsafeResource resource;
-  resource.threat_type = SB_THREAT_TYPE_BLOCKED_AD_POPUP;
-  resource.url = web_contents_->GetURL();
-  resource.web_contents_getter = resource.GetWebContentsGetter(
-      web_contents_->GetMainFrame()->GetProcess()->GetID(),
-      web_contents_->GetMainFrame()->GetRoutingID());
-  TriggerManagerReason reason = TriggerManagerReason::NO_REASON;
-  if (!trigger_manager_->StartCollectingThreatDetailsWithReason(
-          TriggerType::AD_POPUP, web_contents_, resource, url_loader_factory_,
-          history_service_, error_options, &reason)) {
-    if (reason == TriggerManagerReason::DAILY_QUOTA_EXCEEDED) {
-      RecordAdPopupTriggerAction(
-          AdPopupTriggerAction::POPUP_DAILY_QUOTA_EXCEEDED);
-    } else {
-      RecordAdPopupTriggerAction(
-          AdPopupTriggerAction::POPUP_COULD_NOT_START_REPORT);
-    }
-    return;
-  }
-  // Call into TriggerManager to finish the reports after a short delay. Any
-  // ads that are detected during this delay will be rejected by TriggerManager
-  // because a report is already being collected, so we won't send multiple
-  // reports for the same page.
-  task_runner_->PostDelayedTask(
-      FROM_HERE,
-      base::BindOnce(
-          IgnoreResult(&TriggerManager::FinishCollectingThreatDetails),
-          base::Unretained(trigger_manager_), TriggerType::AD_POPUP,
-          base::Unretained(web_contents_), base::TimeDelta(),
-          /*did_proceed=*/false, /*num_visits=*/0, error_options),
-      base::TimeDelta::FromMilliseconds(finish_report_delay_ms_));
-
-  RecordAdPopupTriggerAction(AdPopupTriggerAction::POPUP_REPORTED);
-}
-
-void AdPopupTrigger::PopupWasBlocked(content::RenderFrameHost* render_frame) {
-  RecordAdPopupTriggerAction(AdPopupTriggerAction::POPUP_CHECK);
-  if (!DetectGoogleAd(render_frame, web_contents_->GetURL())) {
-    RecordAdPopupTriggerAction(AdPopupTriggerAction::POPUP_NO_GOOGLE_AD);
-    return;
-  }
-  // Create a report after a short delay. The delay gives more time for ads to
-  // finish loading in the background. This is best-effort.
-  task_runner_->PostDelayedTask(
-      FROM_HERE,
-      base::BindOnce(&AdPopupTrigger::CreateAdPopupReport,
-                     weak_ptr_factory_.GetWeakPtr()),
-      base::TimeDelta::FromMilliseconds(start_report_delay_ms_));
-}
-
-void AdPopupTrigger::SetTaskRunnerForTest(
-    scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
-  task_runner_ = task_runner;
-}
-
-WEB_CONTENTS_USER_DATA_KEY_IMPL(AdPopupTrigger)
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/triggers/ad_popup_trigger.h b/components/safe_browsing/triggers/ad_popup_trigger.h
deleted file mode 100644
index 53b34b29..0000000
--- a/components/safe_browsing/triggers/ad_popup_trigger.h
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_TRIGGERS_AD_POPUP_TRIGGER_H_
-#define COMPONENTS_SAFE_BROWSING_TRIGGERS_AD_POPUP_TRIGGER_H_
-
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "content/public/browser/web_contents_user_data.h"
-
-class PrefService;
-
-namespace history {
-class HistoryService;
-}
-
-namespace network {
-class SharedURLLoaderFactory;
-}  // namespace network
-
-namespace safe_browsing {
-class TriggerManager;
-
-// Metric for tracking what the Ad Popup trigger does on each navigation.
-extern const char kAdPopupTriggerActionMetricName[];
-
-enum class AdPopupTriggerAction {
-  // An event occurred that caused the trigger to perform its checks.
-  POPUP_CHECK = 0,
-  // A popup cause by an ad was detected and a report was collected.
-  POPUP_REPORTED = 1,
-  // No ad was detected.
-  POPUP_NO_GOOGLE_AD = 2,
-  // An ad was detected on the page causing a popup and could have been
-  // reported, but the trigger manager rejected the report (eg: because user is
-  // incognito or has not opted into extended reporting).
-  POPUP_COULD_NOT_START_REPORT = 3,
-  // Daily quota for ads that caused blocked popups was met.
-  POPUP_DAILY_QUOTA_EXCEEDED = 4,
-  // New events must be added before kMaxValue, and the value of kMaxValue
-  // updated.
-  kMaxValue = POPUP_DAILY_QUOTA_EXCEEDED
-};
-
-// This class is notified when a popup caused by an ad in the browser is
-// blocked. If the Ad is a Google Ad, this class sends a report to Google.
-// Design doc: go/extending-chrind-q2-2019-1
-class AdPopupTrigger : public content::WebContentsUserData<AdPopupTrigger> {
- public:
-  ~AdPopupTrigger() override;
-
-  static void CreateForWebContents(
-      content::WebContents* web_contents,
-      TriggerManager* trigger_manager,
-      PrefService* prefs,
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      history::HistoryService* history_service);
-
-  void PopupWasBlocked(content::RenderFrameHost* render_frame);
-
- private:
-  friend class AdPopupTriggerTest;
-  friend class content::WebContentsUserData<AdPopupTrigger>;
-
-  AdPopupTrigger(
-      content::WebContents* web_contents,
-      TriggerManager* trigger_manager,
-      PrefService* prefs,
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      history::HistoryService* history_service);
-
-  // Called to create an ad popup report.
-  void CreateAdPopupReport();
-
-  // Sets a task runner to use for tests.
-  void SetTaskRunnerForTest(
-      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
-
-  // WebContents of the current tab.
-  content::WebContents* web_contents_;
-
-  // The delay (in milliseconds) to wait before starting a report. Can be
-  // ovewritten for tests.
-  int64_t start_report_delay_ms_;
-
-  // The delay (in milliseconds) to wait before finishing a report. Can be
-  // overwritten for tests.
-  int64_t finish_report_delay_ms_;
-
-  // TriggerManager gets called if this trigger detects apopup caused by ad and
-  // wants to collect some data about it. Not owned.
-  TriggerManager* trigger_manager_;
-
-  PrefService* prefs_;
-  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
-  history::HistoryService* history_service_;
-
-  // Task runner for posting delayed tasks. Normally set to the runner for the
-  // UI thread, but can be overwritten for tests.
-  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
-
-  base::WeakPtrFactory<AdPopupTrigger> weak_ptr_factory_{this};
-  WEB_CONTENTS_USER_DATA_KEY_DECL();
-
-  DISALLOW_COPY_AND_ASSIGN(AdPopupTrigger);
-};
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_TRIGGERS_AD_POPUP_TRIGGER_H_
diff --git a/components/safe_browsing/triggers/ad_popup_trigger_unittest.cc b/components/safe_browsing/triggers/ad_popup_trigger_unittest.cc
deleted file mode 100644
index f344c4d..0000000
--- a/components/safe_browsing/triggers/ad_popup_trigger_unittest.cc
+++ /dev/null
@@ -1,219 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/triggers/ad_popup_trigger.h"
-
-#include "base/metrics/field_trial_params.h"
-#include "base/test/metrics/histogram_tester.h"
-#include "base/test/scoped_feature_list.h"
-#include "base/test/test_simple_task_runner.h"
-#include "build/build_config.h"
-#include "components/prefs/testing_pref_service.h"
-#include "components/safe_browsing/features.h"
-#include "components/safe_browsing/triggers/mock_trigger_manager.h"
-#include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/test/browser_task_environment.h"
-#include "content/public/test/navigation_simulator.h"
-#include "content/public/test/test_renderer_host.h"
-#include "testing/gmock/include/gmock/gmock-generated-function-mockers.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using content::NavigationSimulator;
-using content::RenderFrameHost;
-using content::RenderFrameHostTester;
-
-using testing::_;
-using testing::Return;
-
-namespace safe_browsing {
-
-namespace {
-const char kAdUrl[] = "https://tpc.googlesyndication.com/safeframe/1";
-const char kNonAdUrl[] = "https://foo.com/";
-const char kAdName[] = "google_ads_iframe_1";
-const char kNonAdName[] = "foo";
-}  // namespace
-
-class AdPopupTriggerTest : public content::RenderViewHostTestHarness {
- public:
-  AdPopupTriggerTest() : task_runner_(new base::TestSimpleTaskRunner) {}
-  ~AdPopupTriggerTest() override {}
-
-  void SetUp() override {
-    content::RenderViewHostTestHarness::SetUp();
-
-    // Enable any prefs required for the trigger to run.
-    safe_browsing::RegisterProfilePrefs(prefs_.registry());
-    prefs_.SetBoolean(prefs::kSafeBrowsingExtendedReportingOptInAllowed, true);
-    prefs_.SetBoolean(prefs::kSafeBrowsingScoutReportingEnabled, true);
-  }
-
-  void CreateTrigger() {
-    safe_browsing::AdPopupTrigger::CreateForWebContents(
-        web_contents(), &trigger_manager_, &prefs_, nullptr, nullptr);
-
-    safe_browsing::AdPopupTrigger* ad_popup_trigger =
-        safe_browsing::AdPopupTrigger::FromWebContents(web_contents());
-
-    // Give the trigger a test task runner that we can synchronize on.
-    ad_popup_trigger->SetTaskRunnerForTest(task_runner_);
-  }
-
-  // Returns the final RenderFrameHost after navigation commits.
-  RenderFrameHost* NavigateFrame(const std::string& url,
-                                 RenderFrameHost* frame) {
-    GURL gurl(url);
-    auto navigation_simulator =
-        NavigationSimulator::CreateRendererInitiated(gurl, frame);
-    navigation_simulator->Commit();
-    return navigation_simulator->GetFinalRenderFrameHost();
-  }
-
-  // Returns the final RenderFrameHost after navigation commits.
-  RenderFrameHost* NavigateMainFrame(const std::string& url) {
-    return NavigateFrame(url, web_contents()->GetMainFrame());
-  }
-
-  // Returns the final RenderFrameHost after navigation commits.
-  RenderFrameHost* CreateAndNavigatePopup(const std::string& url,
-                                          const std::string& frame_name,
-                                          RenderFrameHost* parent) {
-    RenderFrameHost* popup_opener_frame =
-        RenderFrameHostTester::For(parent)->AppendChild(frame_name);
-    RenderFrameHost* final_frame_host = NavigateFrame(url, popup_opener_frame);
-    // Call the trigger's PopupWasBlocked event handler directly since it
-    // doesn't happen as part of the navigation. This should check if the frame
-    // opening the popup is an ad.
-    safe_browsing::AdPopupTrigger::FromWebContents(web_contents())
-        ->PopupWasBlocked(final_frame_host);
-    return final_frame_host;
-  }
-
-  void WaitForTaskRunnerIdle() {
-    task_runner_->RunUntilIdle();
-    base::RunLoop().RunUntilIdle();
-  }
-
-  MockTriggerManager* get_trigger_manager() { return &trigger_manager_; }
-  base::HistogramTester* get_histograms() { return &histograms_; }
-
- private:
-  TestingPrefServiceSimple prefs_;
-  MockTriggerManager trigger_manager_;
-  base::HistogramTester histograms_;
-  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
-};
-
-TEST_F(AdPopupTriggerTest, PopupWithAds) {
-  // Make sure the trigger fires when there are ads on the page.
-  CreateTrigger();
-  EXPECT_CALL(*get_trigger_manager(),
-              StartCollectingThreatDetailsWithReason(
-                  TriggerType::AD_POPUP, web_contents(), _, _, _, _, _))
-      .Times(1)
-      .WillRepeatedly(Return(true));
-  EXPECT_CALL(*get_trigger_manager(),
-              FinishCollectingThreatDetails(TriggerType::AD_POPUP,
-                                            web_contents(), _, _, _, _))
-      .Times(1);
-
-  // This page contains two popups - one originating from an ad subframe and one
-  // from a non ad subframe.
-  RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
-  CreateAndNavigatePopup(kNonAdUrl, kNonAdName, main_frame);
-  CreateAndNavigatePopup(kNonAdUrl, kAdName, main_frame);
-
-  // Wait for any posted tasks to finish.
-  WaitForTaskRunnerIdle();
-
-  // Two popup navigations, one will cause a report
-  get_histograms()->ExpectBucketCount(kAdPopupTriggerActionMetricName,
-                                      AdPopupTriggerAction::POPUP_CHECK, 2);
-  get_histograms()->ExpectBucketCount(kAdPopupTriggerActionMetricName,
-                                      AdPopupTriggerAction::POPUP_REPORTED, 1);
-  get_histograms()->ExpectBucketCount(kAdPopupTriggerActionMetricName,
-                                      AdPopupTriggerAction::POPUP_NO_GOOGLE_AD,
-                                      1);
-}
-
-// TODO(https://crbug.com/1009917): Fix flakes on Windows bots.
-#if defined(OS_WIN)
-#define MAYBE_ReportRejectedByTriggerManager \
-  DISABLED_ReportRejectedByTriggerManager
-#else
-#define MAYBE_ReportRejectedByTriggerManager ReportRejectedByTriggerManager
-#endif
-TEST_F(AdPopupTriggerTest, MAYBE_ReportRejectedByTriggerManager) {
-  // If the trigger manager rejects the report, we don't try to finish/send the
-  // report.
-  CreateTrigger();
-  EXPECT_CALL(*get_trigger_manager(),
-              StartCollectingThreatDetailsWithReason(_, _, _, _, _, _, _))
-      .Times(2);
-  EXPECT_CALL(*get_trigger_manager(),
-              FinishCollectingThreatDetails(_, _, _, _, _, _))
-      .Times(0);
-
-  RenderFrameHost* main_frame = NavigateMainFrame(kAdUrl);
-  CreateAndNavigatePopup(kAdUrl, kAdName, main_frame);
-  CreateAndNavigatePopup(kNonAdUrl, kAdName, main_frame);
-
-  WaitForTaskRunnerIdle();
-
-  get_histograms()->ExpectBucketCount(kAdPopupTriggerActionMetricName,
-                                      AdPopupTriggerAction::POPUP_CHECK, 2);
-  get_histograms()->ExpectBucketCount(kAdPopupTriggerActionMetricName,
-                                      AdPopupTriggerAction::POPUP_REPORTED, 0);
-  get_histograms()->ExpectBucketCount(
-      kAdPopupTriggerActionMetricName,
-      AdPopupTriggerAction::POPUP_COULD_NOT_START_REPORT, 2);
-}
-
-TEST_F(AdPopupTriggerTest, DISABLED_PopupWithNoAds) {
-  // Make sure the trigger doesn't fire when there are no ads on the page.
-  CreateTrigger();
-  EXPECT_CALL(*get_trigger_manager(),
-              StartCollectingThreatDetailsWithReason(_, _, _, _, _, _, _))
-      .Times(0);
-  EXPECT_CALL(*get_trigger_manager(),
-              FinishCollectingThreatDetails(_, _, _, _, _, _))
-      .Times(0);
-
-  RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
-  CreateAndNavigatePopup(kNonAdUrl, kNonAdName, main_frame);
-  CreateAndNavigatePopup(kNonAdUrl, kNonAdName, main_frame);
-
-  get_histograms()->ExpectBucketCount(kAdPopupTriggerActionMetricName,
-                                      AdPopupTriggerAction::POPUP_CHECK, 2);
-  get_histograms()->ExpectBucketCount(kAdPopupTriggerActionMetricName,
-                                      AdPopupTriggerAction::POPUP_NO_GOOGLE_AD,
-                                      2);
-}
-
-TEST_F(AdPopupTriggerTest, PopupNotFromAdForPageWithAd) {
-  // Make sure that no report is generated when there is an ad on the page but
-  // the popup is caused from a different frame.
-  CreateTrigger();
-  EXPECT_CALL(*get_trigger_manager(),
-              StartCollectingThreatDetailsWithReason(_, _, _, _, _, _, _))
-      .Times(0);
-  EXPECT_CALL(*get_trigger_manager(),
-              FinishCollectingThreatDetails(_, _, _, _, _, _))
-      .Times(0);
-
-  RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
-  RenderFrameHostTester::For(main_frame)->AppendChild(kAdName);
-  CreateAndNavigatePopup(kNonAdUrl, kNonAdName, main_frame);
-
-  // Two navigations (main frame, one subframe), each with no ad.
-  get_histograms()->ExpectBucketCount(kAdPopupTriggerActionMetricName,
-                                      AdPopupTriggerAction::POPUP_CHECK, 1);
-  get_histograms()->ExpectBucketCount(kAdPopupTriggerActionMetricName,
-                                      AdPopupTriggerAction::POPUP_NO_GOOGLE_AD,
-                                      1);
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/triggers/ad_redirect_trigger.cc b/components/safe_browsing/triggers/ad_redirect_trigger.cc
deleted file mode 100644
index a70f466..0000000
--- a/components/safe_browsing/triggers/ad_redirect_trigger.cc
+++ /dev/null
@@ -1,153 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/triggers/ad_redirect_trigger.h"
-
-#include <string>
-
-#include "base/bind.h"
-#include "base/feature_list.h"
-#include "base/metrics/field_trial_params.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/rand_util.h"
-#include "base/single_thread_task_runner.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/task/post_task.h"
-#include "components/safe_browsing/features.h"
-#include "components/safe_browsing/triggers/trigger_manager.h"
-#include "components/safe_browsing/triggers/trigger_throttler.h"
-#include "components/safe_browsing/triggers/trigger_util.h"
-#include "components/security_interstitials/content/unsafe_resource.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/render_process_host.h"
-#include "content/public/browser/web_contents.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "services/network/public/cpp/simple_url_loader.h"
-
-namespace safe_browsing {
-
-// Number of milliseconds to allow data collection to run before sending a
-// report (since this trigger runs in the background).
-const int64_t kAdRedirectCollectionPeriodMilliseconds = 5000;
-
-// Range of number of milliseconds to wait after a page finished loading before
-// starting a report. Allows ads which load in the background to finish loading.
-const int64_t kMaxAdRedirectCollectionStartDelayMilliseconds = 5000;
-const int64_t kMinAdRedirectCollectionStartDelayMilliseconds = 500;
-
-// Metric for tracking what the Ad Redirect trigger does on each navigation.
-const char kAdRedirectTriggerActionMetricName[] =
-    "SafeBrowsing.Triggers.AdRedirect.Action";
-
-namespace {
-
-void RecordAdRedirectTriggerAction(AdRedirectTriggerAction action) {
-  UMA_HISTOGRAM_ENUMERATION(kAdRedirectTriggerActionMetricName, action);
-}
-
-}  // namespace
-
-AdRedirectTrigger::AdRedirectTrigger(
-    content::WebContents* web_contents,
-    TriggerManager* trigger_manager,
-    PrefService* prefs,
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    history::HistoryService* history_service)
-    : web_contents_(web_contents),
-      start_report_delay_ms_(
-          base::RandInt(kMinAdRedirectCollectionStartDelayMilliseconds,
-                        kMaxAdRedirectCollectionStartDelayMilliseconds)),
-      finish_report_delay_ms_(kAdRedirectCollectionPeriodMilliseconds),
-      trigger_manager_(trigger_manager),
-      prefs_(prefs),
-      url_loader_factory_(url_loader_factory),
-      history_service_(history_service),
-      task_runner_(
-          base::CreateSingleThreadTaskRunner({content::BrowserThread::UI})) {}
-
-AdRedirectTrigger::~AdRedirectTrigger() {}
-
-// static
-void AdRedirectTrigger::CreateForWebContents(
-    content::WebContents* web_contents,
-    TriggerManager* trigger_manager,
-    PrefService* prefs,
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    history::HistoryService* history_service) {
-  DCHECK(web_contents);
-  if (!FromWebContents(web_contents)) {
-    web_contents->SetUserData(UserDataKey(),
-                              base::WrapUnique(new AdRedirectTrigger(
-                                  web_contents, trigger_manager, prefs,
-                                  url_loader_factory, history_service)));
-  }
-}
-
-void AdRedirectTrigger::CreateAdRedirectReport() {
-  SBErrorOptions error_options =
-      TriggerManager::GetSBErrorDisplayOptions(*prefs_, web_contents_);
-  security_interstitials::UnsafeResource resource;
-  resource.threat_type = SB_THREAT_TYPE_BLOCKED_AD_REDIRECT;
-  resource.url = web_contents_->GetURL();
-  resource.web_contents_getter = resource.GetWebContentsGetter(
-      web_contents_->GetMainFrame()->GetProcess()->GetID(),
-      web_contents_->GetMainFrame()->GetRoutingID());
-  TriggerManagerReason reason;
-  if (!trigger_manager_->StartCollectingThreatDetailsWithReason(
-          TriggerType::AD_REDIRECT, web_contents_, resource,
-          url_loader_factory_, history_service_, error_options, &reason)) {
-    if (reason == TriggerManagerReason::DAILY_QUOTA_EXCEEDED) {
-      RecordAdRedirectTriggerAction(
-          AdRedirectTriggerAction::REDIRECT_DAILY_QUOTA_EXCEEDED);
-    } else {
-      RecordAdRedirectTriggerAction(
-          AdRedirectTriggerAction::REDIRECT_COULD_NOT_START_REPORT);
-    }
-    return;
-  }
-  // Call into TriggerManager to finish the reports after a short delay. Any
-  // ads that are detected during this delay will be rejected by TriggerManager
-  // because a report is already being collected, so we won't send multiple
-  // reports for the same page.
-  task_runner_->PostDelayedTask(
-      FROM_HERE,
-      base::BindOnce(
-          IgnoreResult(&TriggerManager::FinishCollectingThreatDetails),
-          base::Unretained(trigger_manager_), TriggerType::AD_REDIRECT,
-          base::Unretained(web_contents_), base::TimeDelta(),
-          /*did_proceed=*/false, /*num_visits=*/0, error_options),
-      base::TimeDelta::FromMilliseconds(finish_report_delay_ms_));
-  RecordAdRedirectTriggerAction(AdRedirectTriggerAction::AD_REDIRECT);
-}
-
-void AdRedirectTrigger::OnDidBlockNavigation(const GURL& initiator_url) {
-  RecordAdRedirectTriggerAction(AdRedirectTriggerAction::REDIRECT_CHECK);
-  content::RenderFrameHost* initiator_frame =
-      web_contents_->GetOriginalOpener();
-  // Use focused frame as proxy if there is no opener.
-  if (!initiator_frame)
-    initiator_frame = web_contents_->GetFocusedFrame();
-  if (!DetectGoogleAd(initiator_frame, initiator_url)) {
-    RecordAdRedirectTriggerAction(
-        AdRedirectTriggerAction::REDIRECT_NO_GOOGLE_AD);
-    return;
-  }
-  // Create a report after a short delay.
-  task_runner_->PostDelayedTask(
-      FROM_HERE,
-      base::BindOnce(&AdRedirectTrigger::CreateAdRedirectReport,
-                     weak_ptr_factory_.GetWeakPtr()),
-      base::TimeDelta::FromMilliseconds(start_report_delay_ms_));
-}
-
-void AdRedirectTrigger::SetDelayForTest(int start_report_delay,
-                                        int finish_report_delay) {
-  start_report_delay_ms_ = start_report_delay;
-  finish_report_delay_ms_ = finish_report_delay;
-}
-
-WEB_CONTENTS_USER_DATA_KEY_IMPL(AdRedirectTrigger)
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/triggers/ad_redirect_trigger.h b/components/safe_browsing/triggers/ad_redirect_trigger.h
deleted file mode 100644
index 1a131108..0000000
--- a/components/safe_browsing/triggers/ad_redirect_trigger.h
+++ /dev/null
@@ -1,131 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-
-#ifndef COMPONENTS_SAFE_BROWSING_TRIGGERS_AD_REDIRECT_TRIGGER_H_
-#define COMPONENTS_SAFE_BROWSING_TRIGGERS_AD_REDIRECT_TRIGGER_H_
-
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "content/public/browser/web_contents_user_data.h"
-
-class PrefService;
-
-namespace history {
-class HistoryService;
-}
-
-namespace network {
-class SharedURLLoaderFactory;
-}  // namespace network
-
-namespace safe_browsing {
-class TriggerManager;
-
-using FrameTreeNodeId = int;
-
-// Metric for tracking what the Ad Redirect trigger does on each navigation.
-extern const char kAdRedirectTriggerActionMetricName[];
-
-// Actions performed by this trigger. These values are written to logs. New enum
-// values can be added, but existing enums must never be renumbered or deleted
-// and reused.
-enum class AdRedirectTriggerAction {
-  // A redirect event occurred that caused the trigger to perform its checks.
-  REDIRECT_CHECK = 0,
-  // A redirect caused by an ad was detected and a report was collected.
-  AD_REDIRECT = 1,
-  // No google ad was detected from the frame causing an autoredirect.
-  REDIRECT_NO_GOOGLE_AD = 2,
-  // An ad was detected on the page causing the redirect and could have been
-  // reported, but the trigger manager rejected the report (eg: because user is
-  // incognito or has not opted into extended reporting).
-  REDIRECT_COULD_NOT_START_REPORT = 3,
-  // Daily quota for blocked ad redirect reporting was met.
-  REDIRECT_DAILY_QUOTA_EXCEEDED = 4,
-  // New events must be added before kMaxValue, and the value of kMaxValue
-  // updated.
-  kMaxValue = REDIRECT_DAILY_QUOTA_EXCEEDED
-};
-
-// This class if notified when a redirect navigation is blocked in the renderer
-// because there was no user gesture(an autoredirect). If the frame causing the
-// autoredirect navigation is a Google Ad frame, this class sends a report to
-// Google.
-// Design doc: go/extending-chrind-q2-2019-1
-class AdRedirectTrigger
-    : public content::WebContentsUserData<AdRedirectTrigger> {
- public:
-  ~AdRedirectTrigger() override;
-
-  // |web_contents| is the WebContent of the page that a redirect event took
-  // place on. |trigger_manager| ensures the trigger is only fired when
-  // appropriate and keeps track of how often the trigger is fired. |prefs|
-  // allows us to check that the user has opted in to Extended Reporting.
-  // |url_loader_factory| allows us to access mojom::URLLoaderFactory.
-  // |history_service| records page titles, visit times, and favicons, as well
-  // as information about downloads.
-  static void CreateForWebContents(
-      content::WebContents* web_contents,
-      TriggerManager* trigger_manager,
-      PrefService* prefs,
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      history::HistoryService* history_service);
-
-  // This method is called on the browser thread when a frame tries to navigate
-  // its top level frame from the |initiator_url| without ever having received a
-  // user gesture. If the frame causing the redirect navigation is a Google Ad
-  // frame a report is sent to Google.
-  void OnDidBlockNavigation(const GURL& initiator_url);
-
- private:
-  friend class AdRedirectTriggerBrowserTest;
-  friend class content::WebContentsUserData<AdRedirectTrigger>;
-
-  AdRedirectTrigger(
-      content::WebContents* web_contents,
-      TriggerManager* trigger_manager,
-      PrefService* prefs,
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      history::HistoryService* history_service);
-
-  // Called to create an ad redirect report.
-  void CreateAdRedirectReport();
-
-  // Sets report delay for test.
-  void SetDelayForTest(int start_report_delay, int finish_report_delay);
-
-  // WebContents of the current tab.
-  content::WebContents* web_contents_;
-
-  // The delay (in milliseconds) to wait before starting a report. Can be
-  // ovewritten for tests.
-  int64_t start_report_delay_ms_;
-
-  // The delay (in milliseconds) to wait before finishing a report. Can be
-  // overwritten for tests.
-  int64_t finish_report_delay_ms_;
-
-  // TriggerManager gets called if this trigger detects an autoredirect caused
-  // by a page with an ad and wants to collect some data about it. Not owned.
-  TriggerManager* trigger_manager_;
-
-  PrefService* prefs_;
-  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
-  history::HistoryService* history_service_;
-
-  // Task runner for posting delayed tasks. Normally set to the runner for the
-  // UI thread, but can be overwritten for tests.
-  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
-
-  base::WeakPtrFactory<AdRedirectTrigger> weak_ptr_factory_{this};
-
-  WEB_CONTENTS_USER_DATA_KEY_DECL();
-
-  DISALLOW_COPY_AND_ASSIGN(AdRedirectTrigger);
-};
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_TRIGGERS_AD_REDIRECT_TRIGGER_H_
diff --git a/components/safe_browsing/triggers/ad_sampler_trigger.cc b/components/safe_browsing/triggers/ad_sampler_trigger.cc
deleted file mode 100644
index c6c71ff..0000000
--- a/components/safe_browsing/triggers/ad_sampler_trigger.cc
+++ /dev/null
@@ -1,190 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/triggers/ad_sampler_trigger.h"
-
-#include <string>
-
-#include "base/bind.h"
-#include "base/feature_list.h"
-#include "base/metrics/field_trial_params.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/rand_util.h"
-#include "base/single_thread_task_runner.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/task/post_task.h"
-#include "components/safe_browsing/features.h"
-#include "components/safe_browsing/triggers/trigger_manager.h"
-#include "components/safe_browsing/triggers/trigger_throttler.h"
-#include "components/safe_browsing/triggers/trigger_util.h"
-#include "components/security_interstitials/content/unsafe_resource.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/navigation_handle.h"
-#include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/render_process_host.h"
-#include "content/public/browser/web_contents.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "services/network/public/cpp/simple_url_loader.h"
-
-namespace safe_browsing {
-
-// Param name of the denominator for controlling sampling frequency.
-const char kAdSamplerFrequencyDenominatorParam[] =
-    "safe_browsing_ad_sampler_frequency_denominator";
-
-// Default frequency denominator for the ad sampler.
-const size_t kAdSamplerDefaultFrequency = 1000;
-
-// A frequency denominator with this value indicates sampling is disabled.
-const size_t kAdSamplerFrequencyDisabled = 0;
-
-// Number of milliseconds to allow data collection to run before sending a
-// report (since this trigger runs in the background).
-const int64_t kAdSampleCollectionPeriodMilliseconds = 5000;
-
-// Range of number of milliseconds to wait after a page finished loading before
-// starting a report. Allows ads which load in the background to finish loading.
-const int64_t kMaxAdSampleCollectionStartDelayMilliseconds = 5000;
-const int64_t kMinAdSampleCollectionStartDelayMilliseconds = 500;
-
-// Metric for tracking what the Ad Sampler trigger does on each navigation.
-const char kAdSamplerTriggerActionMetricName[] =
-    "SafeBrowsing.Triggers.AdSampler.Action";
-
-namespace {
-
-size_t GetSamplerFrequencyDenominator() {
-  if (!base::FeatureList::IsEnabled(kAdSamplerTriggerFeature))
-    return kAdSamplerDefaultFrequency;
-
-  const std::string sampler_frequency_denominator =
-      base::GetFieldTrialParamValueByFeature(
-          kAdSamplerTriggerFeature, kAdSamplerFrequencyDenominatorParam);
-  int result;
-  if (!base::StringToInt(sampler_frequency_denominator, &result))
-    return kAdSamplerDefaultFrequency;
-
-  return result;
-}
-
-bool ShouldSampleAd(const size_t frequency_denominator) {
-  return frequency_denominator != kAdSamplerFrequencyDisabled &&
-         (base::RandUint64() % frequency_denominator) == 0;
-}
-
-}  // namespace
-
-AdSamplerTrigger::AdSamplerTrigger(
-    content::WebContents* web_contents,
-    TriggerManager* trigger_manager,
-    PrefService* prefs,
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    history::HistoryService* history_service)
-    : content::WebContentsObserver(web_contents),
-      sampler_frequency_denominator_(GetSamplerFrequencyDenominator()),
-      start_report_delay_ms_(
-          base::RandInt(kMinAdSampleCollectionStartDelayMilliseconds,
-                        kMaxAdSampleCollectionStartDelayMilliseconds)),
-      finish_report_delay_ms_(kAdSampleCollectionPeriodMilliseconds),
-      trigger_manager_(trigger_manager),
-      prefs_(prefs),
-      url_loader_factory_(url_loader_factory),
-      history_service_(history_service),
-      task_runner_(
-          base::CreateSingleThreadTaskRunner({content::BrowserThread::UI})) {}
-
-AdSamplerTrigger::~AdSamplerTrigger() {}
-
-// static
-void AdSamplerTrigger::CreateForWebContents(
-    content::WebContents* web_contents,
-    TriggerManager* trigger_manager,
-    PrefService* prefs,
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    history::HistoryService* history_service) {
-  DCHECK(web_contents);
-  if (!FromWebContents(web_contents)) {
-    web_contents->SetUserData(UserDataKey(),
-                              base::WrapUnique(new AdSamplerTrigger(
-                                  web_contents, trigger_manager, prefs,
-                                  url_loader_factory, history_service)));
-  }
-}
-
-void AdSamplerTrigger::DidFinishLoad(
-    content::RenderFrameHost* render_frame_host,
-    const GURL& validated_url) {
-  UMA_HISTOGRAM_ENUMERATION(kAdSamplerTriggerActionMetricName, TRIGGER_CHECK,
-                            MAX_ACTIONS);
-  // We are using light-weight ad detection logic here so it's safe to do the
-  // check on each navigation for the sake of metrics.
-  if (!DetectGoogleAd(render_frame_host, validated_url)) {
-    UMA_HISTOGRAM_ENUMERATION(kAdSamplerTriggerActionMetricName,
-                              NO_SAMPLE_NO_AD, MAX_ACTIONS);
-    return;
-  }
-  if (!ShouldSampleAd(sampler_frequency_denominator_)) {
-    UMA_HISTOGRAM_ENUMERATION(kAdSamplerTriggerActionMetricName,
-                              NO_SAMPLE_AD_SKIPPED_FOR_FREQUENCY, MAX_ACTIONS);
-    return;
-  }
-
-  // Create a report after a short delay. The delay gives more time for ads to
-  // finish loading in the background. This is best-effort.
-  task_runner_->PostDelayedTask(
-      FROM_HERE,
-      base::BindOnce(&AdSamplerTrigger::CreateAdSampleReport,
-                     weak_ptr_factory_.GetWeakPtr()),
-      base::TimeDelta::FromMilliseconds(start_report_delay_ms_));
-}
-
-void AdSamplerTrigger::CreateAdSampleReport() {
-  SBErrorOptions error_options =
-      TriggerManager::GetSBErrorDisplayOptions(*prefs_, web_contents());
-
-  security_interstitials::UnsafeResource resource;
-  resource.threat_type = SB_THREAT_TYPE_AD_SAMPLE;
-  resource.url = web_contents()->GetURL();
-  resource.web_contents_getter = resource.GetWebContentsGetter(
-      web_contents()->GetMainFrame()->GetProcess()->GetID(),
-      web_contents()->GetMainFrame()->GetRoutingID());
-
-  if (!trigger_manager_->StartCollectingThreatDetails(
-          TriggerType::AD_SAMPLE, web_contents(), resource, url_loader_factory_,
-          history_service_, error_options)) {
-    UMA_HISTOGRAM_ENUMERATION(kAdSamplerTriggerActionMetricName,
-                              NO_SAMPLE_COULD_NOT_START_REPORT, MAX_ACTIONS);
-    return;
-  }
-
-  // Call into TriggerManager to finish the reports after a short delay. Any
-  // ads that are detected during this delay will be rejected by TriggerManager
-  // because a report is already being collected, so we won't send multiple
-  // reports for the same page.
-  task_runner_->PostDelayedTask(
-      FROM_HERE,
-      base::BindOnce(
-          IgnoreResult(&TriggerManager::FinishCollectingThreatDetails),
-          base::Unretained(trigger_manager_), TriggerType::AD_SAMPLE,
-          base::Unretained(web_contents()), base::TimeDelta(),
-          /*did_proceed=*/false, /*num_visits=*/0, error_options),
-      base::TimeDelta::FromMilliseconds(finish_report_delay_ms_));
-
-  UMA_HISTOGRAM_ENUMERATION(kAdSamplerTriggerActionMetricName, AD_SAMPLED,
-                            MAX_ACTIONS);
-}
-
-void AdSamplerTrigger::SetSamplerFrequencyForTest(size_t denominator) {
-  sampler_frequency_denominator_ = denominator;
-}
-
-void AdSamplerTrigger::SetTaskRunnerForTest(
-    scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
-  task_runner_ = task_runner;
-}
-
-WEB_CONTENTS_USER_DATA_KEY_IMPL(AdSamplerTrigger)
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/triggers/ad_sampler_trigger.h b/components/safe_browsing/triggers/ad_sampler_trigger.h
deleted file mode 100644
index 5645e01..0000000
--- a/components/safe_browsing/triggers/ad_sampler_trigger.h
+++ /dev/null
@@ -1,131 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_TRIGGERS_AD_SAMPLER_TRIGGER_H_
-#define COMPONENTS_SAFE_BROWSING_TRIGGERS_AD_SAMPLER_TRIGGER_H_
-
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "content/public/browser/web_contents_observer.h"
-#include "content/public/browser/web_contents_user_data.h"
-
-class PrefService;
-
-namespace history {
-class HistoryService;
-}
-
-namespace network {
-class SharedURLLoaderFactory;
-}  // namespace network
-
-namespace safe_browsing {
-class TriggerManager;
-
-// Param name of the denominator for controlling sampling frequency.
-extern const char kAdSamplerFrequencyDenominatorParam[];
-
-// Default frequency for the ad sampler, if not configured in Finch.
-extern const size_t kAdSamplerDefaultFrequency;
-
-// A frequency denominator with this value indicates sampling is disabled.
-extern const size_t kAdSamplerFrequencyDisabled;
-
-// Metric for tracking what the Ad Sampler trigger does on each navigation.
-extern const char kAdSamplerTriggerActionMetricName[];
-
-// Actions performed by this trigger. These values are written to logs. New enum
-// values can be added, but existing enums must never be renumbered or deleted
-// and reused.
-enum AdSamplerTriggerAction {
-  // An event occurred that caused the trigger to perform its checks.
-  TRIGGER_CHECK = 0,
-  // An ad was detected and a sample was collected.
-  AD_SAMPLED = 1,
-  // An ad was detected but no sample was taken to honour sampling frequency.
-  NO_SAMPLE_AD_SKIPPED_FOR_FREQUENCY = 2,
-  // No ad was detected.
-  NO_SAMPLE_NO_AD = 3,
-  // An ad was detected and could have been sampled, but the trigger manager
-  // rejected the report (eg: because a report was already in progress).
-  NO_SAMPLE_COULD_NOT_START_REPORT = 4,
-  // New actions must be added before MAX_ACTIONS.
-  MAX_ACTIONS
-};
-
-// This class periodically checks for Google ads on the page and may decide to
-// send a report to Google with the ad's structure for further analysis.
-class AdSamplerTrigger : public content::WebContentsObserver,
-                         public content::WebContentsUserData<AdSamplerTrigger> {
- public:
-  ~AdSamplerTrigger() override;
-
-  static void CreateForWebContents(
-      content::WebContents* web_contents,
-      TriggerManager* trigger_manager,
-      PrefService* prefs,
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      history::HistoryService* history_service);
-
-  // content::WebContentsObserver implementation.
-  void DidFinishLoad(content::RenderFrameHost* render_frame_host,
-                     const GURL& validated_url) override;
-
- private:
-  friend class AdSamplerTriggerTest;
-  friend class content::WebContentsUserData<AdSamplerTrigger>;
-  FRIEND_TEST_ALL_PREFIXES(AdSamplerTriggerTestFinch,
-                           FrequencyDenominatorFeature);
-
-  AdSamplerTrigger(
-      content::WebContents* web_contents,
-      TriggerManager* trigger_manager,
-      PrefService* prefs,
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      history::HistoryService* history_service);
-
-  // Called to create an ad sample report.
-  void CreateAdSampleReport();
-
-  // Sets |sampler_frequency_denominator_| for tests.
-  void SetSamplerFrequencyForTest(size_t denominator);
-
-  // Sets a task runner to use for tests.
-  void SetTaskRunnerForTest(
-      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
-
-  // Ad samples will be collected with frequency
-  // 1/|sampler_frequency_denominator_|
-  size_t sampler_frequency_denominator_;
-
-  // The delay (in milliseconds) to wait before starting a report. Can be
-  // ovewritten for tests.
-  int64_t start_report_delay_ms_;
-
-  // The delay (in milliseconds) to wait before finishing a report. Can be
-  // overwritten for tests.
-  int64_t finish_report_delay_ms_;
-
-  // TriggerManager gets called if this trigger detects an ad and wants to
-  // collect some data about it. Not owned.
-  TriggerManager* trigger_manager_;
-
-  PrefService* prefs_;
-  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
-  history::HistoryService* history_service_;
-
-  // Task runner for posting delayed tasks. Normally set to the runner for the
-  // UI thread, but can be overwritten for tests.
-  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
-
-  base::WeakPtrFactory<AdSamplerTrigger> weak_ptr_factory_{this};
-
-  WEB_CONTENTS_USER_DATA_KEY_DECL();
-
-  DISALLOW_COPY_AND_ASSIGN(AdSamplerTrigger);
-};
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_TRIGGERS_AD_SAMPLER_TRIGGER_H_
diff --git a/components/safe_browsing/triggers/ad_sampler_trigger_unittest.cc b/components/safe_browsing/triggers/ad_sampler_trigger_unittest.cc
deleted file mode 100644
index 02a40d28..0000000
--- a/components/safe_browsing/triggers/ad_sampler_trigger_unittest.cc
+++ /dev/null
@@ -1,235 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/triggers/ad_sampler_trigger.h"
-
-#include "base/test/metrics/histogram_tester.h"
-#include "base/test/scoped_feature_list.h"
-#include "base/test/test_simple_task_runner.h"
-#include "components/prefs/testing_pref_service.h"
-#include "components/safe_browsing/features.h"
-#include "components/safe_browsing/triggers/mock_trigger_manager.h"
-#include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/test/browser_task_environment.h"
-#include "content/public/test/navigation_simulator.h"
-#include "content/public/test/test_renderer_host.h"
-#include "testing/gmock/include/gmock/gmock-generated-function-mockers.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using content::NavigationSimulator;
-using content::RenderFrameHost;
-using content::RenderFrameHostTester;
-
-using testing::_;
-using testing::Return;
-
-namespace safe_browsing {
-
-namespace {
-const char kAdUrl[] = "https://tpc.googlesyndication.com/safeframe/1";
-const char kNonAdUrl[] = "https://foo.com/";
-const char kAdName[] = "google_ads_iframe_1";
-const char kNonAdName[] = "foo";
-}  // namespace
-
-class AdSamplerTriggerTest : public content::RenderViewHostTestHarness {
- public:
-  AdSamplerTriggerTest() : task_runner_(new base::TestSimpleTaskRunner) {}
-  ~AdSamplerTriggerTest() override {}
-
-  void SetUp() override {
-    content::RenderViewHostTestHarness::SetUp();
-
-    // Enable any prefs required for the trigger to run.
-    safe_browsing::RegisterProfilePrefs(prefs_.registry());
-    prefs_.SetBoolean(prefs::kSafeBrowsingExtendedReportingOptInAllowed, true);
-    prefs_.SetBoolean(prefs::kSafeBrowsingScoutReportingEnabled, true);
-  }
-
-  void CreateTriggerWithFrequency(const size_t denominator) {
-    safe_browsing::AdSamplerTrigger::CreateForWebContents(
-        web_contents(), &trigger_manager_, &prefs_, nullptr, nullptr);
-
-    safe_browsing::AdSamplerTrigger* ad_sampler =
-        safe_browsing::AdSamplerTrigger::FromWebContents(web_contents());
-    ad_sampler->SetSamplerFrequencyForTest(denominator);
-
-    // Give the trigger a test task runner that we can synchronize on.
-    ad_sampler->SetTaskRunnerForTest(task_runner_);
-  }
-
-  // Returns the final RenderFrameHost after navigation commits.
-  RenderFrameHost* NavigateFrame(const std::string& url,
-                                 RenderFrameHost* frame) {
-    return NavigationSimulator::NavigateAndCommitFromDocument(GURL(url), frame);
-  }
-
-  // Returns the final RenderFrameHost after navigation commits.
-  RenderFrameHost* NavigateMainFrame(const std::string& url) {
-    return NavigateFrame(url, web_contents()->GetMainFrame());
-  }
-
-  // Returns the final RenderFrameHost after navigation commits.
-  RenderFrameHost* CreateAndNavigateSubFrame(const std::string& url,
-                                             const std::string& frame_name,
-                                             RenderFrameHost* parent) {
-    RenderFrameHost* subframe =
-        RenderFrameHostTester::For(parent)->AppendChild(frame_name);
-    return NavigateFrame(url, subframe);
-  }
-
-  void WaitForTaskRunnerIdle() {
-    task_runner_->RunUntilIdle();
-    base::RunLoop().RunUntilIdle();
-  }
-
-  MockTriggerManager* get_trigger_manager() { return &trigger_manager_; }
-  base::HistogramTester* get_histograms() { return &histograms_; }
-
- private:
-  TestingPrefServiceSimple prefs_;
-  MockTriggerManager trigger_manager_;
-  base::HistogramTester histograms_;
-  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
-};
-
-TEST_F(AdSamplerTriggerTest, TriggerDisabledBySamplingFrequency) {
-  // Make sure the trigger doesn't fire when the sampling frequency is set to
-  // zero, which disables the trigger.
-  CreateTriggerWithFrequency(kAdSamplerFrequencyDisabled);
-  EXPECT_CALL(*get_trigger_manager(),
-              StartCollectingThreatDetails(_, _, _, _, _, _))
-      .Times(0);
-  EXPECT_CALL(*get_trigger_manager(),
-              FinishCollectingThreatDetails(_, _, _, _, _, _))
-      .Times(0);
-
-  // This page contains two ads - one identifiable by its URL, the other by the
-  // name of the frame.
-  RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
-  CreateAndNavigateSubFrame(kAdUrl, kNonAdName, main_frame);
-  CreateAndNavigateSubFrame(kNonAdUrl, kAdName, main_frame);
-
-  // Three navigations (main frame, two subframes). One frame with no ads, and
-  // two skipped ad samples.
-  get_histograms()->ExpectBucketCount(kAdSamplerTriggerActionMetricName,
-                                      TRIGGER_CHECK, 3);
-  get_histograms()->ExpectBucketCount(kAdSamplerTriggerActionMetricName,
-                                      NO_SAMPLE_NO_AD, 1);
-  get_histograms()->ExpectBucketCount(kAdSamplerTriggerActionMetricName,
-                                      NO_SAMPLE_AD_SKIPPED_FOR_FREQUENCY, 2);
-}
-
-TEST_F(AdSamplerTriggerTest, DISABLED_PageWithNoAds) {
-  // Make sure the trigger doesn't fire when there are no ads on the page.
-  CreateTriggerWithFrequency(/*denominator=*/1);
-
-  EXPECT_CALL(*get_trigger_manager(),
-              StartCollectingThreatDetails(_, _, _, _, _, _))
-      .Times(0);
-  EXPECT_CALL(*get_trigger_manager(),
-              FinishCollectingThreatDetails(_, _, _, _, _, _))
-      .Times(0);
-
-  RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
-  CreateAndNavigateSubFrame(kNonAdUrl, kNonAdName, main_frame);
-  CreateAndNavigateSubFrame(kNonAdUrl, kNonAdName, main_frame);
-
-  // Three navigations (main frame, two subframes), each with no ad.
-  get_histograms()->ExpectBucketCount(kAdSamplerTriggerActionMetricName,
-                                      TRIGGER_CHECK, 3);
-  get_histograms()->ExpectBucketCount(kAdSamplerTriggerActionMetricName,
-                                      NO_SAMPLE_NO_AD, 3);
-}
-
-TEST_F(AdSamplerTriggerTest, PageWithMultipleAds) {
-  // Make sure the trigger fires when there are ads on the page. We expect
-  // one call for each ad detected.
-  CreateTriggerWithFrequency(/*denominator=*/1);
-  EXPECT_CALL(*get_trigger_manager(),
-              StartCollectingThreatDetails(TriggerType::AD_SAMPLE,
-                                           web_contents(), _, _, _, _))
-      .Times(2)
-      .WillRepeatedly(Return(true));
-  EXPECT_CALL(*get_trigger_manager(),
-              FinishCollectingThreatDetails(TriggerType::AD_SAMPLE,
-                                            web_contents(), _, _, _, _))
-      .Times(2);
-
-  // This page contains two ads - one identifiable by its URL, the other by the
-  // name of the frame.
-  RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
-  CreateAndNavigateSubFrame(kAdUrl, kNonAdName, main_frame);
-  CreateAndNavigateSubFrame(kNonAdUrl, kAdName, main_frame);
-
-  // Wait for any posted tasks to finish.
-  WaitForTaskRunnerIdle();
-
-  // Three navigations (main frame, two subframes). Main frame with no ads, and
-  // two sampled ads
-  get_histograms()->ExpectBucketCount(kAdSamplerTriggerActionMetricName,
-                                      TRIGGER_CHECK, 3);
-  get_histograms()->ExpectBucketCount(kAdSamplerTriggerActionMetricName,
-                                      NO_SAMPLE_NO_AD, 1);
-  get_histograms()->ExpectBucketCount(kAdSamplerTriggerActionMetricName,
-                                      AD_SAMPLED, 2);
-}
-
-TEST_F(AdSamplerTriggerTest, ReportRejectedByTriggerManager) {
-  // If the trigger manager rejects the report, we don't try to finish/send the
-  // report.
-  CreateTriggerWithFrequency(/*denominator=*/1);
-  EXPECT_CALL(*get_trigger_manager(),
-              StartCollectingThreatDetails(TriggerType::AD_SAMPLE,
-                                           web_contents(), _, _, _, _))
-      .Times(1)
-      .WillOnce(Return(false));
-  EXPECT_CALL(*get_trigger_manager(),
-              FinishCollectingThreatDetails(TriggerType::AD_SAMPLE,
-                                            web_contents(), _, _, _, _))
-      .Times(0);
-
-  // One ad on the page, identified by its URL.
-  RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
-  CreateAndNavigateSubFrame(kAdUrl, kNonAdName, main_frame);
-  CreateAndNavigateSubFrame(kNonAdUrl, kNonAdName, main_frame);
-
-  // Wait for any posted tasks to finish.
-  WaitForTaskRunnerIdle();
-
-  // Three navigations (main frame, two subframes). Two frames with no ads, and
-  // one ad rejected by trigger manager.
-  get_histograms()->ExpectBucketCount(kAdSamplerTriggerActionMetricName,
-                                      TRIGGER_CHECK, 3);
-  get_histograms()->ExpectBucketCount(kAdSamplerTriggerActionMetricName,
-                                      NO_SAMPLE_NO_AD, 2);
-  get_histograms()->ExpectBucketCount(kAdSamplerTriggerActionMetricName,
-                                      NO_SAMPLE_COULD_NOT_START_REPORT, 1);
-}
-
-TEST(AdSamplerTriggerTestFinch, FrequencyDenominatorFeature) {
-  // Make sure that setting the frequency denominator via Finch params works as
-  // expected, and that the default frequency is used when no Finch config is
-  // given.
-  content::BrowserTaskEnvironment task_environment;
-  AdSamplerTrigger trigger_default(nullptr, nullptr, nullptr, nullptr, nullptr);
-  EXPECT_EQ(kAdSamplerDefaultFrequency,
-            trigger_default.sampler_frequency_denominator_);
-
-  const size_t kDenominatorInt = 12345;
-
-  base::FieldTrialParams feature_params;
-  feature_params[std::string(
-      safe_browsing::kAdSamplerFrequencyDenominatorParam)] =
-      base::NumberToString(kDenominatorInt);
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeatureWithParameters(
-      safe_browsing::kAdSamplerTriggerFeature, feature_params);
-
-  AdSamplerTrigger trigger_finch(nullptr, nullptr, nullptr, nullptr, nullptr);
-  EXPECT_EQ(kDenominatorInt, trigger_finch.sampler_frequency_denominator_);
-}
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/triggers/mock_trigger_manager.cc b/components/safe_browsing/triggers/mock_trigger_manager.cc
deleted file mode 100644
index 4443c26..0000000
--- a/components/safe_browsing/triggers/mock_trigger_manager.cc
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/triggers/mock_trigger_manager.h"
-
-namespace safe_browsing {
-
-MockTriggerManager::MockTriggerManager()
-    : TriggerManager(nullptr, nullptr, nullptr) {}
-
-MockTriggerManager::~MockTriggerManager() {}
-
-}  // namespace safe_browsing
\ No newline at end of file
diff --git a/components/safe_browsing/triggers/mock_trigger_manager.h b/components/safe_browsing/triggers/mock_trigger_manager.h
deleted file mode 100644
index 8fb7a41..0000000
--- a/components/safe_browsing/triggers/mock_trigger_manager.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_TRIGGERS_MOCK_TRIGGER_MANAGER_H_
-#define COMPONENTS_SAFE_BROWSING_TRIGGERS_MOCK_TRIGGER_MANAGER_H_
-
-#include "base/macros.h"
-#include "components/safe_browsing/triggers/trigger_manager.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "testing/gmock/include/gmock/gmock.h"
-
-namespace safe_browsing {
-
-class MockTriggerManager : public TriggerManager {
- public:
-  MockTriggerManager();
-  ~MockTriggerManager() override;
-
-  MOCK_METHOD6(
-      StartCollectingThreatDetails,
-      bool(TriggerType trigger_type,
-           content::WebContents* web_contents,
-           const security_interstitials::UnsafeResource& resource,
-           scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-           history::HistoryService* history_service,
-           const SBErrorOptions& error_display_options));
-  MOCK_METHOD7(
-      StartCollectingThreatDetailsWithReason,
-      bool(TriggerType trigger_type,
-           content::WebContents* web_contents,
-           const security_interstitials::UnsafeResource& resource,
-           scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-           history::HistoryService* history_service,
-           const SBErrorOptions& error_display_options,
-           TriggerManagerReason* out_reason));
-
-  MOCK_METHOD6(FinishCollectingThreatDetails,
-               bool(TriggerType trigger_type,
-                    content::WebContents* web_contents,
-                    const base::TimeDelta& delay,
-                    bool did_proceed,
-                    int num_visits,
-                    const SBErrorOptions& error_display_options));
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockTriggerManager);
-};
-
-}  // namespace safe_browsing
-
-#endif  // MOCK_TRIGGER_MANAGER_H_
diff --git a/components/safe_browsing/triggers/suspicious_site_trigger.cc b/components/safe_browsing/triggers/suspicious_site_trigger.cc
deleted file mode 100644
index a0fc823..0000000
--- a/components/safe_browsing/triggers/suspicious_site_trigger.cc
+++ /dev/null
@@ -1,293 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/triggers/suspicious_site_trigger.h"
-
-#include "base/bind.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/single_thread_task_runner.h"
-#include "base/task/post_task.h"
-#include "components/history/core/browser/history_service.h"
-#include "components/prefs/pref_service.h"
-#include "components/safe_browsing/triggers/trigger_manager.h"
-#include "components/safe_browsing/triggers/trigger_throttler.h"
-#include "components/security_interstitials/content/unsafe_resource.h"
-#include "content/public/browser/browser_context.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/render_process_host.h"
-#include "content/public/browser/web_contents.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-
-namespace safe_browsing {
-
-namespace {
-// Number of milliseconds to allow data collection to run before sending a
-// report (since this trigger runs in the background).
-const int64_t kSuspiciousSiteCollectionPeriodMilliseconds = 5000;
-}  // namespace
-
-const char kSuspiciousSiteTriggerEventMetricName[] =
-    "SafeBrowsing.Triggers.SuspiciousSite.Event";
-
-const char kSuspiciousSiteTriggerReportRejectionMetricName[] =
-    "SafeBrowsing.Triggers.SuspiciousSite.ReportRejectionReason";
-
-const char kSuspiciousSiteTriggerReportDelayStateMetricName[] =
-    "SafeBrowsing.Triggers.SuspiciousSite.DelayTimerState";
-
-void NotifySuspiciousSiteTriggerDetected(
-    const base::RepeatingCallback<content::WebContents*()>&
-        web_contents_getter) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  content::WebContents* web_contents = web_contents_getter.Run();
-  if (web_contents) {
-    safe_browsing::SuspiciousSiteTrigger* trigger =
-        safe_browsing::SuspiciousSiteTrigger::FromWebContents(web_contents);
-    if (trigger)
-      trigger->SuspiciousSiteDetected();
-  }
-}
-
-SuspiciousSiteTrigger::SuspiciousSiteTrigger(
-    content::WebContents* web_contents,
-    TriggerManager* trigger_manager,
-    PrefService* prefs,
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    history::HistoryService* history_service,
-    bool monitor_mode)
-    : content::WebContentsObserver(web_contents),
-      finish_report_delay_ms_(kSuspiciousSiteCollectionPeriodMilliseconds),
-      current_state_(monitor_mode ? TriggerState::MONITOR_MODE
-                                  : TriggerState::IDLE),
-      trigger_manager_(trigger_manager),
-      prefs_(prefs),
-      url_loader_factory_(url_loader_factory),
-      history_service_(history_service),
-      task_runner_(
-          base::CreateSingleThreadTaskRunner({content::BrowserThread::UI})) {}
-
-SuspiciousSiteTrigger::~SuspiciousSiteTrigger() {}
-
-// static
-void SuspiciousSiteTrigger::CreateForWebContents(
-    content::WebContents* web_contents,
-    TriggerManager* trigger_manager,
-    PrefService* prefs,
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    history::HistoryService* history_service,
-    bool monitor_mode) {
-  if (!FromWebContents(web_contents)) {
-    web_contents->SetUserData(
-        UserDataKey(), base::WrapUnique(new SuspiciousSiteTrigger(
-                           web_contents, trigger_manager, prefs,
-                           url_loader_factory, history_service, monitor_mode)));
-  }
-}
-
-bool SuspiciousSiteTrigger::MaybeStartReport() {
-  SBErrorOptions error_options =
-      TriggerManager::GetSBErrorDisplayOptions(*prefs_, web_contents());
-
-  security_interstitials::UnsafeResource resource;
-  resource.threat_type = SB_THREAT_TYPE_SUSPICIOUS_SITE;
-  resource.url = web_contents()->GetLastCommittedURL();
-  resource.web_contents_getter = resource.GetWebContentsGetter(
-      web_contents()->GetMainFrame()->GetProcess()->GetID(),
-      web_contents()->GetMainFrame()->GetRoutingID());
-
-  TriggerManagerReason reason;
-  if (!trigger_manager_->StartCollectingThreatDetailsWithReason(
-          TriggerType::SUSPICIOUS_SITE, web_contents(), resource,
-          url_loader_factory_, history_service_, error_options, &reason)) {
-    UMA_HISTOGRAM_ENUMERATION(kSuspiciousSiteTriggerEventMetricName,
-                              SuspiciousSiteTriggerEvent::REPORT_START_FAILED);
-    UMA_HISTOGRAM_ENUMERATION(kSuspiciousSiteTriggerReportRejectionMetricName,
-                              reason);
-    return false;
-  }
-
-  // Call back into the trigger after a short delay, allowing the report
-  // to complete.
-  task_runner_->PostDelayedTask(
-      FROM_HERE,
-      base::BindOnce(&SuspiciousSiteTrigger::ReportDelayTimerFired,
-                     weak_ptr_factory_.GetWeakPtr()),
-      base::TimeDelta::FromMilliseconds(finish_report_delay_ms_));
-
-  UMA_HISTOGRAM_ENUMERATION(kSuspiciousSiteTriggerEventMetricName,
-                            SuspiciousSiteTriggerEvent::REPORT_STARTED);
-  return true;
-}
-
-void SuspiciousSiteTrigger::FinishReport() {
-  SBErrorOptions error_options =
-      TriggerManager::GetSBErrorDisplayOptions(*prefs_, web_contents());
-  if (trigger_manager_->FinishCollectingThreatDetails(
-          TriggerType::SUSPICIOUS_SITE, web_contents(), base::TimeDelta(),
-          /*did_proceed=*/false, /*num_visits=*/0, error_options)) {
-    UMA_HISTOGRAM_ENUMERATION(kSuspiciousSiteTriggerEventMetricName,
-                              SuspiciousSiteTriggerEvent::REPORT_FINISHED);
-  } else {
-    UMA_HISTOGRAM_ENUMERATION(kSuspiciousSiteTriggerEventMetricName,
-                              SuspiciousSiteTriggerEvent::REPORT_FINISH_FAILED);
-  }
-}
-
-void SuspiciousSiteTrigger::SuspiciousSiteDetectedWhenMonitoring() {
-  DCHECK_EQ(TriggerState::MONITOR_MODE, current_state_);
-  SBErrorOptions error_options =
-      TriggerManager::GetSBErrorDisplayOptions(*prefs_, web_contents());
-  TriggerManagerReason reason;
-  if (trigger_manager_->CanStartDataCollectionWithReason(
-          error_options, TriggerType::SUSPICIOUS_SITE, &reason) ||
-      reason == TriggerManagerReason::DAILY_QUOTA_EXCEEDED) {
-    UMA_HISTOGRAM_ENUMERATION(
-        kSuspiciousSiteTriggerEventMetricName,
-        SuspiciousSiteTriggerEvent::REPORT_POSSIBLE_BUT_SKIPPED);
-  }
-}
-
-void SuspiciousSiteTrigger::DidStartLoading() {
-  UMA_HISTOGRAM_ENUMERATION(kSuspiciousSiteTriggerEventMetricName,
-                            SuspiciousSiteTriggerEvent::PAGE_LOAD_START);
-  switch (current_state_) {
-    case TriggerState::IDLE:
-      // Load started, move to loading state.
-      current_state_ = TriggerState::LOADING;
-      return;
-
-    case TriggerState::LOADING:
-      // No-op, still loading.
-      return;
-
-    case TriggerState::LOADING_WILL_REPORT:
-      // This happens if the user leaves the suspicious page before it
-      // finishes loading. A report can't be created in this case since the
-      // page is now gone.
-      UMA_HISTOGRAM_ENUMERATION(
-          kSuspiciousSiteTriggerEventMetricName,
-          SuspiciousSiteTriggerEvent::PENDING_REPORT_CANCELLED_BY_LOAD);
-      current_state_ = TriggerState::LOADING;
-      return;
-
-    case TriggerState::REPORT_STARTED:
-      // A new page load has started while creating the current report.
-      // Finish the report immediately with whatever data has been captured
-      // so far. A report timer will have already started, but it will be
-      // ignored when it fires.
-      current_state_ = TriggerState::LOADING;
-      FinishReport();
-      return;
-
-    case TriggerState::MONITOR_MODE:
-      // No-op, monitoring only.
-      return;
-  }
-}
-
-void SuspiciousSiteTrigger::DidStopLoading() {
-  UMA_HISTOGRAM_ENUMERATION(kSuspiciousSiteTriggerEventMetricName,
-                            SuspiciousSiteTriggerEvent::PAGE_LOAD_FINISH);
-
-  switch (current_state_) {
-    case TriggerState::IDLE:
-      // No-op, load stopped and we're already idle.
-      return;
-
-    case TriggerState::LOADING:
-      // Load finished, return to Idle state.
-      current_state_ = TriggerState::IDLE;
-      return;
-
-    case TriggerState::LOADING_WILL_REPORT:
-      // Suspicious site detected mid-load and the page has now
-      // finished loading, so try starting a report now.
-      // If we fail to start a report for whatever reason, return to Idle.
-      if (MaybeStartReport()) {
-        current_state_ = TriggerState::REPORT_STARTED;
-      } else {
-        current_state_ = TriggerState::IDLE;
-      }
-      return;
-
-    case TriggerState::REPORT_STARTED:
-      // No-op. Let the report continue running.
-      return;
-
-    case TriggerState::MONITOR_MODE:
-      // No-op, monitoring only.
-      return;
-  }
-}
-
-void SuspiciousSiteTrigger::SuspiciousSiteDetected() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  UMA_HISTOGRAM_ENUMERATION(
-      kSuspiciousSiteTriggerEventMetricName,
-      SuspiciousSiteTriggerEvent::SUSPICIOUS_SITE_DETECTED);
-
-  switch (current_state_) {
-    case TriggerState::IDLE:
-      // Suspicious site detected while idle, start a report immediately.
-      // If we fail to start a report for whatever reason, remain Idle.
-      if (MaybeStartReport()) {
-        current_state_ = TriggerState::REPORT_STARTED;
-      }
-      return;
-
-    case TriggerState::LOADING:
-      // Suspicious site detected in the middle of the load, remember this
-      // and let the page finish loading. The report will be started after
-      // the page has loaded.
-      current_state_ = TriggerState::LOADING_WILL_REPORT;
-      return;
-
-    case TriggerState::LOADING_WILL_REPORT:
-      // No-op. Current page has multiple suspicious URLs in it, remain in
-      // the LOADING_WILL_REPORT state. A report will begin when the page
-      // finishes loading.
-      return;
-
-    case TriggerState::REPORT_STARTED:
-      // No-op. The current report should capture all suspicious sites.
-      return;
-
-    case TriggerState::MONITOR_MODE:
-      // We monitor how often a suspicious site hit could result in a report.
-      SuspiciousSiteDetectedWhenMonitoring();
-      return;
-  }
-}
-
-void SuspiciousSiteTrigger::ReportDelayTimerFired() {
-  UMA_HISTOGRAM_ENUMERATION(kSuspiciousSiteTriggerEventMetricName,
-                            SuspiciousSiteTriggerEvent::REPORT_DELAY_TIMER);
-  UMA_HISTOGRAM_ENUMERATION(kSuspiciousSiteTriggerReportDelayStateMetricName,
-                            current_state_);
-  switch (current_state_) {
-    case TriggerState::IDLE:
-    case TriggerState::LOADING:
-    case TriggerState::LOADING_WILL_REPORT:
-    case TriggerState::MONITOR_MODE:
-      // Invalid, expecting to be in REPORT_STARTED state.
-      return;
-
-    case TriggerState::REPORT_STARTED:
-      // The delay timer has fired so complete the current report.
-      current_state_ = TriggerState::IDLE;
-      FinishReport();
-      return;
-  }
-}
-
-void SuspiciousSiteTrigger::SetTaskRunnerForTest(
-    scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
-  task_runner_ = task_runner;
-}
-
-WEB_CONTENTS_USER_DATA_KEY_IMPL(SuspiciousSiteTrigger)
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/triggers/suspicious_site_trigger.h b/components/safe_browsing/triggers/suspicious_site_trigger.h
deleted file mode 100644
index 381445da..0000000
--- a/components/safe_browsing/triggers/suspicious_site_trigger.h
+++ /dev/null
@@ -1,191 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_TRIGGERS_SUSPICIOUS_SITE_TRIGGER_H_
-#define COMPONENTS_SAFE_BROWSING_TRIGGERS_SUSPICIOUS_SITE_TRIGGER_H_
-
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "content/public/browser/web_contents_observer.h"
-#include "content/public/browser/web_contents_user_data.h"
-
-class PrefService;
-
-namespace history {
-class HistoryService;
-}
-
-namespace network {
-class SharedURLLoaderFactory;
-}  // namespace network
-
-namespace safe_browsing {
-class TriggerManager;
-
-// Metric for tracking what the Suspicious Site trigger does on each event.
-extern const char kSuspiciousSiteTriggerEventMetricName[];
-
-// Metric for tracking how often reports from this trigger are rejected by the
-// trigger manager, and for what reason.
-extern const char kSuspiciousSiteTriggerReportRejectionMetricName[];
-
-// Metric for tracking the state of the trigger when the report delay timer
-// fires.
-extern const char kSuspiciousSiteTriggerReportDelayStateMetricName[];
-
-// Tracks events this trigger listens for or actions it performs. These values
-// are written to logs. New enum values can be added, but existing enums must
-// never be renumbered or deleted and reused.
-enum class SuspiciousSiteTriggerEvent {
-  // A page load started.
-  PAGE_LOAD_START = 0,
-  // A page load finished.
-  PAGE_LOAD_FINISH = 1,
-  // A suspicious site was detected.
-  SUSPICIOUS_SITE_DETECTED = 2,
-  // The report delay timer fired.
-  REPORT_DELAY_TIMER = 3,
-  // A suspicious site report was started.
-  REPORT_STARTED = 4,
-  // A suspicious site report was created and sent.
-  REPORT_FINISHED = 5,
-  // The trigger was waiting for a load to finish before creating a report but
-  // a new load started before the previous load could finish, so the report
-  // was cancelled.
-  PENDING_REPORT_CANCELLED_BY_LOAD = 6,
-  // The trigger tried to start the report but it was rejected by the trigger
-  // manager.
-  REPORT_START_FAILED = 7,
-  // The trigger tried to finish the report but it was rejected by the trigger
-  // manager.
-  REPORT_FINISH_FAILED = 8,
-  // The trigger could have sent a report but it was skipped, typically because
-  // the trigger was out of quota.
-  REPORT_POSSIBLE_BUT_SKIPPED = 9,
-  // New events must be added before kMaxValue, and the value of kMaxValue
-  // updated.
-  kMaxValue = REPORT_POSSIBLE_BUT_SKIPPED
-};
-
-// Notify a suspicious site trigger on a particular tab that a suspicious site
-// was detected. |web_contents_getter| specifies the tab where the site was
-// detected.
-// Must be called on UI thread.
-void NotifySuspiciousSiteTriggerDetected(
-    const base::RepeatingCallback<content::WebContents*()>&
-        web_contents_getter);
-
-// This class watches tab-level events such as the start and end of a page
-// load, and also listens for events from the SuspiciousSiteURLThrottle that
-// indicate there was a hit on the suspicious site list. This trigger is
-// repsonsible for creating reports about the page at the right time, based on
-// the sequence of such events.
-class SuspiciousSiteTrigger
-    : public content::WebContentsObserver,
-      public content::WebContentsUserData<SuspiciousSiteTrigger> {
- public:
-  // The different states the trigger could be in.
-  // These values are written to logs. New enum values can be added, but
-  // existing enums must never be renumbered or deleted and reused.
-  enum class TriggerState {
-    // Trigger is idle, page is not loading, no report requested.
-    IDLE = 0,
-    // Page load has started, no report requested.
-    LOADING = 1,
-    // Page load has started and a report is requested. The report will be
-    // created when the page load finishes.
-    LOADING_WILL_REPORT = 2,
-    // A page load finished and a report for the page has started.
-    REPORT_STARTED = 3,
-    // The trigger is in monitoring mode where it listens for events and
-    // increments some metrics but never sends reports. The trigger will never
-    // leave this state.
-    MONITOR_MODE = 4,
-    // New states must be added before kMaxValue and the value of kMaxValue
-    // updated.
-    kMaxValue = MONITOR_MODE
-  };
-
-  ~SuspiciousSiteTrigger() override;
-
-  static void CreateForWebContents(
-      content::WebContents* web_contents,
-      TriggerManager* trigger_manager,
-      PrefService* prefs,
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      history::HistoryService* history_service,
-      bool monitor_mode);
-
-  // content::WebContentsObserver implementations.
-  void DidStartLoading() override;
-  void DidStopLoading() override;
-
-  // Called when a suspicious site has been detected on the tab that this
-  // trigger is running on.
-  void SuspiciousSiteDetected();
-
- private:
-  friend class content::WebContentsUserData<SuspiciousSiteTrigger>;
-  friend class SuspiciousSiteTriggerTest;
-
-  SuspiciousSiteTrigger(
-      content::WebContents* web_contents,
-      TriggerManager* trigger_manager,
-      PrefService* prefs,
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      history::HistoryService* history_service,
-      bool monitor_mode);
-
-  // Tries to start a report. Returns whether a report started successfully.
-  // If a report is started, a delayed callback will also begin to notify
-  // the trigger when the report should be completed and sent.
-  bool MaybeStartReport();
-
-  // Calls into the trigger manager to finish the active report and send it.
-  void FinishReport();
-
-  // Called when a suspicious site is detected while in monitor mode. We update
-  // metrics if we determine that a report could have been sent had the trigger
-  // been active.
-  void SuspiciousSiteDetectedWhenMonitoring();
-
-  // Called when the report delay timer fires, indicating that the active
-  // report should be completed and sent.
-  void ReportDelayTimerFired();
-
-  // Sets a task runner to use for tests.
-  void SetTaskRunnerForTest(
-      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
-
-  // The delay (in milliseconds) to wait before finishing a report. Can be
-  // overwritten for tests.
-  int64_t finish_report_delay_ms_;
-
-  // Current state of the trigger. Used to synchronize page load events with
-  // suspicious site list hit events so that reports can be generated at the
-  // right time.
-  TriggerState current_state_;
-
-  // TriggerManager gets called if this trigger detects a suspicious site and
-  // wants to collect data abou tit. Not owned.
-  TriggerManager* trigger_manager_;
-
-  PrefService* prefs_;
-  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
-  history::HistoryService* history_service_;
-
-  // Task runner for posting delayed tasks. Normally set to the runner for the
-  // UI thread, but can be overwritten for tests.
-  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
-
-  base::WeakPtrFactory<SuspiciousSiteTrigger> weak_ptr_factory_{this};
-
-  WEB_CONTENTS_USER_DATA_KEY_DECL();
-
-  DISALLOW_COPY_AND_ASSIGN(SuspiciousSiteTrigger);
-};
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_TRIGGERS_SUSPICIOUS_SITE_TRIGGER_H_
diff --git a/components/safe_browsing/triggers/suspicious_site_trigger_unittest.cc b/components/safe_browsing/triggers/suspicious_site_trigger_unittest.cc
deleted file mode 100644
index 9001d4e..0000000
--- a/components/safe_browsing/triggers/suspicious_site_trigger_unittest.cc
+++ /dev/null
@@ -1,527 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/triggers/suspicious_site_trigger.h"
-
-#include <string>
-
-#include "base/test/metrics/histogram_tester.h"
-#include "base/test/test_simple_task_runner.h"
-#include "build/build_config.h"
-#include "components/prefs/testing_pref_service.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/triggers/mock_trigger_manager.h"
-#include "content/public/test/browser_task_environment.h"
-#include "content/public/test/navigation_simulator.h"
-#include "content/public/test/test_renderer_host.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using content::NavigationSimulator;
-using content::RenderFrameHost;
-using content::RenderFrameHostTester;
-
-using testing::_;
-using testing::DoAll;
-using testing::Return;
-using testing::SetArgPointee;
-
-namespace safe_browsing {
-
-namespace {
-const char kSuspiciousUrl[] = "https://suspicious.com/";
-const char kCleanUrl[] = "https://foo.com/";
-const char kCleanUrl2[] = "https://bar.com/";
-
-// A matcher for the VisibleURLChangeMidLoad_Suspicious test.
-MATCHER_P(ResourceHasUrl, gurl, "") {
-  return arg.url == gurl;
-}
-}  // namespace
-
-class SuspiciousSiteTriggerTest : public content::RenderViewHostTestHarness {
- public:
-  SuspiciousSiteTriggerTest() : task_runner_(new base::TestSimpleTaskRunner) {}
-  ~SuspiciousSiteTriggerTest() override {}
-
-  void SetUp() override {
-    content::RenderViewHostTestHarness::SetUp();
-
-    // Enable any prefs required for the trigger to run.
-    safe_browsing::RegisterProfilePrefs(prefs_.registry());
-    prefs_.SetBoolean(prefs::kSafeBrowsingExtendedReportingOptInAllowed, true);
-    prefs_.SetBoolean(prefs::kSafeBrowsingScoutReportingEnabled, true);
-  }
-
-  void CreateTrigger(bool monitor_mode) {
-    safe_browsing::SuspiciousSiteTrigger::CreateForWebContents(
-        web_contents(), &trigger_manager_, &prefs_, nullptr, nullptr,
-        monitor_mode);
-    safe_browsing::SuspiciousSiteTrigger* trigger =
-        safe_browsing::SuspiciousSiteTrigger::FromWebContents(web_contents());
-    // Give the trigger a test task runner that we can synchronize on.
-    trigger->SetTaskRunnerForTest(task_runner_);
-  }
-
-  // Returns the final RenderFrameHost after navigation commits.
-  RenderFrameHost* NavigateFrame(const std::string& url,
-                                 RenderFrameHost* frame) {
-    GURL gurl(url);
-    auto navigation_simulator =
-        NavigationSimulator::CreateRendererInitiated(gurl, frame);
-    navigation_simulator->SetKeepLoading(true);
-    navigation_simulator->Commit();
-    RenderFrameHost* final_frame_host =
-        navigation_simulator->GetFinalRenderFrameHost();
-    return final_frame_host;
-  }
-
-  // Returns the final RenderFrameHost after navigation commits.
-  RenderFrameHost* NavigateMainFrame(const std::string& url) {
-    return NavigateFrame(url, web_contents()->GetMainFrame());
-  }
-
-  // Returns the final RenderFrameHost after navigation commits.
-  RenderFrameHost* CreateAndNavigateSubFrame(const std::string& url,
-                                             RenderFrameHost* parent) {
-    RenderFrameHost* subframe =
-        RenderFrameHostTester::For(parent)->AppendChild("subframe");
-    return NavigateFrame(url, subframe);
-  }
-
-  // Changes the visible URL (in the URL bar) without committing a navigation.
-  void ChangeVisibleURLWithoutNavigation(const std::string& url) {
-    GURL gurl(url);
-    auto navigation_simulator =
-        NavigationSimulator::CreateBrowserInitiated(gurl, web_contents());
-    navigation_simulator->Start();
-  }
-
-  void StartNewFakeLoad() {
-    // This fakes a new LoadStart event in the trigger, since the navigation
-    // simulator doesn't restart the load when we start a new navigation.
-    safe_browsing::SuspiciousSiteTrigger::FromWebContents(web_contents())
-        ->DidStartLoading();
-  }
-
-  void FinishAllNavigations() {
-    // Call the trigger's DidStopLoading event handler directly since it is not
-    // called as part of the navigating individual frames.
-    safe_browsing::SuspiciousSiteTrigger::FromWebContents(web_contents())
-        ->DidStopLoading();
-  }
-
-  void TriggerSuspiciousSite() {
-    // Notify the trigger that a suspicious site was detected.
-    safe_browsing::SuspiciousSiteTrigger::FromWebContents(web_contents())
-        ->SuspiciousSiteDetected();
-  }
-
-  void WaitForTaskRunnerIdle() {
-    task_runner_->RunUntilIdle();
-    base::RunLoop().RunUntilIdle();
-  }
-
-  // Checks the trigger event histogram and ensures that |event| happened
-  // |count| times.
-  void ExpectEventHistogramCount(const SuspiciousSiteTriggerEvent event,
-                                 int count) {
-    histograms_.ExpectBucketCount(kSuspiciousSiteTriggerEventMetricName,
-                                  static_cast<int>(event), count);
-  }
-
-  // Checks the histogram that tracks what state the trigger was in when the
-  // delay timer fired. Ensures that the trigger was in |state| and occured
-  // |count| times.
-  void ExpectDelayStateHistogramCount(
-      const SuspiciousSiteTrigger::TriggerState state,
-      int count) {
-    histograms_.ExpectBucketCount(
-        kSuspiciousSiteTriggerReportDelayStateMetricName,
-        static_cast<int>(state), count);
-  }
-
-  // Checks the report rejection histogram and makes sure that |count| reports
-  // were rejected for |reason|.
-  void ExpectReportRejectionHistogramCount(const TriggerManagerReason reason,
-                                           int count) {
-    histograms_.ExpectBucketCount(
-        kSuspiciousSiteTriggerReportRejectionMetricName,
-        static_cast<int>(reason), count);
-  }
-
-  // Checks the report rejection histogram and makes sure it was empty,
-  // indicating no errors occurred.
-  void ExpectNoReportRejection() {
-    histograms_.ExpectTotalCount(
-        kSuspiciousSiteTriggerReportRejectionMetricName, 0);
-  }
-
-  MockTriggerManager* get_trigger_manager() { return &trigger_manager_; }
-
- private:
-  TestingPrefServiceSimple prefs_;
-  MockTriggerManager trigger_manager_;
-  base::HistogramTester histograms_;
-  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
-};
-
-TEST_F(SuspiciousSiteTriggerTest, RegularPageNonSuspicious) {
-  // In a normal case where there are no suspicious URLs on the page, the
-  // trigger should not fire.
-  CreateTrigger(/*monitor_mode=*/false);
-
-  EXPECT_CALL(*get_trigger_manager(),
-              StartCollectingThreatDetailsWithReason(_, _, _, _, _, _, _))
-      .Times(0);
-  EXPECT_CALL(*get_trigger_manager(),
-              FinishCollectingThreatDetails(_, _, _, _, _, _))
-      .Times(0);
-
-  RenderFrameHost* main_frame = NavigateMainFrame(kCleanUrl);
-  CreateAndNavigateSubFrame(kCleanUrl, main_frame);
-  CreateAndNavigateSubFrame(kCleanUrl, main_frame);
-  FinishAllNavigations();
-
-  // One page load start and finish. No suspicious sites and no reports sent.
-  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::PAGE_LOAD_START, 1);
-  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::PAGE_LOAD_FINISH, 1);
-  ExpectEventHistogramCount(
-      SuspiciousSiteTriggerEvent::SUSPICIOUS_SITE_DETECTED, 0);
-  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::REPORT_STARTED, 0);
-  ExpectNoReportRejection();
-}
-
-// crbug.com/1010037: fails on win.
-#if defined(OS_WIN)
-#define MAYBE_SuspiciousHitDuringLoad DISABLED_SuspiciousHitDuringLoad
-#else
-#define MAYBE_SuspiciousHitDuringLoad SuspiciousHitDuringLoad
-#endif
-TEST_F(SuspiciousSiteTriggerTest, MAYBE_SuspiciousHitDuringLoad) {
-  // When a suspicious site is detected in the middle of a page load, a report
-  // is created after the page load has finished.
-  CreateTrigger(/*monitor_mode=*/false);
-
-  EXPECT_CALL(*get_trigger_manager(),
-              StartCollectingThreatDetailsWithReason(_, _, _, _, _, _, _))
-      .Times(1)
-      .WillOnce(Return(true));
-  EXPECT_CALL(*get_trigger_manager(),
-              FinishCollectingThreatDetails(_, _, _, _, _, _))
-      .Times(1)
-      .WillOnce(Return(true));
-
-  RenderFrameHost* main_frame = NavigateMainFrame(kCleanUrl);
-  CreateAndNavigateSubFrame(kSuspiciousUrl, main_frame);
-  TriggerSuspiciousSite();
-  CreateAndNavigateSubFrame(kCleanUrl, main_frame);
-  FinishAllNavigations();
-
-  WaitForTaskRunnerIdle();
-
-  // One page load start and finish. One suspicious site detected and one
-  // report started and sent after the page finished loading.
-  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::PAGE_LOAD_START, 1);
-  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::PAGE_LOAD_FINISH, 1);
-  ExpectEventHistogramCount(
-      SuspiciousSiteTriggerEvent::SUSPICIOUS_SITE_DETECTED, 1);
-  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::REPORT_STARTED, 1);
-
-  // Ensure the delay timer fired and it happened in the REPORT_STARTED state
-  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::REPORT_DELAY_TIMER, 1);
-  ExpectDelayStateHistogramCount(
-      SuspiciousSiteTrigger::TriggerState::REPORT_STARTED, 1);
-  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::REPORT_FINISHED, 1);
-  ExpectNoReportRejection();
-}
-
-TEST_F(SuspiciousSiteTriggerTest, SuspiciousHitAfterLoad) {
-  // When a suspicious site is detected in after a page load, a report is
-  // created immediately.
-  CreateTrigger(/*monitor_mode=*/false);
-
-  EXPECT_CALL(*get_trigger_manager(),
-              StartCollectingThreatDetailsWithReason(_, _, _, _, _, _, _))
-      .Times(1)
-      .WillOnce(Return(true));
-  EXPECT_CALL(*get_trigger_manager(),
-              FinishCollectingThreatDetails(_, _, _, _, _, _))
-      .Times(1)
-      .WillOnce(Return(true));
-
-  RenderFrameHost* main_frame = NavigateMainFrame(kCleanUrl);
-  CreateAndNavigateSubFrame(kSuspiciousUrl, main_frame);
-  CreateAndNavigateSubFrame(kCleanUrl, main_frame);
-  FinishAllNavigations();
-  TriggerSuspiciousSite();
-
-  WaitForTaskRunnerIdle();
-
-  // One page load start and finish. One suspicious site detected and one
-  // report started and sent.
-  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::PAGE_LOAD_START, 1);
-  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::PAGE_LOAD_FINISH, 1);
-  ExpectEventHistogramCount(
-      SuspiciousSiteTriggerEvent::SUSPICIOUS_SITE_DETECTED, 1);
-  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::REPORT_STARTED, 1);
-
-  // Ensure the delay timer fired and it happened in the REPORT_STARTED state
-  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::REPORT_DELAY_TIMER, 1);
-  ExpectDelayStateHistogramCount(
-      SuspiciousSiteTrigger::TriggerState::REPORT_STARTED, 1);
-  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::REPORT_FINISHED, 1);
-  ExpectNoReportRejection();
-}
-
-TEST_F(SuspiciousSiteTriggerTest, DISABLED_ReportRejectedByTriggerManager) {
-  // If the trigger manager rejects the report then no report is sent.
-  CreateTrigger(/*monitor_mode=*/false);
-
-  EXPECT_CALL(*get_trigger_manager(),
-              StartCollectingThreatDetailsWithReason(_, _, _, _, _, _, _))
-      .Times(1)
-      .WillOnce(
-          DoAll(SetArgPointee<6>(TriggerManagerReason::DAILY_QUOTA_EXCEEDED),
-                Return(false)));
-  EXPECT_CALL(*get_trigger_manager(),
-              FinishCollectingThreatDetails(_, _, _, _, _, _))
-      .Times(0);
-
-  RenderFrameHost* main_frame = NavigateMainFrame(kCleanUrl);
-  CreateAndNavigateSubFrame(kSuspiciousUrl, main_frame);
-  TriggerSuspiciousSite();
-  CreateAndNavigateSubFrame(kCleanUrl, main_frame);
-  FinishAllNavigations();
-
-  WaitForTaskRunnerIdle();
-
-  // One page load start and finish. One suspicious site detected but no report
-  // is sent because it's rejected. Error stats should reflect the rejection.
-  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::PAGE_LOAD_START, 1);
-  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::PAGE_LOAD_FINISH, 1);
-  ExpectEventHistogramCount(
-      SuspiciousSiteTriggerEvent::SUSPICIOUS_SITE_DETECTED, 1);
-
-  // Ensure no report was started or finished, and no delay timer fired.
-  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::REPORT_STARTED, 0);
-  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::REPORT_DELAY_TIMER, 0);
-  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::REPORT_FINISHED, 0);
-
-  // Ensure that starting a report failed, and it was rejected for the
-  // expected reason (quota).
-  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::REPORT_START_FAILED, 1);
-  ExpectReportRejectionHistogramCount(
-      TriggerManagerReason::DAILY_QUOTA_EXCEEDED, 1);
-}
-
-TEST_F(SuspiciousSiteTriggerTest, NewNavigationMidLoad_NotSuspicious) {
-  // Exercise what happens when a new navigation begins in the middle of a page
-  // load when no suspicious site is detected.
-  CreateTrigger(/*monitor_mode=*/false);
-
-  EXPECT_CALL(*get_trigger_manager(),
-              StartCollectingThreatDetailsWithReason(_, _, _, _, _, _, _))
-      .Times(0);
-  EXPECT_CALL(*get_trigger_manager(),
-              FinishCollectingThreatDetails(_, _, _, _, _, _))
-      .Times(0);
-
-  RenderFrameHost* main_frame = NavigateMainFrame(kCleanUrl);
-  CreateAndNavigateSubFrame(kCleanUrl, main_frame);
-  CreateAndNavigateSubFrame(kCleanUrl, main_frame);
-  // Begin a brand new load before the first one is finished.
-  StartNewFakeLoad();
-  FinishAllNavigations();
-
-  // Two page load start events, but only one finish. No suspicious sites
-  // detected and no reports sent.
-  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::PAGE_LOAD_START, 2);
-  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::PAGE_LOAD_FINISH, 1);
-  ExpectEventHistogramCount(
-      SuspiciousSiteTriggerEvent::SUSPICIOUS_SITE_DETECTED, 0);
-  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::REPORT_STARTED, 0);
-  ExpectNoReportRejection();
-}
-
-// Flaky. http://crbug.com/1010686
-TEST_F(SuspiciousSiteTriggerTest, DISABLED_NewNavigationMidLoad_Suspicious) {
-  // Exercise what happens when a new navigation begins in the middle of a page
-  // load when a suspicious site was detected. The report of the first site
-  // must be cancelled because we were waiting for the first load to finish
-  // before beginning the report.
-  CreateTrigger(/*monitor_mode=*/false);
-
-  EXPECT_CALL(*get_trigger_manager(),
-              StartCollectingThreatDetailsWithReason(_, _, _, _, _, _, _))
-      .Times(0);
-  EXPECT_CALL(*get_trigger_manager(),
-              FinishCollectingThreatDetails(_, _, _, _, _, _))
-      .Times(0);
-
-  RenderFrameHost* main_frame = NavigateMainFrame(kCleanUrl);
-  CreateAndNavigateSubFrame(kCleanUrl, main_frame);
-  // Trigger a suspicious site. We wait for this page load to finish before
-  // creating the report.
-  TriggerSuspiciousSite();
-  CreateAndNavigateSubFrame(kCleanUrl, main_frame);
-  // Begin a brand new load before the first one is finished. This will cancel
-  // the report that is queued.
-  StartNewFakeLoad();
-  FinishAllNavigations();
-
-  // Two page load start events, but only one finish. One suspicious site
-  // detected but no reports created because the report gets cancelled.
-  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::PAGE_LOAD_START, 2);
-  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::PAGE_LOAD_FINISH, 1);
-  ExpectEventHistogramCount(
-      SuspiciousSiteTriggerEvent::SUSPICIOUS_SITE_DETECTED, 1);
-  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::REPORT_STARTED, 0);
-  ExpectNoReportRejection();
-
-  // Ensure that the repot got cancelled by the second load.
-  ExpectEventHistogramCount(
-      SuspiciousSiteTriggerEvent::PENDING_REPORT_CANCELLED_BY_LOAD, 1);
-}
-
-TEST_F(SuspiciousSiteTriggerTest, MonitorMode_NotSuspicious) {
-  // Testing the trigger in monitoring mode, it should never send reports.
-  // In a normal case where there are no suspicious URLs on the page, the
-  // trigger should not fire.
-  CreateTrigger(/*monitor_mode=*/true);
-
-  EXPECT_CALL(*get_trigger_manager(),
-              StartCollectingThreatDetailsWithReason(_, _, _, _, _, _, _))
-      .Times(0);
-  EXPECT_CALL(*get_trigger_manager(),
-              FinishCollectingThreatDetails(_, _, _, _, _, _))
-      .Times(0);
-
-  RenderFrameHost* main_frame = NavigateMainFrame(kCleanUrl);
-  CreateAndNavigateSubFrame(kCleanUrl, main_frame);
-  CreateAndNavigateSubFrame(kCleanUrl, main_frame);
-  FinishAllNavigations();
-
-  // One page load start and finish. No suspicious sites and no reports sent.
-  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::PAGE_LOAD_START, 1);
-  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::PAGE_LOAD_FINISH, 1);
-  ExpectEventHistogramCount(
-      SuspiciousSiteTriggerEvent::SUSPICIOUS_SITE_DETECTED, 0);
-  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::REPORT_STARTED, 0);
-  ExpectNoReportRejection();
-}
-
-TEST_F(SuspiciousSiteTriggerTest, MonitorMode_SuspiciousHitDuringLoad) {
-  // Testing the trigger in monitoring mode, it should never send reports.
-  // When a suspicious site is detected in the middle of a page load, a report
-  // is created after the page load has finished.
-  CreateTrigger(/*monitor_mode=*/true);
-
-  EXPECT_CALL(*get_trigger_manager(),
-              StartCollectingThreatDetailsWithReason(_, _, _, _, _, _, _))
-      .Times(0);
-  EXPECT_CALL(*get_trigger_manager(),
-              FinishCollectingThreatDetails(_, _, _, _, _, _))
-      .Times(0);
-
-  RenderFrameHost* main_frame = NavigateMainFrame(kCleanUrl);
-  CreateAndNavigateSubFrame(kSuspiciousUrl, main_frame);
-  TriggerSuspiciousSite();
-  CreateAndNavigateSubFrame(kCleanUrl, main_frame);
-  FinishAllNavigations();
-
-  WaitForTaskRunnerIdle();
-
-  // One page load start and finish. One suspicious site detected and one
-  // possible report that gets skipped.
-  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::PAGE_LOAD_START, 1);
-  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::PAGE_LOAD_FINISH, 1);
-  ExpectEventHistogramCount(
-      SuspiciousSiteTriggerEvent::SUSPICIOUS_SITE_DETECTED, 1);
-  ExpectEventHistogramCount(
-      SuspiciousSiteTriggerEvent::REPORT_POSSIBLE_BUT_SKIPPED, 1);
-
-  // No reports are started or finished, no delay timer fired.
-  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::REPORT_STARTED, 0);
-  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::REPORT_FINISHED, 0);
-  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::REPORT_DELAY_TIMER, 0);
-  ExpectNoReportRejection();
-}
-
-TEST_F(SuspiciousSiteTriggerTest, VisibleURLChangeMidLoad_NotSuspicious) {
-  // Exercise what happens when the visible URL changes during load and no
-  // suspicious site was detected.
-  CreateTrigger(/*monitor_mode=*/false);
-
-  EXPECT_CALL(*get_trigger_manager(),
-              StartCollectingThreatDetailsWithReason(_, _, _, _, _, _, _))
-      .Times(0);
-  EXPECT_CALL(*get_trigger_manager(),
-              FinishCollectingThreatDetails(_, _, _, _, _, _))
-      .Times(0);
-
-  NavigateMainFrame(kCleanUrl);
-  // Change visible URL by starting a new navigation without committing it.
-  // Sanity check the visible URL changed.
-  ChangeVisibleURLWithoutNavigation(kCleanUrl2);
-  GURL expected_clean_url_2(kCleanUrl2);
-  EXPECT_EQ(expected_clean_url_2, web_contents()->GetVisibleURL());
-  FinishAllNavigations();
-
-  WaitForTaskRunnerIdle();
-
-  // One page load start and finish. No suspicious sites and no reports sent.
-  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::PAGE_LOAD_START, 1);
-  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::PAGE_LOAD_FINISH, 1);
-  ExpectEventHistogramCount(
-      SuspiciousSiteTriggerEvent::SUSPICIOUS_SITE_DETECTED, 0);
-  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::REPORT_STARTED, 0);
-  ExpectNoReportRejection();
-}
-
-TEST_F(SuspiciousSiteTriggerTest, VisibleURLChangeMidLoad_Suspicious) {
-  // Exercise what happens when the visible URL changes after a suspicious site
-  // has already been detected.
-  CreateTrigger(/*monitor_mode=*/false);
-
-  NavigateMainFrame(kSuspiciousUrl);
-
-  // The resource eventually sent to the trigger manager should include the
-  // original (suspicious) URL.
-  GURL suspicious_url(kSuspiciousUrl);
-  EXPECT_CALL(*get_trigger_manager(),
-              StartCollectingThreatDetailsWithReason(
-                  _, _, ResourceHasUrl(suspicious_url), _, _, _, _))
-      .Times(1)
-      .WillOnce(Return(true));
-  EXPECT_CALL(*get_trigger_manager(),
-              FinishCollectingThreatDetails(_, _, _, _, _, _))
-      .Times(1)
-      .WillOnce(Return(true));
-
-  // Change visible URL by starting a new navigation without committing it.
-  // Sanity check the visible URL changed.
-  ChangeVisibleURLWithoutNavigation(kCleanUrl);
-  GURL expected_clean_url(kCleanUrl);
-  EXPECT_EQ(expected_clean_url, web_contents()->GetVisibleURL());
-  TriggerSuspiciousSite();
-  FinishAllNavigations();
-
-  WaitForTaskRunnerIdle();
-
-  // One page load start and finish. One suspicious site detected and one
-  // report started and sent after the page finished loading.
-  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::PAGE_LOAD_START, 1);
-  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::PAGE_LOAD_FINISH, 1);
-  ExpectEventHistogramCount(
-      SuspiciousSiteTriggerEvent::SUSPICIOUS_SITE_DETECTED, 1);
-  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::REPORT_STARTED, 1);
-
-  // Ensure the delay timer fired and it happened in the REPORT_STARTED state
-  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::REPORT_DELAY_TIMER, 1);
-  ExpectDelayStateHistogramCount(
-      SuspiciousSiteTrigger::TriggerState::REPORT_STARTED, 1);
-  ExpectEventHistogramCount(SuspiciousSiteTriggerEvent::REPORT_FINISHED, 1);
-  ExpectNoReportRejection();
-}
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/triggers/trigger_manager.cc b/components/safe_browsing/triggers/trigger_manager.cc
deleted file mode 100644
index 594688c..0000000
--- a/components/safe_browsing/triggers/trigger_manager.cc
+++ /dev/null
@@ -1,257 +0,0 @@
-// Copyright (c) 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/triggers/trigger_manager.h"
-
-#include "base/bind.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "components/prefs/pref_service.h"
-#include "components/safe_browsing/base_ui_manager.h"
-#include "components/safe_browsing/browser/threat_details.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/features.h"
-#include "components/security_interstitials/content/unsafe_resource.h"
-#include "content/public/browser/browser_context.h"
-#include "content/public/browser/browser_thread.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-
-namespace safe_browsing {
-
-namespace {
-
-bool TriggerNeedsOptInForCollection(const TriggerType trigger_type) {
-  switch (trigger_type) {
-    case TriggerType::SECURITY_INTERSTITIAL:
-      // For security interstitials, users can change the opt-in while the
-      // trigger runs, so collection can begin without opt-in.
-      return false;
-    case TriggerType::AD_POPUP:
-    case TriggerType::AD_REDIRECT:
-    case TriggerType::AD_SAMPLE:
-      // Ad samples happen in the background so the user must already be opted
-      // in before the trigger is allowed to run.
-      return true;
-    case TriggerType::GAIA_PASSWORD_REUSE:
-      // For Gaia password reuses, it is unlikely for users to change opt-in
-      // while the trigger runs, so we require opt-in for collection to avoid
-      // overheads.
-      return true;
-    case TriggerType::SUSPICIOUS_SITE:
-      // Suspicious site collection happens in the background so the user must
-      // already be opted in before the trigger is allowed to run.
-      return true;
-    case TriggerType::APK_DOWNLOAD:
-      // APK download collection happens in the background so the user must
-      // already be opted in before the trigger is allowed to run.
-      return true;
-  }
-  // By default, require opt-in for all triggers.
-  return true;
-}
-
-bool CanSendReport(const SBErrorOptions& error_display_options,
-                   const TriggerType trigger_type) {
-  // Reports are only sent for non-incoginito users who are allowed to modify
-  // the Extended Reporting setting and have opted-in to Extended Reporting.
-  return !error_display_options.is_off_the_record &&
-         error_display_options.is_extended_reporting_opt_in_allowed &&
-         error_display_options.is_extended_reporting_enabled;
-}
-
-}  // namespace
-
-DataCollectorsContainer::DataCollectorsContainer() {}
-DataCollectorsContainer::~DataCollectorsContainer() {}
-
-TriggerManager::TriggerManager(BaseUIManager* ui_manager,
-                               ReferrerChainProvider* referrer_chain_provider,
-                               PrefService* local_state_prefs)
-    : ui_manager_(ui_manager),
-      referrer_chain_provider_(referrer_chain_provider),
-      trigger_throttler_(new TriggerThrottler(local_state_prefs)) {}
-
-TriggerManager::~TriggerManager() {}
-
-void TriggerManager::set_trigger_throttler(TriggerThrottler* throttler) {
-  trigger_throttler_.reset(throttler);
-}
-
-// static
-SBErrorOptions TriggerManager::GetSBErrorDisplayOptions(
-    const PrefService& pref_service,
-    content::WebContents* web_contents) {
-  return SBErrorOptions(/*is_main_frame_load_blocked=*/false,
-                        IsExtendedReportingOptInAllowed(pref_service),
-                        web_contents->GetBrowserContext()->IsOffTheRecord(),
-                        IsExtendedReportingEnabled(pref_service),
-                        IsExtendedReportingPolicyManaged(pref_service),
-                        /*is_proceed_anyway_disabled=*/false,
-                        /*should_open_links_in_new_tab=*/false,
-                        /*show_back_to_safety_button=*/true,
-                        /*help_center_article_link=*/std::string());
-}
-
-bool TriggerManager::CanStartDataCollection(
-    const SBErrorOptions& error_display_options,
-    const TriggerType trigger_type) {
-  TriggerManagerReason unused_reason;
-  return CanStartDataCollectionWithReason(error_display_options, trigger_type,
-                                          &unused_reason);
-}
-
-bool TriggerManager::CanStartDataCollectionWithReason(
-    const SBErrorOptions& error_display_options,
-    const TriggerType trigger_type,
-    TriggerManagerReason* out_reason) {
-  *out_reason = TriggerManagerReason::NO_REASON;
-
-  // Some triggers require that the user be opted-in to extended reporting in
-  // order to run, while others can run without opt-in (eg: because users are
-  // prompted for opt-in as part of the trigger).
-  bool optin_required_check_ok =
-      !TriggerNeedsOptInForCollection(trigger_type) ||
-      error_display_options.is_extended_reporting_enabled;
-  // We start data collection as long as user is not incognito and is able to
-  // change the Extended Reporting opt-in, and the |trigger_type| has available
-  // quota. For some triggers we also require extended reporting opt-in in
-  // order to start data collection.
-  if (!error_display_options.is_off_the_record &&
-      error_display_options.is_extended_reporting_opt_in_allowed &&
-      optin_required_check_ok) {
-    bool quota_ok = trigger_throttler_->TriggerCanFire(trigger_type);
-    if (!quota_ok)
-      *out_reason = TriggerManagerReason::DAILY_QUOTA_EXCEEDED;
-    return quota_ok;
-  } else {
-    *out_reason = TriggerManagerReason::USER_PREFERENCES;
-    return false;
-  }
-}
-
-bool TriggerManager::StartCollectingThreatDetails(
-    const TriggerType trigger_type,
-    content::WebContents* web_contents,
-    const security_interstitials::UnsafeResource& resource,
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    history::HistoryService* history_service,
-    const SBErrorOptions& error_display_options) {
-  TriggerManagerReason unused_reason;
-  return StartCollectingThreatDetailsWithReason(
-      trigger_type, web_contents, resource, url_loader_factory, history_service,
-      error_display_options, &unused_reason);
-}
-
-bool TriggerManager::StartCollectingThreatDetailsWithReason(
-    const TriggerType trigger_type,
-    content::WebContents* web_contents,
-    const security_interstitials::UnsafeResource& resource,
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    history::HistoryService* history_service,
-    const SBErrorOptions& error_display_options,
-    TriggerManagerReason* reason) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  if (!CanStartDataCollectionWithReason(error_display_options, trigger_type,
-                                        reason))
-    return false;
-
-  // Ensure we're not already collecting ThreatDetails on this tab. Create an
-  // entry in the map for this |web_contents| if it's not there already.
-  DataCollectorsContainer* collectors = &data_collectors_map_[web_contents];
-  if (collectors->threat_details != nullptr)
-    return false;
-
-  bool should_trim_threat_details = (trigger_type == TriggerType::AD_POPUP ||
-                                     trigger_type == TriggerType::AD_SAMPLE ||
-                                     trigger_type == TriggerType::AD_REDIRECT);
-  collectors->threat_details = ThreatDetails::NewThreatDetails(
-      ui_manager_, web_contents, resource, url_loader_factory, history_service,
-      referrer_chain_provider_, should_trim_threat_details,
-      base::BindOnce(&TriggerManager::ThreatDetailsDone,
-                     weak_factory_.GetWeakPtr()));
-  return true;
-}
-
-bool TriggerManager::FinishCollectingThreatDetails(
-    const TriggerType trigger_type,
-    content::WebContents* web_contents,
-    const base::TimeDelta& delay,
-    bool did_proceed,
-    int num_visits,
-    const SBErrorOptions& error_display_options) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  // Make sure there's a ThreatDetails collector running on this tab.
-  if (!base::Contains(data_collectors_map_, web_contents))
-    return false;
-  DataCollectorsContainer* collectors = &data_collectors_map_[web_contents];
-  if (collectors->threat_details == nullptr)
-    return false;
-
-  // Determine whether a report should be sent.
-  bool should_send_report = CanSendReport(error_display_options, trigger_type);
-
-  if (should_send_report) {
-    // Find the data collector and tell it to finish collecting data. We expect
-    // it to notify us when it's finished so we can clean up references to it.
-
-    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-        FROM_HERE,
-        base::BindOnce(&ThreatDetails::FinishCollection,
-                       collectors->threat_details->GetWeakPtr(), did_proceed,
-                       num_visits),
-        delay);
-
-    // Record that this trigger fired and collected data.
-    trigger_throttler_->TriggerFired(trigger_type);
-  } else {
-    // We aren't telling ThreatDetails to finish the report so we should clean
-    // up our map ourselves.
-    ThreatDetailsDone(web_contents);
-  }
-
-  return should_send_report;
-}
-
-void TriggerManager::ThreatDetailsDone(content::WebContents* web_contents) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  // Clean up the ThreatDetailsdata collector on the specified tab.
-  if (!base::Contains(data_collectors_map_, web_contents))
-    return;
-
-  DataCollectorsContainer* collectors = &data_collectors_map_[web_contents];
-  collectors->threat_details = nullptr;
-}
-
-void TriggerManager::WebContentsDestroyed(content::WebContents* web_contents) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  if (!base::Contains(data_collectors_map_, web_contents))
-    return;
-  data_collectors_map_.erase(web_contents);
-}
-
-TriggerManagerWebContentsHelper::TriggerManagerWebContentsHelper(
-    content::WebContents* web_contents,
-    TriggerManager* trigger_manager)
-    : content::WebContentsObserver(web_contents),
-      trigger_manager_(trigger_manager) {}
-
-TriggerManagerWebContentsHelper::~TriggerManagerWebContentsHelper() {}
-
-void TriggerManagerWebContentsHelper::CreateForWebContents(
-    content::WebContents* web_contents,
-    TriggerManager* trigger_manager) {
-  DCHECK(web_contents);
-  if (!FromWebContents(web_contents)) {
-    web_contents->SetUserData(
-        UserDataKey(), base::WrapUnique(new TriggerManagerWebContentsHelper(
-                           web_contents, trigger_manager)));
-  }
-}
-
-void TriggerManagerWebContentsHelper::WebContentsDestroyed() {
-  trigger_manager_->WebContentsDestroyed(web_contents());
-}
-
-WEB_CONTENTS_USER_DATA_KEY_IMPL(TriggerManagerWebContentsHelper)
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/triggers/trigger_manager.h b/components/safe_browsing/triggers/trigger_manager.h
deleted file mode 100644
index 601c3a9..0000000
--- a/components/safe_browsing/triggers/trigger_manager.h
+++ /dev/null
@@ -1,226 +0,0 @@
-// Copyright (c) 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_TRIGGERS_TRIGGER_MANAGER_H_
-#define COMPONENTS_SAFE_BROWSING_TRIGGERS_TRIGGER_MANAGER_H_
-
-#include <unordered_map>
-
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "components/safe_browsing/browser/referrer_chain_provider.h"
-#include "components/safe_browsing/triggers/trigger_throttler.h"
-#include "components/security_interstitials/content/unsafe_resource.h"
-#include "components/security_interstitials/core/base_safe_browsing_error_ui.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/browser/web_contents_observer.h"
-#include "content/public/browser/web_contents_user_data.h"
-
-class PrefService;
-
-namespace history {
-class HistoryService;
-}
-
-namespace network {
-class SharedURLLoaderFactory;
-}
-
-namespace safe_browsing {
-
-class BaseUIManager;
-class ThreatDetails;
-
-// A wrapper around different kinds of data collectors that can be active on a
-// given browser tab. Any given field can be null or empty if the associated
-// data is not being collected.
-struct DataCollectorsContainer {
- public:
-  DataCollectorsContainer();
-  ~DataCollectorsContainer();
-
-  // Note: new data collection types should be added below as additional fields.
-
-  // Collects ThreatDetails which contains resource URLs and partial DOM.
-  std::unique_ptr<ThreatDetails> threat_details;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(DataCollectorsContainer);
-};
-
-// Stores the data collectors that are active on each WebContents (ie: browser
-// tab).
-using DataCollectorsMap =
-    std::unordered_map<content::WebContents*, DataCollectorsContainer>;
-
-using SBErrorOptions =
-    security_interstitials::BaseSafeBrowsingErrorUI::SBErrorDisplayOptions;
-
-// The reasons that trigger manager fails to create or finish a report.
-// These values are written to logs. New enum values can be added, but
-// existing enums must never be renumbered or deleted and reused.
-enum class TriggerManagerReason {
-  // Default value, used when there is no failure.
-  NO_REASON = 0,
-  // User preferences do not allow the report to be started or finished.
-  USER_PREFERENCES = 1,
-  // A report is already started on this tab, so no new report is started.
-  REPORT_ALREADY_STARTED = 2,
-  // There is no report to finish on this tab.
-  NO_REPORT_TO_FINISH = 3,
-  // No report is started because the user has exceeded their daily quota.
-  DAILY_QUOTA_EXCEEDED = 4,
-  // New reasons must be added before kMaxValue and the value of kMaxValue
-  // updated.
-  kMaxValue = DAILY_QUOTA_EXCEEDED
-};
-
-// This class manages SafeBrowsing data-reporting triggers. Triggers are
-// activated for users opted-in to Extended Reporting and when security-related
-// data collection is required.
-//
-// The TriggerManager has two main responsibilities: 1) ensuring triggers only
-// run when appropriate, by honouring user opt-ins and incognito state, and 2)
-// tracking how often triggers fire and throttling them when necessary.
-class TriggerManager {
- public:
-  TriggerManager(BaseUIManager* ui_manager,
-                 ReferrerChainProvider* referrer_chain_provider,
-                 PrefService* local_state_prefs);
-  virtual ~TriggerManager();
-
-  // Returns a SBErrorDisplayOptions struct containing user state that is
-  // relevant for TriggerManager to decide whether to start/finish data
-  // collection. Looks at incognito state from |web_contents|, and opt-ins from
-  // |pref_service|. Only the fields needed by TriggerManager will be set.
-  static SBErrorOptions GetSBErrorDisplayOptions(
-      const PrefService& pref_service,
-      content::WebContents* web_contents);
-
-  // Returns whether data collection can be started for the |trigger_type| based
-  // on the settings specified in |error_display_options| as well as quota.
-  // If false is returned, |out_reason| will be specify the reason.
-  bool CanStartDataCollectionWithReason(
-      const SBErrorOptions& error_display_options,
-      const TriggerType trigger_type,
-      TriggerManagerReason* out_reason);
-
-  // Simplified signature for |CanStartDataCollectionWithReason| for callers
-  // that don't care about the reason.
-  bool CanStartDataCollection(const SBErrorOptions& error_display_options,
-                              const TriggerType trigger_type);
-
-  // Begins collecting a ThreatDetails report on the specified |web_contents|.
-  // |resource| is the unsafe resource that cause the collection to occur.
-  // |url_loader_factory| is used to retrieve data from the HTTP cache.
-  // |history_service| is used to get data about redirects.
-  // |error_display_options| contains the current state of relevant user
-  // preferences. We use this object for interop with WebView, in Chrome it
-  // should be created by TriggerManager::GetSBErrorDisplayOptions().
-  // Returns true if the collection began, or false if it didn't.
-  // If false is returned, |out_reason| is set to the reason the report didn't
-  // start.
-  virtual bool StartCollectingThreatDetailsWithReason(
-      TriggerType trigger_type,
-      content::WebContents* web_contents,
-      const security_interstitials::UnsafeResource& resource,
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      history::HistoryService* history_service,
-      const SBErrorOptions& error_display_options,
-      TriggerManagerReason* out_reason);
-
-  // Simplified signature for |StartCollectingThreatDetailsWithReason| for
-  // callers that don't care about the reason.
-  virtual bool StartCollectingThreatDetails(
-      TriggerType trigger_type,
-      content::WebContents* web_contents,
-      const security_interstitials::UnsafeResource& resource,
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      history::HistoryService* history_service,
-      const SBErrorOptions& error_display_options);
-
-  // Completes the collection of a ThreatDetails report on the specified
-  // |web_contents| and sends the report. |delay| can be used to wait a period
-  // of time before finishing the report. |did_proceed| indicates whether the
-  // user proceeded through the security interstitial associated with this
-  // report. |num_visits| is how many times the user has visited the site
-  // before. |error_display_options| contains the current state of relevant user
-  // preferences. We use this object for interop with WebView, in Chrome it
-  // should be created by TriggerManager::GetSBErrorDisplayOptions().
-  // Returns true if the report was completed and sent, or false otherwise (eg:
-  // the user was not opted-in to extended reporting after collection began).
-  virtual bool FinishCollectingThreatDetails(
-      TriggerType trigger_type,
-      content::WebContents* web_contents,
-      const base::TimeDelta& delay,
-      bool did_proceed,
-      int num_visits,
-      const SBErrorOptions& error_display_options);
-
-  // Called when a ThreatDetails report finishes for the specified
-  // |web_contents|.
-  void ThreatDetailsDone(content::WebContents* web_contents);
-
-  // Called when the specified |web_contents| is being destroyed. Used to clean
-  // up our map.
-  void WebContentsDestroyed(content::WebContents* web_contents);
-
- private:
-  friend class TriggerManagerTest;
-
-  // For testing only - allows injecting a mock Throttler.
-  void set_trigger_throttler(TriggerThrottler* throttler);
-
-  // The UI manager is used to send reports to Google. Not owned.
-  // TODO(lpz): we may only need a the PingManager here.
-  BaseUIManager* ui_manager_;
-
-  // The Referrer Chain Provider is used to retrieve the referrer chain for
-  // reports that require it. Not owned.
-  ReferrerChainProvider* referrer_chain_provider_;
-
-  // Map of the data collectors running on each tabs. New keys are added the
-  // first time any trigger tries to collect data on a tab and are removed when
-  // the tab is destroyed. The values can be null if a trigger has finished on
-  // a tab but the tab remains open.
-  DataCollectorsMap data_collectors_map_;
-
-  // Keeps track of how often triggers fire and throttles them when needed.
-  std::unique_ptr<TriggerThrottler> trigger_throttler_;
-
-  base::WeakPtrFactory<TriggerManager> weak_factory_{this};
-  // WeakPtrFactory should be last, don't add any members below it.
-  DISALLOW_COPY_AND_ASSIGN(TriggerManager);
-};
-
-// A helper class that listens for events happening on a WebContents and can
-// notify TriggerManager of any that are relevant.
-class TriggerManagerWebContentsHelper
-    : public content::WebContentsObserver,
-      public content::WebContentsUserData<TriggerManagerWebContentsHelper> {
- public:
-  ~TriggerManagerWebContentsHelper() override;
-
-  // Creates a TriggerManagerWebContentsHelper and scopes its lifetime to the
-  // specified |web_contents|.
-  static void CreateForWebContents(content::WebContents* web_contents,
-                                   TriggerManager* trigger_manager);
-
-  // WebContentsObserver implementation.
-  void WebContentsDestroyed() override;
-
- private:
-  friend class content::WebContentsUserData<TriggerManagerWebContentsHelper>;
-
-  TriggerManagerWebContentsHelper(content::WebContents* web_contents,
-                                  TriggerManager* trigger_manager);
-
-  // Trigger Manager will be notified of any relevant WebContents events.
-  TriggerManager* trigger_manager_;
-  WEB_CONTENTS_USER_DATA_KEY_DECL();
-};
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_TRIGGERS_TRIGGER_MANAGER_H_
diff --git a/components/safe_browsing/triggers/trigger_manager_unittest.cc b/components/safe_browsing/triggers/trigger_manager_unittest.cc
deleted file mode 100644
index 486af5b6..0000000
--- a/components/safe_browsing/triggers/trigger_manager_unittest.cc
+++ /dev/null
@@ -1,462 +0,0 @@
-// Copyright (c) 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/triggers/trigger_manager.h"
-
-#include "base/run_loop.h"
-#include "base/stl_util.h"
-#include "base/test/scoped_feature_list.h"
-#include "components/prefs/pref_registry_simple.h"
-#include "components/prefs/testing_pref_service.h"
-#include "components/safe_browsing/browser/threat_details.h"
-#include "components/safe_browsing/features.h"
-#include "components/safe_browsing/triggers/trigger_throttler.h"
-#include "content/public/test/browser_task_environment.h"
-#include "content/public/test/test_browser_context.h"
-#include "content/public/test/test_web_contents_factory.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using testing::_;
-using testing::Key;
-using testing::Return;
-using testing::UnorderedElementsAre;
-
-namespace safe_browsing {
-
-// Mock ThreatDetails class that makes FinishCollection a no-op.
-class MockThreatDetails : public ThreatDetails {
- public:
-  MockThreatDetails() {}
-  ~MockThreatDetails() override {}
-  MOCK_METHOD2(FinishCollection, void(bool did_proceed, int num_visits));
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockThreatDetails);
-};
-
-class MockThreatDetailsFactory : public ThreatDetailsFactory {
- public:
-  ~MockThreatDetailsFactory() override {}
-
-  std::unique_ptr<ThreatDetails> CreateThreatDetails(
-      BaseUIManager* ui_manager,
-      content::WebContents* web_contents,
-      const security_interstitials::UnsafeResource& unsafe_resource,
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      history::HistoryService* history_service,
-      ReferrerChainProvider* referrer_chain_provider,
-      bool trim_to_ad_tags,
-      ThreatDetailsDoneCallback done_callback) override {
-    return std::make_unique<MockThreatDetails>();
-  }
-};
-
-class MockTriggerThrottler : public TriggerThrottler {
- public:
-  MockTriggerThrottler() : TriggerThrottler(nullptr) {}
-  MOCK_CONST_METHOD1(TriggerCanFire, bool(TriggerType trigger_type));
-};
-
-class TriggerManagerTest : public ::testing::Test {
- public:
-  TriggerManagerTest() : trigger_manager_(nullptr, nullptr, nullptr) {}
-  ~TriggerManagerTest() override {}
-
-  void SetUp() override {
-    ThreatDetails::RegisterFactory(&mock_threat_details_factory_);
-
-    // Register any prefs that are needed by the trigger manager. By default,
-    // enable Safe Browsing and Extended Reporting.
-    safe_browsing::RegisterProfilePrefs(pref_service_.registry());
-    SetPref(prefs::kSafeBrowsingExtendedReportingOptInAllowed, true);
-    SetPref(prefs::kSafeBrowsingScoutReportingEnabled, true);
-
-    MockTriggerThrottler* mock_throttler = new MockTriggerThrottler();
-    ON_CALL(*mock_throttler, TriggerCanFire(_)).WillByDefault(Return(true));
-    // Trigger Manager takes ownership of the mock throttler.
-    trigger_manager_.set_trigger_throttler(mock_throttler);
-  }
-
-  void SetPref(const std::string& pref, bool value) {
-    pref_service_.SetBoolean(pref, value);
-  }
-
-  void SetManagedPref(const std::string& pref, bool value) {
-    pref_service_.SetManagedPref(pref, std::make_unique<base::Value>(value));
-  }
-
-  bool GetPref(const std::string& pref) {
-    return pref_service_.GetBoolean(pref);
-  }
-
-  void SetTriggerHasQuota(const TriggerType trigger_type, bool has_quota) {
-    MockTriggerThrottler* mock_throttler = static_cast<MockTriggerThrottler*>(
-        trigger_manager_.trigger_throttler_.get());
-    EXPECT_CALL(*mock_throttler, TriggerCanFire(trigger_type))
-        .WillOnce(Return(has_quota));
-  }
-
-  content::WebContents* CreateWebContents() {
-    DCHECK(!browser_context_.IsOffTheRecord())
-        << "CreateWebContents() should not be called after "
-           "CreateIncognitoWebContents()";
-    return web_contents_factory_.CreateWebContents(&browser_context_);
-  }
-
-  content::WebContents* CreateIncognitoWebContents() {
-    browser_context_.set_is_off_the_record(true);
-    return web_contents_factory_.CreateWebContents(&browser_context_);
-  }
-
-  bool StartCollectingThreatDetails(const TriggerType trigger_type,
-                                    content::WebContents* web_contents) {
-    SBErrorOptions options =
-        TriggerManager::GetSBErrorDisplayOptions(pref_service_, web_contents);
-    return trigger_manager_.StartCollectingThreatDetails(
-        trigger_type, web_contents, security_interstitials::UnsafeResource(),
-        nullptr, nullptr, options);
-  }
-
-  bool FinishCollectingThreatDetails(const TriggerType trigger_type,
-                                     content::WebContents* web_contents,
-                                     bool expect_report_sent) {
-    if (expect_report_sent) {
-      MockThreatDetails* threat_details = static_cast<MockThreatDetails*>(
-          trigger_manager_.data_collectors_map_[web_contents]
-              .threat_details.get());
-      EXPECT_CALL(*threat_details, FinishCollection(_, _)).Times(1);
-    }
-    SBErrorOptions options =
-        TriggerManager::GetSBErrorDisplayOptions(pref_service_, web_contents);
-    bool result = trigger_manager_.FinishCollectingThreatDetails(
-        trigger_type, web_contents, base::TimeDelta(), false, 0, options);
-
-    // Invoke the callback if the report was to be sent.
-    if (expect_report_sent) {
-      // Allow the ThreatDetails to complete, then remove it.
-      base::RunLoop().RunUntilIdle();
-      trigger_manager_.ThreatDetailsDone(web_contents);
-    }
-
-    return result;
-  }
-
-  const DataCollectorsMap& data_collectors_map() {
-    return trigger_manager_.data_collectors_map_;
-  }
-
- private:
-  TriggerManager trigger_manager_;
-  MockThreatDetailsFactory mock_threat_details_factory_;
-  content::BrowserTaskEnvironment task_environment_;
-  content::TestBrowserContext browser_context_;
-  content::TestWebContentsFactory web_contents_factory_;
-  TestingPrefServiceSimple pref_service_;
-  std::unique_ptr<base::test::ScopedFeatureList> feature_list_;
-
-  DISALLOW_COPY_AND_ASSIGN(TriggerManagerTest);
-};
-
-TEST_F(TriggerManagerTest, StartAndFinishCollectingThreatDetails) {
-  // Basic workflow is to start and finish data collection with a single
-  // WebContents.
-  content::WebContents* web_contents1 = CreateWebContents();
-  EXPECT_TRUE(StartCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
-                                           web_contents1));
-  EXPECT_THAT(data_collectors_map(), UnorderedElementsAre(Key(web_contents1)));
-  EXPECT_NE(data_collectors_map().find(web_contents1),
-            data_collectors_map().end());
-  EXPECT_NE(nullptr, data_collectors_map().at(web_contents1).threat_details);
-  EXPECT_TRUE(FinishCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
-                                            web_contents1, true));
-  EXPECT_NE(data_collectors_map().find(web_contents1),
-            data_collectors_map().end());
-  EXPECT_EQ(nullptr, data_collectors_map().at(web_contents1).threat_details);
-
-  // More complex scenarios can happen, where collection happens on two
-  // WebContents at the same time, possibly starting and completing in different
-  // order.
-  content::WebContents* web_contents2 = CreateWebContents();
-  EXPECT_TRUE(StartCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
-                                           web_contents1));
-  EXPECT_TRUE(StartCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
-                                           web_contents2));
-  EXPECT_THAT(data_collectors_map(),
-              UnorderedElementsAre(Key(web_contents1), Key(web_contents2)));
-  EXPECT_NE(data_collectors_map().find(web_contents1),
-            data_collectors_map().end());
-  EXPECT_NE(nullptr, data_collectors_map().at(web_contents1).threat_details);
-  EXPECT_NE(data_collectors_map().find(web_contents2),
-            data_collectors_map().end());
-  EXPECT_NE(nullptr, data_collectors_map().at(web_contents2).threat_details);
-  EXPECT_TRUE(FinishCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
-                                            web_contents2, true));
-  EXPECT_NE(data_collectors_map().find(web_contents1),
-            data_collectors_map().end());
-  EXPECT_NE(nullptr, data_collectors_map().at(web_contents1).threat_details);
-  EXPECT_NE(data_collectors_map().find(web_contents2),
-            data_collectors_map().end());
-  EXPECT_EQ(nullptr, data_collectors_map().at(web_contents2).threat_details);
-  EXPECT_TRUE(FinishCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
-                                            web_contents1, true));
-  EXPECT_NE(data_collectors_map().find(web_contents1),
-            data_collectors_map().end());
-  EXPECT_EQ(nullptr, data_collectors_map().at(web_contents1).threat_details);
-  EXPECT_NE(data_collectors_map().find(web_contents2),
-            data_collectors_map().end());
-  EXPECT_EQ(nullptr, data_collectors_map().at(web_contents2).threat_details);
-
-  // Calling Start twice with the same WebContents is an error, and will return
-  // false the second time. But it can still be completed normally.
-  EXPECT_TRUE(StartCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
-                                           web_contents1));
-  EXPECT_NE(data_collectors_map().find(web_contents1),
-            data_collectors_map().end());
-  EXPECT_NE(nullptr, data_collectors_map().at(web_contents1).threat_details);
-  EXPECT_FALSE(StartCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
-                                            web_contents1));
-  EXPECT_NE(data_collectors_map().find(web_contents1),
-            data_collectors_map().end());
-  EXPECT_NE(nullptr, data_collectors_map().at(web_contents1).threat_details);
-  EXPECT_TRUE(FinishCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
-                                            web_contents1, true));
-  EXPECT_NE(data_collectors_map().find(web_contents1),
-            data_collectors_map().end());
-  EXPECT_EQ(nullptr, data_collectors_map().at(web_contents1).threat_details);
-
-  // Calling Finish twice with the same WebContents is an error, and will return
-  // false the second time. It's basically a no-op.
-  EXPECT_TRUE(StartCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
-                                           web_contents1));
-  EXPECT_NE(data_collectors_map().find(web_contents1),
-            data_collectors_map().end());
-  EXPECT_NE(nullptr, data_collectors_map().at(web_contents1).threat_details);
-  EXPECT_TRUE(FinishCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
-                                            web_contents1, true));
-  EXPECT_NE(data_collectors_map().find(web_contents1),
-            data_collectors_map().end());
-  EXPECT_EQ(nullptr, data_collectors_map().at(web_contents1).threat_details);
-  EXPECT_FALSE(FinishCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
-                                             web_contents1, false));
-  EXPECT_NE(data_collectors_map().find(web_contents1),
-            data_collectors_map().end());
-  EXPECT_EQ(nullptr, data_collectors_map().at(web_contents1).threat_details);
-}
-
-TEST_F(TriggerManagerTest, NoDataCollection_Incognito) {
-  // Data collection will not begin and no reports will be sent when incognito.
-  content::WebContents* web_contents = CreateIncognitoWebContents();
-  EXPECT_FALSE(StartCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
-                                            web_contents));
-  EXPECT_TRUE(data_collectors_map().empty());
-  EXPECT_FALSE(FinishCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
-                                             web_contents, false));
-  EXPECT_TRUE(data_collectors_map().empty());
-}
-
-TEST_F(TriggerManagerTest, NoDataCollection_SBEROptInDisallowed) {
-  // Data collection will not begin and no reports will be sent when the user is
-  // not allowed to opt-in to SBER.
-  SetPref(prefs::kSafeBrowsingExtendedReportingOptInAllowed, false);
-  content::WebContents* web_contents = CreateWebContents();
-  EXPECT_FALSE(StartCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
-                                            web_contents));
-  EXPECT_TRUE(data_collectors_map().empty());
-  EXPECT_FALSE(FinishCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
-                                             web_contents, false));
-  EXPECT_TRUE(data_collectors_map().empty());
-}
-
-TEST_F(TriggerManagerTest, NoDataCollection_IncognitoAndSBEROptInDisallowed) {
-  // Data collection will not begin and no reports will be sent when the user is
-  // not allowed to opt-in to SBER and is also incognito.
-  SetPref(prefs::kSafeBrowsingExtendedReportingOptInAllowed, false);
-  content::WebContents* web_contents = CreateIncognitoWebContents();
-  EXPECT_FALSE(StartCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
-                                            web_contents));
-  EXPECT_TRUE(data_collectors_map().empty());
-  EXPECT_FALSE(FinishCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
-                                             web_contents, false));
-  EXPECT_TRUE(data_collectors_map().empty());
-}
-
-TEST_F(TriggerManagerTest, UserOptedOutOfSBER_DataCollected_NoReportSent) {
-  // When the user is opted-out of SBER then data collection will begin but no
-  // report will be sent when data collection ends.
-  SetPref(prefs::kSafeBrowsingScoutReportingEnabled, false);
-  content::WebContents* web_contents = CreateWebContents();
-  EXPECT_TRUE(StartCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
-                                           web_contents));
-  EXPECT_THAT(data_collectors_map(), UnorderedElementsAre(Key(web_contents)));
-  EXPECT_FALSE(FinishCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
-                                             web_contents, false));
-  EXPECT_NE(data_collectors_map().find(web_contents),
-            data_collectors_map().end());
-  EXPECT_EQ(nullptr, data_collectors_map().at(web_contents).threat_details);
-}
-
-TEST_F(TriggerManagerTest, UserOptsOutOfSBER_DataCollected_NoReportSent) {
-  // If the user opts-out of Extended Reporting while data is being collected
-  // then no report is sent. Note that the test fixture opts the user into
-  // Extended Reporting by default.
-  content::WebContents* web_contents = CreateWebContents();
-  EXPECT_TRUE(StartCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
-                                           web_contents));
-  EXPECT_THAT(data_collectors_map(), UnorderedElementsAre(Key(web_contents)));
-
-  SetPref(prefs::kSafeBrowsingScoutReportingEnabled, false);
-
-  EXPECT_FALSE(FinishCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
-                                             web_contents, false));
-  EXPECT_NE(data_collectors_map().find(web_contents),
-            data_collectors_map().end());
-  EXPECT_EQ(nullptr, data_collectors_map().at(web_contents).threat_details);
-}
-
-TEST_F(TriggerManagerTest, UserOptsInToSBER_DataCollected_ReportSent) {
-  // When the user is opted-out of SBER then data collection will begin. If they
-  // opt-in to SBER while data collection is in progress then the report will
-  // also be sent.
-  SetPref(prefs::kSafeBrowsingScoutReportingEnabled, false);
-  content::WebContents* web_contents = CreateWebContents();
-  EXPECT_TRUE(StartCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
-                                           web_contents));
-  EXPECT_THAT(data_collectors_map(), UnorderedElementsAre(Key(web_contents)));
-
-  SetPref(prefs::kSafeBrowsingScoutReportingEnabled, true);
-
-  EXPECT_TRUE(FinishCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
-                                            web_contents, true));
-  EXPECT_NE(data_collectors_map().find(web_contents),
-            data_collectors_map().end());
-  EXPECT_EQ(nullptr, data_collectors_map().at(web_contents).threat_details);
-}
-
-TEST_F(TriggerManagerTest,
-       SBEROptInBecomesDisallowed_DataCollected_NoReportSent) {
-  // If the user loses the ability to opt-in to SBER in the middle of data
-  // collection then the report will not be sent.
-  content::WebContents* web_contents = CreateWebContents();
-  EXPECT_TRUE(StartCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
-                                           web_contents));
-  EXPECT_THAT(data_collectors_map(), UnorderedElementsAre(Key(web_contents)));
-
-  // Remove the ability to opt-in to SBER.
-  SetPref(prefs::kSafeBrowsingExtendedReportingOptInAllowed, false);
-
-  EXPECT_FALSE(FinishCollectingThreatDetails(TriggerType::SECURITY_INTERSTITIAL,
-                                             web_contents, false));
-  EXPECT_NE(data_collectors_map().find(web_contents),
-            data_collectors_map().end());
-  EXPECT_EQ(nullptr, data_collectors_map().at(web_contents).threat_details);
-}
-
-TEST_F(TriggerManagerTest, NoCollectionWhenOutOfQuota) {
-  // Triggers are not allowed to collect data when they're out of quota, even if
-  // all other conditions are as expected.
-  content::WebContents* web_contents = CreateWebContents();
-
-  // Turn on the AD_SAMPLE trigger inside the throttler and confirm that it can
-  // fire normally.
-  SetTriggerHasQuota(TriggerType::AD_SAMPLE, true);
-  EXPECT_TRUE(
-      StartCollectingThreatDetails(TriggerType::AD_SAMPLE, web_contents));
-  EXPECT_THAT(data_collectors_map(), UnorderedElementsAre(Key(web_contents)));
-  EXPECT_TRUE(FinishCollectingThreatDetails(TriggerType::AD_SAMPLE,
-                                            web_contents, true));
-  EXPECT_NE(data_collectors_map().find(web_contents),
-            data_collectors_map().end());
-  EXPECT_EQ(nullptr, data_collectors_map().at(web_contents).threat_details);
-
-  // Turn off the AD_SAMPLE trigger inside the throttler, the trigger should no
-  // longer be able to fire.
-  SetTriggerHasQuota(TriggerType::AD_SAMPLE, false);
-  EXPECT_FALSE(
-      StartCollectingThreatDetails(TriggerType::AD_SAMPLE, web_contents));
-  EXPECT_NE(data_collectors_map().find(web_contents),
-            data_collectors_map().end());
-  EXPECT_EQ(nullptr, data_collectors_map().at(web_contents).threat_details);
-}
-
-TEST_F(TriggerManagerTest, NoCollectionWhenSBERDisabledByPolicy) {
-  // Confirm that disabling SBER through an enterprise policy does disable
-  // triggers.
-  content::WebContents* web_contents = CreateWebContents();
-
-  SetManagedPref(prefs::kSafeBrowsingScoutReportingEnabled, false);
-  EXPECT_FALSE(
-      StartCollectingThreatDetails(TriggerType::AD_SAMPLE, web_contents));
-  EXPECT_TRUE(data_collectors_map().empty());
-  EXPECT_FALSE(FinishCollectingThreatDetails(TriggerType::AD_SAMPLE,
-                                             web_contents, false));
-  EXPECT_TRUE(data_collectors_map().empty());
-}
-
-TEST_F(TriggerManagerTest, AdSamplerTrigger) {
-  // Check the conditions required for the Ad Sampler trigger to fire. It needs
-  // opt-in to start collecting data, scout opt-in, and quota.
-  content::WebContents* web_contents = CreateWebContents();
-
-  // The default setup in this test makes the trigger fire (all prefs enabled,
-  // all triggers have quota).
-  EXPECT_TRUE(
-      StartCollectingThreatDetails(TriggerType::AD_SAMPLE, web_contents));
-  EXPECT_THAT(data_collectors_map(), UnorderedElementsAre(Key(web_contents)));
-  EXPECT_TRUE(FinishCollectingThreatDetails(TriggerType::AD_SAMPLE,
-                                            web_contents, true));
-  EXPECT_NE(data_collectors_map().find(web_contents),
-            data_collectors_map().end());
-  EXPECT_EQ(nullptr, data_collectors_map().at(web_contents).threat_details);
-
-  // Disabling SBEROptInAllowed disables this trigger.
-  SetPref(prefs::kSafeBrowsingExtendedReportingOptInAllowed, false);
-  EXPECT_FALSE(
-      StartCollectingThreatDetails(TriggerType::AD_SAMPLE, web_contents));
-
-  // Confirm it can fire when we re-enable SBEROptInAllowed
-  SetPref(prefs::kSafeBrowsingExtendedReportingOptInAllowed, true);
-  EXPECT_TRUE(
-      StartCollectingThreatDetails(TriggerType::AD_SAMPLE, web_contents));
-  EXPECT_TRUE(FinishCollectingThreatDetails(TriggerType::AD_SAMPLE,
-                                            web_contents, true));
-
-  // Disabling Scout disables this trigger.
-  SetPref(prefs::kSafeBrowsingScoutReportingEnabled, false);
-  EXPECT_FALSE(
-      StartCollectingThreatDetails(TriggerType::AD_SAMPLE, web_contents));
-
-  // Confirm it can fire when we re-enable Scout and disable legacy SBER.
-  SetPref(prefs::kSafeBrowsingScoutReportingEnabled, true);
-  EXPECT_TRUE(
-      StartCollectingThreatDetails(TriggerType::AD_SAMPLE, web_contents));
-  EXPECT_TRUE(FinishCollectingThreatDetails(TriggerType::AD_SAMPLE,
-                                            web_contents, true));
-
-  // Finally, make sure the trigger can't fire if it has no quota.
-  SetTriggerHasQuota(TriggerType::AD_SAMPLE, false);
-  EXPECT_FALSE(
-      StartCollectingThreatDetails(TriggerType::AD_SAMPLE, web_contents));
-
-  // Confirm it can fire again when quota is available.
-  SetTriggerHasQuota(TriggerType::AD_SAMPLE, true);
-  EXPECT_TRUE(
-      StartCollectingThreatDetails(TriggerType::AD_SAMPLE, web_contents));
-  EXPECT_TRUE(FinishCollectingThreatDetails(TriggerType::AD_SAMPLE,
-                                            web_contents, true));
-}
-
-TEST_F(TriggerManagerTest, AdSamplerTrigger_Incognito) {
-  // Check the conditions required for the Ad Sampler trigger to fire. It needs
-  // opt-in to start collecting data, scout opt-in, and quota, and it can't fire
-  // in inconito (except when forced on by finch feature).
-  content::WebContents* web_contents = CreateIncognitoWebContents();
-
-  // The default setup in this test makes the trigger fire (all prefs enabled,
-  // all triggers have quota), but the incognito window prevents it from firing.
-  EXPECT_FALSE(
-      StartCollectingThreatDetails(TriggerType::AD_SAMPLE, web_contents));
-}
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/triggers/trigger_throttler.cc b/components/safe_browsing/triggers/trigger_throttler.cc
deleted file mode 100644
index e22bf9b..0000000
--- a/components/safe_browsing/triggers/trigger_throttler.cc
+++ /dev/null
@@ -1,312 +0,0 @@
-// Copyright (c) 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/triggers/trigger_throttler.h"
-
-#include "base/metrics/field_trial_params.h"
-#include "base/stl_util.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_split.h"
-#include "base/time/default_clock.h"
-#include "base/time/time.h"
-#include "components/prefs/pref_service.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/features.h"
-
-namespace safe_browsing {
-const size_t kAdPopupTriggerDefaultQuota = 1;
-const size_t kAdRedirectTriggerDefaultQuota = 1;
-const size_t kAdSamplerTriggerDefaultQuota = 10;
-const size_t kSuspiciousSiteTriggerDefaultQuota = 5;
-const char kAdPopupTriggerQuotaParam[] = "ad_popup_trigger_quota";
-const char kAdRedirectTriggerQuotaParam[] = "ad_redirect_trigger_quota";
-const char kSuspiciousSiteTriggerQuotaParam[] = "suspicious_site_trigger_quota";
-const char kTriggerTypeAndQuotaParam[] = "trigger_type_and_quota_csv";
-
-namespace {
-const size_t kUnlimitedTriggerQuota = std::numeric_limits<size_t>::max();
-constexpr base::TimeDelta kOneDayTimeDelta = base::TimeDelta::FromDays(1);
-
-// Predicate used to search |trigger_type_and_quota_list_| by trigger type.
-class TriggerTypeIs {
- public:
-  explicit TriggerTypeIs(const TriggerType type) : type_(type) {}
-  bool operator()(const TriggerTypeAndQuotaItem& trigger_type_and_quota) {
-    return type_ == trigger_type_and_quota.first;
-  }
-
- private:
-  TriggerType type_;
-};
-
-void ParseTriggerTypeAndQuotaParam(
-    std::vector<TriggerTypeAndQuotaItem>* trigger_type_and_quota_list) {
-  DCHECK(trigger_type_and_quota_list);
-  trigger_type_and_quota_list->clear();
-
-  // First, handle the trigger-specific features.
-  int suspicious_site_quota = base::GetFieldTrialParamByFeatureAsInt(
-      kSuspiciousSiteTriggerQuotaFeature, kSuspiciousSiteTriggerQuotaParam,
-      kSuspiciousSiteTriggerDefaultQuota);
-  if (suspicious_site_quota > 0) {
-    trigger_type_and_quota_list->push_back(
-        std::make_pair(TriggerType::SUSPICIOUS_SITE, suspicious_site_quota));
-  }
-
-  int ad_popup_quota = base::GetFieldTrialParamByFeatureAsInt(
-      kAdPopupTriggerFeature, kAdPopupTriggerQuotaParam,
-      kAdPopupTriggerDefaultQuota);
-  if (ad_popup_quota > 0) {
-    trigger_type_and_quota_list->push_back(
-        std::make_pair(TriggerType::AD_POPUP, ad_popup_quota));
-  }
-
-  int ad_redirect_quota = base::GetFieldTrialParamByFeatureAsInt(
-      kAdRedirectTriggerFeature, kAdRedirectTriggerQuotaParam,
-      kAdRedirectTriggerDefaultQuota);
-  if (ad_redirect_quota > 0) {
-    trigger_type_and_quota_list->push_back(
-        std::make_pair(TriggerType::AD_REDIRECT, ad_redirect_quota));
-  }
-
-  // If the feature is disabled we just use the default list. Otherwise the list
-  // from the Finch param will be the one used.
-  if (!base::FeatureList::IsEnabled(kTriggerThrottlerDailyQuotaFeature)) {
-    return;
-  }
-
-  const std::string& trigger_and_quota_csv_param =
-      base::GetFieldTrialParamValueByFeature(kTriggerThrottlerDailyQuotaFeature,
-                                             kTriggerTypeAndQuotaParam);
-  if (trigger_and_quota_csv_param.empty()) {
-    return;
-  }
-
-  std::vector<std::string> split =
-      base::SplitString(trigger_and_quota_csv_param, ",", base::TRIM_WHITESPACE,
-                        base::SPLIT_WANT_NONEMPTY);
-  // If we don't have the right number of pairs in the csv then don't bother
-  // parsing further.
-  if (split.size() % 2 != 0) {
-    return;
-  }
-  for (size_t i = 0; i < split.size(); i += 2) {
-    // Make sure both the trigger type and quota are integers. Skip them if not.
-    int trigger_type_int = -1;
-    int quota_int = -1;
-    if (!base::StringToInt(split[i], &trigger_type_int) ||
-        !base::StringToInt(split[i + 1], &quota_int)) {
-      continue;
-    }
-    trigger_type_and_quota_list->push_back(
-        std::make_pair(static_cast<TriggerType>(trigger_type_int), quota_int));
-  }
-
-  std::sort(trigger_type_and_quota_list->begin(),
-            trigger_type_and_quota_list->end(),
-            [](const TriggerTypeAndQuotaItem& a,
-               const TriggerTypeAndQuotaItem& b) { return a.first < b.first; });
-}
-
-// Looks in |trigger_quota_list| for |trigger_type|. If found, sets |out_quota|
-// to the configured quota, and returns true. If not found, returns false.
-bool TryFindQuotaForTrigger(
-    const TriggerType trigger_type,
-    const std::vector<TriggerTypeAndQuotaItem>& trigger_quota_list,
-    size_t* out_quota) {
-  const auto& trigger_quota_iter =
-      std::find_if(trigger_quota_list.begin(), trigger_quota_list.end(),
-                   TriggerTypeIs(trigger_type));
-  if (trigger_quota_iter != trigger_quota_list.end()) {
-    *out_quota = trigger_quota_iter->second;
-    return true;
-  }
-  return false;
-}
-
-}  // namespace
-
-TriggerThrottler::TriggerThrottler(PrefService* local_state_prefs)
-    : local_state_prefs_(local_state_prefs),
-      clock_(base::DefaultClock::GetInstance()) {
-  ParseTriggerTypeAndQuotaParam(&trigger_type_and_quota_list_);
-  LoadTriggerEventsFromPref();
-}
-
-TriggerThrottler::~TriggerThrottler() {}
-
-void TriggerThrottler::SetClockForTesting(base::Clock* test_clock) {
-  clock_ = test_clock;
-}
-
-bool TriggerThrottler::TriggerCanFire(const TriggerType trigger_type) const {
-  // Lookup how many times this trigger is allowed to fire each day.
-  const size_t trigger_quota = GetDailyQuotaForTrigger(trigger_type);
-
-  // Some basic corner cases for triggers that always fire, or disabled
-  // triggers that never fire.
-  if (trigger_quota == kUnlimitedTriggerQuota)
-    return true;
-  if (trigger_quota == 0)
-    return false;
-
-  // Other triggers are capped, see how many times this trigger has already
-  // fired.
-  if (!base::Contains(trigger_events_, trigger_type))
-    return true;
-
-  const std::vector<base::Time>& timestamps = trigger_events_.at(trigger_type);
-  // More quota is available, so the trigger can fire again.
-  if (trigger_quota > timestamps.size())
-    return true;
-
-  // Otherwise, we have more events than quota, check which day they occurred
-  // on. Newest events are at the end of vector so we can simply look at the
-  // Nth-from-last entry (where N is the quota) to see if it happened within
-  // the current day or earlier.
-  base::Time min_timestamp = clock_->Now() - kOneDayTimeDelta;
-  const size_t pos = timestamps.size() - trigger_quota;
-  return timestamps[pos] < min_timestamp;
-}
-
-void TriggerThrottler::TriggerFired(const TriggerType trigger_type) {
-  // Lookup how many times this trigger is allowed to fire each day.
-  const size_t trigger_quota = GetDailyQuotaForTrigger(trigger_type);
-
-  // For triggers that always fire, don't bother tracking quota.
-  if (trigger_quota == kUnlimitedTriggerQuota)
-    return;
-
-  // Otherwise, record that the trigger fired.
-  std::vector<base::Time>* timestamps = &trigger_events_[trigger_type];
-  timestamps->push_back(clock_->Now());
-
-  // Clean up the trigger events map.
-  CleanupOldEvents();
-
-  // Update the pref
-  WriteTriggerEventsToPref();
-}
-
-void TriggerThrottler::CleanupOldEvents() {
-  for (const auto& map_iter : trigger_events_) {
-    const TriggerType trigger_type = map_iter.first;
-    const size_t trigger_quota = GetDailyQuotaForTrigger(trigger_type);
-    const std::vector<base::Time>& trigger_times = map_iter.second;
-
-    // Skip the cleanup if we have quota room, quotas should generally be small.
-    if (trigger_times.size() < trigger_quota)
-      return;
-
-    std::vector<base::Time> tmp_trigger_times;
-    base::Time min_timestamp = clock_->Now() - kOneDayTimeDelta;
-    // Go over the event times for this trigger and keep timestamps which are
-    // newer than |min_timestamp|. We put timestamps in a temp vector that will
-    // get swapped into the map in place of the existing vector.
-    for (const base::Time timestamp : trigger_times) {
-      if (timestamp > min_timestamp)
-        tmp_trigger_times.push_back(timestamp);
-    }
-
-    trigger_events_[trigger_type].swap(tmp_trigger_times);
-  }
-}
-
-void TriggerThrottler::LoadTriggerEventsFromPref() {
-  trigger_events_.clear();
-  if (!local_state_prefs_)
-    return;
-
-  const base::DictionaryValue* event_dict = local_state_prefs_->GetDictionary(
-      prefs::kSafeBrowsingTriggerEventTimestamps);
-  for (const auto& trigger_pair : event_dict->DictItems()) {
-    // Check that the first item in the pair is convertible to a trigger type
-    // and that the second item is a list.
-    int trigger_type_int;
-    if (!base::StringToInt(trigger_pair.first, &trigger_type_int) ||
-        trigger_type_int < static_cast<int>(TriggerType::kMinTriggerType) ||
-        trigger_type_int > static_cast<int>(TriggerType::kMaxTriggerType)) {
-      continue;
-    }
-    if (!trigger_pair.second.is_list())
-      continue;
-
-    const TriggerType trigger_type = static_cast<TriggerType>(trigger_type_int);
-    for (const auto& timestamp : trigger_pair.second.GetList()) {
-      if (timestamp.is_double())
-        trigger_events_[trigger_type].push_back(
-            base::Time::FromDoubleT(timestamp.GetDouble()));
-    }
-  }
-}
-
-void TriggerThrottler::WriteTriggerEventsToPref() {
-  if (!local_state_prefs_)
-    return;
-
-  base::DictionaryValue trigger_dict;
-  for (const auto& trigger_item : trigger_events_) {
-    base::Value* pref_timestamps = trigger_dict.SetKey(
-        base::NumberToString(static_cast<int>(trigger_item.first)),
-        base::Value(base::Value::Type::LIST));
-    for (const base::Time timestamp : trigger_item.second) {
-      pref_timestamps->Append(base::Value(timestamp.ToDoubleT()));
-    }
-  }
-
-  local_state_prefs_->Set(prefs::kSafeBrowsingTriggerEventTimestamps,
-                          trigger_dict);
-}
-
-size_t TriggerThrottler::GetDailyQuotaForTrigger(
-    const TriggerType trigger_type) const {
-  size_t quota_from_finch = 0;
-  switch (trigger_type) {
-    case TriggerType::SECURITY_INTERSTITIAL:
-    case TriggerType::GAIA_PASSWORD_REUSE:
-    case TriggerType::APK_DOWNLOAD:
-      return kUnlimitedTriggerQuota;
-    case TriggerType::AD_POPUP:
-      // Ad Popup reports are disabled unless they are configured through Finch.
-      if (TryFindQuotaForTrigger(trigger_type, trigger_type_and_quota_list_,
-                                 &quota_from_finch)) {
-        return quota_from_finch;
-      }
-      break;
-    case TriggerType::AD_REDIRECT:
-      // Ad Redirects are disabled unless they are configured through Finch.
-      if (TryFindQuotaForTrigger(trigger_type, trigger_type_and_quota_list_,
-                                 &quota_from_finch)) {
-        return quota_from_finch;
-      }
-      break;
-    case TriggerType::AD_SAMPLE:
-      // Ad Samples have a non-zero default quota, but it can be overwritten
-      // through Finch.
-      if (TryFindQuotaForTrigger(trigger_type, trigger_type_and_quota_list_,
-                                 &quota_from_finch)) {
-        return quota_from_finch;
-      } else {
-        return kAdSamplerTriggerDefaultQuota;
-      }
-
-      break;
-    case TriggerType::SUSPICIOUS_SITE:
-      // Suspicious Sites are disabled unless they are configured through Finch.
-      if (TryFindQuotaForTrigger(trigger_type, trigger_type_and_quota_list_,
-                                 &quota_from_finch)) {
-        return quota_from_finch;
-      }
-      break;
-  }
-  // By default, unhandled or unconfigured trigger types have no quota.
-  return 0;
-}
-
-void TriggerThrottler::ResetPrefsForTesting(PrefService* local_state_prefs) {
-  local_state_prefs_ = local_state_prefs;
-  LoadTriggerEventsFromPref();
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/triggers/trigger_throttler.h b/components/safe_browsing/triggers/trigger_throttler.h
deleted file mode 100644
index f158455..0000000
--- a/components/safe_browsing/triggers/trigger_throttler.h
+++ /dev/null
@@ -1,120 +0,0 @@
-// Copyright (c) 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_TRIGGERS_TRIGGER_THROTTLER_H_
-#define COMPONENTS_SAFE_BROWSING_TRIGGERS_TRIGGER_THROTTLER_H_
-
-#include <memory>
-#include <unordered_map>
-#include <vector>
-
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
-#include "base/time/clock.h"
-
-class PrefService;
-
-namespace safe_browsing {
-// Default quota for ad sampler trigger.
-extern const size_t kAdSamplerTriggerDefaultQuota;
-
-// Default quota for suspicious site trigger.
-extern const size_t kSuspiciousSiteTriggerDefaultQuota;
-
-// Param name of the finch param containing the quota for the suspicious site
-// trigger.
-extern const char kSuspiciousSiteTriggerQuotaParam[];
-
-// Param name of the finch param containing the comma-separated list of trigger
-// types and daily quotas.
-// TODO(crbug.com/744869): This param should be deprecated after ad sampler
-// launch in favour of having a unique quota feature and param per trigger.
-// Having a single shared feature makes it impossible to run multiple trigger
-// trials simultaneously.
-extern const char kTriggerTypeAndQuotaParam[];
-
-enum class TriggerType {
-  SECURITY_INTERSTITIAL = 1,
-  AD_SAMPLE = 2,
-  GAIA_PASSWORD_REUSE = 3,
-  SUSPICIOUS_SITE = 4,
-  APK_DOWNLOAD = 5,
-  AD_POPUP = 6,
-  AD_REDIRECT = 7,
-  kMinTriggerType = SECURITY_INTERSTITIAL,
-  kMaxTriggerType = AD_REDIRECT,
-};
-
-struct TriggerTypeHash {
-  std::size_t operator()(TriggerType trigger_type) const {
-    return static_cast<std::size_t>(trigger_type);
-  }
-};
-
-// A map for storing a list of event timestamps for different trigger types.
-using TriggerTimestampMap =
-    std::unordered_map<TriggerType, std::vector<base::Time>, TriggerTypeHash>;
-
-// A pair containing a TriggerType and its associated daily report quota.
-using TriggerTypeAndQuotaItem = std::pair<TriggerType, int>;
-
-// TriggerThrottler keeps track of how often each type of trigger gets fired
-// and throttles them if they fire too often.
-class TriggerThrottler {
- public:
-  TriggerThrottler(PrefService* local_state_prefs);
-  virtual ~TriggerThrottler();
-
-  // Check if the the specified |trigger_type| has quota available and is
-  // allowed to fire at this time.
-  virtual bool TriggerCanFire(TriggerType trigger_type) const;
-
-  // Called to notify the throttler that a trigger has just fired and quota
-  // should be updated.
-  void TriggerFired(TriggerType trigger_type);
-
- protected:
-  void SetClockForTesting(base::Clock* test_clock);
-
- private:
-  friend class TriggerThrottlerTest;
-  friend class TriggerThrottlerTestFinch;
-
-  // Called to periodically clean-up the list of event timestamps.
-  void CleanupOldEvents();
-
-  // Loads trigger events that have been stored in preferences and adds them
-  // to |trigger_events_|.
-  void LoadTriggerEventsFromPref();
-
-  // Updates preferences with current contents of |trigger_events_|.
-  void WriteTriggerEventsToPref();
-
-  // Returns the daily quota for the specified trigger.
-  size_t GetDailyQuotaForTrigger(const TriggerType trigger_type) const;
-
-  // Resets |local_state_prefs_|. For testing.
-  void ResetPrefsForTesting(PrefService* local_state_prefs);
-
-  // Pref service for accessing local state prefs (ie: unsynced, tied to the
-  // browser not to a profile). Used to persist quota.
-  PrefService* local_state_prefs_;
-
-  // Can be set for testing.
-  base::Clock* clock_;
-
-  // Stores each trigger type that fired along with the timestamps of when it
-  // fired.
-  TriggerTimestampMap trigger_events_;
-
-  // List of trigger types and their quotas, controlled by Finch feature
-  // |kTriggerThrottlerDailyQuotaFeature|.
-  std::vector<TriggerTypeAndQuotaItem> trigger_type_and_quota_list_;
-
-  DISALLOW_COPY_AND_ASSIGN(TriggerThrottler);
-};
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_TRIGGERS_TRIGGER_THROTTLER_H_
diff --git a/components/safe_browsing/triggers/trigger_throttler_unittest.cc b/components/safe_browsing/triggers/trigger_throttler_unittest.cc
deleted file mode 100644
index 3998665..0000000
--- a/components/safe_browsing/triggers/trigger_throttler_unittest.cc
+++ /dev/null
@@ -1,313 +0,0 @@
-// Copyright (c) 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/triggers/trigger_throttler.h"
-
-#include "base/strings/stringprintf.h"
-#include "base/test/scoped_feature_list.h"
-#include "base/test/simple_test_clock.h"
-#include "components/prefs/testing_pref_service.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/features.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using testing::ElementsAre;
-
-namespace safe_browsing {
-
-class TriggerThrottlerTest : public ::testing::Test {
- public:
-  TriggerThrottlerTest() : trigger_throttler_(nullptr) {}
-
-  void SetUp() override {
-    safe_browsing::RegisterLocalStatePrefs(pref_service_.registry());
-    trigger_throttler_.ResetPrefsForTesting(&pref_service_);
-  }
-
-  void SetQuotaForTriggerType(TriggerType trigger_type, size_t max_quota) {
-    SetQuotaForTriggerType(&trigger_throttler_, trigger_type, max_quota);
-  }
-
-  void SetQuotaForTriggerType(TriggerThrottler* throttler,
-                              TriggerType trigger_type,
-                              size_t max_quota) {
-    throttler->trigger_type_and_quota_list_.push_back(
-        std::make_pair(trigger_type, max_quota));
-  }
-
-  TriggerThrottler* throttler() { return &trigger_throttler_; }
-
-  void SetTestClock(base::Clock* clock) {
-    trigger_throttler_.SetClockForTesting(clock);
-  }
-
-  std::vector<base::Time> GetEventTimestampsForTriggerType(
-      TriggerType trigger_type) {
-    return trigger_throttler_.trigger_events_[trigger_type];
-  }
-
-  PrefService* get_pref_service() { return &pref_service_; }
-
- private:
-  TestingPrefServiceSimple pref_service_;
-  TriggerThrottler trigger_throttler_;
-};
-
-TEST_F(TriggerThrottlerTest, SecurityInterstitialsHaveUnlimitedQuota) {
-  // Make sure that security interstitials never run out of quota.
-  for (int i = 0; i < 1000; ++i) {
-    throttler()->TriggerFired(TriggerType::SECURITY_INTERSTITIAL);
-    EXPECT_TRUE(
-        throttler()->TriggerCanFire(TriggerType::SECURITY_INTERSTITIAL));
-  }
-}
-
-TEST_F(TriggerThrottlerTest, SecurityInterstitialQuotaCanNotBeOverwritten) {
-  // Make sure that security interstitials never run out of quota, even if we
-  // try to configure quota for this trigger type.
-  SetQuotaForTriggerType(TriggerType::SECURITY_INTERSTITIAL, 3);
-  for (int i = 0; i < 1000; ++i) {
-    throttler()->TriggerFired(TriggerType::SECURITY_INTERSTITIAL);
-    EXPECT_TRUE(
-        throttler()->TriggerCanFire(TriggerType::SECURITY_INTERSTITIAL));
-  }
-}
-
-TEST_F(TriggerThrottlerTest, TriggerQuotaSetToOne) {
-  // This is a corner case where we can exceed array bounds for triggers that
-  // have quota set to 1 report per day. This can happen when quota is 1 and
-  // exactly one event has fired. When deciding whether another event can fire,
-  // we look at the Nth-from-last event to check if it was recent or not - in
-  // this scenario, Nth-from-last is 1st-from-last (because quota is 1). An
-  // off-by-one error in this calculation can cause us to look at position 1
-  // instead of position 0 in the even list.
-  SetQuotaForTriggerType(TriggerType::AD_SAMPLE, 1);
-
-  // Fire the trigger, first event will be allowed.
-  EXPECT_TRUE(throttler()->TriggerCanFire(TriggerType::AD_SAMPLE));
-  throttler()->TriggerFired(TriggerType::AD_SAMPLE);
-
-  // Ensure that checking whether this trigger can fire again does not cause
-  // an error and also returns the expected result.
-  EXPECT_FALSE(throttler()->TriggerCanFire(TriggerType::AD_SAMPLE));
-}
-
-TEST_F(TriggerThrottlerTest, TriggerExceedsQuota) {
-  // Ensure that a trigger can't fire more than its quota allows.
-  SetQuotaForTriggerType(TriggerType::AD_SAMPLE, 2);
-
-  // First two triggers should work
-  EXPECT_TRUE(throttler()->TriggerCanFire(TriggerType::AD_SAMPLE));
-  throttler()->TriggerFired(TriggerType::AD_SAMPLE);
-  EXPECT_TRUE(throttler()->TriggerCanFire(TriggerType::AD_SAMPLE));
-  throttler()->TriggerFired(TriggerType::AD_SAMPLE);
-
-  // Third attempt will fail since we're out of quota.
-  EXPECT_FALSE(throttler()->TriggerCanFire(TriggerType::AD_SAMPLE));
-}
-
-TEST_F(TriggerThrottlerTest, TriggerQuotaResetsAfterOneDay) {
-  // Ensure that trigger events older than a day are cleaned up and triggers can
-  // resume firing.
-
-  // We initialize the test clock to several days ago and fire some events to
-  // use up quota. We then advance the clock by a day and ensure quota is
-  // available again.
-  base::SimpleTestClock test_clock;
-  test_clock.SetNow(base::Time::Now() - base::TimeDelta::FromDays(10));
-  base::Time base_ts = test_clock.Now();
-
-  SetTestClock(&test_clock);
-  SetQuotaForTriggerType(TriggerType::AD_SAMPLE, 2);
-
-  // First two triggers should work
-  EXPECT_TRUE(throttler()->TriggerCanFire(TriggerType::AD_SAMPLE));
-  throttler()->TriggerFired(TriggerType::AD_SAMPLE);
-  EXPECT_TRUE(throttler()->TriggerCanFire(TriggerType::AD_SAMPLE));
-  throttler()->TriggerFired(TriggerType::AD_SAMPLE);
-
-  // Third attempt will fail since we're out of quota.
-  EXPECT_FALSE(throttler()->TriggerCanFire(TriggerType::AD_SAMPLE));
-
-  // Also confirm that the throttler contains two event timestamps for the above
-  // two events - since we use a test clock, it doesn't move unless we tell it
-  // to.
-  EXPECT_THAT(GetEventTimestampsForTriggerType(TriggerType::AD_SAMPLE),
-              ElementsAre(base_ts, base_ts));
-
-  // Move the clock forward by 1 day (and a bit) and try the trigger again,
-  // quota should be available now.
-  test_clock.Advance(base::TimeDelta::FromDays(1) +
-                     base::TimeDelta::FromSeconds(1));
-  base::Time advanced_ts = test_clock.Now();
-  EXPECT_TRUE(throttler()->TriggerCanFire(TriggerType::AD_SAMPLE));
-
-  // The previous time stamps should remain in the throttler.
-  EXPECT_THAT(GetEventTimestampsForTriggerType(TriggerType::AD_SAMPLE),
-              ElementsAre(base_ts, base_ts));
-
-  // Firing the trigger will clean up the expired timestamps and insert the new
-  // timestamp.
-  throttler()->TriggerFired(TriggerType::AD_SAMPLE);
-  EXPECT_THAT(GetEventTimestampsForTriggerType(TriggerType::AD_SAMPLE),
-              ElementsAre(advanced_ts));
-}
-
-TEST_F(TriggerThrottlerTest, TriggerQuotaPersistence) {
-  // Test that trigger quota is persisted in prefs when triggers fire, and
-  // retrieved from prefs on startup.
-
-  // Set some low quotas for two triggers
-  SetQuotaForTriggerType(TriggerType::AD_SAMPLE, 3);
-  SetQuotaForTriggerType(TriggerType::SUSPICIOUS_SITE, 3);
-
-  // Ensure each trigger can fire.
-  EXPECT_TRUE(throttler()->TriggerCanFire(TriggerType::AD_SAMPLE));
-  EXPECT_TRUE(throttler()->TriggerCanFire(TriggerType::SUSPICIOUS_SITE));
-
-  // Fire each trigger twice to store some events.
-  throttler()->TriggerFired(TriggerType::AD_SAMPLE);
-  throttler()->TriggerFired(TriggerType::AD_SAMPLE);
-  throttler()->TriggerFired(TriggerType::AD_SAMPLE);
-  throttler()->TriggerFired(TriggerType::SUSPICIOUS_SITE);
-  throttler()->TriggerFired(TriggerType::SUSPICIOUS_SITE);
-
-  // The AD_SAMPLE trigger is now out of quota, while SUSPICIOUS_SITE can still
-  // fire one more time.
-  EXPECT_FALSE(throttler()->TriggerCanFire(TriggerType::AD_SAMPLE));
-  EXPECT_TRUE(throttler()->TriggerCanFire(TriggerType::SUSPICIOUS_SITE));
-
-  // Check the pref directly, it should reflect the events for each trigger.
-  PrefService* prefs = get_pref_service();
-  const base::DictionaryValue* event_dict =
-      prefs->GetDictionary(prefs::kSafeBrowsingTriggerEventTimestamps);
-
-  const std::string kAdSampleKey = "2";
-  const base::Value* ad_sample_events = event_dict->FindKey(kAdSampleKey);
-  EXPECT_EQ(3u, ad_sample_events->GetList().size());
-
-  const std::string kSuspiciousSiteKey = "4";
-  const base::Value* suspicious_site_events =
-      event_dict->FindKey(kSuspiciousSiteKey);
-  EXPECT_EQ(2u, suspicious_site_events->GetList().size());
-
-  // To simulate a new startup of the browser, we can create another throttler
-  // using the same quota configuration and pref store. It should read the
-  // events from prefs and and reflect the same status for each trigger.
-  TriggerThrottler throttler2(prefs);
-  SetQuotaForTriggerType(&throttler2, TriggerType::AD_SAMPLE, 3);
-  SetQuotaForTriggerType(&throttler2, TriggerType::SUSPICIOUS_SITE, 3);
-  EXPECT_FALSE(throttler2.TriggerCanFire(TriggerType::AD_SAMPLE));
-  EXPECT_TRUE(throttler2.TriggerCanFire(TriggerType::SUSPICIOUS_SITE));
-}
-
-class TriggerThrottlerTestFinch : public ::testing::Test {
- public:
-  void SetupQuotaParams(const TriggerType trigger_type,
-                        const std::string& group_name,
-                        int quota,
-                        base::test::ScopedFeatureList* scoped_feature_list) {
-    const base::Feature* feature = nullptr;
-    std::string param_name = "";
-    GetFeatureAndParamForTrigger(trigger_type, &feature, &param_name);
-
-    base::FieldTrialParams feature_params;
-    feature_params[param_name] =
-        GetQuotaParamValueForTrigger(trigger_type, quota);
-    scoped_feature_list->InitAndEnableFeatureWithParameters(*feature,
-                                                            feature_params);
-  }
-
-  size_t GetDailyQuotaForTrigger(const TriggerThrottler& throttler,
-                                 const TriggerType trigger_type) {
-    return throttler.GetDailyQuotaForTrigger(trigger_type);
-  }
-
- private:
-  void GetFeatureAndParamForTrigger(const TriggerType trigger_type,
-                                    const base::Feature** out_feature,
-                                    std::string* out_param) {
-    switch (trigger_type) {
-      case TriggerType::AD_SAMPLE:
-        *out_feature = &safe_browsing::kTriggerThrottlerDailyQuotaFeature;
-        *out_param = safe_browsing::kTriggerTypeAndQuotaParam;
-        break;
-
-      case TriggerType::SUSPICIOUS_SITE:
-        *out_feature = &safe_browsing::kSuspiciousSiteTriggerQuotaFeature;
-        *out_param = safe_browsing::kSuspiciousSiteTriggerQuotaParam;
-        break;
-
-      default:
-        NOTREACHED() << "Unhandled trigger type: "
-                     << static_cast<int>(trigger_type);
-    }
-  }
-
-  std::string GetQuotaParamValueForTrigger(const TriggerType trigger_type,
-                                           int quota) {
-    if (trigger_type == TriggerType::AD_SAMPLE)
-      return base::StringPrintf("%d,%d", trigger_type, quota);
-    else
-      return base::StringPrintf("%d", quota);
-  }
-};
-
-TEST_F(TriggerThrottlerTestFinch, ConfigureQuotaViaFinch) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  SetupQuotaParams(TriggerType::AD_SAMPLE, "Group_ConfigureQuotaViaFinch", 3,
-                   &scoped_feature_list);
-  // Make sure that setting the quota param via Finch params works as expected.
-
-  // The throttler has been configured (above) to allow ad samples to fire three
-  // times per day.
-  TriggerThrottler throttler(nullptr);
-
-  // First three triggers should work
-  EXPECT_TRUE(throttler.TriggerCanFire(TriggerType::AD_SAMPLE));
-  throttler.TriggerFired(TriggerType::AD_SAMPLE);
-  EXPECT_TRUE(throttler.TriggerCanFire(TriggerType::AD_SAMPLE));
-  throttler.TriggerFired(TriggerType::AD_SAMPLE);
-  EXPECT_TRUE(throttler.TriggerCanFire(TriggerType::AD_SAMPLE));
-  throttler.TriggerFired(TriggerType::AD_SAMPLE);
-
-  // Fourth attempt will fail since we're out of quota.
-  EXPECT_FALSE(throttler.TriggerCanFire(TriggerType::AD_SAMPLE));
-}
-
-TEST_F(TriggerThrottlerTestFinch, AdSamplerDefaultQuota) {
-  // Make sure that the ad sampler gets its own default quota when no finch
-  // config exists, but the quota can be overwritten through Finch.
-  TriggerThrottler throttler_default(nullptr);
-  EXPECT_EQ(kAdSamplerTriggerDefaultQuota,
-            GetDailyQuotaForTrigger(throttler_default, TriggerType::AD_SAMPLE));
-  EXPECT_TRUE(throttler_default.TriggerCanFire(TriggerType::AD_SAMPLE));
-
-  base::test::ScopedFeatureList scoped_feature_list;
-  SetupQuotaParams(TriggerType::AD_SAMPLE, "Group_AdSamplerDefaultQuota", 4,
-                   &scoped_feature_list);
-  TriggerThrottler throttler_finch(nullptr);
-  EXPECT_EQ(4u,
-            GetDailyQuotaForTrigger(throttler_finch, TriggerType::AD_SAMPLE));
-}
-
-TEST_F(TriggerThrottlerTestFinch, SuspiciousSiteTriggerDefaultQuota) {
-  // Ensure that suspicious site trigger is enabled with default quota.
-  TriggerThrottler throttler_default(nullptr);
-  EXPECT_EQ(
-      kSuspiciousSiteTriggerDefaultQuota,
-      GetDailyQuotaForTrigger(throttler_default, TriggerType::SUSPICIOUS_SITE));
-  EXPECT_TRUE(throttler_default.TriggerCanFire(TriggerType::SUSPICIOUS_SITE));
-
-  base::test::ScopedFeatureList scoped_feature_list;
-  SetupQuotaParams(TriggerType::SUSPICIOUS_SITE,
-                   "Group_SuspiciousSiteTriggerDefaultQuota", 7,
-                   &scoped_feature_list);
-  TriggerThrottler throttler_finch(nullptr);
-  EXPECT_EQ(7u, GetDailyQuotaForTrigger(throttler_finch,
-                                        TriggerType::SUSPICIOUS_SITE));
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/triggers/trigger_util.cc b/components/safe_browsing/triggers/trigger_util.cc
deleted file mode 100644
index 85a0863..0000000
--- a/components/safe_browsing/triggers/trigger_util.cc
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2019 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.
-
-// This file contains some useful utilities for the safe_browsing/triggers
-// classes
-
-#include "components/safe_browsing/triggers/trigger_util.h"
-
-#include <string>
-
-#include "content/public/browser/render_frame_host.h"
-
-namespace safe_browsing {
-
-bool DetectGoogleAd(content::RenderFrameHost* render_frame_host,
-                    const GURL& frame_url) {
-  // In case the navigation aborted, look up the RFH by the Frame Tree Node
-  // ID. It returns the committed frame host or the initial frame host for the
-  // frame if no committed host exists. Using a previous host is fine because
-  // once a frame has an ad we always consider it to have an ad.
-  // We use the unsafe method of FindFrameByFrameTreeNodeId because we're not
-  // concerned with which process the frame lives on (we only want to know if an
-  // ad could be present on the page right now).
-  if (render_frame_host) {
-    const std::string& frame_name = render_frame_host->GetFrameName();
-    if (base::StartsWith(frame_name, "google_ads_iframe",
-                         base::CompareCase::SENSITIVE) ||
-        base::StartsWith(frame_name, "google_ads_frame",
-                         base::CompareCase::SENSITIVE)) {
-      return true;
-    }
-  }
-
-  return frame_url.host_piece() == "tpc.googlesyndication.com" &&
-         base::StartsWith(frame_url.path_piece(), "/safeframe",
-                          base::CompareCase::SENSITIVE);
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/triggers/trigger_util.h b/components/safe_browsing/triggers/trigger_util.h
deleted file mode 100644
index 98babba1..0000000
--- a/components/safe_browsing/triggers/trigger_util.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_TRIGGERS_TRIGGER_UTIL_H_
-#define COMPONENTS_SAFE_BROWSING_TRIGGERS_TRIGGER_UTIL_H_
-
-#include "url/gurl.h"
-
-// This file contains some useful utilities for the safe_browsing/triggers
-// classes
-namespace content {
-class RenderFrameHost;
-}
-
-namespace safe_browsing {
-
-// Based on heuristics, guesses whether |render_frame_host| is showing a Google
-// Ad, or the |frame_url| is a Google Ad URL.
-bool DetectGoogleAd(content::RenderFrameHost* render_frame_host,
-                    const GURL& frame_url);
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_TRIGGERS_TRIGGER_UTIL_H_
diff --git a/components/safe_browsing/verdict_cache_manager.cc b/components/safe_browsing/verdict_cache_manager.cc
deleted file mode 100644
index 0f3c9fa..0000000
--- a/components/safe_browsing/verdict_cache_manager.cc
+++ /dev/null
@@ -1,664 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/verdict_cache_manager.h"
-
-#include "base/base64.h"
-#include "base/optional.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_split.h"
-#include "base/task/post_task.h"
-#include "components/history/core/browser/history_service_observer.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
-#include "components/safe_browsing/proto/csd.pb.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
-
-namespace safe_browsing {
-
-namespace {
-
-// Keys for storing password protection verdict into a DictionaryValue.
-const char kCacheCreationTime[] = "cache_creation_time";
-const char kVerdictProto[] = "verdict_proto";
-const char kRealTimeThreatInfoProto[] = "rt_threat_info_proto";
-const char kPasswordOnFocusCacheKey[] = "password_on_focus_cache_key";
-const char kRealTimeUrlCacheKey[] = "real_time_url_cache_key";
-
-// Given a URL of either http or https scheme, return its http://hostname.
-// e.g., "https://www.foo.com:80/bar/test.cgi" -> "http://www.foo.com".
-GURL GetHostNameWithHTTPScheme(const GURL& url) {
-  DCHECK(url.SchemeIsHTTPOrHTTPS());
-  std::string result(url::kHttpScheme);
-  result.append(url::kStandardSchemeSeparator).append(url.host());
-  return GURL(result);
-}
-
-// Convert a Proto object into a DictionaryValue.
-template <class T>
-std::unique_ptr<base::DictionaryValue> CreateDictionaryFromVerdict(
-    const T& verdict,
-    const base::Time& receive_time,
-    const char* proto_name) {
-  DCHECK(proto_name == kVerdictProto || proto_name == kRealTimeThreatInfoProto);
-  std::unique_ptr<base::DictionaryValue> result =
-      std::make_unique<base::DictionaryValue>();
-  result->SetInteger(kCacheCreationTime,
-                     static_cast<int>(receive_time.ToDoubleT()));
-  std::string serialized_proto(verdict.SerializeAsString());
-  // Performs a base64 encoding on the serialized proto.
-  base::Base64Encode(serialized_proto, &serialized_proto);
-  result->SetString(proto_name, serialized_proto);
-  return result;
-}
-
-// Generate path variants of the given URL.
-void GeneratePathVariantsWithoutQuery(const GURL& url,
-                                      std::vector<std::string>* paths) {
-  std::string canonical_path;
-  V4ProtocolManagerUtil::CanonicalizeUrl(url, nullptr, &canonical_path,
-                                         nullptr);
-  V4ProtocolManagerUtil::GeneratePathVariantsToCheck(canonical_path,
-                                                     std::string(), paths);
-}
-
-template <class T>
-bool ParseVerdictEntry(base::Value* verdict_entry,
-                       int* out_verdict_received_time,
-                       T* out_verdict,
-                       const char* proto_name) {
-  DCHECK(proto_name == kVerdictProto || proto_name == kRealTimeThreatInfoProto);
-
-  if (!verdict_entry || !verdict_entry->is_dict() || !out_verdict)
-    return false;
-  base::Value* cache_creation_time_value =
-      verdict_entry->FindKey(kCacheCreationTime);
-
-  if (!cache_creation_time_value || !cache_creation_time_value->is_int())
-    return false;
-  *out_verdict_received_time = cache_creation_time_value->GetInt();
-
-  base::Value* verdict_proto_value = verdict_entry->FindKey(proto_name);
-  if (!verdict_proto_value || !verdict_proto_value->is_string())
-    return false;
-  std::string serialized_proto = verdict_proto_value->GetString();
-
-  return base::Base64Decode(serialized_proto, &serialized_proto) &&
-         out_verdict->ParseFromString(serialized_proto);
-}
-
-// Return the path of the cache expression. e.g.:
-// "www.google.com"     -> ""
-// "www.google.com/abc" -> "/abc"
-// "foo.com/foo/bar/"  -> "/foo/bar/"
-std::string GetCacheExpressionPath(const std::string& cache_expression) {
-  DCHECK(!cache_expression.empty());
-  size_t first_slash_pos = cache_expression.find_first_of("/");
-  if (first_slash_pos == std::string::npos)
-    return "";
-  return cache_expression.substr(first_slash_pos);
-}
-
-// Returns the number of path segments in |cache_expression_path|.
-// For example, return 0 for "/", since there is no path after the leading
-// slash; return 3 for "/abc/def/gh.html".
-size_t GetPathDepth(const std::string& cache_expression_path) {
-  return base::SplitString(base::StringPiece(cache_expression_path), "/",
-                           base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY)
-      .size();
-}
-
-bool PathVariantsMatchCacheExpression(
-    const std::vector<std::string>& generated_paths,
-    const std::string& cache_expression_path) {
-  return base::Contains(generated_paths, cache_expression_path);
-}
-
-bool IsCacheExpired(int cache_creation_time, int cache_duration) {
-  // Note that we assume client's clock is accurate or almost accurate.
-  return base::Time::Now().ToDoubleT() >
-         static_cast<double>(cache_creation_time + cache_duration);
-}
-
-template <class T>
-size_t RemoveExpiredEntries(base::Value* verdict_dictionary,
-                            const char* proto_name) {
-  DCHECK(proto_name == kVerdictProto || proto_name == kRealTimeThreatInfoProto);
-  std::vector<std::string> expired_keys;
-  for (const auto& item : verdict_dictionary->DictItems()) {
-    int verdict_received_time;
-    T verdict;
-    if (!ParseVerdictEntry<T>(&item.second, &verdict_received_time, &verdict,
-                              proto_name) ||
-        IsCacheExpired(verdict_received_time, verdict.cache_duration_sec())) {
-      expired_keys.push_back(item.first);
-    }
-  }
-
-  for (const std::string& key : expired_keys)
-    verdict_dictionary->RemoveKey(key);
-
-  return expired_keys.size();
-}
-
-// Helper function to determine if the given origin matches content settings
-// map's patterns.
-bool OriginMatchPrimaryPattern(
-    const GURL& origin,
-    const ContentSettingsPattern& primary_pattern,
-    const ContentSettingsPattern& secondary_pattern_unused) {
-  return ContentSettingsPattern::FromURLNoWildcard(origin) == primary_pattern;
-}
-
-std::string GetKeyOfTypeFromTriggerType(
-    LoginReputationClientRequest::TriggerType trigger_type,
-    ReusedPasswordAccountType password_type) {
-  return trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE
-             ? kPasswordOnFocusCacheKey
-             : base::NumberToString(
-                   static_cast<std::underlying_type_t<
-                       ReusedPasswordAccountType::AccountType>>(
-                       password_type.account_type()));
-}
-
-template <class T>
-typename T::VerdictType GetMostMatchingCachedVerdict(
-    const GURL& url,
-    const std::string& type_key,
-    scoped_refptr<HostContentSettingsMap> content_settings,
-    const ContentSettingsType contents_setting_type,
-    const char* proto_name,
-    T* out_response) {
-  DCHECK(proto_name == kVerdictProto || proto_name == kRealTimeThreatInfoProto);
-
-  GURL hostname = GetHostNameWithHTTPScheme(url);
-  std::unique_ptr<base::DictionaryValue> cache_dictionary =
-      base::DictionaryValue::From(content_settings->GetWebsiteSetting(
-          hostname, GURL(), contents_setting_type, std::string(), nullptr));
-
-  if (!cache_dictionary || cache_dictionary->empty())
-    return T::VERDICT_TYPE_UNSPECIFIED;
-
-  base::Value* verdict_dictionary =
-      cache_dictionary->FindKeyOfType(type_key, base::Value::Type::DICTIONARY);
-  if (!verdict_dictionary) {
-    return T::VERDICT_TYPE_UNSPECIFIED;
-  }
-
-  std::vector<std::string> paths;
-  GeneratePathVariantsWithoutQuery(url, &paths);
-  int max_path_depth = -1;
-  typename T::VerdictType most_matching_verdict_type =
-      T::VERDICT_TYPE_UNSPECIFIED;
-  // For all the verdicts of the same origin, we key them by |cache_expression|.
-  // Its corresponding value is a DictionaryValue contains its creation time and
-  // the serialized verdict proto.
-  for (const auto& item : verdict_dictionary->DictItems()) {
-    int verdict_received_time;
-    T verdict;
-    // Ignore any entry that we cannot parse. These invalid entries will be
-    // cleaned up during shutdown.
-    if (!ParseVerdictEntry<T>(&item.second, &verdict_received_time, &verdict,
-                              proto_name))
-      continue;
-    // Since verdict content settings are keyed by origin, we only need to
-    // compare the path part of the cache_expression and the given url.
-    std::string cache_expression_path =
-        GetCacheExpressionPath(verdict.cache_expression());
-
-    // Finds the most specific match.
-    int path_depth = static_cast<int>(GetPathDepth(cache_expression_path));
-    if (path_depth > max_path_depth &&
-        PathVariantsMatchCacheExpression(paths, cache_expression_path)) {
-      max_path_depth = path_depth;
-      // If the most matching verdict is expired, set the result to
-      // VERDICT_TYPE_UNSPECIFIED.
-      most_matching_verdict_type =
-          IsCacheExpired(verdict_received_time, verdict.cache_duration_sec())
-              ? T::VERDICT_TYPE_UNSPECIFIED
-              : verdict.verdict_type();
-      out_response->CopyFrom(verdict);
-    }
-  }
-  return most_matching_verdict_type;
-}
-
-}  // namespace
-
-VerdictCacheManager::VerdictCacheManager(
-    history::HistoryService* history_service,
-    scoped_refptr<HostContentSettingsMap> content_settings)
-    : stored_verdict_count_password_on_focus_(base::nullopt),
-      stored_verdict_count_password_entry_(base::nullopt),
-      content_settings_(content_settings) {
-  if (history_service)
-    history_service_observer_.Add(history_service);
-}
-
-VerdictCacheManager::~VerdictCacheManager() {
-  CleanUpExpiredVerdicts();
-  history_service_observer_.RemoveAll();
-  weak_factory_.InvalidateWeakPtrs();
-}
-
-void VerdictCacheManager::CachePhishGuardVerdict(
-    const GURL& url,
-    LoginReputationClientRequest::TriggerType trigger_type,
-    ReusedPasswordAccountType password_type,
-    const LoginReputationClientResponse& verdict,
-    const base::Time& receive_time) {
-  DCHECK(content_settings_);
-  DCHECK(trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
-         trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
-
-  GURL hostname = GetHostNameWithHTTPScheme(url);
-
-  std::unique_ptr<base::DictionaryValue> cache_dictionary =
-      base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
-          hostname, GURL(), ContentSettingsType::PASSWORD_PROTECTION,
-          std::string(), nullptr));
-
-  if (!cache_dictionary)
-    cache_dictionary = std::make_unique<base::DictionaryValue>();
-
-  std::unique_ptr<base::DictionaryValue> verdict_entry(
-      CreateDictionaryFromVerdict<LoginReputationClientResponse>(
-          verdict, receive_time, kVerdictProto));
-
-  std::string type_key =
-      GetKeyOfTypeFromTriggerType(trigger_type, password_type);
-  base::Value* verdict_dictionary =
-      cache_dictionary->FindKeyOfType(type_key, base::Value::Type::DICTIONARY);
-  if (!verdict_dictionary) {
-    verdict_dictionary = cache_dictionary->SetKey(
-        type_key, base::Value(base::Value::Type::DICTIONARY));
-  }
-
-  // Increases stored verdict count if we haven't seen this cache expression
-  // before.
-  if (!verdict_dictionary->FindKey(verdict.cache_expression())) {
-    base::Optional<size_t>* stored_verdict_count =
-        trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE
-            ? &stored_verdict_count_password_on_focus_
-            : &stored_verdict_count_password_entry_;
-    *stored_verdict_count = GetStoredPhishGuardVerdictCount(trigger_type) + 1;
-  }
-
-  // If same cache_expression is already in this verdict_dictionary, we simply
-  // override it.
-  verdict_dictionary->SetKey(
-      verdict.cache_expression(),
-      base::Value::FromUniquePtrValue(std::move(verdict_entry)));
-  content_settings_->SetWebsiteSettingDefaultScope(
-      hostname, GURL(), ContentSettingsType::PASSWORD_PROTECTION, std::string(),
-      std::move(cache_dictionary));
-}
-
-LoginReputationClientResponse::VerdictType
-VerdictCacheManager::GetCachedPhishGuardVerdict(
-    const GURL& url,
-    LoginReputationClientRequest::TriggerType trigger_type,
-    ReusedPasswordAccountType password_type,
-    LoginReputationClientResponse* out_response) {
-  DCHECK(trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
-         trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
-
-  std::string type_key =
-      GetKeyOfTypeFromTriggerType(trigger_type, password_type);
-
-  return GetMostMatchingCachedVerdict<LoginReputationClientResponse>(
-      url, type_key, content_settings_,
-      ContentSettingsType::PASSWORD_PROTECTION, kVerdictProto, out_response);
-}
-
-size_t VerdictCacheManager::GetStoredPhishGuardVerdictCount(
-    LoginReputationClientRequest::TriggerType trigger_type) {
-  DCHECK(content_settings_);
-  DCHECK(trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
-         trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
-  base::Optional<size_t>* stored_verdict_count =
-      trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE
-          ? &stored_verdict_count_password_on_focus_
-          : &stored_verdict_count_password_entry_;
-  // If we have already computed this, return its value.
-  if (stored_verdict_count->has_value())
-    return stored_verdict_count->value();
-
-  ContentSettingsForOneType password_protection_settings;
-  content_settings_->GetSettingsForOneType(
-      ContentSettingsType::PASSWORD_PROTECTION, std::string(),
-      &password_protection_settings);
-  stored_verdict_count_password_on_focus_ = 0;
-  stored_verdict_count_password_entry_ = 0;
-  if (password_protection_settings.empty())
-    return 0;
-
-  for (const ContentSettingPatternSource& source :
-       password_protection_settings) {
-    std::unique_ptr<base::DictionaryValue> cache_dictionary =
-        base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
-            GURL(source.primary_pattern.ToString()), GURL(),
-            ContentSettingsType::PASSWORD_PROTECTION, std::string(), nullptr));
-    if (cache_dictionary.get() && !cache_dictionary->empty()) {
-      for (const auto& item : cache_dictionary->DictItems()) {
-        if (item.first == base::StringPiece(kPasswordOnFocusCacheKey)) {
-          stored_verdict_count_password_on_focus_.value() +=
-              item.second.DictSize();
-        } else {
-          stored_verdict_count_password_entry_.value() +=
-              item.second.DictSize();
-        }
-      }
-    }
-  }
-
-  return stored_verdict_count->value();
-}
-
-void VerdictCacheManager::CacheRealTimeUrlVerdict(
-    const GURL& url,
-    const RTLookupResponse& verdict,
-    const base::Time& receive_time) {
-  GURL hostname = GetHostNameWithHTTPScheme(url);
-  std::unique_ptr<base::DictionaryValue> cache_dictionary =
-      base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
-          hostname, GURL(), ContentSettingsType::SAFE_BROWSING_URL_CHECK_DATA,
-          std::string(), nullptr));
-
-  if (!cache_dictionary)
-    cache_dictionary = std::make_unique<base::DictionaryValue>();
-
-  base::Value* verdict_dictionary = cache_dictionary->FindKeyOfType(
-      kRealTimeUrlCacheKey, base::Value::Type::DICTIONARY);
-  if (!verdict_dictionary) {
-    verdict_dictionary = cache_dictionary->SetKey(
-        kRealTimeUrlCacheKey, base::Value(base::Value::Type::DICTIONARY));
-  }
-
-  std::vector<std::string> visited_cache_expressions;
-  for (const auto& threat_info : verdict.threat_info()) {
-    std::string cache_expression = threat_info.cache_expression();
-    // TODO(crbug.com/1033692): For the same cache_expression, threat_info is in
-    // decreasing order of severity. To avoid lower severity threat being
-    // overridden by higher one, only store threat info that is first seen for a
-    // cache expression.
-    if (base::Contains(visited_cache_expressions, cache_expression))
-      continue;
-    std::unique_ptr<base::DictionaryValue> threat_info_entry(
-        CreateDictionaryFromVerdict<RTLookupResponse::ThreatInfo>(
-            threat_info, receive_time, kRealTimeThreatInfoProto));
-    // Increases stored verdict count if we haven't seen this cache expression
-    // before.
-    if (!verdict_dictionary->FindKey(cache_expression)) {
-      stored_verdict_count_real_time_url_check_++;
-    }
-    verdict_dictionary->SetKey(
-        cache_expression,
-        base::Value::FromUniquePtrValue(std::move(threat_info_entry)));
-    visited_cache_expressions.push_back(cache_expression);
-  }
-  content_settings_->SetWebsiteSettingDefaultScope(
-      hostname, GURL(), ContentSettingsType::SAFE_BROWSING_URL_CHECK_DATA,
-      std::string(), std::move(cache_dictionary));
-}
-
-RTLookupResponse::ThreatInfo::VerdictType
-VerdictCacheManager::GetCachedRealTimeUrlVerdict(
-    const GURL& url,
-    RTLookupResponse::ThreatInfo* out_threat_info) {
-  return GetMostMatchingCachedVerdict<RTLookupResponse::ThreatInfo>(
-      url, kRealTimeUrlCacheKey, content_settings_,
-      ContentSettingsType::SAFE_BROWSING_URL_CHECK_DATA,
-      kRealTimeThreatInfoProto, out_threat_info);
-}
-
-void VerdictCacheManager::CleanUpExpiredVerdicts() {
-  DCHECK(content_settings_);
-
-  CleanUpExpiredPhishGuardVerdicts();
-  CleanUpExpiredRealTimeUrlCheckVerdicts();
-}
-
-void VerdictCacheManager::CleanUpExpiredPhishGuardVerdicts() {
-  if (GetStoredPhishGuardVerdictCount(
-          LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) <= 0 &&
-      GetStoredPhishGuardVerdictCount(
-          LoginReputationClientRequest::PASSWORD_REUSE_EVENT) <= 0)
-    return;
-
-  ContentSettingsForOneType password_protection_settings;
-  content_settings_->GetSettingsForOneType(
-      ContentSettingsType::PASSWORD_PROTECTION, std::string(),
-      &password_protection_settings);
-
-  for (const ContentSettingPatternSource& source :
-       password_protection_settings) {
-    GURL primary_pattern_url = GURL(source.primary_pattern.ToString());
-    // Find all verdicts associated with this origin.
-    std::unique_ptr<base::DictionaryValue> cache_dictionary =
-        base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
-            primary_pattern_url, GURL(),
-            ContentSettingsType::PASSWORD_PROTECTION, std::string(), nullptr));
-    bool has_expired_password_on_focus_entry = RemoveExpiredPhishGuardVerdicts(
-        LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
-        cache_dictionary.get());
-    bool has_expired_password_reuse_entry = RemoveExpiredPhishGuardVerdicts(
-        LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-        cache_dictionary.get());
-
-    if (cache_dictionary->size() == 0u) {
-      content_settings_->ClearSettingsForOneTypeWithPredicate(
-          ContentSettingsType::PASSWORD_PROTECTION, base::Time(),
-          base::Time::Max(),
-          base::BindRepeating(&OriginMatchPrimaryPattern, primary_pattern_url));
-    } else if (has_expired_password_on_focus_entry ||
-               has_expired_password_reuse_entry) {
-      // Set the website setting of this origin with the updated
-      // |cache_dictionary|.
-      content_settings_->SetWebsiteSettingDefaultScope(
-          primary_pattern_url, GURL(), ContentSettingsType::PASSWORD_PROTECTION,
-          std::string(), std::move(cache_dictionary));
-    }
-  }
-}
-
-void VerdictCacheManager::CleanUpExpiredRealTimeUrlCheckVerdicts() {
-  ContentSettingsForOneType safe_browsing_url_check_data_settings;
-  content_settings_->GetSettingsForOneType(
-      ContentSettingsType::SAFE_BROWSING_URL_CHECK_DATA, std::string(),
-      &safe_browsing_url_check_data_settings);
-
-  for (const ContentSettingPatternSource& source :
-       safe_browsing_url_check_data_settings) {
-    GURL primary_pattern_url = GURL(source.primary_pattern.ToString());
-    // Find all verdicts associated with this origin.
-    std::unique_ptr<base::DictionaryValue> cache_dictionary =
-        base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
-            primary_pattern_url, GURL(),
-            ContentSettingsType::SAFE_BROWSING_URL_CHECK_DATA, std::string(),
-            nullptr));
-    bool has_expired_entry =
-        RemoveExpiredRealTimeUrlCheckVerdicts(cache_dictionary.get());
-
-    if (cache_dictionary->size() == 0u) {
-      content_settings_->ClearSettingsForOneTypeWithPredicate(
-          ContentSettingsType::SAFE_BROWSING_URL_CHECK_DATA, base::Time(),
-          base::Time::Max(),
-          base::BindRepeating(&OriginMatchPrimaryPattern, primary_pattern_url));
-    } else if (has_expired_entry) {
-      // Set the website setting of this origin with the updated
-      // |cache_dictionary|.
-      content_settings_->SetWebsiteSettingDefaultScope(
-          primary_pattern_url, GURL(),
-          ContentSettingsType::SAFE_BROWSING_URL_CHECK_DATA, std::string(),
-          std::move(cache_dictionary));
-    }
-  }
-}
-
-// Overridden from history::HistoryServiceObserver.
-void VerdictCacheManager::OnURLsDeleted(
-    history::HistoryService* history_service,
-    const history::DeletionInfo& deletion_info) {
-  base::PostTask(FROM_HERE, {content::BrowserThread::UI},
-                 base::BindRepeating(
-                     &VerdictCacheManager::RemoveContentSettingsOnURLsDeleted,
-                     GetWeakPtr(), deletion_info.IsAllHistory(),
-                     deletion_info.deleted_rows()));
-}
-
-// Overridden from history::HistoryServiceObserver.
-void VerdictCacheManager::HistoryServiceBeingDeleted(
-    history::HistoryService* history_service) {
-  history_service_observer_.Remove(history_service);
-}
-
-bool VerdictCacheManager::RemoveExpiredPhishGuardVerdicts(
-    LoginReputationClientRequest::TriggerType trigger_type,
-    base::DictionaryValue* cache_dictionary) {
-  DCHECK(trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
-         trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
-  if (!cache_dictionary || cache_dictionary->empty())
-    return false;
-
-  size_t verdicts_removed = 0;
-  std::vector<std::string> empty_keys;
-  for (auto item : cache_dictionary->DictItems()) {
-    if (trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE &&
-        item.first == std::string(kPasswordOnFocusCacheKey)) {
-      size_t removed_cnt = RemoveExpiredEntries<LoginReputationClientResponse>(
-          &item.second, kVerdictProto);
-      verdicts_removed += removed_cnt;
-      if (stored_verdict_count_password_on_focus_.has_value())
-        stored_verdict_count_password_on_focus_.value() -= removed_cnt;
-    } else {
-      size_t removed_cnt = RemoveExpiredEntries<LoginReputationClientResponse>(
-          &item.second, kVerdictProto);
-      verdicts_removed += removed_cnt;
-      if (stored_verdict_count_password_entry_.has_value())
-        stored_verdict_count_password_entry_.value() -= removed_cnt;
-    }
-
-    if (item.second.DictSize() == 0U)
-      empty_keys.push_back(item.first);
-  }
-  for (const auto& key : empty_keys)
-    cache_dictionary->RemoveKey(key);
-
-  return verdicts_removed > 0U;
-}
-
-bool VerdictCacheManager::RemoveExpiredRealTimeUrlCheckVerdicts(
-    base::DictionaryValue* cache_dictionary) {
-  if (!cache_dictionary || cache_dictionary->empty())
-    return false;
-
-  size_t verdicts_removed = 0;
-  std::vector<std::string> empty_keys;
-  for (auto item : cache_dictionary->DictItems()) {
-    size_t removed_cnt = RemoveExpiredEntries<RTLookupResponse::ThreatInfo>(
-        &item.second, kRealTimeThreatInfoProto);
-    verdicts_removed += removed_cnt;
-    stored_verdict_count_real_time_url_check_ -= removed_cnt;
-    if (item.second.DictSize() == 0U)
-      empty_keys.push_back(item.first);
-  }
-  for (const auto& key : empty_keys)
-    cache_dictionary->RemoveKey(key);
-
-  return verdicts_removed > 0U;
-}
-
-void VerdictCacheManager::RemoveContentSettingsOnURLsDeleted(
-    bool all_history,
-    const history::URLRows& deleted_rows) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  DCHECK(content_settings_);
-
-  if (all_history) {
-    content_settings_->ClearSettingsForOneType(
-        ContentSettingsType::PASSWORD_PROTECTION);
-    stored_verdict_count_password_on_focus_ = 0;
-    stored_verdict_count_password_entry_ = 0;
-    stored_verdict_count_real_time_url_check_ = 0;
-    content_settings_->ClearSettingsForOneType(
-        ContentSettingsType::SAFE_BROWSING_URL_CHECK_DATA);
-    return;
-  }
-
-  // For now, if a URL is deleted from history, we simply remove all the
-  // cached verdicts of the same origin. This is a pretty aggressive deletion.
-  // We might revisit this logic later to decide if we want to only delete the
-  // cached verdict whose cache expression matches this URL.
-  for (const history::URLRow& row : deleted_rows) {
-    if (!row.url().SchemeIsHTTPOrHTTPS())
-      continue;
-
-    GURL url_key = GetHostNameWithHTTPScheme(row.url());
-    stored_verdict_count_password_on_focus_ =
-        GetStoredPhishGuardVerdictCount(
-            LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) -
-        GetPhishGuardVerdictCountForURL(
-            url_key, LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE);
-    stored_verdict_count_password_entry_ =
-        GetStoredPhishGuardVerdictCount(
-            LoginReputationClientRequest::PASSWORD_REUSE_EVENT) -
-        GetPhishGuardVerdictCountForURL(
-            url_key, LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
-    stored_verdict_count_real_time_url_check_ -=
-        GetRealTimeUrlCheckVerdictCountForURL(url_key);
-    content_settings_->ClearSettingsForOneTypeWithPredicate(
-        ContentSettingsType::PASSWORD_PROTECTION, base::Time(),
-        base::Time::Max(),
-        base::BindRepeating(&OriginMatchPrimaryPattern, url_key));
-    content_settings_->ClearSettingsForOneTypeWithPredicate(
-        ContentSettingsType::SAFE_BROWSING_URL_CHECK_DATA, base::Time(),
-        base::Time::Max(),
-        base::BindRepeating(&OriginMatchPrimaryPattern, url_key));
-  }
-}
-
-size_t VerdictCacheManager::GetPhishGuardVerdictCountForURL(
-    const GURL& url,
-    LoginReputationClientRequest::TriggerType trigger_type) {
-  DCHECK(trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
-         trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
-  std::unique_ptr<base::DictionaryValue> cache_dictionary =
-      base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
-          url, GURL(), ContentSettingsType::PASSWORD_PROTECTION, std::string(),
-          nullptr));
-  if (!cache_dictionary || cache_dictionary->empty())
-    return 0;
-
-  int verdict_cnt = 0;
-  if (trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) {
-    base::Value* password_on_focus_dict = nullptr;
-    password_on_focus_dict =
-        cache_dictionary->FindKey(kPasswordOnFocusCacheKey);
-    verdict_cnt +=
-        password_on_focus_dict ? password_on_focus_dict->DictSize() : 0;
-  } else {
-    for (const auto& item : cache_dictionary->DictItems()) {
-      if (item.first == kPasswordOnFocusCacheKey)
-        continue;
-      verdict_cnt += item.second.DictSize();
-    }
-  }
-  return verdict_cnt;
-}
-
-size_t VerdictCacheManager::GetRealTimeUrlCheckVerdictCountForURL(
-    const GURL& url) {
-  std::unique_ptr<base::DictionaryValue> cache_dictionary =
-      base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
-          url, GURL(), ContentSettingsType::PASSWORD_PROTECTION, std::string(),
-          nullptr));
-  if (!cache_dictionary || cache_dictionary->empty())
-    return 0;
-  base::Value* verdict_dictionary =
-      cache_dictionary->FindKey(kRealTimeUrlCacheKey);
-  return verdict_dictionary ? verdict_dictionary->DictSize() : 0;
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/verdict_cache_manager.h b/components/safe_browsing/verdict_cache_manager.h
deleted file mode 100644
index f475889..0000000
--- a/components/safe_browsing/verdict_cache_manager.h
+++ /dev/null
@@ -1,147 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_VERDICT_CACHE_MANAGER_H_
-#define COMPONENTS_SAFE_BROWSING_VERDICT_CACHE_MANAGER_H_
-
-#include "base/gtest_prod_util.h"
-#include "base/memory/scoped_refptr.h"
-#include "base/memory/weak_ptr.h"
-#include "base/scoped_observer.h"
-#include "base/time/time.h"
-#include "base/values.h"
-#include "components/content_settings/core/browser/host_content_settings_map.h"
-#include "components/history/core/browser/history_service.h"
-#include "components/history/core/browser/history_service_observer.h"
-#include "components/safe_browsing/proto/csd.pb.h"
-#include "components/safe_browsing/proto/realtimeapi.pb.h"
-#include "url/gurl.h"
-
-class HostContentSettingsMap;
-
-namespace safe_browsing {
-
-using ReusedPasswordAccountType =
-    LoginReputationClientRequest::PasswordReuseEvent::ReusedPasswordAccountType;
-
-class VerdictCacheManager : public history::HistoryServiceObserver {
- public:
-  explicit VerdictCacheManager(
-      history::HistoryService* history_service,
-      scoped_refptr<HostContentSettingsMap> content_settings);
-  VerdictCacheManager(const VerdictCacheManager&) = delete;
-  VerdictCacheManager& operator=(const VerdictCacheManager&) = delete;
-  VerdictCacheManager(VerdictCacheManager&&) = delete;
-  VerdictCacheManager& operator=(const VerdictCacheManager&&) = delete;
-
-  ~VerdictCacheManager() override;
-
-  base::WeakPtr<VerdictCacheManager> GetWeakPtr() {
-    return weak_factory_.GetWeakPtr();
-  }
-
-  // Stores |verdict| in |content_settings_| based on its |trigger_type|, |url|,
-  // reused |password_type|, |verdict| and |receive_time|.
-  void CachePhishGuardVerdict(
-      const GURL& url,
-      LoginReputationClientRequest::TriggerType trigger_type,
-      ReusedPasswordAccountType password_type,
-      const LoginReputationClientResponse& verdict,
-      const base::Time& receive_time);
-
-  // Looks up |content_settings_| to find the cached verdict response. If
-  // verdict is not available or is expired, return VERDICT_TYPE_UNSPECIFIED.
-  // Can be called on any thread.
-  LoginReputationClientResponse::VerdictType GetCachedPhishGuardVerdict(
-      const GURL& url,
-      LoginReputationClientRequest::TriggerType trigger_type,
-      ReusedPasswordAccountType password_type,
-      LoginReputationClientResponse* out_response);
-
-  // Gets the total number of verdicts of the specified |trigger_type| we cached
-  // for this profile. This counts both expired and active verdicts.
-  size_t GetStoredPhishGuardVerdictCount(
-      LoginReputationClientRequest::TriggerType trigger_type);
-
-  // Stores |verdict| in |content_settings_| based on its |url|, |verdict| and
-  // |receive_time|.
-  void CacheRealTimeUrlVerdict(const GURL& url,
-                               const RTLookupResponse& verdict,
-                               const base::Time& receive_time);
-
-  // Looks up |content_settings_| to find the cached verdict response. If
-  // verdict is not available or is expired, return VERDICT_TYPE_UNSPECIFIED.
-  // Otherwise, the most matching theat info will be copied to out_threat_info.
-  // Can be called on any thread.
-  RTLookupResponse::ThreatInfo::VerdictType GetCachedRealTimeUrlVerdict(
-      const GURL& url,
-      RTLookupResponse::ThreatInfo* out_threat_info);
-
-  // Overridden from history::HistoryServiceObserver.
-  void OnURLsDeleted(history::HistoryService* history_service,
-                     const history::DeletionInfo& deletion_info) override;
-
-  void HistoryServiceBeingDeleted(
-      history::HistoryService* history_service) override;
-
- private:
-  FRIEND_TEST_ALL_PREFIXES(VerdictCacheManagerTest, TestCleanUpExpiredVerdict);
-  FRIEND_TEST_ALL_PREFIXES(VerdictCacheManagerTest,
-                           TestCleanUpExpiredVerdictWithInvalidEntry);
-  FRIEND_TEST_ALL_PREFIXES(VerdictCacheManagerTest,
-                           TestRemoveCachedVerdictOnURLsDeleted);
-  FRIEND_TEST_ALL_PREFIXES(
-      VerdictCacheManagerTest,
-      TestRemoveRealTimeUrlCheckCachedVerdictOnURLsDeleted);
-
-  // Removes all the expired verdicts from cache.
-  void CleanUpExpiredVerdicts();
-  void CleanUpExpiredPhishGuardVerdicts();
-  void CleanUpExpiredRealTimeUrlCheckVerdicts();
-
-  // Helper method to remove content settings when URLs are deleted. If
-  // |all_history| is true, removes all cached verdicts. Otherwise it removes
-  // all verdicts associated with the deleted URLs in |deleted_rows|.
-  void RemoveContentSettingsOnURLsDeleted(bool all_history,
-                                          const history::URLRows& deleted_rows);
-  bool RemoveExpiredPhishGuardVerdicts(
-      LoginReputationClientRequest::TriggerType trigger_type,
-      base::DictionaryValue* cache_dictionary);
-  bool RemoveExpiredRealTimeUrlCheckVerdicts(
-      base::DictionaryValue* cache_dictionary);
-
-  size_t GetPhishGuardVerdictCountForURL(
-      const GURL& url,
-      LoginReputationClientRequest::TriggerType trigger_type);
-  // This method is only used for testing.
-  size_t GetRealTimeUrlCheckVerdictCountForURL(const GURL& url);
-
-  // This method is only used for testing.
-  int GetStoredRealTimeUrlCheckVerdictCount() {
-    return stored_verdict_count_real_time_url_check_;
-  }
-
-  // Number of verdict stored for this profile for password on focus pings.
-  base::Optional<size_t> stored_verdict_count_password_on_focus_;
-
-  // Number of verdict stored for this profile for protected password entry
-  // pings.
-  base::Optional<size_t> stored_verdict_count_password_entry_;
-
-  // Number of verdict stored for this profile for real time url check pings.
-  // This is only used for testing.
-  int stored_verdict_count_real_time_url_check_ = 0;
-
-  ScopedObserver<history::HistoryService, history::HistoryServiceObserver>
-      history_service_observer_{this};
-
-  // Content settings maps associated with this instance.
-  scoped_refptr<HostContentSettingsMap> content_settings_;
-
-  base::WeakPtrFactory<VerdictCacheManager> weak_factory_{this};
-};
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_VERDICT_CACHE_MANAGER_H_
diff --git a/components/safe_browsing/verdict_cache_manager_unittest.cc b/components/safe_browsing/verdict_cache_manager_unittest.cc
deleted file mode 100644
index 1e6c9f9..0000000
--- a/components/safe_browsing/verdict_cache_manager_unittest.cc
+++ /dev/null
@@ -1,549 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/verdict_cache_manager.h"
-
-#include "base/base64.h"
-#include "base/memory/scoped_refptr.h"
-#include "base/values.h"
-#include "components/content_settings/core/browser/host_content_settings_map.h"
-#include "components/safe_browsing/proto/csd.pb.h"
-#include "components/safe_browsing/proto/realtimeapi.pb.h"
-#include "components/sync_preferences/testing_pref_service_syncable.h"
-#include "content/public/test/browser_task_environment.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace safe_browsing {
-
-class VerdictCacheManagerTest : public ::testing::Test {
- public:
-  VerdictCacheManagerTest() {}
-
-  void SetUp() override {
-    HostContentSettingsMap::RegisterProfilePrefs(test_pref_service_.registry());
-    content_setting_map_ = new HostContentSettingsMap(
-        &test_pref_service_, false /* is_off_the_record */,
-        false /* store_last_modified */,
-        false /* migrate_requesting_and_top_level_origin_settings */);
-    cache_manager_ = std::make_unique<VerdictCacheManager>(
-        nullptr, content_setting_map_.get());
-  }
-
-  void TearDown() override {
-    cache_manager_.reset();
-    content_setting_map_->ShutdownOnUIThread();
-  }
-
-  void CachePhishGuardVerdict(
-      const GURL& url,
-      LoginReputationClientRequest::TriggerType trigger,
-      ReusedPasswordAccountType password_type,
-      LoginReputationClientResponse::VerdictType verdict,
-      int cache_duration_sec,
-      const std::string& cache_expression,
-      const base::Time& verdict_received_time) {
-    ASSERT_FALSE(cache_expression.empty());
-    LoginReputationClientResponse response;
-    response.set_verdict_type(verdict);
-    response.set_cache_expression(cache_expression);
-    response.set_cache_duration_sec(cache_duration_sec);
-    cache_manager_->CachePhishGuardVerdict(url, trigger, password_type,
-                                           response, verdict_received_time);
-  }
-
-  void AddThreatInfoToResponse(
-      RTLookupResponse& response,
-      RTLookupResponse::ThreatInfo::VerdictType verdict_type,
-      RTLookupResponse::ThreatInfo::ThreatType threat_type,
-      int cache_duration_sec,
-      const std::string& cache_expression) {
-    RTLookupResponse::ThreatInfo* new_threat_info = response.add_threat_info();
-    new_threat_info->set_verdict_type(verdict_type);
-    new_threat_info->set_threat_type(threat_type);
-    new_threat_info->set_cache_duration_sec(cache_duration_sec);
-    new_threat_info->set_cache_expression(cache_expression);
-  }
-
- protected:
-  std::unique_ptr<VerdictCacheManager> cache_manager_;
-  scoped_refptr<HostContentSettingsMap> content_setting_map_;
-
- private:
-  content::BrowserTaskEnvironment task_environment_;
-  sync_preferences::TestingPrefServiceSyncable test_pref_service_;
-};
-
-TEST_F(VerdictCacheManagerTest, TestCanRetrieveCachedVerdict) {
-  GURL url("https://www.google.com/");
-  ReusedPasswordAccountType password_type;
-  password_type.set_account_type(ReusedPasswordAccountType::GSUITE);
-  password_type.set_is_account_syncing(true);
-  LoginReputationClientResponse cached_verdict;
-  cached_verdict.set_cache_expression("www.google.com/");
-  EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
-            cache_manager_->GetCachedPhishGuardVerdict(
-                url, LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-                password_type, &cached_verdict));
-
-  CachePhishGuardVerdict(url,
-                         LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-                         password_type, LoginReputationClientResponse::SAFE, 60,
-                         "www.google.com/", base::Time::Now());
-
-  EXPECT_EQ(LoginReputationClientResponse::SAFE,
-            cache_manager_->GetCachedPhishGuardVerdict(
-                url, LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-                password_type, &cached_verdict));
-}
-
-TEST_F(VerdictCacheManagerTest, TestCacheSplitByTriggerType) {
-  GURL url("https://www.google.com/");
-  ReusedPasswordAccountType password_type;
-  password_type.set_account_type(ReusedPasswordAccountType::GSUITE);
-  password_type.set_is_account_syncing(true);
-  LoginReputationClientResponse cached_verdict;
-  cached_verdict.set_cache_expression("www.google.com/");
-  EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
-            cache_manager_->GetCachedPhishGuardVerdict(
-                url, LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-                password_type, &cached_verdict));
-
-  CachePhishGuardVerdict(url,
-                         LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
-                         password_type, LoginReputationClientResponse::SAFE, 60,
-                         "www.google.com/", base::Time::Now());
-
-  EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
-            cache_manager_->GetCachedPhishGuardVerdict(
-                url, LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-                password_type, &cached_verdict));
-}
-
-TEST_F(VerdictCacheManagerTest, TestCacheSplitByPasswordType) {
-  GURL url("https://www.google.com/");
-  ReusedPasswordAccountType password_type;
-  password_type.set_account_type(ReusedPasswordAccountType::GSUITE);
-  password_type.set_is_account_syncing(true);
-  LoginReputationClientResponse cached_verdict;
-  cached_verdict.set_cache_expression("www.google.com/");
-  EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
-            cache_manager_->GetCachedPhishGuardVerdict(
-                url, LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-                password_type, &cached_verdict));
-
-  password_type.set_account_type(
-      ReusedPasswordAccountType::NON_GAIA_ENTERPRISE);
-  CachePhishGuardVerdict(url,
-                         LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
-                         password_type, LoginReputationClientResponse::SAFE, 60,
-                         "www.google.com/", base::Time::Now());
-  password_type.set_account_type(ReusedPasswordAccountType::GSUITE);
-  EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
-            cache_manager_->GetCachedPhishGuardVerdict(
-                url, LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-                password_type, &cached_verdict));
-}
-
-TEST_F(VerdictCacheManagerTest, TestGetStoredPhishGuardVerdictCount) {
-  GURL url("https://www.google.com/");
-
-  LoginReputationClientResponse cached_verdict;
-  cached_verdict.set_cache_expression("www.google.com/");
-  EXPECT_EQ(0u, cache_manager_->GetStoredPhishGuardVerdictCount(
-                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
-  ReusedPasswordAccountType password_type;
-  password_type.set_account_type(
-      ReusedPasswordAccountType::NON_GAIA_ENTERPRISE);
-  password_type.set_is_account_syncing(true);
-  CachePhishGuardVerdict(url,
-                         LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-                         password_type, LoginReputationClientResponse::SAFE, 60,
-                         "www.google.com/", base::Time::Now());
-
-  EXPECT_EQ(1u, cache_manager_->GetStoredPhishGuardVerdictCount(
-                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
-
-  CachePhishGuardVerdict(url,
-                         LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-                         password_type, LoginReputationClientResponse::SAFE, 60,
-                         "www.google.com/", base::Time::Now());
-
-  EXPECT_EQ(1u, cache_manager_->GetStoredPhishGuardVerdictCount(
-                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
-
-  CachePhishGuardVerdict(url,
-                         LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-                         password_type, LoginReputationClientResponse::SAFE, 60,
-                         "www.google.com/path", base::Time::Now());
-
-  EXPECT_EQ(2u, cache_manager_->GetStoredPhishGuardVerdictCount(
-                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
-}
-
-TEST_F(VerdictCacheManagerTest, TestParseInvalidVerdictEntry) {
-  // Directly save an invalid cache entry.
-  LoginReputationClientResponse verdict;
-  verdict.set_verdict_type(LoginReputationClientResponse::SAFE);
-  verdict.set_cache_expression("www.google.com/");
-  verdict.set_cache_duration_sec(60);
-
-  std::string verdict_serialized;
-  verdict.SerializeToString(&verdict_serialized);
-  base::Base64Encode(verdict_serialized, &verdict_serialized);
-
-  auto cache_dictionary = std::make_unique<base::DictionaryValue>();
-  auto* verdict_dictionary =
-      cache_dictionary->SetKey("2", base::Value(base::Value::Type::DICTIONARY));
-  auto* verdict_entry = verdict_dictionary->SetKey(
-      "www.google.com/", base::Value(base::Value::Type::DICTIONARY));
-  verdict_entry->SetStringKey("cache_creation_time", "invalid_time");
-  verdict_entry->SetStringKey("verdict_proto", verdict_serialized);
-
-  content_setting_map_->SetWebsiteSettingDefaultScope(
-      GURL("http://www.google.com/"), GURL(),
-      ContentSettingsType::PASSWORD_PROTECTION, std::string(),
-      std::move(cache_dictionary));
-
-  ReusedPasswordAccountType password_type;
-  password_type.set_account_type(ReusedPasswordAccountType::GSUITE);
-  password_type.set_is_account_syncing(true);
-
-  LoginReputationClientResponse cached_verdict;
-  EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
-            cache_manager_->GetCachedPhishGuardVerdict(
-                GURL("https://www.google.com/"),
-                LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-                password_type, &cached_verdict));
-}
-
-TEST_F(VerdictCacheManagerTest, TestRemoveCachedVerdictOnURLsDeleted) {
-  ASSERT_EQ(0u, cache_manager_->GetStoredPhishGuardVerdictCount(
-                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
-  ASSERT_EQ(0u, cache_manager_->GetStoredPhishGuardVerdictCount(
-                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
-  // Prepare 5 verdicts. Three are for origin "http://foo.com", and the others
-  // are for "http://bar.com".
-  base::Time now = base::Time::Now();
-  ReusedPasswordAccountType password_type;
-  password_type.set_account_type(ReusedPasswordAccountType::GSUITE);
-  CachePhishGuardVerdict(
-      GURL("http://foo.com/abc/index.jsp"),
-      LoginReputationClientRequest::PASSWORD_REUSE_EVENT, password_type,
-      LoginReputationClientResponse::LOW_REPUTATION, 600, "foo.com/abc/", now);
-  password_type.set_account_type(
-      ReusedPasswordAccountType::NON_GAIA_ENTERPRISE);
-  CachePhishGuardVerdict(
-      GURL("http://foo.com/abc/index.jsp"),
-      LoginReputationClientRequest::PASSWORD_REUSE_EVENT, password_type,
-      LoginReputationClientResponse::LOW_REPUTATION, 600, "foo.com/abc/", now);
-  password_type.set_account_type(ReusedPasswordAccountType::GSUITE);
-  CachePhishGuardVerdict(GURL("http://bar.com/index.jsp"),
-                         LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-                         password_type, LoginReputationClientResponse::PHISHING,
-                         600, "bar.com", now);
-  ASSERT_EQ(3u, cache_manager_->GetStoredPhishGuardVerdictCount(
-                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
-
-  password_type.set_account_type(ReusedPasswordAccountType::UNKNOWN);
-  CachePhishGuardVerdict(
-      GURL("http://foo.com/abc/index.jsp"),
-      LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE, password_type,
-      LoginReputationClientResponse::LOW_REPUTATION, 600, "foo.com/abc/", now);
-  CachePhishGuardVerdict(GURL("http://bar.com/index.jsp"),
-                         LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
-                         password_type, LoginReputationClientResponse::PHISHING,
-                         600, "bar.com", now);
-  ASSERT_EQ(2u, cache_manager_->GetStoredPhishGuardVerdictCount(
-                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
-
-  // Delete a bar.com URL. Corresponding content setting keyed on
-  // origin "http://bar.com" should be removed,
-  history::URLRows deleted_urls;
-  deleted_urls.push_back(history::URLRow(GURL("http://bar.com")));
-
-  // Delete an arbitrary data URL, to ensure the service is robust against
-  // filtering only http/s URLs. See crbug.com/709758.
-  deleted_urls.push_back(history::URLRow(GURL("data:text/html, <p>hellow")));
-
-  cache_manager_->RemoveContentSettingsOnURLsDeleted(false /* all_history */,
-                                                     deleted_urls);
-  EXPECT_EQ(2u, cache_manager_->GetStoredPhishGuardVerdictCount(
-                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
-  EXPECT_EQ(1u, cache_manager_->GetStoredPhishGuardVerdictCount(
-                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
-
-  LoginReputationClientResponse actual_verdict;
-  password_type.set_account_type(ReusedPasswordAccountType::GSUITE);
-  EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
-            cache_manager_->GetCachedPhishGuardVerdict(
-                GURL("http://bar.com"),
-                LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-                password_type, &actual_verdict));
-  password_type.set_account_type(ReusedPasswordAccountType::UNKNOWN);
-  EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
-            cache_manager_->GetCachedPhishGuardVerdict(
-                GURL("http://bar.com"),
-                LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
-                password_type, &actual_verdict));
-
-  // If delete all history. All password protection content settings should be
-  // gone.
-  cache_manager_->RemoveContentSettingsOnURLsDeleted(true /* all_history */,
-                                                     history::URLRows());
-  EXPECT_EQ(0u, cache_manager_->GetStoredPhishGuardVerdictCount(
-                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
-  EXPECT_EQ(0u, cache_manager_->GetStoredPhishGuardVerdictCount(
-                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
-}
-
-TEST_F(VerdictCacheManagerTest, TestCleanUpExpiredVerdict) {
-  // Prepare 4 verdicts for PASSWORD_REUSE_EVENT with SIGN_IN_PASSWORD type:
-  // (1) "foo.com/abc/" valid
-  // (2) "foo.com/def/" expired
-  // (3) "bar.com/abc/" expired
-  // (4) "bar.com/def/" expired
-  base::Time now = base::Time::Now();
-  ReusedPasswordAccountType password_type;
-  password_type.set_account_type(ReusedPasswordAccountType::GSUITE);
-  CachePhishGuardVerdict(
-      GURL("https://foo.com/abc/index.jsp"),
-      LoginReputationClientRequest::PASSWORD_REUSE_EVENT, password_type,
-      LoginReputationClientResponse::LOW_REPUTATION, 600, "foo.com/abc/", now);
-  CachePhishGuardVerdict(
-      GURL("https://foo.com/def/index.jsp"),
-      LoginReputationClientRequest::PASSWORD_REUSE_EVENT, password_type,
-      LoginReputationClientResponse::LOW_REPUTATION, 0, "foo.com/def/", now);
-  CachePhishGuardVerdict(GURL("https://bar.com/abc/index.jsp"),
-                         LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-                         password_type, LoginReputationClientResponse::PHISHING,
-                         0, "bar.com/abc/", now);
-  CachePhishGuardVerdict(GURL("https://bar.com/def/index.jsp"),
-                         LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-                         password_type, LoginReputationClientResponse::PHISHING,
-                         0, "bar.com/def/", now);
-  ASSERT_EQ(4u, cache_manager_->GetStoredPhishGuardVerdictCount(
-                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
-
-  // Prepare 2 verdicts for UNFAMILIAR_LOGIN_PAGE:
-  // (1) "bar.com/def/" valid
-  // (2) "bar.com/xyz/" expired
-  password_type.set_account_type(ReusedPasswordAccountType::UNKNOWN);
-  CachePhishGuardVerdict(GURL("https://bar.com/def/index.jsp"),
-                         LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
-                         password_type, LoginReputationClientResponse::SAFE,
-                         600, "bar.com/def/", now);
-  CachePhishGuardVerdict(GURL("https://bar.com/xyz/index.jsp"),
-                         LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
-                         password_type, LoginReputationClientResponse::PHISHING,
-                         0, "bar.com/xyz/", now);
-  ASSERT_EQ(2u, cache_manager_->GetStoredPhishGuardVerdictCount(
-                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
-
-  RTLookupResponse response;
-  AddThreatInfoToResponse(response, RTLookupResponse::ThreatInfo::DANGEROUS,
-                          RTLookupResponse::ThreatInfo::SOCIAL_ENGINEERING, 0,
-                          "www.example.com/");
-  AddThreatInfoToResponse(response, RTLookupResponse::ThreatInfo::DANGEROUS,
-                          RTLookupResponse::ThreatInfo::UNWANTED_SOFTWARE, 60,
-                          "www.example.com/path");
-  cache_manager_->CacheRealTimeUrlVerdict(GURL("https://www.example.com/"),
-                                          response, base::Time::Now());
-  ASSERT_EQ(2, cache_manager_->GetStoredRealTimeUrlCheckVerdictCount());
-
-  cache_manager_->CleanUpExpiredVerdicts();
-
-  ASSERT_EQ(1u, cache_manager_->GetStoredPhishGuardVerdictCount(
-                    LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
-  ASSERT_EQ(1u, cache_manager_->GetStoredPhishGuardVerdictCount(
-                    LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
-  ASSERT_EQ(1, cache_manager_->GetStoredRealTimeUrlCheckVerdictCount());
-  LoginReputationClientResponse actual_verdict;
-  password_type.set_account_type(ReusedPasswordAccountType::GSUITE);
-  // Has cached PASSWORD_REUSE_EVENT verdict for foo.com/abc/.
-  EXPECT_EQ(LoginReputationClientResponse::LOW_REPUTATION,
-            cache_manager_->GetCachedPhishGuardVerdict(
-                GURL("https://foo.com/abc/test.jsp"),
-                LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-                password_type, &actual_verdict));
-  // No cached PASSWORD_REUSE_EVENT verdict for foo.com/def.
-  EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
-            cache_manager_->GetCachedPhishGuardVerdict(
-                GURL("https://foo.com/def/index.jsp"),
-                LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-                password_type, &actual_verdict));
-  // No cached PASSWORD_REUSE_EVENT verdict for bar.com/abc.
-  EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
-            cache_manager_->GetCachedPhishGuardVerdict(
-                GURL("https://bar.com/abc/index.jsp"),
-                LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-                password_type, &actual_verdict));
-  // No cached PASSWORD_REUSE_EVENT verdict for bar.com/def.
-  EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
-            cache_manager_->GetCachedPhishGuardVerdict(
-                GURL("https://bar.com/def/index.jsp"),
-                LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-                password_type, &actual_verdict));
-
-  // Has cached UNFAMILIAR_LOGIN_PAGE verdict for bar.com/def.
-  password_type.set_account_type(ReusedPasswordAccountType::UNKNOWN);
-  EXPECT_EQ(LoginReputationClientResponse::SAFE,
-            cache_manager_->GetCachedPhishGuardVerdict(
-                GURL("https://bar.com/def/index.jsp"),
-                LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
-                password_type, &actual_verdict));
-
-  // No cached UNFAMILIAR_LOGIN_PAGE verdict for bar.com/xyz.
-  EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
-            cache_manager_->GetCachedPhishGuardVerdict(
-                GURL("https://bar.com/xyz/index.jsp"),
-                LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
-                password_type, &actual_verdict));
-}
-
-TEST_F(VerdictCacheManagerTest, TestCleanUpExpiredVerdictWithInvalidEntry) {
-  // Directly save an invalid cache entry.
-  LoginReputationClientResponse verdict;
-  verdict.set_verdict_type(LoginReputationClientResponse::SAFE);
-  verdict.set_cache_expression("www.google.com/");
-  verdict.set_cache_duration_sec(60);
-
-  std::string verdict_serialized;
-  verdict.SerializeToString(&verdict_serialized);
-  base::Base64Encode(verdict_serialized, &verdict_serialized);
-
-  auto cache_dictionary = std::make_unique<base::DictionaryValue>();
-  auto* verdict_dictionary =
-      cache_dictionary->SetKey("1", base::Value(base::Value::Type::DICTIONARY));
-  auto* verdict_entry = verdict_dictionary->SetKey(
-      "www.google.com/path", base::Value(base::Value::Type::DICTIONARY));
-  verdict_entry->SetStringKey("cache_creation_time", "invalid_time");
-  verdict_entry->SetStringKey("verdict_proto", verdict_serialized);
-
-  content_setting_map_->SetWebsiteSettingDefaultScope(
-      GURL("http://www.google.com/"), GURL(),
-      ContentSettingsType::PASSWORD_PROTECTION, std::string(),
-      std::move(cache_dictionary));
-
-  ReusedPasswordAccountType password_type;
-  password_type.set_account_type(ReusedPasswordAccountType::GSUITE);
-  // Save one valid entry
-  CachePhishGuardVerdict(GURL("https://www.google.com"),
-                         LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
-                         password_type, LoginReputationClientResponse::SAFE, 60,
-                         "www.google.com/", base::Time::Now());
-
-  // Verify we saved two entries under PasswordType PRIMARY_ACCOUNT_PASSWORD
-  EXPECT_EQ(2U,
-            content_setting_map_
-                ->GetWebsiteSetting(GURL("http://www.google.com/"), GURL(),
-                                    ContentSettingsType::PASSWORD_PROTECTION,
-                                    std::string(), nullptr)
-                ->FindDictKey("1")
-                ->DictSize());
-
-  cache_manager_->CleanUpExpiredVerdicts();
-
-  // One should have been cleaned up
-  EXPECT_EQ(1U,
-            content_setting_map_
-                ->GetWebsiteSetting(GURL("http://www.google.com/"), GURL(),
-                                    ContentSettingsType::PASSWORD_PROTECTION,
-                                    std::string(), nullptr)
-                ->FindDictKey("1")
-                ->DictSize());
-}
-
-TEST_F(VerdictCacheManagerTest, TestCanRetrieveCachedRealTimeUrlCheckVerdict) {
-  GURL url("https://www.example.com/path");
-
-  RTLookupResponse response;
-  AddThreatInfoToResponse(response, RTLookupResponse::ThreatInfo::SAFE,
-                          RTLookupResponse::ThreatInfo::THREAT_TYPE_UNSPECIFIED,
-                          60, "www.example.com/");
-  AddThreatInfoToResponse(response, RTLookupResponse::ThreatInfo::DANGEROUS,
-                          RTLookupResponse::ThreatInfo::SOCIAL_ENGINEERING, 60,
-                          "www.example.com/path");
-  cache_manager_->CacheRealTimeUrlVerdict(url, response, base::Time::Now());
-
-  RTLookupResponse::ThreatInfo out_verdict;
-  EXPECT_EQ(RTLookupResponse::ThreatInfo::DANGEROUS,
-            cache_manager_->GetCachedRealTimeUrlVerdict(url, &out_verdict));
-  EXPECT_EQ("www.example.com/path", out_verdict.cache_expression());
-  EXPECT_EQ(60, out_verdict.cache_duration_sec());
-  EXPECT_EQ(RTLookupResponse::ThreatInfo::SOCIAL_ENGINEERING,
-            out_verdict.threat_type());
-}
-
-TEST_F(VerdictCacheManagerTest,
-       TestCanRetrieveCachedRealTimeUrlCheckVerdictWithMultipleThreatInfos) {
-  GURL url1("https://www.example.com/");
-  GURL url2("https://www.example.com/path");
-
-  RTLookupResponse response;
-  AddThreatInfoToResponse(response, RTLookupResponse::ThreatInfo::DANGEROUS,
-                          RTLookupResponse::ThreatInfo::SOCIAL_ENGINEERING, 60,
-                          "www.example.com/");
-  AddThreatInfoToResponse(response, RTLookupResponse::ThreatInfo::DANGEROUS,
-                          RTLookupResponse::ThreatInfo::UNWANTED_SOFTWARE, 60,
-                          "www.example.com/");
-  AddThreatInfoToResponse(response, RTLookupResponse::ThreatInfo::DANGEROUS,
-                          RTLookupResponse::ThreatInfo::UNWANTED_SOFTWARE, 60,
-                          "www.example.com/path");
-  AddThreatInfoToResponse(response, RTLookupResponse::ThreatInfo::DANGEROUS,
-                          RTLookupResponse::ThreatInfo::UNCLEAR_BILLING, 60,
-                          "www.example.com/path");
-  cache_manager_->CacheRealTimeUrlVerdict(url2, response, base::Time::Now());
-
-  RTLookupResponse::ThreatInfo out_verdict;
-  EXPECT_EQ(RTLookupResponse::ThreatInfo::DANGEROUS,
-            cache_manager_->GetCachedRealTimeUrlVerdict(url1, &out_verdict));
-  EXPECT_EQ("www.example.com/", out_verdict.cache_expression());
-  EXPECT_EQ(RTLookupResponse::ThreatInfo::SOCIAL_ENGINEERING,
-            out_verdict.threat_type());
-
-  EXPECT_EQ(RTLookupResponse::ThreatInfo::DANGEROUS,
-            cache_manager_->GetCachedRealTimeUrlVerdict(url2, &out_verdict));
-  EXPECT_EQ("www.example.com/path", out_verdict.cache_expression());
-  EXPECT_EQ(RTLookupResponse::ThreatInfo::UNWANTED_SOFTWARE,
-            out_verdict.threat_type());
-}
-
-TEST_F(VerdictCacheManagerTest,
-       TestCannotRetrieveRealTimeUrlCheckExpiredVerdict) {
-  GURL url("https://www.example.com/path");
-
-  RTLookupResponse response;
-  AddThreatInfoToResponse(response, RTLookupResponse::ThreatInfo::DANGEROUS,
-                          RTLookupResponse::ThreatInfo::SOCIAL_ENGINEERING, 0,
-                          "www.example.com/path");
-  cache_manager_->CacheRealTimeUrlVerdict(url, response, base::Time::Now());
-
-  RTLookupResponse::ThreatInfo out_verdict;
-  EXPECT_EQ(RTLookupResponse::ThreatInfo::VERDICT_TYPE_UNSPECIFIED,
-            cache_manager_->GetCachedRealTimeUrlVerdict(url, &out_verdict));
-}
-
-TEST_F(VerdictCacheManagerTest,
-       TestRemoveRealTimeUrlCheckCachedVerdictOnURLsDeleted) {
-  GURL url("https://www.example.com/path");
-
-  RTLookupResponse response;
-  AddThreatInfoToResponse(response, RTLookupResponse::ThreatInfo::DANGEROUS,
-                          RTLookupResponse::ThreatInfo::SOCIAL_ENGINEERING, 60,
-                          "www.example.com/path");
-  cache_manager_->CacheRealTimeUrlVerdict(url, response, base::Time::Now());
-  RTLookupResponse::ThreatInfo out_verdict;
-  EXPECT_EQ(RTLookupResponse::ThreatInfo::DANGEROUS,
-            cache_manager_->GetCachedRealTimeUrlVerdict(url, &out_verdict));
-
-  history::URLRows deleted_urls;
-  deleted_urls.push_back(history::URLRow(GURL("https://www.example.com/path")));
-
-  cache_manager_->RemoveContentSettingsOnURLsDeleted(false /* all_history */,
-                                                     deleted_urls);
-  EXPECT_EQ(RTLookupResponse::ThreatInfo::VERDICT_TYPE_UNSPECIFIED,
-            cache_manager_->GetCachedRealTimeUrlVerdict(url, &out_verdict));
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/web_ui/BUILD.gn b/components/safe_browsing/web_ui/BUILD.gn
deleted file mode 100644
index 5406e15..0000000
--- a/components/safe_browsing/web_ui/BUILD.gn
+++ /dev/null
@@ -1,58 +0,0 @@
-# Copyright 2017 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//third_party/protobuf/proto_library.gni")
-
-static_library("web_ui") {
-  sources = [
-    "safe_browsing_ui.cc",
-    "safe_browsing_ui.h",
-  ]
-
-  deps = [
-    ":constants",
-    "//base",
-    "//components/password_manager/core/browser:hash_password_manager",
-    "//components/resources:components_resources_grit",
-    "//components/resources:components_scaled_resources_grit",
-    "//components/safe_browsing:buildflags",
-    "//components/safe_browsing:csd_proto",
-    "//components/safe_browsing:features",
-    "//components/safe_browsing:realtimeapi_proto",
-    "//components/safe_browsing/browser:network_context",
-    "//components/safe_browsing/browser:referrer_chain_provider",
-    "//components/safe_browsing/common:safe_browsing_prefs",
-    "//components/safe_browsing/db:v4_local_database_manager",
-    "//components/strings:components_strings_grit",
-    "//components/sync/protocol:protocol",
-    "//components/user_prefs:user_prefs",
-    "//content/public/browser",
-    "//net",
-    "//url",
-  ]
-
-  public_deps = [
-    "//components/safe_browsing:webui_proto",
-  ]
-}
-
-static_library("constants") {
-  sources = [
-    "constants.cc",
-    "constants.h",
-  ]
-}
-
-source_set("unit_tests") {
-  testonly = true
-  sources = [
-    "safe_browsing_ui_unittest.cc",
-  ]
-
-  deps = [
-    ":web_ui",
-    "//content/test:test_support",
-    "//testing/gtest",
-  ]
-}
diff --git a/components/safe_browsing/web_ui/DEPS b/components/safe_browsing/web_ui/DEPS
deleted file mode 100644
index 1936f2f..0000000
--- a/components/safe_browsing/web_ui/DEPS
+++ /dev/null
@@ -1,9 +0,0 @@
-include_rules = [
-  "+components/grit/components_resources.h",
-  "+components/password_manager/core/browser/hash_password_manager.h",
-  "+components/user_prefs",
-  "+components/safe_browsing/proto/csd.pb.h",
-  "+components/strings/grit/components_strings.h",
-  "+components/grit/components_scaled_resources.h",
-  "+components/safe_browsing_db",
-]
diff --git a/components/safe_browsing/web_ui/constants.cc b/components/safe_browsing/web_ui/constants.cc
deleted file mode 100644
index 08f3363..0000000
--- a/components/safe_browsing/web_ui/constants.cc
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/web_ui/constants.h"
-
-namespace safe_browsing {
-
-const char kChromeUISafeBrowsingURL[] = "chrome://safe-browsing/";
-const char kChromeUISafeBrowsingHost[] = "safe-browsing";
-const char kSbUnderConstruction[] =
-    "The safe browsing page is under construction.";
-const char kChromeUISafeBrowsingMatchBillingUrl[] =
-    "chrome://safe-browsing/match?type=billing";
-const char kChromeUISafeBrowsingMatchMalwareUrl[] =
-    "chrome://safe-browsing/match?type=malware";
-const char kChromeUISafeBrowsingMatchPhishingUrl[] =
-    "chrome://safe-browsing/match?type=phishing";
-const char kChromeUISafeBrowsingMatchUnwantedUrl[] =
-    "chrome://safe-browsing/match?type=unwanted";
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/web_ui/constants.h b/components/safe_browsing/web_ui/constants.h
deleted file mode 100644
index 6b54de01..0000000
--- a/components/safe_browsing/web_ui/constants.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_WEB_UI_CONSTANTS_H_
-#define COMPONENTS_SAFE_BROWSING_WEB_UI_CONSTANTS_H_
-
-namespace safe_browsing {
-
-extern const char kChromeUISafeBrowsingURL[];
-extern const char kChromeUISafeBrowsingHost[];
-extern const char kSbUnderConstruction[];
-extern const char kChromeUISafeBrowsingMatchBillingUrl[];
-extern const char kChromeUISafeBrowsingMatchMalwareUrl[];
-extern const char kChromeUISafeBrowsingMatchPhishingUrl[];
-extern const char kChromeUISafeBrowsingMatchUnwantedUrl[];
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_WEB_UI_CONSTANTS_H_
diff --git a/components/safe_browsing/web_ui/safe_browsing_ui.cc b/components/safe_browsing/web_ui/safe_browsing_ui.cc
deleted file mode 100644
index 7df30fc0..0000000
--- a/components/safe_browsing/web_ui/safe_browsing_ui.cc
+++ /dev/null
@@ -1,1752 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/web_ui/safe_browsing_ui.h"
-
-#include <stddef.h>
-#include <algorithm>
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "base/base64.h"
-#include "base/base64url.h"
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/i18n/time_formatting.h"
-#include "base/json/json_string_value_serializer.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/singleton.h"
-#include "base/stl_util.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/task/post_task.h"
-#include "base/time/time.h"
-#include "base/values.h"
-#include "components/grit/components_resources.h"
-#include "components/grit/components_scaled_resources.h"
-#include "components/password_manager/core/browser/hash_password_manager.h"
-#include "components/safe_browsing/browser/referrer_chain_provider.h"
-#include "components/safe_browsing/buildflags.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/features.h"
-#include "components/safe_browsing/proto/csd.pb.h"
-#include "components/safe_browsing/web_ui/constants.h"
-#include "components/strings/grit/components_strings.h"
-#include "components/user_prefs/user_prefs.h"
-#include "content/public/browser/browser_context.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/web_contents.h"
-
-#if BUILDFLAG(SAFE_BROWSING_DB_LOCAL)
-#include "components/safe_browsing/db/v4_local_database_manager.h"
-#endif
-
-using base::Time;
-using sync_pb::GaiaPasswordReuse;
-using PasswordCaptured = sync_pb::UserEventSpecifics::GaiaPasswordCaptured;
-using PasswordReuseLookup = sync_pb::GaiaPasswordReuse::PasswordReuseLookup;
-using PasswordReuseDetected = sync_pb::GaiaPasswordReuse::PasswordReuseDetected;
-using PasswordReuseDialogInteraction =
-    sync_pb::GaiaPasswordReuse::PasswordReuseDialogInteraction;
-
-namespace safe_browsing {
-WebUIInfoSingleton::WebUIInfoSingleton() = default;
-
-WebUIInfoSingleton::~WebUIInfoSingleton() = default;
-
-// static
-WebUIInfoSingleton* WebUIInfoSingleton::GetInstance() {
-  return base::Singleton<WebUIInfoSingleton>::get();
-}
-
-// static
-bool WebUIInfoSingleton::HasListener() {
-  return GetInstance()->has_test_listener_ ||
-         !GetInstance()->webui_instances_.empty();
-}
-
-void WebUIInfoSingleton::AddToClientDownloadRequestsSent(
-    std::unique_ptr<ClientDownloadRequest> client_download_request) {
-  if (!HasListener())
-    return;
-
-  for (auto* webui_listener : webui_instances_)
-    webui_listener->NotifyClientDownloadRequestJsListener(
-        client_download_request.get());
-  client_download_requests_sent_.push_back(std::move(client_download_request));
-}
-
-void WebUIInfoSingleton::ClearClientDownloadRequestsSent() {
-  std::vector<std::unique_ptr<ClientDownloadRequest>>().swap(
-      client_download_requests_sent_);
-}
-
-void WebUIInfoSingleton::AddToClientDownloadResponsesReceived(
-    std::unique_ptr<ClientDownloadResponse> client_download_response) {
-  if (!HasListener())
-    return;
-
-  for (auto* webui_listener : webui_instances_)
-    webui_listener->NotifyClientDownloadResponseJsListener(
-        client_download_response.get());
-  client_download_responses_received_.push_back(
-      std::move(client_download_response));
-}
-
-void WebUIInfoSingleton::ClearClientDownloadResponsesReceived() {
-  std::vector<std::unique_ptr<ClientDownloadResponse>>().swap(
-      client_download_responses_received_);
-}
-
-void WebUIInfoSingleton::AddToCSBRRsSent(
-    std::unique_ptr<ClientSafeBrowsingReportRequest> csbrr) {
-  if (!HasListener())
-    return;
-
-  for (auto* webui_listener : webui_instances_)
-    webui_listener->NotifyCSBRRJsListener(csbrr.get());
-  csbrrs_sent_.push_back(std::move(csbrr));
-}
-
-void WebUIInfoSingleton::ClearCSBRRsSent() {
-  std::vector<std::unique_ptr<ClientSafeBrowsingReportRequest>>().swap(
-      csbrrs_sent_);
-}
-
-void WebUIInfoSingleton::AddToPGEvents(
-    const sync_pb::UserEventSpecifics& event) {
-  if (!HasListener())
-    return;
-
-  for (auto* webui_listener : webui_instances_)
-    webui_listener->NotifyPGEventJsListener(event);
-
-  pg_event_log_.push_back(event);
-}
-
-void WebUIInfoSingleton::ClearPGEvents() {
-  std::vector<sync_pb::UserEventSpecifics>().swap(pg_event_log_);
-}
-
-void WebUIInfoSingleton::AddToSecurityEvents(
-    const sync_pb::GaiaPasswordReuse& event) {
-  if (!HasListener())
-    return;
-
-  for (auto* webui_listener : webui_instances_)
-    webui_listener->NotifySecurityEventJsListener(event);
-
-  security_event_log_.push_back(event);
-}
-
-void WebUIInfoSingleton::ClearSecurityEvents() {
-  std::vector<sync_pb::GaiaPasswordReuse>().swap(security_event_log_);
-}
-
-int WebUIInfoSingleton::AddToPGPings(
-    const LoginReputationClientRequest& request) {
-  if (!HasListener())
-    return -1;
-
-  for (auto* webui_listener : webui_instances_)
-    webui_listener->NotifyPGPingJsListener(pg_pings_.size(), request);
-
-  pg_pings_.push_back(request);
-
-  return pg_pings_.size() - 1;
-}
-
-void WebUIInfoSingleton::AddToPGResponses(
-    int token,
-    const LoginReputationClientResponse& response) {
-  if (!HasListener())
-    return;
-
-  for (auto* webui_listener : webui_instances_)
-    webui_listener->NotifyPGResponseJsListener(token, response);
-
-  pg_responses_[token] = response;
-}
-
-void WebUIInfoSingleton::ClearPGPings() {
-  std::vector<LoginReputationClientRequest>().swap(pg_pings_);
-  std::map<int, LoginReputationClientResponse>().swap(pg_responses_);
-}
-
-int WebUIInfoSingleton::AddToRTLookupPings(const RTLookupRequest request) {
-  if (!HasListener())
-    return -1;
-
-  for (auto* webui_listener : webui_instances_)
-    webui_listener->NotifyRTLookupPingJsListener(rt_lookup_pings_.size(),
-                                                 request);
-
-  rt_lookup_pings_.push_back(request);
-
-  return rt_lookup_pings_.size() - 1;
-}
-
-void WebUIInfoSingleton::AddToRTLookupResponses(
-    int token,
-    const RTLookupResponse response) {
-  if (!HasListener())
-    return;
-
-  for (auto* webui_listener : webui_instances_)
-    webui_listener->NotifyRTLookupResponseJsListener(token, response);
-
-  rt_lookup_responses_[token] = response;
-}
-
-void WebUIInfoSingleton::ClearRTLookupPings() {
-  std::vector<RTLookupRequest>().swap(rt_lookup_pings_);
-  std::map<int, RTLookupResponse>().swap(rt_lookup_responses_);
-}
-
-void WebUIInfoSingleton::LogMessage(const std::string& message) {
-  if (!HasListener())
-    return;
-
-  base::Time timestamp = base::Time::Now();
-  log_messages_.push_back(std::make_pair(timestamp, message));
-
-  base::PostTask(FROM_HERE, {content::BrowserThread::UI},
-                 base::BindOnce(&WebUIInfoSingleton::NotifyLogMessageListeners,
-                                timestamp, message));
-}
-
-void WebUIInfoSingleton::ClearLogMessages() {
-  std::vector<std::pair<base::Time, std::string>>().swap(log_messages_);
-}
-
-/* static */ void WebUIInfoSingleton::NotifyLogMessageListeners(
-    const base::Time& timestamp,
-    const std::string& message) {
-  WebUIInfoSingleton* web_ui_info = GetInstance();
-
-  for (auto* webui_listener : web_ui_info->webui_instances())
-    webui_listener->NotifyLogMessageJsListener(timestamp, message);
-}
-
-void WebUIInfoSingleton::AddToReportingEvents(const base::Value& event) {
-  if (!HasListener())
-    return;
-
-  for (auto* webui_listener : webui_instances_)
-    webui_listener->NotifyReportingEventJsListener(event);
-
-  reporting_events_.push_back(event.Clone());
-}
-
-void WebUIInfoSingleton::ClearReportingEvents() {
-  std::vector<base::Value>().swap(reporting_events_);
-}
-
-void WebUIInfoSingleton::RegisterWebUIInstance(SafeBrowsingUIHandler* webui) {
-  webui_instances_.push_back(webui);
-}
-
-void WebUIInfoSingleton::UnregisterWebUIInstance(SafeBrowsingUIHandler* webui) {
-  base::Erase(webui_instances_, webui);
-  if (!HasListener()) {
-    ClearCSBRRsSent();
-    ClearClientDownloadRequestsSent();
-    ClearClientDownloadResponsesReceived();
-    ClearPGEvents();
-    ClearPGPings();
-    ClearRTLookupPings();
-    ClearLogMessages();
-  }
-}
-
-network::mojom::CookieManager* WebUIInfoSingleton::GetCookieManager() {
-  if (!cookie_manager_remote_)
-    InitializeCookieManager();
-
-  return cookie_manager_remote_.get();
-}
-
-void WebUIInfoSingleton::InitializeCookieManager() {
-  DCHECK(network_context_);
-
-  // Reset |cookie_manager_remote_|, and only re-initialize it if we have a
-  // listening SafeBrowsingUIHandler.
-  cookie_manager_remote_.reset();
-
-  if (HasListener()) {
-    network_context_->GetNetworkContext()->GetCookieManager(
-        cookie_manager_remote_.BindNewPipeAndPassReceiver());
-
-    // base::Unretained is safe because |this| owns |cookie_manager_remote_|.
-    cookie_manager_remote_.set_disconnect_handler(base::BindOnce(
-        &WebUIInfoSingleton::InitializeCookieManager, base::Unretained(this)));
-  }
-}
-
-namespace {
-#if BUILDFLAG(SAFE_BROWSING_DB_LOCAL)
-
-base::Value UserReadableTimeFromMillisSinceEpoch(int64_t time_in_milliseconds) {
-  base::Time time = base::Time::UnixEpoch() +
-                    base::TimeDelta::FromMilliseconds(time_in_milliseconds);
-  return base::Value(
-      base::UTF16ToASCII(base::TimeFormatShortDateAndTime(time)));
-}
-
-void AddStoreInfo(const DatabaseManagerInfo::DatabaseInfo::StoreInfo store_info,
-                  base::ListValue* database_info_list) {
-  if (store_info.has_file_name()) {
-    database_info_list->Append(base::Value(store_info.file_name()));
-  } else {
-    database_info_list->Append(base::Value("Unknown store"));
-  }
-
-  std::string store_info_string = "<blockquote>";
-  if (store_info.has_file_size_bytes()) {
-    store_info_string +=
-        "Size (in bytes): " + std::to_string(store_info.file_size_bytes()) +
-        "<br>";
-  }
-
-  if (store_info.has_update_status()) {
-    store_info_string +=
-        "Update status: " + std::to_string(store_info.update_status()) + "<br>";
-  }
-
-  if (store_info.has_last_apply_update_time_millis()) {
-    store_info_string += "Last update time: " +
-                         UserReadableTimeFromMillisSinceEpoch(
-                             store_info.last_apply_update_time_millis())
-                             .GetString() +
-                         "<br>";
-  }
-
-  if (store_info.has_checks_attempted()) {
-    store_info_string += "Number of database checks: " +
-                         std::to_string(store_info.checks_attempted()) + "<br>";
-  }
-
-  store_info_string += "</blockquote>";
-
-  database_info_list->Append(base::Value(store_info_string));
-}
-
-void AddDatabaseInfo(const DatabaseManagerInfo::DatabaseInfo database_info,
-                     base::ListValue* database_info_list) {
-  if (database_info.has_database_size_bytes()) {
-    database_info_list->Append(base::Value("Database size (in bytes)"));
-    database_info_list->Append(
-        base::Value(static_cast<double>(database_info.database_size_bytes())));
-  }
-
-  // Add the information specific to each store.
-  for (int i = 0; i < database_info.store_info_size(); i++) {
-    AddStoreInfo(database_info.store_info(i), database_info_list);
-  }
-}
-
-void AddUpdateInfo(const DatabaseManagerInfo::UpdateInfo update_info,
-                   base::ListValue* database_info_list) {
-  if (update_info.has_network_status_code()) {
-    // Network status of the last GetUpdate().
-    database_info_list->Append(base::Value("Last update network status code"));
-    database_info_list->Append(base::Value(update_info.network_status_code()));
-  }
-  if (update_info.has_last_update_time_millis()) {
-    database_info_list->Append(base::Value("Last update time"));
-    database_info_list->Append(UserReadableTimeFromMillisSinceEpoch(
-        update_info.last_update_time_millis()));
-  }
-  if (update_info.has_next_update_time_millis()) {
-    database_info_list->Append(base::Value("Next update time"));
-    database_info_list->Append(UserReadableTimeFromMillisSinceEpoch(
-        update_info.next_update_time_millis()));
-  }
-}
-
-void ParseFullHashInfo(
-    const FullHashCacheInfo::FullHashCache::CachedHashPrefixInfo::FullHashInfo
-        full_hash_info,
-    base::DictionaryValue* full_hash_info_dict) {
-  if (full_hash_info.has_positive_expiry()) {
-    full_hash_info_dict->SetString(
-        "Positive expiry",
-        UserReadableTimeFromMillisSinceEpoch(full_hash_info.positive_expiry())
-            .GetString());
-  }
-  if (full_hash_info.has_full_hash()) {
-    std::string full_hash;
-    base::Base64UrlEncode(full_hash_info.full_hash(),
-                          base::Base64UrlEncodePolicy::INCLUDE_PADDING,
-                          &full_hash);
-    full_hash_info_dict->SetString("Full hash (base64)", full_hash);
-  }
-  if (full_hash_info.list_identifier().has_platform_type()) {
-    full_hash_info_dict->SetInteger(
-        "platform_type", full_hash_info.list_identifier().platform_type());
-  }
-  if (full_hash_info.list_identifier().has_threat_entry_type()) {
-    full_hash_info_dict->SetInteger(
-        "threat_entry_type",
-        full_hash_info.list_identifier().threat_entry_type());
-  }
-  if (full_hash_info.list_identifier().has_threat_type()) {
-    full_hash_info_dict->SetInteger(
-        "threat_type", full_hash_info.list_identifier().threat_type());
-  }
-}
-
-void ParseFullHashCache(const FullHashCacheInfo::FullHashCache full_hash_cache,
-                        base::ListValue* full_hash_cache_list) {
-  base::DictionaryValue full_hash_cache_parsed;
-
-  if (full_hash_cache.has_hash_prefix()) {
-    std::string hash_prefix;
-    base::Base64UrlEncode(full_hash_cache.hash_prefix(),
-                          base::Base64UrlEncodePolicy::INCLUDE_PADDING,
-                          &hash_prefix);
-    full_hash_cache_parsed.SetString("Hash prefix (base64)", hash_prefix);
-  }
-  if (full_hash_cache.cached_hash_prefix_info().has_negative_expiry()) {
-    full_hash_cache_parsed.SetString(
-        "Negative expiry",
-        UserReadableTimeFromMillisSinceEpoch(
-            full_hash_cache.cached_hash_prefix_info().negative_expiry())
-            .GetString());
-  }
-
-  full_hash_cache_list->Append(std::move(full_hash_cache_parsed));
-
-  for (auto full_hash_info_it :
-       full_hash_cache.cached_hash_prefix_info().full_hash_info()) {
-    base::DictionaryValue full_hash_info_dict;
-    ParseFullHashInfo(full_hash_info_it, &full_hash_info_dict);
-    full_hash_cache_list->Append(std::move(full_hash_info_dict));
-  }
-}
-
-void ParseFullHashCacheInfo(const FullHashCacheInfo full_hash_cache_info_proto,
-                            base::ListValue* full_hash_cache_info) {
-  if (full_hash_cache_info_proto.has_number_of_hits()) {
-    base::DictionaryValue number_of_hits;
-    number_of_hits.SetInteger("Number of cache hits",
-                              full_hash_cache_info_proto.number_of_hits());
-    full_hash_cache_info->Append(std::move(number_of_hits));
-  }
-
-  // Record FullHashCache list.
-  for (auto full_hash_cache_it : full_hash_cache_info_proto.full_hash_cache()) {
-    base::ListValue full_hash_cache_list;
-    ParseFullHashCache(full_hash_cache_it, &full_hash_cache_list);
-    full_hash_cache_info->Append(std::move(full_hash_cache_list));
-  }
-}
-
-std::string AddFullHashCacheInfo(
-    const FullHashCacheInfo full_hash_cache_info_proto) {
-  std::string full_hash_cache_parsed;
-
-  base::ListValue full_hash_cache;
-  ParseFullHashCacheInfo(full_hash_cache_info_proto, &full_hash_cache);
-
-  base::Value* full_hash_cache_tree = &full_hash_cache;
-
-  JSONStringValueSerializer serializer(&full_hash_cache_parsed);
-  serializer.set_pretty_print(true);
-  serializer.Serialize(*full_hash_cache_tree);
-
-  return full_hash_cache_parsed;
-}
-
-#endif
-
-base::Value SerializeReferrer(const ReferrerChainEntry& referrer) {
-  base::DictionaryValue referrer_dict;
-  referrer_dict.SetKey("url", base::Value(referrer.url()));
-  referrer_dict.SetKey("main_frame_url",
-                       base::Value(referrer.main_frame_url()));
-
-  std::string url_type;
-  switch (referrer.type()) {
-    case ReferrerChainEntry::EVENT_URL:
-      url_type = "EVENT_URL";
-      break;
-    case ReferrerChainEntry::LANDING_PAGE:
-      url_type = "LANDING_PAGE";
-      break;
-    case ReferrerChainEntry::LANDING_REFERRER:
-      url_type = "LANDING_REFERRER";
-      break;
-    case ReferrerChainEntry::CLIENT_REDIRECT:
-      url_type = "CLIENT_REDIRECT";
-      break;
-    case ReferrerChainEntry::DEPRECATED_SERVER_REDIRECT:
-      url_type = "DEPRECATED_SERVER_REDIRECT";
-      break;
-    case ReferrerChainEntry::RECENT_NAVIGATION:
-      url_type = "RECENT_NAVIGATION";
-      break;
-    case ReferrerChainEntry::REFERRER:
-      url_type = "REFERRER";
-      break;
-  }
-  referrer_dict.SetKey("type", base::Value(url_type));
-
-  base::ListValue ip_addresses;
-  for (const std::string& ip_address : referrer.ip_addresses()) {
-    ip_addresses.Append(base::Value(ip_address));
-  }
-  referrer_dict.SetKey("ip_addresses", std::move(ip_addresses));
-
-  referrer_dict.SetKey("referrer_url", base::Value(referrer.referrer_url()));
-
-  referrer_dict.SetKey("referrer_main_frame_url",
-                       base::Value(referrer.referrer_main_frame_url()));
-
-  referrer_dict.SetKey("is_retargeting",
-                       base::Value(referrer.is_retargeting()));
-
-  referrer_dict.SetKey("navigation_time_msec",
-                       base::Value(referrer.navigation_time_msec()));
-
-  base::ListValue server_redirects;
-  for (const ReferrerChainEntry::ServerRedirect& server_redirect :
-       referrer.server_redirect_chain()) {
-    server_redirects.Append(base::Value(server_redirect.url()));
-  }
-  referrer_dict.SetKey("server_redirect_chain", std::move(server_redirects));
-
-  std::string navigation_initiation;
-  switch (referrer.navigation_initiation()) {
-    case ReferrerChainEntry::UNDEFINED:
-      navigation_initiation = "UNDEFINED";
-      break;
-    case ReferrerChainEntry::BROWSER_INITIATED:
-      navigation_initiation = "BROWSER_INITIATED";
-      break;
-    case ReferrerChainEntry::RENDERER_INITIATED_WITHOUT_USER_GESTURE:
-      navigation_initiation = "RENDERER_INITIATED_WITHOUT_USER_GESTURE";
-      break;
-    case ReferrerChainEntry::RENDERER_INITIATED_WITH_USER_GESTURE:
-      navigation_initiation = "RENDERER_INITIATED_WITH_USER_GESTURE";
-      break;
-  }
-  referrer_dict.SetKey("navigation_initiation",
-                       base::Value(navigation_initiation));
-
-  referrer_dict.SetKey(
-      "maybe_launched_by_external_application",
-      base::Value(referrer.maybe_launched_by_external_application()));
-
-  return std::move(referrer_dict);
-}
-
-std::string SerializeClientDownloadRequest(const ClientDownloadRequest& cdr) {
-  base::DictionaryValue dict;
-  if (cdr.has_url())
-    dict.SetString("url", cdr.url());
-  if (cdr.has_download_type())
-    dict.SetInteger("download_type", cdr.download_type());
-  if (cdr.has_length())
-    dict.SetInteger("length", cdr.length());
-  if (cdr.has_file_basename())
-    dict.SetString("file_basename", cdr.file_basename());
-  if (cdr.has_archive_valid())
-    dict.SetBoolean("archive_valid", cdr.archive_valid());
-
-  auto archived_binaries = std::make_unique<base::ListValue>();
-  for (const auto& archived_binary : cdr.archived_binary()) {
-    auto dict_archived_binary = std::make_unique<base::DictionaryValue>();
-    if (archived_binary.has_file_basename())
-      dict_archived_binary->SetString("file_basename",
-                                      archived_binary.file_basename());
-    if (archived_binary.has_download_type())
-      dict_archived_binary->SetInteger("download_type",
-                                       archived_binary.download_type());
-    if (archived_binary.has_length())
-      dict_archived_binary->SetInteger("length", archived_binary.length());
-    if (archived_binary.is_encrypted())
-      dict_archived_binary->SetBoolean("is_encrypted", true);
-    if (archived_binary.digests().has_sha256()) {
-      const std::string& sha256 = archived_binary.digests().sha256();
-      dict_archived_binary->SetString(
-          "digests.sha256", base::HexEncode(sha256.c_str(), sha256.size()));
-    }
-    archived_binaries->Append(std::move(dict_archived_binary));
-  }
-  dict.SetList("archived_binary", std::move(archived_binaries));
-
-  auto referrer_chain = std::make_unique<base::ListValue>();
-  for (const auto& referrer_chain_entry : cdr.referrer_chain()) {
-    referrer_chain->Append(SerializeReferrer(referrer_chain_entry));
-  }
-  dict.SetList("referrer_chain", std::move(referrer_chain));
-
-  dict.SetBoolean("request_ap_verdicts", cdr.request_ap_verdicts());
-
-  dict.SetInteger("archive_file_count", cdr.archive_file_count());
-  dict.SetInteger("archive_directory_count", cdr.archive_directory_count());
-
-  dict.SetBoolean("request_ap_verdicts", cdr.request_ap_verdicts());
-
-  base::Value* request_tree = &dict;
-  std::string request_serialized;
-  JSONStringValueSerializer serializer(&request_serialized);
-  serializer.set_pretty_print(true);
-  serializer.Serialize(*request_tree);
-  return request_serialized;
-}
-
-std::string SerializeClientDownloadResponse(const ClientDownloadResponse& cdr) {
-  base::DictionaryValue dict;
-
-  switch (cdr.verdict()) {
-    case ClientDownloadResponse::SAFE:
-      dict.SetKey("verdict", base::Value("SAFE"));
-      break;
-    case ClientDownloadResponse::DANGEROUS:
-      dict.SetKey("verdict", base::Value("DANGEROUS"));
-      break;
-    case ClientDownloadResponse::UNCOMMON:
-      dict.SetKey("verdict", base::Value("UNCOMMON"));
-      break;
-    case ClientDownloadResponse::POTENTIALLY_UNWANTED:
-      dict.SetKey("verdict", base::Value("POTENTIALLY_UNWANTED"));
-      break;
-    case ClientDownloadResponse::DANGEROUS_HOST:
-      dict.SetKey("verdict", base::Value("DANGEROUS_HOST"));
-      break;
-    case ClientDownloadResponse::UNKNOWN:
-      dict.SetKey("verdict", base::Value("UNKNOWN"));
-      break;
-  }
-
-  if (cdr.has_more_info()) {
-    dict.SetPath({"more_info", "description"},
-                 base::Value(cdr.more_info().description()));
-    dict.SetPath({"more_info", "url"}, base::Value(cdr.more_info().url()));
-  }
-
-  if (cdr.has_token()) {
-    dict.SetKey("token", base::Value(cdr.token()));
-  }
-
-  if (cdr.has_upload()) {
-    dict.SetKey("upload", base::Value(cdr.upload()));
-  }
-
-  base::Value* request_tree = &dict;
-  std::string request_serialized;
-  JSONStringValueSerializer serializer(&request_serialized);
-  serializer.set_pretty_print(true);
-  serializer.Serialize(*request_tree);
-  return request_serialized;
-}
-
-std::string SerializeCSBRR(const ClientSafeBrowsingReportRequest& report) {
-  base::DictionaryValue report_request;
-  if (report.has_type()) {
-    report_request.SetInteger("type", static_cast<int>(report.type()));
-  }
-  if (report.has_page_url())
-    report_request.SetString("page_url", report.page_url());
-  if (report.has_client_country()) {
-    report_request.SetString("client_country", report.client_country());
-  }
-  if (report.has_repeat_visit()) {
-    report_request.SetInteger("repeat_visit", report.repeat_visit());
-  }
-  if (report.has_did_proceed()) {
-    report_request.SetInteger("did_proceed", report.did_proceed());
-  }
-  std::string serialized;
-  if (report.SerializeToString(&serialized)) {
-    std::string base64_encoded;
-    base::Base64Encode(serialized, &base64_encoded);
-    report_request.SetString("csbrr(base64)", base64_encoded);
-  }
-
-  base::Value* report_request_tree = &report_request;
-  std::string report_request_serialized;
-  JSONStringValueSerializer serializer(&report_request_serialized);
-  serializer.set_pretty_print(true);
-  serializer.Serialize(*report_request_tree);
-  return report_request_serialized;
-}
-
-base::Value SerializeReuseLookup(
-    const PasswordReuseLookup password_reuse_lookup) {
-  std::string lookup_result;
-  switch (password_reuse_lookup.lookup_result()) {
-    case PasswordReuseLookup::UNSPECIFIED:
-      lookup_result = "UNSPECIFIED";
-      break;
-    case PasswordReuseLookup::WHITELIST_HIT:
-      lookup_result = "WHITELIST_HIT";
-      break;
-    case PasswordReuseLookup::CACHE_HIT:
-      lookup_result = "CACHE_HIT";
-      break;
-    case PasswordReuseLookup::REQUEST_SUCCESS:
-      lookup_result = "REQUEST_SUCCESS";
-      break;
-    case PasswordReuseLookup::REQUEST_FAILURE:
-      lookup_result = "REQUEST_FAILURE";
-      break;
-    case PasswordReuseLookup::URL_UNSUPPORTED:
-      lookup_result = "URL_UNSUPPORTED";
-      break;
-    case PasswordReuseLookup::ENTERPRISE_WHITELIST_HIT:
-      lookup_result = "ENTERPRISE_WHITELIST_HIT";
-      break;
-    case PasswordReuseLookup::TURNED_OFF_BY_POLICY:
-      lookup_result = "TURNED_OFF_BY_POLICY";
-      break;
-  }
-  return base::Value(lookup_result);
-}
-
-base::Value SerializeVerdict(const PasswordReuseLookup password_reuse_lookup) {
-  std::string verdict;
-  switch (password_reuse_lookup.verdict()) {
-    case PasswordReuseLookup::VERDICT_UNSPECIFIED:
-      verdict = "VERDICT_UNSPECIFIED";
-      break;
-    case PasswordReuseLookup::SAFE:
-      verdict = "SAFE";
-      break;
-    case PasswordReuseLookup::LOW_REPUTATION:
-      verdict = "LOW_REPUTATION";
-      break;
-    case PasswordReuseLookup::PHISHING:
-      verdict = "PHISHING";
-      break;
-  }
-  return base::Value(verdict);
-}
-
-base::DictionaryValue SerializePGEvent(
-    const sync_pb::UserEventSpecifics& event) {
-  base::DictionaryValue result;
-
-  base::Time timestamp = base::Time::FromDeltaSinceWindowsEpoch(
-      base::TimeDelta::FromMicroseconds(event.event_time_usec()));
-  result.SetDouble("time", timestamp.ToJsTime());
-
-  base::DictionaryValue event_dict;
-
-  // Nominally only one of the following if() statements would be true.
-  // Note that top-level path is either password_captured, or one of the fields
-  // under GaiaPasswordReuse (ie. we've flattened the namespace for simplicity).
-
-  if (event.has_gaia_password_captured_event()) {
-    std::string event_trigger;
-
-    switch (event.gaia_password_captured_event().event_trigger()) {
-      case PasswordCaptured::UNSPECIFIED:
-        event_trigger = "UNSPECIFIED";
-        break;
-      case PasswordCaptured::USER_LOGGED_IN:
-        event_trigger = "USER_LOGGED_IN";
-        break;
-      case PasswordCaptured::EXPIRED_28D_TIMER:
-        event_trigger = "EXPIRED_28D_TIMER";
-        break;
-    }
-
-    event_dict.SetPath({"password_captured", "event_trigger"},
-                       base::Value(event_trigger));
-  }
-
-  GaiaPasswordReuse reuse = event.gaia_password_reuse_event();
-  if (reuse.has_reuse_detected()) {
-    event_dict.SetPath({"reuse_detected", "status", "enabled"},
-                       base::Value(reuse.reuse_detected().status().enabled()));
-
-    std::string reporting_population;
-    switch (
-        reuse.reuse_detected().status().safe_browsing_reporting_population()) {
-      case PasswordReuseDetected::SafeBrowsingStatus::
-          REPORTING_POPULATION_UNSPECIFIED:
-        reporting_population = "REPORTING_POPULATION_UNSPECIFIED";
-        break;
-      case PasswordReuseDetected::SafeBrowsingStatus::NONE:
-        reporting_population = "NONE";
-        break;
-      case PasswordReuseDetected::SafeBrowsingStatus::EXTENDED_REPORTING:
-        reporting_population = "EXTENDED_REPORTING";
-        break;
-      case PasswordReuseDetected::SafeBrowsingStatus::SCOUT:
-        reporting_population = "SCOUT";
-        break;
-    }
-    event_dict.SetPath({"reuse_detected", "status", "reporting_population"},
-                       base::Value(reporting_population));
-  }
-
-  if (reuse.has_reuse_lookup()) {
-    event_dict.SetPath({"reuse_lookup", "lookup_result"},
-                       SerializeReuseLookup(reuse.reuse_lookup()));
-    event_dict.SetPath({"reuse_lookup", "verdict"},
-                       SerializeVerdict(reuse.reuse_lookup()));
-    event_dict.SetPath({"reuse_lookup", "verdict_token"},
-                       base::Value(reuse.reuse_lookup().verdict_token()));
-  }
-
-  if (reuse.has_dialog_interaction()) {
-    std::string interaction_result;
-    switch (reuse.dialog_interaction().interaction_result()) {
-      case PasswordReuseDialogInteraction::UNSPECIFIED:
-        interaction_result = "UNSPECIFIED";
-        break;
-      case PasswordReuseDialogInteraction::WARNING_ACTION_TAKEN:
-        interaction_result = "WARNING_ACTION_TAKEN";
-        break;
-      case PasswordReuseDialogInteraction::WARNING_ACTION_IGNORED:
-        interaction_result = "WARNING_ACTION_IGNORED";
-        break;
-      case PasswordReuseDialogInteraction::WARNING_UI_IGNORED:
-        interaction_result = "WARNING_UI_IGNORED";
-        break;
-      case PasswordReuseDialogInteraction::WARNING_ACTION_TAKEN_ON_SETTINGS:
-        interaction_result = "WARNING_ACTION_TAKEN_ON_SETTINGS";
-        break;
-    }
-    event_dict.SetPath({"dialog_interaction", "interaction_result"},
-                       base::Value(interaction_result));
-  }
-
-  std::string event_serialized;
-  JSONStringValueSerializer serializer(&event_serialized);
-  serializer.set_pretty_print(true);
-  serializer.Serialize(event_dict);
-  result.SetString("message", event_serialized);
-  return result;
-}
-
-base::DictionaryValue SerializeSecurityEvent(
-    const sync_pb::GaiaPasswordReuse& event) {
-  base::DictionaryValue result;
-
-  base::DictionaryValue event_dict;
-  if (event.has_reuse_lookup()) {
-    event_dict.SetPath({"reuse_lookup", "lookup_result"},
-                       SerializeReuseLookup(event.reuse_lookup()));
-    event_dict.SetPath({"reuse_lookup", "verdict"},
-                       SerializeVerdict(event.reuse_lookup()));
-    event_dict.SetPath({"reuse_lookup", "verdict_token"},
-                       base::Value(event.reuse_lookup().verdict_token()));
-  }
-
-  std::string event_serialized;
-  JSONStringValueSerializer serializer(&event_serialized);
-  serializer.set_pretty_print(true);
-  serializer.Serialize(event_dict);
-  result.SetString("message", event_serialized);
-  return result;
-}
-
-base::Value SerializeFrame(const LoginReputationClientRequest::Frame& frame) {
-  base::DictionaryValue frame_dict;
-  frame_dict.SetKey("frame_index", base::Value(frame.frame_index()));
-  frame_dict.SetKey("parent_frame_index",
-                    base::Value(frame.parent_frame_index()));
-  frame_dict.SetKey("url", base::Value(frame.url()));
-  frame_dict.SetKey("has_password_field",
-                    base::Value(frame.has_password_field()));
-
-  base::ListValue referrer_list;
-  for (const ReferrerChainEntry& referrer : frame.referrer_chain()) {
-    referrer_list.Append(SerializeReferrer(referrer));
-  }
-  frame_dict.SetKey("referrer_chain", std::move(referrer_list));
-
-  frame_dict.SetPath(
-      {"referrer_chain_options", "recent_navigations_to_collect"},
-      base::Value(
-          frame.referrer_chain_options().recent_navigations_to_collect()));
-
-  base::ListValue form_list;
-  for (const LoginReputationClientRequest::Frame::Form& form : frame.forms()) {
-    base::DictionaryValue form_dict;
-    form_dict.SetKey("action_url", base::Value(form.action_url()));
-    form_dict.SetKey("has_password_field",
-                     base::Value(form.has_password_field()));
-    form_list.Append(std::move(form_dict));
-  }
-  frame_dict.SetKey("forms", std::move(form_list));
-
-  return std::move(frame_dict);
-}
-
-base::Value SerializePasswordReuseEvent(
-    const LoginReputationClientRequest::PasswordReuseEvent& event) {
-  base::DictionaryValue event_dict;
-
-  base::ListValue domains_list;
-  for (const std::string& domain : event.domains_matching_password()) {
-    domains_list.Append(base::Value(domain));
-  }
-  event_dict.SetKey("domains_matching_password", std::move(domains_list));
-
-  event_dict.SetKey("frame_id", base::Value(event.frame_id()));
-  event_dict.SetKey("is_chrome_signin_password",
-                    base::Value(event.is_chrome_signin_password()));
-
-  std::string sync_account_type;
-  switch (event.sync_account_type()) {
-    case LoginReputationClientRequest::PasswordReuseEvent::NOT_SIGNED_IN:
-      sync_account_type = "NOT_SIGNED_IN";
-      break;
-    case LoginReputationClientRequest::PasswordReuseEvent::GMAIL:
-      sync_account_type = "GMAIL";
-      break;
-    case LoginReputationClientRequest::PasswordReuseEvent::GSUITE:
-      sync_account_type = "GSUITE";
-      break;
-  }
-  event_dict.SetKey("sync_account_type", base::Value(sync_account_type));
-
-  std::string reused_password_type;
-  switch (event.reused_password_type()) {
-    case LoginReputationClientRequest::PasswordReuseEvent::
-        REUSED_PASSWORD_TYPE_UNKNOWN:
-      reused_password_type = "REUSED_PASSWORD_TYPE_UNKNOWN";
-      break;
-    case LoginReputationClientRequest::PasswordReuseEvent::SAVED_PASSWORD:
-      reused_password_type = "SAVED_PASSWORD";
-      break;
-    case LoginReputationClientRequest::PasswordReuseEvent::SIGN_IN_PASSWORD:
-      reused_password_type = "SIGN_IN_PASSWORD";
-      break;
-    case LoginReputationClientRequest::PasswordReuseEvent::OTHER_GAIA_PASSWORD:
-      reused_password_type = "OTHER_GAIA_PASSWORD";
-      break;
-    case LoginReputationClientRequest::PasswordReuseEvent::ENTERPRISE_PASSWORD:
-      reused_password_type = "ENTERPRISE_PASSWORD";
-      break;
-  }
-  event_dict.SetKey("reused_password_type", base::Value(reused_password_type));
-
-  return std::move(event_dict);
-}
-
-base::Value SerializeChromeUserPopulation(
-    const ChromeUserPopulation& population) {
-  base::DictionaryValue population_dict;
-
-  std::string user_population;
-  switch (population.user_population()) {
-    case ChromeUserPopulation::UNKNOWN_USER_POPULATION:
-      user_population = "UNKNOWN_USER_POPULATION";
-      break;
-    case ChromeUserPopulation::SAFE_BROWSING:
-      user_population = "SAFE_BROWSING";
-      break;
-    case ChromeUserPopulation::EXTENDED_REPORTING:
-      user_population = "EXTENDED_REPORTING";
-      break;
-  }
-  population_dict.SetKey("user_population", base::Value(user_population));
-
-  population_dict.SetKey("is_history_sync_enabled",
-                         base::Value(population.is_history_sync_enabled()));
-
-  base::ListValue finch_list;
-  for (const std::string& finch_group : population.finch_active_groups()) {
-    finch_list.Append(base::Value(finch_group));
-  }
-  population_dict.SetKey("finch_active_groups", std::move(finch_list));
-
-  std::string management_status;
-  switch (population.profile_management_status()) {
-    case ChromeUserPopulation::UNKNOWN:
-      management_status = "UNKNOWN";
-      break;
-    case ChromeUserPopulation::UNAVAILABLE:
-      management_status = "UNAVAILABLE";
-      break;
-    case ChromeUserPopulation::NOT_MANAGED:
-      management_status = "NOT_MANAGED";
-      break;
-    case ChromeUserPopulation::ENTERPRISE_MANAGED:
-      management_status = "ENTERPRISE_MANAGED";
-      break;
-  }
-  population_dict.SetKey("profile_management_status",
-                         base::Value(management_status));
-  population_dict.SetKey(
-      "is_under_advanced_protection",
-      base::Value(population.is_under_advanced_protection()));
-  population_dict.SetKey("is_incognito",
-                         base::Value(population.is_incognito()));
-
-  return std::move(population_dict);
-}
-
-base::Value SerializeRTThreatInfo(
-    const RTLookupResponse::ThreatInfo& threat_info) {
-  base::DictionaryValue threat_info_dict;
-  std::string threat_type;
-  switch (threat_info.threat_type()) {
-    case RTLookupResponse::ThreatInfo::THREAT_TYPE_UNSPECIFIED:
-      threat_type = "THREAT_TYPE_UNSPECIFIED";
-      break;
-    case RTLookupResponse::ThreatInfo::WEB_MALWARE:
-      threat_type = "WEB_MALWARE";
-      break;
-    case RTLookupResponse::ThreatInfo::SOCIAL_ENGINEERING:
-      threat_type = "SOCIAL_ENGINEERING";
-      break;
-    case RTLookupResponse::ThreatInfo::UNWANTED_SOFTWARE:
-      threat_type = "UNWANTED_SOFTWARE";
-      break;
-    case RTLookupResponse::ThreatInfo::UNCLEAR_BILLING:
-      threat_type = "UNCLEAR_BILLING";
-      break;
-  }
-  threat_info_dict.SetKey("threat_type", base::Value(threat_type));
-
-  threat_info_dict.SetKey(
-      "cache_duration_sec",
-      base::Value(static_cast<double>(threat_info.cache_duration_sec())));
-
-  threat_info_dict.SetKey("cache_expression",
-                          base::Value(threat_info.cache_expression()));
-
-  std::string verdict_type;
-  switch (threat_info.verdict_type()) {
-    case RTLookupResponse::ThreatInfo::VERDICT_TYPE_UNSPECIFIED:
-      verdict_type = "VERDICT_TYPE_UNSPECIFIED";
-      break;
-    case RTLookupResponse::ThreatInfo::SAFE:
-      verdict_type = "SAFE";
-      break;
-    case RTLookupResponse::ThreatInfo::DANGEROUS:
-      verdict_type = "DANGEROUS";
-      break;
-  }
-  threat_info_dict.SetKey("verdict_type", base::Value(verdict_type));
-
-  return std::move(threat_info_dict);
-}
-
-base::Value SerializeDomFeatures(const DomFeatures& dom_features) {
-  base::DictionaryValue dom_features_dict;
-  auto feature_map = std::make_unique<base::ListValue>();
-  for (const auto& feature : dom_features.feature_map()) {
-    auto feature_dict = std::make_unique<base::DictionaryValue>();
-    feature_dict->SetStringKey("name", feature.name());
-    feature_dict->SetDoubleKey("value", feature.value());
-    feature_map->Append(std::move(feature_dict));
-  }
-  dom_features_dict.SetList("feature_map", std::move(feature_map));
-
-  auto shingle_hashes = std::make_unique<base::ListValue>();
-  for (const auto& hash : dom_features.shingle_hashes()) {
-    shingle_hashes->AppendInteger(hash);
-  }
-  dom_features_dict.SetList("shingle_hashes", std::move(shingle_hashes));
-
-  dom_features_dict.SetInteger("model_version", dom_features.model_version());
-
-  return std::move(dom_features_dict);
-}
-
-std::string SerializePGPing(const LoginReputationClientRequest& request) {
-  base::DictionaryValue request_dict;
-
-  request_dict.SetKey("page_url", base::Value(request.page_url()));
-
-  std::string trigger_type;
-  switch (request.trigger_type()) {
-    case LoginReputationClientRequest::TRIGGER_TYPE_UNSPECIFIED:
-      trigger_type = "TRIGGER_TYPE_UNSPECIFIED";
-      break;
-    case LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE:
-      trigger_type = "UNFAMILIAR_LOGIN_PAGE";
-      break;
-    case LoginReputationClientRequest::PASSWORD_REUSE_EVENT:
-      trigger_type = "PASSWORD_REUSE_EVENT";
-      break;
-  }
-  request_dict.SetKey("trigger_type", base::Value(trigger_type));
-
-  base::ListValue frames_list;
-  for (const LoginReputationClientRequest::Frame& frame : request.frames()) {
-    frames_list.Append(SerializeFrame(frame));
-  }
-  request_dict.SetKey("frames", std::move(frames_list));
-
-  request_dict.SetKey(
-      "password_reuse_event",
-      SerializePasswordReuseEvent(request.password_reuse_event()));
-  request_dict.SetKey("stored_verdict_cnt",
-                      base::Value(request.stored_verdict_cnt()));
-  request_dict.SetKey("population",
-                      SerializeChromeUserPopulation(request.population()));
-  request_dict.SetKey("clicked_through_interstitial",
-                      base::Value(request.clicked_through_interstitial()));
-  request_dict.SetKey("content_type", base::Value(request.content_type()));
-
-  if (request.has_content_area_height()) {
-    request_dict.SetKey("content_area_height",
-                        base::Value(request.content_area_height()));
-  }
-  if (request.has_content_area_width()) {
-    request_dict.SetKey("content_area_width",
-                        base::Value(request.content_area_width()));
-  }
-
-  if (request.has_dom_features()) {
-    request_dict.SetKey("dom_features",
-                        SerializeDomFeatures(request.dom_features()));
-  }
-
-  std::string request_serialized;
-  JSONStringValueSerializer serializer(&request_serialized);
-  serializer.set_pretty_print(true);
-  serializer.Serialize(request_dict);
-  return request_serialized;
-}
-
-std::string SerializePGResponse(const LoginReputationClientResponse& response) {
-  base::DictionaryValue response_dict;
-
-  std::string verdict;
-  switch (response.verdict_type()) {
-    case LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED:
-      verdict = "VERDICT_TYPE_UNSPECIFIED";
-      break;
-    case LoginReputationClientResponse::SAFE:
-      verdict = "SAFE";
-      break;
-    case LoginReputationClientResponse::LOW_REPUTATION:
-      verdict = "LOW_REPUTATION";
-      break;
-    case LoginReputationClientResponse::PHISHING:
-      verdict = "PHISHING";
-      break;
-  }
-  response_dict.SetKey("verdict_type", base::Value(verdict));
-  response_dict.SetKey("cache_duration_sec",
-                       base::Value(int(response.cache_duration_sec())));
-  response_dict.SetKey("cache_expression",
-                       base::Value(response.cache_expression()));
-  response_dict.SetKey("verdict_token", base::Value(response.verdict_token()));
-
-  std::string response_serialized;
-  JSONStringValueSerializer serializer(&response_serialized);
-  serializer.set_pretty_print(true);
-  serializer.Serialize(response_dict);
-  return response_serialized;
-}
-
-std::string SerializeRTLookupPing(const RTLookupRequest& request) {
-  base::DictionaryValue request_dict;
-
-  request_dict.SetKey("url", base::Value(request.url()));
-  request_dict.SetKey("population",
-                      SerializeChromeUserPopulation(request.population()));
-
-  std::string lookupType;
-  switch (request.lookup_type()) {
-    case RTLookupRequest::LOOKUP_TYPE_UNSPECIFIED:
-      lookupType = "LOOKUP_TYPE_UNSPECIFIED";
-      break;
-    case RTLookupRequest::NAVIGATION:
-      lookupType = "NAVIGATION";
-      break;
-    case RTLookupRequest::DOWNLOAD:
-      lookupType = "DOWNLOAD";
-      break;
-  }
-
-  request_dict.SetKey("lookup_type", base::Value(lookupType));
-
-  std::string request_serialized;
-  JSONStringValueSerializer serializer(&request_serialized);
-  serializer.set_pretty_print(true);
-  serializer.Serialize(request_dict);
-  return request_serialized;
-}
-
-std::string SerializeRTLookupResponse(const RTLookupResponse& response) {
-  base::DictionaryValue response_dict;
-
-  base::ListValue threat_info_list;
-  for (const RTLookupResponse::ThreatInfo& threat_info :
-       response.threat_info()) {
-    threat_info_list.Append(SerializeRTThreatInfo(threat_info));
-  }
-  response_dict.SetKey("threat_infos", std::move(threat_info_list));
-
-  std::string response_serialized;
-  JSONStringValueSerializer serializer(&response_serialized);
-  serializer.set_pretty_print(true);
-  serializer.Serialize(response_dict);
-  return response_serialized;
-}
-
-base::Value SerializeLogMessage(const base::Time& timestamp,
-                                const std::string& message) {
-  base::DictionaryValue result;
-  result.SetDouble("time", timestamp.ToJsTime());
-  result.SetString("message", message);
-  return std::move(result);
-}
-
-base::Value SerializeReportingEvent(const base::Value& event) {
-  base::DictionaryValue result;
-
-  std::string event_serialized;
-  JSONStringValueSerializer serializer(&event_serialized);
-  serializer.set_pretty_print(true);
-  serializer.Serialize(event);
-
-  result.SetString("message", event_serialized);
-
-  return std::move(result);
-}
-
-}  // namespace
-
-SafeBrowsingUI::SafeBrowsingUI(content::WebUI* web_ui)
-    : content::WebUIController(web_ui) {
-  // Set up the chrome://safe-browsing source.
-
-  content::WebUIDataSource* html_source = content::WebUIDataSource::Create(
-      safe_browsing::kChromeUISafeBrowsingHost);
-
-  content::BrowserContext* browser_context =
-      web_ui->GetWebContents()->GetBrowserContext();
-
-  // Register callback handler.
-  // Handles messages from JavaScript to C++ via chrome.send().
-  web_ui->AddMessageHandler(
-      std::make_unique<SafeBrowsingUIHandler>(browser_context));
-
-  // Add required resources.
-  html_source->AddResourcePath("safe_browsing.css", IDR_SAFE_BROWSING_CSS);
-  html_source->AddResourcePath("safe_browsing.js", IDR_SAFE_BROWSING_JS);
-  html_source->SetDefaultResource(IDR_SAFE_BROWSING_HTML);
-
-  content::WebUIDataSource::Add(browser_context, html_source);
-}
-
-SafeBrowsingUI::~SafeBrowsingUI() {}
-
-SafeBrowsingUIHandler::SafeBrowsingUIHandler(content::BrowserContext* context)
-    : browser_context_(context) {}
-
-SafeBrowsingUIHandler::~SafeBrowsingUIHandler() {
-  WebUIInfoSingleton::GetInstance()->UnregisterWebUIInstance(this);
-}
-
-void SafeBrowsingUIHandler::OnJavascriptAllowed() {
-  // We don't want to register the SafeBrowsingUIHandler with the
-  // WebUIInfoSingleton at construction, since this can lead to
-  // messages being sent to the renderer before it's ready. So register it here.
-  WebUIInfoSingleton::GetInstance()->RegisterWebUIInstance(this);
-}
-
-void SafeBrowsingUIHandler::OnJavascriptDisallowed() {
-  // In certain situations, Javascript can become disallowed before the
-  // destructor is called (e.g. tab refresh/renderer crash). In these situation,
-  // we want to stop receiving JS messages.
-  WebUIInfoSingleton::GetInstance()->UnregisterWebUIInstance(this);
-}
-
-void SafeBrowsingUIHandler::GetExperiments(const base::ListValue* args) {
-  AllowJavascript();
-  std::string callback_id;
-  args->GetString(0, &callback_id);
-  ResolveJavascriptCallback(base::Value(callback_id), GetFeatureStatusList());
-}
-
-void SafeBrowsingUIHandler::GetPrefs(const base::ListValue* args) {
-  AllowJavascript();
-  std::string callback_id;
-  args->GetString(0, &callback_id);
-  ResolveJavascriptCallback(base::Value(callback_id),
-                            safe_browsing::GetSafeBrowsingPreferencesList(
-                                user_prefs::UserPrefs::Get(browser_context_)));
-}
-
-void SafeBrowsingUIHandler::GetCookie(const base::ListValue* args) {
-  std::string callback_id;
-  args->GetString(0, &callback_id);
-
-  WebUIInfoSingleton::GetInstance()->GetCookieManager()->GetAllCookies(
-      base::BindOnce(&SafeBrowsingUIHandler::OnGetCookie,
-                     weak_factory_.GetWeakPtr(), std::move(callback_id)));
-}
-
-void SafeBrowsingUIHandler::OnGetCookie(
-    const std::string& callback_id,
-    const std::vector<net::CanonicalCookie>& cookies) {
-  DCHECK_GE(1u, cookies.size());
-
-  std::string cookie = "No cookie";
-  double time = 0.0;
-  if (!cookies.empty()) {
-    cookie = cookies[0].Value();
-    time = cookies[0].CreationDate().ToJsTime();
-  }
-
-  base::Value response(base::Value::Type::LIST);
-  response.Append(base::Value(cookie));
-  response.Append(base::Value(time));
-
-  AllowJavascript();
-  ResolveJavascriptCallback(base::Value(callback_id), std::move(response));
-}
-
-void SafeBrowsingUIHandler::GetSavedPasswords(const base::ListValue* args) {
-  password_manager::HashPasswordManager hash_manager(
-      user_prefs::UserPrefs::Get(browser_context_));
-
-  base::ListValue saved_passwords;
-  for (const password_manager::PasswordHashData& hash_data :
-       hash_manager.RetrieveAllPasswordHashes()) {
-    saved_passwords.AppendString(hash_data.username);
-    saved_passwords.AppendBoolean(hash_data.is_gaia_password);
-  }
-
-  AllowJavascript();
-  std::string callback_id;
-  args->GetString(0, &callback_id);
-  ResolveJavascriptCallback(base::Value(callback_id), saved_passwords);
-}
-
-void SafeBrowsingUIHandler::GetDatabaseManagerInfo(
-    const base::ListValue* args) {
-  base::ListValue database_manager_info;
-
-#if BUILDFLAG(SAFE_BROWSING_DB_LOCAL)
-  const V4LocalDatabaseManager* local_database_manager_instance =
-      V4LocalDatabaseManager::current_local_database_manager();
-  if (local_database_manager_instance) {
-    DatabaseManagerInfo database_manager_info_proto;
-    FullHashCacheInfo full_hash_cache_info_proto;
-
-    local_database_manager_instance->CollectDatabaseManagerInfo(
-        &database_manager_info_proto, &full_hash_cache_info_proto);
-
-    if (database_manager_info_proto.has_update_info()) {
-      AddUpdateInfo(database_manager_info_proto.update_info(),
-                    &database_manager_info);
-    }
-    if (database_manager_info_proto.has_database_info()) {
-      AddDatabaseInfo(database_manager_info_proto.database_info(),
-                      &database_manager_info);
-    }
-
-    database_manager_info.Append(
-        base::Value(AddFullHashCacheInfo(full_hash_cache_info_proto)));
-  }
-#endif
-
-  AllowJavascript();
-  std::string callback_id;
-  args->GetString(0, &callback_id);
-
-  ResolveJavascriptCallback(base::Value(callback_id), database_manager_info);
-}
-
-void SafeBrowsingUIHandler::GetSentClientDownloadRequests(
-    const base::ListValue* args) {
-  const std::vector<std::unique_ptr<ClientDownloadRequest>>& cdrs =
-      WebUIInfoSingleton::GetInstance()->client_download_requests_sent();
-
-  base::ListValue cdrs_sent;
-
-  for (const auto& cdr : cdrs) {
-    cdrs_sent.Append(base::Value(SerializeClientDownloadRequest(*cdr)));
-  }
-
-  AllowJavascript();
-  std::string callback_id;
-  args->GetString(0, &callback_id);
-  ResolveJavascriptCallback(base::Value(callback_id), cdrs_sent);
-}
-
-void SafeBrowsingUIHandler::GetReceivedClientDownloadResponses(
-    const base::ListValue* args) {
-  const std::vector<std::unique_ptr<ClientDownloadResponse>>& cdrs =
-      WebUIInfoSingleton::GetInstance()->client_download_responses_received();
-
-  base::ListValue cdrs_received;
-
-  for (const auto& cdr : cdrs) {
-    cdrs_received.Append(base::Value(SerializeClientDownloadResponse(*cdr)));
-  }
-
-  AllowJavascript();
-  std::string callback_id;
-  args->GetString(0, &callback_id);
-  ResolveJavascriptCallback(base::Value(callback_id), cdrs_received);
-}
-
-void SafeBrowsingUIHandler::GetSentCSBRRs(const base::ListValue* args) {
-  const std::vector<std::unique_ptr<ClientSafeBrowsingReportRequest>>& reports =
-      WebUIInfoSingleton::GetInstance()->csbrrs_sent();
-
-  base::ListValue sent_reports;
-
-  for (const auto& report : reports) {
-    sent_reports.Append(base::Value(SerializeCSBRR(*report)));
-  }
-
-  AllowJavascript();
-  std::string callback_id;
-  args->GetString(0, &callback_id);
-  ResolveJavascriptCallback(base::Value(callback_id), sent_reports);
-}
-
-void SafeBrowsingUIHandler::GetPGEvents(const base::ListValue* args) {
-  const std::vector<sync_pb::UserEventSpecifics>& events =
-      WebUIInfoSingleton::GetInstance()->pg_event_log();
-
-  base::ListValue events_sent;
-
-  for (const sync_pb::UserEventSpecifics& event : events)
-    events_sent.Append(SerializePGEvent(event));
-
-  AllowJavascript();
-  std::string callback_id;
-  args->GetString(0, &callback_id);
-  ResolveJavascriptCallback(base::Value(callback_id), events_sent);
-}
-
-void SafeBrowsingUIHandler::GetSecurityEvents(const base::ListValue* args) {
-  const std::vector<sync_pb::GaiaPasswordReuse>& events =
-      WebUIInfoSingleton::GetInstance()->security_event_log();
-
-  base::ListValue events_sent;
-
-  for (const sync_pb::GaiaPasswordReuse& event : events)
-    events_sent.Append(SerializeSecurityEvent(event));
-
-  AllowJavascript();
-  std::string callback_id;
-  args->GetString(0, &callback_id);
-  ResolveJavascriptCallback(base::Value(callback_id), events_sent);
-}
-
-void SafeBrowsingUIHandler::GetPGPings(const base::ListValue* args) {
-  const std::vector<LoginReputationClientRequest> requests =
-      WebUIInfoSingleton::GetInstance()->pg_pings();
-
-  base::ListValue pings_sent;
-  for (size_t request_index = 0; request_index < requests.size();
-       request_index++) {
-    base::ListValue ping_entry;
-    ping_entry.Append(base::Value(int(request_index)));
-    ping_entry.Append(base::Value(SerializePGPing(requests[request_index])));
-    pings_sent.Append(std::move(ping_entry));
-  }
-
-  AllowJavascript();
-  std::string callback_id;
-  args->GetString(0, &callback_id);
-  ResolveJavascriptCallback(base::Value(callback_id), pings_sent);
-}
-
-void SafeBrowsingUIHandler::GetPGResponses(const base::ListValue* args) {
-  const std::map<int, LoginReputationClientResponse> responses =
-      WebUIInfoSingleton::GetInstance()->pg_responses();
-
-  base::ListValue responses_sent;
-  for (const auto& token_and_response : responses) {
-    base::ListValue response_entry;
-    response_entry.Append(base::Value(token_and_response.first));
-    response_entry.Append(
-        base::Value(SerializePGResponse(token_and_response.second)));
-    responses_sent.Append(std::move(response_entry));
-  }
-
-  AllowJavascript();
-  std::string callback_id;
-  args->GetString(0, &callback_id);
-  ResolveJavascriptCallback(base::Value(callback_id), responses_sent);
-}
-
-void SafeBrowsingUIHandler::GetRTLookupPings(const base::ListValue* args) {
-  const std::vector<RTLookupRequest> requests =
-      WebUIInfoSingleton::GetInstance()->rt_lookup_pings();
-
-  base::ListValue pings_sent;
-  for (size_t request_index = 0; request_index < requests.size();
-       request_index++) {
-    base::ListValue ping_entry;
-    ping_entry.Append(base::Value(int(request_index)));
-    ping_entry.Append(
-        base::Value(SerializeRTLookupPing(requests[request_index])));
-    pings_sent.Append(std::move(ping_entry));
-  }
-
-  AllowJavascript();
-  std::string callback_id;
-  args->GetString(0, &callback_id);
-  ResolveJavascriptCallback(base::Value(callback_id), pings_sent);
-}
-
-void SafeBrowsingUIHandler::GetRTLookupResponses(const base::ListValue* args) {
-  const std::map<int, RTLookupResponse> responses =
-      WebUIInfoSingleton::GetInstance()->rt_lookup_responses();
-
-  base::ListValue responses_sent;
-  for (const auto& token_and_response : responses) {
-    base::ListValue response_entry;
-    response_entry.Append(base::Value(token_and_response.first));
-    response_entry.Append(
-        base::Value(SerializeRTLookupResponse(token_and_response.second)));
-    responses_sent.Append(std::move(response_entry));
-  }
-
-  AllowJavascript();
-  std::string callback_id;
-  args->GetString(0, &callback_id);
-  ResolveJavascriptCallback(base::Value(callback_id), responses_sent);
-}
-
-void SafeBrowsingUIHandler::GetReferrerChain(const base::ListValue* args) {
-  std::string url_string;
-  args->GetString(1, &url_string);
-
-  ReferrerChainProvider* provider =
-      WebUIInfoSingleton::GetInstance()->referrer_chain_provider();
-
-  std::string callback_id;
-  args->GetString(0, &callback_id);
-
-  if (!provider) {
-    AllowJavascript();
-    ResolveJavascriptCallback(base::Value(callback_id), base::Value(""));
-    return;
-  }
-
-  ReferrerChain referrer_chain;
-  provider->IdentifyReferrerChainByEventURL(
-      GURL(url_string), SessionID::InvalidValue(), 2, &referrer_chain);
-
-  base::ListValue referrer_list;
-  for (const ReferrerChainEntry& entry : referrer_chain) {
-    referrer_list.Append(SerializeReferrer(entry));
-  }
-
-  std::string referrer_chain_serialized;
-  JSONStringValueSerializer serializer(&referrer_chain_serialized);
-  serializer.set_pretty_print(true);
-  serializer.Serialize(referrer_list);
-
-  AllowJavascript();
-  ResolveJavascriptCallback(base::Value(callback_id),
-                            base::Value(referrer_chain_serialized));
-}
-
-void SafeBrowsingUIHandler::GetReportingEvents(const base::ListValue* args) {
-  base::ListValue reporting_events;
-  for (const auto& reporting_event :
-       WebUIInfoSingleton::GetInstance()->reporting_events()) {
-    reporting_events.Append(reporting_event.Clone());
-  }
-
-  AllowJavascript();
-  std::string callback_id;
-  args->GetString(0, &callback_id);
-  ResolveJavascriptCallback(base::Value(callback_id), reporting_events);
-}
-
-void SafeBrowsingUIHandler::GetLogMessages(const base::ListValue* args) {
-  const std::vector<std::pair<base::Time, std::string>>& log_messages =
-      WebUIInfoSingleton::GetInstance()->log_messages();
-
-  base::ListValue messages_received;
-  for (const auto& message : log_messages) {
-    messages_received.Append(
-        base::Value(SerializeLogMessage(message.first, message.second)));
-  }
-
-  AllowJavascript();
-  std::string callback_id;
-  args->GetString(0, &callback_id);
-  ResolveJavascriptCallback(base::Value(callback_id), messages_received);
-}
-
-void SafeBrowsingUIHandler::NotifyClientDownloadRequestJsListener(
-    ClientDownloadRequest* client_download_request) {
-  AllowJavascript();
-  FireWebUIListener(
-      "sent-client-download-requests-update",
-      base::Value(SerializeClientDownloadRequest(*client_download_request)));
-}
-
-void SafeBrowsingUIHandler::NotifyClientDownloadResponseJsListener(
-    ClientDownloadResponse* client_download_response) {
-  AllowJavascript();
-  FireWebUIListener(
-      "received-client-download-responses-update",
-      base::Value(SerializeClientDownloadResponse(*client_download_response)));
-}
-
-void SafeBrowsingUIHandler::NotifyCSBRRJsListener(
-    ClientSafeBrowsingReportRequest* csbrr) {
-  AllowJavascript();
-  FireWebUIListener("sent-csbrr-update", base::Value(SerializeCSBRR(*csbrr)));
-}
-
-void SafeBrowsingUIHandler::NotifyPGEventJsListener(
-    const sync_pb::UserEventSpecifics& event) {
-  AllowJavascript();
-  FireWebUIListener("sent-pg-event", SerializePGEvent(event));
-}
-
-void SafeBrowsingUIHandler::NotifySecurityEventJsListener(
-    const sync_pb::GaiaPasswordReuse& event) {
-  AllowJavascript();
-  FireWebUIListener("sent-security-event", SerializeSecurityEvent(event));
-}
-
-void SafeBrowsingUIHandler::NotifyPGPingJsListener(
-    int token,
-    const LoginReputationClientRequest& request) {
-  base::ListValue request_list;
-  request_list.Append(base::Value(token));
-  request_list.Append(base::Value(SerializePGPing(request)));
-
-  AllowJavascript();
-  FireWebUIListener("pg-pings-update", request_list);
-}
-
-void SafeBrowsingUIHandler::NotifyPGResponseJsListener(
-    int token,
-    const LoginReputationClientResponse& response) {
-  base::ListValue response_list;
-  response_list.Append(base::Value(token));
-  response_list.Append(base::Value(SerializePGResponse(response)));
-
-  AllowJavascript();
-  FireWebUIListener("pg-responses-update", response_list);
-}
-
-void SafeBrowsingUIHandler::NotifyRTLookupPingJsListener(
-    int token,
-    const RTLookupRequest& request) {
-  base::ListValue request_list;
-  request_list.Append(base::Value(token));
-  request_list.Append(base::Value(SerializeRTLookupPing(request)));
-
-  AllowJavascript();
-  FireWebUIListener("rt-lookup-pings-update", request_list);
-}
-
-void SafeBrowsingUIHandler::NotifyRTLookupResponseJsListener(
-    int token,
-    const RTLookupResponse& response) {
-  base::ListValue response_list;
-  response_list.Append(base::Value(token));
-  response_list.Append(base::Value(SerializeRTLookupResponse(response)));
-
-  AllowJavascript();
-  FireWebUIListener("rt-lookup-responses-update", response_list);
-}
-
-void SafeBrowsingUIHandler::NotifyLogMessageJsListener(
-    const base::Time& timestamp,
-    const std::string& message) {
-  AllowJavascript();
-  FireWebUIListener("log-messages-update",
-                    base::Value(SerializeLogMessage(timestamp, message)));
-}
-
-void SafeBrowsingUIHandler::NotifyReportingEventJsListener(
-    const base::Value& event) {
-  AllowJavascript();
-  FireWebUIListener("reporting-events-update", SerializeReportingEvent(event));
-}
-
-void SafeBrowsingUIHandler::RegisterMessages() {
-  web_ui()->RegisterMessageCallback(
-      "getExperiments",
-      base::BindRepeating(&SafeBrowsingUIHandler::GetExperiments,
-                          base::Unretained(this)));
-  web_ui()->RegisterMessageCallback(
-      "getPrefs", base::BindRepeating(&SafeBrowsingUIHandler::GetPrefs,
-                                      base::Unretained(this)));
-  web_ui()->RegisterMessageCallback(
-      "getCookie", base::BindRepeating(&SafeBrowsingUIHandler::GetCookie,
-                                       base::Unretained(this)));
-  web_ui()->RegisterMessageCallback(
-      "getSavedPasswords",
-      base::BindRepeating(&SafeBrowsingUIHandler::GetSavedPasswords,
-                          base::Unretained(this)));
-  web_ui()->RegisterMessageCallback(
-      "getDatabaseManagerInfo",
-      base::BindRepeating(&SafeBrowsingUIHandler::GetDatabaseManagerInfo,
-                          base::Unretained(this)));
-  web_ui()->RegisterMessageCallback(
-      "getSentClientDownloadRequests",
-      base::BindRepeating(&SafeBrowsingUIHandler::GetSentClientDownloadRequests,
-                          base::Unretained(this)));
-  web_ui()->RegisterMessageCallback(
-      "getReceivedClientDownloadResponses",
-      base::BindRepeating(
-          &SafeBrowsingUIHandler::GetReceivedClientDownloadResponses,
-          base::Unretained(this)));
-  web_ui()->RegisterMessageCallback(
-      "getSentCSBRRs",
-      base::BindRepeating(&SafeBrowsingUIHandler::GetSentCSBRRs,
-                          base::Unretained(this)));
-  web_ui()->RegisterMessageCallback(
-      "getPGEvents", base::BindRepeating(&SafeBrowsingUIHandler::GetPGEvents,
-                                         base::Unretained(this)));
-  web_ui()->RegisterMessageCallback(
-      "getSecurityEvents",
-      base::BindRepeating(&SafeBrowsingUIHandler::GetSecurityEvents,
-                          base::Unretained(this)));
-  web_ui()->RegisterMessageCallback(
-      "getPGPings", base::BindRepeating(&SafeBrowsingUIHandler::GetPGPings,
-                                        base::Unretained(this)));
-  web_ui()->RegisterMessageCallback(
-      "getPGResponses",
-      base::BindRepeating(&SafeBrowsingUIHandler::GetPGResponses,
-                          base::Unretained(this)));
-  web_ui()->RegisterMessageCallback(
-      "getRTLookupPings",
-      base::BindRepeating(&SafeBrowsingUIHandler::GetRTLookupPings,
-                          base::Unretained(this)));
-  web_ui()->RegisterMessageCallback(
-      "getRTLookupResponses",
-      base::BindRepeating(&SafeBrowsingUIHandler::GetRTLookupResponses,
-                          base::Unretained(this)));
-  web_ui()->RegisterMessageCallback(
-      "getLogMessages",
-      base::BindRepeating(&SafeBrowsingUIHandler::GetLogMessages,
-                          base::Unretained(this)));
-  web_ui()->RegisterMessageCallback(
-      "getReferrerChain",
-      base::BindRepeating(&SafeBrowsingUIHandler::GetReferrerChain,
-                          base::Unretained(this)));
-  web_ui()->RegisterMessageCallback(
-      "getReportingEvents",
-      base::BindRepeating(&SafeBrowsingUIHandler::GetReportingEvents,
-                          base::Unretained(this)));
-}
-
-void SafeBrowsingUIHandler::SetWebUIForTesting(content::WebUI* web_ui) {
-  set_web_ui(web_ui);
-}
-
-CrSBLogMessage::CrSBLogMessage() {}
-
-CrSBLogMessage::~CrSBLogMessage() {
-  WebUIInfoSingleton::GetInstance()->LogMessage(stream_.str());
-  DLOG(WARNING) << stream_.str();
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/web_ui/safe_browsing_ui.h b/components/safe_browsing/web_ui/safe_browsing_ui.h
deleted file mode 100644
index de297bba..0000000
--- a/components/safe_browsing/web_ui/safe_browsing_ui.h
+++ /dev/null
@@ -1,482 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SAFE_BROWSING_WEBUI_SAFE_BROWSING_UI_H_
-#define COMPONENTS_SAFE_BROWSING_WEBUI_SAFE_BROWSING_UI_H_
-
-#include "base/bind.h"
-#include "base/macros.h"
-#include "components/safe_browsing/browser/safe_browsing_network_context.h"
-#include "components/safe_browsing/proto/csd.pb.h"
-#include "components/safe_browsing/proto/realtimeapi.pb.h"
-#include "components/safe_browsing/proto/webui.pb.h"
-#include "components/sync/protocol/user_event_specifics.pb.h"
-#include "content/public/browser/web_ui_controller.h"
-#include "content/public/browser/web_ui_data_source.h"
-#include "content/public/browser/web_ui_message_handler.h"
-#include "mojo/public/cpp/bindings/remote.h"
-#include "services/network/public/mojom/cookie_manager.mojom.h"
-#include "services/network/public/mojom/network_context.mojom.h"
-
-namespace base {
-class ListValue;
-template <typename T>
-struct DefaultSingletonTraits;
-}  // namespace base
-
-namespace safe_browsing {
-class WebUIInfoSingleton;
-class ReferrerChainProvider;
-
-class SafeBrowsingUIHandler : public content::WebUIMessageHandler {
- public:
-  SafeBrowsingUIHandler(content::BrowserContext* context);
-  ~SafeBrowsingUIHandler() override;
-
-  // Callback when Javascript becomes allowed in the WebUI.
-  void OnJavascriptAllowed() override;
-
-  // Callback when Javascript becomes disallowed in the WebUI.
-  void OnJavascriptDisallowed() override;
-
-  // Get the experiments that are currently enabled per Chrome instance.
-  void GetExperiments(const base::ListValue* args);
-
-  // Get the Safe Browsing related preferences for the current user.
-  void GetPrefs(const base::ListValue* args);
-
-  // Get the Safe Browsing cookie.
-  void GetCookie(const base::ListValue* args);
-
-  // Get the current captured passwords.
-  void GetSavedPasswords(const base::ListValue* args);
-
-  // Get the information related to the Safe Browsing database and full hash
-  // cache.
-  void GetDatabaseManagerInfo(const base::ListValue* args);
-
-  // Get the ClientDownloadRequests that have been collected since the oldest
-  // currently open chrome://safe-browsing tab was opened.
-  void GetSentClientDownloadRequests(const base::ListValue* args);
-
-  // Get the ClientDownloadReponses that have been collected since the oldest
-  // currently open chrome://safe-browsing tab was opened.
-  void GetReceivedClientDownloadResponses(const base::ListValue* args);
-
-  // Get the ThreatDetails that have been collected since the oldest currently
-  // open chrome://safe-browsing tab was opened.
-  void GetSentCSBRRs(const base::ListValue* args);
-
-  // Get the PhishGuard events that have been collected since the oldest
-  // currently open chrome://safe-browsing tab was opened.
-  void GetPGEvents(const base::ListValue* args);
-
-  // Get the Security events that have been collected since the oldest
-  // currently open chrome://safe-browsing tab was opened.
-  void GetSecurityEvents(const base::ListValue* args);
-
-  // Get the PhishGuard pings that have been sent since the oldest currently
-  // open chrome://safe-browsing tab was opened.
-  void GetPGPings(const base::ListValue* args);
-
-  // Get the PhishGuard responses that have been received since the oldest
-  // currently open chrome://safe-browsing tab was opened.
-  void GetPGResponses(const base::ListValue* args);
-
-  // Get the real time lookup pings that have been sent since the oldest
-  // currently open chrome://safe-browsing tab was opened.
-  void GetRTLookupPings(const base::ListValue* args);
-
-  // Get the real time lookup responses that have been received since the oldest
-  // currently open chrome://safe-browsing tab was opened.
-  void GetRTLookupResponses(const base::ListValue* args);
-
-  // Get the current referrer chain for a given URL.
-  void GetReferrerChain(const base::ListValue* args);
-
-  // Get the list of log messages that have been received since the oldest
-  // currently open chrome://safe-browsing tab was opened.
-  void GetLogMessages(const base::ListValue* args);
-
-  // Get the reporting events that have been collected since the oldest
-  // currently open chrome://safe-browsing tab was opened.
-  void GetReportingEvents(const base::ListValue* args);
-
-  // Register callbacks for WebUI messages.
-  void RegisterMessages() override;
-
-  // Sets the WebUI for testing
-  void SetWebUIForTesting(content::WebUI* web_ui);
-
- private:
-  friend class WebUIInfoSingleton;
-
-  // Called when any new ClientDownloadRequest messages are sent while one or
-  // more WebUI tabs are open.
-  void NotifyClientDownloadRequestJsListener(
-      ClientDownloadRequest* client_download_request);
-
-  // Called when any new ClientDownloadResponse messages are received while one
-  // or more WebUI tabs are open.
-  void NotifyClientDownloadResponseJsListener(
-      ClientDownloadResponse* client_download_response);
-
-  // Get the new ThreatDetails messages sent from ThreatDetails when a ping is
-  // sent, while one or more WebUI tabs are opened.
-  void NotifyCSBRRJsListener(ClientSafeBrowsingReportRequest* csbrr);
-
-  // Called when any new PhishGuard events are sent while one or more WebUI tabs
-  // are open.
-  void NotifyPGEventJsListener(const sync_pb::UserEventSpecifics& event);
-
-  // Called when any new Security events are sent while one or more WebUI tabs
-  // are open.
-  void NotifySecurityEventJsListener(const sync_pb::GaiaPasswordReuse& event);
-
-  // Called when any new PhishGuard pings are sent while one or more WebUI tabs
-  // are open.
-  void NotifyPGPingJsListener(int token,
-                              const LoginReputationClientRequest& request);
-
-  // Called when any new PhishGuard responses are received while one or more
-  // WebUI tabs are open.
-  void NotifyPGResponseJsListener(
-      int token,
-      const LoginReputationClientResponse& response);
-
-  // Called when any new real time lookup pings are sent while one or more
-  // WebUI tabs are open.
-  void NotifyRTLookupPingJsListener(int token, const RTLookupRequest& request);
-
-  // Called when any new real time lookup responses are received while one or
-  // more WebUI tabs are open.
-  void NotifyRTLookupResponseJsListener(int token,
-                                        const RTLookupResponse& response);
-
-  // Called when any new log messages are received while one or more WebUI tabs
-  // are open.
-  void NotifyLogMessageJsListener(const base::Time& timestamp,
-                                  const std::string& message);
-
-  // Called when any new reporting events are sent while one or more WebUI tabs
-  // are open.
-  void NotifyReportingEventJsListener(const base::Value& event);
-
-  // Callback when the CookieManager has returned the cookie.
-  void OnGetCookie(const std::string& callback_id,
-                   const std::vector<net::CanonicalCookie>& cookies);
-
-  content::BrowserContext* browser_context_;
-
-  // List that keeps all the WebUI listener objects.
-  static std::vector<SafeBrowsingUIHandler*> webui_list_;
-
-  base::WeakPtrFactory<SafeBrowsingUIHandler> weak_factory_{this};
-
-  DISALLOW_COPY_AND_ASSIGN(SafeBrowsingUIHandler);
-};
-
-// The WebUI for chrome://safe-browsing
-class SafeBrowsingUI : public content::WebUIController {
- public:
-  explicit SafeBrowsingUI(content::WebUI* web_ui);
-  ~SafeBrowsingUI() override;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(SafeBrowsingUI);
-};
-
-class WebUIInfoSingleton {
- public:
-  static WebUIInfoSingleton* GetInstance();
-
-  // Returns true when there is a listening chrome://safe-browsing tab.
-  static bool HasListener();
-
-  // Add the new message in |client_download_requests_sent_| and send it to all
-  // the open chrome://safe-browsing tabs.
-  void AddToClientDownloadRequestsSent(
-      std::unique_ptr<ClientDownloadRequest> report_request);
-
-  // Clear the list of the sent ClientDownloadRequest messages.
-  void ClearClientDownloadRequestsSent();
-
-  // Add the new message in |client_download_responses_received_| and send it to
-  // all the open chrome://safe-browsing tabs.
-  void AddToClientDownloadResponsesReceived(
-      std::unique_ptr<ClientDownloadResponse> response);
-
-  // Clear the list of the received ClientDownloadResponse messages.
-  void ClearClientDownloadResponsesReceived();
-
-  // Add the new message in |csbrrs_sent_| and send it to all the open
-  // chrome://safe-browsing tabs.
-  void AddToCSBRRsSent(std::unique_ptr<ClientSafeBrowsingReportRequest> csbrr);
-
-  // Clear the list of the sent ClientSafeBrowsingReportRequest messages.
-  void ClearCSBRRsSent();
-
-  // Add the new message in |pg_event_log_| and send it to all the open
-  // chrome://safe-browsing tabs.
-  void AddToPGEvents(const sync_pb::UserEventSpecifics& event);
-
-  // Clear the list of sent PhishGuard events.
-  void ClearPGEvents();
-
-  // Add the new message in |security_event_log_| and send it to all the open
-  // chrome://safe-browsing tabs.
-  void AddToSecurityEvents(const sync_pb::GaiaPasswordReuse& event);
-
-  // Clear the list of sent Security events.
-  void ClearSecurityEvents();
-
-  // Add the new ping to |pg_pings_| and send it to all the open
-  // chrome://safe-browsing tabs. Returns a token that can be used in
-  // |AddToPGReponses| to correlate a ping and response.
-  int AddToPGPings(const LoginReputationClientRequest& request);
-
-  // Add the new response to |pg_responses_| and send it to all the open
-  // chrome://safe-browsing tabs.
-  void AddToPGResponses(int token,
-                        const LoginReputationClientResponse& response);
-
-  // Clear the list of sent PhishGuard pings and responses.
-  void ClearPGPings();
-
-  // Add the new ping to |rt_lookup_pings_|. Returns a token that can be used in
-  // |AddToRTLookupResponses| to correlate a ping and response.
-  int AddToRTLookupPings(const RTLookupRequest request);
-
-  // Add the new response to |rt_lookup_responses_| and send it to all the open
-  // chrome://safe-browsing tabs.
-  void AddToRTLookupResponses(int token, const RTLookupResponse response);
-
-  // Clear the list of sent RT Lookup pings and responses.
-  void ClearRTLookupPings();
-
-  // Log an arbitrary message. Frequently used for debugging.
-  void LogMessage(const std::string& message);
-
-  // Clear the log messages.
-  void ClearLogMessages();
-
-  // Notify listeners of changes to the log messages. Static to avoid this being
-  // called after the destruction of the WebUIInfoSingleton
-  static void NotifyLogMessageListeners(const base::Time& timestamp,
-                                        const std::string& message);
-
-  // Add the reporting event to |reporting_events_| and send it to all the open
-  // chrome://safe-browsing tabs.
-  void AddToReportingEvents(const base::Value& event);
-
-  // Clear |reporting_events_|.
-  void ClearReportingEvents();
-
-  // Register the new WebUI listener object.
-  void RegisterWebUIInstance(SafeBrowsingUIHandler* webui);
-
-  // Unregister the WebUI listener object, and clean the list of reports, if
-  // this is last listener.
-  void UnregisterWebUIInstance(SafeBrowsingUIHandler* webui);
-
-  // Get the list of the sent ClientDownloadRequests that have been collected
-  // since the oldest currently open chrome://safe-browsing tab was opened.
-  const std::vector<std::unique_ptr<ClientDownloadRequest>>&
-  client_download_requests_sent() const {
-    return client_download_requests_sent_;
-  }
-
-  // Get the list of the sent ClientDownloadResponses that have been collected
-  // since the oldest currently open chrome://safe-browsing tab was opened.
-  const std::vector<std::unique_ptr<ClientDownloadResponse>>&
-  client_download_responses_received() const {
-    return client_download_responses_received_;
-  }
-
-  // Get the list of the sent CSBRR reports that have been collected since the
-  // oldest currently open chrome://safe-browsing tab was opened.
-  const std::vector<std::unique_ptr<ClientSafeBrowsingReportRequest>>&
-  csbrrs_sent() const {
-    return csbrrs_sent_;
-  }
-
-  // Get the list of WebUI listener objects.
-  const std::vector<SafeBrowsingUIHandler*>& webui_instances() const {
-    return webui_instances_;
-  }
-
-  // Get the list of PhishGuard events since the oldest currently open
-  // chrome://safe-browsing tab was opened.
-  const std::vector<sync_pb::UserEventSpecifics>& pg_event_log() const {
-    return pg_event_log_;
-  }
-
-  // Get the list of Security events since the oldest currently open
-  // chrome://safe-browsing tab was opened.
-  const std::vector<sync_pb::GaiaPasswordReuse>& security_event_log() const {
-    return security_event_log_;
-  }
-
-  // Get the list of PhishGuard pings since the oldest currently open
-  // chrome://safe-browsing tab was opened.
-  const std::vector<LoginReputationClientRequest>& pg_pings() const {
-    return pg_pings_;
-  }
-
-  // Get the list of PhishGuard pings since the oldest currently open
-  // chrome://safe-browsing tab was opened.
-  const std::map<int, LoginReputationClientResponse>& pg_responses() const {
-    return pg_responses_;
-  }
-
-  // Get the list of real time lookup pings since the oldest currently open
-  // chrome://safe-browsing tab was opened.
-  const std::vector<RTLookupRequest>& rt_lookup_pings() const {
-    return rt_lookup_pings_;
-  }
-
-  // Get the list of real time lookup pings since the oldest currently open
-  // chrome://safe-browsing tab was opened.
-  const std::map<int, RTLookupResponse>& rt_lookup_responses() const {
-    return rt_lookup_responses_;
-  }
-
-  ReferrerChainProvider* referrer_chain_provider() {
-    return referrer_chain_provider_;
-  }
-
-  void set_referrer_chain_provider(ReferrerChainProvider* provider) {
-    referrer_chain_provider_ = provider;
-  }
-
-  const std::vector<std::pair<base::Time, std::string>>& log_messages() {
-    return log_messages_;
-  }
-
-  const std::vector<base::Value>& reporting_events() {
-    return reporting_events_;
-  }
-
-  network::mojom::CookieManager* GetCookieManager();
-
-  void set_network_context(SafeBrowsingNetworkContext* network_context) {
-    network_context_ = network_context;
-  }
-
-  void AddListenerForTesting() { has_test_listener_ = true; }
-
- private:
-  WebUIInfoSingleton();
-  ~WebUIInfoSingleton();
-
-  void InitializeCookieManager();
-
-  friend struct base::DefaultSingletonTraits<WebUIInfoSingleton>;
-
-  // List of ClientDownloadRequests sent since since the oldest currently open
-  // chrome://safe-browsing tab was opened.
-  // "ClientDownloadRequests" cannot be const, due to being used by functions
-  // that call AllowJavascript(), which is not marked const.
-  std::vector<std::unique_ptr<ClientDownloadRequest>>
-      client_download_requests_sent_;
-
-  // List of ClientDownloadResponses received since since the oldest currently
-  // open chrome://safe-browsing tab was opened. "ClientDownloadReponse" cannot
-  // be const, due to being used by functions that call AllowJavascript(), which
-  // is not marked const.
-  std::vector<std::unique_ptr<ClientDownloadResponse>>
-      client_download_responses_received_;
-
-  // List of CSBRRs sent since since the oldest currently open
-  // chrome://safe-browsing tab was opened.
-  // "ClientSafeBrowsingReportRequest" cannot be const, due to being used by
-  // functions that call AllowJavascript(), which is not marked const.
-  std::vector<std::unique_ptr<ClientSafeBrowsingReportRequest>> csbrrs_sent_;
-
-  // List of PhishGuard events sent since the oldest currently open
-  // chrome://safe-browsing tab was opened.
-  std::vector<sync_pb::UserEventSpecifics> pg_event_log_;
-
-  // List of Security events sent since the oldest currently open
-  // chrome://safe-browsing tab was opened.
-  std::vector<sync_pb::GaiaPasswordReuse> security_event_log_;
-
-  // List of PhishGuard pings sent since the oldest currently open
-  // chrome://safe-browsing tab was opened.
-  std::vector<LoginReputationClientRequest> pg_pings_;
-
-  // List of PhishGuard responses received since the oldest currently open
-  // chrome://safe-browsing tab was opened.
-  std::map<int, LoginReputationClientResponse> pg_responses_;
-
-  // List of real time lookup pings sent since the oldest currently open
-  // chrome://safe-browsing tab was opened.
-  std::vector<RTLookupRequest> rt_lookup_pings_;
-
-  // List of real time lookup responses received since the oldest currently open
-  // chrome://safe-browsing tab was opened.
-  std::map<int, RTLookupResponse> rt_lookup_responses_;
-
-  // List of WebUI listener objects. "SafeBrowsingUIHandler*" cannot be const,
-  // due to being used by functions that call AllowJavascript(), which is not
-  // marked const.
-  std::vector<SafeBrowsingUIHandler*> webui_instances_;
-
-  // List of messages logged since the oldest currently open
-  // chrome://safe-browsing tab was opened.
-  std::vector<std::pair<base::Time, std::string>> log_messages_;
-
-  // List of reporting events logged since the oldest currently open
-  // chrome://safe-browsing tab was opened.
-  std::vector<base::Value> reporting_events_;
-
-  // The current referrer chain provider, if any. Can be nullptr.
-  ReferrerChainProvider* referrer_chain_provider_ = nullptr;
-
-  // The current NetworkContext for Safe Browsing pings.
-  SafeBrowsingNetworkContext* network_context_ = nullptr;
-
-  // The current CookieManager for the Safe Browsing cookie.
-  mojo::Remote<network::mojom::CookieManager> cookie_manager_remote_;
-
-  // Whether there is a test listener.
-  bool has_test_listener_ = false;
-
-  DISALLOW_COPY_AND_ASSIGN(WebUIInfoSingleton);
-};
-
-// Used for streaming messages to the WebUIInfoSingleton. Collects streamed
-// messages, then sends them to the WebUIInfoSingleton when destroyed. Intended
-// to be used in CRSBLOG macro.
-class CrSBLogMessage {
- public:
-  CrSBLogMessage();
-  ~CrSBLogMessage();
-
-  std::ostream& stream() { return stream_; }
-
- private:
-  std::ostringstream stream_;
-};
-
-// Used to consume a stream so that we don't even evaluate the streamed data if
-// there are no chrome://safe-browsing tabs open.
-class CrSBLogVoidify {
- public:
-  CrSBLogVoidify() = default;
-
-  // This has to be an operator with a precedence lower than <<,
-  // but higher than ?:
-  void operator&(std::ostream&) {}
-};
-
-#define CRSBLOG                                         \
-  (!::safe_browsing::WebUIInfoSingleton::HasListener()) \
-      ? static_cast<void>(0)                            \
-      : ::safe_browsing::CrSBLogVoidify() &             \
-            ::safe_browsing::CrSBLogMessage().stream()
-
-}  // namespace safe_browsing
-
-#endif  // COMPONENTS_SAFE_BROWSING_WEBUI_SAFE_BROWSING_UI_H_
diff --git a/components/safe_browsing/web_ui/safe_browsing_ui_unittest.cc b/components/safe_browsing/web_ui/safe_browsing_ui_unittest.cc
deleted file mode 100644
index 87fdd59..0000000
--- a/components/safe_browsing/web_ui/safe_browsing_ui_unittest.cc
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/web_ui/safe_browsing_ui.h"
-#include "content/public/test/browser_task_environment.h"
-#include "content/public/test/test_browser_context.h"
-#include "content/public/test/test_web_ui.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace safe_browsing {
-
-class SafeBrowsingUITest : public testing::Test {
- public:
-  SafeBrowsingUITest() {}
-
-  void SetUp() override {}
-
-  int SetMemberInt(int member_int) {
-    member_int_ = member_int;
-    return member_int_;
-  }
-
-  SafeBrowsingUIHandler* RegisterNewHandler() {
-    auto handler_unique =
-        std::make_unique<SafeBrowsingUIHandler>(&browser_context_);
-
-    SafeBrowsingUIHandler* handler = handler_unique.get();
-    handler->SetWebUIForTesting(&web_ui_);
-    WebUIInfoSingleton::GetInstance()->RegisterWebUIInstance(handler);
-
-    web_ui_.AddMessageHandler(std::move(handler_unique));
-    return handler;
-  }
-
-  void UnregisterHandler(SafeBrowsingUIHandler* handler) {
-    WebUIInfoSingleton::GetInstance()->UnregisterWebUIInstance(handler);
-  }
-
- protected:
-  int member_int_;
-  content::TestWebUI web_ui_;
-  content::BrowserTaskEnvironment task_environment_;
-  content::TestBrowserContext browser_context_;
-};
-
-TEST_F(SafeBrowsingUITest, CRSBLOGDoesNotEvaluateWhenNoListeners) {
-  member_int_ = 0;
-
-  // Start with no listeners, so SetMemberInt() should not be evaluated.
-  CRSBLOG << SetMemberInt(1);
-  EXPECT_EQ(member_int_, 0);
-
-  // Register a listener, so SetMemberInt() will be evaluated.
-  SafeBrowsingUIHandler* handler = RegisterNewHandler();
-
-  CRSBLOG << SetMemberInt(1);
-  EXPECT_EQ(member_int_, 1);
-
-  UnregisterHandler(handler);
-}
-
-}  // namespace safe_browsing
diff --git a/components/security_interstitials/content/BUILD.gn b/components/security_interstitials/content/BUILD.gn
index 4f0bb733..39327343 100644
--- a/components/security_interstitials/content/BUILD.gn
+++ b/components/security_interstitials/content/BUILD.gn
@@ -13,6 +13,8 @@
     "cert_report_helper.h",
     "certificate_error_report.cc",
     "certificate_error_report.h",
+    "common_name_mismatch_handler.cc",
+    "common_name_mismatch_handler.h",
     "connection_help_ui.cc",
     "connection_help_ui.h",
     "known_interception_disclosure_ui.cc",
@@ -41,7 +43,7 @@
   ]
 
   public_deps = [
-    "//components/safe_browsing/db:hit_report",
+    "//components/safe_browsing/core/db:hit_report",
   ]
 
   deps = [
@@ -52,9 +54,9 @@
     "//components/network_time",
     "//components/prefs:prefs",
     "//components/resources",
-    "//components/safe_browsing/common:safe_browsing_prefs",
-    "//components/safe_browsing/db:hit_report",
-    "//components/safe_browsing/db:util",
+    "//components/safe_browsing/core/common:safe_browsing_prefs",
+    "//components/safe_browsing/core/db:hit_report",
+    "//components/safe_browsing/core/db:util",
     "//components/security_interstitials/core:core",
     "//components/ssl_errors",
     "//components/strings:components_strings_grit",
@@ -75,6 +77,7 @@
 proto_library("proto") {
   sources = [
     "cert_logger.proto",
+    "ssl_error_assistant.proto",
   ]
 }
 
diff --git a/components/security_interstitials/content/DEPS b/components/security_interstitials/content/DEPS
index 6f960a4..b9e4de7b 100644
--- a/components/security_interstitials/content/DEPS
+++ b/components/security_interstitials/content/DEPS
@@ -3,8 +3,8 @@
   "+components/grit/components_resources.h",
   "+components/network_time",
   "+components/prefs",
-  "+components/safe_browsing/common",
-  "+components/safe_browsing/db",
+  "+components/safe_browsing/core/common",
+  "+components/safe_browsing/core/db",
   "+components/security_interstitials/core",
   "+components/ssl_errors",
   "+components/user_prefs",
diff --git a/components/security_interstitials/content/bad_clock_blocking_page.cc b/components/security_interstitials/content/bad_clock_blocking_page.cc
index d85db8b..d3b3d73 100644
--- a/components/security_interstitials/content/bad_clock_blocking_page.cc
+++ b/components/security_interstitials/content/bad_clock_blocking_page.cc
@@ -7,7 +7,7 @@
 #include <utility>
 
 #include "base/strings/string_number_conversions.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/security_interstitials/content/cert_report_helper.h"
 #include "components/security_interstitials/content/security_interstitial_controller_client.h"
 #include "components/security_interstitials/content/ssl_cert_reporter.h"
diff --git a/components/security_interstitials/content/captive_portal_blocking_page.cc b/components/security_interstitials/content/captive_portal_blocking_page.cc
index 5c026490..2d14dbb 100644
--- a/components/security_interstitials/content/captive_portal_blocking_page.cc
+++ b/components/security_interstitials/content/captive_portal_blocking_page.cc
@@ -15,7 +15,7 @@
 #include "build/build_config.h"
 #include "components/captive_portal/captive_portal_detector.h"
 #include "components/captive_portal/captive_portal_metrics.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/security_interstitials/content/cert_report_helper.h"
 #include "components/security_interstitials/content/security_interstitial_controller_client.h"
 #include "components/security_interstitials/content/ssl_cert_reporter.h"
diff --git a/components/security_interstitials/content/cert_report_helper.cc b/components/security_interstitials/content/cert_report_helper.cc
index f149dcf0..833a226 100644
--- a/components/security_interstitials/content/cert_report_helper.cc
+++ b/components/security_interstitials/content/cert_report_helper.cc
@@ -15,7 +15,7 @@
 #include "build/branding_buildflags.h"
 #include "build/build_config.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/security_interstitials/content/ssl_cert_reporter.h"
 #include "components/security_interstitials/core/controller_client.h"
 #include "components/security_interstitials/core/metrics_helper.h"
diff --git a/components/security_interstitials/content/common_name_mismatch_handler.cc b/components/security_interstitials/content/common_name_mismatch_handler.cc
new file mode 100644
index 0000000..0dddbc5
--- /dev/null
+++ b/components/security_interstitials/content/common_name_mismatch_handler.cc
@@ -0,0 +1,171 @@
+// 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 "components/security_interstitials/content/common_name_mismatch_handler.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "components/ssl_errors/error_classification.h"
+#include "net/base/load_flags.h"
+#include "net/http/http_response_headers.h"
+#include "net/http/http_util.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "net/url_request/redirect_info.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/simple_url_loader.h"
+#include "services/network/public/mojom/url_response_head.mojom.h"
+
+CommonNameMismatchHandler::CommonNameMismatchHandler(
+    const GURL& request_url,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
+    : request_url_(request_url),
+      url_loader_factory_(std::move(url_loader_factory)) {}
+
+CommonNameMismatchHandler::~CommonNameMismatchHandler() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+// static
+CommonNameMismatchHandler::TestingState
+    CommonNameMismatchHandler::testing_state_ = NOT_TESTING;
+
+void CommonNameMismatchHandler::CheckSuggestedUrl(
+    const GURL& url,
+    const CheckUrlCallback& callback) {
+  // Should be used only in tests.
+  if (testing_state_ == IGNORE_REQUESTS_FOR_TESTING)
+    return;
+
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(!IsCheckingSuggestedUrl());
+  DCHECK(check_url_callback_.is_null());
+
+  check_url_ = url;
+  check_url_callback_ = callback;
+
+  // Create traffic annotation tag.
+  net::NetworkTrafficAnnotationTag traffic_annotation =
+      net::DefineNetworkTrafficAnnotation("ssl_name_mismatch_lookup", R"(
+        semantics {
+          sender: "SSL Name Mismatch Handler"
+          description:
+            "If Chromium cannot make a secure connection to a site, this can "
+            "be because the site is misconfigured. The site may be serving a "
+            "security certificate intended for another site. If the SSL Common "
+            "Name Mismatch Handling feature is enabled, Chromium will try to "
+            "detect if one of the domains listed in the site's certificate is "
+            "available by issuing requests to those domains. If the response "
+            "indicates that an alternative site for which the certificate is "
+            "valid is available, Chromium will automatically redirect the user "
+            "to the alternative site."
+          trigger: "Resource load."
+          data: "An HTTP HEAD request to the alternative site."
+          destination: WEBSITE
+        }
+        policy {
+          cookies_allowed: NO
+          setting:
+            "Users can disable this feature by command line flag "
+            "'--disable-feature=SSLCommonNameMismatchHandling'."
+          policy_exception_justification:
+            "Not implemented."
+        })");
+
+  auto resource_request = std::make_unique<network::ResourceRequest>();
+  // Can't safely use net::LOAD_DISABLE_CERT_NETWORK_FETCHES here,
+  // since then the connection may be reused without checking the cert.
+  resource_request->url = check_url_;
+  resource_request->method = "HEAD";
+  resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
+
+  simple_url_loader_ = network::SimpleURLLoader::Create(
+      std::move(resource_request), traffic_annotation);
+  // Don't follow redirects to prevent leaking URL data to HTTP sites.
+  simple_url_loader_->SetOnRedirectCallback(
+      base::BindRepeating(&CommonNameMismatchHandler::OnSimpleLoaderRedirect,
+                          base::Unretained(this)));
+  simple_url_loader_->SetOnResponseStartedCallback(
+      base::BindOnce(&CommonNameMismatchHandler::OnSimpleLoaderResponseStarted,
+                     base::Unretained(this)));
+  simple_url_loader_->DownloadToString(
+      url_loader_factory_.get(),
+      base::BindOnce(&CommonNameMismatchHandler::OnSimpleLoaderComplete,
+                     base::Unretained(this)),
+      1 /*max_body_size*/);
+}
+
+// static
+bool CommonNameMismatchHandler::GetSuggestedUrl(
+    const GURL& request_url,
+    const std::vector<std::string>& dns_names,
+    GURL* suggested_url) {
+  std::string www_mismatch_hostname;
+  if (!ssl_errors::GetWWWSubDomainMatch(request_url, dns_names,
+                                        &www_mismatch_hostname)) {
+    return false;
+  }
+  // The full URL should be pinged, not just the new hostname. So, get the
+  // |suggested_url| with the |request_url|'s hostname replaced with
+  // new hostname. Keep resource path, query params the same.
+  GURL::Replacements replacements;
+  replacements.SetHostStr(www_mismatch_hostname);
+  *suggested_url = request_url.ReplaceComponents(replacements);
+  return true;
+}
+
+void CommonNameMismatchHandler::Cancel() {
+  simple_url_loader_.reset();
+  check_url_callback_.Reset();
+}
+
+void CommonNameMismatchHandler::OnSimpleLoaderHandler(
+    const GURL& final_url,
+    const network::mojom::URLResponseHead* head) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(IsCheckingSuggestedUrl());
+  DCHECK(!check_url_callback_.is_null());
+
+  SuggestedUrlCheckResult result = SUGGESTED_URL_NOT_AVAILABLE;
+
+  // Make sure the URL is a HTTPS page and returns a proper response code.
+  int response_code = -1;
+  // head may be null here, if called from OnSimpleLoaderComplete.
+  if (head && head->headers) {
+    response_code = head->headers->response_code();
+  }
+  if (response_code == 200 && final_url.SchemeIsCryptographic() &&
+      final_url.host() != request_url_.host()) {
+    DCHECK_EQ(final_url.host(), final_url.host());
+    result = SUGGESTED_URL_AVAILABLE;
+  }
+  simple_url_loader_.reset();
+  std::move(check_url_callback_).Run(result, check_url_);
+}
+
+void CommonNameMismatchHandler::OnSimpleLoaderRedirect(
+    const net::RedirectInfo& redirect_info,
+    const network::mojom::URLResponseHead& response_head,
+    std::vector<std::string>* to_be_removed_headers) {
+  OnSimpleLoaderHandler(redirect_info.new_url, &response_head);
+}
+
+void CommonNameMismatchHandler::OnSimpleLoaderResponseStarted(
+    const GURL& final_url,
+    const network::mojom::URLResponseHead& response_head) {
+  OnSimpleLoaderHandler(final_url, &response_head);
+}
+
+void CommonNameMismatchHandler::OnSimpleLoaderComplete(
+    std::unique_ptr<std::string> response_body) {
+  OnSimpleLoaderHandler(simple_url_loader_->GetFinalURL(),
+                        simple_url_loader_->ResponseInfo());
+}
+
+bool CommonNameMismatchHandler::IsCheckingSuggestedUrl() const {
+  return !!simple_url_loader_;
+}
diff --git a/components/security_interstitials/content/common_name_mismatch_handler.h b/components/security_interstitials/content/common_name_mismatch_handler.h
new file mode 100644
index 0000000..6f7ea1e2
--- /dev/null
+++ b/components/security_interstitials/content/common_name_mismatch_handler.h
@@ -0,0 +1,108 @@
+// Copyright (c) 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 COMPONENTS_SECURITY_INTERSTITIALS_CONTENT_COMMON_NAME_MISMATCH_HANDLER_H_
+#define COMPONENTS_SECURITY_INTERSTITIALS_CONTENT_COMMON_NAME_MISMATCH_HANDLER_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/sequence_checker.h"
+#include "base/time/time.h"
+#include "services/network/public/mojom/url_response_head.mojom-forward.h"
+#include "url/gurl.h"
+
+namespace net {
+struct RedirectInfo;
+}  // namespace net
+
+namespace network {
+class SharedURLLoaderFactory;
+class SimpleURLLoader;
+}  // namespace network
+
+// This class handles errors due to common name mismatches
+// (|ERR_CERT_COMMON_NAME_INVALID|) and helps remediate them by suggesting
+// alternative URLs that may be what the user intended to load.
+class CommonNameMismatchHandler {
+ public:
+  enum SuggestedUrlCheckResult {
+    // The request succeeds with good response code i.e. URL exists and its
+    // certificate is valid.
+    SUGGESTED_URL_AVAILABLE,
+    // Suggested URL is either not available or has a bad certificate.
+    SUGGESTED_URL_NOT_AVAILABLE
+  };
+
+  enum TestingState {
+    NOT_TESTING,
+    // Disables the actual request to the |suggested_url|.
+    IGNORE_REQUESTS_FOR_TESTING
+  };
+
+  typedef base::Callback<void(SuggestedUrlCheckResult result,
+                              const GURL& suggested_url)>
+      CheckUrlCallback;
+
+  CommonNameMismatchHandler(
+      const GURL& request_url,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
+  ~CommonNameMismatchHandler();
+
+  // Performs a network request to suggested URL. After completion, runs the
+  // |callback|.
+  void CheckSuggestedUrl(const GURL& url, const CheckUrlCallback& callback);
+
+  // Determines if, for |request_url| serving a certificate that is valid for
+  // the domain names |dns_names|, there is a name that the certificate is
+  // valid for that closely matches the original name in |request_url|. If
+  // so, returns true, and sets |*suggested_url| to a URL that is unlikely
+  // to cause an ERR_CERT_COMMON_NAME_INVALID error.
+  static bool GetSuggestedUrl(const GURL& request_url,
+                              const std::vector<std::string>& dns_names,
+                              GURL* suggested_url);
+
+  // Used in tests, to disable the request to |suggested_url|.
+  // If |testing_state| is IGNORE_REQUESTS_FOR_TESTING, then the
+  // callback won't get called.
+  static void set_state_for_testing(TestingState testing_state) {
+    testing_state_ = testing_state;
+  }
+
+  // Cancels the request to |suggested_url|
+  void Cancel();
+
+ private:
+  void OnSimpleLoaderHandler(const GURL& final_url,
+                             const network::mojom::URLResponseHead* head);
+  void OnSimpleLoaderRedirect(
+      const net::RedirectInfo& redirect_info,
+      const network::mojom::URLResponseHead& response_head,
+      std::vector<std::string>* to_be_removed_headers);
+  void OnSimpleLoaderResponseStarted(
+      const GURL& final_url,
+      const network::mojom::URLResponseHead& response_head);
+  void OnSimpleLoaderComplete(std::unique_ptr<std::string> response_body);
+
+  // Returns true if the check is currently running.
+  bool IsCheckingSuggestedUrl() const;
+
+  static TestingState testing_state_;
+  const GURL request_url_;
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
+  GURL check_url_;
+  CheckUrlCallback check_url_callback_;
+  std::unique_ptr<network::SimpleURLLoader> simple_url_loader_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  DISALLOW_COPY_AND_ASSIGN(CommonNameMismatchHandler);
+};
+
+#endif  // COMPONENTS_SECURITY_INTERSTITIALS_CONTENT_COMMON_NAME_MISMATCH_HANDLER_H_
diff --git a/components/security_interstitials/content/security_interstitial_controller_client.cc b/components/security_interstitials/content/security_interstitial_controller_client.cc
index 713a0a9..18ac17e 100644
--- a/components/security_interstitials/content/security_interstitial_controller_client.cc
+++ b/components/security_interstitials/content/security_interstitial_controller_client.cc
@@ -7,7 +7,7 @@
 #include <utility>
 
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/security_interstitials/core/metrics_helper.h"
 #include "content/public/browser/interstitial_page.h"
 #include "content/public/browser/web_contents.h"
diff --git a/components/security_interstitials/content/security_interstitial_page.cc b/components/security_interstitials/content/security_interstitial_page.cc
index 4abbcf4..c404959 100644
--- a/components/security_interstitials/content/security_interstitial_page.cc
+++ b/components/security_interstitials/content/security_interstitial_page.cc
@@ -11,7 +11,7 @@
 #include "base/values.h"
 #include "components/grit/components_resources.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/security_interstitials/content/security_interstitial_controller_client.h"
 #include "components/security_interstitials/core/common_string_util.h"
 #include "content/public/browser/interstitial_page.h"
diff --git a/components/security_interstitials/content/ssl_blocking_page.cc b/components/security_interstitials/content/ssl_blocking_page.cc
index 0486471..6b128e5 100644
--- a/components/security_interstitials/content/ssl_blocking_page.cc
+++ b/components/security_interstitials/content/ssl_blocking_page.cc
@@ -12,7 +12,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/time/time.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/security_interstitials/content/cert_report_helper.h"
 #include "components/security_interstitials/content/security_interstitial_controller_client.h"
 #include "components/security_interstitials/content/ssl_cert_reporter.h"
diff --git a/chrome/browser/ssl/ssl_error_assistant.proto b/components/security_interstitials/content/ssl_error_assistant.proto
similarity index 100%
rename from chrome/browser/ssl/ssl_error_assistant.proto
rename to components/security_interstitials/content/ssl_error_assistant.proto
diff --git a/components/security_interstitials/content/unsafe_resource.h b/components/security_interstitials/content/unsafe_resource.h
index 20010c4..16bb1b1 100644
--- a/components/security_interstitials/content/unsafe_resource.h
+++ b/components/security_interstitials/content/unsafe_resource.h
@@ -10,8 +10,8 @@
 #include "base/callback.h"
 #include "base/memory/ref_counted.h"
 #include "base/single_thread_task_runner.h"
-#include "components/safe_browsing/db/hit_report.h"
-#include "components/safe_browsing/db/util.h"
+#include "components/safe_browsing/core/db/hit_report.h"
+#include "components/safe_browsing/core/db/util.h"
 #include "url/gurl.h"
 
 namespace content {
diff --git a/components/security_state/DEPS b/components/security_state/DEPS
index 1ea5e3e..bc33fd8 100644
--- a/components/security_state/DEPS
+++ b/components/security_state/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
   "+components/prefs",
   "+net",
+  "+services/network/public/cpp",
   "+third_party/blink/public/platform",
-]
\ No newline at end of file
+]
diff --git a/components/security_state/core/BUILD.gn b/components/security_state/core/BUILD.gn
index b3414bd5..3303b4f 100644
--- a/components/security_state/core/BUILD.gn
+++ b/components/security_state/core/BUILD.gn
@@ -25,6 +25,10 @@
     "//net",
     "//url",
   ]
+
+  deps = [
+    "//services/network/public/cpp",
+  ]
 }
 
 if (is_android) {
diff --git a/components/security_state/core/security_state.cc b/components/security_state/core/security_state.cc
index 2cb5a5e..b62d386 100644
--- a/components/security_state/core/security_state.cc
+++ b/components/security_state/core/security_state.cc
@@ -16,23 +16,12 @@
 #include "components/security_state/core/security_state_pref_names.h"
 #include "net/ssl/ssl_cipher_suite_names.h"
 #include "net/ssl/ssl_connection_status_flags.h"
+#include "services/network/public/cpp/is_potentially_trustworthy.h"
 
 namespace security_state {
 
 namespace {
 
-// Returns true if |url| is a blob: URL and its path parses as a GURL with a
-// nonsecure origin, and false otherwise. See
-// https://url.spec.whatwg.org/#origin.
-bool IsNonsecureBlobUrl(
-    const GURL& url,
-    const IsOriginSecureCallback& is_origin_secure_callback) {
-  if (!url.SchemeIs(url::kBlobScheme))
-    return false;
-  GURL inner_url(url.path());
-  return !is_origin_secure_callback.Run(inner_url);
-}
-
 // For nonsecure pages, returns a SecurityLevel based on the
 // provided information and the kMarkHttpAsFeature field trial.
 SecurityLevel GetSecurityLevelForNonSecureFieldTrial(
@@ -137,8 +126,7 @@
 
 SecurityLevel GetSecurityLevel(
     const VisibleSecurityState& visible_security_state,
-    bool used_policy_installed_certificate,
-    IsOriginSecureCallback is_origin_secure_callback) {
+    bool used_policy_installed_certificate) {
   // Override the connection security information if the website failed the
   // browser's malware checks.
   if (visible_security_state.malicious_content_status !=
@@ -190,9 +178,8 @@
       visible_security_state.certificate;
   if (!is_cryptographic_with_certificate) {
     if (!visible_security_state.is_error_page &&
-        !is_origin_secure_callback.Run(url) &&
-        (url.IsStandard() ||
-         IsNonsecureBlobUrl(url, is_origin_secure_callback))) {
+        !network::IsUrlPotentiallyTrustworthy(url) &&
+        (url.IsStandard() || url.SchemeIs(url::kBlobScheme))) {
       return GetSecurityLevelForNonSecureFieldTrial(
           visible_security_state.is_error_page,
           visible_security_state.insecure_input_events);
diff --git a/components/security_state/core/security_state.h b/components/security_state/core/security_state.h
index 290358b..ff6d67c 100644
--- a/components/security_state/core/security_state.h
+++ b/components/security_state/core/security_state.h
@@ -9,7 +9,6 @@
 #include <memory>
 #include <string>
 
-#include "base/callback.h"
 #include "base/feature_list.h"
 #include "base/macros.h"
 #include "components/security_state/core/insecure_input_event_data.h"
@@ -219,19 +218,13 @@
 constexpr SecurityLevel kDisplayedInsecureContentWarningLevel = WARNING;
 constexpr SecurityLevel kRanInsecureContentLevel = DANGEROUS;
 
-// Returns true if the given |url|'s origin should be considered secure.
-using IsOriginSecureCallback = base::RepeatingCallback<bool(const GURL& url)>;
-
 // Returns a SecurityLevel to describe the current page.
 // |visible_security_state| contains the relevant security state.
 // |used_policy_installed_certificate| indicates whether the page or request
 // is known to be loaded with a certificate installed by the system admin.
-// |is_origin_secure_callback| determines whether a URL's origin should be
-// considered secure.
 SecurityLevel GetSecurityLevel(
     const VisibleSecurityState& visible_security_state,
-    bool used_policy_installed_certificate,
-    IsOriginSecureCallback is_origin_secure_callback);
+    bool used_policy_installed_certificate);
 
 // Returns true if the current page was loaded using a cryptographic protocol
 // and its certificate has any major errors.
diff --git a/components/security_state/core/security_state_unittest.cc b/components/security_state/core/security_state_unittest.cc
index 8671a7d..47e167c 100644
--- a/components/security_state/core/security_state_unittest.cc
+++ b/components/security_state/core/security_state_unittest.cc
@@ -41,10 +41,6 @@
     "blob:http://test/some-guid", "filesystem:http://test/some-guid",
 };
 
-bool IsOriginSecure(const GURL& url) {
-  return url == kHttpsUrl;
-}
-
 class TestSecurityStateHelper {
  public:
   TestSecurityStateHelper()
@@ -131,9 +127,8 @@
   }
 
   security_state::SecurityLevel GetSecurityLevel() const {
-    return security_state::GetSecurityLevel(
-        *GetVisibleSecurityState(), has_policy_certificate_,
-        base::BindRepeating(&IsOriginSecure));
+    return security_state::GetSecurityLevel(*GetVisibleSecurityState(),
+                                            has_policy_certificate_);
   }
 
   bool HasMajorCertificateError() const {
diff --git a/components/security_state/ios/security_state_utils.mm b/components/security_state/ios/security_state_utils.mm
index 7468902..6550da7 100644
--- a/components/security_state/ios/security_state_utils.mm
+++ b/components/security_state/ios/security_state_utils.mm
@@ -52,8 +52,7 @@
   }
   return security_state::GetSecurityLevel(
       *GetVisibleSecurityStateForWebState(web_state),
-      false /* used policy installed certificate */,
-      base::BindRepeating(&web::IsOriginSecure));
+      false /* used policy installed certificate */);
 }
 
 }  // namespace security_state
diff --git a/components/subresource_filter/content/browser/BUILD.gn b/components/subresource_filter/content/browser/BUILD.gn
index d40f10a..e479470 100644
--- a/components/subresource_filter/content/browser/BUILD.gn
+++ b/components/subresource_filter/content/browser/BUILD.gn
@@ -41,8 +41,8 @@
   deps = [
     "//base",
     "//components/prefs:prefs",
-    "//components/safe_browsing/db:database_manager",
-    "//components/safe_browsing/db:util",
+    "//components/safe_browsing/core/db:database_manager",
+    "//components/safe_browsing/core/db:util",
     "//components/subresource_filter/content/common",
     "//components/subresource_filter/core/browser",
     "//components/subresource_filter/core/common",
@@ -83,8 +83,8 @@
     "//url",
   ]
   public_deps = [
-    "//components/safe_browsing/db:test_database_manager",
-    "//components/safe_browsing/db:util",
+    "//components/safe_browsing/core/db:test_database_manager",
+    "//components/safe_browsing/core/db:util",
   ]
 }
 
@@ -107,7 +107,7 @@
     ":test_support",
     "//base/test:test_support",
     "//components/prefs:test_support",
-    "//components/safe_browsing/db:util",
+    "//components/safe_browsing/core/db:util",
     "//components/subresource_filter/content/common",
     "//components/subresource_filter/core/browser",
     "//components/subresource_filter/core/browser:test_support",
diff --git a/components/subresource_filter/content/browser/DEPS b/components/subresource_filter/content/browser/DEPS
index 601aff8a..e588ec2 100644
--- a/components/subresource_filter/content/browser/DEPS
+++ b/components/subresource_filter/content/browser/DEPS
@@ -1,5 +1,5 @@
 include_rules = [
-  "+components/safe_browsing/db",
+  "+components/safe_browsing/core/db",
   "+components/ukm",
   "+content/public/browser",
   "+content/public/test",
diff --git a/components/subresource_filter/content/browser/content_activation_list_utils.h b/components/subresource_filter/content/browser/content_activation_list_utils.h
index a5af353a6..41a7e45 100644
--- a/components/subresource_filter/content/browser/content_activation_list_utils.h
+++ b/components/subresource_filter/content/browser/content_activation_list_utils.h
@@ -5,7 +5,7 @@
 #ifndef COMPONENTS_SUBRESOURCE_FILTER_CONTENT_BROWSER_CONTENT_ACTIVATION_LIST_UTILS_H_
 #define COMPONENTS_SUBRESOURCE_FILTER_CONTENT_BROWSER_CONTENT_ACTIVATION_LIST_UTILS_H_
 
-#include "components/safe_browsing/db/util.h"
+#include "components/safe_browsing/core/db/util.h"
 #include "components/subresource_filter/core/common/activation_list.h"
 
 namespace subresource_filter {
diff --git a/components/subresource_filter/content/browser/fake_safe_browsing_database_manager.h b/components/subresource_filter/content/browser/fake_safe_browsing_database_manager.h
index 4893fed3..653f7d7 100644
--- a/components/subresource_filter/content/browser/fake_safe_browsing_database_manager.h
+++ b/components/subresource_filter/content/browser/fake_safe_browsing_database_manager.h
@@ -10,7 +10,7 @@
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "components/safe_browsing/db/test_database_manager.h"
+#include "components/safe_browsing/core/db/test_database_manager.h"
 #include "content/public/common/resource_type.h"
 
 class GURL;
diff --git a/components/subresource_filter/content/browser/subresource_filter_observer.h b/components/subresource_filter/content/browser/subresource_filter_observer.h
index 412a39f7..4150d306 100644
--- a/components/subresource_filter/content/browser/subresource_filter_observer.h
+++ b/components/subresource_filter/content/browser/subresource_filter_observer.h
@@ -5,7 +5,7 @@
 #ifndef COMPONENTS_SUBRESOURCE_FILTER_CONTENT_BROWSER_SUBRESOURCE_FILTER_OBSERVER_H_
 #define COMPONENTS_SUBRESOURCE_FILTER_CONTENT_BROWSER_SUBRESOURCE_FILTER_OBSERVER_H_
 
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
 #include "components/subresource_filter/content/browser/subresource_filter_safe_browsing_client.h"
 #include "components/subresource_filter/core/common/activation_decision.h"
 #include "components/subresource_filter/core/common/load_policy.h"
diff --git a/components/subresource_filter/content/browser/subresource_filter_observer_test_utils.h b/components/subresource_filter/content/browser/subresource_filter_observer_test_utils.h
index a25a47a..6aaa170a 100644
--- a/components/subresource_filter/content/browser/subresource_filter_observer_test_utils.h
+++ b/components/subresource_filter/content/browser/subresource_filter_observer_test_utils.h
@@ -11,8 +11,8 @@
 #include "base/macros.h"
 #include "base/optional.h"
 #include "base/scoped_observer.h"
-#include "components/safe_browsing/db/util.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/core/db/util.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
 #include "components/subresource_filter/content/browser/subresource_filter_observer.h"
 #include "components/subresource_filter/content/browser/subresource_filter_observer_manager.h"
 #include "components/subresource_filter/core/common/load_policy.h"
diff --git a/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.h b/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.h
index 472b8d4..d6b4a476a 100644
--- a/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.h
+++ b/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.h
@@ -17,7 +17,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/single_thread_task_runner.h"
 #include "base/time/time.h"
-#include "components/safe_browsing/db/database_manager.h"
+#include "components/safe_browsing/core/db/database_manager.h"
 #include "components/subresource_filter/content/browser/subresource_filter_safe_browsing_client.h"
 #include "components/subresource_filter/core/browser/subresource_filter_features.h"
 #include "components/subresource_filter/core/common/activation_decision.h"
diff --git a/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle_unittest.cc b/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle_unittest.cc
index 39ad673f..4ac8554 100644
--- a/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle_unittest.cc
+++ b/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle_unittest.cc
@@ -19,7 +19,7 @@
 #include "base/test/test_mock_time_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
-#include "components/safe_browsing/db/test_database_manager.h"
+#include "components/safe_browsing/core/db/test_database_manager.h"
 #include "components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h"
 #include "components/subresource_filter/content/browser/fake_safe_browsing_database_manager.h"
 #include "components/subresource_filter/content/browser/subresource_filter_client.h"
diff --git a/components/subresource_filter/content/browser/subresource_filter_safe_browsing_client.h b/components/subresource_filter/content/browser/subresource_filter_safe_browsing_client.h
index b64280d80..8cb10bb 100644
--- a/components/subresource_filter/content/browser/subresource_filter_safe_browsing_client.h
+++ b/components/subresource_filter/content/browser/subresource_filter_safe_browsing_client.h
@@ -14,7 +14,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
-#include "components/safe_browsing/db/util.h"
+#include "components/safe_browsing/core/db/util.h"
 
 class GURL;
 
diff --git a/components/subresource_filter/content/browser/subresource_filter_safe_browsing_client_request.h b/components/subresource_filter/content/browser/subresource_filter_safe_browsing_client_request.h
index 219cd5f..3eedbbd 100644
--- a/components/subresource_filter/content/browser/subresource_filter_safe_browsing_client_request.h
+++ b/components/subresource_filter/content/browser/subresource_filter_safe_browsing_client_request.h
@@ -11,8 +11,8 @@
 #include "base/memory/ref_counted.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
-#include "components/safe_browsing/db/database_manager.h"
-#include "components/safe_browsing/db/util.h"
+#include "components/safe_browsing/core/db/database_manager.h"
+#include "components/safe_browsing/core/db/util.h"
 
 class GURL;
 
diff --git a/components/subresource_filter/core/common/DEPS b/components/subresource_filter/core/common/DEPS
index 2b9cbe0b..68119b0 100644
--- a/components/subresource_filter/core/common/DEPS
+++ b/components/subresource_filter/core/common/DEPS
@@ -1,3 +1,3 @@
 include_rules = [
-  "+components/safe_browsing/db/util.h",
+  "+components/safe_browsing/core/db/util.h",
 ]
diff --git a/components/sync/BUILD.gn b/components/sync/BUILD.gn
index 93f5b6ea..340fedf 100644
--- a/components/sync/BUILD.gn
+++ b/components/sync/BUILD.gn
@@ -288,6 +288,8 @@
     "model_impl/blocking_model_type_store_impl.h",
     "model_impl/client_tag_based_model_type_processor.cc",
     "model_impl/client_tag_based_model_type_processor.h",
+    "model_impl/client_tag_based_remote_update_handler.cc",
+    "model_impl/client_tag_based_remote_update_handler.h",
     "model_impl/forwarding_model_type_controller_delegate.cc",
     "model_impl/forwarding_model_type_controller_delegate.h",
     "model_impl/in_memory_metadata_change_list.cc",
diff --git a/components/sync/driver/profile_sync_service.cc b/components/sync/driver/profile_sync_service.cc
index fa172d1..1b6ba53 100644
--- a/components/sync/driver/profile_sync_service.cc
+++ b/components/sync/driver/profile_sync_service.cc
@@ -339,7 +339,10 @@
     // Either a new account was signed in, or the existing account's
     // |is_primary| bit was changed. Start up or reconfigure.
     if (!engine_) {
-      startup_controller_->TryStart(/*force_immediate=*/IsSetupInProgress());
+      // Note: We only get here after an actual sign-in (not during browser
+      // startup with an existing signed-in account), so no need for deferred
+      // startup.
+      startup_controller_->TryStart(/*force_immediate=*/true);
     } else {
       ReconfigureDatatypeManager(/*bypass_setup_in_progress_check=*/false);
     }
diff --git a/components/sync/driver/profile_sync_service_startup_unittest.cc b/components/sync/driver/profile_sync_service_startup_unittest.cc
index 991dfa1..3aefc9d 100644
--- a/components/sync/driver/profile_sync_service_startup_unittest.cc
+++ b/components/sync/driver/profile_sync_service_startup_unittest.cc
@@ -619,24 +619,22 @@
   EXPECT_EQ(SyncService::TransportState::DISABLED,
             sync_service()->GetTransportState());
 
-  // Sign in. Now Sync-the-transport could start, but gets deferred by default.
+  // Sign in. Now Sync-the-transport can start. Since this was triggered by an
+  // explicit user event, deferred startup is bypassed.
   // Sync-the-feature still doesn't start until the user says they want it.
+  EXPECT_CALL(*sync_engine, Initialize(_));
   SimulateTestUserSignin();
   EXPECT_EQ(
       SyncService::DisableReasonSet(SyncService::DISABLE_REASON_USER_CHOICE),
       sync_service()->GetDisableReasons());
-  EXPECT_EQ(SyncService::TransportState::START_DEFERRED,
+  EXPECT_EQ(SyncService::TransportState::INITIALIZING,
             sync_service()->GetTransportState());
   EXPECT_FALSE(sync_service()->IsSyncFeatureEnabled());
 
-  // Once we give the service a prod by initiating Sync setup, it'll start and
-  // initialize the engine. Since this is the initial Sync start, this will not
-  // be deferred.
-  EXPECT_CALL(*sync_engine, Initialize(_));
+  // Initiate Sync (the feature) setup before the engine initializes itself in
+  // transport mode.
   sync_service()->GetUserSettings()->SetSyncRequested(true);
   auto setup_in_progress_handle = sync_service()->GetSetupInProgressHandle();
-  EXPECT_EQ(SyncService::TransportState::INITIALIZING,
-            sync_service()->GetTransportState());
 
   // Once the engine calls back and says it's initialized, we're just waiting
   // for the user to finish the initial configuration (choosing data types etc.)
diff --git a/components/sync/model_impl/client_tag_based_model_type_processor.cc b/components/sync/model_impl/client_tag_based_model_type_processor.cc
index ad699ff..49f8284c 100644
--- a/components/sync/model_impl/client_tag_based_model_type_processor.cc
+++ b/components/sync/model_impl/client_tag_based_model_type_processor.cc
@@ -20,6 +20,7 @@
 #include "components/sync/engine/commit_queue.h"
 #include "components/sync/engine/data_type_activation_response.h"
 #include "components/sync/engine/model_type_processor_proxy.h"
+#include "components/sync/model_impl/client_tag_based_remote_update_handler.h"
 #include "components/sync/model_impl/processor_entity.h"
 #include "components/sync/protocol/proto_memory_estimations.h"
 #include "components/sync/protocol/proto_value_conversions.h"
@@ -39,25 +40,6 @@
   return count;
 }
 
-void LogNonReflectionUpdateFreshnessToUma(ModelType type,
-                                          base::Time remote_modification_time) {
-  const base::TimeDelta latency = base::Time::Now() - remote_modification_time;
-
-  UMA_HISTOGRAM_CUSTOM_TIMES("Sync.NonReflectionUpdateFreshnessPossiblySkewed2",
-                             latency,
-                             /*min=*/base::TimeDelta::FromMilliseconds(100),
-                             /*max=*/base::TimeDelta::FromDays(7),
-                             /*bucket_count=*/50);
-
-  base::UmaHistogramCustomTimes(
-      std::string("Sync.NonReflectionUpdateFreshnessPossiblySkewed2.") +
-          ModelTypeToHistogramSuffix(type),
-      latency,
-      /*min=*/base::TimeDelta::FromMilliseconds(100),
-      /*max=*/base::TimeDelta::FromDays(7),
-      /*bucket_count=*/50);
-}
-
 }  // namespace
 
 ClientTagBasedModelTypeProcessor::ClientTagBasedModelTypeProcessor(
@@ -719,208 +701,6 @@
   NudgeForCommitIfNeeded();
 }
 
-ProcessorEntity* ClientTagBasedModelTypeProcessor::ProcessUpdate(
-    std::unique_ptr<UpdateResponseData> update,
-    EntityChangeList* entity_changes,
-    std::string* storage_key_to_clear) {
-  const EntityData& data = *update->entity;
-  const ClientTagHash& client_tag_hash = data.client_tag_hash;
-
-  // Filter out updates without a client tag hash (including permanent nodes,
-  // which have server tags instead).
-  if (client_tag_hash.value().empty()) {
-    return nullptr;
-  }
-
-  // Filter out unexpected client tag hashes.
-  if (!data.is_deleted() && bridge_->SupportsGetClientTag() &&
-      client_tag_hash !=
-          ClientTagHash::FromUnhashed(type_, bridge_->GetClientTag(data))) {
-    DLOG(WARNING) << "Received unexpected client tag hash: " << client_tag_hash
-                  << " for " << ModelTypeToString(type_);
-    return nullptr;
-  }
-
-  ProcessorEntity* entity = GetEntityForTagHash(client_tag_hash);
-
-  // Handle corner cases first.
-  if (entity == nullptr && data.is_deleted()) {
-    // Local entity doesn't exist and update is tombstone.
-    DLOG(WARNING) << "Received remote delete for a non-existing item."
-                  << " client_tag_hash: " << client_tag_hash << " for "
-                  << ModelTypeToString(type_);
-    return nullptr;
-  }
-
-  if (entity) {
-    entity->RecordEntityUpdateLatency(update->response_version, type_);
-  }
-
-  if (entity && entity->UpdateIsReflection(update->response_version)) {
-    // Seen this update before; just ignore it.
-    return nullptr;
-  }
-
-  // Cache update encryption key name in case |update| will be moved away into
-  // ResolveConflict().
-  const std::string update_encryption_key_name = update->encryption_key_name;
-  ConflictResolution resolution_type = ConflictResolution::kTypeSize;
-  if (entity && entity->IsUnsynced()) {
-    // Handle conflict resolution.
-    resolution_type = ResolveConflict(std::move(update), entity, entity_changes,
-                                      storage_key_to_clear);
-    UMA_HISTOGRAM_ENUMERATION("Sync.ResolveConflict", resolution_type,
-                              ConflictResolution::kTypeSize);
-  } else {
-    // Handle simple create/delete/update.
-    base::Optional<EntityChange::ChangeType> change_type;
-
-    if (entity == nullptr) {
-      entity = CreateEntity(data);
-      change_type = EntityChange::ACTION_ADD;
-    } else if (data.is_deleted()) {
-      DCHECK(!entity->metadata().is_deleted());
-      change_type = EntityChange::ACTION_DELETE;
-    } else if (!entity->MatchesData(data)) {
-      change_type = EntityChange::ACTION_UPDATE;
-    }
-    entity->RecordAcceptedUpdate(*update);
-    // Inform the bridge about the changes if needed.
-    if (change_type) {
-      switch (change_type.value()) {
-        case EntityChange::ACTION_ADD:
-          entity_changes->push_back(EntityChange::CreateAdd(
-              entity->storage_key(), std::move(update->entity)));
-          break;
-        case EntityChange::ACTION_DELETE:
-          // The entity was deleted; inform the bridge. Note that the local data
-          // can never be deleted at this point because it would have either
-          // been acked (the add case) or pending (the conflict case).
-          entity_changes->push_back(
-              EntityChange::CreateDelete(entity->storage_key()));
-          break;
-        case EntityChange::ACTION_UPDATE:
-          // Specifics have changed, so update the bridge.
-          entity_changes->push_back(EntityChange::CreateUpdate(
-              entity->storage_key(), std::move(update->entity)));
-          break;
-      }
-    }
-  }
-
-  // If the received entity has out of date encryption, we schedule another
-  // commit to fix it.
-  if (model_type_state_.encryption_key_name() != update_encryption_key_name) {
-    DVLOG(2) << ModelTypeToString(type_) << ": Requesting re-encrypt commit "
-             << update_encryption_key_name << " -> "
-             << model_type_state_.encryption_key_name();
-
-    entity->IncrementSequenceNumber(base::Time::Now());
-  }
-  return entity;
-}
-
-ConflictResolution ClientTagBasedModelTypeProcessor::ResolveConflict(
-    std::unique_ptr<UpdateResponseData> update,
-    ProcessorEntity* entity,
-    EntityChangeList* changes,
-    std::string* storage_key_to_clear) {
-  const EntityData& remote_data = *update->entity;
-
-  ConflictResolution resolution_type = ConflictResolution::kTypeSize;
-
-  // Determine the type of resolution.
-  if (entity->MatchesData(remote_data)) {
-    // The changes are identical so there isn't a real conflict.
-    resolution_type = ConflictResolution::kChangesMatch;
-  } else if (entity->metadata().is_deleted()) {
-    // Local tombstone vs remote update (non-deletion). Should be undeleted.
-    resolution_type = ConflictResolution::kUseRemote;
-  } else if (entity->MatchesOwnBaseData()) {
-    // If there is no real local change, then the entity must be unsynced due to
-    // a pending local re-encryption request. In this case, the remote data
-    // should win.
-    resolution_type = ConflictResolution::kIgnoreLocalEncryption;
-  } else if (entity->MatchesBaseData(remote_data)) {
-    // The remote data isn't actually changing from the last remote data that
-    // was seen, so it must have been a re-encryption and can be ignored.
-    resolution_type = ConflictResolution::kIgnoreRemoteEncryption;
-  } else {
-    // There's a real data conflict here; let the bridge resolve it.
-    resolution_type =
-        bridge_->ResolveConflict(entity->storage_key(), remote_data);
-  }
-
-  // Apply the resolution.
-  switch (resolution_type) {
-    case ConflictResolution::kChangesMatch:
-      // Record the update and squash the pending commit.
-      entity->RecordForcedUpdate(*update);
-      break;
-    case ConflictResolution::kUseLocal:
-    case ConflictResolution::kIgnoreRemoteEncryption:
-      // Record that we received the update from the server but leave the
-      // pending commit intact.
-      entity->RecordIgnoredUpdate(*update);
-      break;
-    case ConflictResolution::kUseRemote:
-    case ConflictResolution::kIgnoreLocalEncryption:
-      // Update client data to match server.
-      if (update->entity->is_deleted()) {
-        DCHECK(!entity->metadata().is_deleted());
-        // Squash the pending commit.
-        entity->RecordForcedUpdate(*update);
-        changes->push_back(EntityChange::CreateDelete(entity->storage_key()));
-      } else if (!entity->metadata().is_deleted()) {
-        // Squash the pending commit.
-        entity->RecordForcedUpdate(*update);
-        changes->push_back(EntityChange::CreateUpdate(
-            entity->storage_key(), std::move(update->entity)));
-      } else {
-        // Remote undeletion. This could imply a new storage key for some
-        // bridges, so we may need to wait until UpdateStorageKey() is called.
-        if (!bridge_->SupportsGetStorageKey()) {
-          *storage_key_to_clear = entity->storage_key();
-          entity->ClearStorageKey();
-        }
-        // Squash the pending commit.
-        entity->RecordForcedUpdate(*update);
-        changes->push_back(EntityChange::CreateAdd(entity->storage_key(),
-                                                   std::move(update->entity)));
-      }
-      break;
-    case ConflictResolution::kUseNewDEPRECATED:
-    case ConflictResolution::kTypeSize:
-      NOTREACHED();
-      break;
-  }
-
-  return resolution_type;
-}
-
-void ClientTagBasedModelTypeProcessor::RecommitAllForEncryption(
-    const std::unordered_set<std::string>& already_updated,
-    MetadataChangeList* metadata_changes) {
-  ModelTypeSyncBridge::StorageKeyList entities_needing_data;
-
-  for (const auto& kv : entities_) {
-    ProcessorEntity* entity = kv.second.get();
-    if (entity->storage_key().empty() ||
-        (already_updated.find(entity->storage_key()) !=
-         already_updated.end())) {
-      // Entities with empty storage key were already processed. ProcessUpdate()
-      // incremented their sequence numbers and cached commit data. Their
-      // metadata will be persisted in UpdateStorageKey().
-      continue;
-    }
-    entity->IncrementSequenceNumber(base::Time::Now());
-    if (entity->RequiresCommitData()) {
-      entities_needing_data.push_back(entity->storage_key());
-    }
-    metadata_changes->UpdateMetadata(entity->storage_key(), entity->metadata());
-  }
-}
-
 bool ClientTagBasedModelTypeProcessor::ValidateUpdate(
     const sync_pb::ModelTypeState& model_type_state,
     const UpdateResponseDataList& updates) {
@@ -1037,81 +817,11 @@
   DCHECK(model_ready_to_sync_);
   DCHECK(model_type_state.initial_sync_done());
 
-  std::unique_ptr<MetadataChangeList> metadata_changes =
-      bridge_->CreateMetadataChangeList();
-  EntityChangeList entity_changes;
-
-  metadata_changes->UpdateModelTypeState(model_type_state);
-  bool got_new_encryption_requirements =
-      model_type_state_.encryption_key_name() !=
-      model_type_state.encryption_key_name();
-  model_type_state_ = model_type_state;
-
-  // If new encryption requirements come from the server, the entities that are
-  // in |updates| will be recorded here so they can be ignored during the
-  // re-encryption phase at the end.
-  std::unordered_set<std::string> already_updated;
-
-  for (std::unique_ptr<syncer::UpdateResponseData>& update : updates) {
-    DCHECK(update);
-    std::string storage_key_to_clear;
-    ProcessorEntity* entity = ProcessUpdate(std::move(update), &entity_changes,
-                                            &storage_key_to_clear);
-
-    if (!entity) {
-      // The update is either of the following:
-      // 1. Tombstone of entity that didn't exist locally.
-      // 2. Reflection, thus should be ignored.
-      // 3. Update without a client tag hash (including permanent nodes, which
-      // have server tags instead).
-      continue;
-    }
-
-    LogNonReflectionUpdateFreshnessToUma(
-        type_,
-        /*remote_modification_time=*/
-        ProtoTimeToTime(entity->metadata().modification_time()));
-
-    if (entity->storage_key().empty()) {
-      // Storage key of this entity is not known yet. Don't update metadata, it
-      // will be done from UpdateStorageKey.
-
-      // If this is the result of a conflict resolution (where a remote
-      // undeletion was preferred), then need to clear a metadata entry from
-      // the database.
-      if (!storage_key_to_clear.empty()) {
-        metadata_changes->ClearMetadata(storage_key_to_clear);
-        storage_key_to_tag_hash_.erase(storage_key_to_clear);
-      }
-      continue;
-    }
-
-    DCHECK(storage_key_to_clear.empty());
-
-    if (entity->CanClearMetadata()) {
-      metadata_changes->ClearMetadata(entity->storage_key());
-      storage_key_to_tag_hash_.erase(entity->storage_key());
-      entities_.erase(
-          ClientTagHash::FromHashed(entity->metadata().client_tag_hash()));
-    } else {
-      metadata_changes->UpdateMetadata(entity->storage_key(),
-                                       entity->metadata());
-    }
-
-    if (got_new_encryption_requirements) {
-      already_updated.insert(entity->storage_key());
-    }
-  }
-
-  if (got_new_encryption_requirements) {
-    // TODO(pavely): Currently we recommit all entities. We should instead
-    // recommit only the ones whose encryption key doesn't match the one in
-    // DataTypeState. Work is tracked in http://crbug.com/727874.
-    RecommitAllForEncryption(already_updated, metadata_changes.get());
-  }
-  // Inform the bridge of the new or updated data.
-  return bridge_->ApplySyncChanges(std::move(metadata_changes),
-                                   std::move(entity_changes));
+  ClientTagBasedRemoteUpdateHandler updates_handler(
+      type_, bridge_, &model_type_state_, &storage_key_to_tag_hash_,
+      &entities_);
+  return updates_handler.ProcessIncrementalUpdate(model_type_state,
+                                                  std::move(updates));
 }
 
 void ClientTagBasedModelTypeProcessor::OnPendingDataLoaded(
diff --git a/components/sync/model_impl/client_tag_based_model_type_processor.h b/components/sync/model_impl/client_tag_based_model_type_processor.h
index 88ebbc3..6918380 100644
--- a/components/sync/model_impl/client_tag_based_model_type_processor.h
+++ b/components/sync/model_impl/client_tag_based_model_type_processor.h
@@ -20,7 +20,6 @@
 #include "components/sync/engine/cycle/status_counters.h"
 #include "components/sync/engine/model_type_processor.h"
 #include "components/sync/engine/non_blocking_sync_common.h"
-#include "components/sync/model/conflict_resolution.h"
 #include "components/sync/model/data_batch.h"
 #include "components/sync/model/data_type_activation_request.h"
 #include "components/sync/model/metadata_batch.h"
@@ -127,27 +126,6 @@
   // If preconditions are met, inform sync that we are ready to connect.
   void ConnectIfReady();
 
-  // Helper function to process the update for a single entity. If a local data
-  // change is required, it will be added to |entity_changes|. The return value
-  // is the tracked entity, or nullptr if the update should be ignored.
-  // |storage_key_to_clear| must not be null and allows the implementation to
-  // indicate that a certain storage key is now obsolete and should be cleared,
-  // which is leveraged in certain conflict resolution scenarios.
-  ProcessorEntity* ProcessUpdate(std::unique_ptr<UpdateResponseData> update,
-                                 EntityChangeList* entity_changes,
-                                 std::string* storage_key_to_clear);
-
-  // Resolve a conflict between |update| and the pending commit in |entity|.
-  ConflictResolution ResolveConflict(std::unique_ptr<UpdateResponseData> update,
-                                     ProcessorEntity* entity,
-                                     EntityChangeList* changes,
-                                     std::string* storage_key_to_clear);
-
-  // Recommit all entities for encryption except those in |already_updated|.
-  void RecommitAllForEncryption(
-      const std::unordered_set<std::string>& already_updated,
-      MetadataChangeList* metadata_changes);
-
   // Validates the update specified by the input parameters and returns whether
   // it should get further processed. If the update is incorrect, this function
   // also reports an error.
diff --git a/components/sync/model_impl/client_tag_based_model_type_processor_unittest.cc b/components/sync/model_impl/client_tag_based_model_type_processor_unittest.cc
index ca61878b..5c276c7 100644
--- a/components/sync/model_impl/client_tag_based_model_type_processor_unittest.cc
+++ b/components/sync/model_impl/client_tag_based_model_type_processor_unittest.cc
@@ -21,6 +21,7 @@
 #include "components/sync/base/sync_mode.h"
 #include "components/sync/base/time.h"
 #include "components/sync/engine/data_type_activation_response.h"
+#include "components/sync/model/conflict_resolution.h"
 #include "components/sync/model/data_type_activation_request.h"
 #include "components/sync/model/fake_model_type_sync_bridge.h"
 #include "components/sync/test/engine/mock_model_type_worker.h"
diff --git a/components/sync/model_impl/client_tag_based_remote_update_handler.cc b/components/sync/model_impl/client_tag_based_remote_update_handler.cc
new file mode 100644
index 0000000..19c9830
--- /dev/null
+++ b/components/sync/model_impl/client_tag_based_remote_update_handler.cc
@@ -0,0 +1,377 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/sync/model_impl/client_tag_based_remote_update_handler.h"
+
+#include <utility>
+
+#include "base/metrics/histogram_functions.h"
+#include "base/metrics/histogram_macros.h"
+#include "components/sync/base/time.h"
+#include "components/sync/model/metadata_change_list.h"
+#include "components/sync/model/model_type_sync_bridge.h"
+#include "components/sync/model_impl/processor_entity.h"
+
+namespace syncer {
+
+namespace {
+
+void LogNonReflectionUpdateFreshnessToUma(ModelType type,
+                                          base::Time remote_modification_time) {
+  const base::TimeDelta latency = base::Time::Now() - remote_modification_time;
+
+  UMA_HISTOGRAM_CUSTOM_TIMES("Sync.NonReflectionUpdateFreshnessPossiblySkewed2",
+                             latency,
+                             /*min=*/base::TimeDelta::FromMilliseconds(100),
+                             /*max=*/base::TimeDelta::FromDays(7),
+                             /*bucket_count=*/50);
+
+  base::UmaHistogramCustomTimes(
+      std::string("Sync.NonReflectionUpdateFreshnessPossiblySkewed2.") +
+          ModelTypeToHistogramSuffix(type),
+      latency,
+      /*min=*/base::TimeDelta::FromMilliseconds(100),
+      /*max=*/base::TimeDelta::FromDays(7),
+      /*bucket_count=*/50);
+}
+
+}  // namespace
+
+ClientTagBasedRemoteUpdateHandler::ClientTagBasedRemoteUpdateHandler(
+    ModelType type,
+    ModelTypeSyncBridge* bridge,
+    sync_pb::ModelTypeState* model_type_state,
+    std::map<std::string, ClientTagHash>* storage_key_to_tag_hash,
+    std::map<ClientTagHash, std::unique_ptr<ProcessorEntity>>* entities)
+    : type_(type),
+      bridge_(bridge),
+      model_type_state_(model_type_state),
+      storage_key_to_tag_hash_(storage_key_to_tag_hash),
+      entities_(entities) {
+  DCHECK(bridge_);
+  DCHECK(model_type_state_);
+  DCHECK(storage_key_to_tag_hash_);
+  DCHECK(entities_);
+}
+
+base::Optional<ModelError>
+ClientTagBasedRemoteUpdateHandler::ProcessIncrementalUpdate(
+    const sync_pb::ModelTypeState& model_type_state,
+    UpdateResponseDataList updates) {
+  std::unique_ptr<MetadataChangeList> metadata_changes =
+      bridge_->CreateMetadataChangeList();
+  EntityChangeList entity_changes;
+
+  metadata_changes->UpdateModelTypeState(model_type_state);
+  const bool got_new_encryption_requirements =
+      model_type_state_->encryption_key_name() !=
+      model_type_state.encryption_key_name();
+  *model_type_state_ = model_type_state;
+
+  // If new encryption requirements come from the server, the entities that are
+  // in |updates| will be recorded here so they can be ignored during the
+  // re-encryption phase at the end.
+  std::unordered_set<std::string> already_updated;
+
+  for (std::unique_ptr<syncer::UpdateResponseData>& update : updates) {
+    DCHECK(update);
+    std::string storage_key_to_clear;
+    ProcessorEntity* entity = ProcessUpdate(std::move(update), &entity_changes,
+                                            &storage_key_to_clear);
+
+    if (!entity) {
+      // The update is either of the following:
+      // 1. Tombstone of entity that didn't exist locally.
+      // 2. Reflection, thus should be ignored.
+      // 3. Update without a client tag hash (including permanent nodes, which
+      // have server tags instead).
+      continue;
+    }
+
+    LogNonReflectionUpdateFreshnessToUma(
+        type_,
+        /*remote_modification_time=*/
+        ProtoTimeToTime(entity->metadata().modification_time()));
+
+    if (entity->storage_key().empty()) {
+      // Storage key of this entity is not known yet. Don't update metadata, it
+      // will be done from UpdateStorageKey.
+
+      // If this is the result of a conflict resolution (where a remote
+      // undeletion was preferred), then need to clear a metadata entry from
+      // the database.
+      if (!storage_key_to_clear.empty()) {
+        metadata_changes->ClearMetadata(storage_key_to_clear);
+        storage_key_to_tag_hash_->erase(storage_key_to_clear);
+      }
+      continue;
+    }
+
+    DCHECK(storage_key_to_clear.empty());
+
+    if (entity->CanClearMetadata()) {
+      metadata_changes->ClearMetadata(entity->storage_key());
+      storage_key_to_tag_hash_->erase(entity->storage_key());
+      entities_->erase(
+          ClientTagHash::FromHashed(entity->metadata().client_tag_hash()));
+    } else {
+      metadata_changes->UpdateMetadata(entity->storage_key(),
+                                       entity->metadata());
+    }
+
+    if (got_new_encryption_requirements) {
+      already_updated.insert(entity->storage_key());
+    }
+  }
+
+  if (got_new_encryption_requirements) {
+    // TODO(pavely): Currently we recommit all entities. We should instead
+    // recommit only the ones whose encryption key doesn't match the one in
+    // DataTypeState. Work is tracked in http://crbug.com/727874.
+    RecommitAllForEncryption(already_updated, metadata_changes.get());
+  }
+
+  // Inform the bridge of the new or updated data.
+  return bridge_->ApplySyncChanges(std::move(metadata_changes),
+                                   std::move(entity_changes));
+}
+
+ProcessorEntity* ClientTagBasedRemoteUpdateHandler::ProcessUpdate(
+    std::unique_ptr<UpdateResponseData> update,
+    EntityChangeList* entity_changes,
+    std::string* storage_key_to_clear) {
+  const EntityData& data = *update->entity;
+  const ClientTagHash& client_tag_hash = data.client_tag_hash;
+
+  // Filter out updates without a client tag hash (including permanent nodes,
+  // which have server tags instead).
+  if (client_tag_hash.value().empty()) {
+    return nullptr;
+  }
+
+  // Filter out unexpected client tag hashes.
+  if (!data.is_deleted() && bridge_->SupportsGetClientTag() &&
+      client_tag_hash !=
+          ClientTagHash::FromUnhashed(type_, bridge_->GetClientTag(data))) {
+    DLOG(WARNING) << "Received unexpected client tag hash: " << client_tag_hash
+                  << " for " << ModelTypeToString(type_);
+    return nullptr;
+  }
+
+  ProcessorEntity* entity = GetEntityForTagHash(client_tag_hash);
+
+  // Handle corner cases first.
+  if (entity == nullptr && data.is_deleted()) {
+    // Local entity doesn't exist and update is tombstone.
+    DLOG(WARNING) << "Received remote delete for a non-existing item."
+                  << " client_tag_hash: " << client_tag_hash << " for "
+                  << ModelTypeToString(type_);
+    return nullptr;
+  }
+
+  if (entity) {
+    entity->RecordEntityUpdateLatency(update->response_version, type_);
+  }
+
+  if (entity && entity->UpdateIsReflection(update->response_version)) {
+    // Seen this update before; just ignore it.
+    return nullptr;
+  }
+
+  // Cache update encryption key name in case |update| will be moved away into
+  // ResolveConflict().
+  const std::string update_encryption_key_name = update->encryption_key_name;
+  ConflictResolution resolution_type = ConflictResolution::kTypeSize;
+  if (entity && entity->IsUnsynced()) {
+    // Handle conflict resolution.
+    resolution_type = ResolveConflict(std::move(update), entity, entity_changes,
+                                      storage_key_to_clear);
+    UMA_HISTOGRAM_ENUMERATION("Sync.ResolveConflict", resolution_type,
+                              ConflictResolution::kTypeSize);
+  } else {
+    // Handle simple create/delete/update.
+    base::Optional<EntityChange::ChangeType> change_type;
+
+    if (entity == nullptr) {
+      entity = CreateEntity(data);
+      change_type = EntityChange::ACTION_ADD;
+    } else if (data.is_deleted()) {
+      DCHECK(!entity->metadata().is_deleted());
+      change_type = EntityChange::ACTION_DELETE;
+    } else if (!entity->MatchesData(data)) {
+      change_type = EntityChange::ACTION_UPDATE;
+    }
+    entity->RecordAcceptedUpdate(*update);
+    // Inform the bridge about the changes if needed.
+    if (change_type) {
+      switch (change_type.value()) {
+        case EntityChange::ACTION_ADD:
+          entity_changes->push_back(EntityChange::CreateAdd(
+              entity->storage_key(), std::move(update->entity)));
+          break;
+        case EntityChange::ACTION_DELETE:
+          // The entity was deleted; inform the bridge. Note that the local data
+          // can never be deleted at this point because it would have either
+          // been acked (the add case) or pending (the conflict case).
+          entity_changes->push_back(
+              EntityChange::CreateDelete(entity->storage_key()));
+          break;
+        case EntityChange::ACTION_UPDATE:
+          // Specifics have changed, so update the bridge.
+          entity_changes->push_back(EntityChange::CreateUpdate(
+              entity->storage_key(), std::move(update->entity)));
+          break;
+      }
+    }
+  }
+
+  // If the received entity has out of date encryption, we schedule another
+  // commit to fix it.
+  if (model_type_state_->encryption_key_name() != update_encryption_key_name) {
+    DVLOG(2) << ModelTypeToString(type_) << ": Requesting re-encrypt commit "
+             << update_encryption_key_name << " -> "
+             << model_type_state_->encryption_key_name();
+
+    entity->IncrementSequenceNumber(base::Time::Now());
+  }
+  return entity;
+}
+
+void ClientTagBasedRemoteUpdateHandler::RecommitAllForEncryption(
+    const std::unordered_set<std::string>& already_updated,
+    MetadataChangeList* metadata_changes) {
+  ModelTypeSyncBridge::StorageKeyList entities_needing_data;
+
+  for (const auto& kv : *entities_) {
+    ProcessorEntity* entity = kv.second.get();
+    if (entity->storage_key().empty() ||
+        (already_updated.find(entity->storage_key()) !=
+         already_updated.end())) {
+      // Entities with empty storage key were already processed. ProcessUpdate()
+      // incremented their sequence numbers and cached commit data. Their
+      // metadata will be persisted in UpdateStorageKey().
+      continue;
+    }
+    entity->IncrementSequenceNumber(base::Time::Now());
+    if (entity->RequiresCommitData()) {
+      entities_needing_data.push_back(entity->storage_key());
+    }
+    metadata_changes->UpdateMetadata(entity->storage_key(), entity->metadata());
+  }
+}
+
+ConflictResolution ClientTagBasedRemoteUpdateHandler::ResolveConflict(
+    std::unique_ptr<UpdateResponseData> update,
+    ProcessorEntity* entity,
+    EntityChangeList* changes,
+    std::string* storage_key_to_clear) {
+  const EntityData& remote_data = *update->entity;
+
+  ConflictResolution resolution_type = ConflictResolution::kTypeSize;
+
+  // Determine the type of resolution.
+  if (entity->MatchesData(remote_data)) {
+    // The changes are identical so there isn't a real conflict.
+    resolution_type = ConflictResolution::kChangesMatch;
+  } else if (entity->metadata().is_deleted()) {
+    // Local tombstone vs remote update (non-deletion). Should be undeleted.
+    resolution_type = ConflictResolution::kUseRemote;
+  } else if (entity->MatchesOwnBaseData()) {
+    // If there is no real local change, then the entity must be unsynced due to
+    // a pending local re-encryption request. In this case, the remote data
+    // should win.
+    resolution_type = ConflictResolution::kIgnoreLocalEncryption;
+  } else if (entity->MatchesBaseData(remote_data)) {
+    // The remote data isn't actually changing from the last remote data that
+    // was seen, so it must have been a re-encryption and can be ignored.
+    resolution_type = ConflictResolution::kIgnoreRemoteEncryption;
+  } else {
+    // There's a real data conflict here; let the bridge resolve it.
+    resolution_type =
+        bridge_->ResolveConflict(entity->storage_key(), remote_data);
+  }
+
+  // Apply the resolution.
+  switch (resolution_type) {
+    case ConflictResolution::kChangesMatch:
+      // Record the update and squash the pending commit.
+      entity->RecordForcedUpdate(*update);
+      break;
+    case ConflictResolution::kUseLocal:
+    case ConflictResolution::kIgnoreRemoteEncryption:
+      // Record that we received the update from the server but leave the
+      // pending commit intact.
+      entity->RecordIgnoredUpdate(*update);
+      break;
+    case ConflictResolution::kUseRemote:
+    case ConflictResolution::kIgnoreLocalEncryption:
+      // Update client data to match server.
+      if (update->entity->is_deleted()) {
+        DCHECK(!entity->metadata().is_deleted());
+        // Squash the pending commit.
+        entity->RecordForcedUpdate(*update);
+        changes->push_back(EntityChange::CreateDelete(entity->storage_key()));
+      } else if (!entity->metadata().is_deleted()) {
+        // Squash the pending commit.
+        entity->RecordForcedUpdate(*update);
+        changes->push_back(EntityChange::CreateUpdate(
+            entity->storage_key(), std::move(update->entity)));
+      } else {
+        // Remote undeletion. This could imply a new storage key for some
+        // bridges, so we may need to wait until UpdateStorageKey() is called.
+        if (!bridge_->SupportsGetStorageKey()) {
+          *storage_key_to_clear = entity->storage_key();
+          entity->ClearStorageKey();
+        }
+        // Squash the pending commit.
+        entity->RecordForcedUpdate(*update);
+        changes->push_back(EntityChange::CreateAdd(entity->storage_key(),
+                                                   std::move(update->entity)));
+      }
+      break;
+    case ConflictResolution::kUseNewDEPRECATED:
+    case ConflictResolution::kTypeSize:
+      NOTREACHED();
+      break;
+  }
+
+  return resolution_type;
+}
+
+ProcessorEntity* ClientTagBasedRemoteUpdateHandler::GetEntityForTagHash(
+    const ClientTagHash& tag_hash) {
+  const auto it = entities_->find(tag_hash);
+  return it != entities_->end() ? it->second.get() : nullptr;
+}
+
+ProcessorEntity* ClientTagBasedRemoteUpdateHandler::CreateEntity(
+    const std::string& storage_key,
+    const EntityData& data) {
+  DCHECK(!data.client_tag_hash.value().empty());
+  DCHECK(entities_->find(data.client_tag_hash) == entities_->end());
+  DCHECK(!bridge_->SupportsGetStorageKey() || !storage_key.empty());
+  DCHECK(storage_key.empty() || storage_key_to_tag_hash_->find(storage_key) ==
+                                    storage_key_to_tag_hash_->end());
+  std::unique_ptr<ProcessorEntity> entity = ProcessorEntity::CreateNew(
+      storage_key, data.client_tag_hash, data.id, data.creation_time);
+  ProcessorEntity* entity_ptr = entity.get();
+  (*entities_)[data.client_tag_hash] = std::move(entity);
+  if (!storage_key.empty())
+    (*storage_key_to_tag_hash_)[storage_key] = data.client_tag_hash;
+  return entity_ptr;
+}
+
+ProcessorEntity* ClientTagBasedRemoteUpdateHandler::CreateEntity(
+    const EntityData& data) {
+  if (bridge_->SupportsGetClientTag()) {
+    DCHECK_EQ(data.client_tag_hash,
+              ClientTagHash::FromUnhashed(type_, bridge_->GetClientTag(data)));
+  }
+  std::string storage_key;
+  if (bridge_->SupportsGetStorageKey())
+    storage_key = bridge_->GetStorageKey(data);
+  return CreateEntity(storage_key, data);
+}
+
+}  // namespace syncer
diff --git a/components/sync/model_impl/client_tag_based_remote_update_handler.h b/components/sync/model_impl/client_tag_based_remote_update_handler.h
new file mode 100644
index 0000000..58bcf8d
--- /dev/null
+++ b/components/sync/model_impl/client_tag_based_remote_update_handler.h
@@ -0,0 +1,108 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SYNC_MODEL_IMPL_CLIENT_TAG_BASED_REMOTE_UPDATE_HANDLER_H_
+#define COMPONENTS_SYNC_MODEL_IMPL_CLIENT_TAG_BASED_REMOTE_UPDATE_HANDLER_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <unordered_set>
+
+#include "base/macros.h"
+#include "base/optional.h"
+
+#include "components/sync/engine/non_blocking_sync_common.h"
+#include "components/sync/model/conflict_resolution.h"
+#include "components/sync/model/entity_change.h"
+#include "components/sync/model/model_error.h"
+
+namespace sync_pb {
+class ModelTypeState;
+}  // namespace sync_pb
+
+namespace syncer {
+
+class MetadataChangeList;
+class ModelTypeSyncBridge;
+class ProcessorEntity;
+
+// A sync component that performs updates from sync server.
+class ClientTagBasedRemoteUpdateHandler {
+ public:
+  // All parameters must not be nullptr and they must outlive this object.
+  // |model_type_state|, |storage_key_to_tag_hash| and |entities| are
+  // ClientTagBasedModelTypeProcessor internal fields. This will be changed in
+  // future.
+  ClientTagBasedRemoteUpdateHandler(
+      ModelType type,
+      ModelTypeSyncBridge* bridge,
+      sync_pb::ModelTypeState* model_type_state,
+      std::map<std::string, ClientTagHash>* storage_key_to_tag_hash,
+      std::map<ClientTagHash, std::unique_ptr<ProcessorEntity>>* entities);
+
+  // Processes incremental updates from the sync server.
+  base::Optional<ModelError> ProcessIncrementalUpdate(
+      const sync_pb::ModelTypeState& model_type_state,
+      UpdateResponseDataList updates);
+
+  ClientTagBasedRemoteUpdateHandler(const ClientTagBasedRemoteUpdateHandler&) =
+      delete;
+  ClientTagBasedRemoteUpdateHandler& operator=(
+      const ClientTagBasedRemoteUpdateHandler&) = delete;
+
+ private:
+  // Helper function to process the update for a single entity. If a local data
+  // change is required, it will be added to |entity_changes|. The return value
+  // is the tracked entity, or nullptr if the update should be ignored.
+  // |storage_key_to_clear| must not be null and allows the implementation to
+  // indicate that a certain storage key is now obsolete and should be cleared,
+  // which is leveraged in certain conflict resolution scenarios.
+  ProcessorEntity* ProcessUpdate(std::unique_ptr<UpdateResponseData> update,
+                                 EntityChangeList* entity_changes,
+                                 std::string* storage_key_to_clear);
+
+  // Recommit all entities for encryption except those in |already_updated|.
+  void RecommitAllForEncryption(
+      const std::unordered_set<std::string>& already_updated,
+      MetadataChangeList* metadata_changes);
+
+  // Resolve a conflict between |update| and the pending commit in |entity|.
+  ConflictResolution ResolveConflict(std::unique_ptr<UpdateResponseData> update,
+                                     ProcessorEntity* entity,
+                                     EntityChangeList* changes,
+                                     std::string* storage_key_to_clear);
+
+  // Gets the entity for the given tag hash, or null if there isn't one.
+  ProcessorEntity* GetEntityForTagHash(const ClientTagHash& tag_hash);
+
+  // Create an entity in the entity map for |storage_key|.
+  // |storage_key| must not exist in |storage_key_to_tag_hash_|.
+  ProcessorEntity* CreateEntity(const std::string& storage_key,
+                                const EntityData& data);
+
+  // Version of the above that generates a tag for |data|.
+  ProcessorEntity* CreateEntity(const EntityData& data);
+
+  // The model type this object syncs.
+  const ModelType type_;
+
+  // ModelTypeSyncBridge linked to associated processor.
+  ModelTypeSyncBridge* const bridge_;
+
+  // The model type metadata (progress marker, initial sync done, etc).
+  sync_pb::ModelTypeState* const model_type_state_;
+
+  // This mapping allows us to convert from storage key to client tag hash.
+  // Should be replaced with new interface.
+  std::map<std::string, ClientTagHash>* const storage_key_to_tag_hash_;
+
+  // A map of client tag hash to sync entities known to the processor.
+  // Should be replaced with new interface.
+  std::map<ClientTagHash, std::unique_ptr<ProcessorEntity>>* const entities_;
+};
+
+}  // namespace syncer
+
+#endif  // COMPONENTS_SYNC_MODEL_IMPL_CLIENT_TAG_BASED_REMOTE_UPDATE_HANDLER_H_
diff --git a/components/sync/model_impl/syncable_service_based_bridge.cc b/components/sync/model_impl/syncable_service_based_bridge.cc
index 864c537..5f6e5903 100644
--- a/components/sync/model_impl/syncable_service_based_bridge.cc
+++ b/components/sync/model_impl/syncable_service_based_bridge.cc
@@ -12,6 +12,7 @@
 #include "base/location.h"
 #include "base/trace_event/memory_usage_estimator.h"
 #include "components/sync/base/client_tag_hash.h"
+#include "components/sync/model/conflict_resolution.h"
 #include "components/sync/model/mutable_data_batch.h"
 #include "components/sync/model/sync_change.h"
 #include "components/sync/model/sync_error_factory.h"
diff --git a/components/url_formatter/spoof_checks/idn_spoof_checker.cc b/components/url_formatter/spoof_checks/idn_spoof_checker.cc
index 3713ffe6..c2d6c3a 100644
--- a/components/url_formatter/spoof_checks/idn_spoof_checker.cc
+++ b/components/url_formatter/spoof_checks/idn_spoof_checker.cc
@@ -9,6 +9,7 @@
 #include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/threading/thread_local_storage.h"
 #include "build/build_config.h"
 #include "net/base/lookup_string_in_fixed_set.h"
@@ -209,12 +210,20 @@
        // (Potential set: [ሀሁሃሠሡሰሱሲስበቡቢተቱቲታነከኩኪካኬክዐዑዕዖዘዙዚዛዝዞጠጡጢጣጦፐፒꬁꬂꬅ])
        "[[:Ethi:]]",
        "[ሀሠሰስበነተከዐዕዘጠፐꬅ]",
-       {"er", "et"}}};
+       {"er", "et"}},
+      {// Greek
+       "[[:Grek:]]",
+       // This ignores variants such as ά, έ, ή, ί.
+       "[αικνρυωηοτ]",
+       {"gr"}},
+  };
   for (const WholeScriptConfusableData& data : kWholeScriptConfusables) {
     auto all_letters = std::make_unique<icu::UnicodeSet>(
         icu::UnicodeString::fromUTF8(data.script_regex), status);
+    DCHECK(U_SUCCESS(status));
     auto latin_lookalikes = std::make_unique<icu::UnicodeSet>(
         icu::UnicodeString::fromUTF8(data.latin_lookalike_letters), status);
+    DCHECK(U_SUCCESS(status));
     auto script = std::make_unique<WholeScriptConfusable>(
         std::move(all_letters), std::move(latin_lookalikes), data.allowed_tlds);
     wholescriptconfusables_.push_back(std::move(script));
diff --git a/components/url_formatter/spoof_checks/idn_spoof_checker.h b/components/url_formatter/spoof_checks/idn_spoof_checker.h
index dd171a9..45bb2494 100644
--- a/components/url_formatter/spoof_checks/idn_spoof_checker.h
+++ b/components/url_formatter/spoof_checks/idn_spoof_checker.h
@@ -100,7 +100,9 @@
         const std::vector<std::string>& allowed_tlds);
     ~WholeScriptConfusable();
 
-    // Captures all letters belonging to this script.
+    // Captures all letters belonging to this script. See kScriptNameCodeList in
+    // blink/renderer/platform/text/locale_to_script_mapping.cc for script
+    // codes.
     std::unique_ptr<icu::UnicodeSet> all_letters;
     // The subset of all_letters that look like Latin ASCII letters. A domain
     // label entirely made of them is blocked as a simplified
diff --git a/components/url_formatter/spoof_checks/idn_spoof_checker_unittest.cc b/components/url_formatter/spoof_checks/idn_spoof_checker_unittest.cc
index 4e567612..547f98f 100644
--- a/components/url_formatter/spoof_checks/idn_spoof_checker_unittest.cc
+++ b/components/url_formatter/spoof_checks/idn_spoof_checker_unittest.cc
@@ -1207,6 +1207,11 @@
     // Whole-script-confusable in Ethiopic.
     {"xn--6xd66aa62c.com", L"ሠዐዐፐ.com", kUnsafe},
     {"xn--6xd66aa62c.et", L"ሠዐዐፐ.et", kSafe},
+
+    // Whole-script-confusable in Greek.
+    {"xn--mxapd.com", L"ικα.com", kUnsafe},
+    {"xn--mxapd.gr", L"ικα.gr", kSafe},
+    {"xn--mxapd.xn--qxam", L"ικα.ελ", kSafe},
 };
 
 namespace test {
diff --git a/components/viz/service/display_embedder/skia_output_device_buffer_queue.cc b/components/viz/service/display_embedder/skia_output_device_buffer_queue.cc
index 647d1556..394bfb33 100644
--- a/components/viz/service/display_embedder/skia_output_device_buffer_queue.cc
+++ b/components/viz/service/display_embedder/skia_output_device_buffer_queue.cc
@@ -174,6 +174,15 @@
   return fence_->GetGpuFence();
 }
 
+SkiaOutputDeviceBufferQueue::InFlightFrame::InFlightFrame(
+    std::unique_ptr<Image> image)
+    : image(std::move(image)) {}
+
+SkiaOutputDeviceBufferQueue::InFlightFrame::InFlightFrame(
+    InFlightFrame&& other) = default;
+
+SkiaOutputDeviceBufferQueue::InFlightFrame::~InFlightFrame() = default;
+
 class SkiaOutputDeviceBufferQueue::OverlayData {
  public:
   OverlayData(
@@ -312,17 +321,17 @@
 }
 
 void SkiaOutputDeviceBufferQueue::PageFlipComplete() {
-  DCHECK(!in_flight_images_.empty());
+  DCHECK(!in_flight_frames_.empty());
 
-  if (in_flight_images_.front()) {
+  if (in_flight_frames_.front().image) {
     if (displayed_image_) {
       displayed_image_->EndPresent();
       available_images_.push_back(std::move(displayed_image_));
     }
-    displayed_image_ = std::move(in_flight_images_.front());
+    displayed_image_ = std::move(in_flight_frames_.front().image);
   }
 
-  in_flight_images_.pop_front();
+  in_flight_frames_.pop_front();
 }
 
 void SkiaOutputDeviceBufferQueue::FreeAllSurfaces() {
@@ -330,8 +339,8 @@
   current_image_.reset();
   // This is intentionally not emptied since the swap buffers acks are still
   // expected to arrive.
-  for (auto& image : in_flight_images_)
-    image = nullptr;
+  for (auto& frame : in_flight_frames_)
+    frame.image = nullptr;
 
   available_images_.clear();
 }
@@ -401,16 +410,20 @@
   // nullptr.
   if (current_image_)
     current_image_->BeginPresent();
-  in_flight_images_.push_back(std::move(current_image_));
+  in_flight_frames_.emplace_back(std::move(current_image_));
 
   StartSwapBuffers({});
 
   if (gl_surface_->SupportsAsyncSwap()) {
-    auto callback =
-        base::BindOnce(&SkiaOutputDeviceBufferQueue::DoFinishSwapBuffers,
-                       weak_ptr_factory_.GetWeakPtr(), image_size_,
-                       std::move(latency_info), std::move(committed_overlays_));
-    gl_surface_->SwapBuffersAsync(std::move(callback), std::move(feedback));
+    in_flight_frames_.back().cancelable_swap_completion_callback =
+        std::make_unique<CancelableSwapCompletionCallback>(base::BindOnce(
+            &SkiaOutputDeviceBufferQueue::DoFinishSwapBuffers,
+            weak_ptr_factory_.GetWeakPtr(), image_size_,
+            std::move(latency_info), std::move(committed_overlays_)));
+    gl_surface_->SwapBuffersAsync(
+        in_flight_frames_.back()
+            .cancelable_swap_completion_callback->callback(),
+        std::move(feedback));
   } else {
     DoFinishSwapBuffers(image_size_, std::move(latency_info),
                         std::move(committed_overlays_),
@@ -424,17 +437,20 @@
     const gfx::Rect& rect,
     BufferPresentedCallback feedback,
     std::vector<ui::LatencyInfo> latency_info) {
-  in_flight_images_.push_back(std::move(current_image_));
+  in_flight_frames_.emplace_back(std::move(current_image_));
   StartSwapBuffers({});
 
   if (gl_surface_->SupportsAsyncSwap()) {
-    auto callback =
-        base::BindOnce(&SkiaOutputDeviceBufferQueue::DoFinishSwapBuffers,
-                       weak_ptr_factory_.GetWeakPtr(), image_size_,
-                       std::move(latency_info), std::move(committed_overlays_));
-    gl_surface_->PostSubBufferAsync(rect.x(), rect.y(), rect.width(),
-                                    rect.height(), std::move(callback),
-                                    std::move(feedback));
+    in_flight_frames_.back().cancelable_swap_completion_callback =
+        std::make_unique<CancelableSwapCompletionCallback>(base::BindOnce(
+            &SkiaOutputDeviceBufferQueue::DoFinishSwapBuffers,
+            weak_ptr_factory_.GetWeakPtr(), image_size_,
+            std::move(latency_info), std::move(committed_overlays_)));
+    gl_surface_->PostSubBufferAsync(
+        rect.x(), rect.y(), rect.width(), rect.height(),
+        in_flight_frames_.back()
+            .cancelable_swap_completion_callback->callback(),
+        std::move(feedback));
 
   } else {
     DoFinishSwapBuffers(
diff --git a/components/viz/service/display_embedder/skia_output_device_buffer_queue.h b/components/viz/service/display_embedder/skia_output_device_buffer_queue.h
index 2ad1af2..14f8f05 100644
--- a/components/viz/service/display_embedder/skia_output_device_buffer_queue.h
+++ b/components/viz/service/display_embedder/skia_output_device_buffer_queue.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_SKIA_OUTPUT_DEVICE_BUFFER_QUEUE_H_
 #define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_SKIA_OUTPUT_DEVICE_BUFFER_QUEUE_H_
 
+#include "base/cancelable_callback.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "components/viz/service/display_embedder/skia_output_device.h"
@@ -66,6 +67,23 @@
   class Image;
   class OverlayData;
 
+  using CancelableSwapCompletionCallback =
+      base::CancelableOnceCallback<void(gfx::SwapResult,
+                                        std::unique_ptr<gfx::GpuFence>)>;
+
+  struct InFlightFrame {
+    explicit InFlightFrame(std::unique_ptr<Image> image);
+    InFlightFrame(const InFlightFrame& other) = delete;
+    InFlightFrame(InFlightFrame&& other);
+    ~InFlightFrame();
+
+    std::unique_ptr<Image> image;
+    // Use CancelableOnceCallback to prevent OverlayData from being destructed
+    // outside SkiaOutputDeviceBufferQueue life span.
+    std::unique_ptr<CancelableSwapCompletionCallback>
+        cancelable_swap_completion_callback;
+  };
+
   Image* GetCurrentImage();
   std::unique_ptr<Image> GetNextImage();
   void PageFlipComplete();
@@ -92,10 +110,10 @@
   std::unique_ptr<Image> displayed_image_;
   // These are free for use, and are not nullptr.
   std::vector<std::unique_ptr<Image>> available_images_;
-  // These have been scheduled to display but are not displayed yet.
-  // Entries of this deque may be nullptr, if they represent frames that have
-  // been destroyed.
-  base::circular_deque<std::unique_ptr<Image>> in_flight_images_;
+  // These contain images that have been scheduled to display but are not
+  // displayed yet. Entries of this deque may have nullptr Image, if they
+  // represent frames that have been destroyed.
+  base::circular_deque<InFlightFrame> in_flight_frames_;
   // Scheduled overlays for the next SwapBuffers call.
   std::vector<OverlayData> pending_overlays_;
   // Committed overlays for the last SwapBuffers call.
diff --git a/components/viz/service/display_embedder/skia_output_device_buffer_queue_unittest.cc b/components/viz/service/display_embedder/skia_output_device_buffer_queue_unittest.cc
index adf4e82e..dba83b17 100644
--- a/components/viz/service/display_embedder/skia_output_device_buffer_queue_unittest.cc
+++ b/components/viz/service/display_embedder/skia_output_device_buffer_queue_unittest.cc
@@ -219,14 +219,15 @@
 
   Image* displayed_image() { return output_device_->displayed_image_.get(); }
 
-  base::circular_deque<std::unique_ptr<Image>>& in_flight_images() {
-    return output_device_->in_flight_images_;
+  base::circular_deque<SkiaOutputDeviceBufferQueue::InFlightFrame>&
+  in_flight_frames() {
+    return output_device_->in_flight_frames_;
   }
 
   const gpu::MemoryTracker& memory_tracker() { return *memory_tracker_; }
 
   int CountBuffers() {
-    int n = available_images().size() + in_flight_images().size();
+    int n = available_images().size() + in_flight_frames().size();
 
     if (displayed_image())
       n++;
@@ -239,8 +240,8 @@
     std::set<Image*> images;
     for (const auto& image : available_images())
       images.insert(image.get());
-    for (const auto& image : in_flight_images())
-      images.insert(image.get());
+    for (const auto& frame : in_flight_frames())
+      images.insert(frame.image.get());
 
     if (displayed_image())
       images.insert(displayed_image());
@@ -309,24 +310,24 @@
   EXPECT_NE(current_image(), nullptr);
   EXPECT_FALSE(displayed_image());
   SwapBuffers();
-  EXPECT_EQ(1U, in_flight_images().size());
+  EXPECT_EQ(1U, in_flight_frames().size());
   PageFlipComplete();
-  EXPECT_EQ(0U, in_flight_images().size());
+  EXPECT_EQ(0U, in_flight_frames().size());
   EXPECT_TRUE(displayed_image());
   EXPECT_NE(GetCurrentImage(), nullptr);
   EXPECT_NE(0U, memory_tracker().GetSize());
   EXPECT_EQ(2, CountBuffers());
   CheckUnique();
   EXPECT_NE(current_image(), nullptr);
-  EXPECT_EQ(0U, in_flight_images().size());
+  EXPECT_EQ(0U, in_flight_frames().size());
   EXPECT_TRUE(displayed_image());
   SwapBuffers();
   CheckUnique();
-  EXPECT_EQ(1U, in_flight_images().size());
+  EXPECT_EQ(1U, in_flight_frames().size());
   EXPECT_TRUE(displayed_image());
   PageFlipComplete();
   CheckUnique();
-  EXPECT_EQ(0U, in_flight_images().size());
+  EXPECT_EQ(0U, in_flight_frames().size());
   EXPECT_EQ(1U, available_images().size());
   EXPECT_TRUE(displayed_image());
   EXPECT_NE(GetCurrentImage(), nullptr);
@@ -353,20 +354,20 @@
   EXPECT_NE(0U, memory_tracker().GetSize());
   EXPECT_EQ(2, CountBuffers());
   CheckUnique();
-  EXPECT_EQ(1U, in_flight_images().size());
+  EXPECT_EQ(1U, in_flight_frames().size());
   EXPECT_TRUE(displayed_image());
   EXPECT_NE(GetCurrentImage(), nullptr);
   EXPECT_NE(0U, memory_tracker().GetSize());
   EXPECT_EQ(3, CountBuffers());
   CheckUnique();
   EXPECT_NE(current_image(), nullptr);
-  EXPECT_EQ(1U, in_flight_images().size());
+  EXPECT_EQ(1U, in_flight_frames().size());
   EXPECT_TRUE(displayed_image());
   PageFlipComplete();
   EXPECT_EQ(3, CountBuffers());
   CheckUnique();
   EXPECT_NE(current_image(), nullptr);
-  EXPECT_EQ(0U, in_flight_images().size());
+  EXPECT_EQ(0U, in_flight_frames().size());
   EXPECT_TRUE(displayed_image());
   EXPECT_EQ(1U, available_images().size());
 }
@@ -392,21 +393,21 @@
   EXPECT_NE(new_image, nullptr);
   EXPECT_NE(image, new_image);
 
-  EXPECT_EQ(1U, in_flight_images().size());
+  EXPECT_EQ(1U, in_flight_frames().size());
   PageFlipComplete();
 
   // Test swapbuffers without calling BeginPaint/EndPaint (i.e without
   // GetCurrentImage)
   SwapBuffers();
-  EXPECT_EQ(1U, in_flight_images().size());
+  EXPECT_EQ(1U, in_flight_frames().size());
   PageFlipComplete();
-  EXPECT_EQ(0U, in_flight_images().size());
+  EXPECT_EQ(0U, in_flight_frames().size());
 
   EXPECT_EQ(current_image(), nullptr);
   SwapBuffers();
-  EXPECT_EQ(1U, in_flight_images().size());
+  EXPECT_EQ(1U, in_flight_frames().size());
   PageFlipComplete();
-  EXPECT_EQ(0U, in_flight_images().size());
+  EXPECT_EQ(0U, in_flight_frames().size());
 }
 
 TEST_F_GPU(SkiaOutputDeviceBufferQueueTest, CheckCorrectBufferOrdering) {
@@ -427,8 +428,8 @@
   for (size_t i = 0; i < kSwapCount; ++i) {
     EXPECT_NE(GetCurrentImage(), nullptr);
     SwapBuffers();
-    EXPECT_EQ(1U, in_flight_images().size());
-    auto* next_image = in_flight_images().front().get();
+    EXPECT_EQ(1U, in_flight_frames().size());
+    auto* next_image = in_flight_frames().front().image.get();
     PageFlipComplete();
     EXPECT_EQ(displayed_image(), next_image);
   }
@@ -451,7 +452,7 @@
 
   output_device_->Reshape(screen_size, 1.0f, gfx::ColorSpace(), false,
                           gfx::OVERLAY_TRANSFORM_NONE);
-  EXPECT_EQ(1u, in_flight_images().size());
+  EXPECT_EQ(1u, in_flight_frames().size());
 
   PageFlipComplete();
   EXPECT_FALSE(displayed_image());
diff --git a/content/browser/back_forward_cache_browsertest.cc b/content/browser/back_forward_cache_browsertest.cc
index dd7ffa9..6c8196f7 100644
--- a/content/browser/back_forward_cache_browsertest.cc
+++ b/content/browser/back_forward_cache_browsertest.cc
@@ -1185,7 +1185,7 @@
   web_contents()->GetController().GoBack();
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   // Note: We still have a transition proxy that will be used to perform the
-  // frame SwapOut. It gets deleted with rfh_b below.
+  // frame swap. It gets deleted with rfh_b below.
   EXPECT_EQ(3u, render_frame_host_manager()->GetProxyCount());
 
   // Page B should be deleted (not cached).
@@ -4792,7 +4792,7 @@
   dialog_observer.WaitForAppModalDialog();
 }
 
-// SwapOutOldFrame will clear all dialogs. We test that further requests for
+// UnloadOldFrame will clear all dialogs. We test that further requests for
 // dialogs coming from JS do not result in the creation of a dialog. This test
 // posts some dialog creation JS to the render from inside the
 // CommitNavigationCallback task. This JS is then able to post a task back to
diff --git a/content/browser/cross_origin_opener_policy_browsertest.cc b/content/browser/cross_origin_opener_policy_browsertest.cc
index b86feda4..4874d01 100644
--- a/content/browser/cross_origin_opener_policy_browsertest.cc
+++ b/content/browser/cross_origin_opener_policy_browsertest.cc
@@ -46,7 +46,7 @@
   EXPECT_TRUE(NavigateToURL(shell(), starting_page));
 
   RenderFrameHostImpl* main_frame = current_frame_host();
-  main_frame->SetCrossOriginOriginOpenerPolicyForTesting(
+  main_frame->set_cross_origin_opener_policy(
       network::mojom::CrossOriginOpenerPolicy::kSameOrigin);
 
   ShellAddedObserver shell_observer;
@@ -72,7 +72,7 @@
   EXPECT_TRUE(NavigateToURL(shell(), starting_page));
 
   RenderFrameHostImpl* main_frame = current_frame_host();
-  main_frame->SetCrossOriginOriginOpenerPolicyForTesting(
+  main_frame->set_cross_origin_opener_policy(
       network::mojom::CrossOriginOpenerPolicy::kSameOriginAllowPopups);
 
   ShellAddedObserver shell_observer;
@@ -98,7 +98,7 @@
   EXPECT_TRUE(NavigateToURL(shell(), starting_page));
 
   RenderFrameHostImpl* main_frame = current_frame_host();
-  main_frame->SetCrossOriginOriginOpenerPolicyForTesting(
+  main_frame->set_cross_origin_opener_policy(
       network::mojom::CrossOriginOpenerPolicy::kSameOrigin);
 
   ShellAddedObserver shell_observer;
@@ -125,7 +125,7 @@
   EXPECT_TRUE(NavigateToURL(shell(), starting_page));
 
   RenderFrameHostImpl* main_frame = current_frame_host();
-  main_frame->SetCrossOriginOriginOpenerPolicyForTesting(
+  main_frame->set_cross_origin_opener_policy(
       network::mojom::CrossOriginOpenerPolicy::kSameOrigin);
 
   ShellAddedObserver new_shell_observer;
diff --git a/content/browser/frame_host/ancestor_throttle_unittest.cc b/content/browser/frame_host/ancestor_throttle_unittest.cc
index d025c1f..605d8a4 100644
--- a/content/browser/frame_host/ancestor_throttle_unittest.cc
+++ b/content/browser/frame_host/ancestor_throttle_unittest.cc
@@ -12,6 +12,7 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/test_renderer_host.h"
 #include "net/http/http_response_headers.h"
+#include "services/network/public/cpp/features.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -129,6 +130,13 @@
 }
 
 TEST_F(AncestorThrottleTest, IgnoreWhenFrameAncestorsPresent) {
+  // When OutOfBlinkFrameAncestors is enabled frame-ancestors is processed in
+  // the AncestorThrottle and XFO will not be parsed.
+  if (base::FeatureList::IsEnabled(
+          network::features::kOutOfBlinkFrameAncestors)) {
+    return;
+  }
+
   struct TestCase {
     const char* csp;
     AncestorThrottle::HeaderDisposition expected;
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index 550c96e..8be2e2a 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -1679,6 +1679,8 @@
   if (render_frame_host_) {
     render_frame_host_->set_cross_origin_embedder_policy(
         cross_origin_embedder_policy);
+    render_frame_host_->set_cross_origin_opener_policy(
+        response_head_->cross_origin_opener_policy);
   }
 
   if (!browser_initiated_ && render_frame_host_ &&
diff --git a/content/browser/frame_host/navigation_request_browsertest.cc b/content/browser/frame_host/navigation_request_browsertest.cc
index 38de5db..1ff7285 100644
--- a/content/browser/frame_host/navigation_request_browsertest.cc
+++ b/content/browser/frame_host/navigation_request_browsertest.cc
@@ -1643,7 +1643,7 @@
 
   EXPECT_TRUE(observer.has_committed());
   EXPECT_TRUE(observer.is_error());
-  EXPECT_EQ(net::ERR_NAME_NOT_RESOLVED, observer.net_error_code());
+  EXPECT_EQ(net::ERR_DNS_TIMED_OUT, observer.net_error_code());
   EXPECT_EQ(net::ERR_DNS_TIMED_OUT, observer.resolve_error_info().error);
 }
 
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index b0cf9707..a5dd6fd 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -869,7 +869,7 @@
       frame_tree_node_(frame_tree_node),
       parent_(nullptr),
       routing_id_(routing_id),
-      is_waiting_for_swapout_ack_(false),
+      is_waiting_for_unload_ack_(false),
       render_frame_created_(false),
       is_waiting_for_beforeunload_ack_(false),
       beforeunload_dialog_request_cancels_unload_(false),
@@ -925,8 +925,9 @@
 
   SetUpMojoIfNeeded();
 
-  swapout_event_monitor_timeout_.reset(new TimeoutMonitor(base::BindRepeating(
-      &RenderFrameHostImpl::OnSwappedOut, weak_ptr_factory_.GetWeakPtr())));
+  unload_event_monitor_timeout_ =
+      std::make_unique<TimeoutMonitor>(base::BindRepeating(
+          &RenderFrameHostImpl::OnUnloaded, weak_ptr_factory_.GetWeakPtr()));
   beforeunload_timeout_.reset(new TimeoutMonitor(
       base::BindRepeating(&RenderFrameHostImpl::BeforeUnloadTimeout,
                           weak_ptr_factory_.GetWeakPtr())));
@@ -993,7 +994,7 @@
 
 RenderFrameHostImpl::~RenderFrameHostImpl() {
   // When a RenderFrameHostImpl is deleted, it may still contain children. This
-  // can happen with the swap out timer. It causes a RenderFrameHost to delete
+  // can happen with the unload timer. It causes a RenderFrameHost to delete
   // itself even if it is still waiting for its children to complete their
   // unload handlers.
   //
@@ -1041,13 +1042,13 @@
   // 1. The RenderFrame can be the main frame. In this case, closing the
   //    associated RenderView will clean up the resources associated with the
   //    main RenderFrame.
-  // 2. The RenderFrame can be swapped out. In this case, the browser sends a
-  //    UnfreezableFrameMsg_SwapOut for the RenderFrame to replace itself with a
+  // 2. The RenderFrame can be unloaded. In this case, the browser sends a
+  //    UnfreezableFrameMsg_Unload for the RenderFrame to replace itself with a
   //    RenderFrameProxy and release its associated resources. |unload_state_|
   //    is advanced to UnloadState::InProgress to track that this IPC is in
   //    flight.
   // 3. The RenderFrame can be detached, as part of removing a subtree (due to
-  //    navigation, swap out, or DOM mutation). In this case, the browser sends
+  //    navigation, unload, or DOM mutation). In this case, the browser sends
   //    a UnfreezableFrameMsg_Delete for the RenderFrame to detach itself and
   //    release its associated resources. If the subframe contains an unload
   //    handler, |unload_state_| is advanced to UnloadState::InProgress to track
@@ -1055,7 +1056,7 @@
   //    UnloadState::Completed.
   //
   // The browser side gives the renderer a small timeout to finish processing
-  // swap out / detach messages. When the timeout expires, the RFH will be
+  // unload / detach messages. When the timeout expires, the RFH will be
   // removed regardless of whether or not the renderer acknowledged that it
   // completed the work, to avoid indefinitely leaking browser-side state. To
   // avoid leaks, ~RenderFrameHostImpl still validates that the appropriate
@@ -1094,9 +1095,9 @@
   g_routing_id_frame_map.Get().erase(
       GlobalFrameRoutingId(GetProcess()->GetID(), routing_id_));
 
-  // Null out the swapout timer; in crash dumps this member will be null only if
+  // Null out the unload timer; in crash dumps this member will be null only if
   // the dtor has run.  (It may also be null in tests.)
-  swapout_event_monitor_timeout_.reset();
+  unload_event_monitor_timeout_.reset();
 
   for (auto& iter : visual_state_callbacks_)
     std::move(iter.second).Run(false);
@@ -1657,7 +1658,7 @@
     IPC_MESSAGE_HANDLER(FrameHostMsg_UpdateState, OnUpdateState)
     IPC_MESSAGE_HANDLER(FrameHostMsg_OpenURL, OnOpenURL)
     IPC_MESSAGE_HANDLER(FrameHostMsg_BeforeUnload_ACK, OnBeforeUnloadACK)
-    IPC_MESSAGE_HANDLER(FrameHostMsg_SwapOut_ACK, OnSwapOutACK)
+    IPC_MESSAGE_HANDLER(FrameHostMsg_Unload_ACK, OnUnloadACK)
     IPC_MESSAGE_HANDLER(FrameHostMsg_ContextMenu, OnContextMenu)
     IPC_MESSAGE_HANDLER(FrameHostMsg_VisualStateResponse, OnVisualStateResponse)
     IPC_MESSAGE_HANDLER_DELAY_REPLY(FrameHostMsg_RunJavaScriptDialog,
@@ -2510,7 +2511,7 @@
   // A frame is removed while replacing this document with the new one. When it
   // happens, delete the frame and both the new and old documents. Unload
   // handlers aren't guaranteed to run here.
-  if (is_waiting_for_swapout_ack_) {
+  if (is_waiting_for_unload_ack_) {
     parent_->RemoveChild(frame_tree_node_);
     return;
   }
@@ -2790,45 +2791,44 @@
       std::move(navigation_request);
 }
 
-void RenderFrameHostImpl::SwapOut(RenderFrameProxyHost* proxy,
-                                  bool is_loading) {
-  // The end of this event is in OnSwapOutACK when the RenderFrame has completed
+void RenderFrameHostImpl::Unload(RenderFrameProxyHost* proxy, bool is_loading) {
+  // The end of this event is in OnUnloadACK when the RenderFrame has completed
   // the operation and sends back an IPC message.
   // The trace event may not end properly if the ACK times out.  We expect this
-  // to be fixed when RenderViewHostImpl::OnSwapOut moves to RenderFrameHost.
-  TRACE_EVENT_ASYNC_BEGIN1("navigation", "RenderFrameHostImpl::SwapOut", this,
+  // to be fixed when RenderViewHostImpl::OnUnload moves to RenderFrameHost.
+  TRACE_EVENT_ASYNC_BEGIN1("navigation", "RenderFrameHostImpl::Unload", this,
                            "frame_tree_node",
                            frame_tree_node_->frame_tree_node_id());
 
   // If this RenderFrameHost is already pending deletion, it must have already
   // gone through this, therefore just return.
   if (unload_state_ != UnloadState::NotRun) {
-    NOTREACHED() << "RFH should be in default state when calling SwapOut.";
+    NOTREACHED() << "RFH should be in default state when calling Unload.";
     return;
   }
 
-  if (swapout_event_monitor_timeout_) {
-    swapout_event_monitor_timeout_->Start(base::TimeDelta::FromMilliseconds(
+  if (unload_event_monitor_timeout_) {
+    unload_event_monitor_timeout_->Start(base::TimeDelta::FromMilliseconds(
         RenderViewHostImpl::kUnloadTimeoutMS));
   }
 
   // There should always be a proxy to replace the old RenderFrameHost.  If
   // there are no remaining active views in the process, the proxy will be
-  // short-lived and will be deleted when the SwapOut ACK is received.
+  // short-lived and will be deleted when the unload ACK is received.
   CHECK(proxy);
 
   // TODO(nasko): If the frame is not live, the RFH should just be deleted by
-  // simulating the receipt of swap out ack.
-  is_waiting_for_swapout_ack_ = true;
+  // simulating the receipt of unload ack.
+  is_waiting_for_unload_ack_ = true;
   unload_state_ = UnloadState::InProgress;
 
   if (IsRenderFrameLive()) {
     FrameReplicationState replication_state =
         proxy->frame_tree_node()->current_replication_state();
-    Send(new UnfreezableFrameMsg_SwapOut(routing_id_, proxy->GetRoutingID(),
-                                         is_loading, replication_state));
+    Send(new UnfreezableFrameMsg_Unload(routing_id_, proxy->GetRoutingID(),
+                                        is_loading, replication_state));
     // Remember that a RenderFrameProxy was created as part of processing the
-    // SwapOut message above.
+    // Unload message above.
     proxy->SetRenderFrameProxyCreated(true);
 
     StartPendingDeletionOnSubtree();
@@ -2839,7 +2839,7 @@
   PendingDeletionCheckCompletedOnSubtree();
 
   if (web_ui())
-    web_ui()->RenderFrameHostSwappingOut();
+    web_ui()->RenderFrameHostUnloading();
 
   web_bluetooth_services_.clear();
 #if !defined(OS_ANDROID)
@@ -3006,19 +3006,19 @@
 
 bool RenderFrameHostImpl::IsWaitingForUnloadACK() const {
   return render_view_host_->is_waiting_for_close_ack_ ||
-         is_waiting_for_swapout_ack_;
+         is_waiting_for_unload_ack_;
 }
 
-void RenderFrameHostImpl::OnSwapOutACK() {
+void RenderFrameHostImpl::OnUnloadACK() {
   if (frame_tree_node_->render_manager()->is_attaching_inner_delegate()) {
-    // This RFH was swapped out while attaching an inner delegate. The RFH
+    // This RFH was unloaded while attaching an inner delegate. The RFH
     // will stay around but it will no longer be associated with a RenderFrame.
     SetRenderFrameCreated(false);
     return;
   }
 
-  // Ignore spurious swap out ack.
-  if (!is_waiting_for_swapout_ack_)
+  // Ignore spurious unload ack.
+  if (!is_waiting_for_unload_ack_)
     return;
 
   DCHECK_EQ(UnloadState::InProgress, unload_state_);
@@ -3026,12 +3026,12 @@
   PendingDeletionCheckCompleted();  // Can delete |this|.
 }
 
-void RenderFrameHostImpl::OnSwappedOut() {
-  DCHECK(is_waiting_for_swapout_ack_);
+void RenderFrameHostImpl::OnUnloaded() {
+  DCHECK(is_waiting_for_unload_ack_);
 
-  TRACE_EVENT_ASYNC_END0("navigation", "RenderFrameHostImpl::SwapOut", this);
-  if (swapout_event_monitor_timeout_)
-    swapout_event_monitor_timeout_->Stop();
+  TRACE_EVENT_ASYNC_END0("navigation", "RenderFrameHostImpl::Unload", this);
+  if (unload_event_monitor_timeout_)
+    unload_event_monitor_timeout_->Stop();
 
   ClearWebUI();
 
@@ -3047,8 +3047,8 @@
   CHECK(deleted);
 }
 
-void RenderFrameHostImpl::DisableSwapOutTimerForTesting() {
-  swapout_event_monitor_timeout_.reset();
+void RenderFrameHostImpl::DisableUnloadTimerForTesting() {
+  unload_event_monitor_timeout_.reset();
 }
 
 void RenderFrameHostImpl::SetSubframeUnloadTimeoutForTesting(
@@ -5060,8 +5060,8 @@
 
 void RenderFrameHostImpl::PendingDeletionCheckCompleted() {
   if (unload_state_ == UnloadState::Completed && children_.empty()) {
-    if (is_waiting_for_swapout_ack_)
-      OnSwappedOut();
+    if (is_waiting_for_unload_ack_)
+      OnUnloaded();
     else
       parent_->RemoveChild(frame_tree_node_);
   }
@@ -5568,7 +5568,7 @@
     // same-process navigations yet as we are reusing the RenderFrameHost and
     // as the local frame navigates it overrides the values that we are
     // interested in. The cross-process navigation case is handled in
-    // RenderFrameHostManager::SwapOutOldFrame.
+    // RenderFrameHostManager::UnloadOldFrame.
     //
     // Here we are recording the metrics for same-process navigations at the
     // point just before the navigation commits.
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h
index ed95eddf..117e17b6 100644
--- a/content/browser/frame_host/render_frame_host_impl.h
+++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -569,14 +569,13 @@
   void SetNavigationRequest(
       std::unique_ptr<NavigationRequest> navigation_request);
 
-  // Tells the renderer that this RenderFrame is being swapped out for one in a
+  // Tells the renderer that this RenderFrame is being replaced with one in a
   // different renderer process.  It should run its unload handler and move to
   // a blank document.  If |proxy| is not null, it should also create a
   // RenderFrameProxy to replace the RenderFrame and set it to |is_loading|
-  // state. The renderer should preserve the RenderFrameProxy object until it
-  // exits, in case we come back.  The renderer can exit if it has no other
-  // active RenderFrames, but not until WasSwappedOut is called.
-  void SwapOut(RenderFrameProxyHost* proxy, bool is_loading);
+  // state. The renderer process keeps the RenderFrameProxy object around as a
+  // placeholder while the frame is rendered in a different process.
+  void Unload(RenderFrameProxyHost* proxy, bool is_loading);
 
   // Remove this frame and its children. This happens asynchronously, an IPC
   // round trip with the renderer process is needed to ensure children's unload
@@ -593,12 +592,12 @@
   // Whether the RFH is waiting for an unload ACK from the renderer.
   bool IsWaitingForUnloadACK() const;
 
-  // Called when either the SwapOut request has been acknowledged or has timed
+  // Called when either the Unload() request has been acknowledged or has timed
   // out.
-  void OnSwappedOut();
+  void OnUnloaded();
 
   // This method returns true from the time this RenderFrameHost is created
-  // until it is pending deletion. Pending deletion starts when SwapOut is
+  // until it is pending deletion. Pending deletion starts when Unload() is
   // called on the frame or one of its ancestors.
   // BackForwardCache: Returns false when the frame is in the BackForwardCache.
   bool is_active() const {
@@ -883,7 +882,7 @@
   void BindBrowserInterfaceBrokerReceiver(
       mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>);
 
-  // Exposed so that tests can swap out the implementation and intercept calls.
+  // Exposed so that tests can swap the implementation and intercept calls.
   mojo::AssociatedReceiver<mojom::FrameHost>&
   frame_host_receiver_for_testing() {
     return frame_host_associated_receiver_;
@@ -1211,7 +1210,7 @@
   network::mojom::CrossOriginOpenerPolicy cross_origin_opener_policy() const {
     return cross_origin_opener_policy_;
   }
-  void SetCrossOriginOriginOpenerPolicyForTesting(
+  void set_cross_origin_opener_policy(
       network::mojom::CrossOriginOpenerPolicy policy) {
     cross_origin_opener_policy_ = policy;
   }
@@ -1376,7 +1375,7 @@
   FRIEND_TEST_ALL_PREFIXES(RenderFrameHostManagerTest,
                            UnloadPushStateOnCrossProcessNavigation);
   FRIEND_TEST_ALL_PREFIXES(RenderFrameHostManagerTest,
-                           WebUIJavascriptDisallowedAfterSwapOut);
+                           WebUIJavascriptDisallowedAfterUnload);
   FRIEND_TEST_ALL_PREFIXES(RenderFrameHostManagerTest, LastCommittedOrigin);
   FRIEND_TEST_ALL_PREFIXES(
       RenderFrameHostManagerUnloadBrowserTest,
@@ -1384,25 +1383,25 @@
   FRIEND_TEST_ALL_PREFIXES(SitePerProcessBrowserTest, CrashSubframe);
   FRIEND_TEST_ALL_PREFIXES(SitePerProcessBrowserTest, FindImmediateLocalRoots);
   FRIEND_TEST_ALL_PREFIXES(SitePerProcessBrowserTest,
-                           RenderViewHostIsNotReusedAfterDelayedSwapOutACK);
+                           RenderViewHostIsNotReusedAfterDelayedUnloadACK);
   FRIEND_TEST_ALL_PREFIXES(SitePerProcessBrowserTest,
-                           RenderViewHostStaysActiveWithLateSwapoutACK);
+                           RenderViewHostStaysActiveWithLateUnloadACK);
   FRIEND_TEST_ALL_PREFIXES(SitePerProcessBrowserTest,
                            LoadEventForwardingWhilePendingDeletion);
   FRIEND_TEST_ALL_PREFIXES(SitePerProcessBrowserTest,
                            ContextMenuAfterCrossProcessNavigation);
   FRIEND_TEST_ALL_PREFIXES(SitePerProcessBrowserTest,
-                           ActiveSandboxFlagsRetainedAfterSwapOut);
+                           ActiveSandboxFlagsRetainedAfterUnload);
   FRIEND_TEST_ALL_PREFIXES(SitePerProcessBrowserTest,
-                           LastCommittedURLRetainedAfterSwapOut);
+                           LastCommittedURLRetainedAfterUnload);
   FRIEND_TEST_ALL_PREFIXES(SitePerProcessBrowserTest,
                            RenderFrameProxyNotRecreatedDuringProcessShutdown);
   FRIEND_TEST_ALL_PREFIXES(SitePerProcessBrowserTest,
-                           SwapOutACKArrivesPriorToProcessShutdownRequest);
+                           UnloadACKArrivesPriorToProcessShutdownRequest);
   FRIEND_TEST_ALL_PREFIXES(SecurityExploitBrowserTest,
                            AttemptDuplicateRenderViewHost);
   FRIEND_TEST_ALL_PREFIXES(WebContentsImplBrowserTest,
-                           FullscreenAfterFrameSwap);
+                           FullscreenAfterFrameUnload);
   FRIEND_TEST_ALL_PREFIXES(SitePerProcessBrowserTest, UnloadHandlerSubframes);
   FRIEND_TEST_ALL_PREFIXES(SitePerProcessBrowserTest, Unload_ABAB);
   FRIEND_TEST_ALL_PREFIXES(SitePerProcessBrowserTest,
@@ -1442,7 +1441,7 @@
       bool proceed,
       const base::TimeTicks& renderer_before_unload_start_time,
       const base::TimeTicks& renderer_before_unload_end_time);
-  void OnSwapOutACK();
+  void OnUnloadACK();
   void OnContextMenu(const ContextMenuParams& params);
   void OnVisualStateResponse(uint64_t id);
   void OnRunJavaScriptDialog(const base::string16& message,
@@ -1696,9 +1695,9 @@
   void GetInterface(const std::string& interface_name,
                     mojo::ScopedMessagePipeHandle interface_pipe) override;
 
-  // Allows tests to disable the swapout event timer to simulate bugs that
+  // Allows tests to disable the unload event timer to simulate bugs that
   // happen before it fires (to avoid flakiness).
-  void DisableSwapOutTimerForTesting();
+  void DisableUnloadTimerForTesting();
 
   void SendJavaScriptDialogReply(IPC::Message* reply_msg,
                                  bool success,
@@ -1885,7 +1884,7 @@
   // subframes have completed running unload handlers. If so, this function
   // destroys this frame. This will happen as soon as...
   // 1) The children in other processes have been deleted.
-  // 2) The ack (FrameHostMsg_Swapout_ACK or FrameHostMsg_Detach) has been
+  // 2) The ack (FrameHostMsg_Unload_ACK or FrameHostMsg_Detach) has been
   //    received. It means this frame in the renderer process is gone.
   void PendingDeletionCheckCompleted();
 
@@ -2054,8 +2053,8 @@
   const int routing_id_;
 
   // Boolean indicating whether this RenderFrameHost is being actively used or
-  // is waiting for FrameHostMsg_SwapOut_ACK and thus pending deletion.
-  bool is_waiting_for_swapout_ack_;
+  // is waiting for FrameHostMsg_Unload_ACK and thus pending deletion.
+  bool is_waiting_for_unload_ack_;
 
   // Tracks whether the RenderFrame for this RenderFrameHost has been created in
   // the renderer process.
@@ -2127,10 +2126,9 @@
   // relevant NavigationEntry.
   int nav_entry_id_;
 
-  // Used to swap out or shut down this RFH when the unload event is taking too
-  // long to execute, depending on the number of active frames in the
-  // SiteInstance.  May be null in tests.
-  std::unique_ptr<TimeoutMonitor> swapout_event_monitor_timeout_;
+  // Used to clean up this RFH when the unload event is taking too long to
+  // execute. May be null in tests.
+  std::unique_ptr<TimeoutMonitor> unload_event_monitor_timeout_;
 
   // GeolocationService which provides Geolocation.
   std::unique_ptr<GeolocationServiceImpl> geolocation_service_;
@@ -2435,7 +2433,7 @@
     // An event such as a navigation happened causing the frame to start its
     // deletion. IPC are sent to execute the unload handlers and delete the
     // RenderFrame. The RenderFrameHost is waiting for an ACK. Either
-    // FrameHostMsg_Swapout_ACK for the navigating frame, or FrameHostMsg_Detach
+    // FrameHostMsg_Unload_ACK for the navigating frame, or FrameHostMsg_Detach
     // for its subframe.
     InProgress,
 
diff --git a/content/browser/frame_host/render_frame_host_impl_browsertest.cc b/content/browser/frame_host/render_frame_host_impl_browsertest.cc
index e671c34..30aa78b 100644
--- a/content/browser/frame_host/render_frame_host_impl_browsertest.cc
+++ b/content/browser/frame_host/render_frame_host_impl_browsertest.cc
@@ -708,11 +708,11 @@
   EXPECT_TRUE(main_frame->is_waiting_for_beforeunload_ack());
 
   // Now answer the dialog and allow the navigation to proceed.  Disable
-  // SwapOut ACK on the old frame so that it sticks around in pending delete
+  // unload ACK on the old frame so that it sticks around in pending delete
   // state, since the test later verifies that it has received the beforeunload
   // ACK.
   TestFrameNavigationObserver commit_observer(root);
-  main_frame->DisableSwapOutTimerForTesting();
+  main_frame->DisableUnloadTimerForTesting();
   CloseDialogAndProceed();
   commit_observer.WaitForCommit();
   EXPECT_EQ(cross_site_url, web_contents()->GetLastCommittedURL());
@@ -721,7 +721,7 @@
 
   // The navigation that succeeded was a browser-initiated, main frame
   // navigation, so it swapped RenderFrameHosts. |main_frame| should now be
-  // pending deletion and waiting for swapout ACK, but it should not be waiting
+  // pending deletion and waiting for unload ACK, but it should not be waiting
   // for the beforeunload ACK.
   EXPECT_FALSE(main_frame->is_active());
   EXPECT_FALSE(main_frame->is_waiting_for_beforeunload_ack());
diff --git a/content/browser/frame_host/render_frame_host_manager.cc b/content/browser/frame_host/render_frame_host_manager.cc
index 40abf086..827f687 100644
--- a/content/browser/frame_host/render_frame_host_manager.cc
+++ b/content/browser/frame_host/render_frame_host_manager.cc
@@ -444,14 +444,15 @@
   }
 }
 
-void RenderFrameHostManager::SwapOutOldFrame(
+void RenderFrameHostManager::UnloadOldFrame(
     std::unique_ptr<RenderFrameHostImpl> old_render_frame_host) {
-  TRACE_EVENT1("navigation", "RenderFrameHostManager::SwapOutOldFrame",
+  TRACE_EVENT1("navigation", "RenderFrameHostManager::UnloadOldFrame",
                "FrameTreeNode id", frame_tree_node_->frame_tree_node_id());
 
-  // Now close any modal dialogs that would prevent us from swapping out.  This
-  // must be done separately from SwapOut, so that the ScopedPageLoadDeferrer is
-  // no longer on the stack when we send the SwapOut message.
+  // Now close any modal dialogs that would prevent us from unloading the frame.
+  // This must be done separately from Unload(), so that the
+  // ScopedPageLoadDeferrer is no longer on the stack when we send the
+  // UnfreezableFrameMsg_Unload message.
   delegate_->CancelModalDialogsForRenderManager();
 
   // If the old RFH is not live, just return as there is no further work to do.
@@ -459,7 +460,7 @@
   if (!old_render_frame_host->IsRenderFrameLive())
     return;
 
-  // Reset any NavigationRequest in the RenderFrameHost. A swapped out
+  // Reset any NavigationRequest in the RenderFrameHost. An unloaded
   // RenderFrameHost should not be trying to commit a navigation.
   old_render_frame_host->ResetNavigationRequests();
 
@@ -487,7 +488,7 @@
   // BackForwardCache:
   //
   // If the old RenderFrameHost can be stored in the BackForwardCache, return
-  // early without swapping out and running unload handlers, as the document may
+  // early without unloading and running unload handlers, as the document may
   // be restored later.
   {
     BackForwardCacheImpl& back_forward_cache =
@@ -544,10 +545,10 @@
       CreateRenderFrameProxyHost(old_render_frame_host->GetSiteInstance(),
                                  old_render_frame_host->render_view_host());
 
-  // Tell the old RenderFrameHost to swap out and be replaced by the proxy.
-  old_render_frame_host->SwapOut(proxy, true);
+  // Tell the old RenderFrameHost to unload and be replaced by the proxy.
+  old_render_frame_host->Unload(proxy, true);
 
-  // |old_render_frame_host| will be deleted when its SwapOut ACK is received,
+  // |old_render_frame_host| will be deleted when its unload ACK is received,
   // or when the timer times out, or when the RFHM itself is deleted (whichever
   // comes first).
   pending_delete_hosts_.push_back(std::move(old_render_frame_host));
@@ -556,7 +557,7 @@
 void RenderFrameHostManager::DiscardUnusedFrame(
     std::unique_ptr<RenderFrameHostImpl> render_frame_host) {
   // TODO(carlosk): this code is very similar to what can be found in
-  // SwapOutOldFrame and we should see that these are unified at some point.
+  // UnloadOldFrame and we should see that these are unified at some point.
 
   // If the SiteInstance for the pending RFH is being used by others, ensure
   // that it is replaced by a RenderFrameProxyHost to allow other frames to
@@ -566,7 +567,7 @@
   RenderFrameProxyHost* proxy = nullptr;
   if (site_instance->HasSite() && site_instance->active_frame_count() > 1) {
     // If a proxy already exists for the |site_instance|, just reuse it instead
-    // of creating a new one. There is no need to call SwapOut on the
+    // of creating a new one. There is no need to call Unload() on the
     // |render_frame_host|, as this method is only called to discard a pending
     // or speculative RenderFrameHost, i.e. one that has never hosted an actual
     // document.
@@ -2080,7 +2081,7 @@
   // TODO(lazyboy): This |is_loading| behavior might not be what we want,
   // investigate and fix.
   DCHECK_EQ(render_frame_host->GetSiteInstance(), proxy->GetSiteInstance());
-  render_frame_host->Send(new UnfreezableFrameMsg_SwapOut(
+  render_frame_host->Send(new UnfreezableFrameMsg_Unload(
       render_frame_host->GetRoutingID(), proxy->GetRoutingID(),
       false /* is_loading */,
       render_frame_host->frame_tree_node()->current_replication_state()));
@@ -2478,7 +2479,7 @@
   // Swap out the old frame now that the new one is visible.
   // This will swap it out and schedule it for deletion when the swap out ack
   // arrives (or immediately if the process isn't live).
-  SwapOutOldFrame(std::move(old_render_frame_host));
+  UnloadOldFrame(std::move(old_render_frame_host));
 
   // Since the new RenderFrameHost is now committed, there must be no proxies
   // for its SiteInstance. Delete any existing ones.
diff --git a/content/browser/frame_host/render_frame_host_manager.h b/content/browser/frame_host/render_frame_host_manager.h
index ec0fa457..6ed19be 100644
--- a/content/browser/frame_host/render_frame_host_manager.h
+++ b/content/browser/frame_host/render_frame_host_manager.h
@@ -725,11 +725,11 @@
   // Runs the unload handler in the old RenderFrameHost, after the new
   // RenderFrameHost has committed.  |old_render_frame_host| will either be
   // deleted or put on the pending delete list during this call.
-  void SwapOutOldFrame(
+  void UnloadOldFrame(
       std::unique_ptr<RenderFrameHostImpl> old_render_frame_host);
 
   // Discards a RenderFrameHost that was never made active (for active ones
-  // SwapOutOldFrame is used instead).
+  // UnloadOldFrame is used instead).
   void DiscardUnusedFrame(
       std::unique_ptr<RenderFrameHostImpl> render_frame_host);
 
diff --git a/content/browser/frame_host/render_frame_host_manager_browsertest.cc b/content/browser/frame_host/render_frame_host_manager_browsertest.cc
index 8c5bcdc..fb2513d 100644
--- a/content/browser/frame_host/render_frame_host_manager_browsertest.cc
+++ b/content/browser/frame_host/render_frame_host_manager_browsertest.cc
@@ -272,8 +272,8 @@
   net::HostPortPair foo_host_port_;
 };
 
-// Web pages should not have script access to the swapped out page.
-IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, NoScriptAccessAfterSwapOut) {
+// Web pages should not have script access to the unloaded page.
+IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, NoScriptAccessAfterUnload) {
   StartEmbeddedServer();
 
   // Load a page with links that open in a new window.
@@ -2542,12 +2542,12 @@
 }
 
 // crbug.com/615274
-// This test ensures that after an RFH is swapped out, the associated WebUI
+// This test ensures that after an RFH is unloaded, the associated WebUI
 // instance is no longer allowed to send JavaScript messages. This is necessary
 // because WebUI currently (and unusually) always sends JavaScript messages to
 // the current main frame, rather than the RFH that owns the WebUI.
 IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
-                       WebUIJavascriptDisallowedAfterSwapOut) {
+                       WebUIJavascriptDisallowedAfterUnload) {
   StartEmbeddedServer();
 
   const GURL web_ui_url(std::string(kChromeUIScheme) + "://" +
@@ -2557,8 +2557,8 @@
   RenderFrameHostImpl* rfh =
       static_cast<WebContentsImpl*>(shell()->web_contents())->GetMainFrame();
 
-  // Set up a slow unload handler to force the RFH to linger in the swapped
-  // out but not-yet-deleted state.
+  // Set up a slow unload handler to force the RFH to linger in the unloaded
+  // but not-yet-deleted state.
   EXPECT_TRUE(
       ExecuteScript(rfh, "window.onunload=function(e){ while(1); };\n"));
 
@@ -2574,7 +2574,7 @@
   handler->AllowJavascript();
   EXPECT_TRUE(handler->IsJavascriptAllowed());
 
-  rfh->DisableSwapOutTimerForTesting();
+  rfh->DisableUnloadTimerForTesting();
   RenderFrameHostDestructionObserver rfh_observer(rfh);
 
   // Navigate, but wait for commit, not the actual load to finish.
@@ -2590,11 +2590,11 @@
       root->render_manager()->GetRenderFrameProxyHost(web_ui_site_instance));
 
   // The previous RFH should still be pending deletion, as we wait for either
-  // the SwapOut ACK or a timeout.
+  // the FrameHostMsg_Unload_ACK or a timeout.
   ASSERT_TRUE(rfh->IsRenderFrameLive());
   ASSERT_FALSE(rfh->is_active());
 
-  // We specifically want verify behavior between swap-out and RFH destruction.
+  // We specifically want verify behavior between unload and RFH destruction.
   ASSERT_FALSE(rfh_observer.deleted());
 
   EXPECT_FALSE(handler->IsJavascriptAllowed());
@@ -2632,10 +2632,10 @@
   EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile(
       process_id, file));
 
-  // Disable the swap out timer so we wait for the UpdateState message.
+  // Disable the unload timer so we wait for the UpdateState message.
   static_cast<WebContentsImpl*>(shell()->web_contents())
       ->GetMainFrame()
-      ->DisableSwapOutTimerForTesting();
+      ->DisableUnloadTimerForTesting();
 
   // Navigate to a different process and wait for the old process to exit.
   RenderProcessHostWatcher exit_observer(
@@ -2689,8 +2689,8 @@
   EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile(
       process_id, file));
 
-  // Disable the swap out timer so we wait for the UpdateState message.
-  wc->GetMainFrame()->DisableSwapOutTimerForTesting();
+  // Disable the unload timer so we wait for the UpdateState message.
+  wc->GetMainFrame()->DisableUnloadTimerForTesting();
 
   // Navigate to a different process without access to the file, and wait for
   // the old process to exit.
@@ -2840,8 +2840,8 @@
   EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile(
       process_id, file));
 
-  // Disable the swap out timer so we wait for the UpdateState message.
-  root->current_frame_host()->DisableSwapOutTimerForTesting();
+  // Disable the unload timer so we wait for the UpdateState message.
+  root->current_frame_host()->DisableUnloadTimerForTesting();
 
   // Do an in-page navigation in the child to make sure we hear a PageState with
   // the chosen file before the subframe's FrameTreeNode is deleted.  In
@@ -3326,7 +3326,7 @@
 }
 
 // Ensure that we don't crash the renderer in CreateRenderView if a proxy goes
-// away between swapout and the next navigation.  See https://crbug.com/581912.
+// away between unload and the next navigation.  See https://crbug.com/581912.
 IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
                        CreateRenderViewAfterProcessKillAndClosedProxy) {
   StartEmbeddedServer();
@@ -3352,7 +3352,7 @@
   // Navigate the first tab to a different site, and only wait for commit, not
   // load stop.
   RenderFrameHostImpl* rfh_a = root->current_frame_host();
-  rfh_a->DisableSwapOutTimerForTesting();
+  rfh_a->DisableUnloadTimerForTesting();
   SiteInstanceImpl* site_instance_a = rfh_a->GetSiteInstance();
   TestFrameNavigationObserver commit_observer(root);
   shell()->LoadURL(embedded_test_server()->GetURL("b.com", "/title2.html"));
@@ -3362,7 +3362,7 @@
   EXPECT_TRUE(root->render_manager()->GetRenderFrameProxyHost(site_instance_a));
 
   // The previous RFH should still be pending deletion, as we wait for either
-  // the SwapOut ACK or a timeout.
+  // the FrameHostMsg_Unload_ACK or a timeout.
   ASSERT_TRUE(rfh_a->IsRenderFrameLive());
   ASSERT_FALSE(rfh_a->is_active());
 
@@ -3407,7 +3407,7 @@
 }
 
 // Ensure that we don't crash in RenderViewImpl::Init if a proxy is created
-// after swapout and before navigation.  See https://crbug.com/544755.
+// after unload and before navigation.  See https://crbug.com/544755.
 IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
                        RenderViewInitAfterNewProxyAndProcessKill) {
   StartEmbeddedServer();
@@ -3424,7 +3424,7 @@
   // Navigate the tab to a different site, and only wait for commit, not load
   // stop.
   RenderFrameHostImpl* rfh_a = root->current_frame_host();
-  rfh_a->DisableSwapOutTimerForTesting();
+  rfh_a->DisableUnloadTimerForTesting();
   SiteInstanceImpl* site_instance_a = rfh_a->GetSiteInstance();
   TestFrameNavigationObserver commit_observer(root);
   shell()->LoadURL(embedded_test_server()->GetURL("b.com", "/title2.html"));
@@ -3432,11 +3432,11 @@
   EXPECT_NE(site_instance_a, shell()->web_contents()->GetSiteInstance());
 
   // The previous RFH should still be pending deletion, as we wait for either
-  // the SwapOut ACK or a timeout.
+  // the unload ACK or a timeout.
   ASSERT_TRUE(rfh_a->IsRenderFrameLive());
   ASSERT_FALSE(rfh_a->is_active());
 
-  // When the previous RFH was swapped out, it should have still gotten a
+  // When the previous RFH was unloaded, it should have still gotten a
   // replacement proxy even though it's the last active frame in the process.
   EXPECT_TRUE(root->render_manager()->GetRenderFrameProxyHost(site_instance_a));
 
@@ -3687,7 +3687,7 @@
   // Navigate the first tab to a different site and wait for the old process to
   // complete its unload handler and exit.
   RenderFrameHostImpl* rfh_a = root->current_frame_host();
-  rfh_a->DisableSwapOutTimerForTesting();
+  rfh_a->DisableUnloadTimerForTesting();
   RenderProcessHostWatcher exit_observer(
       rfh_a->GetProcess(), RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
   TestNavigationObserver commit_observer(web_contents);
@@ -3791,7 +3791,7 @@
       static_cast<WebContentsImpl*>(shell()->web_contents());
   FrameTreeNode* root = web_contents->GetFrameTree()->root();
   RenderFrameHostImpl* rfh_a = root->current_frame_host();
-  rfh_a->DisableSwapOutTimerForTesting();
+  rfh_a->DisableUnloadTimerForTesting();
 
   EXPECT_EQ(url::Origin::Create(url_a), rfh_a->GetLastCommittedOrigin());
   EXPECT_EQ(rfh_a, web_contents->GetMainFrame());
@@ -3833,7 +3833,7 @@
   EXPECT_EQ(url::Origin::Create(url_b), rfh_b->GetLastCommittedOrigin());
   FrameTreeNode* child = root->child_at(0);
   RenderFrameHostImpl* child_rfh_b = root->child_at(0)->current_frame_host();
-  child_rfh_b->DisableSwapOutTimerForTesting();
+  child_rfh_b->DisableUnloadTimerForTesting();
   EXPECT_EQ(url::Origin::Create(url_b), child_rfh_b->GetLastCommittedOrigin());
 
   // Navigate subframe to c.com.  Wait for commit but not full load, and then
@@ -5830,10 +5830,11 @@
   RenderFrameHostImpl* rfh = root->current_frame_host();
 
   // Set up an unload handler which never finishes to force |rfh| to stay
-  // around in pending delete state and never receive the swapout ACK.
+  // around in pending delete state and never receive the
+  // FrameHostMsg_Unload_ACK.
   EXPECT_TRUE(
       ExecuteScript(rfh, "window.onunload = function(e) { while(1); };\n"));
-  rfh->DisableSwapOutTimerForTesting();
+  rfh->DisableUnloadTimerForTesting();
 
   // Navigate to another page with two subframes.
   RenderFrameDeletedObserver rfh_observer(rfh);
@@ -5855,8 +5856,8 @@
   rfh->GetProcess()->Shutdown(0);
   crash_observer.Wait();
 
-  // The process kill should simulate a swapout ACK and trigger destruction of
-  // the pending delete RFH.
+  // The process kill should simulate a FrameHostMsg_Unload_ACK and trigger
+  // destruction of the pending delete RFH.
   rfh_observer.WaitUntilDeleted();
 
   // Ensure that the process kill didn't incorrectly remove subframes from the
diff --git a/content/browser/frame_host/render_frame_host_manager_unittest.cc b/content/browser/frame_host/render_frame_host_manager_unittest.cc
index f8e508c..44c6a68 100644
--- a/content/browser/frame_host/render_frame_host_manager_unittest.cc
+++ b/content/browser/frame_host/render_frame_host_manager_unittest.cc
@@ -534,10 +534,10 @@
   // site.
   ntp_rfh->GetSiteInstance()->IncrementActiveFrameCount();
 
-  // Navigate to a cross-site URL (don't swap out to keep |ntp_rfh| alive).
+  // Navigate to a cross-site URL (don't unload to keep |ntp_rfh| alive).
   auto navigation =
       NavigationSimulatorImpl::CreateBrowserInitiated(kDestUrl, contents());
-  navigation->set_drop_swap_out_ack(true);
+  navigation->set_drop_unload_ack(true);
   navigation->Commit();
   TestRenderFrameHost* dest_rfh = contents()->GetMainFrame();
   ASSERT_TRUE(dest_rfh);
@@ -562,10 +562,10 @@
 }
 
 // Test that the FrameHostMsg_UpdateFaviconURL IPC message is ignored if the
-// renderer is in the STATE_PENDING_SWAP_OUT_STATE. The favicon code assumes
+// renderer is in the pending deletion state. The favicon code assumes
 // that it only gets FrameHostMsg_UpdateFaviconURL messages for the most
 // recently committed navigation for each WebContentsImpl.
-TEST_F(RenderFrameHostManagerTest, UpdateFaviconURLWhilePendingSwapOut) {
+TEST_F(RenderFrameHostManagerTest, UpdateFaviconURLWhilePendingUnload) {
   const GURL kChromeURL(GetWebUIURL("foo"));
   const GURL kDestUrl("http://www.google.com/");
   std::vector<FaviconURL> icons;
@@ -589,13 +589,13 @@
   // Navigate to a cross-site URL and commit the new page.
   auto navigation =
       NavigationSimulatorImpl::CreateBrowserInitiated(kDestUrl, contents());
-  navigation->set_drop_swap_out_ack(true);
+  navigation->set_drop_unload_ack(true);
   navigation->Commit();
   TestRenderFrameHost* dest_rfh = contents()->GetMainFrame();
   EXPECT_FALSE(ntp_rfh->is_active());
   EXPECT_TRUE(dest_rfh->is_active());
 
-  // The new RVH should be able to update its favicons.
+  // The new RFH should be able to update its favicons.
   {
     PluginFaviconMessageObserver observer(contents());
     EXPECT_TRUE(dest_rfh->OnMessageReceived(
@@ -1124,11 +1124,11 @@
   EXPECT_FALSE(main_test_rfh()->web_ui());
 }
 
-// Ensure that we can go back and forward even if a SwapOut ACK isn't received.
+// Ensure that we can go back and forward even if a unload ACK isn't received.
 // See http://crbug.com/93427.
-TEST_F(RenderFrameHostManagerTest, NavigateAfterMissingSwapOutACK) {
+TEST_F(RenderFrameHostManagerTest, NavigateAfterMissingUnloadACK) {
   // When a page enters the BackForwardCache, the RenderFrameHost is not
-  // deleted.  Similarly, no SwapOutACK message is sent.
+  // deleted.  Similarly, no Unload_ACK message is sent.
   contents()->GetController().GetBackForwardCache().DisableForTesting(
       BackForwardCache::TEST_ASSUMES_NO_CACHING);
   const GURL kUrl1("http://www.google.com/");
@@ -1138,15 +1138,15 @@
   contents()->NavigateAndCommit(kUrl1);
   TestRenderFrameHost* rfh1 = main_test_rfh();
 
-  // Keep active_frame_count nonzero so that no swapped out frames in
-  // this SiteInstance get forcefully deleted.
+  // Keep active_frame_count nonzero so that no unloaded frames in this
+  // SiteInstance get forcefully deleted.
   rfh1->GetSiteInstance()->IncrementActiveFrameCount();
 
   contents()->NavigateAndCommit(kUrl2);
   TestRenderFrameHost* rfh2 = main_test_rfh();
   rfh2->GetSiteInstance()->IncrementActiveFrameCount();
 
-  // Now go back, but suppose the SwapOut_ACK isn't received.  This shouldn't
+  // Now go back, but suppose the Unload_ACK isn't received.  This shouldn't
   // happen, but we have seen it when going back quickly across many entries
   // (http://crbug.com/93427).
   auto back_navigation1 =
@@ -1155,7 +1155,7 @@
   EXPECT_FALSE(rfh2->is_waiting_for_beforeunload_ack());
 
   // The back navigation commits.
-  back_navigation1->set_drop_swap_out_ack(true);
+  back_navigation1->set_drop_unload_ack(true);
   back_navigation1->Commit();
   EXPECT_TRUE(rfh2->IsWaitingForUnloadACK());
   EXPECT_FALSE(rfh2->is_active());
@@ -1165,10 +1165,10 @@
   EXPECT_TRUE(main_test_rfh()->is_active());
 }
 
-// Test that we create swapped out RFHs for the opener chain when navigating an
-// opened tab cross-process.  This allows us to support certain cross-process
-// JavaScript calls (http://crbug.com/99202).
-TEST_F(RenderFrameHostManagerTest, CreateSwappedOutOpenerRFHs) {
+// Test that we create RenderFrameProxy objects for the opener chain when
+// navigating an opened tab cross-process.  This allows us to support certain
+// cross-process JavaScript calls (http://crbug.com/99202).
+TEST_F(RenderFrameHostManagerTest, CreateProxiesForOpeners) {
   const GURL kUrl1("http://www.google.com/");
   const GURL kUrl2 = isolated_cross_site_url();
   const GURL kChromeUrl(GetWebUIURL("foo"));
@@ -1618,12 +1618,13 @@
   EXPECT_TRUE(close_delegate.is_closed());
 }
 
-// Tests that the RenderFrameHost is properly deleted when the SwapOutACK is
-// received.  (SwapOut and the corresponding ACK always occur after commit.)
-// Also tests that an early SwapOutACK is properly ignored.
-TEST_F(RenderFrameHostManagerTest, DeleteFrameAfterSwapOutACK) {
+// Tests that the RenderFrameHost is properly deleted when the
+// FrameHostMsg_Unload_ACK is received. (UnfreezableFrameMsg_Unload and the
+// corresponding FrameHostMsg_Unload_ACK always occur after commit.)
+// Also tests that an early FrameHostMsg_Unload_ACK is properly ignored.
+TEST_F(RenderFrameHostManagerTest, DeleteFrameAfterUnloadACK) {
   // When a page enters the BackForwardCache, the RenderFrameHost is not
-  // deleted.  Similarly, no SwapOutACK message is sent.
+  // deleted.  Similarly, no Unload_ACK message is sent.
   contents()->GetController().GetBackForwardCache().DisableForTesting(
       BackForwardCache::TEST_ASSUMES_NO_CACHING);
   const GURL kUrl1("http://www.google.com/");
@@ -1643,14 +1644,14 @@
   EXPECT_TRUE(rfh1->is_active());
   TestRenderFrameHost* rfh2 = contents()->GetPendingMainFrame();
 
-  // Simulate the swap out ack, unexpectedly early (before commit).  It should
+  // Simulate the unload ack, unexpectedly early (before commit).  It should
   // have no effect.
-  rfh1->SimulateSwapOutACK();
+  rfh1->SimulateUnloadACK();
   EXPECT_TRUE(contents()->CrossProcessNavigationPending());
   EXPECT_TRUE(rfh1->is_active());
 
   // The new page commits.
-  navigation->set_drop_swap_out_ack(true);
+  navigation->set_drop_unload_ack(true);
   navigation->Commit();
   EXPECT_FALSE(contents()->CrossProcessNavigationPending());
   EXPECT_EQ(rfh2, contents()->GetMainFrame());
@@ -1658,19 +1659,20 @@
   EXPECT_TRUE(rfh2->is_active());
   EXPECT_FALSE(rfh1->is_active());
 
-  // Simulate the swap out ack.
-  rfh1->SimulateSwapOutACK();
+  // Simulate the unload ack.
+  rfh1->SimulateUnloadACK();
 
   // rfh1 should have been deleted.
   EXPECT_TRUE(rfh_deleted_observer.deleted());
   rfh1 = nullptr;
 }
 
-// Tests that the RenderFrameHost is properly swapped out when the SwapOut ACK
-// is received.  (SwapOut and the corresponding ACK always occur after commit.)
-TEST_F(RenderFrameHostManagerTest, SwapOutFrameAfterSwapOutACK) {
+// Tests that the RenderFrameHost is properly unloaded when the
+// FrameHostMsg_Unload_ACK is received. (UnfreezableFrameMsg_Unload and the
+// corresponding FrameHostMsg_Unload_ACK always occur after commit.)
+TEST_F(RenderFrameHostManagerTest, UnloadFrameAfterUnloadACK) {
   // When a page enters the BackForwardCache, the RenderFrameHost is not
-  // deleted.  Similarly, no SwapOutACK message is sent.
+  // deleted.  Similarly, no Unload_ACK message is sent.
   contents()->GetController().GetBackForwardCache().DisableForTesting(
       BackForwardCache::TEST_ASSUMES_NO_CACHING);
   const GURL kUrl1("http://www.google.com/");
@@ -1683,7 +1685,7 @@
   EXPECT_TRUE(rfh1->is_active());
 
   // Increment the number of active frames in SiteInstanceImpl so that rfh1 is
-  // not deleted on swap out.
+  // not deleted on unload.
   rfh1->GetSiteInstance()->IncrementActiveFrameCount();
 
   // Navigate to new site, simulating onbeforeunload approval.
@@ -1695,7 +1697,7 @@
   TestRenderFrameHost* rfh2 = contents()->GetPendingMainFrame();
 
   // The new page commits.
-  navigation->set_drop_swap_out_ack(true);
+  navigation->set_drop_unload_ack(true);
   navigation->Commit();
   EXPECT_FALSE(contents()->CrossProcessNavigationPending());
   EXPECT_EQ(rfh2, contents()->GetMainFrame());
@@ -1703,20 +1705,21 @@
   EXPECT_FALSE(rfh1->is_active());
   EXPECT_TRUE(rfh2->is_active());
 
-  // Simulate the swap out ack.
-  rfh1->OnSwappedOut();
+  // Simulate the unload ack.
+  rfh1->OnUnloaded();
 
   // rfh1 should be deleted.
   EXPECT_TRUE(rfh_deleted_observer.deleted());
 }
 
 // Test that the RenderViewHost is properly swapped out if a navigation in the
-// new renderer commits before sending the SwapOut message to the old renderer.
+// new renderer commits before sending the UnfreezableFrameMsg_Unload message to
+// the old renderer.
 // This simulates a cross-site navigation to a synchronously committing URL
 // (e.g., a data URL) and ensures it works properly.
-TEST_F(RenderFrameHostManagerTest, CommitNewNavigationBeforeSendingSwapOut) {
+TEST_F(RenderFrameHostManagerTest, CommitNewNavigationBeforeSendingUnload) {
   // When a page enters the BackForwardCache, the RenderFrameHost is not
-  // deleted.  Similarly, no SwapOutACK message is sent.
+  // deleted.  Similarly, no Unload_ACK message is sent.
   contents()->GetController().GetBackForwardCache().DisableForTesting(
       BackForwardCache::TEST_ASSUMES_NO_CACHING);
   const GURL kUrl1("http://www.google.com/");
@@ -1729,7 +1732,7 @@
   EXPECT_TRUE(rfh1->is_active());
 
   // Increment the number of active frames in SiteInstanceImpl so that rfh1 is
-  // not deleted on swap out.
+  // not deleted on unload.
   scoped_refptr<SiteInstanceImpl> site_instance = rfh1->GetSiteInstance();
   site_instance->IncrementActiveFrameCount();
 
@@ -1742,7 +1745,7 @@
   TestRenderFrameHost* rfh2 = contents()->GetPendingMainFrame();
 
   // The new page commits.
-  navigation->set_drop_swap_out_ack(true);
+  navigation->set_drop_unload_ack(true);
   navigation->Commit();
   EXPECT_FALSE(contents()->CrossProcessNavigationPending());
   EXPECT_EQ(rfh2, contents()->GetMainFrame());
@@ -1750,8 +1753,8 @@
   EXPECT_FALSE(rfh1->is_active());
   EXPECT_TRUE(rfh2->is_active());
 
-  // Simulate the swap out ack.
-  rfh1->OnSwappedOut();
+  // Simulate the unload ack.
+  rfh1->OnUnloaded();
 
   // rfh1 should be deleted.
   EXPECT_TRUE(rfh_deleted_observer.deleted());
@@ -3071,7 +3074,7 @@
   // should be pending delete.
   auto navigation_to_kUrl2 =
       NavigationSimulatorImpl::CreateBrowserInitiated(kUrl2, contents());
-  navigation_to_kUrl2->set_drop_swap_out_ack(true);
+  navigation_to_kUrl2->set_drop_unload_ack(true);
   navigation_to_kUrl2->Commit();
   EXPECT_NE(initial_rfh, main_test_rfh());
   ASSERT_FALSE(delete_observer.deleted());
diff --git a/content/browser/scheduler/browser_io_thread_delegate.cc b/content/browser/scheduler/browser_io_thread_delegate.cc
index 5f13084..cac4e866 100644
--- a/content/browser/scheduler/browser_io_thread_delegate.cc
+++ b/content/browser/scheduler/browser_io_thread_delegate.cc
@@ -112,10 +112,4 @@
   }
 }
 
-const scoped_refptr<base::SequencedTaskRunner>&
-BrowserIOThreadDelegate::GetTaskRunnerForCurrentTask() const {
-  DCHECK(sequence_manager_);
-  return sequence_manager_->GetTaskRunnerForCurrentTask();
-}
-
 }  // namespace content
diff --git a/content/browser/scheduler/browser_io_thread_delegate.h b/content/browser/scheduler/browser_io_thread_delegate.h
index 69699369..0954c5e 100644
--- a/content/browser/scheduler/browser_io_thread_delegate.h
+++ b/content/browser/scheduler/browser_io_thread_delegate.h
@@ -55,10 +55,6 @@
 
   scoped_refptr<Handle> GetHandle() { return task_queues_->GetHandle(); }
 
-  // Must be called on the IO thread.
-  const scoped_refptr<base::SequencedTaskRunner>& GetTaskRunnerForCurrentTask()
-      const;
-
  private:
   class TLSMultiplexer;
 
diff --git a/content/browser/scheduler/browser_task_executor.cc b/content/browser/scheduler/browser_task_executor.cc
index 3f8a9f2..ec6b138 100644
--- a/content/browser/scheduler/browser_task_executor.cc
+++ b/content/browser/scheduler/browser_task_executor.cc
@@ -65,12 +65,6 @@
   }
 }
 
-const scoped_refptr<base::SequencedTaskRunner>& GetNullTaskRunner() {
-  static const base::NoDestructor<scoped_refptr<base::SequencedTaskRunner>>
-      null_task_runner;
-  return *null_task_runner;
-}
-
 }  // namespace
 
 BaseBrowserTaskExecutor::BaseBrowserTaskExecutor() = default;
@@ -398,13 +392,6 @@
   return BrowserThread::UI;
 }
 
-const scoped_refptr<base::SequencedTaskRunner>&
-BrowserTaskExecutor::GetContinuationTaskRunner() {
-  NOTREACHED()
-      << "Should have been routed to UIThreadExecutor or IOThreadExecutor";
-  return GetNullTaskRunner();
-}
-
 BrowserTaskExecutor::UIThreadExecutor::UIThreadExecutor(
     std::unique_ptr<BrowserUIThreadScheduler> browser_ui_thread_scheduler)
     : browser_ui_thread_scheduler_(std::move(browser_ui_thread_scheduler)) {
@@ -436,11 +423,6 @@
   return BrowserThread::UI;
 }
 
-const scoped_refptr<base::SequencedTaskRunner>&
-BrowserTaskExecutor::UIThreadExecutor::GetContinuationTaskRunner() {
-  return browser_ui_thread_scheduler_->GetTaskRunnerForCurrentTask();
-}
-
 BrowserTaskExecutor::IOThreadExecutor::IOThreadExecutor(
     std::unique_ptr<BrowserIOThreadDelegate> browser_io_thread_delegate)
     : browser_io_thread_delegate_(std::move(browser_io_thread_delegate)) {
@@ -468,10 +450,4 @@
   return BrowserThread::IO;
 }
 
-const scoped_refptr<base::SequencedTaskRunner>&
-BrowserTaskExecutor::IOThreadExecutor::GetContinuationTaskRunner() {
-  DCHECK(browser_io_thread_delegate_);
-  return browser_io_thread_delegate_->GetTaskRunnerForCurrentTask();
-}
-
 }  // namespace content
diff --git a/content/browser/scheduler/browser_task_executor.h b/content/browser/scheduler/browser_task_executor.h
index af97160..65ec34c2 100644
--- a/content/browser/scheduler/browser_task_executor.h
+++ b/content/browser/scheduler/browser_task_executor.h
@@ -173,10 +173,6 @@
                               BrowserTaskQueues::Validator* validator);
 #endif  // DCHECK_IS_ON()
 
-  // base::TaskExecutor implementation.
-  const scoped_refptr<base::SequencedTaskRunner>& GetContinuationTaskRunner()
-      override;
-
  private:
   friend class BrowserIOThreadDelegate;
   friend class BrowserTaskExecutorTest;
@@ -190,10 +186,6 @@
 
     ~UIThreadExecutor() override;
 
-    // base::TaskExecutor implementation.
-    const scoped_refptr<base::SequencedTaskRunner>& GetContinuationTaskRunner()
-        override;
-
     scoped_refptr<BrowserUIThreadScheduler::Handle> GetUIThreadHandle();
 
     void SetIOThreadHandle(
@@ -217,10 +209,6 @@
 
     ~IOThreadExecutor() override;
 
-    // base::TaskExecutor implementation.
-    const scoped_refptr<base::SequencedTaskRunner>& GetContinuationTaskRunner()
-        override;
-
     scoped_refptr<BrowserUIThreadScheduler::Handle> GetIOThreadHandle();
 
     void SetUIThreadHandle(
diff --git a/content/browser/scheduler/browser_task_executor_unittest.cc b/content/browser/scheduler/browser_task_executor_unittest.cc
index 5dbac91..9ef50ad 100644
--- a/content/browser/scheduler/browser_task_executor_unittest.cc
+++ b/content/browser/scheduler/browser_task_executor_unittest.cc
@@ -15,7 +15,6 @@
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool/thread_pool_instance.h"
 #include "base/test/bind_test_util.h"
-#include "base/test/gtest_util.h"
 #include "base/test/mock_callback.h"
 #include "base/test/task_environment.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -133,14 +132,6 @@
       NOTREACHED();
       return BrowserThread::UI;
     }
-
-    const scoped_refptr<base::SequencedTaskRunner>& GetContinuationTaskRunner()
-        override {
-      return dummy_;
-    }
-
-   private:
-    scoped_refptr<base::SequencedTaskRunner> dummy_;
   };
 
   template <BrowserThread::ID ID>
@@ -319,35 +310,4 @@
                 {base::CurrentThread(), BrowserTaskType::kNavigation}));
 }
 
-TEST_F(BrowserTaskExecutorTest, GetContinuationTaskRunner) {
-  // Ensure task queue priorities are set.
-  BrowserTaskExecutor::PostFeatureListSetup();
-  std::vector<int> order;
-  base::RunLoop run_loop;
-
-  auto task1 = base::BindLambdaForTesting([&]() {
-    order.push_back(1);
-    run_loop.Quit();
-  });
-  auto task2 = base::BindLambdaForTesting([&]() { order.push_back(2); });
-  auto task3 = base::BindLambdaForTesting([&]() { order.push_back(3); });
-
-  base::PostTask(FROM_HERE, {BrowserThread::UI}, task1);
-
-  // Post a bootstrap task whose continuation tasks should run before |task1|.
-  base::PostTask(
-      FROM_HERE, {BrowserThread::UI, BrowserTaskType::kBootstrap},
-      base::BindLambdaForTesting([&]() {
-        base::GetContinuationTaskRunner()->PostTask(FROM_HERE, task2);
-        base::GetContinuationTaskRunner()->PostTask(FROM_HERE, task3);
-      }));
-
-  run_loop.Run();
-  EXPECT_THAT(order, ElementsAre(2, 3, 1));
-}
-
-TEST_F(BrowserTaskExecutorTest, GetContinuationTaskRunnerWithNoTaskExecuting) {
-  EXPECT_DCHECK_DEATH(base::GetContinuationTaskRunner());
-}
-
 }  // namespace content
diff --git a/content/browser/scheduler/browser_ui_thread_scheduler.cc b/content/browser/scheduler/browser_ui_thread_scheduler.cc
index 81d6a51..0925f8f 100644
--- a/content/browser/scheduler/browser_ui_thread_scheduler.cc
+++ b/content/browser/scheduler/browser_ui_thread_scheduler.cc
@@ -63,14 +63,7 @@
 
 void BrowserUIThreadScheduler::CommonSequenceManagerSetup(
     base::sequence_manager::SequenceManager* sequence_manager) {
-  sequence_manager_ = sequence_manager;
-  sequence_manager_->EnableCrashKeys("ui_scheduler_async_stack");
-}
-
-const scoped_refptr<base::SequencedTaskRunner>&
-BrowserUIThreadScheduler::GetTaskRunnerForCurrentTask() const {
-  DCHECK(sequence_manager_);
-  return sequence_manager_->GetTaskRunnerForCurrentTask();
+  sequence_manager->EnableCrashKeys("ui_scheduler_async_stack");
 }
 
 }  // namespace content
diff --git a/content/browser/scheduler/browser_ui_thread_scheduler.h b/content/browser/scheduler/browser_ui_thread_scheduler.h
index 88a3e14e..45a7d31 100644
--- a/content/browser/scheduler/browser_ui_thread_scheduler.h
+++ b/content/browser/scheduler/browser_ui_thread_scheduler.h
@@ -40,10 +40,6 @@
 
   scoped_refptr<Handle> GetHandle() const { return handle_; }
 
-  // Must be called on the UI thread.
-  const scoped_refptr<base::SequencedTaskRunner>& GetTaskRunnerForCurrentTask()
-      const;
-
  private:
   friend class BrowserTaskExecutor;
 
@@ -59,7 +55,6 @@
   std::unique_ptr<base::sequence_manager::SequenceManager>
       owned_sequence_manager_;
 
-  base::sequence_manager::SequenceManager* sequence_manager_ = nullptr;
   BrowserTaskQueues task_queues_;
   scoped_refptr<Handle> handle_;
 
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index b5f72bf0..9896820 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -5594,19 +5594,19 @@
 }
 
 // Test for https://crbug.com/515302. Perform two navigations, A1 -> B2 -> A3,
-// and drop the SwapOut ACK from the A1 -> B2 navigation, so that the second
-// B2 -> A3 navigation is initiated before the first page receives the SwapOut
-// ACK. Ensure that this doesn't crash and that the RVH(A1) is not reused in
-// that case.
+// and drop the FrameHostMsg_Unload_ACK from the A1 -> B2 navigation, so that
+// the second B2 -> A3 navigation is initiated before the first page receives
+// the FrameHostMsg_Unload_ACK. Ensure that this doesn't crash and that the
+// RVH(A1) is not reused in that case.
 #if defined(OS_MACOSX)
-#define MAYBE_RenderViewHostIsNotReusedAfterDelayedSwapOutACK \
-  DISABLED_RenderViewHostIsNotReusedAfterDelayedSwapOutACK
+#define MAYBE_RenderViewHostIsNotReusedAfterDelayedUnloadACK \
+  DISABLED_RenderViewHostIsNotReusedAfterDelayedUnloadACK
 #else
-#define MAYBE_RenderViewHostIsNotReusedAfterDelayedSwapOutACK \
-  RenderViewHostIsNotReusedAfterDelayedSwapOutACK
+#define MAYBE_RenderViewHostIsNotReusedAfterDelayedUnloadACK \
+  RenderViewHostIsNotReusedAfterDelayedUnloadACK
 #endif
 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
-                       MAYBE_RenderViewHostIsNotReusedAfterDelayedSwapOutACK) {
+                       MAYBE_RenderViewHostIsNotReusedAfterDelayedUnloadACK) {
   GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
   EXPECT_TRUE(NavigateToURL(shell(), a_url));
 
@@ -5618,15 +5618,15 @@
   SiteInstanceImpl* site_instance = rfh->GetSiteInstance();
   RenderFrameDeletedObserver deleted_observer(rfh);
 
-  // Install a BrowserMessageFilter to drop SwapOut ACK messages in A's
-  // process.
+  // Install a BrowserMessageFilter to drop FrameHostMsg_Unload_ACK messages in
+  // A's process.
   auto filter = base::MakeRefCounted<DropMessageFilter>(
-      FrameMsgStart, FrameHostMsg_SwapOut_ACK::ID);
+      FrameMsgStart, FrameHostMsg_Unload_ACK::ID);
   rfh->GetProcess()->AddFilter(filter.get());
-  rfh->DisableSwapOutTimerForTesting();
+  rfh->DisableUnloadTimerForTesting();
 
   // Navigate to B.  This must wait for DidCommitProvisionalLoad and not
-  // DidStopLoading, so that the SwapOut timer doesn't call OnSwappedOut and
+  // DidStopLoading, so that the Unload timer doesn't call OnUnloaded and
   // destroy |rfh| and |rvh| before they are checked in the test.
   GURL b_url(embedded_test_server()->GetURL("b.com", "/title2.html"));
   TestFrameNavigationObserver commit_observer(root);
@@ -5634,12 +5634,12 @@
   commit_observer.WaitForCommit();
   EXPECT_FALSE(deleted_observer.deleted());
 
-  // Since the SwapOut ACK for A->B is dropped, the first page's
+  // Since the FrameHostMsg_Unload_ACK for A->B is dropped, the first page's
   // RenderFrameHost should be pending deletion after the last navigation.
   EXPECT_FALSE(rfh->is_active());
 
-  // Without the SwapOut ACK and timer, the process A will never shutdown.
-  // Simulate the process being killed now.
+  // Without the FrameHostMsg_Unload_ACK and timer, the process A will never
+  // shutdown. Simulate the process being killed now.
   content::RenderProcessHostWatcher crash_observer(
       rvh->GetProcess(),
       content::RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
@@ -8485,7 +8485,7 @@
     commit_observer.WaitForCommit();
   }
   RenderFrameHostImpl* child_rfh = child->current_frame_host();
-  child_rfh->DisableSwapOutTimerForTesting();
+  child_rfh->DisableUnloadTimerForTesting();
 
   // At this point, the subframe should have a proxy in its parent's
   // SiteInstance, a.com.
@@ -9100,13 +9100,13 @@
   EXPECT_TRUE(NavigateToURL(
       shell(), embedded_test_server()->GetURL("a.com", "/title1.html")));
 
-  // Disable the swapout ACK and the swapout timer.
+  // Disable the unload ACK and the unload timer.
   RenderFrameHostImpl* rfh = static_cast<RenderFrameHostImpl*>(
       shell()->web_contents()->GetMainFrame());
   auto filter = base::MakeRefCounted<DropMessageFilter>(
-      FrameMsgStart, FrameHostMsg_SwapOut_ACK::ID);
+      FrameMsgStart, FrameHostMsg_Unload_ACK::ID);
   rfh->GetProcess()->AddFilter(filter.get());
-  rfh->DisableSwapOutTimerForTesting();
+  rfh->DisableUnloadTimerForTesting();
 
   // Open a popup on a.com to keep the process alive.
   OpenPopup(shell(), embedded_test_server()->GetURL("a.com", "/title2.html"),
@@ -9117,8 +9117,8 @@
       shell(), embedded_test_server()->GetURL("b.com", "/title3.html")));
 
   // Pretend that a.com just requested a context menu. This used to cause a
-  // because the RenderWidgetHostView is destroyed when the frame is swapped and
-  // added to pending delete list.
+  // because the RenderWidgetHostView is destroyed when the frame is unloaded
+  // and added to pending delete list.
   rfh->OnMessageReceived(
       FrameHostMsg_ContextMenu(rfh->GetRoutingID(), ContextMenuParams()));
 }
@@ -11287,9 +11287,9 @@
             root->child_at(0)->effective_frame_policy().sandbox_flags);
 }
 
-// Test that after an RFH is swapped out, its old sandbox flags remain active.
+// Test that after an RFH is unloaded, its old sandbox flags remain active.
 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
-                       ActiveSandboxFlagsRetainedAfterSwapOut) {
+                       ActiveSandboxFlagsRetainedAfterUnload) {
   GURL main_url(embedded_test_server()->GetURL(
       "a.com", "/sandboxed_main_frame_script.html"));
   EXPECT_TRUE(NavigateToURL(shell(), main_url));
@@ -11309,12 +11309,12 @@
                 ~blink::WebSandboxFlags::kAutomaticFeatures,
             rfh->active_sandbox_flags());
 
-  // Set up a slow unload handler to force the RFH to linger in the swapped
-  // out but not-yet-deleted state.
+  // Set up a slow unload handler to force the RFH to linger in the unloaded but
+  // not-yet-deleted state.
   EXPECT_TRUE(
       ExecuteScript(rfh, "window.onunload=function(e){ while(1); };\n"));
 
-  rfh->DisableSwapOutTimerForTesting();
+  rfh->DisableUnloadTimerForTesting();
   RenderFrameDeletedObserver rfh_observer(rfh);
 
   // Navigate to a page with no sandbox, but wait for commit, not for the actual
@@ -11325,7 +11325,7 @@
   commit_observer.WaitForCommit();
 
   // The previous RFH should still be pending deletion, as we wait for either
-  // the SwapOut ACK or a timeout.
+  // the FrameHostMsg_Unload_ACK or a timeout.
   ASSERT_TRUE(rfh->IsRenderFrameLive());
   ASSERT_FALSE(rfh->is_active());
   ASSERT_FALSE(rfh_observer.deleted());
@@ -11922,11 +11922,12 @@
 
   EXPECT_GT(height, 0);
 }
-// Test that a late swapout ACK won't incorrectly mark RenderViewHost as
-// inactive if it's already been reused and switched to active by another
-// navigation.  See https://crbug.com/823567.
+
+// Test that a late FrameHostMsg_Unload_ACK won't incorrectly mark
+// RenderViewHost as inactive if it's already been reused and switched to active
+// by another navigation.  See https://crbug.com/823567.
 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
-                       RenderViewHostStaysActiveWithLateSwapoutACK) {
+                       RenderViewHostStaysActiveWithLateUnloadACK) {
   EXPECT_TRUE(NavigateToURL(
       shell(), embedded_test_server()->GetURL("a.com", "/title1.html")));
 
@@ -11938,11 +11939,11 @@
   RenderFrameHostImpl* rfh = popup_contents->GetMainFrame();
   RenderViewHostImpl* rvh = rfh->render_view_host();
 
-  // Disable the swapout ACK and the swapout timer.
+  // Disable the unload ACK and the unload timer.
   auto filter = base::MakeRefCounted<DropMessageFilter>(
-      FrameMsgStart, FrameHostMsg_SwapOut_ACK::ID);
+      FrameMsgStart, FrameHostMsg_Unload_ACK::ID);
   rfh->GetProcess()->AddFilter(filter.get());
-  rfh->DisableSwapOutTimerForTesting();
+  rfh->DisableUnloadTimerForTesting();
 
   // Navigate popup to b.com.  Because there's an opener, the RVH for a.com
   // stays around in swapped-out state.
@@ -11968,8 +11969,8 @@
   popup_contents->GetController().GoBack();
 
   // Pretend that the original RFH in a.com now finishes running its unload
-  // handler and sends the swapout ACK.
-  rfh->OnSwappedOut();
+  // handler and sends the FrameHostMsg_Unload_ACK.
+  rfh->OnUnloaded();
 
   // Wait for the new a.com navigation to finish.
   back_observer.Wait();
@@ -12325,18 +12326,18 @@
 // Tests that the last committed URL is preserved on an RFH even after the RFH
 // goes into the pending deletion state.
 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
-                       LastCommittedURLRetainedAfterSwapOut) {
+                       LastCommittedURLRetainedAfterUnload) {
   // Navigate to a.com.
   GURL start_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
   EXPECT_TRUE(NavigateToURL(shell(), start_url));
   RenderFrameHostImpl* rfh = web_contents()->GetMainFrame();
   EXPECT_EQ(start_url, rfh->GetLastCommittedURL());
 
-  // Disable the swapout ACK and the swapout timer.
+  // Disable the unload ACK and the unload timer.
   auto filter = base::MakeRefCounted<DropMessageFilter>(
-      FrameMsgStart, FrameHostMsg_SwapOut_ACK::ID);
+      FrameMsgStart, FrameHostMsg_Unload_ACK::ID);
   rfh->GetProcess()->AddFilter(filter.get());
-  rfh->DisableSwapOutTimerForTesting();
+  rfh->DisableUnloadTimerForTesting();
 
   // Open a popup on a.com to keep the process alive.
   OpenPopup(shell(), embedded_test_server()->GetURL("a.com", "/title2.html"),
@@ -13924,8 +13925,8 @@
           ->root();
   auto* rfh = popup_root->current_frame_host();
 
-  // Disable the swapout timer to prevent flakiness.
-  rfh->DisableSwapOutTimerForTesting();
+  // Disable the unload timer to prevent flakiness.
+  rfh->DisableUnloadTimerForTesting();
 
   // This will be used to monitor that b.com process exits cleanly.
   RenderProcessHostWatcher b_process_observer(
@@ -13957,8 +13958,8 @@
           opener.postMessage('hi','*');
       })"));
 
-  // Navigate popup to a.com.  This swaps out the last active frame in the
-  // b.com process, and hence initiates process shutdown.
+  // Navigate popup to a.com.  This unloads the last active frame in the b.com
+  // process, and hence initiates process shutdown.
   TestFrameNavigationObserver commit_observer(popup_root);
   GURL another_a_url(embedded_test_server()->GetURL("a.com", "/title3.html"));
   EXPECT_TRUE(
@@ -13971,7 +13972,7 @@
   // When the opener receives a postMessage from the popup's unload handler, it
   // should start a navigation back to b.com.  Wait for it.  This navigation
   // creates a speculative RFH which reuses the proxy that was created as part
-  // of swapping out from |popup_url| to |another_a_url|.
+  // of navigating from |popup_url| to |another_a_url|.
   EXPECT_TRUE(manager.WaitForRequestStart());
 
   // Cancel the started navigation (to /hung) in the popup and make sure the
diff --git a/content/browser/site_per_process_unload_browsertest.cc b/content/browser/site_per_process_unload_browsertest.cc
index 0923eb7..68f48bc 100644
--- a/content/browser/site_per_process_unload_browsertest.cc
+++ b/content/browser/site_per_process_unload_browsertest.cc
@@ -283,30 +283,31 @@
 }
 
 // Verify that when the last active frame in a process is going away as part of
-// OnSwapOut, the SwapOut ACK is received prior to the process starting to shut
-// down, ensuring that any related unload work also happens before shutdown.
-// See https://crbug.com/867274 and https://crbug.com/794625.
+// OnUnload, the FrameHostMsg_Unload_ACK is received prior to the process
+// starting to shut down, ensuring that any related unload work also happens
+// before shutdown. See https://crbug.com/867274 and https://crbug.com/794625.
 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
-                       SwapOutACKArrivesPriorToProcessShutdownRequest) {
+                       UnloadACKArrivesPriorToProcessShutdownRequest) {
   GURL start_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
   EXPECT_TRUE(NavigateToURL(shell(), start_url));
   RenderFrameHostImpl* rfh = web_contents()->GetMainFrame();
-  rfh->DisableSwapOutTimerForTesting();
+  rfh->DisableUnloadTimerForTesting();
 
   // Navigate cross-site.  Since the current frame is the last active frame in
   // the current process, the process will eventually shut down.  Once the
-  // process goes away, ensure that the SwapOut ACK was received (i.e., that we
-  // didn't just simulate OnSwappedOut() due to the process erroneously going
-  // away before the SwapOut ACK was received, as in https://crbug.com/867274).
+  // process goes away, ensure that the FrameHostMsg_Unload_ACK was received
+  // (i.e., that we didn't just simulate OnUnloaded() due to the process
+  // erroneously going away before the FrameHostMsg_Unload_ACK was received, as
+  // in https://crbug.com/867274).
   RenderProcessHostWatcher watcher(
       rfh->GetProcess(), RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
-  auto swapout_ack_filter = base::MakeRefCounted<ObserveMessageFilter>(
-      FrameMsgStart, FrameHostMsg_SwapOut_ACK::ID);
-  rfh->GetProcess()->AddFilter(swapout_ack_filter.get());
+  auto unload_ack_filter = base::MakeRefCounted<ObserveMessageFilter>(
+      FrameMsgStart, FrameHostMsg_Unload_ACK::ID);
+  rfh->GetProcess()->AddFilter(unload_ack_filter.get());
   GURL cross_site_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
   EXPECT_TRUE(NavigateToURLFromRenderer(shell(), cross_site_url));
   watcher.Wait();
-  EXPECT_TRUE(swapout_ack_filter->has_received_message());
+  EXPECT_TRUE(unload_ack_filter->has_received_message());
   EXPECT_TRUE(watcher.did_exit_normally());
 }
 
@@ -405,8 +406,8 @@
   DOMMessageQueue dom_message_queue(
       WebContents::FromRenderFrameHost(web_contents()->GetMainFrame()));
 
-  // Disable the swap out timer on B1.
-  root->child_at(0)->current_frame_host()->DisableSwapOutTimerForTesting();
+  // Disable the unload timer on B1.
+  root->child_at(0)->current_frame_host()->DisableUnloadTimerForTesting();
 
   // Process B and C are expected to shutdown once every unload handler has
   // run.
@@ -499,7 +500,7 @@
   UnloadPrint(root->child_at(0), "B1");
   UnloadPrint(root->child_at(0)->child_at(0), "A2");
   UnloadPrint(root->child_at(0)->child_at(0)->child_at(0), "B2");
-  root->current_frame_host()->DisableSwapOutTimerForTesting();
+  root->current_frame_host()->DisableUnloadTimerForTesting();
 
   DOMMessageQueue dom_message_queue(
       WebContents::FromRenderFrameHost(web_contents()->GetMainFrame()));
@@ -563,17 +564,17 @@
   EXPECT_EQ(RenderFrameHostImpl::UnloadState::NotRun, rfh_c->unload_state_);
 
   // Act as if there was a slow unload handler on rfh_b and rfh_c.
-  // The navigating frames are waiting for FrameHostMsg_SwapoutACK.
-  auto swapout_ack_filter_b = base::MakeRefCounted<DropMessageFilter>(
-      FrameMsgStart, FrameHostMsg_SwapOut_ACK::ID);
-  auto swapout_ack_filter_c = base::MakeRefCounted<DropMessageFilter>(
-      FrameMsgStart, FrameHostMsg_SwapOut_ACK::ID);
-  rfh_b->GetProcess()->AddFilter(swapout_ack_filter_b.get());
-  rfh_c->GetProcess()->AddFilter(swapout_ack_filter_c.get());
+  // The navigating frames are waiting for FrameHostMsg_Unload_ACK.
+  auto unload_ack_filter_b = base::MakeRefCounted<DropMessageFilter>(
+      FrameMsgStart, FrameHostMsg_Unload_ACK::ID);
+  auto unload_ack_filter_c = base::MakeRefCounted<DropMessageFilter>(
+      FrameMsgStart, FrameHostMsg_Unload_ACK::ID);
+  rfh_b->GetProcess()->AddFilter(unload_ack_filter_b.get());
+  rfh_c->GetProcess()->AddFilter(unload_ack_filter_c.get());
   EXPECT_TRUE(ExecuteScript(rfh_b->frame_tree_node(), onunload_script));
   EXPECT_TRUE(ExecuteScript(rfh_c->frame_tree_node(), onunload_script));
-  rfh_b->DisableSwapOutTimerForTesting();
-  rfh_c->DisableSwapOutTimerForTesting();
+  rfh_b->DisableUnloadTimerForTesting();
+  rfh_c->DisableUnloadTimerForTesting();
 
   RenderFrameDeletedObserver delete_b(rfh_b), delete_c(rfh_c);
 
@@ -612,7 +613,7 @@
 
   // rfh_b completes its unload event.
   EXPECT_FALSE(delete_b.deleted());
-  rfh_b->OnSwapOutACK();
+  rfh_b->OnUnloadACK();
   EXPECT_TRUE(delete_b.deleted());
 }
 
@@ -639,18 +640,18 @@
   RenderFrameDeletedObserver delete_a2(a2);
   RenderFrameDeletedObserver delete_b1(b1);
 
-  // Disable Detach and Swapout ACK. They will be called manually.
-  auto swapout_ack_filter = base::MakeRefCounted<DropMessageFilter>(
-      FrameMsgStart, FrameHostMsg_SwapOut_ACK::ID);
+  // Disable Detach and FrameHostMsg_Unload_ACK. They will be called manually.
+  auto unload_ack_filter = base::MakeRefCounted<DropMessageFilter>(
+      FrameMsgStart, FrameHostMsg_Unload_ACK::ID);
   auto detach_filter_a = base::MakeRefCounted<DropMessageFilter>(
       FrameMsgStart, FrameHostMsg_Detach::ID);
   auto detach_filter_b = base::MakeRefCounted<DropMessageFilter>(
       FrameMsgStart, FrameHostMsg_Detach::ID);
-  a1->GetProcess()->AddFilter(swapout_ack_filter.get());
+  a1->GetProcess()->AddFilter(unload_ack_filter.get());
   a1->GetProcess()->AddFilter(detach_filter_a.get());
   b1->GetProcess()->AddFilter(detach_filter_b.get());
 
-  a1->DisableSwapOutTimerForTesting();
+  a1->DisableUnloadTimerForTesting();
   // Set an arbitrarily long timeout to ensure the subframe unload timer doesn't
   // fire before we call OnDetach().
   b1->SetSubframeUnloadTimeoutForTesting(base::TimeDelta::FromSeconds(30));
@@ -695,8 +696,8 @@
   EXPECT_TRUE(delete_a2.deleted());
   EXPECT_EQ(RenderFrameHostImpl::UnloadState::InProgress, a1->unload_state_);
 
-  // 5) A1 receives SwapOutACK and deletes itself.
-  a1->OnSwapOutACK();
+  // 5) A1 receives FrameHostMsg_Unload_ACK and deletes itself.
+  a1->OnUnloadACK();
   EXPECT_TRUE(delete_a1.deleted());
 }
 
@@ -764,14 +765,14 @@
   UnloadPrint(rfh_6->frame_tree_node(), "");
   UnloadPrint(rfh_14->frame_tree_node(), "");
 
-  // Disable Detach and Swapout ACK.
-  auto swapout_ack_filter = base::MakeRefCounted<DropMessageFilter>(
-      FrameMsgStart, FrameHostMsg_SwapOut_ACK::ID);
+  // Disable Detach and FrameHostMsg_Unload_ACK.
+  auto unload_ack_filter = base::MakeRefCounted<DropMessageFilter>(
+      FrameMsgStart, FrameHostMsg_Unload_ACK::ID);
   auto detach_filter = base::MakeRefCounted<DropMessageFilter>(
       FrameMsgStart, FrameHostMsg_Detach::ID);
-  rfh_0->GetProcess()->AddFilter(swapout_ack_filter.get());
+  rfh_0->GetProcess()->AddFilter(unload_ack_filter.get());
   rfh_0->GetProcess()->AddFilter(detach_filter.get());
-  rfh_0->DisableSwapOutTimerForTesting();
+  rfh_0->DisableUnloadTimerForTesting();
 
   // 2) Navigate cross process and check the tree. See diagram above.
   EXPECT_TRUE(NavigateToURL(shell(), url_2));
@@ -1009,7 +1010,7 @@
   auto detach_filter = base::MakeRefCounted<DropMessageFilter>(
       FrameMsgStart, FrameHostMsg_Detach::ID);
   node3->GetProcess()->AddFilter(detach_filter.get());
-  node2->DisableSwapOutTimerForTesting();
+  node2->DisableUnloadTimerForTesting();
   ASSERT_TRUE(ExecJs(node3, "window.onunload = ()=>{}"));
 
   // Prepare |node4| to respond to postMessage with a report of whether it can
@@ -1102,7 +1103,7 @@
   auto detach_filter = base::MakeRefCounted<DropMessageFilter>(
       FrameMsgStart, FrameHostMsg_Detach::ID);
   node3->GetProcess()->AddFilter(detach_filter.get());
-  node2->DisableSwapOutTimerForTesting();
+  node2->DisableUnloadTimerForTesting();
   ASSERT_TRUE(ExecJs(node3, "window.onunload = ()=>{}"));
 
   // Prepare |node4| to respond to postMessage with a report of whether it can
@@ -1350,9 +1351,9 @@
   RenderFrameHostImpl* A1 = web_contents()->GetMainFrame();
   RenderFrameHostImpl* B2 = A1->child_at(0)->current_frame_host();
 
-  // Increase SwapOut/Unload timeout to prevent the previous document from
+  // Increase Unload timeout to prevent the previous document from
   // being deleleted before it has finished running B2 unload handler.
-  A1->DisableSwapOutTimerForTesting();
+  A1->DisableUnloadTimerForTesting();
   B2->SetSubframeUnloadTimeoutForTesting(base::TimeDelta::FromSeconds(30));
 
   // Add an unload handler to the subframe and try in that handler to preserve
@@ -1442,9 +1443,9 @@
   RenderFrameHostImpl* B2 = A1->child_at(0)->current_frame_host();
   RenderFrameHostImpl* C3 = B2->child_at(0)->current_frame_host();
 
-  // Increase SwapOut/Unload timeout to prevent the previous document from
+  // Increase Unload timeout to prevent the previous document from
   // being deleleted before it has finished running C3 unload handler.
-  A1->DisableSwapOutTimerForTesting();
+  A1->DisableUnloadTimerForTesting();
   B2->SetSubframeUnloadTimeoutForTesting(base::TimeDelta::FromSeconds(30));
   C3->SetSubframeUnloadTimeoutForTesting(base::TimeDelta::FromSeconds(30));
 
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index f04497c1..9cf730d 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -1159,7 +1159,7 @@
   FRIEND_TEST_ALL_PREFIXES(WebContentsImplBrowserTest,
                            NotifyFullscreenAcquired_SameOrigin);
   FRIEND_TEST_ALL_PREFIXES(WebContentsImplBrowserTest,
-                           FullscreenAfterFrameSwap);
+                           FullscreenAfterFrameUnload);
   FRIEND_TEST_ALL_PREFIXES(WebContentsImplBrowserTest,
                            MaxFrameCountForCrossProcessNavigation);
   FRIEND_TEST_ALL_PREFIXES(WebContentsImplBrowserTest,
diff --git a/content/browser/web_contents/web_contents_impl_browsertest.cc b/content/browser/web_contents/web_contents_impl_browsertest.cc
index 84919144..a272ff4 100644
--- a/content/browser/web_contents/web_contents_impl_browsertest.cc
+++ b/content/browser/web_contents/web_contents_impl_browsertest.cc
@@ -2957,8 +2957,8 @@
 }
 
 // Regression test for https://crbug.com/855018.
-// RenderFrameHostImpls exit fullscreen as soon as they are swapped out.
-IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, FullscreenAfterFrameSwap) {
+// RenderFrameHostImpls exit fullscreen as soon as they are unloaded.
+IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, FullscreenAfterFrameUnload) {
   ASSERT_TRUE(embedded_test_server()->Start());
   WebContentsImpl* web_contents =
       static_cast<WebContentsImpl*>(shell()->web_contents());
@@ -2980,13 +2980,13 @@
   EXPECT_EQ(1u, web_contents->fullscreen_frames_.size());
 
   // 3) Navigate cross origin. Act as if the old frame was very slow delivering
-  //    the swapout ack and stayed in pending deletion for a while. Even if the
+  //    the unload ack and stayed in pending deletion for a while. Even if the
   //    frame is still present, it must be removed from the list of frame in
   //    fullscreen immediately.
   auto filter = base::MakeRefCounted<DropMessageFilter>(
-      FrameMsgStart, FrameHostMsg_SwapOut_ACK::ID);
+      FrameMsgStart, FrameHostMsg_Unload_ACK::ID);
   main_frame->GetProcess()->AddFilter(filter.get());
-  main_frame->DisableSwapOutTimerForTesting();
+  main_frame->DisableUnloadTimerForTesting();
   EXPECT_TRUE(NavigateToURL(shell(), url_b));
   EXPECT_EQ(0u, web_contents->fullscreen_frames_.size());
 }
diff --git a/content/browser/web_contents/web_contents_impl_unittest.cc b/content/browser/web_contents/web_contents_impl_unittest.cc
index ef7d790..3bf6e33b 100644
--- a/content/browser/web_contents/web_contents_impl_unittest.cc
+++ b/content/browser/web_contents/web_contents_impl_unittest.cc
@@ -1269,7 +1269,7 @@
   // Before that commits, go back again.
   auto back_navigation2 =
       NavigationSimulatorImpl::CreateHistoryNavigation(-1, contents());
-  back_navigation2->set_drop_swap_out_ack(true);
+  back_navigation2->set_drop_unload_ack(true);
   back_navigation2->ReadyToCommit();
   EXPECT_TRUE(contents()->CrossProcessNavigationPending());
   EXPECT_TRUE(contents()->GetPendingMainFrame());
diff --git a/content/browser/webui/web_ui_impl.cc b/content/browser/webui/web_ui_impl.cc
index a6d18fa..fe4a756 100644
--- a/content/browser/webui/web_ui_impl.cc
+++ b/content/browser/webui/web_ui_impl.cc
@@ -146,7 +146,7 @@
   }
 }
 
-void WebUIImpl::RenderFrameHostSwappingOut() {
+void WebUIImpl::RenderFrameHostUnloading() {
   DisallowJavascriptOnAllHandlers();
 }
 
diff --git a/content/browser/webui/web_ui_impl.h b/content/browser/webui/web_ui_impl.h
index 2bd6dbe..e2adadc 100644
--- a/content/browser/webui/web_ui_impl.h
+++ b/content/browser/webui/web_ui_impl.h
@@ -39,8 +39,8 @@
   // Called when a RenderFrame is reused for the same WebUI type (i.e. reload).
   void RenderFrameReused(RenderFrameHost* render_frame_host);
 
-  // Called when the owning RenderFrameHost has started swapping out.
-  void RenderFrameHostSwappingOut();
+  // Called when the owning RenderFrameHost has started unloading.
+  void RenderFrameHostUnloading();
 
   // WebUI implementation:
   WebContents* GetWebContents() override;
diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h
index 14a86b4..70388f74 100644
--- a/content/common/frame_messages.h
+++ b/content/common/frame_messages.h
@@ -1128,8 +1128,10 @@
                     base::TimeTicks /* before_unload_start_time */,
                     base::TimeTicks /* before_unload_end_time */)
 
-// Indicates that the current frame has swapped out, after a SwapOut message.
-IPC_MESSAGE_ROUTED0(FrameHostMsg_SwapOut_ACK)
+// Indicates that the current frame has finished running its unload handler (if
+// one was registered) and has been detached, as a response to
+// UnfreezableFrameMsg_Unload message from the browser process.
+IPC_MESSAGE_ROUTED0(FrameHostMsg_Unload_ACK)
 
 // Tells the browser that a child's visual properties have changed.
 IPC_MESSAGE_ROUTED2(FrameHostMsg_SynchronizeVisualProperties,
diff --git a/content/common/unfreezable_frame_messages.h b/content/common/unfreezable_frame_messages.h
index 3e2e181..3535656 100644
--- a/content/common/unfreezable_frame_messages.h
+++ b/content/common/unfreezable_frame_messages.h
@@ -20,9 +20,9 @@
 
 #define IPC_MESSAGE_START UnfreezableFrameMsgStart
 
-// Swap this RenderFrame out so the frame can navigate to a document rendered by
-// a different process.
-IPC_MESSAGE_ROUTED3(UnfreezableFrameMsg_SwapOut,
+// Unload this RenderFrame and potentially replace it by a RenderFrameProxy, so
+// the frame can navigate to a document rendered by a different process.
+IPC_MESSAGE_ROUTED3(UnfreezableFrameMsg_Unload,
                     int /* proxy_routing_id */,
                     bool /* is_loading */,
                     content::FrameReplicationState /* replication_state */)
diff --git a/content/public/android/java/src/org/chromium/content/browser/ChildProcessRanking.java b/content/public/android/java/src/org/chromium/content/browser/ChildProcessRanking.java
index 846d347..fe9f772 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ChildProcessRanking.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ChildProcessRanking.java
@@ -86,9 +86,8 @@
             // * (visible and main frame) or ChildProcessImportance.IMPORTANT
             // * (visible and subframe and intersect viewport) or ChildProcessImportance.MODERATE
             // ---- cutoff for shouldBeInLowRankGroup ----
-            // * invisible main frame
             // * visible subframe and not intersect viewport
-            // * invisible subframes
+            // * invisible main and sub frames (not ranked by frame depth)
             // Within each group, ties are broken by intersect viewport and then frame depth where
             // applicable. Note boostForPendingViews is not used for ranking.
 
@@ -120,24 +119,19 @@
                 return 1;
             }
 
-            boolean o1InvisibleMainFrame = !o1.visible && o1.frameDepth == 0;
-            boolean o2InvisibleMainFrame = !o2.visible && o2.frameDepth == 0;
-            if (o1InvisibleMainFrame && o2InvisibleMainFrame) {
-                return 0;
-            } else if (o1InvisibleMainFrame && !o2InvisibleMainFrame) {
-                return -1;
-            } else if (!o1InvisibleMainFrame && o2InvisibleMainFrame) {
-                return 1;
-            }
-
-            // The rest of the groups can just be ranked by visibility, intersects viewport, and
-            // frame depth.
-            if (o1.visible && !o2.visible) {
+            if (o1.visible && o2.visible) {
+                return compareByIntersectsViewportAndDepth(o1, o2);
+            } else if (o1.visible && !o2.visible) {
                 return -1;
             } else if (!o1.visible && o2.visible) {
                 return 1;
             }
-            return compareByIntersectsViewportAndDepth(o1, o2);
+
+            // Invisible are in one group and are purposefully not ranked by frame depth.
+            // This is because a crashed sub frame will cause the whole tab to be reloaded
+            // when it becomes visible, so there is no need to specifically protect the
+            // main frame or lower depth frames.
+            return 0;
         }
     }
 
diff --git a/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java b/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java
index b068979..1f1f4f5 100644
--- a/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java
@@ -1011,7 +1011,12 @@
 
     @CalledByNative
     private void handleCheckStateChanged(int id) {
-        sendAccessibilityEvent(id, AccessibilityEvent.TYPE_VIEW_CLICKED);
+        // If the node has accessibility focus, fire TYPE_VIEW_CLICKED event. This check ensures
+        // only necessary announcements are made (e.g. changing a radio group selection
+        // would erroneously announce "checked not checked" without this check)
+        if (mAccessibilityFocusId == id) {
+            sendAccessibilityEvent(id, AccessibilityEvent.TYPE_VIEW_CLICKED);
+        }
     }
 
     @CalledByNative
diff --git a/content/public/android/junit/src/org/chromium/content/browser/ChildProcessRankingTest.java b/content/public/android/junit/src/org/chromium/content/browser/ChildProcessRankingTest.java
index be1cf48..5175b70 100644
--- a/content/public/android/junit/src/org/chromium/content/browser/ChildProcessRankingTest.java
+++ b/content/public/android/junit/src/org/chromium/content/browser/ChildProcessRankingTest.java
@@ -102,49 +102,35 @@
 
         // Insert in lowest ranked to highest ranked order.
 
-        // Invisible subframe outside of viewport.
-        ranking.addConnection(c1, false /* foreground */, 2 /* frameDepth */,
-                false /* intersectsViewport */, ChildProcessImportance.NORMAL);
-        ranking.addConnection(c2, false /* foreground */, 1 /* frameDepth */,
-                false /* intersectsViewport */, ChildProcessImportance.NORMAL);
-
-        // Invisible subframe inside viewport.
-        ranking.addConnection(c3, false /* foreground */, 2 /* frameDepth */,
-                true /* intersectsViewport */, ChildProcessImportance.NORMAL);
-        ranking.addConnection(c4, false /* foreground */, 1 /* frameDepth */,
+        // Invisible frame.
+        ranking.addConnection(c1, false /* foreground */, 0 /* frameDepth */,
                 true /* intersectsViewport */, ChildProcessImportance.NORMAL);
 
         // Visible subframe outside viewport.
-        ranking.addConnection(c5, true /* foreground */, 2 /* frameDepth */,
+        ranking.addConnection(c2, true /* foreground */, 2 /* frameDepth */,
                 false /* intersectsViewport */, ChildProcessImportance.NORMAL);
-        ranking.addConnection(c6, true /* foreground */, 1 /* frameDepth */,
+        ranking.addConnection(c3, true /* foreground */, 1 /* frameDepth */,
                 false /* intersectsViewport */, ChildProcessImportance.NORMAL);
 
-        // Invisible main frame.
-        ranking.addConnection(c7, false /* foreground */, 0 /* frameDepth */,
-                true /* intersectsViewport */, ChildProcessImportance.NORMAL);
-
         // Visible subframe inside viewport.
-        ranking.addConnection(c8, true /* foreground */, 2 /* frameDepth */,
+        ranking.addConnection(c4, true /* foreground */, 2 /* frameDepth */,
                 true /* intersectsViewport */, ChildProcessImportance.NORMAL);
-        ranking.addConnection(c9, true /* foreground */, 1 /* frameDepth */,
+        ranking.addConnection(c5, true /* foreground */, 1 /* frameDepth */,
                 true /* intersectsViewport */, ChildProcessImportance.NORMAL);
 
         // Visible main frame.
-        ranking.addConnection(c10, true /* foreground */, 0 /* frameDepth */,
+        ranking.addConnection(c6, true /* foreground */, 0 /* frameDepth */,
                 true /* intersectsViewport */, ChildProcessImportance.NORMAL);
 
         if (enableGroupImportanceAfter) {
-            assertNotInGroup(
-                    new ChildProcessConnection[] {c10, c9, c8, c7, c6, c5, c4, c3, c2, c1});
+            assertNotInGroup(new ChildProcessConnection[] {c6, c5, c4, c3, c2, c1});
             ranking.enableServiceGroupImportance();
         }
 
-        assertRankingAndRemoveAll(
-                ranking, new ChildProcessConnection[] {c10, c9, c8, c7, c6, c5, c4, c3, c2, c1});
+        assertRankingAndRemoveAll(ranking, new ChildProcessConnection[] {c6, c5, c4, c3, c2, c1});
 
-        assertNotInGroup(new ChildProcessConnection[] {c10, c9, c8});
-        assertInGroupOrderedByImportance(new ChildProcessConnection[] {c7, c6, c5, c4, c3, c2, c1});
+        assertNotInGroup(new ChildProcessConnection[] {c6, c5, c4});
+        assertInGroupOrderedByImportance(new ChildProcessConnection[] {c3, c2, c1});
     }
 
     @Test
diff --git a/content/public/test/browser_task_environment_unittest.cc b/content/public/test/browser_task_environment_unittest.cc
index 854221d..cca1c2c 100644
--- a/content/public/test/browser_task_environment_unittest.cc
+++ b/content/public/test/browser_task_environment_unittest.cc
@@ -11,15 +11,12 @@
 #include "base/synchronization/waitable_event.h"
 #include "base/task/post_task.h"
 #include "base/test/bind_test_util.h"
-#include "base/test/gtest_util.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using base::test::TaskEnvironment;
-using ::testing::IsNull;
-using ::testing::NotNull;
 
 namespace content {
 
@@ -226,37 +223,4 @@
   run_loop.Run();
 }
 
-TEST(BrowserTaskEnvironmentTest, GetCurrentTaskWithNoTaskRunning) {
-  BrowserTaskEnvironment task_environment;
-  EXPECT_DCHECK_DEATH(base::GetContinuationTaskRunner());
-}
-
-TEST(BrowserTaskEnvironmentTest, GetContinuationTaskRunnerUI) {
-  BrowserTaskEnvironment task_environment;
-  base::RunLoop run_loop;
-  auto ui_task_runner = base::CreateSingleThreadTaskRunner({BrowserThread::UI});
-
-  ui_task_runner->PostTask(FROM_HERE, base::BindLambdaForTesting([&]() {
-                             EXPECT_EQ(ui_task_runner,
-                                       base::GetContinuationTaskRunner());
-                             run_loop.Quit();
-                           }));
-
-  run_loop.Run();
-}
-
-TEST(BrowserTaskEnvironmentTest, GetContinuationTaskRunnerIO) {
-  BrowserTaskEnvironment task_environment;
-  base::RunLoop run_loop;
-  auto io_task_runner = base::CreateSingleThreadTaskRunner({BrowserThread::IO});
-
-  io_task_runner->PostTask(FROM_HERE, base::BindLambdaForTesting([&]() {
-                             EXPECT_EQ(io_task_runner,
-                                       base::GetContinuationTaskRunner());
-                             run_loop.Quit();
-                           }));
-
-  run_loop.Run();
-}
-
 }  // namespace content
diff --git a/content/public/test/test_renderer_host.h b/content/public/test/test_renderer_host.h
index 44f1e456..e4130fd 100644
--- a/content/public/test/test_renderer_host.h
+++ b/content/public/test/test_renderer_host.h
@@ -94,9 +94,9 @@
   // Calls OnBeforeUnloadACK on this RenderFrameHost with the given parameter.
   virtual void SendBeforeUnloadACK(bool proceed) = 0;
 
-  // Simulates the SwapOut_ACK that fires if you commit a cross-site
+  // Simulates the FrameHostMsg_Unload_ACK that fires if you commit a cross-site
   // navigation without making any network requests.
-  virtual void SimulateSwapOutACK() = 0;
+  virtual void SimulateUnloadACK() = 0;
 
   // Set the feature policy header for the RenderFrameHost for test. Currently
   // this is limited to setting an allowlist for a single feature. This function
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 4587c16..66f04b2 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -2185,7 +2185,7 @@
   bool handled = true;
   IPC_BEGIN_MESSAGE_MAP(RenderFrameImpl, msg)
     IPC_MESSAGE_HANDLER(FrameMsg_BeforeUnload, OnBeforeUnload)
-    IPC_MESSAGE_HANDLER(UnfreezableFrameMsg_SwapOut, OnSwapOut)
+    IPC_MESSAGE_HANDLER(UnfreezableFrameMsg_Unload, OnUnload)
     IPC_MESSAGE_HANDLER(FrameMsg_SwapIn, OnSwapIn)
     IPC_MESSAGE_HANDLER(FrameMsg_Stop, OnStop)
     IPC_MESSAGE_HANDLER(FrameMsg_ContextMenuClosed, OnContextMenuClosed)
@@ -2314,16 +2314,16 @@
       routing_id, proceed, before_unload_start_time, before_unload_end_time));
 }
 
-// Swap this RenderFrame out so the frame can navigate to a document rendered by
+// Unload this RenderFrame so the frame can navigate to a document rendered by
 // a different process. We also allow this process to exit if there are no other
 // active RenderFrames in it.
 // This executes the unload handlers on this frame and its local descendants.
-void RenderFrameImpl::OnSwapOut(
+void RenderFrameImpl::OnUnload(
     int proxy_routing_id,
     bool is_loading,
     const FrameReplicationState& replicated_frame_state) {
-  TRACE_EVENT1("navigation,rail", "RenderFrameImpl::OnSwapOut",
-               "id", routing_id_);
+  TRACE_EVENT1("navigation,rail", "RenderFrameImpl::OnUnload", "id",
+               routing_id_);
   DCHECK(!base::RunLoop::IsNestedOnCurrentThread());
 
   // Send an UpdateState message before we get deleted.
@@ -2340,8 +2340,9 @@
   int routing_id = GetRoutingID();
 
   // Before |this| is destroyed, grab the TaskRunner to be used for sending the
-  // SwapOut ACK.  This will be used to schedule SwapOut ACK to be sent after
-  // any postMessage IPCs scheduled from the unload event above.
+  // FrameHostMsg_Unload_ACK.  This will be used to schedule
+  // FrameHostMsg_Unload_ACK to be sent after any postMessage IPCs scheduled
+  // from the unload event above.
   scoped_refptr<base::SingleThreadTaskRunner> task_runner =
       GetTaskRunner(blink::TaskType::kPostedMessage);
 
@@ -2385,16 +2386,16 @@
   // process that is now rendering the frame.
   proxy->SetReplicatedState(replicated_frame_state);
 
-  // Notify the browser that this frame was swapped. Use the RenderThread
+  // Notify the browser that this frame was unloaded. Use the RenderThread
   // directly because |this| is deleted.  Post a task to send the ACK, so that
   // any postMessage IPCs scheduled from the unload handler are sent before
   // the ACK (see https://crbug.com/857274).
-  auto send_swapout_ack = base::BindOnce(
+  auto send_unload_ack = base::BindOnce(
       [](int routing_id, bool is_main_frame) {
-        RenderThread::Get()->Send(new FrameHostMsg_SwapOut_ACK(routing_id));
+        RenderThread::Get()->Send(new FrameHostMsg_Unload_ACK(routing_id));
       },
       routing_id, is_main_frame);
-  task_runner->PostTask(FROM_HERE, std::move(send_swapout_ack));
+  task_runner->PostTask(FROM_HERE, std::move(send_unload_ack));
 }
 
 void RenderFrameImpl::OnSwapIn() {
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index 00f31f4..b8bb9be 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -1066,9 +1066,9 @@
   // content/common/*_messages.h for the message that the function is handling.
   void OnBeforeUnload(bool is_reload);
   void OnSwapIn();
-  void OnSwapOut(int proxy_routing_id,
-                 bool is_loading,
-                 const FrameReplicationState& replicated_frame_state);
+  void OnUnload(int proxy_routing_id,
+                bool is_loading,
+                const FrameReplicationState& replicated_frame_state);
   void OnDeleteFrame(FrameDeleteIntention intent);
   void OnStop();
   void OnShowContextMenu(const gfx::Point& location);
diff --git a/content/renderer/render_frame_impl_browsertest.cc b/content/renderer/render_frame_impl_browsertest.cc
index 204f74e..43735b1 100644
--- a/content/renderer/render_frame_impl_browsertest.cc
+++ b/content/renderer/render_frame_impl_browsertest.cc
@@ -103,7 +103,7 @@
 
     RenderFrameImpl::FromWebFrame(
         view_->GetMainRenderFrame()->GetWebFrame()->FirstChild())
-        ->OnSwapOut(kFrameProxyRouteId, false, frame_replication_state);
+        ->OnUnload(kFrameProxyRouteId, false, frame_replication_state);
 
     mojo::PendingRemote<service_manager::mojom::InterfaceProvider>
         stub_interface_provider;
diff --git a/content/renderer/render_frame_proxy.cc b/content/renderer/render_frame_proxy.cc
index cf7be8e..f145876 100644
--- a/content/renderer/render_frame_proxy.cc
+++ b/content/renderer/render_frame_proxy.cc
@@ -89,20 +89,28 @@
       scope, proxy.get(), proxy->blink_interface_registry_.get(),
       proxy->GetRemoteAssociatedInterfaces());
 
-  bool parent_is_local =
-      !frame_to_replace->GetWebFrame()->Parent() ||
-      frame_to_replace->GetWebFrame()->Parent()->IsWebLocalFrame();
+  RenderWidget* ancestor_widget = nullptr;
+  bool parent_is_local = false;
 
-  // If frame_to_replace has a RenderFrameProxy parent, then its
-  // RenderWidget will be destroyed along with it, so the new
-  // RenderFrameProxy uses its parent's RenderWidget.
-  RenderWidget* widget =
-      parent_is_local
-          ? frame_to_replace->GetLocalRootRenderWidget()
-          : RenderFrameProxy::FromWebFrame(
-                frame_to_replace->GetWebFrame()->Parent()->ToWebRemoteFrame())
-                ->render_widget_;
-  proxy->Init(web_frame, frame_to_replace->render_view(), widget,
+  // A top level frame proxy doesn't have a RenderWidget pointer. The pointer
+  // is to an ancestor local frame's RenderWidget and there are no ancestors.
+  if (frame_to_replace->GetWebFrame()->Parent()) {
+    if (frame_to_replace->GetWebFrame()->Parent()->IsWebLocalFrame()) {
+      // If the frame was a local frame, get its local root's RenderWidget.
+      ancestor_widget = frame_to_replace->GetLocalRootRenderWidget();
+      parent_is_local = true;
+    } else {
+      // Otherwise, grab the pointer from the parent RenderFrameProxy, as
+      // it would already have the correct pointer. A proxy with a proxy child
+      // must be created before its child, so the first proxy in a descendant
+      // chain is either the root or has a local parent frame.
+      RenderFrameProxy* parent = RenderFrameProxy::FromWebFrame(
+          frame_to_replace->GetWebFrame()->Parent()->ToWebRemoteFrame());
+      ancestor_widget = parent->ancestor_render_widget_;
+    }
+  }
+
+  proxy->Init(web_frame, frame_to_replace->render_view(), ancestor_widget,
               parent_is_local);
   return proxy.release();
 }
@@ -127,7 +135,7 @@
   std::unique_ptr<RenderFrameProxy> proxy(new RenderFrameProxy(routing_id));
   proxy->devtools_frame_token_ = devtools_frame_token;
   RenderViewImpl* render_view = nullptr;
-  RenderWidget* render_widget = nullptr;
+  RenderWidget* ancestor_widget = nullptr;
   blink::WebRemoteFrame* web_frame = nullptr;
 
   if (!parent) {
@@ -137,7 +145,7 @@
         render_view->GetWebView(), proxy.get(),
         proxy->blink_interface_registry_.get(),
         proxy->GetRemoteAssociatedInterfaces(), opener);
-    render_widget = render_view->GetWidget();
+    // Root frame proxy has no ancestors to point to their RenderWidget.
   } else {
     // Create a frame under an existing parent. The parent is always expected
     // to be a RenderFrameProxy, because navigations initiated by local frames
@@ -151,10 +159,10 @@
         proxy->GetRemoteAssociatedInterfaces(), opener);
     proxy->unique_name_ = replicated_state.unique_name;
     render_view = parent->render_view();
-    render_widget = parent->render_widget_;
+    ancestor_widget = parent->ancestor_render_widget_;
   }
 
-  proxy->Init(web_frame, render_view, render_widget, false);
+  proxy->Init(web_frame, render_view, ancestor_widget, false);
 
   // Initialize proxy's WebRemoteFrame with the security origin and other
   // replicated information.
@@ -173,8 +181,7 @@
     int proxy_routing_id,
     const base::UnguessableToken& devtools_frame_token,
     const blink::WebElement& portal_element) {
-  std::unique_ptr<RenderFrameProxy> proxy(
-      new RenderFrameProxy(proxy_routing_id));
+  auto proxy = base::WrapUnique(new RenderFrameProxy(proxy_routing_id));
   proxy->devtools_frame_token_ = devtools_frame_token;
   blink::WebRemoteFrame* web_frame = blink::WebRemoteFrame::CreateForPortal(
       blink::WebTreeScopeType::kDocument, proxy.get(),
@@ -214,9 +221,6 @@
 RenderFrameProxy::RenderFrameProxy(int routing_id)
     : routing_id_(routing_id),
       provisional_frame_routing_id_(MSG_ROUTING_NONE),
-      web_frame_(nullptr),
-      render_view_(nullptr),
-      render_widget_(nullptr),
       // TODO(samans): Investigate if it is safe to delay creation of this
       // object until a FrameSinkId is provided.
       parent_local_surface_id_allocator_(
@@ -230,8 +234,8 @@
 }
 
 RenderFrameProxy::~RenderFrameProxy() {
-  if (render_widget_)
-    render_widget_->UnregisterRenderFrameProxy(this);
+  if (ancestor_render_widget_)
+    ancestor_render_widget_->UnregisterRenderFrameProxy(this);
 
   CHECK(!web_frame_);
   RenderThread::Get()->RemoveRoute(routing_id_);
@@ -240,24 +244,24 @@
 
 void RenderFrameProxy::Init(blink::WebRemoteFrame* web_frame,
                             RenderViewImpl* render_view,
-                            RenderWidget* render_widget,
+                            RenderWidget* ancestor_widget,
                             bool parent_is_local) {
   CHECK(web_frame);
   CHECK(render_view);
 
   web_frame_ = web_frame;
   render_view_ = render_view;
-  render_widget_ = render_widget;
+  ancestor_render_widget_ = ancestor_widget;
 
-  // |render_widget_| can be null if this is a proxy for a remote main frame, or
-  // a subframe of that proxy. We don't need to register as an observer [since
-  // the RenderWidget is undead/won't exist]. The observer is used to propagate
-  // VisualProperty changes down the frame/process hierarchy. Remote main frame
-  // proxies do not participate in this flow.
-  if (render_widget_) {
-    render_widget_->RegisterRenderFrameProxy(this);
+  // |ancestor_render_widget_| can be null if this is a proxy for a remote main
+  // frame, or a subframe of that proxy. We don't need to register as an
+  // observer [since there is no ancestor RenderWidget]. The observer is used to
+  // propagate VisualProperty changes down the frame/process hierarchy. Remote
+  // main frame proxies do not participate in this flow.
+  if (ancestor_render_widget_) {
+    ancestor_render_widget_->RegisterRenderFrameProxy(this);
     pending_visual_properties_.screen_info =
-        render_widget_->GetOriginalScreenInfo();
+        ancestor_render_widget_->GetOriginalScreenInfo();
   }
 
   std::pair<FrameProxyMap::iterator, bool> result =
@@ -276,6 +280,8 @@
 }
 
 void RenderFrameProxy::OnScreenInfoChanged(const ScreenInfo& screen_info) {
+  DCHECK(ancestor_render_widget_);
+
   pending_visual_properties_.screen_info = screen_info;
   if (crashed_) {
     // Update the sad page to match the current ScreenInfo.
@@ -287,12 +293,16 @@
 }
 
 void RenderFrameProxy::OnZoomLevelChanged(double zoom_level) {
+  DCHECK(ancestor_render_widget_);
+
   pending_visual_properties_.zoom_level = zoom_level;
   SynchronizeVisualProperties();
 }
 
 void RenderFrameProxy::OnPageScaleFactorChanged(float page_scale_factor,
                                                 bool is_pinch_gesture_active) {
+  DCHECK(ancestor_render_widget_);
+
   pending_visual_properties_.page_scale_factor = page_scale_factor;
   pending_visual_properties_.is_pinch_gesture_active = is_pinch_gesture_active;
   SynchronizeVisualProperties();
@@ -300,6 +310,8 @@
 
 void RenderFrameProxy::UpdateCaptureSequenceNumber(
     uint32_t capture_sequence_number) {
+  DCHECK(ancestor_render_widget_);
+
   pending_visual_properties_.capture_sequence_number = capture_sequence_number;
   SynchronizeVisualProperties();
 }
@@ -554,6 +566,8 @@
 
 void RenderFrameProxy::OnEnableAutoResize(const gfx::Size& min_size,
                                           const gfx::Size& max_size) {
+  DCHECK(ancestor_render_widget_);
+
   pending_visual_properties_.auto_resize_enabled = true;
   pending_visual_properties_.min_size_for_auto_resize = min_size;
   pending_visual_properties_.max_size_for_auto_resize = max_size;
@@ -561,11 +575,15 @@
 }
 
 void RenderFrameProxy::OnDisableAutoResize() {
+  DCHECK(ancestor_render_widget_);
+
   pending_visual_properties_.auto_resize_enabled = false;
   SynchronizeVisualProperties();
 }
 
 void RenderFrameProxy::SynchronizeVisualProperties() {
+  DCHECK(ancestor_render_widget_);
+
   if (!frame_sink_id_.is_valid() || crashed_)
     return;
 
@@ -758,13 +776,13 @@
 void RenderFrameProxy::FrameRectsChanged(
     const blink::WebRect& local_frame_rect,
     const blink::WebRect& screen_space_rect) {
+  DCHECK(ancestor_render_widget_);
+
   pending_visual_properties_.screen_space_rect = gfx::Rect(screen_space_rect);
   pending_visual_properties_.local_frame_size =
       gfx::Size(local_frame_rect.width, local_frame_rect.height);
-  if (render_widget_) {
-    pending_visual_properties_.screen_info =
-        render_widget_->GetOriginalScreenInfo();
-  }
+  pending_visual_properties_.screen_info =
+      ancestor_render_widget_->GetOriginalScreenInfo();
   if (crashed_) {
     // Update the sad page to match the current size.
     compositing_helper_->ChildFrameGone(local_frame_size(),
@@ -776,6 +794,8 @@
 
 void RenderFrameProxy::UpdateRemoteViewportIntersection(
     const blink::ViewportIntersectionState& intersection_state) {
+  DCHECK(ancestor_render_widget_);
+
   // If the remote viewport intersection has changed, then we should check if
   // the compositing rect has also changed: if it has, then we should update the
   // visible properties.
diff --git a/content/renderer/render_frame_proxy.h b/content/renderer/render_frame_proxy.h
index 6e29954..22634b8 100644
--- a/content/renderer/render_frame_proxy.h
+++ b/content/renderer/render_frame_proxy.h
@@ -273,7 +273,7 @@
   int provisional_frame_routing_id_;
 
   // Stores the WebRemoteFrame we are associated with.
-  blink::WebRemoteFrame* web_frame_;
+  blink::WebRemoteFrame* web_frame_ = nullptr;
   std::string unique_name_;
 
   // Provides the mojo interface to this RenderFrameProxy's
@@ -285,12 +285,12 @@
   // Can be nullptr when this RenderFrameProxy's parent is not a RenderFrame.
   std::unique_ptr<ChildFrameCompositingHelper> compositing_helper_;
 
-  RenderViewImpl* render_view_;
+  RenderViewImpl* render_view_ = nullptr;
 
-  // The widget used for the local frame root. Can be nullptr if there
-  // is no local frame root. This happens for main frame proxies or subframes of
-  // main frame proxies.
-  RenderWidget* render_widget_ = nullptr;
+  // The RenderWidget of the nearest ancestor local root. If the proxy has no
+  // local root ancestor (eg it is a proxy of the root frame) then the pointer
+  // is null.
+  RenderWidget* ancestor_render_widget_ = nullptr;
 
   // Contains token to be used as a frame id in the devtools protocol.
   // It is derived from the content's devtools_frame_token, is
diff --git a/content/renderer/render_view_browsertest.cc b/content/renderer/render_view_browsertest.cc
index b065ec1..c20b747 100644
--- a/content/renderer/render_view_browsertest.cc
+++ b/content/renderer/render_view_browsertest.cc
@@ -566,8 +566,8 @@
       static_cast<TestRenderFrame*>(RenderFrame::FromWebFrame(
           root_web_frame->FirstChild()->NextSibling()->ToWebLocalFrame()));
   ASSERT_TRUE(child_frame_2);
-  child_frame_1->SwapOut(kProxyRoutingId, true,
-                         ReconstructReplicationStateForTesting(child_frame_1));
+  child_frame_1->Unload(kProxyRoutingId, true,
+                        ReconstructReplicationStateForTesting(child_frame_1));
   EXPECT_TRUE(root_web_frame->FirstChild()->IsWebRemoteFrame());
   RenderFrameProxy* child_proxy_1 = RenderFrameProxy::FromWebFrame(
       root_web_frame->FirstChild()->ToWebRemoteFrame());
@@ -586,11 +586,11 @@
   view()->webview()->MainFrameWidget()->ApplyViewportChanges(args);
   EXPECT_TRUE(child_proxy_1->is_pinch_gesture_active_for_testing());
 
-  // Create a new remote child, and get its proxy. Swapping out will force
-  // creation and registering of a new RenderFrameProxy, which should pick up
-  // the existing setting.
-  child_frame_2->SwapOut(kProxyRoutingId + 1, true,
-                         ReconstructReplicationStateForTesting(child_frame_2));
+  // Create a new remote child, and get its proxy. Unloading will force creation
+  // and registering of a new RenderFrameProxy, which should pick up the
+  // existing setting.
+  child_frame_2->Unload(kProxyRoutingId + 1, true,
+                        ReconstructReplicationStateForTesting(child_frame_2));
   EXPECT_TRUE(root_web_frame->FirstChild()->NextSibling()->IsWebRemoteFrame());
   RenderFrameProxy* child_proxy_2 = RenderFrameProxy::FromWebFrame(
       root_web_frame->FirstChild()->NextSibling()->ToWebRemoteFrame());
@@ -1010,8 +1010,8 @@
       RenderFrame::FromWebFrame(web_frame->FirstChild()->ToWebLocalFrame()));
   ASSERT_TRUE(child_frame);
 
-  child_frame->SwapOut(kProxyRoutingId + 1, true,
-                       ReconstructReplicationStateForTesting(child_frame));
+  child_frame->Unload(kProxyRoutingId + 1, true,
+                      ReconstructReplicationStateForTesting(child_frame));
   EXPECT_TRUE(web_frame->FirstChild()->IsWebRemoteFrame());
   RenderFrameProxy* child_proxy = RenderFrameProxy::FromWebFrame(
       web_frame->FirstChild()->ToWebRemoteFrame());
@@ -1040,8 +1040,8 @@
 }
 
 // Verify that security origins are replicated properly to RenderFrameProxies
-// when swapping out.
-TEST_F(RenderViewImplTest, OriginReplicationForSwapOut) {
+// when unloading.
+TEST_F(RenderViewImplTest, OriginReplicationForUnload) {
   LoadHTML(
       "Hello <iframe src='data:text/html,frame 1'></iframe>"
       "<iframe src='data:text/html,frame 2'></iframe>");
@@ -1049,12 +1049,12 @@
   TestRenderFrame* child_frame = static_cast<TestRenderFrame*>(
       RenderFrame::FromWebFrame(web_frame->FirstChild()->ToWebLocalFrame()));
 
-  // Swap the child frame out and pass a replicated origin to be set for
+  // Unload the child frame and pass a replicated origin to be set for
   // WebRemoteFrame.
   content::FrameReplicationState replication_state =
       ReconstructReplicationStateForTesting(child_frame);
   replication_state.origin = url::Origin::Create(GURL("http://foo.com"));
-  child_frame->SwapOut(kProxyRoutingId, true, replication_state);
+  child_frame->Unload(kProxyRoutingId, true, replication_state);
 
   // The child frame should now be a WebRemoteFrame.
   EXPECT_TRUE(web_frame->FirstChild()->IsWebRemoteFrame());
@@ -1065,13 +1065,13 @@
   EXPECT_EQ(origin.ToString(),
             WebString::FromUTF8(replication_state.origin.Serialize()));
 
-  // Now, swap out the second frame using a unique origin and verify that it is
+  // Now, unload the second frame using a unique origin and verify that it is
   // replicated correctly.
   replication_state.origin = url::Origin();
   TestRenderFrame* child_frame2 =
       static_cast<TestRenderFrame*>(RenderFrame::FromWebFrame(
           web_frame->FirstChild()->NextSibling()->ToWebLocalFrame()));
-  child_frame2->SwapOut(kProxyRoutingId + 1, true, replication_state);
+  child_frame2->Unload(kProxyRoutingId + 1, true, replication_state);
   EXPECT_TRUE(web_frame->FirstChild()->NextSibling()->IsWebRemoteFrame());
   EXPECT_TRUE(
       web_frame->FirstChild()->NextSibling()->GetSecurityOrigin().IsOpaque());
@@ -1089,15 +1089,15 @@
   LoadHTML("Hello world!");
 
   // Early grab testing values as the main-frame widget becomes inaccessible
-  // when it swaps out.
+  // when it unloads.
   VisualProperties test_visual_properties =
       MakeVisualPropertiesWithDeviceScaleFactor(device_scale);
 
-  // Swap the main frame out after which it should become a WebRemoteFrame.
+  // Unload the main frame after which it should become a WebRemoteFrame.
   content::FrameReplicationState replication_state =
       ReconstructReplicationStateForTesting(frame());
   // replication_state.origin = url::Origin(GURL("http://foo.com"));
-  frame()->SwapOut(kProxyRoutingId, true, replication_state);
+  frame()->Unload(kProxyRoutingId, true, replication_state);
   EXPECT_TRUE(view()->webview()->MainFrame()->IsWebRemoteFrame());
 
   // Do the remote-to-local transition for the proxy, which is to create a
@@ -1163,10 +1163,10 @@
   TestRenderFrame* child_frame = static_cast<TestRenderFrame*>(
       RenderFrame::FromWebFrame(web_frame->FirstChild()->ToWebLocalFrame()));
 
-  // Swap the child frame out.
+  // Unload the child frame.
   FrameReplicationState replication_state =
       ReconstructReplicationStateForTesting(child_frame);
-  child_frame->SwapOut(kProxyRoutingId, true, replication_state);
+  child_frame->Unload(kProxyRoutingId, true, replication_state);
   EXPECT_TRUE(web_frame->FirstChild()->IsWebRemoteFrame());
 
   // Do the first step of a remote-to-local transition for the child proxy,
@@ -1216,11 +1216,11 @@
        SetZoomLevelAfterCrossProcessNavigation) {
   LoadHTML("Hello world!");
 
-  // Swap the main frame out after which it should become a WebRemoteFrame.
+  // Unload the main frame after which it should become a WebRemoteFrame.
   TestRenderFrame* main_frame =
       static_cast<TestRenderFrame*>(view()->GetMainRenderFrame());
-  main_frame->SwapOut(kProxyRoutingId, true,
-                      ReconstructReplicationStateForTesting(main_frame));
+  main_frame->Unload(kProxyRoutingId, true,
+                     ReconstructReplicationStateForTesting(main_frame));
   EXPECT_TRUE(view()->webview()->MainFrame()->IsWebRemoteFrame());
 }
 
@@ -2521,7 +2521,7 @@
       "<script>window.onbeforeunload = function() { "
       "window.console.log('OnBeforeUnload called'); }</script>");
 
-  // Create a callback that swaps the frame when the 'OnBeforeUnload called'
+  // Create a callback that unloads the frame when the 'OnBeforeUnload called'
   // log is printed from the beforeunload handler.
   base::RunLoop run_loop;
   bool was_callback_run = false;
@@ -2530,8 +2530,8 @@
         // Makes sure this happens during the beforeunload handler.
         EXPECT_EQ(base::UTF8ToUTF16("OnBeforeUnload called"), msg);
 
-        // Swaps the main frame.
-        frame()->OnMessageReceived(UnfreezableFrameMsg_SwapOut(
+        // Unloads the main frame.
+        frame()->OnMessageReceived(UnfreezableFrameMsg_Unload(
             frame()->GetRoutingID(), 1, false, FrameReplicationState()));
 
         was_callback_run = true;
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index 58e09d4..2ab7bde8 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -960,11 +960,9 @@
     // (such as in RenderViewHost) and distribute it to each frame-hosted
     // RenderWidget from there.
     for (auto& child_proxy : render_frame_proxies_) {
-      if (!is_undead_) {
-        child_proxy.OnPageScaleFactorChanged(
-            visual_properties.page_scale_factor,
-            visual_properties.is_pinch_gesture_active);
-      }
+      child_proxy.OnPageScaleFactorChanged(
+          visual_properties.page_scale_factor,
+          visual_properties.is_pinch_gesture_active);
     }
   }
 
@@ -1977,12 +1975,6 @@
   if (compositor_never_visible_)
     return;
 
-  // TODO(danakj): We should start the compositor before becoming shown for
-  // *all* provisional frames not just provisional main frames (as they come
-  // back from being undead). However we need to also prevent BeginMainFrame
-  // from occurring, in both cases, until the frame is attached at least. And in
-  // the case of main frames, until blink requests them through
-  // StopDeferringMainFrameUpdate().
   if (is_undead_) {
     layer_tree_view_->SetVisible(false);
     // Drop all gpu resources, this makes SetVisible(true) more expensive/slower
@@ -2404,10 +2396,8 @@
   // Propagate changes down to child local root RenderWidgets and BrowserPlugins
   // in other frame trees/processes.
   if (previous_original_screen_info != GetOriginalScreenInfo()) {
-    for (auto& observer : render_frame_proxies_) {
-      if (!is_undead_)
-        observer.OnScreenInfoChanged(GetOriginalScreenInfo());
-    }
+    for (auto& observer : render_frame_proxies_)
+      observer.OnScreenInfoChanged(GetOriginalScreenInfo());
   }
 }
 
@@ -3480,10 +3470,8 @@
   // the message to their child RenderWidgets (through their proxy child
   // frames).
   for (auto& observer : render_frame_proxies_) {
-    if (!is_undead_) {
-      observer.OnPageScaleFactorChanged(page_scale_factor,
-                                        is_pinch_gesture_active);
-    }
+    observer.OnPageScaleFactorChanged(page_scale_factor,
+                                      is_pinch_gesture_active);
   }
   // Store the value to give to any new RenderFrameProxy that is registered.
   page_scale_factor_from_mainframe_ = page_scale_factor;
diff --git a/content/shell/browser/web_test/web_test_content_browser_client.h b/content/shell/browser/web_test/web_test_content_browser_client.h
index 0cd293f..c5dd687 100644
--- a/content/shell/browser/web_test/web_test_content_browser_client.h
+++ b/content/shell/browser/web_test/web_test_content_browser_client.h
@@ -9,7 +9,7 @@
 
 #include "content/public/common/client_hints.mojom.h"
 #include "content/shell/browser/shell_content_browser_client.h"
-#include "content/shell/common/web_test/fake_bluetooth_chooser.mojom.h"
+#include "content/shell/common/web_test/fake_bluetooth_chooser.mojom-forward.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "services/service_manager/public/cpp/binder_map.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
diff --git a/content/shell/browser/web_test/web_test_message_filter.h b/content/shell/browser/web_test/web_test_message_filter.h
index 4a97e74f..3c54952 100644
--- a/content/shell/browser/web_test/web_test_message_filter.h
+++ b/content/shell/browser/web_test/web_test_message_filter.h
@@ -16,7 +16,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/network/public/mojom/cookie_manager.mojom.h"
-#include "third_party/blink/public/mojom/permissions/permission_status.mojom.h"
+#include "third_party/blink/public/mojom/permissions/permission_status.mojom-forward.h"
 
 class GURL;
 
diff --git a/content/shell/common/web_test/web_test_messages.h b/content/shell/common/web_test/web_test_messages.h
index bf87de1..59d9a9f 100644
--- a/content/shell/common/web_test/web_test_messages.h
+++ b/content/shell/common/web_test/web_test_messages.h
@@ -13,7 +13,7 @@
 #include "content/public/common/common_param_traits_macros.h"
 #include "ipc/ipc_message_macros.h"
 #include "ipc/ipc_platform_file.h"
-#include "third_party/blink/public/mojom/permissions/permission_status.mojom.h"
+#include "third_party/blink/public/mojom/permissions/permission_status.mojom-forward.h"
 #include "url/gurl.h"
 #include "url/ipc/url_param_traits.h"
 
diff --git a/content/shell/gpu/shell_content_gpu_client.h b/content/shell/gpu/shell_content_gpu_client.h
index c842c3d..72c585d6 100644
--- a/content/shell/gpu/shell_content_gpu_client.h
+++ b/content/shell/gpu/shell_content_gpu_client.h
@@ -9,7 +9,7 @@
 
 #include "base/macros.h"
 #include "content/public/gpu/content_gpu_client.h"
-#include "services/network/public/mojom/network_service_test.mojom.h"
+#include "services/network/public/mojom/network_service_test.mojom-forward.h"
 
 namespace content {
 
diff --git a/content/test/navigation_simulator_impl.cc b/content/test/navigation_simulator_impl.cc
index 7e4536a..959da8e4 100644
--- a/content/test/navigation_simulator_impl.cc
+++ b/content/test/navigation_simulator_impl.cc
@@ -609,7 +609,7 @@
       request_, std::move(params), std::move(interface_provider_receiver_),
       std::move(browser_interface_broker_receiver_), same_document_);
 
-  SimulateSwapOutACKForPreviousFrameIfNeeded(previous_rfh);
+  SimulateUnloadACKForPreviousFrameIfNeeded(previous_rfh);
 
   loading_scenario_ =
       TestRenderFrameHost::LoadingScenario::NewDocumentNavigation;
@@ -746,7 +746,7 @@
       request_, std::move(params), std::move(interface_provider_receiver_),
       std::move(browser_interface_broker_receiver_), false /* same_document */);
 
-  SimulateSwapOutACKForPreviousFrameIfNeeded(previous_rfh);
+  SimulateUnloadACKForPreviousFrameIfNeeded(previous_rfh);
 
   state_ = FINISHED;
   if (!keep_loading_)
@@ -1017,8 +1017,8 @@
       RenderFrameHostImpl* previous_rfh = RenderFrameHostImpl::FromID(
           navigation_handle->GetPreviousRenderFrameHostId());
       CHECK(previous_rfh) << "Previous RenderFrameHost should not be destroyed "
-                             "without a SwapOut_ACK";
-      SimulateSwapOutACKForPreviousFrameIfNeeded(previous_rfh);
+                             "without a Unload_ACK";
+      SimulateUnloadACKForPreviousFrameIfNeeded(previous_rfh);
       state_ = FINISHED;
     }
     request_ = nullptr;
@@ -1352,20 +1352,21 @@
   render_frame_host_->DidFailLoadWithError(url, error_code, error_description);
 }
 
-void NavigationSimulatorImpl::SimulateSwapOutACKForPreviousFrameIfNeeded(
+void NavigationSimulatorImpl::SimulateUnloadACKForPreviousFrameIfNeeded(
     RenderFrameHostImpl* previous_rfh) {
-  // Do not dispatch SwapOutACK if the navigation was committed in the same
-  // RenderFrameHost.
+  // Do not dispatch FrameHostMsg_Unload_ACK if the navigation was committed in
+  // the same RenderFrameHost.
   if (previous_rfh == render_frame_host_)
     return;
-  if (drop_swap_out_ack_)
+  if (drop_unload_ack_)
     return;
   // The previous RenderFrameHost entered the back-forward cache and hasn't been
-  // requested to swap out. The browser process do not expect any swap out ACK.
+  // requested to unload. The browser process do not expect
+  // FrameHostMsg_Unload_ACK.
   if (previous_rfh->is_in_back_forward_cache())
     return;
   previous_rfh->OnMessageReceived(
-      FrameHostMsg_SwapOut_ACK(previous_rfh->GetRoutingID()));
+      FrameHostMsg_Unload_ACK(previous_rfh->GetRoutingID()));
 }
 
 }  // namespace content
diff --git a/content/test/navigation_simulator_impl.h b/content/test/navigation_simulator_impl.h
index e483c296..7572816 100644
--- a/content/test/navigation_simulator_impl.h
+++ b/content/test/navigation_simulator_impl.h
@@ -149,8 +149,8 @@
   // Whether to drop the swap out ack of the previous RenderFrameHost during
   // cross-process navigations. By default this is false, set to true if you
   // want the old RenderFrameHost to be left in a pending swap out state.
-  void set_drop_swap_out_ack(bool drop_swap_out_ack) {
-    drop_swap_out_ack_ = drop_swap_out_ack;
+  void set_drop_unload_ack(bool drop_unload_ack) {
+    drop_unload_ack_ = drop_unload_ack;
   }
 
   // Whether to drop the BeforeUnloadACK of the current RenderFrameHost at the
@@ -238,9 +238,9 @@
   BuildDidCommitProvisionalLoadParams(bool same_document,
                                       bool failed_navigation);
 
-  // Simulate the UnloadACK in the old RenderFrameHost if it was swapped out at
-  // the commit time.
-  void SimulateSwapOutACKForPreviousFrameIfNeeded(
+  // Simulate the UnloadACK in the old RenderFrameHost if it was unloaded at the
+  // commit time.
+  void SimulateUnloadACKForPreviousFrameIfNeeded(
       RenderFrameHostImpl* previous_frame);
 
   enum State {
@@ -302,7 +302,7 @@
   int64_t post_id_ = -1;
 
   bool auto_advance_ = true;
-  bool drop_swap_out_ack_ = false;
+  bool drop_unload_ack_ = false;
   bool block_on_before_unload_ack_ = false;
   bool keep_loading_ = false;
 
diff --git a/content/test/test_render_frame.cc b/content/test/test_render_frame.cc
index 04e5209..665a8b6 100644
--- a/content/test/test_render_frame.cc
+++ b/content/test/test_render_frame.cc
@@ -273,11 +273,11 @@
                      base::Unretained(mock_frame_host_.get())));
 }
 
-void TestRenderFrame::SwapOut(
+void TestRenderFrame::Unload(
     int proxy_routing_id,
     bool is_loading,
     const FrameReplicationState& replicated_frame_state) {
-  OnSwapOut(proxy_routing_id, is_loading, replicated_frame_state);
+  OnUnload(proxy_routing_id, is_loading, replicated_frame_state);
 }
 
 void TestRenderFrame::SetEditableSelectionOffsets(int start, int end) {
diff --git a/content/test/test_render_frame.h b/content/test/test_render_frame.h
index 8dc9c3f..64b3bdc 100644
--- a/content/test/test_render_frame.h
+++ b/content/test/test_render_frame.h
@@ -52,9 +52,9 @@
                          int error_code,
                          const net::ResolveErrorInfo& resolve_error_info,
                          const base::Optional<std::string>& error_page_content);
-  void SwapOut(int proxy_routing_id,
-               bool is_loading,
-               const FrameReplicationState& replicated_frame_state);
+  void Unload(int proxy_routing_id,
+              bool is_loading,
+              const FrameReplicationState& replicated_frame_state);
   void SetEditableSelectionOffsets(int start, int end);
   void ExtendSelectionAndDelete(int before, int after);
   void DeleteSurroundingText(int before, int after);
diff --git a/content/test/test_render_frame_host.cc b/content/test/test_render_frame_host.cc
index c6fbded..9901baf 100644
--- a/content/test/test_render_frame_host.cc
+++ b/content/test/test_render_frame_host.cc
@@ -215,8 +215,8 @@
   OnBeforeUnloadACK(proceed, now, now);
 }
 
-void TestRenderFrameHost::SimulateSwapOutACK() {
-  OnSwapOutACK();
+void TestRenderFrameHost::SimulateUnloadACK() {
+  OnUnloadACK();
 }
 
 // TODO(loonybear): Add a test for non-bool type PolicyValue.
diff --git a/content/test/test_render_frame_host.h b/content/test/test_render_frame_host.h
index b99d7ba..452ea0a 100644
--- a/content/test/test_render_frame_host.h
+++ b/content/test/test_render_frame_host.h
@@ -82,7 +82,7 @@
                                   const GURL& url,
                                   ui::PageTransition transition);
   void SendBeforeUnloadACK(bool proceed) override;
-  void SimulateSwapOutACK() override;
+  void SimulateUnloadACK() override;
   void SimulateFeaturePolicyHeader(
       blink::mojom::FeaturePolicyFeature feature,
       const std::vector<url::Origin>& allowlist) override;
diff --git a/docs/threading_and_tasks.md b/docs/threading_and_tasks.md
index bb1a725..5a705c2 100644
--- a/docs/threading_and_tasks.md
+++ b/docs/threading_and_tasks.md
@@ -376,6 +376,28 @@
 threads](#prefer-sequences-to-physical-threads) and that this thus should rarely
 be necessary.
 
+### Posting to the Current Thread
+
+*** note
+**IMPORTANT:** To post a task that needs mutual exclusion with the current
+sequence of tasks but doesn’t absolutely need to run on the current physical thread,
+use `base::SequencedTaskRunnerHandle::Get()` instead of
+`base::ThreadTaskRunnerHandle::Get()` (ref. [Posting to the Current
+Sequence](#Posting-to-the-Current-Virtual_Thread)). That will better document the
+requirements of the posted task and will avoid unnecessarily making your API
+physical thread-affine. In a single-thread task, `base::SequencedTaskRunnerHandle::Get()`
+is equivalent to `base::ThreadTaskRunnerHandle::Get()`.
+***
+
+If you must post a task to the current physical thread nonetheless, use
+[`base::ThreadTaskRunnerHandle`](https://cs.chromium.org/chromium/src/base/threading/thread_task_runner_handle.h).
+
+```cpp
+// The task will run on the current thread in the future.
+base::ThreadTaskRunnerHandle::Get()->PostTask(
+    FROM_HERE, base::BindOnce(&Task));
+```
+
 ## Posting Tasks to a COM Single-Thread Apartment (STA) Thread (Windows)
 
 Tasks that need to run on a COM Single-Thread Apartment (STA) thread must be
diff --git a/docs/webui_in_components.md b/docs/webui_in_components.md
index 0251c37..1734726 100644
--- a/docs/webui_in_components.md
+++ b/docs/webui_in_components.md
@@ -12,7 +12,7 @@
 
 # Creating WebUI Interfaces in `components/`
 
-To create a WebUI interface in `components/` you need to follow different steps from [Creating WebUI Interfaces in `chrome/`](https://www.chromium.org/developers/webui). This guide is specific to creating a WebUI interface in `src/components/`. It is based on the steps I went through to create the WebUI infrastructure for chrome://safe-browsing in 'src/components/safe_browsing/web_ui/'.
+To create a WebUI interface in `components/` you need to follow different steps from [Creating WebUI Interfaces in `chrome/`](https://www.chromium.org/developers/webui). This guide is specific to creating a WebUI interface in `src/components/`. It is based on the steps I went through to create the WebUI infrastructure for chrome://safe-browsing in 'src/components/safe_browsing/content/web_ui/'.
 
 [TOC]
 
diff --git a/extensions/BUILD.gn b/extensions/BUILD.gn
index 810e40e..e9c9409 100644
--- a/extensions/BUILD.gn
+++ b/extensions/BUILD.gn
@@ -10,6 +10,9 @@
 import("//ui/base/ui_features.gni")
 
 assert(enable_extensions)
+assert(
+    !(enable_autofill_assistant_api && is_official_build),
+    "The AutofillAssistant Extension API must be disabled in official builds.")
 
 group("extensions_resources") {
   public_deps = [
diff --git a/extensions/browser/sandboxed_unpacker.cc b/extensions/browser/sandboxed_unpacker.cc
index c50a41f..eb9dac8 100644
--- a/extensions/browser/sandboxed_unpacker.cc
+++ b/extensions/browser/sandboxed_unpacker.cc
@@ -460,13 +460,10 @@
     return;
   }
 
-  std::unique_ptr<base::DictionaryValue> manifest_dict =
-      base::DictionaryValue::From(
-          base::Value::ToUniquePtrValue(std::move(manifest.value())));
-
   std::string error_msg;
   scoped_refptr<Extension> extension(
-      Extension::Create(extension_root_, location_, *manifest_dict,
+      Extension::Create(extension_root_, location_,
+                        base::Value::AsDictionaryValue(manifest.value()),
                         creation_flags_, extension_id_, &error_msg));
   if (!extension) {
     ReportUnpackExtensionFailed(error_msg);
@@ -480,18 +477,20 @@
   }
   extension->AddInstallWarnings(std::move(warnings));
 
-  UnpackExtensionSucceeded(std::move(manifest_dict));
+  UnpackExtensionSucceeded(std::move(manifest.value()));
 }
 
-void SandboxedUnpacker::UnpackExtensionSucceeded(
-    std::unique_ptr<base::DictionaryValue> manifest) {
+void SandboxedUnpacker::UnpackExtensionSucceeded(base::Value manifest) {
   DCHECK(unpacker_io_task_runner_->RunsTasksInCurrentSequence());
 
-  std::unique_ptr<base::DictionaryValue> final_manifest(
-      RewriteManifestFile(*manifest));
+  base::Optional<base::Value> final_manifest(RewriteManifestFile(manifest));
   if (!final_manifest)
     return;
 
+  std::unique_ptr<base::DictionaryValue> final_manifest_dict =
+      base::DictionaryValue::From(
+          base::Value::ToUniquePtrValue(std::move(final_manifest.value())));
+
   // Create an extension object that refers to the temporary location the
   // extension was unpacked to. We use this until the extension is finally
   // installed. For example, the install UI shows images from inside the
@@ -503,7 +502,7 @@
   // with base::string16
   std::string utf8_error;
   if (!extension_l10n_util::LocalizeExtension(
-          extension_root_, final_manifest.get(),
+          extension_root_, final_manifest_dict.get(),
           extension_l10n_util::GzippedMessagesPermission::kDisallow,
           &utf8_error)) {
     ReportFailure(
@@ -514,7 +513,7 @@
   }
 
   extension_ =
-      Extension::Create(extension_root_, location_, *final_manifest,
+      Extension::Create(extension_root_, location_, *final_manifest_dict,
                         Extension::REQUIRE_KEY | creation_flags_, &utf8_error);
 
   if (!extension_.get()) {
@@ -542,14 +541,15 @@
     return;
   }
 
+  manifest_ = std::move(manifest);
+
   DCHECK(!image_sanitizer_);
   std::set<base::FilePath> image_paths =
       ExtensionsClient::Get()->GetBrowserImagePaths(extension_.get());
   image_sanitizer_ = ImageSanitizer::CreateAndStart(
       &data_decoder_, extension_root_, image_paths,
       base::BindRepeating(&SandboxedUnpacker::ImageSanitizerDecodedImage, this),
-      base::BindOnce(&SandboxedUnpacker::ImageSanitizationDone, this,
-                     std::move(manifest)));
+      base::BindOnce(&SandboxedUnpacker::ImageSanitizationDone, this));
 }
 
 void SandboxedUnpacker::ImageSanitizerDecodedImage(const base::FilePath& path,
@@ -559,12 +559,11 @@
 }
 
 void SandboxedUnpacker::ImageSanitizationDone(
-    std::unique_ptr<base::DictionaryValue> manifest,
     ImageSanitizer::Status status,
     const base::FilePath& file_path_for_error) {
   if (status == ImageSanitizer::Status::kSuccess) {
     // Next step is to sanitize the message catalogs.
-    ReadMessageCatalogs(std::move(manifest));
+    ReadMessageCatalogs();
     return;
   }
 
@@ -621,12 +620,10 @@
   ReportFailure(failure_reason, error);
 }
 
-void SandboxedUnpacker::ReadMessageCatalogs(
-    std::unique_ptr<base::DictionaryValue> manifest) {
+void SandboxedUnpacker::ReadMessageCatalogs() {
   DCHECK(unpacker_io_task_runner_->RunsTasksInCurrentSequence());
   if (LocaleInfo::GetDefaultLocale(extension_.get()).empty()) {
-    MessageCatalogsSanitized(std::move(manifest),
-                             JsonFileSanitizer::Status::kSuccess,
+    MessageCatalogsSanitized(JsonFileSanitizer::Status::kSuccess,
                              std::string());
     return;
   }
@@ -638,27 +635,23 @@
   base::PostTaskAndReplyWithResult(
       extensions::GetExtensionFileTaskRunner().get(), FROM_HERE,
       base::BindOnce(&GetMessageCatalogPathsToBeSanitized, locales_path),
-      base::BindOnce(&SandboxedUnpacker::SanitizeMessageCatalogs, this,
-                     std::move(manifest)));
+      base::BindOnce(&SandboxedUnpacker::SanitizeMessageCatalogs, this));
 }
 
 void SandboxedUnpacker::SanitizeMessageCatalogs(
-    std::unique_ptr<base::DictionaryValue> manifest,
     const std::set<base::FilePath>& message_catalog_paths) {
   DCHECK(unpacker_io_task_runner_->RunsTasksInCurrentSequence());
   json_file_sanitizer_ = JsonFileSanitizer::CreateAndStart(
       &data_decoder_, message_catalog_paths,
-      base::BindOnce(&SandboxedUnpacker::MessageCatalogsSanitized, this,
-                     std::move(manifest)));
+      base::BindOnce(&SandboxedUnpacker::MessageCatalogsSanitized, this));
 }
 
 void SandboxedUnpacker::MessageCatalogsSanitized(
-    std::unique_ptr<base::DictionaryValue> manifest,
     JsonFileSanitizer::Status status,
     const std::string& error_msg) {
   DCHECK(unpacker_io_task_runner_->RunsTasksInCurrentSequence());
   if (status == JsonFileSanitizer::Status::kSuccess) {
-    IndexAndPersistJSONRulesetIfNeeded(std::move(manifest));
+    IndexAndPersistJSONRulesetIfNeeded();
     return;
   }
 
@@ -693,27 +686,24 @@
   ReportFailure(failure_reason, error);
 }
 
-void SandboxedUnpacker::IndexAndPersistJSONRulesetIfNeeded(
-    std::unique_ptr<base::DictionaryValue> manifest) {
+void SandboxedUnpacker::IndexAndPersistJSONRulesetIfNeeded() {
   DCHECK(unpacker_io_task_runner_->RunsTasksInCurrentSequence());
   DCHECK(extension_);
 
   if (!declarative_net_request::DNRManifestData::HasRuleset(*extension_)) {
     // The extension did not provide a ruleset.
-    CheckComputeHashes(std::move(manifest),
-                       base::nullopt /*dnr_ruleset_checksum*/);
+    CheckComputeHashes();
     return;
   }
 
   auto ruleset_source =
       declarative_net_request::RulesetSource::CreateStatic(*extension_);
   ruleset_source.IndexAndPersistJSONRuleset(
-      &data_decoder_, base::BindOnce(&SandboxedUnpacker::OnJSONRulesetIndexed,
-                                     this, std::move(manifest)));
+      &data_decoder_,
+      base::BindOnce(&SandboxedUnpacker::OnJSONRulesetIndexed, this));
 }
 
 void SandboxedUnpacker::OnJSONRulesetIndexed(
-    std::unique_ptr<base::DictionaryValue> manifest,
     declarative_net_request::IndexAndPersistJSONRulesetResult result) {
   if (result.success) {
     if (!result.warnings.empty())
@@ -724,7 +714,8 @@
     UMA_HISTOGRAM_TIMES(
         declarative_net_request::kIndexAndPersistRulesTimeHistogram,
         result.index_and_persist_time);
-    CheckComputeHashes(std::move(manifest), result.ruleset_checksum);
+    dnr_ruleset_checksum_ = result.ruleset_checksum;
+    CheckComputeHashes();
     return;
   }
 
@@ -733,22 +724,16 @@
                                            base::UTF8ToUTF16(result.error)));
 }
 
-void SandboxedUnpacker::CheckComputeHashes(
-    std::unique_ptr<base::DictionaryValue> manifest,
-    const base::Optional<int>& dnr_ruleset_checksum) {
+void SandboxedUnpacker::CheckComputeHashes() {
   DCHECK(unpacker_io_task_runner_->RunsTasksInCurrentSequence());
   client_->ShouldComputeHashesForOffWebstoreExtension(
-      extension_, base::BindOnce(&SandboxedUnpacker::MaybeComputeHashes, this,
-                                 std::move(manifest), dnr_ruleset_checksum));
+      extension_, base::BindOnce(&SandboxedUnpacker::MaybeComputeHashes, this));
 }
 
-void SandboxedUnpacker::MaybeComputeHashes(
-    std::unique_ptr<base::DictionaryValue> original_manifest,
-    const base::Optional<int>& dnr_ruleset_checksum,
-    bool should_compute) {
+void SandboxedUnpacker::MaybeComputeHashes(bool should_compute) {
   DCHECK(unpacker_io_task_runner_->RunsTasksInCurrentSequence());
   if (!should_compute) {
-    ReportSuccess(std::move(original_manifest), dnr_ruleset_checksum);
+    ReportSuccess();
     return;
   }
 
@@ -765,7 +750,7 @@
                << "] Failed to create computed_hashes.json";
   }
 
-  ReportSuccess(std::move(original_manifest), dnr_ruleset_checksum);
+  ReportSuccess();
 }
 
 data_decoder::mojom::JsonParser* SandboxedUnpacker::GetJsonParserPtr() {
@@ -975,9 +960,7 @@
   client_->OnUnpackFailure(CrxInstallError(reason, error));
 }
 
-void SandboxedUnpacker::ReportSuccess(
-    std::unique_ptr<base::DictionaryValue> original_manifest,
-    const base::Optional<int>& dnr_ruleset_checksum) {
+void SandboxedUnpacker::ReportSuccess() {
   DCHECK(unpacker_io_task_runner_->RunsTasksInCurrentSequence());
 
   UMA_HISTOGRAM_COUNTS_1M("Extensions.SandboxUnpackSuccess", 1);
@@ -989,47 +972,48 @@
   DCHECK(!temp_dir_.GetPath().empty());
 
   // Client takes ownership of temporary directory, manifest, and extension.
-  client_->OnUnpackSuccess(temp_dir_.Take(), extension_root_,
-                           std::move(original_manifest), extension_.get(),
-                           install_icon_, dnr_ruleset_checksum);
+  client_->OnUnpackSuccess(
+      temp_dir_.Take(), extension_root_,
+      base::DictionaryValue::From(
+          base::Value::ToUniquePtrValue(std::move(manifest_.value()))),
+      extension_.get(), install_icon_, dnr_ruleset_checksum_);
   extension_.reset();
 
   Cleanup();
 }
 
-base::DictionaryValue* SandboxedUnpacker::RewriteManifestFile(
-    const base::DictionaryValue& manifest) {
+base::Optional<base::Value> SandboxedUnpacker::RewriteManifestFile(
+    const base::Value& manifest) {
   constexpr int64_t kMaxFingerprintSize = 1024;
 
   // Add the public key extracted earlier to the parsed manifest and overwrite
   // the original manifest. We do this to ensure the manifest doesn't contain an
   // exploitable bug that could be used to compromise the browser.
   DCHECK(!public_key_.empty());
-  std::unique_ptr<base::DictionaryValue> final_manifest =
-      manifest.CreateDeepCopy();
-  final_manifest->SetString(manifest_keys::kPublicKey, public_key_);
+  base::Value final_manifest = manifest.Clone();
+  final_manifest.SetStringKey(manifest_keys::kPublicKey, public_key_);
 
   {
     std::string differential_fingerprint;
     if (base::ReadFileToStringWithMaxSize(
             extension_root_.Append(kDifferentialFingerprintFilename),
             &differential_fingerprint, kMaxFingerprintSize)) {
-      final_manifest->SetStringKey(manifest_keys::kDifferentialFingerprint,
-                                   std::move(differential_fingerprint));
+      final_manifest.SetStringKey(manifest_keys::kDifferentialFingerprint,
+                                  std::move(differential_fingerprint));
     }
   }
 
   std::string manifest_json;
   JSONStringValueSerializer serializer(&manifest_json);
   serializer.set_pretty_print(true);
-  if (!serializer.Serialize(*final_manifest)) {
+  if (!serializer.Serialize(final_manifest)) {
     // Error serializing manifest.json.
     ReportFailure(
         SandboxedUnpackerFailureReason::ERROR_SERIALIZING_MANIFEST_JSON,
         l10n_util::GetStringFUTF16(
             IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
             ASCIIToUTF16("ERROR_SERIALIZING_MANIFEST_JSON")));
-    return NULL;
+    return base::nullopt;
   }
 
   base::FilePath manifest_path = extension_root_.Append(kManifestFilename);
@@ -1040,10 +1024,10 @@
         SandboxedUnpackerFailureReason::ERROR_SAVING_MANIFEST_JSON,
         l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
                                    ASCIIToUTF16("ERROR_SAVING_MANIFEST_JSON")));
-    return NULL;
+    return base::nullopt;
   }
 
-  return final_manifest.release();
+  return std::move(final_manifest);
 }
 
 void SandboxedUnpacker::Cleanup() {
diff --git a/extensions/browser/sandboxed_unpacker.h b/extensions/browser/sandboxed_unpacker.h
index f3e1038..f2d4902 100644
--- a/extensions/browser/sandboxed_unpacker.h
+++ b/extensions/browser/sandboxed_unpacker.h
@@ -185,30 +185,25 @@
   void Unpack(const base::FilePath& directory);
   void ReadManifestDone(base::Optional<base::Value> manifest,
                         const base::Optional<std::string>& error);
-  void UnpackExtensionSucceeded(
-      std::unique_ptr<base::DictionaryValue> manifest);
+  void UnpackExtensionSucceeded(base::Value manifest);
 
   // Helper which calls ReportFailure.
   void ReportUnpackExtensionFailed(base::StringPiece error);
 
-  void ImageSanitizationDone(std::unique_ptr<base::DictionaryValue> manifest,
-                             ImageSanitizer::Status status,
+  void ImageSanitizationDone(ImageSanitizer::Status status,
                              const base::FilePath& path);
   void ImageSanitizerDecodedImage(const base::FilePath& path, SkBitmap image);
 
-  void ReadMessageCatalogs(std::unique_ptr<base::DictionaryValue> manifest);
+  void ReadMessageCatalogs();
 
   void SanitizeMessageCatalogs(
-      std::unique_ptr<base::DictionaryValue> manifest,
       const std::set<base::FilePath>& message_catalog_paths);
 
-  void MessageCatalogsSanitized(std::unique_ptr<base::DictionaryValue> manifest,
-                                JsonFileSanitizer::Status status,
+  void MessageCatalogsSanitized(JsonFileSanitizer::Status status,
                                 const std::string& error_msg);
 
   // Reports unpack success or failure, or unzip failure.
-  void ReportSuccess(std::unique_ptr<base::DictionaryValue> original_manifest,
-                     const base::Optional<int>& dnr_ruleset_checksum);
+  void ReportSuccess();
 
   // Puts a sanboxed unpacker failure in histogram
   // Extensions.SandboxUnpackFailureReason.
@@ -216,9 +211,8 @@
                      const base::string16& error);
 
   // Overwrites original manifest with safe result from utility process.
-  // Returns NULL on error. Caller owns the returned object.
-  base::DictionaryValue* RewriteManifestFile(
-      const base::DictionaryValue& manifest);
+  // Returns nullopt on error.
+  base::Optional<base::Value> RewriteManifestFile(const base::Value& manifest);
 
   // Cleans up temp directory artifacts.
   void Cleanup();
@@ -226,25 +220,18 @@
   // If a Declarative Net Request JSON ruleset is present, parses the JSON
   // ruleset for the Declarative Net Request API and persists the indexed
   // ruleset.
-  void IndexAndPersistJSONRulesetIfNeeded(
-      std::unique_ptr<base::DictionaryValue> manifest);
+  void IndexAndPersistJSONRulesetIfNeeded();
 
   void OnJSONRulesetIndexed(
-      std::unique_ptr<base::DictionaryValue> manifest,
       declarative_net_request::IndexAndPersistJSONRulesetResult result);
 
   // Computed hashes: if requested (via ShouldComputeHashes callback in
   // SandbloxedUnpackerClient), calculate hashes of all extensions' resources
   // and writes them in _metadata/computed_hashes.json. This is used by content
   // verification system for extensions outside of Chrome Web Store.
-  void CheckComputeHashes(
-      std::unique_ptr<base::DictionaryValue> original_manifest,
-      const base::Optional<int>& dnr_ruleset_checksum);
+  void CheckComputeHashes();
 
-  void MaybeComputeHashes(
-      std::unique_ptr<base::DictionaryValue> original_manifest,
-      const base::Optional<int>& dnr_ruleset_checksum,
-      bool should_compute_hashes);
+  void MaybeComputeHashes(bool should_compute_hashes);
 
   // Returns a JsonParser that can be used on the |unpacker_io_task_runner|.
   data_decoder::mojom::JsonParser* GetJsonParserPtr();
@@ -271,6 +258,15 @@
   // Root directory of the unpacked extension (a child of temp_dir_).
   base::FilePath extension_root_;
 
+  // Parsed original manifest of the extension. Set after unpacking the
+  // extension and working with its manifest, so after UnpackExtensionSucceeded
+  // is called.
+  base::Optional<base::Value> manifest_;
+
+  // Checksum for the indexed ruleset, see more in
+  // SandboxedUnpackerClient::OnUnpackSuccess description.
+  base::Optional<int> dnr_ruleset_checksum_;
+
   // Represents the extension we're unpacking.
   scoped_refptr<Extension> extension_;
 
diff --git a/extensions/browser/sandboxed_unpacker_unittest.cc b/extensions/browser/sandboxed_unpacker_unittest.cc
index ca1c84c..c0fdb2e8 100644
--- a/extensions/browser/sandboxed_unpacker_unittest.cc
+++ b/extensions/browser/sandboxed_unpacker_unittest.cc
@@ -265,8 +265,7 @@
     sandboxed_unpacker_->extension_root_ = path;
   }
 
-  base::DictionaryValue* RewriteManifestFile(
-      const base::DictionaryValue& manifest) {
+  base::Optional<base::Value> RewriteManifestFile(const base::Value& manifest) {
     return sandboxed_unpacker_->RewriteManifestFile(manifest);
   }
 
@@ -445,7 +444,7 @@
                       FILE_PATH_LITERAL("manifest.fingerprint")),
                   fingerprint.c_str(),
                   base::checked_cast<int>(fingerprint.size()));
-  std::unique_ptr<base::DictionaryValue> manifest(RewriteManifestFile(
+  base::Optional<base::Value> manifest(RewriteManifestFile(
       *DictionaryBuilder().Set(kVersionStr, kTestVersion).Build()));
   auto* key = manifest->FindStringKey("key");
   auto* version = manifest->FindStringKey(kVersionStr);
diff --git a/extensions/buildflags/BUILD.gn b/extensions/buildflags/BUILD.gn
index 6a36fa14..4b92132 100644
--- a/extensions/buildflags/BUILD.gn
+++ b/extensions/buildflags/BUILD.gn
@@ -17,5 +17,6 @@
   flags = [
     "ENABLE_EXTENSIONS=$enable_extensions",
     "ENABLE_WIFI_DISPLAY=$enable_wifi_display",
+    "ENABLE_AUTOFILL_ASSISTANT_API=$enable_autofill_assistant_api",
   ]
 }
diff --git a/extensions/buildflags/buildflags.gni b/extensions/buildflags/buildflags.gni
index 6836e4a..35d9c6ce 100644
--- a/extensions/buildflags/buildflags.gni
+++ b/extensions/buildflags/buildflags.gni
@@ -10,4 +10,8 @@
   # Enables Wi-Fi Display functionality
   # WARNING: This enables MPEG Transport Stream (MPEG-TS) encoding!
   enable_wifi_display = false
+
+  # Compile time flag for the Autofill Assistant API.
+  # WARNING: This must not be enabled in official builds.
+  enable_autofill_assistant_api = false
 }
diff --git a/headless/lib/browser/headless_web_contents_impl.cc b/headless/lib/browser/headless_web_contents_impl.cc
index 84a8833..93898e5 100644
--- a/headless/lib/browser/headless_web_contents_impl.cc
+++ b/headless/lib/browser/headless_web_contents_impl.cc
@@ -85,8 +85,7 @@
     return security_state::GetSecurityStyle(
         security_state::GetSecurityLevel(
             *visible_security_state.get(),
-            false /* used_policy_installed_certificate */,
-            base::BindRepeating(&content::IsOriginSecure)),
+            false /* used_policy_installed_certificate */),
         *visible_security_state.get(), security_style_explanations);
   }
 #endif  // !defined(CHROME_MULTIPLE_DLL_CHILD)
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index 5072748..fd01e71e0 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -1061,7 +1061,7 @@
       <message name="IDS_IOS_OPTIONS_ACCOUNTS_DESCRIPTION" desc="Text informing the user of the accounts they are signed in to Chrome with [iOS only] [150em]">
         Signed In to Google as
       </message>
-      <message name="IDS_IOS_OPTIONS_ACCOUNTS_SYNC_IS_OFF" desc="Text informing the user that sync is off [iOS only] [60em]">
+      <message name="IDS_IOS_OPTIONS_ACCOUNTS_SYNC_IS_OFF" desc="Text informing the user that sync is off [iOS only] [60em]" meaning="The Chrome sync feature is off.">
         Off
       </message>
       <message name="IDS_IOS_OPTIONS_ACCOUNTS_SIGN_OUT_TURN_OFF_SYNC" desc="Title of the button to sign out and turn off sync. [iOS only]">
@@ -1513,10 +1513,10 @@
       <message name="IDS_IOS_SETTINGS_TITLE" desc="Title for the settings panel [iOS].">
         Settings
       </message>
-      <message name="IDS_IOS_SETTING_OFF" desc="Generic status label displayed in Settings to show that a setting is currently turned off. [Length: 5em] [iOS only]">
+      <message name="IDS_IOS_SETTING_OFF" desc="Generic status label displayed in Settings to show that a setting is currently turned off. [Length: 5em] [iOS only]" meaning="The setting is off.">
         Off
       </message>
-      <message name="IDS_IOS_SETTING_ON" desc="Generic status label displayed in Settings to show that a setting is currently turned on. [Length: 5em] [iOS only]">
+      <message name="IDS_IOS_SETTING_ON" desc="Generic status label displayed in Settings to show that a setting is currently turned on. [Length: 5em] [iOS only]" meaning="The setting is on.">
         On
       </message>
       <message name="IDS_IOS_SETTINGS_TOOLBAR_DELETE" desc="Label of the button displayed on the bottom toolbar of the settings' collections when they are in edit mode. Pressing this button deletes the currently selected items.">
diff --git a/ios/chrome/app/theme/default_100_percent/controlled_setting_mandatory.png b/ios/chrome/app/theme/default_100_percent/controlled_setting_mandatory.png
deleted file mode 100644
index 5735ead5..0000000
--- a/ios/chrome/app/theme/default_100_percent/controlled_setting_mandatory.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_100_percent/omnibox/omnibox_clear.png b/ios/chrome/app/theme/default_100_percent/omnibox/omnibox_clear.png
deleted file mode 100644
index 227c95f..0000000
--- a/ios/chrome/app/theme/default_100_percent/omnibox/omnibox_clear.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_100_percent/omnibox/omnibox_clear_pressed.png b/ios/chrome/app/theme/default_100_percent/omnibox/omnibox_clear_pressed.png
deleted file mode 100644
index b4d48bd..0000000
--- a/ios/chrome/app/theme/default_100_percent/omnibox/omnibox_clear_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_100_percent/omnibox/omnibox_incognito_clear.png b/ios/chrome/app/theme/default_100_percent/omnibox/omnibox_incognito_clear.png
deleted file mode 100644
index bf057c2..0000000
--- a/ios/chrome/app/theme/default_100_percent/omnibox/omnibox_incognito_clear.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_100_percent/omnibox/omnibox_incognito_clear_pressed.png b/ios/chrome/app/theme/default_100_percent/omnibox/omnibox_incognito_clear_pressed.png
deleted file mode 100644
index 468d1fb..0000000
--- a/ios/chrome/app/theme/default_100_percent/omnibox/omnibox_incognito_clear_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_100_percent/omnibox/pageinfo_bad.png b/ios/chrome/app/theme/default_100_percent/omnibox/pageinfo_bad.png
deleted file mode 100644
index c036507..0000000
--- a/ios/chrome/app/theme/default_100_percent/omnibox/pageinfo_bad.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_100_percent/omnibox/pageinfo_good.png b/ios/chrome/app/theme/default_100_percent/omnibox/pageinfo_good.png
deleted file mode 100644
index 4bd8c5bc..0000000
--- a/ios/chrome/app/theme/default_100_percent/omnibox/pageinfo_good.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_100_percent/omnibox/pageinfo_info.png b/ios/chrome/app/theme/default_100_percent/omnibox/pageinfo_info.png
deleted file mode 100644
index f88ad36..0000000
--- a/ios/chrome/app/theme/default_100_percent/omnibox/pageinfo_info.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_100_percent/omnibox_calculator.png b/ios/chrome/app/theme/default_100_percent/omnibox_calculator.png
deleted file mode 100644
index 33dd687..0000000
--- a/ios/chrome/app/theme/default_100_percent/omnibox_calculator.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_100_percent/omnibox_history.png b/ios/chrome/app/theme/default_100_percent/omnibox_history.png
deleted file mode 100644
index 9b63e262..0000000
--- a/ios/chrome/app/theme/default_100_percent/omnibox_history.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_100_percent/omnibox_history_incognito.png b/ios/chrome/app/theme/default_100_percent/omnibox_history_incognito.png
deleted file mode 100644
index 418d0b4..0000000
--- a/ios/chrome/app/theme/default_100_percent/omnibox_history_incognito.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_100_percent/omnibox_http.png b/ios/chrome/app/theme/default_100_percent/omnibox_http.png
deleted file mode 100644
index 8de583e..0000000
--- a/ios/chrome/app/theme/default_100_percent/omnibox_http.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_100_percent/omnibox_http_incognito.png b/ios/chrome/app/theme/default_100_percent/omnibox_http_incognito.png
deleted file mode 100644
index 2f5e5e8..0000000
--- a/ios/chrome/app/theme/default_100_percent/omnibox_http_incognito.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_100_percent/omnibox_https_invalid.png b/ios/chrome/app/theme/default_100_percent/omnibox_https_invalid.png
deleted file mode 100644
index ba50e5e..0000000
--- a/ios/chrome/app/theme/default_100_percent/omnibox_https_invalid.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_100_percent/omnibox_offline.png b/ios/chrome/app/theme/default_100_percent/omnibox_offline.png
deleted file mode 100644
index 6c4bd2b5..0000000
--- a/ios/chrome/app/theme/default_100_percent/omnibox_offline.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_100_percent/omnibox_search.png b/ios/chrome/app/theme/default_100_percent/omnibox_search.png
deleted file mode 100644
index 2a8eb96..0000000
--- a/ios/chrome/app/theme/default_100_percent/omnibox_search.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_100_percent/omnibox_search_incognito.png b/ios/chrome/app/theme/default_100_percent/omnibox_search_incognito.png
deleted file mode 100644
index 4c84df0..0000000
--- a/ios/chrome/app/theme/default_100_percent/omnibox_search_incognito.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_100_percent/omnibox_star.png b/ios/chrome/app/theme/default_100_percent/omnibox_star.png
deleted file mode 100644
index 4a31fec..0000000
--- a/ios/chrome/app/theme/default_100_percent/omnibox_star.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_100_percent/omnibox_star_incognito.png b/ios/chrome/app/theme/default_100_percent/omnibox_star_incognito.png
deleted file mode 100644
index b903f5c..0000000
--- a/ios/chrome/app/theme/default_100_percent/omnibox_star_incognito.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_200_percent/controlled_setting_mandatory.png b/ios/chrome/app/theme/default_200_percent/controlled_setting_mandatory.png
deleted file mode 100644
index ae0fee0f..0000000
--- a/ios/chrome/app/theme/default_200_percent/controlled_setting_mandatory.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_200_percent/omnibox/omnibox_clear.png b/ios/chrome/app/theme/default_200_percent/omnibox/omnibox_clear.png
deleted file mode 100644
index 0686d7e..0000000
--- a/ios/chrome/app/theme/default_200_percent/omnibox/omnibox_clear.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_200_percent/omnibox/omnibox_clear_pressed.png b/ios/chrome/app/theme/default_200_percent/omnibox/omnibox_clear_pressed.png
deleted file mode 100644
index f8b0c14..0000000
--- a/ios/chrome/app/theme/default_200_percent/omnibox/omnibox_clear_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_200_percent/omnibox/omnibox_incognito_clear.png b/ios/chrome/app/theme/default_200_percent/omnibox/omnibox_incognito_clear.png
deleted file mode 100644
index c926937..0000000
--- a/ios/chrome/app/theme/default_200_percent/omnibox/omnibox_incognito_clear.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_200_percent/omnibox/omnibox_incognito_clear_pressed.png b/ios/chrome/app/theme/default_200_percent/omnibox/omnibox_incognito_clear_pressed.png
deleted file mode 100644
index 5ba3339..0000000
--- a/ios/chrome/app/theme/default_200_percent/omnibox/omnibox_incognito_clear_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_200_percent/omnibox_calculator.png b/ios/chrome/app/theme/default_200_percent/omnibox_calculator.png
deleted file mode 100644
index e1f0bfc..0000000
--- a/ios/chrome/app/theme/default_200_percent/omnibox_calculator.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_200_percent/omnibox_history.png b/ios/chrome/app/theme/default_200_percent/omnibox_history.png
deleted file mode 100644
index a1ee777..0000000
--- a/ios/chrome/app/theme/default_200_percent/omnibox_history.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_200_percent/omnibox_history_incognito.png b/ios/chrome/app/theme/default_200_percent/omnibox_history_incognito.png
deleted file mode 100644
index 6738098..0000000
--- a/ios/chrome/app/theme/default_200_percent/omnibox_history_incognito.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_200_percent/omnibox_http.png b/ios/chrome/app/theme/default_200_percent/omnibox_http.png
deleted file mode 100644
index cf21532..0000000
--- a/ios/chrome/app/theme/default_200_percent/omnibox_http.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_200_percent/omnibox_http_incognito.png b/ios/chrome/app/theme/default_200_percent/omnibox_http_incognito.png
deleted file mode 100644
index adb55f15..0000000
--- a/ios/chrome/app/theme/default_200_percent/omnibox_http_incognito.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_200_percent/omnibox_https_invalid.png b/ios/chrome/app/theme/default_200_percent/omnibox_https_invalid.png
deleted file mode 100644
index f797139..0000000
--- a/ios/chrome/app/theme/default_200_percent/omnibox_https_invalid.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_200_percent/omnibox_search.png b/ios/chrome/app/theme/default_200_percent/omnibox_search.png
deleted file mode 100644
index 72c30de..0000000
--- a/ios/chrome/app/theme/default_200_percent/omnibox_search.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_200_percent/omnibox_search_incognito.png b/ios/chrome/app/theme/default_200_percent/omnibox_search_incognito.png
deleted file mode 100644
index a1aaa55..0000000
--- a/ios/chrome/app/theme/default_200_percent/omnibox_search_incognito.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_200_percent/omnibox_star.png b/ios/chrome/app/theme/default_200_percent/omnibox_star.png
deleted file mode 100644
index e3aa7df6..0000000
--- a/ios/chrome/app/theme/default_200_percent/omnibox_star.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_200_percent/omnibox_star_incognito.png b/ios/chrome/app/theme/default_200_percent/omnibox_star_incognito.png
deleted file mode 100644
index 62af06b1..0000000
--- a/ios/chrome/app/theme/default_200_percent/omnibox_star_incognito.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_300_percent/controlled_setting_mandatory.png b/ios/chrome/app/theme/default_300_percent/controlled_setting_mandatory.png
deleted file mode 100644
index b83207e..0000000
--- a/ios/chrome/app/theme/default_300_percent/controlled_setting_mandatory.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_300_percent/omnibox/omnibox_clear.png b/ios/chrome/app/theme/default_300_percent/omnibox/omnibox_clear.png
deleted file mode 100644
index 5ab58ae..0000000
--- a/ios/chrome/app/theme/default_300_percent/omnibox/omnibox_clear.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_300_percent/omnibox/omnibox_clear_pressed.png b/ios/chrome/app/theme/default_300_percent/omnibox/omnibox_clear_pressed.png
deleted file mode 100644
index 3acd53f..0000000
--- a/ios/chrome/app/theme/default_300_percent/omnibox/omnibox_clear_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_300_percent/omnibox/omnibox_incognito_clear.png b/ios/chrome/app/theme/default_300_percent/omnibox/omnibox_incognito_clear.png
deleted file mode 100644
index 8a6e258a..0000000
--- a/ios/chrome/app/theme/default_300_percent/omnibox/omnibox_incognito_clear.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_300_percent/omnibox/omnibox_incognito_clear_pressed.png b/ios/chrome/app/theme/default_300_percent/omnibox/omnibox_incognito_clear_pressed.png
deleted file mode 100644
index 3cdbb99..0000000
--- a/ios/chrome/app/theme/default_300_percent/omnibox/omnibox_incognito_clear_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_300_percent/omnibox_calculator.png b/ios/chrome/app/theme/default_300_percent/omnibox_calculator.png
deleted file mode 100644
index ac0e6755..0000000
--- a/ios/chrome/app/theme/default_300_percent/omnibox_calculator.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_300_percent/omnibox_history.png b/ios/chrome/app/theme/default_300_percent/omnibox_history.png
deleted file mode 100644
index cf654021..0000000
--- a/ios/chrome/app/theme/default_300_percent/omnibox_history.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_300_percent/omnibox_history_incognito.png b/ios/chrome/app/theme/default_300_percent/omnibox_history_incognito.png
deleted file mode 100644
index 44537f1..0000000
--- a/ios/chrome/app/theme/default_300_percent/omnibox_history_incognito.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_300_percent/omnibox_http.png b/ios/chrome/app/theme/default_300_percent/omnibox_http.png
deleted file mode 100644
index 2830184..0000000
--- a/ios/chrome/app/theme/default_300_percent/omnibox_http.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_300_percent/omnibox_http_incognito.png b/ios/chrome/app/theme/default_300_percent/omnibox_http_incognito.png
deleted file mode 100644
index 3cb92d6..0000000
--- a/ios/chrome/app/theme/default_300_percent/omnibox_http_incognito.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_300_percent/omnibox_https_invalid.png b/ios/chrome/app/theme/default_300_percent/omnibox_https_invalid.png
deleted file mode 100644
index 2b63261..0000000
--- a/ios/chrome/app/theme/default_300_percent/omnibox_https_invalid.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_300_percent/omnibox_search.png b/ios/chrome/app/theme/default_300_percent/omnibox_search.png
deleted file mode 100644
index 79ed6e0c..0000000
--- a/ios/chrome/app/theme/default_300_percent/omnibox_search.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_300_percent/omnibox_search_incognito.png b/ios/chrome/app/theme/default_300_percent/omnibox_search_incognito.png
deleted file mode 100644
index 1892d6f..0000000
--- a/ios/chrome/app/theme/default_300_percent/omnibox_search_incognito.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_300_percent/omnibox_star.png b/ios/chrome/app/theme/default_300_percent/omnibox_star.png
deleted file mode 100644
index 3e156e3..0000000
--- a/ios/chrome/app/theme/default_300_percent/omnibox_star.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/default_300_percent/omnibox_star_incognito.png b/ios/chrome/app/theme/default_300_percent/omnibox_star_incognito.png
deleted file mode 100644
index 79d035e..0000000
--- a/ios/chrome/app/theme/default_300_percent/omnibox_star_incognito.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/app/theme/ios_theme_resources.grd b/ios/chrome/app/theme/ios_theme_resources.grd
index 2ff02ded..a1ae3fa 100644
--- a/ios/chrome/app/theme/ios_theme_resources.grd
+++ b/ios/chrome/app/theme/ios_theme_resources.grd
@@ -26,7 +26,6 @@
            BECAUSE YOUR RESOURCES ARE FUNCTIONALLY RELATED OR FALL UNDER THE
            SAME CONDITIONALS. -->
       <structure type="chrome_scaled_image" name="IDR_IOS_CHECKMARK" file="checkmark.png" />
-      <structure type="chrome_scaled_image" name="IDR_IOS_CONTROLLED_SETTING_MANDATORY" file="controlled_setting_mandatory.png" />
       <structure type="chrome_scaled_image" name="IDR_IOS_ERROR" file="error.png" />
       <structure type="chrome_scaled_image" name="IDR_IOS_INFOBAR_AUTOLOGIN" file="infobar_autologin.png" />
       <structure type="chrome_scaled_image" name="IDR_IOS_INFOBAR_CLOSE" file="infobar_close.png" />
@@ -34,28 +33,8 @@
       <structure type="chrome_scaled_image" name="IDR_IOS_INFOBAR_SAVE_PASSWORD" file="infobar_save_password.png" />
       <structure type="chrome_scaled_image" name="IDR_IOS_INFOBAR_SEND_TAB_TO_SELF" file="infobar_send_tab_to_self.png" />
       <structure type="chrome_scaled_image" name="IDR_IOS_INFOBAR_TRANSLATE" file="infobar_translate.png" />
-      <structure type="chrome_scaled_image" name="IDR_IOS_LOCATION_BAR_HTTP" file="omnibox_http.png" />
-      <structure type="chrome_scaled_image" name="IDR_IOS_OMNIBOX_CALCULATOR" file="omnibox_calculator.png" />
-      <structure type="chrome_scaled_image" name="IDR_IOS_OMNIBOX_CLEAR" file="omnibox/omnibox_clear.png" />
-      <structure type="chrome_scaled_image" name="IDR_IOS_OMNIBOX_CLEAR_OTR" file="omnibox/omnibox_incognito_clear.png" />
-      <structure type="chrome_scaled_image" name="IDR_IOS_OMNIBOX_CLEAR_OTR_PRESSED" file="omnibox/omnibox_incognito_clear_pressed.png" />
-      <structure type="chrome_scaled_image" name="IDR_IOS_OMNIBOX_CLEAR_PRESSED" file="omnibox/omnibox_clear_pressed.png" />
-      <structure type="chrome_scaled_image" name="IDR_IOS_OMNIBOX_HISTORY" file="omnibox_history.png" />
-      <structure type="chrome_scaled_image" name="IDR_IOS_OMNIBOX_HISTORY_INCOGNITO" file="omnibox_history_incognito.png" />
-      <structure type="chrome_scaled_image" name="IDR_IOS_OMNIBOX_HTTP" file="omnibox_http.png" />
-      <structure type="chrome_scaled_image" name="IDR_IOS_OMNIBOX_HTTP_INCOGNITO" file="omnibox_http_incognito.png" />
-      <structure type="chrome_scaled_image" name="IDR_IOS_OMNIBOX_HTTPS_INVALID" file="omnibox_https_invalid.png" />
-      <structure type="chrome_scaled_image" name="IDR_IOS_OMNIBOX_HTTPS_POLICY_WARNING" file="controlled_setting_mandatory.png" />
       <structure type="chrome_scaled_image" name="IDR_IOS_OMNIBOX_HTTPS_VALID" file="omnibox_https_valid.png" />
       <structure type="chrome_scaled_image" name="IDR_IOS_OMNIBOX_KEYBOARD_VIEW_APPEND" file="omnibox/append_ios.png" />
-      <structure type="chrome_scaled_image" name="IDR_IOS_OMNIBOX_OFFLINE" file="omnibox_offline.png" />
-      <structure type="chrome_scaled_image" name="IDR_IOS_OMNIBOX_SEARCH" file="omnibox_search.png" />
-      <structure type="chrome_scaled_image" name="IDR_IOS_OMNIBOX_SEARCH_INCOGNITO" file="omnibox_search_incognito.png" />
-      <structure type="chrome_scaled_image" name="IDR_IOS_OMNIBOX_STAR" file="omnibox_star.png" />
-      <structure type="chrome_scaled_image" name="IDR_IOS_OMNIBOX_STAR_INCOGNITO" file="omnibox_star_incognito.png" />
-      <structure type="chrome_scaled_image" name="IDR_IOS_PAGEINFO_BAD" file="omnibox/pageinfo_bad.png" />
-      <structure type="chrome_scaled_image" name="IDR_IOS_PAGEINFO_GOOD" file="omnibox/pageinfo_good.png" />
-      <structure type="chrome_scaled_image" name="IDR_IOS_PAGEINFO_INFO" file="omnibox/pageinfo_info.png" />
       <structure type="chrome_scaled_image" name="IDR_IOS_PAYMENTS_WARNING" file="payments_warning.png" />
       <structure type="chrome_scaled_image" name="IDR_IOS_PROMO_INFO" file="promo_info.png" />
       <structure type="chrome_scaled_image" name="IDR_IOS_SETTINGS_INFO_24" file="settings_info_24.png" />
diff --git a/ios/chrome/browser/browsing_data/browsing_data_counter_wrapper.cc b/ios/chrome/browser/browsing_data/browsing_data_counter_wrapper.cc
index 9243d2f..f33b38dc 100644
--- a/ios/chrome/browser/browsing_data/browsing_data_counter_wrapper.cc
+++ b/ios/chrome/browser/browsing_data/browsing_data_counter_wrapper.cc
@@ -31,9 +31,6 @@
 std::unique_ptr<browsing_data::BrowsingDataCounter>
 CreateCounterForBrowserStateAndPref(ios::ChromeBrowserState* browser_state,
                                     base::StringPiece pref_name) {
-  if (!IsNewClearBrowsingDataUIEnabled())
-    return nullptr;
-
   if (pref_name == browsing_data::prefs::kDeleteBrowsingHistory) {
     return std::make_unique<browsing_data::HistoryCounter>(
         ios::HistoryServiceFactory::GetForBrowserStateIfExists(
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm
index 0089d9d..d25ffbac 100644
--- a/ios/chrome/browser/flags/about_flags.mm
+++ b/ios/chrome/browser/flags/about_flags.mm
@@ -375,11 +375,6 @@
     {"toolbar-container", flag_descriptions::kToolbarContainerName,
      flag_descriptions::kToolbarContainerDescription, flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(toolbar_container::kToolbarContainerEnabled)},
-    {"omnibox-popup-shortcuts",
-     flag_descriptions::kOmniboxPopupShortcutIconsInZeroStateName,
-     flag_descriptions::kOmniboxPopupShortcutIconsInZeroStateDescription,
-     flags_ui::kOsIos,
-     FEATURE_VALUE_TYPE(omnibox::kOmniboxPopupShortcutIconsInZeroState)},
     {"omnibox-on-device-head-suggestions",
      flag_descriptions::kOmniboxOnDeviceHeadSuggestionsName,
      flag_descriptions::kOmniboxOnDeviceHeadSuggestionsDescription,
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
index a07b8b3..f9cacad 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
@@ -275,12 +275,6 @@
     "Presents JavaScript dialogs non-modally so that the user can change tabs "
     "while a dialog is displayed.";
 
-const char kOmniboxPopupShortcutIconsInZeroStateName[] =
-    "Show zero-state omnibox shortcuts";
-const char kOmniboxPopupShortcutIconsInZeroStateDescription[] =
-    "Instead of ZeroSuggest, show most visited sites and collection shortcuts "
-    "in the omnibox popup.";
-
 const char kOmniboxPreserveDefaultMatchAgainstAsyncUpdateName[] =
     "Omnibox Preserve Default Match Against Async Update";
 const char kOmniboxPreserveDefaultMatchAgainstAsyncUpdateDescription[] =
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
index c51c062..b72755a 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
@@ -239,11 +239,6 @@
 extern const char kNonModalDialogsName[];
 extern const char kNonModalDialogsDescription[];
 
-// Title and description for the flag to show most visited sites and collection
-// shortcuts in the omnibox popup instead of ZeroSuggest.
-extern const char kOmniboxPopupShortcutIconsInZeroStateName[];
-extern const char kOmniboxPopupShortcutIconsInZeroStateDescription[];
-
 // Title and description for the flag to preserve the default match when an
 // async match updates.
 extern const char kOmniboxPreserveDefaultMatchAgainstAsyncUpdateName[];
diff --git a/ios/chrome/browser/infobars/overlays/BUILD.gn b/ios/chrome/browser/infobars/overlays/BUILD.gn
index 042427d..7c880c2 100644
--- a/ios/chrome/browser/infobars/overlays/BUILD.gn
+++ b/ios/chrome/browser/infobars/overlays/BUILD.gn
@@ -43,6 +43,20 @@
   ]
 }
 
+source_set("util") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  sources = [
+    "overlay_request_infobar_type_util.h",
+    "overlay_request_infobar_type_util.mm",
+  ]
+  deps = [
+    "//base",
+    "//ios/chrome/browser/infobars:public",
+    "//ios/chrome/browser/overlays",
+    "//ios/chrome/browser/overlays/public/common/infobars",
+  ]
+}
+
 source_set("test_support") {
   configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
diff --git a/ios/chrome/browser/infobars/overlays/OWNERS b/ios/chrome/browser/infobars/overlays/OWNERS
new file mode 100644
index 0000000..41b6ec5
--- /dev/null
+++ b/ios/chrome/browser/infobars/overlays/OWNERS
@@ -0,0 +1,5 @@
+kkhorimoto@chromium.org
+sczs@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/infobars/overlays/overlay_request_infobar_type_util.h b/ios/chrome/browser/infobars/overlays/overlay_request_infobar_type_util.h
new file mode 100644
index 0000000..b46511c8
--- /dev/null
+++ b/ios/chrome/browser/infobars/overlays/overlay_request_infobar_type_util.h
@@ -0,0 +1,19 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_OVERLAY_REQUEST_INFOBAR_TYPE_UTIL_H_
+#define IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_OVERLAY_REQUEST_INFOBAR_TYPE_UTIL_H_
+
+#import "ios/chrome/browser/infobars/infobar_type.h"
+
+class OverlayRequest;
+
+// Returns the InfobarType of the InfoBar used to configure |request|.
+// |request| must be non-null and configured with an
+// InfobarOverlayRequestConfig.
+// TODO(crbug.com/1038933): Remove requirements on |request| and return
+// InfobarType::kNone once added.
+InfobarType GetOverlayRequestInfobarType(OverlayRequest* request);
+
+#endif  // IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_OVERLAY_REQUEST_INFOBAR_TYPE_UTIL_H_
diff --git a/ios/chrome/browser/infobars/overlays/overlay_request_infobar_type_util.mm b/ios/chrome/browser/infobars/overlays/overlay_request_infobar_type_util.mm
new file mode 100644
index 0000000..a9df2e1
--- /dev/null
+++ b/ios/chrome/browser/infobars/overlays/overlay_request_infobar_type_util.mm
@@ -0,0 +1,16 @@
+// Copyright 2020 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 "ios/chrome/browser/infobars/overlays/overlay_request_infobar_type_util.h"
+
+#import "ios/chrome/browser/overlays/public/common/infobars/infobar_overlay_request_config.h"
+#include "ios/chrome/browser/overlays/public/overlay_request.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+InfobarType GetOverlayRequestInfobarType(OverlayRequest* request) {
+  return request->GetConfig<InfobarOverlayRequestConfig>()->infobar_type();
+}
diff --git a/ios/chrome/browser/sync/sessions/ios_chrome_local_session_event_router.mm b/ios/chrome/browser/sync/sessions/ios_chrome_local_session_event_router.mm
index 029f205..d5d65d07 100644
--- a/ios/chrome/browser/sync/sessions/ios_chrome_local_session_event_router.mm
+++ b/ios/chrome/browser/sync/sessions/ios_chrome_local_session_event_router.mm
@@ -99,17 +99,16 @@
     web::WebState* old_web_state,
     web::WebState* new_web_state,
     int index) {
-  old_web_state->RemoveObserver(this);
-
-  if (new_web_state)
-    new_web_state->AddObserver(this);
+  OnWebStateChange(old_web_state);
+  DCHECK(new_web_state);
+  new_web_state->AddObserver(this);
 }
 
 void IOSChromeLocalSessionEventRouter::WebStateDetachedAt(
     WebStateList* web_state_list,
     web::WebState* web_state,
     int index) {
-  web_state->RemoveObserver(this);
+  OnWebStateChange(web_state);
 }
 
 void IOSChromeLocalSessionEventRouter::TitleWasSet(web::WebState* web_state) {
@@ -136,6 +135,7 @@
 void IOSChromeLocalSessionEventRouter::WebStateDestroyed(
     web::WebState* web_state) {
   OnWebStateChange(web_state);
+  web_state->RemoveObserver(this);
 }
 
 void IOSChromeLocalSessionEventRouter::StartObservingWebStateList(
diff --git a/ios/chrome/browser/ui/badges/BUILD.gn b/ios/chrome/browser/ui/badges/BUILD.gn
index 1c0ff82..9a8f4820a 100644
--- a/ios/chrome/browser/ui/badges/BUILD.gn
+++ b/ios/chrome/browser/ui/badges/BUILD.gn
@@ -50,9 +50,13 @@
     "//base:base",
     "//ios/chrome/app/strings:ios_strings_grit",
     "//ios/chrome/browser/browser_state",
+    "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/infobars:badge",
     "//ios/chrome/browser/infobars:public",
+    "//ios/chrome/browser/infobars/overlays:util",
     "//ios/chrome/browser/main:public",
+    "//ios/chrome/browser/overlays",
+    "//ios/chrome/browser/overlays/public/common/infobars",
     "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
     "//ios/chrome/browser/ui/elements",
@@ -108,6 +112,7 @@
   deps = [
     ":badges",
     ":public",
+    ":util",
     "//base/test:test_support",
     "//ios/chrome/browser/browser_state:test_support",
     "//ios/chrome/browser/infobars",
@@ -115,6 +120,9 @@
     "//ios/chrome/browser/infobars:badge_public",
     "//ios/chrome/browser/infobars/test",
     "//ios/chrome/browser/main:test_support",
+    "//ios/chrome/browser/overlays",
+    "//ios/chrome/browser/overlays/public/common/infobars",
+    "//ios/chrome/browser/overlays/test",
     "//ios/chrome/browser/ui/infobars:feature_flags",
     "//ios/chrome/browser/ui/infobars:test_support",
     "//ios/chrome/browser/web_state_list",
diff --git a/ios/chrome/browser/ui/badges/badge_mediator.mm b/ios/chrome/browser/ui/badges/badge_mediator.mm
index 8573d06..0cfaada 100644
--- a/ios/chrome/browser/ui/badges/badge_mediator.mm
+++ b/ios/chrome/browser/ui/badges/badge_mediator.mm
@@ -11,7 +11,10 @@
 #include "ios/chrome/browser/infobars/infobar_badge_tab_helper_delegate.h"
 #include "ios/chrome/browser/infobars/infobar_metrics_recorder.h"
 #import "ios/chrome/browser/infobars/infobar_type.h"
+#include "ios/chrome/browser/infobars/overlays/overlay_request_infobar_type_util.h"
 #include "ios/chrome/browser/main/browser.h"
+#import "ios/chrome/browser/overlays/public/overlay_presenter.h"
+#import "ios/chrome/browser/overlays/public/overlay_presenter_observer_bridge.h"
 #import "ios/chrome/browser/ui/badges/badge_button.h"
 #import "ios/chrome/browser/ui/badges/badge_consumer.h"
 #import "ios/chrome/browser/ui/badges/badge_item.h"
@@ -36,7 +39,9 @@
 }  // namespace
 
 @interface BadgeMediator () <InfobarBadgeTabHelperDelegate,
+                             OverlayPresenterObserving,
                              WebStateListObserving> {
+  std::unique_ptr<OverlayPresenterObserver> _overlayPresenterObserver;
   std::unique_ptr<WebStateListObserver> _webStateListObserver;
 }
 
@@ -50,6 +55,9 @@
 // The active WebState's badge tab helper.
 @property(nonatomic, readonly) InfobarBadgeTabHelper* badgeTabHelper;
 
+// The infobar banner OverlayPresenter.
+@property(nonatomic, readonly) OverlayPresenter* overlayPresenter;
+
 // The incognito badge, or nil if the Browser is not off-the-record.
 @property(nonatomic, readonly) id<BadgeItem> offTheRecordBadge;
 
@@ -69,6 +77,12 @@
       _offTheRecordBadge = [[BadgeStaticItem alloc]
           initWithBadgeType:BadgeType::kBadgeTypeIncognito];
     }
+    // Set up the OverlayPresenterObserver for the infobar banner presentation.
+    _overlayPresenterObserver =
+        std::make_unique<OverlayPresenterObserverBridge>(self);
+    _overlayPresenter =
+        OverlayPresenter::FromBrowser(browser, OverlayModality::kInfobarBanner);
+    _overlayPresenter->AddObserver(_overlayPresenterObserver.get());
     // Set up the WebStateList and its observer.
     _webStateList = browser->GetWebStateList();
     _webState = _webStateList->GetActiveWebState();
@@ -84,6 +98,13 @@
 }
 
 - (void)disconnect {
+  [self disconnectWebStateList];
+  [self disconnectOverlayPresenter];
+}
+
+#pragma mark - Disconnect helpers
+
+- (void)disconnectWebStateList {
   if (_webStateList) {
     self.webState = nullptr;
     _webStateList->RemoveObserver(_webStateListObserver.get());
@@ -92,6 +113,14 @@
   }
 }
 
+- (void)disconnectOverlayPresenter {
+  if (_overlayPresenter) {
+    _overlayPresenter->RemoveObserver(_overlayPresenterObserver.get());
+    _overlayPresenterObserver = nullptr;
+    _overlayPresenter = nullptr;
+  }
+}
+
 #pragma mark - Accessors
 
 - (void)setConsumer:(id<BadgeConsumer>)consumer {
@@ -140,7 +169,7 @@
     [self updateBadgesForActiveWebState];
 
   // Show the overflow badge if there are multiple BadgeItems.  Otherwise, use
-  // the last badge.
+  // the first badge if it's not fullscreen.
   NSUInteger fullscreenBadgeCount = self.offTheRecordBadge ? 1U : 0U;
   BOOL shouldDisplayOverflowBadge =
       self.badges.count - fullscreenBadgeCount > 1;
@@ -238,6 +267,33 @@
   }
 }
 
+#pragma mark - OverlayPresenterObserving
+
+- (void)overlayPresenter:(OverlayPresenter*)presenter
+    didShowOverlayForRequest:(OverlayRequest*)request {
+  DCHECK_EQ(self.overlayPresenter, presenter);
+  InfobarBadgeTabHelper* badgeTabHelper = self.badgeTabHelper;
+  if (badgeTabHelper) {
+    self.badgeTabHelper->UpdateBadgeForInfobarBannerPresented(
+        GetOverlayRequestInfobarType(request));
+  }
+}
+
+- (void)overlayPresenter:(OverlayPresenter*)presenter
+    didHideOverlayForRequest:(OverlayRequest*)request {
+  DCHECK_EQ(self.overlayPresenter, presenter);
+  InfobarBadgeTabHelper* badgeTabHelper = self.badgeTabHelper;
+  if (badgeTabHelper) {
+    self.badgeTabHelper->UpdateBadgeForInfobarBannerDismissed(
+        GetOverlayRequestInfobarType(request));
+  }
+}
+
+- (void)overlayPresenterDestroyed:(OverlayPresenter*)presenter {
+  DCHECK_EQ(self.overlayPresenter, presenter);
+  [self disconnectOverlayPresenter];
+}
+
 #pragma mark - WebStateListObserver
 
 - (void)webStateList:(WebStateList*)webStateList
diff --git a/ios/chrome/browser/ui/badges/badge_mediator_unittest.mm b/ios/chrome/browser/ui/badges/badge_mediator_unittest.mm
index 399b231d..8aea99c7 100644
--- a/ios/chrome/browser/ui/badges/badge_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/badges/badge_mediator_unittest.mm
@@ -14,8 +14,14 @@
 #include "ios/chrome/browser/infobars/infobar_manager_impl.h"
 #import "ios/chrome/browser/infobars/test/fake_infobar_ios.h"
 #import "ios/chrome/browser/main/test_browser.h"
+#import "ios/chrome/browser/overlays/public/common/infobars/infobar_overlay_request_config.h"
+#import "ios/chrome/browser/overlays/public/overlay_presenter.h"
+#import "ios/chrome/browser/overlays/public/overlay_request.h"
+#import "ios/chrome/browser/overlays/public/overlay_request_queue.h"
+#include "ios/chrome/browser/overlays/test/fake_overlay_presentation_context.h"
 #import "ios/chrome/browser/ui/badges/badge_consumer.h"
 #import "ios/chrome/browser/ui/badges/badge_item.h"
+#include "ios/chrome/browser/ui/badges/badge_type_util.h"
 #import "ios/chrome/browser/ui/infobars/infobar_feature.h"
 #import "ios/chrome/browser/ui/infobars/test_infobar_delegate.h"
 #import "ios/chrome/browser/web_state_list/fake_web_state_list_delegate.h"
@@ -71,11 +77,17 @@
         browser_state_(TestChromeBrowserState::Builder().Build()),
         web_state_list_(&web_state_list_delegate_) {
     feature_list_.InitAndEnableFeature(kInfobarUIReboot);
+    OverlayPresenter::FromBrowser(browser(), OverlayModality::kInfobarBanner)
+        ->SetPresentationContext(&overlay_presentation_context_);
     badge_mediator_ = [[BadgeMediator alloc] initWithBrowser:browser()];
     badge_mediator_.consumer = badge_consumer_;
   }
 
-  ~BadgeMediatorTest() override { [badge_mediator_ disconnect]; }
+  ~BadgeMediatorTest() override {
+    OverlayPresenter::FromBrowser(browser(), OverlayModality::kInfobarBanner)
+        ->SetPresentationContext(nullptr);
+    [badge_mediator_ disconnect];
+  }
 
   // Inserts a new WebState to the WebStateList at |index| and activates it.
   void InsertActivatedWebState(int index) {
@@ -141,6 +153,7 @@
   FakeWebStateListDelegate web_state_list_delegate_;
   WebStateList web_state_list_;
   std::unique_ptr<Browser> browser_;
+  FakeOverlayPresentationContext overlay_presentation_context_;
   BadgeMediator* badge_mediator_ = nil;
 };
 
@@ -250,6 +263,38 @@
   ASSERT_FALSE(badge_consumer_.displayedBadge);
 }
 
+// Tests that the badge mediator successfully updates the InfobarBadgeTabHelper
+// for the active WebState for infobar banner presentation and dismissal.
+TEST_P(BadgeMediatorTest, InfobarBannerOverlayObserving) {
+  // Add an active WebState at index 0 and add an InfoBar with |type| to the
+  // WebState's InfoBarManager, checking that the badge item has been created
+  // with the default BadgeState.
+  InsertActivatedWebState(/*index=*/0);
+  InfobarType type = kFirstInfobarType;
+  InfobarBadgeTabHelper* tab_helper =
+      InfobarBadgeTabHelper::FromWebState(web_state());
+  InfoBarIOS* infobar = AddInfobar(kFirstInfobarType);
+  NSArray<id<BadgeItem>>* items = tab_helper->GetInfobarBadgeItems();
+  ASSERT_EQ(1U, items.count);
+  id<BadgeItem> item = [items firstObject];
+  ASSERT_EQ(BadgeTypeForInfobarType(type), [item badgeType]);
+  ASSERT_FALSE(item.badgeState & BadgeStatePresented);
+
+  // Simulate the presentation of the infobar banner via OverlayPresenter in the
+  // fake presentation context, verifying that the badge state is updated
+  // accordingly.
+  OverlayRequestQueue* queue = OverlayRequestQueue::FromWebState(
+      web_state(), OverlayModality::kInfobarBanner);
+  queue->AddRequest(
+      OverlayRequest::CreateWithConfig<InfobarOverlayRequestConfig>(infobar));
+  EXPECT_TRUE(item.badgeState & BadgeStatePresented);
+
+  // Simulate dismissal of the banner and verify that the badge state is no
+  // longer presented.
+  queue->CancelAllRequests();
+  EXPECT_FALSE(item.badgeState & BadgeStatePresented);
+}
+
 INSTANTIATE_TEST_SUITE_P(/* No InstantiationName */,
                          BadgeMediatorTest,
                          testing::Values(TestParam::kNormal,
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm b/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
index 74de6eb..a7aacce 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
+++ b/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
@@ -344,7 +344,6 @@
     [self.viewController.dispatcher focusFakebox];
   } else {
     [self.omniboxCoordinator focusOmnibox];
-    [self.omniboxPopupCoordinator presentShortcutsIfNecessary];
   }
 }
 
@@ -354,7 +353,6 @@
   }
   self.isCancellingOmniboxEdit = YES;
   [self.omniboxCoordinator endEditing];
-  [self.omniboxPopupCoordinator dismissShortcuts];
   self.isCancellingOmniboxEdit = NO;
 }
 
diff --git a/ios/chrome/browser/ui/omnibox/BUILD.gn b/ios/chrome/browser/ui/omnibox/BUILD.gn
index e389fcc3..8e0f2a7 100644
--- a/ios/chrome/browser/ui/omnibox/BUILD.gn
+++ b/ios/chrome/browser/ui/omnibox/BUILD.gn
@@ -185,17 +185,102 @@
   ]
 }
 
-source_set("eg_tests") {
+source_set("test_support") {
   defines = [ "CHROME_EARL_GREY_1" ]
+  configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
   sources = [
     "omnibox_app_interface.h",
     "omnibox_app_interface.mm",
+  ]
+  deps = [
+    "//base",
+    "//components/google/core/common",
+    "//components/variations:variations",
+    "//ios/chrome/browser/browser_state",
+    "//ios/chrome/browser/ui/omnibox/popup:popup_ui",
+    "//ios/chrome/test/app:test_support",
+    "//ios/testing/earl_grey:earl_grey_support",
+    "//ios/third_party/earl_grey:earl_grey+link",
+    "//ios/web/public",
+    "//ios/web/public/navigation",
+    "//net:test_support",
+    "//ui/base",
+    "//ui/base:test_support",
+  ]
+}
+source_set("app_support+eg2") {
+  defines = [ "CHROME_EARL_GREY_2" ]
+  configs += [
+    "//build/config/compiler:enable_arc",
+    "//build/config/ios:xctest_config",
+  ]
+  testonly = true
+  sources = [
+    "omnibox_app_interface.h",
+    "omnibox_app_interface.mm",
+  ]
+  deps = [
+    "//base",
+    "//components/google/core/common",
+    "//components/variations:variations",
+    "//ios/chrome/browser/browser_state",
+    "//ios/chrome/browser/ui/omnibox/popup:popup_ui",
+    "//ios/chrome/test/app:test_support",
+    "//ios/testing/earl_grey:eg_app_support+eg2",
+    "//ios/third_party/earl_grey2:app_framework+link",
+    "//ios/web/public",
+    "//ios/web/public/navigation",
+    "//net:test_support",
+    "//ui/base",
+    "//ui/base:test_support",
+  ]
+}
+source_set("test_support+eg2") {
+  defines = [ "CHROME_EARL_GREY_2" ]
+  configs += [
+    "//build/config/compiler:enable_arc",
+    "//build/config/ios:xctest_config",
+  ]
+  testonly = true
+  sources = [
+    "omnibox_app_interface.h",
+  ]
+}
+
+source_set("eg2_tests") {
+  defines = [ "CHROME_EARL_GREY_2" ]
+  configs += [
+    "//build/config/compiler:enable_arc",
+    "//build/config/ios:xctest_config",
+  ]
+  testonly = true
+  sources = [
+    "omnibox_egtest.mm",
+  ]
+  deps = [
+    ":test_support+eg2",
+    "//ios/chrome/app/strings:ios_strings_grit",
+    "//ios/chrome/browser/ui/content_suggestions:content_suggestions_constant",
+    "//ios/chrome/test/earl_grey:eg_test_support+eg2",
+    "//ios/testing/earl_grey:eg_test_support+eg2",
+    "//ios/third_party/earl_grey2:test_lib",
+    "//net:test_support",
+    "//ui/base",
+  ]
+  libs = [ "UIKit.framework" ]
+}
+
+source_set("eg_tests") {
+  defines = [ "CHROME_EARL_GREY_1" ]
+  testonly = true
+  sources = [
     "omnibox_egtest.mm",
   ]
   deps = [
     ":omnibox",
     ":omnibox_internal",
+    ":test_support",
     "//base",
     "//base/test:test_support",
     "//components/google/core/common",
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_app_interface.h b/ios/chrome/browser/ui/omnibox/omnibox_app_interface.h
index 06cb440..f1bdbf3 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_app_interface.h
+++ b/ios/chrome/browser/ui/omnibox/omnibox_app_interface.h
@@ -13,6 +13,10 @@
 // Rewrite google URLs to localhost so they can be loaded by the test server.
 + (void)rewriteGoogleURLToLocalhost;
 
+// Forces a variation to be used on the current HTTP header provider. Returns
+// YES if the forcing was successful.
++ (BOOL)forceVariationID:(int)variationID;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_OMNIBOX_OMNIBOX_APP_INTERFACE_H_
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_app_interface.mm b/ios/chrome/browser/ui/omnibox/omnibox_app_interface.mm
index f95b884..7d13db4 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_app_interface.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_app_interface.mm
@@ -4,7 +4,9 @@
 
 #import "ios/chrome/browser/ui/omnibox/omnibox_app_interface.h"
 
+#include "base/strings/string_number_conversions.h"
 #include "components/google/core/common/google_util.h"
+#include "components/variations/variations_http_header_provider.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #import "ios/chrome/test/app/tab_test_util.h"
 #import "ios/web/public/navigation/navigation_manager.h"
@@ -42,4 +44,12 @@
       ->AddTransientURLRewriter(&GoogleToLocalhostURLRewriter);
 }
 
++ (BOOL)forceVariationID:(int)variationID {
+  return variations::VariationsHttpHeaderProvider::ForceIdsResult::SUCCESS ==
+         variations::VariationsHttpHeaderProvider::GetInstance()
+             ->ForceVariationIds(
+                 /*variation_ids=*/{base::NumberToString(variationID)},
+                 /*command_line_variation_ids=*/"");
+}
+
 @end
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_constants.h b/ios/chrome/browser/ui/omnibox/omnibox_constants.h
index 48a5650..1ce014d3 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_constants.h
+++ b/ios/chrome/browser/ui/omnibox/omnibox_constants.h
@@ -7,9 +7,6 @@
 
 #import <UIKit/UIKit.h>
 
-// The a11y identifier for the shortcuts table view cell in the omnibox popup.
-extern NSString* const kShortcutsAccessibilityIdentifier;
-
 extern const CGFloat kOmniboxPlaceholderAlpha;
 
 #endif  // IOS_CHROME_BROWSER_UI_OMNIBOX_OMNIBOX_CONSTANTS_H_
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_constants.mm b/ios/chrome/browser/ui/omnibox/omnibox_constants.mm
index 7fbcee1..b474e90 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_constants.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_constants.mm
@@ -8,6 +8,4 @@
 #error "This file requires ARC support."
 #endif
 
-NSString* const kShortcutsAccessibilityIdentifier = @"OmniboxShortcuts";
-
 const CGFloat kOmniboxPlaceholderAlpha = 0.3;
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_egtest.mm b/ios/chrome/browser/ui/omnibox/omnibox_egtest.mm
index a00587aa..d8e1dba 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_egtest.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_egtest.mm
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import <EarlGrey/EarlGrey.h>
 #import <XCTest/XCTest.h>
 
 #include "base/bind.h"
@@ -10,10 +9,8 @@
 #include "base/mac/foundation_util.h"
 #include "base/strings/sys_string_conversions.h"
 #import "base/test/ios/wait_util.h"
-#include "components/variations/variations_http_header_provider.h"
 #import "ios/chrome/browser/ui/content_suggestions/ntp_home_constant.h"
 #import "ios/chrome/browser/ui/omnibox/omnibox_app_interface.h"
-#import "ios/chrome/browser/ui/omnibox/popup/omnibox_popup_row.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
@@ -21,7 +18,6 @@
 #import "ios/chrome/test/earl_grey/chrome_matchers_app_interface.h"
 #import "ios/chrome/test/earl_grey/chrome_test_case.h"
 #import "ios/testing/earl_grey/earl_grey_test.h"
-#import "ios/testing/hardware_keyboard_util.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "net/test/embedded_test_server/http_request.h"
 #include "net/test/embedded_test_server/http_response.h"
@@ -31,6 +27,16 @@
 #error "This file requires ARC support."
 #endif
 
+#if defined(CHROME_EARL_GREY_2)
+// TODO(crbug.com/1015113) The EG2 macro is breaking indexing for some reason
+// without the trailing semicolon.  For now, disable the extra semi warning
+// so Xcode indexing works for the egtest.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wc++98-compat-extra-semi"
+GREY_STUB_CLASS_IN_APP_MAIN_QUEUE(OmniboxAppInterface);
+#pragma clang diagnostic pop
+#endif  // defined(CHROME_EARL_GREY_2)
+
 using base::test::ios::kWaitForUIElementTimeout;
 
 namespace {
@@ -150,12 +156,8 @@
   [OmniboxAppInterface rewriteGoogleURLToLocalhost];
 
   // Force variations to send the requests.
-  GREYAssertEqual(
-      variations::VariationsHttpHeaderProvider::ForceIdsResult::SUCCESS,
-      variations::VariationsHttpHeaderProvider::GetInstance()
-          ->ForceVariationIds(
-              /*variation_ids=*/{"100"}, /*command_line_variation_ids=*/""),
-      @"Variation not enabled.");
+  GREYAssert([OmniboxAppInterface forceVariationID:100],
+             @"Variation not enabled.");
 
   [[EarlGrey selectElementWithMatcher:chrome_test_util::FakeOmnibox()]
       performAction:grey_tap()];
@@ -312,7 +314,8 @@
   [ChromeEarlGreyUI focusOmnibox];
   [self checkLocationBarEditState];
 
-  chrome_test_util::SimulatePhysicalKeyboardEvent(UIKeyModifierCommand, @"C");
+  [ChromeEarlGrey simulatePhysicalKeyboardEvent:@"C"
+                                          flags:UIKeyModifierCommand];
 
   // Edit menu takes a while to copy, and not waiting here will cause Page 2 to
   // load before the copy happens, so Page 2 URL may be copied.
@@ -340,14 +343,16 @@
   [ChromeEarlGreyUI focusOmnibox];
 
   // Attempt to paste.
-  chrome_test_util::SimulatePhysicalKeyboardEvent(UIKeyModifierCommand, @"V");
+  [ChromeEarlGrey simulatePhysicalKeyboardEvent:@"V"
+                                          flags:UIKeyModifierCommand];
 
   // Verify that paste happened.
   [[EarlGrey selectElementWithMatcher:chrome_test_util::Omnibox()]
       assertWithMatcher:chrome_test_util::OmniboxContainingText(kPage1URL)];
 
   // Attempt to undo.
-  chrome_test_util::SimulatePhysicalKeyboardEvent(UIKeyModifierCommand, @"Z");
+  [ChromeEarlGrey simulatePhysicalKeyboardEvent:@"Z"
+                                          flags:UIKeyModifierCommand];
 
   // Verify that undo happened.
   [[EarlGrey selectElementWithMatcher:chrome_test_util::Omnibox()]
@@ -355,7 +360,8 @@
 
   // Attempt to undo again. Nothing should happen. In the past this could lead
   // to a crash.
-  chrome_test_util::SimulatePhysicalKeyboardEvent(UIKeyModifierCommand, @"Z");
+  [ChromeEarlGrey simulatePhysicalKeyboardEvent:@"Z"
+                                          flags:UIKeyModifierCommand];
   [[EarlGrey selectElementWithMatcher:chrome_test_util::Omnibox()]
       assertWithMatcher:chrome_test_util::OmniboxContainingText(kPage2URL)];
 }
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_util.h b/ios/chrome/browser/ui/omnibox/omnibox_util.h
index 44df11d..866e4c1 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_util.h
+++ b/ios/chrome/browser/ui/omnibox/omnibox_util.h
@@ -40,17 +40,4 @@
 UIImage* GetLocationBarSecurityIconForSecurityState(
     security_state::SecurityLevel security_level);
 
-#pragma mark - Legacy utils.
-
-// Converts |type| to a resource identifier for the appropriate icon for this
-// type to show in the omnibox.
-int GetIconForAutocompleteMatchType(AutocompleteMatchType::Type type,
-                                    bool is_starred,
-                                    bool is_incognito);
-
-
-// Converts |security_level| to a resource identifier for the appropriate icon
-// for this security level in the omnibox.
-int GetIconForSecurityState(security_state::SecurityLevel security_level);
-
 #endif  // IOS_CHROME_BROWSER_UI_OMNIBOX_OMNIBOX_UTIL_H_
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_util.mm b/ios/chrome/browser/ui/omnibox/omnibox_util.mm
index 7df9cc0..a49584e 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_util.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_util.mm
@@ -115,77 +115,3 @@
       GetLocationBarSecurityIconTypeForSecurityState(security_level);
   return GetLocationBarSecurityIcon(iconType);
 }
-
-#pragma mark - Legacy utils.
-
-int GetIconForAutocompleteMatchType(AutocompleteMatchType::Type type,
-                                    bool is_starred,
-                                    bool is_incognito) {
-  if (is_starred)
-    return is_incognito ? IDR_IOS_OMNIBOX_STAR_INCOGNITO : IDR_IOS_OMNIBOX_STAR;
-
-  switch (type) {
-    case AutocompleteMatchType::BOOKMARK_TITLE:
-    case AutocompleteMatchType::CLIPBOARD_URL:
-    case AutocompleteMatchType::NAVSUGGEST:
-    case AutocompleteMatchType::NAVSUGGEST_PERSONALIZED:
-    case AutocompleteMatchType::PHYSICAL_WEB_DEPRECATED:
-    case AutocompleteMatchType::PHYSICAL_WEB_OVERFLOW_DEPRECATED:
-    case AutocompleteMatchType::URL_WHAT_YOU_TYPED:
-      return is_incognito ? IDR_IOS_OMNIBOX_HTTP_INCOGNITO
-                          : IDR_IOS_OMNIBOX_HTTP;
-    case AutocompleteMatchType::HISTORY_BODY:
-    case AutocompleteMatchType::HISTORY_KEYWORD:
-    case AutocompleteMatchType::HISTORY_TITLE:
-    case AutocompleteMatchType::HISTORY_URL:
-    case AutocompleteMatchType::SEARCH_HISTORY:
-    case AutocompleteMatchType::TAB_SEARCH_DEPRECATED:
-      return is_incognito ? IDR_IOS_OMNIBOX_HISTORY_INCOGNITO
-                          : IDR_IOS_OMNIBOX_HISTORY;
-    case AutocompleteMatchType::CONTACT_DEPRECATED:
-    case AutocompleteMatchType::SEARCH_OTHER_ENGINE:
-    case AutocompleteMatchType::SEARCH_SUGGEST:
-    case AutocompleteMatchType::SEARCH_SUGGEST_ENTITY:
-    case AutocompleteMatchType::SEARCH_SUGGEST_PERSONALIZED:
-    case AutocompleteMatchType::SEARCH_SUGGEST_PROFILE:
-    case AutocompleteMatchType::SEARCH_SUGGEST_TAIL:
-    case AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED:
-    case AutocompleteMatchType::VOICE_SUGGEST:
-    case AutocompleteMatchType::CLIPBOARD_TEXT:
-    case AutocompleteMatchType::CLIPBOARD_IMAGE:
-      return is_incognito ? IDR_IOS_OMNIBOX_SEARCH_INCOGNITO
-                          : IDR_IOS_OMNIBOX_SEARCH;
-    case AutocompleteMatchType::CALCULATOR:
-      // Calculator answers are never shown in incognito mode because input is
-      // never sent to the search provider.
-      DCHECK(!is_incognito);
-      return IDR_IOS_OMNIBOX_CALCULATOR;
-    case AutocompleteMatchType::DOCUMENT_SUGGESTION:
-    case AutocompleteMatchType::PEDAL:
-      // Document and Pedal suggestions aren't yet supported on mobile.
-      NOTREACHED();
-      return IDR_IOS_OMNIBOX_HTTP;
-    case AutocompleteMatchType::EXTENSION_APP_DEPRECATED:
-    case AutocompleteMatchType::NUM_TYPES:
-      NOTREACHED();
-      return IDR_IOS_OMNIBOX_HTTP;
-  }
-}
-
-int GetIconForSecurityState(security_state::SecurityLevel security_level) {
-  switch (security_level) {
-    case security_state::NONE:
-    case security_state::WARNING:
-      return IDR_IOS_OMNIBOX_HTTP;
-    case security_state::EV_SECURE:
-    case security_state::SECURE:
-      return IDR_IOS_OMNIBOX_HTTPS_VALID;
-    case security_state::SECURE_WITH_POLICY_INSTALLED_CERT:
-      return IDR_IOS_OMNIBOX_HTTPS_POLICY_WARNING;
-    case security_state::DANGEROUS:
-      return IDR_IOS_OMNIBOX_HTTPS_INVALID;
-    case security_state::SECURITY_LEVEL_COUNT:
-      NOTREACHED();
-      return IDR_IOS_OMNIBOX_HTTP;
-  }
-}
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_view_ios.h b/ios/chrome/browser/ui/omnibox/omnibox_view_ios.h
index b53c4f1..1397e4f 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_view_ios.h
+++ b/ios/chrome/browser/ui/omnibox/omnibox_view_ios.h
@@ -144,10 +144,6 @@
   // Returns |true| if AutocompletePopupView is currently open.
   BOOL IsPopupOpen();
 
-  // Returns the resource ID of the icon to show for the current text. Takes
-  // into account the security level of the page, and |offline_page|.
-  int GetIcon(bool offline_page) const;
-
  protected:
   int GetOmniboxTextLength() const override;
   void EmphasizeURLComponents() override;
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_view_ios.mm b/ios/chrome/browser/ui/omnibox/omnibox_view_ios.mm
index e75dc57..df3bcc6 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_view_ios.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_view_ios.mm
@@ -667,20 +667,6 @@
   return popup_provider_->IsPopupOpen();
 }
 
-int OmniboxViewIOS::GetIcon(bool offlinePage) const {
-  if (!IsEditingOrEmpty()) {
-    if (offlinePage) {
-      return IDR_IOS_OMNIBOX_OFFLINE;
-    }
-    return GetIconForSecurityState(
-        controller()->GetLocationBarModel()->GetSecurityLevel());
-  }
-  return GetIconForAutocompleteMatchType(
-      model() ? model()->CurrentMatch(nullptr).type
-              : AutocompleteMatchType::URL_WHAT_YOU_TYPED,
-      /* is_starred */ false, /* is_incognito */ false);
-}
-
 int OmniboxViewIOS::GetOmniboxTextLength() const {
   return [field_ displayedText].length();
 }
diff --git a/ios/chrome/browser/ui/omnibox/popup/BUILD.gn b/ios/chrome/browser/ui/omnibox/popup/BUILD.gn
index 32bca74c..dcfd7f53 100644
--- a/ios/chrome/browser/ui/omnibox/popup/BUILD.gn
+++ b/ios/chrome/browser/ui/omnibox/popup/BUILD.gn
@@ -32,7 +32,6 @@
     "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/ui/ntp:util",
     "//ios/chrome/browser/ui/omnibox:omnibox_util",
-    "//ios/chrome/browser/ui/omnibox/popup/shortcuts",
     "//ios/chrome/browser/ui/toolbar/buttons",
     "//ios/chrome/browser/ui/toolbar/public",
     "//ios/chrome/browser/ui/toolbar/public:feature_flags",
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_base_view_controller.h b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_base_view_controller.h
index e0771a8..0900dc8 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_base_view_controller.h
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_base_view_controller.h
@@ -27,24 +27,11 @@
                         OmniboxSuggestionCommands,
                         UIScrollViewDelegate>
 
-// When enabled, this view controller will display shortcuts when no suggestions
-// are available. When enabling this, |shortcutsViewController| must be set.
-// This can be toggled at runtime, for example to only show shortcuts on regular
-// pages and not show them on NTP.
-@property(nonatomic, assign) BOOL shortcutsEnabled;
-// The view controller to display when no suggestions is available. See also:
-// |shortcutsEnabled|.
-@property(nonatomic, weak) UICollectionViewController* shortcutsViewController;
-
 @property(nonatomic, assign) BOOL incognito;
 @property(nonatomic, weak) id<AutocompleteResultConsumerDelegate> delegate;
 @property(nonatomic, weak) id<ImageRetriever> imageRetriever;
 @property(nonatomic, weak) id<FaviconRetriever> faviconRetriever;
 
-// The cell with shortcuts to display when no results are available (only if
-// this is enabled with |shortcutsEnabled|). Lazily instantiated.
-@property(nonatomic, strong) UITableViewCell* shortcutsCell;
-
 @property(nonatomic, strong) NSArray<id<AutocompleteSuggestion>>* currentResult;
 
 - (instancetype)init NS_DESIGNATED_INITIALIZER;
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_base_view_controller.mm b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_base_view_controller.mm
index 2f9b090c..c6e82b5 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_base_view_controller.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_base_view_controller.mm
@@ -52,8 +52,6 @@
 // view controller was on screen.
 @property(nonatomic, assign) base::TimeTicks viewAppearanceTime;
 
-@property(nonatomic, strong) NSLayoutConstraint* shortcutsViewEdgeConstraint;
-
 @end
 
 @implementation OmniboxPopupBaseViewController
@@ -116,35 +114,6 @@
   [self updateBackgroundColor];
 }
 
-- (void)viewWillTransitionToSize:(CGSize)size
-       withTransitionCoordinator:
-           (id<UIViewControllerTransitionCoordinator>)coordinator {
-  [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
-
-  // Update the leading edge constraints for the shortcuts cell when the view
-  // rotates.
-  if (self.shortcutsEnabled && self.currentResult.count == 0) {
-    __weak __typeof(self) weakSelf = self;
-    [coordinator
-        animateAlongsideTransition:^(
-            id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
-          __typeof(self) strongSelf = weakSelf;
-          if (!strongSelf) {
-            return;
-          }
-          CGFloat widthInsets = CenteredTilesMarginForWidth(
-              strongSelf.traitCollection,
-              size.width - strongSelf.view.safeAreaInsets.left -
-                  strongSelf.view.safeAreaInsets.right);
-          strongSelf.shortcutsViewEdgeConstraint.constant = widthInsets;
-          [strongSelf.shortcutsViewController.collectionView
-                  .collectionViewLayout invalidateLayout];
-          [strongSelf.shortcutsCell layoutIfNeeded];
-        }
-                        completion:nil];
-  }
-}
-
 #pragma mark - View lifecycle
 
 - (void)viewDidAppear:(BOOL)animated {
@@ -158,52 +127,6 @@
                              base::TimeTicks::Now() - self.viewAppearanceTime);
 }
 
-#pragma mark - Properties accessors
-
-- (void)setShortcutsEnabled:(BOOL)shortcutsEnabled {
-  if (shortcutsEnabled == _shortcutsEnabled) {
-    return;
-  }
-
-  DCHECK(!shortcutsEnabled || self.shortcutsViewController);
-
-  _shortcutsEnabled = shortcutsEnabled;
-  [self.tableView reloadData];
-}
-
-- (UITableViewCell*)shortcutsCell {
-  if (_shortcutsCell) {
-    return _shortcutsCell;
-  }
-
-  DCHECK(self.shortcutsEnabled);
-  DCHECK(self.shortcutsViewController);
-
-  UITableViewCell* cell = [[UITableViewCell alloc] init];
-  _shortcutsCell = cell;
-  cell.backgroundColor = [UIColor clearColor];
-  [self.shortcutsViewController willMoveToParentViewController:self];
-  [self addChildViewController:self.shortcutsViewController];
-  [cell.contentView addSubview:self.shortcutsViewController.view];
-  self.shortcutsViewController.view.translatesAutoresizingMaskIntoConstraints =
-      NO;
-  AddSameConstraintsToSides(self.shortcutsViewController.view, cell.contentView,
-                            (LayoutSides::kTop | LayoutSides::kBottom));
-  AddSameCenterXConstraint(self.shortcutsViewController.view, cell.contentView);
-  self.shortcutsViewEdgeConstraint =
-      [self.shortcutsViewController.view.leadingAnchor
-          constraintEqualToAnchor:cell.contentView.safeAreaLayoutGuide
-                                      .leadingAnchor];
-  // When the device is rotating, the constraints are slightly off for one
-  // runloop. Lower the priority here to prevent unable to satisfy constraints
-  // warning.
-  self.shortcutsViewEdgeConstraint.priority = UILayoutPriorityRequired - 1;
-  self.shortcutsViewEdgeConstraint.active = YES;
-  [self.shortcutsViewController didMoveToParentViewController:self];
-  cell.accessibilityIdentifier = kShortcutsAccessibilityIdentifier;
-  return cell;
-}
-
 #pragma mark - AutocompleteResultConsumer
 
 - (void)updateMatches:(NSArray<id<AutocompleteSuggestion>>*)result
@@ -312,11 +235,6 @@
 
 - (BOOL)tableView:(UITableView*)tableView
     shouldHighlightRowAtIndexPath:(NSIndexPath*)indexPath {
-  if (self.shortcutsEnabled && indexPath.row == 0 &&
-      self.currentResult.count == 0) {
-    return NO;
-  }
-
   return YES;
 }
 
@@ -334,31 +252,6 @@
   [self.delegate autocompleteResultConsumer:self didSelectRow:row];
 }
 
-- (void)tableView:(UITableView*)tableView
-      willDisplayCell:(UITableViewCell*)cell
-    forRowAtIndexPath:(NSIndexPath*)indexPath {
-  // Update the leading edge constraints for the shortcuts cell before it is
-  // displayed.
-  if (self.shortcutsEnabled && indexPath.row == 0 &&
-      self.currentResult.count == 0) {
-    CGFloat widthInsets = CenteredTilesMarginForWidth(
-        self.traitCollection, self.view.bounds.size.width -
-                                  self.view.safeAreaInsets.left -
-                                  self.view.safeAreaInsets.right);
-    if (widthInsets != self.shortcutsViewEdgeConstraint.constant) {
-      self.shortcutsViewEdgeConstraint.constant = widthInsets;
-      // If the insets have changed, the collection view (and thus the table
-      // view) may have changed heights. This could happen due to dynamic type
-      // changing the height of the collection view. It is also necessary for
-      // the first load.
-      [self.shortcutsViewController.collectionView
-              .collectionViewLayout invalidateLayout];
-      [self.shortcutsCell.contentView layoutIfNeeded];
-      [self.tableView reloadData];
-    }
-  }
-}
-
 #pragma mark - UIScrollViewDelegate
 
 - (void)scrollViewDidScroll:(UIScrollView*)scrollView {
@@ -383,9 +276,6 @@
 - (NSInteger)tableView:(UITableView*)tableView
     numberOfRowsInSection:(NSInteger)section {
   DCHECK_EQ(0, section);
-  if (self.shortcutsEnabled && self.currentResult.count == 0) {
-    return 1;
-  }
   return self.currentResult.count;
 }
 
@@ -399,11 +289,6 @@
     canEditRowAtIndexPath:(NSIndexPath*)indexPath {
   DCHECK_EQ(0U, (NSUInteger)indexPath.section);
 
-  if (self.shortcutsEnabled && indexPath.row == 0 &&
-      self.currentResult.count == 0) {
-    return NO;
-  }
-
   // iOS doesn't check -numberOfRowsInSection before checking
   // -canEditRowAtIndexPath in a reload call. If |indexPath.row| is too large,
   // simple return |NO|.
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_coordinator.h b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_coordinator.h
index c72130c..ee0559f 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_coordinator.h
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_coordinator.h
@@ -42,13 +42,6 @@
 - (void)start;
 - (void)stop;
 
-// Presents the shortcuts feature if the current page allows for it and update
-// the popup.
-- (void)presentShortcutsIfNecessary;
-
-// Dismisses the shortcuts feature and update the popup.
-- (void)dismissShortcuts;
-
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_OMNIBOX_POPUP_OMNIBOX_POPUP_COORDINATOR_H_
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_coordinator.mm b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_coordinator.mm
index c0380e3..183d18a 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_coordinator.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_coordinator.mm
@@ -19,7 +19,6 @@
 #import "ios/chrome/browser/ui/omnibox/popup/omnibox_popup_presenter.h"
 #import "ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller.h"
 #include "ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_ios.h"
-#include "ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_coordinator.h"
 #include "ios/chrome/browser/ui/ui_feature_flags.h"
 #include "ios/chrome/browser/ui/util/ui_util.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
@@ -36,7 +35,6 @@
 @property(nonatomic, strong)
     OmniboxPopupBaseViewController* popupViewController;
 @property(nonatomic, strong) OmniboxPopupMediator* mediator;
-@property(nonatomic, strong) ShortcutsCoordinator* shortcutsCoordinator;
 
 @end
 
@@ -102,7 +100,6 @@
 }
 
 - (void)stop {
-  [self.shortcutsCoordinator stop];
   _popupView.reset();
   [self.dispatcher
       stopDispatchingForProtocol:@protocol(OmniboxSuggestionCommands)];
@@ -112,41 +109,6 @@
   return self.mediator.isOpen;
 }
 
-- (void)presentShortcutsIfNecessary {
-  // Initialize the shortcuts feature when necessary.
-  if (base::FeatureList::IsEnabled(
-          omnibox::kOmniboxPopupShortcutIconsInZeroState) &&
-      !self.browserState->IsOffTheRecord() && !self.shortcutsCoordinator) {
-    self.shortcutsCoordinator = [[ShortcutsCoordinator alloc]
-        initWithBaseViewController:self.popupViewController
-                      browserState:self.browserState];
-    self.shortcutsCoordinator.dispatcher =
-        (id<ApplicationCommands, BrowserCommands,
-            OmniboxFocuser>)(self.dispatcher);
-    [self.shortcutsCoordinator start];
-    self.popupViewController.shortcutsViewController =
-        self.shortcutsCoordinator.viewController;
-  }
-
-  // Show shortcuts when the feature is enabled. Don't show them on NTP as they
-  // are already part of the NTP.
-  if (!IsVisibleURLNewTabPage(self.webStateList->GetActiveWebState()) &&
-      base::FeatureList::IsEnabled(
-          omnibox::kOmniboxPopupShortcutIconsInZeroState) &&
-      !self.browserState->IsOffTheRecord()) {
-    self.popupViewController.shortcutsEnabled = YES;
-  }
-
-  [self.mediator.presenter updatePopup];
-  self.mediator.open = self.mediator.presenter.isOpen;
-}
-
-- (void)dismissShortcuts {
-  self.popupViewController.shortcutsEnabled = NO;
-  [self.mediator.presenter updatePopup];
-  self.mediator.open = self.mediator.presenter.isOpen;
-}
-
 #pragma mark - Property accessor
 
 - (BOOL)hasResults {
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_legacy_view_controller.mm b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_legacy_view_controller.mm
index 0c98970..dae9075 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_legacy_view_controller.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_legacy_view_controller.mm
@@ -380,11 +380,6 @@
 
 - (CGFloat)tableView:(UITableView*)tableView
     heightForRowAtIndexPath:(NSIndexPath*)indexPath {
-  if (self.shortcutsEnabled && indexPath.row == 0 &&
-      self.currentResult.count == 0) {
-    return self.shortcutsViewController.collectionView.collectionViewLayout
-        .collectionViewContentSize.height;
-  }
 
   DCHECK_EQ(0U, (NSUInteger)indexPath.section);
   DCHECK_LT((NSUInteger)indexPath.row, self.currentResult.count);
@@ -396,11 +391,6 @@
         cellForRowAtIndexPath:(NSIndexPath*)indexPath {
   DCHECK_EQ(0U, (NSUInteger)indexPath.section);
 
-  if (self.shortcutsEnabled && indexPath.row == 0 &&
-      self.currentResult.count == 0) {
-    return self.shortcutsCell;
-  }
-
   DCHECK_LT((NSUInteger)indexPath.row, self.currentResult.count);
   return _rows[indexPath.row];
 }
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller.mm b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller.mm
index 8799478..7589570 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller.mm
@@ -65,7 +65,6 @@
 - (void)tableView:(UITableView*)tableView
       willDisplayCell:(UITableViewCell*)cell
     forRowAtIndexPath:(NSIndexPath*)indexPath {
-  [super tableView:tableView willDisplayCell:cell forRowAtIndexPath:indexPath];
   if ([cell isKindOfClass:[OmniboxPopupRowCell class]]) {
     OmniboxPopupRowCell* rowCell =
         base::mac::ObjCCastStrict<OmniboxPopupRowCell>(cell);
@@ -83,11 +82,6 @@
 
 - (CGFloat)tableView:(UITableView*)tableView
     heightForRowAtIndexPath:(NSIndexPath*)indexPath {
-  if (self.shortcutsEnabled && indexPath.row == 0 &&
-      self.currentResult.count == 0) {
-    return self.shortcutsViewController.collectionView.collectionViewLayout
-        .collectionViewContentSize.height;
-  }
   return UITableViewAutomaticDimension;
 }
 
@@ -96,11 +90,6 @@
         cellForRowAtIndexPath:(NSIndexPath*)indexPath {
   DCHECK_EQ(0U, (NSUInteger)indexPath.section);
 
-  if (self.shortcutsEnabled && indexPath.row == 0 &&
-      self.currentResult.count == 0) {
-    return self.shortcutsCell;
-  }
-
   DCHECK_LT((NSUInteger)indexPath.row, self.currentResult.count);
   OmniboxPopupRowCell* cell = [self.tableView
       dequeueReusableCellWithIdentifier:OmniboxPopupRowCellReuseIdentifier
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller_unittest.mm b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller_unittest.mm
index 9bdbddb..e2d9c53 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller_unittest.mm
@@ -26,37 +26,6 @@
   OmniboxPopupLegacyViewController* popup_view_controller_;
 };
 
-TEST_F(OmniboxPopupViewControllerTest, HasCellsWhenShortcutsEnabled) {
-  // This test makes an assumption that this view controller is a datasource for
-  // a table view. Rewrite this test if this is not the case anymore.
-  EXPECT_TRUE([popup_view_controller_
-      conformsToProtocol:@protocol(UITableViewDataSource)]);
-  id<UITableViewDataSource> datasource =
-      (id<UITableViewDataSource>)popup_view_controller_;
-  UITableView* table_view = [[UITableView alloc] init];
-
-  // A stub view controller.
-  UICollectionViewController* shortcutsViewController =
-      [[UICollectionViewController alloc] init];
-
-  // Shortcuts are not enabled by default.
-  EXPECT_FALSE(popup_view_controller_.shortcutsEnabled);
-
-  // Check that the shorcuts row doesn't appear.
-  [popup_view_controller_ updateMatches:@[] withAnimation:NO];
-  EXPECT_EQ([datasource tableView:table_view numberOfRowsInSection:0], 0);
-
-  // Enable shortcuts and verify they appear. When enabling, the view controller
-  // has to be non-nil.
-  popup_view_controller_.shortcutsViewController = shortcutsViewController;
-  popup_view_controller_.shortcutsEnabled = YES;
-  EXPECT_EQ([datasource tableView:table_view numberOfRowsInSection:0], 1);
-
-  // Disable and verify it disappears again.
-  popup_view_controller_.shortcutsEnabled = NO;
-  EXPECT_EQ([datasource tableView:table_view numberOfRowsInSection:0], 0);
-}
-
 TEST_F(OmniboxPopupViewControllerTest, HasTabMatch) {
   EXPECT_TRUE([popup_view_controller_
       conformsToProtocol:@protocol(UITableViewDataSource)]);
diff --git a/ios/chrome/browser/ui/omnibox/popup/shortcuts/BUILD.gn b/ios/chrome/browser/ui/omnibox/popup/shortcuts/BUILD.gn
deleted file mode 100644
index 0f0ebd0..0000000
--- a/ios/chrome/browser/ui/omnibox/popup/shortcuts/BUILD.gn
+++ /dev/null
@@ -1,126 +0,0 @@
-# Copyright 2018 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-source_set("shortcuts") {
-  sources = [
-    "collection_shortcut_cell.h",
-    "collection_shortcut_cell.mm",
-    "most_visited_shortcut_cell.h",
-    "most_visited_shortcut_cell.mm",
-    "shortcuts_coordinator.h",
-    "shortcuts_coordinator.mm",
-    "shortcuts_view_controller.h",
-    "shortcuts_view_controller.mm",
-  ]
-  deps = [
-    ":shortcuts_internal",
-    "//base",
-    "//components/ntp_tiles",
-    "//ios/chrome/browser/favicon",
-    "//ios/chrome/browser/ntp_tiles",
-    "//ios/chrome/browser/reading_list",
-    "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
-    "//ios/chrome/browser/ui/ntp_tile_views",
-    "//ios/chrome/browser/ui/ntp_tile_views:constants",
-    "//ios/chrome/browser/url_loading",
-    "//ios/chrome/common/favicon",
-    "//ios/chrome/common/ui_util",
-  ]
-  configs += [ "//build/config/compiler:enable_arc" ]
-}
-
-source_set("shortcuts_internal") {
-  sources = [
-    "shortcuts_consumer.h",
-    "shortcuts_mediator.h",
-    "shortcuts_mediator.mm",
-    "shortcuts_most_visited_item.h",
-    "shortcuts_most_visited_item.mm",
-    "shortcuts_view_controller_delegate.h",
-  ]
-  configs += [ "//build/config/compiler:enable_arc" ]
-
-  deps = [
-    "//components/ntp_tiles",
-    "//components/reading_list/core",
-    "//components/reading_list/ios",
-    "//ios/chrome/browser/ntp_tiles",
-    "//ios/chrome/browser/ui/commands:commands",
-    "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
-    "//ios/chrome/browser/ui/favicon",
-    "//ios/chrome/browser/ui/toolbar/public",
-    "//ios/chrome/browser/url_loading",
-    "//ios/web/public",
-  ]
-}
-
-source_set("unit_tests") {
-  configs += [ "//build/config/compiler:enable_arc" ]
-  testonly = true
-  sources = [
-    "shortcuts_coordinator_unittest.mm",
-    "shortcuts_view_controller_unittest.mm",
-  ]
-  deps = [
-    ":shortcuts",
-    ":shortcuts_internal",
-    "//base",
-    "//ios/chrome/app/strings",
-    "//ios/chrome/browser",
-    "//testing/gtest",
-    "//ui/base",
-  ]
-}
-
-source_set("eg_tests") {
-  defines = [ "CHROME_EARL_GREY_1" ]
-  testonly = true
-  sources = [
-    "shortcuts_egtest.mm",
-  ]
-  deps = [
-    "//base",
-    "//base/test:test_support",
-    "//components/strings",
-    "//ios/chrome/browser/ui:feature_flags",
-    "//ios/chrome/browser/ui/content_suggestions:content_suggestions_constant",
-    "//ios/chrome/browser/ui/omnibox:omnibox_popup_shared",
-    "//ios/chrome/test/app:test_support",
-    "//ios/chrome/test/earl_grey:test_support",
-    "//ios/testing/earl_grey:earl_grey_support",
-    "//ios/third_party/earl_grey:earl_grey+link",
-    "//net:test_support",
-  ]
-  libs = [
-    "UIKit.framework",
-    "XCTest.framework",
-  ]
-  configs += [ "//build/config/compiler:enable_arc" ]
-}
-
-source_set("eg2_tests") {
-  defines = [ "CHROME_EARL_GREY_2" ]
-  configs += [
-    "//build/config/compiler:enable_arc",
-    "//build/config/ios:xctest_config",
-  ]
-
-  testonly = true
-  sources = [
-    "shortcuts_egtest.mm",
-  ]
-  deps = [
-    "//base",
-    "//base/test:test_support",
-    "//components/strings",
-    "//ios/chrome/browser/ui:feature_flags",
-    "//ios/chrome/browser/ui/content_suggestions:content_suggestions_constant",
-    "//ios/chrome/browser/ui/omnibox:omnibox_popup_shared",
-    "//ios/chrome/test/earl_grey:eg_test_support+eg2",
-    "//ios/testing/earl_grey:eg_test_support+eg2",
-    "//ios/third_party/earl_grey2:test_lib",
-    "//net:test_support",
-  ]
-  libs = [ "UIKit.framework" ]
-}
diff --git a/ios/chrome/browser/ui/omnibox/popup/shortcuts/collection_shortcut_cell.h b/ios/chrome/browser/ui/omnibox/popup/shortcuts/collection_shortcut_cell.h
deleted file mode 100644
index 4b2d10d..0000000
--- a/ios/chrome/browser/ui/omnibox/popup/shortcuts/collection_shortcut_cell.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_OMNIBOX_POPUP_SHORTCUTS_COLLECTION_SHORTCUT_CELL_H_
-#define IOS_CHROME_BROWSER_UI_OMNIBOX_POPUP_SHORTCUTS_COLLECTION_SHORTCUT_CELL_H_
-
-#import <UIKit/UIKit.h>
-
-@class NTPShortcutTileView;
-
-// A collection view subclass that contains a collection shortcut tile.
-@interface CollectionShortcutCell : UICollectionViewCell
-
-// The tile contained in the cell.
-@property(nonatomic, strong) NTPShortcutTileView* tile;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_OMNIBOX_POPUP_SHORTCUTS_COLLECTION_SHORTCUT_CELL_H_
diff --git a/ios/chrome/browser/ui/omnibox/popup/shortcuts/collection_shortcut_cell.mm b/ios/chrome/browser/ui/omnibox/popup/shortcuts/collection_shortcut_cell.mm
deleted file mode 100644
index a639c47..0000000
--- a/ios/chrome/browser/ui/omnibox/popup/shortcuts/collection_shortcut_cell.mm
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/omnibox/popup/shortcuts/collection_shortcut_cell.h"
-
-#import "ios/chrome/browser/ui/ntp_tile_views/ntp_shortcut_tile_view.h"
-#import "ios/chrome/common/ui_util/constraints_ui_util.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-@implementation CollectionShortcutCell
-
-- (instancetype)initWithFrame:(CGRect)frame {
-  self = [super initWithFrame:frame];
-  if (self) {
-    _tile = [[NTPShortcutTileView alloc] init];
-    _tile.translatesAutoresizingMaskIntoConstraints = NO;
-    [self.contentView addSubview:_tile];
-    AddSameConstraints(self.contentView, _tile);
-    self.isAccessibilityElement = YES;
-    _tile.countLabel.text = nil;
-    _tile.countContainer.hidden = YES;
-  }
-  return self;
-}
-
-- (void)prepareForReuse {
-  [super prepareForReuse];
-  self.tile.countLabel.text = nil;
-  self.tile.countContainer.hidden = YES;
-}
-
-@end
diff --git a/ios/chrome/browser/ui/omnibox/popup/shortcuts/most_visited_shortcut_cell.h b/ios/chrome/browser/ui/omnibox/popup/shortcuts/most_visited_shortcut_cell.h
deleted file mode 100644
index ecb3993..0000000
--- a/ios/chrome/browser/ui/omnibox/popup/shortcuts/most_visited_shortcut_cell.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_OMNIBOX_POPUP_SHORTCUTS_MOST_VISITED_SHORTCUT_CELL_H_
-#define IOS_CHROME_BROWSER_UI_OMNIBOX_POPUP_SHORTCUTS_MOST_VISITED_SHORTCUT_CELL_H_
-
-#import <UIKit/UIKit.h>
-
-@class NTPMostVisitedTileView;
-
-// A collection view subclass that contains a most visited tile.
-@interface MostVisitedShortcutCell : UICollectionViewCell
-
-// The tile contained in the cell.
-@property(nonatomic, strong) NTPMostVisitedTileView* tile;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_OMNIBOX_POPUP_SHORTCUTS_MOST_VISITED_SHORTCUT_CELL_H_
diff --git a/ios/chrome/browser/ui/omnibox/popup/shortcuts/most_visited_shortcut_cell.mm b/ios/chrome/browser/ui/omnibox/popup/shortcuts/most_visited_shortcut_cell.mm
deleted file mode 100644
index 4e83d0a..0000000
--- a/ios/chrome/browser/ui/omnibox/popup/shortcuts/most_visited_shortcut_cell.mm
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/omnibox/popup/shortcuts/most_visited_shortcut_cell.h"
-
-#import "ios/chrome/browser/ui/ntp_tile_views/ntp_most_visited_tile_view.h"
-#import "ios/chrome/common/ui_util/constraints_ui_util.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-@implementation MostVisitedShortcutCell
-
-- (instancetype)initWithFrame:(CGRect)frame {
-  self = [super initWithFrame:frame];
-  if (self) {
-    _tile = [[NTPMostVisitedTileView alloc] init];
-    _tile.translatesAutoresizingMaskIntoConstraints = NO;
-    [self.contentView addSubview:_tile];
-    AddSameConstraints(self.contentView, _tile);
-    self.isAccessibilityElement = YES;
-  }
-  return self;
-}
-
-- (void)prepareForReuse {
-  [super prepareForReuse];
-  self.tile.titleLabel.text = nil;
-}
-
-@end
diff --git a/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_consumer.h b/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_consumer.h
deleted file mode 100644
index b57a417..0000000
--- a/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_consumer.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_OMNIBOX_POPUP_SHORTCUTS_SHORTCUTS_CONSUMER_H_
-#define IOS_CHROME_BROWSER_UI_OMNIBOX_POPUP_SHORTCUTS_SHORTCUTS_CONSUMER_H_
-
-#import <UIKit/UIKit.h>
-
-#import "ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_most_visited_item.h"
-
-// A protocol defining a consumer of shortcuts data.
-@protocol ShortcutsConsumer<NSObject>
-
-// Called immediately when the shortcuts are available for the first time.
-- (void)mostVisitedShortcutsAvailable:
-    (NSArray<ShortcutsMostVisitedItem*>*)items;
-// Called when the favicon of a given URL has changed or reloaded.
-- (void)faviconChangedForURL:(const GURL&)url;
-// Called when the reading list badge count changes.
-- (void)readingListBadgeUpdatedWithCount:(NSInteger)count;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_OMNIBOX_POPUP_SHORTCUTS_SHORTCUTS_CONSUMER_H_
diff --git a/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_coordinator.h b/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_coordinator.h
deleted file mode 100644
index fd9cd760..0000000
--- a/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_coordinator.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_OMNIBOX_POPUP_SHORTCUTS_SHORTCUTS_COORDINATOR_H_
-#define IOS_CHROME_BROWSER_UI_OMNIBOX_POPUP_SHORTCUTS_SHORTCUTS_COORDINATOR_H_
-
-#import "ios/chrome/browser/ui/coordinators/chrome_coordinator.h"
-
-@protocol ApplicationCommands;
-@protocol BrowserCommands;
-@protocol OmniboxFocuser;
-
-// The coordinator for the shortcuts.
-// Shortcuts are the tiles displayed in the omnibox in the zero state.
-@interface ShortcutsCoordinator : ChromeCoordinator
-
-// The view controller managed by this coordinator.
-@property(nonatomic, strong, readonly)
-    UICollectionViewController* viewController;
-
-@property(nonatomic, weak)
-    id<ApplicationCommands, BrowserCommands, OmniboxFocuser>
-        dispatcher;
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_OMNIBOX_POPUP_SHORTCUTS_SHORTCUTS_COORDINATOR_H_
diff --git a/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_coordinator.mm b/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_coordinator.mm
deleted file mode 100644
index 83bd14c5..0000000
--- a/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_coordinator.mm
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_coordinator.h"
-
-#include "components/ntp_tiles/most_visited_sites.h"
-#include "ios/chrome/browser/favicon/ios_chrome_large_icon_cache_factory.h"
-#include "ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.h"
-#include "ios/chrome/browser/ntp_tiles/ios_most_visited_sites_factory.h"
-#include "ios/chrome/browser/reading_list/reading_list_model_factory.h"
-#import "ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_mediator.h"
-#import "ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_view_controller.h"
-#import "ios/chrome/browser/url_loading/url_loading_service.h"
-#import "ios/chrome/browser/url_loading/url_loading_service_factory.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-@interface ShortcutsCoordinator ()
-
-// Redefined as readwrite and as ShortcutsViewController.
-@property(nonatomic, strong, readwrite) ShortcutsViewController* viewController;
-// The mediator that pushes the most visited tiles and the reading list badge
-// value to the view controller.
-@property(nonatomic, strong) ShortcutsMediator* mediator;
-
-@end
-
-@implementation ShortcutsCoordinator
-
-- (void)start {
-  favicon::LargeIconService* largeIconService =
-      IOSChromeLargeIconServiceFactory::GetForBrowserState(self.browserState);
-  LargeIconCache* cache =
-      IOSChromeLargeIconCacheFactory::GetForBrowserState(self.browserState);
-  std::unique_ptr<ntp_tiles::MostVisitedSites> mostVisitedSites =
-      IOSMostVisitedSitesFactory::NewForBrowserState(self.browserState);
-  ReadingListModel* readingListModel =
-      ReadingListModelFactory::GetForBrowserState(self.browserState);
-  UrlLoadingService* loadingService =
-      UrlLoadingServiceFactory::GetForBrowserState(self.browserState);
-
-  self.mediator = [[ShortcutsMediator alloc]
-      initWithLargeIconService:largeIconService
-                largeIconCache:cache
-               mostVisitedSite:std::move(mostVisitedSites)
-              readingListModel:readingListModel
-                loadingService:loadingService];
-
-  ShortcutsViewController* shortcutsViewController =
-      [[ShortcutsViewController alloc] init];
-  self.viewController = shortcutsViewController;
-  self.mediator.consumer = shortcutsViewController;
-  self.mediator.dispatcher = self.dispatcher;
-  shortcutsViewController.commandHandler = self.mediator;
-}
-
-- (void)stop {
-  self.viewController = nil;
-  self.mediator = nil;
-}
-
-@end
diff --git a/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_coordinator_unittest.mm b/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_coordinator_unittest.mm
deleted file mode 100644
index 4a14abeb..0000000
--- a/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_coordinator_unittest.mm
+++ /dev/null
@@ -1,9 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_coordinator.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
diff --git a/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_egtest.mm b/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_egtest.mm
deleted file mode 100644
index b4fb1ad..0000000
--- a/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_egtest.mm
+++ /dev/null
@@ -1,285 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import <XCTest/XCTest.h>
-
-#include "base/bind.h"
-#include "base/strings/sys_string_conversions.h"
-#include "base/test/scoped_feature_list.h"
-#import "ios/chrome/browser/ui/content_suggestions/ntp_home_constant.h"
-#import "ios/chrome/browser/ui/omnibox/omnibox_constants.h"
-#include "ios/chrome/browser/ui/ui_feature_flags.h"
-#import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
-#import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
-#import "ios/chrome/test/earl_grey/chrome_matchers.h"
-#import "ios/chrome/test/earl_grey/chrome_test_case.h"
-#import "ios/testing/earl_grey/earl_grey_test.h"
-#include "net/test/embedded_test_server/embedded_test_server.h"
-#include "net/test/embedded_test_server/http_request.h"
-#include "net/test/embedded_test_server/http_response.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-namespace {
-
-// Web page that will show up as one of the most visited tiles.
-const char kTilePageLoadedString[] = "This is a web page that you visit often";
-const char kTilePageURL[] = "/tile-page.html";
-const char kTilePageTitle[] = "Often visited page";
-
-// Web page for navigation.
-const char kPageLoadedString[] = "Page loaded!";
-const char kPageURL[] = "/test-page.html";
-const char kPageTitle[] = "Page title!";
-
-// Provides responses for redirect and changed window location URLs.
-std::unique_ptr<net::test_server::HttpResponse> StandardResponse(
-    const net::test_server::HttpRequest& request) {
-  std::unique_ptr<net::test_server::BasicHttpResponse> http_response =
-      std::make_unique<net::test_server::BasicHttpResponse>();
-  http_response->set_code(net::HTTP_OK);
-
-  if (request.relative_url == kPageURL) {
-    http_response->set_content("<html><head><title>" + std::string(kPageTitle) +
-                               "</title></head><body>" +
-                               std::string(kPageLoadedString) +
-                               "</body></html>");
-    return std::move(http_response);
-  }
-
-  if (request.relative_url == kTilePageURL) {
-    http_response->set_content(
-        "<html><head><title>" + std::string(kTilePageTitle) +
-        "</title></head><body>" + std::string(kTilePageLoadedString) +
-        "</body></html>");
-    return std::move(http_response);
-  }
-
-  return nil;
-}
-
-}  //  namespace
-
-// Test case for the Omnibox Shortcuts UI.
-@interface ShortcutsTestCase : ChromeTestCase
-@end
-
-@implementation ShortcutsTestCase {
-  base::test::ScopedFeatureList _featureList;
-}
-
-- (void)setUp {
-  [super setUp];
-  // Enable the shortcuts flag.
-  _featureList.InitAndEnableFeature(kOmniboxPopupShortcutIconsInZeroState);
-
-  // Start a server to be able to navigate to a web page.
-  self.testServer->RegisterRequestHandler(
-      base::BindRepeating(&StandardResponse));
-  GREYAssertTrue(self.testServer->Start(), @"Test server failed to start.");
-
-  [ChromeEarlGrey clearBrowsingHistory];
-  [self prepareMostVisitedTiles];
-  // Clear pasteboard
-  [[UIPasteboard generalPasteboard] setItems:@[]];
-}
-
-#pragma mark - tests
-
-// Tests that the shortcuts show up on a web page.
-- (void)testShowsUp {
-  [self navigateToAPage];
-  [ChromeEarlGreyUI focusOmnibox];
-  [[EarlGrey selectElementWithMatcher:[self mostVisitedTileMatcher]]
-      assertWithMatcher:grey_sufficientlyVisible()];
-}
-
-// Tests that shortcuts don't show up when there are other omnibox suggestions
-// available, for example "what you typed" suggestion.
-- (void)testNotShownWhenSuggestionsAvailable {
-  [self navigateToAPage];
-  [ChromeEarlGreyUI focusOmniboxAndType:@"foo"];
-  [[EarlGrey selectElementWithMatcher:[self mostVisitedTileMatcher]]
-      assertWithMatcher:grey_nil()];
-}
-
-- (void)testShowsUpWhenOmniboxIsEmpty {
-  [self navigateToAPage];
-  // Focus omnibox and hit backspace to erase the text.
-  [ChromeEarlGreyUI focusOmniboxAndType:@"\b"];
-  [[EarlGrey selectElementWithMatcher:[self mostVisitedTileMatcher]]
-      assertWithMatcher:grey_sufficientlyVisible()];
-}
-
-// Test that tapping a most visited tile navigates to that page.
-- (void)testTapMostVisitedTile {
-  [self navigateToAPage];
-  [ChromeEarlGreyUI focusOmnibox];
-  [[EarlGrey selectElementWithMatcher:[self mostVisitedTileMatcher]]
-      performAction:grey_tap()];
-  [ChromeEarlGrey waitForWebStateContainingText:kTilePageLoadedString];
-}
-
-- (void)testBookmarksShortcut {
-  [self navigateToAPage];
-  [ChromeEarlGreyUI focusOmnibox];
-
-  // Tap on bookmarks.
-  [[EarlGrey selectElementWithMatcher:
-                 chrome_test_util::StaticTextWithAccessibilityLabel(
-                     @"Bookmarks")] performAction:grey_tap()];
-
-  // Verify that the bookmarks dialog opened with Done button and "Bookmarks"
-  // title.
-  [[EarlGrey
-      selectElementWithMatcher:grey_allOf(grey_accessibilityTrait(
-                                              UIAccessibilityTraitHeader),
-                                          grey_accessibilityLabel(@"Bookmarks"),
-                                          nil)]
-      assertWithMatcher:grey_sufficientlyVisible()];
-
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::
-                                          BookmarksNavigationBarDoneButton()]
-      performAction:grey_tap()];
-
-  // Verify that after tapping Done the omnibox is defocused.
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::DefocusedLocationView()]
-      assertWithMatcher:grey_sufficientlyVisible()];
-}
-
-#if defined(CHROME_EARL_GREY_2)
-// TODO(crbug.com/1026579): Enable the tests once the bug is fixed
-- (void)FLAKY_testReadingListShortcut {
-#else
-- (void)testReadingListShortcut {
-#endif
-  [self navigateToAPage];
-  [ChromeEarlGreyUI focusOmnibox];
-
-  // Tap on reading list.
-  [[EarlGrey selectElementWithMatcher:
-                 chrome_test_util::StaticTextWithAccessibilityLabel(
-                     @"Reading List")] performAction:grey_tap()];
-
-  // Verify that the reading list dialog opened with Done button and "Reading
-  // List" title.
-  [[EarlGrey
-      selectElementWithMatcher:grey_allOf(
-                                   grey_accessibilityTrait(
-                                       UIAccessibilityTraitHeader),
-                                   grey_accessibilityLabel(@"Reading List"),
-                                   nil)]
-      assertWithMatcher:grey_sufficientlyVisible()];
-
-  [[EarlGrey selectElementWithMatcher:grey_buttonTitle(@"Done")]
-      performAction:grey_tap()];
-
-  // Verify that after tapping Done the omnibox is defocused.
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::DefocusedLocationView()]
-      assertWithMatcher:grey_sufficientlyVisible()];
-}
-
-- (void)testRecentTabsShortcut {
-  [self navigateToAPage];
-  [ChromeEarlGreyUI focusOmnibox];
-
-  // Tap on recent tabs.
-  [[EarlGrey selectElementWithMatcher:
-                 chrome_test_util::StaticTextWithAccessibilityLabel(
-                     @"Recent Tabs")] performAction:grey_tap()];
-
-  // Verify that the Recent Tabs dialog opened with Done button and "Recent
-  // Tabs" title.
-  [[EarlGrey
-      selectElementWithMatcher:grey_allOf(
-                                   grey_accessibilityTrait(
-                                       UIAccessibilityTraitHeader),
-                                   grey_accessibilityLabel(@"Recent Tabs"),
-                                   nil)]
-      assertWithMatcher:grey_sufficientlyVisible()];
-
-  [[EarlGrey selectElementWithMatcher:grey_buttonTitle(@"Done")]
-      performAction:grey_tap()];
-
-  // Verify that after tapping Done the omnibox is defocused.
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::DefocusedLocationView()]
-      assertWithMatcher:grey_sufficientlyVisible()];
-}
-
-- (void)testHistoryShortcut {
-  [self navigateToAPage];
-  [ChromeEarlGreyUI focusOmnibox];
-
-  // Tap on history.
-  [[EarlGrey selectElementWithMatcher:
-                 chrome_test_util::StaticTextWithAccessibilityLabel(@"History")]
-      performAction:grey_tap()];
-
-  // Verify that the History dialog opened with Done button and "History"
-  // title.
-  [[EarlGrey
-      selectElementWithMatcher:grey_allOf(grey_accessibilityTrait(
-                                              UIAccessibilityTraitHeader),
-                                          grey_accessibilityLabel(@"History"),
-                                          nil)]
-      assertWithMatcher:grey_sufficientlyVisible()];
-
-  [[EarlGrey selectElementWithMatcher:grey_buttonTitle(@"Done")]
-      performAction:grey_tap()];
-
-  // Verify that after tapping Done the omnibox is defocused.
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::DefocusedLocationView()]
-      assertWithMatcher:grey_sufficientlyVisible()];
-}
-
-// Tests that on the NTP the shortcuts don't show up.
-- (void)testNTPShortcutsDontShowUp {
-  [[self class] closeAllTabs];
-  [ChromeEarlGrey openNewTab];
-
-  // Tap the fake omnibox.
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::FakeOmnibox()]
-      performAction:grey_tap()];
-  // Wait for the real omnibox to be visible.
-  [ChromeEarlGrey
-      waitForSufficientlyVisibleElementWithMatcher:chrome_test_util::Omnibox()];
-
-  // The shortcuts should not show up here.
-  // The shortcuts are similar to the NTP tiles, so in this test it's necessary
-  // to differentiate where the tile is actually coming from; therefore check
-  // the a11y identifier of the shortcuts.
-  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(
-                                          kShortcutsAccessibilityIdentifier)]
-      assertWithMatcher:grey_nil()];
-}
-
-#pragma mark - helpers
-
-- (void)navigateToAPage {
-  const GURL pageURL = self.testServer->GetURL(kPageURL);
-  [ChromeEarlGrey loadURL:pageURL];
-  [ChromeEarlGrey waitForWebStateContainingText:kPageLoadedString];
-}
-
-- (void)prepareMostVisitedTiles {
-  const GURL pageURL = self.testServer->GetURL(kTilePageURL);
-  [ChromeEarlGrey loadURL:pageURL];
-  [ChromeEarlGrey waitForWebStateContainingText:kTilePageLoadedString];
-
-  // After loading URL, need to do another action before opening a new tab
-  // with the icon present.
-  [ChromeEarlGrey goBack];
-
-  [[self class] closeAllTabs];
-  [ChromeEarlGrey openNewTab];
-}
-
-- (id<GREYMatcher>)mostVisitedTileMatcher {
-  NSString* pageTitle = base::SysUTF8ToNSString(kTilePageTitle);
-  return chrome_test_util::StaticTextWithAccessibilityLabel(pageTitle);
-}
-
-@end
diff --git a/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_mediator.h b/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_mediator.h
deleted file mode 100644
index ee00a103..0000000
--- a/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_mediator.h
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_OMNIBOX_POPUP_SHORTCUTS_SHORTCUTS_MEDIATOR_H_
-#define IOS_CHROME_BROWSER_UI_OMNIBOX_POPUP_SHORTCUTS_SHORTCUTS_MEDIATOR_H_
-
-#import <UIKit/UIKit.h>
-
-#include <memory>
-#import "ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_view_controller_delegate.h"
-
-namespace favicon {
-class LargeIconService;
-}
-namespace ntp_tiles {
-class MostVisitedSites;
-}
-class LargeIconCache;
-class ReadingListModel;
-class UrlLoadingService;
-
-@protocol ApplicationCommands;
-@protocol BrowserCommands;
-@protocol OmniboxFocuser;
-@protocol ShortcutsConsumer;
-
-// Coordinator for the Omnibox Popup Shortcuts.
-@interface ShortcutsMediator : NSObject<ShortcutsViewControllerDelegate>
-
-- (instancetype)
-    initWithLargeIconService:(favicon::LargeIconService*)largeIconService
-              largeIconCache:(LargeIconCache*)largeIconCache
-             mostVisitedSite:
-                 (std::unique_ptr<ntp_tiles::MostVisitedSites>)mostVisitedSites
-            readingListModel:(ReadingListModel*)readingListModel
-              loadingService:(UrlLoadingService*)loadingService;
-
-// The consumer for the data fetched by this mediator.
-@property(nonatomic, weak) id<ShortcutsConsumer> consumer;
-// Dispatcher.
-@property(nonatomic, weak)
-    id<ApplicationCommands, BrowserCommands, OmniboxFocuser>
-        dispatcher;
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_OMNIBOX_POPUP_SHORTCUTS_SHORTCUTS_MEDIATOR_H_
diff --git a/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_mediator.mm b/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_mediator.mm
deleted file mode 100644
index 80388709..0000000
--- a/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_mediator.mm
+++ /dev/null
@@ -1,169 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_mediator.h"
-
-#include "components/ntp_tiles/most_visited_sites.h"
-#include "components/ntp_tiles/ntp_tile.h"
-#include "components/reading_list/core/reading_list_model.h"
-#import "components/reading_list/ios/reading_list_model_bridge_observer.h"
-#include "ios/chrome/browser/ntp_tiles/most_visited_sites_observer_bridge.h"
-#import "ios/chrome/browser/ui/commands/application_commands.h"
-#import "ios/chrome/browser/ui/commands/browser_commands.h"
-#import "ios/chrome/browser/ui/favicon/favicon_attributes_provider.h"
-#import "ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_consumer.h"
-#import "ios/chrome/browser/ui/toolbar/public/omnibox_focuser.h"
-#import "ios/chrome/browser/url_loading/url_loading_params.h"
-#import "ios/chrome/browser/url_loading/url_loading_service.h"
-#import "ios/web/public/navigation/navigation_item.h"
-#import "ios/web/public/navigation/navigation_manager.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-namespace {
-
-// Maximum number of most visited tiles fetched.
-const NSInteger kMaxNumMostVisitedTiles = 4;
-
-const CGFloat kFaviconSize = 48;
-const CGFloat kFaviconMinimalSize = 32;
-
-}  // namespace
-
-@interface ShortcutsMediator ()<MostVisitedSitesObserving,
-                                ReadingListModelBridgeObserver>
-
-// Most visited items from the MostVisitedSites service currently displayed.
-@property(nonatomic, strong)
-    NSMutableArray<ShortcutsMostVisitedItem*>* mostVisitedItems;
-
-@property(nonatomic, strong)
-    FaviconAttributesProvider* faviconAttributesProvider;
-
-@end
-
-@implementation ShortcutsMediator {
-  std::unique_ptr<ntp_tiles::MostVisitedSites> _mostVisitedSites;
-  std::unique_ptr<ntp_tiles::MostVisitedSitesObserverBridge> _mostVisitedBridge;
-  // ShortcutsMediator observes the reading list model to get the reading list
-  // badge.
-  std::unique_ptr<ReadingListModelBridge> _readingListModelBridge;
-  UrlLoadingService* _loadingService;
-}
-
-- (instancetype)
-    initWithLargeIconService:(favicon::LargeIconService*)largeIconService
-              largeIconCache:(LargeIconCache*)largeIconCache
-             mostVisitedSite:
-                 (std::unique_ptr<ntp_tiles::MostVisitedSites>)mostVisitedSites
-            readingListModel:(ReadingListModel*)readingListModel
-              loadingService:(UrlLoadingService*)loadingService {
-  self = [super init];
-  if (self) {
-    _faviconAttributesProvider = [[FaviconAttributesProvider alloc]
-        initWithFaviconSize:kFaviconSize
-             minFaviconSize:kFaviconMinimalSize
-           largeIconService:largeIconService];
-    _faviconAttributesProvider.cache = largeIconCache;
-
-    _mostVisitedSites = std::move(mostVisitedSites);
-    _mostVisitedBridge =
-        std::make_unique<ntp_tiles::MostVisitedSitesObserverBridge>(self);
-    _mostVisitedSites->SetMostVisitedURLsObserver(_mostVisitedBridge.get(),
-                                                  kMaxNumMostVisitedTiles);
-
-    _readingListModelBridge =
-        std::make_unique<ReadingListModelBridge>(self, readingListModel);
-
-    _loadingService = loadingService;
-  }
-  return self;
-}
-
-- (void)setConsumer:(id<ShortcutsConsumer>)consumer {
-  _consumer = consumer;
-  [self pushData];
-}
-
-#pragma mark - private
-
-- (void)pushData {
-  [self.consumer mostVisitedShortcutsAvailable:self.mostVisitedItems];
-}
-
-- (void)fetchFaviconForItem:(ShortcutsMostVisitedItem*)item {
-  __weak ShortcutsMediator* weakSelf = self;
-  [self.faviconAttributesProvider
-      fetchFaviconAttributesForURL:item.URL
-                        completion:^(FaviconAttributes* attributes) {
-                          item.attributes = attributes;
-                          [weakSelf.consumer faviconChangedForURL:item.URL];
-                        }];
-}
-
-#pragma mark - ShortcutsViewControllerDelegate
-
-- (void)openMostVisitedItem:(ShortcutsMostVisitedItem*)item {
-  [self.dispatcher cancelOmniboxEdit];
-  UrlLoadParams params = UrlLoadParams::InCurrentTab(item.URL);
-  params.web_params.transition_type = ui::PAGE_TRANSITION_AUTO_BOOKMARK;
-  _loadingService->Load(params);
-}
-
-- (void)openBookmarks {
-  [self.dispatcher cancelOmniboxEdit];
-  [self.dispatcher showBookmarksManager];
-}
-
-- (void)openReadingList {
-  [self.dispatcher cancelOmniboxEdit];
-  [self.dispatcher showReadingList];
-}
-- (void)openRecentTabs {
-  [self.dispatcher cancelOmniboxEdit];
-  [self.dispatcher showRecentTabs];
-}
-- (void)openHistory {
-  [self.dispatcher cancelOmniboxEdit];
-  [self.dispatcher showHistory];
-}
-
-#pragma mark - MostVisitedSitesObserving
-
-- (void)onMostVisitedURLsAvailable:
-    (const ntp_tiles::NTPTilesVector&)mostVisited {
-  NSMutableArray* newMostVisited = [NSMutableArray array];
-  for (const ntp_tiles::NTPTile& tile : mostVisited) {
-    ShortcutsMostVisitedItem* item =
-        [ShortcutsMostVisitedItem itemWithNTPTile:tile];
-    [newMostVisited addObject:item];
-    [self fetchFaviconForItem:item];
-  }
-
-  self.mostVisitedItems = newMostVisited;
-  [self pushData];
-}
-
-- (void)onIconMadeAvailable:(const GURL&)siteURL {
-  for (ShortcutsMostVisitedItem* item in self.mostVisitedItems) {
-    if (item.URL == siteURL) {
-      [self fetchFaviconForItem:item];
-      return;
-    }
-  }
-}
-
-#pragma mark - ReadingListModelBridgeObserver
-
-- (void)readingListModelLoaded:(const ReadingListModel*)model {
-  [self.consumer readingListBadgeUpdatedWithCount:model->unread_size()];
-}
-
-- (void)readingListModelDidApplyChanges:(const ReadingListModel*)model {
-  [self.consumer readingListBadgeUpdatedWithCount:model->unread_size()];
-}
-
-@end
diff --git a/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_most_visited_item.h b/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_most_visited_item.h
deleted file mode 100644
index 99758731..0000000
--- a/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_most_visited_item.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_OMNIBOX_POPUP_SHORTCUTS_SHORTCUTS_MOST_VISITED_ITEM_H_
-#define IOS_CHROME_BROWSER_UI_OMNIBOX_POPUP_SHORTCUTS_SHORTCUTS_MOST_VISITED_ITEM_H_
-
-#import <Foundation/Foundation.h>
-
-class GURL;
-namespace ntp_tiles {
-struct NTPTile;
-}  // namespace ntp_tiles
-
-@class FaviconAttributes;
-
-// An item that represents a Most Visited site in omnibox popup zero-state
-// shortcuts.
-@interface ShortcutsMostVisitedItem : NSObject
-
-+ (instancetype)itemWithNTPTile:(const ntp_tiles::NTPTile&)tile;
-
-@property(nonatomic, copy) NSString* title;
-@property(nonatomic, assign) GURL URL;
-@property(nonatomic, strong) FaviconAttributes* attributes;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_OMNIBOX_POPUP_SHORTCUTS_SHORTCUTS_MOST_VISITED_ITEM_H_
diff --git a/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_most_visited_item.mm b/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_most_visited_item.mm
deleted file mode 100644
index d11d5aa..0000000
--- a/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_most_visited_item.mm
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_most_visited_item.h"
-
-#include "base/strings/sys_string_conversions.h"
-#include "components/ntp_tiles/ntp_tile.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-@implementation ShortcutsMostVisitedItem
-
-+ (instancetype)itemWithNTPTile:(const ntp_tiles::NTPTile&)tile {
-  ShortcutsMostVisitedItem* item = [[ShortcutsMostVisitedItem alloc] init];
-  item.title = base::SysUTF16ToNSString(tile.title);
-  item.URL = tile.url;
-  return item;
-}
-
-@end
diff --git a/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_view_controller.h b/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_view_controller.h
deleted file mode 100644
index cac20c1c..0000000
--- a/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_view_controller.h
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_OMNIBOX_POPUP_SHORTCUTS_SHORTCUTS_VIEW_CONTROLLER_H_
-#define IOS_CHROME_BROWSER_UI_OMNIBOX_POPUP_SHORTCUTS_SHORTCUTS_VIEW_CONTROLLER_H_
-
-#import <UIKit/UIKit.h>
-
-#import "ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_consumer.h"
-
-@protocol ShortcutsViewControllerDelegate;
-
-// The view controller displaying the omnibox shortcuts in the zero state.
-@interface ShortcutsViewController
-    : UICollectionViewController <ShortcutsConsumer>
-
-@property(nonatomic, weak) id<ShortcutsViewControllerDelegate> commandHandler;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_OMNIBOX_POPUP_SHORTCUTS_SHORTCUTS_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_view_controller.mm b/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_view_controller.mm
deleted file mode 100644
index 735b034..0000000
--- a/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_view_controller.mm
+++ /dev/null
@@ -1,264 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_view_controller.h"
-
-#include "base/logging.h"
-#include "base/mac/foundation_util.h"
-#include "base/metrics/user_metrics.h"
-#include "base/metrics/user_metrics_action.h"
-#import "ios/chrome/browser/ui/ntp_tile_views/ntp_most_visited_tile_view.h"
-#import "ios/chrome/browser/ui/ntp_tile_views/ntp_shortcut_tile_view.h"
-#import "ios/chrome/browser/ui/ntp_tile_views/ntp_tile_constants.h"
-#import "ios/chrome/browser/ui/ntp_tile_views/ntp_tile_layout_util.h"
-#import "ios/chrome/browser/ui/omnibox/popup/shortcuts/collection_shortcut_cell.h"
-#import "ios/chrome/browser/ui/omnibox/popup/shortcuts/most_visited_shortcut_cell.h"
-#import "ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_view_controller_delegate.h"
-#import "ios/chrome/common/favicon/favicon_view.h"
-#import "ios/chrome/common/ui_util/constraints_ui_util.h"
-#include "url/gurl.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-namespace {
-const CGFloat kTopInset = 10;
-}  // namespace
-
-@interface ShortcutsViewController ()
-
-// Latest most visited items. Updated directly from the consumer calls.
-@property(nonatomic, strong)
-    NSArray<ShortcutsMostVisitedItem*>* latestMostVisitedItems;
-// Currently displayed most visited items. Will be set to nil when the view
-// disappears, and set to |latestMostVisitedItems| when the view appears. This
-// prevents the updates when the user sees the tiles.
-@property(nonatomic, strong)
-    NSArray<ShortcutsMostVisitedItem*>* displayedMostVisitedItems;
-@property(nonatomic, assign) NSInteger readingListBadgeValue;
-
-@end
-
-@implementation ShortcutsViewController
-
-#pragma mark - initializers
-
-- (instancetype)init {
-  UICollectionViewFlowLayout* layout =
-      [[UICollectionViewFlowLayout alloc] init];
-  layout.sectionInset = UIEdgeInsetsMake(kTopInset, 0, 0, 0);
-  self = [super initWithCollectionViewLayout:layout];
-  if (self) {
-    self.collectionView.backgroundColor = [UIColor clearColor];
-    [self.collectionView registerClass:[MostVisitedShortcutCell class]
-            forCellWithReuseIdentifier:NSStringFromClass(
-                                           [MostVisitedShortcutCell class])];
-    [self.collectionView registerClass:[CollectionShortcutCell class]
-            forCellWithReuseIdentifier:NSStringFromClass(
-                                           [CollectionShortcutCell class])];
-  }
-  return self;
-}
-
-#pragma mark - UIViewController
-
-- (void)viewWillAppear:(BOOL)animated {
-  [super viewWillAppear:animated];
-
-  // Promote the latest most visited items to the displayed ones and reload the
-  // collection view data.
-  self.displayedMostVisitedItems = self.latestMostVisitedItems;
-
-  [self configureLayout:base::mac::ObjCCastStrict<UICollectionViewFlowLayout>(
-                            self.collectionViewLayout)];
-}
-
-- (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection {
-  [super traitCollectionDidChange:previousTraitCollection];
-
-  [self configureLayout:base::mac::ObjCCastStrict<UICollectionViewFlowLayout>(
-                            self.collectionViewLayout)];
-}
-
-#pragma mark - ShortcutsConsumer
-
-- (void)mostVisitedShortcutsAvailable:
-    (NSArray<ShortcutsMostVisitedItem*>*)items {
-  self.latestMostVisitedItems = items;
-
-  // Normally, the most visited tiles should not change when the user sees them.
-  // However, in case there were no items, and now they're available, it is
-  // better to show something, even if this means reloading the view.
-  if (self.displayedMostVisitedItems.count == 0 && self.viewLoaded) {
-    self.displayedMostVisitedItems = self.latestMostVisitedItems;
-    [self.collectionView reloadData];
-  }
-}
-
-- (void)faviconChangedForURL:(const GURL&)URL {
-  if (!self.viewLoaded) {
-    return;
-  }
-
-  for (ShortcutsMostVisitedItem* item in self.displayedMostVisitedItems) {
-    if (item.URL == URL) {
-      NSUInteger i = [self.displayedMostVisitedItems indexOfObject:item];
-      NSIndexPath* indexPath = [NSIndexPath indexPathForItem:i inSection:0];
-      MostVisitedShortcutCell* cell =
-          base::mac::ObjCCastStrict<MostVisitedShortcutCell>(
-              [self.collectionView cellForItemAtIndexPath:indexPath]);
-      [self configureMostVisitedCell:cell
-                            withItem:self.displayedMostVisitedItems[i]];
-    }
-  }
-}
-
-- (void)readingListBadgeUpdatedWithCount:(NSInteger)count {
-  self.readingListBadgeValue = count;
-  if (!self.viewLoaded) {
-    return;
-  }
-
-  NSIndexPath* readingListShortcutIndexPath =
-      [NSIndexPath indexPathForItem:NTPCollectionShortcutTypeReadingList +
-                                    self.displayedMostVisitedItems.count
-                          inSection:0];
-  [self.collectionView
-      reloadItemsAtIndexPaths:@[ readingListShortcutIndexPath ]];
-}
-
-#pragma mark - UICollectionViewDataSource
-
-- (NSInteger)numberOfSectionsInCollectionView:
-    (UICollectionView*)collectionView {
-  return 1;
-}
-
-- (NSInteger)collectionView:(UICollectionView*)collectionView
-     numberOfItemsInSection:(NSInteger)section {
-  DCHECK(section == 0);
-  return self.displayedMostVisitedItems.count + NTPCollectionShortcutTypeCount;
-}
-
-// The cell that is returned must be retrieved from a call to
-// -dequeueReusableCellWithReuseIdentifier:forIndexPath:
-- (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView
-                 cellForItemAtIndexPath:(NSIndexPath*)indexPath {
-  if (static_cast<NSUInteger>(indexPath.row) <
-      self.displayedMostVisitedItems.count) {
-    MostVisitedShortcutCell* cell = [self.collectionView
-        dequeueReusableCellWithReuseIdentifier:
-            NSStringFromClass([MostVisitedShortcutCell class])
-                                  forIndexPath:indexPath];
-    ShortcutsMostVisitedItem* item =
-        self.displayedMostVisitedItems[indexPath.item];
-    [self configureMostVisitedCell:cell withItem:item];
-    cell.accessibilityTraits = UIAccessibilityTraitButton;
-    return cell;
-  } else {
-    CollectionShortcutCell* cell = [self.collectionView
-        dequeueReusableCellWithReuseIdentifier:
-            NSStringFromClass([CollectionShortcutCell class])
-                                  forIndexPath:indexPath];
-    DCHECK(static_cast<NSUInteger>(indexPath.row) <
-           self.displayedMostVisitedItems.count +
-               NTPCollectionShortcutTypeCount)
-        << "Only four collection shortcuts described in "
-           "NTPCollectionShortcutType are supported";
-
-    NTPCollectionShortcutType type = (NTPCollectionShortcutType)(
-        indexPath.item - self.displayedMostVisitedItems.count);
-    [self configureCollectionShortcutCell:cell withCollection:type];
-    cell.accessibilityTraits = UIAccessibilityTraitButton;
-    return cell;
-  }
-
-  return nil;
-}
-
-- (void)configureMostVisitedCell:(MostVisitedShortcutCell*)cell
-                        withItem:(ShortcutsMostVisitedItem*)item {
-  [cell.tile.faviconView configureWithAttributes:item.attributes];
-  cell.tile.titleLabel.text = item.title;
-  cell.accessibilityLabel = cell.tile.titleLabel.text;
-}
-
-- (void)configureCollectionShortcutCell:(CollectionShortcutCell*)cell
-                         withCollection:(NTPCollectionShortcutType)type {
-  cell.tile.titleLabel.text = TitleForCollectionShortcutType(type);
-  cell.tile.iconView.image = ImageForCollectionShortcutType(type);
-  cell.accessibilityLabel = cell.tile.titleLabel.text;
-  if (@available(iOS 13, *)) {
-    // The accessibilityUserInputLabel should just be the title, with nothing
-    // extra for the reading list tile.
-    cell.accessibilityUserInputLabels = @[ cell.tile.titleLabel.text ];
-  }
-
-  if (type == NTPCollectionShortcutTypeReadingList) {
-    if (self.readingListBadgeValue > 0) {
-      cell.tile.countLabel.text = [@(self.readingListBadgeValue) stringValue];
-      cell.tile.countContainer.hidden = NO;
-      cell.accessibilityLabel = [NSString
-          stringWithFormat:@"%@, %@", cell.accessibilityLabel,
-                           AccessibilityLabelForReadingListCellWithCount(
-                               self.readingListBadgeValue)];
-    }
-  }
-}
-
-#pragma mark - UICollectionViewDelegate
-
-- (void)collectionView:(UICollectionView*)collectionView
-    didSelectItemAtIndexPath:(NSIndexPath*)indexPath {
-  if (static_cast<NSUInteger>(indexPath.row) <
-      self.displayedMostVisitedItems.count) {
-    ShortcutsMostVisitedItem* item =
-        self.displayedMostVisitedItems[indexPath.item];
-    DCHECK(item);
-    [self.commandHandler openMostVisitedItem:item];
-    base::RecordAction(
-        base::UserMetricsAction("MobileOmniboxShortcutsOpenMostVisitedItem"));
-  } else {
-    NTPCollectionShortcutType type = (NTPCollectionShortcutType)(
-        indexPath.item - self.displayedMostVisitedItems.count);
-    switch (type) {
-      case NTPCollectionShortcutTypeBookmark:
-        [self.commandHandler openBookmarks];
-        base::RecordAction(
-            base::UserMetricsAction("MobileOmniboxShortcutsOpenBookmarks"));
-        break;
-      case NTPCollectionShortcutTypeRecentTabs:
-        [self.commandHandler openRecentTabs];
-        base::RecordAction(
-            base::UserMetricsAction("MobileOmniboxShortcutsOpenRecentTabs"));
-        break;
-      case NTPCollectionShortcutTypeReadingList:
-        [self.commandHandler openReadingList];
-        base::RecordAction(
-            base::UserMetricsAction("MobileOmniboxShortcutsOpenReadingList"));
-        break;
-      case NTPCollectionShortcutTypeHistory:
-        [self.commandHandler openHistory];
-        base::RecordAction(
-            base::UserMetricsAction("MobileOmniboxShortcutsOpenHistory"));
-        break;
-      case NTPCollectionShortcutTypeCount:
-        NOTREACHED();
-        break;
-    }
-  }
-}
-
-#pragma mark - Private
-
-- (void)configureLayout:(UICollectionViewFlowLayout*)layout {
-  layout.minimumLineSpacing = kNtpTilesVerticalSpacing;
-  layout.minimumInteritemSpacing =
-      NtpTilesHorizontalSpacing(self.traitCollection);
-  layout.itemSize =
-      MostVisitedCellSize(self.traitCollection.preferredContentSizeCategory);
-}
-
-@end
diff --git a/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_view_controller_delegate.h b/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_view_controller_delegate.h
deleted file mode 100644
index cc03150..0000000
--- a/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_view_controller_delegate.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_OMNIBOX_POPUP_SHORTCUTS_SHORTCUTS_VIEW_CONTROLLER_DELEGATE_H_
-#define IOS_CHROME_BROWSER_UI_OMNIBOX_POPUP_SHORTCUTS_SHORTCUTS_VIEW_CONTROLLER_DELEGATE_H_
-
-@class ShortcutsMostVisitedItem;
-
-// Commands originating from ShortcutsViewController, for example ones called
-// when the shortcuts are tapped.
-@protocol ShortcutsViewControllerDelegate
-
-// Called when a most visited shortcut is selected by the user.
-- (void)openMostVisitedItem:(ShortcutsMostVisitedItem*)item;
-// Opens the bookmarks screen and defocuses the omnibox.
-- (void)openBookmarks;
-// Opens the reading list screen and defocuses the omnibox.
-- (void)openReadingList;
-// Opens the recent tabs screen and defocuses the omnibox.
-- (void)openRecentTabs;
-// Opens the history screen and defocuses the omnibox.
-- (void)openHistory;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_OMNIBOX_POPUP_SHORTCUTS_SHORTCUTS_VIEW_CONTROLLER_DELEGATE_H_
diff --git a/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_view_controller_unittest.mm b/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_view_controller_unittest.mm
deleted file mode 100644
index 3b0a558..0000000
--- a/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_view_controller_unittest.mm
+++ /dev/null
@@ -1,9 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_view_controller.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
diff --git a/ios/chrome/browser/ui/page_info/BUILD.gn b/ios/chrome/browser/ui/page_info/BUILD.gn
index 8731a2b..e8a6c0ed 100644
--- a/ios/chrome/browser/ui/page_info/BUILD.gn
+++ b/ios/chrome/browser/ui/page_info/BUILD.gn
@@ -7,8 +7,8 @@
   sources = [
     "legacy_page_info_view_controller.h",
     "legacy_page_info_view_controller.mm",
-    "page_info_model.cc",
-    "page_info_model.h",
+    "page_info_config.h",
+    "page_info_config.mm",
     "page_info_view_controller.h",
     "page_info_view_controller.mm",
   ]
@@ -68,10 +68,20 @@
     "page_info_coordinator.mm",
     "page_info_legacy_coordinator.h",
     "page_info_legacy_coordinator.mm",
+    "page_info_mediator.h",
+    "page_info_mediator.mm",
   ]
   deps = [
     ":page_info",
+    "resources:page_info_bad",
+    "resources:page_info_good",
+    "resources:page_info_info",
+    "resources:page_info_offline",
     "//base",
+    "//components/security_state/core",
+    "//components/ssl_errors",
+    "//components/strings",
+    "//ios/chrome/app/strings",
     "//ios/chrome/browser",
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/main",
@@ -85,6 +95,8 @@
     "//ios/chrome/browser/web_state_list",
     "//ios/web",
     "//ios/web/public",
+    "//ios/web/public/security",
+    "//ui/base",
   ]
   libs = [ "UIKit.framework" ]
 }
diff --git a/ios/chrome/browser/ui/page_info/legacy_page_info_view_controller.h b/ios/chrome/browser/ui/page_info/legacy_page_info_view_controller.h
index 3157236..b6b0476 100644
--- a/ios/chrome/browser/ui/page_info/legacy_page_info_view_controller.h
+++ b/ios/chrome/browser/ui/page_info/legacy_page_info_view_controller.h
@@ -12,18 +12,16 @@
 #include "base/memory/weak_ptr.h"
 
 @protocol BrowserCommands;
+@class PageInfoConfig;
 @protocol PageInfoPresentation;
-class PageInfoModel;
 
-// TODO(crbug.com/227827) Merge 178763: PageInfoModel has been removed in
-// upstream; check if we should use PageInfoModel.
 // The view controller for the page info view.
 @interface LegacyPageInfoViewController : NSObject
 // Designated initializer.
 // The |sourcePoint| parameter should be in the coordinate system of
 // |provider|'s view. Typically, |sourcePoint| would be the midpoint of a button
 // that resulted in this popup being displayed.
-- (id)initWithModel:(PageInfoModel*)model
+- (id)initWithModel:(PageInfoConfig*)model
              sourcePoint:(CGPoint)sourcePoint
     presentationProvider:(id<PageInfoPresentation>)provider
                  handler:(id<BrowserCommands>)handler;
diff --git a/ios/chrome/browser/ui/page_info/legacy_page_info_view_controller.mm b/ios/chrome/browser/ui/page_info/legacy_page_info_view_controller.mm
index 05a51fc1..230aaba 100644
--- a/ios/chrome/browser/ui/page_info/legacy_page_info_view_controller.mm
+++ b/ios/chrome/browser/ui/page_info/legacy_page_info_view_controller.mm
@@ -14,8 +14,8 @@
 #include "components/strings/grit/components_strings.h"
 #import "ios/chrome/browser/ui/commands/browser_commands.h"
 #import "ios/chrome/browser/ui/fancy_ui/bidi_container_view.h"
+#include "ios/chrome/browser/ui/page_info/page_info_config.h"
 #import "ios/chrome/browser/ui/page_info/page_info_constants.h"
-#include "ios/chrome/browser/ui/page_info/page_info_model.h"
 #import "ios/chrome/browser/ui/page_info/requirements/page_info_presentation.h"
 #import "ios/chrome/browser/ui/util/animation_util.h"
 #include "ios/chrome/browser/ui/util/rtl_geometry.h"
@@ -105,7 +105,7 @@
   CGPoint _arrowOriginPoint;
 
   // Model for the data to display.
-  std::unique_ptr<PageInfoModel> _model;
+  PageInfoConfig* _model;
 
   // Width of the view. Depends on the device (iPad/iPhone).
   CGFloat _viewWidth;
@@ -119,8 +119,8 @@
 
 @property(nonatomic, strong) UIView* containerView;
 @property(nonatomic, strong) UIView* popupContainer;
-// An invisible button added to the |containerView|. Closes the popup just like
-// the tap on the background. Exposed purely for voiceover purposes.
+// An invisible button added to the |containerView|. Closes the popup just
+// like the tap on the background. Exposed purely for voiceover purposes.
 @property(nonatomic, strong) UIButton* closeButton;
 
 @end
@@ -129,7 +129,7 @@
 
 #pragma mark public
 
-- (id)initWithModel:(PageInfoModel*)model
+- (id)initWithModel:(PageInfoConfig*)model
              sourcePoint:(CGPoint)sourcePoint
     presentationProvider:(id<PageInfoPresentation>)provider
                  handler:(id<BrowserCommands>)handler {
@@ -154,7 +154,7 @@
         setAutoresizingMask:(UIViewAutoresizingFlexibleTrailingMargin() |
                              UIViewAutoresizingFlexibleBottomMargin)];
 
-    _model.reset(model);
+    _model = model;
     _arrowOriginPoint = sourcePoint;
     _handler = handler;
 
@@ -211,61 +211,46 @@
   // Keep the new subviews in an array that gets replaced at the end.
   NSMutableArray* subviews = [NSMutableArray array];
 
-  int sectionCount = _model->GetSectionCount();
-  PageInfoModel::ButtonAction action = PageInfoModel::BUTTON_NONE;
-
-  for (int i = 0; i < sectionCount; i++) {
-    PageInfoModel::SectionInfo info = _model->GetSectionInfo(i);
-
-    if (action == PageInfoModel::BUTTON_NONE &&
-        info.button != PageInfoModel::BUTTON_NONE) {
-      // Show the button corresponding to the first section that requires a
-      // button.
-      action = info.button;
-    }
-
     // Only certain sections have images. This affects the X position.
-    BOOL hasImage = _model->GetIconImage(info.icon_id) != nil;
-    CGFloat xPosition = (hasImage ? kTextXPosition : kTextXPositionNoImage);
+  BOOL hasImage = _model.image != nil;
+  CGFloat xPosition = (hasImage ? kTextXPosition : kTextXPositionNoImage);
 
-    // Insert the image subview for sections that are appropriate.
-    CGFloat imageBaseline = offset + kImageSize;
-    if (hasImage) {
-      [self addImageViewForInfo:info
-                     toSubviews:subviews
-                       atOffset:offset + PageInfoImageVerticalOffset()];
-    }
+  // Insert the image subview for sections that are appropriate.
+  CGFloat imageBaseline = offset + kImageSize;
+  if (hasImage) {
+    [self addImage:[_model.image imageWithRenderingMode:
+                                     UIImageRenderingModeAlwaysTemplate]
+        toSubviews:subviews
+          atOffset:offset + PageInfoImageVerticalOffset()];
+  }
 
     // Add the title.
-    if (!info.headline.empty()) {
-      offset += [self addHeadlineViewForInfo:info
-                                  toSubviews:subviews
-                                     atPoint:CGPointMake(xPosition, offset)];
-      offset += kHeadlineSpacing;
-    }
+  offset += [self addHeadline:_model.title
+                   toSubviews:subviews
+                      atPoint:CGPointMake(xPosition, offset)];
+  offset += kHeadlineSpacing;
 
-    // Create the description of the state.
-    offset += [self addDescriptionViewForInfo:info
-                                   toSubviews:subviews
-                                      atPoint:CGPointMake(xPosition, offset)];
+  // Create the description of the state.
+  offset += [self addDescription:_model.message
+                      toSubviews:subviews
+                         atPoint:CGPointMake(xPosition, offset)];
 
-    // If at this point the description and optional headline and button are
-    // not as tall as the image, adjust the offset by the difference.
-    CGFloat imageBaselineDelta = imageBaseline - offset;
-    if (imageBaselineDelta > 0)
-      offset += imageBaselineDelta;
+  // If at this point the description and optional headline and button are
+  // not as tall as the image, adjust the offset by the difference.
+  CGFloat imageBaselineDelta = imageBaseline - offset;
+  if (imageBaselineDelta > 0)
+    offset += imageBaselineDelta;
 
-    // Add the separators.
-    int testSectionCount = sectionCount - 1;
-    if (i != testSectionCount ||
-        (i == testSectionCount && action != PageInfoModel::BUTTON_NONE)) {
-      offset += kVerticalSpacing;
-    }
+  // Add the separators.
+  if (_model.buttonAction != PageInfoButtonActionNone) {
+    offset += kVerticalSpacing;
   }
 
   // The last item at the bottom of the window is the help center link. Do not
   // show this for the internal pages, which have one section.
-  offset += [self addButton:action toSubviews:subviews atOffset:offset];
+  offset += [self addButton:_model.buttonAction
+                 toSubviews:subviews
+                   atOffset:offset];
 
   // Add the bottom padding.
   offset += kVerticalSpacing;
@@ -444,27 +429,25 @@
 // Adds the state image at a pre-determined x position and the given y. This
 // does not affect the next Y position because the image is placed next to
 // a text field that is larger and accounts for the image's size.
-- (void)addImageViewForInfo:(const PageInfoModel::SectionInfo&)info
-                 toSubviews:(NSMutableArray*)subviews
-                   atOffset:(CGFloat)offset {
+- (void)addImage:(UIImage*)image
+      toSubviews:(NSMutableArray*)subviews
+        atOffset:(CGFloat)offset {
   CGRect frame = CGRectMake(kFramePadding, offset, kImageSize, kImageSize);
   UIImageView* imageView = [[UIImageView alloc] initWithFrame:frame];
   imageView.tintColor = UIColor.cr_labelColor;
-  UIImage* image = [_model->GetIconImage(info.icon_id)->ToUIImage()
-      imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
   [imageView setImage:image];
   [subviews addObject:imageView];
 }
 
 // Adds the title text field at the given x,y position, and returns the y
 // position for the next element.
-- (CGFloat)addHeadlineViewForInfo:(const PageInfoModel::SectionInfo&)info
-                       toSubviews:(NSMutableArray*)subviews
-                          atPoint:(CGPoint)point {
+- (CGFloat)addHeadline:(NSString*)headline
+            toSubviews:(NSMutableArray*)subviews
+               atPoint:(CGPoint)point {
   CGRect frame = CGRectMake(point.x, point.y, _textWidth, kHeadlineHeight);
   UILabel* label = [[UILabel alloc] initWithFrame:frame];
   [label setTextAlignment:NSTextAlignmentNatural];
-  [label setText:base::SysUTF16ToNSString(info.headline)];
+  [label setText:headline];
   [label setTextColor:UIColor.cr_labelColor];
   [label setFont:PageInfoHeadlineFont()];
   [label setFrame:frame];
@@ -475,13 +458,12 @@
 
 // Adds the description text field at the given x,y position, and returns the y
 // position for the next element.
-- (CGFloat)addDescriptionViewForInfo:(const PageInfoModel::SectionInfo&)info
-                          toSubviews:(NSMutableArray*)subviews
-                             atPoint:(CGPoint)point {
+- (CGFloat)addDescription:(NSString*)description
+               toSubviews:(NSMutableArray*)subviews
+                  atPoint:(CGPoint)point {
   CGRect frame = CGRectMake(point.x, point.y, _textWidth, kImageSize);
   UILabel* label = [[UILabel alloc] initWithFrame:frame];
   [label setTextAlignment:NSTextAlignmentNatural];
-  NSString* description = base::SysUTF16ToNSString(info.description);
   UIFont* font = [MDCTypography captionFont];
   [label setTextColor:UIColor.cr_labelColor];
   [label setText:description];
@@ -499,25 +481,25 @@
 }
 
 // Returns a button with title and action configured for |buttonAction|.
-- (UIButton*)buttonForAction:(PageInfoModel::ButtonAction)buttonAction {
-  if (buttonAction == PageInfoModel::BUTTON_NONE) {
+- (UIButton*)buttonForAction:(PageInfoButtonAction)buttonAction {
+  if (buttonAction == PageInfoButtonActionNone) {
     return nil;
   }
   UIButton* button = [[UIButton alloc] initWithFrame:CGRectZero];
   int messageId;
   NSString* accessibilityID = @"Reload button";
   switch (buttonAction) {
-    case PageInfoModel::BUTTON_NONE:
+    case PageInfoButtonActionNone:
       NOTREACHED();
       return nil;
-    case PageInfoModel::BUTTON_SHOW_SECURITY_HELP:
+    case PageInfoButtonActionShowHelp:
       messageId = IDS_LEARN_MORE;
       accessibilityID = @"Learn more";
       [button addTarget:self.handler
                     action:@selector(showSecurityHelpPage)
           forControlEvents:UIControlEventTouchUpInside];
       break;
-    case PageInfoModel::BUTTON_RELOAD:
+    case PageInfoButtonActionReload:
       messageId = IDS_IOS_PAGE_INFO_RELOAD;
       accessibilityID = @"Reload button";
       [button addTarget:self.handler
@@ -537,7 +519,7 @@
 
 // Adds the the button |buttonAction| that explains the icons. Returns the y
 // position delta for the next offset.
-- (CGFloat)addButton:(PageInfoModel::ButtonAction)buttonAction
+- (CGFloat)addButton:(PageInfoButtonAction)buttonAction
           toSubviews:(NSMutableArray*)subviews
             atOffset:(CGFloat)offset {
   UIButton* button = [self buttonForAction:buttonAction];
diff --git a/ios/chrome/browser/ui/page_info/page_info_config.h b/ios/chrome/browser/ui/page_info/page_info_config.h
new file mode 100644
index 0000000..2ad1dac
--- /dev/null
+++ b/ios/chrome/browser/ui/page_info/page_info_config.h
@@ -0,0 +1,30 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_PAGE_INFO_PAGE_INFO_CONFIG_H_
+#define IOS_CHROME_BROWSER_UI_PAGE_INFO_PAGE_INFO_CONFIG_H_
+
+#import <UIKit/UIKit.h>
+
+// Types of the different actions the page info button can have.
+typedef NS_ENUM(NSUInteger, PageInfoButtonAction) {
+  // No action.
+  PageInfoButtonActionNone,
+  // Show the help page.
+  PageInfoButtonActionShowHelp,
+  // Reload the page.
+  PageInfoButtonActionReload,
+};
+
+// Config for the information displayed by the PageInfo.
+@interface PageInfoConfig : NSObject
+
+@property(nonatomic, copy) NSString* title;
+@property(nonatomic, copy) NSString* message;
+@property(nonatomic, strong) UIImage* image;
+@property(nonatomic, assign) PageInfoButtonAction buttonAction;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_PAGE_INFO_PAGE_INFO_CONFIG_H_
diff --git a/ios/chrome/browser/ui/page_info/page_info_config.mm b/ios/chrome/browser/ui/page_info/page_info_config.mm
new file mode 100644
index 0000000..fda5ae7
--- /dev/null
+++ b/ios/chrome/browser/ui/page_info/page_info_config.mm
@@ -0,0 +1,13 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/page_info/page_info_config.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@implementation PageInfoConfig
+
+@end
diff --git a/ios/chrome/browser/ui/page_info/page_info_legacy_coordinator.mm b/ios/chrome/browser/ui/page_info/page_info_legacy_coordinator.mm
index c822d6a..8e289316 100644
--- a/ios/chrome/browser/ui/page_info/page_info_legacy_coordinator.mm
+++ b/ios/chrome/browser/ui/page_info/page_info_legacy_coordinator.mm
@@ -19,7 +19,7 @@
 #import "ios/chrome/browser/ui/fullscreen/chrome_coordinator+fullscreen_disabling.h"
 #import "ios/chrome/browser/ui/page_info/legacy_page_info_view_controller.h"
 #import "ios/chrome/browser/ui/page_info/page_info_constants.h"
-#include "ios/chrome/browser/ui/page_info/page_info_model.h"
+#import "ios/chrome/browser/ui/page_info/page_info_mediator.h"
 #import "ios/chrome/browser/ui/page_info/requirements/page_info_presentation.h"
 #import "ios/chrome/browser/url_loading/url_loading_params.h"
 #import "ios/chrome/browser/url_loading/url_loading_service.h"
@@ -80,17 +80,18 @@
   [self didStartFullscreenDisablingUI];
 
   GURL url = navItem->GetURL();
-  bool presenting_offline_page =
+  bool presentingOfflinePage =
       OfflinePageTabHelper::FromWebState(webState)->presenting_offline_page();
 
-  // TODO(crbug.com/760387): Get rid of PageInfoModel completely.
-  PageInfoModel* pageInfoModel = new PageInfoModel(
-      navItem->GetURL(), navItem->GetSSL(), presenting_offline_page);
+  PageInfoConfig* config =
+      [PageInfoMediator configurationForURL:navItem->GetURL()
+                                  SSLStatus:navItem->GetSSL()
+                                offlinePage:presentingOfflinePage];
 
   CGPoint originPresentationCoordinates = [self.presentationProvider
       convertToPresentationCoordinatesForOrigin:self.originPoint];
   self.pageInfoViewController = [[LegacyPageInfoViewController alloc]
-             initWithModel:pageInfoModel
+             initWithModel:config
                sourcePoint:originPresentationCoordinates
       presentationProvider:self.presentationProvider
                    handler:HandlerForProtocol(
diff --git a/ios/chrome/browser/ui/page_info/page_info_mediator.h b/ios/chrome/browser/ui/page_info/page_info_mediator.h
new file mode 100644
index 0000000..2cb5b13
--- /dev/null
+++ b/ios/chrome/browser/ui/page_info/page_info_mediator.h
@@ -0,0 +1,32 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_PAGE_INFO_PAGE_INFO_MEDIATOR_H_
+#define IOS_CHROME_BROWSER_UI_PAGE_INFO_PAGE_INFO_MEDIATOR_H_
+
+#import <Foundation/Foundation.h>
+
+namespace web {
+struct SSLStatus;
+}
+
+@class PageInfoConfig;
+class GURL;
+
+// Mediator for the PageInfo, extracting the data to be displayed for the web
+// informations.
+@interface PageInfoMediator : NSObject
+
+// For now this object only have static method.
+- (instancetype)init NS_UNAVAILABLE;
+
+// Returns a configuration based on the |URL|, the SSL |status| and if the
+// current page is an |offlinePage|.
++ (PageInfoConfig*)configurationForURL:(const GURL&)URL
+                             SSLStatus:(web::SSLStatus&)status
+                           offlinePage:(BOOL)offlinePage;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_PAGE_INFO_PAGE_INFO_MEDIATOR_H_
diff --git a/ios/chrome/browser/ui/page_info/page_info_mediator.mm b/ios/chrome/browser/ui/page_info/page_info_mediator.mm
new file mode 100644
index 0000000..7e0a080
--- /dev/null
+++ b/ios/chrome/browser/ui/page_info/page_info_mediator.mm
@@ -0,0 +1,173 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/page_info/page_info_mediator.h"
+
+#include "base/strings/sys_string_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/security_state/core/security_state.h"
+#include "components/ssl_errors/error_info.h"
+#include "components/strings/grit/components_chromium_strings.h"
+#include "components/strings/grit/components_google_chrome_strings.h"
+#include "components/strings/grit/components_strings.h"
+#include "ios/chrome/browser/chrome_url_constants.h"
+#import "ios/chrome/browser/ui/page_info/page_info_config.h"
+#include "ios/chrome/grit/ios_chromium_strings.h"
+#include "ios/chrome/grit/ios_strings.h"
+#include "ios/web/public/security/ssl_status.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "url/gurl.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+
+// Build the certificate details based on the |SSLStatus| and the |URL|.
+NSString* BuildCertificateDetailString(web::SSLStatus& SSLStatus,
+                                       const GURL& URL) {
+  NSMutableString* certificateDetails = [NSMutableString
+      stringWithString:l10n_util::GetNSString(
+                           IDS_PAGE_INFO_SECURITY_TAB_INSECURE_IDENTITY)];
+  NSString* bullet = @"\n • ";
+  std::vector<ssl_errors::ErrorInfo> errors;
+  ssl_errors::ErrorInfo::GetErrorsForCertStatus(
+      SSLStatus.certificate, SSLStatus.cert_status, URL, &errors);
+  for (size_t i = 0; i < errors.size(); ++i) {
+    [certificateDetails appendString:bullet];
+    [certificateDetails
+        appendString:base::SysUTF16ToNSString(errors[i].short_description())];
+  }
+
+  return certificateDetails;
+}
+
+// Returns a messages, based on the |messagesComponents|, joined by a spacing.
+NSString* BuildMessage(NSArray<NSString*>* messageComponents) {
+  DCHECK(messageComponents.count > 0);
+  NSMutableString* message =
+      [NSMutableString stringWithString:messageComponents[0]];
+  for (NSUInteger index = 1; index < messageComponents.count; index++) {
+    NSString* component = messageComponents[index];
+    if (component.length == 0)
+      continue;
+    [message appendString:@"\n\n"];
+    [message appendString:component];
+  }
+  return message;
+}
+
+}  // namespace
+
+@implementation PageInfoMediator
+
++ (PageInfoConfig*)configurationForURL:(const GURL&)URL
+                             SSLStatus:(web::SSLStatus&)status
+                           offlinePage:(BOOL)offlinePage {
+  PageInfoConfig* dataHolder = [[PageInfoConfig alloc] init];
+  if (offlinePage) {
+    dataHolder.title = l10n_util::GetNSString(IDS_IOS_PAGE_INFO_OFFLINE_TITLE);
+    dataHolder.message = l10n_util::GetNSString(IDS_IOS_PAGE_INFO_OFFLINE_PAGE);
+    dataHolder.image = [UIImage imageNamed:@"page_info_offline"];
+    dataHolder.buttonAction = PageInfoButtonActionReload;
+    return dataHolder;
+  }
+
+  if (URL.SchemeIs(kChromeUIScheme)) {
+    dataHolder.title = base::SysUTF8ToNSString(URL.spec());
+    dataHolder.message = l10n_util::GetNSString(IDS_PAGE_INFO_INTERNAL_PAGE);
+    dataHolder.image = nil;
+    dataHolder.buttonAction = PageInfoButtonActionNone;
+    return dataHolder;
+  }
+
+  // At this point, this is a web page.
+  dataHolder.title = base::SysUTF8ToNSString(URL.host());
+  dataHolder.buttonAction = PageInfoButtonActionShowHelp;
+
+  // Summary and details.
+  if (!status.certificate) {
+    // Not HTTPS. This maps to the WARNING security level. Show the grey
+    // triangle icon in page info based on the same logic used to determine
+    // the iconography in the omnibox.
+    if (security_state::ShouldShowDangerTriangleForWarningLevel()) {
+      dataHolder.image = [UIImage imageNamed:@"page_info_bad"];
+    } else {
+      dataHolder.image = [UIImage imageNamed:@"page_info_info"];
+    }
+    dataHolder.message = BuildMessage(@[
+      l10n_util::GetNSString(IDS_PAGE_INFO_NOT_SECURE_SUMMARY),
+      l10n_util::GetNSString(IDS_PAGE_INFO_NOT_SECURE_DETAILS)
+    ]);
+    return dataHolder;
+  }
+
+  // It is possible to have |SECURITY_STYLE_AUTHENTICATION_BROKEN| and non-error
+  // |cert_status| for WKWebView because |security_style| and |cert_status|
+  // are
+  // calculated using different API, which may lead to different cert
+  // verification results.
+  if (net::IsCertStatusError(status.cert_status) ||
+      status.security_style == web::SECURITY_STYLE_AUTHENTICATION_BROKEN) {
+    // HTTPS with major errors
+    dataHolder.image = [UIImage imageNamed:@"page_info_bad"];
+
+    NSString* certificateDetails = BuildCertificateDetailString(status, URL);
+
+    dataHolder.message = BuildMessage(@[
+      l10n_util::GetNSString(IDS_PAGE_INFO_NOT_SECURE_SUMMARY),
+      l10n_util::GetNSString(IDS_PAGE_INFO_NOT_SECURE_DETAILS),
+      certificateDetails
+    ]);
+
+    return dataHolder;
+  }
+
+  // The remaining states are valid HTTPS, or HTTPS with minor errors.
+
+  base::string16 issuerName(
+      base::UTF8ToUTF16(status.certificate->issuer().GetDisplayName()));
+  // Have certificateDetails be an empty string to help building the message.
+  NSString* certificateDetails = @"";
+  if (!issuerName.empty()) {
+    // Show the issuer name if it's available.
+    // TODO(crbug.com/502470): Implement a certificate viewer instead.
+    certificateDetails = l10n_util::GetNSStringF(
+        IDS_IOS_PAGE_INFO_SECURITY_TAB_SECURE_IDENTITY, issuerName);
+  }
+
+  if (status.content_status == web::SSLStatus::DISPLAYED_INSECURE_CONTENT) {
+    // HTTPS with mixed content. This maps to the WARNING security level in M80,
+    // so assume the WARNING state when determining whether to swap the icon for
+    // a grey triangle. This will result in an inconsistency between the omnibox
+    // and page info if the mixed content WARNING feature is disabled.
+    if (security_state::ShouldShowDangerTriangleForWarningLevel()) {
+      dataHolder.image = [UIImage imageNamed:@"page_info_bad"];
+    } else {
+      dataHolder.image = [UIImage imageNamed:@"page_info_info"];
+    }
+    dataHolder.message = BuildMessage(@[
+      l10n_util::GetNSString(IDS_PAGE_INFO_MIXED_CONTENT_SUMMARY),
+      l10n_util::GetNSString(IDS_PAGE_INFO_MIXED_CONTENT_DETAILS),
+      certificateDetails
+    ]);
+
+    return dataHolder;
+  }
+
+  // Valid HTTPS
+  dataHolder.image = [UIImage imageNamed:@"page_info_good"];
+  dataHolder.message = BuildMessage(@[
+    l10n_util::GetNSString(IDS_PAGE_INFO_SECURE_SUMMARY),
+    l10n_util::GetNSString(IDS_PAGE_INFO_SECURE_DETAILS), certificateDetails
+  ]);
+
+  DCHECK(!(status.cert_status & net::CERT_STATUS_IS_EV))
+      << "Extended Validation should be disabled";
+
+  return dataHolder;
+}
+
+@end
diff --git a/ios/chrome/browser/ui/page_info/page_info_model.cc b/ios/chrome/browser/ui/page_info/page_info_model.cc
deleted file mode 100644
index 9505470..0000000
--- a/ios/chrome/browser/ui/page_info/page_info_model.cc
+++ /dev/null
@@ -1,183 +0,0 @@
-// Copyright (c) 2012 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 "ios/chrome/browser/ui/page_info/page_info_model.h"
-
-#include <stdint.h>
-
-#include <string>
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/command_line.h"
-#include "base/i18n/time_formatting.h"
-#include "base/strings/string16.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/utf_string_conversions.h"
-#include "components/keyed_service/core/service_access_type.h"
-#include "components/security_state/core/security_state.h"
-#include "components/ssl_errors/error_info.h"
-#include "components/strings/grit/components_chromium_strings.h"
-#include "components/strings/grit/components_google_chrome_strings.h"
-#include "components/strings/grit/components_strings.h"
-#include "ios/chrome/browser/chrome_url_constants.h"
-#include "ios/chrome/grit/ios_chromium_strings.h"
-#include "ios/chrome/grit/ios_strings.h"
-#include "ios/chrome/grit/ios_theme_resources.h"
-#include "ios/web/public/security/ssl_status.h"
-#include "net/cert/cert_status_flags.h"
-#include "net/cert/x509_certificate.h"
-#include "net/ssl/ssl_cipher_suite_names.h"
-#include "net/ssl/ssl_connection_status_flags.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/base/resource/resource_bundle.h"
-
-// TODO(crbug.com/227827) Merge 178763: PageInfoModel has been removed in
-// upstream; check if we should use PageInfoModel.
-PageInfoModel::PageInfoModel(const GURL& url,
-                             const web::SSLStatus& ssl,
-                             bool is_offline_page) {
-  if (is_offline_page) {
-    sections_.push_back(
-        SectionInfo(ICON_STATE_OFFLINE_PAGE,
-                    l10n_util::GetStringUTF16(IDS_IOS_PAGE_INFO_OFFLINE_TITLE),
-                    l10n_util::GetStringUTF16(IDS_IOS_PAGE_INFO_OFFLINE_PAGE),
-                    BUTTON_RELOAD));
-    return;
-  }
-
-  if (url.SchemeIs(kChromeUIScheme)) {
-    base::string16 spec(base::UTF8ToUTF16(url.spec()));
-
-    sections_.push_back(SectionInfo(
-        ICON_STATE_INTERNAL_PAGE, spec,
-        l10n_util::GetStringUTF16(IDS_PAGE_INFO_INTERNAL_PAGE), BUTTON_NONE));
-    return;
-  }
-
-  base::string16 hostname(base::UTF8ToUTF16(url.host()));
-
-  base::string16 summary;
-  base::string16 details;
-  base::string16 certificate_details;
-
-  // Summary and details.
-  SectionStateIcon icon_id = ICON_NONE;
-  if (!ssl.certificate) {
-    // Not HTTPS. This maps to the WARNING security level. Show the grey
-    // triangle icon in page info based on the same logic used to determine
-    // the iconography in the omnibox.
-    if (security_state::ShouldShowDangerTriangleForWarningLevel()) {
-      icon_id = ICON_STATE_ERROR;
-    } else {
-      icon_id = ICON_STATE_INFO;
-    }
-    summary.assign(l10n_util::GetStringUTF16(IDS_PAGE_INFO_NOT_SECURE_SUMMARY));
-    details.assign(l10n_util::GetStringUTF16(IDS_PAGE_INFO_NOT_SECURE_DETAILS));
-  } else {
-    // It is possible to have |SECURITY_STYLE_AUTHENTICATION_BROKEN| and
-    // non-error
-    // |cert_status| for WKWebView because |security_style| and |cert_status|
-    // are
-    // calculated using different API, which may lead to different cert
-    // verification results.
-    if (net::IsCertStatusError(ssl.cert_status) ||
-        ssl.security_style == web::SECURITY_STYLE_AUTHENTICATION_BROKEN) {
-      // HTTPS with major errors
-      icon_id = ICON_STATE_ERROR;
-      summary.assign(
-          l10n_util::GetStringUTF16(IDS_PAGE_INFO_NOT_SECURE_SUMMARY));
-      details.assign(
-          l10n_util::GetStringUTF16(IDS_PAGE_INFO_NOT_SECURE_DETAILS));
-
-      certificate_details.assign(l10n_util::GetStringUTF16(
-          IDS_PAGE_INFO_SECURITY_TAB_INSECURE_IDENTITY));
-      const base::string16 bullet = base::UTF8ToUTF16("\n • ");
-      std::vector<ssl_errors::ErrorInfo> errors;
-      ssl_errors::ErrorInfo::GetErrorsForCertStatus(
-          ssl.certificate, ssl.cert_status, url, &errors);
-      for (size_t i = 0; i < errors.size(); ++i) {
-        certificate_details += bullet;
-        certificate_details += errors[i].short_description();
-      }
-    } else {
-      // Valid HTTPS or HTTPS with minor errors.
-      base::string16 issuer_name(
-          base::UTF8ToUTF16(ssl.certificate->issuer().GetDisplayName()));
-      if (!issuer_name.empty()) {
-        // Show the issuer name if it's available.
-        // TODO(crbug.com/502470): Implement a certificate viewer instead.
-        certificate_details.assign(l10n_util::GetStringFUTF16(
-            IDS_IOS_PAGE_INFO_SECURITY_TAB_SECURE_IDENTITY, issuer_name));
-      }
-      if (ssl.content_status == web::SSLStatus::DISPLAYED_INSECURE_CONTENT) {
-        // HTTPS with mixed content. This maps to the WARNING security level
-        // in M80, so assume the WARNING state when determining whether to
-        // swap the icon for a grey triangle. This will result in an
-        // inconsistency between the omnibox and page info if the mixed
-        // content WARNING feature is disabled.
-        if (security_state::ShouldShowDangerTriangleForWarningLevel()) {
-          icon_id = ICON_STATE_ERROR;
-        } else {
-          icon_id = ICON_STATE_INFO;
-        }
-        summary.assign(
-            l10n_util::GetStringUTF16(IDS_PAGE_INFO_MIXED_CONTENT_SUMMARY));
-        details.assign(
-            l10n_util::GetStringUTF16(IDS_PAGE_INFO_MIXED_CONTENT_DETAILS));
-      } else {
-        // Valid HTTPS
-        icon_id = ICON_STATE_OK;
-        summary.assign(l10n_util::GetStringUTF16(IDS_PAGE_INFO_SECURE_SUMMARY));
-        details.assign(l10n_util::GetStringUTF16(IDS_PAGE_INFO_SECURE_DETAILS));
-
-        DCHECK(!(ssl.cert_status & net::CERT_STATUS_IS_EV))
-            << "Extended Validation should be disabled";
-      }
-    }
-  }
-
-  base::string16 description;
-  base::string16 spacer = base::UTF8ToUTF16("\n\n");
-
-  description.assign(summary);
-  description += spacer;
-  description += details;
-
-  if (!certificate_details.empty()) {
-    description += spacer;
-    description += certificate_details;
-  }
-
-  sections_.push_back(SectionInfo(icon_id, hostname, description,
-                                  BUTTON_SHOW_SECURITY_HELP));
-}
-
-PageInfoModel::~PageInfoModel() {}
-
-int PageInfoModel::GetSectionCount() {
-  return sections_.size();
-}
-
-PageInfoModel::SectionInfo PageInfoModel::GetSectionInfo(int index) {
-  DCHECK(index < static_cast<int>(sections_.size()));
-  return sections_[index];
-}
-
-gfx::Image* PageInfoModel::GetIconImage(SectionStateIcon icon_id) {
-  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
-  switch (icon_id) {
-    case ICON_NONE:
-    case ICON_STATE_INTERNAL_PAGE:
-      return nullptr;
-    case ICON_STATE_OK:
-      return &rb.GetNativeImageNamed(IDR_IOS_PAGEINFO_GOOD);
-    case ICON_STATE_ERROR:
-      return &rb.GetNativeImageNamed(IDR_IOS_PAGEINFO_BAD);
-    case ICON_STATE_INFO:
-      return &rb.GetNativeImageNamed(IDR_IOS_PAGEINFO_INFO);
-    case ICON_STATE_OFFLINE_PAGE:
-      return &rb.GetNativeImageNamed(IDR_IOS_OMNIBOX_OFFLINE);
-  }
-}
diff --git a/ios/chrome/browser/ui/page_info/page_info_model.h b/ios/chrome/browser/ui/page_info/page_info_model.h
deleted file mode 100644
index 89f07fd2..0000000
--- a/ios/chrome/browser/ui/page_info/page_info_model.h
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_PAGE_INFO_PAGE_INFO_MODEL_H_
-#define IOS_CHROME_BROWSER_UI_PAGE_INFO_PAGE_INFO_MODEL_H_
-
-#include <vector>
-
-#include "base/strings/string16.h"
-#include "ui/gfx/image/image.h"
-#include "url/gurl.h"
-
-namespace web {
-struct SSLStatus;
-}
-
-// TODO(crbug.com/227827) Merge 178763: PageInfoModel has been removed in
-// upstream; check if we should use PageInfoModel.
-// The model that provides the information that should be displayed in the page
-// info dialog/bubble.
-class PageInfoModel {
- public:
-  // NOTE: ICON_STATE_OK ... ICON_STATE_ERROR must be listed in increasing
-  // order of severity.  Code may depend on this order.
-  enum SectionStateIcon {
-    // No icon.
-    ICON_NONE = -1,
-    // State is OK.
-    ICON_STATE_OK,
-    // For example, unverified identity over HTTPS.
-    ICON_STATE_ERROR,
-    // An information icon.
-    ICON_STATE_INFO,
-    // Icon for offline pages.
-    ICON_STATE_OFFLINE_PAGE,
-    // Icon for internal pages.
-    ICON_STATE_INTERNAL_PAGE,
-  };
-
-  // The button action that can be displayed in the Page Info. Only the button
-  // of the first section that require one will be displayed.
-  enum ButtonAction {
-    // No button.
-    BUTTON_NONE = -1,
-    // Add a button to open help center on security related page.
-    BUTTON_SHOW_SECURITY_HELP,
-    // Add a button to reload the page.
-    BUTTON_RELOAD,
-  };
-
-  struct SectionInfo {
-    SectionInfo(SectionStateIcon icon_id,
-                const base::string16& headline,
-                const base::string16& description,
-                ButtonAction button)
-        : icon_id(icon_id),
-          headline(headline),
-          description(description),
-          button(button) {}
-
-    // The overall state of the connection (error, warning, ok).
-    SectionStateIcon icon_id;
-
-    // A single line describing the section, optional.
-    base::string16 headline;
-
-    // The full description of what this section is.
-    base::string16 description;
-
-    // The button at the bottom of the sheet that allows the user to do an extra
-    // action on top of dismissing the sheet.
-    ButtonAction button;
-  };
-  // |is_offline_page| is true if related WebState presents Offline Version of
-  // the page.
-  PageInfoModel(const GURL& url,
-                const web::SSLStatus& ssl,
-                bool is_offline_page);
-  ~PageInfoModel();
-
-  int GetSectionCount();
-  SectionInfo GetSectionInfo(int index);
-
-  // Returns the native image type for an icon with the given id.
-  gfx::Image* GetIconImage(SectionStateIcon icon_id);
-
- protected:
-  std::vector<SectionInfo> sections_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(PageInfoModel);
-};
-
-#endif  // IOS_CHROME_BROWSER_UI_PAGE_INFO_PAGE_INFO_MODEL_H_
diff --git a/ios/chrome/browser/ui/page_info/resources/BUILD.gn b/ios/chrome/browser/ui/page_info/resources/BUILD.gn
new file mode 100644
index 0000000..47541af
--- /dev/null
+++ b/ios/chrome/browser/ui/page_info/resources/BUILD.gn
@@ -0,0 +1,37 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/ios/asset_catalog.gni")
+
+imageset("page_info_bad") {
+  sources = [
+    "page_info_bad.imageset/Contents.json",
+    "page_info_bad.imageset/page_info_bad@2x.png",
+    "page_info_bad.imageset/page_info_bad@3x.png",
+  ]
+}
+
+imageset("page_info_good") {
+  sources = [
+    "page_info_good.imageset/Contents.json",
+    "page_info_good.imageset/page_info_good@2x.png",
+    "page_info_good.imageset/page_info_good@3x.png",
+  ]
+}
+
+imageset("page_info_offline") {
+  sources = [
+    "page_info_offline.imageset/Contents.json",
+    "page_info_offline.imageset/page_info_offline@2x.png",
+    "page_info_offline.imageset/page_info_offline@3x.png",
+  ]
+}
+
+imageset("page_info_info") {
+  sources = [
+    "page_info_info.imageset/Contents.json",
+    "page_info_info.imageset/page_info_info@2x.png",
+    "page_info_info.imageset/page_info_info@3x.png",
+  ]
+}
diff --git a/ios/chrome/browser/ui/page_info/resources/page_info_bad.imageset/Contents.json b/ios/chrome/browser/ui/page_info/resources/page_info_bad.imageset/Contents.json
new file mode 100644
index 0000000..4f1d45a
--- /dev/null
+++ b/ios/chrome/browser/ui/page_info/resources/page_info_bad.imageset/Contents.json
@@ -0,0 +1,18 @@
+{
+    "images": [
+        {
+            "idiom": "universal",
+            "scale": "2x",
+            "filename": "page_info_bad@2x.png"
+        },
+        {
+            "idiom": "universal",
+            "scale": "3x",
+            "filename": "page_info_bad@3x.png"
+        }
+    ],
+    "info": {
+        "version": 1,
+        "author": "xcode"
+    }
+}
diff --git a/ios/chrome/app/theme/default_200_percent/omnibox/pageinfo_bad.png b/ios/chrome/browser/ui/page_info/resources/page_info_bad.imageset/page_info_bad@2x.png
similarity index 100%
rename from ios/chrome/app/theme/default_200_percent/omnibox/pageinfo_bad.png
rename to ios/chrome/browser/ui/page_info/resources/page_info_bad.imageset/page_info_bad@2x.png
Binary files differ
diff --git a/ios/chrome/app/theme/default_300_percent/omnibox/pageinfo_bad.png b/ios/chrome/browser/ui/page_info/resources/page_info_bad.imageset/page_info_bad@3x.png
similarity index 100%
rename from ios/chrome/app/theme/default_300_percent/omnibox/pageinfo_bad.png
rename to ios/chrome/browser/ui/page_info/resources/page_info_bad.imageset/page_info_bad@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/page_info/resources/page_info_good.imageset/Contents.json b/ios/chrome/browser/ui/page_info/resources/page_info_good.imageset/Contents.json
new file mode 100644
index 0000000..5654227
--- /dev/null
+++ b/ios/chrome/browser/ui/page_info/resources/page_info_good.imageset/Contents.json
@@ -0,0 +1,18 @@
+{
+    "images": [
+        {
+            "idiom": "universal",
+            "scale": "2x",
+            "filename": "page_info_good@2x.png"
+        },
+        {
+            "idiom": "universal",
+            "scale": "3x",
+            "filename": "page_info_good@3x.png"
+        }
+    ],
+    "info": {
+        "version": 1,
+        "author": "xcode"
+    }
+}
diff --git a/ios/chrome/app/theme/default_200_percent/omnibox/pageinfo_good.png b/ios/chrome/browser/ui/page_info/resources/page_info_good.imageset/page_info_good@2x.png
similarity index 100%
rename from ios/chrome/app/theme/default_200_percent/omnibox/pageinfo_good.png
rename to ios/chrome/browser/ui/page_info/resources/page_info_good.imageset/page_info_good@2x.png
Binary files differ
diff --git a/ios/chrome/app/theme/default_300_percent/omnibox/pageinfo_good.png b/ios/chrome/browser/ui/page_info/resources/page_info_good.imageset/page_info_good@3x.png
similarity index 100%
rename from ios/chrome/app/theme/default_300_percent/omnibox/pageinfo_good.png
rename to ios/chrome/browser/ui/page_info/resources/page_info_good.imageset/page_info_good@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/page_info/resources/page_info_info.imageset/Contents.json b/ios/chrome/browser/ui/page_info/resources/page_info_info.imageset/Contents.json
new file mode 100644
index 0000000..20e00c27
--- /dev/null
+++ b/ios/chrome/browser/ui/page_info/resources/page_info_info.imageset/Contents.json
@@ -0,0 +1,18 @@
+{
+    "images": [
+        {
+            "idiom": "universal",
+            "scale": "2x",
+            "filename": "page_info_info@2x.png"
+        },
+        {
+            "idiom": "universal",
+            "scale": "3x",
+            "filename": "page_info_info@3x.png"
+        }
+    ],
+    "info": {
+        "version": 1,
+        "author": "xcode"
+    }
+}
diff --git a/ios/chrome/app/theme/default_200_percent/omnibox/pageinfo_info.png b/ios/chrome/browser/ui/page_info/resources/page_info_info.imageset/page_info_info@2x.png
similarity index 100%
rename from ios/chrome/app/theme/default_200_percent/omnibox/pageinfo_info.png
rename to ios/chrome/browser/ui/page_info/resources/page_info_info.imageset/page_info_info@2x.png
Binary files differ
diff --git a/ios/chrome/app/theme/default_300_percent/omnibox/pageinfo_info.png b/ios/chrome/browser/ui/page_info/resources/page_info_info.imageset/page_info_info@3x.png
similarity index 100%
rename from ios/chrome/app/theme/default_300_percent/omnibox/pageinfo_info.png
rename to ios/chrome/browser/ui/page_info/resources/page_info_info.imageset/page_info_info@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/page_info/resources/page_info_offline.imageset/Contents.json b/ios/chrome/browser/ui/page_info/resources/page_info_offline.imageset/Contents.json
new file mode 100644
index 0000000..71653b95
--- /dev/null
+++ b/ios/chrome/browser/ui/page_info/resources/page_info_offline.imageset/Contents.json
@@ -0,0 +1,18 @@
+{
+    "images": [
+        {
+            "idiom": "universal",
+            "scale": "2x",
+            "filename": "page_info_offline@2x.png"
+        },
+        {
+            "idiom": "universal",
+            "scale": "3x",
+            "filename": "page_info_offline@3x.png"
+        }
+    ],
+    "info": {
+        "version": 1,
+        "author": "xcode"
+    }
+}
diff --git a/ios/chrome/app/theme/default_200_percent/omnibox_offline.png b/ios/chrome/browser/ui/page_info/resources/page_info_offline.imageset/page_info_offline@2x.png
similarity index 100%
rename from ios/chrome/app/theme/default_200_percent/omnibox_offline.png
rename to ios/chrome/browser/ui/page_info/resources/page_info_offline.imageset/page_info_offline@2x.png
Binary files differ
diff --git a/ios/chrome/app/theme/default_300_percent/omnibox_offline.png b/ios/chrome/browser/ui/page_info/resources/page_info_offline.imageset/page_info_offline@3x.png
similarity index 100%
rename from ios/chrome/app/theme/default_300_percent/omnibox_offline.png
rename to ios/chrome/browser/ui/page_info/resources/page_info_offline.imageset/page_info_offline@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_manager.mm b/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_manager.mm
index 2a13845..83dff0b8 100644
--- a/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_manager.mm
+++ b/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_manager.mm
@@ -178,18 +178,16 @@
     _timeRangePref.Init(browsing_data::prefs::kDeleteTimePeriod,
                         _browserState->GetPrefs());
 
-    if (IsNewClearBrowsingDataUIEnabled()) {
-      _observer = std::make_unique<BrowsingDataRemoverObserverBridge>(self);
-      _scoped_observer = std::make_unique<
-          ScopedObserver<BrowsingDataRemover, BrowsingDataRemoverObserver>>(
-          _observer.get());
-      _scoped_observer->Add(remover);
+    _observer = std::make_unique<BrowsingDataRemoverObserverBridge>(self);
+    _scoped_observer = std::make_unique<
+        ScopedObserver<BrowsingDataRemover, BrowsingDataRemoverObserver>>(
+        _observer.get());
+    _scoped_observer->Add(remover);
 
-      _prefChangeRegistrar.Init(_browserState->GetPrefs());
-      _prefObserverBridge.reset(new PrefObserverBridge(self));
-      _prefObserverBridge->ObserveChangesForPreference(
-          browsing_data::prefs::kDeleteTimePeriod, &_prefChangeRegistrar);
-    }
+    _prefChangeRegistrar.Init(_browserState->GetPrefs());
+    _prefObserverBridge.reset(new PrefObserverBridge(self));
+    _prefObserverBridge->ObserveChangesForPreference(
+        browsing_data::prefs::kDeleteTimePeriod, &_prefChangeRegistrar);
   }
   return self;
 }
@@ -197,22 +195,18 @@
 #pragma mark - Public Methods
 
 - (void)loadModel:(ListModel*)model {
-  // Time range section.
-  // Only implementing new UI for kListTypeCollectionView.
-  if (IsNewClearBrowsingDataUIEnabled()) {
-    [model addSectionWithIdentifier:SectionIdentifierTimeRange];
-    ListItem* timeRangeItem = [self timeRangeItem];
-    [model addItem:timeRangeItem
-        toSectionWithIdentifier:SectionIdentifierTimeRange];
-    if (self.listType == ClearBrowsingDataListType::kListTypeCollectionView) {
-      self.collectionViewTimeRangeItem =
-          base::mac::ObjCCastStrict<LegacySettingsDetailItem>(timeRangeItem);
-    } else {
-      DCHECK(self.listType == ClearBrowsingDataListType::kListTypeTableView);
-      self.tableViewTimeRangeItem =
-          base::mac::ObjCCastStrict<TableViewDetailIconItem>(timeRangeItem);
-      self.tableViewTimeRangeItem.useCustomSeparator = YES;
-    }
+  [model addSectionWithIdentifier:SectionIdentifierTimeRange];
+  ListItem* timeRangeItem = [self timeRangeItem];
+  [model addItem:timeRangeItem
+      toSectionWithIdentifier:SectionIdentifierTimeRange];
+  if (self.listType == ClearBrowsingDataListType::kListTypeCollectionView) {
+    self.collectionViewTimeRangeItem =
+        base::mac::ObjCCastStrict<LegacySettingsDetailItem>(timeRangeItem);
+  } else {
+    DCHECK(self.listType == ClearBrowsingDataListType::kListTypeTableView);
+    self.tableViewTimeRangeItem =
+        base::mac::ObjCCastStrict<TableViewDetailIconItem>(timeRangeItem);
+    self.tableViewTimeRangeItem.useCustomSeparator = YES;
   }
 
   [self addClearBrowsingDataItemsToModel:model];
@@ -297,9 +291,8 @@
                              (~NSByteCountFormatterUseKB);
     formatter.countStyle = NSByteCountFormatterCountStyleMemory;
     NSString* formattedSize = [formatter stringFromByteCount:cacheSizeBytes];
-    return (!IsNewClearBrowsingDataUIEnabled() ||
-            _timeRangePref.GetValue() ==
-                static_cast<int>(browsing_data::TimePeriod::ALL_TIME))
+    return _timeRangePref.GetValue() ==
+                   static_cast<int>(browsing_data::TimePeriod::ALL_TIME)
                ? formattedSize
                : l10n_util::GetNSStringF(
                      IDS_DEL_CACHE_COUNTER_UPPER_ESTIMATE,
@@ -338,8 +331,7 @@
 }
 
 - (void)addClearDataButtonToModel:(ListModel*)model {
-  if (self.listType == ClearBrowsingDataListType::kListTypeTableView &&
-      IsNewClearBrowsingDataUIEnabled()) {
+  if (self.listType == ClearBrowsingDataListType::kListTypeTableView) {
     return;
   }
   // Clear Browsing Data button.
@@ -468,32 +460,29 @@
     collectionClearDataItem.prefName = prefName;
     collectionClearDataItem.accessibilityIdentifier =
         [self accessibilityIdentifierFromItemType:itemType];
-    if (IsNewClearBrowsingDataUIEnabled()) {
-      if (itemType == ItemTypeDataTypeCookiesSiteData) {
-        // Because there is no counter for cookies, an explanatory text is
-        // displayed.
-        collectionClearDataItem.detailText =
-            l10n_util::GetNSString(IDS_DEL_COOKIES_COUNTER);
-      } else {
-        __weak ClearBrowsingDataManager* weakSelf = self;
-        __weak ClearBrowsingDataItem* weakCollectionClearDataItem =
-            collectionClearDataItem;
-        BrowsingDataCounterWrapper::UpdateUICallback callback =
-            base::BindRepeating(
-                ^(const browsing_data::BrowsingDataCounter::Result& result) {
-                  weakCollectionClearDataItem.detailText =
-                      [weakSelf counterTextFromResult:result];
-                  [weakSelf.consumer
-                      updateCellsForItem:weakCollectionClearDataItem];
-                });
-        std::unique_ptr<BrowsingDataCounterWrapper> counter =
-            [self.counterWrapperProducer
-                createCounterWrapperWithPrefName:prefName
-                                    browserState:self.browserState
-                                     prefService:prefs
-                                updateUiCallback:callback];
-        _countersByMasks.emplace(mask, std::move(counter));
-      }
+    if (itemType == ItemTypeDataTypeCookiesSiteData) {
+      // Because there is no counter for cookies, an explanatory text is
+      // displayed.
+      collectionClearDataItem.detailText =
+          l10n_util::GetNSString(IDS_DEL_COOKIES_COUNTER);
+    } else {
+      __weak ClearBrowsingDataManager* weakSelf = self;
+      __weak ClearBrowsingDataItem* weakCollectionClearDataItem =
+          collectionClearDataItem;
+      BrowsingDataCounterWrapper::UpdateUICallback callback =
+          base::BindRepeating(^(
+              const browsing_data::BrowsingDataCounter::Result& result) {
+            weakCollectionClearDataItem.detailText =
+                [weakSelf counterTextFromResult:result];
+            [weakSelf.consumer updateCellsForItem:weakCollectionClearDataItem];
+          });
+      std::unique_ptr<BrowsingDataCounterWrapper> counter =
+          [self.counterWrapperProducer
+              createCounterWrapperWithPrefName:prefName
+                                  browserState:self.browserState
+                                   prefService:prefs
+                              updateUiCallback:callback];
+      _countersByMasks.emplace(mask, std::move(counter));
     }
     clearDataItem = collectionClearDataItem;
   } else {
@@ -505,40 +494,38 @@
         [self accessibilityIdentifierFromItemType:itemType];
     tableViewClearDataItem.dataTypeMask = mask;
     tableViewClearDataItem.prefName = prefName;
-    if (IsNewClearBrowsingDataUIEnabled()) {
-      tableViewClearDataItem.useCustomSeparator = YES;
-      tableViewClearDataItem.checkedBackgroundColor =
-          [[UIColor colorNamed:kBlueColor]
-              colorWithAlphaComponent:kSelectedBackgroundColorAlpha];
-      tableViewClearDataItem.imageName = [_imageNamesByItemTypes
-          objectForKey:[NSNumber numberWithInteger:itemType]];
-      if (itemType == ItemTypeDataTypeCookiesSiteData) {
-        // Because there is no counter for cookies, an explanatory text is
-        // displayed.
-        tableViewClearDataItem.detailText =
-            l10n_util::GetNSString(IDS_DEL_COOKIES_COUNTER);
-      } else {
-        // Having a placeholder |detailText| helps reduce the observable
-        // row-height changes induced by the counter callbacks.
-        tableViewClearDataItem.detailText = @"\u00A0";
-        __weak ClearBrowsingDataManager* weakSelf = self;
-        __weak TableViewClearBrowsingDataItem* weakTableClearDataItem =
-            tableViewClearDataItem;
-        BrowsingDataCounterWrapper::UpdateUICallback callback =
-            base::BindRepeating(
-                ^(const browsing_data::BrowsingDataCounter::Result& result) {
-                  weakTableClearDataItem.detailText =
-                      [weakSelf counterTextFromResult:result];
-                  [weakSelf.consumer updateCellsForItem:weakTableClearDataItem];
-                });
-        std::unique_ptr<BrowsingDataCounterWrapper> counter =
-            [self.counterWrapperProducer
-                createCounterWrapperWithPrefName:prefName
-                                    browserState:self.browserState
-                                     prefService:prefs
-                                updateUiCallback:callback];
-        _countersByMasks.emplace(mask, std::move(counter));
-      }
+    tableViewClearDataItem.useCustomSeparator = YES;
+    tableViewClearDataItem.checkedBackgroundColor =
+        [[UIColor colorNamed:kBlueColor]
+            colorWithAlphaComponent:kSelectedBackgroundColorAlpha];
+    tableViewClearDataItem.imageName = [_imageNamesByItemTypes
+        objectForKey:[NSNumber numberWithInteger:itemType]];
+    if (itemType == ItemTypeDataTypeCookiesSiteData) {
+      // Because there is no counter for cookies, an explanatory text is
+      // displayed.
+      tableViewClearDataItem.detailText =
+          l10n_util::GetNSString(IDS_DEL_COOKIES_COUNTER);
+    } else {
+      // Having a placeholder |detailText| helps reduce the observable
+      // row-height changes induced by the counter callbacks.
+      tableViewClearDataItem.detailText = @"\u00A0";
+      __weak ClearBrowsingDataManager* weakSelf = self;
+      __weak TableViewClearBrowsingDataItem* weakTableClearDataItem =
+          tableViewClearDataItem;
+      BrowsingDataCounterWrapper::UpdateUICallback callback =
+          base::BindRepeating(
+              ^(const browsing_data::BrowsingDataCounter::Result& result) {
+                weakTableClearDataItem.detailText =
+                    [weakSelf counterTextFromResult:result];
+                [weakSelf.consumer updateCellsForItem:weakTableClearDataItem];
+              });
+      std::unique_ptr<BrowsingDataCounterWrapper> counter =
+          [self.counterWrapperProducer
+              createCounterWrapperWithPrefName:prefName
+                                  browserState:self.browserState
+                                   prefService:prefs
+                              updateUiCallback:callback];
+      _countersByMasks.emplace(mask, std::move(counter));
     }
     clearDataItem = tableViewClearDataItem;
   }
@@ -698,9 +685,7 @@
   DCHECK(mask != BrowsingDataRemoveMask::REMOVE_NOTHING);
 
   browsing_data::TimePeriod timePeriod =
-      IsNewClearBrowsingDataUIEnabled()
-          ? static_cast<browsing_data::TimePeriod>(_timeRangePref.GetValue())
-          : browsing_data::TimePeriod::ALL_TIME;
+      static_cast<browsing_data::TimePeriod>(_timeRangePref.GetValue());
   [self.consumer removeBrowsingDataForBrowserState:_browserState
                                         timePeriod:timePeriod
                                         removeMask:mask
diff --git a/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_manager_unittest.mm b/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_manager_unittest.mm
index 6775ba7..2ddf6c3c 100644
--- a/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_manager_unittest.mm
+++ b/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_manager_unittest.mm
@@ -117,15 +117,8 @@
   [manager_ loadModel:model_];
 
   EXPECT_EQ(3, [model_ numberOfSections]);
-  if (IsNewClearBrowsingDataUIEnabled()) {
-    // Time Range selector.
-    EXPECT_EQ(1, [model_ numberOfItemsInSection:0]);
-    EXPECT_EQ(5, [model_ numberOfItemsInSection:1]);
-  } else {
-    EXPECT_EQ(5, [model_ numberOfItemsInSection:0]);
-    // CBD button.
-    EXPECT_EQ(1, [model_ numberOfItemsInSection:1]);
-  }
+  EXPECT_EQ(1, [model_ numberOfItemsInSection:0]);
+  EXPECT_EQ(5, [model_ numberOfItemsInSection:1]);
   EXPECT_EQ(1, [model_ numberOfItemsInSection:2]);
 }
 
@@ -142,15 +135,8 @@
   [manager_ loadModel:model_];
 
   EXPECT_EQ(4, [model_ numberOfSections]);
-  if (IsNewClearBrowsingDataUIEnabled()) {
-    // Time Range selector.
-    EXPECT_EQ(1, [model_ numberOfItemsInSection:0]);
-    EXPECT_EQ(5, [model_ numberOfItemsInSection:1]);
-  } else {
-    EXPECT_EQ(5, [model_ numberOfItemsInSection:0]);
-    // CBD button.
-    EXPECT_EQ(1, [model_ numberOfItemsInSection:1]);
-  }
+  EXPECT_EQ(1, [model_ numberOfItemsInSection:0]);
+  EXPECT_EQ(5, [model_ numberOfItemsInSection:1]);
   EXPECT_EQ(1, [model_ numberOfItemsInSection:2]);
   EXPECT_EQ(1, [model_ numberOfItemsInSection:3]);
 }
@@ -189,11 +175,6 @@
        TestCacheCounterFormattingForLessThanAllTime) {
   ASSERT_EQ("en", GetApplicationContext()->GetApplicationLocale());
 
-  // If the new UI is not enabled then the pref value for the time period
-  // is ignored and the time period defaults to ALL_TIME.
-  if (!IsNewClearBrowsingDataUIEnabled()) {
-    return;
-  }
   PrefService* prefs = browser_state_->GetPrefs();
   prefs->SetInteger(browsing_data::prefs::kDeleteTimePeriod,
                     static_cast<int>(browsing_data::TimePeriod::LAST_HOUR));
@@ -223,10 +204,6 @@
 }
 
 TEST_F(ClearBrowsingDataManagerTest, TestOnPreferenceChanged) {
-  // Only works with new UI
-  if (!IsNewClearBrowsingDataUIEnabled()) {
-    return;
-  }
   [manager_ loadModel:model_];
   NSArray* timeRangeItems =
       [model_ itemsInSectionWithIdentifier:SectionIdentifierTimeRange];
diff --git a/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_table_view_controller.mm b/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_table_view_controller.mm
index ad46868..92d991e 100644
--- a/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_table_view_controller.mm
@@ -130,9 +130,7 @@
   ]
                animated:YES];
 
-  if (IsNewClearBrowsingDataUIEnabled()) {
-    self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
-  }
+  self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
   self.styler.cellBackgroundColor = UIColor.cr_systemBackgroundColor;
   self.styler.tableViewBackgroundColor = UIColor.cr_systemBackgroundColor;
   self.tableView.accessibilityIdentifier =
@@ -167,12 +165,10 @@
   [super viewWillAppear:animated];
   [self.dataManager restartCounters:BrowsingDataRemoveMask::REMOVE_ALL];
 
-  if (IsNewClearBrowsingDataUIEnabled()) {
-    [self updateToolbarButtons];
-    // Showing toolbar here because parent class hides toolbar in
-    // viewWillDisappear:.
-    self.navigationController.toolbarHidden = NO;
-  }
+  [self updateToolbarButtons];
+  // Showing toolbar here because parent class hides toolbar in
+  // viewWillDisappear:.
+  self.navigationController.toolbarHidden = NO;
 }
 
 - (void)viewWillDisappear:(BOOL)animated {
@@ -249,9 +245,7 @@
     case ItemTypeDataTypeAutofill:
       // For these cells the selection style application is specified in the
       // corresponding item definition.
-      if (IsNewClearBrowsingDataUIEnabled()) {
-        cellToReturn.selectionStyle = UITableViewCellSelectionStyleNone;
-      }
+      cellToReturn.selectionStyle = UITableViewCellSelectionStyleNone;
       break;
     default:
       break;
@@ -277,38 +271,34 @@
 
 - (void)tableView:(UITableView*)tableView
     didSelectRowAtIndexPath:(NSIndexPath*)indexPath {
-  if (!IsNewClearBrowsingDataUIEnabled()) {
-    [self tableView:tableView legacyDidSelectRowAtIndexPath:indexPath];
-  } else {
-    TableViewItem* item = [self.tableViewModel itemAtIndexPath:indexPath];
-    DCHECK(item);
-    switch (item.type) {
-      case ItemTypeTimeRange: {
-        UIViewController* controller =
-            [[TimeRangeSelectorTableViewController alloc]
-                initWithPrefs:self.browserState->GetPrefs()];
-        [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
-        [self.navigationController pushViewController:controller animated:YES];
-        break;
-      }
-      case ItemTypeDataTypeBrowsingHistory:
-      case ItemTypeDataTypeCookiesSiteData:
-      case ItemTypeDataTypeCache:
-      case ItemTypeDataTypeSavedPasswords:
-      case ItemTypeDataTypeAutofill: {
-        DCHECK([item isKindOfClass:[TableViewClearBrowsingDataItem class]]);
-        TableViewClearBrowsingDataItem* clearBrowsingDataItem =
-            base::mac::ObjCCastStrict<TableViewClearBrowsingDataItem>(item);
-        clearBrowsingDataItem.checked = !clearBrowsingDataItem.checked;
-        [self reconfigureCellsForItems:@[ clearBrowsingDataItem ]];
-        [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
-        break;
-      }
-      default:
-        break;
+  TableViewItem* item = [self.tableViewModel itemAtIndexPath:indexPath];
+  DCHECK(item);
+  switch (item.type) {
+    case ItemTypeTimeRange: {
+      UIViewController* controller =
+          [[TimeRangeSelectorTableViewController alloc]
+              initWithPrefs:self.browserState->GetPrefs()];
+      [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
+      [self.navigationController pushViewController:controller animated:YES];
+      break;
     }
-    [self updateToolbarButtons];
+    case ItemTypeDataTypeBrowsingHistory:
+    case ItemTypeDataTypeCookiesSiteData:
+    case ItemTypeDataTypeCache:
+    case ItemTypeDataTypeSavedPasswords:
+    case ItemTypeDataTypeAutofill: {
+      DCHECK([item isKindOfClass:[TableViewClearBrowsingDataItem class]]);
+      TableViewClearBrowsingDataItem* clearBrowsingDataItem =
+          base::mac::ObjCCastStrict<TableViewClearBrowsingDataItem>(item);
+      clearBrowsingDataItem.checked = !clearBrowsingDataItem.checked;
+      [self reconfigureCellsForItems:@[ clearBrowsingDataItem ]];
+      [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
+      break;
+    }
+    default:
+      break;
   }
+  [self updateToolbarButtons];
 }
 
 - (void)tableView:(UITableView*)tableView
@@ -453,25 +443,10 @@
       dataTypeMaskToRemove = dataTypeMaskToRemove | dataTypeItem.dataTypeMask;
     }
   }
-  ActionSheetCoordinator* actionSheetCoordinator;
-  if (IsNewClearBrowsingDataUIEnabled()) {
-    actionSheetCoordinator = [self.dataManager
-        actionSheetCoordinatorWithDataTypesToRemove:dataTypeMaskToRemove
-                                 baseViewController:self
-                                sourceBarButtonItem:sender];
-  } else {
-    // Get button's position in coordinate system of table view.
-    DCHECK_EQ(self.clearBrowsingDataButton, sender);
-    CGRect clearBrowsingDataButtonRect = [self.clearBrowsingDataButton
-        convertRect:self.clearBrowsingDataButton.bounds
-             toView:self.tableView];
-    actionSheetCoordinator = [self.dataManager
-        actionSheetCoordinatorWithDataTypesToRemove:dataTypeMaskToRemove
-                                 baseViewController:self
-                                         sourceRect:clearBrowsingDataButtonRect
-                                         sourceView:self.tableView];
-  }
-  self.actionSheetCoordinator = actionSheetCoordinator;
+  self.actionSheetCoordinator = [self.dataManager
+      actionSheetCoordinatorWithDataTypesToRemove:dataTypeMaskToRemove
+                               baseViewController:self
+                              sourceBarButtonItem:sender];
   [self.actionSheetCoordinator start];
 }
 
diff --git a/ios/chrome/browser/ui/settings/privacy_table_view_controller.mm b/ios/chrome/browser/ui/settings/privacy_table_view_controller.mm
index 2ceebd3..4baaa28 100644
--- a/ios/chrome/browser/ui/settings/privacy_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/privacy_table_view_controller.mm
@@ -245,18 +245,14 @@
       controller = [[HandoffTableViewController alloc]
           initWithBrowserState:_browserState];
       break;
-    case ItemTypeClearBrowsingDataClear:
-      if (IsNewClearBrowsingDataUIEnabled()) {
-        ClearBrowsingDataTableViewController* clearBrowsingDataViewController =
-            [[ClearBrowsingDataTableViewController alloc]
-                initWithBrowserState:_browserState];
-        clearBrowsingDataViewController.localDispatcher = self;
-        controller = clearBrowsingDataViewController;
-      } else {
-        controller = [[ClearBrowsingDataCollectionViewController alloc]
-            initWithBrowserState:_browserState];
-      }
+    case ItemTypeClearBrowsingDataClear: {
+      ClearBrowsingDataTableViewController* clearBrowsingDataViewController =
+          [[ClearBrowsingDataTableViewController alloc]
+              initWithBrowserState:_browserState];
+      clearBrowsingDataViewController.localDispatcher = self;
+      controller = clearBrowsingDataViewController;
       break;
+    }
     case ItemTypeWebServicesPaymentSwitch:
     default:
       break;
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm
index 05985b3..4263407 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm
@@ -106,11 +106,10 @@
 
 @interface TabGridViewController () <GridViewControllerDelegate,
                                      UIScrollViewAccessibilityDelegate>
-// It is programmer error to broadcast incognito content visibility when the
-// view is not visible. Bookkeeping is based on |-viewWillAppear:| and
+// Whether the view is visible. Bookkeeping is based on |-viewWillAppear:| and
 // |-viewWillDisappear methods. Note that the |Did| methods are not reliably
 // called (e.g., edge case in multitasking).
-@property(nonatomic, assign) BOOL broadcasting;
+@property(nonatomic, assign) BOOL viewVisible;
 // Child view controllers.
 @property(nonatomic, strong) GridViewController* regularTabsViewController;
 @property(nonatomic, strong) GridViewController* incognitoTabsViewController;
@@ -182,7 +181,7 @@
 }
 
 - (void)viewWillAppear:(BOOL)animated {
-  self.broadcasting = YES;
+  self.viewVisible = YES;
   [self.topToolbar.pageControl setSelectedPage:self.currentPage animated:YES];
   [self configureViewControllerForCurrentSizeClassesAndPage];
   // The toolbars should be hidden (alpha 0.0) before the tab appears, so that
@@ -223,7 +222,7 @@
   } else {
     [self hideToolbars];
   }
-  self.broadcasting = NO;
+  self.viewVisible = NO;
   [super viewWillDisappear:animated];
 }
 
@@ -527,7 +526,7 @@
 
   // If the view is visible and |animated| is YES, animate the change.
   // Otherwise don't.
-  if (self.view.window == nil || !animated) {
+  if (!self.viewVisible || !animated) {
     [self.scrollView setContentOffset:targetOffset animated:NO];
     self.currentPage = targetPage;
   } else {
@@ -1034,7 +1033,9 @@
 
 // Broadcasts whether incognito tabs are showing.
 - (void)broadcastIncognitoContentVisibility {
-  if (!self.broadcasting)
+  // It is programmer error to broadcast incognito content visibility when the
+  // view is not visible.
+  if (!self.viewVisible)
     return;
   BOOL incognitoContentVisible =
       (self.currentPage == TabGridPageIncognitoTabs &&
@@ -1130,7 +1131,7 @@
     if (count == 0 && self.currentPage == TabGridPageIncognitoTabs) {
       // Show the regular tabs to the user if the last incognito tab is closed.
       self.activePage = TabGridPageRegularTabs;
-      if (self.viewLoaded && self.view.window) {
+      if (self.viewLoaded && self.viewVisible) {
         // Visibly scroll to the regular tabs panel after a slight delay when
         // the user is already in the tab switcher.
         // Per crbug.com/980844, if the user has VoiceOver enabled, don't delay
diff --git a/ios/chrome/browser/ui/ui_feature_flags.cc b/ios/chrome/browser/ui/ui_feature_flags.cc
index 75bfa971..5dca4f4 100644
--- a/ios/chrome/browser/ui/ui_feature_flags.cc
+++ b/ios/chrome/browser/ui/ui_feature_flags.cc
@@ -7,9 +7,6 @@
 const base::Feature kBrowserContainerKeepsContentView{
     "BrowserContainerKeepsContentView", base::FEATURE_DISABLED_BY_DEFAULT};
 
-const base::Feature kOmniboxPopupShortcutIconsInZeroState{
-    "OmniboxPopupShortcutIconsInZeroState", base::FEATURE_DISABLED_BY_DEFAULT};
-
 // TODO(crbug.com/945811): Using |-drawViewHierarchyInRect:afterScreenUpdates:|
 // has adverse flickering when taking a snapshot of the NTP while in the app
 // switcher.
diff --git a/ios/chrome/browser/ui/ui_feature_flags.h b/ios/chrome/browser/ui/ui_feature_flags.h
index 9b4f410..034ff91 100644
--- a/ios/chrome/browser/ui/ui_feature_flags.h
+++ b/ios/chrome/browser/ui/ui_feature_flags.h
@@ -10,10 +10,6 @@
 // Feature to retain the contentView in the browser container.
 extern const base::Feature kBrowserContainerKeepsContentView;
 
-// Feature to show most visited sites and collection shortcuts in the omnibox
-// popup instead of ZeroSuggest.
-extern const base::Feature kOmniboxPopupShortcutIconsInZeroState;
-
 // Feature to take snapshots using |-drawViewHierarchy:|.
 extern const base::Feature kSnapshotDrawView;
 
diff --git a/ios/chrome/browser/url_loading/url_loading_service.h b/ios/chrome/browser/url_loading/url_loading_service.h
index 2c02441..0fdd6fc 100644
--- a/ios/chrome/browser/url_loading/url_loading_service.h
+++ b/ios/chrome/browser/url_loading/url_loading_service.h
@@ -18,8 +18,6 @@
 class UrlLoadingNotifier;
 struct UrlLoadParams;
 
-@class OpenNewTabCommand;
-
 // Objective-C delegate for UrlLoadingService.
 @protocol URLLoadingServiceDelegate
 
diff --git a/ios/chrome/test/earl_grey/BUILD.gn b/ios/chrome/test/earl_grey/BUILD.gn
index 6226852..938da77 100644
--- a/ios/chrome/test/earl_grey/BUILD.gn
+++ b/ios/chrome/test/earl_grey/BUILD.gn
@@ -86,7 +86,6 @@
     "//ios/chrome/browser/ui/ntp:eg_tests",
     "//ios/chrome/browser/ui/omnibox:eg_tests",
     "//ios/chrome/browser/ui/omnibox/popup:eg_tests",
-    "//ios/chrome/browser/ui/omnibox/popup/shortcuts:eg_tests",
     "//ios/chrome/browser/ui/page_info:eg_tests",
     "//ios/chrome/browser/ui/popup_menu:eg_tests",
     "//ios/chrome/browser/ui/qr_scanner:eg_tests",
@@ -288,6 +287,7 @@
     "//ios/chrome/browser/web:tab_id_tab_helper",
     "//ios/chrome/test/app:test_support",
     "//ios/testing:block_swizzler",
+    "//ios/testing:hardware_keyboarr_support",
     "//ios/testing:verify_custom_webkit",
     "//ios/testing/earl_grey:earl_grey_support",
     "//ios/third_party/material_components_ios",
@@ -402,6 +402,7 @@
     "//ios/chrome/browser/ui/infobars:eg_app_support+eg2",
     "//ios/chrome/browser/ui/location_bar:location_bar",
     "//ios/chrome/browser/ui/material_components",
+    "//ios/chrome/browser/ui/omnibox:app_support+eg2",
     "//ios/chrome/browser/ui/omnibox:omnibox_internal",
     "//ios/chrome/browser/ui/payments:payments_ui",
     "//ios/chrome/browser/ui/popup_menu:constants",
@@ -442,6 +443,7 @@
     "//ios/chrome/browser/web:tab_id_tab_helper",
     "//ios/chrome/test/app:test_support",
     "//ios/testing:block_swizzler",
+    "//ios/testing:hardware_keyboarr_support",
     "//ios/testing:nserror_support",
     "//ios/testing:verify_custom_webkit",
     "//ios/testing/earl_grey:eg_app_support+eg2",
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey.h b/ios/chrome/test/earl_grey/chrome_earl_grey.h
index 1bd2950a..f725165a 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey.h
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey.h
@@ -477,9 +477,19 @@
 // browser state.
 - (void)setPopupPrefValue:(ContentSetting)value;
 
+#pragma mark - Keyboard utilities
+
 // The count of key commands registered with the currently active BVC.
 - (NSInteger)registeredKeyCommandCount;
 
+// Simulates a physical keyboard event.
+// The input is similar to UIKeyCommand parameters, and is designed for testing
+// keyboard shortcuts.
+// Accepts any strings and also UIKeyInput{Up|Down|Left|Right}Arrow and
+// UIKeyInputEscape constants as |input|.
+- (void)simulatePhysicalKeyboardEvent:(NSString*)input
+                                flags:(UIKeyModifierFlags)flags;
+
 #pragma mark - Pref Utilities (EG2)
 
 // Sets the value of a boolean user pref in the original browser state.
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey.mm b/ios/chrome/test/earl_grey/chrome_earl_grey.mm
index 93e2060..aec609c 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey.mm
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey.mm
@@ -820,6 +820,11 @@
   return [ChromeEarlGreyAppInterface registeredKeyCommandCount];
 }
 
+- (void)simulatePhysicalKeyboardEvent:(NSString*)input
+                                flags:(UIKeyModifierFlags)flags {
+  [ChromeEarlGreyAppInterface simulatePhysicalKeyboardEvent:input flags:flags];
+}
+
 #pragma mark - Pref Utilities (EG2)
 
 - (void)setBoolValue:(BOOL)value forUserPref:(const std::string&)UTF8PrefName {
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h
index 5362c48..1412adf8 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h
@@ -398,6 +398,14 @@
 // The count of key commands registered with the currently active BVC.
 + (NSInteger)registeredKeyCommandCount;
 
+// Simulates a physical keyboard event.
+// The input is similar to UIKeyCommand parameters, and is designed for testing
+// keyboard shortcuts.
+// Accepts any strings and also UIKeyInput{Up|Down|Left|Right}Arrow and
+// UIKeyInputEscape constants as |input|.
++ (void)simulatePhysicalKeyboardEvent:(NSString*)input
+                                flags:(UIKeyModifierFlags)flags;
+
 @end
 
 #endif  // IOS_CHROME_TEST_EARL_GREY_CHROME_EARL_GREY_APP_INTERFACE_H_
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm
index 529cb09..ae83549 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm
@@ -33,6 +33,7 @@
 #import "ios/chrome/test/app/sync_test_util.h"
 #import "ios/chrome/test/app/tab_test_util.h"
 #import "ios/chrome/test/earl_grey/accessibility_util.h"
+#import "ios/testing/hardware_keyboard_util.h"
 #import "ios/testing/nserror_util.h"
 #include "ios/testing/verify_custom_webkit.h"
 #import "ios/web/common/features.h"
@@ -687,4 +688,9 @@
   return mainViewController.keyCommands.count;
 }
 
++ (void)simulatePhysicalKeyboardEvent:(NSString*)input
+                                flags:(UIKeyModifierFlags)flags {
+  chrome_test_util::SimulatePhysicalKeyboardEvent(flags, input);
+}
+
 @end
diff --git a/ios/chrome/test/earl_grey2/BUILD.gn b/ios/chrome/test/earl_grey2/BUILD.gn
index 12db963..93d614a 100644
--- a/ios/chrome/test/earl_grey2/BUILD.gn
+++ b/ios/chrome/test/earl_grey2/BUILD.gn
@@ -95,8 +95,8 @@
     "//ios/chrome/browser/ui/infobars:eg2_tests",
     "//ios/chrome/browser/ui/keyboard:eg2_tests",
     "//ios/chrome/browser/ui/ntp:eg2_tests",
+    "//ios/chrome/browser/ui/omnibox:eg2_tests",
     "//ios/chrome/browser/ui/omnibox/popup:eg2_tests",
-    "//ios/chrome/browser/ui/omnibox/popup/shortcuts:eg2_tests",
     "//ios/chrome/browser/ui/open_in:eg2_tests",
     "//ios/chrome/browser/ui/page_info:eg2_tests",
     "//ios/chrome/browser/ui/popup_menu:eg2_tests",
diff --git a/ios/web/web_thread_impl.cc b/ios/web/web_thread_impl.cc
index 8e35fb9..f7cbf99 100644
--- a/ios/web/web_thread_impl.cc
+++ b/ios/web/web_thread_impl.cc
@@ -13,7 +13,6 @@
 #include "base/compiler_specific.h"
 #include "base/lazy_instance.h"
 #include "base/macros.h"
-#include "base/no_destructor.h"
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
 #include "base/task/post_task.h"
@@ -145,13 +144,6 @@
   return accepting_tasks;
 }
 
-const scoped_refptr<base::SequencedTaskRunner>& GetNullTaskRunner() {
-  static const base::NoDestructor<scoped_refptr<base::SequencedTaskRunner>>
-      null_task_runner;
-  return *null_task_runner;
-}
-
-// Task executor for UI and IO threads.
 class WebThreadTaskExecutor : public base::TaskExecutor {
  public:
   WebThreadTaskExecutor() {}
@@ -185,22 +177,12 @@
     return GetTaskRunnerForThread(GetWebThreadIdentifier(traits));
   }
 
-  const scoped_refptr<base::SequencedTaskRunner>& GetContinuationTaskRunner()
-      override {
-    NOTREACHED() << "WebThreadTaskExecutor isn't registered via "
-                    "base::SetTaskExecutorForCurrentThread";
-    return GetNullTaskRunner();
-  }
-
  private:
   WebThread::ID GetWebThreadIdentifier(const base::TaskTraits& traits) {
     DCHECK_EQ(traits.extension_id(), WebTaskTraitsExtension::kExtensionId);
     WebThread::ID id =
         traits.GetExtension<WebTaskTraitsExtension>().web_thread();
     DCHECK_LT(id, WebThread::ID_COUNT);
-    DCHECK(!traits.use_current_thread())
-        << "WebThreadTaskExecutor isn't registered via "
-           "base::SetTaskExecutorForCurrentThread";
 
     // TODO(crbug.com/872372): Support shutdown behavior on UI/IO threads.
     if (traits.shutdown_behavior_set_explicitly()) {
@@ -235,13 +217,6 @@
     : identifier_(identifier) {
   DCHECK(task_runner);
 
-  if (identifier == WebThread::UI) {
-    DCHECK(task_runner->BelongsToCurrentThread());
-    // TODO(scheduler-dev): Pass the backing SequenceManager in here to ensure
-    // GetContinuationTaskRunner DCHECKS when there's no task running.
-    ui_thread_tls_executor_.emplace(nullptr, task_runner);
-  }
-
   WebThreadGlobals& globals = g_globals.Get();
 
   base::AutoLock lock(globals.lock);
diff --git a/ios/web/web_thread_impl.h b/ios/web/web_thread_impl.h
index 51df75bd..401dc2f6 100644
--- a/ios/web/web_thread_impl.h
+++ b/ios/web/web_thread_impl.h
@@ -7,7 +7,6 @@
 
 #include "base/callback.h"
 #include "base/memory/scoped_refptr.h"
-#include "base/task/simple_task_executor.h"
 #include "base/threading/thread.h"
 #include "ios/web/public/thread/web_thread.h"
 
@@ -66,10 +65,6 @@
   // The identifier of this thread.  Only one thread can exist with a given
   // identifier at a given time.
   ID identifier_;
-
-  // Here to support base::CurrentThread and base::GetContinuationTaskRunner on
-  // the UI thread.
-  base::Optional<base::SimpleTaskExecutor> ui_thread_tls_executor_;
 };
 
 }  // namespace web
diff --git a/ios/web/web_thread_unittest.cc b/ios/web/web_thread_unittest.cc
index 98b4ca8..13011d811 100644
--- a/ios/web/web_thread_unittest.cc
+++ b/ios/web/web_thread_unittest.cc
@@ -6,16 +6,12 @@
 
 #include "base/bind.h"
 #include "base/task/post_task.h"
-#include "base/test/bind_test_util.h"
 #include "ios/web/public/test/web_task_environment.h"
 #include "ios/web/public/thread/web_task_traits.h"
 #include "ios/web/web_thread_impl.h"
-#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
 
-using testing::NotNull;
-
 namespace web {
 
 class WebThreadTest : public PlatformTest {
@@ -68,37 +64,4 @@
   run_loop.Run();
 }
 
-TEST_F(WebThreadTest, CurrentThread) {
-  base::RunLoop run_loop;
-
-  base::PostTask(
-      FROM_HERE, {base::CurrentThread()}, base::BindLambdaForTesting([&]() {
-        PostTask(FROM_HERE, {base::CurrentThread()}, run_loop.QuitClosure());
-      }));
-
-  run_loop.Run();
-}
-
-TEST_F(WebThreadTest, GetContinuationTaskRunner) {
-  base::RunLoop run_loop;
-  auto task_runner =
-      base::CreateSingleThreadTaskRunner({base::CurrentThread()});
-
-  task_runner->PostTask(FROM_HERE, base::BindLambdaForTesting([&]() {
-                          EXPECT_EQ(task_runner,
-                                    base::GetContinuationTaskRunner());
-                          run_loop.Quit();
-                        }));
-
-  run_loop.Run();
-}
-
-TEST_F(WebThreadTest, GetContinuationTaskRunnerWithNoTaskRunning) {
-  // TODO(scheduler-dev): GetContinuationTaskRunner should DCHECK if there's no
-  // task running.
-  EXPECT_EQ(base::CreateSequencedTaskRunner({base::CurrentThread()}),
-            base::GetContinuationTaskRunner());
-  EXPECT_THAT(base::GetContinuationTaskRunner().get(), NotNull());
-}
-
 }  // namespace web
diff --git a/media/test/data/README.md b/media/test/data/README.md
index f3ba0f0f..910d5e9 100644
--- a/media/test/data/README.md
+++ b/media/test/data/README.md
@@ -779,6 +779,18 @@
 To get the uncompressed yuv, execute the following command.
 `ffmpeg -s:v 1280x720 -pix_fmt nv12 -i puppets-1280x720.nv12.yuv -vf scale=640x360 -c:v rawvideo -pix_fmt nv12 puppets-640x360.nv12.yuv`
 
+#### puppets-480x270.nv12.yuv
+RAW NV12 format data. The width and height are 640 and 360, respectively.
+To get the uncompressed yuv, execute the following command.
+`ffmpeg -s:v 1280x720 -pix_fmt nv12 -i puppets-1280x720.nv12.yuv -vf scale=480x270 -c:v rawvideo -pix_fmt nv12 puppets-480x270.nv12.yuv`
+
+### puppets-640x360\_in\_640x480.nv12.yuv
+RAW NV12 format data. The width and height are 640 and 480, respectively.
+The meaningful image is at the rectangle (0, 0, 640x360) in the image. The area
+outside this rectangle is black.
+To get the uncompressed yuv, execute the following command.
+`ffmpeg -s:v 640x360 -pix_fmt nv12 -i puppets-640x360.nv12.yuv -vf "scale=640:480:force_original_aspect_ratio=decrease,pad=640:480:(ow-iw)/2:(oh-ih)/2" -c:v rawvideo -pix_fmt nv12 puppets-640x360_in_640x480.nv12.yuv`
+
 #### puppets-320x180.nv12.yuv
 RAW NV12 format data. The width and height are 320 and 180, respectively.
 To get the uncompressed yuv, execute the following command.
diff --git a/media/test/data/puppets-480x270.nv12.yuv.json b/media/test/data/puppets-480x270.nv12.yuv.json
new file mode 100644
index 0000000..9b9db0de
--- /dev/null
+++ b/media/test/data/puppets-480x270.nv12.yuv.json
@@ -0,0 +1,6 @@
+{
+  "pixel_format": "NV12",
+  "width": 480,
+  "height": 270,
+  "checksum": "bac69b3c46a041ad2a53ddb42b04f87f"
+}
diff --git a/media/test/data/puppets-640x360_in_640x480.nv12.yuv.json b/media/test/data/puppets-640x360_in_640x480.nv12.yuv.json
new file mode 100644
index 0000000..1af34c1
--- /dev/null
+++ b/media/test/data/puppets-640x360_in_640x480.nv12.yuv.json
@@ -0,0 +1,7 @@
+{
+  "pixel_format": "NV12",
+  "width": 640,
+  "height": 480,
+  "visible_rect": [0, 60, 640, 360],
+  "checksum": "ba5de923b0e79f1d3a96e2ccc99104bd"
+}
diff --git a/net/base/net_errors.cc b/net/base/net_errors.cc
index 7e7ee37ac..ed6bc4514 100644
--- a/net/base/net_errors.cc
+++ b/net/base/net_errors.cc
@@ -63,8 +63,8 @@
 }
 
 bool IsDnsError(int error) {
-  DCHECK_NE(ERR_NAME_RESOLUTION_FAILED, error);
-  return error == ERR_NAME_NOT_RESOLVED;
+  return (error == ERR_NAME_NOT_RESOLVED ||
+          error == ERR_NAME_RESOLUTION_FAILED);
 }
 
 Error FileErrorToNetError(base::File::Error file_error) {
diff --git a/net/cert_net/nss_ocsp.cc b/net/cert_net/nss_ocsp.cc
index 84f6c71..5734fddb 100644
--- a/net/cert_net/nss_ocsp.cc
+++ b/net/cert_net/nss_ocsp.cc
@@ -45,9 +45,9 @@
 pthread_mutex_t g_request_session_delegate_factory_lock =
     PTHREAD_MUTEX_INITIALIZER;
 
-scoped_refptr<OCSPRequestSessionDelegateFactory>&
+std::unique_ptr<OCSPRequestSessionDelegateFactory>&
 GetRequestSessionDelegateFactoryPtr() {
-  static base::NoDestructor<scoped_refptr<OCSPRequestSessionDelegateFactory>>
+  static base::NoDestructor<std::unique_ptr<OCSPRequestSessionDelegateFactory>>
       wrapper;
   return *wrapper.get();
 }
@@ -564,11 +564,11 @@
 }
 
 void SetOCSPRequestSessionDelegateFactory(
-    scoped_refptr<OCSPRequestSessionDelegateFactory> new_factory) {
-  scoped_refptr<OCSPRequestSessionDelegateFactory> factory_to_be_destructed;
+    std::unique_ptr<OCSPRequestSessionDelegateFactory> new_factory) {
+  std::unique_ptr<OCSPRequestSessionDelegateFactory> factory_to_be_destructed;
 
   pthread_mutex_lock(&g_request_session_delegate_factory_lock);
-  scoped_refptr<OCSPRequestSessionDelegateFactory>& current_factory =
+  std::unique_ptr<OCSPRequestSessionDelegateFactory>& current_factory =
       GetRequestSessionDelegateFactoryPtr();
   // The same NSS-using process should only ever use one concrete
   // OCSPRequestSessionDelegateFactory for the lifetime of that process. If this
@@ -578,7 +578,8 @@
   // instance.
   DCHECK(!new_factory || !current_factory.get());
 
-  factory_to_be_destructed = std::exchange(current_factory, new_factory);
+  factory_to_be_destructed =
+      std::exchange(current_factory, std::move(new_factory));
   pthread_mutex_unlock(&g_request_session_delegate_factory_lock);
 }
 
diff --git a/net/cert_net/nss_ocsp.h b/net/cert_net/nss_ocsp.h
index 0a96a72..cb33ff3 100644
--- a/net/cert_net/nss_ocsp.h
+++ b/net/cert_net/nss_ocsp.h
@@ -60,23 +60,21 @@
   virtual ~OCSPRequestSessionDelegate();
 };
 
-class NET_EXPORT OCSPRequestSessionDelegateFactory
-    : public base::RefCountedThreadSafe<OCSPRequestSessionDelegateFactory> {
+class NET_EXPORT OCSPRequestSessionDelegateFactory {
  public:
-  REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE();
-
   OCSPRequestSessionDelegateFactory();
+
+  // Not thread-safe, but can be called on different threads with the use of
+  // mutual exclusion.
   virtual scoped_refptr<OCSPRequestSessionDelegate>
   CreateOCSPRequestSessionDelegate() = 0;
 
- protected:
-  friend class base::RefCountedThreadSafe<OCSPRequestSessionDelegateFactory>;
   virtual ~OCSPRequestSessionDelegateFactory();
 };
 
 // Sets the factory that creates OCSPRequestSessions.
 NET_EXPORT void SetOCSPRequestSessionDelegateFactory(
-    scoped_refptr<OCSPRequestSessionDelegateFactory> factory);
+    std::unique_ptr<OCSPRequestSessionDelegateFactory> factory);
 
 // Initializes HTTP client functions for NSS.  This function is thread-safe,
 // and HTTP handlers will only ever be initialized once.
diff --git a/net/cert_net/nss_ocsp_session_url_request.cc b/net/cert_net/nss_ocsp_session_url_request.cc
index 97aab39..a609db7 100644
--- a/net/cert_net/nss_ocsp_session_url_request.cc
+++ b/net/cert_net/nss_ocsp_session_url_request.cc
@@ -24,7 +24,7 @@
 
 // The maximum size in bytes for the response body when fetching an OCSP/CRL
 // URL.
-const int kMaxResponseSizeInBytesForOCSP = 5 * 1024 * 1024;
+const int kMaxResponseSizeInBytes = 5 * 1024 * 1024;
 }  // namespace
 
 class OCSPRequestSessionDelegateURLRequest;
@@ -167,10 +167,9 @@
     }
 
     // Check max size.
-    if (result_->data.size() > kMaxResponseSizeInBytesForOCSP) {
-      result_->data.clear();
-      // Set response code to -1 to signify error.
-      result_->response_code = -1;
+    if (result_->data.size() > kMaxResponseSizeInBytes) {
+      // Reset the result to indicate error.
+      result_.reset();
       FinishLoad();
     }
 
@@ -342,10 +341,10 @@
     (*request_delegates_.begin())->CancelLoad();
 }
 
-class OCSPRequestSessionDelegateURLRequestFactory
+class OCSPRequestSessionDelegateFactoryURLRequest
     : public OCSPRequestSessionDelegateFactory {
  public:
-  OCSPRequestSessionDelegateURLRequestFactory(
+  OCSPRequestSessionDelegateFactoryURLRequest(
       URLRequestContext* request_context)
       : request_context_(request_context) {}
   scoped_refptr<OCSPRequestSessionDelegate> CreateOCSPRequestSessionDelegate()
@@ -354,15 +353,16 @@
         request_context_);
   }
 
+  ~OCSPRequestSessionDelegateFactoryURLRequest() override = default;
+
  private:
-  ~OCSPRequestSessionDelegateURLRequestFactory() override = default;
   URLRequestContext* request_context_;
 };
 
 void SetURLRequestContextForNSSHttpIO(URLRequestContext* request_context) {
   if (request_context) {
     SetOCSPRequestSessionDelegateFactory(
-        base::MakeRefCounted<OCSPRequestSessionDelegateURLRequestFactory>(
+        std::make_unique<OCSPRequestSessionDelegateFactoryURLRequest>(
             request_context));
   } else {
     SetOCSPRequestSessionDelegateFactory(nullptr);
diff --git a/net/dns/host_resolver.h b/net/dns/host_resolver.h
index 264ab583..092ac6f 100644
--- a/net/dns/host_resolver.h
+++ b/net/dns/host_resolver.h
@@ -66,9 +66,12 @@
     // On any other returned value, the request was handled synchronously and
     // |callback| will not be invoked.
     //
-    // Results in ERR_NAME_NOT_RESOLVED if the hostname is not resolved. More
-    // detail about the underlying error can be retrieved using
-    // GetResolveErrorInfo().
+    // Results in ERR_NAME_NOT_RESOLVED if the hostname is invalid, or if it is
+    // an incompatible IP literal (e.g. IPv6 is disabled and it is an IPv6
+    // literal).
+    //
+    // Results in ERR_DNS_CACHE_MISS if only fast local sources are to be
+    // queried and a cache lookup attempt fails.
     //
     // The parent HostResolver must still be alive when Start() is called,  but
     // if it is destroyed before an asynchronous result completes, the request
diff --git a/net/dns/host_resolver_manager.cc b/net/dns/host_resolver_manager.cc
index a98331a..795386a 100644
--- a/net/dns/host_resolver_manager.cc
+++ b/net/dns/host_resolver_manager.cc
@@ -650,7 +650,7 @@
     LogFinishRequest(error);
 
     DCHECK(callback_);
-    std::move(callback_).Run(HostResolver::SquashErrorCode(error));
+    std::move(callback_).Run(error);
   }
 
   Job* job() const { return job_; }
@@ -2992,7 +2992,7 @@
                     effective_secure_dns_mode, base::TimeDelta());
     request->set_error_info(results.error(),
                             false /* is_secure_network_error */);
-    return HostResolver::SquashErrorCode(results.error());
+    return results.error();
   }
 
   CreateAndStartJob(effective_query_type, effective_host_resolver_flags,
diff --git a/net/url_request/http_with_dns_over_https_unittest.cc b/net/url_request/http_with_dns_over_https_unittest.cc
index d4f1d9e..12fe95c0 100644
--- a/net/url_request/http_with_dns_over_https_unittest.cc
+++ b/net/url_request/http_with_dns_over_https_unittest.cc
@@ -313,7 +313,7 @@
   EXPECT_EQ(test_https_requests_served_, 0u);
 
   EXPECT_TRUE(d.response_completed());
-  EXPECT_EQ(d.request_status(), net::ERR_NAME_NOT_RESOLVED);
+  EXPECT_EQ(d.request_status(), net::ERR_DNS_MALFORMED_RESPONSE);
 
   const auto& resolve_error_info = req->response_info().resolve_error_info;
   EXPECT_TRUE(resolve_error_info.is_secure_network_error);
diff --git a/sandbox/linux/system_headers/x86_64_linux_syscalls.h b/sandbox/linux/system_headers/x86_64_linux_syscalls.h
index 349504a..b0ae0a2 100644
--- a/sandbox/linux/system_headers/x86_64_linux_syscalls.h
+++ b/sandbox/linux/system_headers/x86_64_linux_syscalls.h
@@ -1290,5 +1290,65 @@
 #define __NR_memfd_create 319
 #endif
 
+#if !defined(__NR_kexec_file_load)
+#define __NR_kexec_file_load 320
+#endif
+
+#if !defined(__NR_bpf)
+#define __NR_bpf 321
+#endif
+
+#if !defined(__NR_execveat)
+#define __NR_execveat 322
+#endif
+
+#if !defined(__NR_userfaultfd)
+#define __NR_userfaultfd 323
+#endif
+
+#if !defined(__NR_membarrier)
+#define __NR_membarrier 324
+#endif
+
+#if !defined(__NR_mlock2)
+#define __NR_mlock2 325
+#endif
+
+#if !defined(__NR_copy_file_range)
+#define __NR_copy_file_range 326
+#endif
+
+#if !defined(__NR_preadv2)
+#define __NR_preadv2 327
+#endif
+
+#if !defined(__NR_pwritev2)
+#define __NR_pwritev2 328
+#endif
+
+#if !defined(__NR_pkey_mprotect)
+#define __NR_pkey_mprotect 329
+#endif
+
+#if !defined(__NR_pkey_alloc)
+#define __NR_pkey_alloc 330
+#endif
+
+#if !defined(__NR_pkey_free)
+#define __NR_pkey_free 331
+#endif
+
+#if !defined(__NR_statx)
+#define __NR_statx 332
+#endif
+
+#if !defined(__NR_io_pgetevents)
+#define __NR_io_pgetevents 333
+#endif
+
+#if !defined(__NR_rseq)
+#define __NR_rseq 334
+#endif
+
 #endif  // SANDBOX_LINUX_SYSTEM_HEADERS_X86_64_LINUX_SYSCALLS_H_
 
diff --git a/services/network/public/cpp/cert_verifier/BUILD.gn b/services/network/public/cpp/cert_verifier/BUILD.gn
index 23a2070..fbc18ac4 100644
--- a/services/network/public/cpp/cert_verifier/BUILD.gn
+++ b/services/network/public/cpp/cert_verifier/BUILD.gn
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/crypto.gni")
 import("//build/config/jumbo.gni")
 import("//testing/test.gni")
 
@@ -11,6 +12,13 @@
     "cert_net_fetcher_url_loader.h",
   ]
 
+  if (use_nss_certs) {
+    sources += [
+      "nss_ocsp_session_url_loader.cc",
+      "nss_ocsp_session_url_loader.h",
+    ]
+  }
+
   deps = [
     "//base",
     "//mojo/public/cpp/bindings",
@@ -25,11 +33,15 @@
 source_set("tests") {
   testonly = true
 
+  sources = []
+
   if (!is_ios) {
     # Need TestServer
-    sources = [
-      "cert_net_fetcher_url_loader_unittest.cc",
-    ]
+    sources += [ "cert_net_fetcher_url_loader_unittest.cc" ]
+  }
+
+  if (use_nss_certs) {
+    sources += [ "nss_ocsp_session_url_loader_unittest.cc" ]
   }
 
   deps = [
diff --git a/services/network/public/cpp/cert_verifier/nss_ocsp_session_url_loader.cc b/services/network/public/cpp/cert_verifier/nss_ocsp_session_url_loader.cc
new file mode 100644
index 0000000..0a95dacb
--- /dev/null
+++ b/services/network/public/cpp/cert_verifier/nss_ocsp_session_url_loader.cc
@@ -0,0 +1,207 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/network/public/cpp/cert_verifier/nss_ocsp_session_url_loader.h"
+#include <type_traits>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/sequenced_task_runner.h"
+#include "base/task/post_task.h"
+#include "net/base/load_flags.h"
+#include "net/http/http_request_headers.h"
+#include "url/gurl.h"
+
+namespace cert_verifier {
+
+namespace {
+// The maximum size in bytes for the response body when fetching an OCSP/CRL
+// URL.
+const int kMaxResponseSizeInBytes = 5 * 1024 * 1024;
+
+bool CanFetchUrl(const GURL& url) {
+  return url.SchemeIs("http");
+}
+
+}  // namespace
+
+OCSPRequestSessionDelegateURLLoader::OCSPRequestSessionDelegateURLLoader(
+    scoped_refptr<base::SequencedTaskRunner> load_task_runner,
+    base::WeakPtr<OCSPRequestSessionDelegateFactoryURLLoader> delegate_factory)
+    : load_task_runner_(std::move(load_task_runner)),
+      delegate_factory_(std::move(delegate_factory)) {}
+
+OCSPRequestSessionDelegateURLLoader::~OCSPRequestSessionDelegateURLLoader() =
+    default;
+
+std::unique_ptr<net::OCSPRequestSessionResult>
+OCSPRequestSessionDelegateURLLoader::StartAndWait(
+    const net::OCSPRequestSessionParams* params) {
+  Start(params);
+  return WaitForResult();
+}
+
+void OCSPRequestSessionDelegateURLLoader::Start(
+    const net::OCSPRequestSessionParams* params) {
+  load_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&OCSPRequestSessionDelegateURLLoader::StartLoad,
+                                this, params));
+}
+
+std::unique_ptr<net::OCSPRequestSessionResult>
+OCSPRequestSessionDelegateURLLoader::WaitForResult() {
+  // Sit and wait for the load to finish.
+  wait_event_.Wait();
+
+  return std::move(result_);
+}
+
+void OCSPRequestSessionDelegateURLLoader::StartLoad(
+    const net::OCSPRequestSessionParams* params) {
+  DCHECK(load_task_runner_->RunsTasksInCurrentSequence());
+
+  if (!CanFetchUrl(params->url) || !delegate_factory_) {
+    CancelLoad();
+    return;
+  }
+
+  // Start the SimpleURLLoader.
+  net::NetworkTrafficAnnotationTag traffic_annotation =
+      net::DefineNetworkTrafficAnnotation("ocsp_start_url_loader", R"(
+        semantics {
+          sender: "OCSP"
+          description:
+            "Verifying the revocation status of a certificate via OCSP."
+          trigger:
+            "This may happen in response to visiting a website that uses "
+            "https://"
+          data:
+            "Identifier for the certificate whose revocation status is being "
+            "checked. See https://tools.ietf.org/html/rfc6960#section-2.1 for "
+            "more details."
+          destination: OTHER
+          destination_other:
+            "The URI specified in the certificate."
+        }
+        policy {
+          cookies_allowed: NO
+          setting: "This feature cannot be disabled by settings."
+          policy_exception_justification: "Not implemented."
+        })");
+
+  // Create a ResourceRequest based on |params|.
+  auto request = std::make_unique<network::ResourceRequest>();
+  request->url = params->url;
+  request->credentials_mode = network::mojom::CredentialsMode::kOmit;
+  request->load_flags = net::LOAD_DISABLE_CACHE;
+
+  if (!params->extra_request_headers.IsEmpty())
+    request->headers = params->extra_request_headers;
+
+  std::string upload_string;
+  if (params->http_request_method == "POST") {
+    DCHECK(!params->upload_content.empty());
+    DCHECK(!params->upload_content_type.empty());
+
+    request->method = "POST";
+    upload_string = std::string(params->upload_content.data(),
+                                params->upload_content.size());
+  }
+
+  url_loader_ =
+      network::SimpleURLLoader::Create(std::move(request), traffic_annotation);
+  if (!upload_string.empty())
+    url_loader_->AttachStringForUpload(std::move(upload_string),
+                                       params->upload_content_type);
+
+  url_loader_->SetTimeoutDuration(params->timeout);
+  // base::Unretained(this) is safe because |this| owns |url_loader_|, which
+  // will not call the callback if it is deleted.
+  url_loader_->SetOnRedirectCallback(base::BindRepeating(
+      &OCSPRequestSessionDelegateURLLoader::OnReceivedRedirect,
+      base::Unretained(this)));
+  // The completion callback holds a reference to this class to make sure we
+  // always call FinishLoad() and signal the worker thread to continue.
+  // We will not touch |this| after deleting |url_loader_|, which may hold the
+  // last reference to |this|.
+  url_loader_->DownloadToString(
+      delegate_factory_->GetSharedURLLoaderFactory().get(),
+      base::BindOnce(&OCSPRequestSessionDelegateURLLoader::OnUrlLoaderCompleted,
+                     this),
+      kMaxResponseSizeInBytes);
+
+  result_ = std::make_unique<net::OCSPRequestSessionResult>();
+}
+
+void OCSPRequestSessionDelegateURLLoader::OnReceivedRedirect(
+    const net::RedirectInfo& redirect_info,
+    const network::mojom::URLResponseHead& response_head,
+    std::vector<std::string>* removed_headers) {
+  DCHECK(load_task_runner_->RunsTasksInCurrentSequence());
+
+  if (!CanFetchUrl(redirect_info.new_url)) {
+    CancelLoad();  // May delete |this|
+  }
+}
+
+void OCSPRequestSessionDelegateURLLoader::OnUrlLoaderCompleted(
+    std::unique_ptr<std::string> response_body) {
+  DCHECK(load_task_runner_->RunsTasksInCurrentSequence());
+
+  if (!response_body || !url_loader_->ResponseInfo()) {
+    CancelLoad();  // May delete |this|
+    return;
+  }
+
+  result_->response_code =
+      url_loader_->ResponseInfo()->headers->response_code();
+  result_->response_headers = url_loader_->ResponseInfo()->headers;
+  result_->response_content_type = url_loader_->ResponseInfo()->mime_type;
+  result_->data = std::move(*response_body);
+  FinishLoad();  // May delete |this|
+}
+
+void OCSPRequestSessionDelegateURLLoader::CancelLoad() {
+  DCHECK(load_task_runner_->RunsTasksInCurrentSequence());
+
+  result_.reset();
+  FinishLoad();  // May delete |this|
+}
+
+void OCSPRequestSessionDelegateURLLoader::FinishLoad() {
+  DCHECK(load_task_runner_->RunsTasksInCurrentSequence());
+
+  wait_event_.Signal();
+
+  url_loader_.reset();
+  // Cannot touch |this| as |url_loader_| may have owned the last reference to
+  // |this|.
+}
+
+OCSPRequestSessionDelegateFactoryURLLoader::
+    OCSPRequestSessionDelegateFactoryURLLoader(
+        scoped_refptr<base::SequencedTaskRunner> loader_factory_sequence,
+        scoped_refptr<network::SharedURLLoaderFactory> loader_factory)
+    : loader_factory_sequence_(std::move(loader_factory_sequence)),
+      loader_factory_(std::move(loader_factory)),
+      weak_factory_(this) {
+  DCHECK(loader_factory_sequence_->RunsTasksInCurrentSequence());
+
+  weak_ptr_ = weak_factory_.GetWeakPtr();
+}
+
+scoped_refptr<net::OCSPRequestSessionDelegate>
+OCSPRequestSessionDelegateFactoryURLLoader::CreateOCSPRequestSessionDelegate() {
+  return base::MakeRefCounted<OCSPRequestSessionDelegateURLLoader>(
+      loader_factory_sequence_, weak_ptr_);
+}
+
+OCSPRequestSessionDelegateFactoryURLLoader::
+    ~OCSPRequestSessionDelegateFactoryURLLoader() {
+  DCHECK(loader_factory_sequence_->RunsTasksInCurrentSequence());
+}
+
+}  // namespace cert_verifier
diff --git a/services/network/public/cpp/cert_verifier/nss_ocsp_session_url_loader.h b/services/network/public/cpp/cert_verifier/nss_ocsp_session_url_loader.h
new file mode 100644
index 0000000..1873c39
--- /dev/null
+++ b/services/network/public/cpp/cert_verifier/nss_ocsp_session_url_loader.h
@@ -0,0 +1,141 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SERVICES_NETWORK_PUBLIC_CPP_CERT_VERIFIER_NSS_OCSP_SESSION_URL_LOADER_H_
+#define SERVICES_NETWORK_PUBLIC_CPP_CERT_VERIFIER_NSS_OCSP_SESSION_URL_LOADER_H_
+
+#include <memory>
+
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequenced_task_runner.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "net/cert_net/nss_ocsp.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/simple_url_loader.h"
+
+namespace cert_verifier {
+
+class OCSPRequestSessionDelegateFactoryURLLoader;
+
+// Implementation of net::OCSPRequestSessionDelegate that uses a
+// SharedURLLoaderFactory to perform loads via the network service.
+class COMPONENT_EXPORT(CERT_VERIFIER_CPP) OCSPRequestSessionDelegateURLLoader
+    : public net::OCSPRequestSessionDelegate {
+ public:
+  // Creates a new Delegate that will use |loader_factory| to
+  // load URLs as needed. Loading requests will be dispatched and processed
+  // on |load_task_runner|, as StartAndWait() will block the thread/task
+  // runner it is called on.
+  // |delegate_factory| should be bound to |load_task_runner| and will be used
+  // to access |delegate_factory| and perform loads.
+  OCSPRequestSessionDelegateURLLoader(
+      scoped_refptr<base::SequencedTaskRunner> load_task_runner,
+      base::WeakPtr<OCSPRequestSessionDelegateFactoryURLLoader>
+          delegate_factory);
+
+  // OCSPRequestSessionDelegate overrides:
+  std::unique_ptr<net::OCSPRequestSessionResult> StartAndWait(
+      const net::OCSPRequestSessionParams* params) override;
+
+ private:
+  FRIEND_TEST_ALL_PREFIXES(OCSPRequestSessionDelegateURLLoaderTest,
+                           TestTimeout);
+
+  ~OCSPRequestSessionDelegateURLLoader() override;
+
+  // Posts a call to StartLoad() to |load_task_runner|, but does not
+  // wait for that load to be completed.
+  void Start(const net::OCSPRequestSessionParams* params);
+
+  // Blocks the current thread until the load previously started by
+  // Start() completes, returning the result. If Start() has not been
+  // called, this will block indefinitely.
+  std::unique_ptr<net::OCSPRequestSessionResult> WaitForResult();
+
+  // Invoked on |load_task_runner_|. Will bind |factory_pending_remote_| to the
+  // current sequence and instantiate a SimpleURLLoader to load from the
+  // network.
+  void StartLoad(const net::OCSPRequestSessionParams* params);
+
+  void OnReceivedRedirect(const net::RedirectInfo& redirect_info,
+                          const network::mojom::URLResponseHead& response_head,
+                          std::vector<std::string>* removed_headers);
+
+  void OnResponseStarted(const GURL& final_url,
+                         const network::mojom::URLResponseHead& response_head);
+
+  void OnUrlLoaderCompleted(std::unique_ptr<std::string> response_body);
+
+  // May delete |this|.
+  void CancelLoad();
+
+  // May delete |this|.
+  void FinishLoad();
+
+  const scoped_refptr<base::SequencedTaskRunner> load_task_runner_;
+
+  // Accessed only on |load_task_runner_|.
+  const base::WeakPtr<OCSPRequestSessionDelegateFactoryURLLoader>
+      delegate_factory_;
+  std::unique_ptr<network::SimpleURLLoader> url_loader_;
+
+  std::unique_ptr<net::OCSPRequestSessionResult> result_;
+  base::WaitableEvent wait_event_;
+};
+
+// An implementation of net::OCSPRequestSessionDelegateFactory that takes a
+// SharedURLLoaderFactory, and the sequence it's bound to, and will vend
+// net::OCSPRequestSessionDelegate's that use the provided
+// SharedURLLoaderFactory to load from the network.
+class COMPONENT_EXPORT(CERT_VERIFIER_CPP)
+    OCSPRequestSessionDelegateFactoryURLLoader
+    : public net::OCSPRequestSessionDelegateFactory {
+ public:
+  // |loader_factory_sequence| should be the sequence that |loader_factory| is
+  // bound to. Tasks will be posted to the |loader_factory_sequence| that make
+  // use of |loader_factory|.
+  // When the delegate factory is destroyed, vended delegates may finish their
+  // loads or may return early without a result.
+  OCSPRequestSessionDelegateFactoryURLLoader(
+      scoped_refptr<base::SequencedTaskRunner> loader_factory_sequence,
+      scoped_refptr<network::SharedURLLoaderFactory> loader_factory);
+
+  // Returns a scoped_refptr<net::OCSPRequestSessionDelegateURLLoader>.
+  scoped_refptr<net::OCSPRequestSessionDelegate>
+  CreateOCSPRequestSessionDelegate() override;
+
+  ~OCSPRequestSessionDelegateFactoryURLLoader() override;
+
+ private:
+  // The delegate is a friend so that it can call GetSharedURLLoaderFactory().
+  friend OCSPRequestSessionDelegateURLLoader;
+
+  // Must be invoked on |loader_factory_sequence|. Gets the
+  // SharedURLLoaderFactory passed in the constructor.
+  scoped_refptr<network::SharedURLLoaderFactory> GetSharedURLLoaderFactory()
+      const {
+    return loader_factory_;
+  }
+
+  // Sequence that |loader_factory_| is bound to. Use to run the
+  // SimpleURLLoaders.
+  const scoped_refptr<base::SequencedTaskRunner> loader_factory_sequence_;
+  // SharedURLLoaderFactory to use for network loads.
+  const scoped_refptr<network::SharedURLLoaderFactory> loader_factory_;
+
+  // Holds a weak ptr to |this| and bound to |loader_factory_sequence_|.
+  base::WeakPtr<OCSPRequestSessionDelegateFactoryURLLoader> weak_ptr_;
+
+  base::WeakPtrFactory<OCSPRequestSessionDelegateFactoryURLLoader>
+      weak_factory_;
+};
+
+}  // namespace cert_verifier
+
+#endif  // SERVICES_NETWORK_PUBLIC_CPP_CERT_VERIFIER_NSS_OCSP_SESSION_URL_LOADER_H_
diff --git a/services/network/public/cpp/cert_verifier/nss_ocsp_session_url_loader_unittest.cc b/services/network/public/cpp/cert_verifier/nss_ocsp_session_url_loader_unittest.cc
new file mode 100644
index 0000000..b384100
--- /dev/null
+++ b/services/network/public/cpp/cert_verifier/nss_ocsp_session_url_loader_unittest.cc
@@ -0,0 +1,553 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/network/public/cpp/cert_verifier/nss_ocsp_session_url_loader.h"
+
+#include <array>
+#include <memory>
+#include <string>
+#include <type_traits>
+#include <utility>
+
+#include "base/barrier_closure.h"
+#include "base/bind.h"
+#include "base/callback_forward.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/location.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/run_loop.h"
+#include "base/sequenced_task_runner.h"
+#include "base/synchronization/lock.h"
+#include "base/task/post_task.h"
+#include "base/task/task_traits.h"
+#include "base/test/bind_test_util.h"
+#include "base/test/task_environment.h"
+#include "base/threading/scoped_blocking_call.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/time/time.h"
+#include "net/base/net_errors.h"
+#include "net/base/test_completion_callback.h"
+#include "net/cert/cert_status_flags.h"
+#include "net/cert/cert_verifier.h"
+#include "net/cert/cert_verify_proc.h"
+#include "net/cert/cert_verify_proc_nss.h"
+#include "net/cert/cert_verify_result.h"
+#include "net/cert/multi_threaded_cert_verifier.h"
+#include "net/cert/test_root_certs.h"
+#include "net/cert/x509_certificate.h"
+#include "net/cert_net/nss_ocsp.h"
+#include "net/http/http_util.h"
+#include "net/log/net_log_with_source.h"
+#include "net/test/cert_test_util.h"
+#include "net/test/gtest_util.h"
+#include "net/test/test_data_directory.h"
+#include "net/test/test_with_task_environment.h"
+#include "services/network/public/cpp/url_loader_completion_status.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/public/mojom/url_response_head.mojom-forward.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using net::test::IsError;
+using net::test::IsOk;
+
+namespace cert_verifier {
+
+namespace {
+
+// Matches the caIssuers hostname from the generated certificate.
+const char kAiaHost[] = "aia-test.invalid";
+// Returning a single DER-encoded cert, so the mime-type must be
+// application/pkix-cert per RFC 5280.
+const char kAiaHeaders[] =
+    "HTTP/1.1 200 OK\0"
+    "Content-type: application/pkix-cert\0"
+    "\0";
+
+const char kMimeType[] = "application/pkix-cert";
+
+const char kDummyCertContents[] = "dummy_data";
+
+const GURL kInterceptUrl = GURL(std::string("http://") + kAiaHost);
+
+const base::TimeDelta kTimeout = base::TimeDelta::FromHours(1);
+
+network::mojom::URLResponseHeadPtr GetResponseHead() {
+  auto head = network::mojom::URLResponseHead::New();
+  head->headers = base::MakeRefCounted<net::HttpResponseHeaders>(
+      net::HttpUtil::AssembleRawHeaders(kAiaHeaders));
+  head->mime_type = kMimeType;
+  return head;
+}
+
+void GetIntermediateCertContents(std::string* file_contents) {
+  ASSERT_TRUE(base::ReadFileToString(
+      net::GetTestCertsDirectory().AppendASCII("aia-intermediate.der"),
+      file_contents));
+  ASSERT_FALSE(file_contents->empty());
+}
+
+}  // namespace
+
+class OCSPRequestSessionDelegateURLLoaderTest : public ::testing::Test {
+ public:
+  OCSPRequestSessionDelegateURLLoaderTest()
+      : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME),
+        loader_factory_(std::make_unique<network::TestURLLoaderFactory>()),
+        delegate_factory_(
+            std::make_unique<OCSPRequestSessionDelegateFactoryURLLoader>(
+                base::SequencedTaskRunnerHandle::Get(),
+                loader_factory_->GetSafeWeakWrapper())) {}
+
+  void SetUp() override {
+    params_.url = kInterceptUrl;
+    params_.http_request_method = "GET";
+    params_.timeout = kTimeout;
+
+    old_interceptor_ = base::BindLambdaForTesting(
+        [this](const network::ResourceRequest& request) {
+          EXPECT_EQ(request.url, kInterceptUrl);
+          num_loaders_created_++;
+        });
+    loader_factory_->SetInterceptor(old_interceptor_);
+  }
+
+ protected:
+  // Primes |loader_factory_| to respond with |head| and|file_contents| to
+  // |kInterceptUrl}.
+  void AddResponse(network::mojom::URLResponseHeadPtr head,
+                   std::string file_contents,
+                   network::TestURLLoaderFactory::Redirects redirects =
+                       network::TestURLLoaderFactory::Redirects()) {
+    loader_factory_->AddResponse(
+        kInterceptUrl, std::move(head), std::move(file_contents),
+        network::URLLoaderCompletionStatus(), std::move(redirects));
+  }
+
+  // Sets an interceptor for |loader_factory_| that will be run once and then be
+  // replaced with the old interceptor. The old interceptor will still run after
+  // this one.
+  void AddTemporaryInterceptor(
+      base::RepeatingCallback<void(const network::ResourceRequest& request)>
+          temp_interceptor) {
+    loader_factory_->SetInterceptor(base::BindLambdaForTesting(
+        [this, temp_interceptor = std::move(temp_interceptor)](
+            const network::ResourceRequest& request) {
+          temp_interceptor.Run(request);
+          old_interceptor_.Run(request);
+          loader_factory_->SetInterceptor(old_interceptor_);
+        }));
+  }
+
+  base::test::TaskEnvironment* task_environment() { return &task_environment_; }
+
+  int num_loaders_created() const { return num_loaders_created_; }
+
+  // Ensures that at least |n|+1 worker threads have been created, then
+  // returns the nth one.
+  scoped_refptr<base::SequencedTaskRunner> worker_thread(size_t n) {
+    while (worker_threads_.size() <= n) {
+      worker_threads_.push_back(base::CreateSequencedTaskRunner(
+          {base::ThreadPool(), base::MayBlock()}));
+    }
+    return worker_threads_[n];
+  }
+
+  net::OCSPRequestSessionParams* params() { return &params_; }
+
+  OCSPRequestSessionDelegateFactoryURLLoader* delegate_factory() {
+    return delegate_factory_.get();
+  }
+
+  void ResetDelegateFactory() { delegate_factory_.reset(); }
+  void ResetLoaderFactory() { loader_factory_.reset(); }
+
+ private:
+  base::test::TaskEnvironment task_environment_;
+
+  std::unique_ptr<network::TestURLLoaderFactory> loader_factory_;
+  int num_loaders_created_ = 0;
+
+  // Sequence that delegate->StartAndWait() is called on, that blocks.
+  std::vector<scoped_refptr<base::SequencedTaskRunner>> worker_threads_;
+
+  net::OCSPRequestSessionParams params_;
+
+  std::unique_ptr<OCSPRequestSessionDelegateFactoryURLLoader> delegate_factory_;
+
+  base::RepeatingCallback<void(const network::ResourceRequest& request)>
+      old_interceptor_;
+};
+
+// Tests that OCSPRequestSessionURLLoader will fail when asked to load HTTPS
+// URLs.
+TEST_F(OCSPRequestSessionDelegateURLLoaderTest, TestNoHttps) {
+  base::RunLoop run_loop;
+  worker_thread(0)->PostTask(
+      FROM_HERE, base::BindLambdaForTesting([&]() {
+        auto delegate = delegate_factory()->CreateOCSPRequestSessionDelegate();
+
+        std::unique_ptr<net::OCSPRequestSessionResult> result;
+        {
+          // Request a load from an HTTPS URL.
+          params()->url = GURL(std::string("https://") + kAiaHost);
+
+          base::ScopedAllowBaseSyncPrimitivesForTesting allow_base_sync;
+          result = delegate->StartAndWait(params());
+        }
+
+        ASSERT_FALSE(result);
+
+        run_loop.Quit();
+      }));
+  run_loop.Run();
+}
+
+// Tests that the timeout works correctly.
+TEST_F(OCSPRequestSessionDelegateURLLoaderTest, TestTimeout) {
+  scoped_refptr<net::OCSPRequestSessionDelegate> delegate;
+  OCSPRequestSessionDelegateURLLoader* delegate_ptr;
+
+  {
+    base::RunLoop run_loop;
+    worker_thread(0)->PostTask(
+        FROM_HERE, base::BindLambdaForTesting([&]() {
+          delegate = delegate_factory()->CreateOCSPRequestSessionDelegate();
+          delegate_ptr =
+              static_cast<OCSPRequestSessionDelegateURLLoader*>(delegate.get());
+
+          delegate_ptr->Start(params());
+
+          // Tell the main thread to continue once it has serviced the
+          // StartLoad() that was posted to it.
+          run_loop.QuitWhenIdle();
+        }));
+    run_loop.Run();
+  }
+
+  // Drain all the tasks from all the queues to make sure that the
+  // SimpleURLLoader has started its load and timeout.
+  task_environment()->FastForwardUntilNoTasksRemain();
+
+  {
+    base::RunLoop run_loop;
+    worker_thread(0)->PostTask(
+        FROM_HERE, base::BindLambdaForTesting([&]() {
+          std::unique_ptr<net::OCSPRequestSessionResult> result;
+          {
+            base::ScopedAllowBaseSyncPrimitivesForTesting allow_base_sync;
+            result = delegate_ptr->WaitForResult();
+          }
+
+          ASSERT_FALSE(result);
+
+          run_loop.Quit();
+        }));
+
+    // The load_sequence has started the SimpleURLLoader, so we can advance time
+    // by the timeout to cause the timeout to fire.
+    task_environment()->AdvanceClock(kTimeout);
+
+    run_loop.Run();
+  }
+
+  // Expect that a URLLoader was created by the SimpleURLLoader.
+  EXPECT_LT(0, num_loaders_created());
+}
+
+// Tests that a redirect to HTTPS causes a failure.
+TEST_F(OCSPRequestSessionDelegateURLLoaderTest, TestNoHttpsRedirect) {
+  // Add a redirect to an https url.
+  net::RedirectInfo redirect_info;
+  redirect_info.new_url = GURL(std::string("https://") + kAiaHost);
+  auto redirect_head = network::mojom::URLResponseHead::New();
+  redirect_head->headers = base::MakeRefCounted<net::HttpResponseHeaders>("");
+
+  network::TestURLLoaderFactory::Redirects redirects;
+  redirects.push_back({redirect_info, std::move(redirect_head)});
+
+  // Prime the loader to redirect to the https url.
+  AddResponse(GetResponseHead(), kDummyCertContents, std::move(redirects));
+
+  base::RunLoop run_loop;
+  worker_thread(0)->PostTask(
+      FROM_HERE, base::BindLambdaForTesting([&]() {
+        auto delegate = delegate_factory()->CreateOCSPRequestSessionDelegate();
+
+        std::unique_ptr<net::OCSPRequestSessionResult> result;
+        {
+          base::ScopedAllowBaseSyncPrimitivesForTesting allow_base_sync;
+          result = delegate->StartAndWait(params());
+        }
+
+        ASSERT_FALSE(result);
+
+        run_loop.Quit();
+      }));
+  run_loop.Run();
+
+  // This test should have failed when the redirect occurred, so a URLLoader
+  // must have been created already in order to even see the redirect.
+  EXPECT_LT(0, num_loaders_created());
+}
+
+TEST_F(OCSPRequestSessionDelegateURLLoaderTest, TestSuccessfulLoad) {
+  // Prime the loader to respond with our dummy cert contents.
+  AddResponse(GetResponseHead(), kDummyCertContents);
+
+  std::unique_ptr<net::OCSPRequestSessionResult> result;
+
+  base::RunLoop run_loop;
+  worker_thread(0)->PostTask(
+      FROM_HERE, base::BindLambdaForTesting([&]() {
+        auto delegate = delegate_factory()->CreateOCSPRequestSessionDelegate();
+
+        {
+          base::ScopedAllowBaseSyncPrimitivesForTesting allow_base_sync;
+          result = delegate->StartAndWait(params());
+
+          run_loop.Quit();
+        }
+      }));
+  run_loop.Run();
+
+  // We should have seen at least one loader created.
+  EXPECT_LT(0, num_loaders_created());
+
+  ASSERT_TRUE(result);
+
+  // Test that we received the correct response.
+  EXPECT_EQ(result->response_code, 200);
+  EXPECT_EQ(result->response_content_type, "application/pkix-cert");
+  EXPECT_EQ(result->data, kDummyCertContents);
+}
+
+TEST_F(OCSPRequestSessionDelegateURLLoaderTest,
+       TestSimultaneousDelegateFactory) {
+  // Prime the loader to respond with our dummy cert contents.
+  AddResponse(GetResponseHead(), kDummyCertContents);
+
+  constexpr int num_simultaneous = 5;
+  std::vector<std::unique_ptr<net::OCSPRequestSessionResult>> results(
+      num_simultaneous);
+
+  base::RunLoop run_loop;
+  base::RepeatingClosure barrier_closure =
+      base::BarrierClosure(num_simultaneous, run_loop.QuitClosure());
+
+  base::Lock delegate_factory_lock;
+
+  // Tell all the worker threads to create a delegate and call StartAndWait().
+  for (int i = 0; i < num_simultaneous; i++) {
+    worker_thread(i)->PostTask(
+        FROM_HERE, base::BindLambdaForTesting([&, i]() {
+          scoped_refptr<net::OCSPRequestSessionDelegate> delegate;
+          {
+            base::AutoLock autolock(delegate_factory_lock);
+            delegate = delegate_factory()->CreateOCSPRequestSessionDelegate();
+          }
+
+          {
+            base::ScopedAllowBaseSyncPrimitivesForTesting allow_base_sync;
+            // TODO(crbug.com/1038867): remove once this is resolved.
+            base::ScopedBlockingCall scoped_blocking_call(
+                FROM_HERE, base::BlockingType::WILL_BLOCK);
+
+            results[i] = delegate->StartAndWait(params());
+          }
+
+          barrier_closure.Run();
+        }));
+  }
+
+  // Wait for all the delegates for return from StartAndWait().
+  run_loop.Run();
+
+  // We should have seen at least |num_simultaneous| loaders created.
+  EXPECT_LE(num_simultaneous, num_loaders_created());
+
+  for (int i = 0; i < num_simultaneous; i++) {
+    ASSERT_TRUE(results[i]);
+
+    // Test that we received the correct response.
+    EXPECT_EQ(results[i]->response_code, 200);
+    EXPECT_EQ(results[i]->response_content_type, "application/pkix-cert");
+    EXPECT_EQ(results[i]->data, kDummyCertContents);
+  }
+}
+
+// Test that we can delete the delegate factory and its associated
+// TestURLLoaderFactory while the delegates are running.
+TEST_F(OCSPRequestSessionDelegateURLLoaderTest, TestDelegateFactoryDeletion) {
+  // Prime the loader to respond with our dummy cert contents.
+  AddResponse(GetResponseHead(), kDummyCertContents);
+
+  constexpr int num_simultaneous = 10;
+  std::vector<std::unique_ptr<net::OCSPRequestSessionResult>> results(
+      num_simultaneous);
+
+  // Quit() when at least one URLLoader has been created.
+  base::RunLoop run_loop1;
+  AddTemporaryInterceptor(base::BindRepeating(
+      [](base::RepeatingClosure quit_closure,
+         const network::ResourceRequest& request) { quit_closure.Run(); },
+      run_loop1.QuitClosure()));
+
+  // Quit() when each worker has either returned from delegate->WaitForResult()
+  // or will not start,
+  base::RunLoop run_loop2;
+  base::RepeatingClosure barrier_closure2 =
+      base::BarrierClosure(num_simultaneous, run_loop2.QuitClosure());
+
+  base::Lock delegate_factory_lock;
+
+  // Tell all the worker threads to create a delegate and call StartAndWait().
+  for (int i = 0; i < num_simultaneous; i++) {
+    worker_thread(i)->PostTask(
+        FROM_HERE, base::BindLambdaForTesting([&, i]() {
+          scoped_refptr<net::OCSPRequestSessionDelegate> delegate;
+          {
+            base::AutoLock autolock(delegate_factory_lock);
+            if (delegate_factory())
+              delegate = delegate_factory()->CreateOCSPRequestSessionDelegate();
+          }
+
+          if (delegate) {
+            base::ScopedAllowBaseSyncPrimitivesForTesting allow_base_sync;
+            // TODO(crbug.com/1038867): remove once this is resolved.
+            base::ScopedBlockingCall scoped_blocking_call(
+                FROM_HERE, base::BlockingType::WILL_BLOCK);
+
+            results[i] = delegate->StartAndWait(params());
+          }
+
+          barrier_closure2.Run();
+        }));
+  }
+
+  // Run until at least one URLLoader has been created.
+  run_loop1.Run();
+
+  {
+    base::AutoLock autolock(delegate_factory_lock);
+    // Should be okay to release the delegate factory at any time during
+    // execution.
+    ResetDelegateFactory();
+  }
+
+  // Once the delegate factory has been deleted, it should be okay to delete the
+  // TestURLLoaderFactory being used.
+  ResetLoaderFactory();
+
+  run_loop2.Run();
+
+  // Check that any results we received are actually correct.
+  for (int i = 0; i < num_simultaneous; i++) {
+    if (!results[i])
+      continue;
+
+    // Test that we received the correct response.
+    EXPECT_EQ(results[i]->response_code, 200);
+    EXPECT_EQ(results[i]->response_content_type, "application/pkix-cert");
+    EXPECT_EQ(results[i]->data, kDummyCertContents);
+  }
+}
+
+class NssHttpURLLoaderTest : public net::TestWithTaskEnvironment {
+ public:
+  NssHttpURLLoaderTest()
+      : kInterceptUrl(std::string("http://") + kAiaHost),
+        verify_proc_(new net::CertVerifyProcNSS),
+        verifier_(new net::MultiThreadedCertVerifier(verify_proc_.get())) {}
+  ~NssHttpURLLoaderTest() override = default;
+
+  void SetUp() override {
+    loader_factory_.SetInterceptor(base::BindLambdaForTesting(
+        [this](const network::ResourceRequest& request) {
+          EXPECT_EQ(request.url, kInterceptUrl);
+          num_loaders_created_++;
+        }));
+
+    net::SetOCSPRequestSessionDelegateFactory(
+        std::make_unique<OCSPRequestSessionDelegateFactoryURLLoader>(
+            base::SequencedTaskRunnerHandle::Get(),
+            loader_factory_.GetSafeWeakWrapper()));
+
+    test_cert_ =
+        net::ImportCertFromFile(net::GetTestCertsDirectory(), "aia-cert.pem");
+    ASSERT_TRUE(test_cert_.get());
+
+    test_root_ =
+        net::ImportCertFromFile(net::GetTestCertsDirectory(), "aia-root.pem");
+    ASSERT_TRUE(test_root_.get());
+
+    scoped_root_.Reset({test_root_.get()});
+  }
+
+  void TearDown() override {
+    net::SetOCSPRequestSessionDelegateFactory(nullptr);
+  }
+
+  net::CertVerifier* verifier() const { return verifier_.get(); }
+
+  int num_loaders_created() const { return num_loaders_created_; }
+
+  scoped_refptr<net::X509Certificate> test_cert() const { return test_cert_; }
+
+ protected:
+  // Primes |loader_factory_| to respond with |head| and|file_contents| to
+  // |kInterceptUrl}.
+  void AddResponse(network::mojom::URLResponseHeadPtr head,
+                   std::string file_contents) {
+    loader_factory_.AddResponse(kInterceptUrl, std::move(head),
+                                std::move(file_contents),
+                                network::URLLoaderCompletionStatus());
+  }
+
+ private:
+  const GURL kInterceptUrl;
+  scoped_refptr<net::X509Certificate> test_root_;
+  net::ScopedTestRoot scoped_root_;
+  scoped_refptr<net::X509Certificate> test_cert_;
+
+  network::TestURLLoaderFactory loader_factory_;
+  int num_loaders_created_ = 0;
+
+  scoped_refptr<net::CertVerifyProc> verify_proc_;
+  std::unique_ptr<net::CertVerifier> verifier_;
+};
+
+// Tests that when using NSS to verify certificates that a request to fetch
+// missing intermediate certificates is made successfully.
+TEST_F(NssHttpURLLoaderTest, TestAia) {
+  // Prime |loader_factory_| to return the intermediate cert.
+  std::string cert_contents;
+  GetIntermediateCertContents(&cert_contents);
+  AddResponse(GetResponseHead(), std::move(cert_contents));
+
+  net::CertVerifyResult verify_result;
+  net::TestCompletionCallback test_callback;
+  std::unique_ptr<net::CertVerifier::Request> request;
+
+  int flags = 0;
+  int error = verifier()->Verify(
+      net::CertVerifier::RequestParams(test_cert(), "aia-host.invalid", flags,
+                                       /*ocsp_response=*/std::string(),
+                                       /*sct_list=*/std::string()),
+      &verify_result, test_callback.callback(), &request,
+      net::NetLogWithSource());
+  ASSERT_THAT(error, IsError(net::ERR_IO_PENDING));
+
+  error = test_callback.WaitForResult();
+
+  EXPECT_THAT(error, IsOk());
+
+  // Ensure that NSS made an AIA request for the missing intermediate.
+  EXPECT_LT(0, num_loaders_created());
+}
+
+}  // namespace cert_verifier
diff --git a/services/network/public/cpp/cors/cors_unittest.cc b/services/network/public/cpp/cors/cors_unittest.cc
index ffcf50c..111bd343 100644
--- a/services/network/public/cpp/cors/cors_unittest.cc
+++ b/services/network/public/cpp/cors/cors_unittest.cc
@@ -731,6 +731,19 @@
   EXPECT_FALSE(IsPrivilegedNoCorsHeaderName("cookie"));
 }
 
+TEST_F(CorsTest, IsForbiddenMethod) {
+  EXPECT_TRUE(IsForbiddenMethod("connect"));
+  EXPECT_TRUE(IsForbiddenMethod("CONNECT"));
+  EXPECT_TRUE(IsForbiddenMethod("Connect"));
+  EXPECT_TRUE(IsForbiddenMethod("CoNnEcT"));
+  EXPECT_FALSE(IsForbiddenMethod("C0NNECT"));
+
+  EXPECT_TRUE(IsForbiddenMethod("trace"));
+  EXPECT_TRUE(IsForbiddenMethod("track"));
+  EXPECT_FALSE(IsForbiddenMethod("trac"));
+  EXPECT_FALSE(IsForbiddenMethod("tracz"));
+}
+
 }  // namespace
 }  // namespace cors
 }  // namespace network
diff --git a/skia/config/SkUserConfig.h b/skia/config/SkUserConfig.h
index 94e9588a..7874c294 100644
--- a/skia/config/SkUserConfig.h
+++ b/skia/config/SkUserConfig.h
@@ -211,6 +211,10 @@
 #define SK_DISABLE_REDUCE_OPLIST_SPLITTING
 #endif
 
+#ifndef SK_SUPPORT_LEGACY_CANVAS_MATRIX_33
+#define SK_SUPPORT_LEGACY_CANVAS_MATRIX_33
+#endif
+
 #ifndef SK_IGNORE_LINEONLY_AA_CONVEX_PATH_OPTS
 #define SK_IGNORE_LINEONLY_AA_CONVEX_PATH_OPTS
 #endif
diff --git a/storage/browser/quota/client_usage_tracker.cc b/storage/browser/quota/client_usage_tracker.cc
index 5a99a70..0d2a62c 100644
--- a/storage/browser/quota/client_usage_tracker.cc
+++ b/storage/browser/quota/client_usage_tracker.cc
@@ -61,6 +61,12 @@
 
 }  // namespace
 
+struct ClientUsageTracker::AccumulateInfo {
+  size_t pending_jobs = 0;
+  int64_t limited_usage = 0;
+  int64_t unlimited_usage = 0;
+};
+
 ClientUsageTracker::ClientUsageTracker(
     UsageTracker* tracker,
     scoped_refptr<QuotaClient> client,
diff --git a/storage/browser/quota/client_usage_tracker.h b/storage/browser/quota/client_usage_tracker.h
index 4c8fd97b..bd6ffdfb 100644
--- a/storage/browser/quota/client_usage_tracker.h
+++ b/storage/browser/quota/client_usage_tracker.h
@@ -68,11 +68,7 @@
  private:
   using UsageMap = std::map<url::Origin, int64_t>;
 
-  struct AccumulateInfo {
-    size_t pending_jobs = 0;
-    int64_t limited_usage = 0;
-    int64_t unlimited_usage = 0;
-  };
+  struct AccumulateInfo;
 
   void AccumulateLimitedOriginUsage(AccumulateInfo* info,
                                     UsageCallback callback,
diff --git a/styleguide/python/python.md b/styleguide/python/python.md
index 63d72c9..f56902d 100644
--- a/styleguide/python/python.md
+++ b/styleguide/python/python.md
@@ -53,7 +53,7 @@
 file with the following contents:
 ```
 [style]
-based_on_style = chromium
+based_on_style = pep8
 ```
 
 Entire files can be formatted (rather than just touched lines) via:
diff --git a/testing/buildbot/chromium.perf.fyi.json b/testing/buildbot/chromium.perf.fyi.json
index a82660e..89bc4cec 100644
--- a/testing/buildbot/chromium.perf.fyi.json
+++ b/testing/buildbot/chromium.perf.fyi.json
@@ -9,8 +9,7 @@
           "--browser=android-chrome",
           "--upload-results",
           "--test-shard-map-filename=android-nexus5x-perf-fyi_map.json",
-          "--output-format=histograms",
-          "--experimental-proto-trace-format"
+          "--output-format=histograms"
         ],
         "isolate_name": "performance_test_suite",
         "merge": {
@@ -195,7 +194,6 @@
           "--upload-results",
           "--test-shard-map-filename=linux-perf-fyi_map.json",
           "--output-format=histograms",
-          "--experimental-proto-trace-format",
           "--experimental-tbmv3-metrics"
         ],
         "isolate_name": "performance_test_suite",
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 3465473b..067e4c24 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -4108,9 +4108,7 @@
             "experiments": [
                 {
                     "name": "iOSExperiments",
-                    "enable_features": [
-                        "OmniboxPopupShortcutIconsInZeroState"
-                    ]
+                    "enable_features": []
                 }
             ]
         }
diff --git a/third_party/blink/renderer/bindings/core/v8/local_window_proxy.h b/third_party/blink/renderer/bindings/core/v8/local_window_proxy.h
index a763291..5a326af 100644
--- a/third_party/blink/renderer/bindings/core/v8/local_window_proxy.h
+++ b/third_party/blink/renderer/bindings/core/v8/local_window_proxy.h
@@ -37,6 +37,7 @@
 #include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/casting.h"
 #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
 #include "v8/include/v8.h"
 
@@ -111,11 +112,12 @@
   Member<ScriptState> script_state_;
 };
 
-DEFINE_TYPE_CASTS(LocalWindowProxy,
-                  WindowProxy,
-                  windowProxy,
-                  windowProxy->IsLocal(),
-                  windowProxy.IsLocal());
+template <>
+struct DowncastTraits<LocalWindowProxy> {
+  static bool AllowFrom(const WindowProxy& windowProxy) {
+    return windowProxy.IsLocal();
+  }
+};
 
 }  // namespace blink
 
diff --git a/third_party/blink/renderer/bindings/modules/v8/generated.gni b/third_party/blink/renderer/bindings/modules/v8/generated.gni
index a96b7b6..33e9f7a 100644
--- a/third_party/blink/renderer/bindings/modules/v8/generated.gni
+++ b/third_party/blink/renderer/bindings/modules/v8/generated.gni
@@ -51,6 +51,8 @@
   "$bindings_modules_v8_output_dir/worklet_animation_effect_or_worklet_group_effect.h",
   "$bindings_modules_v8_output_dir/float32_array_or_float64_array_or_dom_matrix.cc",
   "$bindings_modules_v8_output_dir/float32_array_or_float64_array_or_dom_matrix.h",
+  "$bindings_modules_v8_output_dir/gpu_buffer_or_array_buffer.cc",
+  "$bindings_modules_v8_output_dir/gpu_buffer_or_array_buffer.h",
   "$bindings_modules_v8_output_dir/gpu_out_of_memory_error_or_gpu_validation_error.cc",
   "$bindings_modules_v8_output_dir/gpu_out_of_memory_error_or_gpu_validation_error.h",
   "$bindings_modules_v8_output_dir/gpu_load_op_or_double_sequence_or_gpu_color_dict.cc",
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/codegen_utils.py b/third_party/blink/renderer/bindings/scripts/bind_gen/codegen_utils.py
index 7831d11..c243a8c 100644
--- a/third_party/blink/renderer/bindings/scripts/bind_gen/codegen_utils.py
+++ b/third_party/blink/renderer/bindings/scripts/bind_gen/codegen_utils.py
@@ -149,7 +149,7 @@
 
     traverse_idl_types(idl_definition, collect_type_def_obj)
 
-    header_paths = set()
+    header_paths = set(idl_definition.code_generator_info.blink_headers or [])
     for type_def_obj in type_def_objs:
         if isinstance(type_def_obj, web_idl.Enumeration):
             continue
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py b/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
index dbe02e9..0d721c2 100644
--- a/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
+++ b/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
@@ -407,6 +407,12 @@
     if "ExecutionContext" in values:
         arguments.append("${execution_context}")
 
+    code_generator_info = cg_context.member_like.code_generator_info
+    is_partial = code_generator_info.defined_in_partial
+    if (is_partial and
+            not (cg_context.constructor or cg_context.member_like.is_static)):
+        arguments.append("*${blink_receiver}")
+
     if "Reflect" in ext_attrs:  # [Reflect]
         key = _make_reflect_content_attribute_key(code_node, cg_context)
         if key:
@@ -429,10 +435,8 @@
     if cg_context.may_throw_exception:
         arguments.append("${exception_state}")
 
-    code_generator_info = cg_context.member_like.code_generator_info
-
     func_name = (code_generator_info.property_implemented_as
-                 or name_style.api_func(cg_context.member_like.identifier))
+                 or cg_context.member_like.identifier)
     if cg_context.attribute_set:
         func_name = name_style.api_func("set", func_name)
     if cg_context.constructor:
@@ -440,16 +444,12 @@
     if "Reflect" in ext_attrs:  # [Reflect]
         func_name = _make_reflect_accessor_func_name(cg_context)
 
-    is_partial_or_mixin = (code_generator_info.defined_in_partial
-                           or code_generator_info.defined_in_mixin)
     if (cg_context.constructor or cg_context.member_like.is_static
-            or is_partial_or_mixin):
+            or is_partial):
         class_like = cg_context.member_like.owner_mixin or cg_context.class_like
         class_name = (code_generator_info.receiver_implemented_as
                       or name_style.class_(class_like.identifier))
         func_designator = "{}::{}".format(class_name, func_name)
-        if not (cg_context.constructor or cg_context.member_like.is_static):
-            arguments.insert(0, "*${blink_receiver}")
     else:
         func_designator = _format("${blink_receiver}->{}", func_name)
 
@@ -640,8 +640,14 @@
 def make_cooperative_scheduling_safepoint(cg_context):
     assert isinstance(cg_context, CodeGenContext)
 
-    return TextNode("scheduler::CooperativeSchedulingManager::Instance()"
+    node = TextNode("scheduler::CooperativeSchedulingManager::Instance()"
                     "->Safepoint();")
+    node.accumulate(
+        CodeGenAccumulator.require_include_headers([
+            "third_party/blink/renderer/platform/scheduler/public/"
+            "cooperative_scheduling_manager.h"
+        ]))
+    return node
 
 
 def make_log_activity(cg_context):
@@ -1028,7 +1034,7 @@
 static const V8PrivateProperty::SymbolKey kPrivatePropertyCachedAttribute;
 auto v8_private_cached_attribute =
     V8PrivateProperty::GetSymbol(${isolate}, kPrivatePropertyCachedAttribute);
-if (!impl->""" + pred + """()) {
+if (!${blink_receiver}->""" + pred + """()) {
   v8::Local<v8::Value> v8_value;
   if (v8_private_cached_attribute.GetOrUndefined(${v8_receiver})
           .ToLocal(&v8_value) && !v8_value->IsUndefined()) {
@@ -1083,22 +1089,18 @@
     counter = target.extended_attributes.value_of("RuntimeCallStatsCounter")
     if counter:
         macro_name = "RUNTIME_CALL_TIMER_SCOPE"
-        counter_name = "k{}{}".format(counter, suffix)
+        counter_name = "RuntimeCallStats::CounterId::k{}{}".format(
+            counter, suffix)
     else:
         macro_name = "RUNTIME_CALL_TIMER_SCOPE_DISABLED_BY_DEFAULT"
         counter_name = "\"Blink_{}_{}{}\"".format(
             blink_class_name(cg_context.class_like), target.identifier, suffix)
 
-    node = TextNode(
+    return TextNode(
         _format(
-            "{macro_name}(${isolate}, {counter_name});",
+            "{macro_name}(${info}.GetIsolate(), {counter_name});",
             macro_name=macro_name,
             counter_name=counter_name))
-    node.accumulate(
-        CodeGenAccumulator.require_include_headers([
-            "third_party/blink/renderer/platform/bindings/runtime_call_stats.h",
-        ]))
-    return node
 
 
 def make_steps_of_ce_reactions(cg_context):
@@ -1123,7 +1125,17 @@
         nodes.append(T("// [CEReactions]"))
         nodes.append(T("CEReactionsScope ce_reactions_scope;"))
 
-    return SequenceNode(nodes) if nodes else None
+    if not nodes:
+        return None
+
+    nodes = SequenceNode(nodes)
+    nodes.accumulate(
+        CodeGenAccumulator.require_include_headers([
+            "third_party/blink/renderer/core/html/custom/ce_reactions_scope.h",
+            "third_party/blink/renderer/core/html/custom/"
+            "v0_custom_element_processing_stack.h"
+        ]))
+    return nodes
 
 
 def make_steps_of_put_forwards(cg_context):
@@ -1182,7 +1194,9 @@
         # any text.
         return T("<% return_value.request_symbol_definition() %>")
 
-    return_type_body = cg_context.return_type.unwrap()
+    return_type = cg_context.return_type.unwrap(typedef=True)
+    return_type_body = return_type.unwrap()
+
     if (cg_context.for_world == cg_context.MAIN_WORLD
             and return_type_body.is_interface):
         return T("V8SetReturnValueForMainWorld(${info}, ${return_value});")
@@ -1191,6 +1205,21 @@
         return T("V8SetReturnValue(${info}, ${return_value}, "
                  "${creation_context_object});")
 
+    if return_type_body.is_string:
+        if return_type.is_nullable:
+            return T("V8SetReturnValueStringOrNull"
+                     "(${info}, ${return_value}, ${isolate});")
+        else:
+            return T("V8SetReturnValueString"
+                     "(${info}, ${return_value}, ${isolate});")
+
+    if return_type.is_frozen_array:
+        return T("V8SetReturnValue(${info}, FreezeV8Object(ToV8("
+                 "${return_value}, ${v8_receiver}, ${isolate}), ${isolate}));")
+
+    if return_type.is_promise:
+        return T("V8SetReturnValue(${info}, ${return_value}.V8Value());")
+
     return T("V8SetReturnValue(${info}, ${return_value});")
 
 
@@ -1529,7 +1558,7 @@
         S("is_in_secure_context",
           ("const bool ${is_in_secure_context} = "
            "${execution_context}->IsSecureContext();")),
-        S("isolate", "v8::Isolate* ${isolate} = ${v8_context}.GetIsolate();"),
+        S("isolate", "v8::Isolate* ${isolate} = ${v8_context}->GetIsolate();"),
         S("prototype_template",
           ("v8::Local<v8::ObjectTemplate> ${prototype_template} = "
            "${interface_template}->PrototypeTemplate();")),
@@ -2728,7 +2757,12 @@
             component_export_header(impl_component),
         ])
     impl_source_node.accumulator.add_include_headers([
-        path_manager.blink_path(ext="h"),
+        "third_party/blink/renderer/bindings/core/v8/"
+        "native_value_traits_impl.h",
+        "third_party/blink/renderer/bindings/core/v8/v8_dom_configuration.h",
+        "third_party/blink/renderer/platform/bindings/exception_messages.h",
+        "third_party/blink/renderer/platform/bindings/runtime_call_stats.h",
+        "third_party/blink/renderer/platform/bindings/v8_binding.h",
     ])
     impl_source_node.accumulator.add_include_headers(
         collect_include_headers(interface))
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/idl_compiler.py b/third_party/blink/renderer/bindings/scripts/web_idl/idl_compiler.py
index 38479b7..67cbc21 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/idl_compiler.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/idl_compiler.py
@@ -6,6 +6,8 @@
 import itertools
 import posixpath
 
+from blinkbuild.name_style_converter import NameStyleConverter
+
 from .callback_function import CallbackFunction
 from .callback_interface import CallbackInterface
 from .composition_parts import Identifier
@@ -225,7 +227,12 @@
             self._ir_map.add(new_ir)
             basepath, _ = posixpath.splitext(
                 new_ir.debug_info.location.filepath)
-            header = posixpath.extsep.join([basepath, "h"])
+            dirpath, filename = posixpath.split(basepath)
+            impl_class = new_ir.extended_attributes.value_of("ImplementedAs")
+            if impl_class:
+                filename = NameStyleConverter(impl_class).to_snake_case()
+            header = posixpath.join(dirpath,
+                                    posixpath.extsep.join([filename, "h"]))
             new_ir.code_generator_info.set_blink_headers([header])
 
     def _merge_partial_interface_likes(self):
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/namespace.py b/third_party/blink/renderer/bindings/scripts/web_idl/namespace.py
index 181b65b5..275d356 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/namespace.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/namespace.py
@@ -140,3 +140,8 @@
     def operation_groups(self):
         """Returns a list of OperationGroups."""
         return self._operation_groups
+
+    @property
+    def exposed_constructs(self):
+        """Returns exposed constructs."""
+        return ()
diff --git a/third_party/blink/renderer/build/scripts/core/css/properties/templates/css_properties.cc.tmpl b/third_party/blink/renderer/build/scripts/core/css/properties/templates/css_properties.cc.tmpl
index 91d9eb3..04f8ea0 100644
--- a/third_party/blink/renderer/build/scripts/core/css/properties/templates/css_properties.cc.tmpl
+++ b/third_party/blink/renderer/build/scripts/core/css/properties/templates/css_properties.cc.tmpl
@@ -36,7 +36,7 @@
 {% set is_alias = property.alias_for %}
 // {{property.name}}
 
-  {% if property.is_internal or property.runtime_flag %}
+  {% if property.is_internal or property.runtime_flag and not property.in_origin_trial%}
 CSSExposure {{class_name}}::Exposure() const {
     {% if property.runtime_flag %}
   if (!RuntimeEnabledFeatures::{{property.runtime_flag}}Enabled())
@@ -50,7 +50,7 @@
 }
   {% endif %}
 
-  {% if property.origin_trial %}
+  {% if property.in_origin_trial %}
 CSSExposure {{class_name}}::Exposure(const ExecutionContext* execution_context) const {
   if (!RuntimeEnabledFeatures::{{property.runtime_flag}}Enabled(execution_context))
     return CSSExposure::kNone;
diff --git a/third_party/blink/renderer/build/scripts/core/css/properties/templates/css_properties.h.tmpl b/third_party/blink/renderer/build/scripts/core/css/properties/templates/css_properties.h.tmpl
index 2e5a839..20d6d09 100644
--- a/third_party/blink/renderer/build/scripts/core/css/properties/templates/css_properties.h.tmpl
+++ b/third_party/blink/renderer/build/scripts/core/css/properties/templates/css_properties.h.tmpl
@@ -51,7 +51,7 @@
   const char* GetPropertyName() const override;
   const WTF::AtomicString& GetPropertyNameAtomicString() const override;
   const char* GetJSPropertyName() const override;
-  {% if property.is_internal or property.runtime_flag %}
+  {% if property.is_internal or property.runtime_flag and not property.in_origin_trial%}
   CSSExposure Exposure() const override;
   {% endif %}
   {% if property.in_origin_trial %}
diff --git a/third_party/blink/renderer/build/scripts/core/css/templates/css_property_names.cc.tmpl b/third_party/blink/renderer/build/scripts/core/css/templates/css_property_names.cc.tmpl
index e25a788..e85ab80 100644
--- a/third_party/blink/renderer/build/scripts/core/css/templates/css_property_names.cc.tmpl
+++ b/third_party/blink/renderer/build/scripts/core/css/templates/css_property_names.cc.tmpl
@@ -40,12 +40,6 @@
   return {{class_name}}Hash::findPropertyImpl(str, len);
 }
 
-CSSPropertyID cssPropertyID(const String& string)
-{
-    // TODO(rodneyding): remove temporary stopgap
-    return resolveCSSPropertyID(unresolvedCSSPropertyID(nullptr, string));
-}
-
 CSSPropertyID cssPropertyID(const ExecutionContext* execution_context, const String& string)
 {
     return resolveCSSPropertyID(unresolvedCSSPropertyID(execution_context, string));
diff --git a/third_party/blink/renderer/build/scripts/core/css/templates/css_property_names.h.tmpl b/third_party/blink/renderer/build/scripts/core/css/templates/css_property_names.h.tmpl
index 8dcf239..da7252f 100644
--- a/third_party/blink/renderer/build/scripts/core/css/templates/css_property_names.h.tmpl
+++ b/third_party/blink/renderer/build/scripts/core/css/templates/css_property_names.h.tmpl
@@ -70,7 +70,6 @@
 
 CSSPropertyID CORE_EXPORT unresolvedCSSPropertyID(const ExecutionContext*, const WTF::String&);
 
-CSSPropertyID CORE_EXPORT cssPropertyID(const WTF::String&);
 CSSPropertyID CORE_EXPORT cssPropertyID(const ExecutionContext*, const WTF::String&);
 
 class CSSPropertyIDList {
diff --git a/third_party/blink/renderer/core/animation/basic_shape_interpolation_functions.cc b/third_party/blink/renderer/core/animation/basic_shape_interpolation_functions.cc
index 9cecc2c..ed8acf1 100644
--- a/third_party/blink/renderer/core/animation/basic_shape_interpolation_functions.cc
+++ b/third_party/blink/renderer/core/animation/basic_shape_interpolation_functions.cc
@@ -69,7 +69,15 @@
 };
 
 DEFINE_NON_INTERPOLABLE_VALUE_TYPE(BasicShapeNonInterpolableValue);
-DEFINE_NON_INTERPOLABLE_VALUE_TYPE_CASTS(BasicShapeNonInterpolableValue);
+template <>
+struct DowncastTraits<BasicShapeNonInterpolableValue> {
+  static bool AllowFrom(const NonInterpolableValue* value) {
+    return value && AllowFrom(*value);
+  }
+  static bool AllowFrom(const NonInterpolableValue& value) {
+    return value.GetType() == BasicShapeNonInterpolableValue::static_type_;
+  }
+};
 
 namespace {
 
@@ -538,8 +546,8 @@
 std::unique_ptr<InterpolableValue>
 basic_shape_interpolation_functions::CreateNeutralValue(
     const NonInterpolableValue& untyped_non_interpolable_value) {
-  const BasicShapeNonInterpolableValue& non_interpolable_value =
-      ToBasicShapeNonInterpolableValue(untyped_non_interpolable_value);
+  const auto& non_interpolable_value =
+      To<BasicShapeNonInterpolableValue>(untyped_non_interpolable_value);
   switch (non_interpolable_value.GetShapeType()) {
     case BasicShape::kBasicShapeCircleType:
       return circle_functions::CreateNeutralValue();
@@ -558,16 +566,16 @@
 bool basic_shape_interpolation_functions::ShapesAreCompatible(
     const NonInterpolableValue& a,
     const NonInterpolableValue& b) {
-  return ToBasicShapeNonInterpolableValue(a).IsCompatibleWith(
-      ToBasicShapeNonInterpolableValue(b));
+  return To<BasicShapeNonInterpolableValue>(a).IsCompatibleWith(
+      To<BasicShapeNonInterpolableValue>(b));
 }
 
 scoped_refptr<BasicShape> basic_shape_interpolation_functions::CreateBasicShape(
     const InterpolableValue& interpolable_value,
     const NonInterpolableValue& untyped_non_interpolable_value,
     const CSSToLengthConversionData& conversion_data) {
-  const BasicShapeNonInterpolableValue& non_interpolable_value =
-      ToBasicShapeNonInterpolableValue(untyped_non_interpolable_value);
+  const auto& non_interpolable_value =
+      To<BasicShapeNonInterpolableValue>(untyped_non_interpolable_value);
   switch (non_interpolable_value.GetShapeType()) {
     case BasicShape::kBasicShapeCircleType:
       return circle_functions::CreateBasicShape(interpolable_value,
diff --git a/third_party/blink/renderer/core/animation/css_border_image_length_box_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_border_image_length_box_interpolation_type.cc
index 8da39b29f..22e7218 100644
--- a/third_party/blink/renderer/core/animation/css_border_image_length_box_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_border_image_length_box_interpolation_type.cc
@@ -98,8 +98,16 @@
 
 DEFINE_NON_INTERPOLABLE_VALUE_TYPE(
     CSSBorderImageLengthBoxSideNonInterpolableValue);
-DEFINE_NON_INTERPOLABLE_VALUE_TYPE_CASTS(
-    CSSBorderImageLengthBoxSideNonInterpolableValue);
+template <>
+struct DowncastTraits<CSSBorderImageLengthBoxSideNonInterpolableValue> {
+  static bool AllowFrom(const NonInterpolableValue* value) {
+    return value && AllowFrom(*value);
+  }
+  static bool AllowFrom(const NonInterpolableValue& value) {
+    return value.GetType() ==
+           CSSBorderImageLengthBoxSideNonInterpolableValue::static_type_;
+  }
+};
 
 namespace {
 
@@ -134,9 +142,11 @@
   // In cases where LengthInterpolationFunctions is not used to convert the
   // value (kAuto, kNumber), we will always have a non-interpolable value of
   // type CSSBorderImageLengthBoxSideNonInterpolableValue.
-  if (!side || !IsCSSBorderImageLengthBoxSideNonInterpolableValue(side))
+  auto* non_interpolable =
+      DynamicTo<CSSBorderImageLengthBoxSideNonInterpolableValue>(side);
+  if (!side || !non_interpolable)
     return SideType::kLength;
-  return ToCSSBorderImageLengthBoxSideNonInterpolableValue(*side).GetSideType();
+  return non_interpolable->GetSideType();
 }
 
 struct SideTypes {
@@ -154,7 +164,7 @@
   }
   explicit SideTypes(const InterpolationValue& underlying) {
     const auto& non_interpolable_list =
-        ToNonInterpolableList(*underlying.non_interpolable_value);
+        To<NonInterpolableList>(*underlying.non_interpolable_value);
     DCHECK_EQ(kSideIndexCount, non_interpolable_list.length());
     type[kSideTop] = GetSideType(non_interpolable_list.Get(0));
     type[kSideRight] = GetSideType(non_interpolable_list.Get(1));
@@ -376,8 +386,8 @@
     const NonInterpolableValue* non_interpolable_value,
     StyleResolverState& state) const {
   const auto& list = To<InterpolableList>(interpolable_value);
-  const NonInterpolableList& non_interpolable_list =
-      ToNonInterpolableList(*non_interpolable_value);
+  const auto& non_interpolable_list =
+      To<NonInterpolableList>(*non_interpolable_value);
   const auto& convert_side = [&list, &non_interpolable_list,
                               &state](wtf_size_t index) -> BorderImageLength {
     switch (GetSideType(non_interpolable_list.Get(index))) {
diff --git a/third_party/blink/renderer/core/animation/css_clip_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_clip_interpolation_type.cc
index 28bd63a..1a816361 100644
--- a/third_party/blink/renderer/core/animation/css_clip_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_clip_interpolation_type.cc
@@ -111,7 +111,15 @@
 };
 
 DEFINE_NON_INTERPOLABLE_VALUE_TYPE(CSSClipNonInterpolableValue);
-DEFINE_NON_INTERPOLABLE_VALUE_TYPE_CASTS(CSSClipNonInterpolableValue);
+template <>
+struct DowncastTraits<CSSClipNonInterpolableValue> {
+  static bool AllowFrom(const NonInterpolableValue* value) {
+    return value && AllowFrom(*value);
+  }
+  static bool AllowFrom(const NonInterpolableValue& value) {
+    return value.GetType() == CSSClipNonInterpolableValue::static_type_;
+  }
+};
 
 class UnderlyingAutosChecker
     : public CSSInterpolationType::CSSConversionChecker {
@@ -123,7 +131,7 @@
   static ClipAutos GetUnderlyingAutos(const InterpolationValue& underlying) {
     if (!underlying)
       return ClipAutos();
-    return ToCSSClipNonInterpolableValue(*underlying.non_interpolable_value)
+    return To<CSSClipNonInterpolableValue>(*underlying.non_interpolable_value)
         .GetClipAutos();
   }
 
@@ -238,11 +246,12 @@
 PairwiseInterpolationValue CSSClipInterpolationType::MaybeMergeSingles(
     InterpolationValue&& start,
     InterpolationValue&& end) const {
-  const ClipAutos& start_autos =
-      ToCSSClipNonInterpolableValue(*start.non_interpolable_value)
+  const auto& start_autos =
+      To<CSSClipNonInterpolableValue>(*start.non_interpolable_value)
           .GetClipAutos();
-  const ClipAutos& end_autos =
-      ToCSSClipNonInterpolableValue(*end.non_interpolable_value).GetClipAutos();
+  const auto& end_autos =
+      To<CSSClipNonInterpolableValue>(*end.non_interpolable_value)
+          .GetClipAutos();
   if (start_autos != end_autos)
     return nullptr;
   return PairwiseInterpolationValue(std::move(start.interpolable_value),
@@ -255,12 +264,12 @@
     double underlying_fraction,
     const InterpolationValue& value,
     double interpolation_fraction) const {
-  const ClipAutos& underlying_autos =
-      ToCSSClipNonInterpolableValue(
+  const auto& underlying_autos =
+      To<CSSClipNonInterpolableValue>(
           *underlying_value_owner.Value().non_interpolable_value)
           .GetClipAutos();
-  const ClipAutos& autos =
-      ToCSSClipNonInterpolableValue(*value.non_interpolable_value)
+  const auto& autos =
+      To<CSSClipNonInterpolableValue>(*value.non_interpolable_value)
           .GetClipAutos();
   if (underlying_autos == autos)
     underlying_value_owner.MutableValue().interpolable_value->ScaleAndAdd(
@@ -273,8 +282,8 @@
     const InterpolableValue& interpolable_value,
     const NonInterpolableValue* non_interpolable_value,
     StyleResolverState& state) const {
-  const ClipAutos& autos =
-      ToCSSClipNonInterpolableValue(non_interpolable_value)->GetClipAutos();
+  const auto& autos =
+      To<CSSClipNonInterpolableValue>(non_interpolable_value)->GetClipAutos();
   const auto& list = To<InterpolableList>(interpolable_value);
   const auto& convert_index = [&list, &state](bool is_auto, wtf_size_t index) {
     if (is_auto)
diff --git a/third_party/blink/renderer/core/animation/css_custom_list_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_custom_list_interpolation_type.cc
index 61ded28..509839e 100644
--- a/third_party/blink/renderer/core/animation/css_custom_list_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_custom_list_interpolation_type.cc
@@ -57,9 +57,8 @@
     const NonInterpolableValue* non_interpolable_value,
     const StyleResolverState& state) const {
   const auto& interpolable_list = To<InterpolableList>(interpolable_value);
-  const NonInterpolableList* non_interpolable_list =
-      non_interpolable_value ? &ToNonInterpolableList(*non_interpolable_value)
-                             : nullptr;
+  const auto* non_interpolable_list =
+      DynamicTo<NonInterpolableList>(*non_interpolable_value);
 
   CSSValueList* list = nullptr;
 
diff --git a/third_party/blink/renderer/core/animation/css_default_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_default_interpolation_type.cc
index 146bb44..f6de4f0 100644
--- a/third_party/blink/renderer/core/animation/css_default_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_default_interpolation_type.cc
@@ -45,11 +45,12 @@
     const InterpolableValue&,
     const NonInterpolableValue* non_interpolable_value,
     InterpolationEnvironment& environment) const {
-  DCHECK(ToCSSDefaultNonInterpolableValue(non_interpolable_value)->CssValue());
+  DCHECK(
+      To<CSSDefaultNonInterpolableValue>(non_interpolable_value)->CssValue());
   StyleBuilder::ApplyProperty(
       GetProperty().GetCSSPropertyName(),
       To<CSSInterpolationEnvironment>(environment).GetState(),
-      *ToCSSDefaultNonInterpolableValue(non_interpolable_value)->CssValue());
+      *To<CSSDefaultNonInterpolableValue>(non_interpolable_value)->CssValue());
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/animation/css_default_interpolation_type.h b/third_party/blink/renderer/core/animation/css_default_interpolation_type.h
index 941b2ca..501fc9c 100644
--- a/third_party/blink/renderer/core/animation/css_default_interpolation_type.h
+++ b/third_party/blink/renderer/core/animation/css_default_interpolation_type.h
@@ -30,7 +30,15 @@
   Persistent<const CSSValue> css_value_;
 };
 
-DEFINE_NON_INTERPOLABLE_VALUE_TYPE_CASTS(CSSDefaultNonInterpolableValue);
+template <>
+struct DowncastTraits<CSSDefaultNonInterpolableValue> {
+  static bool AllowFrom(const NonInterpolableValue* value) {
+    return value && AllowFrom(*value);
+  }
+  static bool AllowFrom(const NonInterpolableValue& value) {
+    return value.GetType() == CSSDefaultNonInterpolableValue::static_type_;
+  }
+};
 
 // Never supports pairwise conversion while always supporting single conversion.
 // A catch all default for CSSValue interpolation.
diff --git a/third_party/blink/renderer/core/animation/css_font_variation_settings_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_font_variation_settings_interpolation_type.cc
index 5d5cef7..1db1e64 100644
--- a/third_party/blink/renderer/core/animation/css_font_variation_settings_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_font_variation_settings_interpolation_type.cc
@@ -41,12 +41,21 @@
 
 DEFINE_NON_INTERPOLABLE_VALUE_TYPE(
     CSSFontVariationSettingsNonInterpolableValue);
-DEFINE_NON_INTERPOLABLE_VALUE_TYPE_CASTS(
-    CSSFontVariationSettingsNonInterpolableValue);
+template <>
+struct DowncastTraits<CSSFontVariationSettingsNonInterpolableValue> {
+  static bool AllowFrom(const NonInterpolableValue* value) {
+    return value && AllowFrom(*value);
+  }
+  static bool AllowFrom(const NonInterpolableValue& value) {
+    return value.GetType() ==
+           CSSFontVariationSettingsNonInterpolableValue::static_type_;
+  }
+};
 
 static const Vector<AtomicString> GetTags(
     const NonInterpolableValue& non_interpolable_value) {
-  return ToCSSFontVariationSettingsNonInterpolableValue(non_interpolable_value)
+  return To<CSSFontVariationSettingsNonInterpolableValue>(
+             non_interpolable_value)
       .Tags();
 }
 
diff --git a/third_party/blink/renderer/core/animation/css_image_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_image_interpolation_type.cc
index 6033e9d..b7d1d7c 100644
--- a/third_party/blink/renderer/core/animation/css_image_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_image_interpolation_type.cc
@@ -79,15 +79,21 @@
 };
 
 DEFINE_NON_INTERPOLABLE_VALUE_TYPE(CSSImageNonInterpolableValue);
-DEFINE_NON_INTERPOLABLE_VALUE_TYPE_CASTS(CSSImageNonInterpolableValue);
+template <>
+struct DowncastTraits<CSSImageNonInterpolableValue> {
+  static bool AllowFrom(const NonInterpolableValue* value) {
+    return value && AllowFrom(*value);
+  }
+  static bool AllowFrom(const NonInterpolableValue& value) {
+    return value.GetType() == CSSImageNonInterpolableValue::static_type_;
+  }
+};
 
 scoped_refptr<CSSImageNonInterpolableValue> CSSImageNonInterpolableValue::Merge(
     scoped_refptr<const NonInterpolableValue> start,
     scoped_refptr<const NonInterpolableValue> end) {
-  const CSSImageNonInterpolableValue& start_image_pair =
-      ToCSSImageNonInterpolableValue(*start);
-  const CSSImageNonInterpolableValue& end_image_pair =
-      ToCSSImageNonInterpolableValue(*end);
+  const auto& start_image_pair = To<CSSImageNonInterpolableValue>(*start);
+  const auto& end_image_pair = To<CSSImageNonInterpolableValue>(*end);
   DCHECK(start_image_pair.is_single_);
   DCHECK(end_image_pair.is_single_);
   return Create(start_image_pair.start_, end_image_pair.end_);
@@ -115,9 +121,10 @@
 CSSImageInterpolationType::StaticMergeSingleConversions(
     InterpolationValue&& start,
     InterpolationValue&& end) {
-  if (!ToCSSImageNonInterpolableValue(*start.non_interpolable_value)
+  if (!To<CSSImageNonInterpolableValue>(*start.non_interpolable_value)
            .IsSingle() ||
-      !ToCSSImageNonInterpolableValue(*end.non_interpolable_value).IsSingle()) {
+      !To<CSSImageNonInterpolableValue>(*end.non_interpolable_value)
+           .IsSingle()) {
     return nullptr;
   }
   return PairwiseInterpolationValue(
@@ -137,7 +144,7 @@
 const CSSValue* CSSImageInterpolationType::StaticCreateCSSValue(
     const InterpolableValue& interpolable_value,
     const NonInterpolableValue* non_interpolable_value) {
-  return ToCSSImageNonInterpolableValue(non_interpolable_value)
+  return To<CSSImageNonInterpolableValue>(non_interpolable_value)
       ->Crossfade(To<InterpolableNumber>(interpolable_value).Value());
 }
 
@@ -154,8 +161,8 @@
 bool CSSImageInterpolationType::EqualNonInterpolableValues(
     const NonInterpolableValue* a,
     const NonInterpolableValue* b) {
-  return ToCSSImageNonInterpolableValue(*a).Equals(
-      ToCSSImageNonInterpolableValue(*b));
+  return To<CSSImageNonInterpolableValue>(*a).Equals(
+      To<CSSImageNonInterpolableValue>(*b));
 }
 
 class UnderlyingImageChecker
diff --git a/third_party/blink/renderer/core/animation/css_image_list_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_image_list_interpolation_type.cc
index 03212b9..52fc772 100644
--- a/third_party/blink/renderer/core/animation/css_image_list_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_image_list_interpolation_type.cc
@@ -170,8 +170,8 @@
   const auto& interpolable_list = To<InterpolableList>(interpolable_value);
   const wtf_size_t length = interpolable_list.length();
   DCHECK_GT(length, 0U);
-  const NonInterpolableList& non_interpolable_list =
-      ToNonInterpolableList(*non_interpolable_value);
+  const auto& non_interpolable_list =
+      To<NonInterpolableList>(*non_interpolable_value);
   DCHECK_EQ(non_interpolable_list.length(), length);
   StyleImageList* image_list = MakeGarbageCollected<StyleImageList>(length);
   for (wtf_size_t i = 0; i < length; i++) {
diff --git a/third_party/blink/renderer/core/animation/css_image_slice_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_image_slice_interpolation_type.cc
index afbe0dc..b12aaa6 100644
--- a/third_party/blink/renderer/core/animation/css_image_slice_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_image_slice_interpolation_type.cc
@@ -84,7 +84,15 @@
 };
 
 DEFINE_NON_INTERPOLABLE_VALUE_TYPE(CSSImageSliceNonInterpolableValue);
-DEFINE_NON_INTERPOLABLE_VALUE_TYPE_CASTS(CSSImageSliceNonInterpolableValue);
+template <>
+struct DowncastTraits<CSSImageSliceNonInterpolableValue> {
+  static bool AllowFrom(const NonInterpolableValue* value) {
+    return value && AllowFrom(*value);
+  }
+  static bool AllowFrom(const NonInterpolableValue& value) {
+    return value.GetType() == CSSImageSliceNonInterpolableValue::static_type_;
+  }
+};
 
 namespace {
 
@@ -96,7 +104,7 @@
 
   static SliceTypes GetUnderlyingSliceTypes(
       const InterpolationValue& underlying) {
-    return ToCSSImageSliceNonInterpolableValue(
+    return To<CSSImageSliceNonInterpolableValue>(
                *underlying.non_interpolable_value)
         .Types();
   }
@@ -226,11 +234,12 @@
 PairwiseInterpolationValue CSSImageSliceInterpolationType::MaybeMergeSingles(
     InterpolationValue&& start,
     InterpolationValue&& end) const {
-  const SliceTypes& start_slice_types =
-      ToCSSImageSliceNonInterpolableValue(*start.non_interpolable_value)
+  const auto& start_slice_types =
+      To<CSSImageSliceNonInterpolableValue>(*start.non_interpolable_value)
           .Types();
-  const SliceTypes& end_slice_types =
-      ToCSSImageSliceNonInterpolableValue(*end.non_interpolable_value).Types();
+  const auto& end_slice_types =
+      To<CSSImageSliceNonInterpolableValue>(*end.non_interpolable_value)
+          .Types();
 
   if (start_slice_types != end_slice_types)
     return nullptr;
@@ -245,12 +254,12 @@
     double underlying_fraction,
     const InterpolationValue& value,
     double interpolation_fraction) const {
-  const SliceTypes& underlying_types =
-      ToCSSImageSliceNonInterpolableValue(
+  const auto& underlying_types =
+      To<CSSImageSliceNonInterpolableValue>(
           *underlying_value_owner.Value().non_interpolable_value)
           .Types();
-  const SliceTypes& types =
-      ToCSSImageSliceNonInterpolableValue(*value.non_interpolable_value)
+  const auto& types =
+      To<CSSImageSliceNonInterpolableValue>(*value.non_interpolable_value)
           .Types();
 
   if (underlying_types == types)
@@ -266,8 +275,8 @@
     StyleResolverState& state) const {
   ComputedStyle& style = *state.Style();
   const auto& list = To<InterpolableList>(interpolable_value);
-  const SliceTypes& types =
-      ToCSSImageSliceNonInterpolableValue(non_interpolable_value)->Types();
+  const auto& types =
+      To<CSSImageSliceNonInterpolableValue>(non_interpolable_value)->Types();
   const auto& convert_side = [&types, &list, &style](wtf_size_t index) {
     float value =
         clampTo<float>(To<InterpolableNumber>(list.Get(index))->Value(), 0);
diff --git a/third_party/blink/renderer/core/animation/css_length_list_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_length_list_interpolation_type.cc
index a8b494a9..d05ce01 100644
--- a/third_party/blink/renderer/core/animation/css_length_list_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_length_list_interpolation_type.cc
@@ -167,8 +167,8 @@
   const auto& interpolable_list = To<InterpolableList>(interpolable_value);
   const wtf_size_t length = interpolable_list.length();
   DCHECK_GT(length, 0U);
-  const NonInterpolableList& non_interpolable_list =
-      ToNonInterpolableList(*non_interpolable_value);
+  const auto& non_interpolable_list =
+      To<NonInterpolableList>(*non_interpolable_value);
   DCHECK_EQ(non_interpolable_list.length(), length);
   Vector<Length> result(length);
   for (wtf_size_t i = 0; i < length; i++) {
diff --git a/third_party/blink/renderer/core/animation/css_offset_rotate_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_offset_rotate_interpolation_type.cc
index b593944..3b9596e 100644
--- a/third_party/blink/renderer/core/animation/css_offset_rotate_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_offset_rotate_interpolation_type.cc
@@ -36,7 +36,16 @@
 };
 
 DEFINE_NON_INTERPOLABLE_VALUE_TYPE(CSSOffsetRotationNonInterpolableValue);
-DEFINE_NON_INTERPOLABLE_VALUE_TYPE_CASTS(CSSOffsetRotationNonInterpolableValue);
+template <>
+struct DowncastTraits<CSSOffsetRotationNonInterpolableValue> {
+  static bool AllowFrom(const NonInterpolableValue* value) {
+    return value && AllowFrom(*value);
+  }
+  static bool AllowFrom(const NonInterpolableValue& value) {
+    return value.GetType() ==
+           CSSOffsetRotationNonInterpolableValue::static_type_;
+  }
+};
 
 namespace {
 
@@ -49,9 +58,10 @@
 
   bool IsValid(const StyleResolverState&,
                const InterpolationValue& underlying) const final {
-    return underlying_rotation_type_ == ToCSSOffsetRotationNonInterpolableValue(
-                                            *underlying.non_interpolable_value)
-                                            .RotationType();
+    return underlying_rotation_type_ ==
+           To<CSSOffsetRotationNonInterpolableValue>(
+               *underlying.non_interpolable_value)
+               .RotationType();
   }
 
  private:
@@ -86,7 +96,7 @@
     const InterpolationValue& underlying,
     ConversionCheckers& conversion_checkers) const {
   OffsetRotationType underlying_rotation_type =
-      ToCSSOffsetRotationNonInterpolableValue(
+      To<CSSOffsetRotationNonInterpolableValue>(
           *underlying.non_interpolable_value)
           .RotationType();
   conversion_checkers.push_back(std::make_unique<UnderlyingRotationTypeChecker>(
@@ -121,10 +131,10 @@
     InterpolationValue&& start,
     InterpolationValue&& end) const {
   const OffsetRotationType& start_type =
-      ToCSSOffsetRotationNonInterpolableValue(*start.non_interpolable_value)
+      To<CSSOffsetRotationNonInterpolableValue>(*start.non_interpolable_value)
           .RotationType();
   const OffsetRotationType& end_type =
-      ToCSSOffsetRotationNonInterpolableValue(*end.non_interpolable_value)
+      To<CSSOffsetRotationNonInterpolableValue>(*end.non_interpolable_value)
           .RotationType();
   if (start_type != end_type)
     return nullptr;
@@ -145,11 +155,11 @@
     const InterpolationValue& value,
     double interpolation_fraction) const {
   const OffsetRotationType& underlying_type =
-      ToCSSOffsetRotationNonInterpolableValue(
+      To<CSSOffsetRotationNonInterpolableValue>(
           *underlying_value_owner.Value().non_interpolable_value)
           .RotationType();
   const OffsetRotationType& rotation_type =
-      ToCSSOffsetRotationNonInterpolableValue(*value.non_interpolable_value)
+      To<CSSOffsetRotationNonInterpolableValue>(*value.non_interpolable_value)
           .RotationType();
   if (underlying_type == rotation_type) {
     underlying_value_owner.MutableValue().interpolable_value->ScaleAndAdd(
@@ -165,7 +175,7 @@
     StyleResolverState& state) const {
   state.Style()->SetOffsetRotate(StyleOffsetRotation(
       clampTo<float>(To<InterpolableNumber>(interpolable_value).Value()),
-      ToCSSOffsetRotationNonInterpolableValue(*non_interpolable_value)
+      To<CSSOffsetRotationNonInterpolableValue>(*non_interpolable_value)
           .RotationType()));
 }
 
diff --git a/third_party/blink/renderer/core/animation/css_ray_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_ray_interpolation_type.cc
index 3eb8265..0b73642 100644
--- a/third_party/blink/renderer/core/animation/css_ray_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_ray_interpolation_type.cc
@@ -57,7 +57,15 @@
 };
 
 DEFINE_NON_INTERPOLABLE_VALUE_TYPE(CSSRayNonInterpolableValue);
-DEFINE_NON_INTERPOLABLE_VALUE_TYPE_CASTS(CSSRayNonInterpolableValue);
+template <>
+struct DowncastTraits<CSSRayNonInterpolableValue> {
+  static bool AllowFrom(const NonInterpolableValue* value) {
+    return value && AllowFrom(*value);
+  }
+  static bool AllowFrom(const NonInterpolableValue& value) {
+    return value.GetType() == CSSRayNonInterpolableValue::static_type_;
+  }
+};
 
 namespace {
 
@@ -78,7 +86,7 @@
   bool IsValid(const StyleResolverState&,
                const InterpolationValue& underlying) const final {
     return mode_ ==
-           ToCSSRayNonInterpolableValue(*underlying.non_interpolable_value)
+           To<CSSRayNonInterpolableValue>(*underlying.non_interpolable_value)
                .Mode();
   }
 
@@ -113,8 +121,8 @@
     const InterpolableValue& interpolable_value,
     const NonInterpolableValue* non_interpolable_value,
     StyleResolverState& state) const {
-  const CSSRayNonInterpolableValue& ray_non_interpolable_value =
-      ToCSSRayNonInterpolableValue(*non_interpolable_value);
+  const auto& ray_non_interpolable_value =
+      To<CSSRayNonInterpolableValue>(*non_interpolable_value);
   state.Style()->SetOffsetPath(
       StyleRay::Create(To<InterpolableNumber>(interpolable_value).Value(),
                        ray_non_interpolable_value.Mode().Size(),
@@ -127,11 +135,11 @@
     const InterpolationValue& value,
     double interpolation_fraction) const {
   const RayMode& underlying_mode =
-      ToCSSRayNonInterpolableValue(
+      To<CSSRayNonInterpolableValue>(
           *underlying_value_owner.Value().non_interpolable_value)
           .Mode();
   const RayMode& ray_mode =
-      ToCSSRayNonInterpolableValue(*value.non_interpolable_value).Mode();
+      To<CSSRayNonInterpolableValue>(*value.non_interpolable_value).Mode();
   if (underlying_mode == ray_mode) {
     underlying_value_owner.MutableValue().interpolable_value->ScaleAndAdd(
         underlying_fraction, *value.interpolable_value);
@@ -144,7 +152,7 @@
     const InterpolationValue& underlying,
     ConversionCheckers& conversion_checkers) const {
   const RayMode& underlying_mode =
-      ToCSSRayNonInterpolableValue(*underlying.non_interpolable_value).Mode();
+      To<CSSRayNonInterpolableValue>(*underlying.non_interpolable_value).Mode();
   conversion_checkers.push_back(
       std::make_unique<UnderlyingRayModeChecker>(underlying_mode));
   return CreateValue(0, underlying_mode);
@@ -176,9 +184,9 @@
     InterpolationValue&& start,
     InterpolationValue&& end) const {
   const RayMode& start_mode =
-      ToCSSRayNonInterpolableValue(*start.non_interpolable_value).Mode();
+      To<CSSRayNonInterpolableValue>(*start.non_interpolable_value).Mode();
   const RayMode& end_mode =
-      ToCSSRayNonInterpolableValue(*end.non_interpolable_value).Mode();
+      To<CSSRayNonInterpolableValue>(*end.non_interpolable_value).Mode();
   if (start_mode != end_mode)
     return nullptr;
   return PairwiseInterpolationValue(std::move(start.interpolable_value),
diff --git a/third_party/blink/renderer/core/animation/css_rotate_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_rotate_interpolation_type.cc
index c3e09166..ec70e75 100644
--- a/third_party/blink/renderer/core/animation/css_rotate_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_rotate_interpolation_type.cc
@@ -140,7 +140,15 @@
 };
 
 DEFINE_NON_INTERPOLABLE_VALUE_TYPE(CSSRotateNonInterpolableValue);
-DEFINE_NON_INTERPOLABLE_VALUE_TYPE_CASTS(CSSRotateNonInterpolableValue);
+template <>
+struct DowncastTraits<CSSRotateNonInterpolableValue> {
+  static bool AllowFrom(const NonInterpolableValue* value) {
+    return value && AllowFrom(*value);
+  }
+  static bool AllowFrom(const NonInterpolableValue& value) {
+    return value.GetType() == CSSRotateNonInterpolableValue::static_type_;
+  }
+};
 
 namespace {
 
@@ -219,7 +227,7 @@
     EffectModel::CompositeOperation,
     ConversionCheckers&) const {
   value.non_interpolable_value = CSSRotateNonInterpolableValue::CreateAdditive(
-      ToCSSRotateNonInterpolableValue(*value.non_interpolable_value));
+      To<CSSRotateNonInterpolableValue>(*value.non_interpolable_value));
   return value;
 }
 
@@ -230,8 +238,8 @@
       std::make_unique<InterpolableNumber>(0),
       std::make_unique<InterpolableNumber>(1),
       CSSRotateNonInterpolableValue::Create(
-          ToCSSRotateNonInterpolableValue(*start.non_interpolable_value),
-          ToCSSRotateNonInterpolableValue(*end.non_interpolable_value)));
+          To<CSSRotateNonInterpolableValue>(*start.non_interpolable_value),
+          To<CSSRotateNonInterpolableValue>(*end.non_interpolable_value)));
 }
 
 InterpolationValue
@@ -245,11 +253,11 @@
     double underlying_fraction,
     const InterpolationValue& value,
     double interpolation_fraction) const {
-  const CSSRotateNonInterpolableValue& underlying_non_interpolable_value =
-      ToCSSRotateNonInterpolableValue(
+  const auto& underlying_non_interpolable_value =
+      To<CSSRotateNonInterpolableValue>(
           *underlying_value_owner.Value().non_interpolable_value);
-  const CSSRotateNonInterpolableValue& non_interpolable_value =
-      ToCSSRotateNonInterpolableValue(*value.non_interpolable_value);
+  const auto& non_interpolable_value =
+      To<CSSRotateNonInterpolableValue>(*value.non_interpolable_value);
   double progress = To<InterpolableNumber>(*value.interpolable_value).Value();
   underlying_value_owner.MutableValue().non_interpolable_value =
       underlying_non_interpolable_value.Composite(non_interpolable_value,
@@ -261,8 +269,8 @@
     const NonInterpolableValue* untyped_non_interpolable_value,
     StyleResolverState& state) const {
   double progress = To<InterpolableNumber>(interpolable_value).Value();
-  const CSSRotateNonInterpolableValue& non_interpolable_value =
-      ToCSSRotateNonInterpolableValue(*untyped_non_interpolable_value);
+  const auto& non_interpolable_value =
+      To<CSSRotateNonInterpolableValue>(*untyped_non_interpolable_value);
   OptionalRotation rotation = non_interpolable_value.SlerpedRotation(progress);
   if (rotation.IsNone()) {
     state.Style()->SetRotate(nullptr);
diff --git a/third_party/blink/renderer/core/animation/css_scale_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_scale_interpolation_type.cc
index 40cc315..9da7670 100644
--- a/third_party/blink/renderer/core/animation/css_scale_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_scale_interpolation_type.cc
@@ -130,7 +130,15 @@
 };
 
 DEFINE_NON_INTERPOLABLE_VALUE_TYPE(CSSScaleNonInterpolableValue);
-DEFINE_NON_INTERPOLABLE_VALUE_TYPE_CASTS(CSSScaleNonInterpolableValue);
+template <>
+struct DowncastTraits<CSSScaleNonInterpolableValue> {
+  static bool AllowFrom(const NonInterpolableValue* value) {
+    return value && AllowFrom(*value);
+  }
+  static bool AllowFrom(const NonInterpolableValue& value) {
+    return value.GetType() == CSSScaleNonInterpolableValue::static_type_;
+  }
+};
 
 InterpolationValue Scale::CreateInterpolationValue() const {
   if (is_none) {
@@ -199,7 +207,7 @@
     EffectModel::CompositeOperation,
     ConversionCheckers&) const {
   value.non_interpolable_value = CSSScaleNonInterpolableValue::CreateAdditive(
-      ToCSSScaleNonInterpolableValue(*value.non_interpolable_value));
+      To<CSSScaleNonInterpolableValue>(*value.non_interpolable_value));
   return value;
 }
 
@@ -218,8 +226,8 @@
   return PairwiseInterpolationValue(
       std::move(start.interpolable_value), std::move(end.interpolable_value),
       CSSScaleNonInterpolableValue::Merge(
-          ToCSSScaleNonInterpolableValue(*start.non_interpolable_value),
-          ToCSSScaleNonInterpolableValue(*end.non_interpolable_value)));
+          To<CSSScaleNonInterpolableValue>(*start.non_interpolable_value),
+          To<CSSScaleNonInterpolableValue>(*end.non_interpolable_value)));
 }
 
 InterpolationValue
@@ -240,8 +248,8 @@
         CreateScaleIdentity();
   }
 
-  const CSSScaleNonInterpolableValue& metadata =
-      ToCSSScaleNonInterpolableValue(*value.non_interpolable_value);
+  const auto& metadata =
+      To<CSSScaleNonInterpolableValue>(*value.non_interpolable_value);
   DCHECK(metadata.IsStartAdditive() || metadata.IsEndAdditive());
 
   auto& underlying_list = To<InterpolableList>(
diff --git a/third_party/blink/renderer/core/animation/css_size_list_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_size_list_interpolation_type.cc
index 85d5f6c..829c292 100644
--- a/third_party/blink/renderer/core/animation/css_size_list_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_size_list_interpolation_type.cc
@@ -30,7 +30,7 @@
   bool IsValid(const StyleResolverState&,
                const InterpolationValue& underlying) const final {
     const auto& underlying_list =
-        ToNonInterpolableList(*underlying.non_interpolable_value);
+        To<NonInterpolableList>(*underlying.non_interpolable_value);
     wtf_size_t underlying_length = underlying_list.length();
     if (underlying_length != underlying_list_->length())
       return false;
@@ -105,7 +105,7 @@
     const InterpolationValue& underlying,
     ConversionCheckers& conversion_checkers) const {
   const auto& underlying_list =
-      ToNonInterpolableList(*underlying.non_interpolable_value);
+      To<NonInterpolableList>(*underlying.non_interpolable_value);
   conversion_checkers.push_back(
       std::make_unique<UnderlyingSizeListChecker>(underlying_list));
   return ListInterpolationFunctions::CreateList(
@@ -177,7 +177,7 @@
     StyleResolverState& state) const {
   const auto& interpolable_list = To<InterpolableList>(interpolable_value);
   const auto& non_interpolable_list =
-      ToNonInterpolableList(*non_interpolable_value);
+      To<NonInterpolableList>(*non_interpolable_value);
   wtf_size_t length = interpolable_list.length();
   DCHECK_EQ(length, non_interpolable_list.length());
   DCHECK_EQ(length % 2, 0ul);
diff --git a/third_party/blink/renderer/core/animation/css_text_indent_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_text_indent_interpolation_type.cc
index 05174e2..97cdbac 100644
--- a/third_party/blink/renderer/core/animation/css_text_indent_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_text_indent_interpolation_type.cc
@@ -65,7 +65,15 @@
 };
 
 DEFINE_NON_INTERPOLABLE_VALUE_TYPE(CSSTextIndentNonInterpolableValue);
-DEFINE_NON_INTERPOLABLE_VALUE_TYPE_CASTS(CSSTextIndentNonInterpolableValue);
+template <>
+struct DowncastTraits<CSSTextIndentNonInterpolableValue> {
+  static bool AllowFrom(const NonInterpolableValue* value) {
+    return value && AllowFrom(*value);
+  }
+  static bool AllowFrom(const NonInterpolableValue& value) {
+    return value.GetType() == CSSTextIndentNonInterpolableValue::static_type_;
+  }
+};
 
 namespace {
 
@@ -76,7 +84,7 @@
 
   bool IsValid(const StyleResolverState&,
                const InterpolationValue& underlying) const final {
-    return mode_ == ToCSSTextIndentNonInterpolableValue(
+    return mode_ == To<CSSTextIndentNonInterpolableValue>(
                         *underlying.non_interpolable_value)
                         .Mode();
   }
@@ -120,7 +128,7 @@
     const InterpolationValue& underlying,
     ConversionCheckers& conversion_checkers) const {
   IndentMode mode =
-      ToCSSTextIndentNonInterpolableValue(*underlying.non_interpolable_value)
+      To<CSSTextIndentNonInterpolableValue>(*underlying.non_interpolable_value)
           .Mode();
   conversion_checkers.push_back(
       std::make_unique<UnderlyingIndentModeChecker>(mode));
@@ -185,10 +193,10 @@
 PairwiseInterpolationValue CSSTextIndentInterpolationType::MaybeMergeSingles(
     InterpolationValue&& start,
     InterpolationValue&& end) const {
-  const CSSTextIndentNonInterpolableValue& start_non_interpolable_value =
-      ToCSSTextIndentNonInterpolableValue(*start.non_interpolable_value);
-  const CSSTextIndentNonInterpolableValue& end_non_interpolable_value =
-      ToCSSTextIndentNonInterpolableValue(*end.non_interpolable_value);
+  const auto& start_non_interpolable_value =
+      To<CSSTextIndentNonInterpolableValue>(*start.non_interpolable_value);
+  const auto& end_non_interpolable_value =
+      To<CSSTextIndentNonInterpolableValue>(*end.non_interpolable_value);
 
   if (start_non_interpolable_value.Mode() != end_non_interpolable_value.Mode())
     return nullptr;
@@ -207,11 +215,11 @@
     const InterpolationValue& value,
     double interpolation_fraction) const {
   const IndentMode& underlying_mode =
-      ToCSSTextIndentNonInterpolableValue(
+      To<CSSTextIndentNonInterpolableValue>(
           *underlying_value_owner.Value().non_interpolable_value)
           .Mode();
-  const CSSTextIndentNonInterpolableValue& non_interpolable_value =
-      ToCSSTextIndentNonInterpolableValue(*value.non_interpolable_value);
+  const auto& non_interpolable_value =
+      To<CSSTextIndentNonInterpolableValue>(*value.non_interpolable_value);
   const IndentMode& mode = non_interpolable_value.Mode();
 
   if (underlying_mode != mode) {
@@ -227,9 +235,8 @@
     const InterpolableValue& interpolable_value,
     const NonInterpolableValue* non_interpolable_value,
     StyleResolverState& state) const {
-  const CSSTextIndentNonInterpolableValue&
-      css_text_indent_non_interpolable_value =
-          ToCSSTextIndentNonInterpolableValue(*non_interpolable_value);
+  const auto& css_text_indent_non_interpolable_value =
+      To<CSSTextIndentNonInterpolableValue>(*non_interpolable_value);
   ComputedStyle& style = *state.Style();
   style.SetTextIndent(
       To<InterpolableLength>(interpolable_value)
diff --git a/third_party/blink/renderer/core/animation/css_visibility_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_visibility_interpolation_type.cc
index 8e30240c..f315667 100644
--- a/third_party/blink/renderer/core/animation/css_visibility_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_visibility_interpolation_type.cc
@@ -49,7 +49,15 @@
 };
 
 DEFINE_NON_INTERPOLABLE_VALUE_TYPE(CSSVisibilityNonInterpolableValue);
-DEFINE_NON_INTERPOLABLE_VALUE_TYPE_CASTS(CSSVisibilityNonInterpolableValue);
+template <>
+struct DowncastTraits<CSSVisibilityNonInterpolableValue> {
+  static bool AllowFrom(const NonInterpolableValue* value) {
+    return value && AllowFrom(*value);
+  }
+  static bool AllowFrom(const NonInterpolableValue& value) {
+    return value.GetType() == CSSVisibilityNonInterpolableValue::static_type_;
+  }
+};
 
 class UnderlyingVisibilityChecker
     : public CSSInterpolationType::CSSConversionChecker {
@@ -65,9 +73,9 @@
                const InterpolationValue& underlying) const final {
     double underlying_fraction =
         To<InterpolableNumber>(*underlying.interpolable_value).Value();
-    EVisibility underlying_visibility =
-        ToCSSVisibilityNonInterpolableValue(*underlying.non_interpolable_value)
-            .Visibility(underlying_fraction);
+    EVisibility underlying_visibility = To<CSSVisibilityNonInterpolableValue>(
+                                            *underlying.non_interpolable_value)
+                                            .Visibility(underlying_fraction);
     return visibility_ == underlying_visibility;
   }
 
@@ -102,7 +110,7 @@
   double underlying_fraction =
       To<InterpolableNumber>(*underlying.interpolable_value).Value();
   EVisibility underlying_visibility =
-      ToCSSVisibilityNonInterpolableValue(*underlying.non_interpolable_value)
+      To<CSSVisibilityNonInterpolableValue>(*underlying.non_interpolable_value)
           .Visibility(underlying_fraction);
   conversion_checkers.push_back(
       std::make_unique<UnderlyingVisibilityChecker>(underlying_visibility));
@@ -156,10 +164,10 @@
     InterpolationValue&& start,
     InterpolationValue&& end) const {
   EVisibility start_visibility =
-      ToCSSVisibilityNonInterpolableValue(*start.non_interpolable_value)
+      To<CSSVisibilityNonInterpolableValue>(*start.non_interpolable_value)
           .Visibility();
   EVisibility end_visibility =
-      ToCSSVisibilityNonInterpolableValue(*end.non_interpolable_value)
+      To<CSSVisibilityNonInterpolableValue>(*end.non_interpolable_value)
           .Visibility();
   // One side must be "visible".
   // Spec: https://drafts.csswg.org/css-transitions/#animtype-visibility
@@ -190,7 +198,7 @@
   // its non-linear behaviour.
   double fraction = To<InterpolableNumber>(interpolable_value).Value();
   EVisibility visibility =
-      ToCSSVisibilityNonInterpolableValue(non_interpolable_value)
+      To<CSSVisibilityNonInterpolableValue>(non_interpolable_value)
           ->Visibility(fraction);
   state.Style()->SetVisibility(visibility);
 }
diff --git a/third_party/blink/renderer/core/animation/keyframe_effect_model_test.cc b/third_party/blink/renderer/core/animation/keyframe_effect_model_test.cc
index 1ffe32c5a..1cf1fad 100644
--- a/third_party/blink/renderer/core/animation/keyframe_effect_model_test.cc
+++ b/third_party/blink/renderer/core/animation/keyframe_effect_model_test.cc
@@ -94,10 +94,10 @@
             ->GetCachedValueForTesting();
     const NonInterpolableValue* non_interpolable_value =
         typed_value->GetNonInterpolableValue();
-    ASSERT_TRUE(IsCSSDefaultNonInterpolableValue(non_interpolable_value));
+    ASSERT_TRUE(IsA<CSSDefaultNonInterpolableValue>(non_interpolable_value));
 
     const CSSValue* css_value =
-        ToCSSDefaultNonInterpolableValue(non_interpolable_value)->CssValue();
+        To<CSSDefaultNonInterpolableValue>(non_interpolable_value)->CssValue();
     EXPECT_EQ(expected_value, css_value->CssText());
   }
 
diff --git a/third_party/blink/renderer/core/animation/list_interpolation_functions.cc b/third_party/blink/renderer/core/animation/list_interpolation_functions.cc
index 93beed8..c88bc947 100644
--- a/third_party/blink/renderer/core/animation/list_interpolation_functions.cc
+++ b/third_party/blink/renderer/core/animation/list_interpolation_functions.cc
@@ -41,7 +41,7 @@
         .Set(index_, std::move(interpolable_value));
   }
   const NonInterpolableValue* GetNonInterpolableValue() const final {
-    return ToNonInterpolableList(*underlying_list_.GetNonInterpolableValue())
+    return To<NonInterpolableList>(*underlying_list_.GetNonInterpolableValue())
         .Get(index_);
   }
   void SetNonInterpolableValue(
@@ -75,10 +75,10 @@
   if (length == 0)
     return true;
 
-  const NonInterpolableList& non_interpolable_list_a =
-      ToNonInterpolableList(*a.non_interpolable_value);
-  const NonInterpolableList& non_interpolable_list_b =
-      ToNonInterpolableList(*b.non_interpolable_value);
+  const auto& non_interpolable_list_a =
+      To<NonInterpolableList>(*a.non_interpolable_value);
+  const auto& non_interpolable_list_b =
+      To<NonInterpolableList>(*b.non_interpolable_value);
 
   for (wtf_size_t i = 0; i < length; i++) {
     if (!equal_non_interpolable_values(non_interpolable_list_a.Get(i),
@@ -165,14 +165,14 @@
   auto& start_interpolable_list =
       To<InterpolableList>(*start.interpolable_value);
   auto& end_interpolable_list = To<InterpolableList>(*end.interpolable_value);
-  const NonInterpolableList& start_non_interpolable_list =
-      ToNonInterpolableList(*start.non_interpolable_value);
-  const NonInterpolableList& end_non_interpolable_list =
-      ToNonInterpolableList(*end.non_interpolable_value);
+  const auto& start_non_interpolable_list =
+      To<NonInterpolableList>(*start.non_interpolable_value);
+  const auto& end_non_interpolable_list =
+      To<NonInterpolableList>(*end.non_interpolable_value);
   const wtf_size_t start_non_interpolable_length =
-      ToNonInterpolableList(*start.non_interpolable_value).length();
+      To<NonInterpolableList>(*start.non_interpolable_value).length();
   const wtf_size_t end_non_interpolable_length =
-      ToNonInterpolableList(*end.non_interpolable_value).length();
+      To<NonInterpolableList>(*end.non_interpolable_value).length();
   for (wtf_size_t i = 0; i < final_length; i++) {
     if (length_matching_strategy ==
             LengthMatchingStrategy::kLowestCommonMultiple ||
@@ -225,8 +225,8 @@
 
 static void RepeatToLength(InterpolationValue& value, wtf_size_t length) {
   auto& interpolable_list = To<InterpolableList>(*value.interpolable_value);
-  const NonInterpolableList& non_interpolable_list =
-      ToNonInterpolableList(*value.non_interpolable_value);
+  const auto& non_interpolable_list =
+      To<NonInterpolableList>(*value.non_interpolable_value);
   wtf_size_t current_length = interpolable_list.length();
   DCHECK_GT(current_length, 0U);
   if (current_length == length)
@@ -253,13 +253,13 @@
 static void PadToSameLength(InterpolationValue& value,
                             const InterpolationValue& length_value) {
   auto& interpolable_list = To<InterpolableList>(*value.interpolable_value);
-  const NonInterpolableList& non_interpolable_list =
-      ToNonInterpolableList(*value.non_interpolable_value);
+  const auto& non_interpolable_list =
+      To<NonInterpolableList>(*value.non_interpolable_value);
   const wtf_size_t current_length = interpolable_list.length();
   auto& target_interpolable_list =
       To<InterpolableList>(*length_value.interpolable_value);
-  const NonInterpolableList& target_non_interpolable_list =
-      ToNonInterpolableList(*length_value.non_interpolable_value);
+  const auto& target_non_interpolable_list =
+      To<NonInterpolableList>(*length_value.non_interpolable_value);
   const wtf_size_t target_length = target_interpolable_list.length();
   DCHECK_LT(current_length, target_length);
   auto new_interpolable_list =
@@ -382,10 +382,10 @@
     return;
   }
 
-  const NonInterpolableList& non_interpolable_list =
-      ToNonInterpolableList(*value.non_interpolable_value);
+  const auto& non_interpolable_list =
+      To<NonInterpolableList>(*value.non_interpolable_value);
   if (!NonInterpolableListsAreCompatible(
-          ToNonInterpolableList(
+          To<NonInterpolableList>(
               *underlying_value_owner.Value().non_interpolable_value),
           non_interpolable_list, final_length, length_matching_strategy,
           non_interpolable_values_are_compatible)) {
@@ -436,7 +436,7 @@
 NonInterpolableList::AutoBuilder::AutoBuilder(UnderlyingValue& underlying_value)
     : underlying_value_(underlying_value) {
   DCHECK(underlying_value.GetNonInterpolableValue());
-  DCHECK(IsNonInterpolableList(underlying_value_.GetNonInterpolableValue()));
+  DCHECK(IsA<NonInterpolableList>(underlying_value_.GetNonInterpolableValue()));
 }
 
 NonInterpolableList::AutoBuilder::~AutoBuilder() {
@@ -445,7 +445,7 @@
   if (!list_.size())
     return;
   const auto& non_interpolable_list =
-      ToNonInterpolableList(*underlying_value_.GetNonInterpolableValue());
+      To<NonInterpolableList>(*underlying_value_.GetNonInterpolableValue());
   DCHECK_EQ(non_interpolable_list.length(), list_.size());
   underlying_value_.SetNonInterpolableValue(
       NonInterpolableList::Create(std::move(list_)));
@@ -457,7 +457,7 @@
   // Copy list on first call to Set.
   if (!list_.size()) {
     const auto& non_interpolable_list =
-        ToNonInterpolableList(*underlying_value_.GetNonInterpolableValue());
+        To<NonInterpolableList>(*underlying_value_.GetNonInterpolableValue());
     wtf_size_t underlying_length = non_interpolable_list.length();
     for (wtf_size_t i = 0; i < underlying_length; ++i)
       list_.push_back(non_interpolable_list.Get(i));
diff --git a/third_party/blink/renderer/core/animation/list_interpolation_functions.h b/third_party/blink/renderer/core/animation/list_interpolation_functions.h
index 1cd58ff..82d035e4 100644
--- a/third_party/blink/renderer/core/animation/list_interpolation_functions.h
+++ b/third_party/blink/renderer/core/animation/list_interpolation_functions.h
@@ -128,7 +128,15 @@
   Vector<scoped_refptr<const NonInterpolableValue>> list_;
 };
 
-DEFINE_NON_INTERPOLABLE_VALUE_TYPE_CASTS(NonInterpolableList);
+template <>
+struct DowncastTraits<NonInterpolableList> {
+  static bool AllowFrom(const NonInterpolableValue* value) {
+    return value && AllowFrom(*value);
+  }
+  static bool AllowFrom(const NonInterpolableValue& value) {
+    return value.GetType() == NonInterpolableList::static_type_;
+  }
+};
 
 template <typename CreateItemCallback>
 InterpolationValue ListInterpolationFunctions::CreateList(
diff --git a/third_party/blink/renderer/core/animation/list_interpolation_functions_test.cc b/third_party/blink/renderer/core/animation/list_interpolation_functions_test.cc
index 4d63279..0921834 100644
--- a/third_party/blink/renderer/core/animation/list_interpolation_functions_test.cc
+++ b/third_party/blink/renderer/core/animation/list_interpolation_functions_test.cc
@@ -314,14 +314,14 @@
 
 TEST(ListInterpolationFunctionsTest, BuilderNoModify) {
   auto list = CreateNonInterpolableList({1, 2, 3});
-  auto& before = ToNonInterpolableList(*list.non_interpolable_value);
+  auto& before = To<NonInterpolableList>(*list.non_interpolable_value);
 
   {
     TestUnderlyingValue underlying_value(list);
     NonInterpolableList::AutoBuilder builder(underlying_value);
   }
 
-  auto& after = ToNonInterpolableList(*list.non_interpolable_value);
+  auto& after = To<NonInterpolableList>(*list.non_interpolable_value);
 
   EXPECT_EQ(&before, &after);
   ASSERT_EQ(3u, before.length());
@@ -332,7 +332,7 @@
 
 TEST(ListInterpolationFunctionsTest, BuilderModifyFirst) {
   auto list = CreateNonInterpolableList({1, 2, 3});
-  auto& before = ToNonInterpolableList(*list.non_interpolable_value);
+  auto& before = To<NonInterpolableList>(*list.non_interpolable_value);
 
   {
     TestUnderlyingValue underlying_value(list);
@@ -340,7 +340,7 @@
     builder.Set(0, TestNonInterpolableValue::Create(4));
   }
 
-  auto& after = ToNonInterpolableList(*list.non_interpolable_value);
+  auto& after = To<NonInterpolableList>(*list.non_interpolable_value);
 
   EXPECT_NE(&before, &after);
   ASSERT_EQ(3u, after.length());
@@ -351,7 +351,7 @@
 
 TEST(ListInterpolationFunctionsTest, BuilderModifyMiddle) {
   auto list = CreateNonInterpolableList({1, 2, 3});
-  auto& before = ToNonInterpolableList(*list.non_interpolable_value);
+  auto& before = To<NonInterpolableList>(*list.non_interpolable_value);
 
   {
     TestUnderlyingValue underlying_value(list);
@@ -359,7 +359,7 @@
     builder.Set(1, TestNonInterpolableValue::Create(4));
   }
 
-  auto& after = ToNonInterpolableList(*list.non_interpolable_value);
+  auto& after = To<NonInterpolableList>(*list.non_interpolable_value);
 
   EXPECT_NE(&before, &after);
   ASSERT_EQ(3u, after.length());
@@ -370,7 +370,7 @@
 
 TEST(ListInterpolationFunctionsTest, BuilderModifyLast) {
   auto list = CreateNonInterpolableList({1, 2, 3});
-  auto& before = ToNonInterpolableList(*list.non_interpolable_value);
+  auto& before = To<NonInterpolableList>(*list.non_interpolable_value);
 
   {
     TestUnderlyingValue underlying_value(list);
@@ -378,7 +378,7 @@
     builder.Set(2, TestNonInterpolableValue::Create(4));
   }
 
-  auto& after = ToNonInterpolableList(*list.non_interpolable_value);
+  auto& after = To<NonInterpolableList>(*list.non_interpolable_value);
 
   EXPECT_NE(&before, &after);
   ASSERT_EQ(3u, after.length());
@@ -389,7 +389,7 @@
 
 TEST(ListInterpolationFunctionsTest, BuilderModifyAll) {
   auto list = CreateNonInterpolableList({1, 2, 3});
-  auto& before = ToNonInterpolableList(*list.non_interpolable_value);
+  auto& before = To<NonInterpolableList>(*list.non_interpolable_value);
 
   {
     TestUnderlyingValue underlying_value(list);
@@ -399,7 +399,7 @@
     builder.Set(2, TestNonInterpolableValue::Create(6));
   }
 
-  auto& after = ToNonInterpolableList(*list.non_interpolable_value);
+  auto& after = To<NonInterpolableList>(*list.non_interpolable_value);
 
   EXPECT_NE(&before, &after);
   ASSERT_EQ(3u, after.length());
@@ -410,7 +410,7 @@
 
 TEST(ListInterpolationFunctionsTest, BuilderModifyReverse) {
   auto list = CreateNonInterpolableList({1, 2, 3, 4, 5});
-  auto& before = ToNonInterpolableList(*list.non_interpolable_value);
+  auto& before = To<NonInterpolableList>(*list.non_interpolable_value);
 
   {
     TestUnderlyingValue underlying_value(list);
@@ -419,7 +419,7 @@
     builder.Set(1, TestNonInterpolableValue::Create(7));
   }
 
-  auto& after = ToNonInterpolableList(*list.non_interpolable_value);
+  auto& after = To<NonInterpolableList>(*list.non_interpolable_value);
 
   EXPECT_NE(&before, &after);
   ASSERT_EQ(5u, after.length());
@@ -432,7 +432,7 @@
 
 TEST(ListInterpolationFunctionsTest, BuilderModifyListWithOneItem) {
   auto list = CreateNonInterpolableList({1});
-  auto& before = ToNonInterpolableList(*list.non_interpolable_value);
+  auto& before = To<NonInterpolableList>(*list.non_interpolable_value);
 
   {
     TestUnderlyingValue underlying_value(list);
@@ -440,7 +440,7 @@
     builder.Set(0, TestNonInterpolableValue::Create(4));
   }
 
-  auto& after = ToNonInterpolableList(*list.non_interpolable_value);
+  auto& after = To<NonInterpolableList>(*list.non_interpolable_value);
 
   EXPECT_NE(&before, &after);
   EXPECT_EQ(4, ToTestNonInterpolableValue(*after.Get(0)).GetValue());
diff --git a/third_party/blink/renderer/core/animation/non_interpolable_value.h b/third_party/blink/renderer/core/animation/non_interpolable_value.h
index a570f8aa8..0e6e9ef8 100644
--- a/third_party/blink/renderer/core/animation/non_interpolable_value.h
+++ b/third_party/blink/renderer/core/animation/non_interpolable_value.h
@@ -29,12 +29,6 @@
 #define DEFINE_NON_INTERPOLABLE_VALUE_TYPE(T) \
   NonInterpolableValue::Type T::static_type_ = &T::static_type_
 
-#define DEFINE_NON_INTERPOLABLE_VALUE_TYPE_CASTS(T)       \
-  inline bool Is##T(const NonInterpolableValue* value) {  \
-    return !value || value->GetType() == T::static_type_; \
-  }                                                       \
-  DEFINE_TYPE_CASTS(T, NonInterpolableValue, value, Is##T(value), Is##T(&value))
-
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_NON_INTERPOLABLE_VALUE_H_
diff --git a/third_party/blink/renderer/core/animation/path_interpolation_functions.cc b/third_party/blink/renderer/core/animation/path_interpolation_functions.cc
index 531c436..4331bcc 100644
--- a/third_party/blink/renderer/core/animation/path_interpolation_functions.cc
+++ b/third_party/blink/renderer/core/animation/path_interpolation_functions.cc
@@ -41,7 +41,15 @@
 };
 
 DEFINE_NON_INTERPOLABLE_VALUE_TYPE(SVGPathNonInterpolableValue);
-DEFINE_NON_INTERPOLABLE_VALUE_TYPE_CASTS(SVGPathNonInterpolableValue);
+template <>
+struct DowncastTraits<SVGPathNonInterpolableValue> {
+  static bool AllowFrom(const NonInterpolableValue* value) {
+    return value && AllowFrom(*value);
+  }
+  static bool AllowFrom(const NonInterpolableValue& value) {
+    return value.GetType() == SVGPathNonInterpolableValue::static_type_;
+  }
+};
 
 enum PathComponentIndex : unsigned {
   kPathArgsIndex,
@@ -110,7 +118,7 @@
 
   static const Vector<SVGPathSegType>& GetPathSegTypes(
       const InterpolationValue& underlying) {
-    return ToSVGPathNonInterpolableValue(*underlying.non_interpolable_value)
+    return To<SVGPathNonInterpolableValue>(*underlying.non_interpolable_value)
         .PathSegTypes();
   }
 
@@ -154,10 +162,11 @@
     InterpolationValue&& start,
     InterpolationValue&& end) {
   const Vector<SVGPathSegType>& start_types =
-      ToSVGPathNonInterpolableValue(*start.non_interpolable_value)
+      To<SVGPathNonInterpolableValue>(*start.non_interpolable_value)
           .PathSegTypes();
   const Vector<SVGPathSegType>& end_types =
-      ToSVGPathNonInterpolableValue(*end.non_interpolable_value).PathSegTypes();
+      To<SVGPathNonInterpolableValue>(*end.non_interpolable_value)
+          .PathSegTypes();
   if (start_types.size() == 0 || !PathSegTypesMatch(start_types, end_types))
     return nullptr;
 
@@ -181,10 +190,10 @@
   }
 
   DCHECK(PathSegTypesMatch(
-      ToSVGPathNonInterpolableValue(
+      To<SVGPathNonInterpolableValue>(
           *underlying_value_owner.Value().non_interpolable_value)
           .PathSegTypes(),
-      ToSVGPathNonInterpolableValue(*value.non_interpolable_value)
+      To<SVGPathNonInterpolableValue>(*value.non_interpolable_value)
           .PathSegTypes()));
   underlying_value_owner.MutableValue().interpolable_value->ScaleAndAdd(
       neutral_component, *value.interpolable_value);
@@ -200,7 +209,7 @@
   InterpolatedSVGPathSource source(
       To<InterpolableList>(
           *To<InterpolableList>(interpolable_value).Get(kPathArgsIndex)),
-      ToSVGPathNonInterpolableValue(non_interpolable_value)->PathSegTypes());
+      To<SVGPathNonInterpolableValue>(non_interpolable_value)->PathSegTypes());
   SVGPathByteStreamBuilder builder(*path_byte_stream);
   svg_path_parser::ParsePath(source, builder);
   return path_byte_stream;
diff --git a/third_party/blink/renderer/core/animation/size_interpolation_functions.cc b/third_party/blink/renderer/core/animation/size_interpolation_functions.cc
index a2fca49..156dc07 100644
--- a/third_party/blink/renderer/core/animation/size_interpolation_functions.cc
+++ b/third_party/blink/renderer/core/animation/size_interpolation_functions.cc
@@ -54,7 +54,15 @@
 };
 
 DEFINE_NON_INTERPOLABLE_VALUE_TYPE(CSSSizeNonInterpolableValue);
-DEFINE_NON_INTERPOLABLE_VALUE_TYPE_CASTS(CSSSizeNonInterpolableValue);
+template <>
+struct DowncastTraits<CSSSizeNonInterpolableValue> {
+  static bool AllowFrom(const NonInterpolableValue* value) {
+    return value && AllowFrom(*value);
+  }
+  static bool AllowFrom(const NonInterpolableValue& value) {
+    return value.GetType() == CSSSizeNonInterpolableValue::static_type_;
+  }
+};
 
 static InterpolationValue ConvertKeyword(CSSValueID keyword) {
   return InterpolationValue(std::make_unique<InterpolableList>(0),
@@ -134,7 +142,7 @@
 
 InterpolationValue SizeInterpolationFunctions::CreateNeutralValue(
     const NonInterpolableValue* non_interpolable_value) {
-  auto& size = ToCSSSizeNonInterpolableValue(*non_interpolable_value);
+  auto& size = To<CSSSizeNonInterpolableValue>(*non_interpolable_value);
   if (size.IsKeyword())
     return ConvertKeyword(size.Keyword());
   return WrapConvertedLength(
@@ -144,8 +152,8 @@
 bool SizeInterpolationFunctions::NonInterpolableValuesAreCompatible(
     const NonInterpolableValue* a,
     const NonInterpolableValue* b) {
-  const auto& size_a = ToCSSSizeNonInterpolableValue(*a);
-  const auto& size_b = ToCSSSizeNonInterpolableValue(*b);
+  const auto& size_a = To<CSSSizeNonInterpolableValue>(*a);
+  const auto& size_b = To<CSSSizeNonInterpolableValue>(*b);
   if (size_a.IsKeyword() != size_b.IsKeyword())
     return false;
   if (size_a.IsKeyword())
@@ -159,7 +167,7 @@
     const InterpolableValue& interpolable_value,
     const NonInterpolableValue* non_interpolable_value) {
   const auto& size_non_interpolable_value =
-      ToCSSSizeNonInterpolableValue(*non_interpolable_value);
+      To<CSSSizeNonInterpolableValue>(*non_interpolable_value);
   if (size_non_interpolable_value.IsKeyword())
     return;
   underlying_value.MutableInterpolableValue().ScaleAndAdd(underlying_fraction,
@@ -184,8 +192,10 @@
     const InterpolableValue& interpolable_value_b,
     const NonInterpolableValue* non_interpolable_value_b,
     const CSSToLengthConversionData& conversion_data) {
-  const auto& side_a = ToCSSSizeNonInterpolableValue(*non_interpolable_value_a);
-  const auto& side_b = ToCSSSizeNonInterpolableValue(*non_interpolable_value_b);
+  const auto& side_a =
+      To<CSSSizeNonInterpolableValue>(*non_interpolable_value_a);
+  const auto& side_b =
+      To<CSSSizeNonInterpolableValue>(*non_interpolable_value_b);
   if (side_a.IsKeyword()) {
     switch (side_a.Keyword()) {
       case CSSValueID::kCover:
diff --git a/third_party/blink/renderer/core/animation/svg_transform_list_interpolation_type.cc b/third_party/blink/renderer/core/animation/svg_transform_list_interpolation_type.cc
index 8e84b83..34838c3c 100644
--- a/third_party/blink/renderer/core/animation/svg_transform_list_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/svg_transform_list_interpolation_type.cc
@@ -43,7 +43,15 @@
 };
 
 DEFINE_NON_INTERPOLABLE_VALUE_TYPE(SVGTransformNonInterpolableValue);
-DEFINE_NON_INTERPOLABLE_VALUE_TYPE_CASTS(SVGTransformNonInterpolableValue);
+template <>
+struct DowncastTraits<SVGTransformNonInterpolableValue> {
+  static bool AllowFrom(const NonInterpolableValue* value) {
+    return value && AllowFrom(*value);
+  }
+  static bool AllowFrom(const NonInterpolableValue& value) {
+    return value.GetType() == SVGTransformNonInterpolableValue::static_type_;
+  }
+};
 
 namespace {
 
@@ -175,7 +183,7 @@
 
 const Vector<SVGTransformType>& GetTransformTypes(
     const InterpolationValue& value) {
-  return ToSVGTransformNonInterpolableValue(*value.non_interpolable_value)
+  return To<SVGTransformNonInterpolableValue>(*value.non_interpolable_value)
       .TransformTypes();
 }
 
@@ -289,7 +297,7 @@
   auto* result = MakeGarbageCollected<SVGTransformList>();
   const auto& list = To<InterpolableList>(interpolable_value);
   const Vector<SVGTransformType>& transform_types =
-      ToSVGTransformNonInterpolableValue(non_interpolable_value)
+      To<SVGTransformNonInterpolableValue>(non_interpolable_value)
           ->TransformTypes();
   for (wtf_size_t i = 0; i < list.length(); ++i)
     result->Append(FromInterpolableValue(*list.Get(i), transform_types.at(i)));
diff --git a/third_party/blink/renderer/core/animation/svg_value_interpolation_type.cc b/third_party/blink/renderer/core/animation/svg_value_interpolation_type.cc
index 5792ea7..9c463a6 100644
--- a/third_party/blink/renderer/core/animation/svg_value_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/svg_value_interpolation_type.cc
@@ -31,7 +31,15 @@
 };
 
 DEFINE_NON_INTERPOLABLE_VALUE_TYPE(SVGValueNonInterpolableValue);
-DEFINE_NON_INTERPOLABLE_VALUE_TYPE_CASTS(SVGValueNonInterpolableValue);
+template <>
+struct DowncastTraits<SVGValueNonInterpolableValue> {
+  static bool AllowFrom(const NonInterpolableValue* value) {
+    return value && AllowFrom(*value);
+  }
+  static bool AllowFrom(const NonInterpolableValue& value) {
+    return value.GetType() == SVGValueNonInterpolableValue::static_type_;
+  }
+};
 
 InterpolationValue SVGValueInterpolationType::MaybeConvertSVGValue(
     const SVGPropertyBase& value) const {
@@ -45,7 +53,7 @@
 SVGPropertyBase* SVGValueInterpolationType::AppliedSVGValue(
     const InterpolableValue&,
     const NonInterpolableValue* non_interpolable_value) const {
-  return ToSVGValueNonInterpolableValue(*non_interpolable_value).SvgValue();
+  return To<SVGValueNonInterpolableValue>(*non_interpolable_value).SvgValue();
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/css_property_value_set.cc b/third_party/blink/renderer/core/css/css_property_value_set.cc
index 3eaea9b0..bcd7263 100644
--- a/third_party/blink/renderer/core/css/css_property_value_set.cc
+++ b/third_party/blink/renderer/core/css/css_property_value_set.cc
@@ -602,7 +602,8 @@
                                                           list.size());
 }
 
-CSSStyleDeclaration* MutableCSSPropertyValueSet::EnsureCSSStyleDeclaration() {
+CSSStyleDeclaration* MutableCSSPropertyValueSet::EnsureCSSStyleDeclaration(
+    ExecutionContext* execution_context) {
   // FIXME: get rid of this weirdness of a CSSStyleDeclaration inside of a
   // style property set.
   if (cssom_wrapper_) {
@@ -611,7 +612,8 @@
     DCHECK(!cssom_wrapper_->ParentElement());
     return cssom_wrapper_.Get();
   }
-  cssom_wrapper_ = MakeGarbageCollected<PropertySetCSSStyleDeclaration>(*this);
+  cssom_wrapper_ = MakeGarbageCollected<PropertySetCSSStyleDeclaration>(
+      execution_context, *this);
   return cssom_wrapper_.Get();
 }
 
diff --git a/third_party/blink/renderer/core/css/css_property_value_set.h b/third_party/blink/renderer/core/css/css_property_value_set.h
index 1ff28a5..5aaca5a 100644
--- a/third_party/blink/renderer/core/css/css_property_value_set.h
+++ b/third_party/blink/renderer/core/css/css_property_value_set.h
@@ -37,6 +37,7 @@
 namespace blink {
 
 class CSSStyleDeclaration;
+class ExecutionContext;
 class ImmutableCSSPropertyValueSet;
 class MutableCSSPropertyValueSet;
 class StyleSheetContents;
@@ -276,7 +277,8 @@
                             SecureContextMode,
                             StyleSheetContents* context_style_sheet);
 
-  CSSStyleDeclaration* EnsureCSSStyleDeclaration();
+  CSSStyleDeclaration* EnsureCSSStyleDeclaration(
+      ExecutionContext* execution_context);
 
   template <typename T>  // CSSPropertyID or AtomicString
   int FindPropertyIndex(T property) const;
diff --git a/third_party/blink/renderer/core/css/css_style_declaration.cc b/third_party/blink/renderer/core/css/css_style_declaration.cc
index b7a0cb78..23ebe6d 100644
--- a/third_party/blink/renderer/core/css/css_style_declaration.cc
+++ b/third_party/blink/renderer/core/css/css_style_declaration.cc
@@ -152,7 +152,7 @@
 }  // namespace
 
 void CSSStyleDeclaration::Trace(Visitor* visitor) {
-  visitor->Trace(execution_context_);
+  ContextClient::Trace(visitor);
   ScriptWrappable::Trace(visitor);
 }
 
@@ -226,10 +226,4 @@
   return isValidCSSPropertyID(CssPropertyInfo(GetExecutionContext(), name));
 }
 
-ExecutionContext* CSSStyleDeclaration::GetExecutionContext() const {
-  return execution_context_ && !execution_context_->IsContextDestroyed()
-             ? execution_context_.Get()
-             : nullptr;
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/css_style_declaration.h b/third_party/blink/renderer/core/css/css_style_declaration.h
index aa4a7fa..138a2d1 100644
--- a/third_party/blink/renderer/core/css/css_style_declaration.h
+++ b/third_party/blink/renderer/core/css/css_style_declaration.h
@@ -24,6 +24,7 @@
 #include "base/macros.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/css/css_property_names.h"
+#include "third_party/blink/renderer/core/execution_context/context_lifecycle_observer.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/wtf/forward.h"
@@ -37,8 +38,10 @@
 class ExceptionState;
 enum class SecureContextMode;
 
-class CORE_EXPORT CSSStyleDeclaration : public ScriptWrappable {
+class CORE_EXPORT CSSStyleDeclaration : public ScriptWrappable,
+                                        public ContextClient {
   DEFINE_WRAPPERTYPEINFO();
+  USING_GARBAGE_COLLECTED_MIXIN(CSSStyleDeclaration);
 
  public:
   ~CSSStyleDeclaration() override = default;
@@ -102,12 +105,9 @@
   bool NamedPropertyQuery(const AtomicString&, ExceptionState&);
 
  protected:
-  CSSStyleDeclaration(ExecutionContext* context)
-      : execution_context_(context) {}
-  ExecutionContext* GetExecutionContext() const;
+  CSSStyleDeclaration(ExecutionContext* context) : ContextClient(context) {}
 
  private:
-  WeakMember<ExecutionContext> execution_context_;
   DISALLOW_COPY_AND_ASSIGN(CSSStyleDeclaration);
 };
 
diff --git a/third_party/blink/renderer/core/css/properties/computed_style_utils.cc b/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
index ddb858a..131abc6 100644
--- a/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
+++ b/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
@@ -1324,18 +1324,7 @@
   bool is_layout_grid = layout_object && layout_object->IsLayoutGrid();
 
   // Handle the 'none' case.
-  bool track_list_is_empty =
-      track_sizes.IsEmpty() && auto_repeat_track_sizes.IsEmpty();
-  if (is_layout_grid && track_list_is_empty) {
-    // For grids we should consider every listed track, whether implicitly or
-    // explicitly created. Empty grids have a sole grid line per axis.
-    auto& positions = is_row_axis
-                          ? ToLayoutGrid(layout_object)->ColumnPositions()
-                          : ToLayoutGrid(layout_object)->RowPositions();
-    track_list_is_empty = positions.size() == 1;
-  }
-
-  if (track_list_is_empty)
+  if (track_sizes.IsEmpty() && auto_repeat_track_sizes.IsEmpty())
     return CSSIdentifierValue::Create(CSSValueID::kNone);
 
   CSSValueList* list = CSSValueList::CreateSpaceSeparated();
diff --git a/third_party/blink/renderer/core/css/property_set_css_style_declaration.h b/third_party/blink/renderer/core/css/property_set_css_style_declaration.h
index 22a4496..8aa7e20 100644
--- a/third_party/blink/renderer/core/css/property_set_css_style_declaration.h
+++ b/third_party/blink/renderer/core/css/property_set_css_style_declaration.h
@@ -36,9 +36,9 @@
 class PropertySetCSSStyleDeclaration
     : public AbstractPropertySetCSSStyleDeclaration {
  public:
-  // TODO(rodneyding): plumb actual context here to replace nullptr
-  PropertySetCSSStyleDeclaration(MutableCSSPropertyValueSet& property_set)
-      : AbstractPropertySetCSSStyleDeclaration(nullptr),
+  PropertySetCSSStyleDeclaration(ExecutionContext* execution_context,
+                                 MutableCSSPropertyValueSet& property_set)
+      : AbstractPropertySetCSSStyleDeclaration(execution_context),
         property_set_(&property_set) {}
 
   void Trace(blink::Visitor*) override;
diff --git a/third_party/blink/renderer/core/css/style_rule_css_style_declaration.cc b/third_party/blink/renderer/core/css/style_rule_css_style_declaration.cc
index 4b45ec72..341f740 100644
--- a/third_party/blink/renderer/core/css/style_rule_css_style_declaration.cc
+++ b/third_party/blink/renderer/core/css/style_rule_css_style_declaration.cc
@@ -31,7 +31,10 @@
 StyleRuleCSSStyleDeclaration::StyleRuleCSSStyleDeclaration(
     MutableCSSPropertyValueSet& property_set_arg,
     CSSRule* parent_rule)
-    : PropertySetCSSStyleDeclaration(property_set_arg),
+    : PropertySetCSSStyleDeclaration(
+          const_cast<Document*>(CSSStyleSheet::SingleOwnerDocument(
+              parent_rule->parentStyleSheet())),
+          property_set_arg),
       parent_rule_(parent_rule) {}
 
 StyleRuleCSSStyleDeclaration::~StyleRuleCSSStyleDeclaration() = default;
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 7a8748f..e057ecd9 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -5797,13 +5797,15 @@
 }
 
 void Document::EnqueueVisualViewportScrollEvent() {
-  VisualViewportScrollEvent* event = VisualViewportScrollEvent::Create();
+  VisualViewportScrollEvent* event =
+      MakeGarbageCollected<VisualViewportScrollEvent>();
   event->SetTarget(domWindow()->visualViewport());
   scripted_animation_controller_->EnqueuePerFrameEvent(event);
 }
 
 void Document::EnqueueVisualViewportResizeEvent() {
-  VisualViewportResizeEvent* event = VisualViewportResizeEvent::Create();
+  VisualViewportResizeEvent* event =
+      MakeGarbageCollected<VisualViewportResizeEvent>();
   event->SetTarget(domWindow()->visualViewport());
   scripted_animation_controller_->EnqueuePerFrameEvent(event);
 }
diff --git a/third_party/blink/renderer/core/dom/events/tree_scope_event_context.cc b/third_party/blink/renderer/core/dom/events/tree_scope_event_context.cc
index 28fbb1ae..8d0dd9b 100644
--- a/third_party/blink/renderer/core/dom/events/tree_scope_event_context.cc
+++ b/third_party/blink/renderer/core/dom/events/tree_scope_event_context.cc
@@ -32,6 +32,7 @@
 #include "third_party/blink/renderer/core/dom/static_node_list.h"
 #include "third_party/blink/renderer/core/events/touch_event_context.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
 
 namespace blink {
 
@@ -82,7 +83,7 @@
 
 TouchEventContext& TreeScopeEventContext::EnsureTouchEventContext() {
   if (!touch_event_context_)
-    touch_event_context_ = TouchEventContext::Create();
+    touch_event_context_ = MakeGarbageCollected<TouchEventContext>();
   return *touch_event_context_;
 }
 
diff --git a/third_party/blink/renderer/core/editing/commands/style_commands.cc b/third_party/blink/renderer/core/editing/commands/style_commands.cc
index 00eef3d..630fd89 100644
--- a/third_party/blink/renderer/core/editing/commands/style_commands.cc
+++ b/third_party/blink/renderer/core/editing/commands/style_commands.cc
@@ -216,7 +216,8 @@
           frame.Selection().ComputeVisibleSelectionInDOMTreeDeprecated(),
           property_id == CSSPropertyID::kBackgroundColor,
           style_to_check->Style());
-  return style_to_check->TriStateOfStyle(style_at_start, secure_context_mode) !=
+  return style_to_check->TriStateOfStyle(frame.GetDocument(), style_at_start,
+                                         secure_context_mode) !=
          EditingTriState::kFalse;
 }
 
diff --git a/third_party/blink/renderer/core/editing/editing_style.cc b/third_party/blink/renderer/core/editing/editing_style.cc
index 2a49fa2..bd64717 100644
--- a/third_party/blink/renderer/core/editing/editing_style.cc
+++ b/third_party/blink/renderer/core/editing/editing_style.cc
@@ -820,12 +820,14 @@
 }
 
 EditingTriState EditingStyle::TriStateOfStyle(
+    ExecutionContext* execution_context,
     EditingStyle* style,
     SecureContextMode secure_context_mode) const {
   if (!style || !style->mutable_style_)
     return EditingTriState::kFalse;
-  return TriStateOfStyle(style->mutable_style_->EnsureCSSStyleDeclaration(),
-                         kDoNotIgnoreTextOnlyProperties, secure_context_mode);
+  return TriStateOfStyle(
+      style->mutable_style_->EnsureCSSStyleDeclaration(execution_context),
+      kDoNotIgnoreTextOnlyProperties, secure_context_mode);
 }
 
 EditingTriState EditingStyle::TriStateOfStyle(
@@ -868,6 +870,7 @@
 
   if (selection.IsCaret()) {
     return TriStateOfStyle(
+        selection.Start().GetDocument(),
         EditingStyleUtilities::CreateStyleAtSelectionStart(selection),
         secure_context_mode);
   }
@@ -1516,10 +1519,11 @@
       StyleFromMatchedRulesForElement(element,
                                       StyleResolver::kAllButEmptyCSSRules);
   if (style_from_matched_rules && !style_from_matched_rules->IsEmpty()) {
-    mutable_style_ = GetPropertiesNotIn(
-        mutable_style_.Get(),
-        style_from_matched_rules->EnsureCSSStyleDeclaration(),
-        secure_context_mode);
+    mutable_style_ =
+        GetPropertiesNotIn(mutable_style_.Get(),
+                           style_from_matched_rules->EnsureCSSStyleDeclaration(
+                               &element->GetDocument()),
+                           secure_context_mode);
   }
 
   // 2. Remove style present in context and not overriden by matched rules.
@@ -1536,7 +1540,8 @@
                             style_from_matched_rules);
     mutable_style_ = GetPropertiesNotIn(
         mutable_style_.Get(),
-        computed_style->mutable_style_->EnsureCSSStyleDeclaration(),
+        computed_style->mutable_style_->EnsureCSSStyleDeclaration(
+            &element->GetDocument()),
         secure_context_mode);
   }
 
diff --git a/third_party/blink/renderer/core/editing/editing_style.h b/third_party/blink/renderer/core/editing/editing_style.h
index e2cddf7..23815f2 100644
--- a/third_party/blink/renderer/core/editing/editing_style.h
+++ b/third_party/blink/renderer/core/editing/editing_style.h
@@ -101,7 +101,9 @@
     kIgnoreTextOnlyProperties,
     kDoNotIgnoreTextOnlyProperties
   };
-  EditingTriState TriStateOfStyle(EditingStyle*, SecureContextMode) const;
+  EditingTriState TriStateOfStyle(ExecutionContext*,
+                                  EditingStyle*,
+                                  SecureContextMode) const;
   EditingTriState TriStateOfStyle(const VisibleSelection&,
                                   SecureContextMode) const;
   bool ConflictsWithInlineStyleOfElement(HTMLElement* element) const {
diff --git a/third_party/blink/renderer/core/events/after_print_event.h b/third_party/blink/renderer/core/events/after_print_event.h
index 5fad32c..48748f38 100644
--- a/third_party/blink/renderer/core/events/after_print_event.h
+++ b/third_party/blink/renderer/core/events/after_print_event.h
@@ -12,10 +12,6 @@
 
 class AfterPrintEvent final : public Event {
  public:
-  static AfterPrintEvent* Create() {
-    return MakeGarbageCollected<AfterPrintEvent>();
-  }
-
   AfterPrintEvent()
       : Event(event_type_names::kAfterprint, Bubbles::kNo, Cancelable::kNo) {}
   ~AfterPrintEvent() override = default;
diff --git a/third_party/blink/renderer/core/events/resource_progress_event.h b/third_party/blink/renderer/core/events/resource_progress_event.h
index d18ff4f..0ceabc3 100644
--- a/third_party/blink/renderer/core/events/resource_progress_event.h
+++ b/third_party/blink/renderer/core/events/resource_progress_event.h
@@ -47,15 +47,6 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  static ResourceProgressEvent* Create(const AtomicString& type,
-                                       bool length_computable,
-                                       uint64_t loaded,
-                                       uint64_t total,
-                                       const String& url) {
-    return MakeGarbageCollected<ResourceProgressEvent>(type, length_computable,
-                                                       loaded, total, url);
-  }
-
   ResourceProgressEvent(const AtomicString& type,
                         bool length_computable,
                         uint64_t loaded,
diff --git a/third_party/blink/renderer/core/events/touch_event_context.cc b/third_party/blink/renderer/core/events/touch_event_context.cc
index 0761d32..593f571 100644
--- a/third_party/blink/renderer/core/events/touch_event_context.cc
+++ b/third_party/blink/renderer/core/events/touch_event_context.cc
@@ -32,10 +32,6 @@
 
 namespace blink {
 
-TouchEventContext* TouchEventContext::Create() {
-  return MakeGarbageCollected<TouchEventContext>();
-}
-
 TouchEventContext::TouchEventContext()
     : touches_(TouchList::Create()),
       target_touches_(TouchList::Create()),
diff --git a/third_party/blink/renderer/core/events/touch_event_context.h b/third_party/blink/renderer/core/events/touch_event_context.h
index 3288741..3f14cbf 100644
--- a/third_party/blink/renderer/core/events/touch_event_context.h
+++ b/third_party/blink/renderer/core/events/touch_event_context.h
@@ -36,8 +36,6 @@
 
 class TouchEventContext : public GarbageCollected<TouchEventContext> {
  public:
-  static TouchEventContext* Create();
-
   TouchEventContext();
 
   void HandleLocalEvents(Event&) const;
diff --git a/third_party/blink/renderer/core/events/visual_viewport_resize_event.h b/third_party/blink/renderer/core/events/visual_viewport_resize_event.h
index 3d4c1d6f3..b165bf3 100644
--- a/third_party/blink/renderer/core/events/visual_viewport_resize_event.h
+++ b/third_party/blink/renderer/core/events/visual_viewport_resize_event.h
@@ -14,10 +14,6 @@
   VisualViewportResizeEvent();
   ~VisualViewportResizeEvent() override;
 
-  static VisualViewportResizeEvent* Create() {
-    return MakeGarbageCollected<VisualViewportResizeEvent>();
-  }
-
   void DoneDispatchingEventAtCurrentTarget() override;
 
   void Trace(blink::Visitor* visitor) override { Event::Trace(visitor); }
diff --git a/third_party/blink/renderer/core/events/visual_viewport_scroll_event.h b/third_party/blink/renderer/core/events/visual_viewport_scroll_event.h
index 8a9675e..aeac5633 100644
--- a/third_party/blink/renderer/core/events/visual_viewport_scroll_event.h
+++ b/third_party/blink/renderer/core/events/visual_viewport_scroll_event.h
@@ -14,10 +14,6 @@
   VisualViewportScrollEvent();
   ~VisualViewportScrollEvent() override;
 
-  static VisualViewportScrollEvent* Create() {
-    return MakeGarbageCollected<VisualViewportScrollEvent>();
-  }
-
   void DoneDispatchingEventAtCurrentTarget() override;
 
   void Trace(blink::Visitor* visitor) override { Event::Trace(visitor); }
diff --git a/third_party/blink/renderer/core/exported/local_frame_client_impl.h b/third_party/blink/renderer/core/exported/local_frame_client_impl.h
index cda4126..a910975 100644
--- a/third_party/blink/renderer/core/exported/local_frame_client_impl.h
+++ b/third_party/blink/renderer/core/exported/local_frame_client_impl.h
@@ -46,6 +46,7 @@
 #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/casting.h"
 
 namespace blink {
 
@@ -301,11 +302,12 @@
   blink::UserAgentMetadata user_agent_metadata_;
 };
 
-DEFINE_TYPE_CASTS(LocalFrameClientImpl,
-                  LocalFrameClient,
-                  client,
-                  client->IsLocalFrameClientImpl(),
-                  client.IsLocalFrameClientImpl());
+template <>
+struct DowncastTraits<LocalFrameClientImpl> {
+  static bool AllowFrom(const LocalFrameClient& client) {
+    return client.IsLocalFrameClientImpl();
+  }
+};
 
 }  // namespace blink
 
diff --git a/third_party/blink/renderer/core/exported/local_frame_client_impl_test.cc b/third_party/blink/renderer/core/exported/local_frame_client_impl_test.cc
index 72d5ced..fb19c32 100644
--- a/third_party/blink/renderer/core/exported/local_frame_client_impl_test.cc
+++ b/third_party/blink/renderer/core/exported/local_frame_client_impl_test.cc
@@ -86,7 +86,7 @@
     return web_frame_client_;
   }
   LocalFrameClient& GetLocalFrameClient() {
-    return *ToLocalFrameClientImpl(MainFrame()->GetFrame()->Client());
+    return *To<LocalFrameClientImpl>(MainFrame()->GetFrame()->Client());
   }
 
  private:
diff --git a/third_party/blink/renderer/core/exported/web_page_popup_impl.h b/third_party/blink/renderer/core/exported/web_page_popup_impl.h
index 8fea472..d85407f 100644
--- a/third_party/blink/renderer/core/exported/web_page_popup_impl.h
+++ b/third_party/blink/renderer/core/exported/web_page_popup_impl.h
@@ -37,6 +37,7 @@
 #include "third_party/blink/renderer/core/page/page_popup.h"
 #include "third_party/blink/renderer/core/page/page_widget_delegate.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
+#include "third_party/blink/renderer/platform/wtf/casting.h"
 #include "third_party/blink/renderer/platform/wtf/ref_counted.h"
 
 namespace cc {
@@ -180,8 +181,11 @@
 
 // WebPagePopupImpl is the only implementation of WebPagePopup and PagePopup, so
 // no further checking required.
-DEFINE_TYPE_CASTS(WebPagePopupImpl, WebPagePopup, widget, true, true);
-DEFINE_TYPE_CASTS(WebPagePopupImpl, PagePopup, popup, true, true);
+template <>
+struct DowncastTraits<WebPagePopupImpl> {
+  static bool AllowFrom(const WebPagePopup& widget) { return true; }
+  static bool AllowFrom(const PagePopup& popup) { return true; }
+};
 
 }  // namespace blink
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_EXPORTED_WEB_PAGE_POPUP_IMPL_H_
diff --git a/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc b/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc
index fb34395..00aa6b9 100644
--- a/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc
@@ -103,6 +103,7 @@
 #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
 #include "third_party/blink/renderer/platform/graphics/paint/foreign_layer_display_item.h"
 #include "third_party/blink/renderer/platform/graphics/paint/scroll_hit_test_display_item.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/keyboard_codes.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/wtf/assertions.h"
@@ -442,8 +443,8 @@
   if (url.IsEmpty()) {
     event = ProgressEvent::Create(type, length_computable, loaded, total);
   } else {
-    event = ResourceProgressEvent::Create(type, length_computable, loaded,
-                                          total, url);
+    event = MakeGarbageCollected<ResourceProgressEvent>(type, length_computable,
+                                                        loaded, total, url);
   }
   element_->DispatchEvent(*event);
 }
diff --git a/third_party/blink/renderer/core/exported/web_plugin_container_impl.h b/third_party/blink/renderer/core/exported/web_plugin_container_impl.h
index c840f06..8d0e085 100644
--- a/third_party/blink/renderer/core/exported/web_plugin_container_impl.h
+++ b/third_party/blink/renderer/core/exported/web_plugin_container_impl.h
@@ -41,6 +41,7 @@
 #include "third_party/blink/renderer/core/execution_context/context_lifecycle_observer.h"
 #include "third_party/blink/renderer/core/frame/embedded_content_view.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/wtf/casting.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
 namespace cc {
@@ -239,18 +240,16 @@
   bool wants_wheel_events_;
 };
 
-DEFINE_TYPE_CASTS(WebPluginContainerImpl,
-                  EmbeddedContentView,
-                  embedded_content_view,
-                  embedded_content_view->IsPluginView(),
-                  embedded_content_view.IsPluginView());
-// Unlike EmbeddedContentView, we need not worry about object type for
-// container. WebPluginContainerImpl is the only subclass of WebPluginContainer.
-DEFINE_TYPE_CASTS(WebPluginContainerImpl,
-                  WebPluginContainer,
-                  container,
-                  true,
-                  true);
+template <>
+struct DowncastTraits<WebPluginContainerImpl> {
+  static bool AllowFrom(const EmbeddedContentView& embedded_content_view) {
+    return embedded_content_view.IsPluginView();
+  }
+  // Unlike EmbeddedContentView, we need not worry about object type for
+  // container. WebPluginContainerImpl is the only subclass of
+  // WebPluginContainer.
+  static bool AllowFrom(const WebPluginContainer& container) { return true; }
+};
 
 }  // namespace blink
 
diff --git a/third_party/blink/renderer/core/exported/web_plugin_container_test.cc b/third_party/blink/renderer/core/exported/web_plugin_container_test.cc
index 9bf7984..56565a6 100644
--- a/third_party/blink/renderer/core/exported/web_plugin_container_test.cc
+++ b/third_party/blink/renderer/core/exported/web_plugin_container_test.cc
@@ -158,7 +158,7 @@
  public:
   static TestPluginWithEditableText* FromContainer(WebElement* element) {
     WebPlugin* plugin =
-        ToWebPluginContainerImpl(element->PluginContainer())->Plugin();
+        To<WebPluginContainerImpl>(element->PluginContainer())->Plugin();
     return static_cast<TestPluginWithEditableText*>(plugin);
   }
 
@@ -274,7 +274,7 @@
       WebInputEvent::GetStaticTimeStampForTests());
   web_keyboard_event.windows_key_code = key_code;
   KeyboardEvent* key_event = KeyboardEvent::Create(web_keyboard_event, nullptr);
-  ToWebPluginContainerImpl(plugin_container_one_element->PluginContainer())
+  To<WebPluginContainerImpl>(plugin_container_one_element->PluginContainer())
       ->HandleEvent(*key_event);
 }
 
@@ -1134,8 +1134,8 @@
       base_url_ + "plugin_container.html", &plugin_web_frame_client);
   EnablePlugins(web_view, WebSize(300, 300));
 
-  WebPluginContainerImpl* plugin_container_impl =
-      ToWebPluginContainerImpl(GetWebPluginContainer(
+  auto* plugin_container_impl =
+      To<WebPluginContainerImpl>(GetWebPluginContainer(
           web_view, WebString::FromUTF8("translated-plugin")));
   plugin_container_impl->SetFrameRect(IntRect(0, 0, 300, 300));
 
@@ -1158,15 +1158,15 @@
       base_url_ + "plugin_container.html", &plugin_web_frame_client);
   EnablePlugins(web_view, WebSize(300, 300));
 
-  WebPluginContainerImpl* even_plugin_container_impl =
-      ToWebPluginContainerImpl(GetWebPluginContainer(
+  auto* even_plugin_container_impl =
+      To<WebPluginContainerImpl>(GetWebPluginContainer(
           web_view, WebString::FromUTF8("translated-plugin")));
   even_plugin_container_impl->SetFrameRect(IntRect(0, 0, 300, 300));
   auto even_rect = even_plugin_container_impl->GetElement().BoundsInViewport();
   EXPECT_TRUE(even_plugin_container_impl->IsRectTopmost(even_rect));
 
-  WebPluginContainerImpl* odd_plugin_container_impl =
-      ToWebPluginContainerImpl(GetWebPluginContainer(
+  auto* odd_plugin_container_impl =
+      To<WebPluginContainerImpl>(GetWebPluginContainer(
           web_view, WebString::FromUTF8("odd-dimensions-plugin")));
   odd_plugin_container_impl->SetFrameRect(IntRect(0, 0, 300, 300));
   auto odd_rect = odd_plugin_container_impl->GetElement().BoundsInViewport();
@@ -1189,8 +1189,8 @@
                                   ->ToWebLocalFrame()
                                   ->GetDocument()
                                   .GetElementById("translated-plugin");
-  WebPluginContainerImpl* plugin_container_impl =
-      ToWebPluginContainerImpl(plugin_element.PluginContainer());
+  auto* plugin_container_impl =
+      To<WebPluginContainerImpl>(plugin_element.PluginContainer());
 
   DCHECK(plugin_container_impl);
 
@@ -1221,8 +1221,8 @@
       web_view->MainFrame()->FirstChild()->ToWebLocalFrame();
   WebElement plugin_element =
       iframe->GetDocument().GetElementById("plugin-hidden-before-scroll");
-  WebPluginContainerImpl* plugin_container_impl =
-      ToWebPluginContainerImpl(plugin_element.PluginContainer());
+  auto* plugin_container_impl =
+      To<WebPluginContainerImpl>(plugin_element.PluginContainer());
 
   DCHECK(plugin_container_impl);
 
@@ -1315,8 +1315,8 @@
   WebElement plugin_element =
       web_view->MainFrameImpl()->GetDocument().GetElementById(
           "subpixel-positioned-plugin");
-  WebPluginContainerImpl* plugin_container_impl =
-      ToWebPluginContainerImpl(plugin_element.PluginContainer());
+  auto* plugin_container_impl =
+      To<WebPluginContainerImpl>(plugin_element.PluginContainer());
 
   DCHECK(plugin_container_impl);
 
@@ -1360,8 +1360,8 @@
       base_url_ + "plugin_container.html", &plugin_web_frame_client);
   EnablePlugins(web_view, WebSize(300, 300));
 
-  WebPluginContainerImpl* plugin_container_impl =
-      ToWebPluginContainerImpl(GetWebPluginContainer(
+  auto* plugin_container_impl =
+      To<WebPluginContainerImpl>(GetWebPluginContainer(
           web_view, WebString::FromUTF8("translated-plugin")));
   plugin_container_impl->SetFrameRect(IntRect(0, 0, 300, 300));
 
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc
index e54974fd..e6a8b0c 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_view_impl.cc
@@ -1140,7 +1140,7 @@
   // CreatePopup returns nullptr if this renderer process is about to die.
   if (!popup_widget)
     return nullptr;
-  page_popup_ = ToWebPagePopupImpl(popup_widget);
+  page_popup_ = To<WebPagePopupImpl>(popup_widget);
   page_popup_->Initialize(this, client);
   EnablePopupMouseWheelEventListener(frame);
   return page_popup_.get();
@@ -1153,7 +1153,7 @@
 
 void WebViewImpl::ClosePagePopup(PagePopup* popup) {
   DCHECK(popup);
-  WebPagePopupImpl* popup_impl = ToWebPagePopupImpl(popup);
+  auto* popup_impl = To<WebPagePopupImpl>(popup);
   DCHECK_EQ(page_popup_.get(), popup_impl);
   if (page_popup_.get() != popup_impl)
     return;
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.h b/third_party/blink/renderer/core/exported/web_view_impl.h
index 6a08fb3..3154dbd 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.h
+++ b/third_party/blink/renderer/core/exported/web_view_impl.h
@@ -705,10 +705,6 @@
   base::Optional<base::TimeTicks> commit_compositor_frame_start_time_;
 };
 
-// We have no ways to check if the specified WebView is an instance of
-// WebViewImpl because WebViewImpl is the only implementation of WebView.
-DEFINE_TYPE_CASTS(WebViewImpl, WebView, webView, true, true);
-
 }  // namespace blink
 
 #endif
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index 02cb562..0d72e17 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -467,7 +467,7 @@
 }
 
 LocalWindowProxy* LocalFrame::WindowProxy(DOMWrapperWorld& world) {
-  return ToLocalWindowProxy(Frame::GetWindowProxy(world));
+  return To<LocalWindowProxy>(Frame::GetWindowProxy(world));
 }
 
 LocalDOMWindow* LocalFrame::DomWindow() const {
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
index 7547cb66..31d40796 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
@@ -1563,7 +1563,7 @@
   } else {
     // We only support printing plugin nodes for now.
     plugin_container =
-        ToWebPluginContainerImpl(constrain_to_node.PluginContainer());
+        To<WebPluginContainerImpl>(constrain_to_node.PluginContainer());
   }
 
   if (plugin_container && plugin_container->SupportsPaginatedPrint()) {
@@ -1608,7 +1608,7 @@
     WebPrintPresetOptions* preset_options) {
   WebPluginContainerImpl* plugin_container =
       node.IsNull() ? GetFrame()->GetWebPluginContainer()
-                    : ToWebPluginContainerImpl(node.PluginContainer());
+                    : To<WebPluginContainerImpl>(node.PluginContainer());
 
   if (!plugin_container || !plugin_container->SupportsPaginatedPrint())
     return false;
diff --git a/third_party/blink/renderer/core/fullscreen/document_fullscreen.cc b/third_party/blink/renderer/core/fullscreen/document_fullscreen.cc
index 9ae4c22..8b38481 100644
--- a/third_party/blink/renderer/core/fullscreen/document_fullscreen.cc
+++ b/third_party/blink/renderer/core/fullscreen/document_fullscreen.cc
@@ -37,9 +37,11 @@
   return Fullscreen::FullscreenElementForBindingFrom(document);
 }
 
-ScriptPromise DocumentFullscreen::exitFullscreen(ScriptState* script_state,
-                                                 Document& document) {
-  return Fullscreen::ExitFullscreen(document, script_state);
+ScriptPromise DocumentFullscreen::exitFullscreen(
+    ScriptState* script_state,
+    Document& document,
+    ExceptionState& exception_state) {
+  return Fullscreen::ExitFullscreen(document, script_state, &exception_state);
 }
 
 void DocumentFullscreen::webkitExitFullscreen(Document& document) {
diff --git a/third_party/blink/renderer/core/fullscreen/document_fullscreen.h b/third_party/blink/renderer/core/fullscreen/document_fullscreen.h
index cb64f05..9f91003 100644
--- a/third_party/blink/renderer/core/fullscreen/document_fullscreen.h
+++ b/third_party/blink/renderer/core/fullscreen/document_fullscreen.h
@@ -42,7 +42,7 @@
  public:
   static bool fullscreenEnabled(Document&);
   static Element* fullscreenElement(Document&);
-  static ScriptPromise exitFullscreen(ScriptState*, Document&);
+  static ScriptPromise exitFullscreen(ScriptState*, Document&, ExceptionState&);
   static void webkitExitFullscreen(Document&);
 
   DEFINE_STATIC_ATTRIBUTE_EVENT_LISTENER(fullscreenchange, kFullscreenchange)
diff --git a/third_party/blink/renderer/core/fullscreen/document_fullscreen.idl b/third_party/blink/renderer/core/fullscreen/document_fullscreen.idl
index cc2cd81b..82d64a4 100644
--- a/third_party/blink/renderer/core/fullscreen/document_fullscreen.idl
+++ b/third_party/blink/renderer/core/fullscreen/document_fullscreen.idl
@@ -27,7 +27,7 @@
     [LenientSetter] readonly attribute boolean fullscreenEnabled;
     [LenientSetter, Unscopable, ImplementedAs=fullscreenElement] readonly attribute boolean fullscreen;
 
-    [CallWith=ScriptState] Promise<void> exitFullscreen();
+    [CallWith=ScriptState, RaisesException] Promise<void> exitFullscreen();
 
     attribute EventHandler onfullscreenchange;
     attribute EventHandler onfullscreenerror;
diff --git a/third_party/blink/renderer/core/fullscreen/element_fullscreen.cc b/third_party/blink/renderer/core/fullscreen/element_fullscreen.cc
index 425e596..c3326cd5 100644
--- a/third_party/blink/renderer/core/fullscreen/element_fullscreen.cc
+++ b/third_party/blink/renderer/core/fullscreen/element_fullscreen.cc
@@ -13,9 +13,11 @@
 ScriptPromise ElementFullscreen::requestFullscreen(
     ScriptState* script_state,
     Element& element,
-    const FullscreenOptions* options) {
-  return Fullscreen::RequestFullscreen(
-      element, options, Fullscreen::RequestType::kUnprefixed, script_state);
+    const FullscreenOptions* options,
+    ExceptionState& exception_state) {
+  return Fullscreen::RequestFullscreen(element, options,
+                                       Fullscreen::RequestType::kUnprefixed,
+                                       script_state, &exception_state);
 }
 
 void ElementFullscreen::webkitRequestFullscreen(Element& element) {
diff --git a/third_party/blink/renderer/core/fullscreen/element_fullscreen.h b/third_party/blink/renderer/core/fullscreen/element_fullscreen.h
index ecca49b..834b4a28 100644
--- a/third_party/blink/renderer/core/fullscreen/element_fullscreen.h
+++ b/third_party/blink/renderer/core/fullscreen/element_fullscreen.h
@@ -21,7 +21,8 @@
  public:
   static ScriptPromise requestFullscreen(ScriptState*,
                                          Element&,
-                                         const FullscreenOptions*);
+                                         const FullscreenOptions*,
+                                         ExceptionState& exception_state);
 
   DEFINE_STATIC_ATTRIBUTE_EVENT_LISTENER(fullscreenchange, kFullscreenchange)
   DEFINE_STATIC_ATTRIBUTE_EVENT_LISTENER(fullscreenerror, kFullscreenerror)
diff --git a/third_party/blink/renderer/core/fullscreen/element_fullscreen.idl b/third_party/blink/renderer/core/fullscreen/element_fullscreen.idl
index 7bfb572..be3feba09 100644
--- a/third_party/blink/renderer/core/fullscreen/element_fullscreen.idl
+++ b/third_party/blink/renderer/core/fullscreen/element_fullscreen.idl
@@ -7,7 +7,7 @@
 [
     ImplementedAs=ElementFullscreen
 ] partial interface Element {
-    [CallWith=ScriptState] Promise<void> requestFullscreen(optional FullscreenOptions options);
+    [CallWith=ScriptState, RaisesException] Promise<void> requestFullscreen(optional FullscreenOptions options);
 
     attribute EventHandler onfullscreenchange;
     attribute EventHandler onfullscreenerror;
diff --git a/third_party/blink/renderer/core/fullscreen/fullscreen.cc b/third_party/blink/renderer/core/fullscreen/fullscreen.cc
index a24b15e..77b1aaf 100644
--- a/third_party/blink/renderer/core/fullscreen/fullscreen.cc
+++ b/third_party/blink/renderer/core/fullscreen/fullscreen.cc
@@ -570,7 +570,8 @@
 ScriptPromise Fullscreen::RequestFullscreen(Element& pending,
                                             const FullscreenOptions* options,
                                             RequestType request_type,
-                                            ScriptState* script_state) {
+                                            ScriptState* script_state,
+                                            ExceptionState* exception_state) {
   RequestFullscreenScope scope;
 
   // 1. Let |pending| be the context object.
@@ -585,11 +586,10 @@
   // 4. If |pendingDoc| is not fully active, then reject |promise| with a
   // TypeError exception and return |promise|.
   if (!document.IsActive() || !document.GetFrame()) {
-    if (!script_state)
+    if (!exception_state)
       return ScriptPromise();
-    return ScriptPromise::Reject(
-        script_state, V8ThrowException::CreateTypeError(
-                          script_state->GetIsolate(), "Document not active"));
+    exception_state->ThrowTypeError("Document not active");
+    return ScriptPromise();
   }
 
   if (script_state) {
@@ -782,12 +782,13 @@
   DCHECK(IsSimpleFullscreenDocument(doc));
 
   // 3. Exit fullscreen |document|.
-  ExitFullscreen(doc, nullptr, ua_originated);
+  ExitFullscreen(doc, nullptr, nullptr, ua_originated);
 }
 
 // https://fullscreen.spec.whatwg.org/#exit-fullscreen
 ScriptPromise Fullscreen::ExitFullscreen(Document& doc,
                                          ScriptState* script_state,
+                                         ExceptionState* exception_state,
                                          bool ua_originated) {
   // 1. Let |promise| be a new promise.
   // ScriptPromiseResolver is allocated after step 2.
@@ -796,11 +797,10 @@
   // 2. If |doc| is not fully active or |doc|'s fullscreen element is null, then
   // reject |promise| with a TypeError exception and return |promise|.
   if (!doc.IsActive() || !doc.GetFrame() || !FullscreenElementFrom(doc)) {
-    if (!script_state)
+    if (!exception_state)
       return ScriptPromise();
-    return ScriptPromise::Reject(
-        script_state, V8ThrowException::CreateTypeError(
-                          script_state->GetIsolate(), "Document not active"));
+    exception_state->ThrowTypeError("Document not active");
+    return ScriptPromise();
   }
 
   if (script_state)
@@ -1003,7 +1003,7 @@
   // 3.1. If |node| is its node document's fullscreen element, exit fullscreen
   // that document.
   if (IsFullscreenElement(node)) {
-    ExitFullscreen(document, nullptr, false);
+    ExitFullscreen(document);
   } else {
     // 3.2. Otherwise, unfullscreen |node| within its node document.
     Unfullscreen(node);
diff --git a/third_party/blink/renderer/core/fullscreen/fullscreen.h b/third_party/blink/renderer/core/fullscreen/fullscreen.h
index 87f1dd6..c55a5342 100644
--- a/third_party/blink/renderer/core/fullscreen/fullscreen.h
+++ b/third_party/blink/renderer/core/fullscreen/fullscreen.h
@@ -79,14 +79,17 @@
   };
 
   static void RequestFullscreen(Element&);
-  static ScriptPromise RequestFullscreen(Element&,
-                                         const FullscreenOptions*,
-                                         RequestType,
-                                         ScriptState* state = nullptr);
+  static ScriptPromise RequestFullscreen(
+      Element&,
+      const FullscreenOptions*,
+      RequestType,
+      ScriptState* state = nullptr,
+      ExceptionState* exception_state = nullptr);
 
   static void FullyExitFullscreen(Document&, bool ua_originated = false);
   static ScriptPromise ExitFullscreen(Document&,
                                       ScriptState* state = nullptr,
+                                      ExceptionState* exception_state = nullptr,
                                       bool ua_originated = false);
 
   static bool FullscreenEnabled(Document&);
diff --git a/third_party/blink/renderer/core/html/html_frame_owner_element.cc b/third_party/blink/renderer/core/html/html_frame_owner_element.cc
index ef9ebce..0779508 100644
--- a/third_party/blink/renderer/core/html/html_frame_owner_element.cc
+++ b/third_party/blink/renderer/core/html/html_frame_owner_element.cc
@@ -379,7 +379,7 @@
     if (old_view->IsAttached()) {
       old_view->DetachFromLayout();
       if (old_view->IsPluginView())
-        DisposePluginSoon(ToWebPluginContainerImpl(old_view));
+        DisposePluginSoon(To<WebPluginContainerImpl>(old_view));
       else
         old_view->Dispose();
     }
diff --git a/third_party/blink/renderer/core/html/html_plugin_element.cc b/third_party/blink/renderer/core/html/html_plugin_element.cc
index 9163e3f..4213805 100644
--- a/third_party/blink/renderer/core/html/html_plugin_element.cc
+++ b/third_party/blink/renderer/core/html/html_plugin_element.cc
@@ -291,7 +291,8 @@
   // Only try to persist a plugin we actually own.
   WebPluginContainerImpl* plugin = OwnedPlugin();
   if (plugin && keep_plugin) {
-    SetPersistedPlugin(ToWebPluginContainerImpl(ReleaseEmbeddedContentView()));
+    SetPersistedPlugin(
+        To<WebPluginContainerImpl>(ReleaseEmbeddedContentView()));
   } else {
     // A persisted plugin isn't processed and hooked up immediately
     // (synchronously) when attaching the layout object, so it's possible that
@@ -391,7 +392,7 @@
 WebPluginContainerImpl* HTMLPlugInElement::OwnedPlugin() const {
   EmbeddedContentView* view = OwnedEmbeddedContentView();
   if (view && view->IsPluginView())
-    return ToWebPluginContainerImpl(view);
+    return To<WebPluginContainerImpl>(view);
   return nullptr;
 }
 
diff --git a/third_party/blink/renderer/core/html/shadow/shadow_element_names.cc b/third_party/blink/renderer/core/html/shadow/shadow_element_names.cc
index ccdca29..d1f4ee4 100644
--- a/third_party/blink/renderer/core/html/shadow/shadow_element_names.cc
+++ b/third_party/blink/renderer/core/html/shadow/shadow_element_names.cc
@@ -89,11 +89,6 @@
   return name;
 }
 
-const AtomicString& SearchDecoration() {
-  DEFINE_STATIC_LOCAL(AtomicString, name, ("decoration"));
-  return name;
-}
-
 const AtomicString& SliderThumb() {
   DEFINE_STATIC_LOCAL(AtomicString, name, ("thumb"));
   return name;
diff --git a/third_party/blink/renderer/core/html/shadow/shadow_element_names.h b/third_party/blink/renderer/core/html/shadow/shadow_element_names.h
index 3e8859e7..3d0dd77 100644
--- a/third_party/blink/renderer/core/html/shadow/shadow_element_names.h
+++ b/third_party/blink/renderer/core/html/shadow/shadow_element_names.h
@@ -48,7 +48,6 @@
 const AtomicString& PickerIndicator();
 const AtomicString& Placeholder();
 const AtomicString& SearchClearButton();
-const AtomicString& SearchDecoration();
 const AtomicString& PasswordRevealButton();
 CORE_EXPORT const AtomicString& SliderThumb();
 const AtomicString& SliderTrack();
diff --git a/third_party/blink/renderer/core/inspector/inspector_css_agent.cc b/third_party/blink/renderer/core/inspector/inspector_css_agent.cc
index 482fb35a..a27174e 100644
--- a/third_party/blink/renderer/core/inspector/inspector_css_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_css_agent.cc
@@ -2078,7 +2078,9 @@
     return nullptr;
 
   InspectorStyle* inspector_style = MakeGarbageCollected<InspectorStyle>(
-      mutable_attribute_style->EnsureCSSStyleDeclaration(), nullptr, nullptr);
+      mutable_attribute_style->EnsureCSSStyleDeclaration(
+          &element->GetDocument()),
+      nullptr, nullptr);
   return inspector_style->BuildObjectForStyle();
 }
 
diff --git a/third_party/blink/renderer/core/layout/layout_embedded_content.cc b/third_party/blink/renderer/core/layout/layout_embedded_content.cc
index f768337..8930d001 100644
--- a/third_party/blink/renderer/core/layout/layout_embedded_content.cc
+++ b/third_party/blink/renderer/core/layout/layout_embedded_content.cc
@@ -95,7 +95,7 @@
 WebPluginContainerImpl* LayoutEmbeddedContent::Plugin() const {
   EmbeddedContentView* embedded_content_view = GetEmbeddedContentView();
   if (embedded_content_view && embedded_content_view->IsPluginView())
-    return ToWebPluginContainerImpl(embedded_content_view);
+    return To<WebPluginContainerImpl>(embedded_content_view);
   return nullptr;
 }
 
diff --git a/third_party/blink/renderer/core/layout/layout_grid.cc b/third_party/blink/renderer/core/layout/layout_grid.cc
index 0d9e5a5..8becf47 100644
--- a/third_party/blink/renderer/core/layout/layout_grid.cc
+++ b/third_party/blink/renderer/core/layout/layout_grid.cc
@@ -1219,11 +1219,22 @@
   DCHECK(!grid_->NeedsItemsPlacement());
   bool has_collapsed_tracks = grid_->HasAutoRepeatEmptyTracks(direction);
   LayoutUnit gap = !has_collapsed_tracks ? GridGap(direction) : LayoutUnit();
-  tracks.ReserveCapacity(num_positions - 1);
-  for (size_t i = 0; i < num_positions - 2; ++i)
+  size_t explicit_start = -grid_->SmallestTrackStart(direction);
+  size_t explicit_end = explicit_start +
+                        (is_row_axis ? StyleRef().GridTemplateColumns()
+                                     : StyleRef().GridTemplateRows())
+                            .size() +
+                        AutoRepeatCountForDirection(direction);
+  // Usually we have `explicit_end <= num_positions - 1`, but the latter may be
+  // smaller when the maximum number of tracks is reached.
+  explicit_end = std::min(explicit_end, num_positions - 1);
+  tracks.ReserveCapacity(explicit_end - explicit_start);
+  size_t loop_end = std::min(explicit_end, num_positions - 2);
+  for (size_t i = explicit_start; i < loop_end; ++i)
     tracks.push_back(positions[i + 1] - positions[i] - offset_between_tracks -
                      gap);
-  tracks.push_back(positions[num_positions - 1] - positions[num_positions - 2]);
+  if (loop_end < explicit_end)
+    tracks.push_back(positions[explicit_end] - positions[explicit_end - 1]);
 
   if (!has_collapsed_tracks)
     return tracks;
@@ -1233,7 +1244,7 @@
   size_t last_line = tracks.size();
   gap = GridGap(direction);
   for (size_t i = 1; i < last_line; ++i) {
-    if (grid_->IsEmptyAutoRepeatTrack(direction, i - 1)) {
+    if (grid_->IsEmptyAutoRepeatTrack(direction, i - 1 + explicit_start)) {
       --remaining_empty_tracks;
     } else {
       // Remove the gap between consecutive non empty tracks. Remove it also
@@ -1242,7 +1253,7 @@
       bool all_remaining_tracks_are_empty =
           remaining_empty_tracks == (last_line - i);
       if (!all_remaining_tracks_are_empty ||
-          !grid_->IsEmptyAutoRepeatTrack(direction, i))
+          !grid_->IsEmptyAutoRepeatTrack(direction, i + explicit_start))
         tracks[i - 1] -= gap;
     }
   }
diff --git a/third_party/blink/renderer/core/layout/layout_inline.cc b/third_party/blink/renderer/core/layout/layout_inline.cc
index a8a8444..383705c 100644
--- a/third_party/blink/renderer/core/layout/layout_inline.cc
+++ b/third_party/blink/renderer/core/layout/layout_inline.cc
@@ -790,9 +790,16 @@
     const auto* box_fragment = ContainingBlockFlowFragmentOf(*this);
     if (!box_fragment)
       return;
-    for (const auto& fragment :
-         NGInlineFragmentTraversal::SelfFragmentsOf(*box_fragment, this))
-      yield(fragment.RectInContainerBox());
+    if (!RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) {
+      for (const auto& fragment :
+           NGInlineFragmentTraversal::SelfFragmentsOf(*box_fragment, this))
+        yield(fragment.RectInContainerBox());
+      return;
+    }
+    NGInlineCursor cursor;
+    cursor.MoveTo(*this);
+    for (; cursor; cursor.MoveToNextForSameLayoutObject())
+      yield(cursor.CurrentRect());
     return;
   }
   if (!AlwaysCreateLineBoxes()) {
diff --git a/third_party/blink/renderer/core/layout/layout_search_field.cc b/third_party/blink/renderer/core/layout/layout_search_field.cc
index 54159e3..eccbbb1 100644
--- a/third_party/blink/renderer/core/layout/layout_search_field.cc
+++ b/third_party/blink/renderer/core/layout/layout_search_field.cc
@@ -38,11 +38,6 @@
 
 LayoutSearchField::~LayoutSearchField() = default;
 
-inline Element* LayoutSearchField::SearchDecorationElement() const {
-  return InputElement()->UserAgentShadowRoot()->getElementById(
-      shadow_element_names::SearchDecoration());
-}
-
 inline Element* LayoutSearchField::CancelButtonElement() const {
   return InputElement()->UserAgentShadowRoot()->getElementById(
       shadow_element_names::ClearButton());
@@ -51,16 +46,6 @@
 LayoutUnit LayoutSearchField::ComputeControlLogicalHeight(
     LayoutUnit line_height,
     LayoutUnit non_content_height) const {
-  Element* search_decoration = SearchDecorationElement();
-  if (LayoutBox* decoration_layout_object =
-          search_decoration ? search_decoration->GetLayoutBox() : nullptr) {
-    decoration_layout_object->UpdateLogicalHeight();
-    non_content_height =
-        max(non_content_height,
-            decoration_layout_object->BorderAndPaddingLogicalHeight() +
-                decoration_layout_object->MarginLogicalHeight());
-    line_height = max(line_height, decoration_layout_object->LogicalHeight());
-  }
   Element* cancel_button = CancelButtonElement();
   if (LayoutBox* cancel_layout_object =
           cancel_button ? cancel_button->GetLayoutBox() : nullptr) {
diff --git a/third_party/blink/renderer/core/layout/layout_search_field.h b/third_party/blink/renderer/core/layout/layout_search_field.h
index f9146cab..f647d54 100644
--- a/third_party/blink/renderer/core/layout/layout_search_field.h
+++ b/third_party/blink/renderer/core/layout/layout_search_field.h
@@ -40,7 +40,6 @@
       LayoutUnit line_height,
       LayoutUnit non_content_height) const override;
 
-  Element* SearchDecorationElement() const;
   Element* CancelButtonElement() const;
 };
 
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_fieldset.cc b/third_party/blink/renderer/core/layout/ng/layout_ng_fieldset.cc
index a39b2b4d..907d45b 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_fieldset.cc
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_fieldset.cc
@@ -73,17 +73,4 @@
   return type == kLayoutObjectNGFieldset || LayoutNGBlockFlow::IsOfType(type);
 }
 
-void LayoutNGFieldset::Paint(const PaintInfo& paint_info) const {
-  // TODO(crbug.com/988015): This override should not be needed when painting
-  // fragment is enabled in parent classes.
-  if (!RuntimeEnabledFeatures::LayoutNGFragmentPaintEnabled()) {
-    if (const NGPhysicalBoxFragment* fragment = CurrentFragment()) {
-      NGBoxFragmentPainter(*fragment, PaintFragment()).Paint(paint_info);
-      return;
-    }
-  }
-
-  LayoutNGBlockFlow::Paint(paint_info);
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_fieldset.h b/third_party/blink/renderer/core/layout/ng/layout_ng_fieldset.h
index 4076cdb..b412c6c 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_fieldset.h
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_fieldset.h
@@ -21,8 +21,6 @@
 
   bool CreatesNewFormattingContext() const final { return true; }
 
-  void Paint(const PaintInfo&) const final;
-
  protected:
   bool IsOfType(LayoutObjectType) const override;
 };
diff --git a/third_party/blink/renderer/core/trustedtypes/trusted_type_policy.cc b/third_party/blink/renderer/core/trustedtypes/trusted_type_policy.cc
index cc197c6..1b3bd49 100644
--- a/third_party/blink/renderer/core/trustedtypes/trusted_type_policy.cc
+++ b/third_party/blink/renderer/core/trustedtypes/trusted_type_policy.cc
@@ -98,6 +98,18 @@
   return MakeGarbageCollected<TrustedScriptURL>(script_url);
 }
 
+bool TrustedTypePolicy::HasCreateHTML() {
+  return policy_options_->createHTML();
+}
+
+bool TrustedTypePolicy::HasCreateScript() {
+  return policy_options_->createScript();
+}
+
+bool TrustedTypePolicy::HasCreateScriptURL() {
+  return policy_options_->createScriptURL();
+}
+
 String TrustedTypePolicy::name() const {
   return name_;
 }
diff --git a/third_party/blink/renderer/core/trustedtypes/trusted_type_policy.h b/third_party/blink/renderer/core/trustedtypes/trusted_type_policy.h
index dff1663..cf1e114 100644
--- a/third_party/blink/renderer/core/trustedtypes/trusted_type_policy.h
+++ b/third_party/blink/renderer/core/trustedtypes/trusted_type_policy.h
@@ -38,6 +38,10 @@
                                     const String&,
                                     ExceptionState&);
 
+  bool HasCreateHTML();
+  bool HasCreateScript();
+  bool HasCreateScriptURL();
+
   String name() const;
 
   void Trace(blink::Visitor*) override;
diff --git a/third_party/blink/renderer/core/trustedtypes/trusted_types_util.cc b/third_party/blink/renderer/core/trustedtypes/trusted_types_util.cc
index 939c9cd..ae483b3 100644
--- a/third_party/blink/renderer/core/trustedtypes/trusted_types_util.cc
+++ b/third_party/blink/renderer/core/trustedtypes/trusted_types_util.cc
@@ -353,6 +353,14 @@
     return string;
   }
 
+  if (!default_policy->HasCreateHTML()) {
+    if (TrustedTypeFail(kTrustedHTMLAssignmentAndDefaultPolicyFailed,
+                        execution_context, exception_state, string)) {
+      return g_empty_string;
+    } else {
+      return string;
+    }
+  }
   TrustedHTML* result = default_policy->CreateHTML(
       execution_context->GetIsolate(), string, exception_state);
   if (exception_state.HadException()) {
@@ -410,6 +418,14 @@
     return potential_script;
   }
 
+  if (!default_policy->HasCreateScript()) {
+    if (TrustedTypeFail(kTrustedScriptAssignmentAndDefaultPolicyFailed,
+                        execution_context, exception_state, potential_script)) {
+      return g_empty_string;
+    } else {
+      return potential_script;
+    }
+  }
   TrustedScript* result = default_policy->CreateScript(
       execution_context->GetIsolate(), potential_script, exception_state);
   DCHECK_EQ(!result, exception_state.HadException());
@@ -457,6 +473,14 @@
     return string;
   }
 
+  if (!default_policy->HasCreateScriptURL()) {
+    if (TrustedTypeFail(kTrustedScriptURLAssignmentAndDefaultPolicyFailed,
+                        execution_context, exception_state, string)) {
+      return g_empty_string;
+    } else {
+      return string;
+    }
+  }
   TrustedScriptURL* result = default_policy->CreateScriptURL(
       execution_context->GetIsolate(), string, exception_state);
 
diff --git a/third_party/blink/renderer/modules/compression/deflate_transformer.cc b/third_party/blink/renderer/modules/compression/deflate_transformer.cc
index 129412d..4ee5a115 100644
--- a/third_party/blink/renderer/modules/compression/deflate_transformer.cc
+++ b/third_party/blink/renderer/modules/compression/deflate_transformer.cc
@@ -66,8 +66,14 @@
   if (buffer_source.IsArrayBufferView()) {
     const auto* view = buffer_source.GetAsArrayBufferView().View();
     const uint8_t* start = static_cast<const uint8_t*>(view->BaseAddress());
-    wtf_size_t length = view->deprecatedByteLengthAsUnsigned();
-    Deflate(start, length, IsFinished(false), controller, exception_state);
+    size_t length = view->byteLengthAsSizeT();
+    if (length > std::numeric_limits<wtf_size_t>::max()) {
+      exception_state.ThrowRangeError(
+          "Buffer size exceeds maximum heap object size.");
+      return ScriptPromise();
+    }
+    Deflate(start, static_cast<wtf_size_t>(length), IsFinished(false),
+            controller, exception_state);
     return ScriptPromise::CastUndefined(script_state_);
   }
   DCHECK(buffer_source.IsArrayBuffer());
diff --git a/third_party/blink/renderer/modules/compression/inflate_transformer.cc b/third_party/blink/renderer/modules/compression/inflate_transformer.cc
index fe66e8d..414b126a 100644
--- a/third_party/blink/renderer/modules/compression/inflate_transformer.cc
+++ b/third_party/blink/renderer/modules/compression/inflate_transformer.cc
@@ -64,8 +64,14 @@
   if (buffer_source.IsArrayBufferView()) {
     const auto* view = buffer_source.GetAsArrayBufferView().View();
     const uint8_t* start = static_cast<const uint8_t*>(view->BaseAddress());
-    wtf_size_t length = view->deprecatedByteLengthAsUnsigned();
-    Inflate(start, length, IsFinished(false), controller, exception_state);
+    size_t length = view->byteLengthAsSizeT();
+    if (length > std::numeric_limits<wtf_size_t>::max()) {
+      exception_state.ThrowRangeError(
+          "Buffer size exceeds maximum heap object size.");
+      return ScriptPromise();
+    }
+    Inflate(start, static_cast<wtf_size_t>(length), IsFinished(false),
+            controller, exception_state);
     return ScriptPromise::CastUndefined(script_state_);
   }
   DCHECK(buffer_source.IsArrayBuffer());
diff --git a/third_party/blink/renderer/modules/encryptedmedia/html_media_element_encrypted_media.cc b/third_party/blink/renderer/modules/encryptedmedia/html_media_element_encrypted_media.cc
index 639d280..03850a5e 100644
--- a/third_party/blink/renderer/modules/encryptedmedia/html_media_element_encrypted_media.cc
+++ b/third_party/blink/renderer/modules/encryptedmedia/html_media_element_encrypted_media.cc
@@ -362,7 +362,8 @@
 ScriptPromise HTMLMediaElementEncryptedMedia::setMediaKeys(
     ScriptState* script_state,
     HTMLMediaElement& element,
-    MediaKeys* media_keys) {
+    MediaKeys* media_keys,
+    ExceptionState& exception_state) {
   HTMLMediaElementEncryptedMedia& this_element =
       HTMLMediaElementEncryptedMedia::From(element);
   DVLOG(EME_LOG_LEVEL) << __func__ << ": current("
@@ -374,10 +375,9 @@
   // 1. If this object's attaching media keys value is true, return a
   //    promise rejected with an InvalidStateError.
   if (this_element.is_attaching_media_keys_) {
-    return ScriptPromise::RejectWithDOMException(
-        script_state,
-        MakeGarbageCollected<DOMException>(DOMExceptionCode::kInvalidStateError,
-                                           "Another request is in progress."));
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      "Another request is in progress.");
+    return ScriptPromise();
   }
 
   // 2. If mediaKeys and the mediaKeys attribute are the same object,
diff --git a/third_party/blink/renderer/modules/encryptedmedia/html_media_element_encrypted_media.h b/third_party/blink/renderer/modules/encryptedmedia/html_media_element_encrypted_media.h
index fb687dfd..6930976 100644
--- a/third_party/blink/renderer/modules/encryptedmedia/html_media_element_encrypted_media.h
+++ b/third_party/blink/renderer/modules/encryptedmedia/html_media_element_encrypted_media.h
@@ -20,6 +20,7 @@
 
 namespace blink {
 
+class ExceptionState;
 class HTMLMediaElement;
 class MediaKeys;
 class ScriptPromise;
@@ -38,7 +39,8 @@
   static MediaKeys* mediaKeys(HTMLMediaElement&);
   static ScriptPromise setMediaKeys(ScriptState*,
                                     HTMLMediaElement&,
-                                    MediaKeys*);
+                                    MediaKeys*,
+                                    ExceptionState&);
   DEFINE_STATIC_ATTRIBUTE_EVENT_LISTENER(encrypted, kEncrypted)
   DEFINE_STATIC_ATTRIBUTE_EVENT_LISTENER(waitingforkey, kWaitingforkey)
 
diff --git a/third_party/blink/renderer/modules/encryptedmedia/html_media_element_encrypted_media.idl b/third_party/blink/renderer/modules/encryptedmedia/html_media_element_encrypted_media.idl
index 8c1a025..ff89800 100644
--- a/third_party/blink/renderer/modules/encryptedmedia/html_media_element_encrypted_media.idl
+++ b/third_party/blink/renderer/modules/encryptedmedia/html_media_element_encrypted_media.idl
@@ -7,7 +7,7 @@
     ImplementedAs=HTMLMediaElementEncryptedMedia
 ] partial interface HTMLMediaElement {
     [SecureContext] readonly attribute MediaKeys mediaKeys;
-    [SecureContext, CallWith=ScriptState] Promise<void> setMediaKeys(MediaKeys? mediaKeys);
+    [SecureContext, CallWith=ScriptState, RaisesException] Promise<void> setMediaKeys(MediaKeys? mediaKeys);
     attribute EventHandler onencrypted;
     attribute EventHandler onwaitingforkey;
 };
diff --git a/third_party/blink/renderer/modules/encryptedmedia/media_key_session.cc b/third_party/blink/renderer/modules/encryptedmedia/media_key_session.cc
index 87f3fd20..2e73aaa 100644
--- a/third_party/blink/renderer/modules/encryptedmedia/media_key_session.cc
+++ b/third_party/blink/renderer/modules/encryptedmedia/media_key_session.cc
@@ -48,6 +48,7 @@
 #include "third_party/blink/renderer/modules/encryptedmedia/media_key_message_event.h"
 #include "third_party/blink/renderer/modules/encryptedmedia/media_keys.h"
 #include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h"
+#include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "third_party/blink/renderer/platform/bindings/v8_throw_exception.h"
 #include "third_party/blink/renderer/platform/content_decryption_module_result.h"
@@ -108,27 +109,24 @@
 }
 
 static ScriptPromise CreateRejectedPromiseNotCallable(
-    ScriptState* script_state) {
-  return ScriptPromise::RejectWithDOMException(
-      script_state,
-      MakeGarbageCollected<DOMException>(DOMExceptionCode::kInvalidStateError,
-                                         "The session is not callable."));
+    ExceptionState& exception_state) {
+  exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                    "The session is not callable.");
+  return ScriptPromise();
 }
 
 static ScriptPromise CreateRejectedPromiseAlreadyClosed(
-    ScriptState* script_state) {
-  return ScriptPromise::RejectWithDOMException(
-      script_state,
-      MakeGarbageCollected<DOMException>(DOMExceptionCode::kInvalidStateError,
-                                         "The session is already closed."));
+    ExceptionState& exception_state) {
+  exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                    "The session is already closed.");
+  return ScriptPromise();
 }
 
 static ScriptPromise CreateRejectedPromiseAlreadyInitialized(
-    ScriptState* script_state) {
-  return ScriptPromise::RejectWithDOMException(
-      script_state, MakeGarbageCollected<DOMException>(
-                        DOMExceptionCode::kInvalidStateError,
-                        "The session is already initialized."));
+    ExceptionState& exception_state) {
+  exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                    "The session is already initialized.");
+  return ScriptPromise();
 }
 
 // A class holding a pending action.
@@ -428,7 +426,8 @@
 ScriptPromise MediaKeySession::generateRequest(
     ScriptState* script_state,
     const String& init_data_type_string,
-    const DOMArrayPiece& init_data) {
+    const DOMArrayPiece& init_data,
+    ExceptionState& exception_state) {
   DVLOG(MEDIA_KEY_SESSION_LOG_LEVEL)
       << __func__ << "(" << this << ") " << init_data_type_string;
 
@@ -439,12 +438,13 @@
   // 1. If this object's closing or closed value is true, return a promise
   //    rejected with an InvalidStateError.
   if (is_closing_or_closed_)
-    return CreateRejectedPromiseAlreadyClosed(script_state);
+    return CreateRejectedPromiseAlreadyClosed(exception_state);
 
   // 2. If this object's uninitialized value is false, return a promise
   //    rejected with an InvalidStateError.
-  if (!is_uninitialized_)
-    return CreateRejectedPromiseAlreadyInitialized(script_state);
+  if (!is_uninitialized_) {
+    return CreateRejectedPromiseAlreadyInitialized(exception_state);
+  }
 
   // 3. Let this object's uninitialized be false.
   is_uninitialized_ = false;
@@ -452,19 +452,15 @@
   // 4. If initDataType is the empty string, return a promise rejected
   //    with a newly created TypeError.
   if (init_data_type_string.IsEmpty()) {
-    return ScriptPromise::Reject(script_state,
-                                 V8ThrowException::CreateTypeError(
-                                     script_state->GetIsolate(),
-                                     "The initDataType parameter is empty."));
+    exception_state.ThrowTypeError("The initDataType parameter is empty.");
+    return ScriptPromise();
   }
 
   // 5. If initData is an empty array, return a promise rejected with a
   //    newly created TypeError.
   if (!init_data.ByteLengthAsSizeT()) {
-    return ScriptPromise::Reject(
-        script_state,
-        V8ThrowException::CreateTypeError(script_state->GetIsolate(),
-                                          "The initData parameter is empty."));
+    exception_state.ThrowTypeError("The initData parameter is empty.");
+    return ScriptPromise();
   }
 
   // 6. If the Key System implementation represented by this object's cdm
@@ -478,11 +474,11 @@
   media::EmeInitDataType init_data_type =
       EncryptedMediaUtils::ConvertToInitDataType(init_data_type_string);
   if (init_data_type == media::EmeInitDataType::UNKNOWN) {
-    return ScriptPromise::RejectWithDOMException(
-        script_state, MakeGarbageCollected<DOMException>(
-                          DOMExceptionCode::kNotSupportedError,
-                          "The initialization data type '" +
-                              init_data_type_string + "' is not supported."));
+    exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError,
+                                      "The initialization data type '" +
+                                          init_data_type_string +
+                                          "' is not supported.");
+    return ScriptPromise();
   }
 
   // 7. Let init data be a copy of the contents of the initData parameter.
@@ -545,7 +541,8 @@
 }
 
 ScriptPromise MediaKeySession::load(ScriptState* script_state,
-                                    const String& session_id) {
+                                    const String& session_id,
+                                    ExceptionState& exception_state) {
   DVLOG(MEDIA_KEY_SESSION_LOG_LEVEL)
       << __func__ << "(" << this << ") " << session_id;
 
@@ -556,12 +553,13 @@
   // 1. If this object's closing or closed value is true, return a promise
   //    rejected with an InvalidStateError.
   if (is_closing_or_closed_)
-    return CreateRejectedPromiseAlreadyClosed(script_state);
+    return CreateRejectedPromiseAlreadyClosed(exception_state);
 
   // 2. If this object's uninitialized value is false, return a promise
   //    rejected with an InvalidStateError.
-  if (!is_uninitialized_)
-    return CreateRejectedPromiseAlreadyInitialized(script_state);
+  if (!is_uninitialized_) {
+    return CreateRejectedPromiseAlreadyInitialized(exception_state);
+  }
 
   // 3. Let this object's uninitialized value be false.
   is_uninitialized_ = false;
@@ -569,20 +567,16 @@
   // 4. If sessionId is the empty string, return a promise rejected with
   //    a newly created TypeError.
   if (session_id.IsEmpty()) {
-    return ScriptPromise::Reject(
-        script_state,
-        V8ThrowException::CreateTypeError(script_state->GetIsolate(),
-                                          "The sessionId parameter is empty."));
+    exception_state.ThrowTypeError("The sessionId parameter is empty.");
+    return ScriptPromise();
   }
 
   // 5. If the result of running the "Is persistent session type?" algorithm
   //    on this object's session type is false, return a promise rejected
   //    with a newly created TypeError.
   if (!IsPersistentSessionType(session_type_)) {
-    return ScriptPromise::Reject(
-        script_state,
-        V8ThrowException::CreateTypeError(
-            script_state->GetIsolate(), "The session type is not persistent."));
+    exception_state.ThrowTypeError("The session type is not persistent.");
+    return ScriptPromise();
   }
 
   // 6. Let origin be the origin of this object's Document.
@@ -680,7 +674,8 @@
 }
 
 ScriptPromise MediaKeySession::update(ScriptState* script_state,
-                                      const DOMArrayPiece& response) {
+                                      const DOMArrayPiece& response,
+                                      ExceptionState& exception_state) {
   DVLOG(MEDIA_KEY_SESSION_LOG_LEVEL) << __func__ << "(" << this << ")";
 
   // From https://w3c.github.io/encrypted-media/#update:
@@ -690,20 +685,18 @@
   // 1. If this object's closing or closed value is true, return a promise
   //    rejected with an InvalidStateError.
   if (is_closing_or_closed_)
-    return CreateRejectedPromiseAlreadyClosed(script_state);
+    return CreateRejectedPromiseAlreadyClosed(exception_state);
 
   // 2. If this object's callable value is false, return a promise
   //    rejected with an InvalidStateError.
   if (!is_callable_)
-    return CreateRejectedPromiseNotCallable(script_state);
+    return CreateRejectedPromiseNotCallable(exception_state);
 
   // 3. If response is an empty array, return a promise rejected with a
   //    newly created TypeError.
   if (!response.ByteLengthAsSizeT()) {
-    return ScriptPromise::Reject(
-        script_state,
-        V8ThrowException::CreateTypeError(script_state->GetIsolate(),
-                                          "The response parameter is empty."));
+    exception_state.ThrowTypeError("The response parameter is empty.");
+    return ScriptPromise();
   }
 
   // 4. Let response copy be a copy of the contents of the response parameter.
@@ -737,7 +730,8 @@
   // Last step (6.8.2 Resolve promise) will be done when |result| is resolved.
 }
 
-ScriptPromise MediaKeySession::close(ScriptState* script_state) {
+ScriptPromise MediaKeySession::close(ScriptState* script_state,
+                                     ExceptionState& exception_state) {
   DVLOG(MEDIA_KEY_SESSION_LOG_LEVEL) << __func__ << "(" << this << ")";
 
   // From https://w3c.github.io/encrypted-media/#close:
@@ -754,7 +748,7 @@
   // 2. If this object's callable value is false, return a promise rejected
   //    with an InvalidStateError.
   if (!is_callable_)
-    return CreateRejectedPromiseNotCallable(script_state);
+    return CreateRejectedPromiseNotCallable(exception_state);
 
   // 3. Let promise be a new promise.
   SimpleResultPromise* result = MakeGarbageCollected<SimpleResultPromise>(
@@ -783,7 +777,8 @@
   // Last step (5.3.2 Resolve promise) will be done when |result| is resolved.
 }
 
-ScriptPromise MediaKeySession::remove(ScriptState* script_state) {
+ScriptPromise MediaKeySession::remove(ScriptState* script_state,
+                                      ExceptionState& exception_state) {
   DVLOG(MEDIA_KEY_SESSION_LOG_LEVEL) << __func__ << "(" << this << ")";
 
   // From https://w3c.github.io/encrypted-media/#remove:
@@ -793,12 +788,12 @@
   // 1. If this object's closing or closed value is true, return a promise
   //    rejected with an InvalidStateError.
   if (is_closing_or_closed_)
-    return CreateRejectedPromiseAlreadyClosed(script_state);
+    return CreateRejectedPromiseAlreadyClosed(exception_state);
 
   // 2. If this object's callable value is false, return a promise rejected
   //    with an InvalidStateError.
   if (!is_callable_)
-    return CreateRejectedPromiseNotCallable(script_state);
+    return CreateRejectedPromiseNotCallable(exception_state);
 
   // 3. Let promise be a new promise.
   SimpleResultPromise* result = MakeGarbageCollected<SimpleResultPromise>(
diff --git a/third_party/blink/renderer/modules/encryptedmedia/media_key_session.h b/third_party/blink/renderer/modules/encryptedmedia/media_key_session.h
index 6a0027c..94f557c 100644
--- a/third_party/blink/renderer/modules/encryptedmedia/media_key_session.h
+++ b/third_party/blink/renderer/modules/encryptedmedia/media_key_session.h
@@ -46,6 +46,7 @@
 
 class DOMException;
 class EventQueue;
+class ExceptionState;
 class MediaKeys;
 
 // References are held by JS only. However, even if all JS references are
@@ -87,11 +88,14 @@
 
   ScriptPromise generateRequest(ScriptState*,
                                 const String& init_data_type,
-                                const DOMArrayPiece& init_data);
-  ScriptPromise load(ScriptState*, const String& session_id);
-  ScriptPromise update(ScriptState*, const DOMArrayPiece& response);
-  ScriptPromise close(ScriptState*);
-  ScriptPromise remove(ScriptState*);
+                                const DOMArrayPiece& init_data,
+                                ExceptionState&);
+  ScriptPromise load(ScriptState*, const String& session_id, ExceptionState&);
+  ScriptPromise update(ScriptState*,
+                       const DOMArrayPiece& response,
+                       ExceptionState&);
+  ScriptPromise close(ScriptState*, ExceptionState&);
+  ScriptPromise remove(ScriptState*, ExceptionState&);
 
   // EventTarget
   const AtomicString& InterfaceName() const override;
diff --git a/third_party/blink/renderer/modules/encryptedmedia/media_key_session.idl b/third_party/blink/renderer/modules/encryptedmedia/media_key_session.idl
index df6764a..58481d0 100644
--- a/third_party/blink/renderer/modules/encryptedmedia/media_key_session.idl
+++ b/third_party/blink/renderer/modules/encryptedmedia/media_key_session.idl
@@ -39,11 +39,11 @@
     attribute EventHandler onmessage;
 
     // session initialization
-    [CallWith=ScriptState] Promise<void> generateRequest(DOMString initDataType, BufferSource initData);
-    [CallWith=ScriptState] Promise<boolean> load(DOMString sessionId);
+    [CallWith=ScriptState, RaisesException] Promise<void> generateRequest(DOMString initDataType, BufferSource initData);
+    [CallWith=ScriptState, RaisesException] Promise<boolean> load(DOMString sessionId);
 
     // session operations
-    [CallWith=ScriptState] Promise<void> update(BufferSource response);
-    [CallWith=ScriptState] Promise<void> close();
-    [CallWith=ScriptState] Promise<void> remove();
+    [CallWith=ScriptState, RaisesException] Promise<void> update(BufferSource response);
+    [CallWith=ScriptState, RaisesException] Promise<void> close();
+    [CallWith=ScriptState, RaisesException] Promise<void> remove();
 };
diff --git a/third_party/blink/renderer/modules/encryptedmedia/media_keys.cc b/third_party/blink/renderer/modules/encryptedmedia/media_keys.cc
index e6c9031..8cdd9940 100644
--- a/third_party/blink/renderer/modules/encryptedmedia/media_keys.cc
+++ b/third_party/blink/renderer/modules/encryptedmedia/media_keys.cc
@@ -40,6 +40,7 @@
 #include "third_party/blink/renderer/modules/encryptedmedia/encrypted_media_utils.h"
 #include "third_party/blink/renderer/modules/encryptedmedia/media_key_session.h"
 #include "third_party/blink/renderer/modules/encryptedmedia/media_keys_policy.h"
+#include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "third_party/blink/renderer/platform/bindings/v8_throw_exception.h"
 #include "third_party/blink/renderer/platform/instrumentation/instance_counters.h"
@@ -271,7 +272,8 @@
 
 ScriptPromise MediaKeys::setServerCertificate(
     ScriptState* script_state,
-    const DOMArrayPiece& server_certificate) {
+    const DOMArrayPiece& server_certificate,
+    ExceptionState& exception_state) {
   // From https://w3c.github.io/encrypted-media/#setServerCertificate
   // The setServerCertificate(serverCertificate) method provides a server
   // certificate to be used to encrypt messages to the license server.
@@ -285,10 +287,8 @@
   // 2. If serverCertificate is an empty array, return a promise rejected
   //    with a new a newly created TypeError.
   if (!server_certificate.ByteLengthAsSizeT()) {
-    return ScriptPromise::Reject(
-        script_state, V8ThrowException::CreateTypeError(
-                          script_state->GetIsolate(),
-                          "The serverCertificate parameter is empty."));
+    exception_state.ThrowTypeError("The serverCertificate parameter is empty.");
+    return ScriptPromise();
   }
 
   // 3. Let certificate be a copy of the contents of the serverCertificate
diff --git a/third_party/blink/renderer/modules/encryptedmedia/media_keys.h b/third_party/blink/renderer/modules/encryptedmedia/media_keys.h
index 542e066..057746d 100644
--- a/third_party/blink/renderer/modules/encryptedmedia/media_keys.h
+++ b/third_party/blink/renderer/modules/encryptedmedia/media_keys.h
@@ -70,7 +70,8 @@
                                  ExceptionState&);
 
   ScriptPromise setServerCertificate(ScriptState*,
-                                     const DOMArrayPiece& server_certificate);
+                                     const DOMArrayPiece& server_certificate,
+                                     ExceptionState& exception_state);
 
   ScriptPromise getStatusForPolicy(ScriptState*, const MediaKeysPolicy*);
 
diff --git a/third_party/blink/renderer/modules/encryptedmedia/media_keys.idl b/third_party/blink/renderer/modules/encryptedmedia/media_keys.idl
index eb0c8fd..48939bf 100644
--- a/third_party/blink/renderer/modules/encryptedmedia/media_keys.idl
+++ b/third_party/blink/renderer/modules/encryptedmedia/media_keys.idl
@@ -38,5 +38,5 @@
 ] interface MediaKeys {
     [CallWith=ScriptState, RaisesException] MediaKeySession createSession(optional MediaKeySessionType sessionType = "temporary");
 
-    [CallWith=ScriptState] Promise<boolean> setServerCertificate(BufferSource serverCertificate);
+    [CallWith=ScriptState, RaisesException] Promise<boolean> setServerCertificate(BufferSource serverCertificate);
 };
diff --git a/third_party/blink/renderer/modules/encryptedmedia/navigator_request_media_key_system_access.cc b/third_party/blink/renderer/modules/encryptedmedia/navigator_request_media_key_system_access.cc
index 12f7156..8ccce29 100644
--- a/third_party/blink/renderer/modules/encryptedmedia/navigator_request_media_key_system_access.cc
+++ b/third_party/blink/renderer/modules/encryptedmedia/navigator_request_media_key_system_access.cc
@@ -27,6 +27,7 @@
 #include "third_party/blink/renderer/modules/encryptedmedia/media_key_system_access.h"
 #include "third_party/blink/renderer/modules/encryptedmedia/media_key_system_access_initializer_base.h"
 #include "third_party/blink/renderer/modules/encryptedmedia/media_keys_controller.h"
+#include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "third_party/blink/renderer/platform/bindings/v8_throw_exception.h"
 #include "third_party/blink/renderer/platform/encrypted_media_request.h"
@@ -106,7 +107,8 @@
     Navigator& navigator,
     const String& key_system,
     const HeapVector<Member<MediaKeySystemConfiguration>>&
-        supported_configurations) {
+        supported_configurations,
+    ExceptionState& exception_state) {
   DVLOG(3) << __func__;
 
   ExecutionContext* execution_context = ExecutionContext::From(script_state);
@@ -120,11 +122,9 @@
         ConsoleMessage::Create(mojom::ConsoleMessageSource::kJavaScript,
                                mojom::ConsoleMessageLevel::kWarning,
                                kEncryptedMediaFeaturePolicyConsoleWarning));
-    return ScriptPromise::RejectWithDOMException(
-        script_state,
-        MakeGarbageCollected<DOMException>(
-            DOMExceptionCode::kSecurityError,
-            "requestMediaKeySystemAccess is disabled by feature policy."));
+    exception_state.ThrowSecurityError(
+        "requestMediaKeySystemAccess is disabled by feature policy.");
+    return ScriptPromise();
   }
 
   // From https://w3c.github.io/encrypted-media/#requestMediaKeySystemAccess
@@ -132,29 +132,25 @@
   // 1. If keySystem is the empty string, return a promise rejected with a
   //    newly created TypeError.
   if (key_system.IsEmpty()) {
-    return ScriptPromise::Reject(
-        script_state,
-        V8ThrowException::CreateTypeError(script_state->GetIsolate(),
-                                          "The keySystem parameter is empty."));
+    exception_state.ThrowTypeError("The keySystem parameter is empty.");
+    return ScriptPromise();
   }
 
   // 2. If supportedConfigurations is empty, return a promise rejected with
   //    a newly created TypeError.
   if (!supported_configurations.size()) {
-    return ScriptPromise::Reject(
-        script_state, V8ThrowException::CreateTypeError(
-                          script_state->GetIsolate(),
-                          "The supportedConfigurations parameter is empty."));
+    exception_state.ThrowTypeError(
+        "The supportedConfigurations parameter is empty.");
+    return ScriptPromise();
   }
 
   // 3. Let document be the calling context's Document.
   //    (Done at the begining of this function.)
   if (!document->GetPage()) {
-    return ScriptPromise::RejectWithDOMException(
-        script_state,
-        MakeGarbageCollected<DOMException>(
-            DOMExceptionCode::kInvalidStateError,
-            "The context provided is not associated with a page."));
+    exception_state.ThrowDOMException(
+        DOMExceptionCode::kInvalidStateError,
+        "The context provided is not associated with a page.");
+    return ScriptPromise();
   }
 
   UseCounter::Count(*document, WebFeature::kEncryptedMediaSecureOrigin);
diff --git a/third_party/blink/renderer/modules/encryptedmedia/navigator_request_media_key_system_access.h b/third_party/blink/renderer/modules/encryptedmedia/navigator_request_media_key_system_access.h
index cce5d48..c1b233d 100644
--- a/third_party/blink/renderer/modules/encryptedmedia/navigator_request_media_key_system_access.h
+++ b/third_party/blink/renderer/modules/encryptedmedia/navigator_request_media_key_system_access.h
@@ -14,6 +14,8 @@
 
 namespace blink {
 
+class ExceptionState;
+
 class NavigatorRequestMediaKeySystemAccess {
   STATIC_ONLY(NavigatorRequestMediaKeySystemAccess);
 
@@ -23,7 +25,8 @@
       Navigator&,
       const String& key_system,
       const HeapVector<Member<MediaKeySystemConfiguration>>&
-          supported_configurations);
+          supported_configurations,
+      ExceptionState&);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/encryptedmedia/navigator_request_media_key_system_access.idl b/third_party/blink/renderer/modules/encryptedmedia/navigator_request_media_key_system_access.idl
index a71acbe..e1a19bb 100644
--- a/third_party/blink/renderer/modules/encryptedmedia/navigator_request_media_key_system_access.idl
+++ b/third_party/blink/renderer/modules/encryptedmedia/navigator_request_media_key_system_access.idl
@@ -7,5 +7,5 @@
 [
     ImplementedAs=NavigatorRequestMediaKeySystemAccess
 ] partial interface Navigator {
-    [SecureContext, CallWith=ScriptState] Promise<MediaKeySystemAccess> requestMediaKeySystemAccess(DOMString keySystem, sequence<MediaKeySystemConfiguration> supportedConfigurations);
+    [SecureContext, CallWith=ScriptState, RaisesException] Promise<MediaKeySystemAccess> requestMediaKeySystemAccess(DOMString keySystem, sequence<MediaKeySystemConfiguration> supportedConfigurations);
 };
diff --git a/third_party/blink/renderer/modules/peerconnection/mock_rtc_peer_connection_handler_platform.cc b/third_party/blink/renderer/modules/peerconnection/mock_rtc_peer_connection_handler_platform.cc
index e0db936..e0c68ef 100644
--- a/third_party/blink/renderer/modules/peerconnection/mock_rtc_peer_connection_handler_platform.cc
+++ b/third_party/blink/renderer/modules/peerconnection/mock_rtc_peer_connection_handler_platform.cc
@@ -349,7 +349,7 @@
 
 webrtc::RTCErrorOr<std::unique_ptr<RTCRtpTransceiverPlatform>>
 MockRTCPeerConnectionHandlerPlatform::AddTransceiverWithKind(
-    std::string kind,
+    const String& kind,
     const webrtc::RtpTransceiverInit&) {
   transceivers_.push_back(std::unique_ptr<DummyRTCRtpTransceiverPlatform>(
       new DummyRTCRtpTransceiverPlatform(
diff --git a/third_party/blink/renderer/modules/peerconnection/mock_rtc_peer_connection_handler_platform.h b/third_party/blink/renderer/modules/peerconnection/mock_rtc_peer_connection_handler_platform.h
index 2db98e0..ad149e04 100644
--- a/third_party/blink/renderer/modules/peerconnection/mock_rtc_peer_connection_handler_platform.h
+++ b/third_party/blink/renderer/modules/peerconnection/mock_rtc_peer_connection_handler_platform.h
@@ -63,7 +63,7 @@
   AddTransceiverWithTrack(const WebMediaStreamTrack&,
                           const webrtc::RtpTransceiverInit&) override;
   webrtc::RTCErrorOr<std::unique_ptr<RTCRtpTransceiverPlatform>>
-  AddTransceiverWithKind(std::string kind,
+  AddTransceiverWithKind(const String& kind,
                          const webrtc::RtpTransceiverInit&) override;
   webrtc::RTCErrorOr<std::unique_ptr<RTCRtpTransceiverPlatform>> AddTrack(
       const WebMediaStreamTrack&,
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
index 96f015e..89aee95 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
@@ -2187,7 +2187,7 @@
     const String& kind_string = track_or_kind.GetAsString();
     // TODO(hbos): Make cricket::MediaType an allowed identifier in
     // rtc_peer_connection.cc and use that instead of a boolean.
-    std::string kind;
+    String kind;
     if (kind_string == "audio") {
       kind = webrtc::MediaStreamTrackInterface::kAudioKind;
     } else if (kind_string == "video") {
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_handler.cc b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_handler.cc
index 057f7cef..020455f46 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_handler.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_handler.cc
@@ -80,6 +80,21 @@
   STATIC_ONLY(CrossThreadCopier);
 };
 
+template <>
+struct CrossThreadCopier<scoped_refptr<webrtc::StatsObserver>>
+    : public CrossThreadCopierPassThrough<
+          scoped_refptr<webrtc::StatsObserver>> {
+  STATIC_ONLY(CrossThreadCopier);
+};
+
+template <>
+struct CrossThreadCopier<
+    RetainedRefWrapper<webrtc::SetSessionDescriptionObserver>>
+    : public CrossThreadCopierPassThrough<
+          RetainedRefWrapper<webrtc::SetSessionDescriptionObserver>> {
+  STATIC_ONLY(CrossThreadCopier);
+};
+
 template <typename T>
 struct CrossThreadCopier<rtc::scoped_refptr<T>> {
   STATIC_ONLY(CrossThreadCopier);
@@ -124,7 +139,7 @@
   return CreateWebKitSessionDescription(sdp, native_desc->type());
 }
 
-void RunClosureWithTrace(base::OnceClosure closure,
+void RunClosureWithTrace(CrossThreadOnceClosure closure,
                          const char* trace_event_name) {
   TRACE_EVENT0("webrtc", trace_event_name);
   std::move(closure).Run();
@@ -505,14 +520,18 @@
   }
 }
 
+using RTCStatsReportCallbackInternal =
+    CrossThreadOnceFunction<void(std::unique_ptr<RTCStatsReportPlatform>)>;
+
 void GetRTCStatsOnSignalingThread(
     const scoped_refptr<base::SingleThreadTaskRunner>& main_thread,
     scoped_refptr<webrtc::PeerConnectionInterface> native_peer_connection,
-    RTCStatsReportCallback callback,
+    RTCStatsReportCallbackInternal callback,
     const Vector<webrtc::NonStandardGroupId>& exposed_group_ids) {
   TRACE_EVENT0("webrtc", "GetRTCStatsOnSignalingThread");
   native_peer_connection->GetStats(CreateRTCStatsCollectorCallback(
-      main_thread, std::move(callback), exposed_group_ids));
+      main_thread, ConvertToBaseOnceCallback(std::move(callback)),
+      exposed_group_ids));
 }
 
 void ConvertOfferOptionsToWebrtcOfferOptions(
@@ -1292,16 +1311,16 @@
           track_adapter_map_, content_observer, surface_receivers_only)
           .get());
 
-  signaling_thread()->PostTask(
-      FROM_HERE,
-      base::BindOnce(
+  PostCrossThreadTask(
+      *signaling_thread().get(), FROM_HERE,
+      CrossThreadBindOnce(
           &RunClosureWithTrace,
-          base::BindOnce(
+          CrossThreadBindOnce(
               static_cast<void (webrtc::PeerConnectionInterface::*)(
                   webrtc::SetSessionDescriptionObserver*)>(
                   &webrtc::PeerConnectionInterface::SetLocalDescription),
-              native_peer_connection_, base::RetainedRef(webrtc_observer)),
-          "SetLocalDescription"));
+              native_peer_connection_, WTF::RetainedRef(webrtc_observer)),
+          CrossThreadUnretained("SetLocalDescription")));
 }
 
 void RTCPeerConnectionHandler::SetLocalDescription(
@@ -1368,20 +1387,18 @@
           track_adapter_map_, content_observer, surface_receivers_only)
           .get());
 
-  signaling_thread()->PostTask(
-      FROM_HERE,
-      base::BindOnce(
+  PostCrossThreadTask(
+      *signaling_thread().get(), FROM_HERE,
+      CrossThreadBindOnce(
           &RunClosureWithTrace,
-          base::BindOnce(
+          CrossThreadBindOnce(
               static_cast<void (webrtc::PeerConnectionInterface::*)(
                   webrtc::SetSessionDescriptionObserver*,
                   webrtc::SessionDescriptionInterface*)>(
                   &webrtc::PeerConnectionInterface::SetLocalDescription),
-              native_peer_connection_,
-              // TODO(crbug.com/787254): Replace with WTF::WrapRefCounted?
-              base::RetainedRef(webrtc_observer),
-              base::Unretained(native_desc)),
-          "SetLocalDescription"));
+              native_peer_connection_, WTF::RetainedRef(webrtc_observer),
+              CrossThreadUnretained(native_desc)),
+          CrossThreadUnretained("SetLocalDescription")));
 }
 
 void RTCPeerConnectionHandler::SetRemoteDescription(
@@ -1451,19 +1468,19 @@
                           content_observer, surface_receivers_only)
                           .get());
 
-  signaling_thread()->PostTask(
-      FROM_HERE,
-      base::BindOnce(
+  PostCrossThreadTask(
+      *signaling_thread().get(), FROM_HERE,
+      CrossThreadBindOnce(
           &RunClosureWithTrace,
-          base::BindOnce(
+          CrossThreadBindOnce(
               static_cast<void (webrtc::PeerConnectionInterface::*)(
                   std::unique_ptr<webrtc::SessionDescriptionInterface>,
                   rtc::scoped_refptr<
                       webrtc::SetRemoteDescriptionObserverInterface>)>(
                   &webrtc::PeerConnectionInterface::SetRemoteDescription),
-              native_peer_connection_, base::Passed(&native_desc),
+              native_peer_connection_, WTF::Passed(std::move(native_desc)),
               webrtc_observer),
-          "SetRemoteDescription"));
+          CrossThreadUnretained("SetRemoteDescription")));
 }
 
 RTCSessionDescriptionPlatform* RTCPeerConnectionHandler::LocalDescription() {
@@ -1677,20 +1694,22 @@
     webrtc::PeerConnectionInterface::StatsOutputLevel level,
     rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> selector) {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
-  signaling_thread()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&GetStatsOnSignalingThread, native_peer_connection_, level,
-                     base::WrapRefCounted(observer), std::move(selector)));
+  PostCrossThreadTask(
+      *signaling_thread().get(), FROM_HERE,
+      CrossThreadBindOnce(&GetStatsOnSignalingThread, native_peer_connection_,
+                          level, base::WrapRefCounted(observer),
+                          std::move(selector)));
 }
 
 void RTCPeerConnectionHandler::GetStats(
     RTCStatsReportCallback callback,
     const Vector<webrtc::NonStandardGroupId>& exposed_group_ids) {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
-  signaling_thread()->PostTask(
-      FROM_HERE, base::BindOnce(&GetRTCStatsOnSignalingThread, task_runner_,
-                                native_peer_connection_, std::move(callback),
-                                exposed_group_ids));
+  PostCrossThreadTask(
+      *signaling_thread().get(), FROM_HERE,
+      CrossThreadBindOnce(
+          &GetRTCStatsOnSignalingThread, task_runner_, native_peer_connection_,
+          CrossThreadBindOnce(std::move(callback)), exposed_group_ids));
 }
 
 webrtc::RTCErrorOr<std::unique_ptr<RTCRtpTransceiverPlatform>>
@@ -1744,7 +1763,7 @@
 
 webrtc::RTCErrorOr<std::unique_ptr<RTCRtpTransceiverPlatform>>
 RTCPeerConnectionHandler::AddTransceiverWithKind(
-    std::string kind,
+    const String& kind,
     const webrtc::RtpTransceiverInit& init) {
   DCHECK_EQ(configuration_.sdp_semantics, webrtc::SdpSemantics::kUnifiedPlan);
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_handler.h b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_handler.h
index 89be851e..3b5815c 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_handler.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_handler.h
@@ -150,7 +150,7 @@
   AddTransceiverWithTrack(const blink::WebMediaStreamTrack& web_track,
                           const webrtc::RtpTransceiverInit& init) override;
   webrtc::RTCErrorOr<std::unique_ptr<RTCRtpTransceiverPlatform>>
-  AddTransceiverWithKind(std::string kind,
+  AddTransceiverWithKind(const String& kind,
                          const webrtc::RtpTransceiverInit& init) override;
   webrtc::RTCErrorOr<std::unique_ptr<RTCRtpTransceiverPlatform>> AddTrack(
       const WebMediaStreamTrack& web_track,
diff --git a/third_party/blink/renderer/modules/push_messaging/BUILD.gn b/third_party/blink/renderer/modules/push_messaging/BUILD.gn
index 7005dd4..fc787d5 100644
--- a/third_party/blink/renderer/modules/push_messaging/BUILD.gn
+++ b/third_party/blink/renderer/modules/push_messaging/BUILD.gn
@@ -18,8 +18,6 @@
     "push_messaging_bridge.h",
     "push_messaging_client.cc",
     "push_messaging_client.h",
-    "push_messaging_type_converters.cc",
-    "push_messaging_type_converters.h",
     "push_messaging_utils.cc",
     "push_messaging_utils.h",
     "push_provider.cc",
diff --git a/third_party/blink/renderer/modules/push_messaging/push_manager.cc b/third_party/blink/renderer/modules/push_messaging/push_manager.cc
index ef77a6e..fe5338e 100644
--- a/third_party/blink/renderer/modules/push_messaging/push_manager.cc
+++ b/third_party/blink/renderer/modules/push_messaging/push_manager.cc
@@ -52,6 +52,20 @@
   return Vector<String>({"aes128gcm", "aesgcm"});
 }
 
+namespace {
+bool ValidateOptions(blink::PushSubscriptionOptions* options,
+                     ExceptionState& exception_state) {
+  DOMArrayBuffer* buffer = options->applicationServerKey();
+  if (!base::CheckedNumeric<wtf_size_t>(buffer->ByteLengthAsSizeT())
+           .IsValid()) {
+    exception_state.ThrowRangeError(
+        "ApplicationServerKey size exceeded the maximum supported size");
+    return false;
+  }
+  return true;
+}
+}  // namespace
+
 ScriptPromise PushManager::subscribe(
     ScriptState* script_state,
     const PushSubscriptionOptionsInit* options_init,
@@ -68,6 +82,9 @@
   if (exception_state.HadException())
     return ScriptPromise();
 
+  if (!ValidateOptions(options, exception_state))
+    return ScriptPromise();
+
   if (!options->IsApplicationServerKeyVapid()) {
     ExecutionContext::From(script_state)
         ->AddConsoleMessage(ConsoleMessage::Create(
diff --git a/third_party/blink/renderer/modules/push_messaging/push_messaging_client.cc b/third_party/blink/renderer/modules/push_messaging/push_messaging_client.cc
index 273922b..4901766 100644
--- a/third_party/blink/renderer/modules/push_messaging/push_messaging_client.cc
+++ b/third_party/blink/renderer/modules/push_messaging/push_messaging_client.cc
@@ -16,7 +16,6 @@
 #include "third_party/blink/renderer/core/frame/local_frame_client.h"
 #include "third_party/blink/renderer/modules/manifest/manifest_manager.h"
 #include "third_party/blink/renderer/modules/push_messaging/push_error.h"
-#include "third_party/blink/renderer/modules/push_messaging/push_messaging_type_converters.h"
 #include "third_party/blink/renderer/modules/push_messaging/push_messaging_utils.h"
 #include "third_party/blink/renderer/modules/push_messaging/push_subscription.h"
 #include "third_party/blink/renderer/modules/push_messaging/push_subscription_options.h"
@@ -60,7 +59,7 @@
   DCHECK(callbacks);
 
   mojom::blink::PushSubscriptionOptionsPtr options_ptr =
-      mojom::blink::PushSubscriptionOptions::From(options);
+      ConvertSubscriptionOptionPointer(options);
 
   // If a developer provided an application server key in |options|, skip
   // fetching the manifest.
diff --git a/third_party/blink/renderer/modules/push_messaging/push_messaging_type_converters.cc b/third_party/blink/renderer/modules/push_messaging/push_messaging_type_converters.cc
deleted file mode 100644
index 9452bd7..0000000
--- a/third_party/blink/renderer/modules/push_messaging/push_messaging_type_converters.cc
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2019 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 "third_party/blink/renderer/modules/push_messaging/push_messaging_type_converters.h"
-
-#include <string>
-#include <utility>
-
-#include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
-#include "third_party/blink/renderer/modules/push_messaging/push_subscription_options.h"
-#include "third_party/blink/renderer/platform/wtf/vector.h"
-
-namespace mojo {
-
-blink::mojom::blink::PushSubscriptionOptionsPtr TypeConverter<
-    blink::mojom::blink::PushSubscriptionOptionsPtr,
-    blink::PushSubscriptionOptions*>::Convert(blink::PushSubscriptionOptions*
-                                                  input) {
-  Vector<uint8_t> application_server_key;
-  application_server_key.Append(
-      reinterpret_cast<uint8_t*>(input->applicationServerKey()->Data()),
-      input->applicationServerKey()->DeprecatedByteLengthAsUnsigned());
-
-  return blink::mojom::blink::PushSubscriptionOptions::New(
-      input->userVisibleOnly(), application_server_key);
-}
-
-}  // namespace mojo
diff --git a/third_party/blink/renderer/modules/push_messaging/push_messaging_type_converters.h b/third_party/blink/renderer/modules/push_messaging/push_messaging_type_converters.h
deleted file mode 100644
index aa1d5597..0000000
--- a/third_party/blink/renderer/modules/push_messaging/push_messaging_type_converters.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2019 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 THIRD_PARTY_BLINK_RENDERER_MODULES_PUSH_MESSAGING_PUSH_MESSAGING_TYPE_CONVERTERS_H_
-#define THIRD_PARTY_BLINK_RENDERER_MODULES_PUSH_MESSAGING_PUSH_MESSAGING_TYPE_CONVERTERS_H_
-
-#include "third_party/blink/public/mojom/push_messaging/push_messaging.mojom-blink.h"
-#include "third_party/blink/renderer/modules/modules_export.h"
-
-namespace blink {
-class PushSubscriptionOptions;
-}  // namespace blink
-
-namespace mojo {
-
-template <>
-struct TypeConverter<blink::mojom::blink::PushSubscriptionOptionsPtr,
-                     blink::PushSubscriptionOptions*> {
-  static blink::mojom::blink::PushSubscriptionOptionsPtr Convert(
-      blink::PushSubscriptionOptions* input);
-};
-
-}  // namespace mojo
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_PUSH_MESSAGING_PUSH_MESSAGING_TYPE_CONVERTERS_H_
diff --git a/third_party/blink/renderer/modules/push_messaging/push_messaging_utils.cc b/third_party/blink/renderer/modules/push_messaging/push_messaging_utils.cc
index fe7845b..00950f8 100644
--- a/third_party/blink/renderer/modules/push_messaging/push_messaging_utils.cc
+++ b/third_party/blink/renderer/modules/push_messaging/push_messaging_utils.cc
@@ -4,8 +4,10 @@
 
 #include "third_party/blink/renderer/modules/push_messaging/push_messaging_utils.h"
 
-#include "third_party/blink/public/mojom/push_messaging/push_messaging.mojom-blink.h"
 #include "third_party/blink/public/mojom/push_messaging/push_messaging_status.mojom-blink.h"
+#include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
+#include "third_party/blink/renderer/modules/push_messaging/push_subscription_options.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
 
 namespace blink {
 
@@ -103,4 +105,18 @@
   return error_type;
 }
 
+blink::mojom::blink::PushSubscriptionOptionsPtr
+ConvertSubscriptionOptionPointer(blink::PushSubscriptionOptions* input) {
+  Vector<uint8_t> application_server_key;
+  // The checked_cast here guarantees that the input buffer fits into the
+  // result buffer.
+  application_server_key.Append(
+      reinterpret_cast<uint8_t*>(input->applicationServerKey()->Data()),
+      base::checked_cast<wtf_size_t>(
+          input->applicationServerKey()->ByteLengthAsSizeT()));
+
+  return blink::mojom::blink::PushSubscriptionOptions::New(
+      input->userVisibleOnly(), application_server_key);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/push_messaging/push_messaging_utils.h b/third_party/blink/renderer/modules/push_messaging/push_messaging_utils.h
index eb86fe7..9fbe147 100644
--- a/third_party/blink/renderer/modules/push_messaging/push_messaging_utils.h
+++ b/third_party/blink/renderer/modules/push_messaging/push_messaging_utils.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_PUSH_MESSAGING_PUSH_MESSAGING_UTILS_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_PUSH_MESSAGING_PUSH_MESSAGING_UTILS_H_
 
+#include "third_party/blink/public/mojom/push_messaging/push_messaging.mojom-blink.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
 namespace blink {
@@ -14,12 +15,21 @@
 enum class PushRegistrationStatus;
 }  // namespace mojom
 
+class PushSubscriptionOptions;
+
 WTF::String PushRegistrationStatusToString(
     mojom::PushRegistrationStatus status);
 
 mojom::PushErrorType PushRegistrationStatusToPushErrorType(
     mojom::PushRegistrationStatus status);
 
+// Converts a {blink::PushSubscriptionOptions} object into a
+// {blink::mojom::blink::PushSubscriptionOptions} object. Since the buffer of
+// the former may be bigger than the capacity of the latter, a caller of this
+// function has to guarantee that the ByteLength of the input buffer fits into
+// {wtf_size_t} range.
+blink::mojom::blink::PushSubscriptionOptionsPtr
+ConvertSubscriptionOptionPointer(blink::PushSubscriptionOptions* input);
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_PUSH_MESSAGING_PUSH_MESSAGING_UTILS_H_
diff --git a/third_party/blink/renderer/modules/push_messaging/push_provider.cc b/third_party/blink/renderer/modules/push_messaging/push_provider.cc
index fad98a3..082166d 100644
--- a/third_party/blink/renderer/modules/push_messaging/push_provider.cc
+++ b/third_party/blink/renderer/modules/push_messaging/push_provider.cc
@@ -10,7 +10,6 @@
 #include "third_party/blink/public/mojom/push_messaging/push_messaging_status.mojom-blink.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/modules/push_messaging/push_error.h"
-#include "third_party/blink/renderer/modules/push_messaging/push_messaging_type_converters.h"
 #include "third_party/blink/renderer/modules/push_messaging/push_messaging_utils.h"
 #include "third_party/blink/renderer/modules/push_messaging/push_subscription.h"
 #include "third_party/blink/renderer/modules/push_messaging/push_subscription_options.h"
@@ -57,7 +56,7 @@
   DCHECK(callbacks);
 
   mojom::blink::PushSubscriptionOptionsPtr content_options_ptr =
-      mojom::blink::PushSubscriptionOptions::From(options);
+      ConvertSubscriptionOptionPointer(options);
 
   GetPushMessagingRemote()->Subscribe(
       GetSupplementable()->RegistrationId(), std::move(content_options_ptr),
diff --git a/third_party/blink/renderer/modules/push_messaging/push_subscription.cc b/third_party/blink/renderer/modules/push_messaging/push_subscription.cc
index 79e9802..6946911 100644
--- a/third_party/blink/renderer/modules/push_messaging/push_subscription.cc
+++ b/third_party/blink/renderer/modules/push_messaging/push_subscription.cc
@@ -27,8 +27,11 @@
 // This method and its dependencies must remain constant time, thus not branch
 // based on the value of |buffer| while encoding, assuming a known length.
 String ToBase64URLWithoutPadding(DOMArrayBuffer* buffer) {
-  String value = WTF::Base64URLEncode(static_cast<const char*>(buffer->Data()),
-                                      buffer->DeprecatedByteLengthAsUnsigned());
+  String value = WTF::Base64URLEncode(
+      static_cast<const char*>(buffer->Data()),
+      // The size of {buffer} should always fit into into {wtf_size_t}, because
+      // the buffer content itself origins from a WTF::Vector.
+      base::checked_cast<wtf_size_t>(buffer->ByteLengthAsSizeT()));
   DCHECK_GT(value.length(), 0u);
 
   unsigned padding_to_remove = 0;
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_device.cc b/third_party/blink/renderer/modules/webgpu/gpu_device.cc
index 618baed2..a2d3ba2d 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_device.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_device.cc
@@ -115,22 +115,16 @@
   return GPUBuffer::Create(this, descriptor);
 }
 
-HeapVector<ScriptValue> GPUDevice::createBufferMapped(
-    ScriptState* script_state,
+HeapVector<GPUBufferOrArrayBuffer> GPUDevice::createBufferMapped(
     const GPUBufferDescriptor* descriptor,
     ExceptionState& exception_state) {
   GPUBuffer* gpu_buffer;
   DOMArrayBuffer* array_buffer;
   std::tie(gpu_buffer, array_buffer) =
       GPUBuffer::CreateMapped(this, descriptor, exception_state);
-
-  v8::Isolate* isolate = script_state->GetIsolate();
-  v8::Local<v8::Object> creation_context = script_state->GetContext()->Global();
-
-  return HeapVector<ScriptValue>({
-      ScriptValue(isolate, ToV8(gpu_buffer, creation_context, isolate)),
-      ScriptValue(isolate, ToV8(array_buffer, creation_context, isolate)),
-  });
+  return HeapVector<GPUBufferOrArrayBuffer>(
+      {GPUBufferOrArrayBuffer::FromGPUBuffer(gpu_buffer),
+       GPUBufferOrArrayBuffer::FromArrayBuffer(array_buffer)});
 }
 
 GPUTexture* GPUDevice::createTexture(const GPUTextureDescriptor* descriptor,
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_device.h b/third_party/blink/renderer/modules/webgpu/gpu_device.h
index d445674..f49611f 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_device.h
+++ b/third_party/blink/renderer/modules/webgpu/gpu_device.h
@@ -8,6 +8,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_property.h"
+#include "third_party/blink/renderer/bindings/modules/v8/gpu_buffer_or_array_buffer.h"
 #include "third_party/blink/renderer/core/dom/events/event_target.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/modules/webgpu/dawn_callback.h"
@@ -68,8 +69,7 @@
   GPUQueue* defaultQueue();
 
   GPUBuffer* createBuffer(const GPUBufferDescriptor* descriptor);
-  HeapVector<ScriptValue> createBufferMapped(
-      ScriptState* script_state,
+  HeapVector<GPUBufferOrArrayBuffer> createBufferMapped(
       const GPUBufferDescriptor* descriptor,
       ExceptionState& exception_state);
   GPUTexture* createTexture(const GPUTextureDescriptor* descriptor,
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_device.idl b/third_party/blink/renderer/modules/webgpu/gpu_device.idl
index 8dd680e..1c8acb4 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_device.idl
+++ b/third_party/blink/renderer/modules/webgpu/gpu_device.idl
@@ -13,7 +13,7 @@
     [SameObject] readonly attribute GPUQueue defaultQueue;
 
     GPUBuffer createBuffer(GPUBufferDescriptor descriptor);
-    [CallWith=ScriptState, RaisesException] GPUMappedBuffer createBufferMapped(GPUBufferDescriptor descriptor);
+    [RaisesException] GPUMappedBuffer createBufferMapped(GPUBufferDescriptor descriptor);
     [RaisesException] GPUTexture createTexture(GPUTextureDescriptor descriptor);
     GPUSampler createSampler(optional GPUSamplerDescriptor descriptor = {});
 
@@ -40,5 +40,5 @@
     "validation"
 };
 
-typedef sequence<any> GPUMappedBuffer;  // [GPUBuffer, ArrayBuffer]
+typedef sequence<(GPUBuffer or ArrayBuffer)> GPUMappedBuffer;
 typedef (GPUOutOfMemoryError or GPUValidationError) GPUError;
diff --git a/third_party/blink/renderer/platform/bindings/v8_interface_bridge.h b/third_party/blink/renderer/platform/bindings/v8_interface_bridge.h
index f182edb7..8d9f068 100644
--- a/third_party/blink/renderer/platform/bindings/v8_interface_bridge.h
+++ b/third_party/blink/renderer/platform/bindings/v8_interface_bridge.h
@@ -5,6 +5,8 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_INTERFACE_BRIDGE_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_INTERFACE_BRIDGE_H_
 
+#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/bindings/wrapper_type_info.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 #include "v8/include/v8.h"
@@ -46,7 +48,12 @@
 };
 
 template <class V8T, class T>
-class V8InterfaceBridge : public V8InterfaceBridgeBase {};
+class V8InterfaceBridge : public V8InterfaceBridgeBase {
+ public:
+  static T* ToBlinkUnsafe(v8::Local<v8::Object> receiver) {
+    return ToScriptWrappable(receiver)->ToImpl<T>();
+  }
+};
 
 }  // namespace bindings
 
diff --git a/third_party/blink/renderer/platform/graphics/test/mock_paint_canvas.h b/third_party/blink/renderer/platform/graphics/test/mock_paint_canvas.h
index 919b9e3..b42f009 100644
--- a/third_party/blink/renderer/platform/graphics/test/mock_paint_canvas.h
+++ b/third_party/blink/renderer/platform/graphics/test/mock_paint_canvas.h
@@ -98,7 +98,7 @@
   MOCK_METHOD1(drawPicture, void(sk_sp<const PaintRecord> record));
   MOCK_CONST_METHOD0(isClipEmpty, bool());
   MOCK_CONST_METHOD0(isClipRect, bool());
-  MOCK_CONST_METHOD0(getTotalMatrix, const SkMatrix&());
+  MOCK_CONST_METHOD0(getTotalMatrix, SkMatrix());
 
   MOCK_METHOD3(Annotate,
                void(AnnotationType type,
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
index cac85602..df35ee8 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
@@ -2039,6 +2039,8 @@
         FetchParameters::SpeculativePreloadType::kNotSpeculative,
         false /* is_link_preload */));
   }
+  resource_request.SetReferrerString(Referrer::NoReferrer());
+  resource_request.SetReferrerPolicy(network::mojom::ReferrerPolicy::kNever);
 
   ResourceLoaderOptions options = resource->Options();
   options.initiator_info.name = initiator_name;
diff --git a/third_party/blink/renderer/platform/peerconnection/rtc_peer_connection_handler_platform.h b/third_party/blink/renderer/platform/peerconnection/rtc_peer_connection_handler_platform.h
index 6b8da26..4c0a2f9 100644
--- a/third_party/blink/renderer/platform/peerconnection/rtc_peer_connection_handler_platform.h
+++ b/third_party/blink/renderer/platform/peerconnection/rtc_peer_connection_handler_platform.h
@@ -32,7 +32,6 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_PEER_CONNECTION_HANDLER_PLATFORM_H_
 
 #include <memory>
-#include <string>
 
 #include "third_party/blink/renderer/platform/peerconnection/rtc_stats.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
@@ -133,7 +132,7 @@
   virtual webrtc::RTCErrorOr<std::unique_ptr<RTCRtpTransceiverPlatform>>
   AddTransceiverWithKind(
       // webrtc::MediaStreamTrackInterface::kAudioKind or kVideoKind
-      std::string kind,
+      const String& kind,
       const webrtc::RtpTransceiverInit&) = 0;
   // Adds the track to the peer connection, returning the resulting transceiver
   // or error.
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 352497fd..ae1f00c3 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -799,7 +799,7 @@
       name: "HrefTranslate",
       depends_on: ["TranslateService"],
       origin_trial_feature_name: "HrefTranslate",
-      status: "experimental",
+      status: "stable",
     },
     // TODO(937746): Web Components v0 is disabled by default, and will be
     // removed after M87.
@@ -1027,7 +1027,7 @@
     },
     {
       name: "MediaSessionPosition",
-      status: "experimental",
+      status: "stable",
     },
     {
       name: "MediaSessionSeeking",
diff --git a/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.cc b/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.cc
index ef55b44..c751d9c 100644
--- a/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.cc
@@ -41,7 +41,7 @@
   DCHECK(sequence_manager_);
   sequence_manager_->SetDefaultTaskRunner(default_task_runner_);
 
-  blink_task_executor_.emplace(default_task_runner_, sequence_manager_);
+  simple_task_executor_.emplace(default_task_runner_);
 }
 
 SchedulerHelper::~SchedulerHelper() {
@@ -182,18 +182,5 @@
   return false;
 }
 
-SchedulerHelper::BlinkTaskExecutor::BlinkTaskExecutor(
-    scoped_refptr<base::SingleThreadTaskRunner> default_task_queue,
-    base::sequence_manager::SequenceManager* sequence_manager)
-    : base::SimpleTaskExecutor(sequence_manager, std::move(default_task_queue)),
-      sequence_manager_(sequence_manager) {}
-
-SchedulerHelper::BlinkTaskExecutor::~BlinkTaskExecutor() = default;
-
-const scoped_refptr<base::SequencedTaskRunner>&
-SchedulerHelper::BlinkTaskExecutor::GetContinuationTaskRunner() {
-  return sequence_manager_->GetTaskRunnerForCurrentTask();
-}
-
 }  // namespace scheduler
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h b/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h
index 52b84746..3f2267e 100644
--- a/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h
+++ b/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h
@@ -130,29 +130,12 @@
  private:
   friend class SchedulerHelperTest;
 
-  // Like SimpleTaskExecutor except it knows how to get the current task runner
-  // from the SequenceManager to implement GetContinuationTaskRunner.
-  class BlinkTaskExecutor : public base::SimpleTaskExecutor {
-   public:
-    BlinkTaskExecutor(
-        scoped_refptr<base::SingleThreadTaskRunner> default_task_queue,
-        base::sequence_manager::SequenceManager* sequence_manager);
-
-    ~BlinkTaskExecutor() override;
-
-    // base::TaskExecutor implementation.
-    const scoped_refptr<base::SequencedTaskRunner>& GetContinuationTaskRunner()
-        override;
-
-   private:
-    base::sequence_manager::SequenceManager* sequence_manager_;  // NOT OWNED
-  };
   scoped_refptr<base::SingleThreadTaskRunner> default_task_runner_;
 
   Observer* observer_;  // NOT OWNED
 
   UkmTaskSampler ukm_task_sampler_;
-  base::Optional<BlinkTaskExecutor> blink_task_executor_;
+  base::Optional<base::SimpleTaskExecutor> simple_task_executor_;
 
   DISALLOW_COPY_AND_ASSIGN(SchedulerHelper);
 };
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc
index c44eecf..1592325c 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc
@@ -18,7 +18,6 @@
 #include "base/task/sequence_manager/test/sequence_manager_for_test.h"
 #include "base/task/task_executor.h"
 #include "base/test/bind_test_util.h"
-#include "base/test/gtest_util.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/simple_test_tick_clock.h"
@@ -47,7 +46,6 @@
 // To avoid symbol collisions in jumbo builds.
 namespace main_thread_scheduler_impl_unittest {
 
-using testing::IsNull;
 using testing::Mock;
 using testing::NotNull;
 using InputEventState = WebThreadScheduler::InputEventState;
@@ -2090,25 +2088,6 @@
                 {base::CurrentThread(), base::TaskPriority::BEST_EFFORT}));
 }
 
-TEST_F(MainThreadSchedulerImplTest, GetContinuationTaskRunner) {
-  scoped_refptr<MainThreadTaskQueue> timer_tq = scheduler_->NewTimerTaskQueue(
-      MainThreadTaskQueue::QueueType::kFrameThrottleable, nullptr);
-  auto task_runner = timer_tq->CreateTaskRunner(TaskType::kJavascriptTimer);
-
-  base::RunLoop run_loop;
-  task_runner->PostTask(FROM_HERE, base::BindLambdaForTesting([&]() {
-                          EXPECT_EQ(task_runner,
-                                    base::GetContinuationTaskRunner());
-                          run_loop.Quit();
-                        }));
-  run_loop.Run();
-}
-
-TEST_F(MainThreadSchedulerImplTest,
-       GetContinuationTaskRunnerWithNoTaskRunning) {
-  EXPECT_DCHECK_DEATH(base::GetContinuationTaskRunner());
-}
-
 TEST_F(MainThreadSchedulerImplTest, TestBeginMainFrameNotExpectedUntil) {
   base::TimeDelta ten_millis(base::TimeDelta::FromMilliseconds(10));
   base::TimeTicks expected_deadline = Now() + ten_millis;
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 18a0cc2..5d7d608 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -3546,12 +3546,18 @@
 # Failures on Mac scrollbars for compositor threaded scrollbar scrolling.
 crbug.com/953847 [ Mac ] fast/scrolling/scrollbars/mouse-autoscrolling-on-scrollbar.html [ Failure Timeout ]
 crbug.com/953847 [ Mac ] virtual/threaded-prefer-compositing/fast/scrolling/scrollbars/mouse-autoscrolling-on-scrollbar.html [ Failure Timeout ]
-crbug.com/953847 [ Mac ] virtual/compositor_threaded_scrollbar_scrolling/fast/scrolling/scrollbars/mouse-autoscrolling-on-scrollbar.html [ Failure Timeout ]
 crbug.com/953847 [ Mac ] virtual/scroll_customization/fast/scrolling/scrollbars/mouse-autoscrolling-on-scrollbar.html [ Failure Timeout ]
+# This one too but is covered below in the CRASH expectation.
+#crbug.com/953847 [ Mac ] virtual/compositor_threaded_scrollbar_scrolling/fast/scrolling/scrollbars/mouse-autoscrolling-on-scrollbar.html [ Failure Timeout ]
 
 # 1px scroll offset difference for compositor threaded scrollbar scrolling on Linux.
 crbug.com/1009892 [ Mac ] virtual/compositor_threaded_scrollbar_scrolling_hidpi/fast/scrolling/scrollbars/dsf-ready/mouse-interactions-dsf-2.html [ Failure Timeout ]
 
+# These cause a violation of a DCHECK in CC by sending a GestureScrollBegin
+# without closing an existing one.
+crbug.com/979408 virtual/compositor_threaded_scrollbar_scrolling/fast/scrolling/scrollbars/mouse-autoscrolling-on-scrollbar.html [ Crash Failure Timeout ]
+crbug.com/979408 virtual/compositor_threaded_scrollbar_scrolling/fast/scrolling/scrollbars/mouse-scrolling-on-div-scrollbar-thumb.html [ Crash ]
+
 # Some control characters still not visible
 crbug.com/893490 [ Mac ] external/wpt/css/css-text/white-space/control-chars-001.html [ Failure ]
 crbug.com/893490 [ Mac ] external/wpt/css/css-text/white-space/control-chars-002.html [ Failure ]
@@ -3672,6 +3678,8 @@
 crbug.com/1035708 external/wpt/css/css-pseudo/grammar-error-001.html [ Failure ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 [ Mac10.10 ] virtual/reporting-api/external/wpt/content-security-policy/reporting-api/reporting-api-doesnt-send-reports-without-violation.https.sub.html [ Timeout ]
+crbug.com/626703 [ Retina ] virtual/reporting-api/external/wpt/content-security-policy/reporting-api/reporting-api-doesnt-send-reports-without-violation.https.sub.html [ Timeout ]
 crbug.com/626703 [ Mac10.11 ] virtual/cascade/external/wpt/css/css-paint-api/parse-input-arguments-018.https.html [ Failure ]
 crbug.com/626703 [ Mac10.13 ] virtual/reporting-api/external/wpt/content-security-policy/reporting-api/reporting-api-doesnt-send-reports-without-violation.https.sub.html [ Timeout ]
 crbug.com/626703 [ Linux ] external/wpt/infrastructure/reftest/reftest_match_and_mismatch-6.html [ Failure ]
@@ -6191,9 +6199,6 @@
 # ecosystem-infra sheriff 2018-11-02, 2019-03-18
 crbug.com/901271 external/wpt/dom/events/Event-dispatch-on-disabled-elements.html [ Pass Failure Timeout ]
 
-#Sheriff 2018-11-02
-crbug.com/901314 inspector-protocol/css/css-coverage-new-stylesheet.js [ Failure Pass ]
-
 #Sheriff 2018-11-06
 crbug.com/902645 [ Retina ] transforms/3d/point-mapping/3d-point-mapping-3.html [ Failure Pass ]
 crbug.com/902645 [ Retina ] transforms/3d/point-mapping/3d-point-mapping-origins.html [ Failure Pass ]
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
index 916b485..986dc1c0 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
@@ -165186,6 +165186,9 @@
    "html/semantics/forms/constraints/form-validation-validity-customError-expected.txt": [
     []
    ],
+   "html/semantics/forms/constraints/form-validation-validity-patternMismatch-expected.txt": [
+    []
+   ],
    "html/semantics/forms/constraints/form-validation-validity-valid-expected.txt": [
     []
    ],
@@ -167133,9 +167136,6 @@
    "import-maps/common/README.md": [
     []
    ],
-   "import-maps/common/resolving.tentative-expected.txt": [
-    []
-   ],
    "import-maps/common/resources/common-test-helper.js": [
     []
    ],
@@ -169935,468 +169935,6 @@
    "offscreen-canvas/OWNERS": [
     []
    ],
-   "offscreen-canvas/compositing/2d.composite.canvas.copy-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.canvas.copy.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.canvas.destination-atop-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.canvas.destination-atop.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.canvas.destination-in-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.canvas.destination-in.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.canvas.destination-out-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.canvas.destination-out.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.canvas.destination-over-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.canvas.destination-over.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.canvas.lighter-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.canvas.lighter.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.canvas.source-atop-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.canvas.source-atop.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.canvas.source-in-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.canvas.source-in.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.canvas.source-out-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.canvas.source-out.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.canvas.source-over-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.canvas.source-over.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.canvas.xor-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.canvas.xor.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.globalAlpha.image-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.globalAlpha.image.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.globalAlpha.imagepattern-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.globalAlpha.imagepattern.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.image.copy-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.image.copy.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.image.destination-atop-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.image.destination-atop.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.image.destination-in-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.image.destination-in.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.image.destination-out-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.image.destination-out.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.image.destination-over-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.image.destination-over.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.image.lighter-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.image.lighter.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.image.source-atop-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.image.source-atop.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.image.source-in-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.image.source-in.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.image.source-out-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.image.source-out.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.image.source-over-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.image.source-over.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.image.xor-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.image.xor.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.uncovered.image.copy-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.uncovered.image.copy.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.uncovered.image.destination-atop-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.uncovered.image.destination-atop.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.uncovered.image.destination-in-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.uncovered.image.destination-in.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.uncovered.image.source-in-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.uncovered.image.source-in.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.uncovered.image.source-out-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.uncovered.image.source-out.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.uncovered.pattern.copy-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.uncovered.pattern.copy.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.uncovered.pattern.destination-atop-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.uncovered.pattern.destination-atop.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.uncovered.pattern.destination-in-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.uncovered.pattern.destination-in.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.uncovered.pattern.source-in-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.uncovered.pattern.source-in.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.uncovered.pattern.source-out-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/compositing/2d.composite.uncovered.pattern.source-out.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.3arg-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.3arg.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.5arg-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.5arg.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.basic-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.basic.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.destpos-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.destpos.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.destsize-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.destsize.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcepos-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcepos.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcesize-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcesize.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.alpha-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.alpha.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.animated.poster-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.animated.poster.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.broken-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.broken.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.clip-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.clip.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.composite-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.composite.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.floatsource-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.floatsource.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativedest-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativedest.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativedir-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativedir.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativesource-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativesource.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.nonfinite-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.nonfinite.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.nowrap-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.nowrap.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.path-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.path.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.svg-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.svg.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.transform-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.transform.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerosource-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerosource.image-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerosource.image.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerosource.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.basic.image-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.basic.image.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.crosscanvas-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.crosscanvas.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.basic-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.basic.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord1-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord1.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord2-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord2.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord3-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord3.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.outside-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.outside.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.orientation.image-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.orientation.image.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.basic-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.basic.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord1-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord1.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord2-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord2.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord3-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord3.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.outside-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.outside.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.basic-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.basic.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.coord1-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.coord1.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.outside-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.outside.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.basic-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.basic.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.coord1-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.coord1.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.outside-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.outside.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.repeat.empty-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.repeat.empty.worker-expected.txt": [
-    []
-   ],
    "offscreen-canvas/filter/offscreencanvas.filter.js": [
     []
    ],
@@ -170430,66 +169968,6 @@
    "offscreen-canvas/pixel-manipulation/2d.imageData.put.nonfinite-expected.txt": [
     []
    ],
-   "offscreen-canvas/shadows/2d.shadow.image.alpha-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/shadows/2d.shadow.image.alpha.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/shadows/2d.shadow.image.basic-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/shadows/2d.shadow.image.basic.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/shadows/2d.shadow.image.scale-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/shadows/2d.shadow.image.scale.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/shadows/2d.shadow.image.section-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/shadows/2d.shadow.image.section.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/shadows/2d.shadow.image.transparent.1-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/shadows/2d.shadow.image.transparent.1.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/shadows/2d.shadow.image.transparent.2-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/shadows/2d.shadow.image.transparent.2.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/shadows/2d.shadow.pattern.alpha-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/shadows/2d.shadow.pattern.alpha.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/shadows/2d.shadow.pattern.basic-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/shadows/2d.shadow.pattern.basic.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/shadows/2d.shadow.pattern.transparent.1-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/shadows/2d.shadow.pattern.transparent.1.worker-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/shadows/2d.shadow.pattern.transparent.2-expected.txt": [
-    []
-   ],
-   "offscreen-canvas/shadows/2d.shadow.pattern.transparent.2.worker-expected.txt": [
-    []
-   ],
    "offscreen-canvas/text/2d.text.draw.align.center-expected.txt": [
     []
    ],
@@ -180990,6 +180468,12 @@
    "trusted-types/default-policy.tentative.html.headers": [
     []
    ],
+   "trusted-types/empty-default-policy-report-only.tentative.html.headers": [
+    []
+   ],
+   "trusted-types/empty-default-policy.tentative.html.headers": [
+    []
+   ],
    "trusted-types/support/WorkerGlobalScope-importScripts.https.js": [
     []
    ],
@@ -310676,6 +310160,18 @@
      {}
     ]
    ],
+   "trusted-types/empty-default-policy-report-only.tentative.html": [
+    [
+     "trusted-types/empty-default-policy-report-only.tentative.html",
+     {}
+    ]
+   ],
+   "trusted-types/empty-default-policy.tentative.html": [
+    [
+     "trusted-types/empty-default-policy.tentative.html",
+     {}
+    ]
+   ],
    "trusted-types/eval-csp-no-tt.tentative.html": [
     [
      "trusted-types/eval-csp-no-tt.tentative.html",
@@ -392417,7 +391913,7 @@
    "testharness"
   ],
   "css/css-grid/grid-definition/grid-inline-support-flexible-lengths-001.html": [
-   "3a2c94238987b84fc99bee3c80e2ddc7fd04793c",
+   "42bbb43cf6ae229222375f3af822246c8bdb8d97",
    "testharness"
   ],
   "css/css-grid/grid-definition/grid-inline-support-grid-template-areas-001.html": [
@@ -392425,19 +391921,19 @@
    "testharness"
   ],
   "css/css-grid/grid-definition/grid-inline-support-grid-template-columns-rows-001.html": [
-   "32733e238fad58677447b44be9c5fcc934fe803d",
+   "cbcdc2c345400235f777974c05a041b13c332b1b",
    "testharness"
   ],
   "css/css-grid/grid-definition/grid-inline-support-named-grid-lines-001.html": [
-   "fc4caf282349776da41ee63c6a07c3a834243fdc",
+   "a405f0557c94cdab2a0bd6803c38b1d790947adc",
    "testharness"
   ],
   "css/css-grid/grid-definition/grid-inline-support-repeat-001.html": [
-   "465e5e8cd826306ce2c5b6661e086df6eb9e96a1",
+   "67a0c6611223f1f610ee575b0dc230027e3105c9",
    "testharness"
   ],
   "css/css-grid/grid-definition/grid-inline-template-columns-rows-resolved-values-001.html": [
-   "ff1f814309603c13248b7045d9ca0f9900bd04fe",
+   "4b1390687dcdb07399b88dad5c93adbd908067e4",
    "testharness"
   ],
   "css/css-grid/grid-definition/grid-layout-auto-tracks.html": [
@@ -392465,7 +391961,7 @@
    "testharness"
   ],
   "css/css-grid/grid-definition/grid-support-flexible-lengths-001.html": [
-   "0dae45b39d1acc556ed79fac3ff58562e8ce8e78",
+   "db9488af25876a439d20b3a93de6be66a2729d43",
    "testharness"
   ],
   "css/css-grid/grid-definition/grid-support-grid-template-areas-001.html": [
@@ -392473,15 +391969,15 @@
    "testharness"
   ],
   "css/css-grid/grid-definition/grid-support-grid-template-columns-rows-001.html": [
-   "bf818cd85eaff5b659165021e317c23b00d71f77",
+   "cb0dbffe1614e286d2b179b75dd0aaf3f195cd98",
    "testharness"
   ],
   "css/css-grid/grid-definition/grid-support-named-grid-lines-001.html": [
-   "bff5e8151c782060f08f8fdcf59ccd142d65547e",
+   "5357855b0474b8036f72de77ead519104ae1fb3d",
    "testharness"
   ],
   "css/css-grid/grid-definition/grid-support-repeat-001.html": [
-   "c2c8be0c5910f6bb0b16f33ac536955b63cca0c3",
+   "54d230cca9d2be3509d7af79c83eb213075d14de",
    "testharness"
   ],
   "css/css-grid/grid-definition/grid-support-repeat-002.html": [
@@ -392497,7 +391993,7 @@
    "reftest"
   ],
   "css/css-grid/grid-definition/grid-template-columns-rows-resolved-values-001.html": [
-   "c992bf66d269d0923e78b59f90da4e34395bebe2",
+   "df91cebf63ee0a251e1c8da56567f7684bf4b1f9",
    "testharness"
   ],
   "css/css-grid/grid-definition/grid-template-rows-fit-content-001-ref.html": [
@@ -392993,7 +392489,7 @@
    "reftest"
   ],
   "css/css-grid/grid-items/grid-minimum-size-grid-items-021.html": [
-   "922223ae445282b91305b7d7e823f0040a898c70",
+   "ab66d70a9732cc2ea270feaa222c9efe88c70886",
    "testharness"
   ],
   "css/css-grid/grid-items/grid-minimum-size-grid-items-022.html": [
@@ -393589,7 +393085,7 @@
    "testharness"
   ],
   "css/css-grid/parsing/grid-template-columns-computed-withcontent.html": [
-   "9b7cd030a7c145a9300ef7d6700ca6a8dd4e3e5e",
+   "a788ca22f77c188e6a574852ca23936bd6349071",
    "testharness"
   ],
   "css/css-grid/parsing/grid-template-columns-computed.html": [
@@ -393613,7 +393109,7 @@
    "testharness"
   ],
   "css/css-grid/parsing/grid-template-rows-computed-withcontent.html": [
-   "693cf338c0dc42406336ccd99a605b1cf0d4f9be",
+   "1e85f16d9338a5b74bd54be79e42a4007ffd715b",
    "testharness"
   ],
   "css/css-grid/parsing/grid-template-rows-computed.html": [
@@ -402017,11 +401513,11 @@
    "reftest"
   ],
   "css/css-pseudo/marker-content-013-ref.html": [
-   "7657cfbd710763325972f860af7744eda98a236a",
+   "c46f8db136c208ec3c88317bc65b92fbddfd9799",
    "support"
   ],
   "css/css-pseudo/marker-content-013.html": [
-   "427578f9c5abfd1be809ea71cfcfdac4298def44",
+   "3b2ce6fa50abac5cd44a75880eb18dad3aae485c",
    "reftest"
   ],
   "css/css-pseudo/marker-content-014-ref.html": [
@@ -467848,8 +467344,12 @@
    "16e64214761bbcccbf2022ad4ba187037bbffced",
    "testharness"
   ],
+  "html/semantics/forms/constraints/form-validation-validity-patternMismatch-expected.txt": [
+   "05bb339e782849a2bbd9a76fa5819cdf162b905c",
+   "support"
+  ],
   "html/semantics/forms/constraints/form-validation-validity-patternMismatch.html": [
-   "d8677898ff135a7db42bc050d2d1dad582477af4",
+   "4ca64aac68983c50297fe48b5cb9423baabab4a4",
    "testharness"
   ],
   "html/semantics/forms/constraints/form-validation-validity-rangeOverflow.html": [
@@ -474969,7 +474469,7 @@
    "testharness"
   ],
   "import-maps/builtin-support.tentative/imported/resolving-builtins.tentative-expected.txt": [
-   "a92b0600636bd8bb1bbb3586a122517f1bdc8d4f",
+   "cd370faaeeab5825b878d0789c6a86f7bdc08b05",
    "support"
   ],
   "import-maps/builtin-support.tentative/imported/resolving-builtins.tentative.html": [
@@ -474985,7 +474485,7 @@
    "testharness"
   ],
   "import-maps/builtin-support.tentative/imported/resolving.tentative-expected.txt": [
-   "39ebb09e8e672104425c908275bde416803a0c65",
+   "ec4b7209086a70af3918fc6995944e7dfef8420c",
    "support"
   ],
   "import-maps/builtin-support.tentative/imported/resolving.tentative.html": [
@@ -475036,10 +474536,6 @@
    "cda42d06099f16f13e61e30394c230cbbb34f8c9",
    "support"
   ],
-  "import-maps/common/resolving.tentative-expected.txt": [
-   "cf7a3a1cb66725cf44b4c8f819f9aa4c332befd5",
-   "support"
-  ],
   "import-maps/common/resolving.tentative.html": [
    "c947232e06322addc60a08004c1f21d317647042",
    "testharness"
@@ -483149,7 +482645,7 @@
    "testharness"
   ],
   "navigation-timing/idlharness.window.js": [
-   "2ec9c754987a66a144c79a0fcc127dd468b62695",
+   "8cc3546b3c27a9d93ee5621584d83d56a308513c",
    "testharness"
   ],
   "navigation-timing/nav2_data_uri.html": [
@@ -483628,180 +483124,92 @@
    "3b83b67facae74327e940c3784d6c9befdb48789",
    "support"
   ],
-  "offscreen-canvas/compositing/2d.composite.canvas.copy-expected.txt": [
-   "58a567dcf4d41915ea8ef64b6a0e712901c379c0",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.canvas.copy.html": [
-   "6cf8779ab0b2a1c822e3ccc1d26ff195c904516f",
+   "824e1ecaeaae675f87d5632d853be2b6c9f03692",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.canvas.copy.worker-expected.txt": [
-   "2e526addd9d4933493ca15bb354a0c553a1fe436",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.canvas.copy.worker.js": [
-   "aa0f39dd2bcbdbf115729f7a245c5b14e9444100",
+   "13f85bc5f7907bbfc86bdeb89a3d5113c7b64b86",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.canvas.destination-atop-expected.txt": [
-   "112814897f3677e258863975a51402b5f61fbc22",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.canvas.destination-atop.html": [
-   "b086a5429bad0b316c37215508db707aef9e632f",
+   "49dc643f082ec3258a85782c89abf553551d8600",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.canvas.destination-atop.worker-expected.txt": [
-   "2e526addd9d4933493ca15bb354a0c553a1fe436",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.canvas.destination-atop.worker.js": [
-   "18785d75070043f02066638501808344eb68a6e2",
+   "7cdac8753dd023e7e99b28e0009b8898159ddc98",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.canvas.destination-in-expected.txt": [
-   "962695ac7380d09a231a7ff186e11b9a14c00ad4",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.canvas.destination-in.html": [
-   "402f2c3931ad32689a53e43ce32a3b9a6d7b558c",
+   "647e4866f60753cb55b13402c69e9fe047d0de25",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.canvas.destination-in.worker-expected.txt": [
-   "2e526addd9d4933493ca15bb354a0c553a1fe436",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.canvas.destination-in.worker.js": [
-   "a2e403bf77b3763c150b688da73841f8b48f078a",
+   "0fbfc761c5b30ffda449c8ec8f05e8e4d4b59217",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.canvas.destination-out-expected.txt": [
-   "df7512016e3f72e891bd499da8ab7aff3b7ebf87",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.canvas.destination-out.html": [
-   "bccc50aea397c2daa7e09404b3e2d66167ddf5fc",
+   "46114c6556557c9f3f81bf09e1758ac3a7a32066",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.canvas.destination-out.worker-expected.txt": [
-   "2e526addd9d4933493ca15bb354a0c553a1fe436",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.canvas.destination-out.worker.js": [
-   "3ae91e39592d596470a3c3e93658f7830bc485ad",
+   "659d0efee46361851a67ffb466f57a61c41a2b42",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.canvas.destination-over-expected.txt": [
-   "62ba07e1cff346119223eeb2ab7da142b3a283db",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.canvas.destination-over.html": [
-   "8128e1108349414a1ece262ea01ae623257aedc3",
+   "a7cfc306c51ea7b73b47385f1b3cab5a3076d4d8",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.canvas.destination-over.worker-expected.txt": [
-   "2e526addd9d4933493ca15bb354a0c553a1fe436",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.canvas.destination-over.worker.js": [
-   "35827d4ea9bdcb4f8300856de777c1ec0e73f051",
+   "648da78ce9bf62a6e896c5ab275343497923de71",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.canvas.lighter-expected.txt": [
-   "5258d04309eda3036d11aa8c42acc92e7288d1ab",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.canvas.lighter.html": [
-   "20083b575851ad6ca335190b1a5060f790094e98",
+   "15012f2232bb15b39a3108382778195c12317173",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.canvas.lighter.worker-expected.txt": [
-   "2e526addd9d4933493ca15bb354a0c553a1fe436",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.canvas.lighter.worker.js": [
-   "474c299d1a245c969761647bb990dddf0db8afd1",
+   "5450fc31ca2de411af207d4df8a4d2c5458326aa",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.canvas.source-atop-expected.txt": [
-   "e8e700147a2d084d6b25c00bb69f484a2ae88d83",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.canvas.source-atop.html": [
-   "16fb9fb3c8747f77a8b1c76905cece22c41dae75",
+   "30afb49476296ff2770c78cad32d62b8f2285025",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.canvas.source-atop.worker-expected.txt": [
-   "2e526addd9d4933493ca15bb354a0c553a1fe436",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.canvas.source-atop.worker.js": [
-   "689152ae6f4142bc9956949ce4b26734617f8bbf",
+   "8b8d183213cb6be19bd276cc6b5d5712194ed8c5",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.canvas.source-in-expected.txt": [
-   "38cb1565c80a41e5f36f181eb6d067fc0c25dd45",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.canvas.source-in.html": [
-   "bf69b2dc0a1b132e85afe3b439d6891a4a857620",
+   "73ae7b710e101cdae57afbc58d3de73f0b98418c",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.canvas.source-in.worker-expected.txt": [
-   "2e526addd9d4933493ca15bb354a0c553a1fe436",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.canvas.source-in.worker.js": [
-   "d68068bd9f5741741987ac70143ff0dc36a5f2e0",
+   "a4b9a5b98aec554b69f6260de88a156de2021b1a",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.canvas.source-out-expected.txt": [
-   "19f83f367cbe6814d0f17b75f84882d79429d495",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.canvas.source-out.html": [
-   "21514d32e5ddf7a095ac513dfb80e4b4ea99e31a",
+   "5f9f3fa228b7b23466fae5e8fb3708c8c7d27d1c",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.canvas.source-out.worker-expected.txt": [
-   "2e526addd9d4933493ca15bb354a0c553a1fe436",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.canvas.source-out.worker.js": [
-   "f30bcd163991dfbdb5beb9025da1d44fc335d72a",
+   "0fff033b09aff0acf38c2331b37cb9fe8b49df8a",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.canvas.source-over-expected.txt": [
-   "7c405d38a153f4c34f816b3785cbee0642b7bd44",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.canvas.source-over.html": [
-   "54810af7f953663d9b15fdee7b3a06e426f01f68",
+   "ed428466cfce6253ce6440e1d9e92ae52ee2dae3",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.canvas.source-over.worker-expected.txt": [
-   "2e526addd9d4933493ca15bb354a0c553a1fe436",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.canvas.source-over.worker.js": [
-   "5a90aedc2c7ea1e8b08c1bb8e91c66a5956b524b",
+   "b0bc5c513454f563a47b6fbdeac62515ac3121e0",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.canvas.xor-expected.txt": [
-   "b9910a20839ecd1b9b41f5acedcd4808427206d8",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.canvas.xor.html": [
-   "1c5ada787d458fcefc58f53f9a8750aee87012da",
+   "2df51323328c181d5516079c323a20a903b667f6",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.canvas.xor.worker-expected.txt": [
-   "2e526addd9d4933493ca15bb354a0c553a1fe436",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.canvas.xor.worker.js": [
-   "16d1e9be9b0a5a2f25167a9bfcbc1f0c83be448e",
+   "9fa550575c13f12daccc1b49d3f8b14c0861d867",
    "testharness"
   ],
   "offscreen-canvas/compositing/2d.composite.clip.copy.html": [
@@ -483932,36 +483340,20 @@
    "aacc43c44599a49bd1debf01f1051568a7c6e27e",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.globalAlpha.image-expected.txt": [
-   "dad07e34aad2f83eb9197330598c97cd927d5c4a",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.globalAlpha.image.html": [
-   "2f7a5e684da46c4281d048efdf79d8e9be3bff29",
+   "5d586a191eef9bcd6d5f7880222bb614af95d7f2",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.globalAlpha.image.worker-expected.txt": [
-   "2e526addd9d4933493ca15bb354a0c553a1fe436",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.globalAlpha.image.worker.js": [
-   "143d2ee3a245322f4faea474426ca08bb67edb1d",
+   "f645a8e55640cc8fe501b0a819e60e99d272a166",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.globalAlpha.imagepattern-expected.txt": [
-   "db39e6d7de127a72a52f5fee0b76de9629049d47",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.globalAlpha.imagepattern.html": [
-   "c577b5bf9e9893d24b4a28b8d679f63f67568e2f",
+   "0c77965f7a8b50da5a62ea916b1ed3412cf9b5a9",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.globalAlpha.imagepattern.worker-expected.txt": [
-   "92bc07d144d736bc4cfdadde14cec3dbdfe10f88",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.globalAlpha.imagepattern.worker.js": [
-   "cc765b4731ec1cbbc940acb8c352d46184b8d2d5",
+   "8441dc275d5a15eefdc453120842552f935fc9c5",
    "testharness"
   ],
   "offscreen-canvas/compositing/2d.composite.globalAlpha.invalid.html": [
@@ -483980,180 +483372,92 @@
    "5038d63da0caecd1e2bc4e9f656800ab35b2d808",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.image.copy-expected.txt": [
-   "cf2e3b64893550335f8cac21877ae5cd2620f9db",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.image.copy.html": [
-   "322f6cfd0a778a4164b8e7408fe85b47a868ed47",
+   "b4944a76c1059caeabf7ab42b1a592b03f38cedf",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.image.copy.worker-expected.txt": [
-   "2e526addd9d4933493ca15bb354a0c553a1fe436",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.image.copy.worker.js": [
-   "b6f842569074b0591e0586a5cbe8dc8183dba1af",
+   "493ee0b4015aa5b77de2d7ed559f5727093145c2",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.image.destination-atop-expected.txt": [
-   "76d13832c91bcaedbccd3224b97d36217d1fc783",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.image.destination-atop.html": [
-   "3c54ac26fb5fc9d73c3cef3bed1ab75b99916995",
+   "79ecfcdf95f31f3684e7fcd18d39336d595fea55",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.image.destination-atop.worker-expected.txt": [
-   "2e526addd9d4933493ca15bb354a0c553a1fe436",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.image.destination-atop.worker.js": [
-   "48f472bee8c203860d1542016c4b83d08bc0476b",
+   "d36ed50a0349d5864b6a423d1389c671acc403a9",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.image.destination-in-expected.txt": [
-   "abb41e94d67c30ac6d25a2174b1cbd06a6d50c56",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.image.destination-in.html": [
-   "1edbcdcf46f47430fbaacf334705bfb805d31476",
+   "ef205f1b955ab1779cf5a7448a2a4f5f160e24ac",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.image.destination-in.worker-expected.txt": [
-   "2e526addd9d4933493ca15bb354a0c553a1fe436",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.image.destination-in.worker.js": [
-   "622b35b7946e4a6ba6a258ad316f045706f8f16d",
+   "60eb61951cbc978b0a8500a854738cbf2a052e81",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.image.destination-out-expected.txt": [
-   "64d16e5869a206075c725ceb408b4b45412a33cd",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.image.destination-out.html": [
-   "e1d0cbad8ced8db0bd14e393c081ddd5866ea642",
+   "ecfeeed6bfdae2954c72624b7c17a33796864731",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.image.destination-out.worker-expected.txt": [
-   "2e526addd9d4933493ca15bb354a0c553a1fe436",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.image.destination-out.worker.js": [
-   "52c142efc7ee34ca0eadd7366d153e9e632ef5bc",
+   "ee97dccf7bd306d1ee06ecc6d7068d9a55d27b9d",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.image.destination-over-expected.txt": [
-   "728700ca02c22f315ee00d3e6f529576366fed13",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.image.destination-over.html": [
-   "c591a6559f1f10cdbdff55e8f2636c4ee313fb9f",
+   "eb58b3159659e42acae71feb859a3ceb2bb9d905",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.image.destination-over.worker-expected.txt": [
-   "2e526addd9d4933493ca15bb354a0c553a1fe436",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.image.destination-over.worker.js": [
-   "fb200df3ff46ff337d663301da17ddebc857a399",
+   "274c32c8906ac1ec74568bdf65428ed9cfde9bda",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.image.lighter-expected.txt": [
-   "fc6a57e09b6edb082374724a5482ae0b4253cd90",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.image.lighter.html": [
-   "4ba8cf7d39ceb507d98d327f14c90eb9c1d5c377",
+   "4935ca3c4114378113c0e5d85efd7fdb19e4244d",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.image.lighter.worker-expected.txt": [
-   "2e526addd9d4933493ca15bb354a0c553a1fe436",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.image.lighter.worker.js": [
-   "212446e51264a04cd08dbb330698922924cc31f9",
+   "05e39f95649873687883483cca33fb9b962ce481",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.image.source-atop-expected.txt": [
-   "6bf094a9f71217559a7d0fcc36122415814c4a9d",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.image.source-atop.html": [
-   "15e057dbe57a0aa9bcf59ef632e71dccbb526902",
+   "93a527de24d984de6761404a79a091d94e6f5617",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.image.source-atop.worker-expected.txt": [
-   "2e526addd9d4933493ca15bb354a0c553a1fe436",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.image.source-atop.worker.js": [
-   "82ca5794e4600a6d8ba15118d2066dcc770b670e",
+   "5fddfcfd8a879c0f01ed95d1a328be48a99d3819",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.image.source-in-expected.txt": [
-   "c0e437e6d6e3ce040054207ddac274d23da8e54e",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.image.source-in.html": [
-   "b8190d3662d09f97981ee13d20e31656a334d293",
+   "2519fe72d779b97fba1d51ef401f4ddb6b8625e4",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.image.source-in.worker-expected.txt": [
-   "2e526addd9d4933493ca15bb354a0c553a1fe436",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.image.source-in.worker.js": [
-   "9169f57986dafdfc05a05980a029e6b25fb27943",
+   "a5ca8d92e9428d9027bd5798a13b254ac13fd9f9",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.image.source-out-expected.txt": [
-   "43eef647c254deb84798bf21a838fe2713e71ef2",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.image.source-out.html": [
-   "7a305ba7d8832131a352de4e96ff9859a17a2b21",
+   "f57393b8c1b948fd7a39ceb29f4fbd3a8509adfb",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.image.source-out.worker-expected.txt": [
-   "2e526addd9d4933493ca15bb354a0c553a1fe436",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.image.source-out.worker.js": [
-   "56cc13e6c139f18c2ba27622e25a1cfd0c5fbfbd",
+   "1038ec5bcfd849eaf976245bb466ab37b452fc09",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.image.source-over-expected.txt": [
-   "e97acb4d5de288ae6ec8f0c6b6225de1f220469e",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.image.source-over.html": [
-   "5a3a67647b45069c7dd508c15ecd0617f85ba0d1",
+   "96c5252eb48faae88cd64912ef44e405706d49f9",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.image.source-over.worker-expected.txt": [
-   "2e526addd9d4933493ca15bb354a0c553a1fe436",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.image.source-over.worker.js": [
-   "210af01ba54ed87bf74d6947d42bbf523f33c8da",
+   "55c0f0eb9c4bbf1c382252f73f49d3d5e6a3952a",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.image.xor-expected.txt": [
-   "00f8c92e1156762fe38a838554069fcdaa1216f9",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.image.xor.html": [
-   "b40cda82e8a3004a10981975f37f3a5dde15a0a5",
+   "6039275bf06b4a9236d56b6e765d9b4a746a6f62",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.image.xor.worker-expected.txt": [
-   "2e526addd9d4933493ca15bb354a0c553a1fe436",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.image.xor.worker.js": [
-   "ed6b7df3eee147019ded79188630c7f69f018f8f",
+   "c4e20ffe51c4e2e129cfc9a9173f0c2780942c34",
    "testharness"
   ],
   "offscreen-canvas/compositing/2d.composite.operation.casesensitive.html": [
@@ -484444,84 +483748,44 @@
    "6db1757d1b80117b51e82cbe6dcb8665247b8468",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.uncovered.image.copy-expected.txt": [
-   "79b063c84931743cc5aa4e84fbf24dec0a3e34d9",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.uncovered.image.copy.html": [
-   "ae9196b7afd452be201c6ea794a4f875b7621df7",
+   "1e1cab64b72c8a10edb0fb5481c61fef143e3629",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.uncovered.image.copy.worker-expected.txt": [
-   "79b063c84931743cc5aa4e84fbf24dec0a3e34d9",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.uncovered.image.copy.worker.js": [
-   "50f3c8256b739acb4a7e5682dc5a4e4b6d6f7d02",
+   "7019270cd696ed017d5076b460831757b207e5f2",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.uncovered.image.destination-atop-expected.txt": [
-   "79b063c84931743cc5aa4e84fbf24dec0a3e34d9",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.uncovered.image.destination-atop.html": [
-   "a1d9c33c6ed206e9aa131c210095a6af578c1d97",
+   "bc6ac3ee2c1ef1c6fdab546d773b60234f2d03ed",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.uncovered.image.destination-atop.worker-expected.txt": [
-   "79b063c84931743cc5aa4e84fbf24dec0a3e34d9",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.uncovered.image.destination-atop.worker.js": [
-   "93bcd94c63235a3c4424e6ead372051175f3ea5b",
+   "aaae19827eebcd2321f49c2f685a244bbd9bf886",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.uncovered.image.destination-in-expected.txt": [
-   "79b063c84931743cc5aa4e84fbf24dec0a3e34d9",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.uncovered.image.destination-in.html": [
-   "2268f93885bdbbe5bee33972ffd357c532484a63",
+   "3410aa42ab9a5c103d15f34b44fbd21aac974007",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.uncovered.image.destination-in.worker-expected.txt": [
-   "79b063c84931743cc5aa4e84fbf24dec0a3e34d9",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.uncovered.image.destination-in.worker.js": [
-   "7bf112ca944fa27c9d08f1067304f9b227da7880",
+   "140e21d8eadf4a21675db8c0a39e7a5f1c518750",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.uncovered.image.source-in-expected.txt": [
-   "79b063c84931743cc5aa4e84fbf24dec0a3e34d9",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.uncovered.image.source-in.html": [
-   "8f544237168bee0b2577561c3fcdcfa6b4b8d757",
+   "979f0aa93ed936fd79e125bf54aaf178e11a2ac9",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.uncovered.image.source-in.worker-expected.txt": [
-   "79b063c84931743cc5aa4e84fbf24dec0a3e34d9",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.uncovered.image.source-in.worker.js": [
-   "600bc2f61595ea43ed3e1df56da621443ff4f33a",
+   "4a91470aa01152cc6d2010209bae666a6ed4af35",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.uncovered.image.source-out-expected.txt": [
-   "79b063c84931743cc5aa4e84fbf24dec0a3e34d9",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.uncovered.image.source-out.html": [
-   "d15616d6bc4036b0a03085eda3d2209e85700386",
+   "ce72032c582bb62a2ed43e8edf90cf130680df05",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.uncovered.image.source-out.worker-expected.txt": [
-   "79b063c84931743cc5aa4e84fbf24dec0a3e34d9",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.uncovered.image.source-out.worker.js": [
-   "d027bf3c1178ead59641519555d59150f53c5e8e",
+   "4c054cb29dff36f30e63c506222dc372715e8f5d",
    "testharness"
   ],
   "offscreen-canvas/compositing/2d.composite.uncovered.nocontext.copy.html": [
@@ -484564,84 +483828,44 @@
    "726c3a80c4997d7876c5e650fa3b17908a63b69c",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.uncovered.pattern.copy-expected.txt": [
-   "9228e4fbf30fd95b2beac87d0cd5c6eff0d5f8ec",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.uncovered.pattern.copy.html": [
-   "16d69485653d4df448a023fcc6460c427425ada1",
+   "84a538878f688d99ba4b5065844624a2fe0e8d74",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.uncovered.pattern.copy.worker-expected.txt": [
-   "9228e4fbf30fd95b2beac87d0cd5c6eff0d5f8ec",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.uncovered.pattern.copy.worker.js": [
-   "5e2d6590fa47ef7ca4a46f928ba03446c26076f2",
+   "b8a2e203423867843c508f5eb0e71d3630d6bc16",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.uncovered.pattern.destination-atop-expected.txt": [
-   "9228e4fbf30fd95b2beac87d0cd5c6eff0d5f8ec",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.uncovered.pattern.destination-atop.html": [
-   "9f5b8e8751a9720249de944fe0f2d7ff3819b90f",
+   "c7c20bb392bb2bcafc0cb8a2df3cfb0210303683",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.uncovered.pattern.destination-atop.worker-expected.txt": [
-   "9228e4fbf30fd95b2beac87d0cd5c6eff0d5f8ec",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.uncovered.pattern.destination-atop.worker.js": [
-   "7c4fb96a3722a9b9e6234a52920d4d7566caca02",
+   "a1a2eb8548f805ea87593e25e7028a5eeaa1a5f9",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.uncovered.pattern.destination-in-expected.txt": [
-   "9228e4fbf30fd95b2beac87d0cd5c6eff0d5f8ec",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.uncovered.pattern.destination-in.html": [
-   "6c08dd1ae226b033498c916318cb04918bf298ea",
+   "5058c75bcd810ab2b8a1bd09ec13200d3f7a81d9",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.uncovered.pattern.destination-in.worker-expected.txt": [
-   "9228e4fbf30fd95b2beac87d0cd5c6eff0d5f8ec",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.uncovered.pattern.destination-in.worker.js": [
-   "006268dcf6407a8040b8f5d7e314afea56738c6c",
+   "2ab713b941bec2d7d63c9f10a5fc78339ca9198d",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.uncovered.pattern.source-in-expected.txt": [
-   "9228e4fbf30fd95b2beac87d0cd5c6eff0d5f8ec",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.uncovered.pattern.source-in.html": [
-   "3cf0ac91c02914a189484adaa7d2ac579cf22a6d",
+   "4beb3fb24b472c35a57953803c7933698cb6bff5",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.uncovered.pattern.source-in.worker-expected.txt": [
-   "9228e4fbf30fd95b2beac87d0cd5c6eff0d5f8ec",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.uncovered.pattern.source-in.worker.js": [
-   "1472e20a2d1fcb0e95dfd14f86f034d092d7d56f",
+   "c4e6c85cd23076d60428e02418c0b9fea69fa94e",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.uncovered.pattern.source-out-expected.txt": [
-   "9228e4fbf30fd95b2beac87d0cd5c6eff0d5f8ec",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.uncovered.pattern.source-out.html": [
-   "7b8dd8ac35890fa01531cb616adb328302bef791",
+   "907c77aa46dd07ab4c3e2a5de1827d02c14fd0a3",
    "testharness"
   ],
-  "offscreen-canvas/compositing/2d.composite.uncovered.pattern.source-out.worker-expected.txt": [
-   "9228e4fbf30fd95b2beac87d0cd5c6eff0d5f8ec",
-   "support"
-  ],
   "offscreen-canvas/compositing/2d.composite.uncovered.pattern.source-out.worker.js": [
-   "c9f71f237804e25903d67e5526faa122b1efbbf1",
+   "6abb66a951a0039e892d6fe27fcc8b54f2fedf81",
    "testharness"
   ],
   "offscreen-canvas/conformance-requirements/2d.coordinatespace.html": [
@@ -484676,164 +483900,84 @@
    "9b08cc86257ff0e5220c23e3a76ffb8a4d7d8fa7",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.3arg-expected.txt": [
-   "3d26a8d0240e7f43bf5c997df45901ff9f7908e4",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.3arg.html": [
-   "44f33cf5e2ffe9b61a30d8c0617daadb9308d686",
+   "f3ede9a81eecfa09703599dce0c5389e24121c00",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.3arg.worker-expected.txt": [
-   "2e526addd9d4933493ca15bb354a0c553a1fe436",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.3arg.worker.js": [
-   "a600e2f28f2cce1f903f13eb8aea221c8e97c512",
+   "6be6bdf96ee115bb8f2d9b330eb31446a555ca24",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.5arg-expected.txt": [
-   "377c9e8afa07cef3d91f41b15ccb57018eba0fe0",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.5arg.html": [
-   "d82aa2d45d2367dfaf3e68d4a5a0b8b7b85bc254",
+   "7671f3a493426c322286ddf758456e41e46e5722",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.5arg.worker-expected.txt": [
-   "2e526addd9d4933493ca15bb354a0c553a1fe436",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.5arg.worker.js": [
-   "6fafc4e4f69b69eb83d24c5db899e4dad7660262",
+   "5917615f3978a334b57b491cb56968709e2a6757",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.basic-expected.txt": [
-   "a85be1ac5d98614a688eb80d35f23253c991e493",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.basic.html": [
-   "44da5d5e5bbda3dde5b71d023b3f704a96e92fe0",
+   "829fc6b4a67458faf7bd766c8eb439bc89500227",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.basic.worker-expected.txt": [
-   "2e526addd9d4933493ca15bb354a0c553a1fe436",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.basic.worker.js": [
-   "697449f6dfe6fdfba2c58ee33e871782523fae80",
+   "afa730b9f7cb0f07e8e37f7cc7210026999a5714",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.destpos-expected.txt": [
-   "9584dc3a89a111a7f287774301f21baf9da390b8",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.destpos.html": [
-   "aad38fee72f09f0e4988df0643441747dfc6a51c",
+   "a01ace172a7a767bd8317b47c0c4a7a340e0fad7",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.destpos.worker-expected.txt": [
-   "2e526addd9d4933493ca15bb354a0c553a1fe436",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.destpos.worker.js": [
-   "19f4bbcb30673856703231c1e5338121151e9520",
+   "687f0244f25b35a477c2131c70b54b7fb0399f1a",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.destsize-expected.txt": [
-   "f9894d2fb38be004d226bc2c63f77c745a81b4c0",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.destsize.html": [
-   "ce7f835bc8d57e5cc0392a65c0448557da082ccc",
+   "ddea3857568db7ad0afd8d48606b91676ee5caca",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.destsize.worker-expected.txt": [
-   "2e526addd9d4933493ca15bb354a0c553a1fe436",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.destsize.worker.js": [
-   "3b85461f71f7f0df332929aa569cc2c1b6c196ba",
+   "7f586ef0558e6ae0b4adfb80ef7bcd8f53828f30",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcepos-expected.txt": [
-   "87d0d713e261a60441a59b087441342e05a072ee",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcepos.html": [
-   "c18a47ff34f241f975dab49b5a10ba11020527c0",
+   "6541d762bf1df8f0f76078888f7f1351578fa8b7",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcepos.worker-expected.txt": [
-   "2e526addd9d4933493ca15bb354a0c553a1fe436",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcepos.worker.js": [
-   "8525ee99efa916c811c8709b164ac875eb87967d",
+   "7cefad92dd69a62f8264ddf58d2cb97c0472a975",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcesize-expected.txt": [
-   "b9ea887140b9970eeef70097518b4fad8d874113",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcesize.html": [
-   "4b43d5fb08eedf17783eb5787df588716cb956f7",
+   "3c718c8da45c8ca1eca4c325ef9f22a620d103c9",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcesize.worker-expected.txt": [
-   "2e526addd9d4933493ca15bb354a0c553a1fe436",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcesize.worker.js": [
-   "d73e3b952dd14753314b354b224a40dd85b03b71",
+   "dac1c7a0a9ab58b27988020a2f27bbb9a5710a3a",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.alpha-expected.txt": [
-   "b728d91ee9e39940f1976d1e2731bde94ef2e5b4",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.alpha.html": [
-   "33b2502f65b0898811d4b79e3bff20b603b11723",
+   "f1a1f083232149929d0e6fd2778e18c7bac9eb33",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.alpha.worker-expected.txt": [
-   "2e526addd9d4933493ca15bb354a0c553a1fe436",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.alpha.worker.js": [
-   "5f29c1a1cca110bb9aff7f683aec514e6ec21cbc",
+   "6828af9225e6e18f7295e6c7127904e3b295b8d1",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.animated.poster-expected.txt": [
-   "b14e146d235fd69e536a03b0e411ad848d69d387",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.animated.poster.html": [
-   "16e9f791b2a8e2b2f95ce15706de853c27a4cc1e",
+   "28708941b466146f46cd5f096f9201405d795409",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.animated.poster.worker-expected.txt": [
-   "b14e146d235fd69e536a03b0e411ad848d69d387",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.animated.poster.worker.js": [
-   "c3b566e844a7a33a724dc362b632b114e50249e9",
+   "ce6e8ee4f55f21445c1a1476f08f2757d365e805",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.broken-expected.txt": [
-   "83d48a08c4489f2008b61b603a859dce0edd60b0",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.broken.html": [
-   "a64d20e731e80a59fda6845f9a373a630b7903a7",
+   "531376aaab7e50f94fb2a1416413a56ecb547063",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.broken.worker-expected.txt": [
-   "2e526addd9d4933493ca15bb354a0c553a1fe436",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.broken.worker.js": [
-   "63163bd60aaac084413f7daa09018a705236710b",
+   "4acc40b49e68b5e7ddfe675333a8abcdc91c683a",
    "testharness"
   ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.canvas.html": [
@@ -484844,132 +483988,68 @@
    "3ff2dbecd7c958c4d51f8e8d1e3c49de71a74aa5",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.clip-expected.txt": [
-   "bf1bea385aac0fec06452dd37ba54b388cf9d2a3",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.clip.html": [
-   "51651e56f390d1174469778811d16f2b1f07f766",
+   "562a9c5d0b840d82773e6ce3bbb41b0b000929ea",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.clip.worker-expected.txt": [
-   "2e526addd9d4933493ca15bb354a0c553a1fe436",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.clip.worker.js": [
-   "90d0aad3ff8c61178e2934f7df104e7f1a3a050f",
+   "1741f15b6822c53ff8125063d3968878d4fad73f",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.composite-expected.txt": [
-   "5c1a406e7cff68cf36899eb9418849298989962c",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.composite.html": [
-   "ba56e366f5a8f1864ae3555582fb66ab3abbcc48",
+   "de37739993f99dff7739d785bede999b5da35f4e",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.composite.worker-expected.txt": [
-   "2e526addd9d4933493ca15bb354a0c553a1fe436",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.composite.worker.js": [
-   "09e3b3a00af164a01b2716e99c6008d32b937f89",
+   "eecc8914e3b5a039f9a298b0f4db4c1b89eeb09d",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.floatsource-expected.txt": [
-   "28bfd4574f6be7b879b5fdc66952b9ae4fc4b4c4",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.floatsource.html": [
-   "49a88fab035f2c68dadabacfb73ff8eac9c44195",
+   "f90b41d3f30aa90b34adf477767494dc6c7afd9e",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.floatsource.worker-expected.txt": [
-   "2e526addd9d4933493ca15bb354a0c553a1fe436",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.floatsource.worker.js": [
-   "45398e83191593eb07af4481eeb2f70a1da14ecf",
+   "d154a12b0a6ce523b4b3fe30b5fae82a85b52501",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativedest-expected.txt": [
-   "9617d8fa6f2f6ff8e64345880f367a40bc7d46a8",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativedest.html": [
-   "9466738bcf5c221bf13b099b635fe1f634ad8c7d",
+   "d018e943d7739e2736bffe487f8883869cb44962",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativedest.worker-expected.txt": [
-   "9617d8fa6f2f6ff8e64345880f367a40bc7d46a8",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativedest.worker.js": [
-   "284b45840677af97a04f2211dc485893d5776cc2",
+   "0185dd4a4529153038bd060404d7740598358fd9",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativedir-expected.txt": [
-   "8ef68a351600a3314e361ae348efc15a6d589706",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativedir.html": [
-   "4cbd37445c18f2deee6cc46950400f2f2765b87f",
+   "2f0a91dacc7cfa56a644ca3a89bf3526ee87a52f",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativedir.worker-expected.txt": [
-   "8ef68a351600a3314e361ae348efc15a6d589706",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativedir.worker.js": [
-   "00f0e7ec0661659ace3d69f7be306d86ffb4d6f6",
+   "b29cdad740ee0d27d43d8443c3d8a1a5a69e6dc6",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativesource-expected.txt": [
-   "17b8650bee4218efd818566d8139bf829402f739",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativesource.html": [
-   "66e9c2122312e6427746dcb27f235d8e191a078d",
+   "c2f78e5a952621be409f2377fd5213286b568705",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativesource.worker-expected.txt": [
-   "17b8650bee4218efd818566d8139bf829402f739",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativesource.worker.js": [
-   "0f24542d928568e45161cf128f43dab84c8b88ec",
+   "2171f692f5303ca6ceb8535850cd28e4a2edf5fd",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.nonfinite-expected.txt": [
-   "7279047229ef5d50fbff2b21f66ec80c632e8e0c",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.nonfinite.html": [
-   "9ca1a884b609bd2b745a1f1d39ec9db4225b3a89",
+   "c6bab2ea43df63d282882276c651936e2b4fd13a",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.nonfinite.worker-expected.txt": [
-   "7279047229ef5d50fbff2b21f66ec80c632e8e0c",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.nonfinite.worker.js": [
-   "f5d26785800a268867df360b90686fd1f9084dde",
+   "eaf58aa4909fd4b0434a33344671afd52e453933",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.nowrap-expected.txt": [
-   "5f13a8070f46ecdda49eba7740631c5bd29593e5",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.nowrap.html": [
-   "4d71c21bfb92adfd75a825df872be24d9ee9c5fa",
+   "f0dfb298f28a8cdf6eb0ca5d6c07e532e42587c8",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.nowrap.worker-expected.txt": [
-   "5f13a8070f46ecdda49eba7740631c5bd29593e5",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.nowrap.worker.js": [
-   "6f6b1e93a393064a7991a80d9e29211b6473144b",
+   "aab08771db443348d193238edc60c1e14d0425eb",
    "testharness"
   ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.null.html": [
@@ -484980,20 +484060,12 @@
    "8ed0887ecd879783bd18e3eb82215a48f8db2285",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.path-expected.txt": [
-   "f621096bb20c24401b1223d8ba4af3db7f8a11df",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.path.html": [
-   "47763cdb434466c3b28dfa87249e0ff4d746810c",
+   "10daf12cd23e0ea254c6561c69a2898e706ee536",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.path.worker-expected.txt": [
-   "2e526addd9d4933493ca15bb354a0c553a1fe436",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.path.worker.js": [
-   "ba8302b5e1e8a0d97c970a41619644cb942e00e0",
+   "45a082974904f12248bec16565ca6e276fc9b14d",
    "testharness"
   ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.self.1.html": [
@@ -485012,36 +484084,20 @@
    "c6fe0dcd2a877d052bb3ba82a6ddf3f9fe8c71de",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.svg-expected.txt": [
-   "cf858bbb307d7efed6e8a8bd8673569de77e7889",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.svg.html": [
-   "ff83aa0330f7f37934833712f5055649c2d162df",
+   "72ba2ec7ba49bdd345b2524c29ab291b2cbe09f5",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.svg.worker-expected.txt": [
-   "cf858bbb307d7efed6e8a8bd8673569de77e7889",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.svg.worker.js": [
-   "382190e6cd31c4122b825424468a84b510903e57",
+   "50892c2b4a0af0b05667bc1cd36577e3c1c403a8",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.transform-expected.txt": [
-   "7dd986dee7c0ecf6452b1d21c5df0894df785675",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.transform.html": [
-   "2adcf02142233de19404eef6e2a965b7b07c5e8e",
+   "b782ba87e68abd39928aeeb9c0d3401699ce6f1b",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.transform.worker-expected.txt": [
-   "2e526addd9d4933493ca15bb354a0c553a1fe436",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.transform.worker.js": [
-   "5e1095bc74efbfddfd7fe8cc00cb244cf5721182",
+   "a4cb865dc1e550820d1225cceae605e2a15bfb1c",
    "testharness"
   ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.wrongtype.html": [
@@ -485060,36 +484116,20 @@
    "1e9d63cf92b0353c88426cf4cd68e5a507333435",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerosource-expected.txt": [
-   "693d3f6be35ca21de53be5f991257e4c7e477245",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerosource.html": [
-   "645e2c9711f5ea2448b237abc271571a9adc2d50",
+   "b941f3aa3aaa89322b40017d029274dcec264c85",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerosource.image-expected.txt": [
-   "aa2f43c05f6bbc3544dbfa28e36c8505ab737e64",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerosource.image.html": [
-   "a38e98208b7b5b5df0c46acafacbad4d512d4fd8",
+   "1abcb6abf495ae8f6959393dd3496d6041b6cc75",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerosource.image.worker-expected.txt": [
-   "aa2f43c05f6bbc3544dbfa28e36c8505ab737e64",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerosource.image.worker.js": [
-   "304acdc11732c3ee9c106587dcbf41fb7d270c68",
+   "cf472ac4179c9903261f8c72f11d49bc610778b9",
    "testharness"
   ],
-  "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerosource.worker-expected.txt": [
-   "693d3f6be35ca21de53be5f991257e4c7e477245",
-   "support"
-  ],
   "offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerosource.worker.js": [
-   "1e6f54f3882d40419c7f7d27c95f9b4c871e2c06",
+   "b3b6a59cb7af150863f483de43837eb914dfafc6",
    "testharness"
   ],
   "offscreen-canvas/drawing-rectangles-to-the-canvas/2d.clearRect.basic.html": [
@@ -486748,20 +485788,12 @@
    "b8366536ae5cb8de5c20e70e1377589d10e7d50f",
    "testharness"
   ],
-  "offscreen-canvas/fill-and-stroke-styles/2d.pattern.basic.image-expected.txt": [
-   "c3717a1dcdda44f0accf062929f9b69c5a90f97c",
-   "support"
-  ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.basic.image.html": [
-   "1e0585ec104e89885bb86fb384497efac19e8a74",
+   "c1e356519b36308191954bb3acfa494500631a61",
    "testharness"
   ],
-  "offscreen-canvas/fill-and-stroke-styles/2d.pattern.basic.image.worker-expected.txt": [
-   "92bc07d144d736bc4cfdadde14cec3dbdfe10f88",
-   "support"
-  ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.basic.image.worker.js": [
-   "adf6b7d580253f9a499bfa6fbd474ebeb9e81e34",
+   "543aaef20f201433cbfe95744b06340806519459",
    "testharness"
   ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.basic.nocontext.html": [
@@ -486780,20 +485812,12 @@
    "c015002c6ada142efd66e6c0d9f75876cb46b13a",
    "testharness"
   ],
-  "offscreen-canvas/fill-and-stroke-styles/2d.pattern.crosscanvas-expected.txt": [
-   "a6e51734c4dab7d2b95c685fb86a0152a20be720",
-   "support"
-  ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.crosscanvas.html": [
-   "d3c8cd636b4baa840195c21fa4e3c17154d363e9",
+   "daf2fd268d9003b6b14ee9c0272f506f5e7ea19f",
    "testharness"
   ],
-  "offscreen-canvas/fill-and-stroke-styles/2d.pattern.crosscanvas.worker-expected.txt": [
-   "92bc07d144d736bc4cfdadde14cec3dbdfe10f88",
-   "support"
-  ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.crosscanvas.worker.js": [
-   "f660f213d6ae3d44bff95c0dc069e3dc25017102",
+   "ea719d423597b1a2794e5ff51f9c25e99dd8e195",
    "testharness"
   ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.image.null.html": [
@@ -486836,84 +485860,44 @@
    "31d7462aaa45cfa96dff1587e3836a9a104556fa",
    "testharness"
   ],
-  "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.basic-expected.txt": [
-   "97307ab61f142d5c324113c465b42c994e314750",
-   "support"
-  ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.basic.html": [
-   "ea5adcc8fe4b934bee37082f70a3c837c7b72575",
+   "ae3bdb2dba553a277d52e319e70f8cda1e542360",
    "testharness"
   ],
-  "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.basic.worker-expected.txt": [
-   "92bc07d144d736bc4cfdadde14cec3dbdfe10f88",
-   "support"
-  ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.basic.worker.js": [
-   "042f0c61a715bf1c4b8be0e04b88bba89c45ff48",
+   "903cab7174a269486a9cc1c5cc66eb09cb10f2bb",
    "testharness"
   ],
-  "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord1-expected.txt": [
-   "777447d1b2d48fd2dd4434c543a3c7ac62f391b7",
-   "support"
-  ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord1.html": [
-   "8fcab2b0c9ab3d6fb017e66311460dd242be544b",
+   "953861cde8cb4ce4ba1504fe95fc6de34e4b6ca1",
    "testharness"
   ],
-  "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord1.worker-expected.txt": [
-   "92bc07d144d736bc4cfdadde14cec3dbdfe10f88",
-   "support"
-  ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord1.worker.js": [
-   "fae7427b58d120c745870c817b4facdb4912fe25",
+   "f8432144cdb4d21355ac8df785636ec158a3164c",
    "testharness"
   ],
-  "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord2-expected.txt": [
-   "a75eeb014714c87f0739ef8c02e1467396b15750",
-   "support"
-  ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord2.html": [
-   "191f0bde3e189b9ac511ae4ab1900174dbff136b",
+   "f475aae99a369217a40e065f23acad53433c43a5",
    "testharness"
   ],
-  "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord2.worker-expected.txt": [
-   "92bc07d144d736bc4cfdadde14cec3dbdfe10f88",
-   "support"
-  ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord2.worker.js": [
-   "5c6c5c1c21e3fcb091df9234a14334500c9698b6",
+   "abf6195c48a694d3a387459a7edc506de9ba81c2",
    "testharness"
   ],
-  "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord3-expected.txt": [
-   "add81f90127902138fb240e427cce23736e254d5",
-   "support"
-  ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord3.html": [
-   "966fac841238aa0a01248736b025c4d96ad588fc",
+   "2c54ad40c58ebf3c3ff69caad36195ae7d489e45",
    "testharness"
   ],
-  "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord3.worker-expected.txt": [
-   "92bc07d144d736bc4cfdadde14cec3dbdfe10f88",
-   "support"
-  ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord3.worker.js": [
-   "8cc326b733ac8175e1ea234029d5a58721de1657",
+   "a1a438af1eb6d861885df634ad9b0e39e44d229f",
    "testharness"
   ],
-  "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.outside-expected.txt": [
-   "b5f655ffe15c5716a2f7ad893acc19aab25d31bf",
-   "support"
-  ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.outside.html": [
-   "7fbee4db8babecaf16a63c1e41263ae03ce71ff2",
+   "3a7374d9d3c4c86b7d67348a47ccc24b6ca82fce",
    "testharness"
   ],
-  "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.outside.worker-expected.txt": [
-   "92bc07d144d736bc4cfdadde14cec3dbdfe10f88",
-   "support"
-  ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.outside.worker.js": [
-   "85594b6370eb85db023419c7df4c1695e72048f3",
+   "0b2c58d52e647f357deff50ab2fc208a8a6be0d2",
    "testharness"
   ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.orientation.canvas.html": [
@@ -486924,196 +485908,100 @@
    "d017f08306f2bf31321fbcc41a501142c99107c2",
    "testharness"
   ],
-  "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.orientation.image-expected.txt": [
-   "feed82cad32a39437b7825d82e9593de07dafc1d",
-   "support"
-  ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.orientation.image.html": [
-   "02ffc97632e521144b82c9a76990a0ff8b1ec859",
+   "ca0bb3351443ca1ef15a4bf28dc1373d273a07af",
    "testharness"
   ],
-  "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.orientation.image.worker-expected.txt": [
-   "feed82cad32a39437b7825d82e9593de07dafc1d",
-   "support"
-  ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.orientation.image.worker.js": [
-   "e365dd6bbec9fc0d92557756794d99ca88acbae5",
+   "f771b226cd7c6238722f091676118f078b235022",
    "testharness"
   ],
-  "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.basic-expected.txt": [
-   "52a356239707438710459e7152b3029b13865479",
-   "support"
-  ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.basic.html": [
-   "8149d8938ea2b11a9a7aae8887b149b46cc0be3b",
+   "8fcfb71d376c8da43dbe856eac6d9c6726bd41e1",
    "testharness"
   ],
-  "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.basic.worker-expected.txt": [
-   "92bc07d144d736bc4cfdadde14cec3dbdfe10f88",
-   "support"
-  ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.basic.worker.js": [
-   "d6bad4c2240e30198424fc75c1e54aa345ea1393",
+   "fa094c35b4830389d717c6c6a278ff10c0c313ee",
    "testharness"
   ],
-  "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord1-expected.txt": [
-   "f3c93924019a37a72c0e28f4ce07d4f5a9871a1d",
-   "support"
-  ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord1.html": [
-   "63d30dba51ed1ca77311e5e7b19b1b74c3b63235",
+   "1a9731aa08b3c27ef9b2944385f4c263f6b6ae6b",
    "testharness"
   ],
-  "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord1.worker-expected.txt": [
-   "92bc07d144d736bc4cfdadde14cec3dbdfe10f88",
-   "support"
-  ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord1.worker.js": [
-   "b31ebcb665642f147d240d0910fe5356330a9394",
+   "8ca59610fec063397fec0ba7cea6c8c384665b29",
    "testharness"
   ],
-  "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord2-expected.txt": [
-   "d684589bc5dc7b510ec9c963b92c60b640e3d8a7",
-   "support"
-  ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord2.html": [
-   "404a51d727b90dc32bff93a2f12ec25d4d6f2344",
+   "63b1cac13b63499b453a7635302fec8116c56a50",
    "testharness"
   ],
-  "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord2.worker-expected.txt": [
-   "92bc07d144d736bc4cfdadde14cec3dbdfe10f88",
-   "support"
-  ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord2.worker.js": [
-   "3ab6b4068af3de97218afc4954e11eecb38517ea",
+   "5ac23fecd91e9df4c35fbbafcd4e11e8d46ef433",
    "testharness"
   ],
-  "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord3-expected.txt": [
-   "a04cb90deb189e6a16de4b68bdfc6d6bc4a887e3",
-   "support"
-  ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord3.html": [
-   "70028f7b9fbf2a5d3c8c2566d2b2635b737e55ac",
+   "7e14e00d6f3a9ea12404a1e4e35ec32be2c07af5",
    "testharness"
   ],
-  "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord3.worker-expected.txt": [
-   "92bc07d144d736bc4cfdadde14cec3dbdfe10f88",
-   "support"
-  ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord3.worker.js": [
-   "3ae9dc9a7aa98993df946f57aeea609d196b22df",
+   "2b8941a0eb162b87a2a104d7b3d022c1c171e046",
    "testharness"
   ],
-  "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.outside-expected.txt": [
-   "c341eb3b497f400a2d184c69cbe2389907181914",
-   "support"
-  ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.outside.html": [
-   "7af8e2dfe19b71056ebd2832c8b3eb155989b62c",
+   "862a80e7dc16fe9dd3df18e4b5605b74eed4a0cc",
    "testharness"
   ],
-  "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.outside.worker-expected.txt": [
-   "92bc07d144d736bc4cfdadde14cec3dbdfe10f88",
-   "support"
-  ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.outside.worker.js": [
-   "3db708229dd930f0388b8f009c8c2a95c4a6542d",
+   "1d6fb4b9f7d5e9e12d57dabe6e9854cb9b6cb5cf",
    "testharness"
   ],
-  "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.basic-expected.txt": [
-   "1e66d620f38e9d52deb7798d103ba213feeca4f3",
-   "support"
-  ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.basic.html": [
-   "45e36a403ca9a84d329081ff062de138b3a06a93",
+   "ccd6ca5841a012bc91c0affc882a166f04a0dc26",
    "testharness"
   ],
-  "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.basic.worker-expected.txt": [
-   "92bc07d144d736bc4cfdadde14cec3dbdfe10f88",
-   "support"
-  ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.basic.worker.js": [
-   "209427ea824be71c82098da9671f121b313fbcb4",
+   "1dfa11a9a84ad9ff4ad33ba063f32bacb2805ba8",
    "testharness"
   ],
-  "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.coord1-expected.txt": [
-   "aae596490e7a97cb14fd086d5474d964a3639ffb",
-   "support"
-  ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.coord1.html": [
-   "7818db33ec304467d4c96473c1395febf30eba61",
+   "b5fbeec3b7399c7e330e8b1e603094bb5dbe06ae",
    "testharness"
   ],
-  "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.coord1.worker-expected.txt": [
-   "92bc07d144d736bc4cfdadde14cec3dbdfe10f88",
-   "support"
-  ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.coord1.worker.js": [
-   "080887b105138813674613b72f4d8cf95d1bd70d",
+   "3e07c9f9717af923736bd05b0052167740dca9fe",
    "testharness"
   ],
-  "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.outside-expected.txt": [
-   "faf86597c333474c6a33bd25fee34ce2d353fd79",
-   "support"
-  ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.outside.html": [
-   "f2fcaac9cbf7091e39f4c766674854341a56ebc4",
+   "1805608d1ec242e5efb9b821ca82db5d42ce9728",
    "testharness"
   ],
-  "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.outside.worker-expected.txt": [
-   "92bc07d144d736bc4cfdadde14cec3dbdfe10f88",
-   "support"
-  ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.outside.worker.js": [
-   "6c43646e1e9bfae724ad4ac90d88e97887f07c72",
+   "e2cb046d2883c31e9c11e10e158e4f7750699b41",
    "testharness"
   ],
-  "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.basic-expected.txt": [
-   "2f7779b6228696bf93e69bb80f85901e821dd979",
-   "support"
-  ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.basic.html": [
-   "813f7f3a581abca6ecc753fb2c95f47318b38142",
+   "259c5a5d1780884e1f72d286b103dc58329c2b81",
    "testharness"
   ],
-  "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.basic.worker-expected.txt": [
-   "92bc07d144d736bc4cfdadde14cec3dbdfe10f88",
-   "support"
-  ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.basic.worker.js": [
-   "d9f53a00eb414a6693e0d94dd10796a4723e4375",
+   "aa886ef9610cf152feb25152a0b3a13b108da455",
    "testharness"
   ],
-  "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.coord1-expected.txt": [
-   "369a37f29e6960678ee966e8b87970a2f1020af7",
-   "support"
-  ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.coord1.html": [
-   "6102405784f0d252148f0df1988f72db74705c33",
+   "373fafea8a66ad6a4cfd6837c95cb572c348ecd6",
    "testharness"
   ],
-  "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.coord1.worker-expected.txt": [
-   "92bc07d144d736bc4cfdadde14cec3dbdfe10f88",
-   "support"
-  ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.coord1.worker.js": [
-   "92c39e2f967b75c7a8cf62385b3df256b9663337",
+   "4fbedca9e73441c70e67e3273f303518103066c4",
    "testharness"
   ],
-  "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.outside-expected.txt": [
-   "8b87770d793a1e4f99b8f35a8297a44c59dcebbe",
-   "support"
-  ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.outside.html": [
-   "67c4061c753af15266fd79c48145727b2da55975",
+   "1ef52dd8d108466f79bba52788706dac0b3f0654",
    "testharness"
   ],
-  "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.outside.worker-expected.txt": [
-   "92bc07d144d736bc4cfdadde14cec3dbdfe10f88",
-   "support"
-  ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.outside.worker.js": [
-   "dd6d243ba5a46b44c5af47907532e03e66d8ff21",
+   "50a15179897ec0fea1ff00283cba81972d1feb61",
    "testharness"
   ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.repeat.case.html": [
@@ -487124,20 +486012,12 @@
    "1f7557e483f0168a272e36073d160a58b21f40ba",
    "testharness"
   ],
-  "offscreen-canvas/fill-and-stroke-styles/2d.pattern.repeat.empty-expected.txt": [
-   "d9664a739e475c6ef9c97828cb3d889222e17e81",
-   "support"
-  ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.repeat.empty.html": [
-   "426c069a446ca4337e100884c2bad2e681f96cb7",
+   "9f40fe695777d73f27bb84e7de9fe406becd31d2",
    "testharness"
   ],
-  "offscreen-canvas/fill-and-stroke-styles/2d.pattern.repeat.empty.worker-expected.txt": [
-   "92bc07d144d736bc4cfdadde14cec3dbdfe10f88",
-   "support"
-  ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.repeat.empty.worker.js": [
-   "5338663f9f5e33b8d51e6a4a0d0562bbef1acae6",
+   "a31afa7139b7f961ea07f16576bee0c334c38395",
    "testharness"
   ],
   "offscreen-canvas/fill-and-stroke-styles/2d.pattern.repeat.null.html": [
@@ -489232,100 +488112,52 @@
    "606e852a1f36ad8c51e2d52266dd02b84e77c777",
    "testharness"
   ],
-  "offscreen-canvas/shadows/2d.shadow.image.alpha-expected.txt": [
-   "51d72c983826008bd4ef282ab82d628267e4fe3d",
-   "support"
-  ],
   "offscreen-canvas/shadows/2d.shadow.image.alpha.html": [
-   "205e32de01c6b012989b042c4f8627dc69b93ed0",
+   "25ce1c1b107ba3ea2639e3e51c76ee5d0c69282a",
    "testharness"
   ],
-  "offscreen-canvas/shadows/2d.shadow.image.alpha.worker-expected.txt": [
-   "51d72c983826008bd4ef282ab82d628267e4fe3d",
-   "support"
-  ],
   "offscreen-canvas/shadows/2d.shadow.image.alpha.worker.js": [
-   "b40be65f5cd34c533c03d353d428af74504ccf0a",
+   "17fc5a7dad337eef680fb37bf4a7e744bac95c1e",
    "testharness"
   ],
-  "offscreen-canvas/shadows/2d.shadow.image.basic-expected.txt": [
-   "330625fc4dba9b309ea34ad17e2c1567c54cc582",
-   "support"
-  ],
   "offscreen-canvas/shadows/2d.shadow.image.basic.html": [
-   "9f16324a861413fbec0b8deb82a88b01e172147c",
+   "e1198acd10c18ee07ed4b9b0ed4da00f590b51a2",
    "testharness"
   ],
-  "offscreen-canvas/shadows/2d.shadow.image.basic.worker-expected.txt": [
-   "330625fc4dba9b309ea34ad17e2c1567c54cc582",
-   "support"
-  ],
   "offscreen-canvas/shadows/2d.shadow.image.basic.worker.js": [
-   "e55f2d64c47f06bb0a84dbb812ded32c5824b361",
+   "45126a218f95ae28a35d318615b57a40af0ec7cb",
    "testharness"
   ],
-  "offscreen-canvas/shadows/2d.shadow.image.scale-expected.txt": [
-   "617393d7a140c889cf4f707bf48c20101653e5a3",
-   "support"
-  ],
   "offscreen-canvas/shadows/2d.shadow.image.scale.html": [
-   "a1197f21828ca1924a76a12691bb6fadb1fdbc91",
+   "7f3057b60220d23f4e141e1a29e4ea72621385dd",
    "testharness"
   ],
-  "offscreen-canvas/shadows/2d.shadow.image.scale.worker-expected.txt": [
-   "617393d7a140c889cf4f707bf48c20101653e5a3",
-   "support"
-  ],
   "offscreen-canvas/shadows/2d.shadow.image.scale.worker.js": [
-   "e1d6198f344e30b7c71137f0a63e66b2c82a3d34",
+   "9ca1d7948a05eb33550431f6744c7e5b18e7ea8e",
    "testharness"
   ],
-  "offscreen-canvas/shadows/2d.shadow.image.section-expected.txt": [
-   "c500dd6374a6b9e7e92f1eaf682764cf0ec590df",
-   "support"
-  ],
   "offscreen-canvas/shadows/2d.shadow.image.section.html": [
-   "b3f9ab17f04dffa76bda6f8d7062ea7d12ba8743",
+   "1abc7558e12134821e008209481ebb6418953428",
    "testharness"
   ],
-  "offscreen-canvas/shadows/2d.shadow.image.section.worker-expected.txt": [
-   "c500dd6374a6b9e7e92f1eaf682764cf0ec590df",
-   "support"
-  ],
   "offscreen-canvas/shadows/2d.shadow.image.section.worker.js": [
-   "a278f4666dc8c6e4c47b4a4c7f9afa2de9e81536",
+   "3b4deaa657d925f5ac3cb126fd34650ce2846e66",
    "testharness"
   ],
-  "offscreen-canvas/shadows/2d.shadow.image.transparent.1-expected.txt": [
-   "874688f6bd51abf37056e8c58c326adf82f3ca02",
-   "support"
-  ],
   "offscreen-canvas/shadows/2d.shadow.image.transparent.1.html": [
-   "05c125a376fbc7815743913753f586b442a4c559",
+   "1d54ae599b7edcf3e92abdcc86ca97798a73c03a",
    "testharness"
   ],
-  "offscreen-canvas/shadows/2d.shadow.image.transparent.1.worker-expected.txt": [
-   "874688f6bd51abf37056e8c58c326adf82f3ca02",
-   "support"
-  ],
   "offscreen-canvas/shadows/2d.shadow.image.transparent.1.worker.js": [
-   "599140ec83aa04a7e0d00b17ae000020968b36d1",
+   "76dc8ff58f641b384f88580e4cb0690c06920c86",
    "testharness"
   ],
-  "offscreen-canvas/shadows/2d.shadow.image.transparent.2-expected.txt": [
-   "96bf5c79d66f06f4c723810940c89e4a409b6eb7",
-   "support"
-  ],
   "offscreen-canvas/shadows/2d.shadow.image.transparent.2.html": [
-   "32fa486b3f489622448cfef9fd96373220c9a2e4",
+   "2e2230b02bf56867978265a7da0176834400b46a",
    "testharness"
   ],
-  "offscreen-canvas/shadows/2d.shadow.image.transparent.2.worker-expected.txt": [
-   "96bf5c79d66f06f4c723810940c89e4a409b6eb7",
-   "support"
-  ],
   "offscreen-canvas/shadows/2d.shadow.image.transparent.2.worker.js": [
-   "5b8862c0c8ae56721479bcc210efbebd644cee63",
+   "5f54763d129f42005cd64d185845bbebe0bea366",
    "testharness"
   ],
   "offscreen-canvas/shadows/2d.shadow.offset.negativeX.html": [
@@ -489368,68 +488200,36 @@
    "f4c7c5bad8fd52496d6fefd091d90d1e4cae7c69",
    "testharness"
   ],
-  "offscreen-canvas/shadows/2d.shadow.pattern.alpha-expected.txt": [
-   "1cedb943de842646c80abf8d350d2706d5e4a297",
-   "support"
-  ],
   "offscreen-canvas/shadows/2d.shadow.pattern.alpha.html": [
-   "3e55eb40003a92d6f01cfc96a6c32f4f211ed46c",
+   "3f11789c88adbec169a918567b11b0d2f8e2b159",
    "testharness"
   ],
-  "offscreen-canvas/shadows/2d.shadow.pattern.alpha.worker-expected.txt": [
-   "1cedb943de842646c80abf8d350d2706d5e4a297",
-   "support"
-  ],
   "offscreen-canvas/shadows/2d.shadow.pattern.alpha.worker.js": [
-   "fc6fdbeb03e38ecd012b40777ea2800d2e37af59",
+   "616169f823cf10be0fe4fcd59317ad17c1446f07",
    "testharness"
   ],
-  "offscreen-canvas/shadows/2d.shadow.pattern.basic-expected.txt": [
-   "f4e0bf10073f93c4d500e85f39b0eb942420b61a",
-   "support"
-  ],
   "offscreen-canvas/shadows/2d.shadow.pattern.basic.html": [
-   "d70f2fce6b3058ba2e391f87f106fcf2d68e0367",
+   "8c4ff934b93315692edaf21d78d1be3f7e9cf7d2",
    "testharness"
   ],
-  "offscreen-canvas/shadows/2d.shadow.pattern.basic.worker-expected.txt": [
-   "f4e0bf10073f93c4d500e85f39b0eb942420b61a",
-   "support"
-  ],
   "offscreen-canvas/shadows/2d.shadow.pattern.basic.worker.js": [
-   "5c2a7c97f94da7ebcfc692f47b6cba20f74ad570",
+   "91e9fe9f1808566f7f4e8b6472a79056493de736",
    "testharness"
   ],
-  "offscreen-canvas/shadows/2d.shadow.pattern.transparent.1-expected.txt": [
-   "f7660f8b6e3be82a0719779b7ad5eb7b3eceef4b",
-   "support"
-  ],
   "offscreen-canvas/shadows/2d.shadow.pattern.transparent.1.html": [
-   "29103ad6214f41ff12195a9554ae9e1616fd4918",
+   "26b50578c1e3801cb57c9e468614116f7f26b2ee",
    "testharness"
   ],
-  "offscreen-canvas/shadows/2d.shadow.pattern.transparent.1.worker-expected.txt": [
-   "f7660f8b6e3be82a0719779b7ad5eb7b3eceef4b",
-   "support"
-  ],
   "offscreen-canvas/shadows/2d.shadow.pattern.transparent.1.worker.js": [
-   "bf8781b3eda120c7f3b553a65e5f2cd01d1c456e",
+   "70ca9a8128dac8baf966080494225fe8677bab03",
    "testharness"
   ],
-  "offscreen-canvas/shadows/2d.shadow.pattern.transparent.2-expected.txt": [
-   "c504a75f040466e0cf0baa5411ac85bade0eb074",
-   "support"
-  ],
   "offscreen-canvas/shadows/2d.shadow.pattern.transparent.2.html": [
-   "6509bae1f55867618e6cbac98e1eadce35e158f3",
+   "983b9ff1fac2ecde4f869e9c77ec7d496fd8af7c",
    "testharness"
   ],
-  "offscreen-canvas/shadows/2d.shadow.pattern.transparent.2.worker-expected.txt": [
-   "c504a75f040466e0cf0baa5411ac85bade0eb074",
-   "support"
-  ],
   "offscreen-canvas/shadows/2d.shadow.pattern.transparent.2.worker.js": [
-   "8a2b7165e78e35b6e15ece3fbce50cb5e521f51a",
+   "db88180fc30d5758b99c891586fc9cbfc6218b54",
    "testharness"
   ],
   "offscreen-canvas/shadows/2d.shadow.stroke.basic.html": [
@@ -490757,7 +489557,7 @@
    "support"
   ],
   "offscreen-canvas/tools/tests2d.yaml": [
-   "6662668f37397145e93eee51e0e4be80f170278f",
+   "645d6847bdf670ff9e2adb4258a675c8a3d2d3a4",
    "support"
   ],
   "offscreen-canvas/transformations/2d.transformation.getTransform.html": [
@@ -506829,7 +505629,7 @@
    "support"
   ],
   "resources/chromium/nfc-mock.js": [
-   "2c1724b7592eb1d6e66177544998abbec70fbfe6",
+   "5cac21bc076f4166ef1b3d14860d69c9f14aedbe",
    "support"
   ],
   "resources/chromium/sensor.mojom.js": [
@@ -507533,7 +506333,7 @@
    "testharness"
   ],
   "server-timing/idlharness.https.any.js": [
-   "b55c741df39e4766afd438daa9037b216b7780ad",
+   "ded320f0f61f4de1132d5cfb76a7e74a16154f3c",
    "testharness"
   ],
   "server-timing/navigation_timing_idl.https.html": [
@@ -520481,7 +519281,7 @@
    "support"
   ],
   "tools/webdriver/webdriver/client.py": [
-   "cf4c5fd35fd98bb54ba248d1c1c31590310727ed",
+   "0c5bbff5629868fdc48a1a41cf882fabe5617c1b",
    "support"
   ],
   "tools/webdriver/webdriver/error.py": [
@@ -521372,6 +520172,22 @@
    "1bc33add09b4b34ce2cba21f488f8ed53345a27a",
    "support"
   ],
+  "trusted-types/empty-default-policy-report-only.tentative.html": [
+   "1ba9c5ec18e5da33725aaa9499977930563c4ba9",
+   "testharness"
+  ],
+  "trusted-types/empty-default-policy-report-only.tentative.html.headers": [
+   "fa87952ae486b3a902eca0257eadace8e25e881f",
+   "support"
+  ],
+  "trusted-types/empty-default-policy.tentative.html": [
+   "2d3a10addbc25221babc37550e36acb8c7252a4b",
+   "testharness"
+  ],
+  "trusted-types/empty-default-policy.tentative.html.headers": [
+   "1bc33add09b4b34ce2cba21f488f8ed53345a27a",
+   "support"
+  ],
   "trusted-types/eval-csp-no-tt.tentative.html": [
    "e8ed57708ef158d36cff2c0d0883910c84bb75e8",
    "testharness"
@@ -529001,15 +527817,15 @@
    "wdspec"
   ],
   "webdriver/tests/take_element_screenshot/__init__.py": [
-   "9a82cc48eab7993dcd6588d89b5aae9ed4ebfc82",
+   "9de8792460797f4a31ee50d112c7132a2c2714ad",
    "support"
   ],
   "webdriver/tests/take_element_screenshot/iframe.py": [
-   "83f55deff753a4525839d3c0675acfb459f64395",
+   "242122f0b5e7359c364a8aa1da345fc3da15acf1",
    "wdspec"
   ],
   "webdriver/tests/take_element_screenshot/screenshot.py": [
-   "50d33a266db75f0fc918ac5fa6d1edc6e548385f",
+   "fd460b656ae307a289174f9180ecc9ed23f0da07",
    "wdspec"
   ],
   "webdriver/tests/take_element_screenshot/user_prompts.py": [
@@ -529017,11 +527833,11 @@
    "wdspec"
   ],
   "webdriver/tests/take_screenshot/__init__.py": [
-   "f3001d946df56e58c4e3fa1319db12bf29f3e341",
+   "c07f8d167030d03abbb263ef08d8846bb7a71eba",
    "support"
   ],
   "webdriver/tests/take_screenshot/iframe.py": [
-   "6186cf3c4be9b220db59c78a3f94ddccc31659bd",
+   "4cf8ad06b56d8f44af06550af4c4a1b7715b908b",
    "wdspec"
   ],
   "webdriver/tests/take_screenshot/screenshot.py": [
diff --git a/third_party/blink/web_tests/external/wpt/cookies/domain/domain-attribute-host-with-and-without-leading-period.sub.html b/third_party/blink/web_tests/external/wpt/cookies/domain/domain-attribute-host-with-and-without-leading-period.sub.html
new file mode 100644
index 0000000..8144b2f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/cookies/domain/domain-attribute-host-with-and-without-leading-period.sub.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<head>
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <script src="/cookies/resources/cookie-helper.sub.js"></script>
+</head>
+<body>
+  <script>
+    //
+    //  Set-Cookie: a=b; Path=/; Domain=.{{host}}
+    //  Set-Cookie: a=c; Path=/; Domain={{host}}
+    //
+    test(t => {
+      assert_dom_cookie("a", "c", true);
+    }, "Domain=.{{host}} => Second value available via `document.cookie`");
+
+    async_test(t => {
+      fetch("/cookies/resources/list.py", { credentials: "include" })
+        .then(t.step_func(r => r.json()))
+        .then(t.step_func_done(r => {
+          assert_equals(r["a"], "c");
+        }))
+        .catch(_ => assert_unreached);
+    }, "Domain=.{{host}} => Second value sent with same-origin requests.");
+
+    async_test(t => {
+      fetch(`${SECURE_SUBDOMAIN_ORIGIN}/cookies/resources/list.py`, { credentials: "include" })
+        .then(t.step_func(r => r.json()))
+        .then(t.step_func_done(r => {
+          assert_equals(r["a"], "c");
+        }))
+        .catch(_ => assert_unreached);
+    }, "Domain=.{{host}} => Second value sent with subdomain requests.");
+  </script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/cookies/domain/domain-attribute-host-with-and-without-leading-period.sub.html.sub.headers b/third_party/blink/web_tests/external/wpt/cookies/domain/domain-attribute-host-with-and-without-leading-period.sub.html.sub.headers
new file mode 100644
index 0000000..4365b7df
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/cookies/domain/domain-attribute-host-with-and-without-leading-period.sub.html.sub.headers
@@ -0,0 +1,2 @@
+Set-Cookie: a=b; Path=/; Domain=.{{host}}
+Set-Cookie: a=c; Path=/; Domain={{host}}
diff --git a/third_party/blink/web_tests/external/wpt/cookies/domain/domain-attribute-host-with-leading-period.sub.html b/third_party/blink/web_tests/external/wpt/cookies/domain/domain-attribute-host-with-leading-period.sub.html
new file mode 100644
index 0000000..1a020d6e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/cookies/domain/domain-attribute-host-with-leading-period.sub.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<head>
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <script src="/cookies/resources/cookie-helper.sub.js"></script>
+</head>
+<body>
+  <script>
+    //
+    //  Set-Cookie: a=b; Path=/; Domain=.{{host}}
+    //
+    test(t => {
+      assert_dom_cookie("a", "b", true);
+    }, "Domain=.{{host}} => available via `document.cookie`");
+
+    async_test(t => {
+      fetch("/cookies/resources/list.py", { credentials: "include" })
+        .then(t.step_func(r => r.json()))
+        .then(t.step_func_done(r => {
+          assert_equals(r["a"], "b");
+        }))
+        .catch(_ => assert_unreached);
+    }, "Domain=.{{host}} => sent with same-origin requests.");
+
+    async_test(t => {
+      fetch(`${SECURE_SUBDOMAIN_ORIGIN}/cookies/resources/list.py`, { credentials: "include" })
+        .then(t.step_func(r => r.json()))
+        .then(t.step_func_done(r => {
+          assert_equals(r["a"], "b");
+        }))
+        .catch(_ => assert_unreached);
+    }, "Domain=.{{host}} => sent with subdomain requests.");
+  </script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/cookies/domain/domain-attribute-host-with-leading-period.sub.html.sub.headers b/third_party/blink/web_tests/external/wpt/cookies/domain/domain-attribute-host-with-leading-period.sub.html.sub.headers
new file mode 100644
index 0000000..3813417
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/cookies/domain/domain-attribute-host-with-leading-period.sub.html.sub.headers
@@ -0,0 +1 @@
+Set-Cookie: a=b; Path=/; Domain=.{{host}}
diff --git a/third_party/blink/web_tests/external/wpt/cookies/domain/domain-attribute-matches-host.sub.html b/third_party/blink/web_tests/external/wpt/cookies/domain/domain-attribute-matches-host.sub.html
new file mode 100644
index 0000000..8244c8f5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/cookies/domain/domain-attribute-matches-host.sub.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<head>
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <script src="/cookies/resources/cookie-helper.sub.js"></script>
+</head>
+<body>
+  <script>
+    //
+    //  Set-Cookie: a=b; Path=/; Domain={{host}}
+    //
+    test(t => {
+      assert_dom_cookie("a", "b", true);
+    }, "Domain={{host}} => available via `document.cookie`");
+
+    async_test(t => {
+      fetch("/cookies/resources/list.py", { credentials: "include" })
+        .then(t.step_func(r => r.json()))
+        .then(t.step_func_done(r => {
+          assert_equals(r["a"], "b");
+        }))
+        .catch(_ => assert_unreached);
+    }, "Domain={{host}} => sent with same-origin requests.");
+
+    async_test(t => {
+      fetch(`${SECURE_SUBDOMAIN_ORIGIN}/cookies/resources/list.py`, { credentials: "include" })
+        .then(t.step_func(r => r.json()))
+        .then(t.step_func_done(r => {
+          assert_equals(r["a"], "b");
+        }))
+        .catch(_ => assert_unreached);
+    }, "Domain={{host}} => sent with subdomain requests.");
+  </script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/cookies/domain/domain-attribute-matches-host.sub.html.sub.headers b/third_party/blink/web_tests/external/wpt/cookies/domain/domain-attribute-matches-host.sub.html.sub.headers
new file mode 100644
index 0000000..3544e22
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/cookies/domain/domain-attribute-matches-host.sub.html.sub.headers
@@ -0,0 +1 @@
+Set-Cookie: a=b; Path=/; Domain={{host}}
diff --git a/third_party/blink/web_tests/external/wpt/cookies/domain/domain-attribute-missing.sub.html b/third_party/blink/web_tests/external/wpt/cookies/domain/domain-attribute-missing.sub.html
new file mode 100644
index 0000000..c4ed6ab
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/cookies/domain/domain-attribute-missing.sub.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<head>
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <script src="/cookies/resources/cookie-helper.sub.js"></script>
+</head>
+<body>
+  <script>
+    //
+    //  Set-Cookie: a=b; Path=/
+    //
+    test(t => {
+      assert_dom_cookie("a", "b", true);
+    }, "No domain attribute => available via `document.cookie`");
+
+    async_test(t => {
+      fetch("/cookies/resources/list.py", { credentials: "include" })
+        .then(t.step_func(r => r.json()))
+        .then(t.step_func_done(r => {
+          assert_equals(r["a"], "b");
+        }))
+        .catch(_ => assert_unreached);
+    }, "No domain attribute => sent with same-origin requests.");
+
+    async_test(t => {
+      fetch(`${SECURE_SUBDOMAIN_ORIGIN}/cookies/resources/list.py`, { credentials: "include" })
+        .then(t.step_func(r => r.json()))
+        .then(t.step_func_done(r => {
+          assert_equals(r["a"], undefined);
+        }))
+        .catch(_ => assert_unreached);
+    }, "No domain attribute => not sent with subdomain requests.");
+  </script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/cookies/domain/domain-attribute-missing.sub.html.headers b/third_party/blink/web_tests/external/wpt/cookies/domain/domain-attribute-missing.sub.html.headers
new file mode 100644
index 0000000..e3910565
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/cookies/domain/domain-attribute-missing.sub.html.headers
@@ -0,0 +1 @@
+Set-Cookie: a=b; Path=/
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/grid-definition/grid-inline-support-flexible-lengths-001.html b/third_party/blink/web_tests/external/wpt/css/css-grid/grid-definition/grid-inline-support-flexible-lengths-001.html
index 3a2c9423..42bbb43c 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/grid-definition/grid-inline-support-flexible-lengths-001.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/grid-definition/grid-inline-support-flexible-lengths-001.html
@@ -85,21 +85,21 @@
 
   // Wrong values.
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "fr", "fr", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "fr", "fr", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "fr", "fr", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "1 fr", "1 fr", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "1 fr", "1 fr", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "1 fr", "1 fr", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "1free-space", "1free-space", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "1free-space", "1free-space", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "1free-space", "1free-space", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "-2fr", "-2fr", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "-2fr", "-2fr", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "-2fr", "-2fr", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "0,5fr", "0,5fr", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "0,5fr", "0,5fr", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "0,5fr", "0,5fr", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "calc(1fr + 100px)", "calc(1fr + 100px)", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "calc(1fr + 100px)", "calc(1fr + 100px)", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "calc(1fr + 100px)", "calc(1fr + 100px)", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "(1fr) auto", "(1fr) auto", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "(1fr) auto", "(1fr) auto", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "(1fr) auto", "(1fr) auto", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "minmax(1fr, 1000px)", "minmax(1fr, 700px)", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "minmax(1fr, 1000px)", "minmax(1fr, 700px)", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "minmax(1fr, 1000px)", "minmax(1fr, 700px)", "none", "none");
   done();
 });
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/grid-definition/grid-inline-support-grid-template-columns-rows-001.html b/third_party/blink/web_tests/external/wpt/css/css-grid/grid-definition/grid-inline-support-grid-template-columns-rows-001.html
index 32733e23..cbcdc2c 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/grid-definition/grid-inline-support-grid-template-columns-rows-001.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/grid-definition/grid-inline-support-grid-template-columns-rows-001.html
@@ -31,7 +31,7 @@
 document.fonts.ready.then(()=> {
   // Single values.
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "none", "none", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "none", "none", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "none", "none", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "auto", "auto", "0px", "0px");
   TestingUtils.testGridTemplateColumnsRows("grid", "auto", "auto", "90px", "10px");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "500px", "300px", "500px", "300px");
@@ -77,13 +77,13 @@
 
   // Wrong values.
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "foo", "bar", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "foo", "bar", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "foo", "bar", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "auto none", "none auto", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "auto none", "none auto", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "auto none", "none auto", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "100px, 200px", "300px, 400px", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "100px, 200px", "300px, 400px", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "100px, 200px", "300px, 400px", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "minmax(100px, 200px, 300px)", "minmax(100px, 200px, 300px)", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "minmax(100px, 200px, 300px)", "minmax(100px, 200px, 300px)", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "minmax(100px, 200px, 300px)", "minmax(100px, 200px, 300px)", "none", "none");
   done();
 });
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/grid-definition/grid-inline-support-named-grid-lines-001.html b/third_party/blink/web_tests/external/wpt/css/css-grid/grid-definition/grid-inline-support-named-grid-lines-001.html
index fc4caf28..a405f055 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/grid-definition/grid-inline-support-named-grid-lines-001.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/grid-definition/grid-inline-support-named-grid-lines-001.html
@@ -77,47 +77,47 @@
 
   // Wrong values.
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "[a]", "[a]", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "[a]", "[a]", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "[a]", "[a]", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "[a b]", "[a b]", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "[a b]", "[a b]", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "[a b]", "[a b]", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "[a] none [b]", "[a] none [b]", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "[a] none [b]", "[a] none [b]", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "[a] none [b]", "[a] none [b]", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "[a] [b]", "[a] [b]", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "[a] [b]", "[a] [b]", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "[a] [b]", "[a] [b]", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "a auto b", "a auto b", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "a auto b", "a auto b", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "a auto b", "a auto b", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "(a) auto (b)", "(a) auto (b)", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "(a) auto (b)", "(a) auto (b)", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "(a) auto (b)", "(a) auto (b)", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "'a' auto 'b'", "'a' auto 'b'", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "'a' auto 'b'", "'a' auto 'b'", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "'a' auto 'b'", "'a' auto 'b'", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "\"a\" auto \"b\"", "\"a\" auto \"b\"", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "\"a\" auto \"b\"", "\"a\" auto \"b\"", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "\"a\" auto \"b\"", "\"a\" auto \"b\"", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "[a, b] auto [a, b]", "[a, b] auto [a, b]", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "[a, b] auto [a, b]", "[a, b] auto [a, b]", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "[a, b] auto [a, b]", "[a, b] auto [a, b]", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "[a] [b] auto [c d] [e]", "[a] [b] auto [c d] [e]", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "[a] [b] auto [c d] [e]", "[a] [b] auto [c d] [e]", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "[a] [b] auto [c d] [e]", "[a] [b] auto [c d] [e]", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "[a [b]] auto [c]", "[a [b]] auto [c]", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "[a [b]] auto [c]", "[a [b]] auto [c]", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "[a [b]] auto [c]", "[a [b]] auto [c]", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "[a] auto [[b]]", "[a] auto [[b]]", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "[a] auto [[b]]", "[a] auto [[b]]", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "[a] auto [[b]]", "[a] auto [[b]]", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "[a auto [b]", "[a auto [b]", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "[a auto [b]", "[a auto [b]", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "[a auto [b]", "[a auto [b]", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "[a 100px] auto [b]", "[a 100px] auto [b]", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "[a 100px] auto [b]", "[a 100px] auto [b]", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "[a 100px] auto [b]", "[a 100px] auto [b]", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "[a 50%] auto [b]", "[a 50%] auto [b]", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "[a 50%] auto [b]", "[a 50%] auto [b]", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "[a 50%] auto [b]", "[a 50%] auto [b]", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "[5] auto [10]", "[5] auto [10]", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "[5] auto [10]", "[5] auto [10]", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "[5] auto [10]", "[5] auto [10]", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "[a.] auto [b*]", "[a.] auto [b*]", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "[a.] auto [b*]", "[a.] auto [b*]", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "[a.] auto [b*]", "[a.] auto [b*]", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "[#a] auto [$b]", "[#a] auto [$b]", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "[#a] auto [$b]", "[#a] auto [$b]", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "[#a] auto [$b]", "[#a] auto [$b]", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "[initial] auto", "[initial] auto", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "[initial] auto", "[initial] auto", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "[initial] auto", "[initial] auto", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "[inherit] auto", "[inherit] auto", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "[inherit] auto", "[inherit] auto", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "[inherit] auto", "[inherit] auto", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "[default] auto", "[default] auto", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "[default] auto", "[default] auto", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "[default] auto", "[default] auto", "none", "none");
   done();
 });
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/grid-definition/grid-inline-support-repeat-001.html b/third_party/blink/web_tests/external/wpt/css/css-grid/grid-definition/grid-inline-support-repeat-001.html
index 465e5e8..67a0c66 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/grid-definition/grid-inline-support-repeat-001.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/grid-definition/grid-inline-support-repeat-001.html
@@ -65,19 +65,19 @@
 
   // Wrong values.
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "repeat(-1, auto)", "repeat(-1, auto)", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "repeat(-1, auto)", "repeat(-1, auto)", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "repeat(-1, auto)", "repeat(-1, auto)", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "repeat(auto, 2)", "repeat(auto, 2)", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "repeat(auto, 2)", "repeat(auto, 2)", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "repeat(auto, 2)", "repeat(auto, 2)", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "repeat 2, auto", "repeat 2, auto", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "repeat 2, auto", "repeat 2, auto", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "repeat 2, auto", "repeat 2, auto", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "repeat(2 auto)", "repeat(2 auto)", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "repeat(2 auto)", "repeat(2 auto)", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "repeat(2 auto)", "repeat(2 auto)", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "100px (repeat 2, auto)", "(repeat 2, auto)", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "100px (repeat 2, auto)", "(repeat 2, auto)", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "100px (repeat 2, auto)", "(repeat 2, auto)", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "repeat(2, 50px repeat(2, 100px))", "repeat(2, 50px repeat(2, 100px))", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "repeat(2, 50px repeat(2, 100px))", "repeat(2, 50px repeat(2, 100px))", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "repeat(2, 50px repeat(2, 100px))", "repeat(2, 50px repeat(2, 100px))", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "100px repeat(2, [a])", "100px repeat(2, [a])", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "100px repeat(2, [a])", "100px repeat(2, [a])", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "100px repeat(2, [a])", "100px repeat(2, [a])", "none", "none");
   done();
 });
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/grid-definition/grid-inline-template-columns-rows-resolved-values-001.html b/third_party/blink/web_tests/external/wpt/css/css-grid/grid-definition/grid-inline-template-columns-rows-resolved-values-001.html
index ff1f814..4b13906 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/grid-definition/grid-inline-template-columns-rows-resolved-values-001.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/grid-definition/grid-inline-template-columns-rows-resolved-values-001.html
@@ -4,7 +4,7 @@
 <link rel="author" title="Manuel Rego Casasnovas" href="mailto:rego@igalia.com">
 <link rel="help" href="http://www.w3.org/TR/css-grid-1/#resolved-track-list" title="5.1.5. Resolved Values">
 <meta name="flags" content="ahem dom">
-<meta name="assert" content="This test checks that resolved values for 'grid-template-columns' and 'grid-template-rows' list tracks implicitly created in an inline grid.">
+<meta name="assert" content="This test checks that resolved values for 'grid-template-columns' and 'grid-template-rows' don't include implicitly created tracks in an inline grid.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="support/testing-utils.js"></script>
@@ -17,6 +17,7 @@
     font: 10px/1 Ahem;
     justify-content: start;
     align-content: start;
+    position: relative;
   }
 
   .fifthColumn {
@@ -58,50 +59,218 @@
 </div>
 
 <script>
+function testSizeAndPositionOfItems(gridId, expectedItemData) {
+  let grid = document.getElementById(gridId);
+  assert_equals(grid.childElementCount, expectedItemData.length, "childElementCount");
+  let props = ["offsetWidth", "offsetTop", "offsetHeight"];
+  for (let i = 0; i < expectedItemData.length; ++i)
+    for (let prop of props)
+      assert_equals(grid.children[i][prop], expectedItemData[i][prop], "children[" + i + "]." + prop);
+}
+
+function testGrid(gridId, columnsStyle, rowsStyle, columnsComputedValue, rowsComputedValue, expectedItemData) {
+  TestingUtils.testGridTemplateColumnsRows(gridId, columnsStyle, rowsStyle, columnsComputedValue, rowsComputedValue);
+  test(function() {
+    testSizeAndPositionOfItems(gridId, expectedItemData);
+  }, "Children of '" + gridId + "' with: grid-template-columns: " + columnsStyle  + "; and grid-template-rows: " + rowsStyle + ";");
+}
+
 setup({explicit_done: true});
 document.fonts.ready.then(()=> {
   // Valid values.
-  TestingUtils.testGridTemplateColumnsRows("grid", "", "", "110px", ["10px 10px 20px", "repeat(2, 10px) 20px"]);
-  TestingUtils.testGridTemplateColumnsRows("grid", "auto auto", "", "100px 110px", "10px 20px");
-  TestingUtils.testGridTemplateColumnsRows("grid", "60px", "", "60px", ["20px 20px 20px", "repeat(3, 20px)"]);
-  TestingUtils.testGridTemplateColumnsRows("grid", "100px 60px", "", "100px 60px", ["20px 20px", "repeat(2, 20px)"]);
-  TestingUtils.testGridTemplateColumnsRows("grid", "", "50px", "110px", "50px 10px 20px");
-  TestingUtils.testGridTemplateColumnsRows("grid", "", "50px 30px", "110px", "50px 30px 20px");
-  TestingUtils.testGridTemplateColumnsRows("grid", "60px", "50px", "60px", ["50px 20px 20px", "50px repeat(2, 20px)"]);
-  TestingUtils.testGridTemplateColumnsRows("grid", "60px", "50px 30px", "60px", "50px 30px 20px");
-  TestingUtils.testGridTemplateColumnsRows("grid", "100px 60px", "50px", "100px 60px", "50px 20px");
-  TestingUtils.testGridTemplateColumnsRows("grid", "100px 60px", "50px 30px", "100px 60px", "50px 30px");
+  testGrid("grid", "", "", "none", "none", [
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 0, offsetHeight: 10},
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 10, offsetHeight: 10},
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 20, offsetHeight: 20},
+  ]);
+  testGrid("grid", "auto auto", "", "100px 110px", "none", [
+    {offsetLeft: 0, offsetWidth: 100, offsetTop: 0, offsetHeight: 10},
+    {offsetLeft: 100, offsetWidth: 110, offsetTop: 0, offsetHeight: 10},
+    {offsetLeft: 0, offsetWidth: 100, offsetTop: 10, offsetHeight: 20},
+  ]);
+  testGrid("grid", "60px", "", "60px", "none", [
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 0, offsetHeight: 20},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 20, offsetHeight: 20},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 40, offsetHeight: 20},
+  ]);
+  testGrid("grid", "100px 60px", "", "100px 60px", "none", [
+    {offsetLeft: 0, offsetWidth: 100, offsetTop: 0, offsetHeight: 20},
+    {offsetLeft: 100, offsetWidth: 60, offsetTop: 0, offsetHeight: 20},
+    {offsetLeft: 0, offsetWidth: 100, offsetTop: 20, offsetHeight: 20},
+  ]);
+  testGrid("grid", "", "50px", "none", "50px", [
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 0, offsetHeight: 50},
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 50, offsetHeight: 10},
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 60, offsetHeight: 20},
+  ]);
+  testGrid("grid", "", "50px 30px", "none", "50px 30px", [
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 0, offsetHeight: 50},
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 50, offsetHeight: 30},
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 80, offsetHeight: 20},
+  ]);
+  testGrid("grid", "60px", "50px", "60px", "50px", [
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 0, offsetHeight: 50},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 50, offsetHeight: 20},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 70, offsetHeight: 20},
+  ]);
+  testGrid("grid", "60px", "50px 30px", "60px", "50px 30px", [
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 0, offsetHeight: 50},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 50, offsetHeight: 30},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 80, offsetHeight: 20},
+  ]);
+  testGrid("grid", "100px 60px", "50px", "100px 60px", "50px", [
+    {offsetLeft: 0, offsetWidth: 100, offsetTop: 0, offsetHeight: 50},
+    {offsetLeft: 100, offsetWidth: 60, offsetTop: 0, offsetHeight: 50},
+    {offsetLeft: 0, offsetWidth: 100, offsetTop: 50, offsetHeight: 20},
+  ]);
+  testGrid("grid", "100px 60px", "50px 30px", "100px 60px", "50px 30px", [
+    {offsetLeft: 0, offsetWidth: 100, offsetTop: 0, offsetHeight: 50},
+    {offsetLeft: 100, offsetWidth: 60, offsetTop: 0, offsetHeight: 50},
+    {offsetLeft: 0, offsetWidth: 100, offsetTop: 50, offsetHeight: 30},
+  ]);
 
-  TestingUtils.testGridTemplateColumnsRows("gridItemsPositions", "", "", ["110px 0px 0px 0px 100px", "110px repeat(3, 0px) 100px"], "10px 20px 0px 10px");
-  TestingUtils.testGridTemplateColumnsRows("gridItemsPositions", "60px", "", ["60px 0px 0px 0px 100px", "60px repeat(3, 0px) 100px"], "10px 20px 0px 20px");
-  TestingUtils.testGridTemplateColumnsRows("gridItemsPositions", "60px 50px", "", ["60px 50px 0px 0px 100px", "60px 50px repeat(2, 0px) 100px"], "10px 20px 0px 20px");
-  TestingUtils.testGridTemplateColumnsRows("gridItemsPositions", "", "60px", ["110px 0px 0px 0px 100px", "110px repeat(3, 0px) 100px"], "60px 20px 0px 10px");
-  TestingUtils.testGridTemplateColumnsRows("gridItemsPositions", "", "60px 50px", ["110px 0px 0px 0px 100px", "110px repeat(3, 0px) 100px"], "60px 50px 0px 10px");
-  TestingUtils.testGridTemplateColumnsRows("gridItemsPositions", "60px", "60px", ["60px 0px 0px 0px 100px", "60px repeat(3, 0px) 100px"], "60px 20px 0px 20px");
-  TestingUtils.testGridTemplateColumnsRows("gridItemsPositions", "60px", "60px 50px", ["60px 0px 0px 0px 100px", "60px repeat(3, 0px) 100px"], "60px 50px 0px 20px");
-  TestingUtils.testGridTemplateColumnsRows("gridItemsPositions", "60px 50px", "60px", ["60px 50px 0px 0px 100px", "60px 50px repeat(2, 0px) 100px"], "60px 20px 0px 20px");
-  TestingUtils.testGridTemplateColumnsRows("gridItemsPositions", "60px 50px", "60px 50px", ["60px 50px 0px 0px 100px", "60px 50px repeat(2, 0px) 100px"], "60px 50px 0px 20px");
+  testGrid("gridItemsPositions", "", "", "none", "none", [
+    {offsetLeft: 110, offsetWidth: 100, offsetTop: 0, offsetHeight: 10},
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 30, offsetHeight: 10},
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 10, offsetHeight: 20},
+  ]);
+  testGrid("gridItemsPositions", "60px", "", "60px", "none", [
+    {offsetLeft: 60, offsetWidth: 100, offsetTop: 0, offsetHeight: 10},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 30, offsetHeight: 20},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 10, offsetHeight: 20},
+  ]);
+  testGrid("gridItemsPositions", "60px 50px", "", "60px 50px", "none", [
+    {offsetLeft: 110, offsetWidth: 100, offsetTop: 0, offsetHeight: 10},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 30, offsetHeight: 20},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 10, offsetHeight: 20},
+  ]);
+  testGrid("gridItemsPositions", "", "60px", "none", "60px", [
+    {offsetLeft: 110, offsetWidth: 100, offsetTop: 0, offsetHeight: 60},
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 80, offsetHeight: 10},
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 60, offsetHeight: 20},
+  ]);
+  testGrid("gridItemsPositions", "", "60px 50px", "none", "60px 50px", [
+    {offsetLeft: 110, offsetWidth: 100, offsetTop: 0, offsetHeight: 60},
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 110, offsetHeight: 10},
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 60, offsetHeight: 50},
+  ]);
+  testGrid("gridItemsPositions", "60px", "60px", "60px", "60px", [
+    {offsetLeft: 60, offsetWidth: 100, offsetTop: 0, offsetHeight: 60},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 80, offsetHeight: 20},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 60, offsetHeight: 20},
+  ]);
+  testGrid("gridItemsPositions", "60px", "60px 50px", "60px", "60px 50px", [
+    {offsetLeft: 60, offsetWidth: 100, offsetTop: 0, offsetHeight: 60},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 110, offsetHeight: 20},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 60, offsetHeight: 50},
+  ]);
+  testGrid("gridItemsPositions", "60px 50px", "60px", "60px 50px", "60px", [
+    {offsetLeft: 110, offsetWidth: 100, offsetTop: 0, offsetHeight: 60},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 80, offsetHeight: 20},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 60, offsetHeight: 20},
+  ]);
+  testGrid("gridItemsPositions", "60px 50px", "60px 50px", "60px 50px", "60px 50px", [
+    {offsetLeft: 110, offsetWidth: 100, offsetTop: 0, offsetHeight: 60},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 110, offsetHeight: 20},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 60, offsetHeight: 50},
+  ]);
 
-  TestingUtils.testGridTemplateColumnsRows("gridAutoFlowColumn", "", "", "100px 110px 50px", "20px");
-  TestingUtils.testGridTemplateColumnsRows("gridAutoFlowColumn", "", "auto auto", "110px 50px", "20px 10px");
-  TestingUtils.testGridTemplateColumnsRows("gridAutoFlowColumn", "60px", "", "60px 110px 50px", "20px");
-  TestingUtils.testGridTemplateColumnsRows("gridAutoFlowColumn", "100px 60px", "", "100px 60px 50px", "20px");
-  TestingUtils.testGridTemplateColumnsRows("gridAutoFlowColumn", "", "50px", "100px 110px 50px", "50px");
-  TestingUtils.testGridTemplateColumnsRows("gridAutoFlowColumn", "", "50px 30px", "110px 50px", "50px 30px");
-  TestingUtils.testGridTemplateColumnsRows("gridAutoFlowColumn", "60px", "50px", "60px 110px 50px", "50px");
-  TestingUtils.testGridTemplateColumnsRows("gridAutoFlowColumn", "60px", "50px 30px", "60px 50px", "50px 30px");
-  TestingUtils.testGridTemplateColumnsRows("gridAutoFlowColumn", "100px 60px", "50px", "100px 60px 50px", "50px");
-  TestingUtils.testGridTemplateColumnsRows("gridAutoFlowColumn", "100px 60px", "50px 30px", "100px 60px", "50px 30px");
+  testGrid("gridAutoFlowColumn", "", "", "none", "none", [
+    {offsetLeft: 0, offsetWidth: 100, offsetTop: 0, offsetHeight: 20},
+    {offsetLeft: 100, offsetWidth: 110, offsetTop: 0, offsetHeight: 20},
+    {offsetLeft: 210, offsetWidth: 50, offsetTop: 0, offsetHeight: 20},
+  ]);
+  testGrid("gridAutoFlowColumn", "", "auto auto", "none", "20px 10px", [
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 0, offsetHeight: 20},
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 20, offsetHeight: 10},
+    {offsetLeft: 110, offsetWidth: 50, offsetTop: 0, offsetHeight: 20},
+  ]);
+  testGrid("gridAutoFlowColumn", "60px", "", "60px", "none", [
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 0, offsetHeight: 20},
+    {offsetLeft: 60, offsetWidth: 110, offsetTop: 0, offsetHeight: 20},
+    {offsetLeft: 170, offsetWidth: 50, offsetTop: 0, offsetHeight: 20},
+  ]);
+  testGrid("gridAutoFlowColumn", "100px 60px", "", "100px 60px", "none", [
+    {offsetLeft: 0, offsetWidth: 100, offsetTop: 0, offsetHeight: 20},
+    {offsetLeft: 100, offsetWidth: 60, offsetTop: 0, offsetHeight: 20},
+    {offsetLeft: 160, offsetWidth: 50, offsetTop: 0, offsetHeight: 20},
+  ]);
+  testGrid("gridAutoFlowColumn", "", "50px", "none", "50px", [
+    {offsetLeft: 0, offsetWidth: 100, offsetTop: 0, offsetHeight: 50},
+    {offsetLeft: 100, offsetWidth: 110, offsetTop: 0, offsetHeight: 50},
+    {offsetLeft: 210, offsetWidth: 50, offsetTop: 0, offsetHeight: 50},
+  ]);
+  testGrid("gridAutoFlowColumn", "", "50px 30px", "none", "50px 30px", [
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 0, offsetHeight: 50},
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 50, offsetHeight: 30},
+    {offsetLeft: 110, offsetWidth: 50, offsetTop: 0, offsetHeight: 50},
+  ]);
+  testGrid("gridAutoFlowColumn", "60px", "50px", "60px", "50px", [
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 0, offsetHeight: 50},
+    {offsetLeft: 60, offsetWidth: 110, offsetTop: 0, offsetHeight: 50},
+    {offsetLeft: 170, offsetWidth: 50, offsetTop: 0, offsetHeight: 50},
+  ]);
+  testGrid("gridAutoFlowColumn", "60px", "50px 30px", "60px", "50px 30px", [
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 0, offsetHeight: 50},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 50, offsetHeight: 30},
+    {offsetLeft: 60, offsetWidth: 50, offsetTop: 0, offsetHeight: 50},
+  ]);
+  testGrid("gridAutoFlowColumn", "100px 60px", "50px", "100px 60px", "50px", [
+    {offsetLeft: 0, offsetWidth: 100, offsetTop: 0, offsetHeight: 50},
+    {offsetLeft: 100, offsetWidth: 60, offsetTop: 0, offsetHeight: 50},
+    {offsetLeft: 160, offsetWidth: 50, offsetTop: 0, offsetHeight: 50},
+  ]);
+  testGrid("gridAutoFlowColumn", "100px 60px", "50px 30px", "100px 60px", "50px 30px", [
+    {offsetLeft: 0, offsetWidth: 100, offsetTop: 0, offsetHeight: 50},
+    {offsetLeft: 0, offsetWidth: 100, offsetTop: 50, offsetHeight: 30},
+    {offsetLeft: 100, offsetWidth: 60, offsetTop: 0, offsetHeight: 50},
+  ]);
 
-  TestingUtils.testGridTemplateColumnsRows("gridAutoFlowColumnItemsPositions", "", "", ["110px 50px 0px 0px 100px", "110px 50px repeat(2, 0px) 100px"], ["20px 0px 0px 10px", "20px repeat(2, 0px) 10px"]);
-  TestingUtils.testGridTemplateColumnsRows("gridAutoFlowColumnItemsPositions", "60px", "", ["60px 50px 0px 0px 100px", "60px 50px repeat(2, 0px) 100px"], ["20px 0px 0px 20px", "20px repeat(2, 0px) 20px"]);
-  TestingUtils.testGridTemplateColumnsRows("gridAutoFlowColumnItemsPositions", "60px 70px", "", ["60px 70px 0px 0px 100px", "60px 70px repeat(2, 0px) 100px"], ["20px 0px 0px 20px", "20px repeat(2, 0px) 20px"]);
-  TestingUtils.testGridTemplateColumnsRows("gridAutoFlowColumnItemsPositions", "", "60px", ["110px 50px 0px 0px 100px", "110px 50px repeat(2, 0px) 100px"], ["60px 0px 0px 10px", "60px repeat(2, 0px) 10px"]);
-  TestingUtils.testGridTemplateColumnsRows("gridAutoFlowColumnItemsPositions", "", "60px 70px", ["110px 50px 0px 0px 100px", "110px 50px repeat(2, 0px) 100px"], "60px 70px 0px 10px");
-  TestingUtils.testGridTemplateColumnsRows("gridAutoFlowColumnItemsPositions", "60px", "60px", ["60px 50px 0px 0px 100px", "60px 50px repeat(2, 0px) 100px"], ["60px 0px 0px 20px", "60px repeat(2, 0px) 20px"]);
-  TestingUtils.testGridTemplateColumnsRows("gridAutoFlowColumnItemsPositions", "60px", "60px 70px", ["60px 50px 0px 0px 100px", "60px 50px repeat(2, 0px) 100px"], "60px 70px 0px 20px");
-  TestingUtils.testGridTemplateColumnsRows("gridAutoFlowColumnItemsPositions", "60px 70px", "60px", ["60px 70px 0px 0px 100px", "60px 70px repeat(2, 0px) 100px"], ["60px 0px 0px 20px", "60px repeat(2, 0px) 20px"]);
-  TestingUtils.testGridTemplateColumnsRows("gridAutoFlowColumnItemsPositions", "60px 70px", "60px 70px", ["60px 70px 0px 0px 100px", "60px 70px repeat(2, 0px) 100px"], "60px 70px 0px 20px");
+  testGrid("gridAutoFlowColumnItemsPositions", "", "", "none", "none", [
+    {offsetLeft: 160, offsetWidth: 100, offsetTop: 0, offsetHeight: 20},
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 20, offsetHeight: 10},
+    {offsetLeft: 110, offsetWidth: 50, offsetTop: 0, offsetHeight: 20},
+  ]);
+  testGrid("gridAutoFlowColumnItemsPositions", "60px", "", "60px", "none", [
+    {offsetLeft: 110, offsetWidth: 100, offsetTop: 0, offsetHeight: 20},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 20, offsetHeight: 20},
+    {offsetLeft: 60, offsetWidth: 50, offsetTop: 0, offsetHeight: 20},
+  ]);
+  testGrid("gridAutoFlowColumnItemsPositions", "60px 70px", "", "60px 70px", "none", [
+    {offsetLeft: 130, offsetWidth: 100, offsetTop: 0, offsetHeight: 20},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 20, offsetHeight: 20},
+    {offsetLeft: 60, offsetWidth: 70, offsetTop: 0, offsetHeight: 20},
+  ]);
+  testGrid("gridAutoFlowColumnItemsPositions", "", "60px", "none", "60px", [
+    {offsetLeft: 160, offsetWidth: 100, offsetTop: 0, offsetHeight: 60},
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 60, offsetHeight: 10},
+    {offsetLeft: 110, offsetWidth: 50, offsetTop: 0, offsetHeight: 60},
+  ]);
+  testGrid("gridAutoFlowColumnItemsPositions", "", "60px 70px", "none", "60px 70px", [
+    {offsetLeft: 160, offsetWidth: 100, offsetTop: 0, offsetHeight: 60},
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 130, offsetHeight: 10},
+    {offsetLeft: 110, offsetWidth: 50, offsetTop: 0, offsetHeight: 60},
+  ]);
+  testGrid("gridAutoFlowColumnItemsPositions", "60px", "60px", "60px", "60px", [
+    {offsetLeft: 110, offsetWidth: 100, offsetTop: 0, offsetHeight: 60},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 60, offsetHeight: 20},
+    {offsetLeft: 60, offsetWidth: 50, offsetTop: 0, offsetHeight: 60},
+  ]);
+  testGrid("gridAutoFlowColumnItemsPositions", "60px", "60px 70px", "60px", "60px 70px", [
+    {offsetLeft: 110, offsetWidth: 100, offsetTop: 0, offsetHeight: 60},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 130, offsetHeight: 20},
+    {offsetLeft: 60, offsetWidth: 50, offsetTop: 0, offsetHeight: 60},
+  ]);
+  testGrid("gridAutoFlowColumnItemsPositions", "60px 70px", "60px", "60px 70px", "60px", [
+    {offsetLeft: 130, offsetWidth: 100, offsetTop: 0, offsetHeight: 60},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 60, offsetHeight: 20},
+    {offsetLeft: 60, offsetWidth: 70, offsetTop: 0, offsetHeight: 60},
+  ]);
+  testGrid("gridAutoFlowColumnItemsPositions", "60px 70px", "60px 70px", "60px 70px", "60px 70px", [
+    {offsetLeft: 130, offsetWidth: 100, offsetTop: 0, offsetHeight: 60},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 130, offsetHeight: 20},
+    {offsetLeft: 60, offsetWidth: 70, offsetTop: 0, offsetHeight: 60},
+  ]);
   done();
 });
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/grid-definition/grid-support-flexible-lengths-001.html b/third_party/blink/web_tests/external/wpt/css/css-grid/grid-definition/grid-support-flexible-lengths-001.html
index 0dae45b..db9488af 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/grid-definition/grid-support-flexible-lengths-001.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/grid-definition/grid-support-flexible-lengths-001.html
@@ -85,21 +85,21 @@
 
   // Wrong values.
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "fr", "fr", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "fr", "fr", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "fr", "fr", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "1 fr", "1 fr", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "1 fr", "1 fr", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "1 fr", "1 fr", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "1free-space", "1free-space", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "1free-space", "1free-space", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "1free-space", "1free-space", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "-2fr", "-2fr", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "-2fr", "-2fr", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "-2fr", "-2fr", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "0,5fr", "0,5fr", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "0,5fr", "0,5fr", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "0,5fr", "0,5fr", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "calc(1fr + 100px)", "calc(1fr + 100px)", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "calc(1fr + 100px)", "calc(1fr + 100px)", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "calc(1fr + 100px)", "calc(1fr + 100px)", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "(1fr) auto", "(1fr) auto", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "(1fr) auto", "(1fr) auto", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "(1fr) auto", "(1fr) auto", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "minmax(1fr, 1000px)", "minmax(1fr, 700px)", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "minmax(1fr, 1000px)", "minmax(1fr, 700px)", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "minmax(1fr, 1000px)", "minmax(1fr, 700px)", "none", "none");
   done();
 });
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/grid-definition/grid-support-grid-template-columns-rows-001.html b/third_party/blink/web_tests/external/wpt/css/css-grid/grid-definition/grid-support-grid-template-columns-rows-001.html
index bf818cd..cb0dbff 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/grid-definition/grid-support-grid-template-columns-rows-001.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/grid-definition/grid-support-grid-template-columns-rows-001.html
@@ -31,7 +31,7 @@
 document.fonts.ready.then(()=> {
   // Single values.
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "none", "none", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "none", "none", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "none", "none", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "auto", "auto", "0px", "0px");
   TestingUtils.testGridTemplateColumnsRows("grid", "auto", "auto", "90px", "10px");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "500px", "300px", "500px", "300px");
@@ -77,13 +77,13 @@
 
   // Wrong values.
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "foo", "bar", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "foo", "bar", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "foo", "bar", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "auto none", "none auto", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "auto none", "none auto", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "auto none", "none auto", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "100px, 200px", "300px, 400px", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "100px, 200px", "300px, 400px", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "100px, 200px", "300px, 400px", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "minmax(100px, 200px, 300px)", "minmax(100px, 200px, 300px)", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "minmax(100px, 200px, 300px)", "minmax(100px, 200px, 300px)", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "minmax(100px, 200px, 300px)", "minmax(100px, 200px, 300px)", "none", "none");
   done();
 });
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/grid-definition/grid-support-named-grid-lines-001.html b/third_party/blink/web_tests/external/wpt/css/css-grid/grid-definition/grid-support-named-grid-lines-001.html
index bff5e81..5357855 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/grid-definition/grid-support-named-grid-lines-001.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/grid-definition/grid-support-named-grid-lines-001.html
@@ -77,47 +77,47 @@
 
   // Wrong values.
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "[a]", "[a]", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "[a]", "[a]", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "[a]", "[a]", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "[a b]", "[a b]", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "[a b]", "[a b]", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "[a b]", "[a b]", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "[a] none [b]", "[a] none [b]", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "[a] none [b]", "[a] none [b]", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "[a] none [b]", "[a] none [b]", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "[a] [b]", "[a] [b]", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "[a] [b]", "[a] [b]", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "[a] [b]", "[a] [b]", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "a auto b", "a auto b", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "a auto b", "a auto b", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "a auto b", "a auto b", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "(a) auto (b)", "(a) auto (b)", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "(a) auto (b)", "(a) auto (b)", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "(a) auto (b)", "(a) auto (b)", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "'a' auto 'b'", "'a' auto 'b'", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "'a' auto 'b'", "'a' auto 'b'", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "'a' auto 'b'", "'a' auto 'b'", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "\"a\" auto \"b\"", "\"a\" auto \"b\"", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "\"a\" auto \"b\"", "\"a\" auto \"b\"", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "\"a\" auto \"b\"", "\"a\" auto \"b\"", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "[a, b] auto [a, b]", "[a, b] auto [a, b]", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "[a, b] auto [a, b]", "[a, b] auto [a, b]", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "[a, b] auto [a, b]", "[a, b] auto [a, b]", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "[a] [b] auto [c d] [e]", "[a] [b] auto [c d] [e]", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "[a] [b] auto [c d] [e]", "[a] [b] auto [c d] [e]", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "[a] [b] auto [c d] [e]", "[a] [b] auto [c d] [e]", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "[a [b]] auto [c]", "[a [b]] auto [c]", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "[a [b]] auto [c]", "[a [b]] auto [c]", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "[a [b]] auto [c]", "[a [b]] auto [c]", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "[a] auto [[b]]", "[a] auto [[b]]", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "[a] auto [[b]]", "[a] auto [[b]]", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "[a] auto [[b]]", "[a] auto [[b]]", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "[a auto [b]", "[a auto [b]", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "[a auto [b]", "[a auto [b]", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "[a auto [b]", "[a auto [b]", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "[a 100px] auto [b]", "[a 100px] auto [b]", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "[a 100px] auto [b]", "[a 100px] auto [b]", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "[a 100px] auto [b]", "[a 100px] auto [b]", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "[a 50%] auto [b]", "[a 50%] auto [b]", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "[a 50%] auto [b]", "[a 50%] auto [b]", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "[a 50%] auto [b]", "[a 50%] auto [b]", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "[5] auto [10]", "[5] auto [10]", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "[5] auto [10]", "[5] auto [10]", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "[5] auto [10]", "[5] auto [10]", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "[a.] auto [b*]", "[a.] auto [b*]", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "[a.] auto [b*]", "[a.] auto [b*]", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "[a.] auto [b*]", "[a.] auto [b*]", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "[#a] auto [$b]", "[#a] auto [$b]", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "[#a] auto [$b]", "[#a] auto [$b]", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "[#a] auto [$b]", "[#a] auto [$b]", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "[initial] auto", "[initial] auto", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "[initial] auto", "[initial] auto", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "[initial] auto", "[initial] auto", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "[inherit] auto", "[inherit] auto", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "[inherit] auto", "[inherit] auto", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "[inherit] auto", "[inherit] auto", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "[default] auto", "[default] auto", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "[default] auto", "[default] auto", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "[default] auto", "[default] auto", "none", "none");
   done();
 });
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/grid-definition/grid-support-repeat-001.html b/third_party/blink/web_tests/external/wpt/css/css-grid/grid-definition/grid-support-repeat-001.html
index c2c8be0c..54d230c 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/grid-definition/grid-support-repeat-001.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/grid-definition/grid-support-repeat-001.html
@@ -65,19 +65,19 @@
 
   // Wrong values.
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "repeat(-1, auto)", "repeat(-1, auto)", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "repeat(-1, auto)", "repeat(-1, auto)", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "repeat(-1, auto)", "repeat(-1, auto)", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "repeat(auto, 2)", "repeat(auto, 2)", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "repeat(auto, 2)", "repeat(auto, 2)", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "repeat(auto, 2)", "repeat(auto, 2)", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "repeat 2, auto", "repeat 2, auto", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "repeat 2, auto", "repeat 2, auto", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "repeat 2, auto", "repeat 2, auto", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "repeat(2 auto)", "repeat(2 auto)", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "repeat(2 auto)", "repeat(2 auto)", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "repeat(2 auto)", "repeat(2 auto)", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "100px (repeat 2, auto)", "(repeat 2, auto)", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "100px (repeat 2, auto)", "(repeat 2, auto)", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "100px (repeat 2, auto)", "(repeat 2, auto)", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "repeat(2, 50px repeat(2, 100px))", "repeat(2, 50px repeat(2, 100px))", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "repeat(2, 50px repeat(2, 100px))", "repeat(2, 50px repeat(2, 100px))", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "repeat(2, 50px repeat(2, 100px))", "repeat(2, 50px repeat(2, 100px))", "none", "none");
   TestingUtils.testGridTemplateColumnsRows("emptyGrid", "100px repeat(2, [a])", "100px repeat(2, [a])", "none", "none");
-  TestingUtils.testGridTemplateColumnsRows("grid", "100px repeat(2, [a])", "100px repeat(2, [a])", "90px", "10px");
+  TestingUtils.testGridTemplateColumnsRows("grid", "100px repeat(2, [a])", "100px repeat(2, [a])", "none", "none");
   done();
 });
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/grid-definition/grid-template-columns-rows-resolved-values-001.html b/third_party/blink/web_tests/external/wpt/css/css-grid/grid-definition/grid-template-columns-rows-resolved-values-001.html
index c992bf66..df91ceb 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/grid-definition/grid-template-columns-rows-resolved-values-001.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/grid-definition/grid-template-columns-rows-resolved-values-001.html
@@ -4,7 +4,7 @@
 <link rel="author" title="Manuel Rego Casasnovas" href="mailto:rego@igalia.com">
 <link rel="help" href="http://www.w3.org/TR/css-grid-1/#resolved-track-list" title="5.1.5. Resolved Values">
 <meta name="flags" content="ahem dom">
-<meta name="assert" content="This test checks that resolved values for 'grid-template-columns' and 'grid-template-rows' list tracks implicitly created.">
+<meta name="assert" content="This test checks that resolved values for 'grid-template-columns' and 'grid-template-rows' don't include implicitly created tracks.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="support/testing-utils.js"></script>
@@ -17,6 +17,7 @@
     font: 10px/1 Ahem;
     justify-content: start;
     align-content: start;
+    position: relative;
   }
 
   .fifthColumn {
@@ -58,50 +59,218 @@
 </div>
 
 <script>
+function testSizeAndPositionOfItems(gridId, expectedItemData) {
+  let grid = document.getElementById(gridId);
+  assert_equals(grid.childElementCount, expectedItemData.length, "childElementCount");
+  let props = ["offsetWidth", "offsetTop", "offsetHeight"];
+  for (let i = 0; i < expectedItemData.length; ++i)
+    for (let prop of props)
+      assert_equals(grid.children[i][prop], expectedItemData[i][prop], "children[" + i + "]." + prop);
+}
+
+function testGrid(gridId, columnsStyle, rowsStyle, columnsComputedValue, rowsComputedValue, expectedItemData) {
+  TestingUtils.testGridTemplateColumnsRows(gridId, columnsStyle, rowsStyle, columnsComputedValue, rowsComputedValue);
+  test(function() {
+    testSizeAndPositionOfItems(gridId, expectedItemData);
+  }, "Children of '" + gridId + "' with: grid-template-columns: " + columnsStyle  + "; and grid-template-rows: " + rowsStyle + ";");
+}
+
 setup({explicit_done: true});
 document.fonts.ready.then(()=> {
   // Valid values.
-  TestingUtils.testGridTemplateColumnsRows("grid", "", "", "110px", ["10px 10px 20px", "repeat(2, 10px) 20px"]);
-  TestingUtils.testGridTemplateColumnsRows("grid", "auto auto", "", "100px 110px", "10px 20px");
-  TestingUtils.testGridTemplateColumnsRows("grid", "60px", "", "60px", ["20px 20px 20px", "repeat(3, 20px)"]);
-  TestingUtils.testGridTemplateColumnsRows("grid", "100px 60px", "", "100px 60px", ["20px 20px", "repeat(2, 20px)"]);
-  TestingUtils.testGridTemplateColumnsRows("grid", "", "50px", "110px", "50px 10px 20px");
-  TestingUtils.testGridTemplateColumnsRows("grid", "", "50px 30px", "110px", "50px 30px 20px");
-  TestingUtils.testGridTemplateColumnsRows("grid", "60px", "50px", "60px", ["50px 20px 20px", "50px repeat(2, 20px)"]);
-  TestingUtils.testGridTemplateColumnsRows("grid", "60px", "50px 30px", "60px", "50px 30px 20px");
-  TestingUtils.testGridTemplateColumnsRows("grid", "100px 60px", "50px", "100px 60px", "50px 20px");
-  TestingUtils.testGridTemplateColumnsRows("grid", "100px 60px", "50px 30px", "100px 60px", "50px 30px");
+  testGrid("grid", "", "", "none", "none", [
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 0, offsetHeight: 10},
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 10, offsetHeight: 10},
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 20, offsetHeight: 20},
+  ]);
+  testGrid("grid", "auto auto", "", "100px 110px", "none", [
+    {offsetLeft: 0, offsetWidth: 100, offsetTop: 0, offsetHeight: 10},
+    {offsetLeft: 100, offsetWidth: 110, offsetTop: 0, offsetHeight: 10},
+    {offsetLeft: 0, offsetWidth: 100, offsetTop: 10, offsetHeight: 20},
+  ]);
+  testGrid("grid", "60px", "", "60px", "none", [
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 0, offsetHeight: 20},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 20, offsetHeight: 20},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 40, offsetHeight: 20},
+  ]);
+  testGrid("grid", "100px 60px", "", "100px 60px", "none", [
+    {offsetLeft: 0, offsetWidth: 100, offsetTop: 0, offsetHeight: 20},
+    {offsetLeft: 100, offsetWidth: 60, offsetTop: 0, offsetHeight: 20},
+    {offsetLeft: 0, offsetWidth: 100, offsetTop: 20, offsetHeight: 20},
+  ]);
+  testGrid("grid", "", "50px", "none", "50px", [
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 0, offsetHeight: 50},
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 50, offsetHeight: 10},
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 60, offsetHeight: 20},
+  ]);
+  testGrid("grid", "", "50px 30px", "none", "50px 30px", [
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 0, offsetHeight: 50},
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 50, offsetHeight: 30},
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 80, offsetHeight: 20},
+  ]);
+  testGrid("grid", "60px", "50px", "60px", "50px", [
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 0, offsetHeight: 50},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 50, offsetHeight: 20},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 70, offsetHeight: 20},
+  ]);
+  testGrid("grid", "60px", "50px 30px", "60px", "50px 30px", [
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 0, offsetHeight: 50},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 50, offsetHeight: 30},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 80, offsetHeight: 20},
+  ]);
+  testGrid("grid", "100px 60px", "50px", "100px 60px", "50px", [
+    {offsetLeft: 0, offsetWidth: 100, offsetTop: 0, offsetHeight: 50},
+    {offsetLeft: 100, offsetWidth: 60, offsetTop: 0, offsetHeight: 50},
+    {offsetLeft: 0, offsetWidth: 100, offsetTop: 50, offsetHeight: 20},
+  ]);
+  testGrid("grid", "100px 60px", "50px 30px", "100px 60px", "50px 30px", [
+    {offsetLeft: 0, offsetWidth: 100, offsetTop: 0, offsetHeight: 50},
+    {offsetLeft: 100, offsetWidth: 60, offsetTop: 0, offsetHeight: 50},
+    {offsetLeft: 0, offsetWidth: 100, offsetTop: 50, offsetHeight: 30},
+  ]);
 
-  TestingUtils.testGridTemplateColumnsRows("gridItemsPositions", "", "", ["110px 0px 0px 0px 100px", "110px repeat(3, 0px) 100px"], "10px 20px 0px 10px");
-  TestingUtils.testGridTemplateColumnsRows("gridItemsPositions", "60px", "", ["60px 0px 0px 0px 100px", "60px repeat(3, 0px) 100px"], "10px 20px 0px 20px");
-  TestingUtils.testGridTemplateColumnsRows("gridItemsPositions", "60px 50px", "", ["60px 50px 0px 0px 100px", "60px 50px repeat(2, 0px) 100px"], "10px 20px 0px 20px");
-  TestingUtils.testGridTemplateColumnsRows("gridItemsPositions", "", "60px", ["110px 0px 0px 0px 100px", "110px repeat(3, 0px) 100px"], "60px 20px 0px 10px");
-  TestingUtils.testGridTemplateColumnsRows("gridItemsPositions", "", "60px 50px", ["110px 0px 0px 0px 100px", "110px repeat(3, 0px) 100px"], "60px 50px 0px 10px");
-  TestingUtils.testGridTemplateColumnsRows("gridItemsPositions", "60px", "60px", ["60px 0px 0px 0px 100px", "60px repeat(3, 0px) 100px"], "60px 20px 0px 20px");
-  TestingUtils.testGridTemplateColumnsRows("gridItemsPositions", "60px", "60px 50px", ["60px 0px 0px 0px 100px", "60px repeat(3, 0px) 100px"], "60px 50px 0px 20px");
-  TestingUtils.testGridTemplateColumnsRows("gridItemsPositions", "60px 50px", "60px", ["60px 50px 0px 0px 100px", "60px 50px repeat(2, 0px) 100px"], "60px 20px 0px 20px");
-  TestingUtils.testGridTemplateColumnsRows("gridItemsPositions", "60px 50px", "60px 50px", ["60px 50px 0px 0px 100px", "60px 50px repeat(2, 0px) 100px"], "60px 50px 0px 20px");
+  testGrid("gridItemsPositions", "", "", "none", "none", [
+    {offsetLeft: 110, offsetWidth: 100, offsetTop: 0, offsetHeight: 10},
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 30, offsetHeight: 10},
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 10, offsetHeight: 20},
+  ]);
+  testGrid("gridItemsPositions", "60px", "", "60px", "none", [
+    {offsetLeft: 60, offsetWidth: 100, offsetTop: 0, offsetHeight: 10},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 30, offsetHeight: 20},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 10, offsetHeight: 20},
+  ]);
+  testGrid("gridItemsPositions", "60px 50px", "", "60px 50px", "none", [
+    {offsetLeft: 110, offsetWidth: 100, offsetTop: 0, offsetHeight: 10},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 30, offsetHeight: 20},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 10, offsetHeight: 20},
+  ]);
+  testGrid("gridItemsPositions", "", "60px", "none", "60px", [
+    {offsetLeft: 110, offsetWidth: 100, offsetTop: 0, offsetHeight: 60},
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 80, offsetHeight: 10},
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 60, offsetHeight: 20},
+  ]);
+  testGrid("gridItemsPositions", "", "60px 50px", "none", "60px 50px", [
+    {offsetLeft: 110, offsetWidth: 100, offsetTop: 0, offsetHeight: 60},
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 110, offsetHeight: 10},
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 60, offsetHeight: 50},
+  ]);
+  testGrid("gridItemsPositions", "60px", "60px", "60px", "60px", [
+    {offsetLeft: 60, offsetWidth: 100, offsetTop: 0, offsetHeight: 60},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 80, offsetHeight: 20},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 60, offsetHeight: 20},
+  ]);
+  testGrid("gridItemsPositions", "60px", "60px 50px", "60px", "60px 50px", [
+    {offsetLeft: 60, offsetWidth: 100, offsetTop: 0, offsetHeight: 60},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 110, offsetHeight: 20},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 60, offsetHeight: 50},
+  ]);
+  testGrid("gridItemsPositions", "60px 50px", "60px", "60px 50px", "60px", [
+    {offsetLeft: 110, offsetWidth: 100, offsetTop: 0, offsetHeight: 60},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 80, offsetHeight: 20},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 60, offsetHeight: 20},
+  ]);
+  testGrid("gridItemsPositions", "60px 50px", "60px 50px", "60px 50px", "60px 50px", [
+    {offsetLeft: 110, offsetWidth: 100, offsetTop: 0, offsetHeight: 60},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 110, offsetHeight: 20},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 60, offsetHeight: 50},
+  ]);
 
-  TestingUtils.testGridTemplateColumnsRows("gridAutoFlowColumn", "", "", "100px 110px 50px", "20px");
-  TestingUtils.testGridTemplateColumnsRows("gridAutoFlowColumn", "", "auto auto", "110px 50px", "20px 10px");
-  TestingUtils.testGridTemplateColumnsRows("gridAutoFlowColumn", "60px", "", "60px 110px 50px", "20px");
-  TestingUtils.testGridTemplateColumnsRows("gridAutoFlowColumn", "100px 60px", "", "100px 60px 50px", "20px");
-  TestingUtils.testGridTemplateColumnsRows("gridAutoFlowColumn", "", "50px", "100px 110px 50px", "50px");
-  TestingUtils.testGridTemplateColumnsRows("gridAutoFlowColumn", "", "50px 30px", "110px 50px", "50px 30px");
-  TestingUtils.testGridTemplateColumnsRows("gridAutoFlowColumn", "60px", "50px", "60px 110px 50px", "50px");
-  TestingUtils.testGridTemplateColumnsRows("gridAutoFlowColumn", "60px", "50px 30px", "60px 50px", "50px 30px");
-  TestingUtils.testGridTemplateColumnsRows("gridAutoFlowColumn", "100px 60px", "50px", "100px 60px 50px", "50px");
-  TestingUtils.testGridTemplateColumnsRows("gridAutoFlowColumn", "100px 60px", "50px 30px", "100px 60px", "50px 30px");
+  testGrid("gridAutoFlowColumn", "", "", "none", "none", [
+    {offsetLeft: 0, offsetWidth: 100, offsetTop: 0, offsetHeight: 20},
+    {offsetLeft: 100, offsetWidth: 110, offsetTop: 0, offsetHeight: 20},
+    {offsetLeft: 210, offsetWidth: 50, offsetTop: 0, offsetHeight: 20},
+  ]);
+  testGrid("gridAutoFlowColumn", "", "auto auto", "none", "20px 10px", [
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 0, offsetHeight: 20},
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 20, offsetHeight: 10},
+    {offsetLeft: 110, offsetWidth: 50, offsetTop: 0, offsetHeight: 20},
+  ]);
+  testGrid("gridAutoFlowColumn", "60px", "", "60px", "none", [
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 0, offsetHeight: 20},
+    {offsetLeft: 60, offsetWidth: 110, offsetTop: 0, offsetHeight: 20},
+    {offsetLeft: 170, offsetWidth: 50, offsetTop: 0, offsetHeight: 20},
+  ]);
+  testGrid("gridAutoFlowColumn", "100px 60px", "", "100px 60px", "none", [
+    {offsetLeft: 0, offsetWidth: 100, offsetTop: 0, offsetHeight: 20},
+    {offsetLeft: 100, offsetWidth: 60, offsetTop: 0, offsetHeight: 20},
+    {offsetLeft: 160, offsetWidth: 50, offsetTop: 0, offsetHeight: 20},
+  ]);
+  testGrid("gridAutoFlowColumn", "", "50px", "none", "50px", [
+    {offsetLeft: 0, offsetWidth: 100, offsetTop: 0, offsetHeight: 50},
+    {offsetLeft: 100, offsetWidth: 110, offsetTop: 0, offsetHeight: 50},
+    {offsetLeft: 210, offsetWidth: 50, offsetTop: 0, offsetHeight: 50},
+  ]);
+  testGrid("gridAutoFlowColumn", "", "50px 30px", "none", "50px 30px", [
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 0, offsetHeight: 50},
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 50, offsetHeight: 30},
+    {offsetLeft: 110, offsetWidth: 50, offsetTop: 0, offsetHeight: 50},
+  ]);
+  testGrid("gridAutoFlowColumn", "60px", "50px", "60px", "50px", [
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 0, offsetHeight: 50},
+    {offsetLeft: 60, offsetWidth: 110, offsetTop: 0, offsetHeight: 50},
+    {offsetLeft: 170, offsetWidth: 50, offsetTop: 0, offsetHeight: 50},
+  ]);
+  testGrid("gridAutoFlowColumn", "60px", "50px 30px", "60px", "50px 30px", [
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 0, offsetHeight: 50},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 50, offsetHeight: 30},
+    {offsetLeft: 60, offsetWidth: 50, offsetTop: 0, offsetHeight: 50},
+  ]);
+  testGrid("gridAutoFlowColumn", "100px 60px", "50px", "100px 60px", "50px", [
+    {offsetLeft: 0, offsetWidth: 100, offsetTop: 0, offsetHeight: 50},
+    {offsetLeft: 100, offsetWidth: 60, offsetTop: 0, offsetHeight: 50},
+    {offsetLeft: 160, offsetWidth: 50, offsetTop: 0, offsetHeight: 50},
+  ]);
+  testGrid("gridAutoFlowColumn", "100px 60px", "50px 30px", "100px 60px", "50px 30px", [
+    {offsetLeft: 0, offsetWidth: 100, offsetTop: 0, offsetHeight: 50},
+    {offsetLeft: 0, offsetWidth: 100, offsetTop: 50, offsetHeight: 30},
+    {offsetLeft: 100, offsetWidth: 60, offsetTop: 0, offsetHeight: 50},
+  ]);
 
-  TestingUtils.testGridTemplateColumnsRows("gridAutoFlowColumnItemsPositions", "", "", ["110px 50px 0px 0px 100px", "110px 50px repeat(2, 0px) 100px"], ["20px 0px 0px 10px", "20px repeat(2, 0px) 10px"]);
-  TestingUtils.testGridTemplateColumnsRows("gridAutoFlowColumnItemsPositions", "60px", "", ["60px 50px 0px 0px 100px", "60px 50px repeat(2, 0px) 100px"], ["20px 0px 0px 20px", "20px repeat(2, 0px) 20px"]);
-  TestingUtils.testGridTemplateColumnsRows("gridAutoFlowColumnItemsPositions", "60px 70px", "", ["60px 70px 0px 0px 100px", "60px 70px repeat(2, 0px) 100px"], ["20px 0px 0px 20px", "20px repeat(2, 0px) 20px"]);
-  TestingUtils.testGridTemplateColumnsRows("gridAutoFlowColumnItemsPositions", "", "60px", ["110px 50px 0px 0px 100px", "110px 50px repeat(2, 0px) 100px"], ["60px 0px 0px 10px", "60px repeat(2, 0px) 10px"]);
-  TestingUtils.testGridTemplateColumnsRows("gridAutoFlowColumnItemsPositions", "", "60px 70px", ["110px 50px 0px 0px 100px", "110px 50px repeat(2, 0px) 100px"], "60px 70px 0px 10px");
-  TestingUtils.testGridTemplateColumnsRows("gridAutoFlowColumnItemsPositions", "60px", "60px", ["60px 50px 0px 0px 100px", "60px 50px repeat(2, 0px) 100px"], ["60px 0px 0px 20px", "60px repeat(2, 0px) 20px"]);
-  TestingUtils.testGridTemplateColumnsRows("gridAutoFlowColumnItemsPositions", "60px", "60px 70px", ["60px 50px 0px 0px 100px", "60px 50px repeat(2, 0px) 100px"], "60px 70px 0px 20px");
-  TestingUtils.testGridTemplateColumnsRows("gridAutoFlowColumnItemsPositions", "60px 70px", "60px", ["60px 70px 0px 0px 100px", "60px 70px repeat(2, 0px) 100px"], ["60px 0px 0px 20px", "60px repeat(2, 0px) 20px"]);
-  TestingUtils.testGridTemplateColumnsRows("gridAutoFlowColumnItemsPositions", "60px 70px", "60px 70px", ["60px 70px 0px 0px 100px", "60px 70px repeat(2, 0px) 100px"], "60px 70px 0px 20px");
+  testGrid("gridAutoFlowColumnItemsPositions", "", "", "none", "none", [
+    {offsetLeft: 160, offsetWidth: 100, offsetTop: 0, offsetHeight: 20},
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 20, offsetHeight: 10},
+    {offsetLeft: 110, offsetWidth: 50, offsetTop: 0, offsetHeight: 20},
+  ]);
+  testGrid("gridAutoFlowColumnItemsPositions", "60px", "", "60px", "none", [
+    {offsetLeft: 110, offsetWidth: 100, offsetTop: 0, offsetHeight: 20},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 20, offsetHeight: 20},
+    {offsetLeft: 60, offsetWidth: 50, offsetTop: 0, offsetHeight: 20},
+  ]);
+  testGrid("gridAutoFlowColumnItemsPositions", "60px 70px", "", "60px 70px", "none", [
+    {offsetLeft: 130, offsetWidth: 100, offsetTop: 0, offsetHeight: 20},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 20, offsetHeight: 20},
+    {offsetLeft: 60, offsetWidth: 70, offsetTop: 0, offsetHeight: 20},
+  ]);
+  testGrid("gridAutoFlowColumnItemsPositions", "", "60px", "none", "60px", [
+    {offsetLeft: 160, offsetWidth: 100, offsetTop: 0, offsetHeight: 60},
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 60, offsetHeight: 10},
+    {offsetLeft: 110, offsetWidth: 50, offsetTop: 0, offsetHeight: 60},
+  ]);
+  testGrid("gridAutoFlowColumnItemsPositions", "", "60px 70px", "none", "60px 70px", [
+    {offsetLeft: 160, offsetWidth: 100, offsetTop: 0, offsetHeight: 60},
+    {offsetLeft: 0, offsetWidth: 110, offsetTop: 130, offsetHeight: 10},
+    {offsetLeft: 110, offsetWidth: 50, offsetTop: 0, offsetHeight: 60},
+  ]);
+  testGrid("gridAutoFlowColumnItemsPositions", "60px", "60px", "60px", "60px", [
+    {offsetLeft: 110, offsetWidth: 100, offsetTop: 0, offsetHeight: 60},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 60, offsetHeight: 20},
+    {offsetLeft: 60, offsetWidth: 50, offsetTop: 0, offsetHeight: 60},
+  ]);
+  testGrid("gridAutoFlowColumnItemsPositions", "60px", "60px 70px", "60px", "60px 70px", [
+    {offsetLeft: 110, offsetWidth: 100, offsetTop: 0, offsetHeight: 60},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 130, offsetHeight: 20},
+    {offsetLeft: 60, offsetWidth: 50, offsetTop: 0, offsetHeight: 60},
+  ]);
+  testGrid("gridAutoFlowColumnItemsPositions", "60px 70px", "60px", "60px 70px", "60px", [
+    {offsetLeft: 130, offsetWidth: 100, offsetTop: 0, offsetHeight: 60},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 60, offsetHeight: 20},
+    {offsetLeft: 60, offsetWidth: 70, offsetTop: 0, offsetHeight: 60},
+  ]);
+  testGrid("gridAutoFlowColumnItemsPositions", "60px 70px", "60px 70px", "60px 70px", "60px 70px", [
+    {offsetLeft: 130, offsetWidth: 100, offsetTop: 0, offsetHeight: 60},
+    {offsetLeft: 0, offsetWidth: 60, offsetTop: 130, offsetHeight: 20},
+    {offsetLeft: 60, offsetWidth: 70, offsetTop: 0, offsetHeight: 60},
+  ]);
   done();
 });
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/grid-items/grid-minimum-size-grid-items-021.html b/third_party/blink/web_tests/external/wpt/css/css-grid/grid-items/grid-minimum-size-grid-items-021.html
index 922223a..ab66d70 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/grid-items/grid-minimum-size-grid-items-021.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/grid-items/grid-minimum-size-grid-items-021.html
@@ -11,9 +11,15 @@
 <style>
   .grid {
     display: inline-grid;
+    grid-template-rows: auto;
+    grid-template-columns: auto;
     font: 25px/1 Ahem;
   }
 
+  .grid2rows {
+    grid-template-rows: auto auto;
+  }
+
   .constrainedGrid {
     width: 10px;
     height: 10px;
@@ -126,32 +132,32 @@
 
 <!-- Grids with a 50x50 image as grid item and a 100x25 text grid item. -->
 
-<div id="grid-7" class="grid">
+<div id="grid-7" class="grid grid2rows">
   <img id="img-7" class="width200px" src="support/50x50-green.png">
   <div>ITEM</div>
 </div>
 
-<div id="grid-8" class="grid constrainedGrid">
+<div id="grid-8" class="grid grid2rows constrainedGrid">
   <img id="img-8" class="width200px" src="support/50x50-green.png">
   <div>ITEM</div>
 </div>
 
-<div id="grid-9" class="grid width200px">
+<div id="grid-9" class="grid grid2rows width200px">
   <img id="img-9" class="width100percent" src="support/50x50-green.png">
   <div>ITEM</div>
 </div>
 
-<div id="grid-10" class="grid width200px constrainedGrid">
+<div id="grid-10" class="grid grid2rows width200px constrainedGrid">
   <img id="img-10" class="width100percent" src="support/50x50-green.png">
   <div>ITEM</div>
 </div>
 
-<div id="grid-11" class="grid width200px justifyContentStart">
+<div id="grid-11" class="grid grid2rows width200px justifyContentStart">
   <img id="img-11" class="width100percent" src="support/50x50-green.png">
   <div>ITEM</div>
 </div>
 
-<div id="grid-12" class="grid width200px constrainedGrid justifyContentStart">
+<div id="grid-12" class="grid grid2rows width200px constrainedGrid justifyContentStart">
   <img id="img-12" class="width100percent" src="support/50x50-green.png">
   <div>ITEM</div>
 </div>
@@ -184,32 +190,32 @@
 
 <!-- Grids with a 500x500 image as grid item and a 100x25 text grid item. -->
 
-<div id="grid-19" class="grid">
+<div id="grid-19" class="grid grid2rows">
   <img id="img-19" class="width200px" src="support/500x500-green.png">
   <div>ITEM</div>
 </div>
 
-<div id="grid-20" class="grid constrainedGrid">
+<div id="grid-20" class="grid grid2rows constrainedGrid">
   <img id="img-20" class="width200px" src="support/500x500-green.png">
   <div>ITEM</div>
 </div>
 
-<div id="grid-21" class="grid width200px">
+<div id="grid-21" class="grid grid2rows width200px">
   <img id="img-21" class="width100percent" src="support/500x500-green.png">
   <div>ITEM</div>
 </div>
 
-<div id="grid-22" class="grid width200px constrainedGrid">
+<div id="grid-22" class="grid grid2rows width200px constrainedGrid">
   <img id="img-22" class="width100percent" src="support/500x500-green.png">
   <div>ITEM</div>
 </div>
 
-<div id="grid-23" class="grid width200px justifyContentStart">
+<div id="grid-23" class="grid grid2rows width200px justifyContentStart">
   <img id="img-23" class="width100percent" src="support/500x500-green.png">
   <div>ITEM</div>
 </div>
 
-<div id="grid-24" class="grid width200px constrainedGrid justifyContentStart">
+<div id="grid-24" class="grid grid2rows width200px constrainedGrid justifyContentStart">
   <img id="img-24" class="width100percent" src="support/500x500-green.png">
   <div>ITEM</div>
 </div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-columns-computed-withcontent.html b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-columns-computed-withcontent.html
index 9b7cd03..a788ca2 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-columns-computed-withcontent.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-columns-computed-withcontent.html
@@ -36,7 +36,7 @@
   </div>
 </div>
 <script>
-test_computed_value("grid-template-columns", 'none', '300px'); // "none" without #child
+test_computed_value("grid-template-columns", 'none', 'none'); // "none" without #child
 
 // track-size <fixed-breadth> = <length-percentage> | <flex> | min-content | max-content | auto
 test_computed_value("grid-template-columns", '20%', '60px'); // 20% * width
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-rows-computed-withcontent.html b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-rows-computed-withcontent.html
index 693cf338..1e85f16d 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-rows-computed-withcontent.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-rows-computed-withcontent.html
@@ -36,7 +36,7 @@
   </div>
 </div>
 <script>
-test_computed_value("grid-template-rows", 'none', '600px'); // "none" without #child
+test_computed_value("grid-template-rows", 'none', 'none'); // "none" without #child
 
 // track-size <fixed-breadth> = <length-percentage> | <flex> | min-content | max-content | auto
 test_computed_value("grid-template-rows", '20%', '120px'); // 20% * height
diff --git a/third_party/blink/web_tests/external/wpt/css/css-pseudo/marker-content-013-ref.html b/third_party/blink/web_tests/external/wpt/css/css-pseudo/marker-content-013-ref.html
index 7657cfb..c46f8db 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-pseudo/marker-content-013-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-pseudo/marker-content-013-ref.html
@@ -13,14 +13,26 @@
 .content::marker {
   content: "content";
 }
+.before, .after {
+  display: contents;
+}
+.before::before, .after::after {
+  content: "item";
+  display: list-item;
+  list-style-type: "nested";
+}
 </style>
 <ol class="outside">
   <li class="decimal">item</li>
   <li class="string">item</li>
   <li class="content">item</li>
+  <li class="before"></li>
+  <li class="after"></li>
 </ol>
 <ol class="inside">
   <li class="decimal">item</li>
   <li class="string">item</li>
   <li class="content">item</li>
+  <li class="before"></li>
+  <li class="after"></li>
 </ol>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-pseudo/marker-content-013.html b/third_party/blink/web_tests/external/wpt/css/css-pseudo/marker-content-013.html
index 427578f..3b2ce6fa 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-pseudo/marker-content-013.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-pseudo/marker-content-013.html
@@ -17,16 +17,28 @@
 .content::marker {
   content: "content";
 }
+.before, .after {
+  display: contents;
+}
+.before::before, .after::after {
+  content: "item";
+  display: list-item;
+  list-style-type: "nested";
+}
 </style>
 <ol class="inside">
   <li class="decimal">item</li>
   <li class="string">item</li>
   <li class="content">item</li>
+  <li class="before"></li>
+  <li class="after"></li>
 </ol>
 <ol class="outside">
   <li class="decimal">item</li>
   <li class="string">item</li>
   <li class="content">item</li>
+  <li class="before"></li>
+  <li class="after"></li>
 </ol>
 <script src="/common/reftest-wait.js"></script>
 <script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/constraints/form-validation-validity-patternMismatch-expected.txt b/third_party/blink/web_tests/external/wpt/html/semantics/forms/constraints/form-validation-validity-patternMismatch-expected.txt
new file mode 100644
index 0000000..05bb339
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/constraints/form-validation-validity-patternMismatch-expected.txt
@@ -0,0 +1,68 @@
+This is a testharness.js-based test.
+Found 64 tests; 60 PASS, 4 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS [INPUT in TEXT status] The pattern attribute is not set
+PASS [INPUT in TEXT status] The value attibute is empty string
+PASS [INPUT in TEXT status] The value attribute matches the pattern attribute
+PASS [INPUT in TEXT status] The value(ABC) in unicode attribute matches the pattern attribute
+PASS [INPUT in TEXT status] The value attribute mismatches the pattern attribute
+PASS [INPUT in TEXT status] The value attribute mismatches the pattern attribute even when a subset matches
+PASS [INPUT in TEXT status] Invalid regular expression gets ignored
+PASS [INPUT in TEXT status] The pattern attribute tries to escape a group
+PASS [INPUT in TEXT status] The pattern attribute uses Unicode features
+PASS [INPUT in SEARCH status] The pattern attribute is not set
+PASS [INPUT in SEARCH status] The value attibute is empty string
+PASS [INPUT in SEARCH status] The value attribute matches the pattern attribute
+PASS [INPUT in SEARCH status] The value(ABC) in unicode attribute matches the pattern attribute
+PASS [INPUT in SEARCH status] The value attribute mismatches the pattern attribute
+PASS [INPUT in SEARCH status] The value attribute mismatches the pattern attribute even when a subset matches
+PASS [INPUT in SEARCH status] Invalid regular expression gets ignored
+PASS [INPUT in SEARCH status] The pattern attribute tries to escape a group
+PASS [INPUT in SEARCH status] The pattern attribute uses Unicode features
+PASS [INPUT in TEL status] The pattern attribute is not set
+PASS [INPUT in TEL status] The value attibute is empty string
+PASS [INPUT in TEL status] The value attribute matches the pattern attribute
+PASS [INPUT in TEL status] The value(ABC) in unicode attribute matches the pattern attribute
+PASS [INPUT in TEL status] The value attribute mismatches the pattern attribute
+PASS [INPUT in TEL status] The value attribute mismatches the pattern attribute even when a subset matches
+PASS [INPUT in TEL status] Invalid regular expression gets ignored
+PASS [INPUT in TEL status] The pattern attribute tries to escape a group
+PASS [INPUT in TEL status] The pattern attribute uses Unicode features
+PASS [INPUT in URL status] The pattern attribute is not set
+PASS [INPUT in URL status] The value attibute is empty string
+PASS [INPUT in URL status] The value attribute matches the pattern attribute
+PASS [INPUT in URL status] The value(ABC) in unicode attribute matches the pattern attribute
+PASS [INPUT in URL status] The value attribute mismatches the pattern attribute
+PASS [INPUT in URL status] The value attribute mismatches the pattern attribute even when a subset matches
+PASS [INPUT in URL status] Invalid regular expression gets ignored
+PASS [INPUT in URL status] The pattern attribute tries to escape a group
+PASS [INPUT in URL status] The pattern attribute uses Unicode features
+PASS [INPUT in EMAIL status] The pattern attribute is not set
+PASS [INPUT in EMAIL status] The value attibute is empty string
+PASS [INPUT in EMAIL status] The value attribute matches the pattern attribute
+PASS [INPUT in EMAIL status] The value(ABC) in unicode attribute matches the pattern attribute
+PASS [INPUT in EMAIL status] The value attribute mismatches the pattern attribute
+PASS [INPUT in EMAIL status] The value attribute mismatches the pattern attribute even when a subset matches
+PASS [INPUT in EMAIL status] Invalid regular expression gets ignored
+PASS [INPUT in EMAIL status] The pattern attribute tries to escape a group
+PASS [INPUT in EMAIL status] The pattern attribute uses Unicode features
+PASS [INPUT in PASSWORD status] The pattern attribute is not set
+PASS [INPUT in PASSWORD status] The value attibute is empty string
+PASS [INPUT in PASSWORD status] The value attribute matches the pattern attribute
+PASS [INPUT in PASSWORD status] The value(ABC) in unicode attribute matches the pattern attribute
+PASS [INPUT in PASSWORD status] The value attribute mismatches the pattern attribute
+PASS [INPUT in PASSWORD status] The value attribute mismatches the pattern attribute even when a subset matches
+PASS [INPUT in PASSWORD status] Invalid regular expression gets ignored
+PASS [INPUT in PASSWORD status] The pattern attribute tries to escape a group
+PASS [INPUT in PASSWORD status] The pattern attribute uses Unicode features
+PASS [INPUT in EMAIL status] The pattern attribute is not set, if multiple is present
+PASS [INPUT in EMAIL status] The value attibute is empty string, if multiple is present
+FAIL [INPUT in EMAIL status] The value attribute matches the pattern attribute, if multiple is present assert_false: The validity.patternMismatch should be false. expected false got true
+FAIL [INPUT in EMAIL status] The value(ABC) in unicode attribute matches the pattern attribute, if multiple is present assert_false: The validity.patternMismatch should be false. expected false got true
+PASS [INPUT in EMAIL status] The value attribute mismatches the pattern attribute, if multiple is present
+PASS [INPUT in EMAIL status] The value attribute mismatches the pattern attribute even when a subset matches, if multiple is present
+PASS [INPUT in EMAIL status] Invalid regular expression gets ignored, if multiple is present
+PASS [INPUT in EMAIL status] The pattern attribute tries to escape a group, if multiple is present
+FAIL [INPUT in EMAIL status] The pattern attribute uses Unicode features, if multiple is present assert_false: The validity.patternMismatch should be false. expected false got true
+FAIL [INPUT in EMAIL status] Commas should be stripped from regex input, if multiple is present assert_true: The validity.patternMismatch should be true. expected true got false
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/constraints/form-validation-validity-patternMismatch.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/constraints/form-validation-validity-patternMismatch.html
index d8677898..4ca64aa 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/forms/constraints/form-validation-validity-patternMismatch.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/constraints/form-validation-validity-patternMismatch.html
@@ -19,7 +19,26 @@
         {conditions: {pattern: "[A-Z]{1}", value: "A"}, expected: false, name: "[target] The value attribute matches the pattern attribute"},
         {conditions: {pattern: "[A-Z]+", value: "\u0041\u0042\u0043"}, expected: false, name: "[target] The value(ABC) in unicode attribute matches the pattern attribute"},
         {conditions: {pattern: "[a-z]{3,}", value: "ABCD"}, expected: true, name: "[target] The value attribute mismatches the pattern attribute"},
-        {conditions: {pattern: "[A-Z]+", value: "ABC123"}, expected: true, name: "[target] The value attribute mismatches the pattern attribute even when a subset matches"}
+        {conditions: {pattern: "[A-Z]+", value: "ABC123"}, expected: true, name: "[target] The value attribute mismatches the pattern attribute even when a subset matches"},
+        {conditions: {pattern: "(abc", value: "de"}, expected: false, name: "[target] Invalid regular expression gets ignored"},
+        {conditions: {pattern: "a)(b", value: "de"}, expected: false, name: "[target] The pattern attribute tries to escape a group"},
+        {conditions: {pattern: "a\\u{10FFFF}", value: "a\u{10FFFF}"}, expected: false, name: "[target] The pattern attribute uses Unicode features"},
+      ]
+    },
+    {
+      tag: "input",
+      types: ["email"],
+      testData: [
+        {conditions: {multiple: true, pattern: null, value: "abc,abc"}, expected: false, name: "[target] The pattern attribute is not set, if multiple is present"},
+        {conditions: {multiple: true, pattern: "[A-Z]+", value: ""}, expected: false, name: "[target] The value attibute is empty string, if multiple is present"},
+        {conditions: {multiple: true, pattern: "[A-Z]{1}", value: "A,A"}, expected: false, name: "[target] The value attribute matches the pattern attribute, if multiple is present"},
+        {conditions: {multiple: true, pattern: "[A-Z]+", value: "\u0041\u0042\u0043,\u0041\u0042\u0043"}, expected: false, name: "[target] The value(ABC) in unicode attribute matches the pattern attribute, if multiple is present"},
+        {conditions: {multiple: true, pattern: "[a-z]{3,}", value: "abcd,ABCD"}, expected: true, name: "[target] The value attribute mismatches the pattern attribute, if multiple is present"},
+        {conditions: {multiple: true, pattern: "[A-Z]+", value: "ABCD,ABC123"}, expected: true, name: "[target] The value attribute mismatches the pattern attribute even when a subset matches, if multiple is present"},
+        {conditions: {multiple: true, pattern: "(abc", value: "de,de"}, expected: false, name: "[target] Invalid regular expression gets ignored, if multiple is present"},
+        {conditions: {multiple: true, pattern: "a)(b", value: "de,de"}, expected: false, name: "[target] The pattern attribute tries to escape a group, if multiple is present"},
+        {conditions: {multiple: true, pattern: "a\\u{10FFFF}", value: "a\u{10FFFF},a\u{10FFFF}"}, expected: false, name: "[target] The pattern attribute uses Unicode features, if multiple is present"},
+        {conditions: {multiple: true, pattern: "a,", value: "a,"}, expected: true, name: "[target] Commas should be stripped from regex input, if multiple is present"},
       ]
     }
   ];
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.copy-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.copy-expected.txt
deleted file mode 100644
index 58a567d..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.copy-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.composite.canvas.copy Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.copy.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.copy.html
index 6cf8779..824e1ec 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.copy.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.copy.html
@@ -33,12 +33,14 @@
     };
 });
 promise.then(function(response) {
-    ctx2.drawImage(response, 0, 0);
-    ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
-    ctx.fillRect(0, 0, 100, 50);
-    ctx.globalCompositeOperation = 'copy';
-    ctx.drawImage(offscreenCanvas2, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 255,255,0,191, "50,25", "255,255,0,191", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx2.drawImage(bitmap, 0, 0);
+        ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+        ctx.fillRect(0, 0, 100, 50);
+        ctx.globalCompositeOperation = 'copy';
+        ctx.drawImage(offscreenCanvas2, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 255,255,0,191, "50,25", "255,255,0,191", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.copy.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.copy.worker-expected.txt
deleted file mode 100644
index 2e526ad..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.copy.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.copy.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.copy.worker.js
index aa0f39d..13f85bc 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.copy.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.copy.worker.js
@@ -29,12 +29,14 @@
     };
 });
 promise.then(function(response) {
-    ctx2.drawImage(response, 0, 0);
-    ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
-    ctx.fillRect(0, 0, 100, 50);
-    ctx.globalCompositeOperation = 'copy';
-    ctx.drawImage(offscreenCanvas2, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 255,255,0,191, "50,25", "255,255,0,191", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx2.drawImage(bitmap, 0, 0);
+        ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+        ctx.fillRect(0, 0, 100, 50);
+        ctx.globalCompositeOperation = 'copy';
+        ctx.drawImage(offscreenCanvas2, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 255,255,0,191, "50,25", "255,255,0,191", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-atop-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-atop-expected.txt
deleted file mode 100644
index 1128148..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-atop-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.composite.canvas.destination-atop Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-atop.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-atop.html
index b086a54..49dc643 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-atop.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-atop.html
@@ -33,12 +33,14 @@
     };
 });
 promise.then(function(response) {
-    ctx2.drawImage(response, 0, 0);
-    ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
-    ctx.fillRect(0, 0, 100, 50);
-    ctx.globalCompositeOperation = 'destination-atop';
-    ctx.drawImage(offscreenCanvas2, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 128,255,128,191, "50,25", "128,255,128,191", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx2.drawImage(bitmap, 0, 0);
+        ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+        ctx.fillRect(0, 0, 100, 50);
+        ctx.globalCompositeOperation = 'destination-atop';
+        ctx.drawImage(offscreenCanvas2, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 128,255,128,191, "50,25", "128,255,128,191", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-atop.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-atop.worker-expected.txt
deleted file mode 100644
index 2e526ad..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-atop.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-atop.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-atop.worker.js
index 18785d7..7cdac87 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-atop.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-atop.worker.js
@@ -29,12 +29,14 @@
     };
 });
 promise.then(function(response) {
-    ctx2.drawImage(response, 0, 0);
-    ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
-    ctx.fillRect(0, 0, 100, 50);
-    ctx.globalCompositeOperation = 'destination-atop';
-    ctx.drawImage(offscreenCanvas2, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 128,255,128,191, "50,25", "128,255,128,191", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx2.drawImage(bitmap, 0, 0);
+        ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+        ctx.fillRect(0, 0, 100, 50);
+        ctx.globalCompositeOperation = 'destination-atop';
+        ctx.drawImage(offscreenCanvas2, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 128,255,128,191, "50,25", "128,255,128,191", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-in-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-in-expected.txt
deleted file mode 100644
index 962695a..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-in-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.composite.canvas.destination-in Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-in.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-in.html
index 402f2c3..647e486 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-in.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-in.html
@@ -33,12 +33,14 @@
     };
 });
 promise.then(function(response) {
-    ctx2.drawImage(response, 0, 0);
-    ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
-    ctx.fillRect(0, 0, 100, 50);
-    ctx.globalCompositeOperation = 'destination-in';
-    ctx.drawImage(offscreenCanvas2, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,255,255,96, "50,25", "0,255,255,96", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx2.drawImage(bitmap, 0, 0);
+        ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+        ctx.fillRect(0, 0, 100, 50);
+        ctx.globalCompositeOperation = 'destination-in';
+        ctx.drawImage(offscreenCanvas2, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,255,255,96, "50,25", "0,255,255,96", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-in.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-in.worker-expected.txt
deleted file mode 100644
index 2e526ad..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-in.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-in.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-in.worker.js
index a2e403bf..0fbfc76 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-in.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-in.worker.js
@@ -29,12 +29,14 @@
     };
 });
 promise.then(function(response) {
-    ctx2.drawImage(response, 0, 0);
-    ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
-    ctx.fillRect(0, 0, 100, 50);
-    ctx.globalCompositeOperation = 'destination-in';
-    ctx.drawImage(offscreenCanvas2, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,255,255,96, "50,25", "0,255,255,96", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx2.drawImage(bitmap, 0, 0);
+        ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+        ctx.fillRect(0, 0, 100, 50);
+        ctx.globalCompositeOperation = 'destination-in';
+        ctx.drawImage(offscreenCanvas2, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,255,255,96, "50,25", "0,255,255,96", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-out-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-out-expected.txt
deleted file mode 100644
index df75120..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-out-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.composite.canvas.destination-out Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-out.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-out.html
index bccc50a..46114c65 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-out.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-out.html
@@ -33,12 +33,14 @@
     };
 });
 promise.then(function(response) {
-    ctx2.drawImage(response, 0, 0);
-    ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
-    ctx.fillRect(0, 0, 100, 50);
-    ctx.globalCompositeOperation = 'destination-out';
-    ctx.drawImage(offscreenCanvas2, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,255,255,32, "50,25", "0,255,255,32", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx2.drawImage(bitmap, 0, 0);
+        ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+        ctx.fillRect(0, 0, 100, 50);
+        ctx.globalCompositeOperation = 'destination-out';
+        ctx.drawImage(offscreenCanvas2, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,255,255,32, "50,25", "0,255,255,32", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-out.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-out.worker-expected.txt
deleted file mode 100644
index 2e526ad..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-out.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-out.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-out.worker.js
index 3ae91e3..659d0ef 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-out.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-out.worker.js
@@ -29,12 +29,14 @@
     };
 });
 promise.then(function(response) {
-    ctx2.drawImage(response, 0, 0);
-    ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
-    ctx.fillRect(0, 0, 100, 50);
-    ctx.globalCompositeOperation = 'destination-out';
-    ctx.drawImage(offscreenCanvas2, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,255,255,32, "50,25", "0,255,255,32", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx2.drawImage(bitmap, 0, 0);
+        ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+        ctx.fillRect(0, 0, 100, 50);
+        ctx.globalCompositeOperation = 'destination-out';
+        ctx.drawImage(offscreenCanvas2, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,255,255,32, "50,25", "0,255,255,32", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-over-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-over-expected.txt
deleted file mode 100644
index 62ba07e..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-over-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.composite.canvas.destination-over Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-over.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-over.html
index 8128e11..a7cfc30 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-over.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-over.html
@@ -33,12 +33,14 @@
     };
 });
 promise.then(function(response) {
-    ctx2.drawImage(response, 0, 0);
-    ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
-    ctx.fillRect(0, 0, 100, 50);
-    ctx.globalCompositeOperation = 'destination-over';
-    ctx.drawImage(offscreenCanvas2, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 109,255,146,223, "50,25", "109,255,146,223", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx2.drawImage(bitmap, 0, 0);
+        ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+        ctx.fillRect(0, 0, 100, 50);
+        ctx.globalCompositeOperation = 'destination-over';
+        ctx.drawImage(offscreenCanvas2, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 109,255,146,223, "50,25", "109,255,146,223", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-over.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-over.worker-expected.txt
deleted file mode 100644
index 2e526ad..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-over.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-over.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-over.worker.js
index 35827d4..648da78ce 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-over.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.destination-over.worker.js
@@ -29,12 +29,14 @@
     };
 });
 promise.then(function(response) {
-    ctx2.drawImage(response, 0, 0);
-    ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
-    ctx.fillRect(0, 0, 100, 50);
-    ctx.globalCompositeOperation = 'destination-over';
-    ctx.drawImage(offscreenCanvas2, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 109,255,146,223, "50,25", "109,255,146,223", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx2.drawImage(bitmap, 0, 0);
+        ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+        ctx.fillRect(0, 0, 100, 50);
+        ctx.globalCompositeOperation = 'destination-over';
+        ctx.drawImage(offscreenCanvas2, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 109,255,146,223, "50,25", "109,255,146,223", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.lighter-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.lighter-expected.txt
deleted file mode 100644
index 5258d043..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.lighter-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.composite.canvas.lighter Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.lighter.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.lighter.html
index 20083b5..15012f2 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.lighter.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.lighter.html
@@ -33,12 +33,14 @@
     };
 });
 promise.then(function(response) {
-    ctx2.drawImage(response, 0, 0);
-    ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
-    ctx.fillRect(0, 0, 100, 50);
-    ctx.globalCompositeOperation = 'lighter';
-    ctx.drawImage(offscreenCanvas2, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 191,255,128,255, "50,25", "191,255,128,255", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx2.drawImage(bitmap, 0, 0);
+        ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+        ctx.fillRect(0, 0, 100, 50);
+        ctx.globalCompositeOperation = 'lighter';
+        ctx.drawImage(offscreenCanvas2, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 191,255,128,255, "50,25", "191,255,128,255", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.lighter.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.lighter.worker-expected.txt
deleted file mode 100644
index 2e526ad..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.lighter.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.lighter.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.lighter.worker.js
index 474c299..5450fc3 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.lighter.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.lighter.worker.js
@@ -29,12 +29,14 @@
     };
 });
 promise.then(function(response) {
-    ctx2.drawImage(response, 0, 0);
-    ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
-    ctx.fillRect(0, 0, 100, 50);
-    ctx.globalCompositeOperation = 'lighter';
-    ctx.drawImage(offscreenCanvas2, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 191,255,128,255, "50,25", "191,255,128,255", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx2.drawImage(bitmap, 0, 0);
+        ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+        ctx.fillRect(0, 0, 100, 50);
+        ctx.globalCompositeOperation = 'lighter';
+        ctx.drawImage(offscreenCanvas2, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 191,255,128,255, "50,25", "191,255,128,255", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-atop-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-atop-expected.txt
deleted file mode 100644
index e8e7001..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-atop-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.composite.canvas.source-atop Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-atop.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-atop.html
index 16fb9fb..30afb49 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-atop.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-atop.html
@@ -33,12 +33,14 @@
     };
 });
 promise.then(function(response) {
-    ctx2.drawImage(response, 0, 0);
-    ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
-    ctx.fillRect(0, 0, 100, 50);
-    ctx.globalCompositeOperation = 'source-atop';
-    ctx.drawImage(offscreenCanvas2, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 191,255,64,128, "50,25", "191,255,64,128", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx2.drawImage(bitmap, 0, 0);
+        ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+        ctx.fillRect(0, 0, 100, 50);
+        ctx.globalCompositeOperation = 'source-atop';
+        ctx.drawImage(offscreenCanvas2, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 191,255,64,128, "50,25", "191,255,64,128", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-atop.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-atop.worker-expected.txt
deleted file mode 100644
index 2e526ad..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-atop.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-atop.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-atop.worker.js
index 689152a..8b8d183 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-atop.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-atop.worker.js
@@ -29,12 +29,14 @@
     };
 });
 promise.then(function(response) {
-    ctx2.drawImage(response, 0, 0);
-    ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
-    ctx.fillRect(0, 0, 100, 50);
-    ctx.globalCompositeOperation = 'source-atop';
-    ctx.drawImage(offscreenCanvas2, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 191,255,64,128, "50,25", "191,255,64,128", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx2.drawImage(bitmap, 0, 0);
+        ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+        ctx.fillRect(0, 0, 100, 50);
+        ctx.globalCompositeOperation = 'source-atop';
+        ctx.drawImage(offscreenCanvas2, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 191,255,64,128, "50,25", "191,255,64,128", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-in-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-in-expected.txt
deleted file mode 100644
index 38cb156..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-in-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.composite.canvas.source-in Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-in.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-in.html
index bf69b2dc..73ae7b7 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-in.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-in.html
@@ -33,12 +33,14 @@
     };
 });
 promise.then(function(response) {
-    ctx2.drawImage(response, 0, 0);
-    ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
-    ctx.fillRect(0, 0, 100, 50);
-    ctx.globalCompositeOperation = 'source-in';
-    ctx.drawImage(offscreenCanvas2, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 255,255,0,96, "50,25", "255,255,0,96", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx2.drawImage(bitmap, 0, 0);
+        ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+        ctx.fillRect(0, 0, 100, 50);
+        ctx.globalCompositeOperation = 'source-in';
+        ctx.drawImage(offscreenCanvas2, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 255,255,0,96, "50,25", "255,255,0,96", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-in.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-in.worker-expected.txt
deleted file mode 100644
index 2e526ad..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-in.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-in.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-in.worker.js
index d68068b..a4b9a5b 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-in.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-in.worker.js
@@ -29,12 +29,14 @@
     };
 });
 promise.then(function(response) {
-    ctx2.drawImage(response, 0, 0);
-    ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
-    ctx.fillRect(0, 0, 100, 50);
-    ctx.globalCompositeOperation = 'source-in';
-    ctx.drawImage(offscreenCanvas2, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 255,255,0,96, "50,25", "255,255,0,96", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx2.drawImage(bitmap, 0, 0);
+        ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+        ctx.fillRect(0, 0, 100, 50);
+        ctx.globalCompositeOperation = 'source-in';
+        ctx.drawImage(offscreenCanvas2, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 255,255,0,96, "50,25", "255,255,0,96", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-out-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-out-expected.txt
deleted file mode 100644
index 19f83f3..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-out-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.composite.canvas.source-out Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-out.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-out.html
index 21514d3..5f9f3fa2 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-out.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-out.html
@@ -33,12 +33,14 @@
     };
 });
 promise.then(function(response) {
-    ctx2.drawImage(response, 0, 0);
-    ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
-    ctx.fillRect(0, 0, 100, 50);
-    ctx.globalCompositeOperation = 'source-out';
-    ctx.drawImage(offscreenCanvas2, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 255,255,0,96, "50,25", "255,255,0,96", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx2.drawImage(bitmap, 0, 0);
+        ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+        ctx.fillRect(0, 0, 100, 50);
+        ctx.globalCompositeOperation = 'source-out';
+        ctx.drawImage(offscreenCanvas2, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 255,255,0,96, "50,25", "255,255,0,96", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-out.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-out.worker-expected.txt
deleted file mode 100644
index 2e526ad..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-out.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-out.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-out.worker.js
index f30bcd1..0fff033 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-out.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-out.worker.js
@@ -29,12 +29,14 @@
     };
 });
 promise.then(function(response) {
-    ctx2.drawImage(response, 0, 0);
-    ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
-    ctx.fillRect(0, 0, 100, 50);
-    ctx.globalCompositeOperation = 'source-out';
-    ctx.drawImage(offscreenCanvas2, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 255,255,0,96, "50,25", "255,255,0,96", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx2.drawImage(bitmap, 0, 0);
+        ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+        ctx.fillRect(0, 0, 100, 50);
+        ctx.globalCompositeOperation = 'source-out';
+        ctx.drawImage(offscreenCanvas2, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 255,255,0,96, "50,25", "255,255,0,96", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-over-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-over-expected.txt
deleted file mode 100644
index 7c405d38..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-over-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.composite.canvas.source-over Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-over.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-over.html
index 54810af7..ed42846 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-over.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-over.html
@@ -33,12 +33,14 @@
     };
 });
 promise.then(function(response) {
-    ctx2.drawImage(response, 0, 0);
-    ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
-    ctx.fillRect(0, 0, 100, 50);
-    ctx.globalCompositeOperation = 'source-over';
-    ctx.drawImage(offscreenCanvas2, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 219,255,36,223, "50,25", "219,255,36,223", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx2.drawImage(bitmap, 0, 0);
+        ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+        ctx.fillRect(0, 0, 100, 50);
+        ctx.globalCompositeOperation = 'source-over';
+        ctx.drawImage(offscreenCanvas2, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 219,255,36,223, "50,25", "219,255,36,223", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-over.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-over.worker-expected.txt
deleted file mode 100644
index 2e526ad..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-over.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-over.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-over.worker.js
index 5a90aed..b0bc5c51 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-over.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.source-over.worker.js
@@ -29,12 +29,14 @@
     };
 });
 promise.then(function(response) {
-    ctx2.drawImage(response, 0, 0);
-    ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
-    ctx.fillRect(0, 0, 100, 50);
-    ctx.globalCompositeOperation = 'source-over';
-    ctx.drawImage(offscreenCanvas2, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 219,255,36,223, "50,25", "219,255,36,223", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx2.drawImage(bitmap, 0, 0);
+        ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+        ctx.fillRect(0, 0, 100, 50);
+        ctx.globalCompositeOperation = 'source-over';
+        ctx.drawImage(offscreenCanvas2, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 219,255,36,223, "50,25", "219,255,36,223", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.xor-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.xor-expected.txt
deleted file mode 100644
index b9910a20..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.xor-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.composite.canvas.xor Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.xor.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.xor.html
index 1c5ada7..2df5132 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.xor.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.xor.html
@@ -33,12 +33,14 @@
     };
 });
 promise.then(function(response) {
-    ctx2.drawImage(response, 0, 0);
-    ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
-    ctx.fillRect(0, 0, 100, 50);
-    ctx.globalCompositeOperation = 'xor';
-    ctx.drawImage(offscreenCanvas2, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 191,255,64,128, "50,25", "191,255,64,128", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx2.drawImage(bitmap, 0, 0);
+        ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+        ctx.fillRect(0, 0, 100, 50);
+        ctx.globalCompositeOperation = 'xor';
+        ctx.drawImage(offscreenCanvas2, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 191,255,64,128, "50,25", "191,255,64,128", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.xor.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.xor.worker-expected.txt
deleted file mode 100644
index 2e526ad..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.xor.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.xor.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.xor.worker.js
index 16d1e9be..9fa5505 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.xor.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.canvas.xor.worker.js
@@ -29,12 +29,14 @@
     };
 });
 promise.then(function(response) {
-    ctx2.drawImage(response, 0, 0);
-    ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
-    ctx.fillRect(0, 0, 100, 50);
-    ctx.globalCompositeOperation = 'xor';
-    ctx.drawImage(offscreenCanvas2, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 191,255,64,128, "50,25", "191,255,64,128", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx2.drawImage(bitmap, 0, 0);
+        ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
+        ctx.fillRect(0, 0, 100, 50);
+        ctx.globalCompositeOperation = 'xor';
+        ctx.drawImage(offscreenCanvas2, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 191,255,64,128, "50,25", "191,255,64,128", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.globalAlpha.image-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.globalAlpha.image-expected.txt
deleted file mode 100644
index dad07e34..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.globalAlpha.image-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.composite.globalAlpha.image Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.globalAlpha.image.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.globalAlpha.image.html
index 2f7a5e6..5d586a19 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.globalAlpha.image.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.globalAlpha.image.html
@@ -33,8 +33,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 2,253,0,255, "50,25", "2,253,0,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 2,253,0,255, "50,25", "2,253,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.globalAlpha.image.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.globalAlpha.image.worker-expected.txt
deleted file mode 100644
index 2e526ad..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.globalAlpha.image.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.globalAlpha.image.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.globalAlpha.image.worker.js
index 143d2ee..f645a8e 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.globalAlpha.image.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.globalAlpha.image.worker.js
@@ -29,8 +29,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 2,253,0,255, "50,25", "2,253,0,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 2,253,0,255, "50,25", "2,253,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.globalAlpha.imagepattern-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.globalAlpha.imagepattern-expected.txt
deleted file mode 100644
index db39e6d7..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.globalAlpha.imagepattern-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.composite.globalAlpha.imagepattern Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.globalAlpha.imagepattern.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.globalAlpha.imagepattern.html
index c577b5b..0c77965f 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.globalAlpha.imagepattern.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.globalAlpha.imagepattern.html
@@ -32,10 +32,12 @@
     };
 });
 promise.then(function(response) {
-    ctx.fillStyle = ctx.createPattern(response, 'no-repeat');
-    ctx.globalAlpha = 0.01; // avoid any potential alpha=0 optimisations
-    ctx.fillRect(0, 0, 100, 50);
-    _assertPixelApprox(offscreenCanvas, 50,25, 2,253,0,255, "50,25", "2,253,0,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        ctx.fillStyle = ctx.createPattern(bitmap, 'no-repeat');
+        ctx.globalAlpha = 0.01; // avoid any potential alpha=0 optimisations
+        ctx.fillRect(0, 0, 100, 50);
+        _assertPixelApprox(offscreenCanvas, 50,25, 2,253,0,255, "50,25", "2,253,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.globalAlpha.imagepattern.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.globalAlpha.imagepattern.worker-expected.txt
deleted file mode 100644
index 92bc07d..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.globalAlpha.imagepattern.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.globalAlpha.imagepattern.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.globalAlpha.imagepattern.worker.js
index cc765b4..8441dc2 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.globalAlpha.imagepattern.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.globalAlpha.imagepattern.worker.js
@@ -28,10 +28,12 @@
     };
 });
 promise.then(function(response) {
-    ctx.fillStyle = ctx.createPattern(response, 'no-repeat');
-    ctx.globalAlpha = 0.01; // avoid any potential alpha=0 optimisations
-    ctx.fillRect(0, 0, 100, 50);
-    _assertPixelApprox(offscreenCanvas, 50,25, 2,253,0,255, "50,25", "2,253,0,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        ctx.fillStyle = ctx.createPattern(bitmap, 'no-repeat');
+        ctx.globalAlpha = 0.01; // avoid any potential alpha=0 optimisations
+        ctx.fillRect(0, 0, 100, 50);
+        _assertPixelApprox(offscreenCanvas, 50,25, 2,253,0,255, "50,25", "2,253,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.copy-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.copy-expected.txt
deleted file mode 100644
index cf2e3b6..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.copy-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.composite.image.copy Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.copy.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.copy.html
index 322f6cf..b4944a7 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.copy.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.copy.html
@@ -34,8 +34,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 255,255,0,191, "50,25", "255,255,0,191", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 255,255,0,191, "50,25", "255,255,0,191", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.copy.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.copy.worker-expected.txt
deleted file mode 100644
index 2e526ad..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.copy.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.copy.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.copy.worker.js
index b6f8425..493ee0b 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.copy.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.copy.worker.js
@@ -30,8 +30,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 255,255,0,191, "50,25", "255,255,0,191", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 255,255,0,191, "50,25", "255,255,0,191", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-atop-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-atop-expected.txt
deleted file mode 100644
index 76d1383..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-atop-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.composite.image.destination-atop Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-atop.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-atop.html
index 3c54ac2..79ecfcd 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-atop.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-atop.html
@@ -34,8 +34,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 128,255,128,191, "50,25", "128,255,128,191", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 128,255,128,191, "50,25", "128,255,128,191", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-atop.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-atop.worker-expected.txt
deleted file mode 100644
index 2e526ad..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-atop.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-atop.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-atop.worker.js
index 48f472be..d36ed50 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-atop.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-atop.worker.js
@@ -30,8 +30,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 128,255,128,191, "50,25", "128,255,128,191", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 128,255,128,191, "50,25", "128,255,128,191", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-in-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-in-expected.txt
deleted file mode 100644
index abb41e9..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-in-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.composite.image.destination-in Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-in.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-in.html
index 1edbcdc..ef205f1 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-in.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-in.html
@@ -34,8 +34,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,255,255,96, "50,25", "0,255,255,96", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,255,255,96, "50,25", "0,255,255,96", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-in.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-in.worker-expected.txt
deleted file mode 100644
index 2e526ad..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-in.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-in.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-in.worker.js
index 622b35b..60eb619 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-in.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-in.worker.js
@@ -30,8 +30,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,255,255,96, "50,25", "0,255,255,96", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,255,255,96, "50,25", "0,255,255,96", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-out-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-out-expected.txt
deleted file mode 100644
index 64d16e58..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-out-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.composite.image.destination-out Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-out.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-out.html
index e1d0cbad..ecfeeed 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-out.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-out.html
@@ -34,8 +34,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,255,255,32, "50,25", "0,255,255,32", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,255,255,32, "50,25", "0,255,255,32", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-out.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-out.worker-expected.txt
deleted file mode 100644
index 2e526ad..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-out.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-out.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-out.worker.js
index 52c142ef..ee97dcc 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-out.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-out.worker.js
@@ -30,8 +30,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,255,255,32, "50,25", "0,255,255,32", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,255,255,32, "50,25", "0,255,255,32", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-over-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-over-expected.txt
deleted file mode 100644
index 728700c..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-over-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.composite.image.destination-over Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-over.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-over.html
index c591a65..eb58b315 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-over.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-over.html
@@ -34,8 +34,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 109,255,146,223, "50,25", "109,255,146,223", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 109,255,146,223, "50,25", "109,255,146,223", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-over.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-over.worker-expected.txt
deleted file mode 100644
index 2e526ad..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-over.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-over.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-over.worker.js
index fb200df3..274c32c 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-over.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.destination-over.worker.js
@@ -30,8 +30,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 109,255,146,223, "50,25", "109,255,146,223", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 109,255,146,223, "50,25", "109,255,146,223", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.lighter-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.lighter-expected.txt
deleted file mode 100644
index fc6a57e0..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.lighter-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.composite.image.lighter Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.lighter.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.lighter.html
index 4ba8cf7..4935ca3c 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.lighter.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.lighter.html
@@ -34,8 +34,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 191,255,128,255, "50,25", "191,255,128,255", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 191,255,128,255, "50,25", "191,255,128,255", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.lighter.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.lighter.worker-expected.txt
deleted file mode 100644
index 2e526ad..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.lighter.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.lighter.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.lighter.worker.js
index 212446e..05e39f9 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.lighter.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.lighter.worker.js
@@ -30,8 +30,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 191,255,128,255, "50,25", "191,255,128,255", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 191,255,128,255, "50,25", "191,255,128,255", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-atop-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-atop-expected.txt
deleted file mode 100644
index 6bf094a..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-atop-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.composite.image.source-atop Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-atop.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-atop.html
index 15e057db..93a527d 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-atop.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-atop.html
@@ -34,8 +34,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 191,255,64,128, "50,25", "191,255,64,128", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 191,255,64,128, "50,25", "191,255,64,128", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-atop.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-atop.worker-expected.txt
deleted file mode 100644
index 2e526ad..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-atop.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-atop.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-atop.worker.js
index 82ca5794..5fddfcfd 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-atop.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-atop.worker.js
@@ -30,8 +30,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 191,255,64,128, "50,25", "191,255,64,128", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 191,255,64,128, "50,25", "191,255,64,128", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-in-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-in-expected.txt
deleted file mode 100644
index c0e437e..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-in-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.composite.image.source-in Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-in.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-in.html
index b8190d36..2519fe7 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-in.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-in.html
@@ -34,8 +34,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 255,255,0,96, "50,25", "255,255,0,96", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 255,255,0,96, "50,25", "255,255,0,96", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-in.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-in.worker-expected.txt
deleted file mode 100644
index 2e526ad..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-in.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-in.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-in.worker.js
index 9169f5798..a5ca8d9 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-in.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-in.worker.js
@@ -30,8 +30,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 255,255,0,96, "50,25", "255,255,0,96", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 255,255,0,96, "50,25", "255,255,0,96", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-out-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-out-expected.txt
deleted file mode 100644
index 43eef64..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-out-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.composite.image.source-out Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-out.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-out.html
index 7a305ba..f57393b 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-out.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-out.html
@@ -34,8 +34,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 255,255,0,96, "50,25", "255,255,0,96", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 255,255,0,96, "50,25", "255,255,0,96", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-out.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-out.worker-expected.txt
deleted file mode 100644
index 2e526ad..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-out.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-out.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-out.worker.js
index 56cc13e6..1038ec5 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-out.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-out.worker.js
@@ -30,8 +30,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 255,255,0,96, "50,25", "255,255,0,96", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 255,255,0,96, "50,25", "255,255,0,96", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-over-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-over-expected.txt
deleted file mode 100644
index e97acb4d..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-over-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.composite.image.source-over Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-over.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-over.html
index 5a3a676..96c5252 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-over.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-over.html
@@ -34,8 +34,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 219,255,36,223, "50,25", "219,255,36,223", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 219,255,36,223, "50,25", "219,255,36,223", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-over.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-over.worker-expected.txt
deleted file mode 100644
index 2e526ad..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-over.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-over.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-over.worker.js
index 210af01..55c0f0eb 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-over.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.source-over.worker.js
@@ -30,8 +30,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 219,255,36,223, "50,25", "219,255,36,223", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 219,255,36,223, "50,25", "219,255,36,223", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.xor-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.xor-expected.txt
deleted file mode 100644
index 00f8c92e..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.xor-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.composite.image.xor Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.xor.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.xor.html
index b40cda8..6039275 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.xor.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.xor.html
@@ -34,8 +34,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 191,255,64,128, "50,25", "191,255,64,128", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 191,255,64,128, "50,25", "191,255,64,128", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.xor.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.xor.worker-expected.txt
deleted file mode 100644
index 2e526ad..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.xor.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.xor.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.xor.worker.js
index ed6b7df..c4e20ffe 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.xor.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.image.xor.worker.js
@@ -30,8 +30,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 191,255,64,128, "50,25", "191,255,64,128", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 191,255,64,128, "50,25", "191,255,64,128", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.copy-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.copy-expected.txt
deleted file mode 100644
index 79b063c..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.copy-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged. Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.copy.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.copy.html
index ae9196b..1e1cab6 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.copy.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.copy.html
@@ -34,9 +34,11 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 40, 40, 10, 10, 40, 50, 10, 10);
-    _assertPixelApprox(offscreenCanvas, 15,15, 0,0,0,0, "15,15", "0,0,0,0", 5);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,0,0,0, "50,25", "0,0,0,0", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 40, 40, 10, 10, 40, 50, 10, 10);
+        _assertPixelApprox(offscreenCanvas, 15,15, 0,0,0,0, "15,15", "0,0,0,0", 5);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,0,0,0, "50,25", "0,0,0,0", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.copy.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.copy.worker-expected.txt
deleted file mode 100644
index 79b063c..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.copy.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged. Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.copy.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.copy.worker.js
index 50f3c82..7019270 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.copy.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.copy.worker.js
@@ -30,9 +30,11 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 40, 40, 10, 10, 40, 50, 10, 10);
-    _assertPixelApprox(offscreenCanvas, 15,15, 0,0,0,0, "15,15", "0,0,0,0", 5);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,0,0,0, "50,25", "0,0,0,0", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 40, 40, 10, 10, 40, 50, 10, 10);
+        _assertPixelApprox(offscreenCanvas, 15,15, 0,0,0,0, "15,15", "0,0,0,0", 5);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,0,0,0, "50,25", "0,0,0,0", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.destination-atop-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.destination-atop-expected.txt
deleted file mode 100644
index 79b063c..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.destination-atop-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged. Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.destination-atop.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.destination-atop.html
index a1d9c33..bc6ac3e 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.destination-atop.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.destination-atop.html
@@ -34,9 +34,11 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 40, 40, 10, 10, 40, 50, 10, 10);
-    _assertPixelApprox(offscreenCanvas, 15,15, 0,0,0,0, "15,15", "0,0,0,0", 5);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,0,0,0, "50,25", "0,0,0,0", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 40, 40, 10, 10, 40, 50, 10, 10);
+        _assertPixelApprox(offscreenCanvas, 15,15, 0,0,0,0, "15,15", "0,0,0,0", 5);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,0,0,0, "50,25", "0,0,0,0", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.destination-atop.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.destination-atop.worker-expected.txt
deleted file mode 100644
index 79b063c..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.destination-atop.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged. Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.destination-atop.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.destination-atop.worker.js
index 93bcd94..aaae198 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.destination-atop.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.destination-atop.worker.js
@@ -30,9 +30,11 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 40, 40, 10, 10, 40, 50, 10, 10);
-    _assertPixelApprox(offscreenCanvas, 15,15, 0,0,0,0, "15,15", "0,0,0,0", 5);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,0,0,0, "50,25", "0,0,0,0", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 40, 40, 10, 10, 40, 50, 10, 10);
+        _assertPixelApprox(offscreenCanvas, 15,15, 0,0,0,0, "15,15", "0,0,0,0", 5);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,0,0,0, "50,25", "0,0,0,0", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.destination-in-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.destination-in-expected.txt
deleted file mode 100644
index 79b063c..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.destination-in-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged. Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.destination-in.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.destination-in.html
index 2268f93..3410aa42 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.destination-in.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.destination-in.html
@@ -34,9 +34,11 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 40, 40, 10, 10, 40, 50, 10, 10);
-    _assertPixelApprox(offscreenCanvas, 15,15, 0,0,0,0, "15,15", "0,0,0,0", 5);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,0,0,0, "50,25", "0,0,0,0", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 40, 40, 10, 10, 40, 50, 10, 10);
+        _assertPixelApprox(offscreenCanvas, 15,15, 0,0,0,0, "15,15", "0,0,0,0", 5);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,0,0,0, "50,25", "0,0,0,0", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.destination-in.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.destination-in.worker-expected.txt
deleted file mode 100644
index 79b063c..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.destination-in.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged. Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.destination-in.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.destination-in.worker.js
index 7bf112c..140e21d 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.destination-in.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.destination-in.worker.js
@@ -30,9 +30,11 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 40, 40, 10, 10, 40, 50, 10, 10);
-    _assertPixelApprox(offscreenCanvas, 15,15, 0,0,0,0, "15,15", "0,0,0,0", 5);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,0,0,0, "50,25", "0,0,0,0", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 40, 40, 10, 10, 40, 50, 10, 10);
+        _assertPixelApprox(offscreenCanvas, 15,15, 0,0,0,0, "15,15", "0,0,0,0", 5);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,0,0,0, "50,25", "0,0,0,0", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.source-in-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.source-in-expected.txt
deleted file mode 100644
index 79b063c..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.source-in-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged. Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.source-in.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.source-in.html
index 8f54423..979f0aa 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.source-in.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.source-in.html
@@ -34,9 +34,11 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 40, 40, 10, 10, 40, 50, 10, 10);
-    _assertPixelApprox(offscreenCanvas, 15,15, 0,0,0,0, "15,15", "0,0,0,0", 5);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,0,0,0, "50,25", "0,0,0,0", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 40, 40, 10, 10, 40, 50, 10, 10);
+        _assertPixelApprox(offscreenCanvas, 15,15, 0,0,0,0, "15,15", "0,0,0,0", 5);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,0,0,0, "50,25", "0,0,0,0", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.source-in.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.source-in.worker-expected.txt
deleted file mode 100644
index 79b063c..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.source-in.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged. Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.source-in.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.source-in.worker.js
index 600bc2f..4a91470 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.source-in.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.source-in.worker.js
@@ -30,9 +30,11 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 40, 40, 10, 10, 40, 50, 10, 10);
-    _assertPixelApprox(offscreenCanvas, 15,15, 0,0,0,0, "15,15", "0,0,0,0", 5);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,0,0,0, "50,25", "0,0,0,0", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 40, 40, 10, 10, 40, 50, 10, 10);
+        _assertPixelApprox(offscreenCanvas, 15,15, 0,0,0,0, "15,15", "0,0,0,0", 5);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,0,0,0, "50,25", "0,0,0,0", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.source-out-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.source-out-expected.txt
deleted file mode 100644
index 79b063c..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.source-out-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged. Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.source-out.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.source-out.html
index d15616d..ce72032 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.source-out.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.source-out.html
@@ -34,9 +34,11 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 40, 40, 10, 10, 40, 50, 10, 10);
-    _assertPixelApprox(offscreenCanvas, 15,15, 0,0,0,0, "15,15", "0,0,0,0", 5);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,0,0,0, "50,25", "0,0,0,0", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 40, 40, 10, 10, 40, 50, 10, 10);
+        _assertPixelApprox(offscreenCanvas, 15,15, 0,0,0,0, "15,15", "0,0,0,0", 5);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,0,0,0, "50,25", "0,0,0,0", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.source-out.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.source-out.worker-expected.txt
deleted file mode 100644
index 79b063c..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.source-out.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged. Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.source-out.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.source-out.worker.js
index d027bf3..4c054cb 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.source-out.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.image.source-out.worker.js
@@ -30,9 +30,11 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 40, 40, 10, 10, 40, 50, 10, 10);
-    _assertPixelApprox(offscreenCanvas, 15,15, 0,0,0,0, "15,15", "0,0,0,0", 5);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,0,0,0, "50,25", "0,0,0,0", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 40, 40, 10, 10, 40, 50, 10, 10);
+        _assertPixelApprox(offscreenCanvas, 15,15, 0,0,0,0, "15,15", "0,0,0,0", 5);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,0,0,0, "50,25", "0,0,0,0", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.copy-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.copy-expected.txt
deleted file mode 100644
index 9228e4f..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.copy-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged. Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.copy.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.copy.html
index 16d6948..84a53887 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.copy.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.copy.html
@@ -34,9 +34,11 @@
     };
 });
 promise.then(function(response) {
-    ctx.fillStyle = ctx.createPattern(response, 'no-repeat');
-    ctx.fillRect(0, 50, 100, 50);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,0,0,0, "50,25", "0,0,0,0", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx.fillStyle = ctx.createPattern(bitmap, 'no-repeat');
+        ctx.fillRect(0, 50, 100, 50);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,0,0,0, "50,25", "0,0,0,0", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.copy.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.copy.worker-expected.txt
deleted file mode 100644
index 9228e4f..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.copy.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged. Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.copy.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.copy.worker.js
index 5e2d659..b8a2e203 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.copy.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.copy.worker.js
@@ -30,9 +30,11 @@
     };
 });
 promise.then(function(response) {
-    ctx.fillStyle = ctx.createPattern(response, 'no-repeat');
-    ctx.fillRect(0, 50, 100, 50);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,0,0,0, "50,25", "0,0,0,0", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx.fillStyle = ctx.createPattern(bitmap, 'no-repeat');
+        ctx.fillRect(0, 50, 100, 50);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,0,0,0, "50,25", "0,0,0,0", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.destination-atop-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.destination-atop-expected.txt
deleted file mode 100644
index 9228e4f..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.destination-atop-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged. Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.destination-atop.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.destination-atop.html
index 9f5b8e8..c7c20bb 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.destination-atop.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.destination-atop.html
@@ -34,9 +34,11 @@
     };
 });
 promise.then(function(response) {
-    ctx.fillStyle = ctx.createPattern(response, 'no-repeat');
-    ctx.fillRect(0, 50, 100, 50);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,0,0,0, "50,25", "0,0,0,0", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx.fillStyle = ctx.createPattern(bitmap, 'no-repeat');
+        ctx.fillRect(0, 50, 100, 50);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,0,0,0, "50,25", "0,0,0,0", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.destination-atop.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.destination-atop.worker-expected.txt
deleted file mode 100644
index 9228e4f..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.destination-atop.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged. Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.destination-atop.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.destination-atop.worker.js
index 7c4fb96..a1a2eb85 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.destination-atop.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.destination-atop.worker.js
@@ -30,9 +30,11 @@
     };
 });
 promise.then(function(response) {
-    ctx.fillStyle = ctx.createPattern(response, 'no-repeat');
-    ctx.fillRect(0, 50, 100, 50);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,0,0,0, "50,25", "0,0,0,0", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx.fillStyle = ctx.createPattern(bitmap, 'no-repeat');
+        ctx.fillRect(0, 50, 100, 50);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,0,0,0, "50,25", "0,0,0,0", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.destination-in-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.destination-in-expected.txt
deleted file mode 100644
index 9228e4f..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.destination-in-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged. Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.destination-in.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.destination-in.html
index 6c08dd1..5058c75 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.destination-in.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.destination-in.html
@@ -34,9 +34,11 @@
     };
 });
 promise.then(function(response) {
-    ctx.fillStyle = ctx.createPattern(response, 'no-repeat');
-    ctx.fillRect(0, 50, 100, 50);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,0,0,0, "50,25", "0,0,0,0", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx.fillStyle = ctx.createPattern(bitmap, 'no-repeat');
+        ctx.fillRect(0, 50, 100, 50);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,0,0,0, "50,25", "0,0,0,0", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.destination-in.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.destination-in.worker-expected.txt
deleted file mode 100644
index 9228e4f..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.destination-in.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged. Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.destination-in.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.destination-in.worker.js
index 006268d..2ab713b9 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.destination-in.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.destination-in.worker.js
@@ -30,9 +30,11 @@
     };
 });
 promise.then(function(response) {
-    ctx.fillStyle = ctx.createPattern(response, 'no-repeat');
-    ctx.fillRect(0, 50, 100, 50);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,0,0,0, "50,25", "0,0,0,0", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx.fillStyle = ctx.createPattern(bitmap, 'no-repeat');
+        ctx.fillRect(0, 50, 100, 50);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,0,0,0, "50,25", "0,0,0,0", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.source-in-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.source-in-expected.txt
deleted file mode 100644
index 9228e4f..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.source-in-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged. Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.source-in.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.source-in.html
index 3cf0ac91..4beb3fb 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.source-in.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.source-in.html
@@ -34,9 +34,11 @@
     };
 });
 promise.then(function(response) {
-    ctx.fillStyle = ctx.createPattern(response, 'no-repeat');
-    ctx.fillRect(0, 50, 100, 50);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,0,0,0, "50,25", "0,0,0,0", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx.fillStyle = ctx.createPattern(bitmap, 'no-repeat');
+        ctx.fillRect(0, 50, 100, 50);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,0,0,0, "50,25", "0,0,0,0", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.source-in.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.source-in.worker-expected.txt
deleted file mode 100644
index 9228e4f..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.source-in.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged. Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.source-in.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.source-in.worker.js
index 1472e20..c4e6c85c 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.source-in.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.source-in.worker.js
@@ -30,9 +30,11 @@
     };
 });
 promise.then(function(response) {
-    ctx.fillStyle = ctx.createPattern(response, 'no-repeat');
-    ctx.fillRect(0, 50, 100, 50);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,0,0,0, "50,25", "0,0,0,0", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx.fillStyle = ctx.createPattern(bitmap, 'no-repeat');
+        ctx.fillRect(0, 50, 100, 50);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,0,0,0, "50,25", "0,0,0,0", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.source-out-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.source-out-expected.txt
deleted file mode 100644
index 9228e4f..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.source-out-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged. Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.source-out.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.source-out.html
index 7b8dd8a..907c77a 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.source-out.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.source-out.html
@@ -34,9 +34,11 @@
     };
 });
 promise.then(function(response) {
-    ctx.fillStyle = ctx.createPattern(response, 'no-repeat');
-    ctx.fillRect(0, 50, 100, 50);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,0,0,0, "50,25", "0,0,0,0", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx.fillStyle = ctx.createPattern(bitmap, 'no-repeat');
+        ctx.fillRect(0, 50, 100, 50);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,0,0,0, "50,25", "0,0,0,0", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.source-out.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.source-out.worker-expected.txt
deleted file mode 100644
index 9228e4f..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.source-out.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged. Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.source-out.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.source-out.worker.js
index c9f71f2..6abb66a 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.source-out.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/compositing/2d.composite.uncovered.pattern.source-out.worker.js
@@ -30,9 +30,11 @@
     };
 });
 promise.then(function(response) {
-    ctx.fillStyle = ctx.createPattern(response, 'no-repeat');
-    ctx.fillRect(0, 50, 100, 50);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,0,0,0, "50,25", "0,0,0,0", 5);
+    createImageBitmap(response).then(bitmap => {
+        ctx.fillStyle = ctx.createPattern(bitmap, 'no-repeat');
+        ctx.fillRect(0, 50, 100, 50);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,0,0,0, "50,25", "0,0,0,0", 5);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.3arg-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.3arg-expected.txt
deleted file mode 100644
index 3d26a8d..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.3arg-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.drawImage.3arg Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.3arg.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.3arg.html
index 44f33cf..f3ede9a 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.3arg.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.3arg.html
@@ -40,15 +40,19 @@
     };
 });
 Promise.all([promise1, promise2]).then(function(response1, response2) {
-    ctx.drawImage(response2, 0, 0);
-    ctx.drawImage(response1, -100, 0);
-    ctx.drawImage(response1, 100, 0);
-    ctx.drawImage(response1, 0, -50);
-    ctx.drawImage(response1, 0, 50);
-    _assertPixelApprox(offscreenCanvas, 0,0, 0,255,0,255, "0,0", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 99,0, 0,255,0,255, "99,0", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 0,49, 0,255,0,255, "0,49", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 99,49, 0,255,0,255, "99,49", "0,255,0,255", 2);
+    var promise3 = createImageBitmap(response1);
+    var promise4 = createImageBitmap(response2);
+    Promise.all([promise3, promise4]).then(function(bitmap1, bitmap2) {
+        ctx.drawImage(bitmap2, 0, 0);
+        ctx.drawImage(bitmap1, -100, 0);
+        ctx.drawImage(bitmap1, 100, 0);
+        ctx.drawImage(bitmap1, 0, -50);
+        ctx.drawImage(bitmap1, 0, 50);
+        _assertPixelApprox(offscreenCanvas, 0,0, 0,255,0,255, "0,0", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 99,0, 0,255,0,255, "99,0", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 0,49, 0,255,0,255, "0,49", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 99,49, 0,255,0,255, "99,49", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.3arg.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.3arg.worker-expected.txt
deleted file mode 100644
index 2e526ad..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.3arg.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.3arg.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.3arg.worker.js
index a600e2f2..6be6bdf 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.3arg.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.3arg.worker.js
@@ -35,15 +35,19 @@
     };
 });
 Promise.all([promise1, promise2]).then(function(response1, response2) {
-    ctx.drawImage(response2, 0, 0);
-    ctx.drawImage(response1, -100, 0);
-    ctx.drawImage(response1, 100, 0);
-    ctx.drawImage(response1, 0, -50);
-    ctx.drawImage(response1, 0, 50);
-    _assertPixelApprox(offscreenCanvas, 0,0, 0,255,0,255, "0,0", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 99,0, 0,255,0,255, "99,0", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 0,49, 0,255,0,255, "0,49", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 99,49, 0,255,0,255, "99,49", "0,255,0,255", 2);
+    var promise3 = createImageBitmap(response1);
+    var promise4 = createImageBitmap(response2);
+    Promise.all([promise3, promise4]).then(function(bitmap1, bitmap2) {
+        ctx.drawImage(bitmap2, 0, 0);
+        ctx.drawImage(bitmap1, -100, 0);
+        ctx.drawImage(bitmap1, 100, 0);
+        ctx.drawImage(bitmap1, 0, -50);
+        ctx.drawImage(bitmap1, 0, 50);
+        _assertPixelApprox(offscreenCanvas, 0,0, 0,255,0,255, "0,0", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 99,0, 0,255,0,255, "99,0", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 0,49, 0,255,0,255, "0,49", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 99,49, 0,255,0,255, "99,49", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.5arg-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.5arg-expected.txt
deleted file mode 100644
index 377c9e8..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.5arg-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.drawImage.5arg Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.5arg.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.5arg.html
index d82aa2d..7671f3a 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.5arg.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.5arg.html
@@ -42,14 +42,18 @@
     };
 });
 Promise.all([promise1, promise2]).then(function(response1, response2) {
-    ctx.drawImage(response2, 50, 0, 50, 50);
-    ctx.drawImage(response1, 0, 0, 50, 50);
-    ctx.fillStyle = '#0f0';
-    ctx.fillRect(0, 0, 50, 50);
-    _assertPixelApprox(offscreenCanvas, 0,0, 0,255,0,255, "0,0", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 99,0, 0,255,0,255, "99,0", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 0,49, 0,255,0,255, "0,49", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 99,49, 0,255,0,255, "99,49", "0,255,0,255", 2);
+    var promise3 = createImageBitmap(response1);
+    var promise4 = createImageBitmap(response2);
+    Promise.all([promise3, promise4]).then(function(bitmap1, bitmap2) {
+        ctx.drawImage(bitmap2, 50, 0, 50, 50);
+        ctx.drawImage(bitmap1, 0, 0, 50, 50);
+        ctx.fillStyle = '#0f0';
+        ctx.fillRect(0, 0, 50, 50);
+        _assertPixelApprox(offscreenCanvas, 0,0, 0,255,0,255, "0,0", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 99,0, 0,255,0,255, "99,0", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 0,49, 0,255,0,255, "0,49", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 99,49, 0,255,0,255, "99,49", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.5arg.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.5arg.worker-expected.txt
deleted file mode 100644
index 2e526ad..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.5arg.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.5arg.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.5arg.worker.js
index 6fafc4e..5917615 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.5arg.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.5arg.worker.js
@@ -37,14 +37,18 @@
     };
 });
 Promise.all([promise1, promise2]).then(function(response1, response2) {
-    ctx.drawImage(response2, 50, 0, 50, 50);
-    ctx.drawImage(response1, 0, 0, 50, 50);
-    ctx.fillStyle = '#0f0';
-    ctx.fillRect(0, 0, 50, 50);
-    _assertPixelApprox(offscreenCanvas, 0,0, 0,255,0,255, "0,0", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 99,0, 0,255,0,255, "99,0", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 0,49, 0,255,0,255, "0,49", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 99,49, 0,255,0,255, "99,49", "0,255,0,255", 2);
+    var promise3 = createImageBitmap(response1);
+    var promise4 = createImageBitmap(response2);
+    Promise.all([promise3, promise4]).then(function(bitmap1, bitmap2) {
+        ctx.drawImage(bitmap2, 50, 0, 50, 50);
+        ctx.drawImage(bitmap1, 0, 0, 50, 50);
+        ctx.fillStyle = '#0f0';
+        ctx.fillRect(0, 0, 50, 50);
+        _assertPixelApprox(offscreenCanvas, 0,0, 0,255,0,255, "0,0", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 99,0, 0,255,0,255, "99,0", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 0,49, 0,255,0,255, "0,49", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 99,49, 0,255,0,255, "99,49", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.basic-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.basic-expected.txt
deleted file mode 100644
index a85be1a..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.basic-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.drawImage.9arg.basic Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.basic.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.basic.html
index 44da5d5..829fc6b 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.basic.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.basic.html
@@ -33,11 +33,13 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0, 100, 50, 0, 0, 100, 50);
-    _assertPixelApprox(offscreenCanvas, 0,0, 0,255,0,255, "0,0", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 99,0, 0,255,0,255, "99,0", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 0,49, 0,255,0,255, "0,49", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 99,49, 0,255,0,255, "99,49", "0,255,0,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, 100, 50);
+        _assertPixelApprox(offscreenCanvas, 0,0, 0,255,0,255, "0,0", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 99,0, 0,255,0,255, "99,0", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 0,49, 0,255,0,255, "0,49", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 99,49, 0,255,0,255, "99,49", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.basic.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.basic.worker-expected.txt
deleted file mode 100644
index 2e526ad..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.basic.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.basic.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.basic.worker.js
index 697449f..afa730b 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.basic.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.basic.worker.js
@@ -28,11 +28,13 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0, 100, 50, 0, 0, 100, 50);
-    _assertPixelApprox(offscreenCanvas, 0,0, 0,255,0,255, "0,0", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 99,0, 0,255,0,255, "99,0", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 0,49, 0,255,0,255, "0,49", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 99,49, 0,255,0,255, "99,49", "0,255,0,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, 100, 50);
+        _assertPixelApprox(offscreenCanvas, 0,0, 0,255,0,255, "0,0", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 99,0, 0,255,0,255, "99,0", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 0,49, 0,255,0,255, "0,49", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 99,49, 0,255,0,255, "99,49", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.destpos-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.destpos-expected.txt
deleted file mode 100644
index 9584dc3a..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.destpos-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.drawImage.9arg.destpos Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.destpos.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.destpos.html
index aad38fe..a01ace1 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.destpos.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.destpos.html
@@ -42,15 +42,19 @@
     };
 });
 Promise.all([promise1, promise2]).then(function(response1, response2) {
-    ctx.drawImage(response2, 0, 0, 100, 50, 0, 0, 100, 50);
-    ctx.drawImage(response1, 0, 0, 100, 50, -100, 0, 100, 50);
-    ctx.drawImage(response1, 0, 0, 100, 50, 100, 0, 100, 50);
-    ctx.drawImage(response1, 0, 0, 100, 50, 0, -50, 100, 50);
-    ctx.drawImage(response1, 0, 0, 100, 50, 0, 50, 100, 50);
-    _assertPixelApprox(offscreenCanvas, 0,0, 0,255,0,255, "0,0", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 99,0, 0,255,0,255, "99,0", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 0,49, 0,255,0,255, "0,49", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 99,49, 0,255,0,255, "99,49", "0,255,0,255", 2);
+    var promise3 = createImageBitmap(response1);
+    var promise4 = createImageBitmap(response2);
+    Promise.all([promise3, promise4]).then(function(bitmap1, bitmap2) {
+        ctx.drawImage(bitmap2, 0, 0, 100, 50, 0, 0, 100, 50);
+        ctx.drawImage(bitmap1, 0, 0, 100, 50, -100, 0, 100, 50);
+        ctx.drawImage(bitmap1, 0, 0, 100, 50, 100, 0, 100, 50);
+        ctx.drawImage(bitmap1, 0, 0, 100, 50, 0, -50, 100, 50);
+        ctx.drawImage(bitmap1, 0, 0, 100, 50, 0, 50, 100, 50);
+        _assertPixelApprox(offscreenCanvas, 0,0, 0,255,0,255, "0,0", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 99,0, 0,255,0,255, "99,0", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 0,49, 0,255,0,255, "0,49", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 99,49, 0,255,0,255, "99,49", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.destpos.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.destpos.worker-expected.txt
deleted file mode 100644
index 2e526ad..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.destpos.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.destpos.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.destpos.worker.js
index 19f4bbcb..687f024 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.destpos.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.destpos.worker.js
@@ -37,15 +37,19 @@
     };
 });
 Promise.all([promise1, promise2]).then(function(response1, response2) {
-    ctx.drawImage(response2, 0, 0, 100, 50, 0, 0, 100, 50);
-    ctx.drawImage(response1, 0, 0, 100, 50, -100, 0, 100, 50);
-    ctx.drawImage(response1, 0, 0, 100, 50, 100, 0, 100, 50);
-    ctx.drawImage(response1, 0, 0, 100, 50, 0, -50, 100, 50);
-    ctx.drawImage(response1, 0, 0, 100, 50, 0, 50, 100, 50);
-    _assertPixelApprox(offscreenCanvas, 0,0, 0,255,0,255, "0,0", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 99,0, 0,255,0,255, "99,0", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 0,49, 0,255,0,255, "0,49", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 99,49, 0,255,0,255, "99,49", "0,255,0,255", 2);
+    var promise3 = createImageBitmap(response1);
+    var promise4 = createImageBitmap(response2);
+    Promise.all([promise3, promise4]).then(function(bitmap1, bitmap2) {
+        ctx.drawImage(bitmap2, 0, 0, 100, 50, 0, 0, 100, 50);
+        ctx.drawImage(bitmap1, 0, 0, 100, 50, -100, 0, 100, 50);
+        ctx.drawImage(bitmap1, 0, 0, 100, 50, 100, 0, 100, 50);
+        ctx.drawImage(bitmap1, 0, 0, 100, 50, 0, -50, 100, 50);
+        ctx.drawImage(bitmap1, 0, 0, 100, 50, 0, 50, 100, 50);
+        _assertPixelApprox(offscreenCanvas, 0,0, 0,255,0,255, "0,0", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 99,0, 0,255,0,255, "99,0", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 0,49, 0,255,0,255, "0,49", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 99,49, 0,255,0,255, "99,49", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.destsize-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.destsize-expected.txt
deleted file mode 100644
index f9894d2..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.destsize-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.drawImage.9arg.destsize Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.destsize.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.destsize.html
index ce7f835..ddea385 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.destsize.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.destsize.html
@@ -42,15 +42,19 @@
     };
 });
 Promise.all([promise1, promise2]).then(function(response1, response2) {
-    ctx.drawImage(response2, 1, 1, 1, 1, 0, 0, 100, 50);
-    ctx.drawImage(response1, 0, 0, 100, 50, -50, 0, 50, 50);
-    ctx.drawImage(response1, 0, 0, 100, 50, 100, 0, 50, 50);
-    ctx.drawImage(response1, 0, 0, 100, 50, 0, -25, 100, 25);
-    ctx.drawImage(response1, 0, 0, 100, 50, 0, 50, 100, 25);
-    _assertPixelApprox(offscreenCanvas, 0,0, 0,255,0,255, "0,0", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 99,0, 0,255,0,255, "99,0", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 0,49, 0,255,0,255, "0,49", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 99,49, 0,255,0,255, "99,49", "0,255,0,255", 2);
+    var promise3 = createImageBitmap(response1);
+    var promise4 = createImageBitmap(response2);
+    Promise.all([promise3, promise4]).then(function(bitmap1, bitmap2) {
+        ctx.drawImage(bitmap2, 1, 1, 1, 1, 0, 0, 100, 50);
+        ctx.drawImage(bitmap1, 0, 0, 100, 50, -50, 0, 50, 50);
+        ctx.drawImage(bitmap1, 0, 0, 100, 50, 100, 0, 50, 50);
+        ctx.drawImage(bitmap1, 0, 0, 100, 50, 0, -25, 100, 25);
+        ctx.drawImage(bitmap1, 0, 0, 100, 50, 0, 50, 100, 25);
+        _assertPixelApprox(offscreenCanvas, 0,0, 0,255,0,255, "0,0", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 99,0, 0,255,0,255, "99,0", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 0,49, 0,255,0,255, "0,49", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 99,49, 0,255,0,255, "99,49", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.destsize.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.destsize.worker-expected.txt
deleted file mode 100644
index 2e526ad..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.destsize.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.destsize.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.destsize.worker.js
index 3b85461f..7f586ef0 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.destsize.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.destsize.worker.js
@@ -37,15 +37,19 @@
     };
 });
 Promise.all([promise1, promise2]).then(function(response1, response2) {
-    ctx.drawImage(response2, 1, 1, 1, 1, 0, 0, 100, 50);
-    ctx.drawImage(response1, 0, 0, 100, 50, -50, 0, 50, 50);
-    ctx.drawImage(response1, 0, 0, 100, 50, 100, 0, 50, 50);
-    ctx.drawImage(response1, 0, 0, 100, 50, 0, -25, 100, 25);
-    ctx.drawImage(response1, 0, 0, 100, 50, 0, 50, 100, 25);
-    _assertPixelApprox(offscreenCanvas, 0,0, 0,255,0,255, "0,0", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 99,0, 0,255,0,255, "99,0", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 0,49, 0,255,0,255, "0,49", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 99,49, 0,255,0,255, "99,49", "0,255,0,255", 2);
+    var promise3 = createImageBitmap(response1);
+    var promise4 = createImageBitmap(response2);
+    Promise.all([promise3, promise4]).then(function(bitmap1, bitmap2) {
+        ctx.drawImage(bitmap2, 1, 1, 1, 1, 0, 0, 100, 50);
+        ctx.drawImage(bitmap1, 0, 0, 100, 50, -50, 0, 50, 50);
+        ctx.drawImage(bitmap1, 0, 0, 100, 50, 100, 0, 50, 50);
+        ctx.drawImage(bitmap1, 0, 0, 100, 50, 0, -25, 100, 25);
+        ctx.drawImage(bitmap1, 0, 0, 100, 50, 0, 50, 100, 25);
+        _assertPixelApprox(offscreenCanvas, 0,0, 0,255,0,255, "0,0", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 99,0, 0,255,0,255, "99,0", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 0,49, 0,255,0,255, "0,49", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 99,49, 0,255,0,255, "99,49", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcepos-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcepos-expected.txt
deleted file mode 100644
index 87d0d713..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcepos-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.drawImage.9arg.sourcepos Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcepos.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcepos.html
index c18a47ff..6541d76 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcepos.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcepos.html
@@ -33,11 +33,13 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 140, 20, 100, 50, 0, 0, 100, 50);
-    _assertPixelApprox(offscreenCanvas, 0,0, 0,255,0,255, "0,0", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 99,0, 0,255,0,255, "99,0", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 0,49, 0,255,0,255, "0,49", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 99,49, 0,255,0,255, "99,49", "0,255,0,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 140, 20, 100, 50, 0, 0, 100, 50);
+        _assertPixelApprox(offscreenCanvas, 0,0, 0,255,0,255, "0,0", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 99,0, 0,255,0,255, "99,0", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 0,49, 0,255,0,255, "0,49", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 99,49, 0,255,0,255, "99,49", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcepos.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcepos.worker-expected.txt
deleted file mode 100644
index 2e526ad..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcepos.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcepos.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcepos.worker.js
index 8525ee9..7cefad9 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcepos.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcepos.worker.js
@@ -28,11 +28,13 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 140, 20, 100, 50, 0, 0, 100, 50);
-    _assertPixelApprox(offscreenCanvas, 0,0, 0,255,0,255, "0,0", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 99,0, 0,255,0,255, "99,0", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 0,49, 0,255,0,255, "0,49", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 99,49, 0,255,0,255, "99,49", "0,255,0,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 140, 20, 100, 50, 0, 0, 100, 50);
+        _assertPixelApprox(offscreenCanvas, 0,0, 0,255,0,255, "0,0", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 99,0, 0,255,0,255, "99,0", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 0,49, 0,255,0,255, "0,49", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 99,49, 0,255,0,255, "99,49", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcesize-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcesize-expected.txt
deleted file mode 100644
index b9ea887..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcesize-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.drawImage.9arg.sourcesize Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcesize.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcesize.html
index 4b43d5f..3c718c8d 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcesize.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcesize.html
@@ -33,18 +33,20 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0, 256, 256, 0, 0, 100, 50);
-    ctx.fillStyle = '#0f0';
-    ctx.fillRect(0, 0, 51, 26);
-    ctx.fillRect(49, 24, 51, 26);
-    _assertPixelApprox(offscreenCanvas, 0,0, 0,255,0,255, "0,0", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 99,0, 0,255,0,255, "99,0", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 0,49, 0,255,0,255, "0,49", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 99,49, 0,255,0,255, "99,49", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 20,20, 0,255,0,255, "20,20", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 80,20, 0,255,0,255, "80,20", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 20,30, 0,255,0,255, "20,30", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 80,30, 0,255,0,255, "80,30", "0,255,0,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0, 256, 256, 0, 0, 100, 50);
+        ctx.fillStyle = '#0f0';
+        ctx.fillRect(0, 0, 51, 26);
+        ctx.fillRect(49, 24, 51, 26);
+        _assertPixelApprox(offscreenCanvas, 0,0, 0,255,0,255, "0,0", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 99,0, 0,255,0,255, "99,0", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 0,49, 0,255,0,255, "0,49", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 99,49, 0,255,0,255, "99,49", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 20,20, 0,255,0,255, "20,20", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 80,20, 0,255,0,255, "80,20", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 20,30, 0,255,0,255, "20,30", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 80,30, 0,255,0,255, "80,30", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcesize.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcesize.worker-expected.txt
deleted file mode 100644
index 2e526ad..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcesize.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcesize.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcesize.worker.js
index d73e3b9..dac1c7a 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcesize.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcesize.worker.js
@@ -28,18 +28,20 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0, 256, 256, 0, 0, 100, 50);
-    ctx.fillStyle = '#0f0';
-    ctx.fillRect(0, 0, 51, 26);
-    ctx.fillRect(49, 24, 51, 26);
-    _assertPixelApprox(offscreenCanvas, 0,0, 0,255,0,255, "0,0", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 99,0, 0,255,0,255, "99,0", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 0,49, 0,255,0,255, "0,49", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 99,49, 0,255,0,255, "99,49", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 20,20, 0,255,0,255, "20,20", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 80,20, 0,255,0,255, "80,20", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 20,30, 0,255,0,255, "20,30", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 80,30, 0,255,0,255, "80,30", "0,255,0,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0, 256, 256, 0, 0, 100, 50);
+        ctx.fillStyle = '#0f0';
+        ctx.fillRect(0, 0, 51, 26);
+        ctx.fillRect(49, 24, 51, 26);
+        _assertPixelApprox(offscreenCanvas, 0,0, 0,255,0,255, "0,0", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 99,0, 0,255,0,255, "99,0", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 0,49, 0,255,0,255, "0,49", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 99,49, 0,255,0,255, "99,49", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 20,20, 0,255,0,255, "20,20", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 80,20, 0,255,0,255, "80,20", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 20,30, 0,255,0,255, "20,30", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 80,30, 0,255,0,255, "80,30", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.alpha-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.alpha-expected.txt
deleted file mode 100644
index b728d91..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.alpha-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.drawImage.alpha Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.alpha.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.alpha.html
index 33b2502..f1a1f08 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.alpha.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.alpha.html
@@ -34,8 +34,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.alpha.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.alpha.worker-expected.txt
deleted file mode 100644
index 2e526ad..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.alpha.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.alpha.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.alpha.worker.js
index 5f29c1a..6828af9 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.alpha.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.alpha.worker.js
@@ -29,8 +29,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.animated.poster-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.animated.poster-expected.txt
deleted file mode 100644
index b14e146..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.animated.poster-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL drawImage() of an APNG draws the poster frame Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.animated.poster.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.animated.poster.html
index 16e9f79..2870894 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.animated.poster.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.animated.poster.html
@@ -31,8 +31,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.animated.poster.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.animated.poster.worker-expected.txt
deleted file mode 100644
index b14e146..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.animated.poster.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL drawImage() of an APNG draws the poster frame Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.animated.poster.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.animated.poster.worker.js
index c3b566e8..ce6e8ee 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.animated.poster.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.animated.poster.worker.js
@@ -26,8 +26,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.broken-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.broken-expected.txt
deleted file mode 100644
index 83d48a0..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.broken-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.drawImage.broken Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.broken.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.broken.html
index a64d20e7..531376aa 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.broken.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.broken.html
@@ -31,10 +31,12 @@
     };
 });
 promise.then(function(response) {
-    ctx.fillStyle = '#0f0';
-    ctx.fillRect(0, 0, 100, 50);
-    ctx.drawImage(response, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        ctx.fillStyle = '#0f0';
+        ctx.fillRect(0, 0, 100, 50);
+        ctx.drawImage(bitmap, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.broken.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.broken.worker-expected.txt
deleted file mode 100644
index 2e526ad..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.broken.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.broken.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.broken.worker.js
index 63163bd..4acc40b 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.broken.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.broken.worker.js
@@ -26,10 +26,12 @@
     };
 });
 promise.then(function(response) {
-    ctx.fillStyle = '#0f0';
-    ctx.fillRect(0, 0, 100, 50);
-    ctx.drawImage(response, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        ctx.fillStyle = '#0f0';
+        ctx.fillRect(0, 0, 100, 50);
+        ctx.drawImage(bitmap, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.clip-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.clip-expected.txt
deleted file mode 100644
index bf1bea3..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.clip-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.drawImage.clip Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.clip.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.clip.html
index 51651e56..562a9c5d 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.clip.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.clip.html
@@ -35,8 +35,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.clip.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.clip.worker-expected.txt
deleted file mode 100644
index 2e526ad..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.clip.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.clip.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.clip.worker.js
index 90d0aad..1741f15b 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.clip.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.clip.worker.js
@@ -30,8 +30,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.composite-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.composite-expected.txt
deleted file mode 100644
index 5c1a406..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.composite-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.drawImage.composite Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.composite.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.composite.html
index ba56e36..de37739 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.composite.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.composite.html
@@ -34,8 +34,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.composite.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.composite.worker-expected.txt
deleted file mode 100644
index 2e526ad..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.composite.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.composite.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.composite.worker.js
index 09e3b3a0..eecc891 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.composite.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.composite.worker.js
@@ -29,8 +29,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.floatsource-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.floatsource-expected.txt
deleted file mode 100644
index 28bfd45..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.floatsource-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.drawImage.floatsource Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.floatsource.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.floatsource.html
index 49a88fa..f90b41d 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.floatsource.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.floatsource.html
@@ -31,8 +31,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 10.1, 10.1, 0.1, 0.1, 0, 0, 100, 50);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 10.1, 10.1, 0.1, 0.1, 0, 0, 100, 50);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.floatsource.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.floatsource.worker-expected.txt
deleted file mode 100644
index 2e526ad..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.floatsource.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.floatsource.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.floatsource.worker.js
index 45398e8..d154a12 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.floatsource.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.floatsource.worker.js
@@ -26,8 +26,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 10.1, 10.1, 0.1, 0.1, 0, 0, 100, 50);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 10.1, 10.1, 0.1, 0.1, 0, 0, 100, 50);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativedest-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativedest-expected.txt
deleted file mode 100644
index 9617d8f..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativedest-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Negative destination width/height represents the correct rectangle Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativedest.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativedest.html
index 9466738b..d018e94 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativedest.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativedest.html
@@ -33,18 +33,20 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 100, 78, 50, 50, 0, 50, 50, -50);
-    ctx.drawImage(response, 100, 128, 50, -50, 100, 50, -50, -50);
-    _assertPixelApprox(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 48,1, 0,255,0,255, "48,1", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 48,48, 0,255,0,255, "48,48", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 51,1, 0,255,0,255, "51,1", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 51,48, 0,255,0,255, "51,48", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 25,25, 0,255,0,255, "25,25", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 75,25, 0,255,0,255, "75,25", "0,255,0,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 100, 78, 50, 50, 0, 50, 50, -50);
+        ctx.drawImage(bitmap, 100, 128, 50, -50, 100, 50, -50, -50);
+        _assertPixelApprox(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 48,1, 0,255,0,255, "48,1", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 48,48, 0,255,0,255, "48,48", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 51,1, 0,255,0,255, "51,1", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 51,48, 0,255,0,255, "51,48", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 25,25, 0,255,0,255, "25,25", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 75,25, 0,255,0,255, "75,25", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativedest.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativedest.worker-expected.txt
deleted file mode 100644
index 9617d8f..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativedest.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Negative destination width/height represents the correct rectangle Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativedest.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativedest.worker.js
index 284b458..0185dd4 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativedest.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativedest.worker.js
@@ -28,18 +28,20 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 100, 78, 50, 50, 0, 50, 50, -50);
-    ctx.drawImage(response, 100, 128, 50, -50, 100, 50, -50, -50);
-    _assertPixelApprox(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 48,1, 0,255,0,255, "48,1", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 48,48, 0,255,0,255, "48,48", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 51,1, 0,255,0,255, "51,1", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 51,48, 0,255,0,255, "51,48", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 25,25, 0,255,0,255, "25,25", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 75,25, 0,255,0,255, "75,25", "0,255,0,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 100, 78, 50, 50, 0, 50, 50, -50);
+        ctx.drawImage(bitmap, 100, 128, 50, -50, 100, 50, -50, -50);
+        _assertPixelApprox(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 48,1, 0,255,0,255, "48,1", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 48,48, 0,255,0,255, "48,48", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 51,1, 0,255,0,255, "51,1", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 51,48, 0,255,0,255, "51,48", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 25,25, 0,255,0,255, "25,25", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 75,25, 0,255,0,255, "75,25", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativedir-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativedir-expected.txt
deleted file mode 100644
index 8ef68a3..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativedir-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Negative dimensions do not affect the direction of the image Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativedir.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativedir.html
index 4cbd374..2f0a91d 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativedir.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativedir.html
@@ -33,18 +33,20 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 178, 50, -100, 0, 0, 50, 100);
-    ctx.drawImage(response, 0, 78, 50, 100, 50, 100, 50, -100);
-    _assertPixelApprox(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 48,1, 0,255,0,255, "48,1", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 48,48, 0,255,0,255, "48,48", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 51,1, 0,255,0,255, "51,1", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 51,48, 0,255,0,255, "51,48", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 25,25, 0,255,0,255, "25,25", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 75,25, 0,255,0,255, "75,25", "0,255,0,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 178, 50, -100, 0, 0, 50, 100);
+        ctx.drawImage(bitmap, 0, 78, 50, 100, 50, 100, 50, -100);
+        _assertPixelApprox(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 48,1, 0,255,0,255, "48,1", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 48,48, 0,255,0,255, "48,48", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 51,1, 0,255,0,255, "51,1", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 51,48, 0,255,0,255, "51,48", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 25,25, 0,255,0,255, "25,25", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 75,25, 0,255,0,255, "75,25", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativedir.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativedir.worker-expected.txt
deleted file mode 100644
index 8ef68a3..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativedir.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Negative dimensions do not affect the direction of the image Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativedir.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativedir.worker.js
index 00f0e7e..b29cdad 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativedir.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativedir.worker.js
@@ -28,18 +28,20 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 178, 50, -100, 0, 0, 50, 100);
-    ctx.drawImage(response, 0, 78, 50, 100, 50, 100, 50, -100);
-    _assertPixelApprox(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 48,1, 0,255,0,255, "48,1", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 48,48, 0,255,0,255, "48,48", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 51,1, 0,255,0,255, "51,1", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 51,48, 0,255,0,255, "51,48", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 25,25, 0,255,0,255, "25,25", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 75,25, 0,255,0,255, "75,25", "0,255,0,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 178, 50, -100, 0, 0, 50, 100);
+        ctx.drawImage(bitmap, 0, 78, 50, 100, 50, 100, 50, -100);
+        _assertPixelApprox(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 48,1, 0,255,0,255, "48,1", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 48,48, 0,255,0,255, "48,48", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 51,1, 0,255,0,255, "51,1", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 51,48, 0,255,0,255, "51,48", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 25,25, 0,255,0,255, "25,25", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 75,25, 0,255,0,255, "75,25", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativesource-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativesource-expected.txt
deleted file mode 100644
index 17b8650..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativesource-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Negative source width/height represents the correct rectangle Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativesource.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativesource.html
index 66e9c212..c2f78e5 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativesource.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativesource.html
@@ -33,18 +33,20 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 100, 78, -100, 50, 0, 0, 50, 50);
-    ctx.drawImage(response, 100, 128, -100, -50, 50, 0, 50, 50);
-    _assertPixelApprox(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 48,1, 0,255,0,255, "48,1", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 48,48, 0,255,0,255, "48,48", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 51,1, 0,255,0,255, "51,1", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 51,48, 0,255,0,255, "51,48", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 25,25, 0,255,0,255, "25,25", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 75,25, 0,255,0,255, "75,25", "0,255,0,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 100, 78, -100, 50, 0, 0, 50, 50);
+        ctx.drawImage(bitmap, 100, 128, -100, -50, 50, 0, 50, 50);
+        _assertPixelApprox(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 48,1, 0,255,0,255, "48,1", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 48,48, 0,255,0,255, "48,48", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 51,1, 0,255,0,255, "51,1", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 51,48, 0,255,0,255, "51,48", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 25,25, 0,255,0,255, "25,25", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 75,25, 0,255,0,255, "75,25", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativesource.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativesource.worker-expected.txt
deleted file mode 100644
index 17b8650..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativesource.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Negative source width/height represents the correct rectangle Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativesource.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativesource.worker.js
index 0f24542..2171f69 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativesource.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.negativesource.worker.js
@@ -28,18 +28,20 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 100, 78, -100, 50, 0, 0, 50, 50);
-    ctx.drawImage(response, 100, 128, -100, -50, 50, 0, 50, 50);
-    _assertPixelApprox(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 48,1, 0,255,0,255, "48,1", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 48,48, 0,255,0,255, "48,48", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 51,1, 0,255,0,255, "51,1", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 51,48, 0,255,0,255, "51,48", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 25,25, 0,255,0,255, "25,25", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 75,25, 0,255,0,255, "75,25", "0,255,0,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 100, 78, -100, 50, 0, 0, 50, 50);
+        ctx.drawImage(bitmap, 100, 128, -100, -50, 50, 0, 50, 50);
+        _assertPixelApprox(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 48,1, 0,255,0,255, "48,1", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 48,48, 0,255,0,255, "48,48", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 51,1, 0,255,0,255, "51,1", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 51,48, 0,255,0,255, "51,48", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 25,25, 0,255,0,255, "25,25", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 75,25, 0,255,0,255, "75,25", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.nonfinite-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.nonfinite-expected.txt
deleted file mode 100644
index 7279047..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.nonfinite-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL drawImage() with Infinity/NaN is ignored Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.nonfinite.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.nonfinite.html
index 9ca1a88..c6bab2ea 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.nonfinite.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.nonfinite.html
@@ -33,308 +33,310 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, Infinity, 0);
-ctx.drawImage(response, -Infinity, 0);
-ctx.drawImage(response, NaN, 0);
-ctx.drawImage(response, 0, Infinity);
-ctx.drawImage(response, 0, -Infinity);
-ctx.drawImage(response, 0, NaN);
-ctx.drawImage(response, Infinity, Infinity);
-    ctx.drawImage(response, Infinity, 0, 100, 50);
-ctx.drawImage(response, -Infinity, 0, 100, 50);
-ctx.drawImage(response, NaN, 0, 100, 50);
-ctx.drawImage(response, 0, Infinity, 100, 50);
-ctx.drawImage(response, 0, -Infinity, 100, 50);
-ctx.drawImage(response, 0, NaN, 100, 50);
-ctx.drawImage(response, 0, 0, Infinity, 50);
-ctx.drawImage(response, 0, 0, -Infinity, 50);
-ctx.drawImage(response, 0, 0, NaN, 50);
-ctx.drawImage(response, 0, 0, 100, Infinity);
-ctx.drawImage(response, 0, 0, 100, -Infinity);
-ctx.drawImage(response, 0, 0, 100, NaN);
-ctx.drawImage(response, Infinity, Infinity, 100, 50);
-ctx.drawImage(response, Infinity, Infinity, Infinity, 50);
-ctx.drawImage(response, Infinity, Infinity, Infinity, Infinity);
-ctx.drawImage(response, Infinity, Infinity, 100, Infinity);
-ctx.drawImage(response, Infinity, 0, Infinity, 50);
-ctx.drawImage(response, Infinity, 0, Infinity, Infinity);
-ctx.drawImage(response, Infinity, 0, 100, Infinity);
-ctx.drawImage(response, 0, Infinity, Infinity, 50);
-ctx.drawImage(response, 0, Infinity, Infinity, Infinity);
-ctx.drawImage(response, 0, Infinity, 100, Infinity);
-ctx.drawImage(response, 0, 0, Infinity, Infinity);
-    ctx.drawImage(response, Infinity, 0, 100, 50, 0, 0, 100, 50);
-ctx.drawImage(response, -Infinity, 0, 100, 50, 0, 0, 100, 50);
-ctx.drawImage(response, NaN, 0, 100, 50, 0, 0, 100, 50);
-ctx.drawImage(response, 0, Infinity, 100, 50, 0, 0, 100, 50);
-ctx.drawImage(response, 0, -Infinity, 100, 50, 0, 0, 100, 50);
-ctx.drawImage(response, 0, NaN, 100, 50, 0, 0, 100, 50);
-ctx.drawImage(response, 0, 0, Infinity, 50, 0, 0, 100, 50);
-ctx.drawImage(response, 0, 0, -Infinity, 50, 0, 0, 100, 50);
-ctx.drawImage(response, 0, 0, NaN, 50, 0, 0, 100, 50);
-ctx.drawImage(response, 0, 0, 100, Infinity, 0, 0, 100, 50);
-ctx.drawImage(response, 0, 0, 100, -Infinity, 0, 0, 100, 50);
-ctx.drawImage(response, 0, 0, 100, NaN, 0, 0, 100, 50);
-ctx.drawImage(response, 0, 0, 100, 50, Infinity, 0, 100, 50);
-ctx.drawImage(response, 0, 0, 100, 50, -Infinity, 0, 100, 50);
-ctx.drawImage(response, 0, 0, 100, 50, NaN, 0, 100, 50);
-ctx.drawImage(response, 0, 0, 100, 50, 0, Infinity, 100, 50);
-ctx.drawImage(response, 0, 0, 100, 50, 0, -Infinity, 100, 50);
-ctx.drawImage(response, 0, 0, 100, 50, 0, NaN, 100, 50);
-ctx.drawImage(response, 0, 0, 100, 50, 0, 0, Infinity, 50);
-ctx.drawImage(response, 0, 0, 100, 50, 0, 0, -Infinity, 50);
-ctx.drawImage(response, 0, 0, 100, 50, 0, 0, NaN, 50);
-ctx.drawImage(response, 0, 0, 100, 50, 0, 0, 100, Infinity);
-ctx.drawImage(response, 0, 0, 100, 50, 0, 0, 100, -Infinity);
-ctx.drawImage(response, 0, 0, 100, 50, 0, 0, 100, NaN);
-ctx.drawImage(response, Infinity, Infinity, 100, 50, 0, 0, 100, 50);
-ctx.drawImage(response, Infinity, Infinity, Infinity, 50, 0, 0, 100, 50);
-ctx.drawImage(response, Infinity, Infinity, Infinity, Infinity, 0, 0, 100, 50);
-ctx.drawImage(response, Infinity, Infinity, Infinity, Infinity, Infinity, 0, 100, 50);
-ctx.drawImage(response, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, 100, 50);
-ctx.drawImage(response, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, 50);
-ctx.drawImage(response, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity);
-ctx.drawImage(response, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, 100, Infinity);
-ctx.drawImage(response, Infinity, Infinity, Infinity, Infinity, Infinity, 0, Infinity, 50);
-ctx.drawImage(response, Infinity, Infinity, Infinity, Infinity, Infinity, 0, Infinity, Infinity);
-ctx.drawImage(response, Infinity, Infinity, Infinity, Infinity, Infinity, 0, 100, Infinity);
-ctx.drawImage(response, Infinity, Infinity, Infinity, Infinity, 0, Infinity, 100, 50);
-ctx.drawImage(response, Infinity, Infinity, Infinity, Infinity, 0, Infinity, Infinity, 50);
-ctx.drawImage(response, Infinity, Infinity, Infinity, Infinity, 0, Infinity, Infinity, Infinity);
-ctx.drawImage(response, Infinity, Infinity, Infinity, Infinity, 0, Infinity, 100, Infinity);
-ctx.drawImage(response, Infinity, Infinity, Infinity, Infinity, 0, 0, Infinity, 50);
-ctx.drawImage(response, Infinity, Infinity, Infinity, Infinity, 0, 0, Infinity, Infinity);
-ctx.drawImage(response, Infinity, Infinity, Infinity, Infinity, 0, 0, 100, Infinity);
-ctx.drawImage(response, Infinity, Infinity, Infinity, 50, Infinity, 0, 100, 50);
-ctx.drawImage(response, Infinity, Infinity, Infinity, 50, Infinity, Infinity, 100, 50);
-ctx.drawImage(response, Infinity, Infinity, Infinity, 50, Infinity, Infinity, Infinity, 50);
-ctx.drawImage(response, Infinity, Infinity, Infinity, 50, Infinity, Infinity, Infinity, Infinity);
-ctx.drawImage(response, Infinity, Infinity, Infinity, 50, Infinity, Infinity, 100, Infinity);
-ctx.drawImage(response, Infinity, Infinity, Infinity, 50, Infinity, 0, Infinity, 50);
-ctx.drawImage(response, Infinity, Infinity, Infinity, 50, Infinity, 0, Infinity, Infinity);
-ctx.drawImage(response, Infinity, Infinity, Infinity, 50, Infinity, 0, 100, Infinity);
-ctx.drawImage(response, Infinity, Infinity, Infinity, 50, 0, Infinity, 100, 50);
-ctx.drawImage(response, Infinity, Infinity, Infinity, 50, 0, Infinity, Infinity, 50);
-ctx.drawImage(response, Infinity, Infinity, Infinity, 50, 0, Infinity, Infinity, Infinity);
-ctx.drawImage(response, Infinity, Infinity, Infinity, 50, 0, Infinity, 100, Infinity);
-ctx.drawImage(response, Infinity, Infinity, Infinity, 50, 0, 0, Infinity, 50);
-ctx.drawImage(response, Infinity, Infinity, Infinity, 50, 0, 0, Infinity, Infinity);
-ctx.drawImage(response, Infinity, Infinity, Infinity, 50, 0, 0, 100, Infinity);
-ctx.drawImage(response, Infinity, Infinity, 100, Infinity, 0, 0, 100, 50);
-ctx.drawImage(response, Infinity, Infinity, 100, Infinity, Infinity, 0, 100, 50);
-ctx.drawImage(response, Infinity, Infinity, 100, Infinity, Infinity, Infinity, 100, 50);
-ctx.drawImage(response, Infinity, Infinity, 100, Infinity, Infinity, Infinity, Infinity, 50);
-ctx.drawImage(response, Infinity, Infinity, 100, Infinity, Infinity, Infinity, Infinity, Infinity);
-ctx.drawImage(response, Infinity, Infinity, 100, Infinity, Infinity, Infinity, 100, Infinity);
-ctx.drawImage(response, Infinity, Infinity, 100, Infinity, Infinity, 0, Infinity, 50);
-ctx.drawImage(response, Infinity, Infinity, 100, Infinity, Infinity, 0, Infinity, Infinity);
-ctx.drawImage(response, Infinity, Infinity, 100, Infinity, Infinity, 0, 100, Infinity);
-ctx.drawImage(response, Infinity, Infinity, 100, Infinity, 0, Infinity, 100, 50);
-ctx.drawImage(response, Infinity, Infinity, 100, Infinity, 0, Infinity, Infinity, 50);
-ctx.drawImage(response, Infinity, Infinity, 100, Infinity, 0, Infinity, Infinity, Infinity);
-ctx.drawImage(response, Infinity, Infinity, 100, Infinity, 0, Infinity, 100, Infinity);
-ctx.drawImage(response, Infinity, Infinity, 100, Infinity, 0, 0, Infinity, 50);
-ctx.drawImage(response, Infinity, Infinity, 100, Infinity, 0, 0, Infinity, Infinity);
-ctx.drawImage(response, Infinity, Infinity, 100, Infinity, 0, 0, 100, Infinity);
-ctx.drawImage(response, Infinity, Infinity, 100, 50, Infinity, 0, 100, 50);
-ctx.drawImage(response, Infinity, Infinity, 100, 50, Infinity, Infinity, 100, 50);
-ctx.drawImage(response, Infinity, Infinity, 100, 50, Infinity, Infinity, Infinity, 50);
-ctx.drawImage(response, Infinity, Infinity, 100, 50, Infinity, Infinity, Infinity, Infinity);
-ctx.drawImage(response, Infinity, Infinity, 100, 50, Infinity, Infinity, 100, Infinity);
-ctx.drawImage(response, Infinity, Infinity, 100, 50, Infinity, 0, Infinity, 50);
-ctx.drawImage(response, Infinity, Infinity, 100, 50, Infinity, 0, Infinity, Infinity);
-ctx.drawImage(response, Infinity, Infinity, 100, 50, Infinity, 0, 100, Infinity);
-ctx.drawImage(response, Infinity, Infinity, 100, 50, 0, Infinity, 100, 50);
-ctx.drawImage(response, Infinity, Infinity, 100, 50, 0, Infinity, Infinity, 50);
-ctx.drawImage(response, Infinity, Infinity, 100, 50, 0, Infinity, Infinity, Infinity);
-ctx.drawImage(response, Infinity, Infinity, 100, 50, 0, Infinity, 100, Infinity);
-ctx.drawImage(response, Infinity, Infinity, 100, 50, 0, 0, Infinity, 50);
-ctx.drawImage(response, Infinity, Infinity, 100, 50, 0, 0, Infinity, Infinity);
-ctx.drawImage(response, Infinity, Infinity, 100, 50, 0, 0, 100, Infinity);
-ctx.drawImage(response, Infinity, 0, Infinity, 50, 0, 0, 100, 50);
-ctx.drawImage(response, Infinity, 0, Infinity, Infinity, 0, 0, 100, 50);
-ctx.drawImage(response, Infinity, 0, Infinity, Infinity, Infinity, 0, 100, 50);
-ctx.drawImage(response, Infinity, 0, Infinity, Infinity, Infinity, Infinity, 100, 50);
-ctx.drawImage(response, Infinity, 0, Infinity, Infinity, Infinity, Infinity, Infinity, 50);
-ctx.drawImage(response, Infinity, 0, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity);
-ctx.drawImage(response, Infinity, 0, Infinity, Infinity, Infinity, Infinity, 100, Infinity);
-ctx.drawImage(response, Infinity, 0, Infinity, Infinity, Infinity, 0, Infinity, 50);
-ctx.drawImage(response, Infinity, 0, Infinity, Infinity, Infinity, 0, Infinity, Infinity);
-ctx.drawImage(response, Infinity, 0, Infinity, Infinity, Infinity, 0, 100, Infinity);
-ctx.drawImage(response, Infinity, 0, Infinity, Infinity, 0, Infinity, 100, 50);
-ctx.drawImage(response, Infinity, 0, Infinity, Infinity, 0, Infinity, Infinity, 50);
-ctx.drawImage(response, Infinity, 0, Infinity, Infinity, 0, Infinity, Infinity, Infinity);
-ctx.drawImage(response, Infinity, 0, Infinity, Infinity, 0, Infinity, 100, Infinity);
-ctx.drawImage(response, Infinity, 0, Infinity, Infinity, 0, 0, Infinity, 50);
-ctx.drawImage(response, Infinity, 0, Infinity, Infinity, 0, 0, Infinity, Infinity);
-ctx.drawImage(response, Infinity, 0, Infinity, Infinity, 0, 0, 100, Infinity);
-ctx.drawImage(response, Infinity, 0, Infinity, 50, Infinity, 0, 100, 50);
-ctx.drawImage(response, Infinity, 0, Infinity, 50, Infinity, Infinity, 100, 50);
-ctx.drawImage(response, Infinity, 0, Infinity, 50, Infinity, Infinity, Infinity, 50);
-ctx.drawImage(response, Infinity, 0, Infinity, 50, Infinity, Infinity, Infinity, Infinity);
-ctx.drawImage(response, Infinity, 0, Infinity, 50, Infinity, Infinity, 100, Infinity);
-ctx.drawImage(response, Infinity, 0, Infinity, 50, Infinity, 0, Infinity, 50);
-ctx.drawImage(response, Infinity, 0, Infinity, 50, Infinity, 0, Infinity, Infinity);
-ctx.drawImage(response, Infinity, 0, Infinity, 50, Infinity, 0, 100, Infinity);
-ctx.drawImage(response, Infinity, 0, Infinity, 50, 0, Infinity, 100, 50);
-ctx.drawImage(response, Infinity, 0, Infinity, 50, 0, Infinity, Infinity, 50);
-ctx.drawImage(response, Infinity, 0, Infinity, 50, 0, Infinity, Infinity, Infinity);
-ctx.drawImage(response, Infinity, 0, Infinity, 50, 0, Infinity, 100, Infinity);
-ctx.drawImage(response, Infinity, 0, Infinity, 50, 0, 0, Infinity, 50);
-ctx.drawImage(response, Infinity, 0, Infinity, 50, 0, 0, Infinity, Infinity);
-ctx.drawImage(response, Infinity, 0, Infinity, 50, 0, 0, 100, Infinity);
-ctx.drawImage(response, Infinity, 0, 100, Infinity, 0, 0, 100, 50);
-ctx.drawImage(response, Infinity, 0, 100, Infinity, Infinity, 0, 100, 50);
-ctx.drawImage(response, Infinity, 0, 100, Infinity, Infinity, Infinity, 100, 50);
-ctx.drawImage(response, Infinity, 0, 100, Infinity, Infinity, Infinity, Infinity, 50);
-ctx.drawImage(response, Infinity, 0, 100, Infinity, Infinity, Infinity, Infinity, Infinity);
-ctx.drawImage(response, Infinity, 0, 100, Infinity, Infinity, Infinity, 100, Infinity);
-ctx.drawImage(response, Infinity, 0, 100, Infinity, Infinity, 0, Infinity, 50);
-ctx.drawImage(response, Infinity, 0, 100, Infinity, Infinity, 0, Infinity, Infinity);
-ctx.drawImage(response, Infinity, 0, 100, Infinity, Infinity, 0, 100, Infinity);
-ctx.drawImage(response, Infinity, 0, 100, Infinity, 0, Infinity, 100, 50);
-ctx.drawImage(response, Infinity, 0, 100, Infinity, 0, Infinity, Infinity, 50);
-ctx.drawImage(response, Infinity, 0, 100, Infinity, 0, Infinity, Infinity, Infinity);
-ctx.drawImage(response, Infinity, 0, 100, Infinity, 0, Infinity, 100, Infinity);
-ctx.drawImage(response, Infinity, 0, 100, Infinity, 0, 0, Infinity, 50);
-ctx.drawImage(response, Infinity, 0, 100, Infinity, 0, 0, Infinity, Infinity);
-ctx.drawImage(response, Infinity, 0, 100, Infinity, 0, 0, 100, Infinity);
-ctx.drawImage(response, Infinity, 0, 100, 50, Infinity, 0, 100, 50);
-ctx.drawImage(response, Infinity, 0, 100, 50, Infinity, Infinity, 100, 50);
-ctx.drawImage(response, Infinity, 0, 100, 50, Infinity, Infinity, Infinity, 50);
-ctx.drawImage(response, Infinity, 0, 100, 50, Infinity, Infinity, Infinity, Infinity);
-ctx.drawImage(response, Infinity, 0, 100, 50, Infinity, Infinity, 100, Infinity);
-ctx.drawImage(response, Infinity, 0, 100, 50, Infinity, 0, Infinity, 50);
-ctx.drawImage(response, Infinity, 0, 100, 50, Infinity, 0, Infinity, Infinity);
-ctx.drawImage(response, Infinity, 0, 100, 50, Infinity, 0, 100, Infinity);
-ctx.drawImage(response, Infinity, 0, 100, 50, 0, Infinity, 100, 50);
-ctx.drawImage(response, Infinity, 0, 100, 50, 0, Infinity, Infinity, 50);
-ctx.drawImage(response, Infinity, 0, 100, 50, 0, Infinity, Infinity, Infinity);
-ctx.drawImage(response, Infinity, 0, 100, 50, 0, Infinity, 100, Infinity);
-ctx.drawImage(response, Infinity, 0, 100, 50, 0, 0, Infinity, 50);
-ctx.drawImage(response, Infinity, 0, 100, 50, 0, 0, Infinity, Infinity);
-ctx.drawImage(response, Infinity, 0, 100, 50, 0, 0, 100, Infinity);
-ctx.drawImage(response, 0, Infinity, Infinity, 50, 0, 0, 100, 50);
-ctx.drawImage(response, 0, Infinity, Infinity, Infinity, 0, 0, 100, 50);
-ctx.drawImage(response, 0, Infinity, Infinity, Infinity, Infinity, 0, 100, 50);
-ctx.drawImage(response, 0, Infinity, Infinity, Infinity, Infinity, Infinity, 100, 50);
-ctx.drawImage(response, 0, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, 50);
-ctx.drawImage(response, 0, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity);
-ctx.drawImage(response, 0, Infinity, Infinity, Infinity, Infinity, Infinity, 100, Infinity);
-ctx.drawImage(response, 0, Infinity, Infinity, Infinity, Infinity, 0, Infinity, 50);
-ctx.drawImage(response, 0, Infinity, Infinity, Infinity, Infinity, 0, Infinity, Infinity);
-ctx.drawImage(response, 0, Infinity, Infinity, Infinity, Infinity, 0, 100, Infinity);
-ctx.drawImage(response, 0, Infinity, Infinity, Infinity, 0, Infinity, 100, 50);
-ctx.drawImage(response, 0, Infinity, Infinity, Infinity, 0, Infinity, Infinity, 50);
-ctx.drawImage(response, 0, Infinity, Infinity, Infinity, 0, Infinity, Infinity, Infinity);
-ctx.drawImage(response, 0, Infinity, Infinity, Infinity, 0, Infinity, 100, Infinity);
-ctx.drawImage(response, 0, Infinity, Infinity, Infinity, 0, 0, Infinity, 50);
-ctx.drawImage(response, 0, Infinity, Infinity, Infinity, 0, 0, Infinity, Infinity);
-ctx.drawImage(response, 0, Infinity, Infinity, Infinity, 0, 0, 100, Infinity);
-ctx.drawImage(response, 0, Infinity, Infinity, 50, Infinity, 0, 100, 50);
-ctx.drawImage(response, 0, Infinity, Infinity, 50, Infinity, Infinity, 100, 50);
-ctx.drawImage(response, 0, Infinity, Infinity, 50, Infinity, Infinity, Infinity, 50);
-ctx.drawImage(response, 0, Infinity, Infinity, 50, Infinity, Infinity, Infinity, Infinity);
-ctx.drawImage(response, 0, Infinity, Infinity, 50, Infinity, Infinity, 100, Infinity);
-ctx.drawImage(response, 0, Infinity, Infinity, 50, Infinity, 0, Infinity, 50);
-ctx.drawImage(response, 0, Infinity, Infinity, 50, Infinity, 0, Infinity, Infinity);
-ctx.drawImage(response, 0, Infinity, Infinity, 50, Infinity, 0, 100, Infinity);
-ctx.drawImage(response, 0, Infinity, Infinity, 50, 0, Infinity, 100, 50);
-ctx.drawImage(response, 0, Infinity, Infinity, 50, 0, Infinity, Infinity, 50);
-ctx.drawImage(response, 0, Infinity, Infinity, 50, 0, Infinity, Infinity, Infinity);
-ctx.drawImage(response, 0, Infinity, Infinity, 50, 0, Infinity, 100, Infinity);
-ctx.drawImage(response, 0, Infinity, Infinity, 50, 0, 0, Infinity, 50);
-ctx.drawImage(response, 0, Infinity, Infinity, 50, 0, 0, Infinity, Infinity);
-ctx.drawImage(response, 0, Infinity, Infinity, 50, 0, 0, 100, Infinity);
-ctx.drawImage(response, 0, Infinity, 100, Infinity, 0, 0, 100, 50);
-ctx.drawImage(response, 0, Infinity, 100, Infinity, Infinity, 0, 100, 50);
-ctx.drawImage(response, 0, Infinity, 100, Infinity, Infinity, Infinity, 100, 50);
-ctx.drawImage(response, 0, Infinity, 100, Infinity, Infinity, Infinity, Infinity, 50);
-ctx.drawImage(response, 0, Infinity, 100, Infinity, Infinity, Infinity, Infinity, Infinity);
-ctx.drawImage(response, 0, Infinity, 100, Infinity, Infinity, Infinity, 100, Infinity);
-ctx.drawImage(response, 0, Infinity, 100, Infinity, Infinity, 0, Infinity, 50);
-ctx.drawImage(response, 0, Infinity, 100, Infinity, Infinity, 0, Infinity, Infinity);
-ctx.drawImage(response, 0, Infinity, 100, Infinity, Infinity, 0, 100, Infinity);
-ctx.drawImage(response, 0, Infinity, 100, Infinity, 0, Infinity, 100, 50);
-ctx.drawImage(response, 0, Infinity, 100, Infinity, 0, Infinity, Infinity, 50);
-ctx.drawImage(response, 0, Infinity, 100, Infinity, 0, Infinity, Infinity, Infinity);
-ctx.drawImage(response, 0, Infinity, 100, Infinity, 0, Infinity, 100, Infinity);
-ctx.drawImage(response, 0, Infinity, 100, Infinity, 0, 0, Infinity, 50);
-ctx.drawImage(response, 0, Infinity, 100, Infinity, 0, 0, Infinity, Infinity);
-ctx.drawImage(response, 0, Infinity, 100, Infinity, 0, 0, 100, Infinity);
-ctx.drawImage(response, 0, Infinity, 100, 50, Infinity, 0, 100, 50);
-ctx.drawImage(response, 0, Infinity, 100, 50, Infinity, Infinity, 100, 50);
-ctx.drawImage(response, 0, Infinity, 100, 50, Infinity, Infinity, Infinity, 50);
-ctx.drawImage(response, 0, Infinity, 100, 50, Infinity, Infinity, Infinity, Infinity);
-ctx.drawImage(response, 0, Infinity, 100, 50, Infinity, Infinity, 100, Infinity);
-ctx.drawImage(response, 0, Infinity, 100, 50, Infinity, 0, Infinity, 50);
-ctx.drawImage(response, 0, Infinity, 100, 50, Infinity, 0, Infinity, Infinity);
-ctx.drawImage(response, 0, Infinity, 100, 50, Infinity, 0, 100, Infinity);
-ctx.drawImage(response, 0, Infinity, 100, 50, 0, Infinity, 100, 50);
-ctx.drawImage(response, 0, Infinity, 100, 50, 0, Infinity, Infinity, 50);
-ctx.drawImage(response, 0, Infinity, 100, 50, 0, Infinity, Infinity, Infinity);
-ctx.drawImage(response, 0, Infinity, 100, 50, 0, Infinity, 100, Infinity);
-ctx.drawImage(response, 0, Infinity, 100, 50, 0, 0, Infinity, 50);
-ctx.drawImage(response, 0, Infinity, 100, 50, 0, 0, Infinity, Infinity);
-ctx.drawImage(response, 0, Infinity, 100, 50, 0, 0, 100, Infinity);
-ctx.drawImage(response, 0, 0, Infinity, Infinity, 0, 0, 100, 50);
-ctx.drawImage(response, 0, 0, Infinity, Infinity, Infinity, 0, 100, 50);
-ctx.drawImage(response, 0, 0, Infinity, Infinity, Infinity, Infinity, 100, 50);
-ctx.drawImage(response, 0, 0, Infinity, Infinity, Infinity, Infinity, Infinity, 50);
-ctx.drawImage(response, 0, 0, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity);
-ctx.drawImage(response, 0, 0, Infinity, Infinity, Infinity, Infinity, 100, Infinity);
-ctx.drawImage(response, 0, 0, Infinity, Infinity, Infinity, 0, Infinity, 50);
-ctx.drawImage(response, 0, 0, Infinity, Infinity, Infinity, 0, Infinity, Infinity);
-ctx.drawImage(response, 0, 0, Infinity, Infinity, Infinity, 0, 100, Infinity);
-ctx.drawImage(response, 0, 0, Infinity, Infinity, 0, Infinity, 100, 50);
-ctx.drawImage(response, 0, 0, Infinity, Infinity, 0, Infinity, Infinity, 50);
-ctx.drawImage(response, 0, 0, Infinity, Infinity, 0, Infinity, Infinity, Infinity);
-ctx.drawImage(response, 0, 0, Infinity, Infinity, 0, Infinity, 100, Infinity);
-ctx.drawImage(response, 0, 0, Infinity, Infinity, 0, 0, Infinity, 50);
-ctx.drawImage(response, 0, 0, Infinity, Infinity, 0, 0, Infinity, Infinity);
-ctx.drawImage(response, 0, 0, Infinity, Infinity, 0, 0, 100, Infinity);
-ctx.drawImage(response, 0, 0, Infinity, 50, Infinity, 0, 100, 50);
-ctx.drawImage(response, 0, 0, Infinity, 50, Infinity, Infinity, 100, 50);
-ctx.drawImage(response, 0, 0, Infinity, 50, Infinity, Infinity, Infinity, 50);
-ctx.drawImage(response, 0, 0, Infinity, 50, Infinity, Infinity, Infinity, Infinity);
-ctx.drawImage(response, 0, 0, Infinity, 50, Infinity, Infinity, 100, Infinity);
-ctx.drawImage(response, 0, 0, Infinity, 50, Infinity, 0, Infinity, 50);
-ctx.drawImage(response, 0, 0, Infinity, 50, Infinity, 0, Infinity, Infinity);
-ctx.drawImage(response, 0, 0, Infinity, 50, Infinity, 0, 100, Infinity);
-ctx.drawImage(response, 0, 0, Infinity, 50, 0, Infinity, 100, 50);
-ctx.drawImage(response, 0, 0, Infinity, 50, 0, Infinity, Infinity, 50);
-ctx.drawImage(response, 0, 0, Infinity, 50, 0, Infinity, Infinity, Infinity);
-ctx.drawImage(response, 0, 0, Infinity, 50, 0, Infinity, 100, Infinity);
-ctx.drawImage(response, 0, 0, Infinity, 50, 0, 0, Infinity, 50);
-ctx.drawImage(response, 0, 0, Infinity, 50, 0, 0, Infinity, Infinity);
-ctx.drawImage(response, 0, 0, Infinity, 50, 0, 0, 100, Infinity);
-ctx.drawImage(response, 0, 0, 100, Infinity, Infinity, 0, 100, 50);
-ctx.drawImage(response, 0, 0, 100, Infinity, Infinity, Infinity, 100, 50);
-ctx.drawImage(response, 0, 0, 100, Infinity, Infinity, Infinity, Infinity, 50);
-ctx.drawImage(response, 0, 0, 100, Infinity, Infinity, Infinity, Infinity, Infinity);
-ctx.drawImage(response, 0, 0, 100, Infinity, Infinity, Infinity, 100, Infinity);
-ctx.drawImage(response, 0, 0, 100, Infinity, Infinity, 0, Infinity, 50);
-ctx.drawImage(response, 0, 0, 100, Infinity, Infinity, 0, Infinity, Infinity);
-ctx.drawImage(response, 0, 0, 100, Infinity, Infinity, 0, 100, Infinity);
-ctx.drawImage(response, 0, 0, 100, Infinity, 0, Infinity, 100, 50);
-ctx.drawImage(response, 0, 0, 100, Infinity, 0, Infinity, Infinity, 50);
-ctx.drawImage(response, 0, 0, 100, Infinity, 0, Infinity, Infinity, Infinity);
-ctx.drawImage(response, 0, 0, 100, Infinity, 0, Infinity, 100, Infinity);
-ctx.drawImage(response, 0, 0, 100, Infinity, 0, 0, Infinity, 50);
-ctx.drawImage(response, 0, 0, 100, Infinity, 0, 0, Infinity, Infinity);
-ctx.drawImage(response, 0, 0, 100, Infinity, 0, 0, 100, Infinity);
-ctx.drawImage(response, 0, 0, 100, 50, Infinity, Infinity, 100, 50);
-ctx.drawImage(response, 0, 0, 100, 50, Infinity, Infinity, Infinity, 50);
-ctx.drawImage(response, 0, 0, 100, 50, Infinity, Infinity, Infinity, Infinity);
-ctx.drawImage(response, 0, 0, 100, 50, Infinity, Infinity, 100, Infinity);
-ctx.drawImage(response, 0, 0, 100, 50, Infinity, 0, Infinity, 50);
-ctx.drawImage(response, 0, 0, 100, 50, Infinity, 0, Infinity, Infinity);
-ctx.drawImage(response, 0, 0, 100, 50, Infinity, 0, 100, Infinity);
-ctx.drawImage(response, 0, 0, 100, 50, 0, Infinity, Infinity, 50);
-ctx.drawImage(response, 0, 0, 100, 50, 0, Infinity, Infinity, Infinity);
-ctx.drawImage(response, 0, 0, 100, 50, 0, Infinity, 100, Infinity);
-ctx.drawImage(response, 0, 0, 100, 50, 0, 0, Infinity, Infinity);
-    _assertPixel(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, Infinity, 0);
+ctx.drawImage(bitmap, -Infinity, 0);
+ctx.drawImage(bitmap, NaN, 0);
+ctx.drawImage(bitmap, 0, Infinity);
+ctx.drawImage(bitmap, 0, -Infinity);
+ctx.drawImage(bitmap, 0, NaN);
+ctx.drawImage(bitmap, Infinity, Infinity);
+        ctx.drawImage(bitmap, Infinity, 0, 100, 50);
+ctx.drawImage(bitmap, -Infinity, 0, 100, 50);
+ctx.drawImage(bitmap, NaN, 0, 100, 50);
+ctx.drawImage(bitmap, 0, Infinity, 100, 50);
+ctx.drawImage(bitmap, 0, -Infinity, 100, 50);
+ctx.drawImage(bitmap, 0, NaN, 100, 50);
+ctx.drawImage(bitmap, 0, 0, Infinity, 50);
+ctx.drawImage(bitmap, 0, 0, -Infinity, 50);
+ctx.drawImage(bitmap, 0, 0, NaN, 50);
+ctx.drawImage(bitmap, 0, 0, 100, Infinity);
+ctx.drawImage(bitmap, 0, 0, 100, -Infinity);
+ctx.drawImage(bitmap, 0, 0, 100, NaN);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, 100, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, 0, 0, Infinity, Infinity);
+        ctx.drawImage(bitmap, Infinity, 0, 100, 50, 0, 0, 100, 50);
+ctx.drawImage(bitmap, -Infinity, 0, 100, 50, 0, 0, 100, 50);
+ctx.drawImage(bitmap, NaN, 0, 100, 50, 0, 0, 100, 50);
+ctx.drawImage(bitmap, 0, Infinity, 100, 50, 0, 0, 100, 50);
+ctx.drawImage(bitmap, 0, -Infinity, 100, 50, 0, 0, 100, 50);
+ctx.drawImage(bitmap, 0, NaN, 100, 50, 0, 0, 100, 50);
+ctx.drawImage(bitmap, 0, 0, Infinity, 50, 0, 0, 100, 50);
+ctx.drawImage(bitmap, 0, 0, -Infinity, 50, 0, 0, 100, 50);
+ctx.drawImage(bitmap, 0, 0, NaN, 50, 0, 0, 100, 50);
+ctx.drawImage(bitmap, 0, 0, 100, Infinity, 0, 0, 100, 50);
+ctx.drawImage(bitmap, 0, 0, 100, -Infinity, 0, 0, 100, 50);
+ctx.drawImage(bitmap, 0, 0, 100, NaN, 0, 0, 100, 50);
+ctx.drawImage(bitmap, 0, 0, 100, 50, Infinity, 0, 100, 50);
+ctx.drawImage(bitmap, 0, 0, 100, 50, -Infinity, 0, 100, 50);
+ctx.drawImage(bitmap, 0, 0, 100, 50, NaN, 0, 100, 50);
+ctx.drawImage(bitmap, 0, 0, 100, 50, 0, Infinity, 100, 50);
+ctx.drawImage(bitmap, 0, 0, 100, 50, 0, -Infinity, 100, 50);
+ctx.drawImage(bitmap, 0, 0, 100, 50, 0, NaN, 100, 50);
+ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, Infinity, 50);
+ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, -Infinity, 50);
+ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, NaN, 50);
+ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, 100, Infinity);
+ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, 100, -Infinity);
+ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, 100, NaN);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, 0, 0, 100, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, 0, 0, 100, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, 0, 0, 100, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, Infinity, 0, 100, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, 100, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, Infinity, 0, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, Infinity, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, Infinity, 0, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, 0, Infinity, 100, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, 0, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, 0, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, 0, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, 0, 0, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, 0, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, 0, 0, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, Infinity, 0, 100, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, Infinity, Infinity, 100, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, Infinity, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, Infinity, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, Infinity, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, Infinity, 0, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, Infinity, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, Infinity, 0, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, 0, Infinity, 100, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, 0, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, 0, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, 0, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, 0, 0, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, 0, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, 0, 0, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, 0, 0, 100, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, Infinity, 0, 100, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, Infinity, Infinity, 100, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, Infinity, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, Infinity, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, Infinity, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, Infinity, 0, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, Infinity, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, Infinity, 0, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, 0, Infinity, 100, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, 0, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, 0, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, 0, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, 0, 0, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, 0, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, 0, 0, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, Infinity, 0, 100, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, Infinity, Infinity, 100, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, Infinity, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, Infinity, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, Infinity, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, Infinity, 0, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, Infinity, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, Infinity, 0, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, 0, Infinity, 100, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, 0, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, 0, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, 0, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, 0, 0, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, 0, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, 0, 0, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, 0, 0, 100, 50);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, 0, 0, 100, 50);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, Infinity, 0, 100, 50);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, Infinity, Infinity, 100, 50);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, Infinity, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, Infinity, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, Infinity, 0, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, Infinity, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, Infinity, 0, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, 0, Infinity, 100, 50);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, 0, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, 0, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, 0, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, 0, 0, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, 0, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, 0, 0, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, Infinity, 0, 100, 50);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, Infinity, Infinity, 100, 50);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, Infinity, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, Infinity, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, Infinity, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, Infinity, 0, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, Infinity, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, Infinity, 0, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, 0, Infinity, 100, 50);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, 0, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, 0, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, 0, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, 0, 0, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, 0, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, 0, 0, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, 0, 0, 100, 50);
+ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, Infinity, 0, 100, 50);
+ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, Infinity, Infinity, 100, 50);
+ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, Infinity, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, Infinity, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, Infinity, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, Infinity, 0, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, Infinity, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, Infinity, 0, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, 0, Infinity, 100, 50);
+ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, 0, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, 0, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, 0, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, 0, 0, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, 0, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, 0, 0, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, 100, 50, Infinity, 0, 100, 50);
+ctx.drawImage(bitmap, Infinity, 0, 100, 50, Infinity, Infinity, 100, 50);
+ctx.drawImage(bitmap, Infinity, 0, 100, 50, Infinity, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, 0, 100, 50, Infinity, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, 100, 50, Infinity, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, 100, 50, Infinity, 0, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, 0, 100, 50, Infinity, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, 100, 50, Infinity, 0, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, 100, 50, 0, Infinity, 100, 50);
+ctx.drawImage(bitmap, Infinity, 0, 100, 50, 0, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, 0, 100, 50, 0, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, 100, 50, 0, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, 100, 50, 0, 0, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, 0, 100, 50, 0, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, 100, 50, 0, 0, 100, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, 0, 0, 100, 50);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, 0, 0, 100, 50);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, Infinity, 0, 100, 50);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, Infinity, Infinity, 100, 50);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, Infinity, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, Infinity, 0, Infinity, 50);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, Infinity, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, Infinity, 0, 100, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, 0, Infinity, 100, 50);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, 0, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, 0, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, 0, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, 0, 0, Infinity, 50);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, 0, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, 0, 0, 100, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, Infinity, 0, 100, 50);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, Infinity, Infinity, 100, 50);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, Infinity, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, Infinity, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, Infinity, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, Infinity, 0, Infinity, 50);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, Infinity, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, Infinity, 0, 100, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, 0, Infinity, 100, 50);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, 0, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, 0, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, 0, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, 0, 0, Infinity, 50);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, 0, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, 0, 0, 100, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, 0, 0, 100, 50);
+ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, Infinity, 0, 100, 50);
+ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, Infinity, Infinity, 100, 50);
+ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, Infinity, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, Infinity, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, Infinity, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, Infinity, 0, Infinity, 50);
+ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, Infinity, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, Infinity, 0, 100, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, 0, Infinity, 100, 50);
+ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, 0, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, 0, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, 0, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, 0, 0, Infinity, 50);
+ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, 0, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, 0, 0, 100, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, 100, 50, Infinity, 0, 100, 50);
+ctx.drawImage(bitmap, 0, Infinity, 100, 50, Infinity, Infinity, 100, 50);
+ctx.drawImage(bitmap, 0, Infinity, 100, 50, Infinity, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, 0, Infinity, 100, 50, Infinity, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, 100, 50, Infinity, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, 100, 50, Infinity, 0, Infinity, 50);
+ctx.drawImage(bitmap, 0, Infinity, 100, 50, Infinity, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, 100, 50, Infinity, 0, 100, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, 100, 50, 0, Infinity, 100, 50);
+ctx.drawImage(bitmap, 0, Infinity, 100, 50, 0, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, 0, Infinity, 100, 50, 0, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, 100, 50, 0, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, 100, 50, 0, 0, Infinity, 50);
+ctx.drawImage(bitmap, 0, Infinity, 100, 50, 0, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, 100, 50, 0, 0, 100, Infinity);
+ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, 0, 0, 100, 50);
+ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, Infinity, 0, 100, 50);
+ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, Infinity, Infinity, 100, 50);
+ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, Infinity, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, Infinity, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, Infinity, 0, Infinity, 50);
+ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, Infinity, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, Infinity, 0, 100, Infinity);
+ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, 0, Infinity, 100, 50);
+ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, 0, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, 0, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, 0, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, 0, 0, Infinity, 50);
+ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, 0, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, 0, 0, 100, Infinity);
+ctx.drawImage(bitmap, 0, 0, Infinity, 50, Infinity, 0, 100, 50);
+ctx.drawImage(bitmap, 0, 0, Infinity, 50, Infinity, Infinity, 100, 50);
+ctx.drawImage(bitmap, 0, 0, Infinity, 50, Infinity, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, 0, 0, Infinity, 50, Infinity, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, 0, Infinity, 50, Infinity, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, 0, 0, Infinity, 50, Infinity, 0, Infinity, 50);
+ctx.drawImage(bitmap, 0, 0, Infinity, 50, Infinity, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, 0, Infinity, 50, Infinity, 0, 100, Infinity);
+ctx.drawImage(bitmap, 0, 0, Infinity, 50, 0, Infinity, 100, 50);
+ctx.drawImage(bitmap, 0, 0, Infinity, 50, 0, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, 0, 0, Infinity, 50, 0, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, 0, Infinity, 50, 0, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, 0, 0, Infinity, 50, 0, 0, Infinity, 50);
+ctx.drawImage(bitmap, 0, 0, Infinity, 50, 0, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, 0, Infinity, 50, 0, 0, 100, Infinity);
+ctx.drawImage(bitmap, 0, 0, 100, Infinity, Infinity, 0, 100, 50);
+ctx.drawImage(bitmap, 0, 0, 100, Infinity, Infinity, Infinity, 100, 50);
+ctx.drawImage(bitmap, 0, 0, 100, Infinity, Infinity, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, 0, 0, 100, Infinity, Infinity, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, 0, 100, Infinity, Infinity, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, 0, 0, 100, Infinity, Infinity, 0, Infinity, 50);
+ctx.drawImage(bitmap, 0, 0, 100, Infinity, Infinity, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, 0, 100, Infinity, Infinity, 0, 100, Infinity);
+ctx.drawImage(bitmap, 0, 0, 100, Infinity, 0, Infinity, 100, 50);
+ctx.drawImage(bitmap, 0, 0, 100, Infinity, 0, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, 0, 0, 100, Infinity, 0, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, 0, 100, Infinity, 0, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, 0, 0, 100, Infinity, 0, 0, Infinity, 50);
+ctx.drawImage(bitmap, 0, 0, 100, Infinity, 0, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, 0, 100, Infinity, 0, 0, 100, Infinity);
+ctx.drawImage(bitmap, 0, 0, 100, 50, Infinity, Infinity, 100, 50);
+ctx.drawImage(bitmap, 0, 0, 100, 50, Infinity, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, 0, 0, 100, 50, Infinity, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, 0, 100, 50, Infinity, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, 0, 0, 100, 50, Infinity, 0, Infinity, 50);
+ctx.drawImage(bitmap, 0, 0, 100, 50, Infinity, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, 0, 100, 50, Infinity, 0, 100, Infinity);
+ctx.drawImage(bitmap, 0, 0, 100, 50, 0, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, 0, 0, 100, 50, 0, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, 0, 100, 50, 0, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, Infinity, Infinity);
+        _assertPixel(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.nonfinite.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.nonfinite.worker-expected.txt
deleted file mode 100644
index 7279047..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.nonfinite.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL drawImage() with Infinity/NaN is ignored Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.nonfinite.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.nonfinite.worker.js
index f5d2678..eaf58aa 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.nonfinite.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.nonfinite.worker.js
@@ -28,308 +28,310 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, Infinity, 0);
-ctx.drawImage(response, -Infinity, 0);
-ctx.drawImage(response, NaN, 0);
-ctx.drawImage(response, 0, Infinity);
-ctx.drawImage(response, 0, -Infinity);
-ctx.drawImage(response, 0, NaN);
-ctx.drawImage(response, Infinity, Infinity);
-    ctx.drawImage(response, Infinity, 0, 100, 50);
-ctx.drawImage(response, -Infinity, 0, 100, 50);
-ctx.drawImage(response, NaN, 0, 100, 50);
-ctx.drawImage(response, 0, Infinity, 100, 50);
-ctx.drawImage(response, 0, -Infinity, 100, 50);
-ctx.drawImage(response, 0, NaN, 100, 50);
-ctx.drawImage(response, 0, 0, Infinity, 50);
-ctx.drawImage(response, 0, 0, -Infinity, 50);
-ctx.drawImage(response, 0, 0, NaN, 50);
-ctx.drawImage(response, 0, 0, 100, Infinity);
-ctx.drawImage(response, 0, 0, 100, -Infinity);
-ctx.drawImage(response, 0, 0, 100, NaN);
-ctx.drawImage(response, Infinity, Infinity, 100, 50);
-ctx.drawImage(response, Infinity, Infinity, Infinity, 50);
-ctx.drawImage(response, Infinity, Infinity, Infinity, Infinity);
-ctx.drawImage(response, Infinity, Infinity, 100, Infinity);
-ctx.drawImage(response, Infinity, 0, Infinity, 50);
-ctx.drawImage(response, Infinity, 0, Infinity, Infinity);
-ctx.drawImage(response, Infinity, 0, 100, Infinity);
-ctx.drawImage(response, 0, Infinity, Infinity, 50);
-ctx.drawImage(response, 0, Infinity, Infinity, Infinity);
-ctx.drawImage(response, 0, Infinity, 100, Infinity);
-ctx.drawImage(response, 0, 0, Infinity, Infinity);
-    ctx.drawImage(response, Infinity, 0, 100, 50, 0, 0, 100, 50);
-ctx.drawImage(response, -Infinity, 0, 100, 50, 0, 0, 100, 50);
-ctx.drawImage(response, NaN, 0, 100, 50, 0, 0, 100, 50);
-ctx.drawImage(response, 0, Infinity, 100, 50, 0, 0, 100, 50);
-ctx.drawImage(response, 0, -Infinity, 100, 50, 0, 0, 100, 50);
-ctx.drawImage(response, 0, NaN, 100, 50, 0, 0, 100, 50);
-ctx.drawImage(response, 0, 0, Infinity, 50, 0, 0, 100, 50);
-ctx.drawImage(response, 0, 0, -Infinity, 50, 0, 0, 100, 50);
-ctx.drawImage(response, 0, 0, NaN, 50, 0, 0, 100, 50);
-ctx.drawImage(response, 0, 0, 100, Infinity, 0, 0, 100, 50);
-ctx.drawImage(response, 0, 0, 100, -Infinity, 0, 0, 100, 50);
-ctx.drawImage(response, 0, 0, 100, NaN, 0, 0, 100, 50);
-ctx.drawImage(response, 0, 0, 100, 50, Infinity, 0, 100, 50);
-ctx.drawImage(response, 0, 0, 100, 50, -Infinity, 0, 100, 50);
-ctx.drawImage(response, 0, 0, 100, 50, NaN, 0, 100, 50);
-ctx.drawImage(response, 0, 0, 100, 50, 0, Infinity, 100, 50);
-ctx.drawImage(response, 0, 0, 100, 50, 0, -Infinity, 100, 50);
-ctx.drawImage(response, 0, 0, 100, 50, 0, NaN, 100, 50);
-ctx.drawImage(response, 0, 0, 100, 50, 0, 0, Infinity, 50);
-ctx.drawImage(response, 0, 0, 100, 50, 0, 0, -Infinity, 50);
-ctx.drawImage(response, 0, 0, 100, 50, 0, 0, NaN, 50);
-ctx.drawImage(response, 0, 0, 100, 50, 0, 0, 100, Infinity);
-ctx.drawImage(response, 0, 0, 100, 50, 0, 0, 100, -Infinity);
-ctx.drawImage(response, 0, 0, 100, 50, 0, 0, 100, NaN);
-ctx.drawImage(response, Infinity, Infinity, 100, 50, 0, 0, 100, 50);
-ctx.drawImage(response, Infinity, Infinity, Infinity, 50, 0, 0, 100, 50);
-ctx.drawImage(response, Infinity, Infinity, Infinity, Infinity, 0, 0, 100, 50);
-ctx.drawImage(response, Infinity, Infinity, Infinity, Infinity, Infinity, 0, 100, 50);
-ctx.drawImage(response, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, 100, 50);
-ctx.drawImage(response, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, 50);
-ctx.drawImage(response, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity);
-ctx.drawImage(response, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, 100, Infinity);
-ctx.drawImage(response, Infinity, Infinity, Infinity, Infinity, Infinity, 0, Infinity, 50);
-ctx.drawImage(response, Infinity, Infinity, Infinity, Infinity, Infinity, 0, Infinity, Infinity);
-ctx.drawImage(response, Infinity, Infinity, Infinity, Infinity, Infinity, 0, 100, Infinity);
-ctx.drawImage(response, Infinity, Infinity, Infinity, Infinity, 0, Infinity, 100, 50);
-ctx.drawImage(response, Infinity, Infinity, Infinity, Infinity, 0, Infinity, Infinity, 50);
-ctx.drawImage(response, Infinity, Infinity, Infinity, Infinity, 0, Infinity, Infinity, Infinity);
-ctx.drawImage(response, Infinity, Infinity, Infinity, Infinity, 0, Infinity, 100, Infinity);
-ctx.drawImage(response, Infinity, Infinity, Infinity, Infinity, 0, 0, Infinity, 50);
-ctx.drawImage(response, Infinity, Infinity, Infinity, Infinity, 0, 0, Infinity, Infinity);
-ctx.drawImage(response, Infinity, Infinity, Infinity, Infinity, 0, 0, 100, Infinity);
-ctx.drawImage(response, Infinity, Infinity, Infinity, 50, Infinity, 0, 100, 50);
-ctx.drawImage(response, Infinity, Infinity, Infinity, 50, Infinity, Infinity, 100, 50);
-ctx.drawImage(response, Infinity, Infinity, Infinity, 50, Infinity, Infinity, Infinity, 50);
-ctx.drawImage(response, Infinity, Infinity, Infinity, 50, Infinity, Infinity, Infinity, Infinity);
-ctx.drawImage(response, Infinity, Infinity, Infinity, 50, Infinity, Infinity, 100, Infinity);
-ctx.drawImage(response, Infinity, Infinity, Infinity, 50, Infinity, 0, Infinity, 50);
-ctx.drawImage(response, Infinity, Infinity, Infinity, 50, Infinity, 0, Infinity, Infinity);
-ctx.drawImage(response, Infinity, Infinity, Infinity, 50, Infinity, 0, 100, Infinity);
-ctx.drawImage(response, Infinity, Infinity, Infinity, 50, 0, Infinity, 100, 50);
-ctx.drawImage(response, Infinity, Infinity, Infinity, 50, 0, Infinity, Infinity, 50);
-ctx.drawImage(response, Infinity, Infinity, Infinity, 50, 0, Infinity, Infinity, Infinity);
-ctx.drawImage(response, Infinity, Infinity, Infinity, 50, 0, Infinity, 100, Infinity);
-ctx.drawImage(response, Infinity, Infinity, Infinity, 50, 0, 0, Infinity, 50);
-ctx.drawImage(response, Infinity, Infinity, Infinity, 50, 0, 0, Infinity, Infinity);
-ctx.drawImage(response, Infinity, Infinity, Infinity, 50, 0, 0, 100, Infinity);
-ctx.drawImage(response, Infinity, Infinity, 100, Infinity, 0, 0, 100, 50);
-ctx.drawImage(response, Infinity, Infinity, 100, Infinity, Infinity, 0, 100, 50);
-ctx.drawImage(response, Infinity, Infinity, 100, Infinity, Infinity, Infinity, 100, 50);
-ctx.drawImage(response, Infinity, Infinity, 100, Infinity, Infinity, Infinity, Infinity, 50);
-ctx.drawImage(response, Infinity, Infinity, 100, Infinity, Infinity, Infinity, Infinity, Infinity);
-ctx.drawImage(response, Infinity, Infinity, 100, Infinity, Infinity, Infinity, 100, Infinity);
-ctx.drawImage(response, Infinity, Infinity, 100, Infinity, Infinity, 0, Infinity, 50);
-ctx.drawImage(response, Infinity, Infinity, 100, Infinity, Infinity, 0, Infinity, Infinity);
-ctx.drawImage(response, Infinity, Infinity, 100, Infinity, Infinity, 0, 100, Infinity);
-ctx.drawImage(response, Infinity, Infinity, 100, Infinity, 0, Infinity, 100, 50);
-ctx.drawImage(response, Infinity, Infinity, 100, Infinity, 0, Infinity, Infinity, 50);
-ctx.drawImage(response, Infinity, Infinity, 100, Infinity, 0, Infinity, Infinity, Infinity);
-ctx.drawImage(response, Infinity, Infinity, 100, Infinity, 0, Infinity, 100, Infinity);
-ctx.drawImage(response, Infinity, Infinity, 100, Infinity, 0, 0, Infinity, 50);
-ctx.drawImage(response, Infinity, Infinity, 100, Infinity, 0, 0, Infinity, Infinity);
-ctx.drawImage(response, Infinity, Infinity, 100, Infinity, 0, 0, 100, Infinity);
-ctx.drawImage(response, Infinity, Infinity, 100, 50, Infinity, 0, 100, 50);
-ctx.drawImage(response, Infinity, Infinity, 100, 50, Infinity, Infinity, 100, 50);
-ctx.drawImage(response, Infinity, Infinity, 100, 50, Infinity, Infinity, Infinity, 50);
-ctx.drawImage(response, Infinity, Infinity, 100, 50, Infinity, Infinity, Infinity, Infinity);
-ctx.drawImage(response, Infinity, Infinity, 100, 50, Infinity, Infinity, 100, Infinity);
-ctx.drawImage(response, Infinity, Infinity, 100, 50, Infinity, 0, Infinity, 50);
-ctx.drawImage(response, Infinity, Infinity, 100, 50, Infinity, 0, Infinity, Infinity);
-ctx.drawImage(response, Infinity, Infinity, 100, 50, Infinity, 0, 100, Infinity);
-ctx.drawImage(response, Infinity, Infinity, 100, 50, 0, Infinity, 100, 50);
-ctx.drawImage(response, Infinity, Infinity, 100, 50, 0, Infinity, Infinity, 50);
-ctx.drawImage(response, Infinity, Infinity, 100, 50, 0, Infinity, Infinity, Infinity);
-ctx.drawImage(response, Infinity, Infinity, 100, 50, 0, Infinity, 100, Infinity);
-ctx.drawImage(response, Infinity, Infinity, 100, 50, 0, 0, Infinity, 50);
-ctx.drawImage(response, Infinity, Infinity, 100, 50, 0, 0, Infinity, Infinity);
-ctx.drawImage(response, Infinity, Infinity, 100, 50, 0, 0, 100, Infinity);
-ctx.drawImage(response, Infinity, 0, Infinity, 50, 0, 0, 100, 50);
-ctx.drawImage(response, Infinity, 0, Infinity, Infinity, 0, 0, 100, 50);
-ctx.drawImage(response, Infinity, 0, Infinity, Infinity, Infinity, 0, 100, 50);
-ctx.drawImage(response, Infinity, 0, Infinity, Infinity, Infinity, Infinity, 100, 50);
-ctx.drawImage(response, Infinity, 0, Infinity, Infinity, Infinity, Infinity, Infinity, 50);
-ctx.drawImage(response, Infinity, 0, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity);
-ctx.drawImage(response, Infinity, 0, Infinity, Infinity, Infinity, Infinity, 100, Infinity);
-ctx.drawImage(response, Infinity, 0, Infinity, Infinity, Infinity, 0, Infinity, 50);
-ctx.drawImage(response, Infinity, 0, Infinity, Infinity, Infinity, 0, Infinity, Infinity);
-ctx.drawImage(response, Infinity, 0, Infinity, Infinity, Infinity, 0, 100, Infinity);
-ctx.drawImage(response, Infinity, 0, Infinity, Infinity, 0, Infinity, 100, 50);
-ctx.drawImage(response, Infinity, 0, Infinity, Infinity, 0, Infinity, Infinity, 50);
-ctx.drawImage(response, Infinity, 0, Infinity, Infinity, 0, Infinity, Infinity, Infinity);
-ctx.drawImage(response, Infinity, 0, Infinity, Infinity, 0, Infinity, 100, Infinity);
-ctx.drawImage(response, Infinity, 0, Infinity, Infinity, 0, 0, Infinity, 50);
-ctx.drawImage(response, Infinity, 0, Infinity, Infinity, 0, 0, Infinity, Infinity);
-ctx.drawImage(response, Infinity, 0, Infinity, Infinity, 0, 0, 100, Infinity);
-ctx.drawImage(response, Infinity, 0, Infinity, 50, Infinity, 0, 100, 50);
-ctx.drawImage(response, Infinity, 0, Infinity, 50, Infinity, Infinity, 100, 50);
-ctx.drawImage(response, Infinity, 0, Infinity, 50, Infinity, Infinity, Infinity, 50);
-ctx.drawImage(response, Infinity, 0, Infinity, 50, Infinity, Infinity, Infinity, Infinity);
-ctx.drawImage(response, Infinity, 0, Infinity, 50, Infinity, Infinity, 100, Infinity);
-ctx.drawImage(response, Infinity, 0, Infinity, 50, Infinity, 0, Infinity, 50);
-ctx.drawImage(response, Infinity, 0, Infinity, 50, Infinity, 0, Infinity, Infinity);
-ctx.drawImage(response, Infinity, 0, Infinity, 50, Infinity, 0, 100, Infinity);
-ctx.drawImage(response, Infinity, 0, Infinity, 50, 0, Infinity, 100, 50);
-ctx.drawImage(response, Infinity, 0, Infinity, 50, 0, Infinity, Infinity, 50);
-ctx.drawImage(response, Infinity, 0, Infinity, 50, 0, Infinity, Infinity, Infinity);
-ctx.drawImage(response, Infinity, 0, Infinity, 50, 0, Infinity, 100, Infinity);
-ctx.drawImage(response, Infinity, 0, Infinity, 50, 0, 0, Infinity, 50);
-ctx.drawImage(response, Infinity, 0, Infinity, 50, 0, 0, Infinity, Infinity);
-ctx.drawImage(response, Infinity, 0, Infinity, 50, 0, 0, 100, Infinity);
-ctx.drawImage(response, Infinity, 0, 100, Infinity, 0, 0, 100, 50);
-ctx.drawImage(response, Infinity, 0, 100, Infinity, Infinity, 0, 100, 50);
-ctx.drawImage(response, Infinity, 0, 100, Infinity, Infinity, Infinity, 100, 50);
-ctx.drawImage(response, Infinity, 0, 100, Infinity, Infinity, Infinity, Infinity, 50);
-ctx.drawImage(response, Infinity, 0, 100, Infinity, Infinity, Infinity, Infinity, Infinity);
-ctx.drawImage(response, Infinity, 0, 100, Infinity, Infinity, Infinity, 100, Infinity);
-ctx.drawImage(response, Infinity, 0, 100, Infinity, Infinity, 0, Infinity, 50);
-ctx.drawImage(response, Infinity, 0, 100, Infinity, Infinity, 0, Infinity, Infinity);
-ctx.drawImage(response, Infinity, 0, 100, Infinity, Infinity, 0, 100, Infinity);
-ctx.drawImage(response, Infinity, 0, 100, Infinity, 0, Infinity, 100, 50);
-ctx.drawImage(response, Infinity, 0, 100, Infinity, 0, Infinity, Infinity, 50);
-ctx.drawImage(response, Infinity, 0, 100, Infinity, 0, Infinity, Infinity, Infinity);
-ctx.drawImage(response, Infinity, 0, 100, Infinity, 0, Infinity, 100, Infinity);
-ctx.drawImage(response, Infinity, 0, 100, Infinity, 0, 0, Infinity, 50);
-ctx.drawImage(response, Infinity, 0, 100, Infinity, 0, 0, Infinity, Infinity);
-ctx.drawImage(response, Infinity, 0, 100, Infinity, 0, 0, 100, Infinity);
-ctx.drawImage(response, Infinity, 0, 100, 50, Infinity, 0, 100, 50);
-ctx.drawImage(response, Infinity, 0, 100, 50, Infinity, Infinity, 100, 50);
-ctx.drawImage(response, Infinity, 0, 100, 50, Infinity, Infinity, Infinity, 50);
-ctx.drawImage(response, Infinity, 0, 100, 50, Infinity, Infinity, Infinity, Infinity);
-ctx.drawImage(response, Infinity, 0, 100, 50, Infinity, Infinity, 100, Infinity);
-ctx.drawImage(response, Infinity, 0, 100, 50, Infinity, 0, Infinity, 50);
-ctx.drawImage(response, Infinity, 0, 100, 50, Infinity, 0, Infinity, Infinity);
-ctx.drawImage(response, Infinity, 0, 100, 50, Infinity, 0, 100, Infinity);
-ctx.drawImage(response, Infinity, 0, 100, 50, 0, Infinity, 100, 50);
-ctx.drawImage(response, Infinity, 0, 100, 50, 0, Infinity, Infinity, 50);
-ctx.drawImage(response, Infinity, 0, 100, 50, 0, Infinity, Infinity, Infinity);
-ctx.drawImage(response, Infinity, 0, 100, 50, 0, Infinity, 100, Infinity);
-ctx.drawImage(response, Infinity, 0, 100, 50, 0, 0, Infinity, 50);
-ctx.drawImage(response, Infinity, 0, 100, 50, 0, 0, Infinity, Infinity);
-ctx.drawImage(response, Infinity, 0, 100, 50, 0, 0, 100, Infinity);
-ctx.drawImage(response, 0, Infinity, Infinity, 50, 0, 0, 100, 50);
-ctx.drawImage(response, 0, Infinity, Infinity, Infinity, 0, 0, 100, 50);
-ctx.drawImage(response, 0, Infinity, Infinity, Infinity, Infinity, 0, 100, 50);
-ctx.drawImage(response, 0, Infinity, Infinity, Infinity, Infinity, Infinity, 100, 50);
-ctx.drawImage(response, 0, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, 50);
-ctx.drawImage(response, 0, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity);
-ctx.drawImage(response, 0, Infinity, Infinity, Infinity, Infinity, Infinity, 100, Infinity);
-ctx.drawImage(response, 0, Infinity, Infinity, Infinity, Infinity, 0, Infinity, 50);
-ctx.drawImage(response, 0, Infinity, Infinity, Infinity, Infinity, 0, Infinity, Infinity);
-ctx.drawImage(response, 0, Infinity, Infinity, Infinity, Infinity, 0, 100, Infinity);
-ctx.drawImage(response, 0, Infinity, Infinity, Infinity, 0, Infinity, 100, 50);
-ctx.drawImage(response, 0, Infinity, Infinity, Infinity, 0, Infinity, Infinity, 50);
-ctx.drawImage(response, 0, Infinity, Infinity, Infinity, 0, Infinity, Infinity, Infinity);
-ctx.drawImage(response, 0, Infinity, Infinity, Infinity, 0, Infinity, 100, Infinity);
-ctx.drawImage(response, 0, Infinity, Infinity, Infinity, 0, 0, Infinity, 50);
-ctx.drawImage(response, 0, Infinity, Infinity, Infinity, 0, 0, Infinity, Infinity);
-ctx.drawImage(response, 0, Infinity, Infinity, Infinity, 0, 0, 100, Infinity);
-ctx.drawImage(response, 0, Infinity, Infinity, 50, Infinity, 0, 100, 50);
-ctx.drawImage(response, 0, Infinity, Infinity, 50, Infinity, Infinity, 100, 50);
-ctx.drawImage(response, 0, Infinity, Infinity, 50, Infinity, Infinity, Infinity, 50);
-ctx.drawImage(response, 0, Infinity, Infinity, 50, Infinity, Infinity, Infinity, Infinity);
-ctx.drawImage(response, 0, Infinity, Infinity, 50, Infinity, Infinity, 100, Infinity);
-ctx.drawImage(response, 0, Infinity, Infinity, 50, Infinity, 0, Infinity, 50);
-ctx.drawImage(response, 0, Infinity, Infinity, 50, Infinity, 0, Infinity, Infinity);
-ctx.drawImage(response, 0, Infinity, Infinity, 50, Infinity, 0, 100, Infinity);
-ctx.drawImage(response, 0, Infinity, Infinity, 50, 0, Infinity, 100, 50);
-ctx.drawImage(response, 0, Infinity, Infinity, 50, 0, Infinity, Infinity, 50);
-ctx.drawImage(response, 0, Infinity, Infinity, 50, 0, Infinity, Infinity, Infinity);
-ctx.drawImage(response, 0, Infinity, Infinity, 50, 0, Infinity, 100, Infinity);
-ctx.drawImage(response, 0, Infinity, Infinity, 50, 0, 0, Infinity, 50);
-ctx.drawImage(response, 0, Infinity, Infinity, 50, 0, 0, Infinity, Infinity);
-ctx.drawImage(response, 0, Infinity, Infinity, 50, 0, 0, 100, Infinity);
-ctx.drawImage(response, 0, Infinity, 100, Infinity, 0, 0, 100, 50);
-ctx.drawImage(response, 0, Infinity, 100, Infinity, Infinity, 0, 100, 50);
-ctx.drawImage(response, 0, Infinity, 100, Infinity, Infinity, Infinity, 100, 50);
-ctx.drawImage(response, 0, Infinity, 100, Infinity, Infinity, Infinity, Infinity, 50);
-ctx.drawImage(response, 0, Infinity, 100, Infinity, Infinity, Infinity, Infinity, Infinity);
-ctx.drawImage(response, 0, Infinity, 100, Infinity, Infinity, Infinity, 100, Infinity);
-ctx.drawImage(response, 0, Infinity, 100, Infinity, Infinity, 0, Infinity, 50);
-ctx.drawImage(response, 0, Infinity, 100, Infinity, Infinity, 0, Infinity, Infinity);
-ctx.drawImage(response, 0, Infinity, 100, Infinity, Infinity, 0, 100, Infinity);
-ctx.drawImage(response, 0, Infinity, 100, Infinity, 0, Infinity, 100, 50);
-ctx.drawImage(response, 0, Infinity, 100, Infinity, 0, Infinity, Infinity, 50);
-ctx.drawImage(response, 0, Infinity, 100, Infinity, 0, Infinity, Infinity, Infinity);
-ctx.drawImage(response, 0, Infinity, 100, Infinity, 0, Infinity, 100, Infinity);
-ctx.drawImage(response, 0, Infinity, 100, Infinity, 0, 0, Infinity, 50);
-ctx.drawImage(response, 0, Infinity, 100, Infinity, 0, 0, Infinity, Infinity);
-ctx.drawImage(response, 0, Infinity, 100, Infinity, 0, 0, 100, Infinity);
-ctx.drawImage(response, 0, Infinity, 100, 50, Infinity, 0, 100, 50);
-ctx.drawImage(response, 0, Infinity, 100, 50, Infinity, Infinity, 100, 50);
-ctx.drawImage(response, 0, Infinity, 100, 50, Infinity, Infinity, Infinity, 50);
-ctx.drawImage(response, 0, Infinity, 100, 50, Infinity, Infinity, Infinity, Infinity);
-ctx.drawImage(response, 0, Infinity, 100, 50, Infinity, Infinity, 100, Infinity);
-ctx.drawImage(response, 0, Infinity, 100, 50, Infinity, 0, Infinity, 50);
-ctx.drawImage(response, 0, Infinity, 100, 50, Infinity, 0, Infinity, Infinity);
-ctx.drawImage(response, 0, Infinity, 100, 50, Infinity, 0, 100, Infinity);
-ctx.drawImage(response, 0, Infinity, 100, 50, 0, Infinity, 100, 50);
-ctx.drawImage(response, 0, Infinity, 100, 50, 0, Infinity, Infinity, 50);
-ctx.drawImage(response, 0, Infinity, 100, 50, 0, Infinity, Infinity, Infinity);
-ctx.drawImage(response, 0, Infinity, 100, 50, 0, Infinity, 100, Infinity);
-ctx.drawImage(response, 0, Infinity, 100, 50, 0, 0, Infinity, 50);
-ctx.drawImage(response, 0, Infinity, 100, 50, 0, 0, Infinity, Infinity);
-ctx.drawImage(response, 0, Infinity, 100, 50, 0, 0, 100, Infinity);
-ctx.drawImage(response, 0, 0, Infinity, Infinity, 0, 0, 100, 50);
-ctx.drawImage(response, 0, 0, Infinity, Infinity, Infinity, 0, 100, 50);
-ctx.drawImage(response, 0, 0, Infinity, Infinity, Infinity, Infinity, 100, 50);
-ctx.drawImage(response, 0, 0, Infinity, Infinity, Infinity, Infinity, Infinity, 50);
-ctx.drawImage(response, 0, 0, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity);
-ctx.drawImage(response, 0, 0, Infinity, Infinity, Infinity, Infinity, 100, Infinity);
-ctx.drawImage(response, 0, 0, Infinity, Infinity, Infinity, 0, Infinity, 50);
-ctx.drawImage(response, 0, 0, Infinity, Infinity, Infinity, 0, Infinity, Infinity);
-ctx.drawImage(response, 0, 0, Infinity, Infinity, Infinity, 0, 100, Infinity);
-ctx.drawImage(response, 0, 0, Infinity, Infinity, 0, Infinity, 100, 50);
-ctx.drawImage(response, 0, 0, Infinity, Infinity, 0, Infinity, Infinity, 50);
-ctx.drawImage(response, 0, 0, Infinity, Infinity, 0, Infinity, Infinity, Infinity);
-ctx.drawImage(response, 0, 0, Infinity, Infinity, 0, Infinity, 100, Infinity);
-ctx.drawImage(response, 0, 0, Infinity, Infinity, 0, 0, Infinity, 50);
-ctx.drawImage(response, 0, 0, Infinity, Infinity, 0, 0, Infinity, Infinity);
-ctx.drawImage(response, 0, 0, Infinity, Infinity, 0, 0, 100, Infinity);
-ctx.drawImage(response, 0, 0, Infinity, 50, Infinity, 0, 100, 50);
-ctx.drawImage(response, 0, 0, Infinity, 50, Infinity, Infinity, 100, 50);
-ctx.drawImage(response, 0, 0, Infinity, 50, Infinity, Infinity, Infinity, 50);
-ctx.drawImage(response, 0, 0, Infinity, 50, Infinity, Infinity, Infinity, Infinity);
-ctx.drawImage(response, 0, 0, Infinity, 50, Infinity, Infinity, 100, Infinity);
-ctx.drawImage(response, 0, 0, Infinity, 50, Infinity, 0, Infinity, 50);
-ctx.drawImage(response, 0, 0, Infinity, 50, Infinity, 0, Infinity, Infinity);
-ctx.drawImage(response, 0, 0, Infinity, 50, Infinity, 0, 100, Infinity);
-ctx.drawImage(response, 0, 0, Infinity, 50, 0, Infinity, 100, 50);
-ctx.drawImage(response, 0, 0, Infinity, 50, 0, Infinity, Infinity, 50);
-ctx.drawImage(response, 0, 0, Infinity, 50, 0, Infinity, Infinity, Infinity);
-ctx.drawImage(response, 0, 0, Infinity, 50, 0, Infinity, 100, Infinity);
-ctx.drawImage(response, 0, 0, Infinity, 50, 0, 0, Infinity, 50);
-ctx.drawImage(response, 0, 0, Infinity, 50, 0, 0, Infinity, Infinity);
-ctx.drawImage(response, 0, 0, Infinity, 50, 0, 0, 100, Infinity);
-ctx.drawImage(response, 0, 0, 100, Infinity, Infinity, 0, 100, 50);
-ctx.drawImage(response, 0, 0, 100, Infinity, Infinity, Infinity, 100, 50);
-ctx.drawImage(response, 0, 0, 100, Infinity, Infinity, Infinity, Infinity, 50);
-ctx.drawImage(response, 0, 0, 100, Infinity, Infinity, Infinity, Infinity, Infinity);
-ctx.drawImage(response, 0, 0, 100, Infinity, Infinity, Infinity, 100, Infinity);
-ctx.drawImage(response, 0, 0, 100, Infinity, Infinity, 0, Infinity, 50);
-ctx.drawImage(response, 0, 0, 100, Infinity, Infinity, 0, Infinity, Infinity);
-ctx.drawImage(response, 0, 0, 100, Infinity, Infinity, 0, 100, Infinity);
-ctx.drawImage(response, 0, 0, 100, Infinity, 0, Infinity, 100, 50);
-ctx.drawImage(response, 0, 0, 100, Infinity, 0, Infinity, Infinity, 50);
-ctx.drawImage(response, 0, 0, 100, Infinity, 0, Infinity, Infinity, Infinity);
-ctx.drawImage(response, 0, 0, 100, Infinity, 0, Infinity, 100, Infinity);
-ctx.drawImage(response, 0, 0, 100, Infinity, 0, 0, Infinity, 50);
-ctx.drawImage(response, 0, 0, 100, Infinity, 0, 0, Infinity, Infinity);
-ctx.drawImage(response, 0, 0, 100, Infinity, 0, 0, 100, Infinity);
-ctx.drawImage(response, 0, 0, 100, 50, Infinity, Infinity, 100, 50);
-ctx.drawImage(response, 0, 0, 100, 50, Infinity, Infinity, Infinity, 50);
-ctx.drawImage(response, 0, 0, 100, 50, Infinity, Infinity, Infinity, Infinity);
-ctx.drawImage(response, 0, 0, 100, 50, Infinity, Infinity, 100, Infinity);
-ctx.drawImage(response, 0, 0, 100, 50, Infinity, 0, Infinity, 50);
-ctx.drawImage(response, 0, 0, 100, 50, Infinity, 0, Infinity, Infinity);
-ctx.drawImage(response, 0, 0, 100, 50, Infinity, 0, 100, Infinity);
-ctx.drawImage(response, 0, 0, 100, 50, 0, Infinity, Infinity, 50);
-ctx.drawImage(response, 0, 0, 100, 50, 0, Infinity, Infinity, Infinity);
-ctx.drawImage(response, 0, 0, 100, 50, 0, Infinity, 100, Infinity);
-ctx.drawImage(response, 0, 0, 100, 50, 0, 0, Infinity, Infinity);
-    _assertPixel(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, Infinity, 0);
+ctx.drawImage(bitmap, -Infinity, 0);
+ctx.drawImage(bitmap, NaN, 0);
+ctx.drawImage(bitmap, 0, Infinity);
+ctx.drawImage(bitmap, 0, -Infinity);
+ctx.drawImage(bitmap, 0, NaN);
+ctx.drawImage(bitmap, Infinity, Infinity);
+        ctx.drawImage(bitmap, Infinity, 0, 100, 50);
+ctx.drawImage(bitmap, -Infinity, 0, 100, 50);
+ctx.drawImage(bitmap, NaN, 0, 100, 50);
+ctx.drawImage(bitmap, 0, Infinity, 100, 50);
+ctx.drawImage(bitmap, 0, -Infinity, 100, 50);
+ctx.drawImage(bitmap, 0, NaN, 100, 50);
+ctx.drawImage(bitmap, 0, 0, Infinity, 50);
+ctx.drawImage(bitmap, 0, 0, -Infinity, 50);
+ctx.drawImage(bitmap, 0, 0, NaN, 50);
+ctx.drawImage(bitmap, 0, 0, 100, Infinity);
+ctx.drawImage(bitmap, 0, 0, 100, -Infinity);
+ctx.drawImage(bitmap, 0, 0, 100, NaN);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, 100, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, 0, 0, Infinity, Infinity);
+        ctx.drawImage(bitmap, Infinity, 0, 100, 50, 0, 0, 100, 50);
+ctx.drawImage(bitmap, -Infinity, 0, 100, 50, 0, 0, 100, 50);
+ctx.drawImage(bitmap, NaN, 0, 100, 50, 0, 0, 100, 50);
+ctx.drawImage(bitmap, 0, Infinity, 100, 50, 0, 0, 100, 50);
+ctx.drawImage(bitmap, 0, -Infinity, 100, 50, 0, 0, 100, 50);
+ctx.drawImage(bitmap, 0, NaN, 100, 50, 0, 0, 100, 50);
+ctx.drawImage(bitmap, 0, 0, Infinity, 50, 0, 0, 100, 50);
+ctx.drawImage(bitmap, 0, 0, -Infinity, 50, 0, 0, 100, 50);
+ctx.drawImage(bitmap, 0, 0, NaN, 50, 0, 0, 100, 50);
+ctx.drawImage(bitmap, 0, 0, 100, Infinity, 0, 0, 100, 50);
+ctx.drawImage(bitmap, 0, 0, 100, -Infinity, 0, 0, 100, 50);
+ctx.drawImage(bitmap, 0, 0, 100, NaN, 0, 0, 100, 50);
+ctx.drawImage(bitmap, 0, 0, 100, 50, Infinity, 0, 100, 50);
+ctx.drawImage(bitmap, 0, 0, 100, 50, -Infinity, 0, 100, 50);
+ctx.drawImage(bitmap, 0, 0, 100, 50, NaN, 0, 100, 50);
+ctx.drawImage(bitmap, 0, 0, 100, 50, 0, Infinity, 100, 50);
+ctx.drawImage(bitmap, 0, 0, 100, 50, 0, -Infinity, 100, 50);
+ctx.drawImage(bitmap, 0, 0, 100, 50, 0, NaN, 100, 50);
+ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, Infinity, 50);
+ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, -Infinity, 50);
+ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, NaN, 50);
+ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, 100, Infinity);
+ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, 100, -Infinity);
+ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, 100, NaN);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, 0, 0, 100, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, 0, 0, 100, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, 0, 0, 100, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, Infinity, 0, 100, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, 100, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, Infinity, 0, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, Infinity, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, Infinity, 0, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, 0, Infinity, 100, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, 0, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, 0, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, 0, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, 0, 0, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, 0, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, Infinity, 0, 0, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, Infinity, 0, 100, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, Infinity, Infinity, 100, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, Infinity, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, Infinity, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, Infinity, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, Infinity, 0, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, Infinity, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, Infinity, 0, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, 0, Infinity, 100, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, 0, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, 0, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, 0, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, 0, 0, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, 0, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, Infinity, 50, 0, 0, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, 0, 0, 100, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, Infinity, 0, 100, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, Infinity, Infinity, 100, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, Infinity, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, Infinity, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, Infinity, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, Infinity, 0, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, Infinity, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, Infinity, 0, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, 0, Infinity, 100, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, 0, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, 0, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, 0, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, 0, 0, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, 0, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, Infinity, 0, 0, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, Infinity, 0, 100, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, Infinity, Infinity, 100, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, Infinity, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, Infinity, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, Infinity, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, Infinity, 0, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, Infinity, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, Infinity, 0, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, 0, Infinity, 100, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, 0, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, 0, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, 0, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, 0, 0, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, 0, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, Infinity, 100, 50, 0, 0, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, 0, 0, 100, 50);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, 0, 0, 100, 50);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, Infinity, 0, 100, 50);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, Infinity, Infinity, 100, 50);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, Infinity, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, Infinity, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, Infinity, 0, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, Infinity, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, Infinity, 0, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, 0, Infinity, 100, 50);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, 0, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, 0, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, 0, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, 0, 0, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, 0, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, Infinity, 0, 0, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, Infinity, 0, 100, 50);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, Infinity, Infinity, 100, 50);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, Infinity, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, Infinity, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, Infinity, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, Infinity, 0, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, Infinity, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, Infinity, 0, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, 0, Infinity, 100, 50);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, 0, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, 0, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, 0, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, 0, 0, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, 0, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, Infinity, 50, 0, 0, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, 0, 0, 100, 50);
+ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, Infinity, 0, 100, 50);
+ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, Infinity, Infinity, 100, 50);
+ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, Infinity, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, Infinity, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, Infinity, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, Infinity, 0, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, Infinity, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, Infinity, 0, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, 0, Infinity, 100, 50);
+ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, 0, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, 0, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, 0, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, 0, 0, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, 0, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, 100, Infinity, 0, 0, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, 100, 50, Infinity, 0, 100, 50);
+ctx.drawImage(bitmap, Infinity, 0, 100, 50, Infinity, Infinity, 100, 50);
+ctx.drawImage(bitmap, Infinity, 0, 100, 50, Infinity, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, 0, 100, 50, Infinity, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, 100, 50, Infinity, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, 100, 50, Infinity, 0, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, 0, 100, 50, Infinity, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, 100, 50, Infinity, 0, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, 100, 50, 0, Infinity, 100, 50);
+ctx.drawImage(bitmap, Infinity, 0, 100, 50, 0, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, 0, 100, 50, 0, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, 100, 50, 0, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, 100, 50, 0, 0, Infinity, 50);
+ctx.drawImage(bitmap, Infinity, 0, 100, 50, 0, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, Infinity, 0, 100, 50, 0, 0, 100, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, 0, 0, 100, 50);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, 0, 0, 100, 50);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, Infinity, 0, 100, 50);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, Infinity, Infinity, 100, 50);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, Infinity, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, Infinity, 0, Infinity, 50);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, Infinity, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, Infinity, 0, 100, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, 0, Infinity, 100, 50);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, 0, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, 0, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, 0, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, 0, 0, Infinity, 50);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, 0, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, Infinity, 0, 0, 100, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, Infinity, 0, 100, 50);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, Infinity, Infinity, 100, 50);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, Infinity, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, Infinity, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, Infinity, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, Infinity, 0, Infinity, 50);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, Infinity, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, Infinity, 0, 100, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, 0, Infinity, 100, 50);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, 0, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, 0, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, 0, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, 0, 0, Infinity, 50);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, 0, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, Infinity, 50, 0, 0, 100, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, 0, 0, 100, 50);
+ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, Infinity, 0, 100, 50);
+ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, Infinity, Infinity, 100, 50);
+ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, Infinity, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, Infinity, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, Infinity, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, Infinity, 0, Infinity, 50);
+ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, Infinity, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, Infinity, 0, 100, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, 0, Infinity, 100, 50);
+ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, 0, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, 0, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, 0, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, 0, 0, Infinity, 50);
+ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, 0, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, 100, Infinity, 0, 0, 100, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, 100, 50, Infinity, 0, 100, 50);
+ctx.drawImage(bitmap, 0, Infinity, 100, 50, Infinity, Infinity, 100, 50);
+ctx.drawImage(bitmap, 0, Infinity, 100, 50, Infinity, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, 0, Infinity, 100, 50, Infinity, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, 100, 50, Infinity, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, 100, 50, Infinity, 0, Infinity, 50);
+ctx.drawImage(bitmap, 0, Infinity, 100, 50, Infinity, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, 100, 50, Infinity, 0, 100, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, 100, 50, 0, Infinity, 100, 50);
+ctx.drawImage(bitmap, 0, Infinity, 100, 50, 0, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, 0, Infinity, 100, 50, 0, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, 100, 50, 0, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, 100, 50, 0, 0, Infinity, 50);
+ctx.drawImage(bitmap, 0, Infinity, 100, 50, 0, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, Infinity, 100, 50, 0, 0, 100, Infinity);
+ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, 0, 0, 100, 50);
+ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, Infinity, 0, 100, 50);
+ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, Infinity, Infinity, 100, 50);
+ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, Infinity, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, Infinity, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, Infinity, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, Infinity, 0, Infinity, 50);
+ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, Infinity, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, Infinity, 0, 100, Infinity);
+ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, 0, Infinity, 100, 50);
+ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, 0, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, 0, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, 0, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, 0, 0, Infinity, 50);
+ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, 0, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, 0, Infinity, Infinity, 0, 0, 100, Infinity);
+ctx.drawImage(bitmap, 0, 0, Infinity, 50, Infinity, 0, 100, 50);
+ctx.drawImage(bitmap, 0, 0, Infinity, 50, Infinity, Infinity, 100, 50);
+ctx.drawImage(bitmap, 0, 0, Infinity, 50, Infinity, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, 0, 0, Infinity, 50, Infinity, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, 0, Infinity, 50, Infinity, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, 0, 0, Infinity, 50, Infinity, 0, Infinity, 50);
+ctx.drawImage(bitmap, 0, 0, Infinity, 50, Infinity, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, 0, Infinity, 50, Infinity, 0, 100, Infinity);
+ctx.drawImage(bitmap, 0, 0, Infinity, 50, 0, Infinity, 100, 50);
+ctx.drawImage(bitmap, 0, 0, Infinity, 50, 0, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, 0, 0, Infinity, 50, 0, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, 0, Infinity, 50, 0, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, 0, 0, Infinity, 50, 0, 0, Infinity, 50);
+ctx.drawImage(bitmap, 0, 0, Infinity, 50, 0, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, 0, Infinity, 50, 0, 0, 100, Infinity);
+ctx.drawImage(bitmap, 0, 0, 100, Infinity, Infinity, 0, 100, 50);
+ctx.drawImage(bitmap, 0, 0, 100, Infinity, Infinity, Infinity, 100, 50);
+ctx.drawImage(bitmap, 0, 0, 100, Infinity, Infinity, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, 0, 0, 100, Infinity, Infinity, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, 0, 100, Infinity, Infinity, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, 0, 0, 100, Infinity, Infinity, 0, Infinity, 50);
+ctx.drawImage(bitmap, 0, 0, 100, Infinity, Infinity, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, 0, 100, Infinity, Infinity, 0, 100, Infinity);
+ctx.drawImage(bitmap, 0, 0, 100, Infinity, 0, Infinity, 100, 50);
+ctx.drawImage(bitmap, 0, 0, 100, Infinity, 0, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, 0, 0, 100, Infinity, 0, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, 0, 100, Infinity, 0, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, 0, 0, 100, Infinity, 0, 0, Infinity, 50);
+ctx.drawImage(bitmap, 0, 0, 100, Infinity, 0, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, 0, 100, Infinity, 0, 0, 100, Infinity);
+ctx.drawImage(bitmap, 0, 0, 100, 50, Infinity, Infinity, 100, 50);
+ctx.drawImage(bitmap, 0, 0, 100, 50, Infinity, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, 0, 0, 100, 50, Infinity, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, 0, 100, 50, Infinity, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, 0, 0, 100, 50, Infinity, 0, Infinity, 50);
+ctx.drawImage(bitmap, 0, 0, 100, 50, Infinity, 0, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, 0, 100, 50, Infinity, 0, 100, Infinity);
+ctx.drawImage(bitmap, 0, 0, 100, 50, 0, Infinity, Infinity, 50);
+ctx.drawImage(bitmap, 0, 0, 100, 50, 0, Infinity, Infinity, Infinity);
+ctx.drawImage(bitmap, 0, 0, 100, 50, 0, Infinity, 100, Infinity);
+ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, Infinity, Infinity);
+        _assertPixel(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.nowrap-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.nowrap-expected.txt
deleted file mode 100644
index 5f13a80..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.nowrap-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Stretched images do not get pixels wrapping around the edges Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.nowrap.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.nowrap.html
index 4d71c21b..f0dfb298 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.nowrap.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.nowrap.html
@@ -33,10 +33,12 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, -1950, 0, 2000, 50);
-    _assertPixelApprox(offscreenCanvas, 45,25, 0,255,0,255, "45,25", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 55,25, 0,255,0,255, "55,25", "0,255,0,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, -1950, 0, 2000, 50);
+        _assertPixelApprox(offscreenCanvas, 45,25, 0,255,0,255, "45,25", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 55,25, 0,255,0,255, "55,25", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.nowrap.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.nowrap.worker-expected.txt
deleted file mode 100644
index 5f13a80..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.nowrap.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Stretched images do not get pixels wrapping around the edges Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.nowrap.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.nowrap.worker.js
index 6f6b1e9..aab0877 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.nowrap.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.nowrap.worker.js
@@ -28,10 +28,12 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, -1950, 0, 2000, 50);
-    _assertPixelApprox(offscreenCanvas, 45,25, 0,255,0,255, "45,25", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 55,25, 0,255,0,255, "55,25", "0,255,0,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, -1950, 0, 2000, 50);
+        _assertPixelApprox(offscreenCanvas, 45,25, 0,255,0,255, "45,25", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 55,25, 0,255,0,255, "55,25", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.path-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.path-expected.txt
deleted file mode 100644
index f621096..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.path-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.drawImage.path Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.path.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.path.html
index 47763cd..10daf12 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.path.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.path.html
@@ -33,9 +33,11 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0);
-    ctx.fill();
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0);
+        ctx.fill();
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.path.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.path.worker-expected.txt
deleted file mode 100644
index 2e526ad..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.path.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.path.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.path.worker.js
index ba8302b..45a08297 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.path.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.path.worker.js
@@ -28,9 +28,11 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0);
-    ctx.fill();
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0);
+        ctx.fill();
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.svg-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.svg-expected.txt
deleted file mode 100644
index cf858bbb3..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.svg-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL drawImage() of an SVG image Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.svg.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.svg.html
index ff83aa0..72ba2ec 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.svg.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.svg.html
@@ -31,8 +31,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.svg.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.svg.worker-expected.txt
deleted file mode 100644
index cf858bbb3..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.svg.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL drawImage() of an SVG image Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.svg.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.svg.worker.js
index 382190e..50892c2 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.svg.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.svg.worker.js
@@ -26,8 +26,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.transform-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.transform-expected.txt
deleted file mode 100644
index 7dd986d..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.transform-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.drawImage.transform Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.transform.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.transform.html
index 2adcf02..b782ba8 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.transform.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.transform.html
@@ -34,8 +34,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.transform.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.transform.worker-expected.txt
deleted file mode 100644
index 2e526ad..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.transform.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.transform.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.transform.worker.js
index 5e1095b..a4cb865 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.transform.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.transform.worker.js
@@ -29,8 +29,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerosource-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerosource-expected.txt
deleted file mode 100644
index 693d3f6b..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerosource-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL drawImage with zero-sized source rectangle throws INDEX_SIZE_ERR assert_throws: function "function() { ctx.drawImage(response, 10, 10, 0, 1, 0, 0, 100, 50); }" threw object "TypeError: Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'" that is not a DOMException INDEX_SIZE_ERR: property "code" is equal to undefined, expected 1
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerosource.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerosource.html
index 645e2c97..b941f3a 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerosource.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerosource.html
@@ -33,10 +33,12 @@
     };
 });
 promise.then(function(response) {
-    assert_throws("INDEX_SIZE_ERR", function() { ctx.drawImage(response, 10, 10, 0, 1, 0, 0, 100, 50); });
-    assert_throws("INDEX_SIZE_ERR", function() { ctx.drawImage(response, 10, 10, 1, 0, 0, 0, 100, 50); });
-    assert_throws("INDEX_SIZE_ERR", function() { ctx.drawImage(response, 10, 10, 0, 0, 0, 0, 100, 50); });
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        assert_throws("INDEX_SIZE_ERR", function() { ctx.drawImage(bitmap, 10, 10, 0, 1, 0, 0, 100, 50); });
+        assert_throws("INDEX_SIZE_ERR", function() { ctx.drawImage(bitmap, 10, 10, 1, 0, 0, 0, 100, 50); });
+        assert_throws("INDEX_SIZE_ERR", function() { ctx.drawImage(bitmap, 10, 10, 0, 0, 0, 0, 100, 50); });
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerosource.image-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerosource.image-expected.txt
deleted file mode 100644
index aa2f43c..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerosource.image-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL drawImage with zero-sized source rectangle from image throws INDEX_SIZE_ERR assert_throws: function "function() { ctx.drawImage(response, 0, 0, 100, 50); }" threw object "TypeError: Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'" that is not a DOMException INDEX_SIZE_ERR: property "code" is equal to undefined, expected 1
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerosource.image.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerosource.image.html
index a38e982..1abcb6ab 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerosource.image.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerosource.image.html
@@ -33,10 +33,12 @@
     };
 });
 promise.then(function(response) {
-    assert_throws("INDEX_SIZE_ERR", function() { ctx.drawImage(response, 0, 0, 100, 50); });
-    assert_throws("INDEX_SIZE_ERR", function() { ctx.drawImage(response, 0, 0, 100, 50); });
-    assert_throws("INDEX_SIZE_ERR", function() { ctx.drawImage(response, 0, 0, 100, 50); });
-    _assertPixel(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        assert_throws("INDEX_SIZE_ERR", function() { ctx.drawImage(bitmap, 0, 0, 100, 50); });
+        assert_throws("INDEX_SIZE_ERR", function() { ctx.drawImage(bitmap, 0, 0, 100, 50); });
+        assert_throws("INDEX_SIZE_ERR", function() { ctx.drawImage(bitmap, 0, 0, 100, 50); });
+        _assertPixel(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerosource.image.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerosource.image.worker-expected.txt
deleted file mode 100644
index aa2f43c..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerosource.image.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL drawImage with zero-sized source rectangle from image throws INDEX_SIZE_ERR assert_throws: function "function() { ctx.drawImage(response, 0, 0, 100, 50); }" threw object "TypeError: Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'" that is not a DOMException INDEX_SIZE_ERR: property "code" is equal to undefined, expected 1
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerosource.image.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerosource.image.worker.js
index 304acdc..cf472ac 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerosource.image.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerosource.image.worker.js
@@ -28,10 +28,12 @@
     };
 });
 promise.then(function(response) {
-    assert_throws("INDEX_SIZE_ERR", function() { ctx.drawImage(response, 0, 0, 100, 50); });
-    assert_throws("INDEX_SIZE_ERR", function() { ctx.drawImage(response, 0, 0, 100, 50); });
-    assert_throws("INDEX_SIZE_ERR", function() { ctx.drawImage(response, 0, 0, 100, 50); });
-    _assertPixel(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        assert_throws("INDEX_SIZE_ERR", function() { ctx.drawImage(bitmap, 0, 0, 100, 50); });
+        assert_throws("INDEX_SIZE_ERR", function() { ctx.drawImage(bitmap, 0, 0, 100, 50); });
+        assert_throws("INDEX_SIZE_ERR", function() { ctx.drawImage(bitmap, 0, 0, 100, 50); });
+        _assertPixel(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerosource.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerosource.worker-expected.txt
deleted file mode 100644
index 693d3f6b..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerosource.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL drawImage with zero-sized source rectangle throws INDEX_SIZE_ERR assert_throws: function "function() { ctx.drawImage(response, 10, 10, 0, 1, 0, 0, 100, 50); }" threw object "TypeError: Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'" that is not a DOMException INDEX_SIZE_ERR: property "code" is equal to undefined, expected 1
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerosource.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerosource.worker.js
index 1e6f54f3..b3b6a59 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerosource.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.zerosource.worker.js
@@ -28,10 +28,12 @@
     };
 });
 promise.then(function(response) {
-    assert_throws("INDEX_SIZE_ERR", function() { ctx.drawImage(response, 10, 10, 0, 1, 0, 0, 100, 50); });
-    assert_throws("INDEX_SIZE_ERR", function() { ctx.drawImage(response, 10, 10, 1, 0, 0, 0, 100, 50); });
-    assert_throws("INDEX_SIZE_ERR", function() { ctx.drawImage(response, 10, 10, 0, 0, 0, 0, 100, 50); });
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        assert_throws("INDEX_SIZE_ERR", function() { ctx.drawImage(bitmap, 10, 10, 0, 1, 0, 0, 100, 50); });
+        assert_throws("INDEX_SIZE_ERR", function() { ctx.drawImage(bitmap, 10, 10, 1, 0, 0, 0, 100, 50); });
+        assert_throws("INDEX_SIZE_ERR", function() { ctx.drawImage(bitmap, 10, 10, 0, 0, 0, 0, 100, 50); });
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.basic.image-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.basic.image-expected.txt
deleted file mode 100644
index c3717a1..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.basic.image-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.pattern.basic.image Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.basic.image.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.basic.image.html
index 1e0585e..c1e3565 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.basic.image.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.basic.image.html
@@ -32,13 +32,15 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'no-repeat');
-    ctx.fillStyle = pattern;
-    ctx.fillRect(0, 0, 100, 50);
-    _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'no-repeat');
+        ctx.fillStyle = pattern;
+        ctx.fillRect(0, 0, 100, 50);
+        _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.basic.image.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.basic.image.worker-expected.txt
deleted file mode 100644
index 92bc07d..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.basic.image.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.basic.image.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.basic.image.worker.js
index adf6b7d5..543aaef 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.basic.image.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.basic.image.worker.js
@@ -28,13 +28,15 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'no-repeat');
-    ctx.fillStyle = pattern;
-    ctx.fillRect(0, 0, 100, 50);
-    _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'no-repeat');
+        ctx.fillStyle = pattern;
+        ctx.fillRect(0, 0, 100, 50);
+        _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.crosscanvas-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.crosscanvas-expected.txt
deleted file mode 100644
index a6e5173..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.crosscanvas-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.pattern.crosscanvas Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.crosscanvas.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.crosscanvas.html
index d3c8cd6..daf2fd26 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.crosscanvas.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.crosscanvas.html
@@ -30,13 +30,15 @@
     };
 });
 promise.then(function(response) {
-    var offscreenCanvas2 = new OffscreenCanvas(100, 50);
-    var pattern = offscreenCanvas2.getContext('2d').createPattern(response, 'no-repeat');
-    ctx.fillStyle = '#f00';
-    ctx.fillRect(0, 0, 100, 50);
-    ctx.fillStyle = pattern;
-    ctx.fillRect(0, 0, 100, 50);
-    _assertPixel(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+        var pattern = offscreenCanvas2.getContext('2d').createPattern(bitmap, 'no-repeat');
+        ctx.fillStyle = '#f00';
+        ctx.fillRect(0, 0, 100, 50);
+        ctx.fillStyle = pattern;
+        ctx.fillRect(0, 0, 100, 50);
+        _assertPixel(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.crosscanvas.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.crosscanvas.worker-expected.txt
deleted file mode 100644
index 92bc07d..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.crosscanvas.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.crosscanvas.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.crosscanvas.worker.js
index f660f21..ea719d42 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.crosscanvas.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.crosscanvas.worker.js
@@ -26,13 +26,15 @@
     };
 });
 promise.then(function(response) {
-    var offscreenCanvas2 = new OffscreenCanvas(100, 50);
-    var pattern = offscreenCanvas2.getContext('2d').createPattern(response, 'no-repeat');
-    ctx.fillStyle = '#f00';
-    ctx.fillRect(0, 0, 100, 50);
-    ctx.fillStyle = pattern;
-    ctx.fillRect(0, 0, 100, 50);
-    _assertPixel(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+        var pattern = offscreenCanvas2.getContext('2d').createPattern(bitmap, 'no-repeat');
+        ctx.fillStyle = '#f00';
+        ctx.fillRect(0, 0, 100, 50);
+        ctx.fillStyle = pattern;
+        ctx.fillRect(0, 0, 100, 50);
+        _assertPixel(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.basic-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.basic-expected.txt
deleted file mode 100644
index 97307ab..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.basic-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.pattern.paint.norepeat.basic Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.basic.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.basic.html
index ea5adcc8..ae3bdb2 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.basic.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.basic.html
@@ -32,13 +32,15 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'no-repeat');
-    ctx.fillStyle = pattern;
-    ctx.fillRect(0, 0, 100, 50);
-    _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'no-repeat');
+        ctx.fillStyle = pattern;
+        ctx.fillRect(0, 0, 100, 50);
+        _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.basic.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.basic.worker-expected.txt
deleted file mode 100644
index 92bc07d..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.basic.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.basic.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.basic.worker.js
index 042f0c6..903cab7 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.basic.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.basic.worker.js
@@ -28,13 +28,15 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'no-repeat');
-    ctx.fillStyle = pattern;
-    ctx.fillRect(0, 0, 100, 50);
-    _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'no-repeat');
+        ctx.fillStyle = pattern;
+        ctx.fillRect(0, 0, 100, 50);
+        _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord1-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord1-expected.txt
deleted file mode 100644
index 777447d1..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord1-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.pattern.paint.norepeat.coord1 Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord1.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord1.html
index 8fcab2b..953861c 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord1.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord1.html
@@ -34,14 +34,16 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'no-repeat');
-    ctx.fillStyle = pattern;
-    ctx.translate(50, 0);
-    ctx.fillRect(-50, 0, 100, 50);
-    _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'no-repeat');
+        ctx.fillStyle = pattern;
+        ctx.translate(50, 0);
+        ctx.fillRect(-50, 0, 100, 50);
+        _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord1.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord1.worker-expected.txt
deleted file mode 100644
index 92bc07d..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord1.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord1.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord1.worker.js
index fae7427..f843214 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord1.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord1.worker.js
@@ -30,14 +30,16 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'no-repeat');
-    ctx.fillStyle = pattern;
-    ctx.translate(50, 0);
-    ctx.fillRect(-50, 0, 100, 50);
-    _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'no-repeat');
+        ctx.fillStyle = pattern;
+        ctx.translate(50, 0);
+        ctx.fillRect(-50, 0, 100, 50);
+        _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord2-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord2-expected.txt
deleted file mode 100644
index a75eeb01..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord2-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.pattern.paint.norepeat.coord2 Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord2.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord2.html
index 191f0bd..f475aae 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord2.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord2.html
@@ -30,18 +30,20 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'no-repeat');
-    ctx.fillStyle = pattern;
-    ctx.fillRect(0, 0, 50, 50);
-    ctx.fillStyle = '#f00';
-    ctx.fillRect(50, 0, 50, 50);
-    ctx.fillStyle = pattern;
-    ctx.translate(50, 0);
-    ctx.fillRect(-50, 0, 100, 50);
-    _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'no-repeat');
+        ctx.fillStyle = pattern;
+        ctx.fillRect(0, 0, 50, 50);
+        ctx.fillStyle = '#f00';
+        ctx.fillRect(50, 0, 50, 50);
+        ctx.fillStyle = pattern;
+        ctx.translate(50, 0);
+        ctx.fillRect(-50, 0, 100, 50);
+        _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord2.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord2.worker-expected.txt
deleted file mode 100644
index 92bc07d..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord2.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord2.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord2.worker.js
index 5c6c5c1..abf6195 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord2.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord2.worker.js
@@ -26,18 +26,20 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'no-repeat');
-    ctx.fillStyle = pattern;
-    ctx.fillRect(0, 0, 50, 50);
-    ctx.fillStyle = '#f00';
-    ctx.fillRect(50, 0, 50, 50);
-    ctx.fillStyle = pattern;
-    ctx.translate(50, 0);
-    ctx.fillRect(-50, 0, 100, 50);
-    _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'no-repeat');
+        ctx.fillStyle = pattern;
+        ctx.fillRect(0, 0, 50, 50);
+        ctx.fillStyle = '#f00';
+        ctx.fillRect(50, 0, 50, 50);
+        ctx.fillStyle = pattern;
+        ctx.translate(50, 0);
+        ctx.fillRect(-50, 0, 100, 50);
+        _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord3-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord3-expected.txt
deleted file mode 100644
index add81f9..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord3-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.pattern.paint.norepeat.coord3 Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord3.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord3.html
index 966fac8..2c54ad4 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord3.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord3.html
@@ -32,16 +32,18 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'no-repeat');
-    ctx.fillStyle = pattern;
-    ctx.translate(50, 25);
-    ctx.fillRect(-50, -25, 100, 50);
-    ctx.fillStyle = '#0f0';
-    ctx.fillRect(0, 0, 50, 25);
-    _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'no-repeat');
+        ctx.fillStyle = pattern;
+        ctx.translate(50, 25);
+        ctx.fillRect(-50, -25, 100, 50);
+        ctx.fillStyle = '#0f0';
+        ctx.fillRect(0, 0, 50, 25);
+        _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord3.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord3.worker-expected.txt
deleted file mode 100644
index 92bc07d..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord3.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord3.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord3.worker.js
index 8cc326b73..a1a438a 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord3.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord3.worker.js
@@ -28,16 +28,18 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'no-repeat');
-    ctx.fillStyle = pattern;
-    ctx.translate(50, 25);
-    ctx.fillRect(-50, -25, 100, 50);
-    ctx.fillStyle = '#0f0';
-    ctx.fillRect(0, 0, 50, 25);
-    _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'no-repeat');
+        ctx.fillStyle = pattern;
+        ctx.translate(50, 25);
+        ctx.fillRect(-50, -25, 100, 50);
+        ctx.fillStyle = '#0f0';
+        ctx.fillRect(0, 0, 50, 25);
+        _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.outside-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.outside-expected.txt
deleted file mode 100644
index b5f655ff..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.outside-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.pattern.paint.norepeat.outside Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.outside.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.outside.html
index 7fbee4d..3a7374d 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.outside.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.outside.html
@@ -32,18 +32,20 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'no-repeat');
-    ctx.fillStyle = '#0f0';
-    ctx.fillRect(0, 0, 100, 50);
-    ctx.fillStyle = pattern;
-    ctx.fillRect(0, -50, 100, 50);
-    ctx.fillRect(-100, 0, 100, 50);
-    ctx.fillRect(0, 50, 100, 50);
-    ctx.fillRect(100, 0, 100, 50);
-    _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'no-repeat');
+        ctx.fillStyle = '#0f0';
+        ctx.fillRect(0, 0, 100, 50);
+        ctx.fillStyle = pattern;
+        ctx.fillRect(0, -50, 100, 50);
+        ctx.fillRect(-100, 0, 100, 50);
+        ctx.fillRect(0, 50, 100, 50);
+        ctx.fillRect(100, 0, 100, 50);
+        _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.outside.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.outside.worker-expected.txt
deleted file mode 100644
index 92bc07d..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.outside.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.outside.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.outside.worker.js
index 85594b6..0b2c58d 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.outside.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.norepeat.outside.worker.js
@@ -28,18 +28,20 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'no-repeat');
-    ctx.fillStyle = '#0f0';
-    ctx.fillRect(0, 0, 100, 50);
-    ctx.fillStyle = pattern;
-    ctx.fillRect(0, -50, 100, 50);
-    ctx.fillRect(-100, 0, 100, 50);
-    ctx.fillRect(0, 50, 100, 50);
-    ctx.fillRect(100, 0, 100, 50);
-    _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'no-repeat');
+        ctx.fillStyle = '#0f0';
+        ctx.fillRect(0, 0, 100, 50);
+        ctx.fillStyle = pattern;
+        ctx.fillRect(0, -50, 100, 50);
+        ctx.fillRect(-100, 0, 100, 50);
+        ctx.fillRect(0, 50, 100, 50);
+        ctx.fillRect(100, 0, 100, 50);
+        _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.orientation.image-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.orientation.image-expected.txt
deleted file mode 100644
index feed82c..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.orientation.image-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Image patterns do not get flipped when painted Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.orientation.image.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.orientation.image.html
index 02ffc97..ca0bb335 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.orientation.image.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.orientation.image.html
@@ -32,18 +32,20 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'no-repeat');
-    ctx.fillStyle = pattern;
-    ctx.save();
-    ctx.translate(0, -103);
-    ctx.fillRect(0, 103, 100, 50);
-    ctx.restore();
-    ctx.fillStyle = '#0f0';
-    ctx.fillRect(0, 0, 100, 25);
-    _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'no-repeat');
+        ctx.fillStyle = pattern;
+        ctx.save();
+        ctx.translate(0, -103);
+        ctx.fillRect(0, 103, 100, 50);
+        ctx.restore();
+        ctx.fillStyle = '#0f0';
+        ctx.fillRect(0, 0, 100, 25);
+        _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.orientation.image.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.orientation.image.worker-expected.txt
deleted file mode 100644
index feed82c..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.orientation.image.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Image patterns do not get flipped when painted Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.orientation.image.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.orientation.image.worker.js
index e365dd6bb..f771b22 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.orientation.image.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.orientation.image.worker.js
@@ -28,18 +28,20 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'no-repeat');
-    ctx.fillStyle = pattern;
-    ctx.save();
-    ctx.translate(0, -103);
-    ctx.fillRect(0, 103, 100, 50);
-    ctx.restore();
-    ctx.fillStyle = '#0f0';
-    ctx.fillRect(0, 0, 100, 25);
-    _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'no-repeat');
+        ctx.fillStyle = pattern;
+        ctx.save();
+        ctx.translate(0, -103);
+        ctx.fillRect(0, 103, 100, 50);
+        ctx.restore();
+        ctx.fillStyle = '#0f0';
+        ctx.fillRect(0, 0, 100, 25);
+        _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.basic-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.basic-expected.txt
deleted file mode 100644
index 52a3562..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.basic-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.pattern.paint.repeat.basic Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.basic.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.basic.html
index 8149d89..8fcfb71 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.basic.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.basic.html
@@ -32,13 +32,15 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'no-repeat');
-    ctx.fillStyle = pattern;
-    ctx.fillRect(0, 0, 100, 50);
-    _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'no-repeat');
+        ctx.fillStyle = pattern;
+        ctx.fillRect(0, 0, 100, 50);
+        _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.basic.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.basic.worker-expected.txt
deleted file mode 100644
index 92bc07d..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.basic.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.basic.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.basic.worker.js
index d6bad4c..fa094c3 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.basic.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.basic.worker.js
@@ -28,13 +28,15 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'no-repeat');
-    ctx.fillStyle = pattern;
-    ctx.fillRect(0, 0, 100, 50);
-    _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'no-repeat');
+        ctx.fillStyle = pattern;
+        ctx.fillRect(0, 0, 100, 50);
+        _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord1-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord1-expected.txt
deleted file mode 100644
index f3c9392..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord1-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.pattern.paint.repeat.coord1 Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord1.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord1.html
index 63d30dba..1a9731a 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord1.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord1.html
@@ -32,14 +32,16 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'no-repeat');
-    ctx.fillStyle = pattern;
-    ctx.translate(-128, -78);
-    ctx.fillRect(128, 78, 100, 50);
-    _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'no-repeat');
+        ctx.fillStyle = pattern;
+        ctx.translate(-128, -78);
+        ctx.fillRect(128, 78, 100, 50);
+        _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord1.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord1.worker-expected.txt
deleted file mode 100644
index 92bc07d..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord1.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord1.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord1.worker.js
index b31ebcb..8ca59610 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord1.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord1.worker.js
@@ -28,14 +28,16 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'no-repeat');
-    ctx.fillStyle = pattern;
-    ctx.translate(-128, -78);
-    ctx.fillRect(128, 78, 100, 50);
-    _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'no-repeat');
+        ctx.fillStyle = pattern;
+        ctx.translate(-128, -78);
+        ctx.fillRect(128, 78, 100, 50);
+        _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord2-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord2-expected.txt
deleted file mode 100644
index d684589..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord2-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.pattern.paint.repeat.coord2 Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord2.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord2.html
index 404a51d727..63b1cac1 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord2.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord2.html
@@ -30,13 +30,15 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'no-repeat');
-    ctx.fillStyle = pattern;
-    ctx.fillRect(0, 0, 100, 50);
-    _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'no-repeat');
+        ctx.fillStyle = pattern;
+        ctx.fillRect(0, 0, 100, 50);
+        _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord2.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord2.worker-expected.txt
deleted file mode 100644
index 92bc07d..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord2.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord2.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord2.worker.js
index 3ab6b406..5ac23fe 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord2.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord2.worker.js
@@ -26,13 +26,15 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'no-repeat');
-    ctx.fillStyle = pattern;
-    ctx.fillRect(0, 0, 100, 50);
-    _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'no-repeat');
+        ctx.fillStyle = pattern;
+        ctx.fillRect(0, 0, 100, 50);
+        _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord3-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord3-expected.txt
deleted file mode 100644
index a04cb90..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord3-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.pattern.paint.repeat.coord3 Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord3.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord3.html
index 70028f7b..7e14e00 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord3.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord3.html
@@ -30,15 +30,17 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'no-repeat');
-    ctx.fillStyle = pattern;
-    ctx.fillRect(0, 0, 100, 50);
-    ctx.translate(-128, -78);
-    ctx.fillRect(128, 78, 100, 50);
-    _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'no-repeat');
+        ctx.fillStyle = pattern;
+        ctx.fillRect(0, 0, 100, 50);
+        ctx.translate(-128, -78);
+        ctx.fillRect(128, 78, 100, 50);
+        _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord3.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord3.worker-expected.txt
deleted file mode 100644
index 92bc07d..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord3.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord3.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord3.worker.js
index 3ae9dc9..2b8941a0 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord3.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.coord3.worker.js
@@ -26,15 +26,17 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'no-repeat');
-    ctx.fillStyle = pattern;
-    ctx.fillRect(0, 0, 100, 50);
-    ctx.translate(-128, -78);
-    ctx.fillRect(128, 78, 100, 50);
-    _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'no-repeat');
+        ctx.fillStyle = pattern;
+        ctx.fillRect(0, 0, 100, 50);
+        ctx.translate(-128, -78);
+        ctx.fillRect(128, 78, 100, 50);
+        _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.outside-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.outside-expected.txt
deleted file mode 100644
index c341eb3b..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.outside-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.pattern.paint.repeat.outside Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.outside.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.outside.html
index 7af8e2d..862a80e 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.outside.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.outside.html
@@ -32,14 +32,16 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'no-repeat');
-    ctx.fillStyle = pattern;
-    ctx.translate(50, 25);
-    ctx.fillRect(-50, -25, 100, 50);
-    _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'no-repeat');
+        ctx.fillStyle = pattern;
+        ctx.translate(50, 25);
+        ctx.fillRect(-50, -25, 100, 50);
+        _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.outside.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.outside.worker-expected.txt
deleted file mode 100644
index 92bc07d..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.outside.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.outside.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.outside.worker.js
index 3db7082..1d6fb4b 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.outside.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeat.outside.worker.js
@@ -28,14 +28,16 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'no-repeat');
-    ctx.fillStyle = pattern;
-    ctx.translate(50, 25);
-    ctx.fillRect(-50, -25, 100, 50);
-    _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'no-repeat');
+        ctx.fillStyle = pattern;
+        ctx.translate(50, 25);
+        ctx.fillRect(-50, -25, 100, 50);
+        _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.basic-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.basic-expected.txt
deleted file mode 100644
index 1e66d620f..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.basic-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.pattern.paint.repeatx.basic Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.basic.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.basic.html
index 45e36a4..ccd6ca58 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.basic.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.basic.html
@@ -34,13 +34,15 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'repeat-x');
-    ctx.fillStyle = pattern;
-    ctx.fillRect(0, 0, 100, 50);
-    _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'repeat-x');
+        ctx.fillStyle = pattern;
+        ctx.fillRect(0, 0, 100, 50);
+        _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.basic.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.basic.worker-expected.txt
deleted file mode 100644
index 92bc07d..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.basic.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.basic.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.basic.worker.js
index 209427ea..1dfa11a 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.basic.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.basic.worker.js
@@ -30,13 +30,15 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'repeat-x');
-    ctx.fillStyle = pattern;
-    ctx.fillRect(0, 0, 100, 50);
-    _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'repeat-x');
+        ctx.fillStyle = pattern;
+        ctx.fillRect(0, 0, 100, 50);
+        _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.coord1-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.coord1-expected.txt
deleted file mode 100644
index aae59649..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.coord1-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.pattern.paint.repeatx.coord1 Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.coord1.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.coord1.html
index 7818db3..b5fbeec 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.coord1.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.coord1.html
@@ -32,18 +32,20 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'repeat-x');
-    ctx.fillStyle = pattern;
-    ctx.translate(0, 16);
-    ctx.fillRect(0, -16, 100, 50);
-    ctx.fillStyle = '#0f0';
-    ctx.fillRect(0, 0, 100, 16);
-    _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 1,25, 0,255,0,255, "1,25", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,25, 0,255,0,255, "98,25", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'repeat-x');
+        ctx.fillStyle = pattern;
+        ctx.translate(0, 16);
+        ctx.fillRect(0, -16, 100, 50);
+        ctx.fillStyle = '#0f0';
+        ctx.fillRect(0, 0, 100, 16);
+        _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 1,25, 0,255,0,255, "1,25", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,25, 0,255,0,255, "98,25", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.coord1.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.coord1.worker-expected.txt
deleted file mode 100644
index 92bc07d..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.coord1.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.coord1.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.coord1.worker.js
index 080887b..3e07c9f9 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.coord1.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.coord1.worker.js
@@ -28,18 +28,20 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'repeat-x');
-    ctx.fillStyle = pattern;
-    ctx.translate(0, 16);
-    ctx.fillRect(0, -16, 100, 50);
-    ctx.fillStyle = '#0f0';
-    ctx.fillRect(0, 0, 100, 16);
-    _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 1,25, 0,255,0,255, "1,25", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,25, 0,255,0,255, "98,25", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'repeat-x');
+        ctx.fillStyle = pattern;
+        ctx.translate(0, 16);
+        ctx.fillRect(0, -16, 100, 50);
+        ctx.fillStyle = '#0f0';
+        ctx.fillRect(0, 0, 100, 16);
+        _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 1,25, 0,255,0,255, "1,25", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,25, 0,255,0,255, "98,25", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.outside-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.outside-expected.txt
deleted file mode 100644
index faf8659..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.outside-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.pattern.paint.repeatx.outside Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.outside.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.outside.html
index f2fcaac..1805608 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.outside.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.outside.html
@@ -32,15 +32,17 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'repeat-x');
-    ctx.fillStyle = pattern;
-    ctx.fillRect(0, 0, 100, 50);
-    ctx.fillStyle = '#0f0';
-    ctx.fillRect(0, 0, 100, 16);
-    _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'repeat-x');
+        ctx.fillStyle = pattern;
+        ctx.fillRect(0, 0, 100, 50);
+        ctx.fillStyle = '#0f0';
+        ctx.fillRect(0, 0, 100, 16);
+        _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.outside.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.outside.worker-expected.txt
deleted file mode 100644
index 92bc07d..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.outside.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.outside.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.outside.worker.js
index 6c43646..e2cb046 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.outside.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeatx.outside.worker.js
@@ -28,15 +28,17 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'repeat-x');
-    ctx.fillStyle = pattern;
-    ctx.fillRect(0, 0, 100, 50);
-    ctx.fillStyle = '#0f0';
-    ctx.fillRect(0, 0, 100, 16);
-    _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'repeat-x');
+        ctx.fillStyle = pattern;
+        ctx.fillRect(0, 0, 100, 50);
+        ctx.fillStyle = '#0f0';
+        ctx.fillRect(0, 0, 100, 16);
+        _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.basic-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.basic-expected.txt
deleted file mode 100644
index 2f7779b..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.basic-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.pattern.paint.repeaty.basic Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.basic.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.basic.html
index 813f7f3..259c5a5 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.basic.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.basic.html
@@ -34,13 +34,15 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'repeat-y');
-    ctx.fillStyle = pattern;
-    ctx.fillRect(0, 0, 100, 50);
-    _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'repeat-y');
+        ctx.fillStyle = pattern;
+        ctx.fillRect(0, 0, 100, 50);
+        _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.basic.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.basic.worker-expected.txt
deleted file mode 100644
index 92bc07d..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.basic.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.basic.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.basic.worker.js
index d9f53a00..aa886ef 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.basic.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.basic.worker.js
@@ -30,13 +30,15 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'repeat-y');
-    ctx.fillStyle = pattern;
-    ctx.fillRect(0, 0, 100, 50);
-    _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'repeat-y');
+        ctx.fillStyle = pattern;
+        ctx.fillRect(0, 0, 100, 50);
+        _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.coord1-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.coord1-expected.txt
deleted file mode 100644
index 369a37f..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.coord1-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.pattern.paint.repeaty.coord1 Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.coord1.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.coord1.html
index 6102405..373fafe 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.coord1.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.coord1.html
@@ -32,18 +32,20 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'repeat-y');
-    ctx.fillStyle = pattern;
-    ctx.translate(48, 0);
-    ctx.fillRect(-48, 0, 100, 50);
-    ctx.fillStyle = '#0f0';
-    ctx.fillRect(0, 0, 16, 50);
-    _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 50,1, 0,255,0,255, "50,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 50,48, 0,255,0,255, "50,48", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'repeat-y');
+        ctx.fillStyle = pattern;
+        ctx.translate(48, 0);
+        ctx.fillRect(-48, 0, 100, 50);
+        ctx.fillStyle = '#0f0';
+        ctx.fillRect(0, 0, 16, 50);
+        _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 50,1, 0,255,0,255, "50,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 50,48, 0,255,0,255, "50,48", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.coord1.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.coord1.worker-expected.txt
deleted file mode 100644
index 92bc07d..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.coord1.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.coord1.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.coord1.worker.js
index 92c39e2..4fbedca 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.coord1.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.coord1.worker.js
@@ -28,18 +28,20 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'repeat-y');
-    ctx.fillStyle = pattern;
-    ctx.translate(48, 0);
-    ctx.fillRect(-48, 0, 100, 50);
-    ctx.fillStyle = '#0f0';
-    ctx.fillRect(0, 0, 16, 50);
-    _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 50,1, 0,255,0,255, "50,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 50,48, 0,255,0,255, "50,48", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'repeat-y');
+        ctx.fillStyle = pattern;
+        ctx.translate(48, 0);
+        ctx.fillRect(-48, 0, 100, 50);
+        ctx.fillStyle = '#0f0';
+        ctx.fillRect(0, 0, 16, 50);
+        _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 50,1, 0,255,0,255, "50,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 50,48, 0,255,0,255, "50,48", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.outside-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.outside-expected.txt
deleted file mode 100644
index 8b87770..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.outside-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.pattern.paint.repeaty.outside Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.outside.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.outside.html
index 67c4061..1ef52dd8 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.outside.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.outside.html
@@ -32,15 +32,17 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'repeat-y');
-    ctx.fillStyle = pattern;
-    ctx.fillRect(0, 0, 100, 50);
-    ctx.fillStyle = '#0f0';
-    ctx.fillRect(0, 0, 16, 50);
-    _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'repeat-y');
+        ctx.fillStyle = pattern;
+        ctx.fillRect(0, 0, 100, 50);
+        ctx.fillStyle = '#0f0';
+        ctx.fillRect(0, 0, 16, 50);
+        _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.outside.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.outside.worker-expected.txt
deleted file mode 100644
index 92bc07d..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.outside.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.outside.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.outside.worker.js
index dd6d243..50a1517 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.outside.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.paint.repeaty.outside.worker.js
@@ -28,15 +28,17 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'repeat-y');
-    ctx.fillStyle = pattern;
-    ctx.fillRect(0, 0, 100, 50);
-    ctx.fillStyle = '#0f0';
-    ctx.fillRect(0, 0, 16, 50);
-    _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'repeat-y');
+        ctx.fillStyle = pattern;
+        ctx.fillRect(0, 0, 100, 50);
+        ctx.fillStyle = '#0f0';
+        ctx.fillRect(0, 0, 16, 50);
+        _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.repeat.empty-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.repeat.empty-expected.txt
deleted file mode 100644
index d9664a73..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.repeat.empty-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL OffscreenCanvas test: 2d.pattern.repeat.empty Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.repeat.empty.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.repeat.empty.html
index 426c069..9f40fe6 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.repeat.empty.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.repeat.empty.html
@@ -32,13 +32,15 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, "");
-    ctx.fillStyle = pattern;
-    ctx.fillRect(0, 0, 200, 50);
-    _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, "");
+        ctx.fillStyle = pattern;
+        ctx.fillRect(0, 0, 200, 50);
+        _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.repeat.empty.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.repeat.empty.worker-expected.txt
deleted file mode 100644
index 92bc07d..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.repeat.empty.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL 2d Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.repeat.empty.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.repeat.empty.worker.js
index 5338663..a31afa7 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.repeat.empty.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.pattern.repeat.empty.worker.js
@@ -28,13 +28,15 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, "");
-    ctx.fillStyle = pattern;
-    ctx.fillRect(0, 0, 200, 50);
-    _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, "");
+        ctx.fillStyle = pattern;
+        ctx.fillRect(0, 0, 200, 50);
+        _assertPixel(offscreenCanvas, 1,1, 0,255,0,255, "1,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,1, 0,255,0,255, "98,1", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 1,48, 0,255,0,255, "1,48", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 98,48, 0,255,0,255, "98,48", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.alpha-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.alpha-expected.txt
deleted file mode 100644
index 51d72c9..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.alpha-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Shadows are drawn correctly for partially-transparent images Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.alpha.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.alpha.html
index 205e32d..25ce1c1 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.alpha.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.alpha.html
@@ -34,8 +34,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, -50);
-    _assertPixelApprox(offscreenCanvas, 50,25, 127,0,127,255, "50,25", "127,0,127,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, -50);
+        _assertPixelApprox(offscreenCanvas, 50,25, 127,0,127,255, "50,25", "127,0,127,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.alpha.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.alpha.worker-expected.txt
deleted file mode 100644
index 51d72c9..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.alpha.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Shadows are drawn correctly for partially-transparent images Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.alpha.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.alpha.worker.js
index b40be65..17fc5a7 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.alpha.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.alpha.worker.js
@@ -30,8 +30,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, -50);
-    _assertPixelApprox(offscreenCanvas, 50,25, 127,0,127,255, "50,25", "127,0,127,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, -50);
+        _assertPixelApprox(offscreenCanvas, 50,25, 127,0,127,255, "50,25", "127,0,127,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.basic-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.basic-expected.txt
deleted file mode 100644
index 330625f..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.basic-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Shadows are drawn for images Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.basic.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.basic.html
index 9f16324..e1198ac 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.basic.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.basic.html
@@ -34,8 +34,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, -50);
-    _assertPixel(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, -50);
+        _assertPixel(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.basic.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.basic.worker-expected.txt
deleted file mode 100644
index 330625f..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.basic.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Shadows are drawn for images Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.basic.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.basic.worker.js
index e55f2d64..45126a2 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.basic.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.basic.worker.js
@@ -30,8 +30,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, -50);
-    _assertPixel(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, -50);
+        _assertPixel(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.scale-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.scale-expected.txt
deleted file mode 100644
index 617393d..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.scale-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Shadows are drawn correctly for scaled images Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.scale.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.scale.html
index a1197f2..7f3057b 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.scale.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.scale.html
@@ -34,10 +34,12 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0, 100, 50, -10, -50, 240, 50);
-    _assertPixelApprox(offscreenCanvas, 25,25, 0,255,0,255, "25,25", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 75,25, 0,255,0,255, "75,25", "0,255,0,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0, 100, 50, -10, -50, 240, 50);
+        _assertPixelApprox(offscreenCanvas, 25,25, 0,255,0,255, "25,25", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 75,25, 0,255,0,255, "75,25", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.scale.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.scale.worker-expected.txt
deleted file mode 100644
index 617393d..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.scale.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Shadows are drawn correctly for scaled images Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.scale.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.scale.worker.js
index e1d6198f..9ca1d79 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.scale.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.scale.worker.js
@@ -30,10 +30,12 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, 0, 100, 50, -10, -50, 240, 50);
-    _assertPixelApprox(offscreenCanvas, 25,25, 0,255,0,255, "25,25", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 75,25, 0,255,0,255, "75,25", "0,255,0,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, 0, 100, 50, -10, -50, 240, 50);
+        _assertPixelApprox(offscreenCanvas, 25,25, 0,255,0,255, "25,25", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 75,25, 0,255,0,255, "75,25", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.section-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.section-expected.txt
deleted file mode 100644
index c500dd6..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.section-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Shadows are not drawn for areas outside image source rectangles Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.section.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.section.html
index b3f9ab1..1abc755 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.section.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.section.html
@@ -34,10 +34,12 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 50, 0, 50, 50, 0, -50, 50, 50);
-    _assertPixelApprox(offscreenCanvas, 25,25, 0,255,0,255, "25,25", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 75,25, 0,255,0,255, "75,25", "0,255,0,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 50, 0, 50, 50, 0, -50, 50, 50);
+        _assertPixelApprox(offscreenCanvas, 25,25, 0,255,0,255, "25,25", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 75,25, 0,255,0,255, "75,25", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.section.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.section.worker-expected.txt
deleted file mode 100644
index c500dd6..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.section.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Shadows are not drawn for areas outside image source rectangles Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.section.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.section.worker.js
index a278f466..3b4deaa 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.section.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.section.worker.js
@@ -30,10 +30,12 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 50, 0, 50, 50, 0, -50, 50, 50);
-    _assertPixelApprox(offscreenCanvas, 25,25, 0,255,0,255, "25,25", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
-    _assertPixelApprox(offscreenCanvas, 75,25, 0,255,0,255, "75,25", "0,255,0,255", 2);
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 50, 0, 50, 50, 0, -50, 50, 50);
+        _assertPixelApprox(offscreenCanvas, 25,25, 0,255,0,255, "25,25", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
+        _assertPixelApprox(offscreenCanvas, 75,25, 0,255,0,255, "75,25", "0,255,0,255", 2);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.transparent.1-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.transparent.1-expected.txt
deleted file mode 100644
index 874688f..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.transparent.1-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Shadows are not drawn for transparent images Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.transparent.1.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.transparent.1.html
index 05c125a3..1d54ae5 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.transparent.1.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.transparent.1.html
@@ -34,8 +34,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, -50);
-    _assertPixel(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, -50);
+        _assertPixel(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.transparent.1.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.transparent.1.worker-expected.txt
deleted file mode 100644
index 874688f..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.transparent.1.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Shadows are not drawn for transparent images Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.transparent.1.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.transparent.1.worker.js
index 599140e..76dc8ff 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.transparent.1.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.transparent.1.worker.js
@@ -30,8 +30,10 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 0, -50);
-    _assertPixel(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 0, -50);
+        _assertPixel(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.transparent.2-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.transparent.2-expected.txt
deleted file mode 100644
index 96bf5c7..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.transparent.2-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Shadows are not drawn for transparent parts of images Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.transparent.2.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.transparent.2.html
index 32fa486..2e2230b 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.transparent.2.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.transparent.2.html
@@ -36,12 +36,14 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 50, -50);
-    ctx.shadowColor = '#f00';
-    ctx.drawImage(response, -50, -50);
-    _assertPixel(offscreenCanvas, 25,25, 0,255,0,255, "25,25", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 75,25, 0,255,0,255, "75,25", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 50, -50);
+        ctx.shadowColor = '#f00';
+        ctx.drawImage(bitmap, -50, -50);
+        _assertPixel(offscreenCanvas, 25,25, 0,255,0,255, "25,25", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 75,25, 0,255,0,255, "75,25", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.transparent.2.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.transparent.2.worker-expected.txt
deleted file mode 100644
index 96bf5c7..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.transparent.2.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Shadows are not drawn for transparent parts of images Failed to execute 'drawImage' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.transparent.2.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.transparent.2.worker.js
index 5b8862c..5f54763 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.transparent.2.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.image.transparent.2.worker.js
@@ -32,12 +32,14 @@
     };
 });
 promise.then(function(response) {
-    ctx.drawImage(response, 50, -50);
-    ctx.shadowColor = '#f00';
-    ctx.drawImage(response, -50, -50);
-    _assertPixel(offscreenCanvas, 25,25, 0,255,0,255, "25,25", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 75,25, 0,255,0,255, "75,25", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        ctx.drawImage(bitmap, 50, -50);
+        ctx.shadowColor = '#f00';
+        ctx.drawImage(bitmap, -50, -50);
+        _assertPixel(offscreenCanvas, 25,25, 0,255,0,255, "25,25", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 75,25, 0,255,0,255, "75,25", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.alpha-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.alpha-expected.txt
deleted file mode 100644
index 1cedb943..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.alpha-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Shadows are drawn correctly for partially-transparent fill patterns Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.alpha.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.alpha.html
index 3e55eb4..3f11789 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.alpha.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.alpha.html
@@ -30,13 +30,15 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'repeat');
-    ctx.fillStyle = '#f00';
-    ctx.fillRect(0, 0, 100, 50);
-    ctx.shadowOffsetY = 50;
-    ctx.shadowColor = '#00f';
-    ctx.fillStyle = pattern;
-    ctx.fillRect(0, -50, 100, 50);
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'repeat');
+        ctx.fillStyle = '#f00';
+        ctx.fillRect(0, 0, 100, 50);
+        ctx.shadowOffsetY = 50;
+        ctx.shadowColor = '#00f';
+        ctx.fillStyle = pattern;
+        ctx.fillRect(0, -50, 100, 50);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.alpha.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.alpha.worker-expected.txt
deleted file mode 100644
index 1cedb943..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.alpha.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Shadows are drawn correctly for partially-transparent fill patterns Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.alpha.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.alpha.worker.js
index fc6fdbeb..616169f 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.alpha.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.alpha.worker.js
@@ -26,13 +26,15 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'repeat');
-    ctx.fillStyle = '#f00';
-    ctx.fillRect(0, 0, 100, 50);
-    ctx.shadowOffsetY = 50;
-    ctx.shadowColor = '#00f';
-    ctx.fillStyle = pattern;
-    ctx.fillRect(0, -50, 100, 50);
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'repeat');
+        ctx.fillStyle = '#f00';
+        ctx.fillRect(0, 0, 100, 50);
+        ctx.shadowOffsetY = 50;
+        ctx.shadowColor = '#00f';
+        ctx.fillStyle = pattern;
+        ctx.fillRect(0, -50, 100, 50);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.basic-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.basic-expected.txt
deleted file mode 100644
index f4e0bf10..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.basic-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Shadows are drawn for fill patterns Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.basic.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.basic.html
index d70f2fce..8c4ff93 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.basic.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.basic.html
@@ -30,13 +30,15 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'repeat');
-    ctx.fillStyle = '#f00';
-    ctx.fillRect(0, 0, 100, 50);
-    ctx.shadowColor = '#0f0';
-    ctx.shadowOffsetY = 50;
-    ctx.fillStyle = pattern;
-    ctx.fillRect(0, -50, 100, 50);
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'repeat');
+        ctx.fillStyle = '#f00';
+        ctx.fillRect(0, 0, 100, 50);
+        ctx.shadowColor = '#0f0';
+        ctx.shadowOffsetY = 50;
+        ctx.fillStyle = pattern;
+        ctx.fillRect(0, -50, 100, 50);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.basic.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.basic.worker-expected.txt
deleted file mode 100644
index f4e0bf10..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.basic.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Shadows are drawn for fill patterns Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.basic.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.basic.worker.js
index 5c2a7c9..91e9fe9 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.basic.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.basic.worker.js
@@ -26,13 +26,15 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'repeat');
-    ctx.fillStyle = '#f00';
-    ctx.fillRect(0, 0, 100, 50);
-    ctx.shadowColor = '#0f0';
-    ctx.shadowOffsetY = 50;
-    ctx.fillStyle = pattern;
-    ctx.fillRect(0, -50, 100, 50);
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'repeat');
+        ctx.fillStyle = '#f00';
+        ctx.fillRect(0, 0, 100, 50);
+        ctx.shadowColor = '#0f0';
+        ctx.shadowOffsetY = 50;
+        ctx.fillStyle = pattern;
+        ctx.fillRect(0, -50, 100, 50);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.transparent.1-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.transparent.1-expected.txt
deleted file mode 100644
index f7660f8..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.transparent.1-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Shadows are not drawn for transparent fill patterns Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.transparent.1.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.transparent.1.html
index 29103ad..26b5057 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.transparent.1.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.transparent.1.html
@@ -30,13 +30,15 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'repeat');
-    ctx.fillStyle = '#0f0';
-    ctx.fillRect(0, 0, 100, 50);
-    ctx.shadowColor = '#f00';
-    ctx.shadowOffsetY = 50;
-    ctx.fillStyle = pattern;
-    ctx.fillRect(0, -50, 100, 50);
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'repeat');
+        ctx.fillStyle = '#0f0';
+        ctx.fillRect(0, 0, 100, 50);
+        ctx.shadowColor = '#f00';
+        ctx.shadowOffsetY = 50;
+        ctx.fillStyle = pattern;
+        ctx.fillRect(0, -50, 100, 50);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.transparent.1.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.transparent.1.worker-expected.txt
deleted file mode 100644
index f7660f8..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.transparent.1.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Shadows are not drawn for transparent fill patterns Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.transparent.1.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.transparent.1.worker.js
index bf8781b..70ca9a8 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.transparent.1.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.transparent.1.worker.js
@@ -26,13 +26,15 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'repeat');
-    ctx.fillStyle = '#0f0';
-    ctx.fillRect(0, 0, 100, 50);
-    ctx.shadowColor = '#f00';
-    ctx.shadowOffsetY = 50;
-    ctx.fillStyle = pattern;
-    ctx.fillRect(0, -50, 100, 50);
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'repeat');
+        ctx.fillStyle = '#0f0';
+        ctx.fillRect(0, 0, 100, 50);
+        ctx.shadowColor = '#f00';
+        ctx.shadowOffsetY = 50;
+        ctx.fillStyle = pattern;
+        ctx.fillRect(0, -50, 100, 50);
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.transparent.2-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.transparent.2-expected.txt
deleted file mode 100644
index c504a75..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.transparent.2-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Shadows are not drawn for transparent parts of fill patterns Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.transparent.2.html b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.transparent.2.html
index 6509bae..983b9ff 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.transparent.2.html
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.transparent.2.html
@@ -30,18 +30,20 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'repeat');
-    ctx.fillStyle = '#f00';
-    ctx.fillRect(0, 0, 50, 50);
-    ctx.fillStyle = '#0f0';
-    ctx.fillRect(50, 0, 50, 50);
-    ctx.shadowOffsetY = 50;
-    ctx.shadowColor = '#0f0';
-    ctx.fillStyle = pattern;
-    ctx.fillRect(0, -50, 100, 50);
-    _assertPixel(offscreenCanvas, 25,25, 0,255,0,255, "25,25", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 75,25, 0,255,0,255, "75,25", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'repeat');
+        ctx.fillStyle = '#f00';
+        ctx.fillRect(0, 0, 50, 50);
+        ctx.fillStyle = '#0f0';
+        ctx.fillRect(50, 0, 50, 50);
+        ctx.shadowOffsetY = 50;
+        ctx.shadowColor = '#0f0';
+        ctx.fillStyle = pattern;
+        ctx.fillRect(0, -50, 100, 50);
+        _assertPixel(offscreenCanvas, 25,25, 0,255,0,255, "25,25", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 75,25, 0,255,0,255, "75,25", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.transparent.2.worker-expected.txt b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.transparent.2.worker-expected.txt
deleted file mode 100644
index c504a75..0000000
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.transparent.2.worker-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Shadows are not drawn for transparent parts of fill patterns Failed to execute 'createPattern' on 'OffscreenCanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.transparent.2.worker.js b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.transparent.2.worker.js
index 8a2b7165..db88180 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.transparent.2.worker.js
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/shadows/2d.shadow.pattern.transparent.2.worker.js
@@ -26,18 +26,20 @@
     };
 });
 promise.then(function(response) {
-    var pattern = ctx.createPattern(response, 'repeat');
-    ctx.fillStyle = '#f00';
-    ctx.fillRect(0, 0, 50, 50);
-    ctx.fillStyle = '#0f0';
-    ctx.fillRect(50, 0, 50, 50);
-    ctx.shadowOffsetY = 50;
-    ctx.shadowColor = '#0f0';
-    ctx.fillStyle = pattern;
-    ctx.fillRect(0, -50, 100, 50);
-    _assertPixel(offscreenCanvas, 25,25, 0,255,0,255, "25,25", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255");
-    _assertPixel(offscreenCanvas, 75,25, 0,255,0,255, "75,25", "0,255,0,255");
+    createImageBitmap(response).then(bitmap => {
+        var pattern = ctx.createPattern(bitmap, 'repeat');
+        ctx.fillStyle = '#f00';
+        ctx.fillRect(0, 0, 50, 50);
+        ctx.fillStyle = '#0f0';
+        ctx.fillRect(50, 0, 50, 50);
+        ctx.shadowOffsetY = 50;
+        ctx.shadowColor = '#0f0';
+        ctx.fillStyle = pattern;
+        ctx.fillRect(0, -50, 100, 50);
+        _assertPixel(offscreenCanvas, 25,25, 0,255,0,255, "25,25", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255");
+        _assertPixel(offscreenCanvas, 75,25, 0,255,0,255, "75,25", "0,255,0,255");
+    }, t_fail);
 }).then(t_pass, t_fail);
 
 });
diff --git a/third_party/blink/web_tests/external/wpt/offscreen-canvas/tools/tests2d.yaml b/third_party/blink/web_tests/external/wpt/offscreen-canvas/tools/tests2d.yaml
index 6662668..645d684 100644
--- a/third_party/blink/web_tests/external/wpt/offscreen-canvas/tools/tests2d.yaml
+++ b/third_party/blink/web_tests/external/wpt/offscreen-canvas/tools/tests2d.yaml
@@ -595,8 +595,10 @@
         };
     });
     promise.then(function(response) {
-        ctx.drawImage(response, 0, 0);
-        @assert pixel 50,25 ==~ 2,253,0,255;
+        createImageBitmap(response).then(bitmap => {
+            ctx.drawImage(bitmap, 0, 0);
+            @assert pixel 50,25 ==~ 2,253,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.composite.globalAlpha.canvas
@@ -630,10 +632,12 @@
         };
     });
     promise.then(function(response) {
-        ctx.fillStyle = ctx.createPattern(response, 'no-repeat');
-        ctx.globalAlpha = 0.01; // avoid any potential alpha=0 optimisations
-        ctx.fillRect(0, 0, 100, 50);
-        @assert pixel 50,25 ==~ 2,253,0,255;
+        createImageBitmap(response).then(bitmap => {
+            ctx.fillStyle = ctx.createPattern(bitmap, 'no-repeat');
+            ctx.globalAlpha = 0.01; // avoid any potential alpha=0 optimisations
+            ctx.fillRect(0, 0, 100, 50);
+            @assert pixel 50,25 ==~ 2,253,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.composite.globalAlpha.canvaspattern
@@ -762,8 +766,10 @@
         };
     });
     promise.then(function(response) {
-        ctx.drawImage(response, 0, 0);
-        @assert pixel 50,25 ==~ %s +/- 5;
+        createImageBitmap(response).then(bitmap => {
+            ctx.drawImage(bitmap, 0, 0);
+            @assert pixel 50,25 ==~ %s +/- 5;
+        }, t_fail);
     }).then(t_pass, t_fail);
     """ % (dest, op, to_test(expected)),
             } )
@@ -788,12 +794,14 @@
         };
     });
     promise.then(function(response) {
-        ctx2.drawImage(response, 0, 0);
-        ctx.fillStyle = 'rgba%s';
-        ctx.fillRect(0, 0, 100, 50);
-        ctx.globalCompositeOperation = '%s';
-        ctx.drawImage(offscreenCanvas2, 0, 0);
-        @assert pixel 50,25 ==~ %s +/- 5;
+        createImageBitmap(response).then(bitmap => {
+            ctx2.drawImage(bitmap, 0, 0);
+            ctx.fillStyle = 'rgba%s';
+            ctx.fillRect(0, 0, 100, 50);
+            ctx.globalCompositeOperation = '%s';
+            ctx.drawImage(offscreenCanvas2, 0, 0);
+            @assert pixel 50,25 ==~ %s +/- 5;
+        }, t_fail);
     }).then(t_pass, t_fail);
     """ % (dest, op, to_test(expected)),
             } )
@@ -844,9 +852,11 @@
         };
     });
     promise.then(function(response) {
-        ctx.drawImage(response, 40, 40, 10, 10, 40, 50, 10, 10);
-        @assert pixel 15,15 ==~ %s +/- 5;
-        @assert pixel 50,25 ==~ %s +/- 5;
+        createImageBitmap(response).then(bitmap => {
+            ctx.drawImage(bitmap, 40, 40, 10, 10, 40, 50, 10, 10);
+            @assert pixel 15,15 ==~ %s +/- 5;
+            @assert pixel 50,25 ==~ %s +/- 5;
+        }, t_fail);
     }).then(t_pass, t_fail);
     """ % (dest, op, to_test(expected0), to_test(expected0)),
             } )
@@ -893,9 +903,11 @@
         };
     });
     promise.then(function(response) {
-        ctx.fillStyle = ctx.createPattern(response, 'no-repeat');
-        ctx.fillRect(0, 50, 100, 50);
-        @assert pixel 50,25 ==~ %s +/- 5;
+        createImageBitmap(response).then(bitmap => {
+            ctx.fillStyle = ctx.createPattern(bitmap, 'no-repeat');
+            ctx.fillRect(0, 50, 100, 50);
+            @assert pixel 50,25 ==~ %s +/- 5;
+        }, t_fail);
     }).then(t_pass, t_fail);
     """ % (dest, op, to_test(expected0)),
             } )
@@ -2131,13 +2143,15 @@
         };
     });
     promise.then(function(response) {
-        var pattern = ctx.createPattern(response, 'no-repeat');
-        ctx.fillStyle = pattern;
-        ctx.fillRect(0, 0, 100, 50);
-        @assert pixel 1,1 == 0,255,0,255;
-        @assert pixel 98,1 == 0,255,0,255;
-        @assert pixel 1,48 == 0,255,0,255;
-        @assert pixel 98,48 == 0,255,0,255;
+        createImageBitmap(response).then(bitmap => {
+            var pattern = ctx.createPattern(bitmap, 'no-repeat');
+            ctx.fillStyle = pattern;
+            ctx.fillRect(0, 0, 100, 50);
+            @assert pixel 1,1 == 0,255,0,255;
+            @assert pixel 98,1 == 0,255,0,255;
+            @assert pixel 1,48 == 0,255,0,255;
+            @assert pixel 98,48 == 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.pattern.basic.canvas
@@ -2239,13 +2253,15 @@
         };
     });
     promise.then(function(response) {
-        var pattern = ctx.createPattern(response, "");
-        ctx.fillStyle = pattern;
-        ctx.fillRect(0, 0, 200, 50);
-        @assert pixel 1,1 == 0,255,0,255;
-        @assert pixel 98,1 == 0,255,0,255;
-        @assert pixel 1,48 == 0,255,0,255;
-        @assert pixel 98,48 == 0,255,0,255;
+        createImageBitmap(response).then(bitmap => {
+            var pattern = ctx.createPattern(bitmap, "");
+            ctx.fillStyle = pattern;
+            ctx.fillRect(0, 0, 200, 50);
+            @assert pixel 1,1 == 0,255,0,255;
+            @assert pixel 98,1 == 0,255,0,255;
+            @assert pixel 1,48 == 0,255,0,255;
+            @assert pixel 98,48 == 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.pattern.repeat.null
@@ -2344,13 +2360,15 @@
         };
     });
     promise.then(function(response) {
-        var offscreenCanvas2 = new OffscreenCanvas(100, 50);
-        var pattern = offscreenCanvas2.getContext('2d').createPattern(response, 'no-repeat');
-        ctx.fillStyle = '#f00';
-        ctx.fillRect(0, 0, 100, 50);
-        ctx.fillStyle = pattern;
-        ctx.fillRect(0, 0, 100, 50);
-        @assert pixel 50,25 == 0,255,0,255;
+        createImageBitmap(response).then(bitmap => {
+            var offscreenCanvas2 = new OffscreenCanvas(100, 50);
+            var pattern = offscreenCanvas2.getContext('2d').createPattern(bitmap, 'no-repeat');
+            ctx.fillStyle = '#f00';
+            ctx.fillRect(0, 0, 100, 50);
+            ctx.fillStyle = pattern;
+            ctx.fillRect(0, 0, 100, 50);
+            @assert pixel 50,25 == 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.pattern.paint.norepeat.basic
@@ -2369,13 +2387,15 @@
         };
     });
     promise.then(function(response) {
-        var pattern = ctx.createPattern(response, 'no-repeat');
-        ctx.fillStyle = pattern;
-        ctx.fillRect(0, 0, 100, 50);
-        @assert pixel 1,1 == 0,255,0,255;
-        @assert pixel 98,1 == 0,255,0,255;
-        @assert pixel 1,48 == 0,255,0,255;
-        @assert pixel 98,48 == 0,255,0,255;
+        createImageBitmap(response).then(bitmap => {
+            var pattern = ctx.createPattern(bitmap, 'no-repeat');
+            ctx.fillStyle = pattern;
+            ctx.fillRect(0, 0, 100, 50);
+            @assert pixel 1,1 == 0,255,0,255;
+            @assert pixel 98,1 == 0,255,0,255;
+            @assert pixel 1,48 == 0,255,0,255;
+            @assert pixel 98,48 == 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.pattern.paint.norepeat.outside
@@ -2394,18 +2414,20 @@
         };
     });
     promise.then(function(response) {
-        var pattern = ctx.createPattern(response, 'no-repeat');
-        ctx.fillStyle = '#0f0';
-        ctx.fillRect(0, 0, 100, 50);
-        ctx.fillStyle = pattern;
-        ctx.fillRect(0, -50, 100, 50);
-        ctx.fillRect(-100, 0, 100, 50);
-        ctx.fillRect(0, 50, 100, 50);
-        ctx.fillRect(100, 0, 100, 50);
-        @assert pixel 1,1 == 0,255,0,255;
-        @assert pixel 98,1 == 0,255,0,255;
-        @assert pixel 1,48 == 0,255,0,255;
-        @assert pixel 98,48 == 0,255,0,255;
+        createImageBitmap(response).then(bitmap => {
+            var pattern = ctx.createPattern(bitmap, 'no-repeat');
+            ctx.fillStyle = '#0f0';
+            ctx.fillRect(0, 0, 100, 50);
+            ctx.fillStyle = pattern;
+            ctx.fillRect(0, -50, 100, 50);
+            ctx.fillRect(-100, 0, 100, 50);
+            ctx.fillRect(0, 50, 100, 50);
+            ctx.fillRect(100, 0, 100, 50);
+            @assert pixel 1,1 == 0,255,0,255;
+            @assert pixel 98,1 == 0,255,0,255;
+            @assert pixel 1,48 == 0,255,0,255;
+            @assert pixel 98,48 == 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.pattern.paint.norepeat.coord1
@@ -2426,14 +2448,16 @@
         };
     });
     promise.then(function(response) {
-        var pattern = ctx.createPattern(response, 'no-repeat');
-        ctx.fillStyle = pattern;
-        ctx.translate(50, 0);
-        ctx.fillRect(-50, 0, 100, 50);
-        @assert pixel 1,1 == 0,255,0,255;
-        @assert pixel 98,1 == 0,255,0,255;
-        @assert pixel 1,48 == 0,255,0,255;
-        @assert pixel 98,48 == 0,255,0,255;
+        createImageBitmap(response).then(bitmap => {
+            var pattern = ctx.createPattern(bitmap, 'no-repeat');
+            ctx.fillStyle = pattern;
+            ctx.translate(50, 0);
+            ctx.fillRect(-50, 0, 100, 50);
+            @assert pixel 1,1 == 0,255,0,255;
+            @assert pixel 98,1 == 0,255,0,255;
+            @assert pixel 1,48 == 0,255,0,255;
+            @assert pixel 98,48 == 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.pattern.paint.norepeat.coord2
@@ -2450,18 +2474,20 @@
         };
     });
     promise.then(function(response) {
-        var pattern = ctx.createPattern(response, 'no-repeat');
-        ctx.fillStyle = pattern;
-        ctx.fillRect(0, 0, 50, 50);
-        ctx.fillStyle = '#f00';
-        ctx.fillRect(50, 0, 50, 50);
-        ctx.fillStyle = pattern;
-        ctx.translate(50, 0);
-        ctx.fillRect(-50, 0, 100, 50);
-        @assert pixel 1,1 == 0,255,0,255;
-        @assert pixel 98,1 == 0,255,0,255;
-        @assert pixel 1,48 == 0,255,0,255;
-        @assert pixel 98,48 == 0,255,0,255;
+        createImageBitmap(response).then(bitmap => {
+            var pattern = ctx.createPattern(bitmap, 'no-repeat');
+            ctx.fillStyle = pattern;
+            ctx.fillRect(0, 0, 50, 50);
+            ctx.fillStyle = '#f00';
+            ctx.fillRect(50, 0, 50, 50);
+            ctx.fillStyle = pattern;
+            ctx.translate(50, 0);
+            ctx.fillRect(-50, 0, 100, 50);
+            @assert pixel 1,1 == 0,255,0,255;
+            @assert pixel 98,1 == 0,255,0,255;
+            @assert pixel 1,48 == 0,255,0,255;
+            @assert pixel 98,48 == 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.pattern.paint.norepeat.coord3
@@ -2480,16 +2506,18 @@
         };
     });
     promise.then(function(response) {
-        var pattern = ctx.createPattern(response, 'no-repeat');
-        ctx.fillStyle = pattern;
-        ctx.translate(50, 25);
-        ctx.fillRect(-50, -25, 100, 50);
-        ctx.fillStyle = '#0f0';
-        ctx.fillRect(0, 0, 50, 25);
-        @assert pixel 1,1 == 0,255,0,255;
-        @assert pixel 98,1 == 0,255,0,255;
-        @assert pixel 1,48 == 0,255,0,255;
-        @assert pixel 98,48 == 0,255,0,255;
+        createImageBitmap(response).then(bitmap => {
+            var pattern = ctx.createPattern(bitmap, 'no-repeat');
+            ctx.fillStyle = pattern;
+            ctx.translate(50, 25);
+            ctx.fillRect(-50, -25, 100, 50);
+            ctx.fillStyle = '#0f0';
+            ctx.fillRect(0, 0, 50, 25);
+            @assert pixel 1,1 == 0,255,0,255;
+            @assert pixel 98,1 == 0,255,0,255;
+            @assert pixel 1,48 == 0,255,0,255;
+            @assert pixel 98,48 == 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.pattern.paint.repeat.basic
@@ -2508,13 +2536,15 @@
         };
     });
     promise.then(function(response) {
-        var pattern = ctx.createPattern(response, 'no-repeat');
-        ctx.fillStyle = pattern;
-        ctx.fillRect(0, 0, 100, 50);
-        @assert pixel 1,1 == 0,255,0,255;
-        @assert pixel 98,1 == 0,255,0,255;
-        @assert pixel 1,48 == 0,255,0,255;
-        @assert pixel 98,48 == 0,255,0,255;
+        createImageBitmap(response).then(bitmap => {
+            var pattern = ctx.createPattern(bitmap, 'no-repeat');
+            ctx.fillStyle = pattern;
+            ctx.fillRect(0, 0, 100, 50);
+            @assert pixel 1,1 == 0,255,0,255;
+            @assert pixel 98,1 == 0,255,0,255;
+            @assert pixel 1,48 == 0,255,0,255;
+            @assert pixel 98,48 == 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.pattern.paint.repeat.outside
@@ -2533,14 +2563,16 @@
         };
     });
     promise.then(function(response) {
-        var pattern = ctx.createPattern(response, 'no-repeat');
-        ctx.fillStyle = pattern;
-        ctx.translate(50, 25);
-        ctx.fillRect(-50, -25, 100, 50);
-        @assert pixel 1,1 == 0,255,0,255;
-        @assert pixel 98,1 == 0,255,0,255;
-        @assert pixel 1,48 == 0,255,0,255;
-        @assert pixel 98,48 == 0,255,0,255;
+        createImageBitmap(response).then(bitmap => {
+            var pattern = ctx.createPattern(bitmap, 'no-repeat');
+            ctx.fillStyle = pattern;
+            ctx.translate(50, 25);
+            ctx.fillRect(-50, -25, 100, 50);
+            @assert pixel 1,1 == 0,255,0,255;
+            @assert pixel 98,1 == 0,255,0,255;
+            @assert pixel 1,48 == 0,255,0,255;
+            @assert pixel 98,48 == 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.pattern.paint.repeat.coord1
@@ -2559,14 +2591,16 @@
         };
     });
     promise.then(function(response) {
-        var pattern = ctx.createPattern(response, 'no-repeat');
-        ctx.fillStyle = pattern;
-        ctx.translate(-128, -78);
-        ctx.fillRect(128, 78, 100, 50);
-        @assert pixel 1,1 == 0,255,0,255;
-        @assert pixel 98,1 == 0,255,0,255;
-        @assert pixel 1,48 == 0,255,0,255;
-        @assert pixel 98,48 == 0,255,0,255;
+        createImageBitmap(response).then(bitmap => {
+            var pattern = ctx.createPattern(bitmap, 'no-repeat');
+            ctx.fillStyle = pattern;
+            ctx.translate(-128, -78);
+            ctx.fillRect(128, 78, 100, 50);
+            @assert pixel 1,1 == 0,255,0,255;
+            @assert pixel 98,1 == 0,255,0,255;
+            @assert pixel 1,48 == 0,255,0,255;
+            @assert pixel 98,48 == 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.pattern.paint.repeat.coord2
@@ -2583,13 +2617,15 @@
         };
     });
     promise.then(function(response) {
-        var pattern = ctx.createPattern(response, 'no-repeat');
-        ctx.fillStyle = pattern;
-        ctx.fillRect(0, 0, 100, 50);
-        @assert pixel 1,1 == 0,255,0,255;
-        @assert pixel 98,1 == 0,255,0,255;
-        @assert pixel 1,48 == 0,255,0,255;
-        @assert pixel 98,48 == 0,255,0,255;
+        createImageBitmap(response).then(bitmap => {
+            var pattern = ctx.createPattern(bitmap, 'no-repeat');
+            ctx.fillStyle = pattern;
+            ctx.fillRect(0, 0, 100, 50);
+            @assert pixel 1,1 == 0,255,0,255;
+            @assert pixel 98,1 == 0,255,0,255;
+            @assert pixel 1,48 == 0,255,0,255;
+            @assert pixel 98,48 == 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.pattern.paint.repeat.coord3
@@ -2606,15 +2642,17 @@
         };
     });
     promise.then(function(response) {
-        var pattern = ctx.createPattern(response, 'no-repeat');
-        ctx.fillStyle = pattern;
-        ctx.fillRect(0, 0, 100, 50);
-        ctx.translate(-128, -78);
-        ctx.fillRect(128, 78, 100, 50);
-        @assert pixel 1,1 == 0,255,0,255;
-        @assert pixel 98,1 == 0,255,0,255;
-        @assert pixel 1,48 == 0,255,0,255;
-        @assert pixel 98,48 == 0,255,0,255;
+        createImageBitmap(response).then(bitmap => {
+            var pattern = ctx.createPattern(bitmap, 'no-repeat');
+            ctx.fillStyle = pattern;
+            ctx.fillRect(0, 0, 100, 50);
+            ctx.translate(-128, -78);
+            ctx.fillRect(128, 78, 100, 50);
+            @assert pixel 1,1 == 0,255,0,255;
+            @assert pixel 98,1 == 0,255,0,255;
+            @assert pixel 1,48 == 0,255,0,255;
+            @assert pixel 98,48 == 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.pattern.paint.repeatx.basic
@@ -2635,13 +2673,15 @@
         };
     });
     promise.then(function(response) {
-        var pattern = ctx.createPattern(response, 'repeat-x');
-        ctx.fillStyle = pattern;
-        ctx.fillRect(0, 0, 100, 50);
-        @assert pixel 1,1 == 0,255,0,255;
-        @assert pixel 98,1 == 0,255,0,255;
-        @assert pixel 1,48 == 0,255,0,255;
-        @assert pixel 98,48 == 0,255,0,255;
+        createImageBitmap(response).then(bitmap => {
+            var pattern = ctx.createPattern(bitmap, 'repeat-x');
+            ctx.fillStyle = pattern;
+            ctx.fillRect(0, 0, 100, 50);
+            @assert pixel 1,1 == 0,255,0,255;
+            @assert pixel 98,1 == 0,255,0,255;
+            @assert pixel 1,48 == 0,255,0,255;
+            @assert pixel 98,48 == 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.pattern.paint.repeatx.outside
@@ -2660,15 +2700,17 @@
         };
     });
     promise.then(function(response) {
-        var pattern = ctx.createPattern(response, 'repeat-x');
-        ctx.fillStyle = pattern;
-        ctx.fillRect(0, 0, 100, 50);
-        ctx.fillStyle = '#0f0';
-        ctx.fillRect(0, 0, 100, 16);
-        @assert pixel 1,1 == 0,255,0,255;
-        @assert pixel 98,1 == 0,255,0,255;
-        @assert pixel 1,48 == 0,255,0,255;
-        @assert pixel 98,48 == 0,255,0,255;
+        createImageBitmap(response).then(bitmap => {
+            var pattern = ctx.createPattern(bitmap, 'repeat-x');
+            ctx.fillStyle = pattern;
+            ctx.fillRect(0, 0, 100, 50);
+            ctx.fillStyle = '#0f0';
+            ctx.fillRect(0, 0, 100, 16);
+            @assert pixel 1,1 == 0,255,0,255;
+            @assert pixel 98,1 == 0,255,0,255;
+            @assert pixel 1,48 == 0,255,0,255;
+            @assert pixel 98,48 == 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.pattern.paint.repeatx.coord1
@@ -2687,18 +2729,20 @@
         };
     });
     promise.then(function(response) {
-        var pattern = ctx.createPattern(response, 'repeat-x');
-        ctx.fillStyle = pattern;
-        ctx.translate(0, 16);
-        ctx.fillRect(0, -16, 100, 50);
-        ctx.fillStyle = '#0f0';
-        ctx.fillRect(0, 0, 100, 16);
-        @assert pixel 1,1 == 0,255,0,255;
-        @assert pixel 98,1 == 0,255,0,255;
-        @assert pixel 1,25 == 0,255,0,255;
-        @assert pixel 98,25 == 0,255,0,255;
-        @assert pixel 1,48 == 0,255,0,255;
-        @assert pixel 98,48 == 0,255,0,255;
+        createImageBitmap(response).then(bitmap => {
+            var pattern = ctx.createPattern(bitmap, 'repeat-x');
+            ctx.fillStyle = pattern;
+            ctx.translate(0, 16);
+            ctx.fillRect(0, -16, 100, 50);
+            ctx.fillStyle = '#0f0';
+            ctx.fillRect(0, 0, 100, 16);
+            @assert pixel 1,1 == 0,255,0,255;
+            @assert pixel 98,1 == 0,255,0,255;
+            @assert pixel 1,25 == 0,255,0,255;
+            @assert pixel 98,25 == 0,255,0,255;
+            @assert pixel 1,48 == 0,255,0,255;
+            @assert pixel 98,48 == 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.pattern.paint.repeaty.basic
@@ -2719,13 +2763,15 @@
         };
     });
     promise.then(function(response) {
-        var pattern = ctx.createPattern(response, 'repeat-y');
-        ctx.fillStyle = pattern;
-        ctx.fillRect(0, 0, 100, 50);
-        @assert pixel 1,1 == 0,255,0,255;
-        @assert pixel 98,1 == 0,255,0,255;
-        @assert pixel 1,48 == 0,255,0,255;
-        @assert pixel 98,48 == 0,255,0,255;
+        createImageBitmap(response).then(bitmap => {
+            var pattern = ctx.createPattern(bitmap, 'repeat-y');
+            ctx.fillStyle = pattern;
+            ctx.fillRect(0, 0, 100, 50);
+            @assert pixel 1,1 == 0,255,0,255;
+            @assert pixel 98,1 == 0,255,0,255;
+            @assert pixel 1,48 == 0,255,0,255;
+            @assert pixel 98,48 == 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.pattern.paint.repeaty.outside
@@ -2744,15 +2790,17 @@
         };
     });
     promise.then(function(response) {
-        var pattern = ctx.createPattern(response, 'repeat-y');
-        ctx.fillStyle = pattern;
-        ctx.fillRect(0, 0, 100, 50);
-        ctx.fillStyle = '#0f0';
-        ctx.fillRect(0, 0, 16, 50);
-        @assert pixel 1,1 == 0,255,0,255;
-        @assert pixel 98,1 == 0,255,0,255;
-        @assert pixel 1,48 == 0,255,0,255;
-        @assert pixel 98,48 == 0,255,0,255;
+        createImageBitmap(response).then(bitmap => {
+            var pattern = ctx.createPattern(bitmap, 'repeat-y');
+            ctx.fillStyle = pattern;
+            ctx.fillRect(0, 0, 100, 50);
+            ctx.fillStyle = '#0f0';
+            ctx.fillRect(0, 0, 16, 50);
+            @assert pixel 1,1 == 0,255,0,255;
+            @assert pixel 98,1 == 0,255,0,255;
+            @assert pixel 1,48 == 0,255,0,255;
+            @assert pixel 98,48 == 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.pattern.paint.repeaty.coord1
@@ -2773,18 +2821,20 @@
         };
     });
     promise.then(function(response) {
-        var pattern = ctx.createPattern(response, 'repeat-y');
-        ctx.fillStyle = pattern;
-        ctx.translate(48, 0);
-        ctx.fillRect(-48, 0, 100, 50);
-        ctx.fillStyle = '#0f0';
-        ctx.fillRect(0, 0, 16, 50);
-        @assert pixel 1,1 == 0,255,0,255;
-        @assert pixel 50,1 == 0,255,0,255;
-        @assert pixel 98,1 == 0,255,0,255;
-        @assert pixel 1,48 == 0,255,0,255;
-        @assert pixel 50,48 == 0,255,0,255;
-        @assert pixel 98,48 == 0,255,0,255;
+        createImageBitmap(response).then(bitmap => {
+            var pattern = ctx.createPattern(bitmap, 'repeat-y');
+            ctx.fillStyle = pattern;
+            ctx.translate(48, 0);
+            ctx.fillRect(-48, 0, 100, 50);
+            ctx.fillStyle = '#0f0';
+            ctx.fillRect(0, 0, 16, 50);
+            @assert pixel 1,1 == 0,255,0,255;
+            @assert pixel 50,1 == 0,255,0,255;
+            @assert pixel 98,1 == 0,255,0,255;
+            @assert pixel 1,48 == 0,255,0,255;
+            @assert pixel 50,48 == 0,255,0,255;
+            @assert pixel 98,48 == 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.pattern.paint.orientation.image
@@ -2804,18 +2854,20 @@
         };
     });
     promise.then(function(response) {
-        var pattern = ctx.createPattern(response, 'no-repeat');
-        ctx.fillStyle = pattern;
-        ctx.save();
-        ctx.translate(0, -103);
-        ctx.fillRect(0, 103, 100, 50);
-        ctx.restore();
-        ctx.fillStyle = '#0f0';
-        ctx.fillRect(0, 0, 100, 25);
-        @assert pixel 1,1 == 0,255,0,255;
-        @assert pixel 98,1 == 0,255,0,255;
-        @assert pixel 1,48 == 0,255,0,255;
-        @assert pixel 98,48 == 0,255,0,255;
+        createImageBitmap(response).then(bitmap => {
+            var pattern = ctx.createPattern(bitmap, 'no-repeat');
+            ctx.fillStyle = pattern;
+            ctx.save();
+            ctx.translate(0, -103);
+            ctx.fillRect(0, 103, 100, 50);
+            ctx.restore();
+            ctx.fillStyle = '#0f0';
+            ctx.fillRect(0, 0, 100, 25);
+            @assert pixel 1,1 == 0,255,0,255;
+            @assert pixel 98,1 == 0,255,0,255;
+            @assert pixel 1,48 == 0,255,0,255;
+            @assert pixel 98,48 == 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.pattern.paint.orientation.canvas
@@ -3349,8 +3401,10 @@
         };
     });
     promise.then(function(response) {
-        ctx.drawImage(response, 0, -50);
-        @assert pixel 50,25 == 0,255,0,255;
+        createImageBitmap(response).then(bitmap => {
+            ctx.drawImage(bitmap, 0, -50);
+            @assert pixel 50,25 == 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.shadow.image.transparent.1
@@ -3374,8 +3428,10 @@
         };
     });
     promise.then(function(response) {
-        ctx.drawImage(response, 0, -50);
-        @assert pixel 50,25 == 0,255,0,255;
+        createImageBitmap(response).then(bitmap => {
+            ctx.drawImage(bitmap, 0, -50);
+            @assert pixel 50,25 == 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.shadow.image.transparent.2
@@ -3401,12 +3457,14 @@
         };
     });
     promise.then(function(response) {
-        ctx.drawImage(response, 50, -50);
-        ctx.shadowColor = '#f00';
-        ctx.drawImage(response, -50, -50);
-        @assert pixel 25,25 == 0,255,0,255;
-        @assert pixel 50,25 == 0,255,0,255;
-        @assert pixel 75,25 == 0,255,0,255;
+        createImageBitmap(response).then(bitmap => {
+            ctx.drawImage(bitmap, 50, -50);
+            ctx.shadowColor = '#f00';
+            ctx.drawImage(bitmap, -50, -50);
+            @assert pixel 25,25 == 0,255,0,255;
+            @assert pixel 50,25 == 0,255,0,255;
+            @assert pixel 75,25 == 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.shadow.image.alpha
@@ -3430,8 +3488,10 @@
         };
     });
     promise.then(function(response) {
-        ctx.drawImage(response, 0, -50);
-        @assert pixel 50,25 ==~ 127,0,127,255;
+        createImageBitmap(response).then(bitmap => {
+            ctx.drawImage(bitmap, 0, -50);
+            @assert pixel 50,25 ==~ 127,0,127,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.shadow.image.section
@@ -3455,10 +3515,12 @@
         };
     });
     promise.then(function(response) {
-        ctx.drawImage(response, 50, 0, 50, 50, 0, -50, 50, 50);
-        @assert pixel 25,25 ==~ 0,255,0,255;
-        @assert pixel 50,25 ==~ 0,255,0,255;
-        @assert pixel 75,25 ==~ 0,255,0,255;
+        createImageBitmap(response).then(bitmap => {
+            ctx.drawImage(bitmap, 50, 0, 50, 50, 0, -50, 50, 50);
+            @assert pixel 25,25 ==~ 0,255,0,255;
+            @assert pixel 50,25 ==~ 0,255,0,255;
+            @assert pixel 75,25 ==~ 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.shadow.image.scale
@@ -3482,10 +3544,12 @@
         };
     });
     promise.then(function(response) {
-        ctx.drawImage(response, 0, 0, 100, 50, -10, -50, 240, 50);
-        @assert pixel 25,25 ==~ 0,255,0,255;
-        @assert pixel 50,25 ==~ 0,255,0,255;
-        @assert pixel 75,25 ==~ 0,255,0,255;
+        createImageBitmap(response).then(bitmap => {
+            ctx.drawImage(bitmap, 0, 0, 100, 50, -10, -50, 240, 50);
+            @assert pixel 25,25 ==~ 0,255,0,255;
+            @assert pixel 50,25 ==~ 0,255,0,255;
+            @assert pixel 75,25 ==~ 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.shadow.canvas.basic
@@ -3577,13 +3641,15 @@
         };
     });
     promise.then(function(response) {
-        var pattern = ctx.createPattern(response, 'repeat');
-        ctx.fillStyle = '#f00';
-        ctx.fillRect(0, 0, 100, 50);
-        ctx.shadowColor = '#0f0';
-        ctx.shadowOffsetY = 50;
-        ctx.fillStyle = pattern;
-        ctx.fillRect(0, -50, 100, 50);
+        createImageBitmap(response).then(bitmap => {
+            var pattern = ctx.createPattern(bitmap, 'repeat');
+            ctx.fillStyle = '#f00';
+            ctx.fillRect(0, 0, 100, 50);
+            ctx.shadowColor = '#0f0';
+            ctx.shadowOffsetY = 50;
+            ctx.fillStyle = pattern;
+            ctx.fillRect(0, -50, 100, 50);
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.shadow.pattern.transparent.1
@@ -3604,13 +3670,15 @@
         };
     });
     promise.then(function(response) {
-        var pattern = ctx.createPattern(response, 'repeat');
-        ctx.fillStyle = '#0f0';
-        ctx.fillRect(0, 0, 100, 50);
-        ctx.shadowColor = '#f00';
-        ctx.shadowOffsetY = 50;
-        ctx.fillStyle = pattern;
-        ctx.fillRect(0, -50, 100, 50);
+        createImageBitmap(response).then(bitmap => {
+            var pattern = ctx.createPattern(bitmap, 'repeat');
+            ctx.fillStyle = '#0f0';
+            ctx.fillRect(0, 0, 100, 50);
+            ctx.shadowColor = '#f00';
+            ctx.shadowOffsetY = 50;
+            ctx.fillStyle = pattern;
+            ctx.fillRect(0, -50, 100, 50);
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.shadow.pattern.transparent.2
@@ -3630,18 +3698,20 @@
         };
     });
     promise.then(function(response) {
-        var pattern = ctx.createPattern(response, 'repeat');
-        ctx.fillStyle = '#f00';
-        ctx.fillRect(0, 0, 50, 50);
-        ctx.fillStyle = '#0f0';
-        ctx.fillRect(50, 0, 50, 50);
-        ctx.shadowOffsetY = 50;
-        ctx.shadowColor = '#0f0';
-        ctx.fillStyle = pattern;
-        ctx.fillRect(0, -50, 100, 50);
-        @assert pixel 25,25 == 0,255,0,255;
-        @assert pixel 50,25 == 0,255,0,255;
-        @assert pixel 75,25 == 0,255,0,255;
+        createImageBitmap(response).then(bitmap => {
+            var pattern = ctx.createPattern(bitmap, 'repeat');
+            ctx.fillStyle = '#f00';
+            ctx.fillRect(0, 0, 50, 50);
+            ctx.fillStyle = '#0f0';
+            ctx.fillRect(50, 0, 50, 50);
+            ctx.shadowOffsetY = 50;
+            ctx.shadowColor = '#0f0';
+            ctx.fillStyle = pattern;
+            ctx.fillRect(0, -50, 100, 50);
+            @assert pixel 25,25 == 0,255,0,255;
+            @assert pixel 50,25 == 0,255,0,255;
+            @assert pixel 75,25 == 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.shadow.pattern.alpha
@@ -3661,13 +3731,15 @@
         };
     });
     promise.then(function(response) {
-        var pattern = ctx.createPattern(response, 'repeat');
-        ctx.fillStyle = '#f00';
-        ctx.fillRect(0, 0, 100, 50);
-        ctx.shadowOffsetY = 50;
-        ctx.shadowColor = '#00f';
-        ctx.fillStyle = pattern;
-        ctx.fillRect(0, -50, 100, 50);
+        createImageBitmap(response).then(bitmap => {
+            var pattern = ctx.createPattern(bitmap, 'repeat');
+            ctx.fillStyle = '#f00';
+            ctx.fillRect(0, 0, 100, 50);
+            ctx.shadowOffsetY = 50;
+            ctx.shadowColor = '#00f';
+            ctx.fillStyle = pattern;
+            ctx.fillRect(0, -50, 100, 50);
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.shadow.gradient.basic
@@ -4356,15 +4428,19 @@
         };
     });
     Promise.all([promise1, promise2]).then(function(response1, response2) {
-        ctx.drawImage(response2, 0, 0);
-        ctx.drawImage(response1, -100, 0);
-        ctx.drawImage(response1, 100, 0);
-        ctx.drawImage(response1, 0, -50);
-        ctx.drawImage(response1, 0, 50);
-        @assert pixel 0,0 ==~ 0,255,0,255;
-        @assert pixel 99,0 ==~ 0,255,0,255;
-        @assert pixel 0,49 ==~ 0,255,0,255;
-        @assert pixel 99,49 ==~ 0,255,0,255;
+        var promise3 = createImageBitmap(response1);
+        var promise4 = createImageBitmap(response2);
+        Promise.all([promise3, promise4]).then(function(bitmap1, bitmap2) {
+            ctx.drawImage(bitmap2, 0, 0);
+            ctx.drawImage(bitmap1, -100, 0);
+            ctx.drawImage(bitmap1, 100, 0);
+            ctx.drawImage(bitmap1, 0, -50);
+            ctx.drawImage(bitmap1, 0, 50);
+            @assert pixel 0,0 ==~ 0,255,0,255;
+            @assert pixel 99,0 ==~ 0,255,0,255;
+            @assert pixel 0,49 ==~ 0,255,0,255;
+            @assert pixel 99,49 ==~ 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.drawImage.5arg
@@ -4396,14 +4472,18 @@
         };
     });
     Promise.all([promise1, promise2]).then(function(response1, response2) {
-        ctx.drawImage(response2, 50, 0, 50, 50);
-        ctx.drawImage(response1, 0, 0, 50, 50);
-        ctx.fillStyle = '#0f0';
-        ctx.fillRect(0, 0, 50, 50);
-        @assert pixel 0,0 ==~ 0,255,0,255;
-        @assert pixel 99,0 ==~ 0,255,0,255;
-        @assert pixel 0,49 ==~ 0,255,0,255;
-        @assert pixel 99,49 ==~ 0,255,0,255;
+        var promise3 = createImageBitmap(response1);
+        var promise4 = createImageBitmap(response2);
+        Promise.all([promise3, promise4]).then(function(bitmap1, bitmap2) {
+            ctx.drawImage(bitmap2, 50, 0, 50, 50);
+            ctx.drawImage(bitmap1, 0, 0, 50, 50);
+            ctx.fillStyle = '#0f0';
+            ctx.fillRect(0, 0, 50, 50);
+            @assert pixel 0,0 ==~ 0,255,0,255;
+            @assert pixel 99,0 ==~ 0,255,0,255;
+            @assert pixel 0,49 ==~ 0,255,0,255;
+            @assert pixel 99,49 ==~ 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.drawImage.9arg.basic
@@ -4425,11 +4505,13 @@
         };
     });
     promise.then(function(response) {
-        ctx.drawImage(response, 0, 0, 100, 50, 0, 0, 100, 50);
-        @assert pixel 0,0 ==~ 0,255,0,255;
-        @assert pixel 99,0 ==~ 0,255,0,255;
-        @assert pixel 0,49 ==~ 0,255,0,255;
-        @assert pixel 99,49 ==~ 0,255,0,255;
+        createImageBitmap(response).then(bitmap => {
+            ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, 100, 50);
+            @assert pixel 0,0 ==~ 0,255,0,255;
+            @assert pixel 99,0 ==~ 0,255,0,255;
+            @assert pixel 0,49 ==~ 0,255,0,255;
+            @assert pixel 99,49 ==~ 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.drawImage.9arg.sourcepos
@@ -4451,11 +4533,13 @@
         };
     });
     promise.then(function(response) {
-        ctx.drawImage(response, 140, 20, 100, 50, 0, 0, 100, 50);
-        @assert pixel 0,0 ==~ 0,255,0,255;
-        @assert pixel 99,0 ==~ 0,255,0,255;
-        @assert pixel 0,49 ==~ 0,255,0,255;
-        @assert pixel 99,49 ==~ 0,255,0,255;
+        createImageBitmap(response).then(bitmap => {
+            ctx.drawImage(bitmap, 140, 20, 100, 50, 0, 0, 100, 50);
+            @assert pixel 0,0 ==~ 0,255,0,255;
+            @assert pixel 99,0 ==~ 0,255,0,255;
+            @assert pixel 0,49 ==~ 0,255,0,255;
+            @assert pixel 99,49 ==~ 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.drawImage.9arg.sourcesize
@@ -4477,18 +4561,20 @@
         };
     });
     promise.then(function(response) {
-        ctx.drawImage(response, 0, 0, 256, 256, 0, 0, 100, 50);
-        ctx.fillStyle = '#0f0';
-        ctx.fillRect(0, 0, 51, 26);
-        ctx.fillRect(49, 24, 51, 26);
-        @assert pixel 0,0 ==~ 0,255,0,255;
-        @assert pixel 99,0 ==~ 0,255,0,255;
-        @assert pixel 0,49 ==~ 0,255,0,255;
-        @assert pixel 99,49 ==~ 0,255,0,255;
-        @assert pixel 20,20 ==~ 0,255,0,255;
-        @assert pixel 80,20 ==~ 0,255,0,255;
-        @assert pixel 20,30 ==~ 0,255,0,255;
-        @assert pixel 80,30 ==~ 0,255,0,255;
+        createImageBitmap(response).then(bitmap => {
+            ctx.drawImage(bitmap, 0, 0, 256, 256, 0, 0, 100, 50);
+            ctx.fillStyle = '#0f0';
+            ctx.fillRect(0, 0, 51, 26);
+            ctx.fillRect(49, 24, 51, 26);
+            @assert pixel 0,0 ==~ 0,255,0,255;
+            @assert pixel 99,0 ==~ 0,255,0,255;
+            @assert pixel 0,49 ==~ 0,255,0,255;
+            @assert pixel 99,49 ==~ 0,255,0,255;
+            @assert pixel 20,20 ==~ 0,255,0,255;
+            @assert pixel 80,20 ==~ 0,255,0,255;
+            @assert pixel 20,30 ==~ 0,255,0,255;
+            @assert pixel 80,30 ==~ 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.drawImage.9arg.destpos
@@ -4520,15 +4606,19 @@
         };
     });
     Promise.all([promise1, promise2]).then(function(response1, response2) {
-        ctx.drawImage(response2, 0, 0, 100, 50, 0, 0, 100, 50);
-        ctx.drawImage(response1, 0, 0, 100, 50, -100, 0, 100, 50);
-        ctx.drawImage(response1, 0, 0, 100, 50, 100, 0, 100, 50);
-        ctx.drawImage(response1, 0, 0, 100, 50, 0, -50, 100, 50);
-        ctx.drawImage(response1, 0, 0, 100, 50, 0, 50, 100, 50);
-        @assert pixel 0,0 ==~ 0,255,0,255;
-        @assert pixel 99,0 ==~ 0,255,0,255;
-        @assert pixel 0,49 ==~ 0,255,0,255;
-        @assert pixel 99,49 ==~ 0,255,0,255;
+        var promise3 = createImageBitmap(response1);
+        var promise4 = createImageBitmap(response2);
+        Promise.all([promise3, promise4]).then(function(bitmap1, bitmap2) {
+            ctx.drawImage(bitmap2, 0, 0, 100, 50, 0, 0, 100, 50);
+            ctx.drawImage(bitmap1, 0, 0, 100, 50, -100, 0, 100, 50);
+            ctx.drawImage(bitmap1, 0, 0, 100, 50, 100, 0, 100, 50);
+            ctx.drawImage(bitmap1, 0, 0, 100, 50, 0, -50, 100, 50);
+            ctx.drawImage(bitmap1, 0, 0, 100, 50, 0, 50, 100, 50);
+            @assert pixel 0,0 ==~ 0,255,0,255;
+            @assert pixel 99,0 ==~ 0,255,0,255;
+            @assert pixel 0,49 ==~ 0,255,0,255;
+            @assert pixel 99,49 ==~ 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.drawImage.9arg.destsize
@@ -4560,15 +4650,19 @@
         };
     });
     Promise.all([promise1, promise2]).then(function(response1, response2) {
-        ctx.drawImage(response2, 1, 1, 1, 1, 0, 0, 100, 50);
-        ctx.drawImage(response1, 0, 0, 100, 50, -50, 0, 50, 50);
-        ctx.drawImage(response1, 0, 0, 100, 50, 100, 0, 50, 50);
-        ctx.drawImage(response1, 0, 0, 100, 50, 0, -25, 100, 25);
-        ctx.drawImage(response1, 0, 0, 100, 50, 0, 50, 100, 25);
-        @assert pixel 0,0 ==~ 0,255,0,255;
-        @assert pixel 99,0 ==~ 0,255,0,255;
-        @assert pixel 0,49 ==~ 0,255,0,255;
-        @assert pixel 99,49 ==~ 0,255,0,255;
+        var promise3 = createImageBitmap(response1);
+        var promise4 = createImageBitmap(response2);
+        Promise.all([promise3, promise4]).then(function(bitmap1, bitmap2) {
+            ctx.drawImage(bitmap2, 1, 1, 1, 1, 0, 0, 100, 50);
+            ctx.drawImage(bitmap1, 0, 0, 100, 50, -50, 0, 50, 50);
+            ctx.drawImage(bitmap1, 0, 0, 100, 50, 100, 0, 50, 50);
+            ctx.drawImage(bitmap1, 0, 0, 100, 50, 0, -25, 100, 25);
+            ctx.drawImage(bitmap1, 0, 0, 100, 50, 0, 50, 100, 25);
+            @assert pixel 0,0 ==~ 0,255,0,255;
+            @assert pixel 99,0 ==~ 0,255,0,255;
+            @assert pixel 0,49 ==~ 0,255,0,255;
+            @assert pixel 99,49 ==~ 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.drawImage.canvas
@@ -4673,8 +4767,10 @@
         };
     });
     promise.then(function(response) {
-        ctx.drawImage(response, 10.1, 10.1, 0.1, 0.1, 0, 0, 100, 50);
-        @assert pixel 50,25 ==~ 0,255,0,255;
+        createImageBitmap(response).then(bitmap => {
+            ctx.drawImage(bitmap, 10.1, 10.1, 0.1, 0.1, 0, 0, 100, 50);
+            @assert pixel 50,25 ==~ 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.drawImage.zerosource
@@ -4697,10 +4793,12 @@
         };
     });
     promise.then(function(response) {
-        @assert throws INDEX_SIZE_ERR ctx.drawImage(response, 10, 10, 0, 1, 0, 0, 100, 50);
-        @assert throws INDEX_SIZE_ERR ctx.drawImage(response, 10, 10, 1, 0, 0, 0, 100, 50);
-        @assert throws INDEX_SIZE_ERR ctx.drawImage(response, 10, 10, 0, 0, 0, 0, 100, 50);
-        @assert pixel 50,25 ==~ 0,255,0,255;
+        createImageBitmap(response).then(bitmap => {
+            @assert throws INDEX_SIZE_ERR ctx.drawImage(bitmap, 10, 10, 0, 1, 0, 0, 100, 50);
+            @assert throws INDEX_SIZE_ERR ctx.drawImage(bitmap, 10, 10, 1, 0, 0, 0, 100, 50);
+            @assert throws INDEX_SIZE_ERR ctx.drawImage(bitmap, 10, 10, 0, 0, 0, 0, 100, 50);
+            @assert pixel 50,25 ==~ 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.drawImage.zerosource.image
@@ -4725,10 +4823,12 @@
         };
     });
     promise.then(function(response) {
-        @assert throws INDEX_SIZE_ERR ctx.drawImage(response, 0, 0, 100, 50);
-        @assert throws INDEX_SIZE_ERR ctx.drawImage(response, 0, 0, 100, 50);
-        @assert throws INDEX_SIZE_ERR ctx.drawImage(response, 0, 0, 100, 50);
-        @assert pixel 50,25 == 0,255,0,255;
+        createImageBitmap(response).then(bitmap => {
+            @assert throws INDEX_SIZE_ERR ctx.drawImage(bitmap, 0, 0, 100, 50);
+            @assert throws INDEX_SIZE_ERR ctx.drawImage(bitmap, 0, 0, 100, 50);
+            @assert throws INDEX_SIZE_ERR ctx.drawImage(bitmap, 0, 0, 100, 50);
+            @assert pixel 50,25 == 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.drawImage.negativesource
@@ -4752,18 +4852,20 @@
         };
     });
     promise.then(function(response) {
-        ctx.drawImage(response, 100, 78, -100, 50, 0, 0, 50, 50);
-        ctx.drawImage(response, 100, 128, -100, -50, 50, 0, 50, 50);
-        @assert pixel 1,1 ==~ 0,255,0,255;
-        @assert pixel 1,48 ==~ 0,255,0,255;
-        @assert pixel 98,1 ==~ 0,255,0,255;
-        @assert pixel 98,48 ==~ 0,255,0,255;
-        @assert pixel 48,1 ==~ 0,255,0,255;
-        @assert pixel 48,48 ==~ 0,255,0,255;
-        @assert pixel 51,1 ==~ 0,255,0,255;
-        @assert pixel 51,48 ==~ 0,255,0,255;
-        @assert pixel 25,25 ==~ 0,255,0,255;
-        @assert pixel 75,25 ==~ 0,255,0,255;
+        createImageBitmap(response).then(bitmap => {
+            ctx.drawImage(bitmap, 100, 78, -100, 50, 0, 0, 50, 50);
+            ctx.drawImage(bitmap, 100, 128, -100, -50, 50, 0, 50, 50);
+            @assert pixel 1,1 ==~ 0,255,0,255;
+            @assert pixel 1,48 ==~ 0,255,0,255;
+            @assert pixel 98,1 ==~ 0,255,0,255;
+            @assert pixel 98,48 ==~ 0,255,0,255;
+            @assert pixel 48,1 ==~ 0,255,0,255;
+            @assert pixel 48,48 ==~ 0,255,0,255;
+            @assert pixel 51,1 ==~ 0,255,0,255;
+            @assert pixel 51,48 ==~ 0,255,0,255;
+            @assert pixel 25,25 ==~ 0,255,0,255;
+            @assert pixel 75,25 ==~ 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.drawImage.negativedest
@@ -4787,18 +4889,20 @@
         };
     });
     promise.then(function(response) {
-        ctx.drawImage(response, 100, 78, 50, 50, 0, 50, 50, -50);
-        ctx.drawImage(response, 100, 128, 50, -50, 100, 50, -50, -50);
-        @assert pixel 1,1 ==~ 0,255,0,255;
-        @assert pixel 1,48 ==~ 0,255,0,255;
-        @assert pixel 98,1 ==~ 0,255,0,255;
-        @assert pixel 98,48 ==~ 0,255,0,255;
-        @assert pixel 48,1 ==~ 0,255,0,255;
-        @assert pixel 48,48 ==~ 0,255,0,255;
-        @assert pixel 51,1 ==~ 0,255,0,255;
-        @assert pixel 51,48 ==~ 0,255,0,255;
-        @assert pixel 25,25 ==~ 0,255,0,255;
-        @assert pixel 75,25 ==~ 0,255,0,255;
+        createImageBitmap(response).then(bitmap => {
+            ctx.drawImage(bitmap, 100, 78, 50, 50, 0, 50, 50, -50);
+            ctx.drawImage(bitmap, 100, 128, 50, -50, 100, 50, -50, -50);
+            @assert pixel 1,1 ==~ 0,255,0,255;
+            @assert pixel 1,48 ==~ 0,255,0,255;
+            @assert pixel 98,1 ==~ 0,255,0,255;
+            @assert pixel 98,48 ==~ 0,255,0,255;
+            @assert pixel 48,1 ==~ 0,255,0,255;
+            @assert pixel 48,48 ==~ 0,255,0,255;
+            @assert pixel 51,1 ==~ 0,255,0,255;
+            @assert pixel 51,48 ==~ 0,255,0,255;
+            @assert pixel 25,25 ==~ 0,255,0,255;
+            @assert pixel 75,25 ==~ 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.drawImage.negativedir
@@ -4822,18 +4926,20 @@
         };
     });
     promise.then(function(response) {
-        ctx.drawImage(response, 0, 178, 50, -100, 0, 0, 50, 100);
-        ctx.drawImage(response, 0, 78, 50, 100, 50, 100, 50, -100);
-        @assert pixel 1,1 ==~ 0,255,0,255;
-        @assert pixel 1,48 ==~ 0,255,0,255;
-        @assert pixel 98,1 ==~ 0,255,0,255;
-        @assert pixel 98,48 ==~ 0,255,0,255;
-        @assert pixel 48,1 ==~ 0,255,0,255;
-        @assert pixel 48,48 ==~ 0,255,0,255;
-        @assert pixel 51,1 ==~ 0,255,0,255;
-        @assert pixel 51,48 ==~ 0,255,0,255;
-        @assert pixel 25,25 ==~ 0,255,0,255;
-        @assert pixel 75,25 ==~ 0,255,0,255;
+        createImageBitmap(response).then(bitmap => {
+            ctx.drawImage(bitmap, 0, 178, 50, -100, 0, 0, 50, 100);
+            ctx.drawImage(bitmap, 0, 78, 50, 100, 50, 100, 50, -100);
+            @assert pixel 1,1 ==~ 0,255,0,255;
+            @assert pixel 1,48 ==~ 0,255,0,255;
+            @assert pixel 98,1 ==~ 0,255,0,255;
+            @assert pixel 98,48 ==~ 0,255,0,255;
+            @assert pixel 48,1 ==~ 0,255,0,255;
+            @assert pixel 48,48 ==~ 0,255,0,255;
+            @assert pixel 51,1 ==~ 0,255,0,255;
+            @assert pixel 51,48 ==~ 0,255,0,255;
+            @assert pixel 25,25 ==~ 0,255,0,255;
+            @assert pixel 75,25 ==~ 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.drawImage.outsidesource
@@ -4860,18 +4966,22 @@
         };
     });
     Promise.all([promise1, promise2]).then(function(response1, response2) {
-        ctx.drawImage(response2, 10.5, 10.5, 89.5, 39.5, 0, 0, 100, 50);
-        ctx.drawImage(response2, 5.5, 5.5, -5.5, -5.5, 0, 0, 100, 50);
-        ctx.drawImage(response2, 100, 50, -5, -5, 0, 0, 100, 50);
-        @assert throws INDEX_SIZE_ERR ctx.drawImage(response1, -0.001, 0, 100, 50, 0, 0, 100, 50);
-        @assert throws INDEX_SIZE_ERR ctx.drawImage(response1, 0, -0.001, 100, 50, 0, 0, 100, 50);
-        @assert throws INDEX_SIZE_ERR ctx.drawImage(response1, 0, 0, 100.001, 50, 0, 0, 100, 50);
-        @assert throws INDEX_SIZE_ERR ctx.drawImage(response1, 0, 0, 100, 50.001, 0, 0, 100, 50);
-        @assert throws INDEX_SIZE_ERR ctx.drawImage(response1, 50, 0, 50.001, 50, 0, 0, 100, 50); @moz-todo
-        @assert throws INDEX_SIZE_ERR ctx.drawImage(response1, 0, 0, -5, 5, 0, 0, 100, 50);
-        @assert throws INDEX_SIZE_ERR ctx.drawImage(response1, 0, 0, 5, -5, 0, 0, 100, 50);
-        @assert throws INDEX_SIZE_ERR ctx.drawImage(response1, 110, 60, -20, -20, 0, 0, 100, 50);
-        @assert pixel 50,25 ==~ 0,255,0,255; @moz-todo
+        var promise3 = createImageBitmap(response1);
+        var promise4 = createImageBitmap(response2);
+        Promise.all([promise3, promise4]).then(function(bitmap1, bitmap2) {
+            ctx.drawImage(bitmap2, 10.5, 10.5, 89.5, 39.5, 0, 0, 100, 50);
+            ctx.drawImage(bitmap2, 5.5, 5.5, -5.5, -5.5, 0, 0, 100, 50);
+            ctx.drawImage(bitmap2, 100, 50, -5, -5, 0, 0, 100, 50);
+            @assert throws INDEX_SIZE_ERR ctx.drawImage(bitmap1, -0.001, 0, 100, 50, 0, 0, 100, 50);
+            @assert throws INDEX_SIZE_ERR ctx.drawImage(bitmap1, 0, -0.001, 100, 50, 0, 0, 100, 50);
+            @assert throws INDEX_SIZE_ERR ctx.drawImage(bitmap1, 0, 0, 100.001, 50, 0, 0, 100, 50);
+            @assert throws INDEX_SIZE_ERR ctx.drawImage(bitmap1, 0, 0, 100, 50.001, 0, 0, 100, 50);
+            @assert throws INDEX_SIZE_ERR ctx.drawImage(bitmap1, 50, 0, 50.001, 50, 0, 0, 100, 50); @moz-todo
+            @assert throws INDEX_SIZE_ERR ctx.drawImage(bitmap1, 0, 0, -5, 5, 0, 0, 100, 50);
+            @assert throws INDEX_SIZE_ERR ctx.drawImage(bitmap1, 0, 0, 5, -5, 0, 0, 100, 50);
+            @assert throws INDEX_SIZE_ERR ctx.drawImage(bitmap1, 110, 60, -20, -20, 0, 0, 100, 50);
+            @assert pixel 50,25 ==~ 0,255,0,255; @moz-todo
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.drawImage.broken
@@ -4889,10 +4999,12 @@
         };
     });
     promise.then(function(response) {
-        ctx.fillStyle = '#0f0';
-        ctx.fillRect(0, 0, 100, 50);
-        ctx.drawImage(response, 0, 0);
-        @assert pixel 50,25 ==~ 0,255,0,255; @moz-todo
+        createImageBitmap(response).then(bitmap => {
+            ctx.fillStyle = '#0f0';
+            ctx.fillRect(0, 0, 100, 50);
+            ctx.drawImage(bitmap, 0, 0);
+            @assert pixel 50,25 ==~ 0,255,0,255; @moz-todo
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.drawImage.svg
@@ -4911,8 +5023,10 @@
         };
     });
     promise.then(function(response) {
-        ctx.drawImage(response, 0, 0);
-        @assert pixel 50,25 ==~ 0,255,0,255;
+        createImageBitmap(response).then(bitmap => {
+            ctx.drawImage(bitmap, 0, 0);
+            @assert pixel 50,25 ==~ 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.drawImage.animated.poster
@@ -4933,8 +5047,10 @@
         };
     });
     promise.then(function(response) {
-        ctx.drawImage(response, 0, 0);
-        @assert pixel 50,25 ==~ 0,255,0,255; @moz-todo
+        createImageBitmap(response).then(bitmap => {
+            ctx.drawImage(bitmap, 0, 0);
+            @assert pixel 50,25 ==~ 0,255,0,255; @moz-todo
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.drawImage.path
@@ -4954,9 +5070,11 @@
         };
     });
     promise.then(function(response) {
-        ctx.drawImage(response, 0, 0);
-        ctx.fill();
-        @assert pixel 50,25 ==~ 0,255,0,255;
+        createImageBitmap(response).then(bitmap => {
+            ctx.drawImage(bitmap, 0, 0);
+            ctx.fill();
+            @assert pixel 50,25 ==~ 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.drawImage.transform
@@ -4979,8 +5097,10 @@
         };
     });
     promise.then(function(response) {
-        ctx.drawImage(response, 0, 0);
-        @assert pixel 50,25 ==~ 0,255,0,255;
+        createImageBitmap(response).then(bitmap => {
+            ctx.drawImage(bitmap, 0, 0);
+            @assert pixel 50,25 ==~ 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.drawImage.alpha
@@ -5003,8 +5123,10 @@
         };
     });
     promise.then(function(response) {
-        ctx.drawImage(response, 0, 0);
-        @assert pixel 50,25 ==~ 0,255,0,255;
+        createImageBitmap(response).then(bitmap => {
+            ctx.drawImage(bitmap, 0, 0);
+            @assert pixel 50,25 ==~ 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.drawImage.clip
@@ -5028,8 +5150,10 @@
         };
     });
     promise.then(function(response) {
-        ctx.drawImage(response, 0, 0);
-        @assert pixel 50,25 ==~ 0,255,0,255;
+        createImageBitmap(response).then(bitmap => {
+            ctx.drawImage(bitmap, 0, 0);
+            @assert pixel 50,25 ==~ 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.drawImage.composite
@@ -5052,8 +5176,10 @@
         };
     });
     promise.then(function(response) {
-        ctx.drawImage(response, 0, 0);
-        @assert pixel 50,25 ==~ 0,255,0,255;
+        createImageBitmap(response).then(bitmap => {
+            ctx.drawImage(bitmap, 0, 0);
+            @assert pixel 50,25 ==~ 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.drawImage.nowrap
@@ -5074,10 +5200,12 @@
         };
     });
     promise.then(function(response) {
-        ctx.drawImage(response, -1950, 0, 2000, 50);
-        @assert pixel 45,25 ==~ 0,255,0,255;
-        @assert pixel 50,25 ==~ 0,255,0,255;
-        @assert pixel 55,25 ==~ 0,255,0,255;
+        createImageBitmap(response).then(bitmap => {
+            ctx.drawImage(bitmap, -1950, 0, 2000, 50);
+            @assert pixel 45,25 ==~ 0,255,0,255;
+            @assert pixel 50,25 ==~ 0,255,0,255;
+            @assert pixel 55,25 ==~ 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.drawImage.nonfinite
@@ -5100,10 +5228,12 @@
         };
     });
     promise.then(function(response) {
-        @nonfinite ctx.drawImage(<response>, <0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>);
-        @nonfinite ctx.drawImage(<response>, <0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <100 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>);
-        @nonfinite ctx.drawImage(<response>, <0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <100 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <100 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>);
-        @assert pixel 50,25 == 0,255,0,255;
+        createImageBitmap(response).then(bitmap => {
+            @nonfinite ctx.drawImage(<bitmap>, <0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>);
+            @nonfinite ctx.drawImage(<bitmap>, <0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <100 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>);
+            @nonfinite ctx.drawImage(<bitmap>, <0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <100 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <100 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>);
+            @assert pixel 50,25 == 0,255,0,255;
+        }, t_fail);
     }).then(t_pass, t_fail);
 
 - name: 2d.imageData.create2.basic
diff --git a/third_party/blink/web_tests/external/wpt/tools/webdriver/webdriver/client.py b/third_party/blink/web_tests/external/wpt/tools/webdriver/webdriver/client.py
index cf4c5fd..0c5bbff 100644
--- a/third_party/blink/web_tests/external/wpt/tools/webdriver/webdriver/client.py
+++ b/third_party/blink/web_tests/external/wpt/tools/webdriver/webdriver/client.py
@@ -738,10 +738,6 @@
         return self.send_element_command("GET", "selected")
 
     @command
-    def screenshot(self):
-        return self.send_element_command("GET", "screenshot")
-
-    @command
     def attribute(self, name):
         return self.send_element_command("GET", "attribute/%s" % name)
 
diff --git a/third_party/blink/web_tests/external/wpt/trusted-types/empty-default-policy-report-only.tentative.html b/third_party/blink/web_tests/external/wpt/trusted-types/empty-default-policy-report-only.tentative.html
new file mode 100644
index 0000000..1ba9c5e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/trusted-types/empty-default-policy-report-only.tentative.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script>
+// Ensure that only the right events trigger violation reports.
+// The Promise will resolve, when an event including the string "done" is
+// received. The last line of this test file will cause this trigger.
+promise_test(t => {
+  let count = 0;
+  return new Promise((resolve, reject) => {
+    document.addEventListener("securitypolicyviolation", e => {
+      e.stopPropagation();
+      // We count the violation reports. We expect one each for "abc" test cases, and one
+      // for the "done" line at the end, which signals the end of the test run.
+      if (e.sample.includes("done")) {
+        resolve(count);
+      } else if (e.sample.includes("abc")) {
+        count++;
+      } else {
+        reject();
+      }
+    });
+  }).then(counter => {
+    assert_equals(counter, testCases.length, "event count");
+  });
+}, "Count SecurityPolicyViolation events.");
+
+const testCases = [
+  [ "script", "src" ],
+  [ "div", "innerHTML" ],
+  [ "script", "text" ],
+];
+
+trustedTypes.createPolicy("default", {});
+
+testCases.forEach(c => {
+  const name = `${c[0]}.${c[1]} `;
+  test(t => {
+    const element = document.createElement(c[0]);
+    element[c[1]] = "abc";
+    if (c[1] == "src") {
+      assert_equals(element[c[1]], location.protocol + "//" + location.host + "/trusted-types/abc");
+    } else {
+      assert_equals(element[c[1]], "abc");
+    }
+  }, name + "default");
+});
+
+// Trigger the exit condition in the "Count" promise test above.
+try { document.createElement("script").text = "done"; } catch (e) {}
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/trusted-types/empty-default-policy-report-only.tentative.html.headers b/third_party/blink/web_tests/external/wpt/trusted-types/empty-default-policy-report-only.tentative.html.headers
new file mode 100644
index 0000000..fa87952
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/trusted-types/empty-default-policy-report-only.tentative.html.headers
@@ -0,0 +1 @@
+Content-Security-Policy-Report-Only: trusted-types *
diff --git a/third_party/blink/web_tests/external/wpt/trusted-types/empty-default-policy.tentative.html b/third_party/blink/web_tests/external/wpt/trusted-types/empty-default-policy.tentative.html
new file mode 100644
index 0000000..2d3a10ad
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/trusted-types/empty-default-policy.tentative.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script>
+// Ensure that only the right events trigger violation reports.
+// The Promise will resolve, when an event including the string "done" is
+// received. The last line of this test file will cause this trigger.
+promise_test(t => {
+  let count = 0;
+  return new Promise((resolve, reject) => {
+    document.addEventListener("securitypolicyviolation", e => {
+      e.stopPropagation();
+      // We count the violation reports. We expect one each for "abc" test cases, and one
+      // for the "done" line at the end, which signals the end of the test run.
+      if (e.sample.includes("done")) {
+        resolve(count);
+      } else if (e.sample.includes("abc")) {
+        count++;
+      } else {
+        reject();
+      }
+    });
+  }).then(counter => {
+    assert_equals(counter, testCases.length, "event count");
+  });
+}, "Count SecurityPolicyViolation events.");
+
+const testCases = [
+  [ "script", "src" ],
+  [ "div", "innerHTML" ],
+  [ "script", "text" ],
+];
+
+trustedTypes.createPolicy("default", {});
+
+testCases.forEach(c => {
+  const name = `${c[0]}.${c[1]} `;
+  test(t => {
+    const element = document.createElement(c[0]);
+    assert_throws(TypeError(), _ => element[c[1]] = "abc");
+    assert_equals(element[c[1]], "");
+  }, name + "default");
+});
+
+// Trigger the exit condition in the "Count" promise test above.
+try { document.createElement("script").text = "done"; } catch (e) {}
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/trusted-types/empty-default-policy.tentative.html.headers b/third_party/blink/web_tests/external/wpt/trusted-types/empty-default-policy.tentative.html.headers
new file mode 100644
index 0000000..1bc33add
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/trusted-types/empty-default-policy.tentative.html.headers
@@ -0,0 +1 @@
+Content-Security-Policy: trusted-types *
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/take_element_screenshot/__init__.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/take_element_screenshot/__init__.py
index 9a82cc4..9de87924 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/take_element_screenshot/__init__.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/take_element_screenshot/__init__.py
@@ -1,10 +1,12 @@
-def element_dimensions(session, element):
-    return tuple(session.execute_script("""
-        const {devicePixelRatio} = window;
-        let {width, height} = arguments[0].getBoundingClientRect();
+def element_rect(session, element):
+    return session.execute_script("""
+        let {devicePixelRatio} = window;
+        let {left, top, width, height} = arguments[0].getBoundingClientRect();
 
-        return [
-          Math.floor(width * devicePixelRatio),
-          Math.floor(height * devicePixelRatio),
-        ];
-        """, args=(element,)))
+        return {
+            x: Math.floor((left + window.pageXOffset) * devicePixelRatio),
+            y: Math.floor((top + window.pageYOffset) * devicePixelRatio),
+            width: Math.floor(width * devicePixelRatio),
+            height: Math.floor(height * devicePixelRatio),
+        };
+        """, args=(element,))
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/take_element_screenshot/iframe.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/take_element_screenshot/iframe.py
index 83f55def..242122f0 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/take_element_screenshot/iframe.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/take_element_screenshot/iframe.py
@@ -4,58 +4,21 @@
 from tests.support.image import png_dimensions
 from tests.support.inline import iframe, inline
 
-from . import element_dimensions
+from . import element_rect
 
-DEFAULT_CONTENT = "<div id='content'>Lorem ipsum dolor sit amet.</div>"
 
-REFERENCE_CONTENT = "<div id='outer'>{}</div>".format(DEFAULT_CONTENT)
-REFERENCE_STYLE = """
+DEFAULT_CSS_STYLE = """
     <style>
-      #outer {
+      div, iframe {
         display: block;
-        margin: 0;
-        border: 0;
-        width: 200px;
-        height: 200px;
-      }
-      #content {
-        display: block;
-        margin: 0;
-        border: 0;
-        width: 100px;
-        height: 100px;
-        background: green;
+        border: 1px solid blue;
+        width: 10em;
+        height: 10em;
       }
     </style>
 """
 
-OUTER_IFRAME_STYLE = """
-    <style>
-      iframe {
-        display: block;
-        margin: 0;
-        border: 0;
-        width: 200px;
-        height: 200px;
-      }
-    </style>
-"""
-
-INNER_IFRAME_STYLE = """
-    <style>
-      body {
-        margin: 0;
-      }
-      div {
-        display: block;
-        margin: 0;
-        border: 0;
-        width: 100px;
-        height: 100px;
-        background: green;
-      }
-    </style>
-"""
+DEFAULT_CONTENT = "<div>Lorem ipsum dolor sit amet.</div>"
 
 
 def take_element_screenshot(session, element_id):
@@ -68,55 +31,24 @@
     )
 
 
-def test_frame_element(session):
-    # Create a reference element which looks exactly like the frame's content
-    session.url = inline("{0}{1}".format(REFERENCE_STYLE, REFERENCE_CONTENT))
-
-    # Capture the inner content as reference image
-    ref_el = session.find.css("#content", all=False)
-    ref_screenshot = ref_el.screenshot()
-    ref_dimensions = element_dimensions(session, ref_el)
-
-    assert png_dimensions(ref_screenshot) == ref_dimensions
-
-    # Capture the frame's element
-    iframe_content = "{0}{1}".format(INNER_IFRAME_STYLE, DEFAULT_CONTENT)
-    session.url = inline("""{0}{1}""".format(OUTER_IFRAME_STYLE, iframe(iframe_content)))
-
-    frame = session.find.css("iframe", all=False)
-    session.switch_frame(frame)
-    div = session.find.css("div", all=False)
-    div_dimensions = element_dimensions(session, div)
-    assert div_dimensions == ref_dimensions
-
-    response = take_element_screenshot(session, div.id)
-    div_screenshot = assert_success(response)
-
-    assert png_dimensions(div_screenshot) == ref_dimensions
-    assert div_screenshot == ref_screenshot
-
-
 @pytest.mark.parametrize("domain", ["", "alt"], ids=["same_origin", "cross_origin"])
 def test_source_origin(session, url, domain):
-    # Create a reference element which looks exactly like the iframe
-    session.url = inline("{0}{1}".format(REFERENCE_STYLE, REFERENCE_CONTENT))
+    session.url = inline("""{0}{1}""".format(DEFAULT_CSS_STYLE, DEFAULT_CONTENT))
+    element = session.find.css("div", all=False)
+    rect = element_rect(session, element)
 
-    div = session.find.css("div", all=False)
-    div_dimensions = element_dimensions(session, div)
-
-    response = take_element_screenshot(session, div.id)
+    response = take_element_screenshot(session, element.id)
     reference_screenshot = assert_success(response)
-    assert png_dimensions(reference_screenshot) == div_dimensions
+    assert png_dimensions(reference_screenshot) == (rect["width"], rect["height"])
 
-    iframe_content = "{0}{1}".format(INNER_IFRAME_STYLE, DEFAULT_CONTENT)
+    iframe_content = "<style>body {{ margin: 0; }}</style>{}".format(DEFAULT_CONTENT)
     session.url = inline("""{0}{1}""".format(
-        OUTER_IFRAME_STYLE, iframe(iframe_content, domain=domain)))
-
+        DEFAULT_CSS_STYLE, iframe(iframe_content, domain=domain)))
     frame_element = session.find.css("iframe", all=False)
-    frame_dimensions = element_dimensions(session, frame_element)
+    frame_rect = element_rect(session, frame_element)
 
     response = take_element_screenshot(session, frame_element.id)
     screenshot = assert_success(response)
-    assert png_dimensions(screenshot) == frame_dimensions
+    assert png_dimensions(screenshot) == (frame_rect["width"], frame_rect["height"])
 
     assert screenshot == reference_screenshot
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/take_element_screenshot/screenshot.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/take_element_screenshot/screenshot.py
index 50d33a2..fd460b6 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/take_element_screenshot/screenshot.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/take_element_screenshot/screenshot.py
@@ -2,7 +2,7 @@
 from tests.support.image import png_dimensions
 from tests.support.inline import inline
 
-from . import element_dimensions
+from . import element_rect
 
 
 def take_element_screenshot(session, element_id):
@@ -32,8 +32,9 @@
 def test_format_and_dimensions(session):
     session.url = inline("<input>")
     element = session.find.css("input", all=False)
+    rect = element_rect(session, element)
 
     response = take_element_screenshot(session, element.id)
     screenshot = assert_success(response)
 
-    assert png_dimensions(screenshot) == element_dimensions(session, element)
+    assert png_dimensions(screenshot) == (rect["width"], rect["height"])
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/take_screenshot/__init__.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/take_screenshot/__init__.py
index f3001d9..c07f8d1 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/take_screenshot/__init__.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/take_screenshot/__init__.py
@@ -1,18 +1,6 @@
-def element_dimensions(session, element):
-    return tuple(session.execute_script("""
-        const {devicePixelRatio} = window;
-        let {width, height} = arguments[0].getBoundingClientRect();
-
-        return [
-          Math.floor(width * devicePixelRatio),
-          Math.floor(height * devicePixelRatio),
-        ];
-        """, args=(element,)))
-
-
 def viewport_dimensions(session):
     return tuple(session.execute_script("""
-        const {devicePixelRatio, innerHeight, innerWidth} = window;
+        let {devicePixelRatio, innerHeight, innerWidth} = window;
 
         return [
           Math.floor(innerWidth * devicePixelRatio),
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/take_screenshot/iframe.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/take_screenshot/iframe.py
index 6186cf3..4cf8ad0 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/take_screenshot/iframe.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/take_screenshot/iframe.py
@@ -4,58 +4,21 @@
 from tests.support.image import png_dimensions
 from tests.support.inline import iframe, inline
 
-from . import element_dimensions, viewport_dimensions
+from . import viewport_dimensions
 
-DEFAULT_CONTENT = "<div id='content'>Lorem ipsum dolor sit amet.</div>"
 
-REFERENCE_CONTENT = "<div id='outer'>{}</div>".format(DEFAULT_CONTENT)
-REFERENCE_STYLE = """
+DEFAULT_CSS_STYLE = """
     <style>
-      #outer {
+      div, iframe {
         display: block;
-        margin: 0;
-        border: 0;
-        width: 200px;
-        height: 200px;
-      }
-      #content {
-        display: block;
-        margin: 0;
-        border: 0;
-        width: 100px;
-        height: 100px;
-        background: green;
+        border: 1px solid blue;
+        width: 10em;
+        height: 10em;
       }
     </style>
 """
 
-OUTER_IFRAME_STYLE = """
-    <style>
-      iframe {
-        display: block;
-        margin: 0;
-        border: 0;
-        width: 200px;
-        height: 200px;
-      }
-    </style>
-"""
-
-INNER_IFRAME_STYLE = """
-    <style>
-      body {
-        margin: 0;
-      }
-      div {
-        display: block;
-        margin: 0;
-        border: 0;
-        width: 100px;
-        height: 100px;
-        background: green;
-      }
-    </style>
-"""
+DEFAULT_CONTENT = "<div>Lorem ipsum dolor sit amet.</div>"
 
 
 def take_screenshot(session):
@@ -63,45 +26,17 @@
         "GET", "session/{session_id}/screenshot".format(**vars(session)))
 
 
-def test_frame_content(session):
-    # Create a reference element which looks exactly like the frame's content
-    session.url = inline("{0}{1}".format(REFERENCE_STYLE, REFERENCE_CONTENT))
-
-    # Capture the inner content as reference image
-    ref_el = session.find.css("div", all=False)
-    ref_screenshot = ref_el.screenshot()
-    ref_dimensions = element_dimensions(session, ref_el)
-
-    assert ref_dimensions != viewport_dimensions
-    assert png_dimensions(ref_screenshot) == ref_dimensions
-
-    # Capture the frame's content
-    iframe_content = "{0}{1}".format(INNER_IFRAME_STYLE, DEFAULT_CONTENT)
-    session.url = inline("""{0}{1}""".format(OUTER_IFRAME_STYLE, iframe(iframe_content)))
-
-    frame = session.find.css("iframe", all=False)
-    session.switch_frame(frame)
-    frame_dimensions = viewport_dimensions(session)
-    assert frame_dimensions == ref_dimensions
-
-    response = take_screenshot(session)
-    frame_screenshot = assert_success(response)
-
-    assert png_dimensions(frame_screenshot) == ref_dimensions
-    assert frame_screenshot == ref_screenshot
-
-
 @pytest.mark.parametrize("domain", ["", "alt"], ids=["same_origin", "cross_origin"])
-def test_source_origin(session, domain):
-    session.url = inline("{0}{1}".format(REFERENCE_STYLE, REFERENCE_CONTENT))
+def test_source_origin(session, url, domain):
+    session.url = inline("""{0}{1}""".format(DEFAULT_CSS_STYLE, DEFAULT_CONTENT))
 
     response = take_screenshot(session)
     reference_screenshot = assert_success(response)
     assert png_dimensions(reference_screenshot) == viewport_dimensions(session)
 
-    iframe_content = "{0}{1}".format(INNER_IFRAME_STYLE, DEFAULT_CONTENT)
+    iframe_content = "<style>body {{ margin: 0; }}</style>{}".format(DEFAULT_CONTENT)
     session.url = inline("""{0}{1}""".format(
-        OUTER_IFRAME_STYLE, iframe(iframe_content, domain=domain)))
+        DEFAULT_CSS_STYLE, iframe(iframe_content, domain=domain)))
 
     response = take_screenshot(session)
     screenshot = assert_success(response)
diff --git a/third_party/blink/web_tests/fast/css-grid-layout/grid-auto-columns-rows-get-set-expected.txt b/third_party/blink/web_tests/fast/css-grid-layout/grid-auto-columns-rows-get-set-expected.txt
index 91772b5c..21d934de 100644
--- a/third_party/blink/web_tests/fast/css-grid-layout/grid-auto-columns-rows-get-set-expected.txt
+++ b/third_party/blink/web_tests/fast/css-grid-layout/grid-auto-columns-rows-get-set-expected.txt
@@ -15,19 +15,19 @@
 PASS window.getComputedStyle(gridAutoFitContent, '').getPropertyValue('grid-auto-columns') is "fit-content(30px)"
 
 
-Test that getting grid-template-columns and grid-template-rows set through CSS lists every track listed whether implicitly or explicitly created
+Test that getting grid-template-columns and grid-template-rows set through CSS only lists explicit tracks, but not implicit ones
 PASS window.getComputedStyle(gridAutoFixedFixedWithChildren, '').getPropertyValue('grid-auto-rows') is "30px"
 PASS window.getComputedStyle(gridAutoFixedFixedWithChildren, '').getPropertyValue('grid-auto-columns') is "50px"
-PASS window.getComputedStyle(gridAutoFixedFixedWithChildren, '').getPropertyValue('grid-template-columns') is "50px"
-PASS window.getComputedStyle(gridAutoFixedFixedWithChildren, '').getPropertyValue('grid-template-rows') is "30px"
+PASS window.getComputedStyle(gridAutoFixedFixedWithChildren, '').getPropertyValue('grid-template-columns') is "none"
+PASS window.getComputedStyle(gridAutoFixedFixedWithChildren, '').getPropertyValue('grid-template-rows') is "none"
 PASS window.getComputedStyle(gridAutoFixedFixedWithFixedFixedWithChildren, '').getPropertyValue('grid-auto-rows') is "30px"
 PASS window.getComputedStyle(gridAutoFixedFixedWithFixedFixedWithChildren, '').getPropertyValue('grid-auto-columns') is "40px"
-PASS window.getComputedStyle(gridAutoFixedFixedWithFixedFixedWithChildren, '').getPropertyValue('grid-template-columns') is "20px 40px 40px"
-PASS window.getComputedStyle(gridAutoFixedFixedWithFixedFixedWithChildren, '').getPropertyValue('grid-template-rows') is "15px 30px 30px"
-PASS window.getComputedStyle(gridAutoMultipleTracks, '').getPropertyValue('grid-template-columns') is "25px 50px 100px 25px"
-PASS window.getComputedStyle(gridAutoMultipleTracks, '').getPropertyValue('grid-template-rows') is "10px 20px 30px 10px"
-PASS window.getComputedStyle(gridAutoMultipleTracksNegativeIndexes, '').getPropertyValue('grid-template-columns') is "100px 25px 50px 100px"
-PASS window.getComputedStyle(gridAutoMultipleTracksNegativeIndexes, '').getPropertyValue('grid-template-rows') is "30px 10px 20px 30px"
+PASS window.getComputedStyle(gridAutoFixedFixedWithFixedFixedWithChildren, '').getPropertyValue('grid-template-columns') is "20px"
+PASS window.getComputedStyle(gridAutoFixedFixedWithFixedFixedWithChildren, '').getPropertyValue('grid-template-rows') is "15px"
+PASS window.getComputedStyle(gridAutoMultipleTracks, '').getPropertyValue('grid-template-columns') is "none"
+PASS window.getComputedStyle(gridAutoMultipleTracks, '').getPropertyValue('grid-template-rows') is "none"
+PASS window.getComputedStyle(gridAutoMultipleTracksNegativeIndexes, '').getPropertyValue('grid-template-columns') is "none"
+PASS window.getComputedStyle(gridAutoMultipleTracksNegativeIndexes, '').getPropertyValue('grid-template-rows') is "none"
 
 
 Test that grid-template-* definitions are not affected by grid-auto-* definitions
diff --git a/third_party/blink/web_tests/fast/css-grid-layout/grid-auto-columns-rows-get-set.html b/third_party/blink/web_tests/fast/css-grid-layout/grid-auto-columns-rows-get-set.html
index 28898073..5a32d9cf 100644
--- a/third_party/blink/web_tests/fast/css-grid-layout/grid-auto-columns-rows-get-set.html
+++ b/third_party/blink/web_tests/fast/css-grid-layout/grid-auto-columns-rows-get-set.html
@@ -84,13 +84,13 @@
 testGridAutoDefinitionsValues(document.getElementById("gridAutoFitContent"), "fit-content(50%)", "fit-content(30px)");
 
 debug("");
-debug("Test that getting grid-template-columns and grid-template-rows set through CSS lists every track listed whether implicitly or explicitly created");
+debug("Test that getting grid-template-columns and grid-template-rows set through CSS only lists explicit tracks, but not implicit ones");
 testGridAutoDefinitionsValues(document.getElementById("gridAutoFixedFixedWithChildren"), "30px", "50px");
-testGridDefinitionsValues(document.getElementById("gridAutoFixedFixedWithChildren"), "50px", "30px");
+testGridDefinitionsValues(document.getElementById("gridAutoFixedFixedWithChildren"), "none", "none");
 testGridAutoDefinitionsValues(document.getElementById("gridAutoFixedFixedWithFixedFixedWithChildren"), "30px", "40px");
-testGridDefinitionsValues(document.getElementById("gridAutoFixedFixedWithFixedFixedWithChildren"), "20px 40px 40px", "15px 30px 30px");
-testGridDefinitionsValues(document.getElementById("gridAutoMultipleTracks"), "25px 50px 100px 25px", "10px 20px 30px 10px");
-testGridDefinitionsValues(document.getElementById("gridAutoMultipleTracksNegativeIndexes"), "100px 25px 50px 100px", "30px 10px 20px 30px");
+testGridDefinitionsValues(document.getElementById("gridAutoFixedFixedWithFixedFixedWithChildren"), "20px", "15px");
+testGridDefinitionsValues(document.getElementById("gridAutoMultipleTracks"), "none", "none");
+testGridDefinitionsValues(document.getElementById("gridAutoMultipleTracksNegativeIndexes"), "none", "none");
 
 debug("");
 debug("Test that grid-template-* definitions are not affected by grid-auto-* definitions");
diff --git a/third_party/blink/web_tests/fast/css-grid-layout/grid-columns-rows-get-set.html b/third_party/blink/web_tests/fast/css-grid-layout/grid-columns-rows-get-set.html
index ea79d1c..01543f4 100644
--- a/third_party/blink/web_tests/fast/css-grid-layout/grid-columns-rows-get-set.html
+++ b/third_party/blink/web_tests/fast/css-grid-layout/grid-columns-rows-get-set.html
@@ -93,7 +93,7 @@
 }
 .gridWithFitContentFunction {
     grid-template-columns: fit-content(30%);
-    grid-template-rows: fit-content(20px):
+    grid-template-rows: fit-content(20px);
 }
 </style>
 <script src="../../resources/js-test.js"></script>
diff --git a/third_party/blink/web_tests/fast/css-grid-layout/grid-template-shorthand-get-set-expected.txt b/third_party/blink/web_tests/fast/css-grid-layout/grid-template-shorthand-get-set-expected.txt
index 52b8653..b7ec83b6 100644
--- a/third_party/blink/web_tests/fast/css-grid-layout/grid-template-shorthand-get-set-expected.txt
+++ b/third_party/blink/web_tests/fast/css-grid-layout/grid-template-shorthand-get-set-expected.txt
@@ -48,10 +48,10 @@
 PASS window.getComputedStyle(gridTemplateComplexFormWithAuto, '').getPropertyValue('grid-template-columns') is "10px"
 PASS window.getComputedStyle(gridTemplateComplexFormWithAuto, '').getPropertyValue('grid-template-rows') is "0px"
 PASS window.getComputedStyle(gridTemplateComplexFormWithAuto, '').getPropertyValue('grid-template-areas') is "\"a\""
-PASS window.getComputedStyle(gridTemplateComplexFormOnlyAreas, '').getPropertyValue('grid-template-columns') is "0px"
+PASS window.getComputedStyle(gridTemplateComplexFormOnlyAreas, '').getPropertyValue('grid-template-columns') is "none"
 PASS window.getComputedStyle(gridTemplateComplexFormOnlyAreas, '').getPropertyValue('grid-template-rows') is "0px"
 PASS window.getComputedStyle(gridTemplateComplexFormOnlyAreas, '').getPropertyValue('grid-template-areas') is "\"a\""
-PASS window.getComputedStyle(gridTemplateNoColumnsRowWithEmptyTrailingLineNames, '').getPropertyValue('grid-template-columns') is "0px"
+PASS window.getComputedStyle(gridTemplateNoColumnsRowWithEmptyTrailingLineNames, '').getPropertyValue('grid-template-columns') is "none"
 PASS window.getComputedStyle(gridTemplateNoColumnsRowWithEmptyTrailingLineNames, '').getPropertyValue('grid-template-rows') is "[first] 0px"
 PASS window.getComputedStyle(gridTemplateNoColumnsRowWithEmptyTrailingLineNames, '').getPropertyValue('grid-template-areas') is "\"a\""
 PASS window.getComputedStyle(gridTemplateConsecutiveAreas, '').getPropertyValue('grid-template-columns') is "10px"
@@ -186,7 +186,7 @@
 PASS element.style.gridTemplateRows is "[foo1 bar1] 50px [foo2 bar2 foo3 bar3] 50px [foo4 bar4]"
 PASS getComputedStyle(element, '').getPropertyValue('grid-template-areas') is "\"a\" \"b\""
 PASS element.style.gridTemplateAreas is "\"a\" \"b\""
-PASS getComputedStyle(element, '').getPropertyValue('grid-template-columns') is "0px"
+PASS getComputedStyle(element, '').getPropertyValue('grid-template-columns') is "none"
 PASS element.style.gridTemplateColumns is "none"
 PASS getComputedStyle(element, '').getPropertyValue('grid-template-rows') is "0px"
 PASS element.style.gridTemplateRows is "auto"
diff --git a/third_party/blink/web_tests/fast/css-grid-layout/grid-template-shorthand-get-set.html b/third_party/blink/web_tests/fast/css-grid-layout/grid-template-shorthand-get-set.html
index de0c6a2..17846cc 100644
--- a/third_party/blink/web_tests/fast/css-grid-layout/grid-template-shorthand-get-set.html
+++ b/third_party/blink/web_tests/fast/css-grid-layout/grid-template-shorthand-get-set.html
@@ -222,8 +222,8 @@
     testGridDefinitionsValues(document.getElementById("gridTemplateComplexFormWithLineNamesMultipleRowsAndColumns"), "[first] 10px [nav nav2] 15px [nav] 15px [last]", "100px [nav nav2] 25px [nav nav2] 25px [last]", '"a b c" "d e f" "g h i"');
     testGridDefinitionsValues(document.getElementById("gridTemplateComplexFormWithLineNamesMultipleRowsAndColumnsWithoutRowsSizes"), "[first] 10px [nav nav2] 15px [nav] 15px [last]", "0px [nav nav2] 0px [nav nav2] 0px [last]", '"a b c" "d e f" "g h i"');
     testGridDefinitionsValues(document.getElementById("gridTemplateComplexFormWithAuto"), "10px", "0px", '"a"');
-    testGridDefinitionsValues(document.getElementById("gridTemplateComplexFormOnlyAreas"), "0px", "0px", '"a"');
-    testGridDefinitionsValues(document.getElementById("gridTemplateNoColumnsRowWithEmptyTrailingLineNames"), "0px", "[first] 0px", '"a"');
+    testGridDefinitionsValues(document.getElementById("gridTemplateComplexFormOnlyAreas"), "none", "0px", '"a"');
+    testGridDefinitionsValues(document.getElementById("gridTemplateNoColumnsRowWithEmptyTrailingLineNames"), "none", "[first] 0px", '"a"');
     testGridDefinitionsValues(document.getElementById("gridTemplateConsecutiveAreas"), "10px", "0px 0px", '"a" "a"');
 
     debug("");
@@ -275,7 +275,7 @@
     testGridDefinitionsSetJSValues("66px / 18px", "18px", "66px", "none");
     testGridDefinitionsSetJSValues("[head] 'a' 15px [tail] / 10px", "10px", "[head] 15px [tail]", "\"a\"");
     testGridDefinitionsSetJSValues("[foo1 bar1] 'a' 50px [foo2 bar2] [foo3 bar3] 'b' 50px [foo4 bar4] / 100px", "100px", "[foo1 bar1] 50px [foo2 bar2 foo3 bar3] 50px [foo4 bar4]", "\"a\" \"b\"");
-    testGridDefinitionsSetJSValues("'a'", "0px", "0px", "\"a\"", "none", "auto");
+    testGridDefinitionsSetJSValues("'a'", "none", "0px", "\"a\"", "none", "auto");
 
     debug("");
     debug("Test setting grid-template shorthand to bad values through JS");
diff --git a/third_party/blink/web_tests/fast/css-grid-layout/mark-as-infinitely-growable.html b/third_party/blink/web_tests/fast/css-grid-layout/mark-as-infinitely-growable.html
index 944d557a..dea1bee 100644
--- a/third_party/blink/web_tests/fast/css-grid-layout/mark-as-infinitely-growable.html
+++ b/third_party/blink/web_tests/fast/css-grid-layout/mark-as-infinitely-growable.html
@@ -5,6 +5,7 @@
 <style>
 .grid {
     font: 10px/1 Ahem;
+    grid-template-rows: auto;
 }
 .gridAutoAndAuto {
     grid-template-columns: auto auto;
diff --git a/third_party/blink/web_tests/fast/css-grid-layout/named-grid-lines-computed-style-implicit-tracks-expected.txt b/third_party/blink/web_tests/fast/css-grid-layout/named-grid-lines-computed-style-implicit-tracks-expected.txt
index e10c026..47673fa 100644
--- a/third_party/blink/web_tests/fast/css-grid-layout/named-grid-lines-computed-style-implicit-tracks-expected.txt
+++ b/third_party/blink/web_tests/fast/css-grid-layout/named-grid-lines-computed-style-implicit-tracks-expected.txt
@@ -1,23 +1,127 @@
-Test that computed style for grid-template-columns and grid-template-rows works as expected with named grid lines and implicit tracks
+Test that computed style for grid-template-columns and grid-template-rows works as expected, with named grid lines but without implicit tracks. Also test the size and position of the items, since the size of implicit tracks is not exposed
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 PASS window.getComputedStyle(gridOneColumnSingle, '').getPropertyValue('grid-template-columns') is "[a] 200px [b]"
-PASS window.getComputedStyle(gridOneColumnSingle, '').getPropertyValue('grid-template-rows') is "[x] 50px [y] 50px 50px 50px"
-PASS window.getComputedStyle(gridTwoColumnsSingle, '').getPropertyValue('grid-template-columns') is "[a] 200px [b] 150px"
-PASS window.getComputedStyle(gridTwoColumnsSingle, '').getPropertyValue('grid-template-rows') is "[x] 50px [y] 50px"
-PASS window.getComputedStyle(gridThreeColumnsSingle, '').getPropertyValue('grid-template-columns') is "[a] 200px [b] 150px 100px"
-PASS window.getComputedStyle(gridThreeColumnsSingle, '').getPropertyValue('grid-template-rows') is "[x] 50px [y] 50px"
-PASS window.getComputedStyle(gridFourColumnsSingle, '').getPropertyValue('grid-template-columns') is "[a] 200px [b] 150px 100px 50px"
+PASS window.getComputedStyle(gridOneColumnSingle, '').getPropertyValue('grid-template-rows') is "[x] 50px [y]"
+PASS gridOneColumnSingle.childElementCount is 4
+PASS gridOneColumnSingle.children[0].offsetWidth is 200
+PASS gridOneColumnSingle.children[0].offsetTop is 0
+PASS gridOneColumnSingle.children[0].offsetHeight is 50
+PASS gridOneColumnSingle.children[1].offsetWidth is 200
+PASS gridOneColumnSingle.children[1].offsetTop is 50
+PASS gridOneColumnSingle.children[1].offsetHeight is 50
+PASS gridOneColumnSingle.children[2].offsetWidth is 200
+PASS gridOneColumnSingle.children[2].offsetTop is 100
+PASS gridOneColumnSingle.children[2].offsetHeight is 50
+PASS gridOneColumnSingle.children[3].offsetWidth is 200
+PASS gridOneColumnSingle.children[3].offsetTop is 150
+PASS gridOneColumnSingle.children[3].offsetHeight is 50
+PASS window.getComputedStyle(gridTwoColumnsSingle, '').getPropertyValue('grid-template-columns') is "[a] 200px [b]"
+PASS window.getComputedStyle(gridTwoColumnsSingle, '').getPropertyValue('grid-template-rows') is "[x] 50px [y]"
+PASS gridTwoColumnsSingle.childElementCount is 4
+PASS gridTwoColumnsSingle.children[0].offsetWidth is 200
+PASS gridTwoColumnsSingle.children[0].offsetTop is 0
+PASS gridTwoColumnsSingle.children[0].offsetHeight is 50
+PASS gridTwoColumnsSingle.children[1].offsetWidth is 150
+PASS gridTwoColumnsSingle.children[1].offsetTop is 0
+PASS gridTwoColumnsSingle.children[1].offsetHeight is 50
+PASS gridTwoColumnsSingle.children[2].offsetWidth is 200
+PASS gridTwoColumnsSingle.children[2].offsetTop is 50
+PASS gridTwoColumnsSingle.children[2].offsetHeight is 50
+PASS gridTwoColumnsSingle.children[3].offsetWidth is 150
+PASS gridTwoColumnsSingle.children[3].offsetTop is 50
+PASS gridTwoColumnsSingle.children[3].offsetHeight is 50
+PASS window.getComputedStyle(gridThreeColumnsSingle, '').getPropertyValue('grid-template-columns') is "[a] 200px [b]"
+PASS window.getComputedStyle(gridThreeColumnsSingle, '').getPropertyValue('grid-template-rows') is "[x] 50px [y]"
+PASS gridThreeColumnsSingle.childElementCount is 4
+PASS gridThreeColumnsSingle.children[0].offsetWidth is 200
+PASS gridThreeColumnsSingle.children[0].offsetTop is 0
+PASS gridThreeColumnsSingle.children[0].offsetHeight is 50
+PASS gridThreeColumnsSingle.children[1].offsetWidth is 150
+PASS gridThreeColumnsSingle.children[1].offsetTop is 0
+PASS gridThreeColumnsSingle.children[1].offsetHeight is 50
+PASS gridThreeColumnsSingle.children[2].offsetWidth is 100
+PASS gridThreeColumnsSingle.children[2].offsetTop is 0
+PASS gridThreeColumnsSingle.children[2].offsetHeight is 50
+PASS gridThreeColumnsSingle.children[3].offsetWidth is 200
+PASS gridThreeColumnsSingle.children[3].offsetTop is 50
+PASS gridThreeColumnsSingle.children[3].offsetHeight is 50
+PASS window.getComputedStyle(gridFourColumnsSingle, '').getPropertyValue('grid-template-columns') is "[a] 200px [b]"
 PASS window.getComputedStyle(gridFourColumnsSingle, '').getPropertyValue('grid-template-rows') is "[x] 50px [y]"
+PASS gridFourColumnsSingle.childElementCount is 4
+PASS gridFourColumnsSingle.children[0].offsetWidth is 200
+PASS gridFourColumnsSingle.children[0].offsetTop is 0
+PASS gridFourColumnsSingle.children[0].offsetHeight is 50
+PASS gridFourColumnsSingle.children[1].offsetWidth is 150
+PASS gridFourColumnsSingle.children[1].offsetTop is 0
+PASS gridFourColumnsSingle.children[1].offsetHeight is 50
+PASS gridFourColumnsSingle.children[2].offsetWidth is 100
+PASS gridFourColumnsSingle.children[2].offsetTop is 0
+PASS gridFourColumnsSingle.children[2].offsetHeight is 50
+PASS gridFourColumnsSingle.children[3].offsetWidth is 50
+PASS gridFourColumnsSingle.children[3].offsetTop is 0
+PASS gridFourColumnsSingle.children[3].offsetHeight is 50
 PASS window.getComputedStyle(gridOneColumnMultiple, '').getPropertyValue('grid-template-columns') is "[a b c] 200px [d e]"
-PASS window.getComputedStyle(gridOneColumnMultiple, '').getPropertyValue('grid-template-rows') is "[x y z] 50px [v w] 50px 50px 50px"
-PASS window.getComputedStyle(gridTwoColumnsMultiple, '').getPropertyValue('grid-template-columns') is "[a b c] 200px [d e] 150px"
-PASS window.getComputedStyle(gridTwoColumnsMultiple, '').getPropertyValue('grid-template-rows') is "[x y z] 50px [v w] 50px"
-PASS window.getComputedStyle(gridThreeColumnsMultiple, '').getPropertyValue('grid-template-columns') is "[a b c] 200px [d e] 150px 100px"
-PASS window.getComputedStyle(gridThreeColumnsMultiple, '').getPropertyValue('grid-template-rows') is "[x y z] 50px [v w] 50px"
-PASS window.getComputedStyle(gridFourColumnsMultiple, '').getPropertyValue('grid-template-columns') is "[a b c] 200px [d e] 150px 100px 50px"
+PASS window.getComputedStyle(gridOneColumnMultiple, '').getPropertyValue('grid-template-rows') is "[x y z] 50px [v w]"
+PASS gridOneColumnMultiple.childElementCount is 4
+PASS gridOneColumnMultiple.children[0].offsetWidth is 200
+PASS gridOneColumnMultiple.children[0].offsetTop is 0
+PASS gridOneColumnMultiple.children[0].offsetHeight is 50
+PASS gridOneColumnMultiple.children[1].offsetWidth is 200
+PASS gridOneColumnMultiple.children[1].offsetTop is 50
+PASS gridOneColumnMultiple.children[1].offsetHeight is 50
+PASS gridOneColumnMultiple.children[2].offsetWidth is 200
+PASS gridOneColumnMultiple.children[2].offsetTop is 100
+PASS gridOneColumnMultiple.children[2].offsetHeight is 50
+PASS gridOneColumnMultiple.children[3].offsetWidth is 200
+PASS gridOneColumnMultiple.children[3].offsetTop is 150
+PASS gridOneColumnMultiple.children[3].offsetHeight is 50
+PASS window.getComputedStyle(gridTwoColumnsMultiple, '').getPropertyValue('grid-template-columns') is "[a b c] 200px [d e]"
+PASS window.getComputedStyle(gridTwoColumnsMultiple, '').getPropertyValue('grid-template-rows') is "[x y z] 50px [v w]"
+PASS gridTwoColumnsMultiple.childElementCount is 4
+PASS gridTwoColumnsMultiple.children[0].offsetWidth is 200
+PASS gridTwoColumnsMultiple.children[0].offsetTop is 0
+PASS gridTwoColumnsMultiple.children[0].offsetHeight is 50
+PASS gridTwoColumnsMultiple.children[1].offsetWidth is 150
+PASS gridTwoColumnsMultiple.children[1].offsetTop is 0
+PASS gridTwoColumnsMultiple.children[1].offsetHeight is 50
+PASS gridTwoColumnsMultiple.children[2].offsetWidth is 200
+PASS gridTwoColumnsMultiple.children[2].offsetTop is 50
+PASS gridTwoColumnsMultiple.children[2].offsetHeight is 50
+PASS gridTwoColumnsMultiple.children[3].offsetWidth is 150
+PASS gridTwoColumnsMultiple.children[3].offsetTop is 50
+PASS gridTwoColumnsMultiple.children[3].offsetHeight is 50
+PASS window.getComputedStyle(gridThreeColumnsMultiple, '').getPropertyValue('grid-template-columns') is "[a b c] 200px [d e]"
+PASS window.getComputedStyle(gridThreeColumnsMultiple, '').getPropertyValue('grid-template-rows') is "[x y z] 50px [v w]"
+PASS gridThreeColumnsMultiple.childElementCount is 4
+PASS gridThreeColumnsMultiple.children[0].offsetWidth is 200
+PASS gridThreeColumnsMultiple.children[0].offsetTop is 0
+PASS gridThreeColumnsMultiple.children[0].offsetHeight is 50
+PASS gridThreeColumnsMultiple.children[1].offsetWidth is 150
+PASS gridThreeColumnsMultiple.children[1].offsetTop is 0
+PASS gridThreeColumnsMultiple.children[1].offsetHeight is 50
+PASS gridThreeColumnsMultiple.children[2].offsetWidth is 100
+PASS gridThreeColumnsMultiple.children[2].offsetTop is 0
+PASS gridThreeColumnsMultiple.children[2].offsetHeight is 50
+PASS gridThreeColumnsMultiple.children[3].offsetWidth is 200
+PASS gridThreeColumnsMultiple.children[3].offsetTop is 50
+PASS gridThreeColumnsMultiple.children[3].offsetHeight is 50
+PASS window.getComputedStyle(gridFourColumnsMultiple, '').getPropertyValue('grid-template-columns') is "[a b c] 200px [d e]"
 PASS window.getComputedStyle(gridFourColumnsMultiple, '').getPropertyValue('grid-template-rows') is "[x y z] 50px [v w]"
+PASS gridFourColumnsMultiple.childElementCount is 4
+PASS gridFourColumnsMultiple.children[0].offsetWidth is 200
+PASS gridFourColumnsMultiple.children[0].offsetTop is 0
+PASS gridFourColumnsMultiple.children[0].offsetHeight is 50
+PASS gridFourColumnsMultiple.children[1].offsetWidth is 150
+PASS gridFourColumnsMultiple.children[1].offsetTop is 0
+PASS gridFourColumnsMultiple.children[1].offsetHeight is 50
+PASS gridFourColumnsMultiple.children[2].offsetWidth is 100
+PASS gridFourColumnsMultiple.children[2].offsetTop is 0
+PASS gridFourColumnsMultiple.children[2].offsetHeight is 50
+PASS gridFourColumnsMultiple.children[3].offsetWidth is 50
+PASS gridFourColumnsMultiple.children[3].offsetTop is 0
+PASS gridFourColumnsMultiple.children[3].offsetHeight is 50
 PASS successfullyParsed is true
 
 TEST COMPLETE
diff --git a/third_party/blink/web_tests/fast/css-grid-layout/named-grid-lines-computed-style-implicit-tracks.html b/third_party/blink/web_tests/fast/css-grid-layout/named-grid-lines-computed-style-implicit-tracks.html
index 7a776c6a..8fe3e5d 100644
--- a/third_party/blink/web_tests/fast/css-grid-layout/named-grid-lines-computed-style-implicit-tracks.html
+++ b/third_party/blink/web_tests/fast/css-grid-layout/named-grid-lines-computed-style-implicit-tracks.html
@@ -4,6 +4,7 @@
 <style>
 .grid {
     font: 50px/1 Ahem;
+    position: relative;
 }
 
 .singleNamedGridLines {
@@ -76,15 +77,70 @@
 
 <script src="resources/grid-definitions-parsing-utils.js"></script>
 <script>
-    description("Test that computed style for grid-template-columns and grid-template-rows works as expected with named grid lines and implicit tracks");
+    function testSizeAndPositionOfItems(element, expectedItemData) {
+        window.element = element;
+        var elementID = element.id || "element";
+        shouldBeEqualToNumber(elementID + ".childElementCount", expectedItemData.length);
+        var props = ["offsetWidth", "offsetTop", "offsetHeight"];
+        for (var i = 0; i < expectedItemData.length; ++i)
+            for (let prop of props)
+                shouldBeEqualToNumber(elementID + ".children[" + i + "]." + prop, expectedItemData[i][prop]);
+    }
 
-    testGridDefinitionsValues(document.getElementById("gridOneColumnSingle"), "[a] 200px [b]", "[x] 50px [y] 50px 50px 50px");
-    testGridDefinitionsValues(document.getElementById("gridTwoColumnsSingle"), "[a] 200px [b] 150px", "[x] 50px [y] 50px");
-    testGridDefinitionsValues(document.getElementById("gridThreeColumnsSingle"), "[a] 200px [b] 150px 100px", "[x] 50px [y] 50px");
-    testGridDefinitionsValues(document.getElementById("gridFourColumnsSingle"), "[a] 200px [b] 150px 100px 50px", "[x] 50px [y]");
+    function testGrid(element, columnValue, rowValue, expectedItemData) {
+        testGridDefinitionsValues(element, columnValue, rowValue);
+        testSizeAndPositionOfItems(element, expectedItemData);
+    }
 
-    testGridDefinitionsValues(document.getElementById("gridOneColumnMultiple"), "[a b c] 200px [d e]", "[x y z] 50px [v w] 50px 50px 50px");
-    testGridDefinitionsValues(document.getElementById("gridTwoColumnsMultiple"), "[a b c] 200px [d e] 150px", "[x y z] 50px [v w] 50px");
-    testGridDefinitionsValues(document.getElementById("gridThreeColumnsMultiple"), "[a b c] 200px [d e] 150px 100px", "[x y z] 50px [v w] 50px");
-    testGridDefinitionsValues(document.getElementById("gridFourColumnsMultiple"), "[a b c] 200px [d e] 150px 100px 50px", "[x y z] 50px [v w]");
+    description("Test that computed style for grid-template-columns and grid-template-rows works as expected, with named grid lines but without implicit tracks. Also test the size and position of the items, since the size of implicit tracks is not exposed");
+
+    testGrid(document.getElementById("gridOneColumnSingle"), "[a] 200px [b]", "[x] 50px [y]", [
+      {offsetLeft: 0, offsetWidth: 200, offsetTop: 0, offsetHeight: 50},
+      {offsetLeft: 0, offsetWidth: 200, offsetTop: 50, offsetHeight: 50},
+      {offsetLeft: 0, offsetWidth: 200, offsetTop: 100, offsetHeight: 50},
+      {offsetLeft: 0, offsetWidth: 200, offsetTop: 150, offsetHeight: 50},
+    ]);
+    testGrid(document.getElementById("gridTwoColumnsSingle"), "[a] 200px [b]", "[x] 50px [y]", [
+      {offsetLeft: 0, offsetWidth: 200, offsetTop: 0, offsetHeight: 50},
+      {offsetLeft: 200, offsetWidth: 150, offsetTop: 0, offsetHeight: 50},
+      {offsetLeft: 0, offsetWidth: 200, offsetTop: 50, offsetHeight: 50},
+      {offsetLeft: 200, offsetWidth: 150, offsetTop: 50, offsetHeight: 50},
+    ]);
+    testGrid(document.getElementById("gridThreeColumnsSingle"), "[a] 200px [b]", "[x] 50px [y]", [
+      {offsetLeft: 0, offsetWidth: 200, offsetTop: 0, offsetHeight: 50},
+      {offsetLeft: 200, offsetWidth: 150, offsetTop: 0, offsetHeight: 50},
+      {offsetLeft: 350, offsetWidth: 100, offsetTop: 0, offsetHeight: 50},
+      {offsetLeft: 0, offsetWidth: 200, offsetTop: 50, offsetHeight: 50},
+    ]);
+    testGrid(document.getElementById("gridFourColumnsSingle"), "[a] 200px [b]", "[x] 50px [y]", [
+      {offsetLeft: 0, offsetWidth: 200, offsetTop: 0, offsetHeight: 50},
+      {offsetLeft: 200, offsetWidth: 150, offsetTop: 0, offsetHeight: 50},
+      {offsetLeft: 350, offsetWidth: 100, offsetTop: 0, offsetHeight: 50},
+      {offsetLeft: 450, offsetWidth: 50, offsetTop: 0, offsetHeight: 50},
+    ]);
+
+    testGrid(document.getElementById("gridOneColumnMultiple"), "[a b c] 200px [d e]", "[x y z] 50px [v w]", [
+      {offsetLeft: 0, offsetWidth: 200, offsetTop: 0, offsetHeight: 50},
+      {offsetLeft: 0, offsetWidth: 200, offsetTop: 50, offsetHeight: 50},
+      {offsetLeft: 0, offsetWidth: 200, offsetTop: 100, offsetHeight: 50},
+      {offsetLeft: 0, offsetWidth: 200, offsetTop: 150, offsetHeight: 50},
+    ]);
+    testGrid(document.getElementById("gridTwoColumnsMultiple"), "[a b c] 200px [d e]", "[x y z] 50px [v w]", [
+      {offsetLeft: 0, offsetWidth: 200, offsetTop: 0, offsetHeight: 50},
+      {offsetLeft: 200, offsetWidth: 150, offsetTop: 0, offsetHeight: 50},
+      {offsetLeft: 0, offsetWidth: 200, offsetTop: 50, offsetHeight: 50},
+      {offsetLeft: 200, offsetWidth: 150, offsetTop: 50, offsetHeight: 50},
+    ]);
+    testGrid(document.getElementById("gridThreeColumnsMultiple"), "[a b c] 200px [d e]", "[x y z] 50px [v w]", [
+      {offsetLeft: 0, offsetWidth: 200, offsetTop: 0, offsetHeight: 50},
+      {offsetLeft: 200, offsetWidth: 150, offsetTop: 0, offsetHeight: 50},
+      {offsetLeft: 350, offsetWidth: 100, offsetTop: 0, offsetHeight: 50},
+      {offsetLeft: 0, offsetWidth: 200, offsetTop: 50, offsetHeight: 50},
+    ]);
+    testGrid(document.getElementById("gridFourColumnsMultiple"), "[a b c] 200px [d e]", "[x y z] 50px [v w]", [
+      {offsetLeft: 0, offsetWidth: 200, offsetTop: 0, offsetHeight: 50},
+      {offsetLeft: 200, offsetWidth: 150, offsetTop: 0, offsetHeight: 50},
+      {offsetLeft: 350, offsetWidth: 100, offsetTop: 0, offsetHeight: 50},
+      {offsetLeft: 450, offsetWidth: 50, offsetTop: 0, offsetHeight: 50},
+    ]);
 </script>
diff --git a/third_party/blink/web_tests/fast/css-grid-layout/negative-growth-share-as-infinity-crash.html b/third_party/blink/web_tests/fast/css-grid-layout/negative-growth-share-as-infinity-crash.html
index 2aa26cf4..35eac13 100644
--- a/third_party/blink/web_tests/fast/css-grid-layout/negative-growth-share-as-infinity-crash.html
+++ b/third_party/blink/web_tests/fast/css-grid-layout/negative-growth-share-as-infinity-crash.html
@@ -3,8 +3,8 @@
 <link href="resources/grid.css" rel="stylesheet">
 <style>
 .grid {
-    grid-auto-rows: minmax(min-content, 9px);
-    grid-auto-columns: min-content;
+    grid-template-rows: minmax(min-content, 9px);
+    grid-template-columns: min-content;
     height: 10px;
 }
 .firstRowFirstColumn {
diff --git a/third_party/blink/web_tests/fast/harness/results.html b/third_party/blink/web_tests/fast/harness/results.html
index 0619a7a..c56ae5a 100644
--- a/third_party/blink/web_tests/fast/harness/results.html
+++ b/third_party/blink/web_tests/fast/harness/results.html
@@ -179,9 +179,6 @@
   margin-left: 5px;
 }
 
-#flag-toolbar {
-  display: inline-block;
-}
 #flag-toolbar.hidden {
   display: none;
 }
@@ -319,13 +316,18 @@
       Flagged
     </button>
     <div id="flag-toolbar" class="hidden">
+      <span class="fix-width"></span>
       <button onclick="javascript:Query.query('Flag failures', Filters.flagFailure, true)">
         <span class="flag_name"></span> failures
-        <span id="count_flagfailure"></span>
+        <span id="count_flag_failure"></span>
       </button>
-      <button onclick="javascript:Query.query('Flag failures', Filters.flagPass, true)">
+      <button onclick="javascript:Query.query('Flag passes', Filters.flagPass, true)">
         <span class="flag_name"></span> passes
-        <span id="count_flagpass"></span>
+        <span id="count_flag_pass"></span>
+      </button>
+      <button onclick="javascript:Query.query('Flag unexpected passes', Filters.flagUnexpectedPass, true)">
+        <span class="flag_name"></span> unexpected passes
+        <span id="count_flag_unexpected_pass"></span>
       </button>
     </div>
   </div>
@@ -360,8 +362,8 @@
   <span style="margin-left: 20px">
     <button id="copy_report" title="Copy the shown/flagged tests to clipboard in the current format"
         onclick="GUI.copyResult(false)" disabled>Copy report</button>
-    <button id="copy_single_line" title="Copy the shown/flagged tests to clipboard in a single line for use in command lines."
-        onclick="GUI.copyResult(true)" disabled>Copy single line</button>
+    <button id="copy_test_names" title="Copy the names of the shown/flagged tests to clipboard in a single line for use in command lines."
+        onclick="GUI.copyResult(true)" disabled>Copy test names</button>
     <label id="flagged_only" class="hidden"><input id="flagged_only_checkbox" type="checkbox" checked>Flagged only</label>
     <span id="copied" class="hidden">Copied.</span>
   </span>
@@ -919,7 +921,7 @@
     document.querySelector("#report_title").innerHTML = name;
     document.querySelector("#progress").style.width = "1%";
     document.querySelector("#copy_report").disabled = true;
-    document.querySelector("#copy_single_line").disabled = true;
+    document.querySelector("#copy_test_names").disabled = true;
         document.querySelector("#report_count").innerText = "";
     let traversal = new Traversal(globalResults.tests);
     let chunkSize = 1000;
@@ -962,22 +964,20 @@
     return !Filters.containsPass(test.expectedMap) && Filters.containsPass(test.actualMap);
   },
   regressionFromExpectedMap: (finalResult, expectedMap) => {
-    if (expectedMap.has("NEEDSREBASELINE"))
-      return false;
     switch (finalResult) {
       case "SKIP":
         return false;
       case "CRASH":
       case "TIMEOUT":
       case "LEAK":
-        if (expectedMap.has(finalResult))
+        if (expectedMap && expectedMap.has(finalResult))
           return false;
         break;
       case "TEXT":
       case "IMAGE":
       case "IMAGE+TEXT":
       case "AUDIO":
-        if (expectedMap.has("FAIL"))
+        if (expectedMap && expectedMap.has("FAIL"))
           return false;
         break;
       case "MISSING":
@@ -1005,35 +1005,15 @@
   flagFailure: test => { // Tests that are failing, but expected to pass in base.
     if (Filters.containsPass(test.actualMap))
       return false;
-    if (test.expectedMap.has("NEEDSREBASELINE"))
-      return false;
     let baseMap = test.flagMap ? test.baseMap : test.expectedMap;
-    switch (test.actualFinal) {
-      case "SKIP":
-        return false;
-      case "CRASH":
-      case "TIMEOUT":
-      case "LEAK":
-        if (baseMap && baseMap.has(test.actualFinal))
-          return false;
-        break;
-      case "TEXT":
-      case "IMAGE":
-      case "IMAGE+TEXT":
-      case "AUDIO":
-        if (baseMap && baseMap.has("FAIL"))
-          return false;
-        break;
-      case "MISSING":
-        break;
-      default:
-        console.error("Unexpected test result", test.actualFinal);
-      }
-    return true;
+    return Filters.regressionFromExpectedMap(test.actualFinal, baseMap);
   },
   flagPass: test => {
     return test.baseMap && (Filters.containsPass(test.actualMap) && !Filters.containsPass(test.baseMap));
-  }
+  },
+  flagUnexpectedPass: test => {
+    return test.flagMap && !Filters.containsPass(test.flagMap) && Filters.containsPass(test.actualMap);
+  },
 }; // Filters
 
 // Event handling, initialization.
@@ -1409,7 +1389,7 @@
   updateCopyButtons: function() {
     let noResult = document.querySelector("#none");
     document.querySelector("#copy_report").disabled = noResult;
-    document.querySelector("#copy_single_line").disabled = noResult;
+    document.querySelector("#copy_test_names").disabled = noResult;
 
     let flagged_only = document.querySelector("#flagged_only");
     if (document.querySelector(".flagged"))
@@ -1440,8 +1420,9 @@
       "count_flaky": 0,
       "count_unexpected_flaky": 0,
       "count_all": 0,
-      "count_flagfailure": 0,
-      "count_flagpass" : 0
+      "count_flag_failure": 0,
+      "count_flag_pass" : 0,
+      "count_flag_unexpected_pass" : 0,
     };
     var t = new Traversal(fullResults.tests);
     t.traverse( test => {
@@ -1457,9 +1438,11 @@
       if (Filters.unexpectedFlaky(test))
         counts.count_unexpected_flaky++;
       if (Filters.flagFailure(test))
-        counts.count_flagfailure++;
+        counts.count_flag_failure++;
       if (Filters.flagPass(test))
-        counts.count_flagpass++;
+        counts.count_flag_pass++;
+      if (Filters.flagUnexpectedPass(test))
+        counts.count_flag_unexpected_pass++;
     });
     console.assert(
         counts.count_regressions == fullResults.num_regressions,
diff --git a/third_party/blink/web_tests/inspector-protocol/css/css-coverage-new-stylesheet.js b/third_party/blink/web_tests/inspector-protocol/css/css-coverage-new-stylesheet.js
index f6d7c2e..ced5703 100644
--- a/third_party/blink/web_tests/inspector-protocol/css/css-coverage-new-stylesheet.js
+++ b/third_party/blink/web_tests/inspector-protocol/css/css-coverage-new-stylesheet.js
@@ -5,7 +5,7 @@
   await dp.CSS.enable();
 
   await dp.CSS.startRuleUsageTracking();
-  await session.evaluateAsync(async function(url) {
+  await session.evaluateAsync(function(url) {
     const div = document.createElement('div');
     div.classList.add('usedAtTheVeryEnd');
     document.body.appendChild(div);
@@ -14,14 +14,24 @@
     link.rel = 'stylesheet';
     link.href = url;
     document.head.appendChild(link);
-    await new Promise(fulfill => link.onload = fulfill);
+    return new Promise(fulfill => link.onload = fulfill);
   }, testRunner.url('./resources/coverage2.css'));
+
+  // The onload is not enough to guarantee that the rendering has completed
+  // so we await an animation frame, too.
+  await session.evaluateAsync(function() {
+    let r;
+    const p = new Promise(resolve => r = resolve);
+    window.requestAnimationFrame(r);
+    return p;
+  });
+
   const response = await dp.CSS.stopRuleUsageTracking();
 
   if (response.result.ruleUsage.length === 1) {
-    testRunner.log('Successfully reported CSS coverage.')
+    testRunner.log('Successfully reported CSS coverage.');
   } else {
-    testRunner.log(`ERROR!`);
+    testRunner.log('ERROR!');
     testRunner.log(response.result.ruleUsage);
   }
   testRunner.completeTest();
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/element-instance-property-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/element-instance-property-listing-expected.txt
index b1ca6b9b1..b910ce4be 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/element-instance-property-listing-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/element-instance-property-listing-expected.txt
@@ -266,6 +266,7 @@
     property host
     property hostname
     property href
+    property hrefTranslate
     property hreflang
     property name
     property origin
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
index 1eae08b2..bffcf9e 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -1987,6 +1987,7 @@
     getter host
     getter hostname
     getter href
+    getter hrefTranslate
     getter hreflang
     getter name
     getter origin
@@ -2014,6 +2015,7 @@
     setter host
     setter hostname
     setter href
+    setter hrefTranslate
     setter hreflang
     setter name
     setter password
@@ -4096,6 +4098,7 @@
     getter playbackState
     method constructor
     method setActionHandler
+    method setPositionState
     setter metadata
     setter playbackState
 interface MediaSettingsRange
diff --git a/third_party/wayland-protocols/unstable/remote-shell/remote-shell-unstable-v1.xml b/third_party/wayland-protocols/unstable/remote-shell/remote-shell-unstable-v1.xml
index 59f0d04..dd3b48d 100644
--- a/third_party/wayland-protocols/unstable/remote-shell/remote-shell-unstable-v1.xml
+++ b/third_party/wayland-protocols/unstable/remote-shell/remote-shell-unstable-v1.xml
@@ -38,7 +38,7 @@
     reset.
   </description>
 
-  <interface name="zcr_remote_shell_v1" version="25">
+  <interface name="zcr_remote_shell_v1" version="26">
     <description summary="remote_shell">
       The global interface that allows clients to turn a wl_surface into a
       "real window" which is remotely managed but can be stacked, activated
@@ -237,7 +237,7 @@
 
   </interface>
 
-  <interface name="zcr_remote_surface_v1" version="25">
+  <interface name="zcr_remote_surface_v1" version="26">
     <description summary="A desktop window">
       An interface that may be implemented by a wl_surface, for
       implementations that provide a desktop-style user interface
@@ -1036,7 +1036,6 @@
     </event>
 
     <!-- Version 25 additions -->
-
     <request name="set_accessibility_id" since="25">
       <description summary="set accessibility ID to the surface">
         Set accessibility window ID to the surface
@@ -1044,6 +1043,18 @@
       <arg name="id" type="int" summary="Accessibility ID. Negative number causes to unset existing accessibility ID from the surface."/>
     </request>
 
+    <!-- Version 26 additions -->
+    <request name="set_pip_original_window" since="25">
+      <description summary="set the pip original window">
+        Set this surface the original window for the current PIP window.
+      </description>
+    </request>
+
+    <request name="unset_pip_original_window" since="25">
+      <description summary="unset the pip original window">
+        Unset this surface the original window for the current PIP window.
+      </description>
+    </request>
   </interface>
 
   <interface name="zcr_notification_surface_v1" version="16">
diff --git a/tools/.style.yapf b/tools/.style.yapf
new file mode 100644
index 0000000..b4ebbe2
--- /dev/null
+++ b/tools/.style.yapf
@@ -0,0 +1,6 @@
+[style]
+based_on_style = pep8
+
+# New directories should use a .style.yapf that does not include the following:
+column_limit = 80
+indent_width = 2
diff --git a/tools/binary_size/.style.yapf b/tools/binary_size/.style.yapf
index ef24bfc6..5e055c8 100644
--- a/tools/binary_size/.style.yapf
+++ b/tools/binary_size/.style.yapf
@@ -1,6 +1,4 @@
 [style]
 based_on_style = pep8
 column_limit = 80
-blank_line_before_nested_class_or_def = true
-blank_line_before_module_docstring = true
 indent_width = 2
diff --git a/tools/ipc_fuzzer/message_lib/BUILD.gn b/tools/ipc_fuzzer/message_lib/BUILD.gn
index 7c42fcc..89787cc 100644
--- a/tools/ipc_fuzzer/message_lib/BUILD.gn
+++ b/tools/ipc_fuzzer/message_lib/BUILD.gn
@@ -14,7 +14,7 @@
     "//chrome/common/safe_browsing:proto",
     "//components/guest_view/common",
     "//components/nacl/common:buildflags",
-    "//components/safe_browsing/common",
+    "//components/safe_browsing/core/common",
     "//components/spellcheck/common",
     "//components/subresource_filter/content/common",
     "//components/tracing",
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 32a3c23..50eda80 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -7987,6 +7987,16 @@
   <int value="10" label="Image was not required for activation."/>
 </enum>
 
+<enum name="ChimeEvent">
+  <int value="0" label="Register"/>
+  <int value="1" label="UnRegister"/>
+  <int value="2" label="Notification Create"/>
+  <int value="3" label="Notification Click"/>
+  <int value="4" label="Notification Remove"/>
+  <int value="5" label="Notification Expire"/>
+  <int value="6" label="Notification Action Click"/>
+</enum>
+
 <enum name="ChromeActivityType">
   <int value="0" label="Tabbed Chrome"/>
   <int value="1" label="Custom Tab"/>
@@ -37308,6 +37318,7 @@
   <int value="-714043324" label="OutOfBlinkCORS:enabled"/>
   <int value="-711991950" label="SiteExplorationUi:enabled"/>
   <int value="-711890895" label="enable-website-settings-manager"/>
+  <int value="-709793261" label="PaintPreviewTest:disabled"/>
   <int value="-709058455" label="ui-slow-animations"/>
   <int value="-706733351" label="enable-floating-virtual-keyboard:enabled"/>
   <int value="-705746939" label="ChromeDuet:enabled"/>
@@ -37605,6 +37616,7 @@
   <int value="-310615515" label="EnableSuggestedFiles:disabled"/>
   <int value="-307260007" label="ExtensionsCheckup:disabled"/>
   <int value="-304951461" label="TextSuggestionsTouchBar:enabled"/>
+  <int value="-301310056" label="PaintPreviewTest:enabled"/>
   <int value="-300018686" label="disable-cloud-import"/>
   <int value="-299841473" label="top-document-isolation:enabled"/>
   <int value="-297716805"
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index e0de2010..75437c3 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -14897,7 +14897,7 @@
 </histogram>
 
 <histogram name="BackgroundMode.BackgroundApplicationsCount" units="units"
-    expires_after="2020-06-07">
+    expires_after="2020-09-01">
   <owner>atwilson@chromium.org</owner>
   <owner>mvanouwerkerk@chromium.org</owner>
   <summary>
@@ -14907,8 +14907,9 @@
 </histogram>
 
 <histogram name="BackgroundMode.BackgroundModeEnabledPrefChanged"
-    enum="BooleanEnabled" expires_after="2020-02-01">
+    enum="BooleanEnabled" expires_after="2020-09-01">
   <owner>mvanouwerkerk@chromium.org</owner>
+  <owner>peter@chromium.org</owner>
   <summary>
     Logged if BackgroundModeManager is running and listening for pref changes,
     and the pref does indeed change. The new value is logged.
@@ -14916,15 +14917,16 @@
 </histogram>
 
 <histogram name="BackgroundMode.MenuItemClick" enum="BackgroundModeMenuItem"
-    expires_after="2020-02-01">
+    expires_after="2020-09-01">
   <owner>mvanouwerkerk@chromium.org</owner>
+  <owner>peter@chromium.org</owner>
   <summary>
     Logged when an item in the system tray icon menu is clicked.
   </summary>
 </histogram>
 
 <histogram name="BackgroundMode.OnStartup.AutoLaunchState"
-    enum="AutoLaunchState" expires_after="2020-02-01">
+    enum="AutoLaunchState" expires_after="2020-09-01">
   <owner>gab@chromium.org</owner>
   <owner>mvanouwerkerk@chromium.org</owner>
   <summary>
@@ -14934,7 +14936,7 @@
 </histogram>
 
 <histogram name="BackgroundMode.OnStartup.IsBackgroundModePrefEnabled"
-    enum="BooleanEnabled" expires_after="2020-02-01">
+    enum="BooleanEnabled" expires_after="2020-09-01">
   <owner>gab@chromium.org</owner>
   <owner>mvanouwerkerk@chromium.org</owner>
   <summary>
@@ -14944,7 +14946,7 @@
 </histogram>
 
 <histogram name="BackgroundMode.TimeBeforeOptimizedRestart" units="ms"
-    expires_after="2020-02-01">
+    expires_after="2020-09-01">
   <owner>peter@chromium.org</owner>
   <owner>mvanouwerkerk@chromium.org</owner>
   <summary>
@@ -57965,7 +57967,7 @@
 </histogram>
 
 <histogram name="History.ClearBrowsingData.UserDeletedFromTab"
-    enum="ClearBrowsingDataTab" expires_after="2020-01-26">
+    enum="ClearBrowsingDataTab" expires_after="2021-01-26">
   <owner>dullweber@chromium.org</owner>
   <owner>msramek@chromium.org</owner>
   <summary>
@@ -69673,7 +69675,9 @@
 </histogram>
 
 <histogram base="true" name="Media.TimeToFirstFrame" units="ms"
-    expires_after="2020-06-07">
+    expires_after="never">
+<!-- expires-never: Media pipeline health metric. -->
+
   <owner>dalecurtis@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>
@@ -69697,7 +69701,9 @@
 </histogram>
 
 <histogram base="true" name="Media.TimeToMetadata" units="ms"
-    expires_after="2020-02-02">
+    expires_after="never">
+<!-- expires-never: Media pipeline health metric. -->
+
   <owner>dalecurtis@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>
@@ -69718,7 +69724,9 @@
 </histogram>
 
 <histogram base="true" name="Media.TimeToPlayReady" units="ms"
-    expires_after="2020-06-07">
+    expires_after="never">
+<!-- expires-never: Media pipeline health metric. -->
+
   <owner>dalecurtis@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>
@@ -96254,6 +96262,32 @@
   </summary>
 </histogram>
 
+<histogram name="Notifications.Chime.Android.Events" enum="ChimeEvent"
+    expires_after="never">
+  <owner>hesen@chromium.org</owner>
+  <owner>xingliu@chromium.org</owner>
+<!-- expires-never: Monitors Chime notification stability. -->
+
+  <summary>
+    Various events when Chrome interacts with Chime notification platform.
+    Recorded when Chrome registers to Chime service, receives notifications, and
+    the user interacts with the notification.
+  </summary>
+</histogram>
+
+<histogram name="Notifications.Chime.Android.Registration"
+    enum="BooleanSuccess" expires_after="never">
+  <owner>hesen@chromium.org</owner>
+  <owner>xingliu@chromium.org</owner>
+<!-- expires-never: Monitors Chime notification stability. -->
+
+  <summary>
+    Records the result of Chime notification platform registration. Each device
+    may have multiple accounts registered. The registration happens right after
+    opening Chrome.
+  </summary>
+</histogram>
+
 <histogram name="Notifications.Database.DeleteAllForOriginsResult"
     enum="NotificationDatabaseStatus" expires_after="M86">
   <owner>knollr@chromium.org</owner>
@@ -151193,7 +151227,7 @@
 <histogram
     name="Sync.Crypto.CustomPassphraseKeyDerivationMethodOnNewPassphrase"
     enum="SyncCustomPassphraseKeyDerivationMethodState"
-    expires_after="2020-06-28">
+    expires_after="2020-09-28">
   <owner>vitaliii@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <summary>
@@ -151207,7 +151241,7 @@
 <histogram
     name="Sync.Crypto.CustomPassphraseKeyDerivationMethodOnSuccessfulDecryption"
     enum="SyncCustomPassphraseKeyDerivationMethodState"
-    expires_after="2020-06-28">
+    expires_after="2020-09-28">
   <owner>vitaliii@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <summary>
@@ -151221,7 +151255,7 @@
 
 <histogram name="Sync.Crypto.CustomPassphraseKeyDerivationMethodStateOnStartup"
     enum="SyncCustomPassphraseKeyDerivationMethodState"
-    expires_after="2020-06-28">
+    expires_after="2020-09-28">
   <owner>vitaliii@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <summary>
@@ -151231,7 +151265,7 @@
 </histogram>
 
 <histogram base="true" name="Sync.Crypto.NigoriKeyDerivationDuration"
-    units="ms" expires_after="2020-05-10">
+    units="ms" expires_after="2020-09-28">
   <owner>vitaliii@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <summary>
diff --git a/tools/perf/cli_tools/pinboard/pinboard.py b/tools/perf/cli_tools/pinboard/pinboard.py
index 5c1678b..edf59783 100644
--- a/tools/perf/cli_tools/pinboard/pinboard.py
+++ b/tools/perf/cli_tools/pinboard/pinboard.py
@@ -92,8 +92,7 @@
   item = {'revision': revision, 'timestamp': timestamp, 'jobs': []}
   configs = LoadJsonFile(JOB_CONFIGS_PATH)
   for config in configs:
-    config['start_git_hash'] = revision
-    config['end_git_hash'] = revision
+    config['base_git_hash'] = revision
     with tempfile_ext.NamedTemporaryFile() as tmp:
       json.dump(config, tmp)
       tmp.close()
diff --git a/tools/perf/cli_tools/pinpoint_cli/commands.py b/tools/perf/cli_tools/pinpoint_cli/commands.py
index 9bdcbb8..5e73ea4 100644
--- a/tools/perf/cli_tools/pinpoint_cli/commands.py
+++ b/tools/perf/cli_tools/pinpoint_cli/commands.py
@@ -26,6 +26,12 @@
   if not isinstance(config, dict):
     raise ValueError('Invalid job config')
 
+  # As of crrev.com/c/1965875 try jobs must specify a base git hash.
+  if not 'base_git_hash' in config:
+    config['base_git_hash'] = config['start_git_hash']
+    del config['start_git_hash']
+    del config['end_git_hash']
+
   response = pinpoint_service.NewJob(**config)
   print('Started:', response['jobUrl'])
 
diff --git a/tools/perf/core/perf_data_generator.py b/tools/perf/core/perf_data_generator.py
index 311b417..d4db064 100755
--- a/tools/perf/core/perf_data_generator.py
+++ b/tools/perf/core/perf_data_generator.py
@@ -98,7 +98,6 @@
         'isolate': 'performance_test_suite',
         'extra_args': [
           '--output-format=histograms',
-          '--experimental-proto-trace-format',
         ],
       }
     ],
@@ -151,7 +150,6 @@
         'isolate': 'performance_test_suite',
         'extra_args': [
             '--output-format=histograms',
-            '--experimental-proto-trace-format',
             '--experimental-tbmv3-metrics',
         ],
       }
diff --git a/tools/perf/expectations.config b/tools/perf/expectations.config
index 2857a2b..39f3492 100644
--- a/tools/perf/expectations.config
+++ b/tools/perf/expectations.config
@@ -344,8 +344,8 @@
 crbug.com/1014655 [ android-webview ] system_health.common_mobile/browse:social:instagram:2019 [ Skip ]
 crbug.com/1036141 [ android-webview ] system_health.common_mobile/browse:shopping:lazada:2019 [ Skip ]
 crbug.com/1036143 [ android-pixel-2 ] system_health.common_mobile/browse:chrome:omnibox:2019 [ Skip ]
-crbug.com/1037687 [ android ] system_health.common_mobile/browse:social:pinterest_infinite_scroll:2019 [ Skip ]
 crbug.com/1039277 [ android ] system_health.common_mobile/load:tools:dropbox:2019 [ Skip ]
+crbug.com/1039734 [ android-go ] system_health.common_mobile/browse:news:washingtonpost:2019 [ Skip ]
 
 # Benchmark: system_health.memory_desktop
 crbug.com/1027686 [ linux ] system_health.memory_desktop/browse:social:tumblr_infinite_scroll:2018 [ Skip ]
@@ -424,7 +424,6 @@
 crbug.com/1017661 [ android-nexus-5x ] system_health.memory_mobile/browse:social:facebook_infinite_scroll:2018 [ Skip ]
 crbug.com/1017661 [ android-nexus-5x ] system_health.memory_mobile/browse:tech:discourse_infinite_scroll:2018 [ Skip ]
 crbug.com/1036143 [ android-pixel-2 ] system_health.memory_mobile/browse:chrome:omnibox:2019 [ Skip ]
-crbug.com/1037687 [ android ] system_health.memory_mobile/browse:social:pinterest_infinite_scroll:2019 [ Skip ]
 crbug.com/1039277 [ android ] system_health.memory_mobile/load:tools:dropbox:2019 [ Skip ]
 
 # Benchmark: tab_switching.typical_25
@@ -478,10 +477,7 @@
 crbug.com/1014655 [ android-webview ] v8.browsing_mobile/browse:social:instagram:2019 [ Skip ]
 crbug.com/1036141 [ android-webview ] v8.browsing_mobile/browse:shopping:lazada:2019 [ Skip ]
 crbug.com/1036143 [ android-pixel-2 ] v8.browsing_mobile/browse:chrome:omnibox:2019 [ Skip ]
-crbug.com/1037687 [ android ] v8.browsing_mobile/browse:social:pinterest_infinite_scroll:2019 [ Skip ]
 crbug.com/1037905 [ android-nexus-6 android-webview ] v8.browsing_mobile/browse:media:imgur:2019 [ Skip ]
-crbug.com/1039295 [ android-go ] v8.browsing_mobile/browse:news:nytimes:2019 [ Skip ]
-crbug.com/1039295 [ android-nexus-5 ] v8.browsing_mobile/browse:news:nytimes:2019 [ Skip ]
 
 # Benchmark: v8.browsing_mobile-future
 # Disabled v8.browsing_mobile-future for capacity reasons while we update
diff --git a/tools/perf/experimental/representative_perf_test_limit_adjuster/adjust_upper_limits.py b/tools/perf/experimental/representative_perf_test_limit_adjuster/adjust_upper_limits.py
index bf8f939..09da51d 100644
--- a/tools/perf/experimental/representative_perf_test_limit_adjuster/adjust_upper_limits.py
+++ b/tools/perf/experimental/representative_perf_test_limit_adjuster/adjust_upper_limits.py
@@ -38,8 +38,8 @@
   """
   swarming_attributes = (
     'tasks/list?tags=name:rendering_representative_perf_tests&tags=os:{os}'
-    '&tags=buildername:{buildername}&state=COMPLETED&fields=cursor,'
-    'items(task_id)').format(**tags)
+    '&tags=buildername:{buildername}&tags=master:chromium.gpu.fyi&state='
+    'COMPLETED&fields=cursor,items(task_id)').format(**tags)
 
   query = [
     SWARMING_PATH, 'query', '-S', 'chromium-swarm.appspot.com', '--limit',
@@ -73,11 +73,14 @@
   result_file_path = os.path.join(
     output_directory, '0', 'rendering.' + benchmark, 'perf_results.csv')
 
-  df = pandas.read_csv(result_file_path)
-  df = df.loc[df['name'] == 'frame_times']
-  df = df[['stories', 'avg', 'ci_095']]
-  df['index'] = index
-  return df
+  try:
+    df = pandas.read_csv(result_file_path)
+    df = df.loc[df['name'] == 'frame_times']
+    df = df[['stories', 'avg', 'ci_095']]
+    df['index'] = index
+    return df
+  except:
+    print("CSV results were not produced!")
 
 
 def GetPercentileValues(benchmark, tags, limit, percentile):
diff --git a/tools/perf/page_sets/data/system_health_desktop.json b/tools/perf/page_sets/data/system_health_desktop.json
index 16f4d04..f7546be5 100644
--- a/tools/perf/page_sets/data/system_health_desktop.json
+++ b/tools/perf/page_sets/data/system_health_desktop.json
@@ -86,7 +86,7 @@
             "DEFAULT": "system_health_desktop_056.wprgo"
         },
         "browse:social:tumblr_infinite_scroll:2018": {
-            "DEFAULT": "system_health_desktop_e6232c2d50.wprgo"
+            "DEFAULT": "system_health_desktop_2015ed2b54.wprgo"
         },
         "browse:social:twitter": {
             "DEFAULT": "system_health_desktop_053.wprgo"
diff --git a/tools/perf/page_sets/data/system_health_desktop_2015ed2b54.wprgo.sha1 b/tools/perf/page_sets/data/system_health_desktop_2015ed2b54.wprgo.sha1
new file mode 100644
index 0000000..eab851c
--- /dev/null
+++ b/tools/perf/page_sets/data/system_health_desktop_2015ed2b54.wprgo.sha1
@@ -0,0 +1 @@
+2015ed2b5410f5ce62d965e735be24556a8f783f
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/system_health_desktop_e6232c2d50.wprgo.sha1 b/tools/perf/page_sets/data/system_health_desktop_e6232c2d50.wprgo.sha1
deleted file mode 100644
index 5625e72..0000000
--- a/tools/perf/page_sets/data/system_health_desktop_e6232c2d50.wprgo.sha1
+++ /dev/null
@@ -1 +0,0 @@
-e6232c2d504f21218d99b1f060622d55a5c2f111
\ No newline at end of file
diff --git a/tools/perf/page_sets/helpers/__init__.py b/tools/perf/page_sets/helpers/__init__.py
new file mode 100644
index 0000000..ae1922e1
--- /dev/null
+++ b/tools/perf/page_sets/helpers/__init__.py
@@ -0,0 +1,3 @@
+# Copyright 2020 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.
diff --git a/tools/perf/page_sets/helpers/override_online.py b/tools/perf/page_sets/helpers/override_online.py
new file mode 100644
index 0000000..6c76b73
--- /dev/null
+++ b/tools/perf/page_sets/helpers/override_online.py
@@ -0,0 +1,12 @@
+# Copyright 2020 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.
+
+""" A script that will override navigator.onLine to always return true. """
+ALWAYS_ONLINE = '''
+  Object.defineProperty(window.navigator, 'onLine', {
+    get: function() {
+      return true;
+    },
+  });'''
+
diff --git a/tools/perf/page_sets/login_helpers/pinterest_login.py b/tools/perf/page_sets/login_helpers/pinterest_login.py
index f1f3d4f..bc3fdd9 100644
--- a/tools/perf/page_sets/login_helpers/pinterest_login.py
+++ b/tools/perf/page_sets/login_helpers/pinterest_login.py
@@ -4,12 +4,15 @@
 
 from page_sets.login_helpers import login_utils
 
+from page_sets.helpers import override_online
+
 
 def _LoginAccount(action_runner, credential, credentials_path):
   account_name, password = login_utils.GetAccountNameAndPassword(
       credential, credentials_path=credentials_path)
 
-  action_runner.Navigate('https://www.pinterest.co.uk/login/')
+  action_runner.Navigate('https://www.pinterest.co.uk/login/',
+                         override_online.ALWAYS_ONLINE)
   action_runner.Wait(1) # Error page happens if this wait is not here.
   action_runner.WaitForElement(selector='button[type=submit]')
 
diff --git a/tools/perf/page_sets/system_health/browsing_stories.py b/tools/perf/page_sets/system_health/browsing_stories.py
index dba9b7c..6c135ca4 100644
--- a/tools/perf/page_sets/system_health/browsing_stories.py
+++ b/tools/perf/page_sets/system_health/browsing_stories.py
@@ -17,6 +17,8 @@
 from page_sets.login_helpers import pinterest_login
 from page_sets.login_helpers import tumblr_login
 
+from page_sets.helpers import override_online
+
 from telemetry.util import js_template
 
 
@@ -34,6 +36,12 @@
   CONTAINER_SELECTOR = None
   ABSTRACT_STORY = True
 
+  def __init__(self, story_set, take_memory_measurement,
+               extra_browser_args=None):
+    super(_BrowsingStory, self).__init__(story_set,
+        take_memory_measurement, extra_browser_args)
+    self.script_to_evaluate_on_commit = override_online.ALWAYS_ONLINE
+
   def _WaitForNavigation(self, action_runner):
     if not self.IS_SINGLE_PAGE_APP:
       action_runner.WaitForNavigate()
@@ -1654,7 +1662,7 @@
     self.script_to_evaluate_on_commit = '''
         window.WebSocket = undefined;
         window.Worker = undefined;
-        window.performance = undefined;'''
+        window.performance = undefined;''' + override_online.ALWAYS_ONLINE
 
   def _DidLoadDocument(self, action_runner):
     action_runner.WaitForJavaScriptCondition(
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index 5358f3b..1100a414 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -170,6 +170,7 @@
  <item id="oauth2_api_call_flow" hash_code="29188932" type="2" content_hash_code="108831236" os_list="linux,windows" policy_fields="-1" file_path="google_apis/gaia/oauth2_api_call_flow.cc"/>
  <item id="oauth2_mint_token_flow" hash_code="1112842" type="1" second_id="29188932" content_hash_code="91581432" os_list="linux,windows" semantics_fields="1,2,3,4,5" policy_fields="3,4" file_path="google_apis/gaia/oauth2_mint_token_flow.cc"/>
  <item id="ocsp_start_url_request" hash_code="60921996" type="0" content_hash_code="6288676" os_list="linux" file_path="net/cert_net/nss_ocsp_session_url_request.cc"/>
+ <item id="ocsp_start_url_loader" hash_code="3646641" type="0" content_hash_code="106270072" os_list="linux" file_path="services/network/public/cpp/cert_verifier/nss_ocsp_session_url_loader.cc"/>
  <item id="offline_prefetch" hash_code="19185953" type="0" content_hash_code="112039446" os_list="linux,windows" file_path="components/offline_pages/core/prefetch/prefetch_request_fetcher.cc"/>
  <item id="omnibox_documentsuggest" hash_code="6055066" type="0" content_hash_code="126973249" os_list="linux,windows" file_path="components/omnibox/browser/document_suggestions_service.cc"/>
  <item id="omnibox_navigation_observer" hash_code="61684939" type="0" content_hash_code="70941231" os_list="linux,windows" file_path="chrome/browser/ui/omnibox/chrome_omnibox_navigation_observer.cc"/>
@@ -184,7 +185,7 @@
  <item id="optimization_guide_model" hash_code="106373593" type="0" content_hash_code="32403047" os_list="linux,windows" file_path="chrome/browser/optimization_guide/prediction/prediction_model_fetcher.cc"/>
  <item id="origin_policy_loader" hash_code="6483617" type="0" content_hash_code="20680909" os_list="linux,windows" file_path="services/network/origin_policy/origin_policy_fetcher.cc"/>
  <item id="parallel_download_job" hash_code="135118587" type="0" content_hash_code="105330419" os_list="linux,windows" file_path="components/download/internal/common/parallel_download_job.cc"/>
- <item id="password_protection_request" hash_code="66322287" type="0" content_hash_code="25596947" os_list="linux,windows" file_path="components/safe_browsing/password_protection/password_protection_request.cc"/>
+ <item id="password_protection_request" hash_code="66322287" type="0" content_hash_code="25596947" os_list="linux,windows" file_path="components/safe_browsing/content/password_protection/password_protection_request.cc"/>
  <item id="password_requirements_spec_fetch" hash_code="69585116" type="0" content_hash_code="5591260" os_list="linux,windows" file_path="components/password_manager/core/browser/generation/password_requirements_spec_fetcher_impl.cc"/>
  <item id="payment_instrument_icon_fetcher" hash_code="73309970" type="0" deprecated="2017-09-16" content_hash_code="84709873" file_path=""/>
  <item id="payment_manifest_downloader" hash_code="84045030" type="0" content_hash_code="19293316" os_list="linux,windows" file_path="components/payments/core/payment_manifest_downloader.cc"/>
@@ -192,7 +193,7 @@
  <item id="pdf_plugin_placeholder" hash_code="56866367" type="0" content_hash_code="16907221" os_list="linux,windows" file_path="chrome/browser/plugins/pdf_plugin_placeholder_observer.cc"/>
  <item id="pepper_tcp_socket" hash_code="120623198" type="0" content_hash_code="105652563" os_list="linux,windows" file_path="content/browser/renderer_host/pepper/pepper_socket_utils.cc"/>
  <item id="pepper_udp_socket" hash_code="53512439" type="0" content_hash_code="85431089" os_list="linux,windows" file_path="content/browser/renderer_host/pepper/pepper_socket_utils.cc"/>
- <item id="per_user_topic_registration_request" hash_code="10498172" type="0" content_hash_code="32495619" os_list="linux,windows" file_path="components/invalidation/impl/per_user_topic_registration_request.cc"/>
+ <item id="per_user_topic_registration_request" hash_code="10498172" type="0" content_hash_code="57098847" os_list="linux,windows" file_path="components/invalidation/impl/per_user_topic_subscription_request.cc"/>
  <item id="permission_reporting" hash_code="131741641" type="0" deprecated="2018-03-06" content_hash_code="7213535" file_path=""/>
  <item id="permission_request_creator" hash_code="43206794" type="0" deprecated="2019-07-30" content_hash_code="73571699" file_path=""/>
  <item id="persist_blob_to_indexed_db" hash_code="32030464" type="0" deprecated="2018-08-13" content_hash_code="35410079" file_path=""/>
@@ -233,19 +234,19 @@
  <item id="rlz_ping" hash_code="99279418" type="0" content_hash_code="102108802" os_list="windows" file_path="rlz/lib/financial_ping.cc"/>
  <item id="safe_browsing_backup_request" hash_code="106980485" type="0" deprecated="2018-08-14" content_hash_code="101760679" file_path=""/>
  <item id="safe_browsing_binary_upload" hash_code="71663319" type="0" content_hash_code="105913171" os_list="linux,windows" file_path="chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc"/>
- <item id="safe_browsing_cache_collector" hash_code="115907811" type="0" content_hash_code="36392362" os_list="linux,windows" file_path="components/safe_browsing/browser/threat_details_cache.cc"/>
+ <item id="safe_browsing_cache_collector" hash_code="115907811" type="0" content_hash_code="36392362" os_list="linux,windows" file_path="components/safe_browsing/content/browser/threat_details_cache.cc"/>
  <item id="safe_browsing_certificate_error_reporting" hash_code="66590631" type="0" content_hash_code="50197576" os_list="linux,windows" file_path="chrome/browser/ssl/certificate_error_reporter.cc"/>
  <item id="safe_browsing_chunk_backup_request" hash_code="79957943" type="0" deprecated="2018-08-14" content_hash_code="133850277" file_path=""/>
  <item id="safe_browsing_client_side_malware_detector" hash_code="102935425" type="0" deprecated="2019-12-07" content_hash_code="79591279" file_path=""/>
  <item id="safe_browsing_client_side_phishing_detector" hash_code="1313982" type="0" content_hash_code="50199143" os_list="linux,windows" file_path="chrome/browser/safe_browsing/client_side_detection_service.cc"/>
- <item id="safe_browsing_extended_reporting" hash_code="42848942" type="0" content_hash_code="50089173" os_list="linux,windows" file_path="components/safe_browsing/ping_manager.cc"/>
+ <item id="safe_browsing_extended_reporting" hash_code="42848942" type="0" content_hash_code="50089173" os_list="linux,windows" file_path="components/safe_browsing/core/ping_manager.cc"/>
  <item id="safe_browsing_feedback" hash_code="44583821" type="0" content_hash_code="114076664" os_list="linux,windows" file_path="chrome/browser/safe_browsing/download_protection/download_feedback.cc"/>
  <item id="safe_browsing_get_full_hash" hash_code="68745894" type="0" deprecated="2018-08-14" content_hash_code="21739198" file_path=""/>
  <item id="safe_browsing_incident" hash_code="124950347" type="0" content_hash_code="58481082" os_list="linux,windows" file_path="chrome/browser/safe_browsing/incident_reporting/incident_report_uploader_impl.cc"/>
  <item id="safe_browsing_module_loader" hash_code="6019475" type="0" content_hash_code="49511650" os_list="linux,windows" file_path="chrome/browser/safe_browsing/client_side_model_loader.cc"/>
- <item id="safe_browsing_realtime_url_lookup" hash_code="119324658" type="0" content_hash_code="17024823" os_list="linux,windows" file_path="components/safe_browsing/realtime/url_lookup_service.cc"/>
- <item id="safe_browsing_v4_get_hash" hash_code="8561691" type="0" content_hash_code="132435617" os_list="linux,windows" file_path="components/safe_browsing/db/v4_get_hash_protocol_manager.cc"/>
- <item id="safe_browsing_v4_update" hash_code="82509217" type="0" content_hash_code="5247849" os_list="linux,windows" file_path="components/safe_browsing/db/v4_update_protocol_manager.cc"/>
+ <item id="safe_browsing_realtime_url_lookup" hash_code="119324658" type="0" content_hash_code="17024823" os_list="linux,windows" file_path="components/safe_browsing/core/realtime/url_lookup_service.cc"/>
+ <item id="safe_browsing_v4_get_hash" hash_code="8561691" type="0" content_hash_code="132435617" os_list="linux,windows" file_path="components/safe_browsing/core/db/v4_get_hash_protocol_manager.cc"/>
+ <item id="safe_browsing_v4_update" hash_code="82509217" type="0" content_hash_code="5247849" os_list="linux,windows" file_path="components/safe_browsing/core/db/v4_update_protocol_manager.cc"/>
  <item id="save_file_manager" hash_code="56275203" type="0" content_hash_code="56692339" os_list="linux,windows" file_path="content/browser/download/save_file_manager.cc"/>
  <item id="sdch_dictionary_fetch" hash_code="47152935" type="0" deprecated="2017-09-16" content_hash_code="16764294" file_path=""/>
  <item id="search_suggest_service" hash_code="57785193" type="0" content_hash_code="132247652" os_list="linux,windows" file_path="chrome/browser/search/search_suggest/search_suggest_loader_impl.cc"/>
@@ -264,7 +265,7 @@
  <item id="spellcheck_hunspell_dictionary" hash_code="117649486" type="0" content_hash_code="45660952" os_list="linux,windows" file_path="chrome/browser/spellchecker/spellcheck_hunspell_dictionary.cc"/>
  <item id="spellcheck_lookup" hash_code="132553989" type="0" content_hash_code="120395045" os_list="linux,windows" file_path="components/spellcheck/browser/spelling_service_client.cc"/>
  <item id="ssl_hmac_channel_authenticator" hash_code="106124561" type="0" content_hash_code="93707499" os_list="linux,windows" file_path="remoting/protocol/ssl_hmac_channel_authenticator.cc"/>
- <item id="ssl_name_mismatch_lookup" hash_code="114468207" type="0" content_hash_code="97619078" os_list="linux,windows" file_path="chrome/browser/ssl/common_name_mismatch_handler.cc"/>
+ <item id="ssl_name_mismatch_lookup" hash_code="114468207" type="0" content_hash_code="97619078" os_list="linux,windows" file_path="components/security_interstitials/content/common_name_mismatch_handler.cc"/>
  <item id="stream_message_pipe_adapter" hash_code="71837756" type="0" content_hash_code="131019242" os_list="linux,windows" file_path="remoting/protocol/stream_message_pipe_adapter.cc"/>
  <item id="suggestions_image_manager" hash_code="13211343" type="0" deprecated="2018-11-28" content_hash_code="36271280" file_path=""/>
  <item id="suggestions_service" hash_code="35370363" type="0" content_hash_code="66296423" os_list="linux,windows" file_path="components/suggestions/suggestions_service_impl.cc"/>
diff --git a/ui/android/javatests/src/org/chromium/ui/test/util/RenderTestRule.java b/ui/android/javatests/src/org/chromium/ui/test/util/RenderTestRule.java
index 3be034b..cd38708 100644
--- a/ui/android/javatests/src/org/chromium/ui/test/util/RenderTestRule.java
+++ b/ui/android/javatests/src/org/chromium/ui/test/util/RenderTestRule.java
@@ -26,9 +26,13 @@
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Locale;
+import java.util.Map;
 import java.util.concurrent.Callable;
 
 /**
@@ -95,6 +99,8 @@
     // How much a channel must differ when comparing pixels in order to be considered different.
     private int mPixelDiffThreshold;
 
+    private Map<String, DiffReport> mMismatchReports = new HashMap<>();
+
     /**
      * An exception thrown after a Render Test if images do not match the goldens or goldens are
      * missing on a render test device.
@@ -174,11 +180,11 @@
         File goldenFile = createGoldenPath(filename);
         Bitmap goldenBitmap = BitmapFactory.decodeFile(goldenFile.getAbsolutePath(), options);
 
-        Pair<ComparisonResult, Bitmap> result = compareBitmapToGolden(testBitmap, goldenBitmap);
+        Pair<DiffReport, Bitmap> result = compareBitmapToGolden(testBitmap, goldenBitmap, id);
         Log.i(TAG, "RenderTest %s %s", id, result.first.toString());
 
         // Save the result and any interesting images.
-        switch (result.first) {
+        switch (result.first.getResult()) {
             case MATCH:
                 // We don't do anything with the matches.
                 break;
@@ -189,6 +195,7 @@
                 break;
             case MISMATCH:
                 mMismatchIds.add(id);
+                mMismatchReports.put(id, result.first);
 
                 saveBitmap(testBitmap, createOutputPath(FAILURE_FOLDER_RELATIVE, filename));
                 saveBitmap(goldenBitmap, createOutputPath(GOLDEN_FOLDER_RELATIVE, filename));
@@ -197,6 +204,17 @@
         }
     }
 
+    /**
+     * Appends the DiffReports if exists.
+     */
+    private void maybeAppendDiffReports(StringBuilder sb) {
+        for (String key : mMismatchIds) {
+            if (mMismatchReports.containsKey(key)) {
+                sb.append(mMismatchReports.get(key));
+            }
+        }
+    }
+
     @Override
     protected void finished(Description desc) {
         if (!onRenderTestDevice() && !mGoldenMissingIds.isEmpty()) {
@@ -223,7 +241,8 @@
             sb.append(".");
         }
 
-        sb.append(" See RENDER_TESTS.md for how to fix this failure.");
+        sb.append(" See RENDER_TESTS.md for how to fix this failure.\n");
+        maybeAppendDiffReports(sb);
         throw new RenderTestException(sb.toString());
     }
 
@@ -277,7 +296,8 @@
             desc = variantPrefix + "-" + desc;
         }
 
-        return String.format("%s.%s.%s.png", testClass, desc, modelSdkIdentifier());
+        return String.format(
+                Locale.getDefault(), "%s.%s.%s.png", testClass, desc, modelSdkIdentifier());
     }
 
     /**
@@ -327,14 +347,15 @@
 
     /**
      * Compares two Bitmaps.
-     * @return A pair of ComparisonResult and Bitmap. If the ComparisonResult is MISMATCH or MATCH,
+     * @return A pair of DiffReport and Bitmap. If the DiffReport.mResult is MISMATCH or MATCH,
      *         the Bitmap will be a generated pixel-by-pixel difference.
      */
-    private Pair<ComparisonResult, Bitmap> compareBitmapToGolden(Bitmap render, Bitmap golden) {
-        if (golden == null) return Pair.create(ComparisonResult.GOLDEN_NOT_FOUND, null);
+    private Pair<DiffReport, Bitmap> compareBitmapToGolden(
+            Bitmap render, Bitmap golden, String id) {
+        if (golden == null) return Pair.create(DiffReport.newGoldenNotFoundDiffReport(), null);
         // This comparison is much, much faster than doing a pixel-by-pixel comparison, so try this
         // first and only fall back to the pixel comparison if it fails.
-        if (render.sameAs(golden)) return Pair.create(ComparisonResult.MATCH, null);
+        if (render.sameAs(golden)) return Pair.create(DiffReport.newMatchDiffReport(), null);
 
         Bitmap diff = Bitmap.createBitmap(Math.max(render.getWidth(), golden.getWidth()),
                 Math.max(render.getHeight(), golden.getHeight()), render.getConfig());
@@ -347,14 +368,188 @@
         int minWidth = Math.min(render.getWidth(), golden.getWidth());
         int minHeight = Math.min(render.getHeight(), golden.getHeight());
 
-        int diffPixelsCount =
-                comparePixels(render, golden, diff, mPixelDiffThreshold, 0, minWidth, 0, minHeight)
-                + compareSizes(diff, minWidth, maxWidth, minHeight, maxHeight);
+        DiffReport.Builder report =
+                comparePixels(render, golden, diff, mPixelDiffThreshold, 0, minWidth, 0, minHeight);
+        report.addPixelDiffCount(compareSizes(diff, minWidth, maxWidth, minHeight, maxHeight));
+        report.setID(id);
 
-        if (diffPixelsCount > 0) {
-            return Pair.create(ComparisonResult.MISMATCH, diff);
+        return Pair.create(report.build(), diff);
+    }
+
+    /**
+     * The class represents a read only copy of comparison result between one test rendered image
+     * and its golden image.
+     */
+    static class DiffReport {
+        // The maximum value difference in the red channel.
+        private final int mMaxRDiff;
+        // The maximum value difference in the green channel.
+        private final int mMaxGDiff;
+        // The maximum value difference in the blue channel.
+        private final int mMaxBDiff;
+        // The maximum value difference in the alpha channel.
+        private final int mMaxADiff;
+        // Only store first 20 pixel coordinates.
+        private final List<Pair<Integer, Integer>> mDiffPixelLocations = new ArrayList<>();
+        // The count of pixels being considered diff.
+        private final int mDiffPixelCount;
+        // The comparison result such as Match, Mismatch, and Golden_not_found.
+        private final ComparisonResult mResult;
+        // The ID used to identify the golden image.
+        private final String mID;
+        // The Match DiffReport with no ID associated to it.
+        private static final DiffReport MATCH_DIFF_REPORT =
+                new DiffReport("", 0, 0, 0, 0, null, 0, ComparisonResult.MATCH);
+        // The GoldenNotFound DiffReport with no ID associated to it.
+        private static final DiffReport GOLDEN_NOT_FOUND_DIFF_REPORT =
+                new DiffReport("", 0, 0, 0, 0, null, 0, ComparisonResult.GOLDEN_NOT_FOUND);
+        // The maximum number of entries in mDiffPixelCount
+        private static final int MAXIMUM_TOTAL_PIXEL_LOCATIONS = 20;
+
+        private DiffReport(String id, int rDiff, int gDiff, int bDiff, int aDiff,
+                List<Pair<Integer, Integer>> diffLocations, int diffPixelCount) {
+            this(id, rDiff, gDiff, bDiff, aDiff, diffLocations, diffPixelCount,
+                    ComparisonResult.MISMATCH);
         }
-        return Pair.create(ComparisonResult.MATCH, diff);
+
+        private DiffReport(String id, int rDiff, int gDiff, int bDiff, int aDiff,
+                List<Pair<Integer, Integer>> diffLocations, int diffPixelCount,
+                ComparisonResult result) {
+            mID = id;
+            mMaxRDiff = rDiff;
+            mMaxGDiff = gDiff;
+            mMaxBDiff = bDiff;
+            mMaxADiff = aDiff;
+            if (diffLocations != null) {
+                for (Pair<Integer, Integer> loc : diffLocations) {
+                    mDiffPixelLocations.add(loc);
+                    if (mDiffPixelLocations.size() >= MAXIMUM_TOTAL_PIXEL_LOCATIONS) {
+                        break;
+                    }
+                }
+            }
+            mDiffPixelCount = diffPixelCount;
+            switch (result) {
+                case MISMATCH:
+                    if (isMismatch()) {
+                        mResult = ComparisonResult.MISMATCH;
+                    } else {
+                        // If the isMismatch() check fails, it is a Match.
+                        mResult = ComparisonResult.MATCH;
+                    }
+                    break;
+                case MATCH:
+                case GOLDEN_NOT_FOUND:
+                    mResult = result;
+                    break;
+                default:
+                    mResult = ComparisonResult.MISMATCH;
+            }
+        }
+
+        static DiffReport newMatchDiffReport() {
+            return MATCH_DIFF_REPORT;
+        }
+
+        static DiffReport newGoldenNotFoundDiffReport() {
+            return GOLDEN_NOT_FOUND_DIFF_REPORT;
+        }
+
+        /**
+         * Retrieves a new Builder.
+         */
+        static DiffReport.Builder newBuilder() {
+            return new Builder();
+        }
+
+        /**
+         * Generates a string format of the DiffReport.
+         */
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append("====DiffReport====\n");
+            sb.append(String.format(Locale.getDefault(), "id : %s\n", mID));
+            sb.append(String.format(Locale.getDefault(),
+                    "There are %d pixels rendered differently.\n", mDiffPixelCount));
+            sb.append(String.format(Locale.getDefault(),
+                    "The maximum difference in RGBA channels are {R:%d, G:%d, B:%d, A:%d}\n",
+                    mMaxRDiff, mMaxGDiff, mMaxBDiff, mMaxADiff));
+            sb.append(String.format(Locale.getDefault(),
+                    "The different pixels are located at (limited to first %d entries):\n",
+                    MAXIMUM_TOTAL_PIXEL_LOCATIONS));
+            for (Pair<Integer, Integer> loc : mDiffPixelLocations) {
+                sb.append(String.format(Locale.getDefault(), "(%d, %d)\n", loc.first, loc.second));
+            }
+            sb.append("====End of DiffReport====\n");
+            return sb.toString();
+        }
+
+        boolean isMismatch() {
+            return mDiffPixelCount > 0;
+        }
+
+        ComparisonResult getResult() {
+            return mResult;
+        }
+
+        /**
+         * Allows constructing a new DiffReport with read-only properties.
+         */
+        static class Builder {
+            private int mMaxRedDiff;
+            private int mMaxGreenDiff;
+            private int mMaxBlueDiff;
+            private int mMaxAlphaDiff;
+            private int mDiffPixelCount;
+            private String mID;
+            private List<Pair<Integer, Integer>> mDiffPixelLocations = new ArrayList<>();
+
+            Builder setMaxRedDiff(int value) {
+                mMaxRedDiff = value;
+                return this;
+            }
+
+            Builder setMaxGreenDiff(int value) {
+                mMaxGreenDiff = value;
+                return this;
+            }
+
+            Builder setMaxBlueDiff(int value) {
+                mMaxBlueDiff = value;
+                return this;
+            }
+
+            Builder setMaxAlphaDiff(int value) {
+                mMaxAlphaDiff = value;
+                return this;
+            }
+
+            Builder addPixelLocation(Pair<Integer, Integer> loc) {
+                mDiffPixelLocations.add(loc);
+                return this;
+            }
+
+            Builder setPixelDiffCount(int count) {
+                mDiffPixelCount = count;
+                return this;
+            }
+
+            Builder addPixelDiffCount(int count) {
+                mDiffPixelCount += count;
+                return this;
+            }
+
+            Builder setID(String id) {
+                mID = id;
+                return this;
+            }
+
+            DiffReport build() {
+                return new DiffReport(mID, mMaxRedDiff, mMaxGreenDiff, mMaxBlueDiff, mMaxAlphaDiff,
+                        mDiffPixelLocations, mDiffPixelCount);
+            }
+        }
     }
 
     /**
@@ -380,10 +575,11 @@
      *
      * @param endHeight End x-coord to start diffing the Bitmaps.
      *
-     * @return Returns number of pixels that differ between |goldenImage| and |testImage|
+     * @return Returns a DiffReport.Builder that is used to create a DiffReport.
      */
-    private static int comparePixels(Bitmap testImage, Bitmap goldenImage, Bitmap diffImage,
-            int diffThreshold, int startWidth, int endWidth, int startHeight, int endHeight) {
+    private static DiffReport.Builder comparePixels(Bitmap testImage, Bitmap goldenImage,
+            Bitmap diffImage, int diffThreshold, int startWidth, int endWidth, int startHeight,
+            int endHeight) {
         int diffPixels = 0;
 
         // Get copies of the pixels and compare using that instead of repeatedly calling getPixel,
@@ -395,6 +591,12 @@
         int[] testPixels =
                 writeBitmapToArray(testImage, startWidth, startHeight, diffWidth, diffHeight);
 
+        int maxRedDiff = 0;
+        int maxGreenDiff = 0;
+        int maxBlueDiff = 0;
+        int maxAlphaDiff = 0;
+        DiffReport.Builder builder = DiffReport.newBuilder();
+
         int diffArea = diffHeight * diffWidth;
         for (int i = 0; i < diffArea; ++i) {
             if (goldenPixels[i] == testPixels[i]) continue;
@@ -406,13 +608,31 @@
             int blueDiff = Math.abs(Color.blue(goldenColor) - Color.blue(testColor));
             int alphaDiff = Math.abs(Color.alpha(goldenColor) - Color.alpha(testColor));
 
+            if (redDiff > maxRedDiff) {
+                maxRedDiff = redDiff;
+            }
+            if (blueDiff > maxBlueDiff) {
+                maxBlueDiff = blueDiff;
+            }
+            if (greenDiff > maxGreenDiff) {
+                maxGreenDiff = greenDiff;
+            }
+            if (alphaDiff > maxAlphaDiff) {
+                maxAlphaDiff = alphaDiff;
+            }
+
             if (redDiff > diffThreshold || blueDiff > diffThreshold || greenDiff > diffThreshold
                     || alphaDiff > diffThreshold) {
+                builder.addPixelLocation(Pair.create(i % diffWidth, i / diffWidth));
                 diffPixels++;
                 diffImage.setPixel(i % diffWidth, i / diffWidth, Color.RED);
             }
         }
-        return diffPixels;
+        return builder.setMaxRedDiff(maxRedDiff)
+                .setMaxGreenDiff(maxGreenDiff)
+                .setMaxBlueDiff(maxBlueDiff)
+                .setMaxAlphaDiff(maxAlphaDiff)
+                .setPixelDiffCount(diffPixels);
     }
 
     /**
diff --git a/ui/base/clipboard/BUILD.gn b/ui/base/clipboard/BUILD.gn
index 1b71f30..57138fa 100644
--- a/ui/base/clipboard/BUILD.gn
+++ b/ui/base/clipboard/BUILD.gn
@@ -107,7 +107,6 @@
     "//mojo/public/cpp/base",
     "//net",
     "//skia",
-    "//ui/events/platform",
     "//ui/gfx",
     "//ui/gfx/geometry",
   ]
@@ -135,6 +134,7 @@
         deps += [
           "//ui/base",
           "//ui/base/x",
+          "//ui/events/platform",
           "//ui/events/x",
           "//ui/gfx/x",
         ]
diff --git a/ui/base/clipboard/clipboard_test_template.h b/ui/base/clipboard/clipboard_test_template.h
index ef551a2..03e86d9 100644
--- a/ui/base/clipboard/clipboard_test_template.h
+++ b/ui/base/clipboard/clipboard_test_template.h
@@ -44,7 +44,7 @@
 #include "ui/base/clipboard/clipboard_util_win.h"
 #endif
 
-#if defined(USE_AURA)
+#if defined(USE_X11)
 #include "ui/events/platform/platform_event_source.h"
 #endif
 
@@ -65,7 +65,7 @@
   // PlatformTest:
   void SetUp() override {
     PlatformTest::SetUp();
-#if defined(USE_AURA)
+#if defined(USE_X11)
     event_source_ = ClipboardTraits::GetEventSource();
 #endif
     clipboard_ = ClipboardTraits::Create();
@@ -87,7 +87,7 @@
   }
 
  private:
-#if defined(USE_AURA)
+#if defined(USE_X11)
   std::unique_ptr<PlatformEventSource> event_source_;
 #endif
   // ui::Clipboard has a protected destructor, so scoped_ptr doesn't work here.
diff --git a/ui/base/clipboard/clipboard_unittest.cc b/ui/base/clipboard/clipboard_unittest.cc
index 13d4af9a..2cc65b8d 100644
--- a/ui/base/clipboard/clipboard_unittest.cc
+++ b/ui/base/clipboard/clipboard_unittest.cc
@@ -7,7 +7,7 @@
 #include "base/test/task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if defined(USE_AURA)
+#if defined(USE_X11)
 #include "ui/events/platform/platform_event_source.h"
 #endif
 
@@ -20,7 +20,7 @@
 }  // namespace
 
 struct PlatformClipboardTraits {
-#if defined(USE_AURA)
+#if defined(USE_X11)
   static std::unique_ptr<PlatformEventSource> GetEventSource() {
     return PlatformEventSource::CreateDefault();
   }
diff --git a/ui/base/clipboard/test/test_clipboard_unittest.cc b/ui/base/clipboard/test/test_clipboard_unittest.cc
index ed7d2fe..f62f4a37 100644
--- a/ui/base/clipboard/test/test_clipboard_unittest.cc
+++ b/ui/base/clipboard/test/test_clipboard_unittest.cc
@@ -7,7 +7,7 @@
 #include "base/test/task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if defined(USE_AURA)
+#if defined(USE_X11)
 #include "ui/events/platform/platform_event_source.h"
 #endif
 
@@ -20,7 +20,7 @@
 }  // namespace
 
 struct TestClipboardTraits {
-#if defined(USE_AURA)
+#if defined(USE_X11)
   static std::unique_ptr<PlatformEventSource> GetEventSource() {
     return nullptr;
   }
diff --git a/ui/display/OWNERS b/ui/display/OWNERS
index 6f13619..c833c75 100644
--- a/ui/display/OWNERS
+++ b/ui/display/OWNERS
@@ -3,4 +3,4 @@
 marcheu@chromium.org
 oshima@chromium.org
 
-# COMPONENT: UI
+# COMPONENT: UI>Systems>Display
diff --git a/ui/events/blink/input_handler_proxy.cc b/ui/events/blink/input_handler_proxy.cc
index ec7b9e8..be1284b 100644
--- a/ui/events/blink/input_handler_proxy.cc
+++ b/ui/events/blink/input_handler_proxy.cc
@@ -842,7 +842,8 @@
                  gesture_event.data.scroll_begin.delta_hint_units !=
                      ui::input_types::ScrollGranularity::kScrollByPixel)) {
     DCHECK(!scroll_state.is_in_inertial_phase());
-    scroll_status = input_handler_->ScrollAnimatedBegin(&scroll_state);
+    scroll_status =
+        input_handler_->ScrollBegin(&scroll_state, cc::InputHandler::WHEEL);
   } else {
     scroll_status = input_handler_->ScrollBegin(
         &scroll_state, GestureScrollInputType(gesture_event.SourceDevice()));
diff --git a/ui/events/blink/input_handler_proxy_unittest.cc b/ui/events/blink/input_handler_proxy_unittest.cc
index 9b2b867f..254924b5 100644
--- a/ui/events/blink/input_handler_proxy_unittest.cc
+++ b/ui/events/blink/input_handler_proxy_unittest.cc
@@ -115,7 +115,6 @@
   MOCK_METHOD2(RootScrollBegin,
                ScrollStatus(cc::ScrollState*,
                             cc::InputHandler::ScrollInputType type));
-  MOCK_METHOD1(ScrollAnimatedBegin, ScrollStatus(cc::ScrollState*));
   MOCK_METHOD3(ScrollAnimated,
                void(const gfx::Point& viewport_point,
                     const gfx::Vector2dF& scroll_delta,
@@ -739,7 +738,7 @@
 
   // Test setup for a kGestureScrollBegin.
   gesture_.SetType(WebInputEvent::kGestureScrollBegin);
-  EXPECT_CALL(mock_input_handler_, ScrollAnimatedBegin(_))
+  EXPECT_CALL(mock_input_handler_, ScrollBegin(_, cc::InputHandler::WHEEL))
       .WillOnce(testing::Return(kImplThreadScrollState));
   EXPECT_EQ(expected_disposition_,
             input_handler_->RouteToTypeSpecificHandler(gesture_));
@@ -836,7 +835,7 @@
   gesture_.SetType(WebInputEvent::kGestureScrollBegin);
   gesture_.data.scroll_begin.delta_hint_units =
       ui::input_types::ScrollGranularity::kScrollByPixel;
-  EXPECT_CALL(mock_input_handler_, ScrollAnimatedBegin(_))
+  EXPECT_CALL(mock_input_handler_, ScrollBegin(_, cc::InputHandler::WHEEL))
       .WillOnce(testing::Return(kImplThreadScrollState));
   EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
       .WillOnce(testing::Return(false));
diff --git a/ui/gfx/BUILD.gn b/ui/gfx/BUILD.gn
index 6054072..6b7293c 100644
--- a/ui/gfx/BUILD.gn
+++ b/ui/gfx/BUILD.gn
@@ -710,10 +710,7 @@
     ]
   }
   if (is_win) {
-    sources += [
-      "font_fallback_win_unittest.cc",
-      "range/range_win_unittest.cc",
-    ]
+    sources += [ "font_fallback_win_unittest.cc" ]
   }
   if (is_ios) {
     sources += [
diff --git a/ui/gfx/range/BUILD.gn b/ui/gfx/range/BUILD.gn
index 67e6fb0..2c9badf 100644
--- a/ui/gfx/range/BUILD.gn
+++ b/ui/gfx/range/BUILD.gn
@@ -19,10 +19,6 @@
     "range_f.h",
   ]
 
-  if (is_win) {
-    sources += [ "range_win.cc" ]
-  }
-
   if (is_ios || is_mac) {
     sources += [ "range_mac.mm" ]
   }
diff --git a/ui/gfx/range/range.h b/ui/gfx/range/range.h
index 5ea9e3c..fcc2ab6d 100644
--- a/ui/gfx/range/range.h
+++ b/ui/gfx/range/range.h
@@ -116,8 +116,6 @@
   // NSRange does not store the directionality of a range, so if this
   // is_reversed(), the range will get flipped when converted to an NSRange.
   NSRange ToNSRange() const;
-#elif defined(OS_WIN)
-  CHARRANGE ToCHARRANGE() const;
 #endif
   // GTK+ has no concept of a range.
 
diff --git a/ui/gfx/range/range_win.cc b/ui/gfx/range/range_win.cc
deleted file mode 100644
index 05b76f6d..0000000
--- a/ui/gfx/range/range_win.cc
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/gfx/range/range.h"
-
-#include <richedit.h>
-
-#include <limits>
-
-#include "base/logging.h"
-
-namespace gfx {
-
-Range::Range(const CHARRANGE& range, long total_length) {
-  // Check if this is an invalid range.
-  if (range.cpMin == -1 && range.cpMax == -1) {
-    *this = InvalidRange();
-  } else {
-    DCHECK_GE(range.cpMin, 0);
-    set_start(range.cpMin);
-    // {0,-1} is the "whole range" but that doesn't mean much out of context,
-    // so use the |total_length| parameter.
-    if (range.cpMax == -1) {
-      DCHECK_EQ(0, range.cpMin);
-      DCHECK_NE(-1, total_length);
-      set_end(total_length);
-    } else {
-      set_end(range.cpMax);
-    }
-  }
-}
-
-CHARRANGE Range::ToCHARRANGE() const {
-  CHARRANGE r = { -1, -1 };
-  if (!IsValid())
-    return r;
-
-  const LONG kLONGMax = std::numeric_limits<LONG>::max();
-  CHECK_LE(static_cast<LONG>(start()), kLONGMax);
-  CHECK_LE(static_cast<LONG>(end()), kLONGMax);
-  r.cpMin = static_cast<LONG>(start());
-  r.cpMax = static_cast<LONG>(end());
-  return r;
-}
-
-}  // namespace gfx
diff --git a/ui/gfx/range/range_win_unittest.cc b/ui/gfx/range/range_win_unittest.cc
deleted file mode 100644
index 1119650..0000000
--- a/ui/gfx/range/range_win_unittest.cc
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright (c) 2011 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 "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/range/range.h"
-
-#include <richedit.h>
-
-TEST(RangeTest, FromCHARRANGE) {
-  CHARRANGE cr = { 10, 32 };
-  gfx::Range r(cr, 50);
-  EXPECT_EQ(10U, r.start());
-  EXPECT_EQ(32U, r.end());
-  EXPECT_EQ(22U, r.length());
-  EXPECT_FALSE(r.is_reversed());
-  EXPECT_TRUE(r.IsValid());
-}
-
-TEST(RangeTest, FromReversedCHARRANGE) {
-  CHARRANGE cr = { 20, 10 };
-  gfx::Range r(cr, 40);
-  EXPECT_EQ(20U, r.start());
-  EXPECT_EQ(10U, r.end());
-  EXPECT_EQ(10U, r.length());
-  EXPECT_TRUE(r.is_reversed());
-  EXPECT_TRUE(r.IsValid());
-}
-
-TEST(RangeTest, FromCHARRANGETotal) {
-  CHARRANGE cr = { 0, -1 };
-  gfx::Range r(cr, 20);
-  EXPECT_EQ(0U, r.start());
-  EXPECT_EQ(20U, r.end());
-  EXPECT_EQ(20U, r.length());
-  EXPECT_FALSE(r.is_reversed());
-  EXPECT_TRUE(r.IsValid());
-}
-
-TEST(RangeTest, ToCHARRANGE) {
-  gfx::Range r(10, 30);
-  CHARRANGE cr = r.ToCHARRANGE();
-  EXPECT_EQ(10, cr.cpMin);
-  EXPECT_EQ(30, cr.cpMax);
-}
-
-TEST(RangeTest, ReversedToCHARRANGE) {
-  gfx::Range r(20, 10);
-  CHARRANGE cr = r.ToCHARRANGE();
-  EXPECT_EQ(20, cr.cpMin);
-  EXPECT_EQ(10, cr.cpMax);
-}
-
-TEST(RangeTest, FromCHARRANGEInvalid) {
-  CHARRANGE cr = { -1, -1 };
-  gfx::Range r(cr, 30);
-  EXPECT_FALSE(r.IsValid());
-}
-
-TEST(RangeTest, ToCHARRANGEInvalid) {
-  gfx::Range r(gfx::Range::InvalidRange());
-  CHARRANGE cr = r.ToCHARRANGE();
-  EXPECT_EQ(-1, cr.cpMin);
-  EXPECT_EQ(-1, cr.cpMax);
-}
diff --git a/ui/webui/mojo_web_ui_controller.cc b/ui/webui/mojo_web_ui_controller.cc
index 4931648..909f343 100644
--- a/ui/webui/mojo_web_ui_controller.cc
+++ b/ui/webui/mojo_web_ui_controller.cc
@@ -4,15 +4,13 @@
 
 #include "ui/webui/mojo_web_ui_controller.h"
 
-#include "content/public/browser/render_process_host.h"
 #include "content/public/common/bindings_policy.h"
 
 namespace ui {
 
 MojoWebUIController::MojoWebUIController(content::WebUI* contents,
                                          bool enable_chrome_send)
-    : content::WebUIController(contents),
-      content::WebContentsObserver(contents->GetWebContents()) {
+    : content::WebUIController(contents) {
   int bindings = content::BINDINGS_POLICY_MOJO_WEB_UI;
   if (enable_chrome_send)
     bindings |= content::BINDINGS_POLICY_WEB_UI;
@@ -20,23 +18,4 @@
 }
 MojoWebUIController::~MojoWebUIController() = default;
 
-void MojoWebUIController::OnInterfaceRequestFromFrame(
-    content::RenderFrameHost* render_frame_host,
-    const std::string& interface_name,
-    mojo::ScopedMessagePipeHandle* interface_pipe) {
-  if (!registry_.CanBindInterface(interface_name))
-    return;
-
-  // Right now, this is expected to be called only for main frames.
-  if (render_frame_host->GetParent()) {
-    LOG(ERROR) << "Terminating renderer for requesting " << interface_name
-               << " interface from subframe";
-    render_frame_host->GetProcess()->ShutdownForBadMessage(
-        content::RenderProcessHost::CrashReportMode::GENERATE_CRASH_DUMP);
-    return;
-  }
-
-  registry_.TryBindInterface(interface_name, interface_pipe);
-}
-
 }  // namespace ui
diff --git a/ui/webui/mojo_web_ui_controller.h b/ui/webui/mojo_web_ui_controller.h
index ddae2e22..fe0a87e 100644
--- a/ui/webui/mojo_web_ui_controller.h
+++ b/ui/webui/mojo_web_ui_controller.h
@@ -5,28 +5,20 @@
 #ifndef UI_WEBUI_MOJO_WEB_UI_CONTROLLER_H_
 #define UI_WEBUI_MOJO_WEB_UI_CONTROLLER_H_
 
-#include <string>
-
 #include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_controller.h"
-#include "mojo/public/cpp/system/core.h"
-#include "services/service_manager/public/cpp/binder_registry.h"
 
 namespace ui {
 
 // MojoWebUIController is intended for WebUI pages that use Mojo. It is
-// expected that subclasses will do two things:
-// . In the constructor invoke AddMojoResourcePath() to register the bindings
-//   files, eg:
-//     AddResourcePath("chrome/browser/ui/webui/omnibox/omnibox.mojom",
-//                     IDR_OMNIBOX_MOJO_JS);
-// . Call AddHandlerToRegistry for all Mojo Interfaces it wishes to handle.
-class MojoWebUIController : public content::WebUIController,
-                            public content::WebContentsObserver {
+// expected that subclasses will:
+// . Add all Mojo Bindings Resources via AddResourcePath(), eg:
+//     source-> AddResourcePath("chrome/browser/ui/webui/omnibox/omnibox.mojom",
+//                              IDR_OMNIBOX_MOJO_JS);
+// . Overload void BindInterface(mojo::PendingReceiver<InterfaceName>) for all
+//   Mojo Interfaces it wishes to handle.
+class MojoWebUIController : public content::WebUIController {
  public:
   // By default MojoWebUIControllers do not have normal WebUI bindings. Pass
   // |enable_chrome_send| as true if these are needed.
@@ -34,27 +26,7 @@
                                bool enable_chrome_send = false);
   ~MojoWebUIController() override;
 
-  // content::WebContentsObserver implementation.
-  // TODO(crbug.com/936482) Remove once all subclasses of MojoWebUIController
-  // have been migrated to BrowserInterfaceBroker
-  void OnInterfaceRequestFromFrame(
-      content::RenderFrameHost* render_frame_host,
-      const std::string& interface_name,
-      mojo::ScopedMessagePipeHandle* interface_pipe) override;
-
-  // TODO(crbug.com/936482) Remove once all subclasses of MojoWebUIController
-  // have been migrated to BrowserInterfaceBroker, and update the class-level
-  // comment instructing to call this method from subclasses' constructors.
-  template <typename Binder>
-  void AddHandlerToRegistry(Binder binder) {
-    registry_.AddInterface(std::move(binder));
-  }
-
  private:
-  // TODO(crbug.com/936482) Remove once all subclasses of MojoWebUIController
-  // have been migrated to BrowserInterfaceBroker
-  service_manager::BinderRegistry registry_;
-
   DISALLOW_COPY_AND_ASSIGN(MojoWebUIController);
 };
 
diff --git a/weblayer/BUILD.gn b/weblayer/BUILD.gn
index 998d193..c517b20 100644
--- a/weblayer/BUILD.gn
+++ b/weblayer/BUILD.gn
@@ -277,14 +277,15 @@
       "//components/crash/android:crashpad_main",
       "//components/metrics",
       "//components/minidump_uploader",
-      "//components/safe_browsing",
       "//components/safe_browsing/android:remote_database_manager",
       "//components/safe_browsing/android:safe_browsing_api_handler",
-      "//components/safe_browsing/browser",
-      "//components/safe_browsing/browser:network_context",
-      "//components/safe_browsing/common",
-      "//components/safe_browsing/db:database_manager",
-      "//components/safe_browsing/renderer:throttles",
+      "//components/safe_browsing/content",
+      "//components/safe_browsing/content/browser",
+      "//components/safe_browsing/content/renderer:throttles",
+      "//components/safe_browsing/core/browser",
+      "//components/safe_browsing/core/browser:network_context",
+      "//components/safe_browsing/core/common",
+      "//components/safe_browsing/core/db:database_manager",
       "//components/version_info/android:channel_getter",
     ]
   }
diff --git a/weblayer/browser/browser_context_impl.cc b/weblayer/browser/browser_context_impl.cc
index c3a8fc4..56cf179 100644
--- a/weblayer/browser/browser_context_impl.cc
+++ b/weblayer/browser/browser_context_impl.cc
@@ -8,7 +8,7 @@
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/pref_service_factory.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/user_prefs/user_prefs.h"
 #include "content/public/browser/resource_context.h"
 
diff --git a/weblayer/browser/content_browser_client_impl.cc b/weblayer/browser/content_browser_client_impl.cc
index 4368e6d..8b0be1f 100644
--- a/weblayer/browser/content_browser_client_impl.cc
+++ b/weblayer/browser/content_browser_client_impl.cc
@@ -253,7 +253,7 @@
       IsSafebrowsingSupported()) {
 #if defined(OS_ANDROID)
     result.push_back(GetSafeBrowsingService()->CreateURLLoaderThrottle(
-        browser_context->GetResourceContext(), wc_getter, frame_tree_node_id));
+        wc_getter, frame_tree_node_id));
 #endif
   }
 
diff --git a/weblayer/browser/safe_browsing/safe_browsing_browsertest.cc b/weblayer/browser/safe_browsing/safe_browsing_browsertest.cc
index 7f23fc8..9b183677 100644
--- a/weblayer/browser/safe_browsing/safe_browsing_browsertest.cc
+++ b/weblayer/browser/safe_browsing/safe_browsing_browsertest.cc
@@ -6,8 +6,8 @@
 
 #include "base/task/post_task.h"
 #include "components/safe_browsing/android/safe_browsing_api_handler.h"
-#include "components/safe_browsing/base_blocking_page.h"
-#include "components/safe_browsing/db/v4_protocol_manager_util.h"
+#include "components/safe_browsing/content/base_blocking_page.h"
+#include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/interstitial_page.h"
diff --git a/weblayer/browser/safe_browsing/safe_browsing_service.cc b/weblayer/browser/safe_browsing/safe_browsing_service.cc
index b09b7cd..a456a181 100644
--- a/weblayer/browser/safe_browsing/safe_browsing_service.cc
+++ b/weblayer/browser/safe_browsing/safe_browsing_service.cc
@@ -9,9 +9,9 @@
 #include "base/task/post_task.h"
 #include "components/safe_browsing/android/remote_database_manager.h"
 #include "components/safe_browsing/android/safe_browsing_api_handler_bridge.h"
-#include "components/safe_browsing/browser/browser_url_loader_throttle.h"
-#include "components/safe_browsing/browser/mojo_safe_browsing_impl.h"
-#include "components/safe_browsing/browser/safe_browsing_network_context.h"
+#include "components/safe_browsing/content/browser/browser_url_loader_throttle.h"
+#include "components/safe_browsing/content/browser/mojo_safe_browsing_impl.h"
+#include "components/safe_browsing/core/browser/safe_browsing_network_context.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
@@ -69,18 +69,17 @@
 
 std::unique_ptr<blink::URLLoaderThrottle>
 SafeBrowsingService::CreateURLLoaderThrottle(
-    content::ResourceContext* resource_context,
     const base::RepeatingCallback<content::WebContents*()>& wc_getter,
     int frame_tree_node_id) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   return safe_browsing::BrowserURLLoaderThrottle::Create(
       base::BindOnce(
-          [](SafeBrowsingService* sb_service, content::ResourceContext*) {
+          [](SafeBrowsingService* sb_service) {
             return sb_service->GetSafeBrowsingUrlCheckerDelegate();
           },
           base::Unretained(this)),
-      wc_getter, frame_tree_node_id, resource_context,
+      wc_getter, frame_tree_node_id,
       // cache_manager is used to perform real time url check, which is gated by
       // UKM opted in. Since WebLayer currently doesn't support UKM, this
       // feature is not enabled.
diff --git a/weblayer/browser/safe_browsing/safe_browsing_service.h b/weblayer/browser/safe_browsing/safe_browsing_service.h
index 04759ec..ad9fd39 100644
--- a/weblayer/browser/safe_browsing/safe_browsing_service.h
+++ b/weblayer/browser/safe_browsing/safe_browsing_service.h
@@ -5,7 +5,7 @@
 #ifndef WEBLAYER_BROWSER_SAFE_BROWSING_SAFE_BROWSING_SERVICE_H_
 #define WEBLAYER_BROWSER_SAFE_BROWSING_SAFE_BROWSING_SERVICE_H_
 
-#include "components/safe_browsing/base_ui_manager.h"
+#include "components/safe_browsing/content/base_ui_manager.h"
 
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
@@ -15,7 +15,6 @@
 
 namespace content {
 class RenderProcessHost;
-class ResourceContext;
 }
 
 namespace blink {
@@ -46,7 +45,6 @@
   // Executed on UI thread
   void Initialize();
   std::unique_ptr<blink::URLLoaderThrottle> CreateURLLoaderThrottle(
-      content::ResourceContext* resource_context,
       const base::RepeatingCallback<content::WebContents*()>& wc_getter,
       int frame_tree_node_id);
   void AddInterface(service_manager::BinderRegistry* registry,
diff --git a/weblayer/browser/safe_browsing/safe_browsing_ui_manager.h b/weblayer/browser/safe_browsing/safe_browsing_ui_manager.h
index f1547eee..5cdb413 100644
--- a/weblayer/browser/safe_browsing/safe_browsing_ui_manager.h
+++ b/weblayer/browser/safe_browsing/safe_browsing_ui_manager.h
@@ -5,7 +5,7 @@
 #ifndef WEBLAYER_BROWSER_SAFE_BROWSING_SAFE_BROWSING_UI_MANAGER_H_
 #define WEBLAYER_BROWSER_SAFE_BROWSING_SAFE_BROWSING_UI_MANAGER_H_
 
-#include "components/safe_browsing/base_ui_manager.h"
+#include "components/safe_browsing/content/base_ui_manager.h"
 
 namespace weblayer {
 
diff --git a/weblayer/browser/safe_browsing/url_checker_delegate_impl.cc b/weblayer/browser/safe_browsing/url_checker_delegate_impl.cc
index e767c9c..726d72f 100644
--- a/weblayer/browser/safe_browsing/url_checker_delegate_impl.cc
+++ b/weblayer/browser/safe_browsing/url_checker_delegate_impl.cc
@@ -6,7 +6,7 @@
 
 #include "base/bind.h"
 #include "base/task/post_task.h"
-#include "components/safe_browsing/db/database_manager.h"
+#include "components/safe_browsing/core/db/database_manager.h"
 #include "components/security_interstitials/content/unsafe_resource.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
@@ -62,7 +62,6 @@
 }
 
 bool UrlCheckerDelegateImpl::ShouldSkipRequestCheck(
-    content::ResourceContext* resource_context,
     const GURL& original_url,
     int frame_tree_node_id,
     int render_process_id,
diff --git a/weblayer/browser/safe_browsing/url_checker_delegate_impl.h b/weblayer/browser/safe_browsing/url_checker_delegate_impl.h
index 1fc972d..fc5fa47 100644
--- a/weblayer/browser/safe_browsing/url_checker_delegate_impl.h
+++ b/weblayer/browser/safe_browsing/url_checker_delegate_impl.h
@@ -7,7 +7,7 @@
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
-#include "components/safe_browsing/browser/url_checker_delegate.h"
+#include "components/safe_browsing/core/browser/url_checker_delegate.h"
 #include "content/public/browser/web_contents.h"
 
 namespace security_interstitials {
@@ -38,8 +38,7 @@
       bool is_main_frame,
       bool has_user_gesture) override;
   bool IsUrlWhitelisted(const GURL& url) override;
-  bool ShouldSkipRequestCheck(content::ResourceContext* resource_context,
-                              const GURL& original_url,
+  bool ShouldSkipRequestCheck(const GURL& original_url,
                               int frame_tree_node_id,
                               int render_process_id,
                               int render_frame_id,
diff --git a/weblayer/renderer/DEPS b/weblayer/renderer/DEPS
index b792c33..c16a871 100644
--- a/weblayer/renderer/DEPS
+++ b/weblayer/renderer/DEPS
@@ -5,8 +5,8 @@
   # that implementation and remove the need for this dependency.
   "+android_webview/grit",
   "+components/autofill/content/renderer",
-  "+components/safe_browsing/common",
-  "+components/safe_browsing/renderer",
+  "+components/safe_browsing/content/renderer",
+  "+components/safe_browsing/core/common",
   "+components/security_interstitials/content/renderer",
   "+components/security_interstitials/core/common",
   "+components/spellcheck/renderer",
diff --git a/weblayer/renderer/url_loader_throttle_provider.cc b/weblayer/renderer/url_loader_throttle_provider.cc
index 942961a..17262ec7 100644
--- a/weblayer/renderer/url_loader_throttle_provider.cc
+++ b/weblayer/renderer/url_loader_throttle_provider.cc
@@ -6,7 +6,7 @@
 
 #include <memory>
 
-#include "components/safe_browsing/renderer/renderer_url_loader_throttle.h"
+#include "components/safe_browsing/content/renderer/renderer_url_loader_throttle.h"
 #include "content/public/renderer/render_thread.h"
 
 namespace weblayer {
diff --git a/weblayer/renderer/url_loader_throttle_provider.h b/weblayer/renderer/url_loader_throttle_provider.h
index a7113f34..b0129b4 100644
--- a/weblayer/renderer/url_loader_throttle_provider.h
+++ b/weblayer/renderer/url_loader_throttle_provider.h
@@ -6,7 +6,7 @@
 #define WEBLAYER_RENDERER_URL_LOADER_THROTTLE_PROVIDER_H_
 
 #include "base/threading/thread_checker.h"
-#include "components/safe_browsing/common/safe_browsing.mojom.h"
+#include "components/safe_browsing/core/common/safe_browsing.mojom.h"
 #include "content/public/renderer/url_loader_throttle_provider.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/remote.h"
diff --git a/weblayer/test/BUILD.gn b/weblayer/test/BUILD.gn
index 41ffbe9..b5314f5f 100644
--- a/weblayer/test/BUILD.gn
+++ b/weblayer/test/BUILD.gn
@@ -125,8 +125,8 @@
       "//android_webview:generate_aw_strings_grit",
       "//android_webview:locale_pak_assets",
       "//android_webview:pak_file_assets",
-      "//components/safe_browsing",
       "//components/safe_browsing/android:safe_browsing_api_handler",
+      "//components/safe_browsing/content",
       "//components/viz/service:service_java",
       "//content/public/test/android:android_test_message_pump_support_java",
       "//content/test:android_test_message_pump_support",