Move PopupBlockedInfoBarDelegate to //components/blocked_content

This infobar will be used in WebLayer when popups are blocked. This CL
componentizes the infobar delegate, and adds support to WebLayer for
showing the infobar.

Bug: 1019922
Change-Id: I21d0f0e40dac6af5f4c8e3d5a4067906f64a009a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2248075
Reviewed-by: Ted Choc <[email protected]>
Reviewed-by: Charlie Harrison <[email protected]>
Reviewed-by: Pavel Yatsuk <[email protected]>
Commit-Queue: Clark DuVall <[email protected]>
Cr-Commit-Position: refs/heads/master@{#779165}
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 309c3ec3..8e6e705 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -191,6 +191,7 @@
     "//chrome/browser/ui/android/strings:ui_strings_grd",
     "//chrome/browser/ui/messages/android:java_resources",
     "//components/autofill/android:autofill_java_resources",
+    "//components/blocked_content/android:java_resources",
     "//components/browser_ui/http_auth/android:java_resources",
     "//components/browser_ui/modaldialog/android:java_resources",
     "//components/browser_ui/settings/android:java_resources",
@@ -695,6 +696,7 @@
   package_path = "org/chromium/chrome/browser/resources"
   inputs = [
     "../browser/android/resource_id.h",
+    "//components/resources/android/blocked_content_resource_id.h",
     "//components/resources/android/page_info_resource_id.h",
     "//components/resources/android/permissions_resource_id.h",
   ]
diff --git a/chrome/android/chrome_java_resources.gni b/chrome/android/chrome_java_resources.gni
index 11a15056..d9102de 100644
--- a/chrome/android/chrome_java_resources.gni
+++ b/chrome/android/chrome_java_resources.gni
@@ -110,7 +110,6 @@
   "java/res/drawable-hdpi/incognito_small.png",
   "java/res/drawable-hdpi/incognito_splash.png",
   "java/res/drawable-hdpi/incognito_switch.png",
-  "java/res/drawable-hdpi/infobar_blocked_popups.png",
   "java/res/drawable-hdpi/infobar_chrome.png",
   "java/res/drawable-hdpi/infobar_mobile_friendly.png",
   "java/res/drawable-hdpi/infobar_restore.png",
@@ -271,7 +270,6 @@
   "java/res/drawable-mdpi/incognito_small.png",
   "java/res/drawable-mdpi/incognito_splash.png",
   "java/res/drawable-mdpi/incognito_switch.png",
-  "java/res/drawable-mdpi/infobar_blocked_popups.png",
   "java/res/drawable-mdpi/infobar_chrome.png",
   "java/res/drawable-mdpi/infobar_mobile_friendly.png",
   "java/res/drawable-mdpi/infobar_restore.png",
@@ -424,7 +422,6 @@
   "java/res/drawable-xhdpi/incognito_small.png",
   "java/res/drawable-xhdpi/incognito_splash.png",
   "java/res/drawable-xhdpi/incognito_switch.png",
-  "java/res/drawable-xhdpi/infobar_blocked_popups.png",
   "java/res/drawable-xhdpi/infobar_chrome.png",
   "java/res/drawable-xhdpi/infobar_mobile_friendly.png",
   "java/res/drawable-xhdpi/infobar_restore.png",
@@ -549,7 +546,6 @@
   "java/res/drawable-xxhdpi/incognito_small.png",
   "java/res/drawable-xxhdpi/incognito_splash.png",
   "java/res/drawable-xxhdpi/incognito_switch.png",
-  "java/res/drawable-xxhdpi/infobar_blocked_popups.png",
   "java/res/drawable-xxhdpi/infobar_chrome.png",
   "java/res/drawable-xxhdpi/infobar_mobile_friendly.png",
   "java/res/drawable-xxhdpi/infobar_restore.png",
@@ -671,7 +667,6 @@
   "java/res/drawable-xxxhdpi/incognito_small.png",
   "java/res/drawable-xxxhdpi/incognito_splash.png",
   "java/res/drawable-xxxhdpi/incognito_switch.png",
-  "java/res/drawable-xxxhdpi/infobar_blocked_popups.png",
   "java/res/drawable-xxxhdpi/infobar_chrome.png",
   "java/res/drawable-xxxhdpi/infobar_mobile_friendly.png",
   "java/res/drawable-xxxhdpi/infobar_restore.png",
diff --git a/chrome/android/java/ResourceId.template b/chrome/android/java/ResourceId.template
index 5009186..a8087f7 100644
--- a/chrome/android/java/ResourceId.template
+++ b/chrome/android/java/ResourceId.template
@@ -12,6 +12,7 @@
 #define LINK_RESOURCE_ID(c_id,java_id) java_id,
 #define DECLARE_RESOURCE_ID(c_id,java_id) java_id,
 #include "chrome/browser/android/resource_id.h"
+#include "components/resources/android/blocked_content_resource_id.h"
 #include "components/resources/android/page_info_resource_id.h"
 #include "components/resources/android/permissions_resource_id.h"
         };
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 861340c..5d941d2 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -6206,14 +6206,6 @@
       <message name="IDS_MEDIA_MENU_NO_DEVICE_TITLE" desc="The title of the media select menu if there is no microphone or camera available on the machine">
         None available
       </message>
