Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(644)

Side by Side Diff: chrome/browser/captive_portal/captive_portal_browsertest.cc

Issue 318213002: Add custom interstitial for captive portals. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Stop the timer at the right place Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 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 <map> 5 #include <map>
6 #include <set> 6 #include <set>
7 7
8 #include "base/basictypes.h" 8 #include "base/basictypes.h"
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/command_line.h" 10 #include "base/command_line.h"
11 #include "base/compiler_specific.h" 11 #include "base/compiler_specific.h"
12 #include "base/files/file_path.h" 12 #include "base/files/file_path.h"
13 #include "base/message_loop/message_loop.h" 13 #include "base/message_loop/message_loop.h"
14 #include "base/path_service.h" 14 #include "base/path_service.h"
15 #include "base/prefs/pref_service.h" 15 #include "base/prefs/pref_service.h"
16 #include "base/strings/utf_string_conversions.h" 16 #include "base/strings/utf_string_conversions.h"
17 #include "chrome/browser/captive_portal/captive_portal_service.h" 17 #include "chrome/browser/captive_portal/captive_portal_service.h"
18 #include "chrome/browser/captive_portal/captive_portal_service_factory.h" 18 #include "chrome/browser/captive_portal/captive_portal_service_factory.h"
19 #include "chrome/browser/captive_portal/captive_portal_tab_helper.h" 19 #include "chrome/browser/captive_portal/captive_portal_tab_helper.h"
20 #include "chrome/browser/captive_portal/captive_portal_tab_reloader.h" 20 #include "chrome/browser/captive_portal/captive_portal_tab_reloader.h"
21 #include "chrome/browser/chrome_notification_types.h" 21 #include "chrome/browser/chrome_notification_types.h"
22 #include "chrome/browser/interstitials/security_interstitial_page.h"
22 #include "chrome/browser/net/url_request_mock_util.h" 23 #include "chrome/browser/net/url_request_mock_util.h"
23 #include "chrome/browser/profiles/profile.h" 24 #include "chrome/browser/profiles/profile.h"
24 #include "chrome/browser/ui/browser.h" 25 #include "chrome/browser/ui/browser.h"
25 #include "chrome/browser/ui/browser_commands.h" 26 #include "chrome/browser/ui/browser_commands.h"
26 #include "chrome/browser/ui/browser_finder.h" 27 #include "chrome/browser/ui/browser_finder.h"
27 #include "chrome/browser/ui/browser_navigator.h" 28 #include "chrome/browser/ui/browser_navigator.h"
28 #include "chrome/browser/ui/tab_contents/tab_contents_iterator.h" 29 #include "chrome/browser/ui/tab_contents/tab_contents_iterator.h"
29 #include "chrome/browser/ui/tabs/tab_strip_model.h" 30 #include "chrome/browser/ui/tabs/tab_strip_model.h"
30 #include "chrome/common/chrome_paths.h" 31 #include "chrome/common/chrome_paths.h"
31 #include "chrome/common/chrome_switches.h" 32 #include "chrome/common/chrome_switches.h"
32 #include "chrome/common/pref_names.h" 33 #include "chrome/common/pref_names.h"
33 #include "chrome/test/base/in_process_browser_test.h" 34 #include "chrome/test/base/in_process_browser_test.h"
34 #include "chrome/test/base/ui_test_utils.h" 35 #include "chrome/test/base/ui_test_utils.h"
35 #include "content/public/browser/browser_thread.h" 36 #include "content/public/browser/browser_thread.h"
37 #include "content/public/browser/interstitial_page.h"
38 #include "content/public/browser/interstitial_page_delegate.h"
36 #include "content/public/browser/navigation_controller.h" 39 #include "content/public/browser/navigation_controller.h"
40 #include "content/public/browser/navigation_entry.h"
37 #include "content/public/browser/notification_observer.h" 41 #include "content/public/browser/notification_observer.h"
38 #include "content/public/browser/notification_registrar.h" 42 #include "content/public/browser/notification_registrar.h"
39 #include "content/public/browser/notification_service.h" 43 #include "content/public/browser/notification_service.h"
40 #include "content/public/browser/notification_types.h" 44 #include "content/public/browser/notification_types.h"
41 #include "content/public/browser/render_frame_host.h" 45 #include "content/public/browser/render_frame_host.h"
42 #include "content/public/browser/web_contents.h" 46 #include "content/public/browser/web_contents.h"
43 #include "content/public/common/url_constants.h" 47 #include "content/public/common/url_constants.h"
44 #include "net/base/net_errors.h" 48 #include "net/base/net_errors.h"
45 #include "net/http/transport_security_state.h" 49 #include "net/http/transport_security_state.h"
46 #include "net/test/url_request/url_request_failed_job.h" 50 #include "net/test/url_request/url_request_failed_job.h"
(...skipping 636 matching lines...) Expand 10 before | Expand all | Expand 10 after
683 } 687 }
684 } 688 }
685 689
686 // An observer for watching the CaptivePortalService. It tracks the last 690 // An observer for watching the CaptivePortalService. It tracks the last
687 // received result and the total number of received results. 691 // received result and the total number of received results.
688 class CaptivePortalObserver : public content::NotificationObserver { 692 class CaptivePortalObserver : public content::NotificationObserver {
689 public: 693 public:
690 explicit CaptivePortalObserver(Profile* profile); 694 explicit CaptivePortalObserver(Profile* profile);
691 695
692 // Runs the message loop until until at exactly |update_count| capitive portal 696 // Runs the message loop until until at exactly |update_count| capitive portal
693 // results have been received, since this creation of |this|. Expects no 697 // results have been received, since the creation of |this|. Expects no
694 // additional captive portal results. 698 // additional captive portal results.
695 void WaitForResults(int num_results_to_wait_for); 699 void WaitForResults(int num_results_to_wait_for);
696 700
697 int num_results_received() const { return num_results_received_; } 701 int num_results_received() const { return num_results_received_; }
698 702
699 CaptivePortalResult captive_portal_result() const { 703 CaptivePortalResult captive_portal_result() const {
700 return captive_portal_result_; 704 return captive_portal_result_;
701 } 705 }
702 706
703 private: 707 private:
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after
809 void EnableCaptivePortalDetection(Profile* profile, bool enabled); 813 void EnableCaptivePortalDetection(Profile* profile, bool enabled);
810 814
811 // Sets up the captive portal service for the given profile so that 815 // Sets up the captive portal service for the given profile so that
812 // all checks go to |test_url|. Also disables all timers. 816 // all checks go to |test_url|. Also disables all timers.
813 void SetUpCaptivePortalService(Profile* profile, const GURL& test_url); 817 void SetUpCaptivePortalService(Profile* profile, const GURL& test_url);
814 818
815 // Returns true if |browser|'s profile is currently running a captive portal 819 // Returns true if |browser|'s profile is currently running a captive portal
816 // check. 820 // check.
817 bool CheckPending(Browser* browser); 821 bool CheckPending(Browser* browser);
818 822
823 // Returns the type of the interstitial being shown.
824 SecurityInterstitialPage::Type GetInterstitialType(
825 WebContents* contents) const;
826
819 // Returns the CaptivePortalTabReloader::State of |web_contents|. 827 // Returns the CaptivePortalTabReloader::State of |web_contents|.
820 CaptivePortalTabReloader::State GetStateOfTabReloader( 828 CaptivePortalTabReloader::State GetStateOfTabReloader(
821 WebContents* web_contents) const; 829 WebContents* web_contents) const;
822 830
823 // Returns the CaptivePortalTabReloader::State of the indicated tab. 831 // Returns the CaptivePortalTabReloader::State of the indicated tab.
824 CaptivePortalTabReloader::State GetStateOfTabReloaderAt(Browser* browser, 832 CaptivePortalTabReloader::State GetStateOfTabReloaderAt(Browser* browser,
825 int index) const; 833 int index) const;
826 834
827 // Returns the number of tabs with the given state, across all profiles. 835 // Returns the number of tabs with the given state, across all profiles.
828 int NumTabsWithState(CaptivePortalTabReloader::State state) const; 836 int NumTabsWithState(CaptivePortalTabReloader::State state) const;
(...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after
942 void RunNavigateLoadingTabToTimeoutTest(Browser* browser, 950 void RunNavigateLoadingTabToTimeoutTest(Browser* browser,
943 const GURL& starting_url, 951 const GURL& starting_url,
944 const GURL& interrupted_url, 952 const GURL& interrupted_url,
945 const GURL& timeout_url); 953 const GURL& timeout_url);
946 954
947 // Sets the timeout used by a CaptivePortalTabReloader on slow SSL loads 955 // Sets the timeout used by a CaptivePortalTabReloader on slow SSL loads
948 // before a captive portal check. 956 // before a captive portal check.
949 void SetSlowSSLLoadTime(CaptivePortalTabReloader* tab_reloader, 957 void SetSlowSSLLoadTime(CaptivePortalTabReloader* tab_reloader,
950 base::TimeDelta slow_ssl_load_time); 958 base::TimeDelta slow_ssl_load_time);
951 959
960 void SetSSLErrorDisplayDelay(CaptivePortalTabHelper* tab_helper,
961 base::TimeDelta ssl_error_delay);
962
952 CaptivePortalTabReloader* GetTabReloader(WebContents* web_contents) const; 963 CaptivePortalTabReloader* GetTabReloader(WebContents* web_contents) const;
953 964
954 private: 965 private:
955 DISALLOW_COPY_AND_ASSIGN(CaptivePortalBrowserTest); 966 DISALLOW_COPY_AND_ASSIGN(CaptivePortalBrowserTest);
956 }; 967 };
957 968
958 CaptivePortalBrowserTest::CaptivePortalBrowserTest() { 969 CaptivePortalBrowserTest::CaptivePortalBrowserTest() {
959 } 970 }
960 971
961 void CaptivePortalBrowserTest::SetUpOnMainThread() { 972 void CaptivePortalBrowserTest::SetUpOnMainThread() {
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
1005 } 1016 }
1006 1017
1007 bool CaptivePortalBrowserTest::CheckPending(Browser* browser) { 1018 bool CaptivePortalBrowserTest::CheckPending(Browser* browser) {
1008 CaptivePortalService* captive_portal_service = 1019 CaptivePortalService* captive_portal_service =
1009 CaptivePortalServiceFactory::GetForProfile(browser->profile()); 1020 CaptivePortalServiceFactory::GetForProfile(browser->profile());
1010 1021
1011 return captive_portal_service->DetectionInProgress() || 1022 return captive_portal_service->DetectionInProgress() ||
1012 captive_portal_service->TimerRunning(); 1023 captive_portal_service->TimerRunning();
1013 } 1024 }
1014 1025
1026 SecurityInterstitialPage::Type CaptivePortalBrowserTest::GetInterstitialType(
1027 WebContents* contents) const {
1028 DCHECK(contents->ShowingInterstitialPage());
1029 SecurityInterstitialPage* blocking_page =
1030 static_cast<SecurityInterstitialPage*>(
1031 contents->GetInterstitialPage()->GetDelegateForTesting());
1032 DCHECK(blocking_page);
1033 return blocking_page->GetTypeForTesting();
1034 }
1035
1015 CaptivePortalTabReloader::State CaptivePortalBrowserTest::GetStateOfTabReloader( 1036 CaptivePortalTabReloader::State CaptivePortalBrowserTest::GetStateOfTabReloader(
1016 WebContents* web_contents) const { 1037 WebContents* web_contents) const {
1017 return GetTabReloader(web_contents)->state(); 1038 return GetTabReloader(web_contents)->state();
1018 } 1039 }
1019 1040
1020 CaptivePortalTabReloader::State 1041 CaptivePortalTabReloader::State
1021 CaptivePortalBrowserTest::GetStateOfTabReloaderAt(Browser* browser, 1042 CaptivePortalBrowserTest::GetStateOfTabReloaderAt(Browser* browser,
1022 int index) const { 1043 int index) const {
1023 return GetStateOfTabReloader( 1044 return GetStateOfTabReloader(
1024 browser->tab_strip_model()->GetWebContentsAt(index)); 1045 browser->tab_strip_model()->GetWebContentsAt(index));
(...skipping 514 matching lines...) Expand 10 before | Expand all | Expand 10 after
1539 // Timeout occurs, and page is automatically reloaded. 1560 // Timeout occurs, and page is automatically reloaded.
1540 FailLoadsAfterLogin(browser, 1); 1561 FailLoadsAfterLogin(browser, 1);
1541 } 1562 }
1542 1563
1543 void CaptivePortalBrowserTest::SetSlowSSLLoadTime( 1564 void CaptivePortalBrowserTest::SetSlowSSLLoadTime(
1544 CaptivePortalTabReloader* tab_reloader, 1565 CaptivePortalTabReloader* tab_reloader,
1545 base::TimeDelta slow_ssl_load_time) { 1566 base::TimeDelta slow_ssl_load_time) {
1546 tab_reloader->set_slow_ssl_load_time(slow_ssl_load_time); 1567 tab_reloader->set_slow_ssl_load_time(slow_ssl_load_time);
1547 } 1568 }
1548 1569
1570 void CaptivePortalBrowserTest::SetSSLErrorDisplayDelay(
1571 CaptivePortalTabHelper* tab_helper,
1572 base::TimeDelta ssl_error_delay) {
1573 tab_helper->SetSSLErrorDelayForTest(ssl_error_delay);
1574 }
1575
1549 CaptivePortalTabReloader* CaptivePortalBrowserTest::GetTabReloader( 1576 CaptivePortalTabReloader* CaptivePortalBrowserTest::GetTabReloader(
1550 WebContents* web_contents) const { 1577 WebContents* web_contents) const {
1551 return CaptivePortalTabHelper::FromWebContents(web_contents)-> 1578 return CaptivePortalTabHelper::FromWebContents(web_contents)->
1552 GetTabReloaderForTest(); 1579 GetTabReloaderForTest();
1553 } 1580 }
1554 1581
1555 // Make sure there's no test for a captive portal on HTTP timeouts. This will 1582 // Make sure there's no test for a captive portal on HTTP timeouts. This will
1556 // also trigger the link doctor page, which results in the load of a second 1583 // also trigger the link doctor page, which results in the load of a second
1557 // error page. 1584 // error page.
1558 IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, HttpTimeout) { 1585 IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, HttpTimeout) {
(...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after
1698 } 1725 }
1699 1726
1700 // Checks the unlikely case that the tab times out before the timer triggers. 1727 // Checks the unlikely case that the tab times out before the timer triggers.
1701 // This most likely won't happen, but should still work: 1728 // This most likely won't happen, but should still work:
1702 IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, LoginFastTimeout) { 1729 IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, LoginFastTimeout) {
1703 FastTimeoutBehindCaptivePortal(browser(), true); 1730 FastTimeoutBehindCaptivePortal(browser(), true);
1704 Login(browser(), 0, 1); 1731 Login(browser(), 0, 1);
1705 } 1732 }
1706 1733
1707 // A cert error triggers a captive portal check and results in opening a login 1734 // A cert error triggers a captive portal check and results in opening a login
1735 // tab.
1736 IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest,
1737 ShowCaptivePortalInterstitialOnCertError) {
1738 net::SpawnedTestServer::SSLOptions https_options;
1739 https_options.server_certificate =
1740 net::SpawnedTestServer::SSLOptions::CERT_MISMATCHED_NAME;
1741 net::SpawnedTestServer https_server(
1742 net::SpawnedTestServer::TYPE_HTTPS, https_options,
1743 base::FilePath(FILE_PATH_LITERAL("chrome/test/data")));
1744 ASSERT_TRUE(https_server.Start());
1745
1746 // Set SSL interstitial delay long enough so that a captive portal result
1747 // is guaranteed to arrive during this window, and a captive portal
1748 // error page is displayed instead of an SSL interstitial.
1749 TabStripModel* tab_strip_model = browser()->tab_strip_model();
1750 SetSSLErrorDisplayDelay(
1751 CaptivePortalTabHelper::FromWebContents(
1752 tab_strip_model->GetActiveWebContents()),
1753 base::TimeDelta::FromHours(1));
1754
1755 content::WebContents* web_contents = tab_strip_model->GetActiveWebContents();
1756 // The path does not matter.
1757 GURL cert_error_url = https_server.GetURL(kTestServerLoginPath);
1758 // The interstitial should trigger a captive portal check when it opens, just
1759 // like navigating to kMockHttpsQuickTimeoutUrl.
1760 FastErrorBehindCaptivePortal(browser(), true, cert_error_url);
1761
1762 EXPECT_EQ(SecurityInterstitialPage::CAPTIVE_PORTAL,
1763 GetInterstitialType(web_contents));
mmenke 2014/11/10 17:32:08 Should switch back to having web_contents active,
meacer 2014/11/25 01:39:51 Done.
1764 }
1765
1766 // A cert error triggers a captive portal check and results in opening a login
1708 // tab. The user then logs in and the page with the error is reloaded. 1767 // tab. The user then logs in and the page with the error is reloaded.
1709 IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, SSLCertErrorLogin) { 1768 IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, SSLCertErrorLogin) {
1710 // Need an HTTP TestServer to handle a dynamically created server redirect. 1769 // Need an HTTP TestServer to handle a dynamically created server redirect.
1711 ASSERT_TRUE(test_server()->Start()); 1770 ASSERT_TRUE(test_server()->Start());
1712 1771
1713 net::SpawnedTestServer::SSLOptions https_options; 1772 net::SpawnedTestServer::SSLOptions https_options;
1714 https_options.server_certificate = 1773 https_options.server_certificate =
1715 net::SpawnedTestServer::SSLOptions::CERT_MISMATCHED_NAME; 1774 net::SpawnedTestServer::SSLOptions::CERT_MISMATCHED_NAME;
1716 net::SpawnedTestServer https_server( 1775 net::SpawnedTestServer https_server(
1717 net::SpawnedTestServer::TYPE_HTTPS, https_options, 1776 net::SpawnedTestServer::TYPE_HTTPS, https_options,
1718 base::FilePath(FILE_PATH_LITERAL("chrome/test/data"))); 1777 base::FilePath(FILE_PATH_LITERAL("chrome/test/data")));
1719 ASSERT_TRUE(https_server.Start()); 1778 ASSERT_TRUE(https_server.Start());
1720 1779
1780 // Set SSL interstitial delay to zero so that a captive portal result can not
1781 // arrive during this window, so an SSL interstitial is displayed instead
1782 // of a captive portal error page.
mmenke 2014/11/10 17:32:08 This is racy. We depend on the captive portal che
meacer 2014/11/14 00:30:25 The probes are triggered automatically from SSLErr
meacer 2014/11/14 01:51:24 I'm also not sure I understand the race here. What
mmenke 2014/11/14 15:28:55 The timer - you tell it to trigger instantly, but
mmenke 2014/11/14 15:28:55 I'm not sure if that fixes the race. I had been t
meacer 2014/11/25 01:39:51 Added a flag to tab reloader to enable/disable por
1783 TabStripModel* tab_strip_model = browser()->tab_strip_model();
1784 WebContents* broken_tab_contents = tab_strip_model->GetActiveWebContents();
1785 SetSSLErrorDisplayDelay(
1786 CaptivePortalTabHelper::FromWebContents(broken_tab_contents),
1787 base::TimeDelta());
1788
1721 // The path does not matter. 1789 // The path does not matter.
1722 GURL cert_error_url = https_server.GetURL(kTestServerLoginPath); 1790 GURL cert_error_url = https_server.GetURL(kTestServerLoginPath);
1723 // The interstitial should trigger a captive portal check when it opens, just 1791 // The interstitial should trigger a captive portal check when it opens, just
1724 // like navigating to kMockHttpsQuickTimeoutUrl. 1792 // like navigating to kMockHttpsQuickTimeoutUrl.
1725 FastErrorBehindCaptivePortal(browser(), true, cert_error_url); 1793 FastErrorBehindCaptivePortal(browser(), true, cert_error_url);
1726 1794
1795 EXPECT_EQ(SecurityInterstitialPage::SSL,
1796 GetInterstitialType(broken_tab_contents));
1797
1727 // Simulate logging in. Can't use Login() because the interstitial tab looks 1798 // Simulate logging in. Can't use Login() because the interstitial tab looks
1728 // like a cross between a hung tab (Load was never committed) and a tab at an 1799 // like a cross between a hung tab (Load was never committed) and a tab at an
1729 // error page (The load was stopped). 1800 // error page (The load was stopped).
1730 URLRequestMockCaptivePortalJobFactory::SetBehindCaptivePortal(false); 1801 URLRequestMockCaptivePortalJobFactory::SetBehindCaptivePortal(false);
1731 MultiNavigationObserver navigation_observer; 1802 MultiNavigationObserver navigation_observer;
1732 CaptivePortalObserver portal_observer(browser()->profile()); 1803 CaptivePortalObserver portal_observer(browser()->profile());
1733 1804
1734 TabStripModel* tab_strip_model = browser()->tab_strip_model();
1735 content::RenderFrameHost* render_frame_host = 1805 content::RenderFrameHost* render_frame_host =
1736 tab_strip_model->GetActiveWebContents()->GetMainFrame(); 1806 tab_strip_model->GetActiveWebContents()->GetMainFrame();
1737 render_frame_host->ExecuteJavaScript(base::ASCIIToUTF16("submitForm()")); 1807 render_frame_host->ExecuteJavaScript(base::ASCIIToUTF16("submitForm()"));
1738 1808
1739 // The captive portal tab navigation will trigger a captive portal check, 1809 // The captive portal tab navigation will trigger a captive portal check,
1740 // and reloading the original tab will bring up the interstitial page again, 1810 // and reloading the original tab will bring up the interstitial page again,
1741 // triggering a second captive portal check. 1811 // triggering a second captive portal check.
1742 portal_observer.WaitForResults(2); 1812 portal_observer.WaitForResults(2);
1743 1813
1744 // Wait for both tabs to finish loading. 1814 // Wait for both tabs to finish loading.
(...skipping 460 matching lines...) Expand 10 before | Expand all | Expand 10 after
2205 URLRequestFailedJob::GetMockHttpUrl(net::ERR_CONNECTION_TIMED_OUT); 2275 URLRequestFailedJob::GetMockHttpUrl(net::ERR_CONNECTION_TIMED_OUT);
2206 content::BrowserThread::PostTask( 2276 content::BrowserThread::PostTask(
2207 content::BrowserThread::IO, FROM_HERE, 2277 content::BrowserThread::IO, FROM_HERE,
2208 base::Bind(&AddHstsHost, 2278 base::Bind(&AddHstsHost,
2209 make_scoped_refptr(browser()->profile()->GetRequestContext()), 2279 make_scoped_refptr(browser()->profile()->GetRequestContext()),
2210 http_timeout_url.host())); 2280 http_timeout_url.host()));
2211 2281
2212 SlowLoadBehindCaptivePortal(browser(), true, http_timeout_url, 1, 1); 2282 SlowLoadBehindCaptivePortal(browser(), true, http_timeout_url, 1, 1);
2213 Login(browser(), 1, 0); 2283 Login(browser(), 1, 0);
2214 FailLoadsAfterLogin(browser(), 1); 2284 FailLoadsAfterLogin(browser(), 1);
2215 } 2285 }
mmenke 2014/11/10 17:32:08 Think we have a new error case that needs to be te
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698