Add quiet safe browsing interstitial for WebView

BUG=716095

Review-Url: https://codereview.chromium.org/2854263003
Cr-Commit-Position: refs/heads/master@{#473150}
diff --git a/chrome/browser/safe_browsing/safe_browsing_blocking_page.h b/chrome/browser/safe_browsing/safe_browsing_blocking_page.h
index 1854a5f..63a28fe 100644
--- a/chrome/browser/safe_browsing/safe_browsing_blocking_page.h
+++ b/chrome/browser/safe_browsing/safe_browsing_blocking_page.h
@@ -83,6 +83,8 @@
  protected:
   friend class SafeBrowsingBlockingPageFactoryImpl;
   friend class SafeBrowsingBlockingPageTest;
+  friend class SafeBrowsingBlockingQuietPageFactoryImpl;
+  friend class SafeBrowsingBlockingQuietPageTest;
   FRIEND_TEST_ALL_PREFIXES(SafeBrowsingBlockingPageTest,
                            ProceedThenDontProceed);
   FRIEND_TEST_ALL_PREFIXES(SafeBrowsingBlockingPageTest,
diff --git a/chrome/browser/safe_browsing/safe_browsing_blocking_page_unittest.cc b/chrome/browser/safe_browsing/safe_browsing_blocking_page_unittest.cc
index 3c988fb..be37db8 100644
--- a/chrome/browser/safe_browsing/safe_browsing_blocking_page_unittest.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_blocking_page_unittest.cc
@@ -14,9 +14,12 @@
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
+#include "components/grit/components_resources.h"
 #include "components/prefs/pref_service.h"
 #include "components/safe_browsing/browser/threat_details.h"
 #include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/security_interstitials/core/safe_browsing_quiet_error_ui.h"
+#include "components/strings/grit/components_strings.h"
 #include "content/public/browser/interstitial_page.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/render_process_host.h"
@@ -24,6 +27,10 @@
 #include "content/public/test/web_contents_tester.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.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"
 
 using content::InterstitialPage;
 using content::NavigationEntry;
@@ -101,6 +108,79 @@
   MOCK_CONST_METHOD0(IsOffTheRecord, bool());
 };
 
+class TestSafeBrowsingBlockingPageQuiet : public SafeBrowsingBlockingPage {
+ public:
+  TestSafeBrowsingBlockingPageQuiet(
+      BaseUIManager* manager,
+      WebContents* web_contents,
+      const GURL& main_frame_url,
+      const UnsafeResourceList& unsafe_resources,
+      const BaseSafeBrowsingErrorUI::SBErrorDisplayOptions& display_options)
+      : SafeBrowsingBlockingPage(manager,
+                                 web_contents,
+                                 main_frame_url,
+                                 unsafe_resources,
+                                 display_options),
+        sb_error_ui_(unsafe_resources[0].url,
+                     main_frame_url,
+                     GetInterstitialReason(unsafe_resources),
+                     display_options,
+                     manager->app_locale(),
+                     base::Time::NowFromSystemTime(),
+                     controller(),
+                     false) {
+    // Don't delay details at all for the unittest.
+    SetThreatDetailsProceedDelayForTesting(0);
+    DontCreateViewForTesting();
+  }
+
+  // Manually specify that the WebView extends beyond viewing bounds.
+  void SetGiantWebView() { sb_error_ui_.SetGiantWebViewForTesting(true); }
+
+  base::DictionaryValue GetUIStrings() {
+    base::DictionaryValue load_time_data;
+    sb_error_ui_.PopulateStringsForHtml(&load_time_data);
+    webui::SetLoadTimeDataDefaults(controller()->GetApplicationLocale(),
+                                   &load_time_data);
+    return load_time_data;
+  }
+
+  security_interstitials::SafeBrowsingQuietErrorUI sb_error_ui_;
+};
+
+// TODO(edwardjung): Refactor into TestSafeBrowsingBlockingPageFactory.
+class TestSafeBrowsingBlockingQuietPageFactory
+    : public SafeBrowsingBlockingPageFactory {
+ public:
+  TestSafeBrowsingBlockingQuietPageFactory() {}
+  ~TestSafeBrowsingBlockingQuietPageFactory() override {}
+
+  SafeBrowsingBlockingPage* CreateSafeBrowsingPage(
+      BaseUIManager* manager,
+      WebContents* web_contents,
+      const GURL& main_frame_url,
+      const SafeBrowsingBlockingPage::UnsafeResourceList& unsafe_resources)
+      override {
+    PrefService* prefs =
+        Profile::FromBrowserContext(web_contents->GetBrowserContext())
+            ->GetPrefs();
+    bool is_extended_reporting_opt_in_allowed =
+        prefs->GetBoolean(prefs::kSafeBrowsingExtendedReportingOptInAllowed);
+    bool is_proceed_anyway_disabled =
+        prefs->GetBoolean(prefs::kSafeBrowsingProceedAnywayDisabled);
+    BaseSafeBrowsingErrorUI::SBErrorDisplayOptions display_options(
+        BaseBlockingPage::IsMainPageLoadBlocked(unsafe_resources),
+        is_extended_reporting_opt_in_allowed,
+        web_contents->GetBrowserContext()->IsOffTheRecord(),
+        IsExtendedReportingEnabled(*prefs), IsScout(*prefs),
+        is_proceed_anyway_disabled,
+        BaseBlockingPage::IsMainPageLoadBlocked(unsafe_resources));
+    return new TestSafeBrowsingBlockingPageQuiet(
+        manager, web_contents, main_frame_url, unsafe_resources,
+        display_options);
+  }
+};
+
 }  // namespace
 
 class SafeBrowsingBlockingPageTest : public ChromeRenderViewHostTestHarness {
@@ -795,4 +875,162 @@
   ui_manager_->GetThreatDetails()->clear();
 }
 