-      <if expr="is_android">
-        <message name="IDS_POPUPS_BLOCKED_INFOBAR_BUTTON_SHOW" desc="Pop-up Blocking Show Button [CHAR-LIMIT=32]">
-          Always show
-        </message>
-        <message name="IDS_POPUPS_BLOCKED_INFOBAR_TEXT" desc="Pop-up Blocking Title [CHAR-LIMIT=32] [ICU Syntax]">
-          {NUM_POPUPS,plural,=1{Pop-up blocked} other{# pop-ups blocked}}
-        </message>
-      </if>
 
       <message name="IDS_ZOOMLEVELS_CHROME_ERROR_PAGES_LABEL" desc="Label for zoom level list entry for Chrome error pages.">
         (Chrome error pages)
diff --git a/chrome/browser/android/resource_id.h b/chrome/browser/android/resource_id.h
index ee7dee2d..438fcb8 100644
--- a/chrome/browser/android/resource_id.h
+++ b/chrome/browser/android/resource_id.h
@@ -29,8 +29,6 @@
 LINK_RESOURCE_ID(IDR_INFOBAR_AUTOFILL_CC, R.drawable.infobar_autofill_cc)
 
 // Android only infobars.
-DECLARE_RESOURCE_ID(IDR_ANDROID_INFOBAR_BLOCKED_POPUPS,
-                    R.drawable.infobar_blocked_popups)
 DECLARE_RESOURCE_ID(IDR_ANDROID_INFOBAR_FROZEN_TAB, R.drawable.infobar_restore)
 DECLARE_RESOURCE_ID(IDR_ANDROID_INFOBAR_LITE_MODE, R.drawable.preview_pin_round)
 DECLARE_RESOURCE_ID(IDR_ANDROID_INFOBAR_MEDIA_STREAM_SCREEN,
diff --git a/chrome/browser/android/resource_mapper.cc b/chrome/browser/android/resource_mapper.cc
index c54ca3e..15422a23 100644
--- a/chrome/browser/android/resource_mapper.cc
+++ b/chrome/browser/android/resource_mapper.cc
@@ -54,6 +54,7 @@
 #define DECLARE_RESOURCE_ID(c_id, java_id) \
   g_id_map.Get()[c_id] = resource_id_list[next_id++];
 #include "chrome/browser/android/resource_id.h"
+#include "components/resources/android/blocked_content_resource_id.h"
 #include "components/resources/android/page_info_resource_id.h"
 #include "components/resources/android/permissions_resource_id.h"
 #undef LINK_RESOURCE_ID
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 6697bb7..842af58 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -664,8 +664,6 @@
       "android/color_chooser_dialog_android.cc",
       "android/content_settings/ads_blocked_infobar_delegate.cc",
       "android/content_settings/ads_blocked_infobar_delegate.h",
-      "android/content_settings/popup_blocked_infobar_delegate.cc",
-      "android/content_settings/popup_blocked_infobar_delegate.h",
       "android/context_menu_helper.cc",
       "android/context_menu_helper.h",
       "android/device_dialog/bluetooth_chooser_android.cc",
diff --git a/chrome/browser/ui/android/content_settings/popup_blocked_infobar_delegate.h b/chrome/browser/ui/android/content_settings/popup_blocked_infobar_delegate.h
deleted file mode 100644
index c193cd6..0000000
--- a/chrome/browser/ui/android/content_settings/popup_blocked_infobar_delegate.h
+++ /dev/null
@@ -1,48 +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.
-
-#ifndef CHROME_BROWSER_UI_ANDROID_CONTENT_SETTINGS_POPUP_BLOCKED_INFOBAR_DELEGATE_H_
-#define CHROME_BROWSER_UI_ANDROID_CONTENT_SETTINGS_POPUP_BLOCKED_INFOBAR_DELEGATE_H_
-
-#include "base/macros.h"
-#include "components/infobars/core/confirm_infobar_delegate.h"
-#include "url/gurl.h"
-
-namespace content {
-class WebContents;
-}  // namespace content
-
-class HostContentSettingsMap;
-
-class PopupBlockedInfoBarDelegate : public ConfirmInfoBarDelegate {
- public:
-  // Creates a popup blocked infobar and delegate and adds the infobar to
-  // |infobar_service|.
-  static void Create(content::WebContents* web_contents, int num_popups);
-
-  ~PopupBlockedInfoBarDelegate() override;
-
- private:
-  PopupBlockedInfoBarDelegate(int num_popups,
-                              const GURL& url,
-                              HostContentSettingsMap* map);
-
-  // ConfirmInfoBarDelegate:
-  infobars::InfoBarDelegate::InfoBarIdentifier GetIdentifier() const override;
-  int GetIconId() const override;
-  PopupBlockedInfoBarDelegate* AsPopupBlockedInfoBarDelegate() override;
-  base::string16 GetMessageText() const override;
-  int GetButtons() const override;
-  base::string16 GetButtonLabel(InfoBarButton button) const override;
-  bool Accept() override;
-
-  int num_popups_;
-  GURL url_;
-  HostContentSettingsMap* map_;
-  bool can_show_popups_;
-
-  DISALLOW_COPY_AND_ASSIGN(PopupBlockedInfoBarDelegate);
-};
-
-#endif  // CHROME_BROWSER_UI_ANDROID_CONTENT_SETTINGS_POPUP_BLOCKED_INFOBAR_DELEGATE_H_
diff --git a/chrome/browser/ui/blocked_content/chrome_popup_navigation_delegate.cc b/chrome/browser/ui/blocked_content/chrome_popup_navigation_delegate.cc
index 430a4d7a..45fb68c5 100644
--- a/chrome/browser/ui/blocked_content/chrome_popup_navigation_delegate.cc
+++ b/chrome/browser/ui/blocked_content/chrome_popup_navigation_delegate.cc
@@ -5,6 +5,9 @@
 #include "chrome/browser/ui/blocked_content/chrome_popup_navigation_delegate.h"
 
 #include "build/build_config.h"
+#include "chrome/browser/content_settings/chrome_content_settings_utils.h"
+#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
+#include "chrome/browser/infobars/infobar_service.h"
 #include "chrome/browser/ui/browser_navigator.h"
 #include "chrome/common/chrome_render_frame.mojom.h"
 #include "components/blocked_content/popup_navigation_delegate.h"
@@ -14,8 +17,8 @@
 #include "third_party/blink/public/mojom/window_features/window_features.mojom.h"
 
 #if defined(OS_ANDROID)
-#include "chrome/browser/ui/android/content_settings/popup_blocked_infobar_delegate.h"
 #include "chrome/browser/ui/android/tab_model/tab_model_list.h"
+#include "components/blocked_content/android/popup_blocked_infobar_delegate.h"
 #endif
 
 ChromePopupNavigationDelegate::ChromePopupNavigationDelegate(
@@ -66,7 +69,16 @@
 #if defined(OS_ANDROID)
   // Should replace existing popup infobars, with an updated count of how many
   // popups have been blocked.
-  PopupBlockedInfoBarDelegate::Create(web_contents,
-                                      total_popups_blocked_on_page);
+  if (blocked_content::PopupBlockedInfoBarDelegate::Create(
+          InfoBarService::FromWebContents(web_contents),
+          total_popups_blocked_on_page,
+          HostContentSettingsMapFactory::GetForProfile(
+              web_contents->GetBrowserContext()),
+          base::BindOnce(
+              &content_settings::RecordPopupsAction,
+              content_settings::POPUPS_ACTION_CLICKED_ALWAYS_SHOW_ON_MOBILE))) {
+    content_settings::RecordPopupsAction(
+        content_settings::POPUPS_ACTION_DISPLAYED_INFOBAR_ON_MOBILE);
+  }
 #endif
 }
diff --git a/components/blocked_content/BUILD.gn b/components/blocked_content/BUILD.gn
index abc2014e..4f86ea0f 100644
--- a/components/blocked_content/BUILD.gn
+++ b/components/blocked_content/BUILD.gn
@@ -38,6 +38,31 @@
     "//services/metrics/public/cpp:ukm_builders",
     "//third_party/blink/public/common",
   ]
+  if (is_android) {
+    sources += [
+      "android/popup_blocked_infobar_delegate.cc",
+      "android/popup_blocked_infobar_delegate.h",
+    ]
+    deps += [
+      "//components/infobars/content",
+      "//components/infobars/core",
+      "//components/resources:android_resources",
+      "//components/strings:components_strings_grit",
+    ]
+  }
+}
+
+source_set("test_support") {
+  testonly = true
+  sources = [
+    "test/test_popup_navigation_delegate.cc",
+    "test/test_popup_navigation_delegate.h",
+  ]
+  deps = [
+    ":blocked_content",
+    "//third_party/blink/public/common",
+    "//url",
+  ]
 }
 
 source_set("unit_tests") {
@@ -48,6 +73,7 @@
   ]
   deps = [
     ":blocked_content",
+    ":test_support",
     "//base",
     "//base/test:test_support",
     "//components/content_settings/browser",
@@ -62,4 +88,8 @@
     "//net:test_support",
     "//testing/gtest",
   ]
+  if (is_android) {
+    sources += [ "android/popup_blocked_infobar_delegate_unittest.cc" ]
+    deps += [ "//components/infobars/content" ]
+  }
 }
