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