| 1 | /* |
| 2 | * Copyright (C) 2011 Igalia S.L. |
| 3 | * Copyright (C) 2014 Collabora Ltd. |
| 4 | * |
| 5 | * This library is free software; you can redistribute it and/or |
| 6 | * modify it under the terms of the GNU Lesser General Public |
| 7 | * License as published by the Free Software Foundation; either |
| 8 | * version 2,1 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 "WebKitTestServer.h" |
| 23 | #include "WebViewTest.h" |
| 24 | #include <glib/gstdio.h> |
| 25 | #include <wtf/glib/GRefPtr.h> |
| 26 | |
| 27 | class IsPlayingAudioWebViewTest : public WebViewTest { |
| 28 | public: |
| 29 | MAKE_GLIB_TEST_FIXTURE(IsPlayingAudioWebViewTest); |
| 30 | |
| 31 | static void isPlayingAudioChanged(GObject*, GParamSpec*, IsPlayingAudioWebViewTest* test) |
| 32 | { |
| 33 | g_signal_handlers_disconnect_by_func(test->m_webView, reinterpret_cast<void*>(isPlayingAudioChanged), test); |
| 34 | g_main_loop_quit(test->m_mainLoop); |
| 35 | } |
| 36 | |
| 37 | void waitUntilIsPlayingAudioChanged() |
| 38 | { |
| 39 | g_signal_connect(m_webView, "notify::is-playing-audio" , G_CALLBACK(isPlayingAudioChanged), this); |
| 40 | g_main_loop_run(m_mainLoop); |
| 41 | } |
| 42 | }; |
| 43 | |
| 44 | static WebKitTestServer* gServer; |
| 45 | |
| 46 | static void testWebViewWebContext(WebViewTest* test, gconstpointer) |
| 47 | { |
| 48 | g_assert_true(webkit_web_view_get_context(test->m_webView) == test->m_webContext.get()); |
| 49 | g_assert_true(webkit_web_context_get_default() != test->m_webContext.get()); |
| 50 | |
| 51 | // Check that a web view created with g_object_new has the default context. |
| 52 | auto webView = Test::adoptView(g_object_new(WEBKIT_TYPE_WEB_VIEW, |
| 53 | #if PLATFORM(WPE) |
| 54 | "backend" , Test::createWebViewBackend(), |
| 55 | #endif |
| 56 | nullptr)); |
| 57 | g_assert_true(webkit_web_view_get_context(webView.get()) == webkit_web_context_get_default()); |
| 58 | |
| 59 | // Check that a web view created with a related view has the related view context. |
| 60 | webView = Test::adoptView(Test::createWebView(test->m_webView)); |
| 61 | g_assert_true(webkit_web_view_get_context(webView.get()) == test->m_webContext.get()); |
| 62 | |
| 63 | // Check that a web context given as construct parameter is ignored if a related view is also provided. |
| 64 | webView = Test::adoptView(g_object_new(WEBKIT_TYPE_WEB_VIEW, |
| 65 | #if PLATFORM(WPE) |
| 66 | "backend" , Test::createWebViewBackend(), |
| 67 | #endif |
| 68 | "web-context" , webkit_web_context_get_default(), |
| 69 | "related-view" , test->m_webView, |
| 70 | nullptr)); |
| 71 | g_assert_true(webkit_web_view_get_context(webView.get()) == test->m_webContext.get()); |
| 72 | } |
| 73 | |
| 74 | static void testWebViewWebContextLifetime(WebViewTest* test, gconstpointer) |
| 75 | { |
| 76 | WebKitWebContext* webContext = webkit_web_context_new(); |
| 77 | test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webContext)); |
| 78 | |
| 79 | auto* webView = Test::createWebView(webContext); |
| 80 | test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webView)); |
| 81 | |
| 82 | #if PLATFORM(GTK) |
| 83 | g_object_ref_sink(webView); |
| 84 | #endif |
| 85 | g_object_unref(webContext); |
| 86 | |
| 87 | // Check that the web view still has a valid context. |
| 88 | WebKitWebContext* tmpContext = webkit_web_view_get_context(WEBKIT_WEB_VIEW(webView)); |
| 89 | g_assert_true(WEBKIT_IS_WEB_CONTEXT(tmpContext)); |
| 90 | g_object_unref(webView); |
| 91 | |
| 92 | WebKitWebContext* webContext2 = webkit_web_context_new(); |
| 93 | test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webContext2)); |
| 94 | |
| 95 | auto* webView2 = Test::createWebView(webContext2); |
| 96 | test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webView2)); |
| 97 | |
| 98 | #if PLATFORM(GTK) |
| 99 | g_object_ref_sink(webView2); |
| 100 | #endif |
| 101 | g_object_unref(webView2); |
| 102 | |
| 103 | // Check that the context is still valid. |
| 104 | g_assert_true(WEBKIT_IS_WEB_CONTEXT(webContext2)); |
| 105 | g_object_unref(webContext2); |
| 106 | } |
| 107 | |
| 108 | static void testWebViewCloseQuickly(WebViewTest* test, gconstpointer) |
| 109 | { |
| 110 | auto webView = Test::adoptView(Test::createWebView()); |
| 111 | test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webView.get())); |
| 112 | g_idle_add([](gpointer userData) -> gboolean { |
| 113 | static_cast<WebViewTest*>(userData)->quitMainLoop(); |
| 114 | return G_SOURCE_REMOVE; |
| 115 | }, test); |
| 116 | g_main_loop_run(test->m_mainLoop); |
| 117 | webView = nullptr; |
| 118 | } |
| 119 | |
| 120 | #if PLATFORM(WPE) |
| 121 | static void testWebViewWebBackend(Test* test, gconstpointer) |
| 122 | { |
| 123 | static struct wpe_view_backend_interface s_testingInterface = { |
| 124 | // create |
| 125 | [](void*, struct wpe_view_backend*) -> void* { return nullptr; }, |
| 126 | // destroy |
| 127 | [](void*) { }, |
| 128 | // initialize |
| 129 | [](void*) { }, |
| 130 | // get_renderer_host_fd |
| 131 | [](void*) -> int { return -1; }, |
| 132 | // padding |
| 133 | nullptr, |
| 134 | nullptr, |
| 135 | nullptr, |
| 136 | nullptr |
| 137 | }; |
| 138 | |
| 139 | // User provided backend with default deleter (we don't have a way to check the backend will be actually freed). |
| 140 | GRefPtr<WebKitWebView> webView = adoptGRef(webkit_web_view_new(webkit_web_view_backend_new(wpe_view_backend_create_with_backend_interface(&s_testingInterface, nullptr), nullptr, nullptr))); |
| 141 | test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webView.get())); |
| 142 | auto* viewBackend = webkit_web_view_get_backend(webView.get()); |
| 143 | g_assert_nonnull(viewBackend); |
| 144 | auto* wpeBackend = webkit_web_view_backend_get_wpe_backend(viewBackend); |
| 145 | g_assert_nonnull(wpeBackend); |
| 146 | webView = nullptr; |
| 147 | |
| 148 | // User provided backend with destroy notify. |
| 149 | wpeBackend = wpe_view_backend_create_with_backend_interface(&s_testingInterface, nullptr); |
| 150 | webView = adoptGRef(webkit_web_view_new(webkit_web_view_backend_new(wpeBackend, [](gpointer userData) { |
| 151 | auto* backend = *static_cast<struct wpe_view_backend**>(userData); |
| 152 | wpe_view_backend_destroy(backend); |
| 153 | *static_cast<struct wpe_view_backend**>(userData) = nullptr; |
| 154 | }, &wpeBackend))); |
| 155 | test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webView.get())); |
| 156 | webView = nullptr; |
| 157 | g_assert_null(wpeBackend); |
| 158 | |
| 159 | // User provided backend owned by another object with destroy notify. |
| 160 | static bool hasInstance = false; |
| 161 | struct BackendOwner { |
| 162 | BackendOwner(struct wpe_view_backend* backend) |
| 163 | : backend(backend) |
| 164 | { |
| 165 | hasInstance = true; |
| 166 | } |
| 167 | |
| 168 | ~BackendOwner() |
| 169 | { |
| 170 | wpe_view_backend_destroy(backend); |
| 171 | hasInstance = false; |
| 172 | } |
| 173 | |
| 174 | struct wpe_view_backend* backend; |
| 175 | }; |
| 176 | auto* owner = new BackendOwner(wpe_view_backend_create_with_backend_interface(&s_testingInterface, nullptr)); |
| 177 | g_assert_true(hasInstance); |
| 178 | webView = adoptGRef(webkit_web_view_new(webkit_web_view_backend_new(owner->backend, [](gpointer userData) { |
| 179 | delete static_cast<BackendOwner*>(userData); |
| 180 | }, owner))); |
| 181 | test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webView.get())); |
| 182 | g_assert_true(hasInstance); |
| 183 | webView = nullptr; |
| 184 | g_assert_false(hasInstance); |
| 185 | } |
| 186 | #endif // PLATFORM(WPE) |
| 187 | |
| 188 | static void ephemeralViewloadChanged(WebKitWebView* webView, WebKitLoadEvent loadEvent, WebViewTest* test) |
| 189 | { |
| 190 | if (loadEvent != WEBKIT_LOAD_FINISHED) |
| 191 | return; |
| 192 | g_signal_handlers_disconnect_by_func(webView, reinterpret_cast<void*>(ephemeralViewloadChanged), test); |
| 193 | test->quitMainLoop(); |
| 194 | } |
| 195 | |
| 196 | static void testWebViewEphemeral(WebViewTest* test, gconstpointer) |
| 197 | { |
| 198 | g_assert_false(webkit_web_view_is_ephemeral(test->m_webView)); |
| 199 | g_assert_false(webkit_web_context_is_ephemeral(webkit_web_view_get_context(test->m_webView))); |
| 200 | auto* manager = webkit_web_context_get_website_data_manager(test->m_webContext.get()); |
| 201 | g_assert_false(webkit_website_data_manager_is_ephemeral(manager)); |
| 202 | g_assert_true(webkit_web_view_get_website_data_manager(test->m_webView) == manager); |
| 203 | webkit_website_data_manager_clear(manager, WEBKIT_WEBSITE_DATA_DISK_CACHE, 0, nullptr, [](GObject* manager, GAsyncResult* result, gpointer userData) { |
| 204 | webkit_website_data_manager_clear_finish(WEBKIT_WEBSITE_DATA_MANAGER(manager), result, nullptr); |
| 205 | static_cast<WebViewTest*>(userData)->quitMainLoop(); |
| 206 | }, test); |
| 207 | g_main_loop_run(test->m_mainLoop); |
| 208 | |
| 209 | // A WebView on a non ephemeral context can be ephemeral. |
| 210 | auto webView = Test::adoptView(g_object_new(WEBKIT_TYPE_WEB_VIEW, |
| 211 | #if PLATFORM(WPE) |
| 212 | "backend" , Test::createWebViewBackend(), |
| 213 | #endif |
| 214 | "web-context" , webkit_web_view_get_context(test->m_webView), |
| 215 | "is-ephemeral" , TRUE, |
| 216 | nullptr)); |
| 217 | g_assert_true(webkit_web_view_is_ephemeral(webView.get())); |
| 218 | g_assert_false(webkit_web_context_is_ephemeral(webkit_web_view_get_context(webView.get()))); |
| 219 | g_assert_true(webkit_web_view_get_website_data_manager(webView.get()) != manager); |
| 220 | |
| 221 | g_signal_connect(webView.get(), "load-changed" , G_CALLBACK(ephemeralViewloadChanged), test); |
| 222 | webkit_web_view_load_uri(webView.get(), gServer->getURIForPath("/" ).data()); |
| 223 | g_main_loop_run(test->m_mainLoop); |
| 224 | |
| 225 | // Disk cache delays the storing of initial resources for 1 second to avoid |
| 226 | // affecting early page load. So, wait 1 second here to make sure resources |
| 227 | // have already been stored. |
| 228 | test->wait(1); |
| 229 | |
| 230 | webkit_website_data_manager_fetch(manager, WEBKIT_WEBSITE_DATA_DISK_CACHE, nullptr, [](GObject* manager, GAsyncResult* result, gpointer userData) { |
| 231 | auto* test = static_cast<WebViewTest*>(userData); |
| 232 | g_assert_null(webkit_website_data_manager_fetch_finish(WEBKIT_WEBSITE_DATA_MANAGER(manager), result, nullptr)); |
| 233 | test->quitMainLoop(); |
| 234 | }, test); |
| 235 | g_main_loop_run(test->m_mainLoop); |
| 236 | } |
| 237 | |
| 238 | static void testWebViewCustomCharset(WebViewTest* test, gconstpointer) |
| 239 | { |
| 240 | test->loadURI(gServer->getURIForPath("/" ).data()); |
| 241 | test->waitUntilLoadFinished(); |
| 242 | g_assert_null(webkit_web_view_get_custom_charset(test->m_webView)); |
| 243 | webkit_web_view_set_custom_charset(test->m_webView, "utf8" ); |
| 244 | // Changing the charset reloads the page, so wait until reloaded. |
| 245 | test->waitUntilLoadFinished(); |
| 246 | g_assert_cmpstr(webkit_web_view_get_custom_charset(test->m_webView), ==, "utf8" ); |
| 247 | |
| 248 | // Go back to the default charset and wait until reloaded. |
| 249 | webkit_web_view_set_custom_charset(test->m_webView, nullptr); |
| 250 | test->waitUntilLoadFinished(); |
| 251 | g_assert_null(webkit_web_view_get_custom_charset(test->m_webView)); |
| 252 | } |
| 253 | |
| 254 | static void testWebViewSettings(WebViewTest* test, gconstpointer) |
| 255 | { |
| 256 | WebKitSettings* defaultSettings = webkit_web_view_get_settings(test->m_webView); |
| 257 | test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(defaultSettings)); |
| 258 | g_assert_nonnull(defaultSettings); |
| 259 | g_assert_true(webkit_settings_get_enable_javascript(defaultSettings)); |
| 260 | |
| 261 | GRefPtr<WebKitSettings> newSettings = adoptGRef(webkit_settings_new()); |
| 262 | test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(newSettings.get())); |
| 263 | g_object_set(G_OBJECT(newSettings.get()), "enable-javascript" , FALSE, NULL); |
| 264 | webkit_web_view_set_settings(test->m_webView, newSettings.get()); |
| 265 | |
| 266 | WebKitSettings* settings = webkit_web_view_get_settings(test->m_webView); |
| 267 | g_assert_true(settings != defaultSettings); |
| 268 | g_assert_false(webkit_settings_get_enable_javascript(settings)); |
| 269 | |
| 270 | auto webView2 = Test::adoptView(Test::createWebView()); |
| 271 | test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webView2.get())); |
| 272 | webkit_web_view_set_settings(WEBKIT_WEB_VIEW(webView2.get()), settings); |
| 273 | g_assert_true(webkit_web_view_get_settings(WEBKIT_WEB_VIEW(webView2.get())) == settings); |
| 274 | |
| 275 | GRefPtr<WebKitSettings> newSettings2 = adoptGRef(webkit_settings_new()); |
| 276 | test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(newSettings2.get())); |
| 277 | webkit_web_view_set_settings(WEBKIT_WEB_VIEW(webView2.get()), newSettings2.get()); |
| 278 | settings = webkit_web_view_get_settings(WEBKIT_WEB_VIEW(webView2.get())); |
| 279 | g_assert_true(settings == newSettings2.get()); |
| 280 | g_assert_true(webkit_settings_get_enable_javascript(settings)); |
| 281 | |
| 282 | auto webView3 = Test::adoptView(Test::createWebView(newSettings2.get())); |
| 283 | test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webView3.get())); |
| 284 | g_assert_true(webkit_web_view_get_settings(WEBKIT_WEB_VIEW(webView3.get())) == newSettings2.get()); |
| 285 | } |
| 286 | |
| 287 | static void testWebViewZoomLevel(WebViewTest* test, gconstpointer) |
| 288 | { |
| 289 | g_assert_cmpfloat(webkit_web_view_get_zoom_level(test->m_webView), ==, 1); |
| 290 | webkit_web_view_set_zoom_level(test->m_webView, 2.5); |
| 291 | g_assert_cmpfloat(webkit_web_view_get_zoom_level(test->m_webView), ==, 2.5); |
| 292 | |
| 293 | webkit_settings_set_zoom_text_only(webkit_web_view_get_settings(test->m_webView), TRUE); |
| 294 | // The zoom level shouldn't change when zoom-text-only setting changes. |
| 295 | g_assert_cmpfloat(webkit_web_view_get_zoom_level(test->m_webView), ==, 2.5); |
| 296 | } |
| 297 | |
| 298 | static void testWebViewRunJavaScript(WebViewTest* test, gconstpointer) |
| 299 | { |
| 300 | static const char* html = "<html><body><a id='WebKitLink' href='http://www.webkitgtk.org/' title='WebKitGTK+ Title'>WebKitGTK+ Website</a></body></html>" ; |
| 301 | test->loadHtml(html, 0); |
| 302 | test->waitUntilLoadFinished(); |
| 303 | |
| 304 | GUniqueOutPtr<GError> error; |
| 305 | WebKitJavascriptResult* javascriptResult = test->runJavaScriptAndWaitUntilFinished("window.document.getElementById('WebKitLink').title;" , &error.outPtr()); |
| 306 | g_assert_nonnull(javascriptResult); |
| 307 | test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webkit_javascript_result_get_js_value(javascriptResult))); |
| 308 | g_assert_no_error(error.get()); |
| 309 | GUniquePtr<char> valueString(WebViewTest::javascriptResultToCString(javascriptResult)); |
| 310 | g_assert_cmpstr(valueString.get(), ==, "WebKitGTK+ Title" ); |
| 311 | |
| 312 | javascriptResult = test->runJavaScriptAndWaitUntilFinished("window.document.getElementById('WebKitLink').href;" , &error.outPtr()); |
| 313 | g_assert_nonnull(javascriptResult); |
| 314 | test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webkit_javascript_result_get_js_value(javascriptResult))); |
| 315 | g_assert_no_error(error.get()); |
| 316 | valueString.reset(WebViewTest::javascriptResultToCString(javascriptResult)); |
| 317 | g_assert_cmpstr(valueString.get(), ==, "http://www.webkitgtk.org/" ); |
| 318 | |
| 319 | javascriptResult = test->runJavaScriptAndWaitUntilFinished("window.document.getElementById('WebKitLink').textContent" , &error.outPtr()); |
| 320 | g_assert_nonnull(javascriptResult); |
| 321 | test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webkit_javascript_result_get_js_value(javascriptResult))); |
| 322 | g_assert_no_error(error.get()); |
| 323 | valueString.reset(WebViewTest::javascriptResultToCString(javascriptResult)); |
| 324 | g_assert_cmpstr(valueString.get(), ==, "WebKitGTK+ Website" ); |
| 325 | |
| 326 | javascriptResult = test->runJavaScriptAndWaitUntilFinished("a = 25;" , &error.outPtr()); |
| 327 | g_assert_nonnull(javascriptResult); |
| 328 | test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webkit_javascript_result_get_js_value(javascriptResult))); |
| 329 | g_assert_no_error(error.get()); |
| 330 | g_assert_cmpfloat(WebViewTest::javascriptResultToNumber(javascriptResult), ==, 25); |
| 331 | |
| 332 | javascriptResult = test->runJavaScriptAndWaitUntilFinished("a = 2.5;" , &error.outPtr()); |
| 333 | g_assert_nonnull(javascriptResult); |
| 334 | test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webkit_javascript_result_get_js_value(javascriptResult))); |
| 335 | g_assert_no_error(error.get()); |
| 336 | g_assert_cmpfloat(WebViewTest::javascriptResultToNumber(javascriptResult), ==, 2.5); |
| 337 | |
| 338 | javascriptResult = test->runJavaScriptAndWaitUntilFinished("a = true" , &error.outPtr()); |
| 339 | g_assert_nonnull(javascriptResult); |
| 340 | test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webkit_javascript_result_get_js_value(javascriptResult))); |
| 341 | g_assert_no_error(error.get()); |
| 342 | g_assert_true(WebViewTest::javascriptResultToBoolean(javascriptResult)); |
| 343 | |
| 344 | javascriptResult = test->runJavaScriptAndWaitUntilFinished("a = false" , &error.outPtr()); |
| 345 | g_assert_nonnull(javascriptResult); |
| 346 | test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webkit_javascript_result_get_js_value(javascriptResult))); |
| 347 | g_assert_no_error(error.get()); |
| 348 | g_assert_false(WebViewTest::javascriptResultToBoolean(javascriptResult)); |
| 349 | |
| 350 | javascriptResult = test->runJavaScriptAndWaitUntilFinished("a = null" , &error.outPtr()); |
| 351 | g_assert_nonnull(javascriptResult); |
| 352 | test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webkit_javascript_result_get_js_value(javascriptResult))); |
| 353 | g_assert_no_error(error.get()); |
| 354 | g_assert_true(WebViewTest::javascriptResultIsNull(javascriptResult)); |
| 355 | |
| 356 | javascriptResult = test->runJavaScriptAndWaitUntilFinished("function Foo() { a = 25; } Foo();" , &error.outPtr()); |
| 357 | g_assert_nonnull(javascriptResult); |
| 358 | test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webkit_javascript_result_get_js_value(javascriptResult))); |
| 359 | g_assert_no_error(error.get()); |
| 360 | g_assert_true(WebViewTest::javascriptResultIsUndefined(javascriptResult)); |
| 361 | |
| 362 | javascriptResult = test->runJavaScriptFromGResourceAndWaitUntilFinished("/org/webkit/glib/tests/link-title.js" , &error.outPtr()); |
| 363 | g_assert_nonnull(javascriptResult); |
| 364 | test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webkit_javascript_result_get_js_value(javascriptResult))); |
| 365 | g_assert_no_error(error.get()); |
| 366 | valueString.reset(WebViewTest::javascriptResultToCString(javascriptResult)); |
| 367 | g_assert_cmpstr(valueString.get(), ==, "WebKitGTK+ Title" ); |
| 368 | |
| 369 | javascriptResult = test->runJavaScriptFromGResourceAndWaitUntilFinished("/wrong/path/to/resource.js" , &error.outPtr()); |
| 370 | g_assert_null(javascriptResult); |
| 371 | g_assert_error(error.get(), G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND); |
| 372 | |
| 373 | javascriptResult = test->runJavaScriptAndWaitUntilFinished("foo();" , &error.outPtr()); |
| 374 | g_assert_null(javascriptResult); |
| 375 | g_assert_error(error.get(), WEBKIT_JAVASCRIPT_ERROR, WEBKIT_JAVASCRIPT_ERROR_SCRIPT_FAILED); |
| 376 | |
| 377 | // Values of the main world are not available in the isolated one. |
| 378 | javascriptResult = test->runJavaScriptInWorldAndWaitUntilFinished("a" , "WebExtensionTestScriptWorld" , &error.outPtr()); |
| 379 | g_assert_null(javascriptResult); |
| 380 | g_assert_error(error.get(), WEBKIT_JAVASCRIPT_ERROR, WEBKIT_JAVASCRIPT_ERROR_SCRIPT_FAILED); |
| 381 | |
| 382 | javascriptResult = test->runJavaScriptInWorldAndWaitUntilFinished("a = 50" , "WebExtensionTestScriptWorld" , &error.outPtr()); |
| 383 | g_assert_nonnull(javascriptResult); |
| 384 | test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webkit_javascript_result_get_js_value(javascriptResult))); |
| 385 | g_assert_no_error(error.get()); |
| 386 | g_assert_cmpfloat(WebViewTest::javascriptResultToNumber(javascriptResult), ==, 50); |
| 387 | |
| 388 | // Values of the isolated world are not available in the normal one. |
| 389 | javascriptResult = test->runJavaScriptAndWaitUntilFinished("a" , &error.outPtr()); |
| 390 | g_assert_nonnull(javascriptResult); |
| 391 | test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webkit_javascript_result_get_js_value(javascriptResult))); |
| 392 | g_assert_no_error(error.get()); |
| 393 | g_assert_cmpfloat(WebViewTest::javascriptResultToNumber(javascriptResult), ==, 25); |
| 394 | |
| 395 | // Running a script in a world that doesn't exist should fail. |
| 396 | javascriptResult = test->runJavaScriptInWorldAndWaitUntilFinished("a" , "InvalidScriptWorld" , &error.outPtr()); |
| 397 | g_assert_null(javascriptResult); |
| 398 | g_assert_error(error.get(), WEBKIT_JAVASCRIPT_ERROR, WEBKIT_JAVASCRIPT_ERROR_SCRIPT_FAILED); |
| 399 | } |
| 400 | |
| 401 | class FullScreenClientTest: public WebViewTest { |
| 402 | public: |
| 403 | MAKE_GLIB_TEST_FIXTURE(FullScreenClientTest); |
| 404 | |
| 405 | enum FullScreenEvent { |
| 406 | None, |
| 407 | Enter, |
| 408 | Leave |
| 409 | }; |
| 410 | |
| 411 | static gboolean viewEnterFullScreenCallback(WebKitWebView*, FullScreenClientTest* test) |
| 412 | { |
| 413 | test->m_event = Enter; |
| 414 | g_main_loop_quit(test->m_mainLoop); |
| 415 | return FALSE; |
| 416 | } |
| 417 | |
| 418 | static gboolean viewLeaveFullScreenCallback(WebKitWebView*, FullScreenClientTest* test) |
| 419 | { |
| 420 | test->m_event = Leave; |
| 421 | g_main_loop_quit(test->m_mainLoop); |
| 422 | return FALSE; |
| 423 | } |
| 424 | |
| 425 | FullScreenClientTest() |
| 426 | : m_event(None) |
| 427 | { |
| 428 | webkit_settings_set_enable_fullscreen(webkit_web_view_get_settings(m_webView), TRUE); |
| 429 | g_signal_connect(m_webView, "enter-fullscreen" , G_CALLBACK(viewEnterFullScreenCallback), this); |
| 430 | g_signal_connect(m_webView, "leave-fullscreen" , G_CALLBACK(viewLeaveFullScreenCallback), this); |
| 431 | } |
| 432 | |
| 433 | ~FullScreenClientTest() |
| 434 | { |
| 435 | g_signal_handlers_disconnect_matched(m_webView, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this); |
| 436 | } |
| 437 | |
| 438 | void requestFullScreenAndWaitUntilEnteredFullScreen() |
| 439 | { |
| 440 | m_event = None; |
| 441 | webkit_web_view_run_javascript(m_webView, "document.documentElement.webkitRequestFullScreen();" , 0, 0, 0); |
| 442 | g_main_loop_run(m_mainLoop); |
| 443 | } |
| 444 | |
| 445 | static gboolean leaveFullScreenIdle(FullScreenClientTest* test) |
| 446 | { |
| 447 | // FIXME: Implement key strokes in WPE |
| 448 | #if PLATFORM(GTK) |
| 449 | test->keyStroke(GDK_KEY_Escape); |
| 450 | #endif |
| 451 | return FALSE; |
| 452 | } |
| 453 | |
| 454 | void leaveFullScreenAndWaitUntilLeftFullScreen() |
| 455 | { |
| 456 | m_event = None; |
| 457 | g_idle_add(reinterpret_cast<GSourceFunc>(leaveFullScreenIdle), this); |
| 458 | g_main_loop_run(m_mainLoop); |
| 459 | } |
| 460 | |
| 461 | FullScreenEvent m_event; |
| 462 | }; |
| 463 | |
| 464 | #if ENABLE(FULLSCREEN_API) |
| 465 | static void testWebViewFullScreen(FullScreenClientTest* test, gconstpointer) |
| 466 | { |
| 467 | #if PLATFORM(GTK) |
| 468 | test->showInWindowAndWaitUntilMapped(); |
| 469 | #endif |
| 470 | test->loadHtml("<html><body>FullScreen test</body></html>" , 0); |
| 471 | test->waitUntilLoadFinished(); |
| 472 | test->requestFullScreenAndWaitUntilEnteredFullScreen(); |
| 473 | g_assert_cmpint(test->m_event, ==, FullScreenClientTest::Enter); |
| 474 | test->leaveFullScreenAndWaitUntilLeftFullScreen(); |
| 475 | g_assert_cmpint(test->m_event, ==, FullScreenClientTest::Leave); |
| 476 | } |
| 477 | #endif |
| 478 | |
| 479 | static void testWebViewCanShowMIMEType(WebViewTest* test, gconstpointer) |
| 480 | { |
| 481 | // Supported MIME types. |
| 482 | g_assert_true(webkit_web_view_can_show_mime_type(test->m_webView, "text/html" )); |
| 483 | g_assert_true(webkit_web_view_can_show_mime_type(test->m_webView, "text/plain" )); |
| 484 | g_assert_true(webkit_web_view_can_show_mime_type(test->m_webView, "image/jpeg" )); |
| 485 | |
| 486 | // Unsupported MIME types. |
| 487 | g_assert_false(webkit_web_view_can_show_mime_type(test->m_webView, "text/vcard" )); |
| 488 | g_assert_false(webkit_web_view_can_show_mime_type(test->m_webView, "application/zip" )); |
| 489 | g_assert_false(webkit_web_view_can_show_mime_type(test->m_webView, "application/octet-stream" )); |
| 490 | |
| 491 | #if ENABLE(NETSCAPE_PLUGIN_API) |
| 492 | // Plugins are only supported when enabled. |
| 493 | webkit_web_context_set_additional_plugins_directory(webkit_web_view_get_context(test->m_webView), WEBKIT_TEST_PLUGIN_DIR); |
| 494 | g_assert_true(webkit_web_view_can_show_mime_type(test->m_webView, "application/x-webkit-test-netscape" )); |
| 495 | webkit_settings_set_enable_plugins(webkit_web_view_get_settings(test->m_webView), FALSE); |
| 496 | g_assert_false(webkit_web_view_can_show_mime_type(test->m_webView, "application/x-webkit-test-netscape" )); |
| 497 | #endif |
| 498 | } |
| 499 | |
| 500 | #if PLATFORM(GTK) |
| 501 | class FormClientTest: public WebViewTest { |
| 502 | public: |
| 503 | MAKE_GLIB_TEST_FIXTURE(FormClientTest); |
| 504 | |
| 505 | static void submitFormCallback(WebKitWebView*, WebKitFormSubmissionRequest* request, FormClientTest* test) |
| 506 | { |
| 507 | test->submitForm(request); |
| 508 | } |
| 509 | |
| 510 | FormClientTest() |
| 511 | : m_submitPositionX(0) |
| 512 | , m_submitPositionY(0) |
| 513 | { |
| 514 | g_signal_connect(m_webView, "submit-form" , G_CALLBACK(submitFormCallback), this); |
| 515 | } |
| 516 | |
| 517 | ~FormClientTest() |
| 518 | { |
| 519 | g_signal_handlers_disconnect_matched(m_webView, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this); |
| 520 | } |
| 521 | |
| 522 | void submitForm(WebKitFormSubmissionRequest* request) |
| 523 | { |
| 524 | assertObjectIsDeletedWhenTestFinishes(G_OBJECT(request)); |
| 525 | m_request = request; |
| 526 | webkit_form_submission_request_submit(request); |
| 527 | quitMainLoop(); |
| 528 | } |
| 529 | |
| 530 | GHashTable* getTextFieldsAsHashTable() |
| 531 | { |
| 532 | #pragma GCC diagnostic push |
| 533 | #pragma GCC diagnostic ignored "-Wdeprecated-declarations" |
| 534 | return webkit_form_submission_request_get_text_fields(m_request.get()); |
| 535 | #pragma GCC diagnostic pop |
| 536 | } |
| 537 | |
| 538 | GPtrArray* getTextFieldNames() |
| 539 | { |
| 540 | GPtrArray* names; |
| 541 | webkit_form_submission_request_list_text_fields(m_request.get(), &names, nullptr); |
| 542 | return names; |
| 543 | } |
| 544 | |
| 545 | GPtrArray* getTextFieldValues() |
| 546 | { |
| 547 | GPtrArray* values; |
| 548 | webkit_form_submission_request_list_text_fields(m_request.get(), nullptr, &values); |
| 549 | return values; |
| 550 | } |
| 551 | |
| 552 | static gboolean doClickIdleCallback(FormClientTest* test) |
| 553 | { |
| 554 | test->clickMouseButton(test->m_submitPositionX, test->m_submitPositionY, 1); |
| 555 | return FALSE; |
| 556 | } |
| 557 | |
| 558 | void submitFormAtPosition(int x, int y) |
| 559 | { |
| 560 | m_submitPositionX = x; |
| 561 | m_submitPositionY = y; |
| 562 | g_idle_add(reinterpret_cast<GSourceFunc>(doClickIdleCallback), this); |
| 563 | g_main_loop_run(m_mainLoop); |
| 564 | } |
| 565 | |
| 566 | int m_submitPositionX; |
| 567 | int m_submitPositionY; |
| 568 | GRefPtr<WebKitFormSubmissionRequest> m_request; |
| 569 | }; |
| 570 | |
| 571 | static void testWebViewSubmitForm(FormClientTest* test, gconstpointer) |
| 572 | { |
| 573 | test->showInWindowAndWaitUntilMapped(); |
| 574 | |
| 575 | const char* formHTML = |
| 576 | "<html><body>" |
| 577 | " <form action='#'>" |
| 578 | " <input type='text' name='text1' value='value1'>" |
| 579 | " <input type='text' name='text2' value='value2'>" |
| 580 | " <input type='text' value='value3'>" |
| 581 | " <input type='text' name='text2'>" |
| 582 | " <input type='password' name='password' value='secret'>" |
| 583 | " <textarea cols='5' rows='5' name='textarea'>Text</textarea>" |
| 584 | " <input type='hidden' name='hidden1' value='hidden1'>" |
| 585 | " <input type='submit' value='Submit' style='position:absolute; left:1; top:1' size='10'>" |
| 586 | " </form>" |
| 587 | "</body></html>" ; |
| 588 | |
| 589 | test->loadHtml(formHTML, "file:///" ); |
| 590 | test->waitUntilLoadFinished(); |
| 591 | |
| 592 | test->submitFormAtPosition(5, 5); |
| 593 | GHashTable* tableValues = test->getTextFieldsAsHashTable(); |
| 594 | g_assert_nonnull(tableValues); |
| 595 | g_assert_cmpuint(g_hash_table_size(tableValues), ==, 4); |
| 596 | g_assert_cmpstr(static_cast<char*>(g_hash_table_lookup(tableValues, "text1" )), ==, "value1" ); |
| 597 | g_assert_cmpstr(static_cast<char*>(g_hash_table_lookup(tableValues, "" )), ==, "value3" ); |
| 598 | g_assert_cmpstr(static_cast<char*>(g_hash_table_lookup(tableValues, "text2" )), ==, "" ); |
| 599 | g_assert_cmpstr(static_cast<char*>(g_hash_table_lookup(tableValues, "password" )), ==, "secret" ); |
| 600 | |
| 601 | GPtrArray* names = test->getTextFieldNames(); |
| 602 | g_assert_nonnull(names); |
| 603 | g_assert_cmpuint(names->len, ==, 5); |
| 604 | g_assert_cmpstr(static_cast<char*>(names->pdata[0]), ==, "text1" ); |
| 605 | g_assert_cmpstr(static_cast<char*>(names->pdata[1]), ==, "text2" ); |
| 606 | g_assert_cmpstr(static_cast<char*>(names->pdata[2]), ==, "" ); |
| 607 | g_assert_cmpstr(static_cast<char*>(names->pdata[3]), ==, "text2" ); |
| 608 | g_assert_cmpstr(static_cast<char*>(names->pdata[4]), ==, "password" ); |
| 609 | |
| 610 | GPtrArray* values = test->getTextFieldValues(); |
| 611 | g_assert_nonnull(values); |
| 612 | g_assert_cmpuint(values->len, ==, 5); |
| 613 | g_assert_cmpstr(static_cast<char*>(values->pdata[0]), ==, "value1" ); |
| 614 | g_assert_cmpstr(static_cast<char*>(values->pdata[1]), ==, "value2" ); |
| 615 | g_assert_cmpstr(static_cast<char*>(values->pdata[2]), ==, "value3" ); |
| 616 | g_assert_cmpstr(static_cast<char*>(values->pdata[3]), ==, "" ); |
| 617 | g_assert_cmpstr(static_cast<char*>(values->pdata[4]), ==, "secret" ); |
| 618 | } |
| 619 | #endif // PLATFORM(GTK) |
| 620 | |
| 621 | class SaveWebViewTest: public WebViewTest { |
| 622 | public: |
| 623 | MAKE_GLIB_TEST_FIXTURE(SaveWebViewTest); |
| 624 | |
| 625 | SaveWebViewTest() |
| 626 | : m_tempDirectory(g_dir_make_tmp("WebKit2SaveViewTest-XXXXXX" , 0)) |
| 627 | { |
| 628 | } |
| 629 | |
| 630 | ~SaveWebViewTest() |
| 631 | { |
| 632 | if (G_IS_FILE(m_file.get())) |
| 633 | g_file_delete(m_file.get(), 0, 0); |
| 634 | |
| 635 | if (G_IS_INPUT_STREAM(m_inputStream.get())) |
| 636 | g_input_stream_close(m_inputStream.get(), 0, 0); |
| 637 | |
| 638 | if (m_tempDirectory) |
| 639 | g_rmdir(m_tempDirectory.get()); |
| 640 | } |
| 641 | |
| 642 | static void webViewSavedToStreamCallback(GObject* object, GAsyncResult* result, SaveWebViewTest* test) |
| 643 | { |
| 644 | GUniqueOutPtr<GError> error; |
| 645 | test->m_inputStream = adoptGRef(webkit_web_view_save_finish(test->m_webView, result, &error.outPtr())); |
| 646 | g_assert_true(G_IS_INPUT_STREAM(test->m_inputStream.get())); |
| 647 | g_assert_no_error(error.get()); |
| 648 | |
| 649 | test->quitMainLoop(); |
| 650 | } |
| 651 | |
| 652 | static void webViewSavedToFileCallback(GObject* object, GAsyncResult* result, SaveWebViewTest* test) |
| 653 | { |
| 654 | GUniqueOutPtr<GError> error; |
| 655 | g_assert_true(webkit_web_view_save_to_file_finish(test->m_webView, result, &error.outPtr())); |
| 656 | g_assert_no_error(error.get()); |
| 657 | |
| 658 | test->quitMainLoop(); |
| 659 | } |
| 660 | |
| 661 | void saveAndWaitForStream() |
| 662 | { |
| 663 | webkit_web_view_save(m_webView, WEBKIT_SAVE_MODE_MHTML, 0, reinterpret_cast<GAsyncReadyCallback>(webViewSavedToStreamCallback), this); |
| 664 | g_main_loop_run(m_mainLoop); |
| 665 | } |
| 666 | |
| 667 | void saveAndWaitForFile() |
| 668 | { |
| 669 | m_saveDestinationFilePath.reset(g_build_filename(m_tempDirectory.get(), "testWebViewSaveResult.mht" , NULL)); |
| 670 | m_file = adoptGRef(g_file_new_for_path(m_saveDestinationFilePath.get())); |
| 671 | webkit_web_view_save_to_file(m_webView, m_file.get(), WEBKIT_SAVE_MODE_MHTML, 0, reinterpret_cast<GAsyncReadyCallback>(webViewSavedToFileCallback), this); |
| 672 | g_main_loop_run(m_mainLoop); |
| 673 | } |
| 674 | |
| 675 | GUniquePtr<char> m_tempDirectory; |
| 676 | GUniquePtr<char> m_saveDestinationFilePath; |
| 677 | GRefPtr<GInputStream> m_inputStream; |
| 678 | GRefPtr<GFile> m_file; |
| 679 | }; |
| 680 | |
| 681 | static void testWebViewSave(SaveWebViewTest* test, gconstpointer) |
| 682 | { |
| 683 | test->loadHtml("<html>" |
| 684 | "<body>" |
| 685 | " <p>A paragraph with plain text</p>" |
| 686 | " <p>" |
| 687 | " A red box: <img src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3AYWDTMVwnSZnwAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAAAFklEQVQI12P8z8DAwMDAxMDAwMDAAAANHQEDK+mmyAAAAABJRU5ErkJggg=='></br>" |
| 688 | " A blue box: <img src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3AYWDTMvBHhALQAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAAAFklEQVQI12Nk4PnPwMDAxMDAwMDAAAALrwEPPIs1pgAAAABJRU5ErkJggg=='>" |
| 689 | " </p>" |
| 690 | "</body>" |
| 691 | "</html>" , 0); |
| 692 | test->waitUntilLoadFinished(); |
| 693 | |
| 694 | // Write to a file and to an input stream. |
| 695 | test->saveAndWaitForFile(); |
| 696 | test->saveAndWaitForStream(); |
| 697 | |
| 698 | // We should have exactly the same amount of bytes in the file |
| 699 | // than those coming from the GInputStream. We don't compare the |
| 700 | // strings read since the 'Date' field and the boundaries will be |
| 701 | // different on each case. MHTML functionality will be tested by |
| 702 | // Layout tests, so checking the amount of bytes is enough. |
| 703 | GUniqueOutPtr<GError> error; |
| 704 | gchar buffer[512] = { 0 }; |
| 705 | gssize readBytes = 0; |
| 706 | gssize totalBytesFromStream = 0; |
| 707 | while ((readBytes = g_input_stream_read(test->m_inputStream.get(), &buffer, 512, 0, &error.outPtr()))) { |
| 708 | g_assert_no_error(error.get()); |
| 709 | totalBytesFromStream += readBytes; |
| 710 | } |
| 711 | |
| 712 | // Check that the file exists and that it contains the same amount of bytes. |
| 713 | GRefPtr<GFileInfo> fileInfo = adoptGRef(g_file_query_info(test->m_file.get(), G_FILE_ATTRIBUTE_STANDARD_SIZE, static_cast<GFileQueryInfoFlags>(0), 0, 0)); |
| 714 | g_assert_cmpint(g_file_info_get_size(fileInfo.get()), ==, totalBytesFromStream); |
| 715 | } |
| 716 | |
| 717 | // To test page visibility API. Currently only 'visible', 'hidden' and 'prerender' states are implemented fully in WebCore. |
| 718 | // See also http://www.w3.org/TR/2011/WD-page-visibility-20110602/ and https://developers.google.com/chrome/whitepapers/pagevisibility |
| 719 | static void testWebViewPageVisibility(WebViewTest* test, gconstpointer) |
| 720 | { |
| 721 | test->loadHtml("<html><title></title>" |
| 722 | "<body><p>Test Web Page Visibility</p>" |
| 723 | "<script>" |
| 724 | "document.addEventListener(\"visibilitychange\", onVisibilityChange, false);" |
| 725 | "function onVisibilityChange() {" |
| 726 | " document.title = document.visibilityState;" |
| 727 | "}" |
| 728 | "</script>" |
| 729 | "</body></html>" , |
| 730 | 0); |
| 731 | |
| 732 | // Wait until the page is loaded. Initial visibility should be 'prerender'. |
| 733 | test->waitUntilLoadFinished(); |
| 734 | |
| 735 | GUniqueOutPtr<GError> error; |
| 736 | WebKitJavascriptResult* javascriptResult = test->runJavaScriptAndWaitUntilFinished("document.visibilityState;" , &error.outPtr()); |
| 737 | g_assert_nonnull(javascriptResult); |
| 738 | g_assert_no_error(error.get()); |
| 739 | GUniquePtr<char> valueString(WebViewTest::javascriptResultToCString(javascriptResult)); |
| 740 | g_assert_cmpstr(valueString.get(), ==, "prerender" ); |
| 741 | |
| 742 | javascriptResult = test->runJavaScriptAndWaitUntilFinished("document.hidden;" , &error.outPtr()); |
| 743 | g_assert_nonnull(javascriptResult); |
| 744 | g_assert_no_error(error.get()); |
| 745 | g_assert_true(WebViewTest::javascriptResultToBoolean(javascriptResult)); |
| 746 | |
| 747 | // Show the page. The visibility should be updated to 'visible'. |
| 748 | test->showInWindow(); |
| 749 | test->waitUntilTitleChanged(); |
| 750 | |
| 751 | javascriptResult = test->runJavaScriptAndWaitUntilFinished("document.visibilityState;" , &error.outPtr()); |
| 752 | g_assert_nonnull(javascriptResult); |
| 753 | g_assert_no_error(error.get()); |
| 754 | valueString.reset(WebViewTest::javascriptResultToCString(javascriptResult)); |
| 755 | g_assert_cmpstr(valueString.get(), ==, "visible" ); |
| 756 | |
| 757 | javascriptResult = test->runJavaScriptAndWaitUntilFinished("document.hidden;" , &error.outPtr()); |
| 758 | g_assert_nonnull(javascriptResult); |
| 759 | g_assert_no_error(error.get()); |
| 760 | g_assert_false(WebViewTest::javascriptResultToBoolean(javascriptResult)); |
| 761 | |
| 762 | // Hide the page. The visibility should be updated to 'hidden'. |
| 763 | test->hideView(); |
| 764 | test->waitUntilTitleChanged(); |
| 765 | |
| 766 | javascriptResult = test->runJavaScriptAndWaitUntilFinished("document.visibilityState;" , &error.outPtr()); |
| 767 | g_assert_nonnull(javascriptResult); |
| 768 | g_assert_no_error(error.get()); |
| 769 | valueString.reset(WebViewTest::javascriptResultToCString(javascriptResult)); |
| 770 | g_assert_cmpstr(valueString.get(), ==, "hidden" ); |
| 771 | |
| 772 | javascriptResult = test->runJavaScriptAndWaitUntilFinished("document.hidden;" , &error.outPtr()); |
| 773 | g_assert_nonnull(javascriptResult); |
| 774 | g_assert_no_error(error.get()); |
| 775 | g_assert_true(WebViewTest::javascriptResultToBoolean(javascriptResult)); |
| 776 | } |
| 777 | |
| 778 | #if PLATFORM(GTK) |
| 779 | class SnapshotWebViewTest: public WebViewTest { |
| 780 | public: |
| 781 | MAKE_GLIB_TEST_FIXTURE(SnapshotWebViewTest); |
| 782 | |
| 783 | static void onSnapshotCancelledReady(WebKitWebView* web_view, GAsyncResult* res, SnapshotWebViewTest* test) |
| 784 | { |
| 785 | GUniqueOutPtr<GError> error; |
| 786 | test->m_surface = webkit_web_view_get_snapshot_finish(web_view, res, &error.outPtr()); |
| 787 | g_assert_null(test->m_surface); |
| 788 | g_assert_error(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED); |
| 789 | test->quitMainLoop(); |
| 790 | } |
| 791 | |
| 792 | gboolean getSnapshotAndCancel() |
| 793 | { |
| 794 | if (m_surface) |
| 795 | cairo_surface_destroy(m_surface); |
| 796 | m_surface = 0; |
| 797 | GRefPtr<GCancellable> cancellable = adoptGRef(g_cancellable_new()); |
| 798 | webkit_web_view_get_snapshot(m_webView, WEBKIT_SNAPSHOT_REGION_VISIBLE, WEBKIT_SNAPSHOT_OPTIONS_NONE, cancellable.get(), reinterpret_cast<GAsyncReadyCallback>(onSnapshotCancelledReady), this); |
| 799 | g_cancellable_cancel(cancellable.get()); |
| 800 | g_main_loop_run(m_mainLoop); |
| 801 | |
| 802 | return true; |
| 803 | } |
| 804 | |
| 805 | }; |
| 806 | |
| 807 | static void testWebViewSnapshot(SnapshotWebViewTest* test, gconstpointer) |
| 808 | { |
| 809 | test->loadHtml("<html><head><style>html { width: 200px; height: 100px; } ::-webkit-scrollbar { display: none; }</style></head><body><p>Whatever</p></body></html>" , nullptr); |
| 810 | test->waitUntilLoadFinished(); |
| 811 | |
| 812 | // WEBKIT_SNAPSHOT_REGION_VISIBLE returns a null surface when the view is not visible. |
| 813 | cairo_surface_t* surface1 = test->getSnapshotAndWaitUntilReady(WEBKIT_SNAPSHOT_REGION_VISIBLE, WEBKIT_SNAPSHOT_OPTIONS_NONE); |
| 814 | g_assert_null(surface1); |
| 815 | |
| 816 | // WEBKIT_SNAPSHOT_REGION_FULL_DOCUMENT works even if the window is not visible. |
| 817 | surface1 = test->getSnapshotAndWaitUntilReady(WEBKIT_SNAPSHOT_REGION_FULL_DOCUMENT, WEBKIT_SNAPSHOT_OPTIONS_NONE); |
| 818 | g_assert_nonnull(surface1); |
| 819 | g_assert_cmpuint(cairo_surface_get_type(surface1), ==, CAIRO_SURFACE_TYPE_IMAGE); |
| 820 | g_assert_cmpint(cairo_image_surface_get_width(surface1), ==, 200); |
| 821 | g_assert_cmpint(cairo_image_surface_get_height(surface1), ==, 100); |
| 822 | |
| 823 | // Show the WebView in a popup widow of 50x50 and try again with WEBKIT_SNAPSHOT_REGION_VISIBLE. |
| 824 | test->showInWindowAndWaitUntilMapped(GTK_WINDOW_POPUP, 50, 50); |
| 825 | surface1 = cairo_surface_reference(test->getSnapshotAndWaitUntilReady(WEBKIT_SNAPSHOT_REGION_VISIBLE, WEBKIT_SNAPSHOT_OPTIONS_NONE)); |
| 826 | g_assert_nonnull(surface1); |
| 827 | g_assert_cmpuint(cairo_surface_get_type(surface1), ==, CAIRO_SURFACE_TYPE_IMAGE); |
| 828 | g_assert_cmpint(cairo_image_surface_get_width(surface1), ==, 50); |
| 829 | g_assert_cmpint(cairo_image_surface_get_height(surface1), ==, 50); |
| 830 | |
| 831 | // Select all text in the WebView, request a snapshot ignoring selection. |
| 832 | test->selectAll(); |
| 833 | cairo_surface_t* surface2 = test->getSnapshotAndWaitUntilReady(WEBKIT_SNAPSHOT_REGION_VISIBLE, WEBKIT_SNAPSHOT_OPTIONS_NONE); |
| 834 | g_assert_nonnull(surface2); |
| 835 | g_assert_true(Test::cairoSurfacesEqual(surface1, surface2)); |
| 836 | |
| 837 | // Request a new snapshot, including the selection this time. The size should be the same but the result |
| 838 | // must be different to the one previously obtained. |
| 839 | surface2 = test->getSnapshotAndWaitUntilReady(WEBKIT_SNAPSHOT_REGION_VISIBLE, WEBKIT_SNAPSHOT_OPTIONS_INCLUDE_SELECTION_HIGHLIGHTING); |
| 840 | g_assert_cmpuint(cairo_surface_get_type(surface2), ==, CAIRO_SURFACE_TYPE_IMAGE); |
| 841 | g_assert_cmpint(cairo_image_surface_get_width(surface1), ==, cairo_image_surface_get_width(surface2)); |
| 842 | g_assert_cmpint(cairo_image_surface_get_height(surface1), ==, cairo_image_surface_get_height(surface2)); |
| 843 | g_assert_false(Test::cairoSurfacesEqual(surface1, surface2)); |
| 844 | |
| 845 | // Get a snpashot with a transparent background, the result must be different. |
| 846 | surface2 = test->getSnapshotAndWaitUntilReady(WEBKIT_SNAPSHOT_REGION_VISIBLE, WEBKIT_SNAPSHOT_OPTIONS_TRANSPARENT_BACKGROUND); |
| 847 | g_assert_cmpuint(cairo_surface_get_type(surface2), ==, CAIRO_SURFACE_TYPE_IMAGE); |
| 848 | g_assert_cmpint(cairo_image_surface_get_width(surface1), ==, cairo_image_surface_get_width(surface2)); |
| 849 | g_assert_cmpint(cairo_image_surface_get_height(surface1), ==, cairo_image_surface_get_height(surface2)); |
| 850 | g_assert_false(Test::cairoSurfacesEqual(surface1, surface2)); |
| 851 | cairo_surface_destroy(surface1); |
| 852 | |
| 853 | // Test that cancellation works. |
| 854 | g_assert_true(test->getSnapshotAndCancel()); |
| 855 | } |
| 856 | #endif // PLATFORM(GTK) |
| 857 | |
| 858 | #if ENABLE(NOTIFICATIONS) |
| 859 | class NotificationWebViewTest: public WebViewTest { |
| 860 | public: |
| 861 | MAKE_GLIB_TEST_FIXTURE_WITH_SETUP_TEARDOWN(NotificationWebViewTest, setup, teardown); |
| 862 | |
| 863 | static void setup() |
| 864 | { |
| 865 | WebViewTest::shouldInitializeWebViewInConstructor = false; |
| 866 | } |
| 867 | |
| 868 | static void teardown() |
| 869 | { |
| 870 | WebViewTest::shouldInitializeWebViewInConstructor = true; |
| 871 | } |
| 872 | |
| 873 | enum NotificationEvent { |
| 874 | None, |
| 875 | Permission, |
| 876 | Shown, |
| 877 | Clicked, |
| 878 | OnClicked, |
| 879 | Closed, |
| 880 | OnClosed, |
| 881 | }; |
| 882 | |
| 883 | static gboolean permissionRequestCallback(WebKitWebView*, WebKitPermissionRequest *request, NotificationWebViewTest* test) |
| 884 | { |
| 885 | g_assert_true(WEBKIT_IS_NOTIFICATION_PERMISSION_REQUEST(request)); |
| 886 | g_assert_true(test->m_isExpectingPermissionRequest); |
| 887 | test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(request)); |
| 888 | |
| 889 | test->m_event = Permission; |
| 890 | |
| 891 | webkit_permission_request_allow(request); |
| 892 | |
| 893 | g_main_loop_quit(test->m_mainLoop); |
| 894 | |
| 895 | return TRUE; |
| 896 | } |
| 897 | |
| 898 | static gboolean notificationClosedCallback(WebKitNotification* notification, NotificationWebViewTest* test) |
| 899 | { |
| 900 | g_assert_true(test->m_notification == notification); |
| 901 | test->m_notification = nullptr; |
| 902 | test->m_event = Closed; |
| 903 | if (g_main_loop_is_running(test->m_mainLoop)) |
| 904 | g_main_loop_quit(test->m_mainLoop); |
| 905 | return TRUE; |
| 906 | } |
| 907 | |
| 908 | static gboolean notificationClickedCallback(WebKitNotification* notification, NotificationWebViewTest* test) |
| 909 | { |
| 910 | g_assert_true(test->m_notification == notification); |
| 911 | test->m_event = Clicked; |
| 912 | return TRUE; |
| 913 | } |
| 914 | |
| 915 | static gboolean showNotificationCallback(WebKitWebView*, WebKitNotification* notification, NotificationWebViewTest* test) |
| 916 | { |
| 917 | g_assert_null(test->m_notification); |
| 918 | test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(notification)); |
| 919 | test->m_notification = notification; |
| 920 | g_signal_connect(notification, "closed" , G_CALLBACK(notificationClosedCallback), test); |
| 921 | g_signal_connect(notification, "clicked" , G_CALLBACK(notificationClickedCallback), test); |
| 922 | test->m_event = Shown; |
| 923 | g_main_loop_quit(test->m_mainLoop); |
| 924 | return TRUE; |
| 925 | } |
| 926 | |
| 927 | static void notificationsMessageReceivedCallback(WebKitUserContentManager* userContentManager, WebKitJavascriptResult* javascriptResult, NotificationWebViewTest* test) |
| 928 | { |
| 929 | GUniquePtr<char> valueString(WebViewTest::javascriptResultToCString(javascriptResult)); |
| 930 | |
| 931 | if (g_str_equal(valueString.get(), "clicked" )) |
| 932 | test->m_event = OnClicked; |
| 933 | else if (g_str_equal(valueString.get(), "closed" )) |
| 934 | test->m_event = OnClosed; |
| 935 | |
| 936 | g_main_loop_quit(test->m_mainLoop); |
| 937 | } |
| 938 | |
| 939 | void initialize() |
| 940 | { |
| 941 | initializeWebView(); |
| 942 | g_signal_connect(m_webView, "permission-request" , G_CALLBACK(permissionRequestCallback), this); |
| 943 | g_signal_connect(m_webView, "show-notification" , G_CALLBACK(showNotificationCallback), this); |
| 944 | webkit_user_content_manager_register_script_message_handler(m_userContentManager.get(), "notifications" ); |
| 945 | g_signal_connect(m_userContentManager.get(), "script-message-received::notifications" , G_CALLBACK(notificationsMessageReceivedCallback), this); |
| 946 | } |
| 947 | |
| 948 | ~NotificationWebViewTest() |
| 949 | { |
| 950 | g_signal_handlers_disconnect_matched(m_webView, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this); |
| 951 | g_signal_handlers_disconnect_matched(m_userContentManager.get(), G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this); |
| 952 | webkit_user_content_manager_unregister_script_message_handler(m_userContentManager.get(), "notifications" ); |
| 953 | } |
| 954 | |
| 955 | bool hasPermission() |
| 956 | { |
| 957 | auto* result = runJavaScriptAndWaitUntilFinished("Notification.permission;" , nullptr); |
| 958 | g_assert_nonnull(result); |
| 959 | GUniquePtr<char> value(javascriptResultToCString(result)); |
| 960 | return !g_strcmp0(value.get(), "granted" ); |
| 961 | } |
| 962 | |
| 963 | void requestPermissionAndWaitUntilGiven() |
| 964 | { |
| 965 | m_event = None; |
| 966 | m_isExpectingPermissionRequest = true; |
| 967 | webkit_web_view_run_javascript(m_webView, "Notification.requestPermission();" , nullptr, nullptr, nullptr); |
| 968 | g_main_loop_run(m_mainLoop); |
| 969 | } |
| 970 | |
| 971 | void requestNotificationAndWaitUntilShown(const char* title, const char* body) |
| 972 | { |
| 973 | m_event = None; |
| 974 | |
| 975 | GUniquePtr<char> jscode(g_strdup_printf("n = new Notification('%s', { body: '%s'});" , title, body)); |
| 976 | webkit_web_view_run_javascript(m_webView, jscode.get(), nullptr, nullptr, nullptr); |
| 977 | |
| 978 | g_main_loop_run(m_mainLoop); |
| 979 | } |
| 980 | |
| 981 | void requestNotificationAndWaitUntilShown(const char* title, const char* body, const char* tag) |
| 982 | { |
| 983 | m_event = None; |
| 984 | |
| 985 | GUniquePtr<char> jscode(g_strdup_printf("n = new Notification('%s', { body: '%s', tag: '%s'});" , title, body, tag)); |
| 986 | webkit_web_view_run_javascript(m_webView, jscode.get(), nullptr, nullptr, nullptr); |
| 987 | |
| 988 | g_main_loop_run(m_mainLoop); |
| 989 | } |
| 990 | |
| 991 | void clickNotificationAndWaitUntilClicked() |
| 992 | { |
| 993 | m_event = None; |
| 994 | runJavaScriptAndWaitUntilFinished("n.onclick = function() { window.webkit.messageHandlers.notifications.postMessage('clicked'); }" , nullptr); |
| 995 | webkit_notification_clicked(m_notification); |
| 996 | g_assert_cmpint(m_event, ==, Clicked); |
| 997 | g_main_loop_run(m_mainLoop); |
| 998 | } |
| 999 | |
| 1000 | void closeNotificationAndWaitUntilClosed() |
| 1001 | { |
| 1002 | m_event = None; |
| 1003 | webkit_web_view_run_javascript(m_webView, "n.close()" , nullptr, nullptr, nullptr); |
| 1004 | g_main_loop_run(m_mainLoop); |
| 1005 | } |
| 1006 | |
| 1007 | void closeNotificationAndWaitUntilOnClosed() |
| 1008 | { |
| 1009 | g_assert_nonnull(m_notification); |
| 1010 | m_event = None; |
| 1011 | runJavaScriptAndWaitUntilFinished("n.onclose = function() { window.webkit.messageHandlers.notifications.postMessage('closed'); }" , nullptr); |
| 1012 | webkit_notification_close(m_notification); |
| 1013 | g_assert_cmpint(m_event, ==, Closed); |
| 1014 | g_main_loop_run(m_mainLoop); |
| 1015 | } |
| 1016 | |
| 1017 | NotificationEvent m_event { None }; |
| 1018 | WebKitNotification* m_notification { nullptr }; |
| 1019 | bool m_isExpectingPermissionRequest { false }; |
| 1020 | bool m_hasPermission { false }; |
| 1021 | }; |
| 1022 | |
| 1023 | static void testWebViewNotification(NotificationWebViewTest* test, gconstpointer) |
| 1024 | { |
| 1025 | test->initialize(); |
| 1026 | |
| 1027 | // Notifications don't work with local or special schemes. |
| 1028 | test->loadURI(gServer->getURIForPath("/" ).data()); |
| 1029 | test->waitUntilLoadFinished(); |
| 1030 | g_assert_false(test->hasPermission()); |
| 1031 | |
| 1032 | test->requestPermissionAndWaitUntilGiven(); |
| 1033 | g_assert_cmpint(test->m_event, ==, NotificationWebViewTest::Permission); |
| 1034 | g_assert_true(test->hasPermission()); |
| 1035 | |
| 1036 | static const char* title = "This is a notification" ; |
| 1037 | static const char* body = "This is the body." ; |
| 1038 | static const char* tag = "This is the tag." ; |
| 1039 | test->requestNotificationAndWaitUntilShown(title, body, tag); |
| 1040 | |
| 1041 | g_assert_cmpint(test->m_event, ==, NotificationWebViewTest::Shown); |
| 1042 | g_assert_nonnull(test->m_notification); |
| 1043 | g_assert_cmpstr(webkit_notification_get_title(test->m_notification), ==, title); |
| 1044 | g_assert_cmpstr(webkit_notification_get_body(test->m_notification), ==, body); |
| 1045 | g_assert_cmpstr(webkit_notification_get_tag(test->m_notification), ==, tag); |
| 1046 | |
| 1047 | test->clickNotificationAndWaitUntilClicked(); |
| 1048 | g_assert_cmpint(test->m_event, ==, NotificationWebViewTest::OnClicked); |
| 1049 | |
| 1050 | test->closeNotificationAndWaitUntilClosed(); |
| 1051 | g_assert_cmpint(test->m_event, ==, NotificationWebViewTest::Closed); |
| 1052 | |
| 1053 | test->requestNotificationAndWaitUntilShown(title, body); |
| 1054 | g_assert_cmpint(test->m_event, ==, NotificationWebViewTest::Shown); |
| 1055 | g_assert_cmpstr(webkit_notification_get_tag(test->m_notification), ==, nullptr); |
| 1056 | |
| 1057 | test->closeNotificationAndWaitUntilOnClosed(); |
| 1058 | g_assert_cmpint(test->m_event, ==, NotificationWebViewTest::OnClosed); |
| 1059 | |
| 1060 | // The first notification should be closed automatically because the tag is |
| 1061 | // the same. It will crash in showNotificationCallback on failure. |
| 1062 | test->requestNotificationAndWaitUntilShown(title, body, tag); |
| 1063 | test->requestNotificationAndWaitUntilShown(title, body, tag); |
| 1064 | g_assert_cmpint(test->m_event, ==, NotificationWebViewTest::Shown); |
| 1065 | } |
| 1066 | |
| 1067 | static void setInitialNotificationPermissionsAllowedCallback(WebKitWebContext* context, NotificationWebViewTest* test) |
| 1068 | { |
| 1069 | GUniquePtr<char> baseURI(soup_uri_to_string(gServer->baseURI(), FALSE)); |
| 1070 | GList* allowedOrigins = g_list_prepend(nullptr, webkit_security_origin_new_for_uri(baseURI.get())); |
| 1071 | webkit_web_context_initialize_notification_permissions(test->m_webContext.get(), allowedOrigins, nullptr); |
| 1072 | g_list_free_full(allowedOrigins, reinterpret_cast<GDestroyNotify>(webkit_security_origin_unref)); |
| 1073 | } |
| 1074 | |
| 1075 | static void setInitialNotificationPermissionsDisallowedCallback(WebKitWebContext* context, NotificationWebViewTest* test) |
| 1076 | { |
| 1077 | GUniquePtr<char> baseURI(soup_uri_to_string(gServer->baseURI(), FALSE)); |
| 1078 | GList* disallowedOrigins = g_list_prepend(nullptr, webkit_security_origin_new_for_uri(baseURI.get())); |
| 1079 | webkit_web_context_initialize_notification_permissions(test->m_webContext.get(), nullptr, disallowedOrigins); |
| 1080 | g_list_free_full(disallowedOrigins, reinterpret_cast<GDestroyNotify>(webkit_security_origin_unref)); |
| 1081 | } |
| 1082 | |
| 1083 | static void testWebViewNotificationInitialPermissionAllowed(NotificationWebViewTest* test, gconstpointer) |
| 1084 | { |
| 1085 | g_signal_connect(test->m_webContext.get(), "initialize-notification-permissions" , G_CALLBACK(setInitialNotificationPermissionsAllowedCallback), test); |
| 1086 | test->initialize(); |
| 1087 | |
| 1088 | test->loadURI(gServer->getURIForPath("/" ).data()); |
| 1089 | test->waitUntilLoadFinished(); |
| 1090 | g_assert_true(test->hasPermission()); |
| 1091 | |
| 1092 | test->requestNotificationAndWaitUntilShown("This is a notification" , "This is the body." ); |
| 1093 | g_assert_cmpint(test->m_event, ==, NotificationWebViewTest::Shown); |
| 1094 | } |
| 1095 | |
| 1096 | static void testWebViewNotificationInitialPermissionDisallowed(NotificationWebViewTest* test, gconstpointer) |
| 1097 | { |
| 1098 | g_signal_connect(test->m_webContext.get(), "initialize-notification-permissions" , G_CALLBACK(setInitialNotificationPermissionsDisallowedCallback), test); |
| 1099 | test->initialize(); |
| 1100 | |
| 1101 | test->loadURI(gServer->getURIForPath("/" ).data()); |
| 1102 | test->waitUntilLoadFinished(); |
| 1103 | g_assert_false(test->hasPermission()); |
| 1104 | } |
| 1105 | #endif // ENABLE(NOTIFICATIONS) |
| 1106 | |
| 1107 | static void testWebViewIsPlayingAudio(IsPlayingAudioWebViewTest* test, gconstpointer) |
| 1108 | { |
| 1109 | #if PLATFORM(GTK) |
| 1110 | // The web view must be realized for the video to start playback and |
| 1111 | // trigger changes in WebKitWebView::is-playing-audio. |
| 1112 | test->showInWindowAndWaitUntilMapped(GTK_WINDOW_TOPLEVEL); |
| 1113 | #endif |
| 1114 | |
| 1115 | // Initially, web views should always report no audio being played. |
| 1116 | g_assert_false(webkit_web_view_is_playing_audio(test->m_webView)); |
| 1117 | |
| 1118 | GUniquePtr<char> resourcePath(g_build_filename(Test::getResourcesDir(Test::WebKit2Resources).data(), "file-with-video.html" , nullptr)); |
| 1119 | GUniquePtr<char> resourceURL(g_filename_to_uri(resourcePath.get(), nullptr, nullptr)); |
| 1120 | webkit_web_view_load_uri(test->m_webView, resourceURL.get()); |
| 1121 | test->waitUntilLoadFinished(); |
| 1122 | g_assert_false(webkit_web_view_is_playing_audio(test->m_webView)); |
| 1123 | |
| 1124 | test->runJavaScriptAndWaitUntilFinished("playVideo();" , nullptr); |
| 1125 | if (!webkit_web_view_is_playing_audio(test->m_webView)) |
| 1126 | test->waitUntilIsPlayingAudioChanged(); |
| 1127 | g_assert_true(webkit_web_view_is_playing_audio(test->m_webView)); |
| 1128 | |
| 1129 | // Pause the video, and check again. |
| 1130 | test->runJavaScriptAndWaitUntilFinished("document.getElementById('test-video').pause();" , nullptr); |
| 1131 | if (webkit_web_view_is_playing_audio(test->m_webView)) |
| 1132 | test->waitUntilIsPlayingAudioChanged(); |
| 1133 | g_assert_false(webkit_web_view_is_playing_audio(test->m_webView)); |
| 1134 | } |
| 1135 | |
| 1136 | static void testWebViewBackgroundColor(WebViewTest* test, gconstpointer) |
| 1137 | { |
| 1138 | #if PLATFORM(GTK) |
| 1139 | #define ColorType GdkRGBA |
| 1140 | #elif PLATFORM(WPE) |
| 1141 | #define ColorType WebKitColor |
| 1142 | #endif |
| 1143 | |
| 1144 | // White is the default background. |
| 1145 | ColorType rgba; |
| 1146 | webkit_web_view_get_background_color(test->m_webView, &rgba); |
| 1147 | g_assert_cmpfloat(rgba.red, ==, 1); |
| 1148 | g_assert_cmpfloat(rgba.green, ==, 1); |
| 1149 | g_assert_cmpfloat(rgba.blue, ==, 1); |
| 1150 | g_assert_cmpfloat(rgba.alpha, ==, 1); |
| 1151 | |
| 1152 | // Set a different (semi-transparent red). |
| 1153 | rgba.red = 1; |
| 1154 | rgba.green = 0; |
| 1155 | rgba.blue = 0; |
| 1156 | rgba.alpha = 0.5; |
| 1157 | webkit_web_view_set_background_color(test->m_webView, &rgba); |
| 1158 | g_assert_cmpfloat(rgba.red, ==, 1); |
| 1159 | g_assert_cmpfloat(rgba.green, ==, 0); |
| 1160 | g_assert_cmpfloat(rgba.blue, ==, 0); |
| 1161 | g_assert_cmpfloat(rgba.alpha, ==, 0.5); |
| 1162 | |
| 1163 | #if PLATFORM(WPE) |
| 1164 | ColorType color; |
| 1165 | g_assert(webkit_color_parse(&color, "red" )); |
| 1166 | g_assert_cmpfloat(color.red, ==, 1); |
| 1167 | webkit_web_view_set_background_color(test->m_webView, &color); |
| 1168 | webkit_web_view_get_background_color(test->m_webView, &rgba); |
| 1169 | g_assert_cmpfloat(rgba.red, ==, 1); |
| 1170 | g_assert_cmpfloat(rgba.green, ==, 0); |
| 1171 | g_assert_cmpfloat(rgba.blue, ==, 0); |
| 1172 | g_assert_cmpfloat(rgba.alpha, ==, 1); |
| 1173 | #endif |
| 1174 | |
| 1175 | // The actual rendering can't be tested using unit tests, use |
| 1176 | // MiniBrowser --bg-color="<color-value>" for manually testing this API. |
| 1177 | } |
| 1178 | |
| 1179 | #if PLATFORM(GTK) |
| 1180 | static void testWebViewPreferredSize(WebViewTest* test, gconstpointer) |
| 1181 | { |
| 1182 | test->loadHtml("<html style='width: 325px; height: 615px'></html>" , nullptr); |
| 1183 | test->waitUntilLoadFinished(); |
| 1184 | test->showInWindowAndWaitUntilMapped(); |
| 1185 | GtkRequisition minimunSize, naturalSize; |
| 1186 | gtk_widget_get_preferred_size(GTK_WIDGET(test->m_webView), &minimunSize, &naturalSize); |
| 1187 | g_assert_cmpint(minimunSize.width, ==, 0); |
| 1188 | g_assert_cmpint(minimunSize.height, ==, 0); |
| 1189 | g_assert_cmpint(naturalSize.width, ==, 325); |
| 1190 | g_assert_cmpint(naturalSize.height, ==, 615); |
| 1191 | } |
| 1192 | #endif |
| 1193 | |
| 1194 | class WebViewTitleTest: public WebViewTest { |
| 1195 | public: |
| 1196 | MAKE_GLIB_TEST_FIXTURE(WebViewTitleTest); |
| 1197 | |
| 1198 | static void titleChangedCallback(WebKitWebView* view, GParamSpec*, WebViewTitleTest* test) |
| 1199 | { |
| 1200 | test->m_webViewTitles.append(webkit_web_view_get_title(view)); |
| 1201 | } |
| 1202 | |
| 1203 | WebViewTitleTest() |
| 1204 | { |
| 1205 | g_signal_connect(m_webView, "notify::title" , G_CALLBACK(titleChangedCallback), this); |
| 1206 | } |
| 1207 | |
| 1208 | Vector<CString> m_webViewTitles; |
| 1209 | }; |
| 1210 | |
| 1211 | static void testWebViewTitleChange(WebViewTitleTest* test, gconstpointer) |
| 1212 | { |
| 1213 | g_assert_cmpint(test->m_webViewTitles.size(), ==, 0); |
| 1214 | |
| 1215 | test->loadHtml("<head><title>Page Title</title></head>" , nullptr); |
| 1216 | test->waitUntilLoadFinished(); |
| 1217 | g_assert_cmpint(test->m_webViewTitles.size(), ==, 1); |
| 1218 | g_assert_cmpstr(test->m_webViewTitles[0].data(), ==, "Page Title" ); |
| 1219 | |
| 1220 | test->loadHtml("<head><title>Another Page Title</title></head>" , nullptr); |
| 1221 | test->waitUntilLoadFinished(); |
| 1222 | g_assert_cmpint(test->m_webViewTitles.size(), ==, 3); |
| 1223 | /* Page title should be immediately unset when loading a new page. */ |
| 1224 | g_assert_cmpstr(test->m_webViewTitles[1].data(), ==, "" ); |
| 1225 | g_assert_cmpstr(test->m_webViewTitles[2].data(), ==, "Another Page Title" ); |
| 1226 | |
| 1227 | test->loadHtml("<p>This page has no title!</p>" , nullptr); |
| 1228 | test->waitUntilLoadFinished(); |
| 1229 | g_assert_cmpint(test->m_webViewTitles.size(), ==, 4); |
| 1230 | g_assert_cmpstr(test->m_webViewTitles[3].data(), ==, "" ); |
| 1231 | |
| 1232 | test->loadHtml("<script>document.title = 'one'; document.title = 'two'; document.title = 'three';</script>" , nullptr); |
| 1233 | test->waitUntilLoadFinished(); |
| 1234 | g_assert_cmpint(test->m_webViewTitles.size(), ==, 7); |
| 1235 | g_assert_cmpstr(test->m_webViewTitles[4].data(), ==, "one" ); |
| 1236 | g_assert_cmpstr(test->m_webViewTitles[5].data(), ==, "two" ); |
| 1237 | g_assert_cmpstr(test->m_webViewTitles[6].data(), ==, "three" ); |
| 1238 | } |
| 1239 | |
| 1240 | #if PLATFORM(WPE) |
| 1241 | class FrameDisplayedTest: public WebViewTest { |
| 1242 | public: |
| 1243 | MAKE_GLIB_TEST_FIXTURE(FrameDisplayedTest); |
| 1244 | |
| 1245 | static void titleChangedCallback(WebKitWebView* view, GParamSpec*, WebViewTitleTest* test) |
| 1246 | { |
| 1247 | test->m_webViewTitles.append(webkit_web_view_get_title(view)); |
| 1248 | } |
| 1249 | |
| 1250 | FrameDisplayedTest() |
| 1251 | : m_id(webkit_web_view_add_frame_displayed_callback(m_webView, [](WebKitWebView*, gpointer userData) { |
| 1252 | auto* test = static_cast<FrameDisplayedTest*>(userData); |
| 1253 | if (!test->m_maxFrames) |
| 1254 | return; |
| 1255 | |
| 1256 | if (++test->m_frameCounter == test->m_maxFrames) |
| 1257 | RunLoop::main().dispatch([test] { test->quitMainLoop(); }); |
| 1258 | }, this, nullptr)) |
| 1259 | { |
| 1260 | g_assert_cmpuint(m_id, >, 0); |
| 1261 | } |
| 1262 | |
| 1263 | ~FrameDisplayedTest() |
| 1264 | { |
| 1265 | webkit_web_view_remove_frame_displayed_callback(m_webView, m_id); |
| 1266 | } |
| 1267 | |
| 1268 | void waitUntilFramesDisplayed(unsigned framesCount = 1) |
| 1269 | { |
| 1270 | m_maxFrames = framesCount; |
| 1271 | m_frameCounter = 0; |
| 1272 | g_main_loop_run(m_mainLoop); |
| 1273 | } |
| 1274 | |
| 1275 | unsigned m_id { 0 }; |
| 1276 | unsigned m_frameCounter { 0 }; |
| 1277 | unsigned m_maxFrames { 0 }; |
| 1278 | }; |
| 1279 | |
| 1280 | static void testWebViewFrameDisplayed(FrameDisplayedTest* test, gconstpointer) |
| 1281 | { |
| 1282 | test->showInWindow(); |
| 1283 | |
| 1284 | test->loadHtml("<html></html>" , nullptr); |
| 1285 | test->waitUntilFramesDisplayed(); |
| 1286 | |
| 1287 | test->loadHtml("<html><head><style>@keyframes fadeIn { from { opacity: 0; } }</style></head><p style='animation: fadeIn 1s infinite alternate;'>Foo</p></html>" , nullptr); |
| 1288 | test->waitUntilFramesDisplayed(10); |
| 1289 | |
| 1290 | bool secondCallbackCalled = false; |
| 1291 | auto id = webkit_web_view_add_frame_displayed_callback(test->m_webView, [](WebKitWebView*, gpointer userData) { |
| 1292 | auto* secondCallbackCalled = static_cast<bool*>(userData); |
| 1293 | *secondCallbackCalled = true; |
| 1294 | }, &secondCallbackCalled, nullptr); |
| 1295 | test->waitUntilFramesDisplayed(); |
| 1296 | g_assert_true(secondCallbackCalled); |
| 1297 | |
| 1298 | secondCallbackCalled = false; |
| 1299 | webkit_web_view_remove_frame_displayed_callback(test->m_webView, id); |
| 1300 | test->waitUntilFramesDisplayed(); |
| 1301 | g_assert_false(secondCallbackCalled); |
| 1302 | |
| 1303 | id = webkit_web_view_add_frame_displayed_callback(test->m_webView, [](WebKitWebView* webView, gpointer userData) { |
| 1304 | auto* id = static_cast<unsigned*>(userData); |
| 1305 | webkit_web_view_remove_frame_displayed_callback(webView, *id); |
| 1306 | }, &id, [](gpointer userData) { |
| 1307 | auto* id = static_cast<unsigned*>(userData); |
| 1308 | *id = 0; |
| 1309 | }); |
| 1310 | test->waitUntilFramesDisplayed(); |
| 1311 | g_assert_cmpuint(id, ==, 0); |
| 1312 | |
| 1313 | auto id2 = webkit_web_view_add_frame_displayed_callback(test->m_webView, [](WebKitWebView* webView, gpointer userData) { |
| 1314 | auto* id = static_cast<unsigned*>(userData); |
| 1315 | if (*id) { |
| 1316 | webkit_web_view_remove_frame_displayed_callback(webView, *id); |
| 1317 | *id = 0; |
| 1318 | } |
| 1319 | }, &id, nullptr); |
| 1320 | |
| 1321 | secondCallbackCalled = false; |
| 1322 | id = webkit_web_view_add_frame_displayed_callback(test->m_webView, [](WebKitWebView* webView, gpointer userData) { |
| 1323 | auto* secondCallbackCalled = static_cast<bool*>(userData); |
| 1324 | *secondCallbackCalled = true; |
| 1325 | }, &secondCallbackCalled, nullptr); |
| 1326 | test->waitUntilFramesDisplayed(); |
| 1327 | g_assert_cmpuint(id, ==, 0); |
| 1328 | g_assert_false(secondCallbackCalled); |
| 1329 | |
| 1330 | webkit_web_view_remove_frame_displayed_callback(test->m_webView, id2); |
| 1331 | } |
| 1332 | #endif |
| 1333 | |
| 1334 | static void serverCallback(SoupServer* server, SoupMessage* message, const char* path, GHashTable*, SoupClientContext*, gpointer) |
| 1335 | { |
| 1336 | if (message->method != SOUP_METHOD_GET) { |
| 1337 | soup_message_set_status(message, SOUP_STATUS_NOT_IMPLEMENTED); |
| 1338 | return; |
| 1339 | } |
| 1340 | |
| 1341 | if (g_str_equal(path, "/" )) { |
| 1342 | soup_message_set_status(message, SOUP_STATUS_OK); |
| 1343 | soup_message_body_complete(message->response_body); |
| 1344 | } else |
| 1345 | soup_message_set_status(message, SOUP_STATUS_NOT_FOUND); |
| 1346 | } |
| 1347 | |
| 1348 | void beforeAll() |
| 1349 | { |
| 1350 | gServer = new WebKitTestServer(); |
| 1351 | gServer->run(serverCallback); |
| 1352 | |
| 1353 | WebViewTest::add("WebKitWebView" , "web-context" , testWebViewWebContext); |
| 1354 | WebViewTest::add("WebKitWebView" , "web-context-lifetime" , testWebViewWebContextLifetime); |
| 1355 | WebViewTest::add("WebKitWebView" , "close-quickly" , testWebViewCloseQuickly); |
| 1356 | #if PLATFORM(WPE) |
| 1357 | Test::add("WebKitWebView" , "backend" , testWebViewWebBackend); |
| 1358 | #endif |
| 1359 | WebViewTest::add("WebKitWebView" , "ephemeral" , testWebViewEphemeral); |
| 1360 | WebViewTest::add("WebKitWebView" , "custom-charset" , testWebViewCustomCharset); |
| 1361 | WebViewTest::add("WebKitWebView" , "settings" , testWebViewSettings); |
| 1362 | WebViewTest::add("WebKitWebView" , "zoom-level" , testWebViewZoomLevel); |
| 1363 | WebViewTest::add("WebKitWebView" , "run-javascript" , testWebViewRunJavaScript); |
| 1364 | #if ENABLE(FULLSCREEN_API) |
| 1365 | FullScreenClientTest::add("WebKitWebView" , "fullscreen" , testWebViewFullScreen); |
| 1366 | #endif |
| 1367 | WebViewTest::add("WebKitWebView" , "can-show-mime-type" , testWebViewCanShowMIMEType); |
| 1368 | // FIXME: implement mouse clicks in WPE. |
| 1369 | #if PLATFORM(GTK) |
| 1370 | FormClientTest::add("WebKitWebView" , "submit-form" , testWebViewSubmitForm); |
| 1371 | #endif |
| 1372 | SaveWebViewTest::add("WebKitWebView" , "save" , testWebViewSave); |
| 1373 | // FIXME: View is initially visible in WPE and has a fixed hardcoded size. |
| 1374 | #if PLATFORM(GTK) |
| 1375 | SnapshotWebViewTest::add("WebKitWebView" , "snapshot" , testWebViewSnapshot); |
| 1376 | #endif |
| 1377 | WebViewTest::add("WebKitWebView" , "page-visibility" , testWebViewPageVisibility); |
| 1378 | #if ENABLE(NOTIFICATIONS) |
| 1379 | NotificationWebViewTest::add("WebKitWebView" , "notification" , testWebViewNotification); |
| 1380 | NotificationWebViewTest::add("WebKitWebView" , "notification-initial-permission-allowed" , testWebViewNotificationInitialPermissionAllowed); |
| 1381 | NotificationWebViewTest::add("WebKitWebView" , "notification-initial-permission-disallowed" , testWebViewNotificationInitialPermissionDisallowed); |
| 1382 | #endif |
| 1383 | IsPlayingAudioWebViewTest::add("WebKitWebView" , "is-playing-audio" , testWebViewIsPlayingAudio); |
| 1384 | WebViewTest::add("WebKitWebView" , "background-color" , testWebViewBackgroundColor); |
| 1385 | #if PLATFORM(GTK) |
| 1386 | WebViewTest::add("WebKitWebView" , "preferred-size" , testWebViewPreferredSize); |
| 1387 | #endif |
| 1388 | WebViewTitleTest::add("WebKitWebView" , "title-change" , testWebViewTitleChange); |
| 1389 | #if PLATFORM(WPE) |
| 1390 | FrameDisplayedTest::add("WebKitWebView" , "frame-displayed" , testWebViewFrameDisplayed); |
| 1391 | #endif |
| 1392 | } |
| 1393 | |
| 1394 | void afterAll() |
| 1395 | { |
| 1396 | } |
| 1397 | |