1/*
2 * Copyright (C) 2011, 2017 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
26bool WebViewTest::shouldInitializeWebViewInConstructor = true;
27
28WebViewTest::WebViewTest()
29 : m_userContentManager(adoptGRef(webkit_user_content_manager_new()))
30 , m_mainLoop(g_main_loop_new(nullptr, TRUE))
31{
32 if (shouldInitializeWebViewInConstructor)
33 initializeWebView();
34 assertObjectIsDeletedWhenTestFinishes(G_OBJECT(m_userContentManager.get()));
35}
36
37WebViewTest::~WebViewTest()
38{
39 platformDestroy();
40 if (m_javascriptResult)
41 webkit_javascript_result_unref(m_javascriptResult);
42 if (m_surface)
43 cairo_surface_destroy(m_surface);
44 g_object_unref(m_webView);
45 g_main_loop_unref(m_mainLoop);
46}
47
48void WebViewTest::initializeWebView()
49{
50 g_assert_null(m_webView);
51
52 m_webView = WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW,
53#if PLATFORM(WPE)
54 "backend", Test::createWebViewBackend(),
55#endif
56 "web-context", m_webContext.get(),
57 "user-content-manager", m_userContentManager.get(),
58 nullptr));
59 platformInitializeWebView();
60 assertObjectIsDeletedWhenTestFinishes(G_OBJECT(m_webView));
61
62 g_signal_connect(m_webView, "web-process-terminated", G_CALLBACK(WebViewTest::webProcessTerminated), this);
63}
64
65gboolean WebViewTest::webProcessTerminated(WebKitWebView*, WebKitWebProcessTerminationReason, WebViewTest* test)
66{
67 if (test->m_expectedWebProcessCrash) {
68 test->m_expectedWebProcessCrash = false;
69 return FALSE;
70 }
71 g_assert_not_reached();
72 return TRUE;
73}
74
75void WebViewTest::loadURI(const char* uri)
76{
77 m_activeURI = uri;
78 webkit_web_view_load_uri(m_webView, uri);
79 g_assert_true(webkit_web_view_is_loading(m_webView));
80 g_assert_cmpstr(webkit_web_view_get_uri(m_webView), ==, m_activeURI.data());
81}
82
83void WebViewTest::loadHtml(const char* html, const char* baseURI)
84{
85 if (!baseURI)
86 m_activeURI = "about:blank";
87 else
88 m_activeURI = baseURI;
89 webkit_web_view_load_html(m_webView, html, baseURI);
90 g_assert_true(webkit_web_view_is_loading(m_webView));
91 g_assert_cmpstr(webkit_web_view_get_uri(m_webView), ==, m_activeURI.data());
92}
93
94void WebViewTest::loadPlainText(const char* plainText)
95{
96 m_activeURI = "about:blank";
97 webkit_web_view_load_plain_text(m_webView, plainText);
98 g_assert_true(webkit_web_view_is_loading(m_webView));
99 g_assert_cmpstr(webkit_web_view_get_uri(m_webView), ==, m_activeURI.data());
100}
101
102void WebViewTest::loadBytes(GBytes* bytes, const char* mimeType, const char* encoding, const char* baseURI)
103{
104 if (!baseURI)
105 m_activeURI = "about:blank";
106 else
107 m_activeURI = baseURI;
108 webkit_web_view_load_bytes(m_webView, bytes, mimeType, encoding, baseURI);
109 g_assert_true(webkit_web_view_is_loading(m_webView));
110 g_assert_cmpstr(webkit_web_view_get_uri(m_webView), ==, m_activeURI.data());
111}
112
113void WebViewTest::loadRequest(WebKitURIRequest* request)
114{
115 m_activeURI = webkit_uri_request_get_uri(request);
116 webkit_web_view_load_request(m_webView, request);
117 g_assert_true(webkit_web_view_is_loading(m_webView));
118 g_assert_cmpstr(webkit_web_view_get_uri(m_webView), ==, m_activeURI.data());
119}
120
121void WebViewTest::loadAlternateHTML(const char* html, const char* contentURI, const char* baseURI)
122{
123 m_activeURI = contentURI;
124 webkit_web_view_load_alternate_html(m_webView, html, contentURI, baseURI);
125 g_assert_true(webkit_web_view_is_loading(m_webView));
126 g_assert_cmpstr(webkit_web_view_get_uri(m_webView), ==, m_activeURI.data());
127}
128
129void 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_true(webkit_web_view_is_loading(m_webView));
142 g_assert_cmpstr(webkit_web_view_get_uri(m_webView), ==, m_activeURI.data());
143 }
144}
145
146void 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_true(webkit_web_view_is_loading(m_webView));
159 g_assert_cmpstr(webkit_web_view_get_uri(m_webView), ==, m_activeURI.data());
160 }
161}
162
163void 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_true(webkit_web_view_is_loading(m_webView));
168 g_assert_cmpstr(webkit_web_view_get_uri(m_webView), ==, m_activeURI.data());
169}
170
171void WebViewTest::quitMainLoop()
172{
173 g_main_loop_quit(m_mainLoop);
174}
175
176void WebViewTest::wait(double seconds)
177{
178 g_timeout_add(seconds * 1000, [](gpointer userData) -> gboolean {
179 static_cast<WebViewTest*>(userData)->quitMainLoop();
180 return G_SOURCE_REMOVE;
181 }, this);
182 g_main_loop_run(m_mainLoop);
183}
184
185static void loadChanged(WebKitWebView* webView, WebKitLoadEvent loadEvent, WebViewTest* test)
186{
187 if (loadEvent != WEBKIT_LOAD_FINISHED)
188 return;
189 g_signal_handlers_disconnect_by_func(webView, reinterpret_cast<void*>(loadChanged), test);
190 g_main_loop_quit(test->m_mainLoop);
191}
192
193void WebViewTest::waitUntilLoadFinished()
194{
195 g_signal_connect(m_webView, "load-changed", G_CALLBACK(loadChanged), this);
196 g_main_loop_run(m_mainLoop);
197}
198
199static void titleChanged(WebKitWebView* webView, GParamSpec*, WebViewTest* test)
200{
201 if (!test->m_expectedTitle.isNull() && test->m_expectedTitle != webkit_web_view_get_title(webView))
202 return;
203
204 g_signal_handlers_disconnect_by_func(webView, reinterpret_cast<void*>(titleChanged), test);
205 g_main_loop_quit(test->m_mainLoop);
206}
207
208void WebViewTest::waitUntilTitleChangedTo(const char* expectedTitle)
209{
210 m_expectedTitle = expectedTitle;
211 g_signal_connect(m_webView, "notify::title", G_CALLBACK(titleChanged), this);
212 g_main_loop_run(m_mainLoop);
213 m_expectedTitle = CString();
214}
215
216void WebViewTest::waitUntilTitleChanged()
217{
218 waitUntilTitleChangedTo(0);
219}
220
221void WebViewTest::selectAll()
222{
223 webkit_web_view_execute_editing_command(m_webView, "SelectAll");
224}
225
226bool WebViewTest::isEditable()
227{
228 return webkit_web_view_is_editable(m_webView);
229}
230
231void WebViewTest::setEditable(bool editable)
232{
233 webkit_web_view_set_editable(m_webView, editable);
234}
235
236static void resourceGetDataCallback(GObject* object, GAsyncResult* result, gpointer userData)
237{
238 size_t dataSize;
239 GUniqueOutPtr<GError> error;
240 unsigned char* data = webkit_web_resource_get_data_finish(WEBKIT_WEB_RESOURCE(object), result, &dataSize, &error.outPtr());
241 g_assert_nonnull(data);
242
243 WebViewTest* test = static_cast<WebViewTest*>(userData);
244 test->m_resourceData.reset(reinterpret_cast<char*>(data));
245 test->m_resourceDataSize = dataSize;
246 g_main_loop_quit(test->m_mainLoop);
247}
248
249const char* WebViewTest::mainResourceData(size_t& mainResourceDataSize)
250{
251 m_resourceDataSize = 0;
252 m_resourceData.reset();
253 WebKitWebResource* resource = webkit_web_view_get_main_resource(m_webView);
254 g_assert_nonnull(resource);
255
256 webkit_web_resource_get_data(resource, 0, resourceGetDataCallback, this);
257 g_main_loop_run(m_mainLoop);
258
259 mainResourceDataSize = m_resourceDataSize;
260 return m_resourceData.get();
261}
262
263static void runJavaScriptReadyCallback(GObject*, GAsyncResult* result, WebViewTest* test)
264{
265 test->m_javascriptResult = webkit_web_view_run_javascript_finish(test->m_webView, result, test->m_javascriptError);
266 g_main_loop_quit(test->m_mainLoop);
267}
268
269static void runJavaScriptFromGResourceReadyCallback(GObject*, GAsyncResult* result, WebViewTest* test)
270{
271 test->m_javascriptResult = webkit_web_view_run_javascript_from_gresource_finish(test->m_webView, result, test->m_javascriptError);
272 g_main_loop_quit(test->m_mainLoop);
273}
274
275static void runJavaScriptInWorldReadyCallback(GObject*, GAsyncResult* result, WebViewTest* test)
276{
277 test->m_javascriptResult = webkit_web_view_run_javascript_in_world_finish(test->m_webView, result, test->m_javascriptError);
278 g_main_loop_quit(test->m_mainLoop);
279}
280
281WebKitJavascriptResult* WebViewTest::runJavaScriptAndWaitUntilFinished(const char* javascript, GError** error)
282{
283 if (m_javascriptResult)
284 webkit_javascript_result_unref(m_javascriptResult);
285 m_javascriptResult = 0;
286 m_javascriptError = error;
287 webkit_web_view_run_javascript(m_webView, javascript, 0, reinterpret_cast<GAsyncReadyCallback>(runJavaScriptReadyCallback), this);
288 g_main_loop_run(m_mainLoop);
289
290 return m_javascriptResult;
291}
292
293WebKitJavascriptResult* WebViewTest::runJavaScriptFromGResourceAndWaitUntilFinished(const char* resource, GError** error)
294{
295 if (m_javascriptResult)
296 webkit_javascript_result_unref(m_javascriptResult);
297 m_javascriptResult = 0;
298 m_javascriptError = error;
299 webkit_web_view_run_javascript_from_gresource(m_webView, resource, 0, reinterpret_cast<GAsyncReadyCallback>(runJavaScriptFromGResourceReadyCallback), this);
300 g_main_loop_run(m_mainLoop);
301
302 return m_javascriptResult;
303}
304
305WebKitJavascriptResult* WebViewTest::runJavaScriptInWorldAndWaitUntilFinished(const char* javascript, const char* world, GError** error)
306{
307 if (m_javascriptResult)
308 webkit_javascript_result_unref(m_javascriptResult);
309 m_javascriptResult = 0;
310 m_javascriptError = error;
311 webkit_web_view_run_javascript_in_world(m_webView, javascript, world, nullptr, reinterpret_cast<GAsyncReadyCallback>(runJavaScriptInWorldReadyCallback), this);
312 g_main_loop_run(m_mainLoop);
313
314 return m_javascriptResult;
315}
316
317char* WebViewTest::javascriptResultToCString(WebKitJavascriptResult* javascriptResult)
318{
319 auto* value = webkit_javascript_result_get_js_value(javascriptResult);
320 g_assert_true(JSC_IS_VALUE(value));
321 g_assert_true(jsc_value_is_string(value));
322 return jsc_value_to_string(value);
323}
324
325double WebViewTest::javascriptResultToNumber(WebKitJavascriptResult* javascriptResult)
326{
327 auto* value = webkit_javascript_result_get_js_value(javascriptResult);
328 g_assert_true(JSC_IS_VALUE(value));
329 g_assert_true(jsc_value_is_number(value));
330 return jsc_value_to_double(value);
331}
332
333bool WebViewTest::javascriptResultToBoolean(WebKitJavascriptResult* javascriptResult)
334{
335 auto* value = webkit_javascript_result_get_js_value(javascriptResult);
336 g_assert_true(JSC_IS_VALUE(value));
337 g_assert_true(jsc_value_is_boolean(value));
338 return jsc_value_to_boolean(value);
339}
340
341bool WebViewTest::javascriptResultIsNull(WebKitJavascriptResult* javascriptResult)
342{
343 auto* value = webkit_javascript_result_get_js_value(javascriptResult);
344 g_assert_true(JSC_IS_VALUE(value));
345 return jsc_value_is_null(value);
346}
347
348bool WebViewTest::javascriptResultIsUndefined(WebKitJavascriptResult* javascriptResult)
349{
350 auto* value = webkit_javascript_result_get_js_value(javascriptResult);
351 g_assert_true(JSC_IS_VALUE(value));
352 return jsc_value_is_undefined(value);
353}
354
355#if PLATFORM(GTK)
356static void onSnapshotReady(WebKitWebView* web_view, GAsyncResult* res, WebViewTest* test)
357{
358 GUniqueOutPtr<GError> error;
359 test->m_surface = webkit_web_view_get_snapshot_finish(web_view, res, &error.outPtr());
360 g_assert_true(!test->m_surface || !error.get());
361 if (error)
362 g_assert_error(error.get(), WEBKIT_SNAPSHOT_ERROR, WEBKIT_SNAPSHOT_ERROR_FAILED_TO_CREATE);
363 test->quitMainLoop();
364}
365
366cairo_surface_t* WebViewTest::getSnapshotAndWaitUntilReady(WebKitSnapshotRegion region, WebKitSnapshotOptions options)
367{
368 if (m_surface)
369 cairo_surface_destroy(m_surface);
370 m_surface = 0;
371 webkit_web_view_get_snapshot(m_webView, region, options, 0, reinterpret_cast<GAsyncReadyCallback>(onSnapshotReady), this);
372 g_main_loop_run(m_mainLoop);
373 return m_surface;
374}
375#endif
376
377bool WebViewTest::runWebProcessTest(const char* suiteName, const char* testName, const char* contents, const char* contentType)
378{
379 if (!contentType) {
380 static const char* emptyHTML = "<html><body></body></html>";
381 loadHtml(contents ? contents : emptyHTML, "webprocess://test");
382 } else {
383 GRefPtr<GBytes> bytes = adoptGRef(g_bytes_new_static(contents, strlen(contents)));
384 loadBytes(bytes.get(), contentType, nullptr, "webprocess://test");
385 }
386 waitUntilLoadFinished();
387
388 GUniquePtr<char> script(g_strdup_printf("WebProcessTestRunner.runTest('%s/%s');", suiteName, testName));
389 GUniqueOutPtr<GError> error;
390 WebKitJavascriptResult* javascriptResult = runJavaScriptAndWaitUntilFinished(script.get(), &error.outPtr());
391 g_assert_no_error(error.get());
392 loadURI("about:blank");
393 waitUntilLoadFinished();
394 return javascriptResultToBoolean(javascriptResult);
395}
396