Move the supervised user error page to a component

This is a refactoring to allow WebView to use the supervised user error
page.

BUG=594973

Review URL: https://codereview.chromium.org/1808653003

Cr-Commit-Position: refs/heads/master@{#382015}
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 43c4bfee..51b7f24 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -7659,88 +7659,8 @@
       </message>
 
       <!-- Supervised User Block Interstitial data -->
-      <message name="IDS_BLOCK_INTERSTITIAL_TITLE" desc="A title for the supervised-user block interstitial page.">
-        Page blocked
-      </message>
-      <message name="IDS_BLOCK_INTERSTITIAL_MESSAGE" desc="A message for the supervised user when they attempt to visit a site that is not permitted by the manager.">
-        Oops! You need permission from <ph name="NAME">$1<ex>John Doe</ex></ph> to access this page.
-      </message>
-      <message name="IDS_CHILD_BLOCK_INTERSTITIAL_MESSAGE_SINGLE_PARENT" desc="A message for the child user when they attempt to visit a site that is not permitted by their parent.">
-        Oops! You need to ask your parent if it's OK to visit this page.
-      </message>
-      <message name="IDS_CHILD_BLOCK_INTERSTITIAL_MESSAGE_MULTI_PARENT" desc="A message for the child user when they attempt to visit a site that is not permitted by their parents.">
-        Oops! You need to ask your parents if it's OK to visit this page.
-      </message>
-      <message name="IDS_BLOCK_INTERSTITIAL_MESSAGE_ACCESS_REQUESTS_DISABLED" desc="A message for the supervised user when they attempt to visit a site that is not permitted by the manager (and they can't ask for permission).">
-        Oops, looks like you don't have permission to access this page.
-      </message>
-      <message name="IDS_BACK_BUTTON" desc="A button for going back to the last safe url after being blocked.">
-        Go back
-      </message>
-      <message name="IDS_BLOCK_INTERSTITIAL_REQUEST_ACCESS_BUTTON" desc="A button for requesting access to blocked sites.">
-        Ask permission
-      </message>
-      <message name="IDS_CHILD_BLOCK_INTERSTITIAL_REQUEST_ACCESS_BUTTON" desc="A button for requesting access to blocked sites.">
-        Ask permission
-      </message>
-      <message name="IDS_BLOCK_INTERSTITIAL_REQUEST_SENT_MESSAGE" desc="The text that tells the supervised user that a request has been sent.">
-        Your request to access this site has been sent to <ph name="NAME">$1<ex>John Doe</ex></ph>.
-      </message>
-      <message name="IDS_BLOCK_INTERSTITIAL_REQUEST_FAILED_MESSAGE" desc="The text that tells the supervised user that a request could not be sent.">
-        Your request to access this site could not be sent to <ph name="NAME">$1<ex>John Doe</ex></ph>. Please try again.
-      </message>
-      <message name="IDS_CHILD_BLOCK_INTERSTITIAL_REQUEST_SENT_MESSAGE_SINGLE_PARENT" desc="The text that tells the child user that a request has been sent to his/her parent.">
-        You asked your parent if it's ok to visit this page.
-      </message>
-      <message name="IDS_CHILD_BLOCK_INTERSTITIAL_REQUEST_SENT_MESSAGE_MULTI_PARENT" desc="The text that tells the child user that a request has been sent to his/her parents.">
-        You asked your parents if it's ok to visit this page.
-      </message>
-      <message name="IDS_CHILD_BLOCK_INTERSTITIAL_REQUEST_FAILED_MESSAGE_SINGLE_PARENT" desc="The text that tells the child user that a request to his/her parent failed.">
-        We could not reach your parent at the moment. Please try again.
-      </message>
-      <message name="IDS_CHILD_BLOCK_INTERSTITIAL_REQUEST_FAILED_MESSAGE_MULTI_PARENT" desc="The text that tells the child user that a request to his/her parents failed.">
-        We could not reach your parents at the moment. Please try again.
-      </message>
-      <message name="IDS_CHILD_BLOCK_MESSAGE_DEFAULT_SINGLE_PARENT" desc="Message to be shown when a site was blocked due to the default fallback behavior and the child has one parent.">
-        You're seeing this message because your parent needs to approve new sites on your first visit.
-      </message>
-      <message name="IDS_CHILD_BLOCK_MESSAGE_DEFAULT_MULTI_PARENT" desc="Message to be shown when a site was blocked due to the default fallback behavior and the child has two parents.">
-        You're seeing this message because your parents need to approve new sites on your first visit.
-      </message>
-      <message name="IDS_SUPERVISED_USER_BLOCK_MESSAGE_DEFAULT" desc="Message to be show to a supervised user when a site was blocked due to the default fallback behaviour.">
-        You're seeing this message because your manager needs to approve new sites on your first visit.
-      </message>
-      <message name="IDS_SUPERVISED_USER_BLOCK_HEADER_DEFAULT" desc="Header for the message to be shown when a site was blocked due to the default fallback behaviour.">
-        Not approved
-      </message>
-      <message name="IDS_SUPERVISED_USER_BLOCK_MESSAGE_SAFE_SITES" desc="Message to be shown when a site was blocked due to the SafeSites safety check.">
-        You're seeing this message because Google SafeSites is enabled.
-      </message>
-      <message name="IDS_SUPERVISED_USER_BLOCK_HEADER_SAFE_SITES" desc="Header for the message to be shown when a site was blocked due to the SafeSites safety check.">
-        SafeSites
-      </message>
-      <message name="IDS_CHILD_BLOCK_MESSAGE_MANUAL_SINGLE_PARENT" desc="Message to be shown to a child when a site was blocked due to a manual blacklist entry and the child has one parent.">
-        You're seeing this message because your parent blocked this site.
-      </message>
-      <message name="IDS_CHILD_BLOCK_MESSAGE_MANUAL_MULTI_PARENT" desc="Message to be shown to a child when a site was blocked due to a manual blacklist entry and the child has two parents.">
-        You're seeing this message because one of your parents blocked this site.
-      </message>
-      <message name="IDS_SUPERVISED_USER_BLOCK_MESSAGE_MANUAL" desc="Message to be shown to a supervised user when a site is blocked due to a manual blacklist entry">
-        You're seeing this message because your manager blocked this site.
-      </message>
-      <message name="IDS_SUPERVISED_USER_BLOCK_HEADER_MANUAL" desc="Header for the message to be shown when a site was blocked due to a manual blacklist entry.">
-        Blocked
-      </message>
-      <message name="IDS_BLOCK_INTERSTITIAL_SEND_FEEDBACK" desc="The text for a link to submit feedback about a blocked site.">
-        Was this unexpected? <ph name="BEGIN_LINK">&lt;a is="action-link" id="feedback-link"&gt;</ph>Let us know<ph name="END_LINK">&lt;/a&gt;</ph>
-      </message>
-      <message name="IDS_BLOCK_INTERSTITIAL_SHOW_DETAILS" desc="The text for the link to show details about the interstitial.">
-        Details
-      </message>
-      <message name="IDS_BLOCK_INTERSTITIAL_HIDE_DETAILS" desc="The text for the link to hide details about the interstitial.">
-        Hide details
-      </message>
-      <message name="IDS_BLOCK_INTERSTITIAL_DEFAULT_FEEDBACK_TEXT" desc="The default text for feedback about a blocked site.">
+
+       <message name="IDS_BLOCK_INTERSTITIAL_DEFAULT_FEEDBACK_TEXT" desc="The default text for feedback about a blocked site.">
         <ph name="REASON">$1<ex>This site was blocked due to the SafeSites filter.</ex></ph>
 I don't think this site should be blocked!
       </message>
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index bb1bdb65..e1264ba 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -170,6 +170,7 @@
     "//components/startup_metric_utils/common",
     "//components/strings",
     "//components/suggestions",
+    "//components/supervised_user_error_page",
     "//components/sync_bookmarks",
     "//components/sync_driver",
     "//components/sync_sessions",
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index 9f2f302..a096a38 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -417,7 +417,6 @@
         <include name="IDR_MD_USER_MANAGER_PAGES_JS" file="resources\md_user_manager\user_manager_pages.js" type="BINDATA" />
         <include name="IDR_MD_USER_MANAGER_STRINGS_HTML" file="resources\md_user_manager\strings.html" flattenhtml="true" type="BINDATA" />
       </if>
-      <include name="IDR_SUPERVISED_USER_BLOCK_INTERSTITIAL_HTML" file="resources\supervised_user_block_interstitial.html" flattenhtml="true" type="BINDATA" />
       <include name="IDR_PROFILE_SIGNIN_CONFIRMATION_HTML" file="resources\profile_signin_confirmation.html" type="BINDATA" />
       <include name="IDR_PROFILE_SIGNIN_CONFIRMATION_JS" file="resources\profile_signin_confirmation.js" type="BINDATA" />
       <include name="IDR_PROFILE_SIGNIN_CONFIRMATION_CSS" file="resources\profile_signin_confirmation.css" type="BINDATA" />
diff --git a/chrome/browser/supervised_user/supervised_user_content_provider_android.cc b/chrome/browser/supervised_user/supervised_user_content_provider_android.cc
index d5881a2..bc5cc90 100644
--- a/chrome/browser/supervised_user/supervised_user_content_provider_android.cc
+++ b/chrome/browser/supervised_user/supervised_user_content_provider_android.cc
@@ -103,7 +103,7 @@
 void SupervisedUserContentProvider::OnQueryComplete(
     ScopedJavaGlobalRef<jobject> query_reply_jobj,
     SupervisedUserURLFilter::FilteringBehavior behavior,
-    SupervisedUserURLFilter::FilteringBehaviorReason reason,
+    supervised_user_error_page::FilteringBehaviorReason reason,
     bool /* uncertain */) {
   if (behavior != SupervisedUserURLFilter::BLOCK) {
     Java_SupervisedUserQueryReply_onQueryComplete(
diff --git a/chrome/browser/supervised_user/supervised_user_content_provider_android.h b/chrome/browser/supervised_user/supervised_user_content_provider_android.h
index d1b6431a..8f1ca059 100644
--- a/chrome/browser/supervised_user/supervised_user_content_provider_android.h
+++ b/chrome/browser/supervised_user/supervised_user_content_provider_android.h
@@ -10,6 +10,7 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/supervised_user/supervised_user_url_filter.h"
+#include "components/supervised_user_error_page/supervised_user_error_page.h"
 
 class SupervisedUserService;
 
@@ -39,7 +40,7 @@
   void OnQueryComplete(
       base::android::ScopedJavaGlobalRef<jobject> query_reply_jobj,
       SupervisedUserURLFilter::FilteringBehavior behavior,
-      SupervisedUserURLFilter::FilteringBehaviorReason reason,
+      supervised_user_error_page::FilteringBehaviorReason reason,
       bool /* uncertain */);
   void OnInsertRequestSendComplete(
       base::android::ScopedJavaGlobalRef<jobject> insert_reply_jobj,
diff --git a/chrome/browser/supervised_user/supervised_user_interstitial.cc b/chrome/browser/supervised_user/supervised_user_interstitial.cc
index 5e04dd2..a0b16d3 100644
--- a/chrome/browser/supervised_user/supervised_user_interstitial.cc
+++ b/chrome/browser/supervised_user/supervised_user_interstitial.cc
@@ -53,17 +53,6 @@
 
 namespace {
 
-static const int kAvatarSize1x = 45;
-static const int kAvatarSize2x = 90;
-
-std::string BuildAvatarImageUrl(const std::string& url, int size) {
-  std::string result = url;
-  size_t slash = result.rfind('/');
-  if (slash != std::string::npos)
-    result.insert(slash, "/s" + base::IntToString(size));
-  return result;
-}
-
 class TabCloser : public content::WebContentsUserData<TabCloser> {
  public:
   static void MaybeClose(WebContents* web_contents) {
@@ -124,7 +113,7 @@
 void SupervisedUserInterstitial::Show(
     WebContents* web_contents,
     const GURL& url,
-    SupervisedUserURLFilter::FilteringBehaviorReason reason,
+    supervised_user_error_page::FilteringBehaviorReason reason,
     const base::Callback<void(bool)>& callback) {
   SupervisedUserInterstitial* interstitial =
       new SupervisedUserInterstitial(web_contents, url, reason, callback);
@@ -138,7 +127,7 @@
 SupervisedUserInterstitial::SupervisedUserInterstitial(
     WebContents* web_contents,
     const GURL& url,
-    SupervisedUserURLFilter::FilteringBehaviorReason reason,
+    supervised_user_error_page::FilteringBehaviorReason reason,
     const base::Callback<void(bool)>& callback)
     : web_contents_(web_contents),
       profile_(Profile::FromBrowserContext(web_contents->GetBrowserContext())),
@@ -201,33 +190,12 @@
 // static
 std::string SupervisedUserInterstitial::GetHTMLContents(
     Profile* profile,
-    SupervisedUserURLFilter::FilteringBehaviorReason reason) {
-  base::DictionaryValue strings;
-  strings.SetString("blockPageTitle",
-                    l10n_util::GetStringUTF16(IDS_BLOCK_INTERSTITIAL_TITLE));
+    supervised_user_error_page::FilteringBehaviorReason reason) {
+  bool is_child_account = profile->IsChild();
 
   SupervisedUserService* supervised_user_service =
       SupervisedUserServiceFactory::GetForProfile(profile);
 
-  bool allow_access_requests = supervised_user_service->AccessRequestsEnabled();
-  strings.SetBoolean("allowAccessRequests", allow_access_requests);
-
-  std::string profile_image_url = profile->GetPrefs()->GetString(
-      prefs::kSupervisedUserCustodianProfileImageURL);
-  strings.SetString("avatarURL1x", BuildAvatarImageUrl(profile_image_url,
-                                                       kAvatarSize1x));
-  strings.SetString("avatarURL2x", BuildAvatarImageUrl(profile_image_url,
-                                                       kAvatarSize2x));
-
-  std::string profile_image_url2 = profile->GetPrefs()->GetString(
-      prefs::kSupervisedUserSecondCustodianProfileImageURL);
-  strings.SetString("secondAvatarURL1x", BuildAvatarImageUrl(profile_image_url2,
-                                                             kAvatarSize1x));
-  strings.SetString("secondAvatarURL2x", BuildAvatarImageUrl(profile_image_url2,
-                                                             kAvatarSize2x));
-
-  bool is_child_account = profile->IsChild();
-
   base::string16 custodian =
       base::UTF8ToUTF16(supervised_user_service->GetCustodianName());
   base::string16 second_custodian =
@@ -236,83 +204,17 @@
       base::UTF8ToUTF16(supervised_user_service->GetCustodianEmailAddress());
   base::string16 second_custodian_email = base::UTF8ToUTF16(
       supervised_user_service->GetSecondCustodianEmailAddress());
-  strings.SetString("custodianName", custodian);
-  strings.SetString("custodianEmail", custodian_email);
-  strings.SetString("secondCustodianName", second_custodian);
-  strings.SetString("secondCustodianEmail", second_custodian_email);
+  std::string profile_image_url = profile->GetPrefs()->GetString(
+      prefs::kSupervisedUserCustodianProfileImageURL);
+  std::string profile_image_url2 = profile->GetPrefs()->GetString(
+      prefs::kSupervisedUserSecondCustodianProfileImageURL);
 
-  base::string16 block_message;
-  if (allow_access_requests) {
-    if (is_child_account) {
-      block_message = l10n_util::GetStringUTF16(
-          second_custodian.empty()
-              ? IDS_CHILD_BLOCK_INTERSTITIAL_MESSAGE_SINGLE_PARENT
-              : IDS_CHILD_BLOCK_INTERSTITIAL_MESSAGE_MULTI_PARENT);
-    } else {
-      block_message = l10n_util::GetStringFUTF16(
-          IDS_BLOCK_INTERSTITIAL_MESSAGE, custodian);
-    }
-  } else {
-    block_message = l10n_util::GetStringUTF16(
-        IDS_BLOCK_INTERSTITIAL_MESSAGE_ACCESS_REQUESTS_DISABLED);
-  }
-  strings.SetString("blockPageMessage", block_message);
-  strings.SetString("blockReasonMessage", l10n_util::GetStringUTF16(
-      SupervisedUserURLFilter::GetBlockMessageID(
-          reason, is_child_account, second_custodian.empty())));
-  strings.SetString("blockReasonHeader", l10n_util::GetStringUTF16(
-      SupervisedUserURLFilter::GetBlockHeaderID(reason)));
-  bool show_feedback = false;
-#if defined(GOOGLE_CHROME_BUILD)
-  show_feedback =
-      is_child_account && SupervisedUserURLFilter::ReasonIsAutomatic(reason);
-#endif
-  strings.SetBoolean("showFeedbackLink", show_feedback);
-  strings.SetString("feedbackLink",
-      l10n_util::GetStringUTF16(IDS_BLOCK_INTERSTITIAL_SEND_FEEDBACK));
+  bool allow_access_requests = supervised_user_service->AccessRequestsEnabled();
 
-  strings.SetString("backButton", l10n_util::GetStringUTF16(IDS_BACK_BUTTON));
-  strings.SetString("requestAccessButton", l10n_util::GetStringUTF16(
-      IDS_BLOCK_INTERSTITIAL_REQUEST_ACCESS_BUTTON));
-
-  strings.SetString("showDetailsLink", l10n_util::GetStringUTF16(
-      IDS_BLOCK_INTERSTITIAL_SHOW_DETAILS));
-  strings.SetString("hideDetailsLink", l10n_util::GetStringUTF16(
-        IDS_BLOCK_INTERSTITIAL_HIDE_DETAILS));
-
-  base::string16 request_sent_message;
-  base::string16 request_failed_message;
-  if (is_child_account) {
-    if (second_custodian.empty()) {
-      request_sent_message = l10n_util::GetStringUTF16(
-          IDS_CHILD_BLOCK_INTERSTITIAL_REQUEST_SENT_MESSAGE_SINGLE_PARENT);
-      request_failed_message = l10n_util::GetStringUTF16(
-          IDS_CHILD_BLOCK_INTERSTITIAL_REQUEST_FAILED_MESSAGE_SINGLE_PARENT);
-    } else {
-      request_sent_message = l10n_util::GetStringUTF16(
-          IDS_CHILD_BLOCK_INTERSTITIAL_REQUEST_SENT_MESSAGE_MULTI_PARENT);
-      request_failed_message = l10n_util::GetStringUTF16(
-          IDS_CHILD_BLOCK_INTERSTITIAL_REQUEST_FAILED_MESSAGE_MULTI_PARENT);
-    }
-  } else {
-    request_sent_message = l10n_util::GetStringFUTF16(
-        IDS_BLOCK_INTERSTITIAL_REQUEST_SENT_MESSAGE, custodian);
-    request_failed_message = l10n_util::GetStringFUTF16(
-        IDS_BLOCK_INTERSTITIAL_REQUEST_FAILED_MESSAGE, custodian);
-  }
-  strings.SetString("requestSentMessage", request_sent_message);
-  strings.SetString("requestFailedMessage", request_failed_message);
-
-  const std::string& app_locale = g_browser_process->GetApplicationLocale();
-  webui::SetLoadTimeDataDefaults(app_locale, &strings);
-
-  std::string html =
-      ResourceBundle::GetSharedInstance()
-          .GetRawDataResource(IDR_SUPERVISED_USER_BLOCK_INTERSTITIAL_HTML)
-          .as_string();
-  webui::AppendWebUiCssTextDefaults(&html);
-
-  return webui::GetI18nTemplateHtml(html, &strings);
+  return supervised_user_error_page::BuildHtml(
+      allow_access_requests, profile_image_url, profile_image_url2, custodian,
+      custodian_email, second_custodian, second_custodian_email,
+      is_child_account, reason, g_browser_process->GetApplicationLocale());
 }
 
 std::string SupervisedUserInterstitial::GetHTMLContents() {
@@ -363,8 +265,8 @@
       base::UTF8ToUTF16(supervised_user_service->GetSecondCustodianName());
 
   if (command == "\"feedback\"") {
-    base::string16 reason = l10n_util::GetStringUTF16(
-        SupervisedUserURLFilter::GetBlockMessageID(
+    base::string16 reason =
+        l10n_util::GetStringUTF16(supervised_user_error_page::GetBlockMessageID(
             reason_, true, second_custodian.empty()));
     std::string message = l10n_util::GetStringFUTF8(
         IDS_BLOCK_INTERSTITIAL_DEFAULT_FEEDBACK_TEXT, reason);
diff --git a/chrome/browser/supervised_user/supervised_user_interstitial.h b/chrome/browser/supervised_user/supervised_user_interstitial.h
index f90bf1e..29485481 100644
--- a/chrome/browser/supervised_user/supervised_user_interstitial.h
+++ b/chrome/browser/supervised_user/supervised_user_interstitial.h
@@ -12,6 +12,7 @@
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/supervised_user/supervised_user_service_observer.h"
 #include "chrome/browser/supervised_user/supervised_user_url_filter.h"
+#include "components/supervised_user_error_page/supervised_user_error_page.h"
 #include "content/public/browser/interstitial_page_delegate.h"
 #include "url/gurl.h"
 
@@ -33,18 +34,18 @@
 
   static void Show(content::WebContents* web_contents,
                    const GURL& url,
-                   SupervisedUserURLFilter::FilteringBehaviorReason reason,
+                   supervised_user_error_page::FilteringBehaviorReason reason,
                    const base::Callback<void(bool)>& callback);
 
   static std::string GetHTMLContents(
       Profile* profile,
-      SupervisedUserURLFilter::FilteringBehaviorReason reason);
+      supervised_user_error_page::FilteringBehaviorReason reason);
 
  private:
   SupervisedUserInterstitial(
       content::WebContents* web_contents,
       const GURL& url,
-      SupervisedUserURLFilter::FilteringBehaviorReason reason,
+      supervised_user_error_page::FilteringBehaviorReason reason,
       const base::Callback<void(bool)>& callback);
   ~SupervisedUserInterstitial() override;
 
@@ -80,7 +81,7 @@
   content::InterstitialPage* interstitial_page_;  // Owns us.
 
   GURL url_;
-  SupervisedUserURLFilter::FilteringBehaviorReason reason_;
+  supervised_user_error_page::FilteringBehaviorReason reason_;
 
   base::Callback<void(bool)> callback_;
 
diff --git a/chrome/browser/supervised_user/supervised_user_navigation_observer.cc b/chrome/browser/supervised_user/supervised_user_navigation_observer.cc
index fda80c99..a0840f7c 100644
--- a/chrome/browser/supervised_user/supervised_user_navigation_observer.cc
+++ b/chrome/browser/supervised_user/supervised_user_navigation_observer.cc
@@ -43,7 +43,7 @@
 void SupervisedUserNavigationObserver::OnRequestBlocked(
     const content::ResourceRequestInfo::WebContentsGetter& web_contents_getter,
     const GURL& url,
-    SupervisedUserURLFilter::FilteringBehaviorReason reason,
+    supervised_user_error_page::FilteringBehaviorReason reason,
     const base::Callback<void(bool)>& callback) {
   content::WebContents* web_contents = web_contents_getter.Run();
   if (!web_contents) {
@@ -104,7 +104,7 @@
 void SupervisedUserNavigationObserver::URLFilterCheckCallback(
     const GURL& url,
     SupervisedUserURLFilter::FilteringBehavior behavior,
-    SupervisedUserURLFilter::FilteringBehaviorReason reason,
+    supervised_user_error_page::FilteringBehaviorReason reason,
     bool uncertain) {
   // If the page has been changed in the meantime, we can exit.
   if (url != web_contents_->GetLastCommittedURL())
diff --git a/chrome/browser/supervised_user/supervised_user_navigation_observer.h b/chrome/browser/supervised_user/supervised_user_navigation_observer.h
index 4783f4a..6b1e237 100644
--- a/chrome/browser/supervised_user/supervised_user_navigation_observer.h
+++ b/chrome/browser/supervised_user/supervised_user_navigation_observer.h
@@ -13,6 +13,7 @@
 #include "chrome/browser/supervised_user/supervised_user_url_filter.h"
 #include "chrome/browser/supervised_user/supervised_users.h"
 #include "components/sessions/core/serialized_navigation_entry.h"
+#include "components/supervised_user_error_page/supervised_user_error_page.h"
 #include "content/public/browser/resource_request_info.h"
 #include "content/public/browser/web_contents_user_data.h"
 
@@ -39,7 +40,7 @@
       const content::ResourceRequestInfo::WebContentsGetter&
           web_contents_getter,
       const GURL& url,
-      SupervisedUserURLFilter::FilteringBehaviorReason reason,
+      supervised_user_error_page::FilteringBehaviorReason reason,
       const base::Callback<void(bool)>& callback);
 
   // SupervisedUserServiceObserver implementation.
@@ -48,7 +49,7 @@
   void URLFilterCheckCallback(
       const GURL& url,
       SupervisedUserURLFilter::FilteringBehavior behavior,
-      SupervisedUserURLFilter::FilteringBehaviorReason reason,
+      supervised_user_error_page::FilteringBehaviorReason reason,
       bool uncertain);
 
  private:
diff --git a/chrome/browser/supervised_user/supervised_user_resource_throttle.cc b/chrome/browser/supervised_user/supervised_user_resource_throttle.cc
index f7c85d7..3dfcbda 100644
--- a/chrome/browser/supervised_user/supervised_user_resource_throttle.cc
+++ b/chrome/browser/supervised_user/supervised_user_resource_throttle.cc
@@ -49,27 +49,27 @@
 
 int GetHistogramValueForFilteringBehavior(
     SupervisedUserURLFilter::FilteringBehavior behavior,
-    SupervisedUserURLFilter::FilteringBehaviorReason reason,
+    supervised_user_error_page::FilteringBehaviorReason reason,
     bool uncertain) {
   switch (behavior) {
     case SupervisedUserURLFilter::ALLOW:
     case SupervisedUserURLFilter::WARN:
-      if (reason == SupervisedUserURLFilter::WHITELIST)
+      if (reason == supervised_user_error_page::WHITELIST)
         return FILTERING_BEHAVIOR_ALLOW_WHITELIST;
       return uncertain ? FILTERING_BEHAVIOR_ALLOW_UNCERTAIN
                        : FILTERING_BEHAVIOR_ALLOW;
     case SupervisedUserURLFilter::BLOCK:
       switch (reason) {
-        case SupervisedUserURLFilter::BLACKLIST:
+        case supervised_user_error_page::BLACKLIST:
           return FILTERING_BEHAVIOR_BLOCK_BLACKLIST;
-        case SupervisedUserURLFilter::ASYNC_CHECKER:
+        case supervised_user_error_page::ASYNC_CHECKER:
           return FILTERING_BEHAVIOR_BLOCK_SAFESITES;
-        case SupervisedUserURLFilter::WHITELIST:
+        case supervised_user_error_page::WHITELIST:
           NOTREACHED();
           break;
-        case SupervisedUserURLFilter::MANUAL:
+        case supervised_user_error_page::MANUAL:
           return FILTERING_BEHAVIOR_BLOCK_MANUAL;
-        case SupervisedUserURLFilter::DEFAULT:
+        case supervised_user_error_page::DEFAULT:
           return FILTERING_BEHAVIOR_BLOCK_DEFAULT;
       }
     case SupervisedUserURLFilter::INVALID:
@@ -90,7 +90,7 @@
 void RecordFilterResultEvent(
     bool safesites_histogram,
     SupervisedUserURLFilter::FilteringBehavior behavior,
-    SupervisedUserURLFilter::FilteringBehaviorReason reason,
+    supervised_user_error_page::FilteringBehaviorReason reason,
     bool uncertain,
     ui::PageTransition transition_type) {
   int value =
@@ -145,7 +145,7 @@
 
 void SupervisedUserResourceThrottle::ShowInterstitial(
     const GURL& url,
-    SupervisedUserURLFilter::FilteringBehaviorReason reason) {
+    supervised_user_error_page::FilteringBehaviorReason reason) {
   const content::ResourceRequestInfo* info =
       content::ResourceRequestInfo::ForRequest(request_);
   BrowserThread::PostTask(
@@ -174,7 +174,7 @@
 void SupervisedUserResourceThrottle::OnCheckDone(
     const GURL& url,
     SupervisedUserURLFilter::FilteringBehavior behavior,
-    SupervisedUserURLFilter::FilteringBehaviorReason reason,
+    supervised_user_error_page::FilteringBehaviorReason reason,
     bool uncertain) {
   DCHECK_EQ(SupervisedUserURLFilter::INVALID, behavior_);
   // If we got a result synchronously, pass it back to ShowInterstitialIfNeeded.
@@ -189,8 +189,8 @@
   // If both the static blacklist and the async checker are enabled, also record
   // SafeSites-only UMA events.
   if (url_filter_->HasBlacklist() && url_filter_->HasAsyncURLChecker() &&
-      (reason == SupervisedUserURLFilter::ASYNC_CHECKER ||
-       reason == SupervisedUserURLFilter::BLACKLIST)) {
+      (reason == supervised_user_error_page::ASYNC_CHECKER ||
+       reason == supervised_user_error_page::BLACKLIST)) {
     RecordFilterResultEvent(true, behavior, reason, uncertain, transition);
   }
 
diff --git a/chrome/browser/supervised_user/supervised_user_resource_throttle.h b/chrome/browser/supervised_user/supervised_user_resource_throttle.h
index be5679e..b082f20 100644
--- a/chrome/browser/supervised_user/supervised_user_resource_throttle.h
+++ b/chrome/browser/supervised_user/supervised_user_resource_throttle.h
@@ -10,6 +10,7 @@
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/supervised_user/supervised_user_url_filter.h"
 #include "chrome/browser/supervised_user/supervised_users.h"
+#include "components/supervised_user_error_page/supervised_user_error_page.h"
 #include "content/public/browser/resource_throttle.h"
 
 namespace net {
@@ -35,10 +36,10 @@
   void ShowInterstitialIfNeeded(bool is_redirect, const GURL& url, bool* defer);
   void ShowInterstitial(
       const GURL& url,
-      SupervisedUserURLFilter::FilteringBehaviorReason reason);
+      supervised_user_error_page::FilteringBehaviorReason reason);
   void OnCheckDone(const GURL& url,
                    SupervisedUserURLFilter::FilteringBehavior behavior,
-                   SupervisedUserURLFilter::FilteringBehaviorReason reason,
+                   supervised_user_error_page::FilteringBehaviorReason reason,
                    bool uncertain);
   void OnInterstitialResult(bool continue_request);
 
diff --git a/chrome/browser/supervised_user/supervised_user_url_filter.cc b/chrome/browser/supervised_user/supervised_user_url_filter.cc
index 1b9f71e8..3c6820c 100644
--- a/chrome/browser/supervised_user/supervised_user_url_filter.cc
+++ b/chrome/browser/supervised_user/supervised_user_url_filter.cc
@@ -207,57 +207,6 @@
 }
 
 // static
-int SupervisedUserURLFilter::GetBlockMessageID(
-    FilteringBehaviorReason reason, bool is_child_account, bool single_parent) {
-  switch (reason) {
-    case DEFAULT:
-      return is_child_account ?
-          (single_parent ?
-              IDS_CHILD_BLOCK_MESSAGE_DEFAULT_SINGLE_PARENT :
-              IDS_CHILD_BLOCK_MESSAGE_DEFAULT_MULTI_PARENT) :
-          IDS_SUPERVISED_USER_BLOCK_MESSAGE_DEFAULT;
-    case BLACKLIST:
-    case ASYNC_CHECKER:
-      return IDS_SUPERVISED_USER_BLOCK_MESSAGE_SAFE_SITES;
-    case WHITELIST:
-      NOTREACHED();
-      break;
-    case MANUAL:
-      return is_child_account ?
-          (single_parent ?
-              IDS_CHILD_BLOCK_MESSAGE_MANUAL_SINGLE_PARENT :
-              IDS_CHILD_BLOCK_MESSAGE_MANUAL_MULTI_PARENT) :
-          IDS_SUPERVISED_USER_BLOCK_MESSAGE_MANUAL;
-  }
-  NOTREACHED();
-  return 0;
-}
-
-// static
-int SupervisedUserURLFilter::GetBlockHeaderID(FilteringBehaviorReason reason) {
-  switch (reason) {
-    case DEFAULT:
-      return IDS_SUPERVISED_USER_BLOCK_HEADER_DEFAULT;
-    case BLACKLIST:
-    case ASYNC_CHECKER:
-      return IDS_SUPERVISED_USER_BLOCK_HEADER_SAFE_SITES;
-    case WHITELIST:
-      NOTREACHED();
-      break;
-    case MANUAL:
-      return IDS_SUPERVISED_USER_BLOCK_HEADER_MANUAL;
-  }
-  NOTREACHED();
-  return 0;
-}
-
-// static
-bool SupervisedUserURLFilter::ReasonIsAutomatic(
-    FilteringBehaviorReason reason) {
-  return reason == ASYNC_CHECKER || reason == BLACKLIST;
-}
-
-// static
 GURL SupervisedUserURLFilter::Normalize(const GURL& url) {
   GURL normalized_url = url;
   GURL::Replacements replacements;
@@ -319,25 +268,25 @@
 
 SupervisedUserURLFilter::FilteringBehavior
 SupervisedUserURLFilter::GetFilteringBehaviorForURL(const GURL& url) const {
-  FilteringBehaviorReason reason;
+  supervised_user_error_page::FilteringBehaviorReason reason;
   return GetFilteringBehaviorForURL(url, false, &reason);
 }
 
 bool SupervisedUserURLFilter::GetManualFilteringBehaviorForURL(
     const GURL& url, FilteringBehavior* behavior) const {
-  FilteringBehaviorReason reason;
+  supervised_user_error_page::FilteringBehaviorReason reason;
   *behavior = GetFilteringBehaviorForURL(url, true, &reason);
-  return reason == MANUAL;
+  return reason == supervised_user_error_page::MANUAL;
 }
 
 SupervisedUserURLFilter::FilteringBehavior
 SupervisedUserURLFilter::GetFilteringBehaviorForURL(
     const GURL& url,
     bool manual_only,
-    FilteringBehaviorReason* reason) const {
+    supervised_user_error_page::FilteringBehaviorReason* reason) const {
   DCHECK(CalledOnValidThread());
 
-  *reason = MANUAL;
+  *reason = supervised_user_error_page::MANUAL;
 
   // URLs with a non-standard scheme (e.g. chrome://) are always allowed.
   if (!HasFilteredScheme(url))
@@ -368,36 +317,38 @@
       contents_->url_matcher.MatchURL(url);
 
   if (!matching_ids.empty()) {
-    *reason = WHITELIST;
+    *reason = supervised_user_error_page::WHITELIST;
     return ALLOW;
   }
 
   // Check the list of hostname hashes.
   if (contents_->hostname_hashes.count(HostnameHash(url.host()))) {
-    *reason = WHITELIST;
+    *reason = supervised_user_error_page::WHITELIST;
     return ALLOW;
   }
 
   // Check the static blacklist, unless the default is to block anyway.
   if (!manual_only && default_behavior_ != BLOCK &&
       blacklist_ && blacklist_->HasURL(url)) {
-    *reason = BLACKLIST;
+    *reason = supervised_user_error_page::BLACKLIST;
     return BLOCK;
   }
 
   // Fall back to the default behavior.
-  *reason = DEFAULT;
+  *reason = supervised_user_error_page::DEFAULT;
   return default_behavior_;
 }
 
 bool SupervisedUserURLFilter::GetFilteringBehaviorForURLWithAsyncChecks(
     const GURL& url,
     const FilteringBehaviorCallback& callback) const {
-  FilteringBehaviorReason reason = DEFAULT;
+  supervised_user_error_page::FilteringBehaviorReason reason =
+      supervised_user_error_page::DEFAULT;
   FilteringBehavior behavior = GetFilteringBehaviorForURL(url, false, &reason);
   // Any non-default reason trumps the async checker.
   // Also, if we're blocking anyway, then there's no need to check it.
-  if (reason != DEFAULT || behavior == BLOCK || !async_url_checker_) {
+  if (reason != supervised_user_error_page::DEFAULT || behavior == BLOCK ||
+      !async_url_checker_) {
     callback.Run(behavior, reason, false);
     FOR_EACH_OBSERVER(Observer, observers_,
                       OnURLChecked(url, behavior, reason, false));
@@ -546,7 +497,9 @@
     bool uncertain) const {
   DCHECK(default_behavior_ != BLOCK);
 
-  callback.Run(behavior, ASYNC_CHECKER, uncertain);
-  FOR_EACH_OBSERVER(Observer, observers_,
-                    OnURLChecked(url, behavior, ASYNC_CHECKER, uncertain));
+  callback.Run(behavior, supervised_user_error_page::ASYNC_CHECKER, uncertain);
+  FOR_EACH_OBSERVER(
+      Observer, observers_,
+      OnURLChecked(url, behavior, supervised_user_error_page::ASYNC_CHECKER,
+                   uncertain));
 }
diff --git a/chrome/browser/supervised_user/supervised_user_url_filter.h b/chrome/browser/supervised_user/supervised_user_url_filter.h
index e02792d..b532e5e 100644
--- a/chrome/browser/supervised_user/supervised_user_url_filter.h
+++ b/chrome/browser/supervised_user/supervised_user_url_filter.h
@@ -18,6 +18,7 @@
 #include "base/values.h"
 #include "chrome/browser/supervised_user/supervised_user_site_list.h"
 #include "chrome/browser/supervised_user/supervised_users.h"
+#include "components/supervised_user_error_page/supervised_user_error_page.h"
 
 class GURL;
 class SupervisedUserBlacklist;
@@ -51,25 +52,20 @@
     BLOCK,
     INVALID
   };
-  enum FilteringBehaviorReason {
-    DEFAULT,
-    ASYNC_CHECKER,
-    BLACKLIST,
-    MANUAL,
-    WHITELIST
-  };
 
-  using FilteringBehaviorCallback = base::Callback<void(FilteringBehavior,
-                                                        FilteringBehaviorReason,
-                                                        bool /* uncertain */)>;
+  using FilteringBehaviorCallback =
+      base::Callback<void(FilteringBehavior,
+                          supervised_user_error_page::FilteringBehaviorReason,
+                          bool /* uncertain */)>;
 
   class Observer {
    public:
     virtual void OnSiteListUpdated() = 0;
-    virtual void OnURLChecked(const GURL& url,
-                              FilteringBehavior behavior,
-                              FilteringBehaviorReason reason,
-                              bool uncertain) {}
+    virtual void OnURLChecked(
+        const GURL& url,
+        FilteringBehavior behavior,
+        supervised_user_error_page::FilteringBehaviorReason reason,
+        bool uncertain) {}
   };
 
   struct Contents;
@@ -78,14 +74,8 @@
 
   static FilteringBehavior BehaviorFromInt(int behavior_value);
 
-  static int GetBlockMessageID(
-      FilteringBehaviorReason reason,
-      bool is_child_account,
-      bool single_parent);
-
-  static int GetBlockHeaderID(FilteringBehaviorReason reason);
-
-  static bool ReasonIsAutomatic(FilteringBehaviorReason reason);
+  static bool ReasonIsAutomatic(
+      supervised_user_error_page::FilteringBehaviorReason reason);
 
   // Normalizes a URL for matching purposes.
   static GURL Normalize(const GURL& url);
@@ -189,7 +179,9 @@
   void SetContents(scoped_ptr<Contents> url_matcher);
 
   FilteringBehavior GetFilteringBehaviorForURL(
-      const GURL& url, bool manual_only, FilteringBehaviorReason* reason) const;
+      const GURL& url,
+      bool manual_only,
+      supervised_user_error_page::FilteringBehaviorReason* reason) const;
 
   void CheckCallback(const FilteringBehaviorCallback& callback,
                      const GURL& url,
diff --git a/chrome/browser/ui/webui/supervised_user_internals_message_handler.cc b/chrome/browser/ui/webui/supervised_user_internals_message_handler.cc
index 9d8cc63..7315b53 100644
--- a/chrome/browser/ui/webui/supervised_user_internals_message_handler.cc
+++ b/chrome/browser/ui/webui/supervised_user_internals_message_handler.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/supervised_user/supervised_user_settings_service.h"
 #include "chrome/browser/supervised_user/supervised_user_settings_service_factory.h"
 #include "components/signin/core/browser/account_tracker_service.h"
+#include "components/supervised_user_error_page/supervised_user_error_page.h"
 #include "components/url_formatter/url_fixer.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/web_ui.h"
@@ -90,17 +91,17 @@
 }
 
 std::string FilteringBehaviorReasonToString(
-    SupervisedUserURLFilter::FilteringBehaviorReason reason) {
+    supervised_user_error_page::FilteringBehaviorReason reason) {
   switch (reason) {
-    case SupervisedUserURLFilter::DEFAULT:
+    case supervised_user_error_page::DEFAULT:
       return "Default";
-    case SupervisedUserURLFilter::ASYNC_CHECKER:
+    case supervised_user_error_page::ASYNC_CHECKER:
       return "AsyncChecker";
-    case SupervisedUserURLFilter::BLACKLIST:
+    case supervised_user_error_page::BLACKLIST:
       return "Blacklist";
-    case SupervisedUserURLFilter::MANUAL:
+    case supervised_user_error_page::MANUAL:
       return "Manual";
-    case SupervisedUserURLFilter::WHITELIST:
+    case supervised_user_error_page::WHITELIST:
       return "Whitelist";
   }
   return "Unknown/invalid";
@@ -118,7 +119,7 @@
   using OnURLCheckedCallback =
       base::Callback<void(const GURL&,
                           SupervisedUserURLFilter::FilteringBehavior,
-                          SupervisedUserURLFilter::FilteringBehaviorReason,
+                          supervised_user_error_page::FilteringBehaviorReason,
                           bool uncertain)>;
 
   IOThreadHelper(scoped_refptr<const SupervisedUserURLFilter> filter,
@@ -141,7 +142,7 @@
   void OnSiteListUpdated() override {}
   void OnURLChecked(const GURL& url,
                     SupervisedUserURLFilter::FilteringBehavior behavior,
-                    SupervisedUserURLFilter::FilteringBehaviorReason reason,
+                    supervised_user_error_page::FilteringBehaviorReason reason,
                     bool uncertain) override {
     BrowserThread::PostTask(BrowserThread::UI,
                             FROM_HERE,
@@ -294,7 +295,7 @@
 void SupervisedUserInternalsMessageHandler::OnTryURLResult(
     const std::map<std::string, base::string16>& whitelists,
     SupervisedUserURLFilter::FilteringBehavior behavior,
-    SupervisedUserURLFilter::FilteringBehaviorReason reason,
+    supervised_user_error_page::FilteringBehaviorReason reason,
     bool uncertain) {
   std::vector<std::string> whitelists_list;
   for (const auto& whitelist : whitelists) {
@@ -306,7 +307,7 @@
   base::DictionaryValue result;
   result.SetString("allowResult",
                    FilteringBehaviorToString(behavior, uncertain));
-  result.SetBoolean("manual", reason == SupervisedUserURLFilter::MANUAL &&
+  result.SetBoolean("manual", reason == supervised_user_error_page::MANUAL &&
                                   behavior == SupervisedUserURLFilter::ALLOW);
   result.SetString("whitelists", whitelists_str);
   web_ui()->CallJavascriptFunction(
@@ -316,7 +317,7 @@
 void SupervisedUserInternalsMessageHandler::OnURLChecked(
     const GURL& url,
     SupervisedUserURLFilter::FilteringBehavior behavior,
-    SupervisedUserURLFilter::FilteringBehaviorReason reason,
+    supervised_user_error_page::FilteringBehaviorReason reason,
     bool uncertain) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   base::DictionaryValue result;
diff --git a/chrome/browser/ui/webui/supervised_user_internals_message_handler.h b/chrome/browser/ui/webui/supervised_user_internals_message_handler.h
index 7967d74e..b416e77 100644
--- a/chrome/browser/ui/webui/supervised_user_internals_message_handler.h
+++ b/chrome/browser/ui/webui/supervised_user_internals_message_handler.h
@@ -10,6 +10,7 @@
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/supervised_user/supervised_user_service_observer.h"
 #include "chrome/browser/supervised_user/supervised_user_url_filter.h"
+#include "components/supervised_user_error_page/supervised_user_error_page.h"
 #include "content/public/browser/web_ui_message_handler.h"
 
 namespace base {
@@ -44,14 +45,15 @@
   void SendBasicInfo();
   void SendSupervisedUserSettings(const base::DictionaryValue* settings);
 
-  void OnTryURLResult(const std::map<std::string, base::string16>& whitelists,
-                      SupervisedUserURLFilter::FilteringBehavior behavior,
-                      SupervisedUserURLFilter::FilteringBehaviorReason reason,
-                      bool uncertain);
+  void OnTryURLResult(
+      const std::map<std::string, base::string16>& whitelists,
+      SupervisedUserURLFilter::FilteringBehavior behavior,
+      supervised_user_error_page::FilteringBehaviorReason reason,
+      bool uncertain);
 
   void OnURLChecked(const GURL& url,
                     SupervisedUserURLFilter::FilteringBehavior behavior,
-                    SupervisedUserURLFilter::FilteringBehaviorReason reason,
+                    supervised_user_error_page::FilteringBehaviorReason reason,
                     bool uncertain);
 
   scoped_ptr<base::CallbackList<void(
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 2795022..4c642ed5 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -3215,6 +3215,7 @@
         # TODO(fdoray): Remove this once the PreRead field trial has expired.
         # crbug.com/577698
         '../components/components.gyp:startup_metric_utils_common',
+        '../components/components.gyp:supervised_user_error_page',
         '../components/components.gyp:sync_bookmarks',
         '../components/components.gyp:sync_driver',
         '../components/components.gyp:sync_sessions',
diff --git a/components/BUILD.gn b/components/BUILD.gn
index 57d1edb3..67d9218 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -60,6 +60,7 @@
     "//components/security_state:unit_tests",
     "//components/sessions:unit_tests",
     "//components/suggestions:unit_tests",
+    "//components/supervised_user_error_page:unit_tests",
     "//components/syncable_prefs:unit_tests",
     "//components/translate/core/browser:unit_tests",
     "//components/translate/core/common:unit_tests",
diff --git a/components/OWNERS b/components/OWNERS
index a37dc71..48e9069 100644
--- a/components/OWNERS
+++ b/components/OWNERS
@@ -185,6 +185,9 @@
 
 per-file suggestions.gypi=file://components/suggestions/OWNERS
 
+per-file supervised_user_error_page.gypi=file://components/supervised_user_error_page/OWNERS
+per-file supervised_user_error_page_strings.grdp=file://components/supervised_user_error_page/OWNERS
+
 per-file sync_driver.gypi=file://components/sync_driver/OWNERS
 
 per-file sync_sessions.gypi=file://components/sync_sessions/OWNERS
diff --git a/components/components.gyp b/components/components.gyp
index 00142bb..e05f3935 100644
--- a/components/components.gyp
+++ b/components/components.gyp
@@ -81,6 +81,7 @@
     'ssl_config.gypi',
     'ssl_errors.gypi',
     'suggestions.gypi',
+    'supervised_user_error_page.gypi',
     'sync_bookmarks.gypi',
     'sync_driver.gypi',
     'sync_sessions.gypi',
diff --git a/components/components_strings.grd b/components/components_strings.grd
index d6ecbbd..d195968 100644
--- a/components/components_strings.grd
+++ b/components/components_strings.grd
@@ -205,6 +205,7 @@
       <part file="search_engines_strings.grdp" />
       <part file="security_interstitials_strings.grdp" />
       <part file="ssl_errors_strings.grdp" />
+      <part file="supervised_user_error_page_strings.grdp" />
       <part file="sync_ui_strings.grdp" />
       <part file="translate_strings.grdp" />
       <part file="undo_strings.grdp" />
diff --git a/components/components_tests.gyp b/components/components_tests.gyp
index 44959ef..0d546c6c 100644
--- a/components/components_tests.gyp
+++ b/components/components_tests.gyp
@@ -740,6 +740,9 @@
       'suggestions/suggestions_service_unittest.cc',
       'suggestions/suggestions_store_unittest.cc',
     ],
+    'supervised_user_error_page_unittest_sources': [
+      'supervised_user_error_page/supervised_user_error_page_unittest.cc',
+    ],
     'sync_bookmarks_unittest_sources': [
       'sync_bookmarks/bookmark_data_type_controller_unittest.cc',
     ],
@@ -995,6 +998,7 @@
         '<@(ssl_config_unittest_sources)',
         '<@(ssl_errors_unittest_sources)',
         '<@(suggestions_unittest_sources)',
+        '<@(supervised_user_error_page_unittest_sources)',
         '<@(sync_bookmarks_unittest_sources)',
         '<@(sync_driver_unittest_sources)',
         '<@(sync_sessions_unittest_sources)',
@@ -1122,6 +1126,7 @@
         'components.gyp:ssl_config',
         'components.gyp:ssl_errors',
         'components.gyp:suggestions',
+        'components.gyp:supervised_user_error_page',
         'components.gyp:sync_bookmarks',
         'components.gyp:sync_driver',
         'components.gyp:sync_driver_test_support',
diff --git a/components/resources/OWNERS b/components/resources/OWNERS
index 83609568..76b652d 100644
--- a/components/resources/OWNERS
+++ b/components/resources/OWNERS
@@ -22,6 +22,10 @@
 per-file neterror*[email protected]
 per-file proximity_auth*[email protected]
 per-file proximity_auth*[email protected]
+per-file [email protected]
+per-file [email protected]
+per-file [email protected]
+per-file [email protected]
 per-file version_ui*[email protected]
 per-file version_ui*[email protected]
 per-file version_ui*[email protected]
diff --git a/components/resources/components_resources.grd b/components/resources/components_resources.grd
index 1f88844..ba428ec 100644
--- a/components/resources/components_resources.grd
+++ b/components/resources/components_resources.grd
@@ -19,6 +19,7 @@
       <part file="proximity_auth_resources.grdp" />
       <part file="security_interstitials_resources.grdp" />
       <part file="signin_resources.grdp" />
+      <part file="supervised_user_error_page_resources.grdp" />
       <part file="sync_driver_resources.grdp" />
       <part file="translate_resources.grdp" />
       <part file="version_ui_resources.grdp" />
diff --git a/components/resources/supervised_user_error_page_resources.grdp b/components/resources/supervised_user_error_page_resources.grdp
new file mode 100644
index 0000000..b9c9577b
--- /dev/null
+++ b/components/resources/supervised_user_error_page_resources.grdp
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+      <include name="IDR_SUPERVISED_USER_BLOCK_INTERSTITIAL_HTML" file="../supervised_user_error_page/resources/supervised_user_block_interstitial.html" flattenhtml="true" type="BINDATA" />
+</grit-part>
diff --git a/components/supervised_user_error_page.gypi b/components/supervised_user_error_page.gypi
new file mode 100644
index 0000000..a405aa9
--- /dev/null
+++ b/components/supervised_user_error_page.gypi
@@ -0,0 +1,22 @@
+{
+  'targets': [
+    {
+      # GN version: //components/supervused_user_error_page
+      'target_name': 'supervised_user_error_page',
+      'type': 'static_library',
+      'dependencies': [
+        'components_resources.gyp:components_resources',
+        'components_strings.gyp:components_strings',
+        '../base/base.gyp:base',
+        '../ui/base/ui_base.gyp:ui_base'
+      ],
+      'include_dirs': [
+        '..',
+      ],
+      'sources': [
+        'supervised_user_error_page/supervised_user_error_page.cc',
+        'supervised_user_error_page/supervised_user_error_page.h',
+      ],
+    },
+  ],
+}
diff --git a/components/supervised_user_error_page/BUILD.gn b/components/supervised_user_error_page/BUILD.gn
new file mode 100644
index 0000000..41071284
--- /dev/null
+++ b/components/supervised_user_error_page/BUILD.gn
@@ -0,0 +1,34 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("supervised_user_error_page") {
+  sources = [
+    "supervised_user_error_page.cc",
+    "supervised_user_error_page.h",
+  ]
+
+  deps = [
+    "//base",
+    "//components/resources",
+    "//components/strings",
+    "//ui/base",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "supervised_user_error_page_unittest.cc",
+  ]
+  deps = [
+    ":supervised_user_error_page",
+    "//base",
+    "//base/test:test_support",
+    "//components/resources",
+    "//components/strings",
+    "//testing/gmock",
+    "//testing/gtest",
+    "//ui/base",
+  ]
+}
diff --git a/components/supervised_user_error_page/DEPS b/components/supervised_user_error_page/DEPS
new file mode 100644
index 0000000..9b9f627
--- /dev/null
+++ b/components/supervised_user_error_page/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  "+ui/base",
+  "+grit",
+]
\ No newline at end of file
diff --git a/components/supervised_user_error_page/OWNERS b/components/supervised_user_error_page/OWNERS
new file mode 100644
index 0000000..1fc7bea
--- /dev/null
+++ b/components/supervised_user_error_page/OWNERS
@@ -0,0 +1,4 @@
[email protected]
[email protected]
[email protected]
[email protected]
diff --git a/components/supervised_user_error_page/resources/default_100_percent/logo_avatar_circle_blue_color.png b/components/supervised_user_error_page/resources/default_100_percent/logo_avatar_circle_blue_color.png
new file mode 100644
index 0000000..79080803
--- /dev/null
+++ b/components/supervised_user_error_page/resources/default_100_percent/logo_avatar_circle_blue_color.png
Binary files differ
diff --git a/components/supervised_user_error_page/resources/default_200_percent/logo_avatar_circle_blue_color.png b/components/supervised_user_error_page/resources/default_200_percent/logo_avatar_circle_blue_color.png
new file mode 100644
index 0000000..0ec3547b
--- /dev/null
+++ b/components/supervised_user_error_page/resources/default_200_percent/logo_avatar_circle_blue_color.png
Binary files differ
diff --git a/chrome/browser/resources/supervised_user_block_interstitial.css b/components/supervised_user_error_page/resources/supervised_user_block_interstitial.css
similarity index 92%
rename from chrome/browser/resources/supervised_user_block_interstitial.css
rename to components/supervised_user_error_page/resources/supervised_user_block_interstitial.css
index bd37186f..fac30f4 100644
--- a/chrome/browser/resources/supervised_user_block_interstitial.css
+++ b/components/supervised_user_error_page/resources/supervised_user_block_interstitial.css
@@ -24,8 +24,8 @@
   border: 3px solid rgb(251, 251, 251);
   border-radius: 50%;
   content: -webkit-image-set(
-      url(../../app/theme/default_100_percent/cros/logo_avatar_circle_blue_color.png) 1x,
-      url(../../app/theme/default_200_percent/cros/logo_avatar_circle_blue_color.png) 2x);
+      url(default_100_percent/logo_avatar_circle_blue_color.png) 1x,
+      url(default_200_percent/logo_avatar_circle_blue_color.png) 2x);
   margin-bottom: 5px;
   margin-right: 15px;
   margin-top: 5px;
diff --git a/chrome/browser/resources/supervised_user_block_interstitial.html b/components/supervised_user_error_page/resources/supervised_user_block_interstitial.html
similarity index 100%
rename from chrome/browser/resources/supervised_user_block_interstitial.html
rename to components/supervised_user_error_page/resources/supervised_user_block_interstitial.html
diff --git a/chrome/browser/resources/supervised_user_block_interstitial.js b/components/supervised_user_error_page/resources/supervised_user_block_interstitial.js
similarity index 100%
rename from chrome/browser/resources/supervised_user_block_interstitial.js
rename to components/supervised_user_error_page/resources/supervised_user_block_interstitial.js
diff --git a/components/supervised_user_error_page/supervised_user_error_page.cc b/components/supervised_user_error_page/supervised_user_error_page.cc
new file mode 100644
index 0000000..ffb0fa8
--- /dev/null
+++ b/components/supervised_user_error_page/supervised_user_error_page.cc
@@ -0,0 +1,178 @@
+// 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/supervised_user_error_page/supervised_user_error_page.h"
+
+#include "base/macros.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/values.h"
+#include "grit/components_resources.h"
+#include "grit/components_strings.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/webui/jstemplate_builder.h"
+#include "ui/base/webui/web_ui_util.h"
+
+namespace supervised_user_error_page {
+
+namespace {
+
+static const int kAvatarSize1x = 45;
+static const int kAvatarSize2x = 90;
+
+#if defined(GOOGLE_CHROME_BUILD)
+bool ReasonIsAutomatic(FilteringBehaviorReason reason) {
+  return reason == ASYNC_CHECKER || reason == BLACKLIST;
+}
+#endif
+
+std::string BuildAvatarImageUrl(const std::string& url, int size) {
+  std::string result = url;
+  size_t slash = result.rfind('/');
+  if (slash != std::string::npos)
+    result.insert(slash, "/s" + base::IntToString(size));
+  return result;
+}
+
+int GetBlockHeaderID(FilteringBehaviorReason reason) {
+  switch (reason) {
+    case DEFAULT:
+      return IDS_SUPERVISED_USER_BLOCK_HEADER_DEFAULT;
+    case BLACKLIST:
+    case ASYNC_CHECKER:
+      return IDS_SUPERVISED_USER_BLOCK_HEADER_SAFE_SITES;
+    case WHITELIST:
+      NOTREACHED();
+      break;
+    case MANUAL:
+      return IDS_SUPERVISED_USER_BLOCK_HEADER_MANUAL;
+  }
+  NOTREACHED();
+  return 0;
+}
+}  //  namespace
+
+int GetBlockMessageID(FilteringBehaviorReason reason,
+                      bool is_child_account,
+                      bool single_parent) {
+  switch (reason) {
+    case DEFAULT:
+      if (!is_child_account)
+        return IDS_SUPERVISED_USER_BLOCK_MESSAGE_DEFAULT;
+      if (single_parent)
+        return IDS_CHILD_BLOCK_MESSAGE_DEFAULT_SINGLE_PARENT;
+      return IDS_CHILD_BLOCK_MESSAGE_DEFAULT_MULTI_PARENT;
+    case BLACKLIST:
+    case ASYNC_CHECKER:
+      return IDS_SUPERVISED_USER_BLOCK_MESSAGE_SAFE_SITES;
+    case WHITELIST:
+      NOTREACHED();
+      break;
+    case MANUAL:
+      if (!is_child_account)
+        return IDS_SUPERVISED_USER_BLOCK_MESSAGE_MANUAL;
+      if (single_parent)
+        return IDS_CHILD_BLOCK_MESSAGE_MANUAL_SINGLE_PARENT;
+      return IDS_CHILD_BLOCK_MESSAGE_MANUAL_MULTI_PARENT;
+  }
+  NOTREACHED();
+  return 0;
+}
+
+std::string BuildHtml(bool allow_access_requests,
+                      const std::string& profile_image_url,
+                      const std::string& profile_image_url2,
+                      const base::string16& custodian,
+                      const base::string16& custodian_email,
+                      const base::string16& second_custodian,
+                      const base::string16& second_custodian_email,
+                      bool is_child_account,
+                      FilteringBehaviorReason reason,
+                      const std::string& app_locale) {
+  base::DictionaryValue strings;
+  strings.SetString("blockPageTitle",
+                    l10n_util::GetStringUTF16(IDS_BLOCK_INTERSTITIAL_TITLE));
+  strings.SetBoolean("allowAccessRequests", allow_access_requests);
+  strings.SetString("avatarURL1x",
+                    BuildAvatarImageUrl(profile_image_url, kAvatarSize1x));
+  strings.SetString("avatarURL2x",
+                    BuildAvatarImageUrl(profile_image_url, kAvatarSize2x));
+  strings.SetString("secondAvatarURL1x",
+                    BuildAvatarImageUrl(profile_image_url2, kAvatarSize1x));
+  strings.SetString("secondAvatarURL2x",
+                    BuildAvatarImageUrl(profile_image_url2, kAvatarSize2x));
+  strings.SetString("custodianName", custodian);
+  strings.SetString("custodianEmail", custodian_email);
+  strings.SetString("secondCustodianName", second_custodian);
+  strings.SetString("secondCustodianEmail", second_custodian_email);
+  base::string16 block_message;
+  if (allow_access_requests) {
+    if (is_child_account) {
+      block_message = l10n_util::GetStringUTF16(
+          second_custodian.empty()
+              ? IDS_CHILD_BLOCK_INTERSTITIAL_MESSAGE_SINGLE_PARENT
+              : IDS_CHILD_BLOCK_INTERSTITIAL_MESSAGE_MULTI_PARENT);
+    } else {
+      block_message =
+          l10n_util::GetStringFUTF16(IDS_BLOCK_INTERSTITIAL_MESSAGE, custodian);
+    }
+  } else {
+    block_message = l10n_util::GetStringUTF16(
+        IDS_BLOCK_INTERSTITIAL_MESSAGE_ACCESS_REQUESTS_DISABLED);
+  }
+  strings.SetString("blockPageMessage", block_message);
+  strings.SetString("blockReasonMessage",
+                    l10n_util::GetStringUTF16(GetBlockMessageID(
+                        reason, is_child_account, second_custodian.empty())));
+  strings.SetString("blockReasonHeader",
+                    l10n_util::GetStringUTF16(GetBlockHeaderID(reason)));
+  bool show_feedback = false;
+#if defined(GOOGLE_CHROME_BUILD)
+  show_feedback = is_child_account && ReasonIsAutomatic(reason);
+#endif
+  strings.SetBoolean("showFeedbackLink", show_feedback);
+  strings.SetString("feedbackLink", l10n_util::GetStringUTF16(
+                                        IDS_BLOCK_INTERSTITIAL_SEND_FEEDBACK));
+  strings.SetString("backButton", l10n_util::GetStringUTF16(IDS_BACK_BUTTON));
+  strings.SetString(
+      "requestAccessButton",
+      l10n_util::GetStringUTF16(IDS_BLOCK_INTERSTITIAL_REQUEST_ACCESS_BUTTON));
+  strings.SetString(
+      "showDetailsLink",
+      l10n_util::GetStringUTF16(IDS_BLOCK_INTERSTITIAL_SHOW_DETAILS));
+  strings.SetString(
+      "hideDetailsLink",
+      l10n_util::GetStringUTF16(IDS_BLOCK_INTERSTITIAL_HIDE_DETAILS));
+  base::string16 request_sent_message;
+  base::string16 request_failed_message;
+  if (is_child_account) {
+    if (second_custodian.empty()) {
+      request_sent_message = l10n_util::GetStringUTF16(
+          IDS_CHILD_BLOCK_INTERSTITIAL_REQUEST_SENT_MESSAGE_SINGLE_PARENT);
+      request_failed_message = l10n_util::GetStringUTF16(
+          IDS_CHILD_BLOCK_INTERSTITIAL_REQUEST_FAILED_MESSAGE_SINGLE_PARENT);
+    } else {
+      request_sent_message = l10n_util::GetStringUTF16(
+          IDS_CHILD_BLOCK_INTERSTITIAL_REQUEST_SENT_MESSAGE_MULTI_PARENT);
+      request_failed_message = l10n_util::GetStringUTF16(
+          IDS_CHILD_BLOCK_INTERSTITIAL_REQUEST_FAILED_MESSAGE_MULTI_PARENT);
+    }
+  } else {
+    request_sent_message = l10n_util::GetStringFUTF16(
+        IDS_BLOCK_INTERSTITIAL_REQUEST_SENT_MESSAGE, custodian);
+    request_failed_message = l10n_util::GetStringFUTF16(
+        IDS_BLOCK_INTERSTITIAL_REQUEST_FAILED_MESSAGE, custodian);
+  }
+  strings.SetString("requestSentMessage", request_sent_message);
+  strings.SetString("requestFailedMessage", request_failed_message);
+  webui::SetLoadTimeDataDefaults(app_locale, &strings);
+  std::string html =
+      ResourceBundle::GetSharedInstance()
+          .GetRawDataResource(IDR_SUPERVISED_USER_BLOCK_INTERSTITIAL_HTML)
+          .as_string();
+  webui::AppendWebUiCssTextDefaults(&html);
+  return webui::GetI18nTemplateHtml(html, &strings);
+}
+
+}  //  namespace supervised_user_error_page
diff --git a/components/supervised_user_error_page/supervised_user_error_page.h b/components/supervised_user_error_page/supervised_user_error_page.h
new file mode 100644
index 0000000..1254b095
--- /dev/null
+++ b/components/supervised_user_error_page/supervised_user_error_page.h
@@ -0,0 +1,40 @@
+// 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_SUPERVISED_USER_ERROR_PAGE_SUPERVISED_USER_ERROR_PAGE_H_
+#define COMPONENTS_SUPERVISED_USER_ERROR_PAGE_SUPERVISED_USER_ERROR_PAGE_H_
+
+#include <string>
+
+#include "base/strings/string16.h"
+
+namespace supervised_user_error_page {
+
+enum FilteringBehaviorReason {
+  DEFAULT,
+  ASYNC_CHECKER,
+  BLACKLIST,
+  MANUAL,
+  WHITELIST
+};
+
+int GetBlockMessageID(
+    supervised_user_error_page::FilteringBehaviorReason reason,
+    bool is_child_account,
+    bool single_parent);
+
+std::string BuildHtml(bool allow_access_requests,
+                      const std::string& profile_image_url,
+                      const std::string& profile_image_url2,
+                      const base::string16& custodian,
+                      const base::string16& custodian_email,
+                      const base::string16& second_custodian,
+                      const base::string16& second_custodian_email,
+                      bool is_child_account,
+                      FilteringBehaviorReason reason,
+                      const std::string& app_locale);
+
+}  //  namespace supervised_user_error_page
+
+#endif // COMPONENTS_SUPERVISED_USER_ERROR_PAGE_SUPERVISED_USER_ERROR_PAGE_H_
diff --git a/components/supervised_user_error_page/supervised_user_error_page_unittest.cc b/components/supervised_user_error_page/supervised_user_error_page_unittest.cc
new file mode 100644
index 0000000..e4c65dab
--- /dev/null
+++ b/components/supervised_user_error_page/supervised_user_error_page_unittest.cc
@@ -0,0 +1,230 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/strings/utf_string_conversions.h"
+#include "components/supervised_user_error_page/supervised_user_error_page.h"
+#include "grit/components_resources.h"
+#include "grit/components_strings.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest-param-test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+
+namespace supervised_user_error_page {
+
+struct BlockMessageIDTestParameter {
+  FilteringBehaviorReason reason;
+  bool is_child_account;
+  bool single_parent;
+  int expected_result;
+};
+
+class SupervisedUserErrorPageTest_GetBlockMessageID
+    : public ::testing::TestWithParam<BlockMessageIDTestParameter> {};
+
+TEST_P(SupervisedUserErrorPageTest_GetBlockMessageID, GetBlockMessageID) {
+  BlockMessageIDTestParameter param = GetParam();
+  EXPECT_EQ(param.expected_result,
+            GetBlockMessageID(param.reason, param.is_child_account,
+                              param.single_parent))
+      << "reason = " << param.reason
+      << " is_child_account = " << param.is_child_account
+      << " single parent = " << param.single_parent;
+}
+
+BlockMessageIDTestParameter block_message_id_test_params[] = {
+    {DEFAULT, false, false, IDS_SUPERVISED_USER_BLOCK_MESSAGE_DEFAULT},
+    {DEFAULT, false, true, IDS_SUPERVISED_USER_BLOCK_MESSAGE_DEFAULT},
+    {DEFAULT, true, true, IDS_CHILD_BLOCK_MESSAGE_DEFAULT_SINGLE_PARENT},
+    {DEFAULT, true, false, IDS_CHILD_BLOCK_MESSAGE_DEFAULT_MULTI_PARENT},
+    {ASYNC_CHECKER, false, false, IDS_SUPERVISED_USER_BLOCK_MESSAGE_SAFE_SITES},
+    {ASYNC_CHECKER, false, true, IDS_SUPERVISED_USER_BLOCK_MESSAGE_SAFE_SITES},
+    {ASYNC_CHECKER, true, true, IDS_SUPERVISED_USER_BLOCK_MESSAGE_SAFE_SITES},
+    {ASYNC_CHECKER, true, false, IDS_SUPERVISED_USER_BLOCK_MESSAGE_SAFE_SITES},
+    {MANUAL, false, false, IDS_SUPERVISED_USER_BLOCK_MESSAGE_MANUAL},
+    {MANUAL, false, true, IDS_SUPERVISED_USER_BLOCK_MESSAGE_MANUAL},
+    {MANUAL, true, true, IDS_CHILD_BLOCK_MESSAGE_MANUAL_SINGLE_PARENT},
+    {MANUAL, true, false, IDS_CHILD_BLOCK_MESSAGE_MANUAL_MULTI_PARENT},
+};
+
+INSTANTIATE_TEST_CASE_P(GetBlockMessageIDParameterized,
+                        SupervisedUserErrorPageTest_GetBlockMessageID,
+                        ::testing::ValuesIn(block_message_id_test_params));
+
+struct BuildHtmlTestParameter {
+  bool allow_access_requests;
+  const std::string& profile_image_url;
+  const std::string& profile_image_url2;
+  const std::string& custodian;
+  const std::string& custodian_email;
+  const std::string& second_custodian;
+  const std::string& second_custodian_email;
+  bool is_child_account;
+  FilteringBehaviorReason reason;
+  bool has_two_parents;
+};
+
+class SupervisedUserErrorPageTest_BuildHtml
+    : public ::testing::TestWithParam<BuildHtmlTestParameter> {};
+
+TEST_P(SupervisedUserErrorPageTest_BuildHtml, BuildHtml) {
+  BuildHtmlTestParameter param = GetParam();
+  std::string result =
+      BuildHtml(param.allow_access_requests, param.profile_image_url,
+                param.profile_image_url2, base::UTF8ToUTF16(param.custodian),
+                base::UTF8ToUTF16(param.custodian_email),
+                base::UTF8ToUTF16(param.second_custodian),
+                base::UTF8ToUTF16(param.second_custodian_email),
+                param.is_child_account, param.reason, "");
+  // The result should contain the original HTML plus scripts that plug values
+  // into it. The test can't easily check that the scripts are correct, but
+  // can check that the output contains the expected values.
+  std::string html =
+      ResourceBundle::GetSharedInstance()
+          .GetRawDataResource(IDR_SUPERVISED_USER_BLOCK_INTERSTITIAL_HTML)
+          .as_string();
+  EXPECT_THAT(result, testing::HasSubstr(html));
+  EXPECT_THAT(result, testing::HasSubstr(param.profile_image_url));
+  EXPECT_THAT(result, testing::HasSubstr(param.profile_image_url2));
+  EXPECT_THAT(result, testing::HasSubstr(param.custodian));
+  EXPECT_THAT(result, testing::HasSubstr(param.custodian_email));
+  if (param.has_two_parents) {
+    EXPECT_THAT(result, testing::HasSubstr(param.second_custodian));
+    EXPECT_THAT(result, testing::HasSubstr(param.second_custodian_email));
+  }
+#if defined(GOOGLE_CHROME_BUILD)
+  if (param.is_child_account &&
+      (param.reason == ASYNC_CHECKER || param.reason == BLACKLIST))
+    EXPECT_THAT(result, testing::HasSubstr("\"showFeedbackLink\":true"));
+  else
+#endif
+    EXPECT_THAT(result, testing::HasSubstr("\"showFeedbackLink\":false"));
+  // Messages containing links aren't tested since they get modified before they
+  // are added to the result.
+  if (param.allow_access_requests) {
+    if (param.is_child_account) {
+      if (param.has_two_parents) {
+        EXPECT_THAT(result,
+                    testing::Not(testing::HasSubstr(l10n_util::GetStringUTF8(
+                        IDS_CHILD_BLOCK_INTERSTITIAL_MESSAGE_SINGLE_PARENT))));
+        EXPECT_THAT(result,
+                    testing::HasSubstr(l10n_util::GetStringUTF8(
+                        IDS_CHILD_BLOCK_INTERSTITIAL_MESSAGE_MULTI_PARENT)));
+        EXPECT_THAT(result,
+                    testing::Not(testing::HasSubstr(l10n_util::GetStringUTF8(
+                        IDS_BLOCK_INTERSTITIAL_MESSAGE))));
+        EXPECT_THAT(
+            result,
+            testing::Not(testing::HasSubstr(l10n_util::GetStringUTF8(
+                IDS_BLOCK_INTERSTITIAL_MESSAGE_ACCESS_REQUESTS_DISABLED))));
+      } else {
+        EXPECT_THAT(result,
+                    testing::HasSubstr(l10n_util::GetStringUTF8(
+                        IDS_CHILD_BLOCK_INTERSTITIAL_MESSAGE_SINGLE_PARENT)));
+        EXPECT_THAT(result,
+                    testing::Not(testing::HasSubstr(l10n_util::GetStringUTF8(
+                        IDS_CHILD_BLOCK_INTERSTITIAL_MESSAGE_MULTI_PARENT))));
+        EXPECT_THAT(
+            result,
+            testing::Not(testing::HasSubstr(l10n_util::GetStringUTF8(
+                IDS_BLOCK_INTERSTITIAL_MESSAGE_ACCESS_REQUESTS_DISABLED))));
+      }
+    } else {
+      EXPECT_THAT(result,
+                  testing::Not(testing::HasSubstr(l10n_util::GetStringUTF8(
+                      IDS_CHILD_BLOCK_INTERSTITIAL_MESSAGE_SINGLE_PARENT))));
+      EXPECT_THAT(result,
+                  testing::Not(testing::HasSubstr(l10n_util::GetStringUTF8(
+                      IDS_CHILD_BLOCK_INTERSTITIAL_MESSAGE_MULTI_PARENT))));
+      EXPECT_THAT(
+          result,
+          testing::Not(testing::HasSubstr(l10n_util::GetStringUTF8(
+              IDS_BLOCK_INTERSTITIAL_MESSAGE_ACCESS_REQUESTS_DISABLED))));
+    }
+  } else {
+    EXPECT_THAT(result,
+                testing::Not(testing::HasSubstr(l10n_util::GetStringUTF8(
+                    IDS_CHILD_BLOCK_INTERSTITIAL_MESSAGE_SINGLE_PARENT))));
+    EXPECT_THAT(result,
+                testing::Not(testing::HasSubstr(l10n_util::GetStringUTF8(
+                    IDS_CHILD_BLOCK_INTERSTITIAL_MESSAGE_MULTI_PARENT))));
+    EXPECT_THAT(result,
+                testing::HasSubstr(l10n_util::GetStringUTF8(
+                    IDS_BLOCK_INTERSTITIAL_MESSAGE_ACCESS_REQUESTS_DISABLED)));
+  }
+  if (param.is_child_account) {
+    if (param.has_two_parents) {
+      EXPECT_THAT(
+          result,
+          testing::Not(testing::HasSubstr(l10n_util::GetStringUTF8(
+              IDS_CHILD_BLOCK_INTERSTITIAL_REQUEST_SENT_MESSAGE_SINGLE_PARENT))));
+      EXPECT_THAT(
+          result,
+          testing::HasSubstr(l10n_util::GetStringUTF8(
+              IDS_CHILD_BLOCK_INTERSTITIAL_REQUEST_SENT_MESSAGE_MULTI_PARENT)));
+      EXPECT_THAT(
+          result,
+          testing::Not(testing::HasSubstr(l10n_util::GetStringUTF8(
+              IDS_CHILD_BLOCK_INTERSTITIAL_REQUEST_FAILED_MESSAGE_SINGLE_PARENT))));
+      EXPECT_THAT(
+          result,
+          testing::HasSubstr(l10n_util::GetStringUTF8(
+              IDS_CHILD_BLOCK_INTERSTITIAL_REQUEST_FAILED_MESSAGE_MULTI_PARENT)));
+    } else {
+      EXPECT_THAT(
+          result,
+          testing::HasSubstr(l10n_util::GetStringUTF8(
+              IDS_CHILD_BLOCK_INTERSTITIAL_REQUEST_SENT_MESSAGE_SINGLE_PARENT)));
+      EXPECT_THAT(
+          result,
+          testing::Not(testing::HasSubstr(l10n_util::GetStringUTF8(
+              IDS_CHILD_BLOCK_INTERSTITIAL_REQUEST_SENT_MESSAGE_MULTI_PARENT))));
+      EXPECT_THAT(
+          result,
+          testing::HasSubstr(l10n_util::GetStringUTF8(
+              IDS_CHILD_BLOCK_INTERSTITIAL_REQUEST_FAILED_MESSAGE_SINGLE_PARENT)));
+      EXPECT_THAT(
+          result,
+          testing::Not(testing::HasSubstr(l10n_util::GetStringUTF8(
+              IDS_CHILD_BLOCK_INTERSTITIAL_REQUEST_FAILED_MESSAGE_MULTI_PARENT))));
+    }
+  } else {
+    EXPECT_THAT(
+        result,
+        testing::Not(testing::HasSubstr(l10n_util::GetStringUTF8(
+            IDS_CHILD_BLOCK_INTERSTITIAL_REQUEST_SENT_MESSAGE_SINGLE_PARENT))));
+    EXPECT_THAT(
+        result,
+        testing::Not(testing::HasSubstr(l10n_util::GetStringUTF8(
+            IDS_CHILD_BLOCK_INTERSTITIAL_REQUEST_SENT_MESSAGE_MULTI_PARENT))));
+    EXPECT_THAT(
+        result,
+        testing::Not(testing::HasSubstr(l10n_util::GetStringUTF8(
+            IDS_CHILD_BLOCK_INTERSTITIAL_REQUEST_FAILED_MESSAGE_SINGLE_PARENT))));
+    EXPECT_THAT(
+        result,
+        testing::Not(testing::HasSubstr(l10n_util::GetStringUTF8(
+            IDS_CHILD_BLOCK_INTERSTITIAL_REQUEST_FAILED_MESSAGE_MULTI_PARENT))));
+  }
+}
+
+BuildHtmlTestParameter build_html_test_parameter[] = {
+    {true, "url1", "url2", "custodian", "custodian_email", "", "", true,
+     DEFAULT, false},
+    {true, "url1", "url2", "custodian", "custodian_email", "custodian2",
+     "custodian2_email", true, DEFAULT, true},
+    {false, "url1", "url2", "custodian", "custodian_email", "custodian2",
+     "custodian2_email", true, DEFAULT, true},
+    {true, "url1", "url2", "custodian", "custodian_email", "custodian2",
+     "custodian2_email", false, DEFAULT, true},
+    {true, "url1", "url2", "custodian", "custodian_email", "custodian2",
+     "custodian2_email", false, ASYNC_CHECKER, true},
+};
+
+INSTANTIATE_TEST_CASE_P(GetBlockMessageIDParameterized,
+                        SupervisedUserErrorPageTest_BuildHtml,
+                        ::testing::ValuesIn(build_html_test_parameter));
+
+}  //  namespace supervised_user_error_page
diff --git a/components/supervised_user_error_page_strings.grdp b/components/supervised_user_error_page_strings.grdp
new file mode 100644
index 0000000..471e5129
--- /dev/null
+++ b/components/supervised_user_error_page_strings.grdp
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+      <!-- Supervised User Block Interstitial data -->
+      <message name="IDS_BLOCK_INTERSTITIAL_TITLE" desc="A title for the supervised-user block interstitial page.">
+        Page blocked
+      </message>
+      <message name="IDS_BLOCK_INTERSTITIAL_MESSAGE" desc="A message for the supervised user when they attempt to visit a site that is not permitted by the manager.">
+        Oops! You need permission from <ph name="NAME">$1<ex>John Doe</ex></ph> to access this page.
+      </message>
+      <message name="IDS_CHILD_BLOCK_INTERSTITIAL_MESSAGE_SINGLE_PARENT" desc="A message for the child user when they attempt to visit a site that is not permitted by their parent.">
+        Oops! You need to ask your parent if it's OK to visit this page.
+      </message>
+      <message name="IDS_CHILD_BLOCK_INTERSTITIAL_MESSAGE_MULTI_PARENT" desc="A message for the child user when they attempt to visit a site that is not permitted by their parents.">
+        Oops! You need to ask your parents if it's OK to visit this page.
+      </message>
+      <message name="IDS_BLOCK_INTERSTITIAL_MESSAGE_ACCESS_REQUESTS_DISABLED" desc="A message for the supervised user when they attempt to visit a site that is not permitted by the manager (and they can't ask for permission).">
+        Oops, looks like you don't have permission to access this page.
+      </message>
+      <message name="IDS_BACK_BUTTON" desc="A button for going back to the last safe url after being blocked.">
+        Go back
+      </message>
+      <message name="IDS_BLOCK_INTERSTITIAL_REQUEST_ACCESS_BUTTON" desc="A button for requesting access to blocked sites.">
+        Ask permission
+      </message>
+      <message name="IDS_CHILD_BLOCK_INTERSTITIAL_REQUEST_ACCESS_BUTTON" desc="A button for requesting access to blocked sites.">
+        Ask permission
+      </message>
+      <message name="IDS_BLOCK_INTERSTITIAL_REQUEST_SENT_MESSAGE" desc="The text that tells the supervised user that a request has been sent.">
+        Your request to access this site has been sent to <ph name="NAME">$1<ex>John Doe</ex></ph>.
+      </message>
+      <message name="IDS_BLOCK_INTERSTITIAL_REQUEST_FAILED_MESSAGE" desc="The text that tells the supervised user that a request could not be sent.">
+        Your request to access this site could not be sent to <ph name="NAME">$1<ex>John Doe</ex></ph>. Please try again.
+      </message>
+      <message name="IDS_CHILD_BLOCK_INTERSTITIAL_REQUEST_SENT_MESSAGE_SINGLE_PARENT" desc="The text that tells the child user that a request has been sent to his/her parent.">
+        You asked your parent if it's ok to visit this page.
+      </message>
+      <message name="IDS_CHILD_BLOCK_INTERSTITIAL_REQUEST_SENT_MESSAGE_MULTI_PARENT" desc="The text that tells the child user that a request has been sent to his/her parents.">
+        You asked your parents if it's ok to visit this page.
+      </message>
+      <message name="IDS_CHILD_BLOCK_INTERSTITIAL_REQUEST_FAILED_MESSAGE_SINGLE_PARENT" desc="The text that tells the child user that a request to his/her parent failed.">
+        We could not reach your parent at the moment. Please try again.
+      </message>
+      <message name="IDS_CHILD_BLOCK_INTERSTITIAL_REQUEST_FAILED_MESSAGE_MULTI_PARENT" desc="The text that tells the child user that a request to his/her parents failed.">
+        We could not reach your parents at the moment. Please try again.
+      </message>
+      <message name="IDS_BLOCK_INTERSTITIAL_SEND_FEEDBACK" desc="The text for a link to submit feedback about a blocked site.">
+        Was this unexpected? <ph name="BEGIN_LINK">&lt;a is="action-link" id="feedback-link"&gt;</ph>Let us know<ph name="END_LINK">&lt;/a&gt;</ph>
+      </message>
+      <message name="IDS_BLOCK_INTERSTITIAL_SHOW_DETAILS" desc="The text for the link to show details about the interstitial.">
+        Details
+      </message>
+      <message name="IDS_BLOCK_INTERSTITIAL_HIDE_DETAILS" desc="The text for the link to hide details about the interstitial.">
+        Hide details
+      </message>
+      <message name="IDS_CHILD_BLOCK_MESSAGE_DEFAULT_SINGLE_PARENT" desc="Message to be shown when a site was blocked due to the default fallback behavior and the child has one parent.">
+        You're seeing this message because your parent needs to approve new sites on your first visit.
+      </message>
+      <message name="IDS_CHILD_BLOCK_MESSAGE_DEFAULT_MULTI_PARENT" desc="Message to be shown when a site was blocked due to the default fallback behavior and the child has two parents.">
+        You're seeing this message because your parents need to approve new sites on your first visit.
+      </message>
+      <message name="IDS_SUPERVISED_USER_BLOCK_MESSAGE_DEFAULT" desc="Message to be show to a supervised user when a site was blocked due to the default fallback behaviour.">
+        You're seeing this message because your manager needs to approve new sites on your first visit.
+      </message>
+      <message name="IDS_SUPERVISED_USER_BLOCK_HEADER_DEFAULT" desc="Header for the message to be shown when a site was blocked due to the default fallback behaviour.">
+        Not approved
+      </message>
+       <message name="IDS_SUPERVISED_USER_BLOCK_MESSAGE_SAFE_SITES" desc="Message to be shown when a site was blocked due to the SafeSites safety check.">
+        You're seeing this message because Google SafeSites is enabled.
+      </message>
+      <message name="IDS_SUPERVISED_USER_BLOCK_HEADER_SAFE_SITES" desc="Header for the message to be shown when a site was blocked due to the SafeSites safety check.">
+        SafeSites
+      </message>
+      <message name="IDS_CHILD_BLOCK_MESSAGE_MANUAL_SINGLE_PARENT" desc="Message to be shown to a child when a site was blocked due to a manual blacklist entry and the child has one parent.">
+        You're seeing this message because your parent blocked this site.
+      </message>
+      <message name="IDS_CHILD_BLOCK_MESSAGE_MANUAL_MULTI_PARENT" desc="Message to be shown to a child when a site was blocked due to a manual blacklist entry and the child has two parents.">
+        You're seeing this message because one of your parents blocked this site.
+      </message>
+      <message name="IDS_SUPERVISED_USER_BLOCK_MESSAGE_MANUAL" desc="Message to be shown to a supervised user when a site is blocked due to a manual blacklist entry">
+        You're seeing this message because your manager blocked this site.
+      </message>
+      <message name="IDS_SUPERVISED_USER_BLOCK_HEADER_MANUAL" desc="Header for the message to be shown when a site was blocked due to a manual blacklist entry.">
+        Blocked
+      </message>
+
+</grit-part>