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