[GLIB] Remove delete on destroy GMainLoopSources
[WebKit-https.git] / Tools / TestWebKitAPI / gtk / WebKit2Gtk / WebViewTest.cpp
1 /*
2  * Copyright (C) 2011 Igalia S.L.
3  * Portions Copyright (c) 2011 Motorola Mobility, Inc.  All rights reserved.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 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 "WebViewTest.h"
23
24 #include <JavaScriptCore/JSRetainPtr.h>
25 #include <WebCore/GUniquePtrGtk.h>
26 #include <wtf/glib/GMainLoopSource.h>
27
28 WebViewTest::WebViewTest(WebKitUserContentManager* userContentManager)
29     : m_webView(WEBKIT_WEB_VIEW(g_object_ref_sink(g_object_new(WEBKIT_TYPE_WEB_VIEW, "web-context", m_webContext.get(), "user-content-manager", userContentManager, nullptr))))
30     , m_mainLoop(g_main_loop_new(nullptr, TRUE))
31     , m_parentWindow(nullptr)
32     , m_javascriptResult(nullptr)
33     , m_resourceDataSize(0)
34     , m_surface(nullptr)
35     , m_expectedWebProcessCrash(false)
36 {
37     assertObjectIsDeletedWhenTestFinishes(G_OBJECT(m_webView));
38     g_signal_connect(m_webView, "web-process-crashed", G_CALLBACK(WebViewTest::webProcessCrashed), this);
39 }
40
41 WebViewTest::~WebViewTest()
42 {
43     if (m_parentWindow)
44         gtk_widget_destroy(m_parentWindow);
45     if (m_javascriptResult)
46         webkit_javascript_result_unref(m_javascriptResult);
47     if (m_surface)
48         cairo_surface_destroy(m_surface);
49     g_object_unref(m_webView);
50     g_main_loop_unref(m_mainLoop);
51 }
52
53 gboolean WebViewTest::webProcessCrashed(WebKitWebView*, WebViewTest* test)
54 {
55     if (test->m_expectedWebProcessCrash) {
56         test->m_expectedWebProcessCrash = false;
57         return FALSE;
58     }
59     g_assert_not_reached();
60     return TRUE;
61 }
62
63 void WebViewTest::loadURI(const char* uri)
64 {
65     m_activeURI = uri;
66     webkit_web_view_load_uri(m_webView, uri);
67     g_assert(webkit_web_view_is_loading(m_webView));
68     g_assert_cmpstr(webkit_web_view_get_uri(m_webView), ==, m_activeURI.data());
69 }
70
71 void WebViewTest::loadHtml(const char* html, const char* baseURI)
72 {
73     if (!baseURI)
74         m_activeURI = "about:blank";
75     else
76         m_activeURI = baseURI;
77     webkit_web_view_load_html(m_webView, html, baseURI);
78     g_assert(webkit_web_view_is_loading(m_webView));
79     g_assert_cmpstr(webkit_web_view_get_uri(m_webView), ==, m_activeURI.data());
80 }
81
82 void WebViewTest::loadPlainText(const char* plainText)
83 {
84     m_activeURI = "about:blank";
85     webkit_web_view_load_plain_text(m_webView, plainText);
86 #if 0
87     // FIXME: Pending API request URL no set when loading plain text.
88     // See https://bugs.webkit.org/show_bug.cgi?id=136916.
89     g_assert(webkit_web_view_is_loading(m_webView));
90     g_assert_cmpstr(webkit_web_view_get_uri(m_webView), ==, m_activeURI.data());
91 #endif
92 }
93
94 void WebViewTest::loadBytes(GBytes* bytes, const char* mimeType, const char* encoding, const char* baseURI)
95 {
96     if (!baseURI)
97         m_activeURI = "about:blank";
98     else
99         m_activeURI = baseURI;
100     webkit_web_view_load_bytes(m_webView, bytes, mimeType, encoding, baseURI);
101 #if 0
102     // FIXME: Pending API request URL no set when loading data.
103     // See https://bugs.webkit.org/show_bug.cgi?id=136916.
104     g_assert(webkit_web_view_is_loading(m_webView));
105     g_assert_cmpstr(webkit_web_view_get_uri(m_webView), ==, m_activeURI.data());
106 #endif
107 }
108
109 void WebViewTest::loadRequest(WebKitURIRequest* request)
110 {
111     m_activeURI = webkit_uri_request_get_uri(request);
112     webkit_web_view_load_request(m_webView, request);
113     g_assert(webkit_web_view_is_loading(m_webView));
114     g_assert_cmpstr(webkit_web_view_get_uri(m_webView), ==, m_activeURI.data());
115 }
116
117 void WebViewTest::loadAlternateHTML(const char* html, const char* contentURI, const char* baseURI)
118 {
119     m_activeURI = contentURI;
120     webkit_web_view_load_alternate_html(m_webView, html, contentURI, baseURI);
121 #if 0
122     // FIXME: Pending API request URL no set when loading Alternate HTML.
123     // See https://bugs.webkit.org/show_bug.cgi?id=136916.
124     g_assert(webkit_web_view_is_loading(m_webView));
125 #endif
126     g_assert_cmpstr(webkit_web_view_get_uri(m_webView), ==, m_activeURI.data());
127 }
128
129 void WebViewTest::goBack()
130 {
131     bool canGoBack = webkit_web_view_can_go_back(m_webView);
132     if (canGoBack) {
133         WebKitBackForwardList* list = webkit_web_view_get_back_forward_list(m_webView);
134         WebKitBackForwardListItem* item = webkit_back_forward_list_get_nth_item(list, -1);
135         m_activeURI = webkit_back_forward_list_item_get_original_uri(item);
136     }
137
138     // Call go_back even when can_go_back returns FALSE to check nothing happens.
139     webkit_web_view_go_back(m_webView);
140     if (canGoBack) {
141         g_assert(webkit_web_view_is_loading(m_webView));
142         g_assert_cmpstr(webkit_web_view_get_uri(m_webView), ==, m_activeURI.data());
143     }
144 }
145
146 void WebViewTest::goForward()
147 {
148     bool canGoForward = webkit_web_view_can_go_forward(m_webView);
149     if (canGoForward) {
150         WebKitBackForwardList* list = webkit_web_view_get_back_forward_list(m_webView);
151         WebKitBackForwardListItem* item = webkit_back_forward_list_get_nth_item(list, 1);
152         m_activeURI = webkit_back_forward_list_item_get_original_uri(item);
153     }
154
155     // Call go_forward even when can_go_forward returns FALSE to check nothing happens.
156     webkit_web_view_go_forward(m_webView);
157     if (canGoForward) {
158         g_assert(webkit_web_view_is_loading(m_webView));
159         g_assert_cmpstr(webkit_web_view_get_uri(m_webView), ==, m_activeURI.data());
160     }
161 }
162
163 void WebViewTest::goToBackForwardListItem(WebKitBackForwardListItem* item)
164 {
165     m_activeURI = webkit_back_forward_list_item_get_original_uri(item);
166     webkit_web_view_go_to_back_forward_list_item(m_webView, item);
167     g_assert(webkit_web_view_is_loading(m_webView));
168     g_assert_cmpstr(webkit_web_view_get_uri(m_webView), ==, m_activeURI.data());
169 }
170
171 void WebViewTest::quitMainLoop()
172 {
173     g_main_loop_quit(m_mainLoop);
174 }
175
176 void WebViewTest::quitMainLoopAfterProcessingPendingEvents()
177 {
178     while (gtk_events_pending())
179         gtk_main_iteration();
180     quitMainLoop();
181 }
182
183 void WebViewTest::wait(double seconds)
184 {
185     g_timeout_add(seconds * 1000, [](gpointer userData) -> gboolean {
186         static_cast<WebViewTest*>(userData)->quitMainLoop();
187         return G_SOURCE_REMOVE;
188     }, this);
189     g_main_loop_run(m_mainLoop);
190 }
191
192 static void loadChanged(WebKitWebView* webView, WebKitLoadEvent loadEvent, WebViewTest* test)
193 {
194     if (loadEvent != WEBKIT_LOAD_FINISHED)
195         return;
196     g_signal_handlers_disconnect_by_func(webView, reinterpret_cast<void*>(loadChanged), test);
197     g_main_loop_quit(test->m_mainLoop);
198 }
199
200 void WebViewTest::waitUntilLoadFinished()
201 {
202     g_signal_connect(m_webView, "load-changed", G_CALLBACK(loadChanged), this);
203     g_main_loop_run(m_mainLoop);
204 }
205
206 static void titleChanged(WebKitWebView* webView, GParamSpec*, WebViewTest* test)
207 {
208     if (!test->m_expectedTitle.isNull() && test->m_expectedTitle != webkit_web_view_get_title(webView))
209         return;
210
211     g_signal_handlers_disconnect_by_func(webView, reinterpret_cast<void*>(titleChanged), test);
212     g_main_loop_quit(test->m_mainLoop);
213 }
214
215 void WebViewTest::waitUntilTitleChangedTo(const char* expectedTitle)
216 {
217     m_expectedTitle = expectedTitle;
218     g_signal_connect(m_webView, "notify::title", G_CALLBACK(titleChanged), this);
219     g_main_loop_run(m_mainLoop);
220     m_expectedTitle = CString();
221 }
222
223 void WebViewTest::waitUntilTitleChanged()
224 {
225     waitUntilTitleChangedTo(0);
226 }
227
228 static gboolean parentWindowMapped(GtkWidget* widget, GdkEvent*, WebViewTest* test)
229 {
230     g_signal_handlers_disconnect_by_func(widget, reinterpret_cast<void*>(parentWindowMapped), test);
231     g_main_loop_quit(test->m_mainLoop);
232
233     return FALSE;
234 }
235
236 void WebViewTest::showInWindow(GtkWindowType windowType)
237 {
238     g_assert(!m_parentWindow);
239     m_parentWindow = gtk_window_new(windowType);
240     gtk_container_add(GTK_CONTAINER(m_parentWindow), GTK_WIDGET(m_webView));
241     gtk_widget_show(GTK_WIDGET(m_webView));
242     gtk_widget_show(m_parentWindow);
243 }
244
245 void WebViewTest::showInWindowAndWaitUntilMapped(GtkWindowType windowType, int width, int height)
246 {
247     g_assert(!m_parentWindow);
248     m_parentWindow = gtk_window_new(windowType);
249     if (width && height)
250         gtk_window_resize(GTK_WINDOW(m_parentWindow), width, height);
251     gtk_container_add(GTK_CONTAINER(m_parentWindow), GTK_WIDGET(m_webView));
252     gtk_widget_show(GTK_WIDGET(m_webView));
253
254     g_signal_connect(m_parentWindow, "map-event", G_CALLBACK(parentWindowMapped), this);
255     gtk_widget_show(m_parentWindow);
256     g_main_loop_run(m_mainLoop);
257 }
258
259 void WebViewTest::resizeView(int width, int height)
260 {
261     GtkAllocation allocation;
262     gtk_widget_get_allocation(GTK_WIDGET(m_webView), &allocation);
263     if (width != -1)
264         allocation.width = width;
265     if (height != -1)
266         allocation.height = height;
267     gtk_widget_size_allocate(GTK_WIDGET(m_webView), &allocation);
268 }
269
270 void WebViewTest::selectAll()
271 {
272     webkit_web_view_execute_editing_command(m_webView, "SelectAll");
273 }
274
275 bool WebViewTest::isEditable()
276 {
277     return webkit_web_view_is_editable(m_webView);
278 }
279
280 void WebViewTest::setEditable(bool editable)
281 {
282     webkit_web_view_set_editable(m_webView, editable);
283 }
284
285 static void resourceGetDataCallback(GObject* object, GAsyncResult* result, gpointer userData)
286 {
287     size_t dataSize;
288     GUniqueOutPtr<GError> error;
289     unsigned char* data = webkit_web_resource_get_data_finish(WEBKIT_WEB_RESOURCE(object), result, &dataSize, &error.outPtr());
290     g_assert(data);
291
292     WebViewTest* test = static_cast<WebViewTest*>(userData);
293     test->m_resourceData.reset(reinterpret_cast<char*>(data));
294     test->m_resourceDataSize = dataSize;
295     g_main_loop_quit(test->m_mainLoop);
296 }
297
298 const char* WebViewTest::mainResourceData(size_t& mainResourceDataSize)
299 {
300     m_resourceDataSize = 0;
301     m_resourceData.reset();
302     WebKitWebResource* resource = webkit_web_view_get_main_resource(m_webView);
303     g_assert(resource);
304
305     webkit_web_resource_get_data(resource, 0, resourceGetDataCallback, this);
306     g_main_loop_run(m_mainLoop);
307
308     mainResourceDataSize = m_resourceDataSize;
309     return m_resourceData.get();
310 }
311
312 void WebViewTest::mouseMoveTo(int x, int y, unsigned mouseModifiers)
313 {
314     g_assert(m_parentWindow);
315     GtkWidget* viewWidget = GTK_WIDGET(m_webView);
316     g_assert(gtk_widget_get_realized(viewWidget));
317
318     GUniquePtr<GdkEvent> event(gdk_event_new(GDK_MOTION_NOTIFY));
319     event->motion.x = x;
320     event->motion.y = y;
321
322     event->motion.time = GDK_CURRENT_TIME;
323     event->motion.window = gtk_widget_get_window(viewWidget);
324     g_object_ref(event->motion.window);
325     event->motion.device = gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gtk_widget_get_display(viewWidget)));
326     event->motion.state = mouseModifiers;
327     event->motion.axes = 0;
328
329     int xRoot, yRoot;
330     gdk_window_get_root_coords(gtk_widget_get_window(viewWidget), x, y, &xRoot, &yRoot);
331     event->motion.x_root = xRoot;
332     event->motion.y_root = yRoot;
333     gtk_main_do_event(event.get());
334 }
335
336 void WebViewTest::clickMouseButton(int x, int y, unsigned button, unsigned mouseModifiers)
337 {
338     doMouseButtonEvent(GDK_BUTTON_PRESS, x, y, button, mouseModifiers);
339     doMouseButtonEvent(GDK_BUTTON_RELEASE, x, y, button, mouseModifiers);
340 }
341
342 void WebViewTest::keyStroke(unsigned keyVal, unsigned keyModifiers)
343 {
344     g_assert(m_parentWindow);
345     GtkWidget* viewWidget = GTK_WIDGET(m_webView);
346     g_assert(gtk_widget_get_realized(viewWidget));
347
348     GUniquePtr<GdkEvent> event(gdk_event_new(GDK_KEY_PRESS));
349     event->key.keyval = keyVal;
350
351     event->key.time = GDK_CURRENT_TIME;
352     event->key.window = gtk_widget_get_window(viewWidget);
353     g_object_ref(event->key.window);
354     gdk_event_set_device(event.get(), gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gtk_widget_get_display(viewWidget))));
355     event->key.state = keyModifiers;
356
357     // When synthesizing an event, an invalid hardware_keycode value can cause it to be badly processed by GTK+.
358     GUniqueOutPtr<GdkKeymapKey> keys;
359     int keysCount;
360     if (gdk_keymap_get_entries_for_keyval(gdk_keymap_get_default(), keyVal, &keys.outPtr(), &keysCount))
361         event->key.hardware_keycode = keys.get()[0].keycode;
362
363     gtk_main_do_event(event.get());
364     event->key.type = GDK_KEY_RELEASE;
365     gtk_main_do_event(event.get());
366 }
367
368 void WebViewTest::doMouseButtonEvent(GdkEventType eventType, int x, int y, unsigned button, unsigned mouseModifiers)
369 {
370     g_assert(m_parentWindow);
371     GtkWidget* viewWidget = GTK_WIDGET(m_webView);
372     g_assert(gtk_widget_get_realized(viewWidget));
373
374     GUniquePtr<GdkEvent> event(gdk_event_new(eventType));
375     event->button.window = gtk_widget_get_window(viewWidget);
376     g_object_ref(event->button.window);
377
378     event->button.time = GDK_CURRENT_TIME;
379     event->button.x = x;
380     event->button.y = y;
381     event->button.axes = 0;
382     event->button.state = mouseModifiers;
383     event->button.button = button;
384
385     event->button.device = gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gtk_widget_get_display(viewWidget)));
386
387     int xRoot, yRoot;
388     gdk_window_get_root_coords(gtk_widget_get_window(viewWidget), x, y, &xRoot, &yRoot);
389     event->button.x_root = xRoot;
390     event->button.y_root = yRoot;
391     gtk_main_do_event(event.get());
392 }
393
394 static void runJavaScriptReadyCallback(GObject*, GAsyncResult* result, WebViewTest* test)
395 {
396     test->m_javascriptResult = webkit_web_view_run_javascript_finish(test->m_webView, result, test->m_javascriptError);
397     g_main_loop_quit(test->m_mainLoop);
398 }
399
400 static void runJavaScriptFromGResourceReadyCallback(GObject*, GAsyncResult* result, WebViewTest* test)
401 {
402     test->m_javascriptResult = webkit_web_view_run_javascript_from_gresource_finish(test->m_webView, result, test->m_javascriptError);
403     g_main_loop_quit(test->m_mainLoop);
404 }
405
406 WebKitJavascriptResult* WebViewTest::runJavaScriptAndWaitUntilFinished(const char* javascript, GError** error)
407 {
408     if (m_javascriptResult)
409         webkit_javascript_result_unref(m_javascriptResult);
410     m_javascriptResult = 0;
411     m_javascriptError = error;
412     webkit_web_view_run_javascript(m_webView, javascript, 0, reinterpret_cast<GAsyncReadyCallback>(runJavaScriptReadyCallback), this);
413     g_main_loop_run(m_mainLoop);
414
415     return m_javascriptResult;
416 }
417
418 WebKitJavascriptResult* WebViewTest::runJavaScriptFromGResourceAndWaitUntilFinished(const char* resource, GError** error)
419 {
420     if (m_javascriptResult)
421         webkit_javascript_result_unref(m_javascriptResult);
422     m_javascriptResult = 0;
423     m_javascriptError = error;
424     webkit_web_view_run_javascript_from_gresource(m_webView, resource, 0, reinterpret_cast<GAsyncReadyCallback>(runJavaScriptFromGResourceReadyCallback), this);
425     g_main_loop_run(m_mainLoop);
426
427     return m_javascriptResult;
428 }
429
430 static char* jsValueToCString(JSGlobalContextRef context, JSValueRef value)
431 {
432     g_assert(value);
433     g_assert(JSValueIsString(context, value));
434
435     JSRetainPtr<JSStringRef> stringValue(Adopt, JSValueToStringCopy(context, value, 0));
436     g_assert(stringValue);
437
438     size_t cStringLength = JSStringGetMaximumUTF8CStringSize(stringValue.get());
439     char* cString = static_cast<char*>(g_malloc(cStringLength));
440     JSStringGetUTF8CString(stringValue.get(), cString, cStringLength);
441     return cString;
442 }
443
444 char* WebViewTest::javascriptResultToCString(WebKitJavascriptResult* javascriptResult)
445 {
446     JSGlobalContextRef context = webkit_javascript_result_get_global_context(javascriptResult);
447     g_assert(context);
448     return jsValueToCString(context, webkit_javascript_result_get_value(javascriptResult));
449 }
450
451 double WebViewTest::javascriptResultToNumber(WebKitJavascriptResult* javascriptResult)
452 {
453     JSGlobalContextRef context = webkit_javascript_result_get_global_context(javascriptResult);
454     g_assert(context);
455     JSValueRef value = webkit_javascript_result_get_value(javascriptResult);
456     g_assert(value);
457     g_assert(JSValueIsNumber(context, value));
458
459     return JSValueToNumber(context, value, 0);
460 }
461
462 bool WebViewTest::javascriptResultToBoolean(WebKitJavascriptResult* javascriptResult)
463 {
464     JSGlobalContextRef context = webkit_javascript_result_get_global_context(javascriptResult);
465     g_assert(context);
466     JSValueRef value = webkit_javascript_result_get_value(javascriptResult);
467     g_assert(value);
468     g_assert(JSValueIsBoolean(context, value));
469
470     return JSValueToBoolean(context, value);
471 }
472
473 bool WebViewTest::javascriptResultIsNull(WebKitJavascriptResult* javascriptResult)
474 {
475     JSGlobalContextRef context = webkit_javascript_result_get_global_context(javascriptResult);
476     g_assert(context);
477     JSValueRef value = webkit_javascript_result_get_value(javascriptResult);
478     g_assert(value);
479
480     return JSValueIsNull(context, value);
481 }
482
483 bool WebViewTest::javascriptResultIsUndefined(WebKitJavascriptResult* javascriptResult)
484 {
485     JSGlobalContextRef context = webkit_javascript_result_get_global_context(javascriptResult);
486     g_assert(context);
487     JSValueRef value = webkit_javascript_result_get_value(javascriptResult);
488     g_assert(value);
489
490     return JSValueIsUndefined(context, value);
491 }
492
493 static void onSnapshotReady(WebKitWebView* web_view, GAsyncResult* res, WebViewTest* test)
494 {
495     GUniqueOutPtr<GError> error;
496     test->m_surface = webkit_web_view_get_snapshot_finish(web_view, res, &error.outPtr());
497     g_assert(!test->m_surface || !error.get());
498     if (error)
499         g_assert_error(error.get(), WEBKIT_SNAPSHOT_ERROR, WEBKIT_SNAPSHOT_ERROR_FAILED_TO_CREATE);
500     test->quitMainLoop();
501 }
502
503 cairo_surface_t* WebViewTest::getSnapshotAndWaitUntilReady(WebKitSnapshotRegion region, WebKitSnapshotOptions options)
504 {
505     if (m_surface)
506         cairo_surface_destroy(m_surface);
507     m_surface = 0;
508     webkit_web_view_get_snapshot(m_webView, region, options, 0, reinterpret_cast<GAsyncReadyCallback>(onSnapshotReady), this);
509     g_main_loop_run(m_mainLoop);
510     return m_surface;
511 }
512
513 bool WebViewTest::runWebProcessTest(const char* suiteName, const char* testName)
514 {
515     GUniquePtr<char> script(g_strdup_printf("WebProcessTestRunner.runTest('%s/%s');", suiteName, testName));
516     GUniqueOutPtr<GError> error;
517     WebKitJavascriptResult* javascriptResult = runJavaScriptAndWaitUntilFinished(script.get(), &error.outPtr());
518     g_assert(!error);
519     return javascriptResultToBoolean(javascriptResult);
520 }