[GTK] WebKitWebView::load-failed-with-tls-errors should receive the failing URI inste...
[WebKit-https.git] / Tools / TestWebKitAPI / Tests / WebKit2Gtk / TestSSL.cpp
1 /*
2  * Copyright (C) 2012 Igalia S.L.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public License
15  * along with this library; see the file COPYING.LIB.  If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 #include "config.h"
21
22 #include "LoadTrackingTest.h"
23 #include "WebKitTestServer.h"
24 #include <gtk/gtk.h>
25
26 static WebKitTestServer* kHttpsServer;
27 static WebKitTestServer* kHttpServer;
28
29 static const char* indexHTML = "<html><body>Testing WebKit2GTK+ SSL</body></htmll>";
30 static const char* insecureContentHTML = "<html><script src=\"%s\"></script><body><p>Text + image <img src=\"%s\" align=\"right\"/></p></body></html>";
31 static const char TLSExpectedSuccessTitle[] = "WebKit2Gtk+ TLS permission test";
32 static const char TLSSuccessHTMLString[] = "<html><head><title>WebKit2Gtk+ TLS permission test</title></head><body></body></html>";
33
34 class SSLTest: public LoadTrackingTest {
35 public:
36     MAKE_GLIB_TEST_FIXTURE(SSLTest);
37
38     SSLTest()
39         : m_tlsErrors(static_cast<GTlsCertificateFlags>(0))
40     {
41     }
42
43     virtual void provisionalLoadFailed(const gchar* failingURI, GError* error)
44     {
45         g_assert_error(error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED);
46         LoadTrackingTest::provisionalLoadFailed(failingURI, error);
47     }
48
49     virtual void loadCommitted()
50     {
51         GTlsCertificate* certificate = 0;
52         webkit_web_view_get_tls_info(m_webView, &certificate, &m_tlsErrors);
53         m_certificate = certificate;
54         LoadTrackingTest::loadCommitted();
55     }
56
57     void waitUntilLoadFinished()
58     {
59         m_certificate = 0;
60         m_tlsErrors = static_cast<GTlsCertificateFlags>(0);
61         LoadTrackingTest::waitUntilLoadFinished();
62     }
63
64     GRefPtr<GTlsCertificate> m_certificate;
65     GTlsCertificateFlags m_tlsErrors;
66 };
67
68 static void testSSL(SSLTest* test, gconstpointer)
69 {
70     test->loadURI(kHttpsServer->getURIForPath("/").data());
71     test->waitUntilLoadFinished();
72     g_assert(test->m_certificate);
73     // We always expect errors because we are using a self-signed certificate,
74     // but only G_TLS_CERTIFICATE_UNKNOWN_CA flags should be present.
75     g_assert(test->m_tlsErrors);
76     g_assert_cmpuint(test->m_tlsErrors, ==, G_TLS_CERTIFICATE_UNKNOWN_CA);
77
78     // Non HTTPS loads shouldn't have a certificate nor errors.
79     test->loadHtml(indexHTML, 0);
80     test->waitUntilLoadFinished();
81     g_assert(!test->m_certificate);
82     g_assert(!test->m_tlsErrors);
83 }
84
85 class InsecureContentTest: public WebViewTest {
86 public:
87     MAKE_GLIB_TEST_FIXTURE(InsecureContentTest);
88
89     InsecureContentTest()
90         : m_insecureContentRun(false)
91         , m_insecureContentDisplayed(false)
92     {
93         g_signal_connect(m_webView, "insecure-content-detected", G_CALLBACK(insecureContentDetectedCallback), this);
94     }
95
96     static void insecureContentDetectedCallback(WebKitWebView* webView, WebKitInsecureContentEvent event, InsecureContentTest* test)
97     {
98         g_assert(webView == test->m_webView);
99
100         if (event == WEBKIT_INSECURE_CONTENT_RUN)
101             test->m_insecureContentRun = true;
102
103         if (event == WEBKIT_INSECURE_CONTENT_DISPLAYED)
104             test->m_insecureContentDisplayed = true;
105     }
106
107     bool m_insecureContentRun;
108     bool m_insecureContentDisplayed;
109 };
110
111 static void testInsecureContent(InsecureContentTest* test, gconstpointer)
112 {
113     test->loadURI(kHttpsServer->getURIForPath("/insecure-content/").data());
114     test->waitUntilLoadFinished();
115
116     g_assert(test->m_insecureContentRun);
117     g_assert(test->m_insecureContentDisplayed);
118 }
119
120 static void testTLSErrorsPolicy(SSLTest* test, gconstpointer)
121 {
122     WebKitWebContext* context = webkit_web_view_get_context(test->m_webView);
123     // TLS errors are ignored by default.
124     g_assert(webkit_web_context_get_tls_errors_policy(context) == WEBKIT_TLS_ERRORS_POLICY_IGNORE);
125     test->loadURI(kHttpsServer->getURIForPath("/").data());
126     test->waitUntilLoadFinished();
127     g_assert(!test->m_loadFailed);
128
129     webkit_web_context_set_tls_errors_policy(context, WEBKIT_TLS_ERRORS_POLICY_FAIL);
130     test->loadURI(kHttpsServer->getURIForPath("/").data());
131     test->waitUntilLoadFinished();
132     g_assert(test->m_loadFailed);
133     g_assert(test->m_loadEvents.contains(LoadTrackingTest::ProvisionalLoadFailed));
134     g_assert(!test->m_loadEvents.contains(LoadTrackingTest::LoadCommitted));
135 }
136
137 static void testTLSErrorsRedirect(SSLTest* test, gconstpointer)
138 {
139     webkit_web_context_set_tls_errors_policy(webkit_web_view_get_context(test->m_webView), WEBKIT_TLS_ERRORS_POLICY_FAIL);
140     test->loadURI(kHttpsServer->getURIForPath("/redirect").data());
141     test->waitUntilLoadFinished();
142     g_assert(test->m_loadFailed);
143     g_assert(test->m_loadEvents.contains(LoadTrackingTest::ProvisionalLoadFailed));
144     g_assert(!test->m_loadEvents.contains(LoadTrackingTest::LoadCommitted));
145 }
146
147 class TLSErrorsTest: public SSLTest {
148 public:
149     MAKE_GLIB_TEST_FIXTURE(TLSErrorsTest);
150
151     TLSErrorsTest()
152         : m_tlsErrors(static_cast<GTlsCertificateFlags>(0))
153         , m_failingURI(nullptr)
154     {
155     }
156
157     ~TLSErrorsTest()
158     {
159         if (m_failingURI)
160             soup_uri_free(m_failingURI);
161     }
162
163     bool loadFailedWithTLSErrors(const char* failingURI, GTlsCertificate* certificate, GTlsCertificateFlags tlsErrors) override
164     {
165         LoadTrackingTest::loadFailedWithTLSErrors(failingURI, certificate, tlsErrors);
166
167         assertObjectIsDeletedWhenTestFinishes(G_OBJECT(certificate));
168         m_certificate = certificate;
169         m_tlsErrors = tlsErrors;
170         if (m_failingURI)
171             soup_uri_free(m_failingURI);
172         m_failingURI = soup_uri_new(failingURI);
173         return true;
174     }
175
176     GTlsCertificate* certificate() const { return m_certificate.get(); }
177     GTlsCertificateFlags tlsErrors() const { return m_tlsErrors; }
178     const char* host() const { return m_failingURI->host; }
179
180 private:
181     GRefPtr<GTlsCertificate> m_certificate;
182     GTlsCertificateFlags m_tlsErrors;
183     SoupURI* m_failingURI;
184 };
185
186 static void testLoadFailedWithTLSErrors(TLSErrorsTest* test, gconstpointer)
187 {
188     WebKitWebContext* context = webkit_web_view_get_context(test->m_webView);
189     webkit_web_context_set_tls_errors_policy(context, WEBKIT_TLS_ERRORS_POLICY_FAIL);
190
191     // The load-failed-with-tls-errors signal should be emitted when there is a TLS failure.
192     test->loadURI(kHttpsServer->getURIForPath("/test-tls/").data());
193     test->waitUntilLoadFinished();
194     g_assert(G_IS_TLS_CERTIFICATE(test->certificate()));
195     g_assert_cmpuint(test->tlsErrors(), ==, G_TLS_CERTIFICATE_UNKNOWN_CA);
196     g_assert_cmpstr(test->host(), ==, soup_uri_get_host(kHttpsServer->baseURI()));
197     g_assert_cmpint(test->m_loadEvents[0], ==, LoadTrackingTest::ProvisionalLoadStarted);
198     g_assert_cmpint(test->m_loadEvents[1], ==, LoadTrackingTest::LoadFailedWithTLSErrors);
199     g_assert_cmpint(test->m_loadEvents[2], ==, LoadTrackingTest::LoadFinished);
200
201     // Test allowing an exception for this certificate on this host.
202     webkit_web_context_allow_tls_certificate_for_host(context, test->certificate(), test->host());
203     // The page should now load without errors.
204     test->loadURI(kHttpsServer->getURIForPath("/test-tls/").data());
205     test->waitUntilLoadFinished();
206
207     g_assert_cmpint(test->m_loadEvents[0], ==, LoadTrackingTest::ProvisionalLoadStarted);
208     g_assert_cmpint(test->m_loadEvents[1], ==, LoadTrackingTest::LoadCommitted);
209     g_assert_cmpint(test->m_loadEvents[2], ==, LoadTrackingTest::LoadFinished);
210     g_assert_cmpstr(webkit_web_view_get_title(test->m_webView), ==, TLSExpectedSuccessTitle);
211 }
212
213
214 static void httpsServerCallback(SoupServer* server, SoupMessage* message, const char* path, GHashTable*, SoupClientContext*, gpointer)
215 {
216     if (message->method != SOUP_METHOD_GET) {
217         soup_message_set_status(message, SOUP_STATUS_NOT_IMPLEMENTED);
218         return;
219     }
220
221     if (g_str_equal(path, "/")) {
222         soup_message_set_status(message, SOUP_STATUS_OK);
223         soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, indexHTML, strlen(indexHTML));
224         soup_message_body_complete(message->response_body);
225     } else if (g_str_equal(path, "/insecure-content/")) {
226         GUniquePtr<char> responseHTML(g_strdup_printf(insecureContentHTML, kHttpServer->getURIForPath("/test-script").data(), kHttpServer->getURIForPath("/test-image").data()));
227         soup_message_body_append(message->response_body, SOUP_MEMORY_COPY, responseHTML.get(), strlen(responseHTML.get()));
228         soup_message_set_status(message, SOUP_STATUS_OK);
229         soup_message_body_complete(message->response_body);
230     } else if (g_str_equal(path, "/test-tls/")) {
231         soup_message_set_status(message, SOUP_STATUS_OK);
232         soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, TLSSuccessHTMLString, strlen(TLSSuccessHTMLString));
233         soup_message_body_complete(message->response_body);
234     } else if (g_str_equal(path, "/redirect")) {
235         soup_message_set_status(message, SOUP_STATUS_MOVED_PERMANENTLY);
236         soup_message_headers_append(message->response_headers, "Location", kHttpServer->getURIForPath("/test-image").data());
237     } else
238         soup_message_set_status(message, SOUP_STATUS_NOT_FOUND);
239 }
240
241 static void httpServerCallback(SoupServer* server, SoupMessage* message, const char* path, GHashTable*, SoupClientContext*, gpointer)
242 {
243     if (message->method != SOUP_METHOD_GET) {
244         soup_message_set_status(message, SOUP_STATUS_NOT_IMPLEMENTED);
245         return;
246     }
247
248     if (g_str_equal(path, "/test-script")) {
249         GUniquePtr<char> pathToFile(g_build_filename(Test::getResourcesDir().data(), "link-title.js", nullptr));
250         char* contents;
251         gsize length;
252         g_file_get_contents(pathToFile.get(), &contents, &length, 0);
253
254         soup_message_body_append(message->response_body, SOUP_MEMORY_TAKE, contents, length);
255         soup_message_set_status(message, SOUP_STATUS_OK);
256         soup_message_body_complete(message->response_body);
257     } else if (g_str_equal(path, "/test-image")) {
258         GUniquePtr<char> pathToFile(g_build_filename(Test::getResourcesDir().data(), "blank.ico", nullptr));
259         char* contents;
260         gsize length;
261         g_file_get_contents(pathToFile.get(), &contents, &length, 0);
262
263         soup_message_body_append(message->response_body, SOUP_MEMORY_TAKE, contents, length);
264         soup_message_set_status(message, SOUP_STATUS_OK);
265         soup_message_body_complete(message->response_body);
266     } else
267         soup_message_set_status(message, SOUP_STATUS_NOT_FOUND);
268 }
269
270 void beforeAll()
271 {
272     kHttpsServer = new WebKitTestServer(WebKitTestServer::ServerHTTPS);
273     kHttpsServer->run(httpsServerCallback);
274
275     kHttpServer = new WebKitTestServer(WebKitTestServer::ServerHTTP);
276     kHttpServer->run(httpServerCallback);
277
278     SSLTest::add("WebKitWebView", "ssl", testSSL);
279     InsecureContentTest::add("WebKitWebView", "insecure-content", testInsecureContent);
280     // In this case the order of the tests does matter because tls-errors-policy tests the default policy,
281     // and expects that no exception will have been added for this certificate and host pair as is
282     // done in the tls-permission-request test.
283     SSLTest::add("WebKitWebView", "tls-errors-policy", testTLSErrorsPolicy);
284     SSLTest::add("WebKitWebView", "tls-errors-redirect-to-http", testTLSErrorsRedirect);
285     TLSErrorsTest::add("WebKitWebView", "load-failed-with-tls-errors", testLoadFailedWithTLSErrors);
286 }
287
288 void afterAll()
289 {
290     delete kHttpsServer;
291     delete kHttpServer;
292 }