+class SafeBrowsingBlockingQuietPageTest
+    : public ChromeRenderViewHostTestHarness {
+ public:
+  // The decision the user made.
+  enum UserResponse { PENDING, OK, CANCEL };
+
+  SafeBrowsingBlockingQuietPageTest() {
+    // The safe browsing UI manager does not need a service for this test.
+    ui_manager_ = new TestSafeBrowsingUIManager(NULL);
+  }
+
+  void SetUp() override {
+    ChromeRenderViewHostTestHarness::SetUp();
+
+    SafeBrowsingBlockingPage::RegisterFactory(&factory_);
+    SafeBrowsingUIManager::CreateWhitelistForTesting(web_contents());
+
+    safe_browsing::TestSafeBrowsingServiceFactory sb_service_factory;
+    sb_service_factory.SetTestUIManager(ui_manager_.get());
+    auto* safe_browsing_service =
+        sb_service_factory.CreateSafeBrowsingService();
+    // A profile was created already but SafeBrowsingService wasn't around to
+    // get notified of it, so include that notification now.
+    safe_browsing_service->AddPrefService(
+        Profile::FromBrowserContext(web_contents()->GetBrowserContext())
+            ->GetPrefs());
+    TestingBrowserProcess::GetGlobal()->SetSafeBrowsingService(
+        safe_browsing_service);
+    g_browser_process->safe_browsing_service()->Initialize();
+  }
+
+  void TearDown() override {
+    // Release the UI manager before the BrowserThreads are destroyed.
+    ui_manager_ = NULL;
+    TestingBrowserProcess::GetGlobal()->safe_browsing_service()->ShutDown();
+    TestingBrowserProcess::GetGlobal()->SetSafeBrowsingService(nullptr);
+    SafeBrowsingBlockingPage::RegisterFactory(NULL);
+    // Clean up singleton reference (crbug.com/110594).
+    ThreatDetails::RegisterFactory(NULL);
+    ChromeRenderViewHostTestHarness::TearDown();
+  }
+
+  void OnBlockingPageComplete(bool proceed) {
+    if (proceed)
+      user_response_ = OK;
+    else
+      user_response_ = CANCEL;
+  }
+
+  void ShowInterstitial(bool is_subresource,
+                        const char* url,
+                        SBThreatType type) {
+    security_interstitials::UnsafeResource resource;
+    InitResource(&resource, is_subresource, GURL(url), type);
+    SafeBrowsingBlockingPage::ShowBlockingPage(ui_manager_.get(), resource);
+  }
+
+  // Returns the SafeBrowsingBlockingPage currently showing or NULL if none is
+  // showing.
+  TestSafeBrowsingBlockingPageQuiet* GetSafeBrowsingBlockingPage() {
+    InterstitialPage* interstitial =
+        InterstitialPage::GetInterstitialPage(web_contents());
+    if (!interstitial)
+      return NULL;
+    return static_cast<TestSafeBrowsingBlockingPageQuiet*>(
+        interstitial->GetDelegateForTesting());
+  }
+
+  scoped_refptr<TestSafeBrowsingUIManager> ui_manager_;
+
+  // Owned by TestSafeBrowsingBlockingQuietPage.
+  MockTestingProfile* mock_profile_;
+
+ private:
+  void InitResource(security_interstitials::UnsafeResource* resource,
+                    bool is_subresource,
+                    const GURL& url,
+                    SBThreatType type) {
+    resource->callback =
+        base::Bind(&SafeBrowsingBlockingQuietPageTest::OnBlockingPageComplete,
+                   base::Unretained(this));
+    resource->callback_thread = content::BrowserThread::GetTaskRunnerForThread(
+        content::BrowserThread::IO);
+    resource->url = url;
+    resource->is_subresource = is_subresource;
+    resource->threat_type = type;
+    resource->web_contents_getter =
+        security_interstitials::UnsafeResource::GetWebContentsGetter(
+            web_contents()->GetRenderProcessHost()->GetID(),
+            web_contents()->GetMainFrame()->GetRoutingID());
+    resource->threat_source = safe_browsing::ThreatSource::LOCAL_PVER3;
+  }
+
+  UserResponse user_response_;
+  TestSafeBrowsingBlockingQuietPageFactory factory_;
+};
+
+// Tests showing a quiet blocking page for a malware page.
+TEST_F(SafeBrowsingBlockingQuietPageTest, MalwarePage) {
+  // Start a load.
+  controller().LoadURL(GURL(kBadURL), content::Referrer(),
+                       ui::PAGE_TRANSITION_TYPED, std::string());
+
+  // Simulate the load causing a safe browsing interstitial to be shown.
+  ShowInterstitial(false, kBadURL, SB_THREAT_TYPE_URL_MALWARE);
+  TestSafeBrowsingBlockingPageQuiet* sb_interstitial =
+      GetSafeBrowsingBlockingPage();
+  ASSERT_TRUE(sb_interstitial);
+
+  base::DictionaryValue load_time_data = sb_interstitial->GetUIStrings();
+  base::string16 str;
+  load_time_data.GetString("heading", &str);
+  EXPECT_EQ(str, l10n_util::GetStringUTF16(IDS_MALWARE_WEBVIEW_HEADING));
+  bool is_giant;
+  load_time_data.GetBoolean("is_giant", &is_giant);
+  EXPECT_FALSE(is_giant);
+}
+
+// Tests showing a quiet blocking page for a phishing page.
+TEST_F(SafeBrowsingBlockingQuietPageTest, PhishingPage) {
+  // Start a load.
+  controller().LoadURL(GURL(kBadURL), content::Referrer(),
+                       ui::PAGE_TRANSITION_TYPED, std::string());
+
+  // Simulate the load causing a safe browsing interstitial to be shown.
+  ShowInterstitial(false, kBadURL, SB_THREAT_TYPE_URL_PHISHING);
+  TestSafeBrowsingBlockingPageQuiet* sb_interstitial =
+      GetSafeBrowsingBlockingPage();
+  ASSERT_TRUE(sb_interstitial);
+
+  base::DictionaryValue load_time_data = sb_interstitial->GetUIStrings();
+  base::string16 str;
+  load_time_data.GetString("heading", &str);
+  EXPECT_EQ(str, l10n_util::GetStringUTF16(IDS_PHISHING_WEBVIEW_HEADING));
+  bool is_giant;
+  load_time_data.GetBoolean("is_giant", &is_giant);
+  EXPECT_FALSE(is_giant);
+}
+
+// Tests showing a quiet blocking page in a giant webview.
+TEST_F(SafeBrowsingBlockingQuietPageTest, GiantWebView) {
+  // Start a load.
+  controller().LoadURL(GURL(kBadURL), content::Referrer(),
+                       ui::PAGE_TRANSITION_TYPED, std::string());
+
+  // Simulate the load causing a safe browsing interstitial to be shown.
+  ShowInterstitial(false, kBadURL, SB_THREAT_TYPE_URL_MALWARE);
+  TestSafeBrowsingBlockingPageQuiet* sb_interstitial =
+      GetSafeBrowsingBlockingPage();
+  EXPECT_TRUE(sb_interstitial);
+
+  sb_interstitial->SetGiantWebView();
+  base::DictionaryValue load_time_data = sb_interstitial->GetUIStrings();
+  bool is_giant;
+  load_time_data.GetBoolean("is_giant", &is_giant);
+  EXPECT_TRUE(is_giant);
+}
+
 }  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/safe_browsing_service.h b/chrome/browser/safe_browsing/safe_browsing_service.h