diff --git a/components/blocked_content/android/BUILD.gn b/components/blocked_content/android/BUILD.gn
new file mode 100644
index 0000000..1a42a65
--- /dev/null
+++ b/components/blocked_content/android/BUILD.gn
@@ -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.
+
+import("//build/config/android/rules.gni")
+
+android_resources("java_resources") {
+  sources = [
+    "res/drawable-hdpi/infobar_blocked_popups.png",
+    "res/drawable-mdpi/infobar_blocked_popups.png",
+    "res/drawable-xhdpi/infobar_blocked_popups.png",
+    "res/drawable-xxhdpi/infobar_blocked_popups.png",
+    "res/drawable-xxxhdpi/infobar_blocked_popups.png",
+  ]
+  custom_package = "org.chromium.components.blocked_content"
+}
diff --git a/components/blocked_content/android/DEPS b/components/blocked_content/android/DEPS
new file mode 100644
index 0000000..61e69ec
--- /dev/null
+++ b/components/blocked_content/android/DEPS
@@ -0,0 +1,6 @@
+include_rules = [
+  "+components/infobars/content",
+  "+components/infobars/core",
+  "+components/resources/android",
+  "+components/strings",
+]
diff --git a/chrome/browser/ui/android/content_settings/popup_blocked_infobar_delegate.cc b/components/blocked_content/android/popup_blocked_infobar_delegate.cc
similarity index 65%
rename from chrome/browser/ui/android/content_settings/popup_blocked_infobar_delegate.cc
rename to components/blocked_content/android/popup_blocked_infobar_delegate.cc
index 22231f2..0e4d381 100644
--- a/chrome/browser/ui/android/content_settings/popup_blocked_infobar_delegate.cc
+++ b/components/blocked_content/android/popup_blocked_infobar_delegate.cc
@@ -2,61 +2,55 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/android/content_settings/popup_blocked_infobar_delegate.h"
+#include "components/blocked_content/android/popup_blocked_infobar_delegate.h"
 
 #include <stddef.h>
 #include <utility>
 
-#include "chrome/browser/android/android_theme_resources.h"
-#include "chrome/browser/content_settings/chrome_content_settings_utils.h"
-#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
-#include "chrome/browser/infobars/infobar_service.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/grit/generated_resources.h"
 #include "components/blocked_content/popup_blocker_tab_helper.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/content_settings/core/common/content_settings_types.h"
+#include "components/infobars/content/content_infobar_manager.h"
 #include "components/infobars/core/infobar.h"
 #include "components/prefs/pref_service.h"
+#include "components/resources/android/theme_resources.h"
 #include "components/strings/grit/components_strings.h"
 #include "ui/base/l10n/l10n_util.h"
 
+namespace blocked_content {
+
 // static
-void PopupBlockedInfoBarDelegate::Create(content::WebContents* web_contents,
-                                         int num_popups) {
-  const GURL& url = web_contents->GetURL();
-  Profile* profile =
-      Profile::FromBrowserContext(web_contents->GetBrowserContext());
-  InfoBarService* infobar_service =
-      InfoBarService::FromWebContents(web_contents);
+bool PopupBlockedInfoBarDelegate::Create(
+    infobars::ContentInfoBarManager* infobar_manager,
+    int num_popups,
+    HostContentSettingsMap* settings_map,
+    base::OnceClosure on_accept_callback) {
+  const GURL& url = infobar_manager->web_contents()->GetURL();
   std::unique_ptr<infobars::InfoBar> infobar(
-      infobar_service->CreateConfirmInfoBar(
+      infobar_manager->CreateConfirmInfoBar(
           std::unique_ptr<ConfirmInfoBarDelegate>(
-              new PopupBlockedInfoBarDelegate(
-                  num_popups, url,
-                  HostContentSettingsMapFactory::GetForProfile(profile)))));
+              new PopupBlockedInfoBarDelegate(num_popups, url, settings_map,
+                                              std::move(on_accept_callback)))));
 
   // See if there is an existing popup infobar already.
   // TODO(dfalcantara) When triggering more than one popup the infobar
   // will be shown once, then hide then be shown again.
   // This will be fixed once we have an in place replace infobar mechanism.
-  for (size_t i = 0; i < infobar_service->infobar_count(); ++i) {
-    infobars::InfoBar* existing_infobar = infobar_service->infobar_at(i);
+  for (size_t i = 0; i < infobar_manager->infobar_count(); ++i) {
+    infobars::InfoBar* existing_infobar = infobar_manager->infobar_at(i);
     if (existing_infobar->delegate()->AsPopupBlockedInfoBarDelegate()) {
-      infobar_service->ReplaceInfoBar(existing_infobar, std::move(infobar));
-      return;
+      infobar_manager->ReplaceInfoBar(existing_infobar, std::move(infobar));
+      return false;
     }
   }
 
-  infobar_service->AddInfoBar(std::move(infobar));
+  infobar_manager->AddInfoBar(std::move(infobar));
 
-  content_settings::RecordPopupsAction(
-      content_settings::POPUPS_ACTION_DISPLAYED_INFOBAR_ON_MOBILE);
+  return true;
 }
 
-PopupBlockedInfoBarDelegate::~PopupBlockedInfoBarDelegate() {
-}
+PopupBlockedInfoBarDelegate::~PopupBlockedInfoBarDelegate() = default;
 
 infobars::InfoBarDelegate::InfoBarIdentifier
 PopupBlockedInfoBarDelegate::GetIdentifier() const {
@@ -68,15 +62,20 @@
 }
 
 PopupBlockedInfoBarDelegate*
-    PopupBlockedInfoBarDelegate::AsPopupBlockedInfoBarDelegate() {
+PopupBlockedInfoBarDelegate::AsPopupBlockedInfoBarDelegate() {
   return this;
 }
 
 PopupBlockedInfoBarDelegate::PopupBlockedInfoBarDelegate(
     int num_popups,
     const GURL& url,
-    HostContentSettingsMap* map)
-    : ConfirmInfoBarDelegate(), num_popups_(num_popups), url_(url), map_(map) {
+    HostContentSettingsMap* map,
+    base::OnceClosure on_accept_callback)
+    : ConfirmInfoBarDelegate(),
+      num_popups_(num_popups),
+      url_(url),
+      map_(map),
+      on_accept_callback_(std::move(on_accept_callback)) {
   content_settings::SettingInfo setting_info;
   std::unique_ptr<base::Value> setting = map->GetWebsiteSetting(
       url, url, ContentSettingsType::POPUPS, std::string(), &setting_info);
@@ -121,7 +120,7 @@
 
   // Launch popups.
   content::WebContents* web_contents =
-      InfoBarService::WebContentsFromInfoBar(infobar());
+      infobars::ContentInfoBarManager::WebContentsFromInfoBar(infobar());
   blocked_content::PopupBlockerTabHelper* popup_blocker_helper =
       blocked_content::PopupBlockerTabHelper::FromWebContents(web_contents);
   DCHECK(popup_blocker_helper);
@@ -134,7 +133,9 @@
                                            WindowOpenDisposition::CURRENT_TAB);
   }
 
-  content_settings::RecordPopupsAction(
-      content_settings::POPUPS_ACTION_CLICKED_ALWAYS_SHOW_ON_MOBILE);
+  if (on_accept_callback_)
+    std::move(on_accept_callback_).Run();
   return true;
 }
