1/*
2 * Copyright (C) 2011 Igalia S.L.
3 * Copyright (C) 2014 Collabora Ltd.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2,1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public License
16 * along with this library; see the file COPYING.LIB. If not, write to
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21#include "config.h"
22#include "WebKitTestServer.h"
23#include "WebViewTest.h"
24#include <glib/gstdio.h>
25#include <wtf/glib/GRefPtr.h>
26
27class IsPlayingAudioWebViewTest : public WebViewTest {
28public:
29 MAKE_GLIB_TEST_FIXTURE(IsPlayingAudioWebViewTest);
30
31 static void isPlayingAudioChanged(GObject*, GParamSpec*, IsPlayingAudioWebViewTest* test)
32 {
33 g_signal_handlers_disconnect_by_func(test->m_webView, reinterpret_cast<void*>(isPlayingAudioChanged), test);
34 g_main_loop_quit(test->m_mainLoop);
35 }
36
37 void waitUntilIsPlayingAudioChanged()
38 {
39 g_signal_connect(m_webView, "notify::is-playing-audio", G_CALLBACK(isPlayingAudioChanged), this);
40 g_main_loop_run(m_mainLoop);
41 }
42};
43
44static WebKitTestServer* gServer;
45
46static void testWebViewWebContext(WebViewTest* test, gconstpointer)
47{
48 g_assert_true(webkit_web_view_get_context(test->m_webView) == test->m_webContext.get());
49 g_assert_true(webkit_web_context_get_default() != test->m_webContext.get());
50
51 // Check that a web view created with g_object_new has the default context.
52 auto webView = Test::adoptView(g_object_new(WEBKIT_TYPE_WEB_VIEW,
53#if PLATFORM(WPE)
54 "backend", Test::createWebViewBackend(),
55#endif
56 nullptr));
57 g_assert_true(webkit_web_view_get_context(webView.get()) == webkit_web_context_get_default());
58
59 // Check that a web view created with a related view has the related view context.
60 webView = Test::adoptView(Test::createWebView(test->m_webView));
61 g_assert_true(webkit_web_view_get_context(webView.get()) == test->m_webContext.get());
62
63 // Check that a web context given as construct parameter is ignored if a related view is also provided.
64 webView = Test::adoptView(g_object_new(WEBKIT_TYPE_WEB_VIEW,
65#if PLATFORM(WPE)
66 "backend", Test::createWebViewBackend(),
67#endif
68 "web-context", webkit_web_context_get_default(),
69 "related-view", test->m_webView,
70 nullptr));
71 g_assert_true(webkit_web_view_get_context(webView.get()) == test->m_webContext.get());
72}
73
74static void testWebViewWebContextLifetime(WebViewTest* test, gconstpointer)
75{
76 WebKitWebContext* webContext = webkit_web_context_new();
77 test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webContext));
78
79 auto* webView = Test::createWebView(webContext);
80 test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webView));
81
82#if PLATFORM(GTK)
83 g_object_ref_sink(webView);
84#endif
85 g_object_unref(webContext);
86
87 // Check that the web view still has a valid context.
88 WebKitWebContext* tmpContext = webkit_web_view_get_context(WEBKIT_WEB_VIEW(webView));
89 g_assert_true(WEBKIT_IS_WEB_CONTEXT(tmpContext));
90 g_object_unref(webView);
91
92 WebKitWebContext* webContext2 = webkit_web_context_new();
93 test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webContext2));
94
95 auto* webView2 = Test::createWebView(webContext2);
96 test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webView2));
97
98#if PLATFORM(GTK)
99 g_object_ref_sink(webView2);
100#endif
101 g_object_unref(webView2);
102
103 // Check that the context is still valid.
104 g_assert_true(WEBKIT_IS_WEB_CONTEXT(webContext2));
105 g_object_unref(webContext2);
106}
107
108static void testWebViewCloseQuickly(WebViewTest* test, gconstpointer)
109{
110 auto webView = Test::adoptView(Test::createWebView());
111 test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webView.get()));
112 g_idle_add([](gpointer userData) -> gboolean {
113 static_cast<WebViewTest*>(userData)->quitMainLoop();
114 return G_SOURCE_REMOVE;
115 }, test);
116 g_main_loop_run(test->m_mainLoop);
117 webView = nullptr;
118}
119
120#if PLATFORM(WPE)
121static void testWebViewWebBackend(Test* test, gconstpointer)
122{
123 static struct wpe_view_backend_interface s_testingInterface = {
124 // create
125 [](void*, struct wpe_view_backend*) -> void* { return nullptr; },
126 // destroy
127 [](void*) { },
128 // initialize
129 [](void*) { },
130 // get_renderer_host_fd
131 [](void*) -> int { return -1; },
132 // padding
133 nullptr,
134 nullptr,
135 nullptr,
136 nullptr
137 };
138
139 // User provided backend with default deleter (we don't have a way to check the backend will be actually freed).
140 GRefPtr<WebKitWebView> webView = adoptGRef(webkit_web_view_new(webkit_web_view_backend_new(wpe_view_backend_create_with_backend_interface(&s_testingInterface, nullptr), nullptr, nullptr)));
141 test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webView.get()));
142 auto* viewBackend = webkit_web_view_get_backend(webView.get());
143 g_assert_nonnull(viewBackend);
144 auto* wpeBackend = webkit_web_view_backend_get_wpe_backend(viewBackend);
145 g_assert_nonnull(wpeBackend);
146 webView = nullptr;
147
148 // User provided backend with destroy notify.
149 wpeBackend = wpe_view_backend_create_with_backend_interface(&s_testingInterface, nullptr);
150 webView = adoptGRef(webkit_web_view_new(webkit_web_view_backend_new(wpeBackend, [](gpointer userData) {
151 auto* backend = *static_cast<struct wpe_view_backend**>(userData);
152 wpe_view_backend_destroy(backend);
153 *static_cast<struct wpe_view_backend**>(userData) = nullptr;
154 }, &wpeBackend)));
155 test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webView.get()));
156 webView = nullptr;
157 g_assert_null(wpeBackend);
158
159 // User provided backend owned by another object with destroy notify.
160 static bool hasInstance = false;
161 struct BackendOwner {
162 BackendOwner(struct wpe_view_backend* backend)
163 : backend(backend)
164 {
165 hasInstance = true;
166 }
167
168 ~BackendOwner()
169 {
170 wpe_view_backend_destroy(backend);
171 hasInstance = false;
172 }
173
174 struct wpe_view_backend* backend;
175 };
176 auto* owner = new BackendOwner(wpe_view_backend_create_with_backend_interface(&s_testingInterface, nullptr));
177 g_assert_true(hasInstance);
178 webView = adoptGRef(webkit_web_view_new(webkit_web_view_backend_new(owner->backend, [](gpointer userData) {
179 delete static_cast<BackendOwner*>(userData);
180 }, owner)));
181 test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webView.get()));
182 g_assert_true(hasInstance);
183 webView = nullptr;
184 g_assert_false(hasInstance);
185}
186#endif // PLATFORM(WPE)
187
188static void ephemeralViewloadChanged(WebKitWebView* webView, WebKitLoadEvent loadEvent, WebViewTest* test)
189{
190 if (loadEvent != WEBKIT_LOAD_FINISHED)
191 return;
192 g_signal_handlers_disconnect_by_func(webView, reinterpret_cast<void*>(ephemeralViewloadChanged), test);
193 test->quitMainLoop();
194}
195
196static void testWebViewEphemeral(WebViewTest* test, gconstpointer)
197{
198 g_assert_false(webkit_web_view_is_ephemeral(test->m_webView));
199 g_assert_false(webkit_web_context_is_ephemeral(webkit_web_view_get_context(test->m_webView)));
200 auto* manager = webkit_web_context_get_website_data_manager(test->m_webContext.get());
201 g_assert_false(webkit_website_data_manager_is_ephemeral(manager));
202 g_assert_true(webkit_web_view_get_website_data_manager(test->m_webView) == manager);
203 webkit_website_data_manager_clear(manager, WEBKIT_WEBSITE_DATA_DISK_CACHE, 0, nullptr, [](GObject* manager, GAsyncResult* result, gpointer userData) {
204 webkit_website_data_manager_clear_finish(WEBKIT_WEBSITE_DATA_MANAGER(manager), result, nullptr);
205 static_cast<WebViewTest*>(userData)->quitMainLoop();
206 }, test);
207 g_main_loop_run(test->m_mainLoop);
208
209 // A WebView on a non ephemeral context can be ephemeral.
210 auto webView = Test::adoptView(g_object_new(WEBKIT_TYPE_WEB_VIEW,
211#if PLATFORM(WPE)
212 "backend", Test::createWebViewBackend(),
213#endif
214 "web-context", webkit_web_view_get_context(test->m_webView),
215 "is-ephemeral", TRUE,
216 nullptr));
217 g_assert_true(webkit_web_view_is_ephemeral(webView.get()));
218 g_assert_false(webkit_web_context_is_ephemeral(webkit_web_view_get_context(webView.get())));
219 g_assert_true(webkit_web_view_get_website_data_manager(webView.get()) != manager);
220
221 g_signal_connect(webView.get(), "load-changed", G_CALLBACK(ephemeralViewloadChanged), test);
222 webkit_web_view_load_uri(webView.get(), gServer->getURIForPath("/").data());
223 g_main_loop_run(test->m_mainLoop);
224
225 // Disk cache delays the storing of initial resources for 1 second to avoid
226 // affecting early page load. So, wait 1 second here to make sure resources
227 // have already been stored.
228 test->wait(1);
229
230 webkit_website_data_manager_fetch(manager, WEBKIT_WEBSITE_DATA_DISK_CACHE, nullptr, [](GObject* manager, GAsyncResult* result, gpointer userData) {
231 auto* test = static_cast<WebViewTest*>(userData);
232 g_assert_null(webkit_website_data_manager_fetch_finish(WEBKIT_WEBSITE_DATA_MANAGER(manager), result, nullptr));
233 test->quitMainLoop();
234 }, test);
235 g_main_loop_run(test->m_mainLoop);
236}
237
238static void testWebViewCustomCharset(WebViewTest* test, gconstpointer)
239{
240 test->loadURI(gServer->getURIForPath("/").data());
241 test->waitUntilLoadFinished();
242 g_assert_null(webkit_web_view_get_custom_charset(test->m_webView));
243 webkit_web_view_set_custom_charset(test->m_webView, "utf8");
244 // Changing the charset reloads the page, so wait until reloaded.
245 test->waitUntilLoadFinished();
246 g_assert_cmpstr(webkit_web_view_get_custom_charset(test->m_webView), ==, "utf8");
247
248 // Go back to the default charset and wait until reloaded.
249 webkit_web_view_set_custom_charset(test->m_webView, nullptr);
250 test->waitUntilLoadFinished();
251 g_assert_null(webkit_web_view_get_custom_charset(test->m_webView));
252}
253
254static void testWebViewSettings(WebViewTest* test, gconstpointer)
255{
256 WebKitSettings* defaultSettings = webkit_web_view_get_settings(test->m_webView);
257 test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(defaultSettings));
258 g_assert_nonnull(defaultSettings);
259 g_assert_true(webkit_settings_get_enable_javascript(defaultSettings));
260
261 GRefPtr<WebKitSettings> newSettings = adoptGRef(webkit_settings_new());
262 test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(newSettings.get()));
263 g_object_set(G_OBJECT(newSettings.get()), "enable-javascript", FALSE, NULL);
264 webkit_web_view_set_settings(test->m_webView, newSettings.get());
265
266 WebKitSettings* settings = webkit_web_view_get_settings(test->m_webView);
267 g_assert_true(settings != defaultSettings);
268 g_assert_false(webkit_settings_get_enable_javascript(settings));
269
270 auto webView2 = Test::adoptView(Test::createWebView());
271 test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webView2.get()));
272 webkit_web_view_set_settings(WEBKIT_WEB_VIEW(webView2.get()), settings);
273 g_assert_true(webkit_web_view_get_settings(WEBKIT_WEB_VIEW(webView2.get())) == settings);
274
275 GRefPtr<WebKitSettings> newSettings2 = adoptGRef(webkit_settings_new());
276 test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(newSettings2.get()));
277 webkit_web_view_set_settings(WEBKIT_WEB_VIEW(webView2.get()), newSettings2.get());
278 settings = webkit_web_view_get_settings(WEBKIT_WEB_VIEW(webView2.get()));
279 g_assert_true(settings == newSettings2.get());
280 g_assert_true(webkit_settings_get_enable_javascript(settings));
281
282 auto webView3 = Test::adoptView(Test::createWebView(newSettings2.get()));
283 test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webView3.get()));
284 g_assert_true(webkit_web_view_get_settings(WEBKIT_WEB_VIEW(webView3.get())) == newSettings2.get());
285}
286
287static void testWebViewZoomLevel(WebViewTest* test, gconstpointer)
288{
289 g_assert_cmpfloat(webkit_web_view_get_zoom_level(test->m_webView), ==, 1);
290 webkit_web_view_set_zoom_level(test->m_webView, 2.5);
291 g_assert_cmpfloat(webkit_web_view_get_zoom_level(test->m_webView), ==, 2.5);
292
293 webkit_settings_set_zoom_text_only(webkit_web_view_get_settings(test->m_webView), TRUE);
294 // The zoom level shouldn't change when zoom-text-only setting changes.
295 g_assert_cmpfloat(webkit_web_view_get_zoom_level(test->m_webView), ==, 2.5);
296}
297
298static void testWebViewRunJavaScript(WebViewTest* test, gconstpointer)
299{
300 static const char* html = "<html><body><a id='WebKitLink' href='http://www.webkitgtk.org/' title='WebKitGTK+ Title'>WebKitGTK+ Website</a></body></html>";
301 test->loadHtml(html, 0);
302 test->waitUntilLoadFinished();
303
304 GUniqueOutPtr<GError> error;
305 WebKitJavascriptResult* javascriptResult = test->runJavaScriptAndWaitUntilFinished("window.document.getElementById('WebKitLink').title;", &error.outPtr());
306 g_assert_nonnull(javascriptResult);
307 test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webkit_javascript_result_get_js_value(javascriptResult)));
308 g_assert_no_error(error.get());
309 GUniquePtr<char> valueString(WebViewTest::javascriptResultToCString(javascriptResult));
310 g_assert_cmpstr(valueString.get(), ==, "WebKitGTK+ Title");
311
312 javascriptResult = test->runJavaScriptAndWaitUntilFinished("window.document.getElementById('WebKitLink').href;", &error.outPtr());
313 g_assert_nonnull(javascriptResult);
314 test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webkit_javascript_result_get_js_value(javascriptResult)));
315 g_assert_no_error(error.get());
316 valueString.reset(WebViewTest::javascriptResultToCString(javascriptResult));
317 g_assert_cmpstr(valueString.get(), ==, "http://www.webkitgtk.org/");
318
319 javascriptResult = test->runJavaScriptAndWaitUntilFinished("window.document.getElementById('WebKitLink').textContent", &error.outPtr());
320 g_assert_nonnull(javascriptResult);
321 test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webkit_javascript_result_get_js_value(javascriptResult)));
322 g_assert_no_error(error.get());
323 valueString.reset(WebViewTest::javascriptResultToCString(javascriptResult));
324 g_assert_cmpstr(valueString.get(), ==, "WebKitGTK+ Website");
325
326 javascriptResult = test->runJavaScriptAndWaitUntilFinished("a = 25;", &error.outPtr());
327 g_assert_nonnull(javascriptResult);
328 test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webkit_javascript_result_get_js_value(javascriptResult)));
329 g_assert_no_error(error.get());
330 g_assert_cmpfloat(WebViewTest::javascriptResultToNumber(javascriptResult), ==, 25);
331
332 javascriptResult = test->runJavaScriptAndWaitUntilFinished("a = 2.5;", &error.outPtr());
333 g_assert_nonnull(javascriptResult);
334 test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webkit_javascript_result_get_js_value(javascriptResult)));
335 g_assert_no_error(error.get());
336 g_assert_cmpfloat(WebViewTest::javascriptResultToNumber(javascriptResult), ==, 2.5);
337
338 javascriptResult = test->runJavaScriptAndWaitUntilFinished("a = true", &error.outPtr());
339 g_assert_nonnull(javascriptResult);
340 test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webkit_javascript_result_get_js_value(javascriptResult)));
341 g_assert_no_error(error.get());
342 g_assert_true(WebViewTest::javascriptResultToBoolean(javascriptResult));
343
344 javascriptResult = test->runJavaScriptAndWaitUntilFinished("a = false", &error.outPtr());
345 g_assert_nonnull(javascriptResult);
346 test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webkit_javascript_result_get_js_value(javascriptResult)));
347 g_assert_no_error(error.get());
348 g_assert_false(WebViewTest::javascriptResultToBoolean(javascriptResult));
349
350 javascriptResult = test->runJavaScriptAndWaitUntilFinished("a = null", &error.outPtr());
351 g_assert_nonnull(javascriptResult);
352 test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webkit_javascript_result_get_js_value(javascriptResult)));
353 g_assert_no_error(error.get());
354 g_assert_true(WebViewTest::javascriptResultIsNull(javascriptResult));
355
356 javascriptResult = test->runJavaScriptAndWaitUntilFinished("function Foo() { a = 25; } Foo();", &error.outPtr());
357 g_assert_nonnull(javascriptResult);
358 test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webkit_javascript_result_get_js_value(javascriptResult)));
359 g_assert_no_error(error.get());
360 g_assert_true(WebViewTest::javascriptResultIsUndefined(javascriptResult));
361
362 javascriptResult = test->runJavaScriptFromGResourceAndWaitUntilFinished("/org/webkit/glib/tests/link-title.js", &error.outPtr());
363 g_assert_nonnull(javascriptResult);
364 test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webkit_javascript_result_get_js_value(javascriptResult)));
365 g_assert_no_error(error.get());
366 valueString.reset(WebViewTest::javascriptResultToCString(javascriptResult));
367 g_assert_cmpstr(valueString.get(), ==, "WebKitGTK+ Title");
368
369 javascriptResult = test->runJavaScriptFromGResourceAndWaitUntilFinished("/wrong/path/to/resource.js", &error.outPtr());
370 g_assert_null(javascriptResult);
371 g_assert_error(error.get(), G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND);
372
373 javascriptResult = test->runJavaScriptAndWaitUntilFinished("foo();", &error.outPtr());
374 g_assert_null(javascriptResult);
375 g_assert_error(error.get(), WEBKIT_JAVASCRIPT_ERROR, WEBKIT_JAVASCRIPT_ERROR_SCRIPT_FAILED);
376
377 // Values of the main world are not available in the isolated one.
378 javascriptResult = test->runJavaScriptInWorldAndWaitUntilFinished("a", "WebExtensionTestScriptWorld", &error.outPtr());
379 g_assert_null(javascriptResult);
380 g_assert_error(error.get(), WEBKIT_JAVASCRIPT_ERROR, WEBKIT_JAVASCRIPT_ERROR_SCRIPT_FAILED);
381
382 javascriptResult = test->runJavaScriptInWorldAndWaitUntilFinished("a = 50", "WebExtensionTestScriptWorld", &error.outPtr());
383 g_assert_nonnull(javascriptResult);
384 test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webkit_javascript_result_get_js_value(javascriptResult)));
385 g_assert_no_error(error.get());
386 g_assert_cmpfloat(WebViewTest::javascriptResultToNumber(javascriptResult), ==, 50);
387
388 // Values of the isolated world are not available in the normal one.
389 javascriptResult = test->runJavaScriptAndWaitUntilFinished("a", &error.outPtr());
390 g_assert_nonnull(javascriptResult);
391 test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webkit_javascript_result_get_js_value(javascriptResult)));
392 g_assert_no_error(error.get());
393 g_assert_cmpfloat(WebViewTest::javascriptResultToNumber(javascriptResult), ==, 25);
394
395 // Running a script in a world that doesn't exist should fail.
396 javascriptResult = test->runJavaScriptInWorldAndWaitUntilFinished("a", "InvalidScriptWorld", &error.outPtr());
397 g_assert_null(javascriptResult);
398 g_assert_error(error.get(), WEBKIT_JAVASCRIPT_ERROR, WEBKIT_JAVASCRIPT_ERROR_SCRIPT_FAILED);
399}
400
401class FullScreenClientTest: public WebViewTest {
402public:
403 MAKE_GLIB_TEST_FIXTURE(FullScreenClientTest);
404
405 enum FullScreenEvent {
406 None,
407 Enter,
408 Leave
409 };
410
411 static gboolean viewEnterFullScreenCallback(WebKitWebView*, FullScreenClientTest* test)
412 {
413 test->m_event = Enter;
414 g_main_loop_quit(test->m_mainLoop);
415 return FALSE;
416 }
417
418 static gboolean viewLeaveFullScreenCallback(WebKitWebView*, FullScreenClientTest* test)
419 {
420 test->m_event = Leave;
421 g_main_loop_quit(test->m_mainLoop);
422 return FALSE;
423 }
424
425 FullScreenClientTest()
426 : m_event(None)
427 {
428 webkit_settings_set_enable_fullscreen(webkit_web_view_get_settings(m_webView), TRUE);
429 g_signal_connect(m_webView, "enter-fullscreen", G_CALLBACK(viewEnterFullScreenCallback), this);
430 g_signal_connect(m_webView, "leave-fullscreen", G_CALLBACK(viewLeaveFullScreenCallback), this);
431 }
432
433 ~FullScreenClientTest()
434 {
435 g_signal_handlers_disconnect_matched(m_webView, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this);
436 }
437
438 void requestFullScreenAndWaitUntilEnteredFullScreen()
439 {
440 m_event = None;
441 webkit_web_view_run_javascript(m_webView, "document.documentElement.webkitRequestFullScreen();", 0, 0, 0);
442 g_main_loop_run(m_mainLoop);
443 }
444
445 static gboolean leaveFullScreenIdle(FullScreenClientTest* test)
446 {
447 // FIXME: Implement key strokes in WPE
448#if PLATFORM(GTK)
449 test->keyStroke(GDK_KEY_Escape);
450#endif
451 return FALSE;
452 }
453
454 void leaveFullScreenAndWaitUntilLeftFullScreen()
455 {
456 m_event = None;
457 g_idle_add(reinterpret_cast<GSourceFunc>(leaveFullScreenIdle), this);
458 g_main_loop_run(m_mainLoop);
459 }
460
461 FullScreenEvent m_event;
462};
463
464#if ENABLE(FULLSCREEN_API)
465static void testWebViewFullScreen(FullScreenClientTest* test, gconstpointer)
466{
467#if PLATFORM(GTK)
468 test->showInWindowAndWaitUntilMapped();
469#endif
470 test->loadHtml("<html><body>FullScreen test</body></html>", 0);
471 test->waitUntilLoadFinished();
472 test->requestFullScreenAndWaitUntilEnteredFullScreen();
473 g_assert_cmpint(test->m_event, ==, FullScreenClientTest::Enter);
474 test->leaveFullScreenAndWaitUntilLeftFullScreen();
475 g_assert_cmpint(test->m_event, ==, FullScreenClientTest::Leave);
476}
477#endif
478
479static void testWebViewCanShowMIMEType(WebViewTest* test, gconstpointer)
480{
481 // Supported MIME types.
482 g_assert_true(webkit_web_view_can_show_mime_type(test->m_webView, "text/html"));
483 g_assert_true(webkit_web_view_can_show_mime_type(test->m_webView, "text/plain"));
484 g_assert_true(webkit_web_view_can_show_mime_type(test->m_webView, "image/jpeg"));
485
486 // Unsupported MIME types.
487 g_assert_false(webkit_web_view_can_show_mime_type(test->m_webView, "text/vcard"));
488 g_assert_false(webkit_web_view_can_show_mime_type(test->m_webView, "application/zip"));
489 g_assert_false(webkit_web_view_can_show_mime_type(test->m_webView, "application/octet-stream"));
490
491#if ENABLE(NETSCAPE_PLUGIN_API)
492 // Plugins are only supported when enabled.
493 webkit_web_context_set_additional_plugins_directory(webkit_web_view_get_context(test->m_webView), WEBKIT_TEST_PLUGIN_DIR);
494 g_assert_true(webkit_web_view_can_show_mime_type(test->m_webView, "application/x-webkit-test-netscape"));
495 webkit_settings_set_enable_plugins(webkit_web_view_get_settings(test->m_webView), FALSE);
496 g_assert_false(webkit_web_view_can_show_mime_type(test->m_webView, "application/x-webkit-test-netscape"));
497#endif
498}
499
500#if PLATFORM(GTK)
501class FormClientTest: public WebViewTest {
502public:
503 MAKE_GLIB_TEST_FIXTURE(FormClientTest);
504
505 static void submitFormCallback(WebKitWebView*, WebKitFormSubmissionRequest* request, FormClientTest* test)
506 {
507 test->submitForm(request);
508 }
509
510 FormClientTest()
511 : m_submitPositionX(0)
512 , m_submitPositionY(0)
513 {
514 g_signal_connect(m_webView, "submit-form", G_CALLBACK(submitFormCallback), this);
515 }
516
517 ~FormClientTest()
518 {
519 g_signal_handlers_disconnect_matched(m_webView, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this);
520 }
521
522 void submitForm(WebKitFormSubmissionRequest* request)
523 {
524 assertObjectIsDeletedWhenTestFinishes(G_OBJECT(request));
525 m_request = request;
526 webkit_form_submission_request_submit(request);
527 quitMainLoop();
528 }
529
530 GHashTable* getTextFieldsAsHashTable()
531 {
532#pragma GCC diagnostic push
533#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
534 return webkit_form_submission_request_get_text_fields(m_request.get());
535#pragma GCC diagnostic pop
536 }
537
538 GPtrArray* getTextFieldNames()
539 {
540 GPtrArray* names;
541 webkit_form_submission_request_list_text_fields(m_request.get(), &names, nullptr);
542 return names;
543 }
544
545 GPtrArray* getTextFieldValues()
546 {
547 GPtrArray* values;
548 webkit_form_submission_request_list_text_fields(m_request.get(), nullptr, &values);
549 return values;
550 }
551
552 static gboolean doClickIdleCallback(FormClientTest* test)
553 {
554 test->clickMouseButton(test->m_submitPositionX, test->m_submitPositionY, 1);
555 return FALSE;
556 }
557
558 void submitFormAtPosition(int x, int y)
559 {
560 m_submitPositionX = x;
561 m_submitPositionY = y;
562 g_idle_add(reinterpret_cast<GSourceFunc>(doClickIdleCallback), this);
563 g_main_loop_run(m_mainLoop);
564 }
565
566 int m_submitPositionX;
567 int m_submitPositionY;
568 GRefPtr<WebKitFormSubmissionRequest> m_request;
569};
570
571static void testWebViewSubmitForm(FormClientTest* test, gconstpointer)
572{
573 test->showInWindowAndWaitUntilMapped();
574
575 const char* formHTML =
576 "<html><body>"
577 " <form action='#'>"
578 " <input type='text' name='text1' value='value1'>"
579 " <input type='text' name='text2' value='value2'>"
580 " <input type='text' value='value3'>"
581 " <input type='text' name='text2'>"
582 " <input type='password' name='password' value='secret'>"
583 " <textarea cols='5' rows='5' name='textarea'>Text</textarea>"
584 " <input type='hidden' name='hidden1' value='hidden1'>"
585 " <input type='submit' value='Submit' style='position:absolute; left:1; top:1' size='10'>"
586 " </form>"
587 "</body></html>";
588
589 test->loadHtml(formHTML, "file:///");
590 test->waitUntilLoadFinished();
591
592 test->submitFormAtPosition(5, 5);
593 GHashTable* tableValues = test->getTextFieldsAsHashTable();
594 g_assert_nonnull(tableValues);
595 g_assert_cmpuint(g_hash_table_size(tableValues), ==, 4);
596 g_assert_cmpstr(static_cast<char*>(g_hash_table_lookup(tableValues, "text1")), ==, "value1");
597 g_assert_cmpstr(static_cast<char*>(g_hash_table_lookup(tableValues, "")), ==, "value3");
598 g_assert_cmpstr(static_cast<char*>(g_hash_table_lookup(tableValues, "text2")), ==, "");
599 g_assert_cmpstr(static_cast<char*>(g_hash_table_lookup(tableValues, "password")), ==, "secret");
600
601 GPtrArray* names = test->getTextFieldNames();
602 g_assert_nonnull(names);
603 g_assert_cmpuint(names->len, ==, 5);
604 g_assert_cmpstr(static_cast<char*>(names->pdata[0]), ==, "text1");
605 g_assert_cmpstr(static_cast<char*>(names->pdata[1]), ==, "text2");
606 g_assert_cmpstr(static_cast<char*>(names->pdata[2]), ==, "");
607 g_assert_cmpstr(static_cast<char*>(names->pdata[3]), ==, "text2");
608 g_assert_cmpstr(static_cast<char*>(names->pdata[4]), ==, "password");
609
610 GPtrArray* values = test->getTextFieldValues();
611 g_assert_nonnull(values);
612 g_assert_cmpuint(values->len, ==, 5);
613 g_assert_cmpstr(static_cast<char*>(values->pdata[0]), ==, "value1");
614 g_assert_cmpstr(static_cast<char*>(values->pdata[1]), ==, "value2");
615 g_assert_cmpstr(static_cast<char*>(values->pdata[2]), ==, "value3");
616 g_assert_cmpstr(static_cast<char*>(values->pdata[3]), ==, "");
617 g_assert_cmpstr(static_cast<char*>(values->pdata[4]), ==, "secret");
618}
619#endif // PLATFORM(GTK)
620
621class SaveWebViewTest: public WebViewTest {
622public:
623 MAKE_GLIB_TEST_FIXTURE(SaveWebViewTest);
624
625 SaveWebViewTest()
626 : m_tempDirectory(g_dir_make_tmp("WebKit2SaveViewTest-XXXXXX", 0))
627 {
628 }
629
630 ~SaveWebViewTest()
631 {
632 if (G_IS_FILE(m_file.get()))
633 g_file_delete(m_file.get(), 0, 0);
634
635 if (G_IS_INPUT_STREAM(m_inputStream.get()))
636 g_input_stream_close(m_inputStream.get(), 0, 0);
637
638 if (m_tempDirectory)
639 g_rmdir(m_tempDirectory.get());
640 }
641
642 static void webViewSavedToStreamCallback(GObject* object, GAsyncResult* result, SaveWebViewTest* test)
643 {
644 GUniqueOutPtr<GError> error;
645 test->m_inputStream = adoptGRef(webkit_web_view_save_finish(test->m_webView, result, &error.outPtr()));
646 g_assert_true(G_IS_INPUT_STREAM(test->m_inputStream.get()));
647 g_assert_no_error(error.get());
648
649 test->quitMainLoop();
650 }
651
652 static void webViewSavedToFileCallback(GObject* object, GAsyncResult* result, SaveWebViewTest* test)
653 {
654 GUniqueOutPtr<GError> error;
655 g_assert_true(webkit_web_view_save_to_file_finish(test->m_webView, result, &error.outPtr()));
656 g_assert_no_error(error.get());
657
658 test->quitMainLoop();
659 }
660
661 void saveAndWaitForStream()
662 {
663 webkit_web_view_save(m_webView, WEBKIT_SAVE_MODE_MHTML, 0, reinterpret_cast<GAsyncReadyCallback>(webViewSavedToStreamCallback), this);
664 g_main_loop_run(m_mainLoop);
665 }
666
667 void saveAndWaitForFile()
668 {
669 m_saveDestinationFilePath.reset(g_build_filename(m_tempDirectory.get(), "testWebViewSaveResult.mht", NULL));
670 m_file = adoptGRef(g_file_new_for_path(m_saveDestinationFilePath.get()));
671 webkit_web_view_save_to_file(m_webView, m_file.get(), WEBKIT_SAVE_MODE_MHTML, 0, reinterpret_cast<GAsyncReadyCallback>(webViewSavedToFileCallback), this);
672 g_main_loop_run(m_mainLoop);
673 }
674
675 GUniquePtr<char> m_tempDirectory;
676 GUniquePtr<char> m_saveDestinationFilePath;
677 GRefPtr<GInputStream> m_inputStream;
678 GRefPtr<GFile> m_file;
679};
680
681static void testWebViewSave(SaveWebViewTest* test, gconstpointer)
682{
683 test->loadHtml("<html>"
684 "<body>"
685 " <p>A paragraph with plain text</p>"
686 " <p>"
687 " A red box: <img src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3AYWDTMVwnSZnwAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAAAFklEQVQI12P8z8DAwMDAxMDAwMDAAAANHQEDK+mmyAAAAABJRU5ErkJggg=='></br>"
688 " A blue box: <img src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3AYWDTMvBHhALQAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAAAFklEQVQI12Nk4PnPwMDAxMDAwMDAAAALrwEPPIs1pgAAAABJRU5ErkJggg=='>"
689 " </p>"
690 "</body>"
691 "</html>", 0);
692 test->waitUntilLoadFinished();
693
694 // Write to a file and to an input stream.
695 test->saveAndWaitForFile();
696 test->saveAndWaitForStream();
697
698 // We should have exactly the same amount of bytes in the file
699 // than those coming from the GInputStream. We don't compare the
700 // strings read since the 'Date' field and the boundaries will be
701 // different on each case. MHTML functionality will be tested by
702 // Layout tests, so checking the amount of bytes is enough.
703 GUniqueOutPtr<GError> error;
704 gchar buffer[512] = { 0 };
705 gssize readBytes = 0;
706 gssize totalBytesFromStream = 0;
707 while ((readBytes = g_input_stream_read(test->m_inputStream.get(), &buffer, 512, 0, &error.outPtr()))) {
708 g_assert_no_error(error.get());
709 totalBytesFromStream += readBytes;
710 }
711
712 // Check that the file exists and that it contains the same amount of bytes.
713 GRefPtr<GFileInfo> fileInfo = adoptGRef(g_file_query_info(test->m_file.get(), G_FILE_ATTRIBUTE_STANDARD_SIZE, static_cast<GFileQueryInfoFlags>(0), 0, 0));
714 g_assert_cmpint(g_file_info_get_size(fileInfo.get()), ==, totalBytesFromStream);
715}
716
717// To test page visibility API. Currently only 'visible', 'hidden' and 'prerender' states are implemented fully in WebCore.
718// See also http://www.w3.org/TR/2011/WD-page-visibility-20110602/ and https://developers.google.com/chrome/whitepapers/pagevisibility
719static void testWebViewPageVisibility(WebViewTest* test, gconstpointer)
720{
721 test->loadHtml("<html><title></title>"
722 "<body><p>Test Web Page Visibility</p>"
723 "<script>"
724 "document.addEventListener(\"visibilitychange\", onVisibilityChange, false);"
725 "function onVisibilityChange() {"
726 " document.title = document.visibilityState;"
727 "}"
728 "</script>"
729 "</body></html>",
730 0);
731
732 // Wait until the page is loaded. Initial visibility should be 'prerender'.
733 test->waitUntilLoadFinished();
734
735 GUniqueOutPtr<GError> error;
736 WebKitJavascriptResult* javascriptResult = test->runJavaScriptAndWaitUntilFinished("document.visibilityState;", &error.outPtr());
737 g_assert_nonnull(javascriptResult);
738 g_assert_no_error(error.get());
739 GUniquePtr<char> valueString(WebViewTest::javascriptResultToCString(javascriptResult));
740 g_assert_cmpstr(valueString.get(), ==, "prerender");
741
742 javascriptResult = test->runJavaScriptAndWaitUntilFinished("document.hidden;", &error.outPtr());
743 g_assert_nonnull(javascriptResult);
744 g_assert_no_error(error.get());
745 g_assert_true(WebViewTest::javascriptResultToBoolean(javascriptResult));
746
747 // Show the page. The visibility should be updated to 'visible'.
748 test->showInWindow();
749 test->waitUntilTitleChanged();
750
751 javascriptResult = test->runJavaScriptAndWaitUntilFinished("document.visibilityState;", &error.outPtr());
752 g_assert_nonnull(javascriptResult);
753 g_assert_no_error(error.get());
754 valueString.reset(WebViewTest::javascriptResultToCString(javascriptResult));
755 g_assert_cmpstr(valueString.get(), ==, "visible");
756
757 javascriptResult = test->runJavaScriptAndWaitUntilFinished("document.hidden;", &error.outPtr());
758 g_assert_nonnull(javascriptResult);
759 g_assert_no_error(error.get());
760 g_assert_false(WebViewTest::javascriptResultToBoolean(javascriptResult));
761
762 // Hide the page. The visibility should be updated to 'hidden'.
763 test->hideView();
764 test->waitUntilTitleChanged();
765
766 javascriptResult = test->runJavaScriptAndWaitUntilFinished("document.visibilityState;", &error.outPtr());
767 g_assert_nonnull(javascriptResult);
768 g_assert_no_error(error.get());
769 valueString.reset(WebViewTest::javascriptResultToCString(javascriptResult));
770 g_assert_cmpstr(valueString.get(), ==, "hidden");
771
772 javascriptResult = test->runJavaScriptAndWaitUntilFinished("document.hidden;", &error.outPtr());
773 g_assert_nonnull(javascriptResult);
774 g_assert_no_error(error.get());
775 g_assert_true(WebViewTest::javascriptResultToBoolean(javascriptResult));
776}
777
778#if PLATFORM(GTK)
779class SnapshotWebViewTest: public WebViewTest {
780public:
781 MAKE_GLIB_TEST_FIXTURE(SnapshotWebViewTest);
782
783 static void onSnapshotCancelledReady(WebKitWebView* web_view, GAsyncResult* res, SnapshotWebViewTest* test)
784 {
785 GUniqueOutPtr<GError> error;
786 test->m_surface = webkit_web_view_get_snapshot_finish(web_view, res, &error.outPtr());
787 g_assert_null(test->m_surface);
788 g_assert_error(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED);
789 test->quitMainLoop();
790 }
791
792 gboolean getSnapshotAndCancel()
793 {
794 if (m_surface)
795 cairo_surface_destroy(m_surface);
796 m_surface = 0;
797 GRefPtr<GCancellable> cancellable = adoptGRef(g_cancellable_new());
798 webkit_web_view_get_snapshot(m_webView, WEBKIT_SNAPSHOT_REGION_VISIBLE, WEBKIT_SNAPSHOT_OPTIONS_NONE, cancellable.get(), reinterpret_cast<GAsyncReadyCallback>(onSnapshotCancelledReady), this);
799 g_cancellable_cancel(cancellable.get());
800 g_main_loop_run(m_mainLoop);
801
802 return true;
803 }
804
805};
806
807static void testWebViewSnapshot(SnapshotWebViewTest* test, gconstpointer)
808{
809 test->loadHtml("<html><head><style>html { width: 200px; height: 100px; } ::-webkit-scrollbar { display: none; }</style></head><body><p>Whatever</p></body></html>", nullptr);
810 test->waitUntilLoadFinished();
811
812 // WEBKIT_SNAPSHOT_REGION_VISIBLE returns a null surface when the view is not visible.
813 cairo_surface_t* surface1 = test->getSnapshotAndWaitUntilReady(WEBKIT_SNAPSHOT_REGION_VISIBLE, WEBKIT_SNAPSHOT_OPTIONS_NONE);
814 g_assert_null(surface1);
815
816 // WEBKIT_SNAPSHOT_REGION_FULL_DOCUMENT works even if the window is not visible.
817 surface1 = test->getSnapshotAndWaitUntilReady(WEBKIT_SNAPSHOT_REGION_FULL_DOCUMENT, WEBKIT_SNAPSHOT_OPTIONS_NONE);
818 g_assert_nonnull(surface1);
819 g_assert_cmpuint(cairo_surface_get_type(surface1), ==, CAIRO_SURFACE_TYPE_IMAGE);
820 g_assert_cmpint(cairo_image_surface_get_width(surface1), ==, 200);
821 g_assert_cmpint(cairo_image_surface_get_height(surface1), ==, 100);
822
823 // Show the WebView in a popup widow of 50x50 and try again with WEBKIT_SNAPSHOT_REGION_VISIBLE.
824 test->showInWindowAndWaitUntilMapped(GTK_WINDOW_POPUP, 50, 50);
825 surface1 = cairo_surface_reference(test->getSnapshotAndWaitUntilReady(WEBKIT_SNAPSHOT_REGION_VISIBLE, WEBKIT_SNAPSHOT_OPTIONS_NONE));
826 g_assert_nonnull(surface1);
827 g_assert_cmpuint(cairo_surface_get_type(surface1), ==, CAIRO_SURFACE_TYPE_IMAGE);
828 g_assert_cmpint(cairo_image_surface_get_width(surface1), ==, 50);
829 g_assert_cmpint(cairo_image_surface_get_height(surface1), ==, 50);
830
831 // Select all text in the WebView, request a snapshot ignoring selection.
832 test->selectAll();
833 cairo_surface_t* surface2 = test->getSnapshotAndWaitUntilReady(WEBKIT_SNAPSHOT_REGION_VISIBLE, WEBKIT_SNAPSHOT_OPTIONS_NONE);
834 g_assert_nonnull(surface2);
835 g_assert_true(Test::cairoSurfacesEqual(surface1, surface2));
836
837 // Request a new snapshot, including the selection this time. The size should be the same but the result
838 // must be different to the one previously obtained.
839 surface2 = test->getSnapshotAndWaitUntilReady(WEBKIT_SNAPSHOT_REGION_VISIBLE, WEBKIT_SNAPSHOT_OPTIONS_INCLUDE_SELECTION_HIGHLIGHTING);
840 g_assert_cmpuint(cairo_surface_get_type(surface2), ==, CAIRO_SURFACE_TYPE_IMAGE);
841 g_assert_cmpint(cairo_image_surface_get_width(surface1), ==, cairo_image_surface_get_width(surface2));
842 g_assert_cmpint(cairo_image_surface_get_height(surface1), ==, cairo_image_surface_get_height(surface2));
843 g_assert_false(Test::cairoSurfacesEqual(surface1, surface2));
844
845 // Get a snpashot with a transparent background, the result must be different.
846 surface2 = test->getSnapshotAndWaitUntilReady(WEBKIT_SNAPSHOT_REGION_VISIBLE, WEBKIT_SNAPSHOT_OPTIONS_TRANSPARENT_BACKGROUND);
847 g_assert_cmpuint(cairo_surface_get_type(surface2), ==, CAIRO_SURFACE_TYPE_IMAGE);
848 g_assert_cmpint(cairo_image_surface_get_width(surface1), ==, cairo_image_surface_get_width(surface2));
849 g_assert_cmpint(cairo_image_surface_get_height(surface1), ==, cairo_image_surface_get_height(surface2));
850 g_assert_false(Test::cairoSurfacesEqual(surface1, surface2));
851 cairo_surface_destroy(surface1);
852
853 // Test that cancellation works.
854 g_assert_true(test->getSnapshotAndCancel());
855}
856#endif // PLATFORM(GTK)
857
858#if ENABLE(NOTIFICATIONS)
859class NotificationWebViewTest: public WebViewTest {
860public:
861 MAKE_GLIB_TEST_FIXTURE_WITH_SETUP_TEARDOWN(NotificationWebViewTest, setup, teardown);
862
863 static void setup()
864 {
865 WebViewTest::shouldInitializeWebViewInConstructor = false;
866 }
867
868 static void teardown()
869 {
870 WebViewTest::shouldInitializeWebViewInConstructor = true;
871 }
872
873 enum NotificationEvent {
874 None,
875 Permission,
876 Shown,
877 Clicked,
878 OnClicked,
879 Closed,
880 OnClosed,
881 };
882
883 static gboolean permissionRequestCallback(WebKitWebView*, WebKitPermissionRequest *request, NotificationWebViewTest* test)
884 {
885 g_assert_true(WEBKIT_IS_NOTIFICATION_PERMISSION_REQUEST(request));
886 g_assert_true(test->m_isExpectingPermissionRequest);
887 test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(request));
888
889 test->m_event = Permission;
890
891 webkit_permission_request_allow(request);
892
893 g_main_loop_quit(test->m_mainLoop);
894
895 return TRUE;
896 }
897
898 static gboolean notificationClosedCallback(WebKitNotification* notification, NotificationWebViewTest* test)
899 {
900 g_assert_true(test->m_notification == notification);
901 test->m_notification = nullptr;
902 test->m_event = Closed;
903 if (g_main_loop_is_running(test->m_mainLoop))
904 g_main_loop_quit(test->m_mainLoop);
905 return TRUE;
906 }
907
908 static gboolean notificationClickedCallback(WebKitNotification* notification, NotificationWebViewTest* test)
909 {
910 g_assert_true(test->m_notification == notification);
911 test->m_event = Clicked;
912 return TRUE;
913 }
914
915 static gboolean showNotificationCallback(WebKitWebView*, WebKitNotification* notification, NotificationWebViewTest* test)
916 {
917 g_assert_null(test->m_notification);
918 test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(notification));
919 test->m_notification = notification;
920 g_signal_connect(notification, "closed", G_CALLBACK(notificationClosedCallback), test);
921 g_signal_connect(notification, "clicked", G_CALLBACK(notificationClickedCallback), test);
922 test->m_event = Shown;
923 g_main_loop_quit(test->m_mainLoop);
924 return TRUE;
925 }
926
927 static void notificationsMessageReceivedCallback(WebKitUserContentManager* userContentManager, WebKitJavascriptResult* javascriptResult, NotificationWebViewTest* test)
928 {
929 GUniquePtr<char> valueString(WebViewTest::javascriptResultToCString(javascriptResult));
930
931 if (g_str_equal(valueString.get(), "clicked"))
932 test->m_event = OnClicked;
933 else if (g_str_equal(valueString.get(), "closed"))
934 test->m_event = OnClosed;
935
936 g_main_loop_quit(test->m_mainLoop);
937 }
938
939 void initialize()
940 {
941 initializeWebView();
942 g_signal_connect(m_webView, "permission-request", G_CALLBACK(permissionRequestCallback), this);
943 g_signal_connect(m_webView, "show-notification", G_CALLBACK(showNotificationCallback), this);
944 webkit_user_content_manager_register_script_message_handler(m_userContentManager.get(), "notifications");
945 g_signal_connect(m_userContentManager.get(), "script-message-received::notifications", G_CALLBACK(notificationsMessageReceivedCallback), this);
946 }
947
948 ~NotificationWebViewTest()
949 {
950 g_signal_handlers_disconnect_matched(m_webView, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this);
951 g_signal_handlers_disconnect_matched(m_userContentManager.get(), G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this);
952 webkit_user_content_manager_unregister_script_message_handler(m_userContentManager.get(), "notifications");
953 }
954
955 bool hasPermission()
956 {
957 auto* result = runJavaScriptAndWaitUntilFinished("Notification.permission;", nullptr);
958 g_assert_nonnull(result);
959 GUniquePtr<char> value(javascriptResultToCString(result));
960 return !g_strcmp0(value.get(), "granted");
961 }
962
963 void requestPermissionAndWaitUntilGiven()
964 {
965 m_event = None;
966 m_isExpectingPermissionRequest = true;
967 webkit_web_view_run_javascript(m_webView, "Notification.requestPermission();", nullptr, nullptr, nullptr);
968 g_main_loop_run(m_mainLoop);
969 }
970
971 void requestNotificationAndWaitUntilShown(const char* title, const char* body)
972 {
973 m_event = None;
974
975 GUniquePtr<char> jscode(g_strdup_printf("n = new Notification('%s', { body: '%s'});", title, body));
976 webkit_web_view_run_javascript(m_webView, jscode.get(), nullptr, nullptr, nullptr);
977
978 g_main_loop_run(m_mainLoop);
979 }
980
981 void requestNotificationAndWaitUntilShown(const char* title, const char* body, const char* tag)
982 {
983 m_event = None;
984
985 GUniquePtr<char> jscode(g_strdup_printf("n = new Notification('%s', { body: '%s', tag: '%s'});", title, body, tag));
986 webkit_web_view_run_javascript(m_webView, jscode.get(), nullptr, nullptr, nullptr);
987
988 g_main_loop_run(m_mainLoop);
989 }
990
991 void clickNotificationAndWaitUntilClicked()
992 {
993 m_event = None;
994 runJavaScriptAndWaitUntilFinished("n.onclick = function() { window.webkit.messageHandlers.notifications.postMessage('clicked'); }", nullptr);
995 webkit_notification_clicked(m_notification);
996 g_assert_cmpint(m_event, ==, Clicked);
997 g_main_loop_run(m_mainLoop);
998 }
999
1000 void closeNotificationAndWaitUntilClosed()
1001 {
1002 m_event = None;
1003 webkit_web_view_run_javascript(m_webView, "n.close()", nullptr, nullptr, nullptr);
1004 g_main_loop_run(m_mainLoop);
1005 }
1006
1007 void closeNotificationAndWaitUntilOnClosed()
1008 {
1009 g_assert_nonnull(m_notification);
1010 m_event = None;
1011 runJavaScriptAndWaitUntilFinished("n.onclose = function() { window.webkit.messageHandlers.notifications.postMessage('closed'); }", nullptr);
1012 webkit_notification_close(m_notification);
1013 g_assert_cmpint(m_event, ==, Closed);
1014 g_main_loop_run(m_mainLoop);
1015 }
1016
1017 NotificationEvent m_event { None };
1018 WebKitNotification* m_notification { nullptr };
1019 bool m_isExpectingPermissionRequest { false };
1020 bool m_hasPermission { false };
1021};
1022
1023static void testWebViewNotification(NotificationWebViewTest* test, gconstpointer)
1024{
1025 test->initialize();
1026
1027 // Notifications don't work with local or special schemes.
1028 test->loadURI(gServer->getURIForPath("/").data());
1029 test->waitUntilLoadFinished();
1030 g_assert_false(test->hasPermission());
1031
1032 test->requestPermissionAndWaitUntilGiven();
1033 g_assert_cmpint(test->m_event, ==, NotificationWebViewTest::Permission);
1034 g_assert_true(test->hasPermission());
1035
1036 static const char* title = "This is a notification";
1037 static const char* body = "This is the body.";
1038 static const char* tag = "This is the tag.";
1039 test->requestNotificationAndWaitUntilShown(title, body, tag);
1040
1041 g_assert_cmpint(test->m_event, ==, NotificationWebViewTest::Shown);
1042 g_assert_nonnull(test->m_notification);
1043 g_assert_cmpstr(webkit_notification_get_title(test->m_notification), ==, title);
1044 g_assert_cmpstr(webkit_notification_get_body(test->m_notification), ==, body);
1045 g_assert_cmpstr(webkit_notification_get_tag(test->m_notification), ==, tag);
1046
1047 test->clickNotificationAndWaitUntilClicked();
1048 g_assert_cmpint(test->m_event, ==, NotificationWebViewTest::OnClicked);
1049
1050 test->closeNotificationAndWaitUntilClosed();
1051 g_assert_cmpint(test->m_event, ==, NotificationWebViewTest::Closed);
1052
1053 test->requestNotificationAndWaitUntilShown(title, body);
1054 g_assert_cmpint(test->m_event, ==, NotificationWebViewTest::Shown);
1055 g_assert_cmpstr(webkit_notification_get_tag(test->m_notification), ==, nullptr);
1056
1057 test->closeNotificationAndWaitUntilOnClosed();
1058 g_assert_cmpint(test->m_event, ==, NotificationWebViewTest::OnClosed);
1059
1060 // The first notification should be closed automatically because the tag is
1061 // the same. It will crash in showNotificationCallback on failure.
1062 test->requestNotificationAndWaitUntilShown(title, body, tag);
1063 test->requestNotificationAndWaitUntilShown(title, body, tag);
1064 g_assert_cmpint(test->m_event, ==, NotificationWebViewTest::Shown);
1065}
1066
1067static void setInitialNotificationPermissionsAllowedCallback(WebKitWebContext* context, NotificationWebViewTest* test)
1068{
1069 GUniquePtr<char> baseURI(soup_uri_to_string(gServer->baseURI(), FALSE));
1070 GList* allowedOrigins = g_list_prepend(nullptr, webkit_security_origin_new_for_uri(baseURI.get()));
1071 webkit_web_context_initialize_notification_permissions(test->m_webContext.get(), allowedOrigins, nullptr);
1072 g_list_free_full(allowedOrigins, reinterpret_cast<GDestroyNotify>(webkit_security_origin_unref));
1073}
1074
1075static void setInitialNotificationPermissionsDisallowedCallback(WebKitWebContext* context, NotificationWebViewTest* test)
1076{
1077 GUniquePtr<char> baseURI(soup_uri_to_string(gServer->baseURI(), FALSE));
1078 GList* disallowedOrigins = g_list_prepend(nullptr, webkit_security_origin_new_for_uri(baseURI.get()));
1079 webkit_web_context_initialize_notification_permissions(test->m_webContext.get(), nullptr, disallowedOrigins);
1080 g_list_free_full(disallowedOrigins, reinterpret_cast<GDestroyNotify>(webkit_security_origin_unref));
1081}
1082
1083static void testWebViewNotificationInitialPermissionAllowed(NotificationWebViewTest* test, gconstpointer)
1084{
1085 g_signal_connect(test->m_webContext.get(), "initialize-notification-permissions", G_CALLBACK(setInitialNotificationPermissionsAllowedCallback), test);
1086 test->initialize();
1087
1088 test->loadURI(gServer->getURIForPath("/").data());
1089 test->waitUntilLoadFinished();
1090 g_assert_true(test->hasPermission());
1091
1092 test->requestNotificationAndWaitUntilShown("This is a notification", "This is the body.");
1093 g_assert_cmpint(test->m_event, ==, NotificationWebViewTest::Shown);
1094}
1095
1096static void testWebViewNotificationInitialPermissionDisallowed(NotificationWebViewTest* test, gconstpointer)
1097{
1098 g_signal_connect(test->m_webContext.get(), "initialize-notification-permissions", G_CALLBACK(setInitialNotificationPermissionsDisallowedCallback), test);
1099 test->initialize();
1100
1101 test->loadURI(gServer->getURIForPath("/").data());
1102 test->waitUntilLoadFinished();
1103 g_assert_false(test->hasPermission());
1104}
1105#endif // ENABLE(NOTIFICATIONS)
1106
1107static void testWebViewIsPlayingAudio(IsPlayingAudioWebViewTest* test, gconstpointer)
1108{
1109#if PLATFORM(GTK)
1110 // The web view must be realized for the video to start playback and
1111 // trigger changes in WebKitWebView::is-playing-audio.
1112 test->showInWindowAndWaitUntilMapped(GTK_WINDOW_TOPLEVEL);
1113#endif
1114
1115 // Initially, web views should always report no audio being played.
1116 g_assert_false(webkit_web_view_is_playing_audio(test->m_webView));
1117
1118 GUniquePtr<char> resourcePath(g_build_filename(Test::getResourcesDir(Test::WebKit2Resources).data(), "file-with-video.html", nullptr));
1119 GUniquePtr<char> resourceURL(g_filename_to_uri(resourcePath.get(), nullptr, nullptr));
1120 webkit_web_view_load_uri(test->m_webView, resourceURL.get());
1121 test->waitUntilLoadFinished();
1122 g_assert_false(webkit_web_view_is_playing_audio(test->m_webView));
1123
1124 test->runJavaScriptAndWaitUntilFinished("playVideo();", nullptr);
1125 if (!webkit_web_view_is_playing_audio(test->m_webView))
1126 test->waitUntilIsPlayingAudioChanged();
1127 g_assert_true(webkit_web_view_is_playing_audio(test->m_webView));
1128
1129 // Pause the video, and check again.
1130 test->runJavaScriptAndWaitUntilFinished("document.getElementById('test-video').pause();", nullptr);
1131 if (webkit_web_view_is_playing_audio(test->m_webView))
1132 test->waitUntilIsPlayingAudioChanged();
1133 g_assert_false(webkit_web_view_is_playing_audio(test->m_webView));
1134}
1135
1136static void testWebViewBackgroundColor(WebViewTest* test, gconstpointer)
1137{
1138#if PLATFORM(GTK)
1139#define ColorType GdkRGBA
1140#elif PLATFORM(WPE)
1141#define ColorType WebKitColor
1142#endif
1143
1144 // White is the default background.
1145 ColorType rgba;
1146 webkit_web_view_get_background_color(test->m_webView, &rgba);
1147 g_assert_cmpfloat(rgba.red, ==, 1);
1148 g_assert_cmpfloat(rgba.green, ==, 1);
1149 g_assert_cmpfloat(rgba.blue, ==, 1);
1150 g_assert_cmpfloat(rgba.alpha, ==, 1);
1151
1152 // Set a different (semi-transparent red).
1153 rgba.red = 1;
1154 rgba.green = 0;
1155 rgba.blue = 0;
1156 rgba.alpha = 0.5;
1157 webkit_web_view_set_background_color(test->m_webView, &rgba);
1158 g_assert_cmpfloat(rgba.red, ==, 1);
1159 g_assert_cmpfloat(rgba.green, ==, 0);
1160 g_assert_cmpfloat(rgba.blue, ==, 0);
1161 g_assert_cmpfloat(rgba.alpha, ==, 0.5);
1162
1163#if PLATFORM(WPE)
1164 ColorType color;
1165 g_assert(webkit_color_parse(&color, "red"));
1166 g_assert_cmpfloat(color.red, ==, 1);
1167 webkit_web_view_set_background_color(test->m_webView, &color);
1168 webkit_web_view_get_background_color(test->m_webView, &rgba);
1169 g_assert_cmpfloat(rgba.red, ==, 1);
1170 g_assert_cmpfloat(rgba.green, ==, 0);
1171 g_assert_cmpfloat(rgba.blue, ==, 0);
1172 g_assert_cmpfloat(rgba.alpha, ==, 1);
1173#endif
1174
1175 // The actual rendering can't be tested using unit tests, use
1176 // MiniBrowser --bg-color="<color-value>" for manually testing this API.
1177}
1178
1179#if PLATFORM(GTK)
1180static void testWebViewPreferredSize(WebViewTest* test, gconstpointer)
1181{
1182 test->loadHtml("<html style='width: 325px; height: 615px'></html>", nullptr);
1183 test->waitUntilLoadFinished();
1184 test->showInWindowAndWaitUntilMapped();
1185 GtkRequisition minimunSize, naturalSize;
1186 gtk_widget_get_preferred_size(GTK_WIDGET(test->m_webView), &minimunSize, &naturalSize);
1187 g_assert_cmpint(minimunSize.width, ==, 0);
1188 g_assert_cmpint(minimunSize.height, ==, 0);
1189 g_assert_cmpint(naturalSize.width, ==, 325);
1190 g_assert_cmpint(naturalSize.height, ==, 615);
1191}
1192#endif
1193
1194class WebViewTitleTest: public WebViewTest {
1195public:
1196 MAKE_GLIB_TEST_FIXTURE(WebViewTitleTest);
1197
1198 static void titleChangedCallback(WebKitWebView* view, GParamSpec*, WebViewTitleTest* test)
1199 {
1200 test->m_webViewTitles.append(webkit_web_view_get_title(view));
1201 }
1202
1203 WebViewTitleTest()
1204 {
1205 g_signal_connect(m_webView, "notify::title", G_CALLBACK(titleChangedCallback), this);
1206 }
1207
1208 Vector<CString> m_webViewTitles;
1209};
1210
1211static void testWebViewTitleChange(WebViewTitleTest* test, gconstpointer)
1212{
1213 g_assert_cmpint(test->m_webViewTitles.size(), ==, 0);
1214
1215 test->loadHtml("<head><title>Page Title</title></head>", nullptr);
1216 test->waitUntilLoadFinished();
1217 g_assert_cmpint(test->m_webViewTitles.size(), ==, 1);
1218 g_assert_cmpstr(test->m_webViewTitles[0].data(), ==, "Page Title");
1219
1220 test->loadHtml("<head><title>Another Page Title</title></head>", nullptr);
1221 test->waitUntilLoadFinished();
1222 g_assert_cmpint(test->m_webViewTitles.size(), ==, 3);
1223 /* Page title should be immediately unset when loading a new page. */
1224 g_assert_cmpstr(test->m_webViewTitles[1].data(), ==, "");
1225 g_assert_cmpstr(test->m_webViewTitles[2].data(), ==, "Another Page Title");
1226
1227 test->loadHtml("<p>This page has no title!</p>", nullptr);
1228 test->waitUntilLoadFinished();
1229 g_assert_cmpint(test->m_webViewTitles.size(), ==, 4);
1230 g_assert_cmpstr(test->m_webViewTitles[3].data(), ==, "");
1231
1232 test->loadHtml("<script>document.title = 'one'; document.title = 'two'; document.title = 'three';</script>", nullptr);
1233 test->waitUntilLoadFinished();
1234 g_assert_cmpint(test->m_webViewTitles.size(), ==, 7);
1235 g_assert_cmpstr(test->m_webViewTitles[4].data(), ==, "one");
1236 g_assert_cmpstr(test->m_webViewTitles[5].data(), ==, "two");
1237 g_assert_cmpstr(test->m_webViewTitles[6].data(), ==, "three");
1238}
1239
1240#if PLATFORM(WPE)
1241class FrameDisplayedTest: public WebViewTest {
1242public:
1243 MAKE_GLIB_TEST_FIXTURE(FrameDisplayedTest);
1244
1245 static void titleChangedCallback(WebKitWebView* view, GParamSpec*, WebViewTitleTest* test)
1246 {
1247 test->m_webViewTitles.append(webkit_web_view_get_title(view));
1248 }
1249
1250 FrameDisplayedTest()
1251 : m_id(webkit_web_view_add_frame_displayed_callback(m_webView, [](WebKitWebView*, gpointer userData) {
1252 auto* test = static_cast<FrameDisplayedTest*>(userData);
1253 if (!test->m_maxFrames)
1254 return;
1255
1256 if (++test->m_frameCounter == test->m_maxFrames)
1257 RunLoop::main().dispatch([test] { test->quitMainLoop(); });
1258 }, this, nullptr))
1259 {
1260 g_assert_cmpuint(m_id, >, 0);
1261 }
1262
1263 ~FrameDisplayedTest()
1264 {
1265 webkit_web_view_remove_frame_displayed_callback(m_webView, m_id);
1266 }
1267
1268 void waitUntilFramesDisplayed(unsigned framesCount = 1)
1269 {
1270 m_maxFrames = framesCount;
1271 m_frameCounter = 0;
1272 g_main_loop_run(m_mainLoop);
1273 }
1274
1275 unsigned m_id { 0 };
1276 unsigned m_frameCounter { 0 };
1277 unsigned m_maxFrames { 0 };
1278};
1279
1280static void testWebViewFrameDisplayed(FrameDisplayedTest* test, gconstpointer)
1281{
1282 test->showInWindow();
1283
1284 test->loadHtml("<html></html>", nullptr);
1285 test->waitUntilFramesDisplayed();
1286
1287 test->loadHtml("<html><head><style>@keyframes fadeIn { from { opacity: 0; } }</style></head><p style='animation: fadeIn 1s infinite alternate;'>Foo</p></html>", nullptr);
1288 test->waitUntilFramesDisplayed(10);
1289
1290 bool secondCallbackCalled = false;
1291 auto id = webkit_web_view_add_frame_displayed_callback(test->m_webView, [](WebKitWebView*, gpointer userData) {
1292 auto* secondCallbackCalled = static_cast<bool*>(userData);
1293 *secondCallbackCalled = true;
1294 }, &secondCallbackCalled, nullptr);
1295 test->waitUntilFramesDisplayed();
1296 g_assert_true(secondCallbackCalled);
1297
1298 secondCallbackCalled = false;
1299 webkit_web_view_remove_frame_displayed_callback(test->m_webView, id);
1300 test->waitUntilFramesDisplayed();
1301 g_assert_false(secondCallbackCalled);
1302
1303 id = webkit_web_view_add_frame_displayed_callback(test->m_webView, [](WebKitWebView* webView, gpointer userData) {
1304 auto* id = static_cast<unsigned*>(userData);
1305 webkit_web_view_remove_frame_displayed_callback(webView, *id);
1306 }, &id, [](gpointer userData) {
1307 auto* id = static_cast<unsigned*>(userData);
1308 *id = 0;
1309 });
1310 test->waitUntilFramesDisplayed();
1311 g_assert_cmpuint(id, ==, 0);
1312
1313 auto id2 = webkit_web_view_add_frame_displayed_callback(test->m_webView, [](WebKitWebView* webView, gpointer userData) {
1314 auto* id = static_cast<unsigned*>(userData);
1315 if (*id) {
1316 webkit_web_view_remove_frame_displayed_callback(webView, *id);
1317 *id = 0;
1318 }
1319 }, &id, nullptr);
1320
1321 secondCallbackCalled = false;
1322 id = webkit_web_view_add_frame_displayed_callback(test->m_webView, [](WebKitWebView* webView, gpointer userData) {
1323 auto* secondCallbackCalled = static_cast<bool*>(userData);
1324 *secondCallbackCalled = true;
1325 }, &secondCallbackCalled, nullptr);
1326 test->waitUntilFramesDisplayed();
1327 g_assert_cmpuint(id, ==, 0);
1328 g_assert_false(secondCallbackCalled);
1329
1330 webkit_web_view_remove_frame_displayed_callback(test->m_webView, id2);
1331}
1332#endif
1333
1334static void serverCallback(SoupServer* server, SoupMessage* message, const char* path, GHashTable*, SoupClientContext*, gpointer)
1335{
1336 if (message->method != SOUP_METHOD_GET) {
1337 soup_message_set_status(message, SOUP_STATUS_NOT_IMPLEMENTED);
1338 return;
1339 }
1340
1341 if (g_str_equal(path, "/")) {
1342 soup_message_set_status(message, SOUP_STATUS_OK);
1343 soup_message_body_complete(message->response_body);
1344 } else
1345 soup_message_set_status(message, SOUP_STATUS_NOT_FOUND);
1346}
1347
1348void beforeAll()
1349{
1350 gServer = new WebKitTestServer();
1351 gServer->run(serverCallback);
1352
1353 WebViewTest::add("WebKitWebView", "web-context", testWebViewWebContext);
1354 WebViewTest::add("WebKitWebView", "web-context-lifetime", testWebViewWebContextLifetime);
1355 WebViewTest::add("WebKitWebView", "close-quickly", testWebViewCloseQuickly);
1356#if PLATFORM(WPE)
1357 Test::add("WebKitWebView", "backend", testWebViewWebBackend);
1358#endif
1359 WebViewTest::add("WebKitWebView", "ephemeral", testWebViewEphemeral);
1360 WebViewTest::add("WebKitWebView", "custom-charset", testWebViewCustomCharset);
1361 WebViewTest::add("WebKitWebView", "settings", testWebViewSettings);
1362 WebViewTest::add("WebKitWebView", "zoom-level", testWebViewZoomLevel);
1363 WebViewTest::add("WebKitWebView", "run-javascript", testWebViewRunJavaScript);
1364#if ENABLE(FULLSCREEN_API)
1365 FullScreenClientTest::add("WebKitWebView", "fullscreen", testWebViewFullScreen);
1366#endif
1367 WebViewTest::add("WebKitWebView", "can-show-mime-type", testWebViewCanShowMIMEType);
1368 // FIXME: implement mouse clicks in WPE.
1369#if PLATFORM(GTK)
1370 FormClientTest::add("WebKitWebView", "submit-form", testWebViewSubmitForm);
1371#endif
1372 SaveWebViewTest::add("WebKitWebView", "save", testWebViewSave);
1373 // FIXME: View is initially visible in WPE and has a fixed hardcoded size.
1374#if PLATFORM(GTK)
1375 SnapshotWebViewTest::add("WebKitWebView", "snapshot", testWebViewSnapshot);
1376#endif
1377 WebViewTest::add("WebKitWebView", "page-visibility", testWebViewPageVisibility);
1378#if ENABLE(NOTIFICATIONS)
1379 NotificationWebViewTest::add("WebKitWebView", "notification", testWebViewNotification);
1380 NotificationWebViewTest::add("WebKitWebView", "notification-initial-permission-allowed", testWebViewNotificationInitialPermissionAllowed);
1381 NotificationWebViewTest::add("WebKitWebView", "notification-initial-permission-disallowed", testWebViewNotificationInitialPermissionDisallowed);
1382#endif
1383 IsPlayingAudioWebViewTest::add("WebKitWebView", "is-playing-audio", testWebViewIsPlayingAudio);
1384 WebViewTest::add("WebKitWebView", "background-color", testWebViewBackgroundColor);
1385#if PLATFORM(GTK)
1386 WebViewTest::add("WebKitWebView", "preferred-size", testWebViewPreferredSize);
1387#endif
1388 WebViewTitleTest::add("WebKitWebView", "title-change", testWebViewTitleChange);
1389#if PLATFORM(WPE)
1390 FrameDisplayedTest::add("WebKitWebView", "frame-displayed", testWebViewFrameDisplayed);
1391#endif
1392}
1393
1394void afterAll()
1395{
1396}
1397