index c98e36b..262facf 100644
--- a/chrome/browser/safe_browsing/safe_browsing_service.h
+++ b/chrome/browser/safe_browsing/safe_browsing_service.h
@@ -234,6 +234,7 @@
       content::BrowserThread::UI>;
   friend class base::DeleteHelper<SafeBrowsingService>;
   friend class SafeBrowsingBlockingPageTest;
+  friend class SafeBrowsingBlockingQuietPageTest;
   friend class SafeBrowsingServerTest;
   friend class SafeBrowsingServiceTest;
   friend class SafeBrowsingURLRequestContextGetter;
diff --git a/components/OWNERS b/components/OWNERS
index 00b920b..08e4a212 100644
--- a/components/OWNERS
+++ b/components/OWNERS
@@ -21,6 +21,7 @@
 per-file [email protected]
 per-file physical_web_ui_strings.grdp=file://components/physical_web/OWNERS
 per-file policy_strings.grdp=file://components/policy/OWNERS
+per-file security_interstitials_resources.grdp=file://components/security_interstitials/OWNERS
 per-file security_interstitials_strings.grdp=file://components/security_interstitials/OWNERS
 per-file security_state_strings.grdp=file://components/security_state/OWNERS
 per-file ssl_errors_strings.grdp=file://components/ssl_errors/OWNERS
diff --git a/components/neterror/resources/neterror.html b/components/neterror/resources/neterror.html
index e3c3032..3851e4fe 100644
--- a/components/neterror/resources/neterror.html
+++ b/components/neterror/resources/neterror.html
@@ -5,6 +5,7 @@
   <meta name="viewport" content="width=device-width, initial-scale=1.0,
                                  maximum-scale=1.0, user-scalable=no">
   <title i18n-content="title"></title>
+  <link rel="stylesheet" href="../../../components/security_interstitials/core/browser/resources/interstitial_common.css">
   <link rel="stylesheet" href="../../../components/security_interstitials/core/browser/resources/interstitial_v2.css">
   <link rel="stylesheet" href="neterror.css">
   <script src="../../../components/security_interstitials/core/browser/resources/interstitial_v2_mobile.js"></script>
diff --git a/components/resources/security_interstitials_resources.grdp b/components/resources/security_interstitials_resources.grdp
index 907ef5fe..2fb0f05c 100644
--- a/components/resources/security_interstitials_resources.grdp
+++ b/components/resources/security_interstitials_resources.grdp
@@ -2,4 +2,5 @@
 <grit-part>
   <include name="IDR_SECURITY_INTERSTITIAL_UI_HTML" file="../security_interstitials/core/browser/resources/interstitial_ui.html" flattenhtml="true" type="BINDATA" />
   <include name="IDR_SECURITY_INTERSTITIAL_HTML" file="../security_interstitials/core/browser/resources/interstitial_v2.html" flattenhtml="true" type="BINDATA" />
+  <include name="IDR_SECURITY_INTERSTITIAL_QUIET_HTML" file="../security_interstitials/core/browser/resources/interstitial_webview_quiet.html" flattenhtml="true" type="BINDATA" />
 </grit-part>
\ No newline at end of file
diff --git a/components/safe_browsing/base_blocking_page.cc b/components/safe_browsing/base_blocking_page.cc
index b1b57c6b..6c1272f 100644
--- a/components/safe_browsing/base_blocking_page.cc
+++ b/components/safe_browsing/base_blocking_page.cc
@@ -208,7 +208,7 @@
 
 void BaseBlockingPage::PopulateInterstitialStrings(
     base::DictionaryValue* load_time_data) {
-  sb_error_ui_->PopulateStringsForHTML(load_time_data);
+  sb_error_ui_->PopulateStringsForHtml(load_time_data);
 }
 
 void BaseBlockingPage::FinishThreatDetails(const base::TimeDelta& delay,
diff --git a/components/security_interstitials/core/BUILD.gn b/components/security_interstitials/core/BUILD.gn
index f29f158c..b63c920 100644
--- a/components/security_interstitials/core/BUILD.gn
+++ b/components/security_interstitials/core/BUILD.gn
@@ -16,6 +16,8 @@
     "metrics_helper.h",
     "safe_browsing_loud_error_ui.cc",
     "safe_browsing_loud_error_ui.h",
+    "safe_browsing_quiet_error_ui.cc",
+    "safe_browsing_quiet_error_ui.h",
     "ssl_error_ui.cc",
     "ssl_error_ui.h",
   ]
