[GTK][WPE] Stop using legacy custom protocol implementation
[WebKit-https.git] / Tools / TestWebKitAPI / Tests / WebKitGLib / TestWebKitWebContext.cpp
1 /*
2  * Copyright (C) 2011 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 Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2,1 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 <limits.h>
25 #include <stdlib.h>
26 #include <wtf/HashMap.h>
27 #include <wtf/glib/GRefPtr.h>
28 #include <wtf/glib/GUniquePtr.h>
29 #include <wtf/text/StringBuilder.h>
30 #include <wtf/text/StringHash.h>
31
32 static WebKitTestServer* kServer;
33
34 static void testWebContextDefault(Test* test, gconstpointer)
35 {
36     // Check there's a single instance of the default web context.
37     g_assert_true(webkit_web_context_get_default() == webkit_web_context_get_default());
38     g_assert_true(webkit_web_context_get_default() != test->m_webContext.get());
39 }
40
41 static void testWebContextEphemeral(Test* test, gconstpointer)
42 {
43     // By default web contexts are not ephemeral.
44     g_assert_false(webkit_web_context_is_ephemeral(webkit_web_context_get_default()));
45     g_assert_false(webkit_web_context_is_ephemeral(test->m_webContext.get()));
46
47     WebKitWebsiteDataManager* manager = webkit_web_context_get_website_data_manager(webkit_web_context_get_default());
48     g_assert_true(WEBKIT_IS_WEBSITE_DATA_MANAGER(manager));
49     g_assert_false(webkit_website_data_manager_is_ephemeral(manager));
50     manager = webkit_web_context_get_website_data_manager(test->m_webContext.get());
51     g_assert_true(WEBKIT_IS_WEBSITE_DATA_MANAGER(manager));
52     g_assert_false(webkit_website_data_manager_is_ephemeral(manager));
53
54     auto webView = Test::adoptView(Test::createWebView());
55     g_assert_false(webkit_web_view_is_ephemeral(webView.get()));
56     g_assert_true(webkit_web_view_get_website_data_manager(webView.get()) == webkit_web_context_get_website_data_manager(webkit_web_context_get_default()));
57
58     webView = Test::adoptView(Test::createWebView(test->m_webContext.get()));
59     g_assert_false(webkit_web_view_is_ephemeral(webView.get()));
60     g_assert_true(webkit_web_view_get_website_data_manager(webView.get()) == manager);
61
62     GRefPtr<WebKitWebContext> context = adoptGRef(webkit_web_context_new_ephemeral());
63     g_assert_true(webkit_web_context_is_ephemeral(context.get()));
64     manager = webkit_web_context_get_website_data_manager(context.get());
65     g_assert_true(WEBKIT_IS_WEBSITE_DATA_MANAGER(manager));
66     g_assert_true(webkit_website_data_manager_is_ephemeral(manager));
67     g_assert_true(webkit_web_view_get_website_data_manager(webView.get()) != manager);
68
69     webView = Test::adoptView(Test::createWebView(context.get()));
70     g_assert_true(webkit_web_view_is_ephemeral(webView.get()));
71     g_assert_true(webkit_web_view_get_website_data_manager(webView.get()) == manager);
72
73     GRefPtr<WebKitWebsiteDataManager> ephemeralManager = adoptGRef(webkit_website_data_manager_new_ephemeral());
74     g_assert_true(webkit_website_data_manager_is_ephemeral(ephemeralManager.get()));
75     context = adoptGRef(webkit_web_context_new_with_website_data_manager(ephemeralManager.get()));
76     g_assert_true(webkit_web_context_is_ephemeral(context.get()));
77 }
78
79 #if ENABLE(NETSCAPE_PLUGIN_API)
80 class PluginsTest: public Test {
81 public:
82     MAKE_GLIB_TEST_FIXTURE(PluginsTest);
83
84     PluginsTest()
85         : m_mainLoop(g_main_loop_new(nullptr, TRUE))
86         , m_plugins(nullptr)
87     {
88         webkit_web_context_set_additional_plugins_directory(m_webContext.get(), WEBKIT_TEST_PLUGIN_DIR);
89     }
90
91     ~PluginsTest()
92     {
93         g_main_loop_unref(m_mainLoop);
94         g_list_free_full(m_plugins, g_object_unref);
95     }
96
97     static void getPluginsAsyncReadyCallback(GObject*, GAsyncResult* result, PluginsTest* test)
98     {
99         test->m_plugins = webkit_web_context_get_plugins_finish(test->m_webContext.get(), result, nullptr);
100         g_main_loop_quit(test->m_mainLoop);
101     }
102
103     GList* getPlugins()
104     {
105         g_list_free_full(m_plugins, g_object_unref);
106         webkit_web_context_get_plugins(m_webContext.get(), nullptr, reinterpret_cast<GAsyncReadyCallback>(getPluginsAsyncReadyCallback), this);
107         g_main_loop_run(m_mainLoop);
108         return m_plugins;
109     }
110
111     GMainLoop* m_mainLoop;
112     GList* m_plugins;
113 };
114
115 static void testWebContextGetPlugins(PluginsTest* test, gconstpointer)
116 {
117     GList* plugins = test->getPlugins();
118     g_assert_nonnull(plugins);
119
120     GRefPtr<WebKitPlugin> testPlugin;
121     for (GList* item = plugins; item; item = g_list_next(item)) {
122         WebKitPlugin* plugin = WEBKIT_PLUGIN(item->data);
123         test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(plugin));
124         if (!g_strcmp0(webkit_plugin_get_name(plugin), "WebKit Test PlugIn")) {
125             testPlugin = plugin;
126             break;
127         }
128     }
129     g_assert_true(WEBKIT_IS_PLUGIN(testPlugin.get()));
130
131     char normalizedPath[PATH_MAX];
132     g_assert_nonnull(realpath(WEBKIT_TEST_PLUGIN_DIR, normalizedPath));
133     GUniquePtr<char> pluginPath(g_build_filename(normalizedPath, "libTestNetscapePlugIn.so", nullptr));
134     g_assert_cmpstr(webkit_plugin_get_path(testPlugin.get()), ==, pluginPath.get());
135     g_assert_cmpstr(webkit_plugin_get_description(testPlugin.get()), ==, "Simple NetscapeĀ® plug-in that handles test content for WebKit");
136     GList* mimeInfoList = webkit_plugin_get_mime_info_list(testPlugin.get());
137     g_assert_nonnull(mimeInfoList);
138     g_assert_cmpuint(g_list_length(mimeInfoList), ==, 2);
139
140     WebKitMimeInfo* mimeInfo = static_cast<WebKitMimeInfo*>(mimeInfoList->data);
141     g_assert_cmpstr(webkit_mime_info_get_mime_type(mimeInfo), ==, "image/png");
142     g_assert_cmpstr(webkit_mime_info_get_description(mimeInfo), ==, "png image");
143     const gchar* const* extensions = webkit_mime_info_get_extensions(mimeInfo);
144     g_assert_nonnull(extensions);
145     g_assert_cmpstr(extensions[0], ==, "png");
146
147     mimeInfoList = g_list_next(mimeInfoList);
148     mimeInfo = static_cast<WebKitMimeInfo*>(mimeInfoList->data);
149     g_assert_cmpstr(webkit_mime_info_get_mime_type(mimeInfo), ==, "application/x-webkit-test-netscape");
150     g_assert_cmpstr(webkit_mime_info_get_description(mimeInfo), ==, "test netscape content");
151     extensions = webkit_mime_info_get_extensions(mimeInfo);
152     g_assert_nonnull(extensions);
153     g_assert_cmpstr(extensions[0], ==, "testnetscape");
154 }
155 #endif // ENABLE(NETSCAPE_PLUGIN_API)
156
157 static const char* kBarHTML = "<html><body>Bar</body></html>";
158 static const char* kEchoHTMLFormat = "<html><body>%s</body></html>";
159 static const char* errorDomain = "test";
160 static const int errorCode = 10;
161
162 static const char* genericErrorMessage = "Error message.";
163 static const char* beforeReceiveResponseErrorMessage = "Error before didReceiveResponse.";
164
165 class URISchemeTest: public LoadTrackingTest {
166 public:
167     MAKE_GLIB_TEST_FIXTURE(URISchemeTest);
168
169     struct URISchemeHandler {
170         URISchemeHandler()
171             : replyLength(0)
172         {
173         }
174
175         URISchemeHandler(const char* reply, int replyLength, const char* mimeType)
176             : reply(reply)
177             , replyLength(replyLength)
178             , mimeType(mimeType)
179         {
180         }
181
182         CString reply;
183         int replyLength;
184         CString mimeType;
185     };
186
187     static void uriSchemeRequestCallback(WebKitURISchemeRequest* request, gpointer userData)
188     {
189         URISchemeTest* test = static_cast<URISchemeTest*>(userData);
190         test->m_uriSchemeRequest = request;
191         test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(request));
192
193         g_assert_true(webkit_uri_scheme_request_get_web_view(request) == test->m_webView);
194
195         const char* scheme = webkit_uri_scheme_request_get_scheme(request);
196         g_assert_nonnull(scheme);
197         g_assert_true(test->m_handlersMap.contains(String::fromUTF8(scheme)));
198
199         const URISchemeHandler& handler = test->m_handlersMap.get(String::fromUTF8(scheme));
200
201         GRefPtr<GInputStream> inputStream = adoptGRef(g_memory_input_stream_new());
202         test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(inputStream.get()));
203
204         const gchar* requestPath = webkit_uri_scheme_request_get_path(request);
205
206         if (!g_strcmp0(scheme, "error")) {
207             if (!g_strcmp0(requestPath, "before-response")) {
208                 GUniquePtr<GError> error(g_error_new_literal(g_quark_from_string(errorDomain), errorCode, beforeReceiveResponseErrorMessage));
209                 // We call finish() and then finish_error() to make sure that not even
210                 // the didReceiveResponse message is processed at the time of failing.
211                 webkit_uri_scheme_request_finish(request, G_INPUT_STREAM(inputStream.get()), handler.replyLength, handler.mimeType.data());
212                 webkit_uri_scheme_request_finish_error(request, error.get());
213             } else if (!g_strcmp0(requestPath, "after-first-chunk")) {
214                 g_memory_input_stream_add_data(G_MEMORY_INPUT_STREAM(inputStream.get()), handler.reply.data(), handler.reply.length(), 0);
215                 webkit_uri_scheme_request_finish(request, inputStream.get(), handler.replyLength, handler.mimeType.data());
216                 // We need to wait until we reach the load-committed state before calling webkit_uri_scheme_request_finish_error(),
217                 // so we rely on the test using finishOnCommittedAndWaitUntilLoadFinished() to actually call it from loadCommitted().
218             } else {
219                 GUniquePtr<GError> error(g_error_new_literal(g_quark_from_string(errorDomain), errorCode, genericErrorMessage));
220                 webkit_uri_scheme_request_finish_error(request, error.get());
221             }
222             return;
223         }
224
225         if (!g_strcmp0(scheme, "echo")) {
226             char* replyHTML = g_strdup_printf(handler.reply.data(), requestPath);
227             g_memory_input_stream_add_data(G_MEMORY_INPUT_STREAM(inputStream.get()), replyHTML, strlen(replyHTML), g_free);
228         } else if (!g_strcmp0(scheme, "closed"))
229             g_input_stream_close(inputStream.get(), 0, 0);
230         else if (!handler.reply.isNull())
231             g_memory_input_stream_add_data(G_MEMORY_INPUT_STREAM(inputStream.get()), handler.reply.data(), handler.reply.length(), 0);
232
233         webkit_uri_scheme_request_finish(request, inputStream.get(), handler.replyLength, handler.mimeType.data());
234     }
235
236     void registerURISchemeHandler(const char* scheme, const char* reply, int replyLength, const char* mimeType)
237     {
238         m_handlersMap.set(String::fromUTF8(scheme), URISchemeHandler(reply, replyLength, mimeType));
239         webkit_web_context_register_uri_scheme(m_webContext.get(), scheme, uriSchemeRequestCallback, this, 0);
240     }
241
242     GRefPtr<WebKitURISchemeRequest> m_uriSchemeRequest;
243     HashMap<String, URISchemeHandler> m_handlersMap;
244 };
245
246 String generateHTMLContent(unsigned contentLength)
247 {
248     String baseString("abcdefghijklmnopqrstuvwxyz0123457890");
249     unsigned baseLength = baseString.length();
250
251     StringBuilder builder;
252     builder.append("<html><body>");
253
254     if (contentLength <= baseLength)
255         builder.appendSubstring(baseString, 0, contentLength);
256     else {
257         unsigned currentLength = 0;
258         while (currentLength < contentLength) {
259             if ((currentLength + baseLength) <= contentLength)
260                 builder.append(baseString);
261             else
262                 builder.appendSubstring(baseString, 0, contentLength - currentLength);
263
264             // Account for the 12 characters of the '<html><body>' prefix.
265             currentLength = builder.length() - 12;
266         }
267     }
268     builder.append("</body></html>");
269
270     return builder.toString();
271 }
272
273 static void testWebContextURIScheme(URISchemeTest* test, gconstpointer)
274 {
275     test->registerURISchemeHandler("foo", kBarHTML, strlen(kBarHTML), "text/html");
276     test->loadURI("foo:blank");
277     test->waitUntilLoadFinished();
278     size_t mainResourceDataSize = 0;
279     const char* mainResourceData = test->mainResourceData(mainResourceDataSize);
280     g_assert_cmpint(mainResourceDataSize, ==, strlen(kBarHTML));
281     g_assert_cmpint(strncmp(mainResourceData, kBarHTML, mainResourceDataSize), ==, 0);
282
283     test->registerURISchemeHandler("echo", kEchoHTMLFormat, -1, "text/html");
284     test->loadURI("echo:hello-world");
285     test->waitUntilLoadFinished();
286     GUniquePtr<char> echoHTML(g_strdup_printf(kEchoHTMLFormat, webkit_uri_scheme_request_get_path(test->m_uriSchemeRequest.get())));
287     mainResourceDataSize = 0;
288     mainResourceData = test->mainResourceData(mainResourceDataSize);
289     g_assert_cmpint(mainResourceDataSize, ==, strlen(echoHTML.get()));
290     g_assert_cmpint(strncmp(mainResourceData, echoHTML.get(), mainResourceDataSize), ==, 0);
291
292     test->loadURI("echo:with#fragment");
293     test->waitUntilLoadFinished();
294     g_assert_cmpstr(webkit_uri_scheme_request_get_path(test->m_uriSchemeRequest.get()), ==, "with");
295     g_assert_cmpstr(webkit_uri_scheme_request_get_uri(test->m_uriSchemeRequest.get()), ==, "echo:with#fragment");
296     echoHTML.reset(g_strdup_printf(kEchoHTMLFormat, webkit_uri_scheme_request_get_path(test->m_uriSchemeRequest.get())));
297     mainResourceDataSize = 0;
298     mainResourceData = test->mainResourceData(mainResourceDataSize);
299     g_assert_cmpint(mainResourceDataSize, ==, strlen(echoHTML.get()));
300     g_assert_cmpint(strncmp(mainResourceData, echoHTML.get(), mainResourceDataSize), ==, 0);
301
302     test->registerURISchemeHandler("nomime", kBarHTML, -1, nullptr);
303     test->m_loadEvents.clear();
304     test->loadURI("nomime:foo-bar");
305     test->waitUntilLoadFinished();
306     g_assert_true(test->m_loadEvents.contains(LoadTrackingTest::ProvisionalLoadFailed));
307
308     test->registerURISchemeHandler("empty", nullptr, 0, "text/html");
309     test->m_loadEvents.clear();
310     test->loadURI("empty:nothing");
311     test->waitUntilLoadFinished();
312     g_assert_false(test->m_loadEvents.contains(LoadTrackingTest::ProvisionalLoadFailed));
313     g_assert_false(test->m_loadEvents.contains(LoadTrackingTest::LoadFailed));
314
315     // Anything over 8192 bytes will get multiple calls to g_input_stream_read_async in
316     // WebKitURISchemeRequest when reading data, but we still need way more than that to
317     // ensure that we reach the load-committed state before failing, so we use an 8MB HTML.
318     String longHTMLContent = generateHTMLContent(8 * 1024 * 1024);
319     test->registerURISchemeHandler("error", longHTMLContent.utf8().data(), -1, "text/html");
320     test->m_loadEvents.clear();
321     test->loadURI("error:error");
322     test->waitUntilLoadFinished();
323     g_assert_true(test->m_loadEvents.contains(LoadTrackingTest::ProvisionalLoadFailed));
324     g_assert_true(test->m_loadFailed);
325     g_assert_error(test->m_error.get(), g_quark_from_string(errorDomain), errorCode);
326     g_assert_cmpstr(test->m_error->message, ==, genericErrorMessage);
327
328     test->m_loadEvents.clear();
329     test->loadURI("error:before-response");
330     test->waitUntilLoadFinished();
331     g_assert_true(test->m_loadEvents.contains(LoadTrackingTest::ProvisionalLoadFailed));
332     g_assert_true(test->m_loadFailed);
333     g_assert_error(test->m_error.get(), g_quark_from_string(errorDomain), errorCode);
334     g_assert_cmpstr(test->m_error->message, ==, beforeReceiveResponseErrorMessage);
335
336     test->registerURISchemeHandler("closed", nullptr, 0, nullptr);
337     test->m_loadEvents.clear();
338     test->loadURI("closed:input-stream");
339     test->waitUntilLoadFinished();
340     g_assert_true(test->m_loadEvents.contains(LoadTrackingTest::ProvisionalLoadFailed));
341     g_assert_true(test->m_loadFailed);
342     g_assert_error(test->m_error.get(), G_IO_ERROR, G_IO_ERROR_CLOSED);
343 }
344
345 #if PLATFORM(GTK)
346 static void testWebContextSpellChecker(Test* test, gconstpointer)
347 {
348     WebKitWebContext* webContext = test->m_webContext.get();
349
350     // Check what happens if no spell checking language has been set.
351     const gchar* const* currentLanguage = webkit_web_context_get_spell_checking_languages(webContext);
352     g_assert_null(currentLanguage);
353
354     // Set the language to a specific one.
355     GRefPtr<GPtrArray> languages = adoptGRef(g_ptr_array_new());
356     g_ptr_array_add(languages.get(), const_cast<gpointer>(static_cast<const void*>("en_US")));
357     g_ptr_array_add(languages.get(), 0);
358     webkit_web_context_set_spell_checking_languages(webContext, reinterpret_cast<const char* const*>(languages->pdata));
359     currentLanguage = webkit_web_context_get_spell_checking_languages(webContext);
360     g_assert_cmpuint(g_strv_length(const_cast<char**>(currentLanguage)), ==, 1);
361     g_assert_cmpstr(currentLanguage[0], ==, "en_US");
362
363     // Set the language string to list of valid languages.
364     g_ptr_array_remove_index_fast(languages.get(), languages->len - 1);
365     g_ptr_array_add(languages.get(), const_cast<gpointer>(static_cast<const void*>("en_GB")));
366     g_ptr_array_add(languages.get(), 0);
367     webkit_web_context_set_spell_checking_languages(webContext, reinterpret_cast<const char* const*>(languages->pdata));
368     currentLanguage = webkit_web_context_get_spell_checking_languages(webContext);
369     g_assert_cmpuint(g_strv_length(const_cast<char**>(currentLanguage)), ==, 2);
370     g_assert_cmpstr(currentLanguage[0], ==, "en_US");
371     g_assert_cmpstr(currentLanguage[1], ==, "en_GB");
372
373     // Try passing a wrong language along with good ones.
374     g_ptr_array_remove_index_fast(languages.get(), languages->len - 1);
375     g_ptr_array_add(languages.get(), const_cast<gpointer>(static_cast<const void*>("bd_WR")));
376     g_ptr_array_add(languages.get(), 0);
377     webkit_web_context_set_spell_checking_languages(webContext, reinterpret_cast<const char* const*>(languages->pdata));
378     currentLanguage = webkit_web_context_get_spell_checking_languages(webContext);
379     g_assert_cmpuint(g_strv_length(const_cast<char**>(currentLanguage)), ==, 2);
380     g_assert_cmpstr(currentLanguage[0], ==, "en_US");
381     g_assert_cmpstr(currentLanguage[1], ==, "en_GB");
382
383     // Try passing a list with only wrong languages.
384     languages = adoptGRef(g_ptr_array_new());
385     g_ptr_array_add(languages.get(), const_cast<gpointer>(static_cast<const void*>("bd_WR")));
386     g_ptr_array_add(languages.get(), const_cast<gpointer>(static_cast<const void*>("wr_BD")));
387     g_ptr_array_add(languages.get(), 0);
388     webkit_web_context_set_spell_checking_languages(webContext, reinterpret_cast<const char* const*>(languages->pdata));
389     currentLanguage = webkit_web_context_get_spell_checking_languages(webContext);
390     g_assert_null(currentLanguage);
391
392     // Check disabling and re-enabling spell checking.
393     webkit_web_context_set_spell_checking_enabled(webContext, FALSE);
394     g_assert_false(webkit_web_context_get_spell_checking_enabled(webContext));
395     webkit_web_context_set_spell_checking_enabled(webContext, TRUE);
396     g_assert_true(webkit_web_context_get_spell_checking_enabled(webContext));
397 }
398 #endif // PLATFORM(GTK)
399
400 static void testWebContextLanguages(WebViewTest* test, gconstpointer)
401 {
402     static const char* expectedDefaultLanguage = "en-US";
403     test->loadURI(kServer->getURIForPath("/").data());
404     test->waitUntilLoadFinished();
405     size_t mainResourceDataSize = 0;
406     const char* mainResourceData = test->mainResourceData(mainResourceDataSize);
407     g_assert_cmpuint(mainResourceDataSize, ==, strlen(expectedDefaultLanguage));
408     g_assert_cmpint(strncmp(mainResourceData, expectedDefaultLanguage, mainResourceDataSize), ==, 0);
409
410     GRefPtr<GPtrArray> languages = adoptGRef(g_ptr_array_new());
411     g_ptr_array_add(languages.get(), const_cast<gpointer>(static_cast<const void*>("en")));
412     g_ptr_array_add(languages.get(), const_cast<gpointer>(static_cast<const void*>("ES_es")));
413     g_ptr_array_add(languages.get(), const_cast<gpointer>(static_cast<const void*>("dE")));
414     g_ptr_array_add(languages.get(), 0);
415     webkit_web_context_set_preferred_languages(test->m_webContext.get(), reinterpret_cast<const char* const*>(languages->pdata));
416
417     static const char* expectedLanguages = "en,ES-es;q=0.90,dE;q=0.80";
418     test->loadURI(kServer->getURIForPath("/").data());
419     test->waitUntilLoadFinished();
420     mainResourceDataSize = 0;
421     mainResourceData = test->mainResourceData(mainResourceDataSize);
422     g_assert_cmpuint(mainResourceDataSize, ==, strlen(expectedLanguages));
423     g_assert_cmpint(strncmp(mainResourceData, expectedLanguages, mainResourceDataSize), ==, 0);
424
425     // When using the C locale, en-US should be used as default.
426     const char* cLanguage[] = { "C", nullptr };
427     webkit_web_context_set_preferred_languages(test->m_webContext.get(), cLanguage);
428     GUniqueOutPtr<GError> error;
429     WebKitJavascriptResult* javascriptResult = test->runJavaScriptAndWaitUntilFinished("Intl.DateTimeFormat().resolvedOptions().locale", &error.outPtr());
430     g_assert_nonnull(javascriptResult);
431     g_assert_no_error(error.get());
432     GUniquePtr<char> locale(WebViewTest::javascriptResultToCString(javascriptResult));
433     g_assert_cmpstr(locale.get(), ==, expectedDefaultLanguage);
434
435     // When using the POSIX locale, en-US should be used as default.
436     const char* posixLanguage[] = { "POSIX", nullptr };
437     webkit_web_context_set_preferred_languages(test->m_webContext.get(), posixLanguage);
438     javascriptResult = test->runJavaScriptAndWaitUntilFinished("Intl.DateTimeFormat().resolvedOptions().locale", &error.outPtr());
439     g_assert_nonnull(javascriptResult);
440     g_assert_no_error(error.get());
441     locale.reset(WebViewTest::javascriptResultToCString(javascriptResult));
442     g_assert_cmpstr(locale.get(), ==, expectedDefaultLanguage);
443
444     // An invalid locale should throw an exception.
445     const char* invalidLanguage[] = { "A", nullptr };
446     webkit_web_context_set_preferred_languages(test->m_webContext.get(), invalidLanguage);
447     javascriptResult = test->runJavaScriptAndWaitUntilFinished("Intl.DateTimeFormat().resolvedOptions().locale", &error.outPtr());
448     g_assert_nonnull(javascriptResult);
449     g_assert_error(error.get(), WEBKIT_JAVASCRIPT_ERROR, WEBKIT_JAVASCRIPT_ERROR_SCRIPT_FAILED);
450 }
451
452 static void serverCallback(SoupServer* server, SoupMessage* message, const char* path, GHashTable*, SoupClientContext*, gpointer)
453 {
454     if (message->method != SOUP_METHOD_GET) {
455         soup_message_set_status(message, SOUP_STATUS_NOT_IMPLEMENTED);
456         return;
457     }
458
459     if (g_str_equal(path, "/")) {
460         const char* acceptLanguage = soup_message_headers_get_one(message->request_headers, "Accept-Language");
461         soup_message_set_status(message, SOUP_STATUS_OK);
462         soup_message_body_append(message->response_body, SOUP_MEMORY_COPY, acceptLanguage, strlen(acceptLanguage));
463         soup_message_body_complete(message->response_body);
464     } else if (g_str_equal(path, "/empty")) {
465         const char* emptyHTML = "<html><body></body></html>";
466         soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, emptyHTML, strlen(emptyHTML));
467         soup_message_body_complete(message->response_body);
468         soup_message_set_status(message, SOUP_STATUS_OK);
469     } else if (g_str_equal(path, "/echoPort")) {
470         char* port = g_strdup_printf("%u", soup_server_get_port(server));
471         soup_message_body_append(message->response_body, SOUP_MEMORY_TAKE, port, strlen(port));
472         soup_message_body_complete(message->response_body);
473         soup_message_set_status(message, SOUP_STATUS_OK);
474     } else
475         soup_message_set_status(message, SOUP_STATUS_NOT_FOUND);
476 }
477
478 class SecurityPolicyTest: public Test {
479 public:
480     MAKE_GLIB_TEST_FIXTURE(SecurityPolicyTest);
481
482     enum SecurityPolicy {
483         Local = 1 << 1,
484         NoAccess = 1 << 2,
485         DisplayIsolated = 1 << 3,
486         Secure = 1 << 4,
487         CORSEnabled = 1 << 5,
488         EmptyDocument = 1 << 6
489     };
490
491     SecurityPolicyTest()
492         : m_manager(webkit_web_context_get_security_manager(m_webContext.get()))
493     {
494     }
495
496     void verifyThatSchemeMatchesPolicy(const char* scheme, unsigned policy)
497     {
498         if (policy & Local)
499             g_assert_true(webkit_security_manager_uri_scheme_is_local(m_manager, scheme));
500         else
501             g_assert_false(webkit_security_manager_uri_scheme_is_local(m_manager, scheme));
502         if (policy & NoAccess)
503             g_assert_true(webkit_security_manager_uri_scheme_is_no_access(m_manager, scheme));
504         else
505             g_assert_false(webkit_security_manager_uri_scheme_is_no_access(m_manager, scheme));
506         if (policy & DisplayIsolated)
507             g_assert_true(webkit_security_manager_uri_scheme_is_display_isolated(m_manager, scheme));
508         else
509             g_assert_false(webkit_security_manager_uri_scheme_is_display_isolated(m_manager, scheme));
510         if (policy & Secure)
511             g_assert_true(webkit_security_manager_uri_scheme_is_secure(m_manager, scheme));
512         else
513             g_assert_false(webkit_security_manager_uri_scheme_is_secure(m_manager, scheme));
514         if (policy & CORSEnabled)
515             g_assert_true(webkit_security_manager_uri_scheme_is_cors_enabled(m_manager, scheme));
516         else
517             g_assert_false(webkit_security_manager_uri_scheme_is_cors_enabled(m_manager, scheme));
518         if (policy & EmptyDocument)
519             g_assert_true(webkit_security_manager_uri_scheme_is_empty_document(m_manager, scheme));
520         else
521             g_assert_false(webkit_security_manager_uri_scheme_is_empty_document(m_manager, scheme));
522     }
523
524     WebKitSecurityManager* m_manager;
525 };
526
527 static void testWebContextSecurityPolicy(SecurityPolicyTest* test, gconstpointer)
528 {
529     // VerifyThatSchemeMatchesPolicy default policy for well known schemes.
530     test->verifyThatSchemeMatchesPolicy("http", SecurityPolicyTest::CORSEnabled);
531     test->verifyThatSchemeMatchesPolicy("https", SecurityPolicyTest::CORSEnabled | SecurityPolicyTest::Secure);
532     test->verifyThatSchemeMatchesPolicy("file", SecurityPolicyTest::Local);
533     test->verifyThatSchemeMatchesPolicy("data", SecurityPolicyTest::NoAccess | SecurityPolicyTest::Secure);
534     test->verifyThatSchemeMatchesPolicy("about", SecurityPolicyTest::NoAccess | SecurityPolicyTest::Secure | SecurityPolicyTest::EmptyDocument);
535
536     // Custom scheme.
537     test->verifyThatSchemeMatchesPolicy("foo", 0);
538
539     webkit_security_manager_register_uri_scheme_as_local(test->m_manager, "foo");
540     test->verifyThatSchemeMatchesPolicy("foo", SecurityPolicyTest::Local);
541     webkit_security_manager_register_uri_scheme_as_no_access(test->m_manager, "foo");
542     test->verifyThatSchemeMatchesPolicy("foo", SecurityPolicyTest::Local | SecurityPolicyTest::NoAccess);
543     webkit_security_manager_register_uri_scheme_as_display_isolated(test->m_manager, "foo");
544     test->verifyThatSchemeMatchesPolicy("foo", SecurityPolicyTest::Local | SecurityPolicyTest::NoAccess | SecurityPolicyTest::DisplayIsolated);
545     webkit_security_manager_register_uri_scheme_as_secure(test->m_manager, "foo");
546     test->verifyThatSchemeMatchesPolicy("foo", SecurityPolicyTest::Local | SecurityPolicyTest::NoAccess | SecurityPolicyTest::DisplayIsolated | SecurityPolicyTest::Secure);
547     webkit_security_manager_register_uri_scheme_as_cors_enabled(test->m_manager, "foo");
548     test->verifyThatSchemeMatchesPolicy("foo", SecurityPolicyTest::Local | SecurityPolicyTest::NoAccess | SecurityPolicyTest::DisplayIsolated | SecurityPolicyTest::Secure
549         | SecurityPolicyTest::CORSEnabled);
550     webkit_security_manager_register_uri_scheme_as_empty_document(test->m_manager, "foo");
551     test->verifyThatSchemeMatchesPolicy("foo", SecurityPolicyTest::Local | SecurityPolicyTest::NoAccess | SecurityPolicyTest::DisplayIsolated | SecurityPolicyTest::Secure
552         | SecurityPolicyTest::CORSEnabled | SecurityPolicyTest::EmptyDocument);
553 }
554
555 static void consoleMessageReceivedCallback(WebKitUserContentManager*, WebKitJavascriptResult* message, Vector<WebKitJavascriptResult*>* result)
556 {
557     g_assert_nonnull(message);
558     g_assert_nonnull(result);
559     result->append(webkit_javascript_result_ref(message));
560 }
561
562 static void testWebContextSecurityFileXHR(WebViewTest* test, gconstpointer)
563 {
564     GUniquePtr<char> fileURL(g_strdup_printf("file://%s/simple.html", Test::getResourcesDir(Test::WebKit2Resources).data()));
565     test->loadURI(fileURL.get());
566     test->waitUntilLoadFinished();
567
568     GUniquePtr<char> jsonURL(g_strdup_printf("file://%s/simple.json", Test::getResourcesDir().data()));
569     GUniquePtr<char> xhr(g_strdup_printf("var xhr = new XMLHttpRequest; xhr.open(\"GET\", \"%s\"); xhr.send();", jsonURL.get()));
570
571     Vector<WebKitJavascriptResult*> consoleMessages;
572     webkit_user_content_manager_register_script_message_handler(test->m_userContentManager.get(), "console");
573     g_signal_connect(test->m_userContentManager.get(), "script-message-received::console", G_CALLBACK(consoleMessageReceivedCallback), &consoleMessages);
574
575     // By default file access is not allowed, this will show a console message with a cross-origin error.
576     GUniqueOutPtr<GError> error;
577     WebKitJavascriptResult* javascriptResult = test->runJavaScriptAndWaitUntilFinished(xhr.get(), &error.outPtr());
578     g_assert_nonnull(javascriptResult);
579     g_assert_no_error(error.get());
580     g_assert_cmpuint(consoleMessages.size(), ==, 2);
581     Vector<GUniquePtr<char>, 2> expectedMessages;
582     expectedMessages.append(g_strdup("Cross origin requests are only supported for HTTP."));
583     expectedMessages.append(g_strdup_printf("XMLHttpRequest cannot load %s due to access control checks.", jsonURL.get()));
584     unsigned i = 0;
585     for (auto* consoleMessage : consoleMessages) {
586         g_assert_nonnull(consoleMessage);
587         GUniquePtr<char> messageString(WebViewTest::javascriptResultToCString(consoleMessage));
588         GRefPtr<GVariant> variant = g_variant_parse(G_VARIANT_TYPE("(uusus)"), messageString.get(), nullptr, nullptr, nullptr);
589         g_assert_nonnull(variant.get());
590         unsigned level;
591         const char* messageText;
592         g_variant_get(variant.get(), "(uu&su&s)", nullptr, &level, &messageText, nullptr, nullptr);
593         g_assert_cmpuint(level, ==, 3); // Console error message.
594         g_assert_cmpstr(messageText, ==, expectedMessages[i++].get());
595         webkit_javascript_result_unref(consoleMessage);
596     }
597     consoleMessages.clear();
598
599     // Allow file access from file URLs.
600     webkit_settings_set_allow_file_access_from_file_urls(webkit_web_view_get_settings(test->m_webView), TRUE);
601     test->loadURI(fileURL.get());
602     test->waitUntilLoadFinished();
603     javascriptResult = test->runJavaScriptAndWaitUntilFinished(xhr.get(), &error.outPtr());
604     g_assert_nonnull(javascriptResult);
605     g_assert_no_error(error.get());
606
607     // It isn't still possible to load file from an HTTP URL.
608     test->loadURI(kServer->getURIForPath("/").data());
609     test->waitUntilLoadFinished();
610     javascriptResult = test->runJavaScriptAndWaitUntilFinished(xhr.get(), &error.outPtr());
611     g_assert_nonnull(javascriptResult);
612     g_assert_no_error(error.get());
613     i = 0;
614     for (auto* consoleMessage : consoleMessages) {
615         g_assert_nonnull(consoleMessage);
616         GUniquePtr<char> messageString(WebViewTest::javascriptResultToCString(consoleMessage));
617         GRefPtr<GVariant> variant = g_variant_parse(G_VARIANT_TYPE("(uusus)"), messageString.get(), nullptr, nullptr, nullptr);
618         g_assert_nonnull(variant.get());
619         unsigned level;
620         const char* messageText;
621         g_variant_get(variant.get(), "(uu&su&s)", nullptr, &level, &messageText, nullptr, nullptr);
622         g_assert_cmpuint(level, ==, 3); // Console error message.
623         g_assert_cmpstr(messageText, ==, expectedMessages[i++].get());
624         webkit_javascript_result_unref(consoleMessage);
625     }
626     consoleMessages.clear();
627
628     g_signal_handlers_disconnect_matched(test->m_userContentManager.get(), G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, &consoleMessages);
629     webkit_user_content_manager_unregister_script_message_handler(test->m_userContentManager.get(), "console");
630
631     webkit_settings_set_allow_file_access_from_file_urls(webkit_web_view_get_settings(test->m_webView), FALSE);
632 }
633
634 class ProxyTest : public WebViewTest {
635 public:
636     MAKE_GLIB_TEST_FIXTURE(ProxyTest);
637
638 #if SOUP_CHECK_VERSION(2, 61, 90)
639     enum class WebSocketServerType {
640         Unknown,
641         NoProxy,
642         Proxy
643     };
644
645     static void webSocketProxyServerCallback(SoupServer*, SoupWebsocketConnection*, const char* path, SoupClientContext*, gpointer userData)
646     {
647         static_cast<ProxyTest*>(userData)->webSocketConnected(ProxyTest::WebSocketServerType::Proxy);
648     }
649 #endif
650
651     ProxyTest()
652     {
653         // This "proxy server" is actually just a different instance of the main
654         // test server (kServer), listening on a different port. Requests
655         // will not actually be proxied to kServer because proxyServer is not
656         // actually a proxy server. We're testing whether the proxy settings
657         // work, not whether we can write a soup proxy server.
658         m_proxyServer.run(serverCallback);
659         g_assert_nonnull(m_proxyServer.baseURI());
660 #if SOUP_CHECK_VERSION(2, 61, 90)
661         m_proxyServer.addWebSocketHandler(webSocketProxyServerCallback, this);
662         g_assert_nonnull(m_proxyServer.baseWebSocketURI());
663 #endif
664     }
665
666     CString loadURIAndGetMainResourceData(const char* uri)
667     {
668         loadURI(uri);
669         waitUntilLoadFinished();
670         size_t dataSize = 0;
671         const char* data = mainResourceData(dataSize);
672         return CString(data, dataSize);
673     }
674
675     GUniquePtr<char> proxyServerPortAsString()
676     {
677         GUniquePtr<char> port(g_strdup_printf("%u", soup_uri_get_port(m_proxyServer.baseURI())));
678         return port;
679     }
680
681 #if SOUP_CHECK_VERSION(2, 61, 90)
682     void webSocketConnected(WebSocketServerType serverType)
683     {
684         m_webSocketRequestReceived = serverType;
685         quitMainLoop();
686     }
687
688     WebSocketServerType createWebSocketAndWaitUntilConnected()
689     {
690         m_webSocketRequestReceived = WebSocketServerType::Unknown;
691         GUniquePtr<char> createWebSocket(g_strdup_printf("var ws = new WebSocket('%s');", kServer->getWebSocketURIForPath("/foo").data()));
692         webkit_web_view_run_javascript(m_webView, createWebSocket.get(), nullptr, nullptr, nullptr);
693         g_main_loop_run(m_mainLoop);
694         return m_webSocketRequestReceived;
695     }
696 #endif
697
698     WebKitTestServer m_proxyServer;
699
700 #if SOUP_CHECK_VERSION(2, 61, 90)
701     WebSocketServerType m_webSocketRequestReceived { WebSocketServerType::Unknown };
702 #endif
703 };
704
705 #if SOUP_CHECK_VERSION(2, 61, 90)
706 static void webSocketServerCallback(SoupServer*, SoupWebsocketConnection*, const char*, SoupClientContext*, gpointer userData)
707 {
708     static_cast<ProxyTest*>(userData)->webSocketConnected(ProxyTest::WebSocketServerType::NoProxy);
709 }
710 #endif
711
712 static void ephemeralViewloadChanged(WebKitWebView* webView, WebKitLoadEvent loadEvent, WebViewTest* test)
713 {
714     if (loadEvent != WEBKIT_LOAD_FINISHED)
715         return;
716     g_signal_handlers_disconnect_by_func(webView, reinterpret_cast<void*>(ephemeralViewloadChanged), test);
717     test->quitMainLoop();
718 }
719
720 static void testWebContextProxySettings(ProxyTest* test, gconstpointer)
721 {
722     // Proxy URI is unset by default. Requests to kServer should be received by kServer.
723     GUniquePtr<char> serverPortAsString(g_strdup_printf("%u", soup_uri_get_port(kServer->baseURI())));
724     auto mainResourceData = test->loadURIAndGetMainResourceData(kServer->getURIForPath("/echoPort").data());
725     ASSERT_CMP_CSTRING(mainResourceData, ==, serverPortAsString.get());
726
727 #if SOUP_CHECK_VERSION(2, 61, 90)
728     // WebSocket requests should also be received by kServer.
729     kServer->addWebSocketHandler(webSocketServerCallback, test);
730     auto serverType = test->createWebSocketAndWaitUntilConnected();
731     g_assert_true(serverType == ProxyTest::WebSocketServerType::NoProxy);
732 #endif
733
734     // Set default proxy URI to point to proxyServer. Requests to kServer should be received by proxyServer instead.
735     GUniquePtr<char> proxyURI(soup_uri_to_string(test->m_proxyServer.baseURI(), FALSE));
736     WebKitNetworkProxySettings* settings = webkit_network_proxy_settings_new(proxyURI.get(), nullptr);
737     webkit_web_context_set_network_proxy_settings(test->m_webContext.get(), WEBKIT_NETWORK_PROXY_MODE_CUSTOM, settings);
738     GUniquePtr<char> proxyServerPortAsString = test->proxyServerPortAsString();
739     mainResourceData = test->loadURIAndGetMainResourceData(kServer->getURIForPath("/echoPort").data());
740     ASSERT_CMP_CSTRING(mainResourceData, ==, proxyServerPortAsString.get());
741     webkit_network_proxy_settings_free(settings);
742
743 #if SOUP_CHECK_VERSION(2, 61, 90)
744     // WebSocket requests should also be received by proxyServer.
745     serverType = test->createWebSocketAndWaitUntilConnected();
746     g_assert_true(serverType == ProxyTest::WebSocketServerType::Proxy);
747 #endif
748
749     // Proxy settings also affect ephemeral web views.
750     auto webView = Test::adoptView(g_object_new(WEBKIT_TYPE_WEB_VIEW,
751 #if PLATFORM(WPE)
752         "backend", Test::createWebViewBackend(),
753 #endif
754         "web-context", test->m_webContext.get(),
755         "is-ephemeral", TRUE,
756         nullptr));
757     g_assert_true(webkit_web_view_is_ephemeral(webView.get()));
758     g_assert_false(webkit_web_context_is_ephemeral(webkit_web_view_get_context(webView.get())));
759
760     g_signal_connect(webView.get(), "load-changed", G_CALLBACK(ephemeralViewloadChanged), test);
761     webkit_web_view_load_uri(webView.get(), kServer->getURIForPath("/echoPort").data());
762     g_main_loop_run(test->m_mainLoop);
763     WebKitWebResource* resource = webkit_web_view_get_main_resource(webView.get());
764     g_assert_true(WEBKIT_IS_WEB_RESOURCE(resource));
765     webkit_web_resource_get_data(resource, nullptr, [](GObject* object, GAsyncResult* result, gpointer userData) {
766         size_t dataSize;
767         GUniquePtr<char> data(reinterpret_cast<char*>(webkit_web_resource_get_data_finish(WEBKIT_WEB_RESOURCE(object), result, &dataSize, nullptr)));
768         g_assert_nonnull(data);
769         auto* test = static_cast<ProxyTest*>(userData);
770         GUniquePtr<char> proxyServerPortAsString = test->proxyServerPortAsString();
771         ASSERT_CMP_CSTRING(CString(data.get(), dataSize), ==, proxyServerPortAsString.get());
772         test->quitMainLoop();
773         }, test);
774     g_main_loop_run(test->m_mainLoop);
775
776     // Remove the proxy. Requests to kServer should be received by kServer again.
777     webkit_web_context_set_network_proxy_settings(test->m_webContext.get(), WEBKIT_NETWORK_PROXY_MODE_NO_PROXY, nullptr);
778     mainResourceData = test->loadURIAndGetMainResourceData(kServer->getURIForPath("/echoPort").data());
779     ASSERT_CMP_CSTRING(mainResourceData, ==, serverPortAsString.get());
780
781     // Use a default proxy uri, but ignoring requests to localhost.
782     static const char* ignoreHosts[] = { "localhost", nullptr };
783     settings = webkit_network_proxy_settings_new(proxyURI.get(), ignoreHosts);
784     webkit_web_context_set_network_proxy_settings(test->m_webContext.get(), WEBKIT_NETWORK_PROXY_MODE_CUSTOM, settings);
785     mainResourceData = test->loadURIAndGetMainResourceData(kServer->getURIForPath("/echoPort").data());
786     ASSERT_CMP_CSTRING(mainResourceData, ==, proxyServerPortAsString.get());
787     GUniquePtr<char> localhostEchoPortURI(g_strdup_printf("http://localhost:%s/echoPort", serverPortAsString.get()));
788     mainResourceData = test->loadURIAndGetMainResourceData(localhostEchoPortURI.get());
789     ASSERT_CMP_CSTRING(mainResourceData, ==, serverPortAsString.get());
790     webkit_network_proxy_settings_free(settings);
791
792     // Remove the proxy again to ensure next test is not using any previous values.
793     webkit_web_context_set_network_proxy_settings(test->m_webContext.get(), WEBKIT_NETWORK_PROXY_MODE_NO_PROXY, nullptr);
794     mainResourceData = test->loadURIAndGetMainResourceData(kServer->getURIForPath("/echoPort").data());
795     ASSERT_CMP_CSTRING(mainResourceData, ==, serverPortAsString.get());
796
797     // Use scheme specific proxy instead of the default.
798     settings = webkit_network_proxy_settings_new(nullptr, nullptr);
799     webkit_network_proxy_settings_add_proxy_for_scheme(settings, "http", proxyURI.get());
800     webkit_web_context_set_network_proxy_settings(test->m_webContext.get(), WEBKIT_NETWORK_PROXY_MODE_CUSTOM, settings);
801     mainResourceData = test->loadURIAndGetMainResourceData(kServer->getURIForPath("/echoPort").data());
802     ASSERT_CMP_CSTRING(mainResourceData, ==, proxyServerPortAsString.get());
803     webkit_network_proxy_settings_free(settings);
804
805     // Reset to use the default resolver.
806     webkit_web_context_set_network_proxy_settings(test->m_webContext.get(), WEBKIT_NETWORK_PROXY_MODE_DEFAULT, nullptr);
807     mainResourceData = test->loadURIAndGetMainResourceData(kServer->getURIForPath("/echoPort").data());
808     ASSERT_CMP_CSTRING(mainResourceData, ==, serverPortAsString.get());
809
810 #if SOUP_CHECK_VERSION(2, 61, 90)
811     kServer->removeWebSocketHandler();
812 #endif
813 }
814
815 void beforeAll()
816 {
817     kServer = new WebKitTestServer();
818     kServer->run(serverCallback);
819
820     Test::add("WebKitWebContext", "default-context", testWebContextDefault);
821     Test::add("WebKitWebContext", "ephemeral", testWebContextEphemeral);
822 #if ENABLE(NETSCAPE_PLUGIN_API)
823     PluginsTest::add("WebKitWebContext", "get-plugins", testWebContextGetPlugins);
824 #endif
825     URISchemeTest::add("WebKitWebContext", "uri-scheme", testWebContextURIScheme);
826     // FIXME: implement spellchecker in WPE.
827 #if PLATFORM(GTK)
828     Test::add("WebKitWebContext", "spell-checker", testWebContextSpellChecker);
829 #endif
830     WebViewTest::add("WebKitWebContext", "languages", testWebContextLanguages);
831     SecurityPolicyTest::add("WebKitSecurityManager", "security-policy", testWebContextSecurityPolicy);
832     WebViewTest::add("WebKitSecurityManager", "file-xhr", testWebContextSecurityFileXHR);
833     ProxyTest::add("WebKitWebContext", "proxy", testWebContextProxySettings);
834 }
835
836 void afterAll()
837 {
838     delete kServer;
839 }