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