47f666a9dc19cce067789d23f9584ef251c7dcda
[WebKit-https.git] / Tools / TestWebKitAPI / Tests / WebKit2Gtk / TestResources.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 "WebKitTestServer.h"
23 #include "WebViewTest.h"
24 #include <wtf/Vector.h>
25 #include <wtf/glib/GMainLoopSource.h>
26 #include <wtf/glib/GMutexLocker.h>
27 #include <wtf/glib/GRefPtr.h>
28
29 static WebKitTestServer* kServer;
30
31 static const char* kIndexHtml =
32     "<html><head>"
33     " <link rel='stylesheet' href='/style.css' type='text/css'>"
34     " <script language='javascript' src='/javascript.js'></script>"
35     "</head><body>WebKitGTK+ resources test</body></html>";
36
37 static const char* kStyleCSS =
38     "body {"
39     "    margin: 0px;"
40     "    padding: 0px;"
41     "    font-family: sans-serif;"
42     "    background: url(/blank.ico) 0 0 no-repeat;"
43     "    color: black;"
44     "}";
45
46 static const char* kJavascript = "function foo () { var a = 1; }";
47
48 class ResourcesTest: public WebViewTest {
49 public:
50     MAKE_GLIB_TEST_FIXTURE(ResourcesTest);
51
52     static void resourceSentRequestCallback(WebKitWebResource* resource, WebKitURIRequest* request, WebKitURIResponse* redirectResponse, ResourcesTest* test)
53     {
54         test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(request));
55         if (redirectResponse)
56             test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(redirectResponse));
57         test->resourceSentRequest(resource, request, redirectResponse);
58     }
59
60     static void resourceReceivedResponseCallback(WebKitWebResource* resource, GParamSpec*, ResourcesTest* test)
61     {
62         g_assert(webkit_web_resource_get_response(resource));
63         test->resourceReceivedResponse(resource);
64     }
65
66     static void resourceReceivedDataCallback(WebKitWebResource* resource, guint64 bytesReceived, ResourcesTest* test)
67     {
68         test->resourceReceivedData(resource, bytesReceived);
69     }
70
71     static void resourceFinishedCallback(WebKitWebResource* resource, ResourcesTest* test)
72     {
73         test->resourceFinished(resource);
74     }
75
76     static void resourceFailedCallback(WebKitWebResource* resource, GError* error, ResourcesTest* test)
77     {
78         g_assert(error);
79         test->resourceFailed(resource, error);
80     }
81
82     static void resourceLoadStartedCallback(WebKitWebView* webView, WebKitWebResource* resource, WebKitURIRequest* request, ResourcesTest* test)
83     {
84         test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(resource));
85         test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(request));
86
87         // Ignore favicons.
88         if (g_str_has_suffix(webkit_uri_request_get_uri(request), "favicon.ico"))
89             return;
90
91         test->resourceLoadStarted(resource, request);
92         g_signal_connect(resource, "sent-request", G_CALLBACK(resourceSentRequestCallback), test);
93         g_signal_connect(resource, "notify::response", G_CALLBACK(resourceReceivedResponseCallback), test);
94         g_signal_connect(resource, "received-data", G_CALLBACK(resourceReceivedDataCallback), test);
95         g_signal_connect(resource, "finished", G_CALLBACK(resourceFinishedCallback), test);
96         g_signal_connect(resource, "failed", G_CALLBACK(resourceFailedCallback), test);
97     }
98
99     void clearSubresources()
100     {
101         g_list_free_full(m_subresources, reinterpret_cast<GDestroyNotify>(g_object_unref));
102         m_subresources = 0;
103     }
104
105     ResourcesTest()
106         : WebViewTest()
107         , m_resourcesLoaded(0)
108         , m_resourcesToLoad(0)
109         , m_resourceDataSize(0)
110         , m_subresources(0)
111     {
112         g_signal_connect(m_webView, "resource-load-started", G_CALLBACK(resourceLoadStartedCallback), this);
113     }
114
115     ~ResourcesTest()
116     {
117         clearSubresources();
118     }
119
120     virtual void resourceLoadStarted(WebKitWebResource* resource, WebKitURIRequest* request)
121     {
122     }
123
124     virtual void resourceSentRequest(WebKitWebResource* resource, WebKitURIRequest* request, WebKitURIResponse* redirectResponse)
125     {
126     }
127
128     virtual void resourceReceivedResponse(WebKitWebResource* resource)
129     {
130     }
131
132     virtual void resourceReceivedData(WebKitWebResource* resource, guint64 bytesReceived)
133     {
134     }
135
136     virtual void resourceFinished(WebKitWebResource* resource)
137     {
138         g_signal_handlers_disconnect_matched(resource, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this);
139         if (webkit_web_view_get_main_resource(m_webView) != resource)
140             m_subresources = g_list_prepend(m_subresources, g_object_ref(resource));
141         if (++m_resourcesLoaded == m_resourcesToLoad)
142             g_main_loop_quit(m_mainLoop);
143     }
144
145     virtual void resourceFailed(WebKitWebResource* resource, GError* error)
146     {
147         g_assert_not_reached();
148     }
149
150     void waitUntilResourcesLoaded(size_t resourcesCount)
151     {
152         m_resourcesLoaded = 0;
153         m_resourcesToLoad = resourcesCount;
154         clearSubresources();
155         g_main_loop_run(m_mainLoop);
156     }
157
158     GList* subresources()
159     {
160         return m_subresources;
161     }
162
163     static void resourceGetDataCallback(GObject* object, GAsyncResult* result, gpointer userData)
164     {
165         size_t dataSize;
166         GUniqueOutPtr<GError> error;
167         unsigned char* data = webkit_web_resource_get_data_finish(WEBKIT_WEB_RESOURCE(object), result, &dataSize, &error.outPtr());
168         g_assert(!error.get());
169         g_assert(data);
170         g_assert_cmpint(dataSize, >, 0);
171
172         ResourcesTest* test = static_cast<ResourcesTest*>(userData);
173         test->m_resourceData.reset(reinterpret_cast<char*>(data));
174         test->m_resourceDataSize = dataSize;
175         g_main_loop_quit(test->m_mainLoop);
176     }
177
178     void checkResourceData(WebKitWebResource* resource)
179     {
180         m_resourceDataSize = 0;
181         webkit_web_resource_get_data(resource, 0, resourceGetDataCallback, this);
182         g_main_loop_run(m_mainLoop);
183
184         const char* uri = webkit_web_resource_get_uri(resource);
185         if (uri == kServer->getURIForPath("/")) {
186             g_assert_cmpint(m_resourceDataSize, ==, strlen(kIndexHtml));
187             g_assert(!strncmp(m_resourceData.get(), kIndexHtml, m_resourceDataSize));
188         } else if (uri == kServer->getURIForPath("/style.css")) {
189             g_assert_cmpint(m_resourceDataSize, ==, strlen(kStyleCSS));
190             g_assert(!strncmp(m_resourceData.get(), kStyleCSS, m_resourceDataSize));
191         } else if (uri == kServer->getURIForPath("/javascript.js")) {
192             g_assert_cmpint(m_resourceDataSize, ==, strlen(kJavascript));
193             g_assert(!strncmp(m_resourceData.get(), kJavascript, m_resourceDataSize));
194         } else
195             g_assert_not_reached();
196         m_resourceData.reset();
197     }
198
199     size_t m_resourcesLoaded;
200     size_t m_resourcesToLoad;
201     GUniquePtr<char> m_resourceData;
202     size_t m_resourceDataSize;
203     GList* m_subresources;
204 };
205
206 static void testWebViewResources(ResourcesTest* test, gconstpointer)
207 {
208     // Nothing loaded yet, there shoulnd't be resources.
209     g_assert(!webkit_web_view_get_main_resource(test->m_webView));
210     g_assert(!test->subresources());
211
212     // Load simple page without subresources.
213     test->loadHtml("<html><body>Testing WebKitGTK+</body></html>", 0);
214     test->waitUntilLoadFinished();
215     WebKitWebResource* resource = webkit_web_view_get_main_resource(test->m_webView);
216     g_assert(resource);
217     g_assert_cmpstr(webkit_web_view_get_uri(test->m_webView), ==, webkit_web_resource_get_uri(resource));
218     g_assert(!test->subresources());
219
220     // Load simple page with subresources.
221     test->loadURI(kServer->getURIForPath("/").data());
222     test->waitUntilResourcesLoaded(4);
223
224     resource = webkit_web_view_get_main_resource(test->m_webView);
225     g_assert(resource);
226     g_assert_cmpstr(webkit_web_view_get_uri(test->m_webView), ==, webkit_web_resource_get_uri(resource));
227     GList* subresources = test->subresources();
228     g_assert(subresources);
229     g_assert_cmpint(g_list_length(subresources), ==, 3);
230
231 #if 0
232     // Load the same URI again.
233     // FIXME: we need a workaround for bug https://bugs.webkit.org/show_bug.cgi?id=78510.
234     test->loadURI(kServer->getURIForPath("/").data());
235     test->waitUntilResourcesLoaded(4);
236 #endif
237
238     // Reload.
239     webkit_web_view_reload_bypass_cache(test->m_webView);
240     test->waitUntilResourcesLoaded(4);
241 }
242
243 class SingleResourceLoadTest: public ResourcesTest {
244 public:
245     MAKE_GLIB_TEST_FIXTURE(SingleResourceLoadTest);
246
247     enum LoadEvents {
248         Started,
249         SentRequest,
250         Redirected,
251         ReceivedResponse,
252         ReceivedData,
253         Finished,
254         Failed
255     };
256
257     SingleResourceLoadTest()
258         : ResourcesTest()
259         , m_resourceDataReceived(0)
260     {
261         m_resourcesToLoad = 2;
262     }
263
264     void resourceLoadStarted(WebKitWebResource* resource, WebKitURIRequest* request)
265     {
266         if (resource == webkit_web_view_get_main_resource(m_webView))
267             return;
268
269         m_resourceDataReceived = 0;
270         m_resource = resource;
271         m_loadEvents.append(Started);
272     }
273
274     void resourceSentRequest(WebKitWebResource* resource, WebKitURIRequest* request, WebKitURIResponse* redirectResponse)
275     {
276         if (resource != m_resource)
277             return;
278
279         if (redirectResponse)
280             m_loadEvents.append(Redirected);
281         else
282             m_loadEvents.append(SentRequest);
283     }
284
285     void resourceReceivedResponse(WebKitWebResource* resource)
286     {
287         if (resource != m_resource)
288             return;
289
290         m_loadEvents.append(ReceivedResponse);
291     }
292
293     void resourceReceivedData(WebKitWebResource* resource, guint64 bytesReceived)
294     {
295         if (resource != m_resource)
296             return;
297
298         m_resourceDataReceived += bytesReceived;
299         if (!m_loadEvents.contains(ReceivedData))
300             m_loadEvents.append(ReceivedData);
301     }
302
303     void resourceFinished(WebKitWebResource* resource)
304     {
305         if (resource != m_resource) {
306             ResourcesTest::resourceFinished(resource);
307             return;
308         }
309
310         if (!m_loadEvents.contains(Failed)) {
311             WebKitURIResponse* response = webkit_web_resource_get_response(m_resource.get());
312             g_assert(response);
313             g_assert_cmpint(webkit_uri_response_get_content_length(response), ==, m_resourceDataReceived);
314         }
315         m_loadEvents.append(Finished);
316         ResourcesTest::resourceFinished(resource);
317     }
318
319     void resourceFailed(WebKitWebResource* resource, GError* error)
320     {
321         if (resource == m_resource)
322             m_loadEvents.append(Failed);
323     }
324
325     void waitUntilResourceLoadFinished()
326     {
327         m_resource = 0;
328         m_resourcesLoaded = 0;
329         g_main_loop_run(m_mainLoop);
330     }
331
332     WebKitURIResponse* waitUntilResourceLoadFinishedAndReturnURIResponse()
333     {
334         waitUntilResourceLoadFinished();
335         g_assert(m_resource);
336         return webkit_web_resource_get_response(m_resource.get());
337     }
338
339     GRefPtr<WebKitWebResource> m_resource;
340     Vector<LoadEvents> m_loadEvents;
341     guint64 m_resourceDataReceived;
342 };
343
344 static void testWebResourceLoading(SingleResourceLoadTest* test, gconstpointer)
345 {
346     test->loadURI(kServer->getURIForPath("/javascript.html").data());
347     test->waitUntilResourceLoadFinished();
348     g_assert(test->m_resource);
349     Vector<SingleResourceLoadTest::LoadEvents>& events = test->m_loadEvents;
350     g_assert_cmpint(events.size(), ==, 5);
351     g_assert_cmpint(events[0], ==, SingleResourceLoadTest::Started);
352     g_assert_cmpint(events[1], ==, SingleResourceLoadTest::SentRequest);
353     g_assert_cmpint(events[2], ==, SingleResourceLoadTest::ReceivedResponse);
354     g_assert_cmpint(events[3], ==, SingleResourceLoadTest::ReceivedData);
355     g_assert_cmpint(events[4], ==, SingleResourceLoadTest::Finished);
356     events.clear();
357
358     test->loadURI(kServer->getURIForPath("/redirected-css.html").data());
359     test->waitUntilResourceLoadFinished();
360     g_assert(test->m_resource);
361     g_assert_cmpint(events.size(), ==, 6);
362     g_assert_cmpint(events[0], ==, SingleResourceLoadTest::Started);
363     g_assert_cmpint(events[1], ==, SingleResourceLoadTest::SentRequest);
364     g_assert_cmpint(events[2], ==, SingleResourceLoadTest::Redirected);
365     g_assert_cmpint(events[3], ==, SingleResourceLoadTest::ReceivedResponse);
366     g_assert_cmpint(events[4], ==, SingleResourceLoadTest::ReceivedData);
367     g_assert_cmpint(events[5], ==, SingleResourceLoadTest::Finished);
368     events.clear();
369
370     test->loadURI(kServer->getURIForPath("/invalid-css.html").data());
371     test->waitUntilResourceLoadFinished();
372     g_assert(test->m_resource);
373     g_assert_cmpint(events.size(), ==, 4);
374     g_assert_cmpint(events[0], ==, SingleResourceLoadTest::Started);
375     g_assert_cmpint(events[1], ==, SingleResourceLoadTest::SentRequest);
376     g_assert_cmpint(events[2], ==, SingleResourceLoadTest::Failed);
377     g_assert_cmpint(events[3], ==, SingleResourceLoadTest::Finished);
378     events.clear();
379 }
380
381 static void testWebResourceResponse(SingleResourceLoadTest* test, gconstpointer)
382 {
383     // No cached resource: First load.
384     test->loadURI(kServer->getURIForPath("/javascript.html").data());
385     WebKitURIResponse* response = test->waitUntilResourceLoadFinishedAndReturnURIResponse();
386     g_assert_cmpint(webkit_uri_response_get_status_code(response), ==, SOUP_STATUS_OK);
387
388     // No cached resource: Second load.
389     test->loadURI(kServer->getURIForPath("/javascript.html").data());
390     response = test->waitUntilResourceLoadFinishedAndReturnURIResponse();
391     g_assert_cmpint(webkit_uri_response_get_status_code(response), ==, SOUP_STATUS_OK);
392
393     // No cached resource: Reload.
394     webkit_web_view_reload(test->m_webView);
395     response = test->waitUntilResourceLoadFinishedAndReturnURIResponse();
396     g_assert_cmpint(webkit_uri_response_get_status_code(response), ==, SOUP_STATUS_OK);
397
398     // Cached resource: First load.
399     test->loadURI(kServer->getURIForPath("/image.html").data());
400     response = test->waitUntilResourceLoadFinishedAndReturnURIResponse();
401     g_assert_cmpint(webkit_uri_response_get_status_code(response), ==, SOUP_STATUS_OK);
402
403     // Cached resource: Second load.
404     test->loadURI(kServer->getURIForPath("/image.html").data());
405     response = test->waitUntilResourceLoadFinishedAndReturnURIResponse();
406     g_assert_cmpint(webkit_uri_response_get_status_code(response), ==, SOUP_STATUS_OK);
407
408     // Cached resource: Reload.
409     webkit_web_view_reload(test->m_webView);
410     response = test->waitUntilResourceLoadFinishedAndReturnURIResponse();
411     g_assert_cmpint(webkit_uri_response_get_status_code(response), ==, SOUP_STATUS_NOT_MODIFIED);
412 }
413
414 static void testWebResourceMimeType(SingleResourceLoadTest* test, gconstpointer)
415 {
416     test->loadURI(kServer->getURIForPath("/javascript.html").data());
417     WebKitURIResponse* response = test->waitUntilResourceLoadFinishedAndReturnURIResponse();
418     g_assert_cmpstr(webkit_uri_response_get_mime_type(response), ==, "text/javascript");
419
420     test->loadURI(kServer->getURIForPath("/image.html").data());
421     response = test->waitUntilResourceLoadFinishedAndReturnURIResponse();
422     g_assert_cmpstr(webkit_uri_response_get_mime_type(response), ==, "image/x-icon");
423
424     test->loadURI(kServer->getURIForPath("/redirected-css.html").data());
425     response = test->waitUntilResourceLoadFinishedAndReturnURIResponse();
426     g_assert_cmpstr(webkit_uri_response_get_mime_type(response), ==, "text/css");
427 }
428
429 static void testWebResourceSuggestedFilename(SingleResourceLoadTest* test, gconstpointer)
430 {
431     test->loadURI(kServer->getURIForPath("/javascript.html").data());
432     WebKitURIResponse* response = test->waitUntilResourceLoadFinishedAndReturnURIResponse();
433     g_assert_cmpstr(webkit_uri_response_get_suggested_filename(response), ==, "JavaScript.js");
434
435     test->loadURI(kServer->getURIForPath("/image.html").data());
436     response = test->waitUntilResourceLoadFinishedAndReturnURIResponse();
437     g_assert(!webkit_uri_response_get_suggested_filename(response));
438 }
439
440 class ResourceURITrackingTest: public SingleResourceLoadTest {
441 public:
442     MAKE_GLIB_TEST_FIXTURE(ResourceURITrackingTest);
443
444     ResourceURITrackingTest()
445         : SingleResourceLoadTest()
446     {
447     }
448
449     static void uriChanged(WebKitWebResource* resource, GParamSpec*, ResourceURITrackingTest* test)
450     {
451         g_assert(resource == test->m_resource.get());
452         g_assert_cmpstr(test->m_activeURI.data(), !=, webkit_web_resource_get_uri(test->m_resource.get()));
453         test->m_activeURI = webkit_web_resource_get_uri(test->m_resource.get());
454     }
455
456     void resourceLoadStarted(WebKitWebResource* resource, WebKitURIRequest* request)
457     {
458         if (resource == webkit_web_view_get_main_resource(m_webView))
459             return;
460
461         m_resource = resource;
462         m_activeURI = webkit_web_resource_get_uri(resource);
463         checkActiveURI("/redirected.css");
464         g_assert_cmpstr(m_activeURI.data(), ==, webkit_uri_request_get_uri(request));
465         g_signal_connect(resource, "notify::uri", G_CALLBACK(uriChanged), this);
466     }
467
468     void resourceSentRequest(WebKitWebResource* resource, WebKitURIRequest* request, WebKitURIResponse* redirectResponse)
469     {
470         if (resource != m_resource)
471             return;
472
473         if (redirectResponse)
474             checkActiveURI("/simple-style.css");
475         else
476             checkActiveURI("/redirected.css");
477         g_assert_cmpstr(m_activeURI.data(), ==, webkit_uri_request_get_uri(request));
478     }
479
480     void resourceReceivedResponse(WebKitWebResource* resource)
481     {
482         if (resource != m_resource)
483             return;
484
485         checkActiveURI("/simple-style.css");
486     }
487
488     void resourceReceivedData(WebKitWebResource* resource, guint64 bytesReceived)
489     {
490     }
491
492     void resourceFinished(WebKitWebResource* resource)
493     {
494         if (resource == m_resource)
495             checkActiveURI("/simple-style.css");
496         ResourcesTest::resourceFinished(resource);
497     }
498
499     void resourceFailed(WebKitWebResource*, GError*)
500     {
501         g_assert_not_reached();
502     }
503
504     CString m_activeURI;
505
506 private:
507     void checkActiveURI(const char* uri)
508     {
509         ASSERT_CMP_CSTRING(m_activeURI, ==, kServer->getURIForPath(uri));
510     }
511 };
512
513 static void testWebResourceActiveURI(ResourceURITrackingTest* test, gconstpointer)
514 {
515     test->loadURI(kServer->getURIForPath("/redirected-css.html").data());
516     test->waitUntilResourceLoadFinished();
517 }
518
519 static void testWebResourceGetData(ResourcesTest* test, gconstpointer)
520 {
521     test->loadURI(kServer->getURIForPath("/").data());
522     // FIXME: this should be 4 instead of 3, but we don't get the css image resource
523     // due to bug https://bugs.webkit.org/show_bug.cgi?id=78510.
524     test->waitUntilResourcesLoaded(3);
525
526     WebKitWebResource* resource = webkit_web_view_get_main_resource(test->m_webView);
527     g_assert(resource);
528     test->checkResourceData(resource);
529
530     GList* subresources = test->subresources();
531     for (GList* item = subresources; item; item = g_list_next(item))
532         test->checkResourceData(WEBKIT_WEB_RESOURCE(item->data));
533 }
534
535 static void testWebViewResourcesHistoryCache(SingleResourceLoadTest* test, gconstpointer)
536 {
537     CString javascriptURI = kServer->getURIForPath("/javascript.html");
538     test->loadURI(javascriptURI.data());
539     test->waitUntilResourceLoadFinished();
540     WebKitWebResource* resource = webkit_web_view_get_main_resource(test->m_webView);
541     g_assert(resource);
542     g_assert_cmpstr(webkit_web_resource_get_uri(resource), ==, javascriptURI.data());
543
544     CString simpleStyleCSSURI = kServer->getURIForPath("/simple-style-css.html");
545     test->loadURI(simpleStyleCSSURI.data());
546     test->waitUntilResourceLoadFinished();
547     resource = webkit_web_view_get_main_resource(test->m_webView);
548     g_assert(resource);
549     g_assert_cmpstr(webkit_web_resource_get_uri(resource), ==, simpleStyleCSSURI.data());
550
551     test->goBack();
552     test->waitUntilResourceLoadFinished();
553     resource = webkit_web_view_get_main_resource(test->m_webView);
554     g_assert(resource);
555     g_assert_cmpstr(webkit_web_resource_get_uri(resource), ==, javascriptURI.data());
556
557     test->goForward();
558     test->waitUntilResourceLoadFinished();
559     resource = webkit_web_view_get_main_resource(test->m_webView);
560     g_assert(resource);
561     g_assert_cmpstr(webkit_web_resource_get_uri(resource), ==, simpleStyleCSSURI.data());
562 }
563
564 class SendRequestTest: public SingleResourceLoadTest {
565 public:
566     MAKE_GLIB_TEST_FIXTURE(SendRequestTest);
567
568     void resourceSentRequest(WebKitWebResource* resource, WebKitURIRequest* request, WebKitURIResponse* redirectResponse)
569     {
570         if (resource != m_resource)
571             return;
572
573         if (redirectResponse)
574             g_assert_cmpstr(webkit_uri_request_get_uri(request), ==, m_expectedNewResourceURIAfterRedirection.data());
575         else
576             g_assert_cmpstr(webkit_uri_request_get_uri(request), ==, m_expectedNewResourceURI.data());
577         g_assert_cmpstr(webkit_uri_request_get_uri(request), ==, webkit_web_resource_get_uri(resource));
578
579         SingleResourceLoadTest::resourceSentRequest(resource, request, redirectResponse);
580     }
581
582     void resourceFailed(WebKitWebResource* resource, GError* error)
583     {
584         if (resource != m_resource)
585             return;
586
587         g_assert_cmpstr(webkit_web_resource_get_uri(resource), ==, m_expectedCancelledResourceURI.data());
588         g_assert_error(error, WEBKIT_NETWORK_ERROR, WEBKIT_NETWORK_ERROR_CANCELLED);
589
590         SingleResourceLoadTest::resourceFailed(resource, error);
591     }
592
593     void setExpectedNewResourceURI(const CString& uri)
594     {
595         m_expectedNewResourceURI = uri;
596     }
597
598     void setExpectedCancelledResourceURI(const CString& uri)
599     {
600         m_expectedCancelledResourceURI = uri;
601     }
602
603     void setExpectedNewResourceURIAfterRedirection(const CString& uri)
604     {
605         m_expectedNewResourceURIAfterRedirection = uri;
606     }
607
608     CString m_expectedNewResourceURI;
609     CString m_expectedCancelledResourceURI;
610     CString m_expectedNewResourceURIAfterRedirection;
611 };
612
613 static void testWebResourceSendRequest(SendRequestTest* test, gconstpointer)
614 {
615     test->setExpectedNewResourceURI(kServer->getURIForPath("/javascript.js"));
616     test->loadURI(kServer->getURIForPath("relative-javascript.html").data());
617     test->waitUntilResourceLoadFinished();
618     g_assert(test->m_resource);
619
620     Vector<SingleResourceLoadTest::LoadEvents>& events = test->m_loadEvents;
621     g_assert_cmpint(events.size(), ==, 5);
622     g_assert_cmpint(events[0], ==, SingleResourceLoadTest::Started);
623     g_assert_cmpint(events[1], ==, SingleResourceLoadTest::SentRequest);
624     g_assert_cmpint(events[2], ==, SingleResourceLoadTest::ReceivedResponse);
625     g_assert_cmpint(events[3], ==, SingleResourceLoadTest::ReceivedData);
626     g_assert_cmpint(events[4], ==, SingleResourceLoadTest::Finished);
627     events.clear();
628
629     // Cancel request.
630     test->setExpectedCancelledResourceURI(kServer->getURIForPath("/cancel-this.js"));
631     test->loadURI(kServer->getURIForPath("/resource-to-cancel.html").data());
632     test->waitUntilResourceLoadFinished();
633     g_assert(test->m_resource);
634
635     g_assert_cmpint(events.size(), ==, 3);
636     g_assert_cmpint(events[0], ==, SingleResourceLoadTest::Started);
637     g_assert_cmpint(events[1], ==, SingleResourceLoadTest::Failed);
638     g_assert_cmpint(events[2], ==, SingleResourceLoadTest::Finished);
639     events.clear();
640
641     // URI changed after a redirect.
642     test->setExpectedNewResourceURI(kServer->getURIForPath("/redirected.js"));
643     test->setExpectedNewResourceURIAfterRedirection(kServer->getURIForPath("/javascript-after-redirection.js"));
644     test->loadURI(kServer->getURIForPath("redirected-javascript.html").data());
645     test->waitUntilResourceLoadFinished();
646     g_assert(test->m_resource);
647
648     g_assert_cmpint(events.size(), ==, 6);
649     g_assert_cmpint(events[0], ==, SingleResourceLoadTest::Started);
650     g_assert_cmpint(events[1], ==, SingleResourceLoadTest::SentRequest);
651     g_assert_cmpint(events[2], ==, SingleResourceLoadTest::Redirected);
652     g_assert_cmpint(events[3], ==, SingleResourceLoadTest::ReceivedResponse);
653     g_assert_cmpint(events[4], ==, SingleResourceLoadTest::ReceivedData);
654     g_assert_cmpint(events[5], ==, SingleResourceLoadTest::Finished);
655     events.clear();
656
657     // Cancel after a redirect.
658     test->setExpectedNewResourceURI(kServer->getURIForPath("/redirected-to-cancel.js"));
659     test->setExpectedCancelledResourceURI(kServer->getURIForPath("/redirected-to-cancel.js"));
660     test->loadURI(kServer->getURIForPath("/redirected-to-cancel.html").data());
661     test->waitUntilResourceLoadFinished();
662     g_assert(test->m_resource);
663
664     g_assert_cmpint(events.size(), ==, 4);
665     g_assert_cmpint(events[0], ==, SingleResourceLoadTest::Started);
666     g_assert_cmpint(events[1], ==, SingleResourceLoadTest::SentRequest);
667     g_assert_cmpint(events[2], ==, SingleResourceLoadTest::Failed);
668     g_assert_cmpint(events[3], ==, SingleResourceLoadTest::Finished);
669     events.clear();
670 }
671
672 static GMutex s_serverMutex;
673 static const unsigned s_maxConnectionsPerHost = 6;
674
675 class SyncRequestOnMaxConnsTest: public ResourcesTest {
676 public:
677     MAKE_GLIB_TEST_FIXTURE(SyncRequestOnMaxConnsTest);
678
679     void resourceLoadStarted(WebKitWebResource*, WebKitURIRequest*) override
680     {
681         if (!m_resourcesToStartPending)
682             return;
683
684         if (!--m_resourcesToStartPending)
685             g_main_loop_quit(m_mainLoop);
686     }
687
688     void waitUntilResourcesStarted(unsigned requestCount)
689     {
690         m_resourcesToStartPending = requestCount;
691         g_main_loop_run(m_mainLoop);
692     }
693
694     unsigned m_resourcesToStartPending;
695 };
696
697 static void testWebViewSyncRequestOnMaxConns(SyncRequestOnMaxConnsTest* test, gconstpointer)
698 {
699     WTF::GMutexLocker<GMutex> lock(s_serverMutex);
700     test->loadURI(kServer->getURIForPath("/sync-request-on-max-conns-0").data());
701     test->waitUntilResourcesStarted(s_maxConnectionsPerHost + 1); // s_maxConnectionsPerHost resource + main resource.
702
703     for (unsigned i = 0; i < 2; ++i) {
704         GUniquePtr<char> xhr(g_strdup_printf("xhr = new XMLHttpRequest; xhr.open('GET', '/sync-request-on-max-conns-xhr%u', false); xhr.send();", i));
705         webkit_web_view_run_javascript(test->m_webView, xhr.get(), nullptr, nullptr, nullptr);
706     }
707
708     // By default sync XHRs have a 10 seconds timeout, we don't want to wait all that so use our own timeout.
709     GMainLoopSource timeoutSource;
710     timeoutSource.scheduleAfterDelay("Timeout", [] { g_assert_not_reached(); }, std::chrono::seconds(1));
711
712     GMainLoopSource unlockServerSource;
713     unlockServerSource.schedule("Unlock Server Idle", [&lock] { lock.unlock(); });
714     test->waitUntilResourcesLoaded(s_maxConnectionsPerHost + 3); // s_maxConnectionsPerHost resource + main resource + 2 XHR.
715 }
716
717 static void addCacheHTTPHeadersToResponse(SoupMessage* message)
718 {
719     // The actual date doesn't really matter.
720     SoupDate* soupDate = soup_date_new_from_now(0);
721     GUniquePtr<char> date(soup_date_to_string(soupDate, SOUP_DATE_HTTP));
722     soup_message_headers_append(message->response_headers, "Last-Modified", date.get());
723     soup_date_free(soupDate);
724     soup_message_headers_append(message->response_headers, "Cache-control", "public, max-age=31536000");
725     soupDate = soup_date_new_from_now(3600);
726     date.reset(soup_date_to_string(soupDate, SOUP_DATE_HTTP));
727     soup_message_headers_append(message->response_headers, "Expires", date.get());
728     soup_date_free(soupDate);
729 }
730
731 static void serverCallback(SoupServer* server, SoupMessage* message, const char* path, GHashTable*, SoupClientContext*, gpointer)
732 {
733     if (message->method != SOUP_METHOD_GET) {
734         soup_message_set_status(message, SOUP_STATUS_NOT_IMPLEMENTED);
735         return;
736     }
737
738     soup_message_set_status(message, SOUP_STATUS_OK);
739
740     if (soup_message_headers_get_one(message->request_headers, "If-Modified-Since")) {
741         soup_message_set_status(message, SOUP_STATUS_NOT_MODIFIED);
742         soup_message_body_complete(message->response_body);
743         return;
744     }
745
746     if (g_str_equal(path, "/")) {
747         soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, kIndexHtml, strlen(kIndexHtml));
748     } else if (g_str_equal(path, "/javascript.html")) {
749         static const char* javascriptHtml = "<html><head><script language='javascript' src='/javascript.js'></script></head><body></body></html>";
750         soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, javascriptHtml, strlen(javascriptHtml));
751     } else if (g_str_equal(path, "/image.html")) {
752         static const char* imageHTML = "<html><body><img src='/blank.ico'></img></body></html>";
753         soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, imageHTML, strlen(imageHTML));
754     } else if (g_str_equal(path, "/redirected-css.html")) {
755         static const char* redirectedCSSHtml = "<html><head><link rel='stylesheet' href='/redirected.css' type='text/css'></head><body></html>";
756         soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, redirectedCSSHtml, strlen(redirectedCSSHtml));
757     } else if (g_str_equal(path, "/invalid-css.html")) {
758         static const char* invalidCSSHtml = "<html><head><link rel='stylesheet' href='/invalid.css' type='text/css'></head><body></html>";
759         soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, invalidCSSHtml, strlen(invalidCSSHtml));
760     } else if (g_str_equal(path, "/simple-style-css.html")) {
761         static const char* simpleStyleCSSHtml = "<html><head><link rel='stylesheet' href='/simple-style.css' type='text/css'></head><body></html>";
762         soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, simpleStyleCSSHtml, strlen(simpleStyleCSSHtml));
763     } else if (g_str_equal(path, "/style.css")) {
764         soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, kStyleCSS, strlen(kStyleCSS));
765         addCacheHTTPHeadersToResponse(message);
766         soup_message_headers_append(message->response_headers, "Content-Type", "text/css");
767     } else if (g_str_equal(path, "/javascript.js") || g_str_equal(path, "/javascript-after-redirection.js")) {
768         soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, kJavascript, strlen(kJavascript));
769         soup_message_headers_append(message->response_headers, "Content-Type", "text/javascript");
770         soup_message_headers_append(message->response_headers, "Content-Disposition", "filename=JavaScript.js");
771     } else if (g_str_equal(path, "/relative-javascript.html")) {
772         static const char* javascriptRelativeHTML = "<html><head><script language='javascript' src='remove-this/javascript.js'></script></head><body></body></html>";
773         soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, javascriptRelativeHTML, strlen(javascriptRelativeHTML));
774     } else if (g_str_equal(path, "/resource-to-cancel.html")) {
775         static const char* resourceToCancelHTML = "<html><head><script language='javascript' src='cancel-this.js'></script></head><body></body></html>";
776         soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, resourceToCancelHTML, strlen(resourceToCancelHTML));
777     } else if (g_str_equal(path, "/redirected-javascript.html")) {
778         static const char* javascriptRelativeHTML = "<html><head><script language='javascript' src='/redirected.js'></script></head><body></body></html>";
779         soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, javascriptRelativeHTML, strlen(javascriptRelativeHTML));
780     } else if (g_str_equal(path, "/redirected-to-cancel.html")) {
781         static const char* javascriptRelativeHTML = "<html><head><script language='javascript' src='/redirected-to-cancel.js'></script></head><body></body></html>";
782         soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, javascriptRelativeHTML, strlen(javascriptRelativeHTML));
783     } else if (g_str_equal(path, "/blank.ico")) {
784         GUniquePtr<char> filePath(g_build_filename(Test::getResourcesDir().data(), path, nullptr));
785         char* contents;
786         gsize contentsLength;
787         g_file_get_contents(filePath.get(), &contents, &contentsLength, 0);
788         soup_message_body_append(message->response_body, SOUP_MEMORY_TAKE, contents, contentsLength);
789         addCacheHTTPHeadersToResponse(message);
790         soup_message_headers_append(message->response_headers, "Content-Type", "image/vnd.microsoft.icon");
791     } else if (g_str_equal(path, "/simple-style.css")) {
792         static const char* simpleCSS =
793             "body {"
794             "    margin: 0px;"
795             "    padding: 0px;"
796             "}";
797         soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, simpleCSS, strlen(simpleCSS));
798         soup_message_headers_append(message->response_headers, "Content-Type", "text/css");
799     } else if (g_str_equal(path, "/redirected.css")) {
800         soup_message_set_status(message, SOUP_STATUS_MOVED_PERMANENTLY);
801         soup_message_headers_append(message->response_headers, "Location", "/simple-style.css");
802     } else if (g_str_equal(path, "/redirected.js")) {
803         soup_message_set_status(message, SOUP_STATUS_MOVED_PERMANENTLY);
804         soup_message_headers_append(message->response_headers, "Location", "/remove-this/javascript-after-redirection.js");
805     } else if (g_str_equal(path, "/redirected-to-cancel.js")) {
806         soup_message_set_status(message, SOUP_STATUS_MOVED_PERMANENTLY);
807         soup_message_headers_append(message->response_headers, "Location", "/cancel-this.js");
808     } else if (g_str_equal(path, "/invalid.css"))
809         soup_message_set_status(message, SOUP_STATUS_CANT_CONNECT);
810     else if (g_str_has_prefix(path, "/sync-request-on-max-conns-")) {
811         char* contents;
812         gsize contentsLength;
813         if (g_str_equal(path, "/sync-request-on-max-conns-0")) {
814             GString* imagesHTML = g_string_new("<html><body>");
815             for (unsigned i = 1; i <= s_maxConnectionsPerHost; ++i)
816                 g_string_append_printf(imagesHTML, "<img src='/sync-request-on-max-conns-%u'>", i);
817             g_string_append(imagesHTML, "</body></html>");
818
819             contentsLength = imagesHTML->len;
820             contents = g_string_free(imagesHTML, FALSE);
821         } else {
822             {
823                 // We don't actually need to keep the mutex, so we release it as soon as we get it.
824                 WTF::GMutexLocker<GMutex> lock(s_serverMutex);
825             }
826
827             GUniquePtr<char> filePath(g_build_filename(Test::getResourcesDir().data(), "blank.ico", nullptr));
828             g_file_get_contents(filePath.get(), &contents, &contentsLength, 0);
829         }
830         soup_message_body_append(message->response_body, SOUP_MEMORY_TAKE, contents, contentsLength);
831     } else
832         soup_message_set_status(message, SOUP_STATUS_NOT_FOUND);
833     soup_message_body_complete(message->response_body);
834 }
835
836 void beforeAll()
837 {
838     kServer = new WebKitTestServer();
839     kServer->run(serverCallback);
840
841     ResourcesTest::add("WebKitWebView", "resources", testWebViewResources);
842     SingleResourceLoadTest::add("WebKitWebResource", "loading", testWebResourceLoading);
843     SingleResourceLoadTest::add("WebKitWebResource", "response", testWebResourceResponse);
844     SingleResourceLoadTest::add("WebKitWebResource", "mime-type", testWebResourceMimeType);
845     SingleResourceLoadTest::add("WebKitWebResource", "suggested-filename", testWebResourceSuggestedFilename);
846     ResourceURITrackingTest::add("WebKitWebResource", "active-uri", testWebResourceActiveURI);
847     ResourcesTest::add("WebKitWebResource", "get-data", testWebResourceGetData);
848     SingleResourceLoadTest::add("WebKitWebView", "history-cache", testWebViewResourcesHistoryCache);
849     SendRequestTest::add("WebKitWebPage", "send-request", testWebResourceSendRequest);
850 #if SOUP_CHECK_VERSION(2, 49, 91)
851     SyncRequestOnMaxConnsTest::add("WebKitWebView", "sync-request-on-max-conns", testWebViewSyncRequestOnMaxConns);
852 #endif
853 }
854
855 void afterAll()
856 {
857     delete kServer;
858 }