+
+}  // namespace blocked_content
diff --git a/components/blocked_content/android/popup_blocked_infobar_delegate.h b/components/blocked_content/android/popup_blocked_infobar_delegate.h
new file mode 100644
index 0000000..43e3126
--- /dev/null
+++ b/components/blocked_content/android/popup_blocked_infobar_delegate.h
@@ -0,0 +1,61 @@
+// 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.
+
+#ifndef COMPONENTS_BLOCKED_CONTENT_ANDROID_POPUP_BLOCKED_INFOBAR_DELEGATE_H_
+#define COMPONENTS_BLOCKED_CONTENT_ANDROID_POPUP_BLOCKED_INFOBAR_DELEGATE_H_
+
+#include "base/callback.h"
+#include "components/infobars/core/confirm_infobar_delegate.h"
+#include "url/gurl.h"
+
+namespace infobars {
+class ContentInfoBarManager;
+}
+
+class HostContentSettingsMap;
+
+namespace blocked_content {
+
+class PopupBlockedInfoBarDelegate : public ConfirmInfoBarDelegate {
+ public:
+  // Creates a popup blocked infobar and delegate and adds the infobar to
+  // |infobar_manager|. Returns true if the infobar was created, and false if it
+  // replaced an existing popup infobar. |on_accept_callback| will be run if the
+  // accept button is pressed on the infobar.
+  static bool Create(infobars::ContentInfoBarManager* infobar_manager,
+                     int num_popups,
+                     HostContentSettingsMap* settings_map,
+                     base::OnceClosure on_accept_callback);
+
+  ~PopupBlockedInfoBarDelegate() override;
+
+  PopupBlockedInfoBarDelegate(const PopupBlockedInfoBarDelegate&) = delete;
+  PopupBlockedInfoBarDelegate& operator=(const PopupBlockedInfoBarDelegate&) =
+      delete;
+
+ private:
+  PopupBlockedInfoBarDelegate(int num_popups,
+                              const GURL& url,
+                              HostContentSettingsMap* map,
+                              base::OnceClosure on_accept_callback);
+
+  // ConfirmInfoBarDelegate:
+  infobars::InfoBarDelegate::InfoBarIdentifier GetIdentifier() const override;
+  int GetIconId() const override;
+  PopupBlockedInfoBarDelegate* AsPopupBlockedInfoBarDelegate() override;
+  base::string16 GetMessageText() const override;
+  int GetButtons() const override;
+  base::string16 GetButtonLabel(InfoBarButton button) const override;
+  bool Accept() override;
+
+  const int num_popups_;
+  const GURL url_;
+  HostContentSettingsMap* map_;
+  bool can_show_popups_;
+  base::OnceClosure on_accept_callback_;
+};
+
+}  // namespace blocked_content
+
+#endif  // COMPONENTS_BLOCKED_CONTENT_ANDROID_POPUP_BLOCKED_INFOBAR_DELEGATE_H_
diff --git a/components/blocked_content/android/popup_blocked_infobar_delegate_unittest.cc b/components/blocked_content/android/popup_blocked_infobar_delegate_unittest.cc
new file mode 100644
index 0000000..e9d3bdf2
--- /dev/null
+++ b/components/blocked_content/android/popup_blocked_infobar_delegate_unittest.cc
@@ -0,0 +1,133 @@
+// 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/blocked_content/android/popup_blocked_infobar_delegate.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/bind_test_util.h"
+#include "base/test/scoped_feature_list.h"
+#include "components/blocked_content/popup_blocker_tab_helper.h"
+#include "components/blocked_content/safe_browsing_triggered_popup_blocker.h"
+#include "components/blocked_content/test/test_popup_navigation_delegate.h"
+#include "components/content_settings/browser/tab_specific_content_settings.h"
+#include "components/content_settings/browser/test_tab_specific_content_settings_delegate.h"
+#include "components/content_settings/core/browser/host_content_settings_map.h"
+#include "components/infobars/content/content_infobar_manager.h"
+#include "components/infobars/core/infobar.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "content/public/test/test_renderer_host.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace blocked_content {
+namespace {
+constexpr char kPageUrl[] = "http://example_page.test";
+constexpr char kPopupUrl[] = "http://example_popup.test";
+
+class TestInfoBarManager : public infobars::ContentInfoBarManager {
+ public:
+  explicit TestInfoBarManager(content::WebContents* web_contents)
+      : ContentInfoBarManager(web_contents) {}
+
+  // infobars::InfoBarManager:
+  std::unique_ptr<infobars::InfoBar> CreateConfirmInfoBar(
+      std::unique_ptr<ConfirmInfoBarDelegate> delegate) override {
+    return std::make_unique<infobars::InfoBar>(std::move(delegate));
+  }
+};
+
+}  // namespace
+
+class PopupBlockedInfoBarDelegateTest
+    : public content::RenderViewHostTestHarness {
+ public:
+  ~PopupBlockedInfoBarDelegateTest() override {
+    settings_map_->ShutdownOnUIThread();
+  }
+
+  // content::RenderViewHostTestHarness:
+  void SetUp() override {
+    content::RenderViewHostTestHarness::SetUp();
+    // Make sure the SafeBrowsingTriggeredPopupBlocker is not created.
+    feature_list_.InitAndDisableFeature(kAbusiveExperienceEnforce);
+
+    HostContentSettingsMap::RegisterProfilePrefs(pref_service_.registry());
+    settings_map_ = base::MakeRefCounted<HostContentSettingsMap>(
+        &pref_service_, false, false, false, false);
+    content_settings::TabSpecificContentSettings::CreateForWebContents(
+        web_contents(),
+        std::make_unique<
+            content_settings::TestTabSpecificContentSettingsDelegate>(
+            /*prefs=*/nullptr, settings_map_.get()));
+
+    PopupBlockerTabHelper::CreateForWebContents(web_contents());
+    helper_ = PopupBlockerTabHelper::FromWebContents(web_contents());
+    infobar_manager_ = std::make_unique<TestInfoBarManager>(web_contents());
+
+    NavigateAndCommit(GURL(kPageUrl));
+  }
+
+  PopupBlockerTabHelper* helper() { return helper_; }
+
+  TestInfoBarManager* infobar_manager() { return infobar_manager_.get(); }
+
+  HostContentSettingsMap* settings_map() { return settings_map_.get(); }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+  PopupBlockerTabHelper* helper_ = nullptr;
+  sync_preferences::TestingPrefServiceSyncable pref_service_;
+  scoped_refptr<HostContentSettingsMap> settings_map_;
+  std::unique_ptr<TestInfoBarManager> infobar_manager_;
+};
+
+TEST_F(PopupBlockedInfoBarDelegateTest, ReplacesInfobarOnSecondPopup) {
+  EXPECT_TRUE(PopupBlockedInfoBarDelegate::Create(
+      infobar_manager(), 1, settings_map(), base::NullCallback()));
+  EXPECT_EQ(infobar_manager()->infobar_count(), 1u);
+  // First message should not contain "2";
+  EXPECT_FALSE(base::Contains(infobar_manager()
+                                  ->infobar_at(0)
+                                  ->delegate()
+                                  ->AsConfirmInfoBarDelegate()
+                                  ->GetMessageText(),
+                              base::ASCIIToUTF16("2")));
+
+  EXPECT_FALSE(PopupBlockedInfoBarDelegate::Create(
+      infobar_manager(), 2, settings_map(), base::NullCallback()));
+  EXPECT_EQ(infobar_manager()->infobar_count(), 1u);
+  // Second message blocks 2 popups, so should contain "2";
+  EXPECT_TRUE(base::Contains(infobar_manager()
+                                 ->infobar_at(0)
+                                 ->delegate()
+                                 ->AsConfirmInfoBarDelegate()
+                                 ->GetMessageText(),
+                             base::ASCIIToUTF16("2")));
+}
+
+TEST_F(PopupBlockedInfoBarDelegateTest, ShowsBlockedPopups) {
+  TestPopupNavigationDelegate::ResultHolder result;
+  helper()->AddBlockedPopup(
+      std::make_unique<TestPopupNavigationDelegate>(GURL(kPopupUrl), &result),
+      blink::mojom::WindowFeatures(), PopupBlockType::kNoGesture);
+  bool on_accept_called = false;
+  EXPECT_TRUE(PopupBlockedInfoBarDelegate::Create(
+      infobar_manager(), 1, settings_map(),
+      base::BindLambdaForTesting(
+          [&on_accept_called] { on_accept_called = true; })));
+  EXPECT_FALSE(on_accept_called);
+
+  EXPECT_TRUE(infobar_manager()
+                  ->infobar_at(0)
+                  ->delegate()
+                  ->AsConfirmInfoBarDelegate()
+                  ->Accept());
+  EXPECT_TRUE(result.did_navigate);
+  EXPECT_TRUE(on_accept_called);
+  EXPECT_EQ(settings_map()->GetContentSetting(GURL(kPageUrl), GURL(kPageUrl),
+                                              ContentSettingsType::POPUPS,
+                                              std::string()),
+            CONTENT_SETTING_ALLOW);
+}
+
+}  // namespace blocked_content
diff --git a/chrome/android/java/res/drawable-hdpi/infobar_blocked_popups.png b/components/blocked_content/android/res/drawable-hdpi/infobar_blocked_popups.png
similarity index 100%
rename from chrome/android/java/res/drawable-hdpi/infobar_blocked_popups.png
rename to components/blocked_content/android/res/drawable-hdpi/infobar_blocked_popups.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/infobar_blocked_popups.png b/components/blocked_content/android/res/drawable-mdpi/infobar_blocked_popups.png
similarity index 100%
rename from chrome/android/java/res/drawable-mdpi/infobar_blocked_popups.png
rename to components/blocked_content/android/res/drawable-mdpi/infobar_blocked_popups.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/infobar_blocked_popups.png b/components/blocked_content/android/res/drawable-xhdpi/infobar_blocked_popups.png
similarity index 100%
rename from chrome/android/java/res/drawable-xhdpi/infobar_blocked_popups.png
rename to components/blocked_content/android/res/drawable-xhdpi/infobar_blocked_popups.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/infobar_blocked_popups.png b/components/blocked_content/android/res/drawable-xxhdpi/infobar_blocked_popups.png
similarity index 100%
rename from chrome/android/java/res/drawable-xxhdpi/infobar_blocked_popups.png
rename to components/blocked_content/android/res/drawable-xxhdpi/infobar_blocked_popups.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/infobar_blocked_popups.png b/components/blocked_content/android/res/drawable-xxxhdpi/infobar_blocked_popups.png
similarity index 100%
rename from chrome/android/java/res/drawable-xxxhdpi/infobar_blocked_popups.png
rename to components/blocked_content/android/res/drawable-xxxhdpi/infobar_blocked_popups.png
Binary files differ
diff --git a/components/blocked_content/popup_blocker_tab_helper_unittest.cc b/components/blocked_content/popup_blocker_tab_helper_unittest.cc
index 43b9305..1eedfe04 100644
--- a/components/blocked_content/popup_blocker_tab_helper_unittest.cc
+++ b/components/blocked_content/popup_blocker_tab_helper_unittest.cc
@@ -7,6 +7,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "components/blocked_content/popup_navigation_delegate.h"
 #include "components/blocked_content/safe_browsing_triggered_popup_blocker.h"