diff --git a/components/security_interstitials/core/base_safe_browsing_error_ui.h b/components/security_interstitials/core/base_safe_browsing_error_ui.h
index 02d2517..bd7cf52a 100644
--- a/components/security_interstitials/core/base_safe_browsing_error_ui.h
+++ b/components/security_interstitials/core/base_safe_browsing_error_ui.h
@@ -123,7 +123,7 @@
   GURL request_url() const { return request_url_; }
   GURL main_frame_url() const { return main_frame_url_; }
 
-  virtual void PopulateStringsForHTML(
+  virtual void PopulateStringsForHtml(
       base::DictionaryValue* load_time_data) = 0;
   virtual void HandleCommand(SecurityInterstitialCommands command) = 0;
 
diff --git a/components/security_interstitials/core/browser/resources/extended_reporting.js b/components/security_interstitials/core/browser/resources/extended_reporting.js
index 5a72ad1..6ebec2c 100644
--- a/components/security_interstitials/core/browser/resources/extended_reporting.js
+++ b/components/security_interstitials/core/browser/resources/extended_reporting.js
@@ -41,7 +41,7 @@
 
   $('opt-in-checkbox').addEventListener('click', function() {
     sendCommand($('opt-in-checkbox').checked ?
-                CMD_DO_REPORT :
-                CMD_DONT_REPORT);
+                SecurityInterstitialCommandId.CMD_DO_REPORT :
+                SecurityInterstitialCommandId.CMD_DONT_REPORT);
   });
 }
diff --git a/components/security_interstitials/core/browser/resources/images/blocked.svg b/components/security_interstitials/core/browser/resources/images/blocked.svg
new file mode 100644
index 0000000..102d2b0
--- /dev/null
+++ b/components/security_interstitials/core/browser/resources/images/blocked.svg
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="20px" height="16px" viewBox="0 0 20 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+  <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+    <g transform="translate(-2.000000, -4.000000)">
+      <g>
+          <polygon points="0 0 24 0 24 24 0 24"></polygon>
+          <path d="M20,4 C21,4 22,5 22,6 L22,18 C22,19 21,20 20,20 L4,20 C2.9,20 2,19.1 2,18 L2,6 C2,5 3,4 4,4 L20,4 Z M14.3923033,15.4598278 L15.4949742,14.3581062 L13.1207639,11.9859397 L15.5,9.60774749 L14.3973291,8.50602582 L12.0170879,10.8842181 L9.63182079,8.5 L8.52914991,9.60172166 L10.9154222,11.9859397 L8.5,14.3982783 L9.60267088,15.5 L12.0170879,13.0866571 L14.3923033,15.4598278 Z" fill-opacity="0.2" fill="#000000"></path>
+      </g>
+    </g>
+  </g>
+</svg>
\ No newline at end of file
diff --git a/components/security_interstitials/core/browser/resources/interstitial_common.css b/components/security_interstitials/core/browser/resources/interstitial_common.css
new file mode 100644
index 0000000..32a27b9
--- /dev/null
+++ b/components/security_interstitials/core/browser/resources/interstitial_common.css
@@ -0,0 +1,35 @@
+/* Copyright 2017 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file. */
+
+a {
+  color: rgb(88, 88, 88);
+}
+
+body {
+  background-color: rgb(247, 247, 247);
+  color: rgb(100, 100, 100);
+}
+
+#details-button {
+  background: inherit;
+  border: 0;
+  float: none;
+  margin: 0;
+  padding: 10px 0;
+  text-transform: uppercase;
+}
+
+.hidden {
+  display: none;
+}
+
+html {
+  -webkit-text-size-adjust: 100%;
+  font-size: 125%;
+}
+
+.icon {
+  background-repeat: no-repeat;
+  background-size: 100%;
+}
\ No newline at end of file
diff --git a/components/security_interstitials/core/browser/resources/interstitial_common.js b/components/security_interstitials/core/browser/resources/interstitial_common.js
new file mode 100644
index 0000000..e826a149
--- /dev/null
+++ b/components/security_interstitials/core/browser/resources/interstitial_common.js
@@ -0,0 +1,46 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This is the shared code for security interstitials. It is used for both SSL
+// interstitials and Safe Browsing interstitials.
+
+// Should match security_interstitials::SecurityInterstitialCommands
+/** @enum| {string} */
+var SecurityInterstitialCommandId = {
+  CMD_DONT_PROCEED: 0,
+  CMD_PROCEED: 1,
+  // Ways for user to get more information
+  CMD_SHOW_MORE_SECTION: 2,
+  CMD_OPEN_HELP_CENTER: 3,
+  CMD_OPEN_DIAGNOSTIC: 4,
+  // Primary button actions
+  CMD_RELOAD: 5,
+  CMD_OPEN_DATE_SETTINGS: 6,
+  CMD_OPEN_LOGIN: 7,
+  // Safe Browsing Extended Reporting
+  CMD_DO_REPORT: 8,
+  CMD_DONT_REPORT: 9,
+  CMD_OPEN_REPORTING_PRIVACY: 10,
+  CMD_OPEN_WHITEPAPER: 11,
+  // Report a phishing error.
+  CMD_REPORT_PHISHING_ERROR: 12
+};
+
+/**
+ * A convenience method for sending commands to the parent page.
+ * @param {string} cmd  The command to send.
+ */
+function sendCommand(cmd) {
+// <if expr="not is_ios">
+  window.domAutomationController.setAutomationId(1);
+  window.domAutomationController.send(cmd);
+// </if>
+// <if expr="is_ios">
+  // TODO(crbug.com/565877): Revisit message passing for WKWebView.
+  var iframe = document.createElement('IFRAME');
+  iframe.setAttribute('src', 'js-command:' + cmd);
+  document.documentElement.appendChild(iframe);
+  iframe.parentNode.removeChild(iframe);
+// </if>
+}
\ No newline at end of file
diff --git a/components/security_interstitials/core/browser/resources/interstitial_v2.css b/components/security_interstitials/core/browser/resources/interstitial_v2.css
index d82e6bd..5af309a 100644
--- a/components/security_interstitials/core/browser/resources/interstitial_v2.css
+++ b/components/security_interstitials/core/browser/resources/interstitial_v2.css
@@ -2,21 +2,12 @@
    Use of this source code is governed by a BSD-style license that can be
    found in the LICENSE file. */
 
