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