[GTK] Add initial WebKitWebsiteDataManager API for process configuration options
[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     GMainLoopSource::scheduleAfterDelayAndDeleteOnDestroy("WebViewTest wait", [this] { quitMainLoop(); },
186         std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::duration<double>(seconds)));
187     g_main_loop_run(m_mainLoop);
188 }
189
190 static void loadChanged(WebKitWebView* webView, WebKitLoadEvent loadEvent, WebViewTest* test)
191 {
192     if (loadEvent != WEBKIT_LOAD_FINISHED)
193         return;
194     g_signal_handlers_disconnect_by_func(webView, reinterpret_cast<void*>(loadChanged), test);
195     g_main_loop_quit(test->m_mainLoop);
196 }
197
198 void WebViewTest::waitUntilLoadFinished()
199 {
200     g_signal_connect(m_webView, "load-changed", G_CALLBACK(loadChanged), this);
201     g_main_loop_run(m_mainLoop);
202 }
203
204 static void titleChanged(WebKitWebView* webView, GParamSpec*, WebViewTest* test)
205 {
206     if (!test->m_expectedTitle.isNull() && test->m_expectedTitle != webkit_web_view_get_title(webView))
207         return;
208
209     g_signal_handlers_disconnect_by_func(webView, reinterpret_cast<void*>(titleChanged), test);
210     g_main_loop_quit(test->m_mainLoop);
211 }
212
213 void WebViewTest::waitUntilTitleChangedTo(const char* expectedTitle)
214 {
215     m_expectedTitle = expectedTitle;
216     g_signal_connect(m_webView, "notify::title", G_CALLBACK(titleChanged), this);
217     g_main_loop_run(m_mainLoop);
218     m_expectedTitle = CString();
219 }
220
221 void WebViewTest::waitUntilTitleChanged()
222 {
223     waitUntilTitleChangedTo(0);
224 }
225
226 static gboolean parentWindowMapped(GtkWidget* widget, GdkEvent*, WebViewTest* test)
227 {
228     g_signal_handlers_disconnect_by_func(widget, reinterpret_cast<void*>(parentWindowMapped), test);
229     g_main_loop_quit(test->m_mainLoop);
230
231     return FALSE;
232 }
233
234 void WebViewTest::showInWindow(GtkWindowType windowType)
235 {
236     g_assert(!m_parentWindow);
237     m_parentWindow = gtk_window_new(windowType);
238     gtk_container_add(GTK_CONTAINER(m_parentWindow), GTK_WIDGET(m_webView));
239     gtk_widget_show(GTK_WIDGET(m_webView));
240     gtk_widget_show(m_parentWindow);
241 }
242
243 void WebViewTest::showInWindowAndWaitUntilMapped(GtkWindowType windowType, int width, int height)
244 {
245     g_assert(!m_parentWindow);
246     m_parentWindow = gtk_window_new(windowType);
247     if (width && height)
248         gtk_window_resize(GTK_WINDOW(m_parentWindow), width, height);
249     gtk_container_add(GTK_CONTAINER(m_parentWindow), GTK_WIDGET(m_webView));
250     gtk_widget_show(GTK_WIDGET(m_webView));
251
252     g_signal_connect(m_parentWindow, "map-event", G_CALLBACK(parentWindowMapped), this);
253     gtk_widget_show(m_parentWindow);
254     g_main_loop_run(m_mainLoop);
255 }
256
257 void WebViewTest::resizeView(int width, int height)
258 {
259     GtkAllocation allocation;
260     gtk_widget_get_allocation(GTK_WIDGET(m_webView), &allocation);
261     if (width != -1)
262         allocation.width = width;
263     if (height != -1)
264         allocation.height = height;
265     gtk_widget_size_allocate(GTK_WIDGET(m_webView), &allocation);
266 }
267
268 void WebViewTest::selectAll()
269 {
270     webkit_web_view_execute_editing_command(m_webView, "SelectAll");
271 }
272
273 bool WebViewTest::isEditable()
274 {
275     return webkit_web_view_is_editable(m_webView);
276 }
277
278 void WebViewTest::setEditable(bool editable)
279 {
280     webkit_web_view_set_editable(m_webView, editable);
281 }
282
283 static void resourceGetDataCallback(GObject* object, GAsyncResult* result, gpointer userData)
284 {
285     size_t dataSize;
286     GUniqueOutPtr<GError> error;
287     unsigned char* data = webkit_web_resource_get_data_finish(WEBKIT_WEB_RESOURCE(object), result, &dataSize, &error.outPtr());
288     g_assert(data);
289
290     WebViewTest* test = static_cast<WebViewTest*>(userData);
291     test->m_resourceData.reset(reinterpret_cast<char*>(data));
292     test->m_resourceDataSize = dataSize;
293     g_main_loop_quit(test->m_mainLoop);
294 }
295
296 const char* WebViewTest::mainResourceData(size_t& mainResourceDataSize)
297 {
298     m_resourceDataSize = 0;
299     m_resourceData.reset();
300     WebKitWebResource* resource = webkit_web_view_get_main_resource(m_webView);
301     g_assert(resource);
302
303     webkit_web_resource_get_data(resource, 0, resourceGetDataCallback, this);
304     g_main_loop_run(m_mainLoop);
305
306     mainResourceDataSize = m_resourceDataSize;
307     return m_resourceData.get();
308 }
309
310 void WebViewTest::mouseMoveTo(int x, int y, unsigned mouseModifiers)
311 {
312     g_assert(m_parentWindow);
313     GtkWidget* viewWidget = GTK_WIDGET(m_webView);
314     g_assert(gtk_widget_get_realized(viewWidget));
315
316     GUniquePtr<GdkEvent> event(gdk_event_new(GDK_MOTION_NOTIFY));
317     event->motion.x = x;
318     event->motion.y = y;
319
320     event->motion.time = GDK_CURRENT_TIME;
321     event->motion.window = gtk_widget_get_window(viewWidget);
322     g_object_ref(event->motion.window);
323     event->motion.device = gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gtk_widget_get_display(viewWidget)));
324     event->motion.state = mouseModifiers;
325     event->motion.axes = 0;
326
327     int xRoot, yRoot;
328     gdk_window_get_root_coords(gtk_widget_get_window(viewWidget), x, y, &xRoot, &yRoot);
329     event->motion.x_root = xRoot;
330     event->motion.y_root = yRoot;
331     gtk_main_do_event(event.get());
332 }
333
334 void WebViewTest::clickMouseButton(int x, int y, unsigned button, unsigned mouseModifiers)
335 {
336     doMouseButtonEvent(GDK_BUTTON_PRESS, x, y, button, mouseModifiers);
337     doMouseButtonEvent(GDK_BUTTON_RELEASE, x, y, button, mouseModifiers);
338 }
339
340 void WebViewTest::keyStroke(unsigned keyVal, unsigned keyModifiers)
341 {
342     g_assert(m_parentWindow);
343     GtkWidget* viewWidget = GTK_WIDGET(m_webView);
344     g_assert(gtk_widget_get_realized(viewWidget));
345
346     GUniquePtr<GdkEvent> event(gdk_event_new(GDK_KEY_PRESS));
347     event->key.keyval = keyVal;
348
349     event->key.time = GDK_CURRENT_TIME;
350     event->key.window = gtk_widget_get_window(viewWidget);
351     g_object_ref(event->key.window);
352     gdk_event_set_device(event.get(), gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gtk_widget_get_display(viewWidget))));
353     event->key.state = keyModifiers;
354
355     // When synthesizing an event, an invalid hardware_keycode value can cause it to be badly processed by GTK+.
356     GUniqueOutPtr<GdkKeymapKey> keys;
357     int keysCount;
358     if (gdk_keymap_get_entries_for_keyval(gdk_keymap_get_default(), keyVal, &keys.outPtr(), &keysCount))
359         event->key.hardware_keycode = keys.get()[0].keycode;
360
361     gtk_main_do_event(event.get());
362     event->key.type = GDK_KEY_RELEASE;
363     gtk_main_do_event(event.get());
364 }
365
366 void WebViewTest::doMouseButtonEvent(GdkEventType eventType, int x, int y, unsigned button, unsigned mouseModifiers)
367 {
368     g_assert(m_parentWindow);
369     GtkWidget* viewWidget = GTK_WIDGET(m_webView);
370     g_assert(gtk_widget_get_realized(viewWidget));
371
372     GUniquePtr<GdkEvent> event(gdk_event_new(eventType));
373     event->button.window = gtk_widget_get_window(viewWidget);
374     g_object_ref(event->button.window);
375
376     event->button.time = GDK_CURRENT_TIME;
377     event->button.x = x;
378     event->button.y = y;
379     event->button.axes = 0;
380     event->button.state = mouseModifiers;
381     event->button.button = button;
382
383     event->button.device = gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gtk_widget_get_display(viewWidget)));
384
385     int xRoot, yRoot;
386     gdk_window_get_root_coords(gtk_widget_get_window(viewWidget), x, y, &xRoot, &yRoot);
387     event->button.x_root = xRoot;
388     event->button.y_root = yRoot;
389     gtk_main_do_event(event.get());
390 }
391
392 static void runJavaScriptReadyCallback(GObject*, GAsyncResult* result, WebViewTest* test)
393 {
394     test->m_javascriptResult = webkit_web_view_run_javascript_finish(test->m_webView, result, test->m_javascriptError);
395     g_main_loop_quit(test->m_mainLoop);
396 }
397
398 static void runJavaScriptFromGResourceReadyCallback(GObject*, GAsyncResult* result, WebViewTest* test)
399 {
400     test->m_javascriptResult = webkit_web_view_run_javascript_from_gresource_finish(test->m_webView, result, test->m_javascriptError);
401     g_main_loop_quit(test->m_mainLoop);
402 }
403
404 WebKitJavascriptResult* WebViewTest::runJavaScriptAndWaitUntilFinished(const char* javascript, GError** error)
405 {
406     if (m_javascriptResult)
407         webkit_javascript_result_unref(m_javascriptResult);
408     m_javascriptResult = 0;
409     m_javascriptError = error;
410     webkit_web_view_run_javascript(m_webView, javascript, 0, reinterpret_cast<GAsyncReadyCallback>(runJavaScriptReadyCallback), this);
411     g_main_loop_run(m_mainLoop);
412
413     return m_javascriptResult;
414 }
415
416 WebKitJavascriptResult* WebViewTest::runJavaScriptFromGResourceAndWaitUntilFinished(const char* resource, GError** error)
417 {
418     if (m_javascriptResult)
419         webkit_javascript_result_unref(m_javascriptResult);
420     m_javascriptResult = 0;
421     m_javascriptError = error;
422     webkit_web_view_run_javascript_from_gresource(m_webView, resource, 0, reinterpret_cast<GAsyncReadyCallback>(runJavaScriptFromGResourceReadyCallback), this);
423     g_main_loop_run(m_mainLoop);
424
425     return m_javascriptResult;
426 }
427
428 static char* jsValueToCString(JSGlobalContextRef context, JSValueRef value)
429 {
430     g_assert(value);
431     g_assert(JSValueIsString(context, value));
432
433     JSRetainPtr<JSStringRef> stringValue(Adopt, JSValueToStringCopy(context, value, 0));
434     g_assert(stringValue);
435
436     size_t cStringLength = JSStringGetMaximumUTF8CStringSize(stringValue.get());
437     char* cString = static_cast<char*>(g_malloc(cStringLength));
438     JSStringGetUTF8CString(stringValue.get(), cString, cStringLength);
439     return cString;
440 }
441
442 char* WebViewTest::javascriptResultToCString(WebKitJavascriptResult* javascriptResult)
443 {
444     JSGlobalContextRef context = webkit_javascript_result_get_global_context(javascriptResult);
445     g_assert(context);
446     return jsValueToCString(context, webkit_javascript_result_get_value(javascriptResult));
447 }
448
449 double WebViewTest::javascriptResultToNumber(WebKitJavascriptResult* javascriptResult)
450 {
451     JSGlobalContextRef context = webkit_javascript_result_get_global_context(javascriptResult);
452     g_assert(context);
453     JSValueRef value = webkit_javascript_result_get_value(javascriptResult);
454     g_assert(value);
455     g_assert(JSValueIsNumber(context, value));
456
457     return JSValueToNumber(context, value, 0);
458 }
459
460 bool WebViewTest::javascriptResultToBoolean(WebKitJavascriptResult* javascriptResult)
461 {
462     JSGlobalContextRef context = webkit_javascript_result_get_global_context(javascriptResult);
463     g_assert(context);
464     JSValueRef value = webkit_javascript_result_get_value(javascriptResult);
465     g_assert(value);
466     g_assert(JSValueIsBoolean(context, value));
467
468     return JSValueToBoolean(context, value);
469 }
470
471 bool WebViewTest::javascriptResultIsNull(WebKitJavascriptResult* javascriptResult)
472 {
473     JSGlobalContextRef context = webkit_javascript_result_get_global_context(javascriptResult);
474     g_assert(context);
475     JSValueRef value = webkit_javascript_result_get_value(javascriptResult);
476     g_assert(value);
477
478     return JSValueIsNull(context, value);
479 }
480
481 bool WebViewTest::javascriptResultIsUndefined(WebKitJavascriptResult* javascriptResult)
482 {
483     JSGlobalContextRef context = webkit_javascript_result_get_global_context(javascriptResult);
484     g_assert(context);
485     JSValueRef value = webkit_javascript_result_get_value(javascriptResult);
486     g_assert(value);
487
488     return JSValueIsUndefined(context, value);
489 }
490
491 static void onSnapshotReady(WebKitWebView* web_view, GAsyncResult* res, WebViewTest* test)
492 {
493     GUniqueOutPtr<GError> error;
494     test->m_surface = webkit_web_view_get_snapshot_finish(web_view, res, &error.outPtr());
495     g_assert(!test->m_surface || !error.get());
496     if (error)
497         g_assert_error(error.get(), WEBKIT_SNAPSHOT_ERROR, WEBKIT_SNAPSHOT_ERROR_FAILED_TO_CREATE);
498     test->quitMainLoop();
499 }
500
501 cairo_surface_t* WebViewTest::getSnapshotAndWaitUntilReady(WebKitSnapshotRegion region, WebKitSnapshotOptions options)
502 {
503     if (m_surface)
504         cairo_surface_destroy(m_surface);
505     m_surface = 0;
506     webkit_web_view_get_snapshot(m_webView, region, options, 0, reinterpret_cast<GAsyncReadyCallback>(onSnapshotReady), this);
507     g_main_loop_run(m_mainLoop);
508     return m_surface;
509 }
510
511 bool WebViewTest::runWebProcessTest(const char* suiteName, const char* testName)
512 {
513     GUniquePtr<char> script(g_strdup_printf("WebProcessTestRunner.runTest('%s/%s');", suiteName, testName));
514     GUniqueOutPtr<GError> error;
515     WebKitJavascriptResult* javascriptResult = runJavaScriptAndWaitUntilFinished(script.get(), &error.outPtr());
516     g_assert(!error);
517     return javascriptResultToBoolean(javascriptResult);
518 }