-a {
-  color: #585858;
-}
-
 .bad-clock .icon {
   background-image: -webkit-image-set(
       url(images/1x/clock.png) 1x,
       url(images/2x/clock.png) 2x);
 }
 
-body {
-  background-color: #f7f7f7;
-  color: #646464;
-}
-
 body.safe-browsing {
   background-color: rgb(206, 52, 38);
   color: white;
@@ -81,15 +72,6 @@
   margin-top: 20px;
 }
 
-#details-button {
-  background: inherit;
-  border: 0;
-  float: none;
-  margin: 0;
-  padding: 10px 0;
-  text-transform: uppercase;
-}
-
 #details-button:hover {
   box-shadow: inherit;
   text-decoration: underline;
@@ -118,18 +100,7 @@
   font-weight: normal;
 }
 
-.hidden {
-  display: none;
-}
-
-html {
-  -webkit-text-size-adjust: 100%;
-  font-size: 125%;
-}
-
 .icon {
-  background-repeat: no-repeat;
-  background-size: 100%;
   height: 72px;
   margin: 0 0 40px;
   width: 72px;
diff --git a/components/security_interstitials/core/browser/resources/interstitial_v2.html b/components/security_interstitials/core/browser/resources/interstitial_v2.html
index 9a3ad69..f382689f 100644
--- a/components/security_interstitials/core/browser/resources/interstitial_v2.html
+++ b/components/security_interstitials/core/browser/resources/interstitial_v2.html
@@ -1,16 +1,18 @@
 <!doctype html>
-<html i18n-values="dir:textdirection;lang:language">
+<html dir="$i18n{textdirection}" lang="$i18n{language}">
 <head>
   <meta charset="utf-8">
   <meta name="viewport"
       content="initial-scale=1, minimum-scale=1, width=device-width">
   <title i18n-content="tabTitle"></title>
+  <link rel="stylesheet" href="interstitial_common.css">
   <link rel="stylesheet" href="interstitial_v2.css">
   <script src="../../../../../ui/webui/resources/js/util.js"></script>
   <script src="captive_portal.js"></script>
   <script src="ssl.js"></script>
   <script src="extended_reporting.js"></script>
   <script src="interstitial_v2_mobile.js"></script>
+  <script src="interstitial_common.js"></script>
   <script src="interstitial_v2.js"></script>
 </head>
 <body id="body">
@@ -18,7 +20,7 @@
     <div id="main-content">
       <div class="icon" id="icon"></div>
       <div id="main-message">
-        <h1 i18n-content="heading"></h1>
+        <h1>$i18n{heading}</h1>
         <p i18n-values=".innerHTML:primaryParagraph"></p>
         <div id="debugging">
           <div id="error-code" class="error-code"></div>
@@ -36,9 +38,8 @@
       </div>
     </div>
     <div class="nav-wrapper">
-      <button i18n-content="primaryButtonText" id="primary-button"></button>
-      <button id="details-button" class="small-link"
-          i18n-content="openDetails"></button>
+      <button id="primary-button">$i18n{primaryButtonText}</button>
+      <button id="details-button" class="small-link">$i18n{openDetails}</button>
     </div>
     <div id="details" class="hidden">
       <p i18n-values=".innerHTML:explanationParagraph"></p>
diff --git a/components/security_interstitials/core/browser/resources/interstitial_v2.js b/components/security_interstitials/core/browser/resources/interstitial_v2.js
index 88ad830e..c570798 100644
--- a/components/security_interstitials/core/browser/resources/interstitial_v2.js
+++ b/components/security_interstitials/core/browser/resources/interstitial_v2.js
@@ -8,43 +8,6 @@
 var expandedDetails = false;
 var keyPressState = 0;
 
-// Should match security_interstitials::SecurityInterstitialCommands
-var CMD_DONT_PROCEED = 0;
-var CMD_PROCEED = 1;
-// Ways for user to get more information
-var CMD_SHOW_MORE_SECTION = 2;
-var CMD_OPEN_HELP_CENTER = 3;
-var CMD_OPEN_DIAGNOSTIC = 4;
-// Primary button actions
-var CMD_RELOAD = 5;
-var CMD_OPEN_DATE_SETTINGS = 6;
-var CMD_OPEN_LOGIN = 7;
-// Safe Browsing Extended Reporting
-var CMD_DO_REPORT = 8;
-var CMD_DONT_REPORT = 9;
-var CMD_OPEN_REPORTING_PRIVACY = 10;
-var CMD_OPEN_WHITEPAPER = 11;
-// Report a phishing error.
-var CMD_REPORT_PHISHING_ERROR = 12;
-
-/**
- * A convenience method for sending commands to the parent page.
- * @param {string} cmd  The command to send.
- */
-function sendCommand(cmd) {
-// <if expr="not is_ios">
-  window.domAutomationController.setAutomationId(1);
-  window.domAutomationController.send(cmd);
-// </if>
-// <if expr="is_ios">
-  // TODO(crbug.com/565877): Revisit message passing for WKWebView.
-  var iframe = document.createElement('IFRAME');
-  iframe.setAttribute('src', 'js-command:' + cmd);
-  document.documentElement.appendChild(iframe);
-  iframe.parentNode.removeChild(iframe);
-// </if>
-}
-
 /**
  * This allows errors to be skippped by typing a secret phrase into the page.
  * @param {string} e The key that was just pressed.
@@ -54,7 +17,7 @@
   if (BYPASS_SEQUENCE.charCodeAt(keyPressState) == e.keyCode) {
     keyPressState++;
     if (keyPressState == BYPASS_SEQUENCE.length) {
-      sendCommand(CMD_PROCEED);
+      sendCommand(SecurityInterstitialCommandId.CMD_PROCEED);
       keyPressState = 0;
     }
   } else {
@@ -122,20 +85,20 @@
     $('primary-button').addEventListener('click', function() {
       switch (interstitialType) {
         case 'CAPTIVE_PORTAL':
-          sendCommand(CMD_OPEN_LOGIN);
+          sendCommand(SecurityInterstitialCommandId.CMD_OPEN_LOGIN);
           break;
 
         case 'SSL':
           if (badClock)
-            sendCommand(CMD_OPEN_DATE_SETTINGS);
+            sendCommand(SecurityInterstitialCommandId.CMD_OPEN_DATE_SETTINGS);
           else if (overridable)
-            sendCommand(CMD_DONT_PROCEED);
+            sendCommand(SecurityInterstitialCommandId.CMD_DONT_PROCEED);
           else
-            sendCommand(CMD_RELOAD);
+            sendCommand(SecurityInterstitialCommandId.CMD_RELOAD);
           break;
 
         case 'SAFEBROWSING':
-          sendCommand(CMD_DONT_PROCEED);
+          sendCommand(SecurityInterstitialCommandId.CMD_DONT_PROCEED);
           break;
 
         default:
@@ -147,7 +110,7 @@
   if (overridable) {
     // Captive portal page isn't overridable.
     $('proceed-link').addEventListener('click', function(event) {
-      sendCommand(CMD_PROCEED);
+      sendCommand(SecurityInterstitialCommandId.CMD_PROCEED);
     });
   } else if (!ssl) {
     $('final-paragraph').classList.add('hidden');
@@ -159,13 +122,13 @@
 
   if ($('diagnostic-link')) {
     $('diagnostic-link').addEventListener('click', function(event) {
-      sendCommand(CMD_OPEN_DIAGNOSTIC);
+      sendCommand(SecurityInterstitialCommandId.CMD_OPEN_DIAGNOSTIC);
     });
   }
 
   if ($('learn-more-link')) {
     $('learn-more-link').addEventListener('click', function(event) {
-      sendCommand(CMD_OPEN_HELP_CENTER);
+      sendCommand(SecurityInterstitialCommandId.CMD_OPEN_HELP_CENTER);
     });
   }
 
@@ -188,7 +151,7 @@
           loadTimeData.getString('closeDetails');
       if (!expandedDetails) {
         // Record a histogram entry only the first time that details is opened.
-        sendCommand(CMD_SHOW_MORE_SECTION);
+        sendCommand(SecurityInterstitialCommandId.CMD_SHOW_MORE_SECTION);
         expandedDetails = true;
       }
     });
@@ -199,7 +162,7 @@
   if (interstitialType == 'SAFEBROWSING' &&
       loadTimeData.getBoolean('phishing') && $('report-error-link')) {
     $('report-error-link').addEventListener('click', function(event) {
-      sendCommand(CMD_REPORT_PHISHING_ERROR);
+      sendCommand(SecurityInterstitialCommandId.CMD_REPORT_PHISHING_ERROR);
     });
   }
 
diff --git a/components/security_interstitials/core/browser/resources/interstitial_webview_quiet.css b/components/security_interstitials/core/browser/resources/interstitial_webview_quiet.css
new file mode 100644
index 0000000..80a87648c
--- /dev/null
+++ b/components/security_interstitials/core/browser/resources/interstitial_webview_quiet.css
@@ -0,0 +1,112 @@
+/* Copyright 2017 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file. */
+
+body {
+  margin: 0;
+}
+
+#details {
+  box-sizing: border-box;
+  height: auto;
+  line-height: 1.48em;
+  margin: 0;
+  opacity: 1;
+  transition: opacity 250ms cubic-bezier(0.4, 0, 0.2, 1);
+}
+
+#details.hidden {
+  display: block;
+  height: 0;
+  opacity: 0;
+  overflow: hidden;
+  padding-bottom: 0;
+  transition: none;
+}
+
+#details-link {
+  color: rgba(0,0,0,.38);
+  /* For V1, the details link is hidden. */
+  display: none;
+  text-decoration: underline;
+  text-transform: none;
+}
+
+h1 {
+  color: rgba(0,0,0,.38);
+  font-size: 1.037037em;
+  line-height: 1.4em;
+  margin: 8px 0 8px;
+}
+
+.giant .icon {
+  bottom: 0;
+  left: 0;
+  margin: auto;
+  position: fixed;
+  right: 0;
+  top: 0;
+}
+
+.giant #details,
+.giant #main-message {
+  display: none;
+}
+
+.icon {
+  background-image: url(images/blocked.svg);
+  height: 20vh;
+  margin: 0 auto;
+  max-height: 36px;
+  max-width: 36px;
+  min-height: 18px;
+  min-width: 18px;
+  opacity: .54;
+  width: 20vh;
+}
+
+.interstitial-wrapper {
+  align-items: center;
+  box-sizing: border-box;
+  display: flex;
+  flex-direction: column;
+  font-size: 0.9em;
+  height: 100vh;
+  justify-content: center;
+  line-height: 1.6em;
+  margin: 0 auto;
+  max-width: 640px;
+  padding: 16px;
+  width: 100%;
+}
+
+#main-content {
+  align-self: auto;
+  color: rgba(0, 0, 0, .54);
+  flex: 0 1 auto;
+  text-align: center;
+}
+
+@media (max-height:2em), (max-width:2em) {
+  .icon {
+    display: none;
+  }
+}
+
+@media (min-height:25em) and (min-width:37.5em),
+       (min-height:37.5em) and (min-width:25em) {
+  .icon {
+    height: 36px;
+    width: 36px;
+  }
+}
+
+/* Views that don't fit the details text. */
+@media (max-height:11.25em) and (max-width:18.75em),
+       (max-height:18.75em) and (max-width:11.25em),
+       (max-height:5em), (max-width:5em) {
+  #details,
+  #main-message {
+    display: none;
+  }
+}
diff --git a/components/security_interstitials/core/browser/resources/interstitial_webview_quiet.html b/components/security_interstitials/core/browser/resources/interstitial_webview_quiet.html
new file mode 100644
index 0000000..63de889
--- /dev/null
+++ b/components/security_interstitials/core/browser/resources/interstitial_webview_quiet.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<html dir="$i18n{textdirection}" lang="$i18n{language}">
+<head>
+  <meta charset="utf-8">
+  <meta name="viewport"
+      content="initial-scale=1, minimum-scale=1, width=device-width">
+  <title>$i18n{tabTitle}</title>
+  <link rel="stylesheet" href="interstitial_common.css">
+  <link rel="stylesheet" href="interstitial_webview_quiet.css">
+  <script src="../../../../../ui/webui/resources/js/util.js"></script>
+  <script src="interstitial_webview_quiet.js"></script>
+</head>
+<body id="body">
+  <div class="interstitial-wrapper">
+    <div id="main-content">
+      <div class="icon"></div>
+      <div id="main-message">
+        <h1>
+          <span>$i18n{heading}</span>
+          <a id="details-link">$i18n{openDetails}</a>
+        </h1>
+      </div>
+    </div>
+    <div id="details" class="hidden">
+      <p>
+        $i18Raw{explanationParagraph}
+      </p>
+    </div>
+  </div>
+</body>
+</html>
diff --git a/components/security_interstitials/core/browser/resources/interstitial_webview_quiet.js b/components/security_interstitials/core/browser/resources/interstitial_webview_quiet.js
new file mode 100644
index 0000000..cb189d7
--- /dev/null
+++ b/components/security_interstitials/core/browser/resources/interstitial_webview_quiet.js
@@ -0,0 +1,10 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+function initPage() {
+  var isGiantWebView = loadTimeData.getBoolean('is_giant');
+  document.body.className = isGiantWebView ? 'giant' : '';
+}
+
+document.addEventListener('DOMContentLoaded', initPage);
diff --git a/components/security_interstitials/core/safe_browsing_loud_error_ui.cc b/components/security_interstitials/core/safe_browsing_loud_error_ui.cc
index 8c0df9a..96207898 100644
--- a/components/security_interstitials/core/safe_browsing_loud_error_ui.cc
+++ b/components/security_interstitials/core/safe_browsing_loud_error_ui.cc
@@ -78,7 +78,7 @@
   controller()->metrics_helper()->RecordShutdownMetrics();
 }
 