+#include "components/blocked_content/test/test_popup_navigation_delegate.h"
 #include "components/blocked_content/url_list_manager.h"
 #include "components/content_settings/browser/tab_specific_content_settings.h"
 #include "components/content_settings/browser/test_tab_specific_content_settings_delegate.h"
@@ -42,46 +43,6 @@
   std::map<int32_t, GURL> blocked_urls_;
   ScopedObserver<UrlListManager, UrlListManager::Observer> observer_{this};
 };
-
-// Struct used to hold results from calls on TestPopupNavigationDelegate since
-// the delegate will be destroyed on calls to ShowBlockedPopup().
-struct ResultHolder {
-  bool did_navigate = false;
-  blink::mojom::WindowFeatures navigation_window_features;
-  base::Optional<WindowOpenDisposition> navigation_disposition;
-  int total_popups_blocked_on_page = 0;
-};
-
-// Test delegate which stores results of calls in a ResultHolder.
-class TestPopupNavigationDelegate : public PopupNavigationDelegate {
- public:
-  explicit TestPopupNavigationDelegate(const GURL& url,
-                                       ResultHolder* result_holder)
-      : url_(url), result_holder_(result_holder) {}
-
-  // PopupNavigationDelegate:
-  content::RenderFrameHost* GetOpener() override { return nullptr; }
-  bool GetOriginalUserGesture() override { return false; }
-  const GURL& GetURL() override { return url_; }
-
-  NavigateResult NavigateWithGesture(
-      const blink::mojom::WindowFeatures& window_features,
-      base::Optional<WindowOpenDisposition> updated_disposition) override {
-    result_holder_->did_navigate = true;
-    result_holder_->navigation_window_features = window_features;
-    result_holder_->navigation_disposition = updated_disposition;
-    return NavigateResult();
-  }
-
-  void OnPopupBlocked(content::WebContents* web_contents,
-                      int total_popups_blocked_on_page) override {
-    result_holder_->total_popups_blocked_on_page = total_popups_blocked_on_page;
-  }
-
- private:
-  const GURL url_;
-  ResultHolder* result_holder_;
-};
 }  // namespace
 
 class PopupBlockerTabHelperTest : public content::RenderViewHostTestHarness {
@@ -118,7 +79,7 @@
 
 TEST_F(PopupBlockerTabHelperTest, BlocksAndShowsPopup) {
   BlockedUrlListObserver observer(helper());
-  ResultHolder result;
+  TestPopupNavigationDelegate::ResultHolder result;
   blink::mojom::WindowFeatures window_features;
   window_features.has_x = true;
   helper()->AddBlockedPopup(
@@ -138,7 +99,7 @@
 
 TEST_F(PopupBlockerTabHelperTest, MultiplePopups) {
   BlockedUrlListObserver observer(helper());
-  ResultHolder result1;
+  TestPopupNavigationDelegate::ResultHolder result1;
   helper()->AddBlockedPopup(
       std::make_unique<TestPopupNavigationDelegate>(GURL(kUrl1), &result1),
       blink::mojom::WindowFeatures(), PopupBlockType::kNoGesture);
@@ -147,7 +108,7 @@
               UnorderedElementsAre(Pair(0, GURL(kUrl1))));
   EXPECT_EQ(helper()->GetBlockedPopupsCount(), 1u);
 
-  ResultHolder result2;
+  TestPopupNavigationDelegate::ResultHolder result2;
   helper()->AddBlockedPopup(
       std::make_unique<TestPopupNavigationDelegate>(GURL(kUrl2), &result2),
       blink::mojom::WindowFeatures(), PopupBlockType::kNoGesture);
@@ -170,7 +131,7 @@
 }
 
 TEST_F(PopupBlockerTabHelperTest, DoesNotShowPopupWithInvalidID) {
-  ResultHolder result;
+  TestPopupNavigationDelegate::ResultHolder result;
   helper()->AddBlockedPopup(
       std::make_unique<TestPopupNavigationDelegate>(GURL(kUrl1), &result),
       blink::mojom::WindowFeatures(), PopupBlockType::kNoGesture);
@@ -192,7 +153,7 @@
           web_contents());
   EXPECT_FALSE(content_settings->IsContentBlocked(ContentSettingsType::POPUPS));
 
-  ResultHolder result;
+  TestPopupNavigationDelegate::ResultHolder result;
   helper()->AddBlockedPopup(
       std::make_unique<TestPopupNavigationDelegate>(GURL(kUrl1), &result),
       blink::mojom::WindowFeatures(), PopupBlockType::kNoGesture);
@@ -211,7 +172,7 @@
 }
 
 TEST_F(PopupBlockerTabHelperTest, ClearsContentSettingsPopupStateOnNavigation) {
-  ResultHolder result;
+  TestPopupNavigationDelegate::ResultHolder result;
   helper()->AddBlockedPopup(
       std::make_unique<TestPopupNavigationDelegate>(GURL(kUrl1), &result),
       blink::mojom::WindowFeatures(), PopupBlockType::kNoGesture);
diff --git a/components/blocked_content/safe_browsing_triggered_popup_blocker_unittest.cc b/components/blocked_content/safe_browsing_triggered_popup_blocker_unittest.cc
index c593481..8277998 100644
--- a/components/blocked_content/safe_browsing_triggered_popup_blocker_unittest.cc
+++ b/components/blocked_content/safe_browsing_triggered_popup_blocker_unittest.cc
@@ -17,6 +17,7 @@
 #include "components/blocked_content/popup_blocker.h"
 #include "components/blocked_content/popup_blocker_tab_helper.h"
 #include "components/blocked_content/popup_navigation_delegate.h"
+#include "components/blocked_content/test/test_popup_navigation_delegate.h"
 #include "components/content_settings/browser/tab_specific_content_settings.h"
 #include "components/content_settings/browser/test_tab_specific_content_settings_delegate.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
@@ -46,24 +47,6 @@
 const char kNumBlockedHistogram[] =
     "ContentSettings.Popups.StrongBlocker.NumBlocked";
 
-class TestPopupNavigationDelegate : public PopupNavigationDelegate {
- public:
-  explicit TestPopupNavigationDelegate(const GURL& url) : url_(url) {}
-  content::RenderFrameHost* GetOpener() override { return nullptr; }
-  bool GetOriginalUserGesture() override { return true; }
-  const GURL& GetURL() override { return url_; }
-  NavigateResult NavigateWithGesture(
-      const blink::mojom::WindowFeatures& window_features,
-      base::Optional<WindowOpenDisposition> updated_disposition) override {
-    return NavigateResult{nullptr, WindowOpenDisposition::UNKNOWN};
-  }
-  void OnPopupBlocked(content::WebContents* web_contents,
-                      int num_blocked) override {}
-
- private:
-  const GURL url_;
-};
-
 class SafeBrowsingTriggeredPopupBlockerTest
     : public content::RenderViewHostTestHarness,
       public subresource_filter::SubresourceFilterClient {
@@ -245,7 +228,8 @@
       blink::TriggeringEventInfo::kFromUntrustedEvent;
 
   MaybeBlockPopup(web_contents(), nullptr,
-                  std::make_unique<TestPopupNavigationDelegate>(popup_url),
+                  std::make_unique<TestPopupNavigationDelegate>(
+                      popup_url, nullptr /* result_holder */),
                   &params, blink::mojom::WindowFeatures(), settings_map());
 
   EXPECT_EQ(1u, PopupBlockerTabHelper::FromWebContents(web_contents())
@@ -269,7 +253,8 @@
   params.triggering_event_info = blink::TriggeringEventInfo::kFromTrustedEvent;
 
   MaybeBlockPopup(web_contents(), nullptr,
-                  std::make_unique<TestPopupNavigationDelegate>(popup_url),
+                  std::make_unique<TestPopupNavigationDelegate>(
+                      popup_url, nullptr /* result_holder */),
                   &params, blink::mojom::WindowFeatures(), settings_map());
 
   EXPECT_EQ(0u, PopupBlockerTabHelper::FromWebContents(web_contents())
diff --git a/components/blocked_content/test/test_popup_navigation_delegate.cc b/components/blocked_content/test/test_popup_navigation_delegate.cc
new file mode 100644
index 0000000..046d6ff
--- /dev/null
+++ b/components/blocked_content/test/test_popup_navigation_delegate.cc
@@ -0,0 +1,49 @@
+// 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/blocked_content/test/test_popup_navigation_delegate.h"
+
+namespace blocked_content {
+
+TestPopupNavigationDelegate::ResultHolder::ResultHolder() = default;
+
+TestPopupNavigationDelegate::ResultHolder::~ResultHolder() = default;
+
+TestPopupNavigationDelegate::TestPopupNavigationDelegate(
+    const GURL& url,
+    ResultHolder* result_holder)
+    : url_(url), result_holder_(result_holder) {}
+
+content::RenderFrameHost* TestPopupNavigationDelegate::GetOpener() {
+  return nullptr;
+}
+
+bool TestPopupNavigationDelegate::GetOriginalUserGesture() {
+  return true;
+}
+
+const GURL& TestPopupNavigationDelegate::GetURL() {
+  return url_;
+}
+
+PopupNavigationDelegate::NavigateResult
+TestPopupNavigationDelegate::NavigateWithGesture(
+    const blink::mojom::WindowFeatures& window_features,
+    base::Optional<WindowOpenDisposition> updated_disposition) {
+  if (result_holder_) {
+    result_holder_->did_navigate = true;
+    result_holder_->navigation_window_features = window_features;
+    result_holder_->navigation_disposition = updated_disposition;
+  }
+  return NavigateResult();
+}
+
+void TestPopupNavigationDelegate::OnPopupBlocked(
+    content::WebContents* web_contents,
+    int total_popups_blocked_on_page) {
+  if (result_holder_)
+    result_holder_->total_popups_blocked_on_page = total_popups_blocked_on_page;
+}
+
+}  // namespace blocked_content
diff --git a/components/blocked_content/test/test_popup_navigation_delegate.h b/components/blocked_content/test/test_popup_navigation_delegate.h
new file mode 100644
index 0000000..8da7926
--- /dev/null
+++ b/components/blocked_content/test/test_popup_navigation_delegate.h
@@ -0,0 +1,48 @@
+// 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_BLOCKED_CONTENT_TEST_TEST_POPUP_NAVIGATION_DELEGATE_H_
+#define COMPONENTS_BLOCKED_CONTENT_TEST_TEST_POPUP_NAVIGATION_DELEGATE_H_
+
+#include "components/blocked_content/popup_navigation_delegate.h"
+#include "third_party/blink/public/mojom/window_features/window_features.mojom.h"
+#include "url/gurl.h"
+
+namespace blocked_content {
+
+// Test delegate which stores results of calls in a ResultHolder.
+class TestPopupNavigationDelegate : public PopupNavigationDelegate {
+ public:
+  // Struct used to hold results from calls on TestPopupNavigationDelegate since
+  // the delegate will be destroyed on calls to ShowBlockedPopup().
+  struct ResultHolder {
+    ResultHolder();
+    ~ResultHolder();
+
+    bool did_navigate = false;
+    blink::mojom::WindowFeatures navigation_window_features;
+    base::Optional<WindowOpenDisposition> navigation_disposition;
+    int total_popups_blocked_on_page = 0;
+  };
+
+  TestPopupNavigationDelegate(const GURL& url, ResultHolder* result_holder);
+
+  // PopupNavigationDelegate:
+  content::RenderFrameHost* GetOpener() override;
+  bool GetOriginalUserGesture() override;
+  const GURL& GetURL() override;
+  NavigateResult NavigateWithGesture(
+      const blink::mojom::WindowFeatures& window_features,
+      base::Optional<WindowOpenDisposition> updated_disposition) override;
+  void OnPopupBlocked(content::WebContents* web_contents,
+                      int total_popups_blocked_on_page) override;
+
+ private:
+  const GURL url_;
+  ResultHolder* result_holder_;
+};
+
+}  // namespace blocked_content
+
+#endif  // COMPONENTS_BLOCKED_CONTENT_TEST_TEST_POPUP_NAVIGATION_DELEGATE_H_
diff --git a/components/blocked_content_strings.grdp b/components/blocked_content_strings.grdp
new file mode 100644
index 0000000..799db71
--- /dev/null
+++ b/components/blocked_content_strings.grdp
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <if expr="is_android">
+    <message name="IDS_POPUPS_BLOCKED_INFOBAR_BUTTON_SHOW" desc="Pop-up Blocking Show Button [CHAR-LIMIT=32]">
+      Always show
+    </message>
+    <message name="IDS_POPUPS_BLOCKED_INFOBAR_TEXT" desc="Pop-up Blocking Title [CHAR-LIMIT=32] [ICU Syntax]">
+      {NUM_POPUPS,plural,=1{Pop-up blocked} other{# pop-ups blocked}}
+    </message>
+  </if>
+</grit-part>
diff --git a/components/components_strings.grd b/components/components_strings.grd
index b88e67d..7e0e9d6 100644
--- a/components/components_strings.grd
+++ b/components/components_strings.grd
@@ -282,6 +282,7 @@
       <part file="autofill_strings.grdp" />
       <part file="bookmark_bar_strings.grdp" />
       <part file="bookmark_component_strings.grdp" />
+      <part file="blocked_content_strings.grdp" />
       <part file="browsing_data_strings.grdp" />
       <part file="components_settings_strings.grdp" />
       <part file="crash_strings.grdp" />
diff --git a/components/infobars/core/infobar_delegate.cc b/components/infobars/core/infobar_delegate.cc
index 4f829ad..2c5e77a 100644
--- a/components/infobars/core/infobar_delegate.cc
+++ b/components/infobars/core/infobar_delegate.cc
@@ -100,7 +100,8 @@
   return nullptr;
 }
 
-PopupBlockedInfoBarDelegate* InfoBarDelegate::AsPopupBlockedInfoBarDelegate() {
+blocked_content::PopupBlockedInfoBarDelegate*
+InfoBarDelegate::AsPopupBlockedInfoBarDelegate() {
   return nullptr;
 }
 
diff --git a/components/infobars/core/infobar_delegate.h b/components/infobars/core/infobar_delegate.h
index cc6ac57..f0ff594 100644
--- a/components/infobars/core/infobar_delegate.h
+++ b/components/infobars/core/infobar_delegate.h
@@ -13,9 +13,12 @@
 
 class ConfirmInfoBarDelegate;
 class HungRendererInfoBarDelegate;
-class PopupBlockedInfoBarDelegate;
 class ThemeInstalledInfoBarDelegate;
 
+namespace blocked_content {
+class PopupBlockedInfoBarDelegate;
+}
+
 #if defined(OS_ANDROID)
 namespace offline_pages {
 class OfflinePageInfoBarDelegate;
@@ -256,7 +259,8 @@
   // Type-checking downcast routines:
   virtual ConfirmInfoBarDelegate* AsConfirmInfoBarDelegate();
   virtual HungRendererInfoBarDelegate* AsHungRendererInfoBarDelegate();
-  virtual PopupBlockedInfoBarDelegate* AsPopupBlockedInfoBarDelegate();
+  virtual blocked_content::PopupBlockedInfoBarDelegate*
+  AsPopupBlockedInfoBarDelegate();
   virtual ThemeInstalledInfoBarDelegate* AsThemePreviewInfobarDelegate();
   virtual translate::TranslateInfoBarDelegate* AsTranslateInfoBarDelegate();
 #if defined(OS_ANDROID)
diff --git a/components/resources/BUILD.gn b/components/resources/BUILD.gn
index 49a0480..a5dc084e 100644
--- a/components/resources/BUILD.gn
+++ b/components/resources/BUILD.gn
@@ -108,6 +108,7 @@
 if (is_android) {
   source_set("android_resources") {
     sources = [
+      "android/blocked_content_resource_id.h",
       "android/page_info_resource_id.h",
       "android/permissions_resource_id.h",
       "android/theme_resources.h",
diff --git a/components/resources/android/blocked_content_resource_id.h b/components/resources/android/blocked_content_resource_id.h
new file mode 100644
index 0000000..aa1e9cd
--- /dev/null
+++ b/components/resources/android/blocked_content_resource_id.h
@@ -0,0 +1,27 @@
+// 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.
+
+// This file maps resource IDs to Android resource IDs.
+
+// Presence of regular include guards is checked by:
+// 1. cpplint
+// 2. a custom presubmit in src/PRESUBMIT.py
+// 3. clang (but it only checks the guard is correct if present)
+// Disable the first two with these magic comments:
+// NOLINT(build/header_guard)
+// no-include-guard-because-multiply-included
+
+// LINK_RESOURCE_ID is used for IDs that come from a .grd file.
+#ifndef LINK_RESOURCE_ID
+#error "LINK_RESOURCE_ID should be defined before including this file"
+#endif
+// DECLARE_RESOURCE_ID is used for IDs that don't have .grd entries, and
+// are only declared in this file.
+#ifndef DECLARE_RESOURCE_ID
+#error "DECLARE_RESOURCE_ID should be defined before including this file"
+#endif
+
+// InfoBar resources.
+DECLARE_RESOURCE_ID(IDR_ANDROID_INFOBAR_BLOCKED_POPUPS,
+                    R.drawable.infobar_blocked_popups)
diff --git a/components/resources/android/theme_resources.h b/components/resources/android/theme_resources.h
index a84712d..b48a7a5 100644
--- a/components/resources/android/theme_resources.h
+++ b/components/resources/android/theme_resources.h
@@ -14,6 +14,7 @@
   // Not used; just provides a starting value for the enum. These must
   // not conflict with IDR_* values, which top out at 2^16 - 1.
   ANDROID_COMPONENTS_RESOURCE_ID_NONE = 1 << 16,
+#include "components/resources/android/blocked_content_resource_id.h"
 #include "components/resources/android/page_info_resource_id.h"
 #include "components/resources/android/permissions_resource_id.h"
   ANDROID_COMPONENTS_RESOURCE_ID_MAX,
diff --git a/weblayer/browser/android/resource_mapper.cc b/weblayer/browser/android/resource_mapper.cc
index 24862f4..ae5bcdf 100644
--- a/weblayer/browser/android/resource_mapper.cc
+++ b/weblayer/browser/android/resource_mapper.cc
@@ -38,6 +38,7 @@
   (*GetIdMap())[c_id] = resource_id_list[next_id++];
 #define DECLARE_RESOURCE_ID(c_id, java_id) \
   (*GetIdMap())[c_id] = resource_id_list[next_id++];
+#include "components/resources/android/blocked_content_resource_id.h"
 #include "components/resources/android/page_info_resource_id.h"
 #include "components/resources/android/permissions_resource_id.h"
 #undef LINK_RESOURCE_ID
diff --git a/weblayer/browser/java/BUILD.gn b/weblayer/browser/java/BUILD.gn
index 35f7b72f..0520956 100644
--- a/weblayer/browser/java/BUILD.gn
+++ b/weblayer/browser/java/BUILD.gn
@@ -26,6 +26,7 @@
   custom_package = "org.chromium.weblayer_private"
   deps = [
     ":weblayer_strings_grd",
+    "//components/blocked_content/android:java_resources",
     "//components/browser_ui/http_auth/android:java_resources",
     "//components/browser_ui/settings/android:java_resources",
     "//components/browser_ui/site_settings/android:java_resources",
@@ -47,6 +48,7 @@
   sources = [ "ResourceId.template" ]
   package_path = "org/chromium/weblayer_private/resources"
   inputs = [
+    "//components/resources/android/blocked_content_resource_id.h",
     "//components/resources/android/page_info_resource_id.h",
     "//components/resources/android/permissions_resource_id.h",
   ]
diff --git a/weblayer/browser/java/ResourceId.template b/weblayer/browser/java/ResourceId.template
index 0f5352d..1e16c782 100644
--- a/weblayer/browser/java/ResourceId.template
+++ b/weblayer/browser/java/ResourceId.template
@@ -11,6 +11,7 @@
         int[] resourceList = {
 #define LINK_RESOURCE_ID(c_id,java_id) java_id,
 #define DECLARE_RESOURCE_ID(c_id,java_id) java_id,
+#include "components/resources/android/blocked_content_resource_id.h"
 #include "components/resources/android/page_info_resource_id.h"
 #include "components/resources/android/permissions_resource_id.h"
         };
diff --git a/weblayer/browser/popup_navigation_delegate_impl.cc b/weblayer/browser/popup_navigation_delegate_impl.cc
index 3dccded..01a9176 100644
--- a/weblayer/browser/popup_navigation_delegate_impl.cc
+++ b/weblayer/browser/popup_navigation_delegate_impl.cc
@@ -4,7 +4,14 @@
 
 #include "weblayer/browser/popup_navigation_delegate_impl.h"
 
+#include "build/build_config.h"
 #include "content/public/browser/web_contents.h"
+#include "weblayer/browser/host_content_settings_map_factory.h"
+#include "weblayer/browser/infobar_service.h"
+
+#if defined(OS_ANDROID)
+#include "components/blocked_content/android/popup_blocked_infobar_delegate.h"
+#endif
 
 namespace weblayer {
 
@@ -48,8 +55,15 @@
 
 void PopupNavigationDelegateImpl::OnPopupBlocked(
     content::WebContents* web_contents,
-    int num_blocked) {
-  // TODO(crbug.com/1019922): Add popup blocked infobar.
+    int total_popups_blocked_on_page) {
+#if defined(OS_ANDROID)
+  blocked_content::PopupBlockedInfoBarDelegate::Create(
+      InfoBarService::FromWebContents(web_contents),
+      total_popups_blocked_on_page,
+      HostContentSettingsMapFactory::GetForBrowserContext(
+          web_contents->GetBrowserContext()),
+      base::NullCallback());
+#endif
 }
 
 }  // namespace weblayer
diff --git a/weblayer/grit_strings_whitelist.txt b/weblayer/grit_strings_whitelist.txt
index 2f98d6b..a39bfca 100644
--- a/weblayer/grit_strings_whitelist.txt
+++ b/weblayer/grit_strings_whitelist.txt
@@ -207,6 +207,8 @@
 IDS_PAGE_INFO_USB_DEVICE_SECONDARY_LABEL
 IDS_PERMISSION_ALLOW
 IDS_PERMISSION_DENY
+IDS_POPUPS_BLOCKED_INFOBAR_BUTTON_SHOW
+IDS_POPUPS_BLOCKED_INFOBAR_TEXT
 IDS_PROTECTED_MEDIA_IDENTIFIER_PERMISSION_FRAGMENT
 IDS_PROTECTED_MEDIA_IDENTIFIER_PER_DEVICE_PROVISIONING_INFOBAR_TEXT
 IDS_PROTECTED_MEDIA_IDENTIFIER_PER_ORIGIN_PROVISIONING_INFOBAR_TEXT