| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/ssl/captive_portal_blocking_page.h" | 5 #include "chrome/browser/ssl/captive_portal_blocking_page.h" |
| 6 | 6 |
| 7 #include <string> | 7 #include <string> |
| 8 | 8 |
| 9 #include "base/callback.h" | 9 #include "base/callback.h" |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| 11 #include "base/macros.h" | 11 #include "base/macros.h" |
| 12 #include "base/prefs/pref_service.h" | 12 #include "base/prefs/pref_service.h" |
| 13 #include "base/strings/stringprintf.h" | 13 #include "base/strings/stringprintf.h" |
| 14 #include "chrome/browser/profiles/profile.h" | 14 #include "chrome/browser/profiles/profile.h" |
| 15 #include "chrome/browser/ui/browser.h" | 15 #include "chrome/browser/ui/browser.h" |
| 16 #include "chrome/browser/ui/tabs/tab_strip_model.h" | 16 #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| 17 #include "chrome/common/pref_names.h" | 17 #include "chrome/common/pref_names.h" |
| 18 #include "chrome/test/base/in_process_browser_test.h" | 18 #include "chrome/test/base/in_process_browser_test.h" |
| 19 #include "components/captive_portal/captive_portal_detector.h" | 19 #include "components/captive_portal/captive_portal_detector.h" |
| 20 #include "content/public/browser/interstitial_page.h" | 20 #include "content/public/browser/interstitial_page.h" |
| 21 #include "content/public/browser/web_contents.h" | 21 #include "content/public/browser/web_contents.h" |
| 22 #include "content/public/test/browser_test_utils.h" | 22 #include "content/public/test/browser_test_utils.h" |
| 23 #include "testing/gtest/include/gtest/gtest.h" | 23 #include "testing/gtest/include/gtest/gtest.h" |
| 24 #include "url/gurl.h" | 24 #include "url/gurl.h" |
| 25 | 25 |
| 26 namespace { |
| 26 // Partial text in the captive portal interstitial's main paragraph when the | 27 // Partial text in the captive portal interstitial's main paragraph when the |
| 27 // login domain isn't displayed. | 28 // login domain isn't displayed. |
| 28 const char kGenericLoginURLText[] = "its login page"; | 29 const char kGenericLoginURLText[] = "its login page"; |
| 29 const char kBrokenSSL[] = "https://broken.ssl"; | 30 const char kBrokenSSL[] = "https://broken.ssl"; |
| 31 const char kWiFiSSID[] = "WiFiSSID"; |
| 30 | 32 |
| 31 // Returns true if the interstitial contains |text| in its body. | 33 // Returns true if the interstitial contains |text| in its body. |
| 32 bool IsInterstitialDisplayingText(content::InterstitialPage* interstitial, | 34 bool IsInterstitialDisplayingText(content::InterstitialPage* interstitial, |
| 33 const std::string& text) { | 35 const std::string& text) { |
| 34 // It's valid for |text| to contain "\'", but simply look for "'" instead | 36 // It's valid for |text| to contain "\'", but simply look for "'" instead |
| 35 // since this function is used for searching host names and a predefined | 37 // since this function is used for searching host names and a predefined |
| 36 // string. | 38 // string. |
| 37 DCHECK(text.find("\'") == std::string::npos); | 39 DCHECK(text.find("\'") == std::string::npos); |
| 38 std::string command = base::StringPrintf( | 40 std::string command = base::StringPrintf( |
| 39 "var hasText = document.body.textContent.indexOf('%s') >= 0;" | 41 "var hasText = document.body.textContent.indexOf('%s') >= 0;" |
| 40 "window.domAutomationController.send(hasText);", | 42 "window.domAutomationController.send(hasText);", |
| 41 text.c_str()); | 43 text.c_str()); |
| 42 bool result = false; | 44 bool result = false; |
| 43 EXPECT_TRUE(content::ExecuteScriptAndExtractBool( | 45 EXPECT_TRUE(content::ExecuteScriptAndExtractBool( |
| 44 interstitial->GetMainFrame(), command, &result)); | 46 interstitial->GetMainFrame(), command, &result)); |
| 45 return result; | 47 return result; |
| 46 } | 48 } |
| 47 | 49 |
| 50 enum ExpectWiFi { |
| 51 EXPECT_WIFI_NO, |
| 52 EXPECT_WIFI_YES |
| 53 }; |
| 54 |
| 55 enum ExpectWiFiSSID { |
| 56 EXPECT_WIFI_SSID_NO, |
| 57 EXPECT_WIFI_SSID_YES |
| 58 }; |
| 59 |
| 60 enum ExpectLoginURL { |
| 61 EXPECT_LOGIN_URL_NO, |
| 62 EXPECT_LOGIN_URL_YES |
| 63 }; |
| 64 |
| 65 } // namespace |
| 66 |
| 48 class CaptivePortalBlockingPageTest : public InProcessBrowserTest { | 67 class CaptivePortalBlockingPageTest : public InProcessBrowserTest { |
| 49 public: | 68 public: |
| 50 CaptivePortalBlockingPageTest() {} | 69 CaptivePortalBlockingPageTest() {} |
| 51 | 70 |
| 71 void TestInterstitial(bool is_wifi_connection, |
| 72 const std::string& wifi_ssid, |
| 73 const GURL& login_url, |
| 74 ExpectWiFi expect_wifi, |
| 75 ExpectWiFiSSID expect_wifi_ssid, |
| 76 ExpectLoginURL expect_login_url, |
| 77 const std::string& expected_login_hostname); |
| 78 |
| 79 void TestInterstitial(bool is_wifi_connection, |
| 80 const std::string& wifi_ssid, |
| 81 const GURL& login_url, |
| 82 ExpectWiFi expect_wifi, |
| 83 ExpectWiFiSSID expect_wifi_ssid, |
| 84 ExpectLoginURL expect_login_url); |
| 85 |
| 52 private: | 86 private: |
| 53 DISALLOW_COPY_AND_ASSIGN(CaptivePortalBlockingPageTest); | 87 DISALLOW_COPY_AND_ASSIGN(CaptivePortalBlockingPageTest); |
| 54 }; | 88 }; |
| 55 | 89 |
| 56 // If the connection is not a Wi-Fi connection, the wired network version of the | 90 void CaptivePortalBlockingPageTest::TestInterstitial( |
| 57 // captive portal interstitial should be displayed. | 91 bool is_wifi_connection, |
| 58 IN_PROC_BROWSER_TEST_F(CaptivePortalBlockingPageTest, | 92 const std::string& wifi_ssid, |
| 59 ShowWiredNetworkInterstitial) { | 93 const GURL& login_url, |
| 60 const GURL kLandingUrl("http://captive.portal/landing_url"); | 94 ExpectWiFi expect_wifi, |
| 95 ExpectWiFiSSID expect_wifi_ssid, |
| 96 ExpectLoginURL expect_login_url, |
| 97 const std::string& expected_login_hostname) { |
| 61 content::WebContents* contents = | 98 content::WebContents* contents = |
| 62 browser()->tab_strip_model()->GetActiveWebContents(); | 99 browser()->tab_strip_model()->GetActiveWebContents(); |
| 63 DCHECK(contents); | 100 DCHECK(contents); |
| 64 // Blocking page is owned by the interstitial. | 101 // Blocking page is owned by the interstitial. |
| 65 CaptivePortalBlockingPage* blocking_page = new CaptivePortalBlockingPage( | 102 CaptivePortalBlockingPage* blocking_page = new CaptivePortalBlockingPage( |
| 66 contents, GURL(kBrokenSSL), kLandingUrl, base::Callback<void(bool)>()); | 103 contents, GURL(kBrokenSSL), login_url, base::Callback<void(bool)>()); |
| 67 blocking_page->SetWiFiConnectionForTesting(false); | 104 blocking_page->SetWiFiConnectionForTesting(is_wifi_connection); |
| 105 blocking_page->SetWiFiSSIDForTesting(wifi_ssid); |
| 68 blocking_page->Show(); | 106 blocking_page->Show(); |
| 69 | 107 |
| 70 WaitForInterstitialAttach(contents); | 108 WaitForInterstitialAttach(contents); |
| 71 EXPECT_TRUE( | 109 EXPECT_TRUE( |
| 72 WaitForRenderFrameReady(contents->GetInterstitialPage()->GetMainFrame())); | 110 WaitForRenderFrameReady(contents->GetInterstitialPage()->GetMainFrame())); |
| 73 EXPECT_FALSE( | 111 EXPECT_EQ(expect_wifi == EXPECT_WIFI_YES, |
| 74 IsInterstitialDisplayingText(contents->GetInterstitialPage(), "Wi-Fi")); | 112 IsInterstitialDisplayingText(contents->GetInterstitialPage(), |
| 113 "Wi-Fi")); |
| 114 if (!wifi_ssid.empty()) { |
| 115 EXPECT_EQ(expect_wifi_ssid == EXPECT_WIFI_SSID_YES, |
| 116 IsInterstitialDisplayingText(contents->GetInterstitialPage(), |
| 117 wifi_ssid)); |
| 118 } |
| 119 EXPECT_EQ(expect_login_url == EXPECT_LOGIN_URL_YES, |
| 120 IsInterstitialDisplayingText(contents->GetInterstitialPage(), |
| 121 expected_login_hostname)); |
| 122 EXPECT_EQ(expect_login_url == EXPECT_LOGIN_URL_NO, |
| 123 IsInterstitialDisplayingText(contents->GetInterstitialPage(), |
| 124 kGenericLoginURLText)); |
| 125 } |
| 126 |
| 127 void CaptivePortalBlockingPageTest::TestInterstitial( |
| 128 bool is_wifi_connection, |
| 129 const std::string& wifi_ssid, |
| 130 const GURL& login_url, |
| 131 ExpectWiFi expect_wifi, |
| 132 ExpectWiFiSSID expect_wifi_ssid, |
| 133 ExpectLoginURL expect_login_url) { |
| 134 TestInterstitial(is_wifi_connection, wifi_ssid, login_url, |
| 135 expect_wifi, expect_wifi_ssid, expect_login_url, |
| 136 login_url.host()); |
| 137 } |
| 138 |
| 139 // If the connection is not a Wi-Fi connection, the wired network version of the |
| 140 // captive portal interstitial should be displayed. |
| 141 IN_PROC_BROWSER_TEST_F(CaptivePortalBlockingPageTest, |
| 142 WiredNetwork_LoginURL) { |
| 143 TestInterstitial(false, kWiFiSSID, GURL("http://captive.portal/landing_url"), |
| 144 EXPECT_WIFI_NO, EXPECT_WIFI_SSID_NO, EXPECT_LOGIN_URL_YES); |
| 145 |
| 146 } |
| 147 |
| 148 // Same as above, expect the login URL is the same as the captive portal ping |
| 149 // url (i.e. the portal intercepts requests without using HTTP redirects), in |
| 150 // which case the login URL shouldn't be displayed. |
| 151 IN_PROC_BROWSER_TEST_F(CaptivePortalBlockingPageTest, |
| 152 WiredNetwork_NoLoginURL) { |
| 153 const GURL kLandingUrl(captive_portal::CaptivePortalDetector::kDefaultURL); |
| 154 TestInterstitial(false, kWiFiSSID, kLandingUrl, |
| 155 EXPECT_WIFI_NO, EXPECT_WIFI_SSID_NO, EXPECT_LOGIN_URL_NO); |
| 75 } | 156 } |
| 76 | 157 |
| 77 // If the connection is a Wi-Fi connection, the Wi-Fi version of the captive | 158 // If the connection is a Wi-Fi connection, the Wi-Fi version of the captive |
| 78 // portal interstitial should be displayed. | 159 // portal interstitial should be displayed. |
| 79 IN_PROC_BROWSER_TEST_F(CaptivePortalBlockingPageTest, | 160 IN_PROC_BROWSER_TEST_F(CaptivePortalBlockingPageTest, |
| 80 ShowWiFiInterstitial) { | 161 WiFi_SSID_LoginURL) { |
| 81 const GURL kLandingUrl("http://captive.portal/landing_url"); | 162 TestInterstitial(true, kWiFiSSID, GURL("http://captive.portal/landing_url"), |
| 82 content::WebContents* contents = | 163 EXPECT_WIFI_YES, EXPECT_WIFI_SSID_YES, EXPECT_LOGIN_URL_YES); |
| 83 browser()->tab_strip_model()->GetActiveWebContents(); | |
| 84 DCHECK(contents); | |
| 85 // Blocking page is owned by the interstitial. | |
| 86 CaptivePortalBlockingPage* blocking_page = new CaptivePortalBlockingPage( | |
| 87 contents, GURL(kBrokenSSL), kLandingUrl, base::Callback<void(bool)>()); | |
| 88 blocking_page->SetWiFiConnectionForTesting(true); | |
| 89 blocking_page->Show(); | |
| 90 | |
| 91 WaitForInterstitialAttach(contents); | |
| 92 EXPECT_TRUE( | |
| 93 WaitForRenderFrameReady(contents->GetInterstitialPage()->GetMainFrame())); | |
| 94 EXPECT_TRUE( | |
| 95 IsInterstitialDisplayingText(contents->GetInterstitialPage(), "Wi-Fi")); | |
| 96 } | 164 } |
| 97 | 165 |
| 98 // The captive portal interstitial should show the login url if the login url | 166 // Same as above, with login URL but no SSID. |
| 99 // is different than the captive portal ping url (i.e. the portal intercepts | |
| 100 // requests via HTTP redirects). | |
| 101 IN_PROC_BROWSER_TEST_F(CaptivePortalBlockingPageTest, | 167 IN_PROC_BROWSER_TEST_F(CaptivePortalBlockingPageTest, |
| 102 ShowLoginDomainIfPortalRedirectsDetectionURL) { | 168 WiFi_NoSSID_LoginURL) { |
| 103 const GURL kLandingUrl("http://captive.portal/landing_url"); | 169 TestInterstitial(true, "", GURL("http://captive.portal/landing_url"), |
| 104 content::WebContents* contents = | 170 EXPECT_WIFI_YES, EXPECT_WIFI_SSID_NO, EXPECT_LOGIN_URL_YES); |
| 105 browser()->tab_strip_model()->GetActiveWebContents(); | |
| 106 DCHECK(contents); | |
| 107 // Blocking page is owned by the interstitial. | |
| 108 CaptivePortalBlockingPage* blocking_page = new CaptivePortalBlockingPage( | |
| 109 contents, GURL(kBrokenSSL), kLandingUrl, base::Callback<void(bool)>()); | |
| 110 blocking_page->SetWiFiConnectionForTesting(false); | |
| 111 blocking_page->Show(); | |
| 112 | |
| 113 WaitForInterstitialAttach(contents); | |
| 114 EXPECT_TRUE( | |
| 115 WaitForRenderFrameReady(contents->GetInterstitialPage()->GetMainFrame())); | |
| 116 | |
| 117 EXPECT_TRUE(IsInterstitialDisplayingText(contents->GetInterstitialPage(), | |
| 118 kLandingUrl.host())); | |
| 119 EXPECT_FALSE(IsInterstitialDisplayingText(contents->GetInterstitialPage(), | |
| 120 kGenericLoginURLText)); | |
| 121 } | 171 } |
| 122 | 172 |
| 123 // The captive portal interstitial should show a generic text if the login url | 173 // Same as above, with SSID but no login URL. |
| 124 // is the same as the captive portal ping url (i.e. the portal intercepts | |
| 125 // requests without using HTTP redirects). | |
| 126 IN_PROC_BROWSER_TEST_F(CaptivePortalBlockingPageTest, | 174 IN_PROC_BROWSER_TEST_F(CaptivePortalBlockingPageTest, |
| 127 DontShowLoginDomainIfPortalDoesntRedirectDetectionURL) { | 175 WiFi_SSID_NoLoginURL) { |
| 128 const GURL kLandingUrl(captive_portal::CaptivePortalDetector::kDefaultURL); | 176 const GURL kLandingUrl(captive_portal::CaptivePortalDetector::kDefaultURL); |
| 129 content::WebContents* contents = | 177 TestInterstitial(true, kWiFiSSID, kLandingUrl, |
| 130 browser()->tab_strip_model()->GetActiveWebContents(); | 178 EXPECT_WIFI_YES, EXPECT_WIFI_SSID_YES, EXPECT_LOGIN_URL_NO); |
| 131 DCHECK(contents); | |
| 132 // Blocking page is owned by the interstitial. | |
| 133 CaptivePortalBlockingPage* blocking_page = new CaptivePortalBlockingPage( | |
| 134 contents, GURL(kBrokenSSL), kLandingUrl, base::Callback<void(bool)>()); | |
| 135 blocking_page->SetWiFiConnectionForTesting(false); | |
| 136 blocking_page->Show(); | |
| 137 | |
| 138 WaitForInterstitialAttach(contents); | |
| 139 EXPECT_TRUE( | |
| 140 WaitForRenderFrameReady(contents->GetInterstitialPage()->GetMainFrame())); | |
| 141 | |
| 142 EXPECT_FALSE(IsInterstitialDisplayingText(contents->GetInterstitialPage(), | |
| 143 kLandingUrl.host())); | |
| 144 EXPECT_TRUE(IsInterstitialDisplayingText(contents->GetInterstitialPage(), | |
| 145 kGenericLoginURLText)); | |
| 146 } | 179 } |
| 147 | 180 |
| 148 class CaptivePortalBlockingPageIDNTest : public InProcessBrowserTest { | 181 // Same as above, with no SSID and no login URL. |
| 182 IN_PROC_BROWSER_TEST_F(CaptivePortalBlockingPageTest, |
| 183 WiFi_NoSSID_NoLoginURL) { |
| 184 const GURL kLandingUrl(captive_portal::CaptivePortalDetector::kDefaultURL); |
| 185 TestInterstitial(true, "", kLandingUrl, |
| 186 EXPECT_WIFI_YES, EXPECT_WIFI_SSID_NO, EXPECT_LOGIN_URL_NO); |
| 187 } |
| 188 |
| 189 class CaptivePortalBlockingPageIDNTest : public CaptivePortalBlockingPageTest { |
| 149 public: | 190 public: |
| 150 // InProcessBrowserTest: | 191 // InProcessBrowserTest: |
| 151 void SetUpOnMainThread() override { | 192 void SetUpOnMainThread() override { |
| 152 // Clear AcceptLanguages to force punycode decoding. | 193 // Clear AcceptLanguages to force punycode decoding. |
| 153 browser()->profile()->GetPrefs()->SetString(prefs::kAcceptLanguages, | 194 browser()->profile()->GetPrefs()->SetString(prefs::kAcceptLanguages, |
| 154 std::string()); | 195 std::string()); |
| 155 } | 196 } |
| 156 }; | 197 }; |
| 157 | 198 |
| 158 // Same as ShowLoginDomainIfPortalRedirectsDetectionURL, except the login | 199 // Same as CaptivePortalBlockingPageTest.WiredNetwork_LoginURL, except the login |
| 159 // domain is an IDN. | 200 // domain is an IDN. |
| 160 IN_PROC_BROWSER_TEST_F(CaptivePortalBlockingPageIDNTest, | 201 IN_PROC_BROWSER_TEST_F(CaptivePortalBlockingPageIDNTest, |
| 161 ShowLoginIDNIfPortalRedirectsDetectionURL) { | 202 ShowLoginIDNIfPortalRedirectsDetectionURL) { |
| 162 const char kHostname[] = | 203 const char kHostname[] = |
| 163 "xn--d1abbgf6aiiy.xn--p1ai"; | 204 "xn--d1abbgf6aiiy.xn--p1ai"; |
| 164 const char kHostnameJSUnicode[] = | 205 const char kHostnameJSUnicode[] = |
| 165 "\\u043f\\u0440\\u0435\\u0437\\u0438\\u0434\\u0435\\u043d\\u0442." | 206 "\\u043f\\u0440\\u0435\\u0437\\u0438\\u0434\\u0435\\u043d\\u0442." |
| 166 "\\u0440\\u0444"; | 207 "\\u0440\\u0444"; |
| 167 std::string landing_url_spec = | 208 std::string landing_url_spec = |
| 168 base::StringPrintf("http://%s/landing_url", kHostname); | 209 base::StringPrintf("http://%s/landing_url", kHostname); |
| 169 GURL landing_url(landing_url_spec); | 210 GURL landing_url(landing_url_spec); |
| 170 | 211 |
| 171 content::WebContents* contents = | 212 TestInterstitial(false, kWiFiSSID, landing_url, |
| 172 browser()->tab_strip_model()->GetActiveWebContents(); | 213 EXPECT_WIFI_NO, EXPECT_WIFI_SSID_NO, EXPECT_LOGIN_URL_YES, |
| 173 DCHECK(contents); | 214 kHostnameJSUnicode); |
| 174 // Blocking page is owned by the interstitial. | |
| 175 CaptivePortalBlockingPage* blocking_page = new CaptivePortalBlockingPage( | |
| 176 contents, GURL(kBrokenSSL), landing_url, base::Callback<void(bool)>()); | |
| 177 blocking_page->SetWiFiConnectionForTesting(false); | |
| 178 blocking_page->Show(); | |
| 179 | |
| 180 WaitForInterstitialAttach(contents); | |
| 181 EXPECT_TRUE( | |
| 182 WaitForRenderFrameReady(contents->GetInterstitialPage()->GetMainFrame())); | |
| 183 | |
| 184 EXPECT_TRUE(IsInterstitialDisplayingText(contents->GetInterstitialPage(), | |
| 185 kHostnameJSUnicode)); | |
| 186 EXPECT_FALSE(IsInterstitialDisplayingText(contents->GetInterstitialPage(), | |
| 187 kGenericLoginURLText)); | |
| 188 } | 215 } |
| OLD | NEW |