-void SafeBrowsingLoudErrorUI::PopulateStringsForHTML(
+void SafeBrowsingLoudErrorUI::PopulateStringsForHtml(
     base::DictionaryValue* load_time_data) {
   DCHECK(load_time_data);
 
diff --git a/components/security_interstitials/core/safe_browsing_loud_error_ui.h b/components/security_interstitials/core/safe_browsing_loud_error_ui.h
index c90391a..76efa5a63 100644
--- a/components/security_interstitials/core/safe_browsing_loud_error_ui.h
+++ b/components/security_interstitials/core/safe_browsing_loud_error_ui.h
@@ -35,7 +35,7 @@
   ~SafeBrowsingLoudErrorUI() override;
 
   // Implement BaseSafeBrowsingErrorUI.
-  void PopulateStringsForHTML(base::DictionaryValue* load_time_data) override;
+  void PopulateStringsForHtml(base::DictionaryValue* load_time_data) override;
   void HandleCommand(SecurityInterstitialCommands command) override;
 
  private:
diff --git a/components/security_interstitials/core/safe_browsing_quiet_error_ui.cc b/components/security_interstitials/core/safe_browsing_quiet_error_ui.cc
new file mode 100644
index 0000000..0167025
--- /dev/null
+++ b/components/security_interstitials/core/safe_browsing_quiet_error_ui.cc
@@ -0,0 +1,95 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/security_interstitials/core/safe_browsing_quiet_error_ui.h"
+
+#include "base/i18n/time_formatting.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/google/core/browser/google_util.h"
+#include "components/security_interstitials/core/common_string_util.h"
+#include "components/security_interstitials/core/metrics_helper.h"
+#include "components/strings/grit/components_strings.h"
+#include "net/base/escape.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace security_interstitials {
+
+SafeBrowsingQuietErrorUI::SafeBrowsingQuietErrorUI(
+    const GURL& request_url,
+    const GURL& main_frame_url,
+    BaseSafeBrowsingErrorUI::SBInterstitialReason reason,
+    const BaseSafeBrowsingErrorUI::SBErrorDisplayOptions& display_options,
+    const std::string& app_locale,
+    const base::Time& time_triggered,
+    ControllerClient* controller,
+    const bool is_giant_webview)
+    : BaseSafeBrowsingErrorUI(request_url,
+                              main_frame_url,
+                              reason,
+                              display_options,
+                              app_locale,
+                              time_triggered,
+                              controller),
+      is_giant_webview_(is_giant_webview) {
+  controller->metrics_helper()->RecordUserDecision(MetricsHelper::SHOW);
+  controller->metrics_helper()->RecordUserInteraction(
+      MetricsHelper::TOTAL_VISITS);
+  if (is_proceed_anyway_disabled()) {
+    controller->metrics_helper()->RecordUserDecision(
+        security_interstitials::MetricsHelper::PROCEEDING_DISABLED);
+  }
+}
+
+SafeBrowsingQuietErrorUI::~SafeBrowsingQuietErrorUI() {
+  controller()->metrics_helper()->RecordShutdownMetrics();
+}
+
+void SafeBrowsingQuietErrorUI::PopulateStringsForHtml(
+    base::DictionaryValue* load_time_data) {
+  DCHECK(load_time_data);
+
+  load_time_data->SetString("type", "SAFEBROWSING");
+  load_time_data->SetString(
+      "tabTitle", l10n_util::GetStringUTF16(IDS_SAFEBROWSING_V3_TITLE));
+  load_time_data->SetBoolean("overridable", !is_proceed_anyway_disabled());
+  load_time_data->SetString(
+      "openDetails",
+      l10n_util::GetStringUTF16(IDS_SAFEBROWSING_V3_OPEN_DETAILS_BUTTON));
+  load_time_data->SetBoolean("is_giant", is_giant_webview_);
+
+  bool phishing =
+      interstitial_reason() == BaseSafeBrowsingErrorUI::SB_REASON_PHISHING;
+  load_time_data->SetBoolean("phishing", phishing);
+  load_time_data->SetString(
+      "heading", phishing
+                     ? l10n_util::GetStringUTF16(IDS_PHISHING_WEBVIEW_HEADING)
+                     : l10n_util::GetStringUTF16(IDS_MALWARE_WEBVIEW_HEADING));
+
+  int explanation_ids = -1;
+  if (phishing)
+    explanation_ids = IDS_PHISHING_WEBVIEW_EXPLANATION_PARAGRAPH;
+  else if (interstitial_reason() == BaseSafeBrowsingErrorUI::SB_REASON_MALWARE)
+    explanation_ids = IDS_MALWARE_WEBVIEW_EXPLANATION_PARAGRAPH;
+
+  if (explanation_ids > -1) {
+    load_time_data->SetString("explanationParagraph",
+                              l10n_util::GetStringUTF16(explanation_ids));
+  } else {
+    NOTREACHED();
+  }
+}
+
+void SafeBrowsingQuietErrorUI::SetGiantWebViewForTesting(
+    bool is_giant_webview) {
+  is_giant_webview_ = is_giant_webview;
+}
+
+void SafeBrowsingQuietErrorUI::HandleCommand(
+    SecurityInterstitialCommands command) {
+  NOTREACHED();
+}
+
+}  // security_interstitials
diff --git a/components/security_interstitials/core/safe_browsing_quiet_error_ui.h b/components/security_interstitials/core/safe_browsing_quiet_error_ui.h
new file mode 100644
index 0000000..948a8d66
--- /dev/null
+++ b/components/security_interstitials/core/safe_browsing_quiet_error_ui.h
@@ -0,0 +1,55 @@
+// 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_SECURITY_INTERSTITIALS_CORE_SAFE_BROWSING_QUIET_ERROR_UI_H_
+#define COMPONENTS_SECURITY_INTERSTITIALS_CORE_SAFE_BROWSING_QUIET_ERROR_UI_H_
+
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "components/security_interstitials/core/base_safe_browsing_error_ui.h"
+#include "components/security_interstitials/core/controller_client.h"
+#include "url/gurl.h"
+
+namespace security_interstitials {
+
+// Quiet version of the safe browsing interstitial. This is the small screen
+// version of the interstitial selectively used in parts of WebView.
+// This class displays a quiet UI for Safe Browsing errors that block page loads
+// specifically for WebView. This class is purely about visual display; it does
+// not do any error-handling logic to determine what type of error should be
+// displayed when.
+class SafeBrowsingQuietErrorUI
+    : public security_interstitials::BaseSafeBrowsingErrorUI {
+ public:
+  SafeBrowsingQuietErrorUI(
+      const GURL& request_url,
+      const GURL& main_frame_url,
+      BaseSafeBrowsingErrorUI::SBInterstitialReason reason,
+      const BaseSafeBrowsingErrorUI::SBErrorDisplayOptions& display_options,
+      const std::string& app_locale,
+      const base::Time& time_triggered,
+      ControllerClient* controller,
+      const bool is_giant_webview);
+  ~SafeBrowsingQuietErrorUI() override;
+
+  // Fills the passed dictionary with the values to be passed to the template
+  // when creating the HTML.
+  void PopulateStringsForHtml(base::DictionaryValue* load_time_data) override;
+
+  void HandleCommand(SecurityInterstitialCommands command) override;
+
+  // Manually set whether displaying in a giant WebView. Specifially used in
+  // tests.
+  void SetGiantWebViewForTesting(bool is_giant_webview);
+
+ private:
+  bool is_giant_webview_;
+
+  DISALLOW_COPY_AND_ASSIGN(SafeBrowsingQuietErrorUI);
+};
+
+}  // security_interstitials
+
+#endif  // COMPONENTS_SECURITY_INTERSTITIALS_CORE_SAFE_BROWSING_QUIET_ERROR_UI_H_
diff --git a/components/security_interstitials_strings.grdp b/components/security_interstitials_strings.grdp
index 51b64d82..6693485eb 100644
--- a/components/security_interstitials_strings.grdp
+++ b/components/security_interstitials_strings.grdp
@@ -186,4 +186,18 @@
     You can <ph name="BEGIN_ERROR_LINK">&lt;a href="#" id="report-error-link"&gt;</ph>report a detection problem<ph name="END_ERROR_LINK">&lt;/a&gt;</ph> or, if you understand the risks to your security, <ph name="BEGIN_LINK">&lt;a href="#" id="proceed-link"&gt;</ph>visit this unsafe site<ph name="END_LINK">&lt;/a&gt;</ph>.
   </message>
 
+  <!-- WebView Safe Browsing quiet interstitals medium sized -->
+  <message name="IDS_MALWARE_WEBVIEW_HEADING" desc="The heading of the malware interstitial on medium sized Webview.">
+    Dangerous content blocked
+  </message>
+  <message name="IDS_MALWARE_WEBVIEW_EXPLANATION_PARAGRAPH" desc="The explanation of why Safe Browsing has blocked the page. Allows the user to proceed using a link.">
+    This content might try to install dangerous software on your device that steals or deletes your information. <ph name="BEGIN_LINK">&lt;a href="#" id="proceed-link"&gt;</ph>Show anyway<ph name="END_LINK">&lt;/a&gt;</ph>.
+  </message>
+  <message name="IDS_PHISHING_WEBVIEW_HEADING" desc="The heading of the phishing interstitial on medium sized Webview.">
+    Deceptive content blocked
+  </message>
+  <message name="IDS_PHISHING_WEBVIEW_EXPLANATION_PARAGRAPH" desc="The explanation of why Safe Browsing has blocked the page. Allows the user to proceed using a link.">
+    This content might try to trick you into installing software or revealing personal information. <ph name="BEGIN_LINK">&lt;a href="#" id="proceed-link"&gt;</ph>Show anyway<ph name="END_LINK">&lt;/a&gt;</ph>.
+  </message>
+
 </grit-part>