diff options
Diffstat (limited to 'Tools/TestWebKitAPI/Tests/WebKit2Gtk')
54 files changed, 6152 insertions, 766 deletions
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/AutocleanupsTest.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/AutocleanupsTest.cpp new file mode 100644 index 000000000..a20cbfd73 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/AutocleanupsTest.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2013 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#include "WebProcessTest.h" +#include <webkit2/webkit-web-extension.h> + +#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC + +class AutocleanupsTest : public WebProcessTest { +public: + static std::unique_ptr<WebProcessTest> create() { return std::unique_ptr<WebProcessTest>(new AutocleanupsTest()); } + +private: + bool testWebProcessAutocleanups(WebKitWebPage* webPage) + { + // Transfer none + g_autoptr(WebKitWebPage) page = WEBKIT_WEB_PAGE(g_object_ref(G_OBJECT(webPage))); + g_assert(WEBKIT_IS_WEB_PAGE(page)); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(page)); + + // Transfer none + g_autoptr(WebKitDOMDocument) document = WEBKIT_DOM_DOCUMENT(g_object_ref(G_OBJECT(webkit_web_page_get_dom_document(page)))); + g_assert(WEBKIT_DOM_IS_DOCUMENT(document)); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(document)); + + // Transfer full + g_autoptr(WebKitDOMDOMWindow) window = webkit_dom_document_get_default_view(document); + g_assert(WEBKIT_DOM_IS_DOM_WINDOW(window)); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(window)); + + // Transfer full + g_autoptr(WebKitDOMRange) range = webkit_dom_document_create_range(document); + g_assert(WEBKIT_DOM_IS_RANGE(range)); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(range)); + + return true; + } + + bool runTest(const char* testName, WebKitWebPage* page) override + { + if (!strcmp(testName, "web-process-autocleanups")) + return testWebProcessAutocleanups(page); + + g_assert_not_reached(); + return false; + } +}; + +static void __attribute__((constructor)) registerTests() +{ + REGISTER_TEST(AutocleanupsTest, "Autocleanups/web-process-autocleanups"); +} + +#endif // G_DEFINE_AUTOPTR_CLEANUP_FUNC diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/CMakeLists.txt b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/CMakeLists.txt new file mode 100644 index 000000000..64640f51c --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/CMakeLists.txt @@ -0,0 +1,135 @@ +set(TEST_LIBRARY_DIR ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/WebKit2GtkAPITests) +set(TEST_BINARY_DIR ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/TestWebKitAPI/WebKit2Gtk) +set(TEST_RESOURCES_DIR ${TEST_BINARY_DIR}/resources) +file(MAKE_DIRECTORY ${TEST_RESOURCES_DIR}) + +add_definitions( + -DWEBKIT_TEST_PLUGIN_DIR="${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/plugins" + -DWEBKIT_EXEC_PATH="${CMAKE_RUNTIME_OUTPUT_DIRECTORY}" + -DWEBKIT_SRC_DIR="${CMAKE_SOURCE_DIR}" + -DWEBKIT_TEST_WEB_EXTENSIONS_DIR="${TEST_LIBRARY_DIR}" + -DWEBKIT_INJECTED_BUNDLE_PATH="${CMAKE_LIBRARY_OUTPUT_DIRECTORY}" +) + +include_directories( + ${CMAKE_SOURCE_DIR}/Source + ${CMAKE_SOURCE_DIR}/Source/WTF + ${DERIVED_SOURCES_WEBKIT2GTK_DIR} + ${FORWARDING_HEADERS_DIR} + ${FORWARDING_HEADERS_WEBKIT2GTK_DIR} + ${FORWARDING_HEADERS_WEBKIT2GTK_EXTENSION_DIR} + ${TOOLS_DIR}/TestWebKitAPI/gtk/WebKit2Gtk +) + +include_directories(SYSTEM + ${ATSPI_INCLUDE_DIRS} + ${GLIB_INCLUDE_DIRS} + ${GSTREAMER_INCLUDE_DIRS} + ${GTK3_INCLUDE_DIRS} + ${GTK_UNIX_PRINT_INCLUDE_DIRS} + ${LIBSOUP_INCLUDE_DIRS} +) + +add_library(WebKit2APITestCore STATIC + ${TOOLS_DIR}/TestWebKitAPI/gtk/WebKit2Gtk/LoadTrackingTest.cpp + ${TOOLS_DIR}/TestWebKitAPI/gtk/WebKit2Gtk/WebKitTestBus.cpp + ${TOOLS_DIR}/TestWebKitAPI/gtk/WebKit2Gtk/WebKitTestServer.cpp + ${TOOLS_DIR}/TestWebKitAPI/gtk/WebKit2Gtk/TestMain.cpp + ${TOOLS_DIR}/TestWebKitAPI/gtk/WebKit2Gtk/WebViewTest.cpp +) +target_link_libraries(WebKit2APITestCore WebKit2) + +add_custom_command( + OUTPUT ${TEST_RESOURCES_DIR}/webkit2gtk-tests-resources.gresource + DEPENDS resources/webkit2gtk-tests.gresource.xml + resources/link-title.js + COMMAND glib-compile-resources + --target=${TEST_RESOURCES_DIR}/webkit2gtk-tests-resources.gresource + --sourcedir=${CMAKE_SOURCE_DIR} + ${CMAKE_CURRENT_LIST_DIR}/resources/webkit2gtk-tests.gresource.xml +) + +add_custom_target(test-gresource-bundle + DEPENDS ${TEST_RESOURCES_DIR}/webkit2gtk-tests-resources.gresource +) + +macro(ADD_WK2_TEST_WEB_EXTENSION extension_name) + add_library(${extension_name} MODULE ${ARGN}) + add_dependencies(${extension_name} WebKit2) + set_property( + TARGET ${extension_name} + APPEND + PROPERTY COMPILE_DEFINITIONS WEBKIT2_COMPILATION + ) + set_target_properties(${extension_name} PROPERTIES + LIBRARY_OUTPUT_DIRECTORY ${TEST_LIBRARY_DIR} + ) + target_link_libraries(${extension_name} + JavaScriptCore + WebKit2 + ${GLIB_LIBRARIES} + ) +endmacro() + +macro(ADD_WK2_TEST test_name) + add_executable(${test_name} ${ARGN}) + add_dependencies(${test_name} + test-gresource-bundle + WebExtensionTest + ) + target_link_libraries(${test_name} + JavaScriptCore + WebKit2 + WebKit2APITestCore + ${ATSPI_LIBRARIES} + ${GLIB_LIBRARIES} + ${GTK3_LIBRARIES} + ${GTK_UNIX_PRINT_LIBRARIES} + ${LIBSOUP_LIBRARIES} + ) + set_target_properties(${test_name} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${TEST_BINARY_DIR} + ) +endmacro() + +ADD_WK2_TEST_WEB_EXTENSION(WebExtensionTest WebExtensionTest.cpp) +ADD_WK2_TEST_WEB_EXTENSION(WebProcessTest AutocleanupsTest.cpp DOMNodeTest.cpp DOMNodeFilterTest.cpp DOMXPathNSResolverTest.cpp FrameTest.cpp WebProcessTest.cpp EditorTest.cpp) + +ADD_WK2_TEST(InspectorTestServer InspectorTestServer.cpp) +ADD_WK2_TEST(TestAuthentication TestAuthentication.cpp) +ADD_WK2_TEST(TestAutocleanups TestAutocleanups.cpp) +ADD_WK2_TEST(TestBackForwardList TestBackForwardList.cpp) +ADD_WK2_TEST(TestContextMenu TestContextMenu.cpp) +ADD_WK2_TEST(TestCookieManager TestCookieManager.cpp) +ADD_WK2_TEST(TestDOMNode TestDOMNode.cpp) +ADD_WK2_TEST(TestDOMNodeFilter TestDOMNodeFilter.cpp) +ADD_WK2_TEST(TestDOMXPathNSResolver TestDOMXPathNSResolver.cpp) +ADD_WK2_TEST(TestDownloads TestDownloads.cpp) +ADD_WK2_TEST(TestWebKitFaviconDatabase TestWebKitFaviconDatabase.cpp) +ADD_WK2_TEST(TestWebKitFindController TestWebKitFindController.cpp) +ADD_WK2_TEST(TestFrame TestFrame.cpp) +ADD_WK2_TEST(TestInspector TestInspector.cpp) +ADD_WK2_TEST(TestInspectorServer TestInspectorServer.cpp) +ADD_WK2_TEST(TestLoaderClient TestLoaderClient.cpp) +ADD_WK2_TEST(TestMultiprocess TestMultiprocess.cpp) +ADD_WK2_TEST(TestPrinting TestPrinting.cpp) +ADD_WK2_TEST(TestResources TestResources.cpp) +ADD_WK2_TEST(TestSSL TestSSL.cpp) +ADD_WK2_TEST(TestUIClient TestUIClient.cpp) +ADD_WK2_TEST(TestWebExtensions TestWebExtensions.cpp) +ADD_WK2_TEST(TestWebKitPolicyClient TestWebKitPolicyClient.cpp) +ADD_WK2_TEST(TestWebKitSecurityOrigin TestWebKitSecurityOrigin.cpp) +ADD_WK2_TEST(TestWebKitSettings TestWebKitSettings.cpp) +ADD_WK2_TEST(TestWebKitVersion TestWebKitVersion.cpp) +ADD_WK2_TEST(TestWebViewEditor TestWebViewEditor.cpp) +ADD_WK2_TEST(TestWebKitWebContext TestWebKitWebContext.cpp) +ADD_WK2_TEST(TestWebKitWebView TestWebKitWebView.cpp) +ADD_WK2_TEST(TestWebKitUserContentManager TestWebKitUserContentManager.cpp) +ADD_WK2_TEST(TestWebsiteData TestWebsiteData.cpp) +ADD_WK2_TEST(TestEditor TestEditor.cpp) +ADD_WK2_TEST(TestConsoleMessage TestConsoleMessage.cpp) + +if (ATSPI_FOUND) + ADD_WK2_TEST(AccessibilityTestServer AccessibilityTestServer.cpp) + ADD_WK2_TEST(TestWebKitAccessibility TestWebKitAccessibility.cpp) +endif () diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/DOMDOMWindowTest.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/DOMDOMWindowTest.cpp new file mode 100644 index 000000000..fc8cefb52 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/DOMDOMWindowTest.cpp @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2013 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#include "WebProcessTest.h" +#include <gio/gio.h> +#include <webkit2/webkit-web-extension.h> +#include <wtf/RunLoop.h> + +class WebKitDOMDOMWindowTest; +static gboolean loadedCallback(WebKitDOMDOMWindow*, WebKitDOMEvent*, WebKitDOMDOMWindowTest*); +static gboolean clickedCallback(WebKitDOMDOMWindow*, WebKitDOMEvent*, WebKitDOMDOMWindowTest*); + +class WebKitDOMDOMWindowTest : public WebProcessTest { +public: + static std::unique_ptr<WebProcessTest> create() { return std::make_unique<WebKitDOMDOMWindowTest>(); } + +private: + guint64 webPageFromArgs(GVariant* args) + { + GVariantIter iter; + g_variant_iter_init(&iter, args); + + const char* key; + GVariant* value; + while (g_variant_iter_loop(&iter, "{&sv}", &key, &value)) { + if (!strcmp(key, "pageID") && g_variant_classify(value) == G_VARIANT_CLASS_UINT64) + return g_variant_get_uint64(value); + } + + g_assert_not_reached(); + return 0; + } + + bool testSignals(WebKitWebExtension* extension, GVariant* args) + { + notify("ready", ""); + + WebKitWebPage* page = webkit_web_extension_get_page(extension, webPageFromArgs(args)); + g_assert(WEBKIT_IS_WEB_PAGE(page)); + WebKitDOMDocument* document = webkit_web_page_get_dom_document(page); + g_assert(WEBKIT_DOM_IS_DOCUMENT(document)); + + WebKitDOMDOMWindow* domWindow = webkit_dom_document_get_default_view(document); + g_assert(domWindow); + + // The "load" WebKitDOMDOMWindow signal is issued before this test is + // called. There's no way to capture it here. We simply assume that + // the document is loaded and notify the uiprocess accordingly + // notify("loaded", ""); + + webkit_dom_event_target_add_event_listener( + WEBKIT_DOM_EVENT_TARGET(domWindow), + "load", + G_CALLBACK(loadedCallback), + false, + this); + + // loadedCallback() will stop this loop + RunLoop::run(); + + document = webkit_web_page_get_dom_document(page); + g_assert(WEBKIT_DOM_IS_DOCUMENT(document)); + + WebKitDOMElement* element = webkit_dom_document_get_element_by_id(document, "test"); + g_assert(element); + + webkit_dom_event_target_add_event_listener( + WEBKIT_DOM_EVENT_TARGET(element), + "click", + G_CALLBACK(clickedCallback), + false, + this); + + // The "click" action will be issued in the uiprocess and that will + // trigger the dom event here. + // clickedCallback() will stop this loop + RunLoop::run(); + + return true; + } + + bool testDispatchEvent(WebKitWebExtension* extension, GVariant* args) + { + notify("ready", ""); + + WebKitWebPage* page = webkit_web_extension_get_page(extension, webPageFromArgs(args)); + g_assert(WEBKIT_IS_WEB_PAGE(page)); + WebKitDOMDocument* document = webkit_web_page_get_dom_document(page); + g_assert(WEBKIT_DOM_IS_DOCUMENT(document)); + + WebKitDOMDOMWindow* domWindow = webkit_dom_document_get_default_view(document); + g_assert(domWindow); + + // The "load" WebKitDOMDOMWindow signal is issued before this test is + // called. There's no way to capture it here. We simply assume that + // the document is loaded and notify the uiprocess accordingly + // notify("loaded", ""); + + webkit_dom_event_target_add_event_listener( + WEBKIT_DOM_EVENT_TARGET(domWindow), + "load", + G_CALLBACK(loadedCallback), + false, + this); + + // loadedCallback() will stop this loop + RunLoop::run(); + + document = webkit_web_page_get_dom_document(page); + g_assert(WEBKIT_DOM_IS_DOCUMENT(document)); + + WebKitDOMElement* element = webkit_dom_document_get_element_by_id(document, "test"); + g_assert(element); + + WebKitDOMEvent* event = webkit_dom_document_create_event(document, "MouseEvent", 0); + g_assert(event); + g_assert(WEBKIT_DOM_IS_EVENT(event)); + g_assert(WEBKIT_DOM_IS_MOUSE_EVENT(event)); + + glong clientX, clientY; + clientX = webkit_dom_element_get_client_left(element); + clientY = webkit_dom_element_get_client_top(element); + + webkit_dom_event_target_add_event_listener( + WEBKIT_DOM_EVENT_TARGET(element), + "click", + G_CALLBACK(clickedCallback), + false, + this); + + webkit_dom_mouse_event_init_mouse_event(WEBKIT_DOM_MOUSE_EVENT(event), + "click", TRUE, TRUE, + domWindow, 0, 0, 0, clientX, clientY, + FALSE, FALSE, FALSE, FALSE, + 1, WEBKIT_DOM_EVENT_TARGET(element)); + + webkit_dom_event_target_dispatch_event(WEBKIT_DOM_EVENT_TARGET(element), event, 0); + + // clickedCallback() will stop this loop + RunLoop::run(); + + return true; + } + + bool testGetComputedStyle(WebKitWebExtension* extension, GVariant* args) + { + WebKitWebPage* page = webkit_web_extension_get_page(extension, webPageFromArgs(args)); + g_assert(WEBKIT_IS_WEB_PAGE(page)); + WebKitDOMDocument* document = webkit_web_page_get_dom_document(page); + g_assert(WEBKIT_DOM_IS_DOCUMENT(document)); + WebKitDOMDOMWindow* domWindow = webkit_dom_document_get_default_view(document); + g_assert(domWindow); + WebKitDOMElement* element = webkit_dom_document_get_element_by_id(document, "test"); + g_assert(element); + g_assert(WEBKIT_DOM_IS_ELEMENT(element)); + WebKitDOMCSSStyleDeclaration* cssStyle = webkit_dom_dom_window_get_computed_style(domWindow, element, 0); + gchar* fontSize = webkit_dom_css_style_declaration_get_property_value(cssStyle, "font-size"); + g_assert_cmpstr(fontSize, ==, "16px"); + + return true; + } + + virtual bool runTest(const char* testName, WebKitWebExtension* extension, GVariant* args) + { + if (!strcmp(testName, "signals")) + return testSignals(extension, args); + if (!strcmp(testName, "dispatch-event")) + return testDispatchEvent(extension, args); + if (!strcmp(testName, "get-computed-style")) + return testGetComputedStyle(extension, args); + + g_assert_not_reached(); + return false; + } +}; + +static void __attribute__((constructor)) registerTests() +{ + REGISTER_TEST(WebKitDOMDOMWindowTest, "WebKitDOMDOMWindow/signals"); + REGISTER_TEST(WebKitDOMDOMWindowTest, "WebKitDOMDOMWindow/dispatch-event"); + REGISTER_TEST(WebKitDOMDOMWindowTest, "WebKitDOMDOMWindow/get-computed-style"); +} + +static gboolean loadedCallback(WebKitDOMDOMWindow* view, WebKitDOMEvent* event, WebKitDOMDOMWindowTest* test) +{ + test->notify("loaded", ""); + + // Stop the loop and let testSignals() or testDispatchEvent() continue its course + RunLoop::current().stop(); + + return FALSE; +} + +static gboolean clickedCallback(WebKitDOMDOMWindow* view, WebKitDOMEvent* event, WebKitDOMDOMWindowTest* test) +{ + test->notify("clicked", ""); + test->notify("finish", ""); + + // Stop the loop and let testSignals() or testDispatchEvent() continue its course + RunLoop::current().stop(); + + return FALSE; +} diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/DOMNodeFilterTest.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/DOMNodeFilterTest.cpp new file mode 100644 index 000000000..0f18d1f31 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/DOMNodeFilterTest.cpp @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2013 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#include "WebProcessTest.h" +#include <gio/gio.h> +#include <webkit2/webkit-web-extension.h> +#include <wtf/glib/GUniquePtr.h> + +typedef struct _WebKitNodeFilter { + GObject parent; +} WebKitNodeFilter; + +typedef struct _WebKitNodeFilterClass { + GObjectClass parentClass; +} WebKitNodeFilterClass; + +static short webkitNodeFilterAcceptNode(WebKitDOMNodeFilter*, WebKitDOMNode* node) +{ + // Filter out input elements. + return WEBKIT_DOM_IS_HTML_INPUT_ELEMENT(node) ? WEBKIT_DOM_NODE_FILTER_REJECT : WEBKIT_DOM_NODE_FILTER_ACCEPT; +} + +static void webkitNodeFilterDOMNodeFilterIfaceInit(WebKitDOMNodeFilterIface* iface) +{ + iface->accept_node = webkitNodeFilterAcceptNode; +} + +G_DEFINE_TYPE_WITH_CODE(WebKitNodeFilter, webkit_node_filter, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE(WEBKIT_DOM_TYPE_NODE_FILTER, webkitNodeFilterDOMNodeFilterIfaceInit)) + +static void webkit_node_filter_init(WebKitNodeFilter*) +{ +} + +static void webkit_node_filter_class_init(WebKitNodeFilterClass*) +{ +} + +static const char* expectedNodesAll[] = { "HTML", "HEAD", "TITLE", "#text", "BODY", "INPUT", "INPUT", "BR" }; +static const char* expectedNodesNoInput[] = { "HTML", "HEAD", "TITLE", "#text", "BODY", "BR" }; +static const char* expectedElementsNoInput[] = { "HTML", "HEAD", "TITLE", "BODY", "BR" }; + +class WebKitDOMNodeFilterTest : public WebProcessTest { +public: + static std::unique_ptr<WebProcessTest> create() { return std::unique_ptr<WebProcessTest>(new WebKitDOMNodeFilterTest()); } + +private: + bool testTreeWalker(WebKitWebPage* page) + { + WebKitDOMDocument* document = webkit_web_page_get_dom_document(page); + g_assert(WEBKIT_DOM_IS_DOCUMENT(document)); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(document)); + + WebKitDOMElement* root = webkit_dom_document_get_element_by_id(document, "root"); + g_assert(WEBKIT_DOM_IS_NODE(root)); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(root)); + + // No filter. + GRefPtr<WebKitDOMTreeWalker> walker = adoptGRef(webkit_dom_document_create_tree_walker(document, WEBKIT_DOM_NODE(root), WEBKIT_DOM_NODE_FILTER_SHOW_ALL, nullptr, FALSE, nullptr)); + g_assert(WEBKIT_DOM_IS_TREE_WALKER(walker.get())); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(walker.get())); + g_assert(!webkit_dom_tree_walker_get_filter(walker.get())); + + unsigned i = 0; + for (WebKitDOMNode* node = WEBKIT_DOM_NODE(root); node; node = webkit_dom_tree_walker_next_node(walker.get()), ++i) { + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node)); + g_assert_cmpuint(i, <, G_N_ELEMENTS(expectedNodesAll)); + GUniquePtr<char> nodeName(webkit_dom_node_get_node_name(node)); + g_assert_cmpstr(nodeName.get(), ==, expectedNodesAll[i]); + } + g_assert_cmpuint(i, ==, G_N_ELEMENTS(expectedNodesAll)); + + // Input elements filter. + GRefPtr<WebKitDOMNodeFilter> filter = adoptGRef(static_cast<WebKitDOMNodeFilter*>(g_object_new(webkit_node_filter_get_type(), nullptr))); + walker = adoptGRef(webkit_dom_document_create_tree_walker(document, WEBKIT_DOM_NODE(root), WEBKIT_DOM_NODE_FILTER_SHOW_ALL, filter.get(), FALSE, nullptr)); + g_assert(WEBKIT_DOM_IS_TREE_WALKER(walker.get())); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(filter.get())); + g_assert(webkit_dom_tree_walker_get_filter(walker.get()) == filter.get()); + + i = 0; + for (WebKitDOMNode* node = WEBKIT_DOM_NODE(root); node; node = webkit_dom_tree_walker_next_node(walker.get()), ++i) { + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node)); + g_assert_cmpuint(i, <, G_N_ELEMENTS(expectedNodesNoInput)); + GUniquePtr<char> nodeName(webkit_dom_node_get_node_name(node)); + g_assert_cmpstr(nodeName.get(), ==, expectedNodesNoInput[i]); + } + g_assert_cmpuint(i, ==, G_N_ELEMENTS(expectedNodesNoInput)); + + // Show only elements, reusing the input filter. + walker = adoptGRef(webkit_dom_document_create_tree_walker(document, WEBKIT_DOM_NODE(root), WEBKIT_DOM_NODE_FILTER_SHOW_ELEMENT, filter.get(), FALSE, nullptr)); + g_assert(WEBKIT_DOM_IS_TREE_WALKER(walker.get())); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(walker.get())); + g_assert(webkit_dom_tree_walker_get_filter(walker.get()) == filter.get()); + + i = 0; + for (WebKitDOMNode* node = WEBKIT_DOM_NODE(root); node; node = webkit_dom_tree_walker_next_node(walker.get()), ++i) { + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node)); + g_assert_cmpuint(i, <, G_N_ELEMENTS(expectedElementsNoInput)); + GUniquePtr<char> nodeName(webkit_dom_node_get_node_name(node)); + g_assert_cmpstr(nodeName.get(), ==, expectedElementsNoInput[i]); + } + g_assert_cmpuint(i, ==, G_N_ELEMENTS(expectedElementsNoInput)); + + return true; + } + + bool testNodeIterator(WebKitWebPage* page) + { + WebKitDOMDocument* document = webkit_web_page_get_dom_document(page); + g_assert(WEBKIT_DOM_IS_DOCUMENT(document)); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(document)); + + WebKitDOMElement* root = webkit_dom_document_get_element_by_id(document, "root"); + g_assert(WEBKIT_DOM_IS_NODE(root)); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(root)); + + // No filter. + GRefPtr<WebKitDOMNodeIterator> iter = adoptGRef(webkit_dom_document_create_node_iterator(document, WEBKIT_DOM_NODE(root), WEBKIT_DOM_NODE_FILTER_SHOW_ALL, nullptr, FALSE, nullptr)); + g_assert(WEBKIT_DOM_IS_NODE_ITERATOR(iter.get())); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(iter.get())); + g_assert(!webkit_dom_node_iterator_get_filter(iter.get())); + + unsigned i = 0; + while (WebKitDOMNode* node = webkit_dom_node_iterator_next_node(iter.get(), nullptr)) { + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node)); + g_assert_cmpuint(i, <, G_N_ELEMENTS(expectedNodesAll)); + GUniquePtr<char> nodeName(webkit_dom_node_get_node_name(node)); + g_assert_cmpstr(nodeName.get(), ==, expectedNodesAll[i]); + i++; + } + g_assert_cmpuint(i, ==, G_N_ELEMENTS(expectedNodesAll)); + + // Input elements filter. + GRefPtr<WebKitDOMNodeFilter> filter = adoptGRef(static_cast<WebKitDOMNodeFilter*>(g_object_new(webkit_node_filter_get_type(), nullptr))); + iter = adoptGRef(webkit_dom_document_create_node_iterator(document, WEBKIT_DOM_NODE(root), WEBKIT_DOM_NODE_FILTER_SHOW_ALL, filter.get(), FALSE, nullptr)); + g_assert(WEBKIT_DOM_IS_NODE_ITERATOR(iter.get())); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(iter.get())); + g_assert(webkit_dom_node_iterator_get_filter(iter.get()) == filter.get()); + + i = 0; + while (WebKitDOMNode* node = webkit_dom_node_iterator_next_node(iter.get(), nullptr)) { + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node)); + g_assert_cmpuint(i, <, G_N_ELEMENTS(expectedNodesNoInput)); + GUniquePtr<char> nodeName(webkit_dom_node_get_node_name(node)); + g_assert_cmpstr(nodeName.get(), ==, expectedNodesNoInput[i]); + i++; + } + g_assert_cmpuint(i, ==, G_N_ELEMENTS(expectedNodesNoInput)); + + // Show only elements, reusing the input filter. + iter = adoptGRef(webkit_dom_document_create_node_iterator(document, WEBKIT_DOM_NODE(root), WEBKIT_DOM_NODE_FILTER_SHOW_ELEMENT, filter.get(), FALSE, nullptr)); + g_assert(WEBKIT_DOM_IS_NODE_ITERATOR(iter.get())); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(iter.get())); + g_assert(webkit_dom_node_iterator_get_filter(iter.get()) == filter.get()); + + i = 0; + while (WebKitDOMNode* node = webkit_dom_node_iterator_next_node(iter.get(), nullptr)) { + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node)); + g_assert_cmpuint(i, <, G_N_ELEMENTS(expectedElementsNoInput)); + GUniquePtr<char> nodeName(webkit_dom_node_get_node_name(node)); + g_assert_cmpstr(nodeName.get(), ==, expectedElementsNoInput[i]); + i++; + } + g_assert_cmpuint(i, ==, G_N_ELEMENTS(expectedElementsNoInput)); + + return true; + } + + bool runTest(const char* testName, WebKitWebPage* page) override + { + if (!strcmp(testName, "tree-walker")) + return testTreeWalker(page); + if (!strcmp(testName, "node-iterator")) + return testNodeIterator(page); + + g_assert_not_reached(); + return false; + } +}; + +static void __attribute__((constructor)) registerTests() +{ + REGISTER_TEST(WebKitDOMNodeFilterTest, "WebKitDOMNodeFilter/tree-walker"); + REGISTER_TEST(WebKitDOMNodeFilterTest, "WebKitDOMNodeFilter/node-iterator"); +} + + diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/DOMNodeTest.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/DOMNodeTest.cpp index 5f62e5493..9d57b42d2 100644 --- a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/DOMNodeTest.cpp +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/DOMNodeTest.cpp @@ -22,7 +22,7 @@ #include "WebProcessTest.h" #include <gio/gio.h> #include <webkit2/webkit-web-extension.h> -#include <wtf/gobject/GUniquePtr.h> +#include <wtf/glib/GUniquePtr.h> class WebKitDOMNodeTest : public WebProcessTest { public: @@ -33,21 +33,26 @@ private: { WebKitDOMDocument* document = webkit_web_page_get_dom_document(page); g_assert(WEBKIT_DOM_IS_DOCUMENT(document)); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(document)); WebKitDOMHTMLHeadElement* head = webkit_dom_document_get_head(document); g_assert(WEBKIT_DOM_IS_HTML_HEAD_ELEMENT(head)); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(head)); // Title, head's child. g_assert(webkit_dom_node_has_child_nodes(WEBKIT_DOM_NODE(head))); GRefPtr<WebKitDOMNodeList> list = adoptGRef(webkit_dom_node_get_child_nodes(WEBKIT_DOM_NODE(head))); g_assert(WEBKIT_DOM_IS_NODE_LIST(list.get())); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(list.get())); g_assert_cmpint(webkit_dom_node_list_get_length(list.get()), ==, 1); WebKitDOMNode* node = webkit_dom_node_list_item(list.get(), 0); g_assert(WEBKIT_DOM_IS_HTML_TITLE_ELEMENT(node)); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node)); // Body, Head sibling. node = webkit_dom_node_get_next_sibling(WEBKIT_DOM_NODE(head)); g_assert(WEBKIT_DOM_IS_HTML_BODY_ELEMENT(node)); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node)); WebKitDOMHTMLBodyElement* body = WEBKIT_DOM_HTML_BODY_ELEMENT(node); // There is no third sibling @@ -56,10 +61,13 @@ private: // Body's previous sibling is Head. node = webkit_dom_node_get_previous_sibling(WEBKIT_DOM_NODE(body)); g_assert(WEBKIT_DOM_IS_HTML_HEAD_ELEMENT(node)); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node)); // Body has 3 children. g_assert(webkit_dom_node_has_child_nodes(WEBKIT_DOM_NODE(body))); list = adoptGRef(webkit_dom_node_get_child_nodes(WEBKIT_DOM_NODE(body))); + g_assert(WEBKIT_DOM_IS_NODE_LIST(list.get())); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(list.get())); unsigned long length = webkit_dom_node_list_get_length(list.get()); g_assert_cmpint(length, ==, 3); @@ -67,6 +75,7 @@ private: for (unsigned long i = 0; i < length; i++) { node = webkit_dom_node_list_item(list.get(), i); g_assert(WEBKIT_DOM_IS_HTML_PARAGRAPH_ELEMENT(node)); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node)); } // Go backwards @@ -81,88 +90,135 @@ private: { WebKitDOMDocument* document = webkit_web_page_get_dom_document(page); g_assert(WEBKIT_DOM_IS_DOCUMENT(document)); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(document)); WebKitDOMHTMLElement* body = webkit_dom_document_get_body(document); g_assert(WEBKIT_DOM_IS_HTML_ELEMENT(body)); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(body)); // Body shouldn't have any children at this point. g_assert(!webkit_dom_node_has_child_nodes(WEBKIT_DOM_NODE(body))); + // The value of a non-existent attribute should be null, not an empty string + g_assert(!webkit_dom_html_body_element_get_background(WEBKIT_DOM_HTML_BODY_ELEMENT(body))); + // Insert one P element. WebKitDOMElement* p = webkit_dom_document_create_element(document, "P", 0); g_assert(WEBKIT_DOM_IS_HTML_ELEMENT(p)); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(p)); webkit_dom_node_append_child(WEBKIT_DOM_NODE(body), WEBKIT_DOM_NODE(p), 0); // Now it should have one, the same that we inserted. g_assert(webkit_dom_node_has_child_nodes(WEBKIT_DOM_NODE(body))); GRefPtr<WebKitDOMNodeList> list = adoptGRef(webkit_dom_node_get_child_nodes(WEBKIT_DOM_NODE(body))); g_assert(WEBKIT_DOM_IS_NODE_LIST(list.get())); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(list.get())); g_assert_cmpint(webkit_dom_node_list_get_length(list.get()), ==, 1); WebKitDOMNode* node = webkit_dom_node_list_item(list.get(), 0); g_assert(WEBKIT_DOM_IS_HTML_ELEMENT(node)); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node)); g_assert(webkit_dom_node_is_same_node(WEBKIT_DOM_NODE(p), node)); // Replace the P tag with a DIV tag. WebKitDOMElement* div = webkit_dom_document_create_element(document, "DIV", 0); g_assert(WEBKIT_DOM_IS_HTML_ELEMENT(div)); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(div)); webkit_dom_node_replace_child(WEBKIT_DOM_NODE(body), WEBKIT_DOM_NODE(div), WEBKIT_DOM_NODE(p), 0); g_assert(webkit_dom_node_has_child_nodes(WEBKIT_DOM_NODE(body))); list = adoptGRef(webkit_dom_node_get_child_nodes(WEBKIT_DOM_NODE(body))); g_assert(WEBKIT_DOM_IS_NODE_LIST(list.get())); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(list.get())); g_assert_cmpint(webkit_dom_node_list_get_length(list.get()), ==, 1); node = webkit_dom_node_list_item(list.get(), 0); g_assert(WEBKIT_DOM_IS_HTML_ELEMENT(node)); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node)); g_assert(webkit_dom_node_is_same_node(WEBKIT_DOM_NODE(div), node)); // Now remove the tag. webkit_dom_node_remove_child(WEBKIT_DOM_NODE(body), node, 0); list = adoptGRef(webkit_dom_node_get_child_nodes(WEBKIT_DOM_NODE(body))); g_assert(WEBKIT_DOM_IS_NODE_LIST(list.get())); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(list.get())); g_assert_cmpint(webkit_dom_node_list_get_length(list.get()), ==, 0); // Test insert before. If refChild is null, insert newChild as last element of parent. div = webkit_dom_document_create_element(document, "DIV", 0); g_assert(WEBKIT_DOM_IS_HTML_ELEMENT(div)); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(div)); webkit_dom_node_insert_before(WEBKIT_DOM_NODE(body), WEBKIT_DOM_NODE(div), 0, 0); g_assert(webkit_dom_node_has_child_nodes(WEBKIT_DOM_NODE(body))); list = adoptGRef(webkit_dom_node_get_child_nodes(WEBKIT_DOM_NODE(body))); g_assert(WEBKIT_DOM_IS_NODE_LIST(list.get())); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(list.get())); g_assert_cmpint(webkit_dom_node_list_get_length(list.get()), ==, 1); node = webkit_dom_node_list_item(list.get(), 0); g_assert(WEBKIT_DOM_IS_HTML_ELEMENT(node)); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node)); g_assert(webkit_dom_node_is_same_node(WEBKIT_DOM_NODE(div), node)); // Now insert a 'p' before 'div'. p = webkit_dom_document_create_element(document, "P", 0); g_assert(WEBKIT_DOM_IS_HTML_ELEMENT(p)); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(p)); webkit_dom_node_insert_before(WEBKIT_DOM_NODE(body), WEBKIT_DOM_NODE(p), WEBKIT_DOM_NODE(div), 0); g_assert(webkit_dom_node_has_child_nodes(WEBKIT_DOM_NODE(body))); list = adoptGRef(webkit_dom_node_get_child_nodes(WEBKIT_DOM_NODE(body))); g_assert(WEBKIT_DOM_IS_NODE_LIST(list.get())); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(list.get())); g_assert_cmpint(webkit_dom_node_list_get_length(list.get()), ==, 2); node = webkit_dom_node_list_item(list.get(), 0); g_assert(WEBKIT_DOM_IS_HTML_ELEMENT(node)); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node)); g_assert(webkit_dom_node_is_same_node(WEBKIT_DOM_NODE(p), node)); node = webkit_dom_node_list_item(list.get(), 1); g_assert(WEBKIT_DOM_IS_HTML_ELEMENT(node)); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node)); g_assert(webkit_dom_node_is_same_node(WEBKIT_DOM_NODE(div), node)); return true; } - bool testTagNames(WebKitWebPage* page) + bool testTagNamesNodeList(WebKitWebPage* page) + { + static const char* expectedTagNames[] = { "HTML", "HEAD", "BODY", "VIDEO", "SOURCE", "VIDEO", "SOURCE", "INPUT" }; + + WebKitDOMDocument* document = webkit_web_page_get_dom_document(page); + g_assert(WEBKIT_DOM_IS_DOCUMENT(document)); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(document)); + + GRefPtr<WebKitDOMNodeList> list = adoptGRef(webkit_dom_document_query_selector_all(document, "*", nullptr)); + g_assert(WEBKIT_DOM_IS_NODE_LIST(list.get())); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(list.get())); + gulong nodeCount = webkit_dom_node_list_get_length(list.get()); + g_assert_cmpuint(nodeCount, ==, G_N_ELEMENTS(expectedTagNames)); + for (unsigned i = 0; i < nodeCount; i++) { + WebKitDOMNode* node = webkit_dom_node_list_item(list.get(), i); + g_assert(WEBKIT_DOM_IS_NODE(node)); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node)); + GUniquePtr<char> tagName(webkit_dom_node_get_node_name(node)); + g_assert_cmpstr(tagName.get(), ==, expectedTagNames[i]); + } + + return true; + } + + bool testTagNamesHTMLCollection(WebKitWebPage* page) { static const char* expectedTagNames[] = { "HTML", "HEAD", "BODY", "VIDEO", "SOURCE", "VIDEO", "SOURCE", "INPUT" }; WebKitDOMDocument* document = webkit_web_page_get_dom_document(page); g_assert(WEBKIT_DOM_IS_DOCUMENT(document)); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(document)); - WebKitDOMNodeList* list = webkit_dom_document_get_elements_by_tag_name(document, "*"); - gulong nodeCount = webkit_dom_node_list_get_length(list); + GRefPtr<WebKitDOMHTMLCollection> collection = adoptGRef(webkit_dom_document_get_elements_by_tag_name_as_html_collection(document, "*")); + g_assert(WEBKIT_DOM_IS_HTML_COLLECTION(collection.get())); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(collection.get())); + gulong nodeCount = webkit_dom_html_collection_get_length(collection.get()); g_assert_cmpuint(nodeCount, ==, G_N_ELEMENTS(expectedTagNames)); for (unsigned i = 0; i < nodeCount; i++) { - WebKitDOMNode* node = webkit_dom_node_list_item(list, i); + WebKitDOMNode* node = webkit_dom_html_collection_item(collection.get(), i); g_assert(WEBKIT_DOM_IS_NODE(node)); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node)); GUniquePtr<char> tagName(webkit_dom_node_get_node_name(node)); g_assert_cmpstr(tagName.get(), ==, expectedTagNames[i]); } @@ -170,14 +226,62 @@ private: return true; } + bool testDOMCache(WebKitWebPage* page) + { + WebKitDOMDocument* document = webkit_web_page_get_dom_document(page); + g_assert(WEBKIT_DOM_IS_DOCUMENT(document)); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(document)); + + // DOM objects already in the document should be automatically handled by the cache. + WebKitDOMElement* div = webkit_dom_document_get_element_by_id(document, "container"); + g_assert(WEBKIT_DOM_IS_HTML_ELEMENT(div)); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(div)); + + // Get the same elment twice should return the same pointer. + g_assert(div == webkit_dom_document_get_element_by_id(document, "container")); + + // A new DOM object created that is derived from Node should be automatically handled by the cache. + WebKitDOMElement* p = webkit_dom_document_create_element(document, "P", nullptr); + g_assert(WEBKIT_DOM_IS_HTML_PARAGRAPH_ELEMENT(p)); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(p)); + + // A new DOM object created that isn't derived from Node should be manually handled. + GRefPtr<WebKitDOMNodeIterator> iter = adoptGRef(webkit_dom_document_create_node_iterator(document, WEBKIT_DOM_NODE(div), WEBKIT_DOM_NODE_FILTER_SHOW_ALL, nullptr, FALSE, nullptr)); + g_assert(WEBKIT_DOM_IS_NODE_ITERATOR(iter.get())); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(iter.get())); + + // We can also manually handle a DOM object handled by the cache. + GRefPtr<WebKitDOMElement> p2 = adoptGRef(webkit_dom_document_create_element(document, "P", nullptr)); + g_assert(WEBKIT_DOM_IS_HTML_PARAGRAPH_ELEMENT(p2.get())); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(p2.get())); + + // Manually handling a DOM object owned by the cache shouldn't crash when the cache has more than one reference. + GRefPtr<WebKitDOMElement> p3 = adoptGRef(webkit_dom_document_create_element(document, "P", nullptr)); + g_assert(WEBKIT_DOM_IS_HTML_PARAGRAPH_ELEMENT(p3.get())); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(p3.get())); + webkit_dom_node_append_child(WEBKIT_DOM_NODE(div), WEBKIT_DOM_NODE(p3.get()), nullptr); + + // DOM objects removed from the document are also correctly handled by the cache. + WebKitDOMElement* a = webkit_dom_document_create_element(document, "A", nullptr); + g_assert(WEBKIT_DOM_IS_ELEMENT(a)); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(a)); + webkit_dom_node_remove_child(WEBKIT_DOM_NODE(div), WEBKIT_DOM_NODE(a), nullptr); + + return true; + } + bool runTest(const char* testName, WebKitWebPage* page) override { if (!strcmp(testName, "hierarchy-navigation")) return testHierarchyNavigation(page); if (!strcmp(testName, "insertion")) return testInsertion(page); - if (!strcmp(testName, "tag-names")) - return testTagNames(page); + if (!strcmp(testName, "tag-names-node-list")) + return testTagNamesNodeList(page); + if (!strcmp(testName, "tag-names-html-collection")) + return testTagNamesHTMLCollection(page); + if (!strcmp(testName, "dom-cache")) + return testDOMCache(page); g_assert_not_reached(); return false; @@ -188,7 +292,9 @@ static void __attribute__((constructor)) registerTests() { REGISTER_TEST(WebKitDOMNodeTest, "WebKitDOMNode/hierarchy-navigation"); REGISTER_TEST(WebKitDOMNodeTest, "WebKitDOMNode/insertion"); - REGISTER_TEST(WebKitDOMNodeTest, "WebKitDOMNode/tag-names"); + REGISTER_TEST(WebKitDOMNodeTest, "WebKitDOMNode/tag-names-node-list"); + REGISTER_TEST(WebKitDOMNodeTest, "WebKitDOMNode/tag-names-html-collection"); + REGISTER_TEST(WebKitDOMNodeTest, "WebKitDOMNode/dom-cache"); } diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/DOMXPathNSResolverTest.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/DOMXPathNSResolverTest.cpp new file mode 100644 index 000000000..110677e41 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/DOMXPathNSResolverTest.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2014 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#include "WebProcessTest.h" +#include <gio/gio.h> +#include <webkit2/webkit-web-extension.h> +#include <wtf/glib/GUniquePtr.h> + +typedef struct _WebKitXPathNSResolver { + GObject parent; +} WebKitXPathNSResolver; + +typedef struct _WebKitXPathNSResolverClass { + GObjectClass parentClass; +} WebKitXPathNSResolverClass; + +static char* webkitXPathNSResolverLookupNamespaceURI(WebKitDOMXPathNSResolver* resolver, const char* prefix) +{ + if (!g_strcmp0(prefix, "foo")) + return g_strdup("http://www.example.com"); + + return nullptr; +} + +static void webkitXPathNSResolverDOMXPathNSResolverIfaceInit(WebKitDOMXPathNSResolverIface* iface) +{ + iface->lookup_namespace_uri = webkitXPathNSResolverLookupNamespaceURI; +} + +G_DEFINE_TYPE_WITH_CODE(WebKitXPathNSResolver, webkit_xpath_ns_resolver, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE(WEBKIT_DOM_TYPE_XPATH_NS_RESOLVER, webkitXPathNSResolverDOMXPathNSResolverIfaceInit)) + +static void webkit_xpath_ns_resolver_init(WebKitXPathNSResolver*) +{ +} + +static void webkit_xpath_ns_resolver_class_init(WebKitXPathNSResolverClass*) +{ +} + +class WebKitDOMXPathNSResolverTest : public WebProcessTest { +public: + static std::unique_ptr<WebProcessTest> create() { return std::unique_ptr<WebProcessTest>(new WebKitDOMXPathNSResolverTest()); } + +private: + void evaluateFooChildTextAndCheckResult(WebKitDOMDocument* document, WebKitDOMXPathNSResolver* resolver) + { + WebKitDOMElement* documentElement = webkit_dom_document_get_document_element(document); + g_assert(WEBKIT_DOM_IS_ELEMENT(documentElement)); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(documentElement)); + + GRefPtr<WebKitDOMXPathResult> result = adoptGRef(webkit_dom_document_evaluate(document, "foo:child/text()", WEBKIT_DOM_NODE(documentElement), resolver, WEBKIT_DOM_XPATH_RESULT_ORDERED_NODE_ITERATOR_TYPE, nullptr, nullptr)); + g_assert(WEBKIT_DOM_IS_XPATH_RESULT(result.get())); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(result.get())); + + WebKitDOMNode* nodeResult = webkit_dom_xpath_result_iterate_next(result.get(), nullptr); + g_assert(WEBKIT_DOM_IS_NODE(nodeResult)); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(nodeResult)); + + GUniquePtr<char> nodeValue(webkit_dom_node_get_node_value(nodeResult)); + g_assert_cmpstr(nodeValue.get(), ==, "SUCCESS"); + } + + bool testXPathNSResolverNative(WebKitWebPage* page) + { + WebKitDOMDocument* document = webkit_web_page_get_dom_document(page); + g_assert(WEBKIT_DOM_IS_DOCUMENT(document)); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(document)); + + GRefPtr<WebKitDOMXPathNSResolver> resolver = adoptGRef(webkit_dom_document_create_ns_resolver(document, WEBKIT_DOM_NODE(webkit_dom_document_get_document_element(document)))); + g_assert(WEBKIT_DOM_IS_XPATH_NS_RESOLVER(resolver.get())); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(resolver.get())); + evaluateFooChildTextAndCheckResult(document, resolver.get()); + + return true; + } + + bool testXPathNSResolverCustom(WebKitWebPage* page) + { + WebKitDOMDocument* document = webkit_web_page_get_dom_document(page); + g_assert(WEBKIT_DOM_IS_DOCUMENT(document)); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(document)); + + GRefPtr<WebKitDOMXPathNSResolver> resolver = adoptGRef(WEBKIT_DOM_XPATH_NS_RESOLVER(g_object_new(webkit_xpath_ns_resolver_get_type(), nullptr))); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(resolver.get())); + evaluateFooChildTextAndCheckResult(document, resolver.get()); + + return true; + } + + bool runTest(const char* testName, WebKitWebPage* page) override + { + if (!strcmp(testName, "native")) + return testXPathNSResolverNative(page); + if (!strcmp(testName, "custom")) + return testXPathNSResolverCustom(page); + + g_assert_not_reached(); + return false; + } +}; + +static void __attribute__((constructor)) registerTests() +{ + REGISTER_TEST(WebKitDOMXPathNSResolverTest, "WebKitDOMXPathNSResolver/native"); + REGISTER_TEST(WebKitDOMXPathNSResolverTest, "WebKitDOMXPathNSResolver/custom"); +} diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/EditorTest.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/EditorTest.cpp new file mode 100644 index 000000000..873344271 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/EditorTest.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2015 Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#include "WebProcessTest.h" +#include <webkit2/webkit-web-extension.h> + +#define WEBKIT_DOM_USE_UNSTABLE_API +#include <webkitdom/WebKitDOMDOMSelection.h> +#include <webkitdom/WebKitDOMDOMWindowUnstable.h> + +class WebKitWebEditorTest : public WebProcessTest { +public: + static std::unique_ptr<WebProcessTest> create() { return std::unique_ptr<WebProcessTest>(new WebKitWebEditorTest()); } + +private: + static void selectionChangedCallback(bool* selectionChanged) + { + *selectionChanged = true; + } + + void testSelectionSelectAll(WebKitWebPage* page) + { + WebKitDOMDocument* document = webkit_web_page_get_dom_document(page); + g_assert(WEBKIT_DOM_IS_DOCUMENT(document)); + + webkit_dom_document_exec_command(document, "SelectAll", false, ""); + } + + void testSelectionCollapse(WebKitWebPage* page) + { + WebKitDOMDocument* document = webkit_web_page_get_dom_document(page); + g_assert(WEBKIT_DOM_IS_DOCUMENT(document)); + GRefPtr<WebKitDOMDOMWindow> domWindow = adoptGRef(webkit_dom_document_get_default_view(document)); + g_assert(WEBKIT_DOM_IS_DOM_WINDOW(domWindow.get())); + GRefPtr<WebKitDOMDOMSelection> domSelection = adoptGRef(webkit_dom_dom_window_get_selection(domWindow.get())); + g_assert(WEBKIT_DOM_IS_DOM_SELECTION(domSelection.get())); + + webkit_dom_dom_selection_collapse_to_start(domSelection.get(), nullptr); + } + + void testSelectionModifyMove(WebKitWebPage* page) + { + WebKitDOMDocument* document = webkit_web_page_get_dom_document(page); + g_assert(WEBKIT_DOM_IS_DOCUMENT(document)); + GRefPtr<WebKitDOMDOMWindow> domWindow = adoptGRef(webkit_dom_document_get_default_view(document)); + g_assert(WEBKIT_DOM_IS_DOM_WINDOW(domWindow.get())); + GRefPtr<WebKitDOMDOMSelection> domSelection = adoptGRef(webkit_dom_dom_window_get_selection(domWindow.get())); + g_assert(WEBKIT_DOM_IS_DOM_SELECTION(domSelection.get())); + + webkit_dom_dom_selection_modify(domSelection.get(), "move", "forward", "character"); + } + + void testSelectionModifyExtend(WebKitWebPage* page) + { + WebKitDOMDocument* document = webkit_web_page_get_dom_document(page); + g_assert(WEBKIT_DOM_IS_DOCUMENT(document)); + GRefPtr<WebKitDOMDOMWindow> domWindow = adoptGRef(webkit_dom_document_get_default_view(document)); + g_assert(WEBKIT_DOM_IS_DOM_WINDOW(domWindow.get())); + GRefPtr<WebKitDOMDOMSelection> domSelection = adoptGRef(webkit_dom_dom_window_get_selection(domWindow.get())); + g_assert(WEBKIT_DOM_IS_DOM_SELECTION(domSelection.get())); + + webkit_dom_dom_selection_modify(domSelection.get(), "extend", "forward", "word"); + } + + void testSelectionUnselect(WebKitWebPage* page) + { + WebKitDOMDocument* document = webkit_web_page_get_dom_document(page); + g_assert(WEBKIT_DOM_IS_DOCUMENT(document)); + + webkit_dom_document_exec_command(document, "Unselect", false, ""); + } + + bool runTest(const char* testName, WebKitWebPage* page) override + { + if (!strcmp(testName, "selection-changed")) { + bool selectionChanged = false; + + WebKitWebEditor* editor = webkit_web_page_get_editor(page); + g_assert(WEBKIT_IS_WEB_EDITOR(editor)); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(editor)); + g_signal_connect_swapped(editor, "selection-changed", G_CALLBACK(selectionChangedCallback), &selectionChanged); + + testSelectionSelectAll(page); + g_assert(selectionChanged); + + selectionChanged = false; + testSelectionCollapse(page); + g_assert(selectionChanged); + + selectionChanged = false; + testSelectionModifyMove(page); + g_assert(selectionChanged); + + selectionChanged = false; + testSelectionModifyExtend(page); + g_assert(selectionChanged); + + selectionChanged = false; + testSelectionUnselect(page); + g_assert(selectionChanged); + + return true; + } + + g_assert_not_reached(); + return false; + } +}; + +static void __attribute__((constructor)) registerTests() +{ + REGISTER_TEST(WebKitWebEditorTest, "WebKitWebEditor/selection-changed"); +} diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/FrameTest.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/FrameTest.cpp index b9c86365b..a32d3e52f 100644 --- a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/FrameTest.cpp +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/FrameTest.cpp @@ -32,6 +32,7 @@ private: { WebKitFrame* frame = webkit_web_page_get_main_frame(page); g_assert(WEBKIT_IS_FRAME(frame)); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(frame)); g_assert(webkit_frame_is_main_frame(frame)); return true; @@ -41,6 +42,7 @@ private: { WebKitFrame* frame = webkit_web_page_get_main_frame(page); g_assert(WEBKIT_IS_FRAME(frame)); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(frame)); g_assert_cmpstr(webkit_web_page_get_uri(page), ==, webkit_frame_get_uri(frame)); return true; @@ -50,6 +52,7 @@ private: { WebKitFrame* frame = webkit_web_page_get_main_frame(page); g_assert(WEBKIT_IS_FRAME(frame)); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(frame)); g_assert(webkit_frame_get_javascript_global_context(frame)); return true; diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestAuthentication.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestAuthentication.cpp index ede422556..9cd9f5e7a 100644 --- a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestAuthentication.cpp +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestAuthentication.cpp @@ -20,7 +20,7 @@ #include "config.h" #include "LoadTrackingTest.h" #include "WebKitTestServer.h" -#include <wtf/gobject/GRefPtr.h> +#include <wtf/glib/GRefPtr.h> static WebKitTestServer* kServer; @@ -187,7 +187,9 @@ static void testWebViewAuthenticationNoCredential(AuthenticationTest* test, gcon static void testWebViewAuthenticationStorage(AuthenticationTest* test, gconstpointer) { // Enable private browsing before authentication request to test that credentials can't be saved. + G_GNUC_BEGIN_IGNORE_DEPRECATIONS; webkit_settings_set_enable_private_browsing(webkit_web_view_get_settings(test->m_webView), TRUE); + G_GNUC_END_IGNORE_DEPRECATIONS; test->loadURI(kServer->getURIForPath("/auth-test.html").data()); WebKitAuthenticationRequest* request = test->waitForAuthenticationRequest(); g_assert(!webkit_authentication_request_get_proposed_credential(request)); @@ -195,8 +197,10 @@ static void testWebViewAuthenticationStorage(AuthenticationTest* test, gconstpoi // If WebKit has been compiled with libsecret, and private browsing is disabled // then check that credentials can be saved. -#if ENABLE(CREDENTIAL_STORAGE) +#if USE(LIBSECRET) + G_GNUC_BEGIN_IGNORE_DEPRECATIONS; webkit_settings_set_enable_private_browsing(webkit_web_view_get_settings(test->m_webView), FALSE); + G_GNUC_END_IGNORE_DEPRECATIONS; test->loadURI(kServer->getURIForPath("/auth-test.html").data()); request = test->waitForAuthenticationRequest(); g_assert(!webkit_authentication_request_get_proposed_credential(request)); @@ -232,6 +236,22 @@ static void testWebViewAuthenticationSuccess(AuthenticationTest* test, gconstpoi g_assert_cmpstr(webkit_web_view_get_title(test->m_webView), ==, authExpectedSuccessTitle); } +static void testWebViewAuthenticationEmptyRealm(AuthenticationTest* test, gconstpointer) +{ + test->loadURI(kServer->getURIForPath("/empty-realm.html").data()); + WebKitAuthenticationRequest* request = test->waitForAuthenticationRequest(); + WebKitCredential* credential = webkit_credential_new(authTestUsername, authTestPassword, WEBKIT_CREDENTIAL_PERSISTENCE_FOR_SESSION); + webkit_authentication_request_authenticate(request, credential); + webkit_credential_free(credential); + test->waitUntilLoadFinished(); + + g_assert_cmpint(test->m_loadEvents.size(), ==, 3); + g_assert_cmpint(test->m_loadEvents[0], ==, LoadTrackingTest::ProvisionalLoadStarted); + g_assert_cmpint(test->m_loadEvents[1], ==, LoadTrackingTest::LoadCommitted); + g_assert_cmpint(test->m_loadEvents[2], ==, LoadTrackingTest::LoadFinished); + g_assert_cmpstr(webkit_web_view_get_title(test->m_webView), ==, authExpectedSuccessTitle); +} + static void serverCallback(SoupServer*, SoupMessage* message, const char* path, GHashTable*, SoupClientContext*, void*) { if (message->method != SOUP_METHOD_GET) { @@ -239,7 +259,7 @@ static void serverCallback(SoupServer*, SoupMessage* message, const char* path, return; } - if (!strcmp(path, "/auth-test.html")) { + if (!strcmp(path, "/auth-test.html") || !strcmp(path, "/empty-realm.html")) { const char* authorization = soup_message_headers_get_one(message->request_headers, "Authorization"); // Require authentication. if (!g_strcmp0(authorization, authExpectedAuthorization)) { @@ -250,7 +270,10 @@ static void serverCallback(SoupServer*, SoupMessage* message, const char* path, } else if (++AuthenticationTest::authenticationRetries < 3) { // No or invalid authorization header provided by the client, request authentication twice then fail. soup_message_set_status(message, SOUP_STATUS_UNAUTHORIZED); - soup_message_headers_append(message->response_headers, "WWW-Authenticate", "Basic realm=\"my realm\""); + if (!strcmp(path, "/empty-realm.html")) + soup_message_headers_append(message->response_headers, "WWW-Authenticate", "Basic"); + else + soup_message_headers_append(message->response_headers, "WWW-Authenticate", "Basic realm=\"my realm\""); // Include a failure message in case the user attempts to proceed without authentication. soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, authFailureHTMLString, strlen(authFailureHTMLString)); } else { @@ -272,12 +295,11 @@ void beforeAll() AuthenticationTest::add("WebKitWebView", "authentication-request", testWebViewAuthenticationRequest); AuthenticationTest::add("WebKitWebView", "authentication-cancel", testWebViewAuthenticationCancel); AuthenticationTest::add("WebKitWebView", "authentication-load-cancelled", testWebViewAuthenticationLoadCancelled); + AuthenticationTest::add("WebKitWebView", "authentication-success", testWebViewAuthenticationSuccess); AuthenticationTest::add("WebKitWebView", "authentication-failure", testWebViewAuthenticationFailure); AuthenticationTest::add("WebKitWebView", "authentication-no-credential", testWebViewAuthenticationNoCredential); AuthenticationTest::add("WebKitWebView", "authentication-storage", testWebViewAuthenticationStorage); - // Testing authentication success must be done last because libsoup will never fire - // the authenticate signal again once authentication is successful. - AuthenticationTest::add("WebKitWebView", "authentication-success", testWebViewAuthenticationSuccess); + AuthenticationTest::add("WebKitWebView", "authentication-empty-realm", testWebViewAuthenticationEmptyRealm); } void afterAll() diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestAutocleanups.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestAutocleanups.cpp new file mode 100644 index 000000000..975688ced --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestAutocleanups.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2015 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#include "WebViewTest.h" +#include <webkit2/webkit2.h> + +#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC + +static void testUIProcessAutocleanups(WebViewTest* test, gconstpointer) +{ + // Sanity-check a couple UI process API autocleanups that are easy to test.... + g_autoptr(WebKitWebContext) context = webkit_web_context_new(); + g_assert(WEBKIT_IS_WEB_CONTEXT(context)); + test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(context)); + + g_autoptr(WebKitWebsiteDataManager) manager = webkit_website_data_manager_new(nullptr); + g_assert(WEBKIT_IS_WEBSITE_DATA_MANAGER(manager)); + test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(context)); + + g_autoptr(WebKitUserScript) userScript = webkit_user_script_new("", + WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES, WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_START, + nullptr, nullptr); + g_assert(userScript); + // Not a GObject, so just checking that this doesn't crash.... +} + +static void testWebProcessAutocleanups(WebViewTest* test, gconstpointer) +{ + static const char* testHTML = "<html><body></body></html>"; + test->loadHtml(testHTML, nullptr); + test->waitUntilLoadFinished(); + + g_assert(test->runWebProcessTest("Autocleanups", "web-process-autocleanups")); +} + +void beforeAll() +{ + WebViewTest::add("Autocleanups", "ui-process-autocleanups", testUIProcessAutocleanups); + WebViewTest::add("Autocleanups", "web-process-autocleanups", testWebProcessAutocleanups); +} + +#else + +void beforeAll() +{ +} + +#endif // G_DEFINE_AUTOPTR_CLEANUP_FUNC + +void afterAll() +{ +} diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestBackForwardList.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestBackForwardList.cpp index 65c3d2d7c..fd2fbf5f2 100644 --- a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestBackForwardList.cpp +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestBackForwardList.cpp @@ -264,6 +264,140 @@ static void testBackForwardListLimitAndCache(BackForwardListTest* test, gconstpo g_assert_cmpuint(webkit_back_forward_list_get_length(test->m_list), ==, kBackForwardListLimit); } +static void testWebKitWebViewSessionState(BackForwardListTest* test, gconstpointer) +{ + WebKitWebViewSessionState* state = webkit_web_view_get_session_state(test->m_webView); + g_assert(state); + GRefPtr<WebKitWebView> view = WEBKIT_WEB_VIEW(webkit_web_view_new()); + WebKitBackForwardList* bfList = webkit_web_view_get_back_forward_list(view.get()); + g_assert_cmpuint(webkit_back_forward_list_get_length(bfList), ==, 0); + webkit_web_view_restore_session_state(view.get(), state); + g_assert_cmpuint(webkit_back_forward_list_get_length(bfList), ==, 0); + GRefPtr<GBytes> data = adoptGRef(webkit_web_view_session_state_serialize(state)); + g_assert(data); + state = webkit_web_view_session_state_new(data.get()); + g_assert(state); + view = WEBKIT_WEB_VIEW(webkit_web_view_new()); + bfList = webkit_web_view_get_back_forward_list(view.get()); + g_assert_cmpuint(webkit_back_forward_list_get_length(bfList), ==, 0); + webkit_web_view_restore_session_state(view.get(), state); + g_assert_cmpuint(webkit_back_forward_list_get_length(bfList), ==, 0); + webkit_web_view_session_state_unref(state); + + CString uriPage1 = kServer->getURIForPath("/Page1"); + test->m_changedFlags = BackForwardListTest::CurrentItem | BackForwardListTest::AddedItem; + test->loadURI(uriPage1.data()); + test->waitUntilLoadFinished(); + + CString uriPage2 = kServer->getURIForPath("/Page2"); + test->m_changedFlags = BackForwardListTest::CurrentItem | BackForwardListTest::AddedItem; + test->loadURI(uriPage2.data()); + test->waitUntilLoadFinished(); + + CString uriPage3 = kServer->getURIForPath("/Page3"); + test->m_changedFlags = BackForwardListTest::CurrentItem | BackForwardListTest::AddedItem; + test->loadURI(uriPage3.data()); + test->waitUntilLoadFinished(); + + test->m_changedFlags = BackForwardListTest::CurrentItem; + test->goBack(); + test->waitUntilLoadFinished(); + + state = webkit_web_view_get_session_state(test->m_webView); + g_assert(state); + + g_assert_cmpuint(webkit_back_forward_list_get_length(bfList), ==, 0); + webkit_web_view_restore_session_state(view.get(), state); + g_assert_cmpuint(webkit_back_forward_list_get_length(bfList), ==, 3); + + BackForwardListTest::checkItem(webkit_back_forward_list_get_nth_item(bfList, -1), "Page1", uriPage1.data(), uriPage1.data()); + BackForwardListTest::checkItem(webkit_back_forward_list_get_current_item(bfList), "Page2", uriPage2.data(), uriPage2.data()); + BackForwardListTest::checkItem(webkit_back_forward_list_get_nth_item(bfList, 1), "Page3", uriPage3.data(), uriPage3.data()); + + data = adoptGRef(webkit_web_view_session_state_serialize(state)); + g_assert(data); + webkit_web_view_session_state_unref(state); + state = webkit_web_view_session_state_new(data.get()); + g_assert(state); + + view = WEBKIT_WEB_VIEW(webkit_web_view_new()); + bfList = webkit_web_view_get_back_forward_list(view.get()); + g_assert_cmpuint(webkit_back_forward_list_get_length(bfList), ==, 0); + webkit_web_view_restore_session_state(view.get(), state); + g_assert_cmpuint(webkit_back_forward_list_get_length(bfList), ==, 3); + webkit_web_view_session_state_unref(state); + + BackForwardListTest::checkItem(webkit_back_forward_list_get_nth_item(bfList, -1), "Page1", uriPage1.data(), uriPage1.data()); + BackForwardListTest::checkItem(webkit_back_forward_list_get_current_item(bfList), "Page2", uriPage2.data(), uriPage2.data()); + BackForwardListTest::checkItem(webkit_back_forward_list_get_nth_item(bfList, 1), "Page3", uriPage3.data(), uriPage3.data()); + + static const char* invalidSessionData = "invalid session data"; + data = adoptGRef(g_bytes_new_static(invalidSessionData, strlen(invalidSessionData))); + g_assert(!webkit_web_view_session_state_new(data.get())); +} + +static void testWebKitWebViewSessionStateWithFormData(BackForwardListTest* test, gconstpointer) +{ + GUniquePtr<char> htmlPath(g_build_filename(Test::getResourcesDir(Test::WebKit2Resources).data(), "simple-form.html", nullptr)); + GUniquePtr<char> htmlURL(g_filename_to_uri(htmlPath.get(), nullptr, nullptr)); + test->m_changedFlags = BackForwardListTest::CurrentItem | BackForwardListTest::AddedItem; + test->loadURI(htmlURL.get()); + test->waitUntilLoadFinished(); + + webkit_web_view_run_javascript(test->m_webView, "submitForm();", nullptr, nullptr, nullptr); + test->waitUntilLoadFinished(); + + WebKitWebViewSessionState* state = webkit_web_view_get_session_state(test->m_webView); + g_assert(state); + GRefPtr<WebKitWebView> view = WEBKIT_WEB_VIEW(webkit_web_view_new()); + WebKitBackForwardList* bfList = webkit_web_view_get_back_forward_list(view.get()); + g_assert_cmpuint(webkit_back_forward_list_get_length(bfList), ==, 0); + webkit_web_view_restore_session_state(view.get(), state); + g_assert_cmpuint(webkit_back_forward_list_get_length(bfList), ==, 2); + GRefPtr<GBytes> data = adoptGRef(webkit_web_view_session_state_serialize(state)); + g_assert(data); + state = webkit_web_view_session_state_new(data.get()); + g_assert(state); + view = WEBKIT_WEB_VIEW(webkit_web_view_new()); + bfList = webkit_web_view_get_back_forward_list(view.get()); + g_assert_cmpuint(webkit_back_forward_list_get_length(bfList), ==, 0); + webkit_web_view_restore_session_state(view.get(), state); + g_assert_cmpuint(webkit_back_forward_list_get_length(bfList), ==, 2); + webkit_web_view_session_state_unref(state); +} + +static void viewLoadChanged(WebKitWebView* webView, WebKitLoadEvent loadEvent, GMainLoop* mainLoop) +{ + if (loadEvent == WEBKIT_LOAD_FINISHED) + g_main_loop_quit(mainLoop); +} + +static void testWebKitWebViewNavigationAfterSessionRestore(BackForwardListTest* test, gconstpointer) +{ + // This test checks that a normal load after a session restore with a BackForard list having + // forward items doesn't produce any runtime critical warning. See https://bugs.webkit.org/show_bug.cgi?id=153233. + GRefPtr<WebKitWebView> view = WEBKIT_WEB_VIEW(webkit_web_view_new()); + g_signal_connect(view.get(), "load-changed", G_CALLBACK(viewLoadChanged), test->m_mainLoop); + + webkit_web_view_load_uri(view.get(), kServer->getURIForPath("/Page1").data()); + g_main_loop_run(test->m_mainLoop); + webkit_web_view_load_uri(view.get(), kServer->getURIForPath("/Page2").data()); + g_main_loop_run(test->m_mainLoop); + webkit_web_view_load_uri(view.get(), kServer->getURIForPath("/Page3").data()); + g_main_loop_run(test->m_mainLoop); + webkit_web_view_go_back(view.get()); + g_main_loop_run(test->m_mainLoop); + + WebKitWebViewSessionState* state = webkit_web_view_get_session_state(view.get()); + webkit_web_view_restore_session_state(test->m_webView, state); + webkit_web_view_session_state_unref(state); + + // A normal load after a session restore should remove the forward list, add the new item and update the current one. + test->m_changedFlags = BackForwardListTest::CurrentItem | BackForwardListTest::AddedItem | BackForwardListTest::RemovedItems; + test->loadURI(kServer->getURIForPath("/Page4").data()); + test->waitUntilLoadFinished(); +} + void beforeAll() { kServer = new WebKitTestServer(); @@ -271,6 +405,9 @@ void beforeAll() BackForwardListTest::add("BackForwardList", "navigation", testBackForwardListNavigation); BackForwardListTest::add("BackForwardList", "list-limit-and-cache", testBackForwardListLimitAndCache); + BackForwardListTest::add("WebKitWebView", "session-state", testWebKitWebViewSessionState); + BackForwardListTest::add("WebKitWebView", "session-state-with-form-data", testWebKitWebViewSessionStateWithFormData); + BackForwardListTest::add("WebKitWebView", "navigation-after-session-restore", testWebKitWebViewNavigationAfterSessionRestore); } void afterAll() diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestConsoleMessage.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestConsoleMessage.cpp new file mode 100644 index 000000000..2595a8a42 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestConsoleMessage.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2015 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2,1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#include "WebViewTest.h" +#include <gtk/gtk.h> +#include <webkit2/webkit2.h> + +class ConsoleMessageTest : public WebViewTest { +public: + MAKE_GLIB_TEST_FIXTURE(ConsoleMessageTest); + + // This should be keep in sync with the public enums in WebKitConsoleMessage.h. + enum class MessageSource { JavaScript, Network, ConsoleAPI, Security, Other }; + enum class MessageLevel { Info, Log, Warning, Error, Debug }; + struct ConsoleMessage { + bool operator==(const ConsoleMessage& other) + { + return source == other.source + && level == other.level + && message == other.message + && lineNumber == other.lineNumber + && sourceID == other.sourceID; + } + + MessageSource source; + MessageLevel level; + CString message; + unsigned lineNumber; + CString sourceID; + }; + + static void consoleMessageReceivedCallback(WebKitUserContentManager*, WebKitJavascriptResult* message, ConsoleMessageTest* test) + { + g_assert(message); + GUniquePtr<char> messageString(WebViewTest::javascriptResultToCString(message)); + GRefPtr<GVariant> variant = g_variant_parse(G_VARIANT_TYPE("(uusus)"), messageString.get(), nullptr, nullptr, nullptr); + g_assert(variant.get()); + + unsigned source, level, lineNumber; + const char* messageText; + const char* sourceID; + g_variant_get(variant.get(), "(uu&su&s)", &source, &level, &messageText, &lineNumber, &sourceID); + test->m_consoleMessage = { static_cast<ConsoleMessageTest::MessageSource>(source), static_cast<ConsoleMessageTest::MessageLevel>(level), messageText, lineNumber, sourceID }; + + g_main_loop_quit(test->m_mainLoop); + } + + ConsoleMessageTest() + { + webkit_user_content_manager_register_script_message_handler(m_userContentManager.get(), "console"); + g_signal_connect(m_userContentManager.get(), "script-message-received::console", G_CALLBACK(consoleMessageReceivedCallback), this); + } + + ~ConsoleMessageTest() + { + g_signal_handlers_disconnect_matched(m_userContentManager.get(), G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, this); + webkit_user_content_manager_unregister_script_message_handler(m_userContentManager.get(), "console"); + } + + void waitUntilConsoleMessageReceived() + { + g_main_loop_run(m_mainLoop); + } + + ConsoleMessage m_consoleMessage; +}; + +static void testWebKitConsoleMessageConsoleAPI(ConsoleMessageTest* test, gconstpointer) +{ + ConsoleMessageTest::ConsoleMessage referenceMessage = { ConsoleMessageTest::MessageSource::ConsoleAPI, ConsoleMessageTest::MessageLevel::Log, "Log Console Message", 1, "http://foo.com/bar" }; + test->loadHtml("<html><body onload='console.log(\"Log Console Message\");'></body></html>", "http://foo.com/bar"); + test->waitUntilConsoleMessageReceived(); + g_assert(test->m_consoleMessage == referenceMessage); + + referenceMessage.level = ConsoleMessageTest::MessageLevel::Info; + referenceMessage.message = "Info Console Message"; + test->loadHtml("<html><body onload='console.info(\"Info Console Message\");'></body></html>", "http://foo.com/bar"); + test->waitUntilConsoleMessageReceived(); + g_assert(test->m_consoleMessage == referenceMessage); + + referenceMessage.level = ConsoleMessageTest::MessageLevel::Warning; + referenceMessage.message = "Warning Console Message"; + test->loadHtml("<html><body onload='console.warn(\"Warning Console Message\");'></body></html>", "http://foo.com/bar"); + test->waitUntilConsoleMessageReceived(); + g_assert(test->m_consoleMessage == referenceMessage); + + referenceMessage.level = ConsoleMessageTest::MessageLevel::Error; + referenceMessage.message = "Error Console Message"; + test->loadHtml("<html><body onload='console.error(\"Error Console Message\");'></body></html>", "http://foo.com/bar"); + test->waitUntilConsoleMessageReceived(); + g_assert(test->m_consoleMessage == referenceMessage); + + referenceMessage.level = ConsoleMessageTest::MessageLevel::Debug; + referenceMessage.message = "Debug Console Message"; + test->loadHtml("<html><body onload='console.debug(\"Debug Console Message\");'></body></html>", "http://foo.com/bar"); + test->waitUntilConsoleMessageReceived(); + g_assert(test->m_consoleMessage == referenceMessage); +} + +static void testWebKitConsoleMessageJavaScriptException(ConsoleMessageTest* test, gconstpointer) +{ + ConsoleMessageTest::ConsoleMessage referenceMessage = { ConsoleMessageTest::MessageSource::JavaScript, ConsoleMessageTest::MessageLevel::Error, + "ReferenceError: Can't find variable: foo", 1, "http://foo.com/bar" }; + test->loadHtml("<html><body onload='foo()'></body></html>", "http://foo.com/bar"); + test->waitUntilConsoleMessageReceived(); + g_assert(test->m_consoleMessage == referenceMessage); +} + +static void testWebKitConsoleMessageNetworkError(ConsoleMessageTest* test, gconstpointer) +{ + ConsoleMessageTest::ConsoleMessage referenceMessage = { ConsoleMessageTest::MessageSource::Network, ConsoleMessageTest::MessageLevel::Error, + "Failed to load resource: The resource at '/org/webkit/webkit2gtk/tests/not-found.css' does not exist", 0, "resource:///org/webkit/webkit2gtk/tests/not-found.css" }; + test->loadHtml("<html><head><link rel='stylesheet' href='not-found.css' type='text/css'></head><body></body></html>", "resource:///org/webkit/webkit2gtk/tests/"); + test->waitUntilConsoleMessageReceived(); + g_assert(test->m_consoleMessage == referenceMessage); +} + +static void testWebKitConsoleMessageSecurityError(ConsoleMessageTest* test, gconstpointer) +{ + ConsoleMessageTest::ConsoleMessage referenceMessage = { ConsoleMessageTest::MessageSource::Security, ConsoleMessageTest::MessageLevel::Error, + "Not allowed to load local resource: file:///foo/bar/source.png", 1, "http://foo.com/bar" }; + test->loadHtml("<html><body><img src=\"file:///foo/bar/source.png\"/></body></html>", "http://foo.com/bar"); + test->waitUntilConsoleMessageReceived(); + g_assert(test->m_consoleMessage == referenceMessage); +} + +void beforeAll() +{ + ConsoleMessageTest::add("WebKitConsoleMessage", "console-api", testWebKitConsoleMessageConsoleAPI); + ConsoleMessageTest::add("WebKitConsoleMessage", "js-exception", testWebKitConsoleMessageJavaScriptException); + ConsoleMessageTest::add("WebKitConsoleMessage", "network-error", testWebKitConsoleMessageNetworkError); + ConsoleMessageTest::add("WebKitConsoleMessage", "security-error", testWebKitConsoleMessageSecurityError); +} + +void afterAll() +{ +} diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestContextMenu.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestContextMenu.cpp index a91e78741..3d7ab00cc 100644 --- a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestContextMenu.cpp +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestContextMenu.cpp @@ -19,7 +19,8 @@ #include "config.h" #include "WebViewTest.h" -#include <wtf/gobject/GRefPtr.h> +#include <wtf/Vector.h> +#include <wtf/glib/GRefPtr.h> class ContextMenuTest: public WebViewTest { public: @@ -32,10 +33,24 @@ public: void checkContextMenuEvent(GdkEvent* event) { g_assert(event); - g_assert_cmpint(event->type, ==, GDK_BUTTON_PRESS); - g_assert_cmpint(event->button.button, ==, 3); - g_assert_cmpint(event->button.x, ==, m_menuPositionX); - g_assert_cmpint(event->button.y, ==, m_menuPositionY); + g_assert_cmpint(event->type, ==, m_expectedEventType); + + switch (m_expectedEventType) { + case GDK_BUTTON_PRESS: + g_assert_cmpint(event->button.button, ==, 3); + g_assert_cmpint(event->button.x, ==, m_menuPositionX); + g_assert_cmpint(event->button.y, ==, m_menuPositionY); + break; + case GDK_KEY_PRESS: + g_assert_cmpint(event->key.keyval, ==, GDK_KEY_Menu); + break; + case GDK_NOTHING: + // GDK_NOTHING means that the context menu was triggered by the + // popup-menu signal. We don't have anything to check here. + break; + default: + g_assert_not_reached(); + } } static gboolean contextMenuCallback(WebKitWebView* webView, WebKitContextMenu* contextMenu, GdkEvent* event, WebKitHitTestResult* hitTestResult, ContextMenuTest* test) @@ -57,6 +72,7 @@ public: ContextMenuTest() : m_menuPositionX(0) , m_menuPositionY(0) + , m_expectedEventType(GDK_BUTTON_PRESS) { g_signal_connect(m_webView, "context-menu", G_CALLBACK(contextMenuCallback), this); g_signal_connect(m_webView, "context-menu-dismissed", G_CALLBACK(contextMenuDismissedCallback), this); @@ -92,17 +108,6 @@ public: return 0; } - bool shouldShowInputMethodsMenu() - { - GtkSettings* settings = gtk_widget_get_settings(GTK_WIDGET(m_webView)); - if (!settings) - return true; - - gboolean showInputMethodMenu; - g_object_get(settings, "gtk-show-input-method-menu", &showInputMethodMenu, NULL); - return showInputMethodMenu; - } - void checkActionState(GtkAction* action, unsigned state) { if (state & Visible) @@ -209,6 +214,7 @@ public: void showContextMenuAndWaitUntilFinished() { + m_expectedEventType = GDK_BUTTON_PRESS; showContextMenuAtPositionAndWaitUntilFinished(0, 0); } @@ -224,8 +230,35 @@ public: g_main_loop_run(m_mainLoop); } + static gboolean emitPopupMenuSignalIdleCallback(ContextMenuTest* test) + { + test->emitPopupMenuSignal(); + return FALSE; + } + + void showContextMenuTriggeredByPopupEventAndWaitUntilFinished() + { + m_expectedEventType = GDK_NOTHING; + g_idle_add(reinterpret_cast<GSourceFunc>(emitPopupMenuSignalIdleCallback), this); + g_main_loop_run(m_mainLoop); + } + + static gboolean simulateMenuKeyIdleCallback(ContextMenuTest* test) + { + test->keyStroke(GDK_KEY_Menu); + return FALSE; + } + + void showContextMenuTriggeredByContextMenuKeyAndWaitUntilFinished() + { + m_expectedEventType = GDK_KEY_PRESS; + g_idle_add(reinterpret_cast<GSourceFunc>(simulateMenuKeyIdleCallback), this); + g_main_loop_run(m_mainLoop); + } + double m_menuPositionX; double m_menuPositionY; + GdkEventType m_expectedEventType; }; class ContextMenuDefaultTest: public ContextMenuTest { @@ -239,7 +272,8 @@ public: LinkImage, Video, Audio, - Editable + Editable, + Selection }; ContextMenuDefaultTest() @@ -257,6 +291,7 @@ public: g_assert(!webkit_hit_test_result_context_is_image(hitTestResult)); g_assert(!webkit_hit_test_result_context_is_media(hitTestResult)); g_assert(!webkit_hit_test_result_context_is_editable(hitTestResult)); + g_assert(!webkit_hit_test_result_context_is_selection(hitTestResult)); iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_GO_BACK, Visible); iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_GO_FORWARD, Visible); iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_STOP, Visible); @@ -267,6 +302,7 @@ public: g_assert(!webkit_hit_test_result_context_is_image(hitTestResult)); g_assert(!webkit_hit_test_result_context_is_media(hitTestResult)); g_assert(!webkit_hit_test_result_context_is_editable(hitTestResult)); + g_assert(!webkit_hit_test_result_context_is_selection(hitTestResult)); iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_OPEN_LINK, Visible | Enabled); iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_OPEN_LINK_IN_NEW_WINDOW, Visible | Enabled); iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_DOWNLOAD_LINK_TO_DISK, Visible | Enabled); @@ -277,6 +313,7 @@ public: g_assert(webkit_hit_test_result_context_is_image(hitTestResult)); g_assert(!webkit_hit_test_result_context_is_media(hitTestResult)); g_assert(!webkit_hit_test_result_context_is_editable(hitTestResult)); + g_assert(!webkit_hit_test_result_context_is_selection(hitTestResult)); iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_OPEN_IMAGE_IN_NEW_WINDOW, Visible | Enabled); iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_DOWNLOAD_IMAGE_TO_DISK, Visible | Enabled); iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_COPY_IMAGE_TO_CLIPBOARD, Visible | Enabled); @@ -287,6 +324,7 @@ public: g_assert(webkit_hit_test_result_context_is_image(hitTestResult)); g_assert(!webkit_hit_test_result_context_is_media(hitTestResult)); g_assert(!webkit_hit_test_result_context_is_editable(hitTestResult)); + g_assert(!webkit_hit_test_result_context_is_selection(hitTestResult)); iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_OPEN_LINK, Visible | Enabled); iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_OPEN_LINK_IN_NEW_WINDOW, Visible | Enabled); iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_DOWNLOAD_LINK_TO_DISK, Visible | Enabled); @@ -302,6 +340,7 @@ public: g_assert(!webkit_hit_test_result_context_is_image(hitTestResult)); g_assert(webkit_hit_test_result_context_is_media(hitTestResult)); g_assert(!webkit_hit_test_result_context_is_editable(hitTestResult)); + g_assert(!webkit_hit_test_result_context_is_selection(hitTestResult)); iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_MEDIA_PLAY, Visible | Enabled); iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_MEDIA_MUTE, Visible); iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_TOGGLE_MEDIA_CONTROLS, Visible | Enabled | Checked); @@ -317,6 +356,7 @@ public: g_assert(!webkit_hit_test_result_context_is_image(hitTestResult)); g_assert(webkit_hit_test_result_context_is_media(hitTestResult)); g_assert(!webkit_hit_test_result_context_is_editable(hitTestResult)); + g_assert(!webkit_hit_test_result_context_is_selection(hitTestResult)); iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_MEDIA_PLAY, Visible | Enabled); iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_MEDIA_MUTE, Visible); iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_TOGGLE_MEDIA_CONTROLS, Visible | Enabled | Checked); @@ -332,6 +372,7 @@ public: g_assert(!webkit_hit_test_result_context_is_image(hitTestResult)); g_assert(!webkit_hit_test_result_context_is_media(hitTestResult)); g_assert(webkit_hit_test_result_context_is_editable(hitTestResult)); + g_assert(!webkit_hit_test_result_context_is_selection(hitTestResult)); iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_CUT, Visible); iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_COPY, Visible); iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_PASTE, Visible | Enabled); @@ -339,10 +380,16 @@ public: iter = checkCurrentItemIsSeparatorAndGetNext(iter); iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_SELECT_ALL, Visible | Enabled); iter = checkCurrentItemIsSeparatorAndGetNext(iter); - if (shouldShowInputMethodsMenu()) - iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_INPUT_METHODS, Visible | Enabled); iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_UNICODE, Visible | Enabled); break; + case Selection: + g_assert(!webkit_hit_test_result_context_is_link(hitTestResult)); + g_assert(!webkit_hit_test_result_context_is_image(hitTestResult)); + g_assert(!webkit_hit_test_result_context_is_media(hitTestResult)); + g_assert(!webkit_hit_test_result_context_is_editable(hitTestResult)); + g_assert(webkit_hit_test_result_context_is_selection(hitTestResult)); + iter = checkCurrentItemIsStockActionAndGetNext(iter, WEBKIT_CONTEXT_MENU_ACTION_COPY, Visible | Enabled); + break; default: g_assert_not_reached(); } @@ -361,21 +408,39 @@ public: DefaultMenuType m_expectedMenuType; }; -static void testContextMenuDefaultMenu(ContextMenuDefaultTest* test, gconstpointer) +static void prepareContextMenuTestView(ContextMenuDefaultTest* test) { - test->showInWindowAndWaitUntilMapped(); - + GUniquePtr<char> baseDir(g_strdup_printf("file://%s/", Test::getResourcesDir().data())); const char* linksHTML = "<html><body>" " <a style='position:absolute; left:1; top:1' href='http://www.webkitgtk.org' title='WebKitGTK+ Title'>WebKitGTK+ Website</a>" - " <img style='position:absolute; left:1; top:10' src='0xdeadbeef' width=5 height=5></img>" - " <a style='position:absolute; left:1; top:20' href='http://www.webkitgtk.org/logo' title='WebKitGTK+ Logo'><img src='0xdeadbeef' width=5 height=5></img></a>" + " <img style='position:absolute; left:1; top:10' src='blank.ico' width=5 height=5></img>" + " <a style='position:absolute; left:1; top:20' href='http://www.webkitgtk.org/logo' title='WebKitGTK+ Logo'><img src='blank.ico' width=5 height=5></img></a>" " <input style='position:absolute; left:1; top:30' size='10'></input>" - " <video style='position:absolute; left:1; top:50' width='300' height='300' controls='controls' preload='none'><source src='movie.ogg' type='video/ogg' /></video>" - " <audio style='position:absolute; left:1; top:60' width='50' height='20' controls='controls' preload='none'><source src='track.mp3' type='audio/mp3' /></audio>" + " <video style='position:absolute; left:1; top:50' width='300' height='300' controls='controls' preload='none'><source src='silence.mpg' type='video/mpeg' /></video>" + " <audio style='position:absolute; left:1; top:60' width='50' height='20' controls='controls' preload='none'><source src='track.ogg' type='audio/ogg' /></audio>" + " <p style='position:absolute; left:1; top:90' id='text_to_select'>Lorem ipsum.</p>" + " <script>" + " window.getSelection().removeAllRanges();" + " var select_range = document.createRange();" + " select_range.selectNodeContents(document.getElementById('text_to_select'));" + " window.getSelection().addRange(select_range);" + " </script>" "</body></html>"; - test->loadHtml(linksHTML, "file:///"); + test->loadHtml(linksHTML, baseDir.get()); test->waitUntilLoadFinished(); +} + +static void testContextMenuDefaultMenu(ContextMenuDefaultTest* test, gconstpointer) +{ + test->showInWindowAndWaitUntilMapped(); + + prepareContextMenuTestView(test); + + // Context menu for selection. + // This test should always be the first because any other click removes the selection. + test->m_expectedMenuType = ContextMenuDefaultTest::Selection; + test->showContextMenuAtPositionAndWaitUntilFinished(2, 115); // Context menu for document. test->m_expectedMenuType = ContextMenuDefaultTest::Navigation; @@ -410,6 +475,26 @@ static void testContextMenuDefaultMenu(ContextMenuDefaultTest* test, gconstpoint test->showContextMenuAtPositionAndWaitUntilFinished(5, 35); } +static void testPopupEventSignal(ContextMenuDefaultTest* test, gconstpointer) +{ + test->showInWindowAndWaitUntilMapped(); + + prepareContextMenuTestView(test); + + test->m_expectedMenuType = ContextMenuDefaultTest::Selection; + test->showContextMenuTriggeredByPopupEventAndWaitUntilFinished(); +} + +static void testContextMenuKey(ContextMenuDefaultTest* test, gconstpointer) +{ + test->showInWindowAndWaitUntilMapped(); + + prepareContextMenuTestView(test); + + test->m_expectedMenuType = ContextMenuDefaultTest::Selection; + test->showContextMenuTriggeredByContextMenuKeyAndWaitUntilFinished(); +} + class ContextMenuCustomTest: public ContextMenuTest { public: MAKE_GLIB_TEST_FIXTURE(ContextMenuCustomTest); @@ -742,124 +827,173 @@ static void testContextMenuDismissed(ContextMenuDismissedTest* test, gconstpoint g_assert(test->m_dismissed); } -class ContextMenuSmartSeparatorsTest: public ContextMenuTest { +class ContextMenuWebExtensionTest: public ContextMenuTest { public: - MAKE_GLIB_TEST_FIXTURE(ContextMenuSmartSeparatorsTest); + MAKE_GLIB_TEST_FIXTURE(ContextMenuWebExtensionTest); - bool contextMenu(WebKitContextMenu* contextMenu, GdkEvent*, WebKitHitTestResult*) + void deserializeContextMenuFromUserData(GVariant* userData) { - webkit_context_menu_remove_all(contextMenu); + m_actions.clear(); + if (!userData) + return; - webkit_context_menu_append(contextMenu, webkit_context_menu_item_new_separator()); - webkit_context_menu_append(contextMenu, webkit_context_menu_item_new_separator()); - webkit_context_menu_append(contextMenu, webkit_context_menu_item_new_from_stock_action(WEBKIT_CONTEXT_MENU_ACTION_GO_BACK)); - webkit_context_menu_append(contextMenu, webkit_context_menu_item_new_from_stock_action(WEBKIT_CONTEXT_MENU_ACTION_GO_FORWARD)); - webkit_context_menu_append(contextMenu, webkit_context_menu_item_new_separator()); - webkit_context_menu_append(contextMenu, webkit_context_menu_item_new_separator()); - webkit_context_menu_append(contextMenu, webkit_context_menu_item_new_from_stock_action(WEBKIT_CONTEXT_MENU_ACTION_COPY)); - webkit_context_menu_append(contextMenu, webkit_context_menu_item_new_separator()); - webkit_context_menu_append(contextMenu, webkit_context_menu_item_new_from_stock_action(WEBKIT_CONTEXT_MENU_ACTION_INSPECT_ELEMENT)); - webkit_context_menu_append(contextMenu, webkit_context_menu_item_new_separator()); - webkit_context_menu_append(contextMenu, webkit_context_menu_item_new_separator()); + GVariantIter iter; + g_variant_iter_init(&iter, userData); + m_actions.reserveInitialCapacity(g_variant_iter_n_children(&iter)); + + uint32_t item; + while (g_variant_iter_next(&iter, "u", &item)) + m_actions.uncheckedAppend(static_cast<WebKitContextMenuAction>(item)); + } + + bool contextMenu(WebKitContextMenu* menu, GdkEvent*, WebKitHitTestResult*) + { + deserializeContextMenuFromUserData(webkit_context_menu_get_user_data(menu)); + GList* items = webkit_context_menu_get_items(menu); + g_assert_cmpuint(g_list_length(items), ==, m_actions.size()); + + unsigned actionIndex = 0; + for (GList* it = items; it; it = g_list_next(it)) { + WebKitContextMenuItem* item = WEBKIT_CONTEXT_MENU_ITEM(it->data); + g_assert_cmpuint(webkit_context_menu_item_get_stock_action(item), ==, m_actions[actionIndex++]); + } quitMainLoop(); - return false; + return true; + } + + Vector<WebKitContextMenuAction> m_actions; +}; + +static void testContextMenuWebExtensionMenu(ContextMenuWebExtensionTest* test, gconstpointer) +{ + test->showInWindowAndWaitUntilMapped(); + test->loadHtml("<html><body>WebKitGTK+ Context menu tests<br>" + "<a style='position:absolute; left:1; top:10' href='http://www.webkitgtk.org'>WebKitGTK+ Website</a></body></html>", + "ContextMenuTestDefault"); + test->waitUntilLoadFinished(); + + // Default context menu. + test->showContextMenuAtPositionAndWaitUntilFinished(1, 1); + g_assert_cmpuint(test->m_actions.size(), ==, 4); + g_assert_cmpuint(test->m_actions[0], ==, WEBKIT_CONTEXT_MENU_ACTION_GO_BACK); + g_assert_cmpuint(test->m_actions[1], ==, WEBKIT_CONTEXT_MENU_ACTION_GO_FORWARD); + g_assert_cmpuint(test->m_actions[2], ==, WEBKIT_CONTEXT_MENU_ACTION_STOP); + g_assert_cmpuint(test->m_actions[3], ==, WEBKIT_CONTEXT_MENU_ACTION_RELOAD); + + // Link menu. + test->showContextMenuAtPositionAndWaitUntilFinished(1, 11); + g_assert_cmpuint(test->m_actions.size(), ==, 4); + g_assert_cmpuint(test->m_actions[0], ==, WEBKIT_CONTEXT_MENU_ACTION_OPEN_LINK); + g_assert_cmpuint(test->m_actions[1], ==, WEBKIT_CONTEXT_MENU_ACTION_OPEN_LINK_IN_NEW_WINDOW); + g_assert_cmpuint(test->m_actions[2], ==, WEBKIT_CONTEXT_MENU_ACTION_DOWNLOAD_LINK_TO_DISK); + g_assert_cmpuint(test->m_actions[3], ==, WEBKIT_CONTEXT_MENU_ACTION_COPY_LINK_TO_CLIPBOARD); + + // Custom menu. + test->loadHtml("<html><body></body></html>", "ContextMenuTestCustom"); + test->showContextMenuAndWaitUntilFinished(); + g_assert_cmpuint(test->m_actions.size(), ==, 4); + g_assert_cmpuint(test->m_actions[0], ==, WEBKIT_CONTEXT_MENU_ACTION_STOP); + g_assert_cmpuint(test->m_actions[1], ==, WEBKIT_CONTEXT_MENU_ACTION_RELOAD); + g_assert_cmpuint(test->m_actions[2], ==, WEBKIT_CONTEXT_MENU_ACTION_NO_ACTION); + g_assert_cmpuint(test->m_actions[3], ==, WEBKIT_CONTEXT_MENU_ACTION_INSPECT_ELEMENT); + + // Menu cleared by the web process. + test->loadHtml("<html><body></body></html>", "ContextMenuTestClear"); + test->showContextMenuAndWaitUntilFinished(); + g_assert_cmpuint(test->m_actions.size(), ==, 0); +} + +class ContextMenuWebExtensionNodeTest: public ContextMenuTest { +public: + MAKE_GLIB_TEST_FIXTURE(ContextMenuWebExtensionNodeTest); + + struct Node { + enum { + NodeUnknown = 0, + NodeElement = 1, + NodeText = 3 + }; + typedef unsigned Type; + + CString name; + Type type; + CString contents; + CString parentName; + }; + + void deserializeNodeFromUserData(GVariant* userData) + { + GVariantIter iter; + g_variant_iter_init(&iter, userData); + + const char* key; + GVariant* value; + while (g_variant_iter_next(&iter, "{&sv}", &key, &value)) { + if (!strcmp(key, "Name") && g_variant_classify(value) == G_VARIANT_CLASS_STRING) + m_node.name = g_variant_get_string(value, nullptr); + else if (!strcmp(key, "Type") && g_variant_classify(value) == G_VARIANT_CLASS_UINT32) + m_node.type = g_variant_get_uint32(value); + else if (!strcmp(key, "Contents") && g_variant_classify(value) == G_VARIANT_CLASS_STRING) + m_node.contents = g_variant_get_string(value, nullptr); + else if (!strcmp(key, "Parent") && g_variant_classify(value) == G_VARIANT_CLASS_STRING) + m_node.parentName = g_variant_get_string(value, nullptr); + g_variant_unref(value); + } } - GtkMenu* showContextMenuAndGetGtkMenu() + bool contextMenu(WebKitContextMenu* menu, GdkEvent*, WebKitHitTestResult*) { - showContextMenuAndWaitUntilFinished(); - return getPopupMenu(); + deserializeNodeFromUserData(webkit_context_menu_get_user_data(menu)); + quitMainLoop(); + + return true; } + + Node m_node; }; -static void testContextMenuSmartSeparators(ContextMenuSmartSeparatorsTest* test, gconstpointer) +static void testContextMenuWebExtensionNode(ContextMenuWebExtensionNodeTest* test, gconstpointer) { test->showInWindowAndWaitUntilMapped(); - - test->loadHtml("<html><body>WebKitGTK+ Context menu tests</body></html>", "file:///"); + test->loadHtml("<html><body><p style='position:absolute; left:1; top:1'>WebKitGTK+ Context menu tests</p><br>" + "<a style='position:absolute; left:1; top:100' href='http://www.webkitgtk.org'>WebKitGTK+ Website</a></body></html>", + "ContextMenuTestNode"); test->waitUntilLoadFinished(); - GtkMenu* menu = test->showContextMenuAndGetGtkMenu(); - g_assert(menu); - - // Leading and trailing separators are not added to the context menu. - GUniquePtr<GList> menuItems(gtk_container_get_children(GTK_CONTAINER(menu))); - g_assert_cmpuint(g_list_length(menuItems.get()), ==, 6); - GtkWidget* item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 0)); - g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item)); - item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 1)); - g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item)); - item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 2)); - g_assert(GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item)); - item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 3)); - g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item)); - item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 4)); - g_assert(GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item)); - item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 5)); - g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item)); - - // Hiding a menu item between two separators hides the following separator. - GtkAction* action = gtk_activatable_get_related_action(GTK_ACTIVATABLE(g_list_nth_data(menuItems.get(), 3))); - gtk_action_set_visible(action, FALSE); - menuItems.reset(gtk_container_get_children(GTK_CONTAINER(menu))); - g_assert_cmpuint(g_list_length(menuItems.get()), ==, 6); - item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 0)); - g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item)); - item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 1)); - g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item)); - item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 2)); - g_assert(GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item)); - item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 3)); - g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && !gtk_widget_get_visible(item)); - item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 4)); - g_assert(GTK_IS_SEPARATOR_MENU_ITEM(item) && !gtk_widget_get_visible(item)); - item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 5)); - g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item)); - gtk_action_set_visible(action, TRUE); - - // Showing an action between two separators shows the hidden separator. - item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 0)); - g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item)); - item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 1)); - g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item)); - item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 2)); - g_assert(GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item)); - item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 3)); - g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item)); - item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 4)); - g_assert(GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item)); - item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 5)); - g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item)); - - // Trailing separators are hidden too. - action = gtk_activatable_get_related_action(GTK_ACTIVATABLE(g_list_nth_data(menuItems.get(), 5))); - gtk_action_set_visible(action, FALSE); - menuItems.reset(gtk_container_get_children(GTK_CONTAINER(menu))); - item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 0)); - g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item)); - item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 1)); - g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item)); - item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 2)); - g_assert(GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item)); - item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 3)); - g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && gtk_widget_get_visible(item)); - item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 4)); - g_assert(GTK_IS_SEPARATOR_MENU_ITEM(item) && !gtk_widget_get_visible(item)); - item = GTK_WIDGET(g_list_nth_data(menuItems.get(), 5)); - g_assert(!GTK_IS_SEPARATOR_MENU_ITEM(item) && !gtk_widget_get_visible(item)); + test->showContextMenuAtPositionAndWaitUntilFinished(0, 0); + g_assert_cmpstr(test->m_node.name.data(), ==, "HTML"); + g_assert_cmpuint(test->m_node.type, ==, ContextMenuWebExtensionNodeTest::Node::NodeElement); + g_assert_cmpstr(test->m_node.contents.data(), ==, "WebKitGTK+ Context menu testsWebKitGTK+ Website"); + g_assert_cmpstr(test->m_node.parentName.data(), ==, "#document"); + + test->showContextMenuAtPositionAndWaitUntilFinished(1, 20); + g_assert_cmpstr(test->m_node.name.data(), ==, "#text"); + g_assert_cmpuint(test->m_node.type, ==, ContextMenuWebExtensionNodeTest::Node::NodeText); + g_assert_cmpstr(test->m_node.contents.data(), ==, "WebKitGTK+ Context menu tests"); + g_assert_cmpstr(test->m_node.parentName.data(), ==, "P"); + + // Link menu. + test->showContextMenuAtPositionAndWaitUntilFinished(1, 101); + g_assert_cmpstr(test->m_node.name.data(), ==, "#text"); + g_assert_cmpuint(test->m_node.type, ==, ContextMenuWebExtensionNodeTest::Node::NodeText); + g_assert_cmpstr(test->m_node.contents.data(), ==, "WebKitGTK+ Website"); + g_assert_cmpstr(test->m_node.parentName.data(), ==, "A"); } void beforeAll() { ContextMenuDefaultTest::add("WebKitWebView", "default-menu", testContextMenuDefaultMenu); + ContextMenuDefaultTest::add("WebKitWebView", "context-menu-key", testContextMenuKey); + ContextMenuDefaultTest::add("WebKitWebView", "popup-event-signal", testPopupEventSignal); ContextMenuCustomTest::add("WebKitWebView", "populate-menu", testContextMenuPopulateMenu); ContextMenuCustomFullTest::add("WebKitWebView", "custom-menu", testContextMenuCustomMenu); ContextMenuDisabledTest::add("WebKitWebView", "disable-menu", testContextMenuDisableMenu); ContextMenuSubmenuTest::add("WebKitWebView", "submenu", testContextMenuSubMenu); ContextMenuDismissedTest::add("WebKitWebView", "menu-dismissed", testContextMenuDismissed); - ContextMenuSmartSeparatorsTest::add("WebKitWebView", "smart-separators", testContextMenuSmartSeparators); + ContextMenuWebExtensionTest::add("WebKitWebPage", "context-menu", testContextMenuWebExtensionMenu); + ContextMenuWebExtensionNodeTest::add("WebKitWebPage", "context-menu-node", testContextMenuWebExtensionNode); } void afterAll() diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestCookieManager.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestCookieManager.cpp index 2e98645e8..4cd4b5c8c 100644 --- a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestCookieManager.cpp +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestCookieManager.cpp @@ -24,7 +24,6 @@ #include <glib/gstdio.h> static WebKitTestServer* kServer; -static char* kTempDirectory; static const char* kFirstPartyDomain = "127.0.0.1"; static const char* kThirdPartyDomain = "localhost"; @@ -53,6 +52,7 @@ public: , m_cookiesChanged(false) , m_finishLoopWhenCookiesChange(false) { + g_assert(webkit_website_data_manager_get_cookie_manager(webkit_web_context_get_website_data_manager(webkit_web_view_get_context(m_webView))) == m_cookieManager); g_signal_connect(m_cookieManager, "changed", G_CALLBACK(cookiesChangedCallback), this); } @@ -72,12 +72,12 @@ public: switch (storage) { case WEBKIT_COOKIE_PERSISTENT_STORAGE_TEXT: if (!m_cookiesTextFile) - m_cookiesTextFile.reset(g_build_filename(kTempDirectory, "cookies.txt", NULL)); + m_cookiesTextFile.reset(g_build_filename(Test::dataDirectory(), "cookies.txt", nullptr)); filename = m_cookiesTextFile.get(); break; case WEBKIT_COOKIE_PERSISTENT_STORAGE_SQLITE: if (!m_cookiesSQLiteFile) - m_cookiesSQLiteFile.reset(g_build_filename(kTempDirectory, "cookies.db", NULL)); + m_cookiesSQLiteFile.reset(g_build_filename(Test::dataDirectory(), "cookies.db", nullptr)); filename = m_cookiesSQLiteFile.get(); break; default: @@ -114,7 +114,9 @@ public: static void getDomainsReadyCallback(GObject* object, GAsyncResult* result, gpointer userData) { GUniqueOutPtr<GError> error; + G_GNUC_BEGIN_IGNORE_DEPRECATIONS; char** domains = webkit_cookie_manager_get_domains_with_cookies_finish(WEBKIT_COOKIE_MANAGER(object), result, &error.outPtr()); + G_GNUC_END_IGNORE_DEPRECATIONS; g_assert(!error.get()); CookieManagerTest* test = static_cast<CookieManagerTest*>(userData); @@ -126,7 +128,9 @@ public: { g_strfreev(m_domains); m_domains = 0; + G_GNUC_BEGIN_IGNORE_DEPRECATIONS; webkit_cookie_manager_get_domains_with_cookies(m_cookieManager, 0, getDomainsReadyCallback, this); + G_GNUC_END_IGNORE_DEPRECATIONS; g_main_loop_run(m_mainLoop); return m_domains; @@ -145,12 +149,16 @@ public: void deleteCookiesForDomain(const char* domain) { + G_GNUC_BEGIN_IGNORE_DEPRECATIONS; webkit_cookie_manager_delete_cookies_for_domain(m_cookieManager, domain); + G_GNUC_END_IGNORE_DEPRECATIONS; } void deleteAllCookies() { + G_GNUC_BEGIN_IGNORE_DEPRECATIONS; webkit_cookie_manager_delete_all_cookies(m_cookieManager); + G_GNUC_END_IGNORE_DEPRECATIONS; } void waitUntilCookiesChanged() @@ -291,6 +299,58 @@ static void testCookieManagerPersistentStorage(CookieManagerTest* test, gconstpo g_assert_cmpint(g_strv_length(test->getDomains()), ==, 0); } +static void ephemeralViewloadChanged(WebKitWebView* webView, WebKitLoadEvent loadEvent, WebViewTest* test) +{ + if (loadEvent != WEBKIT_LOAD_FINISHED) + return; + g_signal_handlers_disconnect_by_func(webView, reinterpret_cast<void*>(ephemeralViewloadChanged), test); + test->quitMainLoop(); +} + +static void testCookieManagerEphemeral(CookieManagerTest* test, gconstpointer) +{ + test->setAcceptPolicy(WEBKIT_COOKIE_POLICY_ACCEPT_ALWAYS); + test->setPersistentStorage(WEBKIT_COOKIE_PERSISTENT_STORAGE_TEXT); + char** domains = test->getDomains(); + g_assert(domains); + g_assert_cmpint(g_strv_length(domains), ==, 0); + + GRefPtr<WebKitWebView> webView = WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW, + "web-context", webkit_web_view_get_context(test->m_webView), + "is-ephemeral", TRUE, + nullptr)); + g_assert(webkit_web_view_is_ephemeral(webView.get())); + g_assert(!webkit_web_context_is_ephemeral(webkit_web_view_get_context(webView.get()))); + + g_signal_connect(webView.get(), "load-changed", G_CALLBACK(ephemeralViewloadChanged), test); + webkit_web_view_load_uri(webView.get(), kServer->getURIForPath("/index.html").data()); + g_main_loop_run(test->m_mainLoop); + + domains = test->getDomains(); + g_assert(domains); + g_assert_cmpint(g_strv_length(domains), ==, 0); + + auto* viewDataManager = webkit_web_view_get_website_data_manager(webView.get()); + g_assert(WEBKIT_IS_WEBSITE_DATA_MANAGER(viewDataManager)); + test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(viewDataManager)); + g_assert(viewDataManager != webkit_web_context_get_website_data_manager(webkit_web_view_get_context(test->m_webView))); + auto* cookieManager = webkit_website_data_manager_get_cookie_manager(viewDataManager); + g_assert(WEBKIT_IS_COOKIE_MANAGER(cookieManager)); + test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(cookieManager)); + g_assert(cookieManager != test->m_cookieManager); + G_GNUC_BEGIN_IGNORE_DEPRECATIONS; + webkit_cookie_manager_get_domains_with_cookies(cookieManager, nullptr, [](GObject* object, GAsyncResult* result, gpointer userData) { + auto* test = static_cast<CookieManagerTest*>(userData); + GUniquePtr<char*> domains(webkit_cookie_manager_get_domains_with_cookies_finish(WEBKIT_COOKIE_MANAGER(object), result, nullptr)); + g_assert(domains); + g_assert_cmpint(g_strv_length(domains.get()), ==, 1); + g_assert_cmpstr(domains.get()[0], ==, kFirstPartyDomain); + test->quitMainLoop(); + }, test); + G_GNUC_END_IGNORE_DEPRECATIONS; + g_main_loop_run(test->m_mainLoop); +} + static void serverCallback(SoupServer* server, SoupMessage* message, const char* path, GHashTable*, SoupClientContext*, gpointer) { if (message->method != SOUP_METHOD_GET) { @@ -315,17 +375,14 @@ void beforeAll() kServer = new WebKitTestServer(); kServer->run(serverCallback); - kTempDirectory = g_dir_make_tmp("WebKit2Tests-XXXXXX", 0); - g_assert(kTempDirectory); - CookieManagerTest::add("WebKitCookieManager", "accept-policy", testCookieManagerAcceptPolicy); CookieManagerTest::add("WebKitCookieManager", "delete-cookies", testCookieManagerDeleteCookies); CookieManagerTest::add("WebKitCookieManager", "cookies-changed", testCookieManagerCookiesChanged); CookieManagerTest::add("WebKitCookieManager", "persistent-storage", testCookieManagerPersistentStorage); + CookieManagerTest::add("WebKitCookieManager", "ephemeral", testCookieManagerEphemeral); } void afterAll() { delete kServer; - g_rmdir(kTempDirectory); } diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDOMDOMWindow.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDOMDOMWindow.cpp new file mode 100644 index 000000000..98c6e1064 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDOMDOMWindow.cpp @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2013 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2,1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#include "WebProcessTestRunner.h" +#include "WebViewTest.h" +#include <gtk/gtk.h> +#include <webkit2/webkit2.h> + +#define HTML_DOCUMENT "<html><head><title></title></head><style type='text/css'>#test { font-size: 16px; }</style><body><p id='test'>test</p></body></html>" + +typedef struct { + gboolean loaded; + gboolean clicked; + WebProcessTestRunner* testRunner; + WebViewTest* test; +} DomDomWindowTestStatus; + +static DomDomWindowTestStatus status; + +static void signalsNotifyCallback(const gchar *key, const gchar *value, gconstpointer) +{ + if (g_str_equal(key, "ready")) { + // The document was already loaded in the webprocess, and its "load" + // signal couldn't be captured on time (was issued before the test + // started). We load it again to force a new "load" signal in the + // webprocess, which will be captured this time + status.test->loadHtml(HTML_DOCUMENT, 0); + } + + if (g_str_equal(key, "loaded")) { + status.loaded = TRUE; + status.test->showInWindowAndWaitUntilMapped(); + + // Click in a known location where the text is + status.test->clickMouseButton(20, 18, 1, 0); + } + + if (g_str_equal(key, "clicked")) + status.clicked = TRUE; + + if (g_str_equal(key, "finish")) { + status.test = 0; + status.testRunner->finishTest(status.loaded && status.clicked); + } +} + +static void dispatchEventNotifyCallback(const gchar *key, const gchar *value, gconstpointer) +{ + if (g_str_equal(key, "ready")) { + // The document was already loaded in the webprocess, and its "load" + // signal couldn't be captured on time (was issued before the test + // started). We load it again to force a new "load" signal in the + // webprocess, which will be captured this time + status.test->loadHtml(HTML_DOCUMENT, 0); + } + + if (g_str_equal(key, "loaded")) + status.loaded = TRUE; + + if (g_str_equal(key, "clicked")) + status.clicked = TRUE; + + if (g_str_equal(key, "finish")) { + status.test = 0; + status.testRunner->finishTest(status.loaded && status.clicked); + } +} + +static void testWebKitDOMDOMWindowSignals(WebViewTest* test, gconstpointer) +{ + status.loaded = FALSE; + status.clicked = FALSE; + status.test = test; + + status.testRunner->setNotifyCallback(G_CALLBACK(signalsNotifyCallback), 0); + + // The HTML document will we loaded later, when the test is "ready" because + // we want to test the "load" signal + + GVariantBuilder builder; + g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add(&builder, "{sv}", "pageID", g_variant_new_uint64(webkit_web_view_get_page_id(status.test->m_webView))); + status.testRunner->runTestAndWait("WebKitDOMDOMWindow", "signals", g_variant_builder_end(&builder)); + g_assert(status.testRunner->getTestResult()); +} + +static void testWebKitDOMDOMWindowDispatchEvent(WebViewTest* test, gconstpointer) +{ + status.loaded = FALSE; + status.clicked = FALSE; + status.test = test; + + status.testRunner->setNotifyCallback(G_CALLBACK(dispatchEventNotifyCallback), 0); + + // The HTML document will we loaded later, when the test is "ready" because + // we want to test the "load" signal + + GVariantBuilder builder; + g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add(&builder, "{sv}", "pageID", g_variant_new_uint64(webkit_web_view_get_page_id(status.test->m_webView))); + status.testRunner->runTestAndWait("WebKitDOMDOMWindow", "dispatch-event", g_variant_builder_end(&builder)); + g_assert(status.testRunner->getTestResult()); +} + +static void testWebKitDOMDOMWindowGetComputedStyle(WebViewTest* test, gconstpointer) +{ + status.loaded = FALSE; + status.clicked = FALSE; + status.test = test; + + static const char* testHTML = HTML_DOCUMENT; + status.test->loadHtml(testHTML, 0); + status.test->waitUntilLoadFinished(); + + GVariantBuilder builder; + g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add(&builder, "{sv}", "pageID", g_variant_new_uint64(webkit_web_view_get_page_id(status.test->m_webView))); + g_assert(status.testRunner->runTest("WebKitDOMDOMWindow", "get-computed-style", g_variant_builder_end(&builder))); +} + +void beforeAll() +{ + status.testRunner = new WebProcessTestRunner(); + webkit_web_context_set_web_extensions_directory(webkit_web_context_get_default(), WEBKIT_TEST_WEB_EXTENSIONS_DIR); + + WebViewTest::add("WebKitDOMDOMWindow", "signals", testWebKitDOMDOMWindowSignals); + WebViewTest::add("WebKitDOMDOMWindow", "dispatch-event", testWebKitDOMDOMWindowDispatchEvent); + WebViewTest::add("WebKitDOMDOMWindow", "get-computed-style", testWebKitDOMDOMWindowGetComputedStyle); +} + +void afterAll() +{ + delete status.testRunner; +} diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDOMNode.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDOMNode.cpp index 16db87d98..886356b7e 100644 --- a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDOMNode.cpp +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDOMNode.cpp @@ -41,7 +41,7 @@ static void testWebKitDOMNodeInsertion(WebViewTest* test, gconstpointer) g_assert(test->runWebProcessTest("WebKitDOMNode", "insertion")); } -static void testWebKitDOMNodeTagNames(WebViewTest* test, gconstpointer) +static void prepareDOMForTagNamesTests(WebViewTest* test) { static const char* testHTML = "<html><head></head><body>" "<video id='video' preload='none'>" @@ -53,19 +53,44 @@ static void testWebKitDOMNodeTagNames(WebViewTest* test, gconstpointer) " Your browser does not support the video tag." "</video>" "<input type='hidden' id='test' name='finish' value='false'></body></html>"; - test->loadHtml(testHTML, 0); + test->loadHtml(testHTML, nullptr); test->waitUntilLoadFinished(); +} - g_assert(test->runWebProcessTest("WebKitDOMNode", "tag-names")); +static void testWebKitDOMNodeTagNamesNodeList(WebViewTest* test, gconstpointer) +{ + prepareDOMForTagNamesTests(test); + g_assert(test->runWebProcessTest("WebKitDOMNode", "tag-names-node-list")); } -void beforeAll() +static void testWebKitDOMNodeTagNamesHTMLCollection(WebViewTest* test, gconstpointer) +{ + prepareDOMForTagNamesTests(test); + g_assert(test->runWebProcessTest("WebKitDOMNode", "tag-names-html-collection")); +} + +static void testWebKitDOMObjectCache(WebViewTest* test, gconstpointer) { - webkit_web_context_set_web_extensions_directory(webkit_web_context_get_default(), WEBKIT_TEST_WEB_EXTENSIONS_DIR); + static const char* testHTML = "<html><body><div id='container'><p>DOM Cache test</p><a id='link href='#'>link</a></div></body></html>"; + // Run the test 3 times to make sure the DOM objects are correctly released when the + // document is detached from the frame for every new document created. + for (unsigned i = 0; i < 3; ++i) { + test->loadHtml(testHTML, nullptr); + test->waitUntilLoadFinished(); + + g_assert(test->runWebProcessTest("WebKitDOMNode", "dom-cache")); + } +} + + +void beforeAll() +{ WebViewTest::add("WebKitDOMNode", "hierarchy-navigation", testWebKitDOMNodeHierarchyNavigation); WebViewTest::add("WebKitDOMNode", "insertion", testWebKitDOMNodeInsertion); - WebViewTest::add("WebKitDOMNode", "tag-names", testWebKitDOMNodeTagNames); + WebViewTest::add("WebKitDOMNode", "tag-names-node-list", testWebKitDOMNodeTagNamesNodeList); + WebViewTest::add("WebKitDOMNode", "tag-names-html-collection", testWebKitDOMNodeTagNamesHTMLCollection); + WebViewTest::add("WebKitDOMNode", "dom-cache", testWebKitDOMObjectCache); } void afterAll() diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDOMNodeFilter.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDOMNodeFilter.cpp new file mode 100644 index 000000000..812946153 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDOMNodeFilter.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2014 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2,1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#include "WebViewTest.h" +#include <gtk/gtk.h> +#include <webkit2/webkit2.h> + +static const char* testHTML = "<html id='root'><head><title>DOMNodeTreeWalker</title></head>" + "<body><input type='button' name='push' value='push'><input type='button' name='clear' value='clear'><br></body></html>"; + +static void runTest(WebViewTest* test, const char* name) +{ + test->loadHtml(testHTML, nullptr); + test->waitUntilLoadFinished(); + + g_assert(test->runWebProcessTest("WebKitDOMNodeFilter", name)); +} + +static void testWebKitDOMNodeFilterTreeWalker(WebViewTest* test, gconstpointer) +{ + runTest(test, "tree-walker"); +} + +static void testWebKitDOMNodeFilterNodeIterator(WebViewTest* test, gconstpointer) +{ + runTest(test, "node-iterator"); +} + +void beforeAll() +{ + WebViewTest::add("WebKitDOMNodeFilter", "tree-walker", testWebKitDOMNodeFilterTreeWalker); + WebViewTest::add("WebKitDOMNodeFilter", "node-iterator", testWebKitDOMNodeFilterNodeIterator); +} + +void afterAll() +{ +} diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDOMXPathNSResolver.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDOMXPathNSResolver.cpp new file mode 100644 index 000000000..ff9a77192 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDOMXPathNSResolver.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2014 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2,1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#include "WebViewTest.h" +#include <gtk/gtk.h> +#include <webkit2/webkit2.h> + +static void testWebKitDOMXPathNSResolverNative(WebViewTest* test, gconstpointer) +{ + static const char* nativeXML = "<root xmlns:foo='http://www.example.org'><foo:child>SUCCESS</foo:child></root>"; + GRefPtr<GBytes> bytes = adoptGRef(g_bytes_new_static(nativeXML, strlen(nativeXML))); + test->loadBytes(bytes.get(), "text/xml", nullptr, nullptr); + test->waitUntilLoadFinished(); + g_assert(test->runWebProcessTest("WebKitDOMXPathNSResolver", "native")); +} + +static void testWebKitDOMXPathNSResolverCustom(WebViewTest* test, gconstpointer) +{ + static const char* customXML = "<root xmlns='http://www.example.com'><child>SUCCESS</child></root>"; + GRefPtr<GBytes> bytes = adoptGRef(g_bytes_new_static(customXML, strlen(customXML))); + test->loadBytes(bytes.get(), "text/xml", nullptr, nullptr); + test->waitUntilLoadFinished(); + g_assert(test->runWebProcessTest("WebKitDOMXPathNSResolver", "custom")); +} + +void beforeAll() +{ + WebViewTest::add("WebKitDOMXPathNSResolver", "native", testWebKitDOMXPathNSResolverNative); + WebViewTest::add("WebKitDOMXPathNSResolver", "custom", testWebKitDOMXPathNSResolverCustom); +} + +void afterAll() +{ +} diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDownloads.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDownloads.cpp index a4d27a508..6b95a7fe4 100644 --- a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDownloads.cpp +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDownloads.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Igalia S.L. + * Copyright (C) 2012, 2014 Igalia S.L. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -27,12 +27,10 @@ #include <string.h> #include <webkit2/webkit2.h> #include <wtf/Vector.h> -#include <wtf/gobject/GRefPtr.h> -#include <wtf/gobject/GUniquePtr.h> +#include <wtf/glib/GRefPtr.h> +#include <wtf/glib/GUniquePtr.h> #include <wtf/text/CString.h> -static char* kTempDirectory; - class DownloadTest: public Test { public: MAKE_GLIB_TEST_FIXTURE(DownloadTest); @@ -56,6 +54,8 @@ public: { g_assert(webkit_download_get_destination(download)); g_assert_cmpstr(webkit_download_get_destination(download), ==, destination); + GRefPtr<GFile> file = adoptGRef(g_file_new_for_uri(destination)); + g_assert(g_file_query_exists(file.get(), nullptr)); test->createdDestination(download, destination); } @@ -72,6 +72,14 @@ public: static void failedCallback(WebKitDownload* download, GError* error, DownloadTest* test) { g_assert(error); + + const char* destinationURI = webkit_download_get_destination(download); + if (destinationURI) { + GUniquePtr<char> tempFileURI(g_strconcat(destinationURI, ".wkdownload", nullptr)); + GRefPtr<GFile> tempFile = adoptGRef(g_file_new_for_uri(tempFileURI.get())); + g_assert(!g_file_query_exists(tempFile.get(), nullptr)); + } + test->failed(download, error); } @@ -95,21 +103,23 @@ public: } DownloadTest() - : m_webContext(webkit_web_context_get_default()) - , m_mainLoop(g_main_loop_new(0, TRUE)) + : m_mainLoop(g_main_loop_new(nullptr, TRUE)) , m_downloadSize(0) + , m_allowOverwrite(false) { - g_signal_connect(m_webContext, "download-started", G_CALLBACK(downloadStartedCallback), this); + g_signal_connect(m_webContext.get(), "download-started", G_CALLBACK(downloadStartedCallback), this); } ~DownloadTest() { - g_signal_handlers_disconnect_matched(m_webContext, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this); + g_signal_handlers_disconnect_matched(m_webContext.get(), G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this); g_main_loop_unref(m_mainLoop); } virtual void started(WebKitDownload* download) { + m_downloadSize = 0; + m_downloadEvents.clear(); m_downloadEvents.append(Started); } @@ -144,17 +154,21 @@ public: virtual void decideDestination(WebKitDownload* download, const gchar* suggestedFilename) { - GUniquePtr<char> destination(g_build_filename(kTempDirectory, suggestedFilename, NULL)); + GUniquePtr<char> destination(g_build_filename(Test::dataDirectory(), suggestedFilename, nullptr)); GUniquePtr<char> destinationURI(g_filename_to_uri(destination.get(), 0, 0)); webkit_download_set_destination(download, destinationURI.get()); } - WebKitDownload* downloadURIAndWaitUntilFinishes(const CString& requestURI) + GRefPtr<WebKitDownload> downloadURIAndWaitUntilFinishes(const CString& requestURI) { - WebKitDownload* download = webkit_web_context_download_uri(m_webContext, requestURI.data()); - assertObjectIsDeletedWhenTestFinishes(G_OBJECT(download)); + GRefPtr<WebKitDownload> download = adoptGRef(webkit_web_context_download_uri(m_webContext.get(), requestURI.data())); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(download.get())); - WebKitURIRequest* request = webkit_download_get_request(download); + g_assert(!webkit_download_get_allow_overwrite(download.get())); + webkit_download_set_allow_overwrite(download.get(), m_allowOverwrite); + g_assert(webkit_download_get_allow_overwrite(download.get()) == m_allowOverwrite); + + WebKitURIRequest* request = webkit_download_get_request(download.get()); g_assert(request); ASSERT_CMP_CSTRING(webkit_uri_request_get_uri(request), ==, requestURI); @@ -174,19 +188,19 @@ public: g_file_delete(destFile.get(), 0, 0); } - WebKitWebContext* m_webContext; GMainLoop* m_mainLoop; Vector<DownloadEvent> m_downloadEvents; guint64 m_downloadSize; + bool m_allowOverwrite; }; -static void testDownloadLocalFile(DownloadTest* test, gconstpointer) +static GRefPtr<WebKitDownload> downloadLocalFileSuccessfully(DownloadTest* test, const char* filename) { - GUniquePtr<char> sourcePath(g_build_filename(Test::getWebKit1TestResoucesDir().data(), "test.pdf", NULL)); + GUniquePtr<char> sourcePath(g_build_filename(Test::getResourcesDir().data(), filename, nullptr)); GRefPtr<GFile> source = adoptGRef(g_file_new_for_path(sourcePath.get())); GRefPtr<GFileInfo> sourceInfo = adoptGRef(g_file_query_info(source.get(), G_FILE_ATTRIBUTE_STANDARD_SIZE, static_cast<GFileQueryInfoFlags>(0), 0, 0)); GUniquePtr<char> sourceURI(g_file_get_uri(source.get())); - GRefPtr<WebKitDownload> download = adoptGRef(test->downloadURIAndWaitUntilFinishes(sourceURI.get())); + GRefPtr<WebKitDownload> download = test->downloadURIAndWaitUntilFinishes(sourceURI.get()); g_assert(!webkit_download_get_web_view(download.get())); Vector<DownloadTest::DownloadEvent>& events = test->m_downloadEvents; @@ -204,15 +218,50 @@ static void testDownloadLocalFile(DownloadTest* test, gconstpointer) g_assert_cmpint(test->m_downloadSize, ==, g_file_info_get_size(sourceInfo.get())); g_assert(webkit_download_get_destination(download.get())); g_assert_cmpfloat(webkit_download_get_estimated_progress(download.get()), ==, 1); - test->checkDestinationAndDeleteFile(download.get(), "test.pdf"); + + return download; +} + +static void testDownloadLocalFile(DownloadTest* test, gconstpointer) +{ + static const char* filename = "test.pdf"; + GRefPtr<WebKitDownload> download = downloadLocalFileSuccessfully(test, filename); + test->checkDestinationAndDeleteFile(download.get(), filename); +} + +static void createFileAtDestination(const char* filename) +{ + GUniquePtr<char> path(g_build_filename(Test::dataDirectory(), filename, nullptr)); + GRefPtr<GFile> file = adoptGRef(g_file_new_for_path(path.get())); + GUniqueOutPtr<GError> error; + g_file_create(file.get(), G_FILE_CREATE_NONE, nullptr, &error.outPtr()); + g_assert(!error); + g_assert(g_file_query_exists(file.get(), nullptr)); +} + +static void testDownloadOverwriteDestinationAllowed(DownloadTest* test, gconstpointer) +{ + static const char* filename = "test.pdf"; + createFileAtDestination(filename); + + test->m_allowOverwrite = true; + GRefPtr<WebKitDownload> download = downloadLocalFileSuccessfully(test, filename); + test->checkDestinationAndDeleteFile(download.get(), filename); } class DownloadErrorTest: public DownloadTest { public: MAKE_GLIB_TEST_FIXTURE(DownloadErrorTest); + enum ExpectedError { + NetworkError, + DownloadCancelled, + InvalidDestination, + DestinationExists + }; + DownloadErrorTest() - : m_expectedError(WEBKIT_DOWNLOAD_ERROR_NETWORK) + : m_expectedError(NetworkError) { } @@ -223,7 +272,7 @@ public: void createdDestination(WebKitDownload* download, const char* destination) { - if (m_expectedError == WEBKIT_DOWNLOAD_ERROR_CANCELLED_BY_USER) + if (m_expectedError == DownloadCancelled) webkit_download_cancel(download); else g_assert_not_reached(); @@ -231,26 +280,65 @@ public: void failed(WebKitDownload* download, GError* error) { - g_assert(g_error_matches(error, WEBKIT_DOWNLOAD_ERROR, m_expectedError)); + g_assert(g_error_matches(error, WEBKIT_DOWNLOAD_ERROR, expectedErrorToWebKitDownloadError(m_expectedError))); DownloadTest::failed(download, error); } void decideDestination(WebKitDownload* download, const gchar* suggestedFilename) { - if (m_expectedError != WEBKIT_DOWNLOAD_ERROR_DESTINATION) { + if (m_expectedError != InvalidDestination) { DownloadTest::decideDestination(download, suggestedFilename); return; } webkit_download_set_destination(download, "file:///foo/bar"); } - WebKitDownloadError m_expectedError; + static WebKitDownloadError expectedErrorToWebKitDownloadError(ExpectedError expected) + { + switch (expected) { + case NetworkError: + return WEBKIT_DOWNLOAD_ERROR_NETWORK; + case DownloadCancelled: + return WEBKIT_DOWNLOAD_ERROR_CANCELLED_BY_USER; + case InvalidDestination: + return WEBKIT_DOWNLOAD_ERROR_DESTINATION; + case DestinationExists: + return WEBKIT_DOWNLOAD_ERROR_DESTINATION; + default: + g_assert_not_reached(); + } + } + + ExpectedError m_expectedError; }; +static void testDownloadOverwriteDestinationDisallowed(DownloadErrorTest* test, gconstpointer) +{ + static const char* filename = "test.pdf"; + createFileAtDestination(filename); + + test->m_expectedError = DownloadErrorTest::DestinationExists; + GUniquePtr<char> sourcePath(g_build_filename(Test::getResourcesDir().data(), filename, nullptr)); + GRefPtr<GFile> source = adoptGRef(g_file_new_for_path(sourcePath.get())); + GUniquePtr<char> sourceURI(g_file_get_uri(source.get())); + GRefPtr<WebKitDownload> download = test->downloadURIAndWaitUntilFinishes(sourceURI.get()); + g_assert(!webkit_download_get_web_view(download.get())); + + Vector<DownloadTest::DownloadEvent>& events = test->m_downloadEvents; + g_assert_cmpint(events.size(), ==, 4); + g_assert_cmpint(events[0], ==, DownloadTest::Started); + g_assert_cmpint(events[1], ==, DownloadTest::ReceivedResponse); + g_assert_cmpint(events[2], ==, DownloadTest::Failed); + g_assert_cmpint(events[3], ==, DownloadTest::Finished); + g_assert_cmpfloat(webkit_download_get_estimated_progress(download.get()), ==, 0); + + test->checkDestinationAndDeleteFile(download.get(), filename); +} + static void testDownloadLocalFileError(DownloadErrorTest* test, gconstpointer) { - test->m_expectedError = WEBKIT_DOWNLOAD_ERROR_NETWORK; - GRefPtr<WebKitDownload> download = adoptGRef(test->downloadURIAndWaitUntilFinishes("file:///foo/bar")); + test->m_expectedError = DownloadErrorTest::NetworkError; + GRefPtr<WebKitDownload> download = test->downloadURIAndWaitUntilFinishes("file:///foo/bar"); g_assert(!webkit_download_get_web_view(download.get())); Vector<DownloadTest::DownloadEvent>& events = test->m_downloadEvents; @@ -261,11 +349,11 @@ static void testDownloadLocalFileError(DownloadErrorTest* test, gconstpointer) events.clear(); g_assert_cmpfloat(webkit_download_get_estimated_progress(download.get()), <, 1); - test->m_expectedError = WEBKIT_DOWNLOAD_ERROR_DESTINATION; - GUniquePtr<char> path(g_build_filename(Test::getWebKit1TestResoucesDir().data(), "test.pdf", NULL)); + test->m_expectedError = DownloadErrorTest::InvalidDestination; + GUniquePtr<char> path(g_build_filename(Test::getResourcesDir().data(), "test.pdf", nullptr)); GRefPtr<GFile> file = adoptGRef(g_file_new_for_path(path.get())); GUniquePtr<char> uri(g_file_get_uri(file.get())); - download = adoptGRef(test->downloadURIAndWaitUntilFinishes(uri.get())); + download = test->downloadURIAndWaitUntilFinishes(uri.get()); g_assert(!webkit_download_get_web_view(download.get())); g_assert_cmpint(events.size(), ==, 4); @@ -277,8 +365,8 @@ static void testDownloadLocalFileError(DownloadErrorTest* test, gconstpointer) g_assert_cmpfloat(webkit_download_get_estimated_progress(download.get()), <, 1); test->checkDestinationAndDeleteFile(download.get(), "bar"); - test->m_expectedError = WEBKIT_DOWNLOAD_ERROR_CANCELLED_BY_USER; - download = adoptGRef(test->downloadURIAndWaitUntilFinishes(uri.get())); + test->m_expectedError = DownloadErrorTest::DownloadCancelled; + download = test->downloadURIAndWaitUntilFinishes(uri.get()); g_assert(!webkit_download_get_web_view(download.get())); g_assert_cmpint(events.size(), ==, 4); @@ -300,15 +388,17 @@ static void addContentDispositionHTTPHeaderToResponse(SoupMessage* message) soup_message_headers_append(message->response_headers, "Content-Disposition", contentDisposition.get()); } -static gboolean writeNextChunkIdle(SoupMessage* message) -{ - soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, "chunk", 5); - return FALSE; -} - static void writeNextChunk(SoupMessage* message) { - g_timeout_add(100, reinterpret_cast<GSourceFunc>(writeNextChunkIdle), message); + /* We need a big enough chunk for the sniffer to not block the load */ + static const char* chunk = "Testing!Testing!Testing!Testing!Testing!Testing!Testing!" + "Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!" + "Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!" + "Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!" + "Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!" + "Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!" + "Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!"; + soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, chunk, strlen(chunk)); } static void serverCallback(SoupServer* server, SoupMessage* message, const char* path, GHashTable*, SoupClientContext*, gpointer) @@ -329,7 +419,10 @@ static void serverCallback(SoupServer* server, SoupMessage* message, const char* return; } - GUniquePtr<char> filePath(g_build_filename(Test::getWebKit1TestResoucesDir().data(), path, NULL)); + if (g_str_equal(path, "/unknown")) + path = "/test.pdf"; + + GUniquePtr<char> filePath(g_build_filename(Test::getResourcesDir().data(), path, nullptr)); char* contents; gsize contentsLength; if (!g_file_get_contents(filePath.get(), &contents, &contentsLength, 0)) { @@ -346,7 +439,7 @@ static void serverCallback(SoupServer* server, SoupMessage* message, const char* static void testDownloadRemoteFile(DownloadTest* test, gconstpointer) { - GRefPtr<WebKitDownload> download = adoptGRef(test->downloadURIAndWaitUntilFinishes(kServer->getURIForPath("/test.pdf"))); + GRefPtr<WebKitDownload> download = test->downloadURIAndWaitUntilFinishes(kServer->getURIForPath("/test.pdf")); g_assert(!webkit_download_get_web_view(download.get())); Vector<DownloadTest::DownloadEvent>& events = test->m_downloadEvents; @@ -364,13 +457,14 @@ static void testDownloadRemoteFile(DownloadTest* test, gconstpointer) g_assert(webkit_download_get_destination(download.get())); g_assert_cmpfloat(webkit_download_get_estimated_progress(download.get()), ==, 1); - test->checkDestinationAndDeleteFile(download.get(), kServerSuggestedFilename); + GUniquePtr<char> expectedFilename(g_strdup_printf("%s.pdf", kServerSuggestedFilename)); + test->checkDestinationAndDeleteFile(download.get(), expectedFilename.get()); } static void testDownloadRemoteFileError(DownloadErrorTest* test, gconstpointer) { - test->m_expectedError = WEBKIT_DOWNLOAD_ERROR_NETWORK; - GRefPtr<WebKitDownload> download = adoptGRef(test->downloadURIAndWaitUntilFinishes(kServer->getURIForPath("/foo"))); + test->m_expectedError = DownloadErrorTest::NetworkError; + GRefPtr<WebKitDownload> download = test->downloadURIAndWaitUntilFinishes(kServer->getURIForPath("/foo")); g_assert(!webkit_download_get_web_view(download.get())); Vector<DownloadTest::DownloadEvent>& events = test->m_downloadEvents; @@ -384,8 +478,8 @@ static void testDownloadRemoteFileError(DownloadErrorTest* test, gconstpointer) g_assert_cmpuint(webkit_uri_response_get_status_code(response), ==, 404); g_assert_cmpfloat(webkit_download_get_estimated_progress(download.get()), <, 1); - test->m_expectedError = WEBKIT_DOWNLOAD_ERROR_DESTINATION; - download = adoptGRef(test->downloadURIAndWaitUntilFinishes(kServer->getURIForPath("/test.pdf"))); + test->m_expectedError = DownloadErrorTest::InvalidDestination; + download = test->downloadURIAndWaitUntilFinishes(kServer->getURIForPath("/test.pdf")); g_assert(!webkit_download_get_web_view(download.get())); g_assert_cmpint(events.size(), ==, 4); @@ -397,8 +491,8 @@ static void testDownloadRemoteFileError(DownloadErrorTest* test, gconstpointer) g_assert_cmpfloat(webkit_download_get_estimated_progress(download.get()), <, 1); test->checkDestinationAndDeleteFile(download.get(), "bar"); - test->m_expectedError = WEBKIT_DOWNLOAD_ERROR_CANCELLED_BY_USER; - download = adoptGRef(test->downloadURIAndWaitUntilFinishes(kServer->getURIForPath("/cancel-after-destination"))); + test->m_expectedError = DownloadErrorTest::DownloadCancelled; + download = test->downloadURIAndWaitUntilFinishes(kServer->getURIForPath("/cancel-after-destination")); g_assert(!webkit_download_get_web_view(download.get())); g_assert_cmpint(events.size(), ==, 4); @@ -422,6 +516,8 @@ public: { test->m_download = download; test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(download)); + g_signal_connect(download, "decide-destination", G_CALLBACK(downloadDecideDestinationCallback), test); + g_signal_connect(download, "finished", G_CALLBACK(downloadFinishedCallback), test); test->quitMainLoop(); } @@ -444,8 +540,10 @@ public: static gboolean downloadDecideDestinationCallback(WebKitDownload* download, const gchar* suggestedFilename, WebViewDownloadTest* test) { - GUniquePtr<char> destination(g_build_filename(kTempDirectory, suggestedFilename, NULL)); + GUniquePtr<char> destination(g_build_filename(Test::dataDirectory(), suggestedFilename, nullptr)); GUniquePtr<char> destinationURI(g_filename_to_uri(destination.get(), 0, 0)); + if (test->m_shouldDelayDecideDestination) + g_usleep(0.2 * G_USEC_PER_SEC); webkit_download_set_destination(download, destinationURI.get()); return TRUE; } @@ -457,17 +555,17 @@ public: void waitUntilDownloadFinished() { - g_signal_connect(m_download.get(), "decide-destination", G_CALLBACK(downloadDecideDestinationCallback), this); - g_signal_connect(m_download.get(), "finished", G_CALLBACK(downloadFinishedCallback), this); g_main_loop_run(m_mainLoop); } GRefPtr<WebKitDownload> m_download; + bool m_shouldDelayDecideDestination { false }; }; static void testWebViewDownloadURI(WebViewDownloadTest* test, gconstpointer) { GRefPtr<WebKitDownload> download = adoptGRef(webkit_web_view_download_uri(test->m_webView, kServer->getURIForPath("/test.pdf").data())); + test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(download.get())); test->waitUntilDownloadStarted(); g_assert(test->m_webView == webkit_download_get_web_view(download.get())); test->waitUntilDownloadFinished(); @@ -511,7 +609,28 @@ public: static void testPolicyResponseDownload(PolicyResponseDownloadTest* test, gconstpointer) { - // Test that a download started by the the policy checker contains the web view. + CString requestURI = kServer->getURIForPath("/test.pdf").data(); + // Delay the DecideDestination to ensure that the load is aborted before the network task has became a download. + // See https://bugs.webkit.org/show_bug.cgi?id=164220. + test->m_shouldDelayDecideDestination = true; + test->loadURI(requestURI.data()); + test->waitUntilDownloadStarted(); + + WebKitURIRequest* request = webkit_download_get_request(test->m_download.get()); + g_assert(request); + ASSERT_CMP_CSTRING(webkit_uri_request_get_uri(request), ==, requestURI); + + g_assert(test->m_webView == webkit_download_get_web_view(test->m_download.get())); + test->waitUntilDownloadFinished(); + + GRefPtr<GFile> downloadFile = adoptGRef(g_file_new_for_uri(webkit_download_get_destination(test->m_download.get()))); + GRefPtr<GFileInfo> downloadFileInfo = adoptGRef(g_file_query_info(downloadFile.get(), G_FILE_ATTRIBUTE_STANDARD_SIZE, static_cast<GFileQueryInfoFlags>(0), nullptr, nullptr)); + g_assert_cmpint(g_file_info_get_size(downloadFileInfo.get()), >, 0); + g_file_delete(downloadFile.get(), nullptr, nullptr); +} + +static void testPolicyResponseDownloadCancel(PolicyResponseDownloadTest* test, gconstpointer) +{ CString requestURI = kServer->getURIForPath("/test.pdf").data(); test->loadURI(requestURI.data()); test->waitUntilDownloadStarted(); @@ -524,24 +643,146 @@ static void testPolicyResponseDownload(PolicyResponseDownloadTest* test, gconstp test->cancelDownloadAndWaitUntilFinished(); } +static void testDownloadMIMEType(DownloadTest* test, gconstpointer) +{ + GRefPtr<WebKitDownload> download = test->downloadURIAndWaitUntilFinishes(kServer->getURIForPath("/unknown")); + g_assert(!webkit_download_get_web_view(download.get())); + + Vector<DownloadTest::DownloadEvent>& events = test->m_downloadEvents; + g_assert_cmpint(events.size(), ==, 5); + g_assert_cmpint(events[0], ==, DownloadTest::Started); + g_assert_cmpint(events[1], ==, DownloadTest::ReceivedResponse); + g_assert_cmpint(events[2], ==, DownloadTest::CreatedDestination); + g_assert_cmpint(events[3], ==, DownloadTest::ReceivedData); + g_assert_cmpint(events[4], ==, DownloadTest::Finished); + events.clear(); + + WebKitURIRequest* request = webkit_download_get_request(download.get()); + WEBKIT_IS_URI_REQUEST(request); + ASSERT_CMP_CSTRING(webkit_uri_request_get_uri(request), ==, kServer->getURIForPath("/unknown")); + + WebKitURIResponse* response = webkit_download_get_response(download.get()); + WEBKIT_IS_URI_RESPONSE(response); + g_assert_cmpstr(webkit_uri_response_get_mime_type(response), ==, "application/pdf"); + + g_assert(webkit_download_get_destination(download.get())); + g_assert_cmpfloat(webkit_download_get_estimated_progress(download.get()), ==, 1); + GUniquePtr<char> expectedFilename(g_strdup_printf("%s.pdf", kServerSuggestedFilename)); + test->checkDestinationAndDeleteFile(download.get(), expectedFilename.get()); +} + +static gboolean contextMenuCallback(WebKitWebView* webView, WebKitContextMenu* contextMenu, GdkEvent*, WebKitHitTestResult* hitTestResult, WebViewDownloadTest* test) +{ + g_assert(WEBKIT_IS_HIT_TEST_RESULT(hitTestResult)); + g_assert(webkit_hit_test_result_context_is_link(hitTestResult)); + GList* items = webkit_context_menu_get_items(contextMenu); + GRefPtr<WebKitContextMenuItem> contextMenuItem; + for (GList* l = items; l; l = g_list_next(l)) { + g_assert(WEBKIT_IS_CONTEXT_MENU_ITEM(l->data)); + auto* item = WEBKIT_CONTEXT_MENU_ITEM(l->data); + if (webkit_context_menu_item_get_stock_action(item) == WEBKIT_CONTEXT_MENU_ACTION_DOWNLOAD_LINK_TO_DISK) { + contextMenuItem = item; + break; + } + } + g_assert(contextMenuItem.get()); + webkit_context_menu_remove_all(contextMenu); + webkit_context_menu_append(contextMenu, contextMenuItem.get()); + test->quitMainLoop(); + return FALSE; +} + +static void testContextMenuDownloadActions(WebViewDownloadTest* test, gconstpointer) +{ + test->showInWindowAndWaitUntilMapped(); + + static const char* linkHTMLFormat = "<html><body><a style='position:absolute; left:1; top:1' href='%s'>Download Me</a></body></html>"; + GUniquePtr<char> linkHTML(g_strdup_printf(linkHTMLFormat, kServer->getURIForPath("/test.pdf").data())); + test->loadHtml(linkHTML.get(), kServer->getURIForPath("/").data()); + test->waitUntilLoadFinished(); + + g_signal_connect(test->m_webView, "context-menu", G_CALLBACK(contextMenuCallback), test); + g_idle_add([](gpointer userData) -> gboolean { + auto* test = static_cast<WebViewDownloadTest*>(userData); + test->clickMouseButton(1, 1, 3); + return FALSE; + }, test); + g_main_loop_run(test->m_mainLoop); + + g_idle_add([](gpointer userData) -> gboolean { + auto* test = static_cast<WebViewDownloadTest*>(userData); + // Select and activate the context menu action. + test->keyStroke(GDK_KEY_Down); + test->keyStroke(GDK_KEY_Return); + return FALSE; + }, test); + test->waitUntilDownloadStarted(); + + g_assert(test->m_webView == webkit_download_get_web_view(test->m_download.get())); + test->waitUntilDownloadFinished(); + + GRefPtr<GFile> downloadFile = adoptGRef(g_file_new_for_uri(webkit_download_get_destination(test->m_download.get()))); + GRefPtr<GFileInfo> downloadFileInfo = adoptGRef(g_file_query_info(downloadFile.get(), G_FILE_ATTRIBUTE_STANDARD_SIZE, static_cast<GFileQueryInfoFlags>(0), nullptr, nullptr)); + g_assert_cmpint(g_file_info_get_size(downloadFileInfo.get()), >, 0); + g_file_delete(downloadFile.get(), nullptr, nullptr); +} + +static void testBlobDownload(WebViewDownloadTest* test, gconstpointer) +{ + test->showInWindowAndWaitUntilMapped(); + + static const char* linkBlobHTML = + "<html><body>" + "<a id='downloadLink' style='position:absolute; left:1; top:1' download='foo.pdf'>Download Me</a>" + "<script>" + " blob = new Blob(['Hello world'], {type: 'text/plain'});" + " document.getElementById('downloadLink').href = window.URL.createObjectURL(blob);" + "</script>" + "</body></html>"; + test->loadHtml(linkBlobHTML, kServer->getURIForPath("/").data()); + test->waitUntilLoadFinished(); + + g_idle_add([](gpointer userData) -> gboolean { + auto* test = static_cast<WebViewDownloadTest*>(userData); + test->clickMouseButton(1, 1, 1); + return FALSE; + }, test); + test->waitUntilDownloadStarted(); + + g_assert(test->m_webView == webkit_download_get_web_view(test->m_download.get())); + test->waitUntilDownloadFinished(); + + GRefPtr<GFile> downloadFile = adoptGRef(g_file_new_for_uri(webkit_download_get_destination(test->m_download.get()))); + GRefPtr<GFileInfo> downloadFileInfo = adoptGRef(g_file_query_info(downloadFile.get(), G_FILE_ATTRIBUTE_STANDARD_SIZE, static_cast<GFileQueryInfoFlags>(0), nullptr, nullptr)); + GUniquePtr<char> downloadPath(g_file_get_path(downloadFile.get())); + GUniqueOutPtr<char> downloadContents; + gsize downloadContentsLength; + g_assert(g_file_get_contents(downloadPath.get(), &downloadContents.outPtr(), &downloadContentsLength, nullptr)); + g_assert_cmpint(g_file_info_get_size(downloadFileInfo.get()), ==, downloadContentsLength); + g_assert_cmpstr(downloadContents.get(), ==, "Hello world"); + g_file_delete(downloadFile.get(), nullptr, nullptr); +} + void beforeAll() { kServer = new WebKitTestServer(); kServer->run(serverCallback); - kTempDirectory = g_dir_make_tmp("WebKit2Tests-XXXXXX", 0); - g_assert(kTempDirectory); - DownloadTest::add("Downloads", "local-file", testDownloadLocalFile); + DownloadTest::add("Downloads", "overwrite-destination-allowed", testDownloadOverwriteDestinationAllowed); + DownloadErrorTest::add("Downloads", "overwrite-destination-disallowed", testDownloadOverwriteDestinationDisallowed); DownloadErrorTest::add("Downloads", "local-file-error", testDownloadLocalFileError); DownloadTest::add("Downloads", "remote-file", testDownloadRemoteFile); DownloadErrorTest::add("Downloads", "remote-file-error", testDownloadRemoteFileError); WebViewDownloadTest::add("WebKitWebView", "download-uri", testWebViewDownloadURI); PolicyResponseDownloadTest::add("Downloads", "policy-decision-download", testPolicyResponseDownload); + PolicyResponseDownloadTest::add("Downloads", "policy-decision-download-cancel", testPolicyResponseDownloadCancel); + DownloadTest::add("Downloads", "mime-type", testDownloadMIMEType); + WebViewDownloadTest::add("Downloads", "contex-menu-download-actions", testContextMenuDownloadActions); + WebViewDownloadTest::add("Downloads", "blob-download", testBlobDownload); } void afterAll() { delete kServer; - g_rmdir(kTempDirectory); } diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestEditor.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestEditor.cpp new file mode 100644 index 000000000..fc429d4c3 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestEditor.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2015 Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2,1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#include "WebViewTest.h" +#include <gtk/gtk.h> +#include <webkit2/webkit2.h> + +static void testWebKitWebEditorSelectionChanged(WebViewTest* test, gconstpointer) +{ + static const gchar* testHTML = "<html><body>All work and no play make Jack a dull boy.</body></html>"; + test->loadHtml(testHTML, nullptr); + test->waitUntilLoadFinished(); + + g_assert(test->runWebProcessTest("WebKitWebEditor", "selection-changed")); +} + +void beforeAll() +{ + WebViewTest::add("WebKitWebEditor", "selection-changed", testWebKitWebEditorSelectionChanged); +} + +void afterAll() +{ +} diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestFrame.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestFrame.cpp index 69b5082cd..9145ab813 100644 --- a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestFrame.cpp +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestFrame.cpp @@ -49,8 +49,6 @@ static void testWebKitFrameJavaScriptContext(WebViewTest* test, gconstpointer) void beforeAll() { - webkit_web_context_set_web_extensions_directory(webkit_web_context_get_default(), WEBKIT_TEST_WEB_EXTENSIONS_DIR); - WebViewTest::add("WebKitFrame", "main-frame", testWebKitFrameMainFrame); WebViewTest::add("WebKitFrame", "uri", testWebKitFrameURI); WebViewTest::add("WebKitFrame", "javascript-context", testWebKitFrameJavaScriptContext); diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestInspector.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestInspector.cpp index 1f0430389..3d993e24f 100644 --- a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestInspector.cpp +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestInspector.cpp @@ -21,7 +21,7 @@ #include "WebViewTest.h" #include <wtf/Vector.h> -#include <wtf/gobject/GRefPtr.h> +#include <wtf/glib/GRefPtr.h> class InspectorTest: public WebViewTest { public: @@ -125,10 +125,22 @@ public: g_main_loop_run(m_mainLoop); } + static void canAttachChanged(InspectorTest* test) + { + g_main_loop_quit(test->m_mainLoop); + } + void resizeViewAndAttach() { // Resize the view to make room for the inspector. - resizeView(gMinimumAttachedInspectorWidth, (gMinimumAttachedInspectorHeight + 1) * 4 / 3); + if (!webkit_web_inspector_get_can_attach(m_inspector)) { + unsigned long handler = g_signal_connect_swapped(m_inspector, "notify::can-attach", G_CALLBACK(canAttachChanged), this); + resizeView(gMinimumAttachedInspectorWidth, (gMinimumAttachedInspectorHeight + 1) * 4 / 3); + g_main_loop_run(m_mainLoop); + g_signal_handler_disconnect(m_inspector, handler); + } + + g_assert(webkit_web_inspector_get_can_attach(m_inspector)); webkit_web_inspector_attach(m_inspector); } @@ -167,6 +179,7 @@ static void testInspectorDefault(InspectorTest* test, gconstpointer) test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(inspectorView.get())); g_assert(!webkit_web_inspector_is_attached(test->m_inspector)); g_assert_cmpuint(webkit_web_inspector_get_attached_height(test->m_inspector), ==, 0); + g_assert(!webkit_web_inspector_get_can_attach(test->m_inspector)); Vector<InspectorTest::InspectorEvents>& events = test->m_events; g_assert_cmpint(events.size(), ==, 1); g_assert_cmpint(events[0], ==, InspectorTest::OpenWindow); diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestInspectorServer.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestInspectorServer.cpp index 58fefea5e..bbdd104c5 100644 --- a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestInspectorServer.cpp +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestInspectorServer.cpp @@ -25,7 +25,7 @@ #include "config.h" #include "WebViewTest.h" -#include <wtf/gobject/GRefPtr.h> +#include <wtf/glib/GRefPtr.h> #include <wtf/text/WTFString.h> // Name of the test server application creating the webView object. @@ -237,12 +237,10 @@ static void openRemoteDebuggingSession(InspectorServerTest* test, gconstpointer) test->loadURI(resolvedURL.utf8().data()); test->waitUntilLoadFinished(); - javascriptResult = test->runJavaScriptAndWaitUntilFinished("window.document.getElementsByTagName('li')[0].title", &error.outPtr()); - g_assert(javascriptResult); - g_assert(!error.get()); - - GUniquePtr<char> title(WebViewTest::javascriptResultToCString(javascriptResult)); - g_assert_cmpstr(title.get(), ==, "http://127.0.0.1:2999/"); + const char* title = webkit_web_view_get_title(test->m_webView); + if (!title || !*title) + test->waitUntilTitleChanged(); + g_assert_cmpstr(webkit_web_view_get_title(test->m_webView), ==, "127.0.0.1"); } static void sendIncompleteRequest(InspectorServerTest* test, gconstpointer) diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestLoaderClient.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestLoaderClient.cpp index 331915e00..7dd69f5f9 100644 --- a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestLoaderClient.cpp +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestLoaderClient.cpp @@ -27,6 +27,7 @@ #include "WebViewTest.h" #include <gtk/gtk.h> #include <libsoup/soup.h> +#include <wtf/Vector.h> #include <wtf/text/CString.h> static WebKitTestBus* bus; @@ -82,6 +83,13 @@ static void testLoadAlternateHTML(LoadTrackingTest* test, gconstpointer) assertNormalLoadHappened(test->m_loadEvents); } +static void testLoadAlternateHTMLForLocalPage(LoadTrackingTest* test, gconstpointer) +{ + test->loadAlternateHTML("<html><body>Alternate page</body></html>", "file:///not/actually/loaded.html", 0); + test->waitUntilLoadFinished(); + assertNormalLoadHappened(test->m_loadEvents); +} + static void testLoadPlainText(LoadTrackingTest* test, gconstpointer) { test->loadPlainText("Hello WebKit-GTK+"); @@ -89,6 +97,18 @@ static void testLoadPlainText(LoadTrackingTest* test, gconstpointer) assertNormalLoadHappened(test->m_loadEvents); } +static void testLoadBytes(LoadTrackingTest* test, gconstpointer) +{ + GUniquePtr<char> filePath(g_build_filename(Test::getResourcesDir().data(), "blank.ico", nullptr)); + char* contents; + gsize contentsLength; + g_file_get_contents(filePath.get(), &contents, &contentsLength, nullptr); + GRefPtr<GBytes> bytes = adoptGRef(g_bytes_new_take(contents, contentsLength)); + test->loadBytes(bytes.get(), "image/vnd.microsoft.icon", nullptr, nullptr); + test->waitUntilLoadFinished(); + assertNormalLoadHappened(test->m_loadEvents); +} + static void testLoadRequest(LoadTrackingTest* test, gconstpointer) { GRefPtr<WebKitURIRequest> request(webkit_uri_request_new(kServer->getURIForPath("/normal").data())); @@ -97,6 +117,14 @@ static void testLoadRequest(LoadTrackingTest* test, gconstpointer) assertNormalLoadHappened(test->m_loadEvents); } +static void testLoadFromGResource(LoadTrackingTest* test, gconstpointer) +{ + GRefPtr<WebKitURIRequest> request(webkit_uri_request_new("resource:///org/webkit/webkit2gtk/tests/boring.html")); + test->loadRequest(request.get()); + test->waitUntilLoadFinished(); + assertNormalLoadHappened(test->m_loadEvents); +} + class LoadStopTrackingTest : public LoadTrackingTest { public: MAKE_GLIB_TEST_FIXTURE(LoadStopTrackingTest); @@ -182,51 +210,100 @@ public: static void uriChanged(GObject*, GParamSpec*, ViewURITrackingTest* test) { - g_assert_cmpstr(test->m_activeURI.data(), !=, webkit_web_view_get_uri(test->m_webView)); - test->m_activeURI = webkit_web_view_get_uri(test->m_webView); + g_assert_cmpstr(test->m_currentURI.data(), !=, webkit_web_view_get_uri(test->m_webView)); + test->m_currentURI = webkit_web_view_get_uri(test->m_webView); } ViewURITrackingTest() - : m_activeURI(webkit_web_view_get_uri(m_webView)) + : m_currentURI(webkit_web_view_get_uri(m_webView)) { - g_assert(m_activeURI.isNull()); + g_assert(m_currentURI.isNull()); + m_currentURIList.grow(m_currentURIList.capacity()); g_signal_connect(m_webView, "notify::uri", G_CALLBACK(uriChanged), this); } + enum State { Provisional, ProvisionalAfterRedirect, Commited, Finished }; + + void loadURI(const char* uri) + { + reset(); + LoadTrackingTest::loadURI(uri); + } + void provisionalLoadStarted() { - checkActiveURI("/redirect"); + m_currentURIList[Provisional] = m_currentURI; } void provisionalLoadReceivedServerRedirect() { - checkActiveURI("/normal"); + m_currentURIList[ProvisionalAfterRedirect] = m_currentURI; } void loadCommitted() { - checkActiveURI("/normal"); + m_currentURIList[Commited] = m_currentURI; } void loadFinished() { - checkActiveURI("/normal"); + m_currentURIList[Finished] = m_currentURI; LoadTrackingTest::loadFinished(); } - CString m_activeURI; + void checkURIAtState(State state, const char* path) + { + if (path) + ASSERT_CMP_CSTRING(m_currentURIList[state], ==, kServer->getURIForPath(path)); + else + g_assert(m_currentURIList[state].isNull()); + } private: - void checkActiveURI(const char* uri) + void reset() { - ASSERT_CMP_CSTRING(m_activeURI, ==, kServer->getURIForPath(uri)); + m_currentURI = CString(); + m_currentURIList.clear(); + m_currentURIList.grow(m_currentURIList.capacity()); } + + CString m_currentURI; + Vector<CString, 4> m_currentURIList; }; static void testWebViewActiveURI(ViewURITrackingTest* test, gconstpointer) { + // Normal load, the URL doesn't change. + test->loadURI(kServer->getURIForPath("/normal1").data()); + test->waitUntilLoadFinished(); + test->checkURIAtState(ViewURITrackingTest::State::Provisional, "/normal1"); + test->checkURIAtState(ViewURITrackingTest::State::ProvisionalAfterRedirect, nullptr); + test->checkURIAtState(ViewURITrackingTest::State::Commited, "/normal1"); + test->checkURIAtState(ViewURITrackingTest::State::Finished, "/normal1"); + + // Redirect, the URL changes after the redirect. test->loadURI(kServer->getURIForPath("/redirect").data()); test->waitUntilLoadFinished(); + test->checkURIAtState(ViewURITrackingTest::State::Provisional, "/redirect"); + test->checkURIAtState(ViewURITrackingTest::State::ProvisionalAfterRedirect, "/normal"); + test->checkURIAtState(ViewURITrackingTest::State::Commited, "/normal"); + test->checkURIAtState(ViewURITrackingTest::State::Finished, "/normal"); + + // Normal load, URL changed by WebKitPage::send-request. + test->loadURI(kServer->getURIForPath("/normal-change-request").data()); + test->waitUntilLoadFinished(); + test->checkURIAtState(ViewURITrackingTest::State::Provisional, "/normal-change-request"); + test->checkURIAtState(ViewURITrackingTest::State::ProvisionalAfterRedirect, nullptr); + test->checkURIAtState(ViewURITrackingTest::State::Commited, "/request-changed"); + test->checkURIAtState(ViewURITrackingTest::State::Finished, "/request-changed"); + + // Redirect, URL changed by WebKitPage::send-request. + test->loadURI(kServer->getURIForPath("/redirect-to-change-request").data()); + test->waitUntilLoadFinished(); + test->checkURIAtState(ViewURITrackingTest::State::Provisional, "/redirect-to-change-request"); + test->checkURIAtState(ViewURITrackingTest::State::ProvisionalAfterRedirect, "/normal-change-request"); + test->checkURIAtState(ViewURITrackingTest::State::Commited, "/request-changed-on-redirect"); + test->checkURIAtState(ViewURITrackingTest::State::Finished, "/request-changed-on-redirect"); } class ViewIsLoadingTest: public LoadTrackingTest { @@ -303,7 +380,8 @@ public: WebPageURITest() { - GRefPtr<GDBusProxy> proxy = adoptGRef(bus->createProxy("org.webkit.gtk.WebExtensionTest", + GUniquePtr<char> extensionBusName(g_strdup_printf("org.webkit.gtk.WebExtensionTest%u", Test::s_webExtensionID)); + GRefPtr<GDBusProxy> proxy = adoptGRef(bus->createProxy(extensionBusName.get(), "/org/webkit/gtk/WebExtensionTest", "org.webkit.gtk.WebExtensionTest", m_mainLoop)); m_uriChangedSignalID = g_dbus_connection_signal_subscribe( g_dbus_proxy_get_connection(proxy.get()), @@ -327,6 +405,20 @@ public: g_dbus_connection_signal_unsubscribe(bus->connection(), m_uriChangedSignalID); } + void loadURI(const char* uri) + { + m_webPageURIs.clear(); + m_webViewURIs.clear(); + WebViewTest::loadURI(uri); + } + + void checkViewAndPageURIsMatch() const + { + g_assert_cmpint(m_webPageURIs.size(), ==, m_webViewURIs.size()); + for (size_t i = 0; i < m_webPageURIs.size(); ++i) + ASSERT_CMP_CSTRING(m_webPageURIs[i], ==, m_webViewURIs[i]); + } + unsigned m_uriChangedSignalID; Vector<CString> m_webPageURIs; Vector<CString> m_webViewURIs; @@ -334,17 +426,37 @@ public: static void testWebPageURI(WebPageURITest* test, gconstpointer) { - test->loadURI(kServer->getURIForPath("/redirect").data()); + // Normal load. + test->loadURI(kServer->getURIForPath("/normal1").data()); test->waitUntilLoadFinished(); + test->checkViewAndPageURIsMatch(); + g_assert_cmpint(test->m_webPageURIs.size(), ==, 1); + ASSERT_CMP_CSTRING(test->m_webPageURIs[0], ==, kServer->getURIForPath("/normal1")); - g_assert_cmpint(test->m_webPageURIs.size(), ==, test->m_webViewURIs.size()); - for (size_t i = 0; i < test->m_webPageURIs.size(); ++i) - ASSERT_CMP_CSTRING(test->m_webPageURIs[i], ==, test->m_webViewURIs[i]); - + // Redirect + test->loadURI(kServer->getURIForPath("/redirect").data()); + test->waitUntilLoadFinished(); + test->checkViewAndPageURIsMatch(); g_assert_cmpint(test->m_webPageURIs.size(), ==, 2); ASSERT_CMP_CSTRING(test->m_webPageURIs[0], ==, kServer->getURIForPath("/redirect")); ASSERT_CMP_CSTRING(test->m_webPageURIs[1], ==, kServer->getURIForPath("/normal")); + // Normal load, URL changed by WebKitPage::send-request. + test->loadURI(kServer->getURIForPath("/normal-change-request").data()); + test->waitUntilLoadFinished(); + test->checkViewAndPageURIsMatch(); + g_assert_cmpint(test->m_webPageURIs.size(), ==, 2); + ASSERT_CMP_CSTRING(test->m_webPageURIs[0], ==, kServer->getURIForPath("/normal-change-request")); + ASSERT_CMP_CSTRING(test->m_webPageURIs[1], ==, kServer->getURIForPath("/request-changed")); + + // Redirect, URL changed by WebKitPage::send-request. + test->loadURI(kServer->getURIForPath("/redirect-to-change-request").data()); + test->waitUntilLoadFinished(); + test->checkViewAndPageURIsMatch(); + g_assert_cmpint(test->m_webPageURIs.size(), ==, 3); + ASSERT_CMP_CSTRING(test->m_webPageURIs[0], ==, kServer->getURIForPath("/redirect-to-change-request")); + ASSERT_CMP_CSTRING(test->m_webPageURIs[1], ==, kServer->getURIForPath("/normal-change-request")); + ASSERT_CMP_CSTRING(test->m_webPageURIs[2], ==, kServer->getURIForPath("/request-changed-on-redirect")); } static void testURIRequestHTTPHeaders(WebViewTest* test, gconstpointer) @@ -384,6 +496,53 @@ static void testURIRequestHTTPHeaders(WebViewTest* test, gconstpointer) g_assert(!strncmp(mainResourceData, "1", mainResourceDataSize)); } +static void testURIRequestHTTPMethod(WebViewTest* test, gconstpointer) +{ + GRefPtr<WebKitURIRequest> uriRequest = adoptGRef(webkit_uri_request_new("file:///foo/bar")); + g_assert(uriRequest.get()); + g_assert_cmpstr(webkit_uri_request_get_uri(uriRequest.get()), ==, "file:///foo/bar"); + g_assert(!webkit_uri_request_get_http_method(uriRequest.get())); + + webkit_uri_request_set_uri(uriRequest.get(), kServer->getURIForPath("/http-get-method").data()); + test->loadRequest(uriRequest.get()); + test->waitUntilLoadFinished(); + + test->runJavaScriptAndWaitUntilFinished("xhr = new XMLHttpRequest; xhr.open('POST', '/http-post-method', false); xhr.send();", nullptr); +} + +static void testURIResponseHTTPHeaders(WebViewTest* test, gconstpointer) +{ + test->loadHtml("<html><body>No HTTP headers</body></html>", "file:///"); + test->waitUntilLoadFinished(); + WebKitWebResource* resource = webkit_web_view_get_main_resource(test->m_webView); + g_assert(WEBKIT_IS_WEB_RESOURCE(resource)); + WebKitURIResponse* response = webkit_web_resource_get_response(resource); + g_assert(WEBKIT_IS_URI_RESPONSE(response)); + g_assert(!webkit_uri_response_get_http_headers(response)); + + test->loadURI(kServer->getURIForPath("/headers").data()); + test->waitUntilLoadFinished(); + resource = webkit_web_view_get_main_resource(test->m_webView); + g_assert(WEBKIT_IS_WEB_RESOURCE(resource)); + response = webkit_web_resource_get_response(resource); + g_assert(WEBKIT_IS_URI_RESPONSE(response)); + SoupMessageHeaders* headers = webkit_uri_response_get_http_headers(response); + g_assert(headers); + g_assert_cmpstr(soup_message_headers_get_one(headers, "Foo"), ==, "bar"); +} + +static void testRedirectToDataURI(WebViewTest* test, gconstpointer) +{ + test->loadURI(kServer->getURIForPath("/redirect-to-data").data()); + test->waitUntilLoadFinished(); + + static const char* expectedData = "data-uri"; + size_t mainResourceDataSize = 0; + const char* mainResourceData = test->mainResourceData(mainResourceDataSize); + g_assert_cmpint(mainResourceDataSize, ==, strlen(expectedData)); + g_assert(!strncmp(mainResourceData, expectedData, mainResourceDataSize)); +} + static void serverCallback(SoupServer* server, SoupMessage* message, const char* path, GHashTable*, SoupClientContext*, gpointer) { static const char* responseString = "<html><body>Testing!Testing!Testing!Testing!Testing!Testing!Testing!" @@ -402,13 +561,16 @@ static void serverCallback(SoupServer* server, SoupMessage* message, const char* soup_message_set_status(message, SOUP_STATUS_OK); - if (g_str_has_prefix(path, "/normal")) + if (g_str_has_prefix(path, "/normal") || g_str_has_prefix(path, "/http-get-method")) soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, responseString, strlen(responseString)); else if (g_str_equal(path, "/error")) soup_message_set_status(message, SOUP_STATUS_CANT_CONNECT); else if (g_str_equal(path, "/redirect")) { soup_message_set_status(message, SOUP_STATUS_MOVED_PERMANENTLY); soup_message_headers_append(message->response_headers, "Location", "/normal"); + } else if (g_str_equal(path, "/redirect-to-change-request")) { + soup_message_set_status(message, SOUP_STATUS_MOVED_PERMANENTLY); + soup_message_headers_append(message->response_headers, "Location", "/normal-change-request"); } else if (g_str_equal(path, "/cancelled")) { soup_message_headers_set_encoding(message->response_headers, SOUP_ENCODING_CHUNKED); soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, responseString, strlen(responseString)); @@ -421,6 +583,12 @@ static void serverCallback(SoupServer* server, SoupMessage* message, const char* else soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, kDNTHeaderNotPresent, strlen(kDNTHeaderNotPresent)); soup_message_set_status(message, SOUP_STATUS_OK); + } else if (g_str_equal(path, "/headers")) { + soup_message_headers_append(message->response_headers, "Foo", "bar"); + soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, responseString, strlen(responseString)); + } else if (g_str_equal(path, "/redirect-to-data")) { + soup_message_set_status(message, SOUP_STATUS_MOVED_PERMANENTLY); + soup_message_headers_append(message->response_headers, "Location", "data:text/plain;charset=utf-8,data-uri"); } else soup_message_set_status(message, SOUP_STATUS_NOT_FOUND); @@ -429,7 +597,6 @@ static void serverCallback(SoupServer* server, SoupMessage* message, const char* void beforeAll() { - webkit_web_context_set_web_extensions_directory(webkit_web_context_get_default(), WEBKIT_TEST_WEB_EXTENSIONS_DIR); bus = new WebKitTestBus(); if (!bus->run()) return; @@ -441,8 +608,11 @@ void beforeAll() LoadTrackingTest::add("WebKitWebView", "loading-error", testLoadingError); LoadTrackingTest::add("WebKitWebView", "load-html", testLoadHtml); LoadTrackingTest::add("WebKitWebView", "load-alternate-html", testLoadAlternateHTML); + LoadTrackingTest::add("WebKitWebView", "load-alternate-html-for-local-page", testLoadAlternateHTMLForLocalPage); LoadTrackingTest::add("WebKitWebView", "load-plain-text", testLoadPlainText); + LoadTrackingTest::add("WebKitWebView", "load-bytes", testLoadBytes); LoadTrackingTest::add("WebKitWebView", "load-request", testLoadRequest); + LoadTrackingTest::add("WebKitWebView", "load-gresource", testLoadFromGResource); LoadStopTrackingTest::add("WebKitWebView", "stop-loading", testLoadCancelled); LoadTrackingTest::add("WebKitWebView", "title", testWebViewTitle); LoadTrackingTest::add("WebKitWebView", "progress", testLoadProgress); @@ -456,6 +626,9 @@ void beforeAll() ViewIsLoadingTest::add("WebKitWebView", "is-loading", testWebViewIsLoading); WebPageURITest::add("WebKitWebPage", "get-uri", testWebPageURI); WebViewTest::add("WebKitURIRequest", "http-headers", testURIRequestHTTPHeaders); + WebViewTest::add("WebKitURIRequest", "http-method", testURIRequestHTTPMethod); + WebViewTest::add("WebKitURIResponse", "http-headers", testURIResponseHTTPHeaders); + WebViewTest::add("WebKitWebPage", "redirect-to-data-uri", testRedirectToDataURI); } void afterAll() diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestMultiprocess.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestMultiprocess.cpp index f48b6a3b6..020ad3529 100644 --- a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestMultiprocess.cpp +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestMultiprocess.cpp @@ -26,8 +26,6 @@ #include <wtf/Vector.h> static const unsigned numViews = 2; -static guint32 nextInitializationId = 1; -static unsigned initializeWebExtensionsSignalCount; static WebKitTestBus* bus; class MultiprocessTest: public Test { @@ -36,8 +34,18 @@ public: MultiprocessTest() : m_mainLoop(g_main_loop_new(nullptr, TRUE)) + , m_initializeWebExtensionsSignalCount(0) , m_webViewBusNames(numViews) - , m_webViews(numViews) { } + , m_webViews(numViews) + { + webkit_web_context_set_process_model(m_webContext.get(), WEBKIT_PROCESS_MODEL_MULTIPLE_SECONDARY_PROCESSES); + } + + void initializeWebExtensions() override + { + Test::initializeWebExtensions(); + m_initializeWebExtensionsSignalCount++; + } static void loadChanged(WebKitWebView* webView, WebKitLoadEvent loadEvent, MultiprocessTest* test) { @@ -51,11 +59,11 @@ public: { g_assert_cmpuint(index, <, numViews); - m_webViewBusNames[index] = GUniquePtr<char>(g_strdup_printf("org.webkit.gtk.WebExtensionTest%u", nextInitializationId)); - - m_webViews[index] = WEBKIT_WEB_VIEW(webkit_web_view_new()); + m_webViews[index] = WEBKIT_WEB_VIEW(webkit_web_view_new_with_context(m_webContext.get())); assertObjectIsDeletedWhenTestFinishes(G_OBJECT(m_webViews[index].get())); + m_webViewBusNames[index] = GUniquePtr<char>(g_strdup_printf("org.webkit.gtk.WebExtensionTest%u", Test::s_webExtensionID)); + webkit_web_view_load_html(m_webViews[index].get(), "<html></html>", nullptr); g_signal_connect(m_webViews[index].get(), "load-changed", G_CALLBACK(loadChanged), this); g_main_loop_run(m_mainLoop); @@ -88,8 +96,6 @@ public: void destroyWebViewAndWaitUntilWebProcessFinishes(unsigned index) { - // FIXME: This test is disabled because the web processed don't actually die - // due to bug https://bugs.webkit.org/show_bug.cgi?id=129684. g_assert_cmpuint(index, <, numViews); unsigned watcherID = g_bus_watch_name_on_connection(bus->connection(), m_webViewBusNames[index].get(), G_BUS_NAME_WATCHER_FLAGS_NONE, @@ -100,6 +106,7 @@ public: } GMainLoop* m_mainLoop; + unsigned m_initializeWebExtensionsSignalCount; Vector<GUniquePtr<char>, numViews> m_webViewBusNames; Vector<GRefPtr<WebKitWebView>, numViews> m_webViews; }; @@ -118,7 +125,7 @@ static void testProcessPerWebView(MultiprocessTest* test, gconstpointer) g_assert(test->m_webViewBusNames[i]); } - g_assert_cmpuint(initializeWebExtensionsSignalCount, ==, numViews); + g_assert_cmpuint(test->m_initializeWebExtensionsSignalCount, ==, numViews); g_assert_cmpstr(test->m_webViewBusNames[0].get(), !=, test->m_webViewBusNames[1].get()); g_assert_cmpuint(test->webProcessPid(0), !=, test->webProcessPid(1)); @@ -130,7 +137,7 @@ static void testProcessPerWebView(MultiprocessTest* test, gconstpointer) } } -class UIClientMultiprocessTest: public WebViewTest { +class UIClientMultiprocessTest: public Test { public: MAKE_GLIB_TEST_FIXTURE(UIClientMultiprocessTest); @@ -140,7 +147,7 @@ public: Close }; - static GtkWidget* viewCreateCallback(WebKitWebView* webView, UIClientMultiprocessTest* test) + static GtkWidget* viewCreateCallback(WebKitWebView* webView, WebKitNavigationAction*, UIClientMultiprocessTest* test) { return test->viewCreate(webView); } @@ -156,14 +163,26 @@ public: } UIClientMultiprocessTest() + : m_mainLoop(g_main_loop_new(nullptr, TRUE)) + , m_initializeWebExtensionsSignalCount(0) { + webkit_web_context_set_process_model(m_webContext.get(), WEBKIT_PROCESS_MODEL_MULTIPLE_SECONDARY_PROCESSES); + m_webView = WEBKIT_WEB_VIEW(g_object_ref_sink(webkit_web_view_new_with_context(m_webContext.get()))); webkit_settings_set_javascript_can_open_windows_automatically(webkit_web_view_get_settings(m_webView), TRUE); + g_signal_connect(m_webView, "create", G_CALLBACK(viewCreateCallback), this); } ~UIClientMultiprocessTest() { g_signal_handlers_disconnect_matched(m_webView, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this); + gtk_widget_destroy(GTK_WIDGET(m_webView)); + } + + void initializeWebExtensions() override + { + Test::initializeWebExtensions(); + m_initializeWebExtensionsSignalCount++; } GtkWidget* viewCreate(WebKitWebView* webView) @@ -201,16 +220,15 @@ public: g_main_loop_run(m_mainLoop); } + WebKitWebView* m_webView; + GMainLoop* m_mainLoop; + unsigned m_initializeWebExtensionsSignalCount; Vector<WebViewEvents> m_webViewEvents; }; static void testMultiprocessWebViewCreateReadyClose(UIClientMultiprocessTest* test, gconstpointer) { - // At this point the web process of the current view has already been created. - // We save it here to check that after window.open() the number of processes - // is the same. - guint32 processCountBefore = nextInitializationId - 1; - test->loadHtml("<html><body onLoad=\"window.open().close();\"></html>", nullptr); + webkit_web_view_load_html(test->m_webView, "<html><body onLoad=\"window.open().close();\"></html>", nullptr); test->waitUntilNewWebViewClose(); Vector<UIClientMultiprocessTest::WebViewEvents>& events = test->m_webViewEvents; @@ -219,23 +237,27 @@ static void testMultiprocessWebViewCreateReadyClose(UIClientMultiprocessTest* te g_assert_cmpint(events[1], ==, UIClientMultiprocessTest::ReadyToShow); g_assert_cmpint(events[2], ==, UIClientMultiprocessTest::Close); - guint32 processesCountAfter = nextInitializationId - 1; - g_assert_cmpuint(processesCountAfter, ==, processCountBefore); + g_assert_cmpuint(test->m_initializeWebExtensionsSignalCount, ==, 1); } -static void initializeWebExtensions(WebKitWebContext* context, gpointer) +static void testWebProcessLimit(MultiprocessTest* test, gconstpointer) { - initializeWebExtensionsSignalCount++; - webkit_web_context_set_web_extensions_directory(context, WEBKIT_TEST_WEB_EXTENSIONS_DIR); - webkit_web_context_set_web_extensions_initialization_user_data(context, - g_variant_new_uint32(nextInitializationId++)); + g_assert_cmpuint(webkit_web_context_get_web_process_count_limit(test->m_webContext.get()), ==, 0); + + webkit_web_context_set_web_process_count_limit(test->m_webContext.get(), 1); + g_assert_cmpuint(webkit_web_context_get_web_process_count_limit(test->m_webContext.get()), ==, 1); + + // Create two web views but there should be only one web process. + for (unsigned i = 0; i < numViews; i++) { + test->loadWebViewAndWaitUntilLoaded(i); + g_assert(WEBKIT_IS_WEB_VIEW(test->m_webViews[i].get())); + } + + g_assert_cmpuint(test->m_initializeWebExtensionsSignalCount, ==, 1); } void beforeAll() { - g_signal_connect(webkit_web_context_get_default(), - "initialize-web-extensions", G_CALLBACK(initializeWebExtensions), nullptr); - // Check that default setting is the one stated in the documentation g_assert_cmpuint(webkit_web_context_get_process_model(webkit_web_context_get_default()), ==, WEBKIT_PROCESS_MODEL_SHARED_SECONDARY_PROCESS); @@ -253,11 +275,10 @@ void beforeAll() MultiprocessTest::add("WebKitWebContext", "process-per-web-view", testProcessPerWebView); UIClientMultiprocessTest::add("WebKitWebView", "multiprocess-create-ready-close", testMultiprocessWebViewCreateReadyClose); + MultiprocessTest::add("WebKitWebContext", "web-process-limit", testWebProcessLimit); } void afterAll() { delete bus; - g_signal_handlers_disconnect_by_func(webkit_web_context_get_default(), - reinterpret_cast<void*>(initializeWebExtensions), nullptr); } diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestPrinting.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestPrinting.cpp index 99efa7cfb..2b5221192 100644 --- a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestPrinting.cpp +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestPrinting.cpp @@ -20,14 +20,12 @@ #include "config.h" #include "WebViewTest.h" #include <glib/gstdio.h> -#include <wtf/gobject/GRefPtr.h> +#include <wtf/glib/GRefPtr.h> #ifdef HAVE_GTK_UNIX_PRINTING #include <gtk/gtkunixprint.h> #endif -static char* kTempDirectory; - static void testPrintOperationPrintSettings(WebViewTest* test, gconstpointer) { GRefPtr<WebKitPrintOperation> printOperation = adoptGRef(webkit_print_operation_new(test->m_webView)); @@ -134,7 +132,7 @@ static void testPrintOperationPrint(PrintTest* test, gconstpointer) return; } - GUniquePtr<char> outputFilename(g_build_filename(kTempDirectory, "webkit-print.pdf", NULL)); + GUniquePtr<char> outputFilename(g_build_filename(Test::dataDirectory(), "webkit-print.pdf", nullptr)); GRefPtr<GFile> outputFile = adoptGRef(g_file_new_for_path(outputFilename.get())); GUniquePtr<char> outputURI(g_file_get_uri(outputFile.get())); @@ -194,7 +192,7 @@ class CloseAfterPrintTest: public WebViewTest { public: MAKE_GLIB_TEST_FIXTURE(CloseAfterPrintTest); - static GtkWidget* webViewCreate(WebKitWebView* webView, CloseAfterPrintTest* test) + static GtkWidget* webViewCreate(WebKitWebView* webView, WebKitNavigationAction*, CloseAfterPrintTest* test) { return test->createWebView(); } @@ -228,7 +226,7 @@ public: GtkWidget* createWebView() { - GtkWidget* newWebView = webkit_web_view_new(); + GtkWidget* newWebView = webkit_web_view_new_with_context(m_webContext.get()); g_object_ref_sink(newWebView); assertObjectIsDeletedWhenTestFinishes(G_OBJECT(newWebView)); @@ -247,7 +245,7 @@ public: return; } - GUniquePtr<char> outputFilename(g_build_filename(kTempDirectory, "webkit-close-after-print.pdf", NULL)); + GUniquePtr<char> outputFilename(g_build_filename(Test::dataDirectory(), "webkit-close-after-print.pdf", nullptr)); m_outputFile = adoptGRef(g_file_new_for_path(outputFilename.get())); GUniquePtr<char> outputURI(g_file_get_uri(m_outputFile.get())); @@ -288,23 +286,210 @@ static void testPrintOperationCloseAfterPrint(CloseAfterPrintTest* test, gconstp test->loadHtml("<html><body onLoad=\"w = window.open();w.print();w.close();\"></body></html>", 0); test->waitUntilPrintFinishedAndViewClosed(); } + +class PrintCustomWidgetTest: public WebViewTest { +public: + MAKE_GLIB_TEST_FIXTURE(PrintCustomWidgetTest); + + static void applyCallback(WebKitPrintCustomWidget*, PrintCustomWidgetTest* test) + { + test->m_applyEmitted = true; + } + + static gboolean scheduleJumpToCustomWidget(PrintCustomWidgetTest* test) + { + test->jumpToCustomWidget(); + + return FALSE; + } + + static void updateCallback(WebKitPrintCustomWidget* customWidget, GtkPageSetup*, GtkPrintSettings*, PrintCustomWidgetTest* test) + { + g_assert(test->m_widget == webkit_print_custom_widget_get_widget(customWidget)); + + test->m_updateEmitted = true; + // Would be nice to avoid the 1 second timeout here - but I didn't found + // a way to do so without making the test flaky. + g_timeout_add_seconds(1, reinterpret_cast<GSourceFunc>(scheduleJumpToCustomWidget), test); + } + + static void widgetRealizeCallback(GtkWidget* widget, PrintCustomWidgetTest* test) + { + g_assert(GTK_IS_LABEL(widget)); + g_assert(!g_strcmp0(gtk_label_get_text(GTK_LABEL(widget)), "Label")); + + test->m_widgetRealized = true; + test->startPrinting(); + } + + static WebKitPrintCustomWidget* createCustomWidgetCallback(WebKitPrintOperation* printOperation, PrintCustomWidgetTest* test) + { + test->m_createEmitted = true; + WebKitPrintCustomWidget* printCustomWidget = test->createPrintCustomWidget(); + test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(printCustomWidget)); + g_signal_connect(printCustomWidget, "apply", G_CALLBACK(applyCallback), test); + g_signal_connect(printCustomWidget, "update", G_CALLBACK(updateCallback), test); + + GtkWidget* widget = webkit_print_custom_widget_get_widget(printCustomWidget); + test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(widget)); + g_signal_connect(widget, "realize", G_CALLBACK(widgetRealizeCallback), test); + + return printCustomWidget; + } + + static gboolean scheduleMovementThroughDialog(PrintCustomWidgetTest* test) + { + test->jumpToFirstPrinter(); + + return FALSE; + } + + static gboolean openPrintDialog(PrintCustomWidgetTest* test) + { + g_idle_add(reinterpret_cast<GSourceFunc>(scheduleMovementThroughDialog), test); + test->m_response = webkit_print_operation_run_dialog(test->m_printOperation.get(), GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(test->m_webView)))); + + return FALSE; + } + + static void printOperationFinished(WebKitPrintOperation* printOperation, PrintCustomWidgetTest* test) + { + test->printFinished(); + } + + void printFinished() + { + g_assert(m_outputFile); + g_file_delete(m_outputFile.get(), nullptr, nullptr); + m_outputFile = nullptr; + g_main_loop_quit(m_mainLoop); + } + + void sendKeyEvent(unsigned gdkKeyValue, GdkEventType type, unsigned modifiers) + { + GdkEvent* event = gdk_event_new(type); + event->key.keyval = gdkKeyValue; + event->key.state = modifiers; + event->key.window = gtk_widget_get_window(GTK_WIDGET(m_webView)); + event->key.time = GDK_CURRENT_TIME; + g_object_ref(event->key.window); + gdk_event_set_device(event, gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gdk_display_get_default()))); + + GUniqueOutPtr<GdkKeymapKey> keys; + gint nKeys; + if (gdk_keymap_get_entries_for_keyval(gdk_keymap_get_default(), gdkKeyValue, &keys.outPtr(), &nKeys)) + event->key.hardware_keycode = keys.get()[0].keycode; + + gtk_main_do_event(event); + + gdk_event_free(event); + } + + void sendKeyPressAndReleaseEvent(unsigned gdkKeyValue, unsigned modifiers = 0) + { + sendKeyEvent(gdkKeyValue, GDK_KEY_PRESS, modifiers); + sendKeyEvent(gdkKeyValue, GDK_KEY_RELEASE, modifiers); + } + + void createWebKitPrintOperation() + { + m_printOperation = adoptGRef(webkit_print_operation_new(m_webView)); + g_assert(m_printOperation); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(m_printOperation.get())); + + g_signal_connect(m_printOperation.get(), "create-custom-widget", G_CALLBACK(createCustomWidgetCallback), this); + g_signal_connect(m_printOperation.get(), "finished", G_CALLBACK(printOperationFinished), this); + } + + WebKitPrintCustomWidget* createPrintCustomWidget() + { + m_widget = gtk_label_new("Label"); + return webkit_print_custom_widget_new(m_widget, "Custom Widget"); + } + + void startPrinting() + { + // To start printing it is enough to press the Return key + sendKeyPressAndReleaseEvent(GDK_KEY_Return); + } + + void jumpToFirstPrinter() + { + // Initially the GtkNotebook has focus, so we just need to press the Tab + // key to jump to the first printer + sendKeyPressAndReleaseEvent(GDK_KEY_Tab); + } + + void jumpToCustomWidget() + { + // Jump back to the GtkNotebook + sendKeyPressAndReleaseEvent(GDK_KEY_Tab, GDK_SHIFT_MASK); + // Custom widget is on the third tab + sendKeyPressAndReleaseEvent(GDK_KEY_Right); + sendKeyPressAndReleaseEvent(GDK_KEY_Right); + } + + void openDialogMoveThroughItAndWaitUntilClosed() + { + g_idle_add(reinterpret_cast<GSourceFunc>(openPrintDialog), this); + g_main_loop_run(m_mainLoop); + } + + GRefPtr<WebKitPrintOperation> m_printOperation; + GRefPtr<GFile> m_outputFile; + GtkWidget* m_widget; + bool m_widgetRealized {false}; + bool m_applyEmitted {false}; + bool m_updateEmitted {false}; + bool m_createEmitted {false}; + WebKitPrintOperationResponse m_response {WEBKIT_PRINT_OPERATION_RESPONSE_CANCEL}; +}; + +static void testPrintCustomWidget(PrintCustomWidgetTest* test, gconstpointer) +{ + test->showInWindowAndWaitUntilMapped(GTK_WINDOW_TOPLEVEL, 0, 0); + + test->loadHtml("<html><body>Text</body></html>", 0); + test->waitUntilLoadFinished(); + + test->createWebKitPrintOperation(); + + GRefPtr<GtkPrinter> printer = adoptGRef(findPrintToFilePrinter()); + if (!printer) { + g_message("%s", "Cannot test WebKitPrintOperation/print: no suitable printer found"); + return; + } + + GUniquePtr<char> outputFilename(g_build_filename(Test::dataDirectory(), "webkit-close-after-print.pdf", nullptr)); + test->m_outputFile = adoptGRef(g_file_new_for_path(outputFilename.get())); + GUniquePtr<char> outputURI(g_file_get_uri(test->m_outputFile.get())); + + GRefPtr<GtkPrintSettings> printSettings = adoptGRef(gtk_print_settings_new()); + gtk_print_settings_set(printSettings.get(), GTK_PRINT_SETTINGS_OUTPUT_URI, outputURI.get()); + webkit_print_operation_set_print_settings(test->m_printOperation.get(), printSettings.get()); + + test->openDialogMoveThroughItAndWaitUntilClosed(); + + g_assert(test->m_response == WEBKIT_PRINT_OPERATION_RESPONSE_PRINT); + g_assert(test->m_createEmitted); + g_assert(test->m_widgetRealized); + g_assert(test->m_updateEmitted); + g_assert(test->m_applyEmitted); +} #endif // HAVE_GTK_UNIX_PRINTING void beforeAll() { - kTempDirectory = g_dir_make_tmp("WebKit2Tests-XXXXXX", 0); - g_assert(kTempDirectory); - WebViewTest::add("WebKitPrintOperation", "printing-settings", testPrintOperationPrintSettings); WebViewTest::add("WebKitWebView", "print", testWebViewPrint); #ifdef HAVE_GTK_UNIX_PRINTING PrintTest::add("WebKitPrintOperation", "print", testPrintOperationPrint); PrintTest::add("WebKitPrintOperation", "print-errors", testPrintOperationErrors); CloseAfterPrintTest::add("WebKitPrintOperation", "close-after-print", testPrintOperationCloseAfterPrint); + PrintCustomWidgetTest::add("WebKitPrintOperation", "custom-widget", testPrintCustomWidget); #endif } void afterAll() { - g_rmdir(kTempDirectory); } diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestResources.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestResources.cpp index 4d97846a2..1510e5d49 100644 --- a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestResources.cpp +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestResources.cpp @@ -22,7 +22,8 @@ #include "WebKitTestServer.h" #include "WebViewTest.h" #include <wtf/Vector.h> -#include <wtf/gobject/GRefPtr.h> +#include <wtf/glib/GMutexLocker.h> +#include <wtf/glib/GRefPtr.h> static WebKitTestServer* kServer; @@ -189,6 +190,13 @@ public: } else if (uri == kServer->getURIForPath("/javascript.js")) { g_assert_cmpint(m_resourceDataSize, ==, strlen(kJavascript)); g_assert(!strncmp(m_resourceData.get(), kJavascript, m_resourceDataSize)); + } else if (uri == kServer->getURIForPath("/blank.ico")) { + GUniquePtr<char> filePath(g_build_filename(Test::getResourcesDir().data(), "blank.ico", nullptr)); + GUniqueOutPtr<char> contents; + gsize contentsLength; + g_file_get_contents(filePath.get(), &contents.outPtr(), &contentsLength, nullptr); + g_assert_cmpint(m_resourceDataSize, ==, contentsLength); + g_assert(!memcmp(m_resourceData.get(), contents.get(), contentsLength)); } else g_assert_not_reached(); m_resourceData.reset(); @@ -417,7 +425,7 @@ static void testWebResourceMimeType(SingleResourceLoadTest* test, gconstpointer) test->loadURI(kServer->getURIForPath("/image.html").data()); response = test->waitUntilResourceLoadFinishedAndReturnURIResponse(); - g_assert_cmpstr(webkit_uri_response_get_mime_type(response), ==, "image/vnd.microsoft.icon"); + g_assert_cmpstr(webkit_uri_response_get_mime_type(response), ==, "image/x-icon"); test->loadURI(kServer->getURIForPath("/redirected-css.html").data()); response = test->waitUntilResourceLoadFinishedAndReturnURIResponse(); @@ -517,9 +525,7 @@ static void testWebResourceActiveURI(ResourceURITrackingTest* test, gconstpointe static void testWebResourceGetData(ResourcesTest* test, gconstpointer) { test->loadURI(kServer->getURIForPath("/").data()); - // FIXME: this should be 4 instead of 3, but we don't get the css image resource - // due to bug https://bugs.webkit.org/show_bug.cgi?id=78510. - test->waitUntilResourcesLoaded(3); + test->waitUntilResourcesLoaded(4); WebKitWebResource* resource = webkit_web_view_get_main_resource(test->m_webView); g_assert(resource); @@ -667,6 +673,66 @@ static void testWebResourceSendRequest(SendRequestTest* test, gconstpointer) events.clear(); } +static GMutex s_serverMutex; +static const unsigned s_maxConnectionsPerHost = 6; + +class SyncRequestOnMaxConnsTest: public ResourcesTest { +public: + MAKE_GLIB_TEST_FIXTURE(SyncRequestOnMaxConnsTest); + + void resourceLoadStarted(WebKitWebResource*, WebKitURIRequest*) override + { + if (!m_resourcesToStartPending) + return; + + if (!--m_resourcesToStartPending) + g_main_loop_quit(m_mainLoop); + } + + void waitUntilResourcesStarted(unsigned requestCount) + { + m_resourcesToStartPending = requestCount; + g_main_loop_run(m_mainLoop); + } + + unsigned m_resourcesToStartPending; +}; + +static void testWebViewSyncRequestOnMaxConns(SyncRequestOnMaxConnsTest* test, gconstpointer) +{ + WTF::GMutexLocker<GMutex> lock(s_serverMutex); + test->loadURI(kServer->getURIForPath("/sync-request-on-max-conns-0").data()); + test->waitUntilResourcesStarted(s_maxConnectionsPerHost + 1); // s_maxConnectionsPerHost resource + main resource. + + for (unsigned i = 0; i < 2; ++i) { + GUniquePtr<char> xhr(g_strdup_printf("xhr = new XMLHttpRequest; xhr.open('GET', '/sync-request-on-max-conns-xhr%u', false); xhr.send();", i)); + webkit_web_view_run_javascript(test->m_webView, xhr.get(), nullptr, nullptr, nullptr); + } + + // By default sync XHRs have a 10 seconds timeout, we don't want to wait all that so use our own timeout. + guint timeoutSourceID = g_timeout_add(1000, [] (gpointer) -> gboolean { + g_assert_not_reached(); + return G_SOURCE_REMOVE; + }, nullptr); + + struct UnlockServerSourceContext { + WTF::GMutexLocker<GMutex>& lock; + guint unlockServerSourceID; + } context = { + lock, + g_idle_add_full(G_PRIORITY_DEFAULT, [](gpointer userData) -> gboolean { + auto& context = *static_cast<UnlockServerSourceContext*>(userData); + context.unlockServerSourceID = 0; + context.lock.unlock(); + return G_SOURCE_REMOVE; + }, &context, nullptr) + }; + test->waitUntilResourcesLoaded(s_maxConnectionsPerHost + 3); // s_maxConnectionsPerHost resource + main resource + 2 XHR. + g_source_remove(timeoutSourceID); + if (context.unlockServerSourceID) + g_source_remove(context.unlockServerSourceID); +} + static void addCacheHTTPHeadersToResponse(SoupMessage* message) { // The actual date doesn't really matter. @@ -734,7 +800,7 @@ static void serverCallback(SoupServer* server, SoupMessage* message, const char* static const char* javascriptRelativeHTML = "<html><head><script language='javascript' src='/redirected-to-cancel.js'></script></head><body></body></html>"; soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, javascriptRelativeHTML, strlen(javascriptRelativeHTML)); } else if (g_str_equal(path, "/blank.ico")) { - GUniquePtr<char> filePath(g_build_filename(Test::getWebKit1TestResoucesDir().data(), path, NULL)); + GUniquePtr<char> filePath(g_build_filename(Test::getResourcesDir().data(), path, nullptr)); char* contents; gsize contentsLength; g_file_get_contents(filePath.get(), &contents, &contentsLength, 0); @@ -760,18 +826,37 @@ static void serverCallback(SoupServer* server, SoupMessage* message, const char* soup_message_headers_append(message->response_headers, "Location", "/cancel-this.js"); } else if (g_str_equal(path, "/invalid.css")) soup_message_set_status(message, SOUP_STATUS_CANT_CONNECT); - else + else if (g_str_has_prefix(path, "/sync-request-on-max-conns-")) { + char* contents; + gsize contentsLength; + if (g_str_equal(path, "/sync-request-on-max-conns-0")) { + GString* imagesHTML = g_string_new("<html><body>"); + for (unsigned i = 1; i <= s_maxConnectionsPerHost; ++i) + g_string_append_printf(imagesHTML, "<img src='/sync-request-on-max-conns-%u'>", i); + g_string_append(imagesHTML, "</body></html>"); + + contentsLength = imagesHTML->len; + contents = g_string_free(imagesHTML, FALSE); + } else { + { + // We don't actually need to keep the mutex, so we release it as soon as we get it. + WTF::GMutexLocker<GMutex> lock(s_serverMutex); + } + + GUniquePtr<char> filePath(g_build_filename(Test::getResourcesDir().data(), "blank.ico", nullptr)); + g_file_get_contents(filePath.get(), &contents, &contentsLength, 0); + } + soup_message_body_append(message->response_body, SOUP_MEMORY_TAKE, contents, contentsLength); + } else soup_message_set_status(message, SOUP_STATUS_NOT_FOUND); soup_message_body_complete(message->response_body); } void beforeAll() { - kServer = new WebKitTestServer(); + kServer = new WebKitTestServer(WebKitTestServer::ServerOptions::ServerRunInThread); kServer->run(serverCallback); - webkit_web_context_set_web_extensions_directory(webkit_web_context_get_default(), WEBKIT_TEST_WEB_EXTENSIONS_DIR); - ResourcesTest::add("WebKitWebView", "resources", testWebViewResources); SingleResourceLoadTest::add("WebKitWebResource", "loading", testWebResourceLoading); SingleResourceLoadTest::add("WebKitWebResource", "response", testWebResourceResponse); @@ -781,6 +866,9 @@ void beforeAll() ResourcesTest::add("WebKitWebResource", "get-data", testWebResourceGetData); SingleResourceLoadTest::add("WebKitWebView", "history-cache", testWebViewResourcesHistoryCache); SendRequestTest::add("WebKitWebPage", "send-request", testWebResourceSendRequest); +#if SOUP_CHECK_VERSION(2, 49, 91) + SyncRequestOnMaxConnsTest::add("WebKitWebView", "sync-request-on-max-conns", testWebViewSyncRequestOnMaxConns); +#endif } void afterAll() diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestSSL.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestSSL.cpp index c3075b697..10c5e73b6 100644 --- a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestSSL.cpp +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestSSL.cpp @@ -67,6 +67,10 @@ public: static void testSSL(SSLTest* test, gconstpointer) { + WebKitWebContext* context = webkit_web_view_get_context(test->m_webView); + WebKitTLSErrorsPolicy originalPolicy = webkit_web_context_get_tls_errors_policy(context); + webkit_web_context_set_tls_errors_policy(context, WEBKIT_TLS_ERRORS_POLICY_IGNORE); + test->loadURI(kHttpsServer->getURIForPath("/").data()); test->waitUntilLoadFinished(); g_assert(test->m_certificate); @@ -80,6 +84,8 @@ static void testSSL(SSLTest* test, gconstpointer) test->waitUntilLoadFinished(); g_assert(!test->m_certificate); g_assert(!test->m_tlsErrors); + + webkit_web_context_set_tls_errors_policy(context, originalPolicy); } class InsecureContentTest: public WebViewTest { @@ -110,38 +116,64 @@ public: static void testInsecureContent(InsecureContentTest* test, gconstpointer) { + WebKitWebContext* context = webkit_web_view_get_context(test->m_webView); + WebKitTLSErrorsPolicy originalPolicy = webkit_web_context_get_tls_errors_policy(context); + webkit_web_context_set_tls_errors_policy(context, WEBKIT_TLS_ERRORS_POLICY_IGNORE); + test->loadURI(kHttpsServer->getURIForPath("/insecure-content/").data()); test->waitUntilLoadFinished(); - g_assert(test->m_insecureContentRun); + g_assert(!test->m_insecureContentRun); + // Images are currently always displayed, even bypassing mixed content settings. Check + // https://bugs.webkit.org/show_bug.cgi?id=142469 g_assert(test->m_insecureContentDisplayed); + + webkit_web_context_set_tls_errors_policy(context, originalPolicy); } +static bool assertIfSSLRequestProcessed = false; + static void testTLSErrorsPolicy(SSLTest* test, gconstpointer) { WebKitWebContext* context = webkit_web_view_get_context(test->m_webView); - // TLS errors are ignored by default. - g_assert(webkit_web_context_get_tls_errors_policy(context) == WEBKIT_TLS_ERRORS_POLICY_IGNORE); - test->loadURI(kHttpsServer->getURIForPath("/").data()); - test->waitUntilLoadFinished(); - g_assert(!test->m_loadFailed); + // TLS errors are treated as transport failures by default. + g_assert(webkit_web_context_get_tls_errors_policy(context) == WEBKIT_TLS_ERRORS_POLICY_FAIL); - webkit_web_context_set_tls_errors_policy(context, WEBKIT_TLS_ERRORS_POLICY_FAIL); + assertIfSSLRequestProcessed = true; test->loadURI(kHttpsServer->getURIForPath("/").data()); test->waitUntilLoadFinished(); g_assert(test->m_loadFailed); g_assert(test->m_loadEvents.contains(LoadTrackingTest::ProvisionalLoadFailed)); g_assert(!test->m_loadEvents.contains(LoadTrackingTest::LoadCommitted)); + assertIfSSLRequestProcessed = false; + + webkit_web_context_set_tls_errors_policy(context, WEBKIT_TLS_ERRORS_POLICY_IGNORE); + g_assert(webkit_web_context_get_tls_errors_policy(context) == WEBKIT_TLS_ERRORS_POLICY_IGNORE); + + test->m_loadFailed = false; + test->loadURI(kHttpsServer->getURIForPath("/").data()); + test->waitUntilLoadFinished(); + g_assert(!test->m_loadFailed); + + webkit_web_context_set_tls_errors_policy(context, WEBKIT_TLS_ERRORS_POLICY_FAIL); + g_assert(webkit_web_context_get_tls_errors_policy(context) == WEBKIT_TLS_ERRORS_POLICY_FAIL); } static void testTLSErrorsRedirect(SSLTest* test, gconstpointer) { - webkit_web_context_set_tls_errors_policy(webkit_web_view_get_context(test->m_webView), WEBKIT_TLS_ERRORS_POLICY_FAIL); + WebKitWebContext* context = webkit_web_view_get_context(test->m_webView); + WebKitTLSErrorsPolicy originalPolicy = webkit_web_context_get_tls_errors_policy(context); + webkit_web_context_set_tls_errors_policy(context, WEBKIT_TLS_ERRORS_POLICY_FAIL); + + assertIfSSLRequestProcessed = true; test->loadURI(kHttpsServer->getURIForPath("/redirect").data()); test->waitUntilLoadFinished(); g_assert(test->m_loadFailed); g_assert(test->m_loadEvents.contains(LoadTrackingTest::ProvisionalLoadFailed)); g_assert(!test->m_loadEvents.contains(LoadTrackingTest::LoadCommitted)); + assertIfSSLRequestProcessed = false; + + webkit_web_context_set_tls_errors_policy(context, originalPolicy); } static gboolean webViewAuthenticationCallback(WebKitWebView*, WebKitAuthenticationRequest* request) @@ -153,13 +185,20 @@ static gboolean webViewAuthenticationCallback(WebKitWebView*, WebKitAuthenticati static void testTLSErrorsHTTPAuth(SSLTest* test, gconstpointer) { - webkit_web_context_set_tls_errors_policy(webkit_web_view_get_context(test->m_webView), WEBKIT_TLS_ERRORS_POLICY_FAIL); + WebKitWebContext* context = webkit_web_view_get_context(test->m_webView); + WebKitTLSErrorsPolicy originalPolicy = webkit_web_context_get_tls_errors_policy(context); + webkit_web_context_set_tls_errors_policy(context, WEBKIT_TLS_ERRORS_POLICY_FAIL); + + assertIfSSLRequestProcessed = true; g_signal_connect(test->m_webView, "authenticate", G_CALLBACK(webViewAuthenticationCallback), NULL); test->loadURI(kHttpsServer->getURIForPath("/auth").data()); test->waitUntilLoadFinished(); g_assert(test->m_loadFailed); g_assert(test->m_loadEvents.contains(LoadTrackingTest::ProvisionalLoadFailed)); g_assert(!test->m_loadEvents.contains(LoadTrackingTest::LoadCommitted)); + assertIfSSLRequestProcessed = false; + + webkit_web_context_set_tls_errors_policy(context, originalPolicy); } class TLSErrorsTest: public SSLTest { @@ -167,69 +206,60 @@ public: MAKE_GLIB_TEST_FIXTURE(TLSErrorsTest); TLSErrorsTest() + : m_tlsErrors(static_cast<GTlsCertificateFlags>(0)) + , m_failingURI(nullptr) { - g_signal_connect(m_webView, "load-failed-with-tls-errors", G_CALLBACK(runLoadFailedWithTLSErrorsCallback), this); } ~TLSErrorsTest() { - g_signal_handlers_disconnect_matched(m_webView, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this); - if (m_certificateInfo) - webkit_certificate_info_free(m_certificateInfo); - } - - static gboolean runLoadFailedWithTLSErrorsCallback(WebKitWebView*, WebKitCertificateInfo* info, const char* host, TLSErrorsTest* test) - { - test->runLoadFailedWithTLSErrors(info, host); - return TRUE; - } - - void runLoadFailedWithTLSErrors(WebKitCertificateInfo* info, const char* host) - { - if (m_certificateInfo) - webkit_certificate_info_free(m_certificateInfo); - m_certificateInfo = webkit_certificate_info_copy(info); - m_host.reset(g_strdup(host)); - g_main_loop_quit(m_mainLoop); + if (m_failingURI) + soup_uri_free(m_failingURI); } - void waitUntilLoadFailedWithTLSErrors() + bool loadFailedWithTLSErrors(const char* failingURI, GTlsCertificate* certificate, GTlsCertificateFlags tlsErrors) override { - g_main_loop_run(m_mainLoop); - } + LoadTrackingTest::loadFailedWithTLSErrors(failingURI, certificate, tlsErrors); - WebKitCertificateInfo* certificateInfo() - { - return m_certificateInfo; + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(certificate)); + m_certificate = certificate; + m_tlsErrors = tlsErrors; + if (m_failingURI) + soup_uri_free(m_failingURI); + m_failingURI = soup_uri_new(failingURI); + return true; } - const char* host() - { - return m_host.get(); - } + GTlsCertificate* certificate() const { return m_certificate.get(); } + GTlsCertificateFlags tlsErrors() const { return m_tlsErrors; } + const char* host() const { return m_failingURI->host; } private: - WebKitCertificateInfo* m_certificateInfo; - GUniquePtr<char> m_host; + GRefPtr<GTlsCertificate> m_certificate; + GTlsCertificateFlags m_tlsErrors; + SoupURI* m_failingURI; }; static void testLoadFailedWithTLSErrors(TLSErrorsTest* test, gconstpointer) { WebKitWebContext* context = webkit_web_view_get_context(test->m_webView); + WebKitTLSErrorsPolicy originalPolicy = webkit_web_context_get_tls_errors_policy(context); webkit_web_context_set_tls_errors_policy(context, WEBKIT_TLS_ERRORS_POLICY_FAIL); + assertIfSSLRequestProcessed = true; // The load-failed-with-tls-errors signal should be emitted when there is a TLS failure. test->loadURI(kHttpsServer->getURIForPath("/test-tls/").data()); - test->waitUntilLoadFailedWithTLSErrors(); - // Test the WebKitCertificateInfo API. - g_assert(G_IS_TLS_CERTIFICATE(webkit_certificate_info_get_tls_certificate(test->certificateInfo()))); - g_assert_cmpuint(webkit_certificate_info_get_tls_errors(test->certificateInfo()), ==, G_TLS_CERTIFICATE_UNKNOWN_CA); + test->waitUntilLoadFinished(); + g_assert(G_IS_TLS_CERTIFICATE(test->certificate())); + g_assert_cmpuint(test->tlsErrors(), ==, G_TLS_CERTIFICATE_UNKNOWN_CA); g_assert_cmpstr(test->host(), ==, soup_uri_get_host(kHttpsServer->baseURI())); g_assert_cmpint(test->m_loadEvents[0], ==, LoadTrackingTest::ProvisionalLoadStarted); - g_assert_cmpint(test->m_loadEvents[1], ==, LoadTrackingTest::LoadFinished); + g_assert_cmpint(test->m_loadEvents[1], ==, LoadTrackingTest::LoadFailedWithTLSErrors); + g_assert_cmpint(test->m_loadEvents[2], ==, LoadTrackingTest::LoadFinished); + assertIfSSLRequestProcessed = false; // Test allowing an exception for this certificate on this host. - webkit_web_context_allow_tls_certificate_for_host(context, test->certificateInfo(), test->host()); + webkit_web_context_allow_tls_certificate_for_host(context, test->certificate(), test->host()); // The page should now load without errors. test->loadURI(kHttpsServer->getURIForPath("/test-tls/").data()); test->waitUntilLoadFinished(); @@ -238,8 +268,76 @@ static void testLoadFailedWithTLSErrors(TLSErrorsTest* test, gconstpointer) g_assert_cmpint(test->m_loadEvents[1], ==, LoadTrackingTest::LoadCommitted); g_assert_cmpint(test->m_loadEvents[2], ==, LoadTrackingTest::LoadFinished); g_assert_cmpstr(webkit_web_view_get_title(test->m_webView), ==, TLSExpectedSuccessTitle); + + webkit_web_context_set_tls_errors_policy(context, originalPolicy); } +class TLSSubresourceTest : public WebViewTest { +public: + MAKE_GLIB_TEST_FIXTURE(TLSSubresourceTest); + + static void resourceLoadStartedCallback(WebKitWebView* webView, WebKitWebResource* resource, WebKitURIRequest* request, TLSSubresourceTest* test) + { + if (webkit_web_view_get_main_resource(test->m_webView) == resource) + return; + + // Ignore favicons. + if (g_str_has_suffix(webkit_uri_request_get_uri(request), "favicon.ico")) + return; + + test->subresourceLoadStarted(resource); + } + + TLSSubresourceTest() + : m_tlsErrors(static_cast<GTlsCertificateFlags>(0)) + { + g_signal_connect(m_webView, "resource-load-started", G_CALLBACK(resourceLoadStartedCallback), this); + } + + static void subresourceFailedCallback(WebKitWebResource*, GError*) + { + g_assert_not_reached(); + } + + static void subresourceFailedWithTLSErrorsCallback(WebKitWebResource* resource, GTlsCertificate* certificate, GTlsCertificateFlags tlsErrors, TLSSubresourceTest* test) + { + test->subresourceFailedWithTLSErrors(resource, certificate, tlsErrors); + } + + void subresourceLoadStarted(WebKitWebResource* resource) + { + g_signal_connect(resource, "failed", G_CALLBACK(subresourceFailedCallback), nullptr); + g_signal_connect(resource, "failed-with-tls-errors", G_CALLBACK(subresourceFailedWithTLSErrorsCallback), this); + } + + void subresourceFailedWithTLSErrors(WebKitWebResource* resource, GTlsCertificate* certificate, GTlsCertificateFlags tlsErrors) + { + m_certificate = certificate; + m_tlsErrors = tlsErrors; + g_main_loop_quit(m_mainLoop); + } + + void waitUntilSubresourceLoadFail() + { + g_main_loop_run(m_mainLoop); + } + + GRefPtr<GTlsCertificate> m_certificate; + GTlsCertificateFlags m_tlsErrors; +}; + +static void testSubresourceLoadFailedWithTLSErrors(TLSSubresourceTest* test, gconstpointer) +{ + WebKitWebContext* context = webkit_web_view_get_context(test->m_webView); + webkit_web_context_set_tls_errors_policy(context, WEBKIT_TLS_ERRORS_POLICY_FAIL); + + assertIfSSLRequestProcessed = true; + test->loadURI(kHttpServer->getURIForPath("/").data()); + test->waitUntilSubresourceLoadFail(); + g_assert(G_IS_TLS_CERTIFICATE(test->m_certificate.get())); + g_assert_cmpuint(test->m_tlsErrors, ==, G_TLS_CERTIFICATE_UNKNOWN_CA); + assertIfSSLRequestProcessed = false; +} static void httpsServerCallback(SoupServer* server, SoupMessage* message, const char* path, GHashTable*, SoupClientContext*, gpointer) { @@ -248,6 +346,8 @@ static void httpsServerCallback(SoupServer* server, SoupMessage* message, const return; } + g_assert(!assertIfSSLRequestProcessed); + if (g_str_equal(path, "/")) { soup_message_set_status(message, SOUP_STATUS_OK); soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, indexHTML, strlen(indexHTML)); @@ -267,6 +367,10 @@ static void httpsServerCallback(SoupServer* server, SoupMessage* message, const } else if (g_str_equal(path, "/auth")) { soup_message_set_status(message, SOUP_STATUS_UNAUTHORIZED); soup_message_headers_append(message->response_headers, "WWW-Authenticate", "Basic realm=\"HTTPS auth\""); + } else if (g_str_equal(path, "/style.css")) { + soup_message_set_status(message, SOUP_STATUS_OK); + static const char* styleCSS = "body { color: black; }"; + soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, styleCSS, strlen(styleCSS)); } else soup_message_set_status(message, SOUP_STATUS_NOT_FOUND); } @@ -279,7 +383,7 @@ static void httpServerCallback(SoupServer* server, SoupMessage* message, const c } if (g_str_equal(path, "/test-script")) { - GUniquePtr<char> pathToFile(g_build_filename(Test::getResourcesDir().data(), "link-title.js", NULL)); + GUniquePtr<char> pathToFile(g_build_filename(Test::getResourcesDir().data(), "link-title.js", nullptr)); char* contents; gsize length; g_file_get_contents(pathToFile.get(), &contents, &length, 0); @@ -288,7 +392,7 @@ static void httpServerCallback(SoupServer* server, SoupMessage* message, const c soup_message_set_status(message, SOUP_STATUS_OK); soup_message_body_complete(message->response_body); } else if (g_str_equal(path, "/test-image")) { - GUniquePtr<char> pathToFile(g_build_filename(Test::getWebKit1TestResoucesDir().data(), "blank.ico", NULL)); + GUniquePtr<char> pathToFile(g_build_filename(Test::getResourcesDir().data(), "blank.ico", nullptr)); char* contents; gsize length; g_file_get_contents(pathToFile.get(), &contents, &length, 0); @@ -296,6 +400,12 @@ static void httpServerCallback(SoupServer* server, SoupMessage* message, const c soup_message_body_append(message->response_body, SOUP_MEMORY_TAKE, contents, length); soup_message_set_status(message, SOUP_STATUS_OK); soup_message_body_complete(message->response_body); + } else if (g_str_equal(path, "/")) { + soup_message_set_status(message, SOUP_STATUS_OK); + char* responseHTML = g_strdup_printf("<html><head><link rel='stylesheet' href='%s' type='text/css'></head><body>SSL subresource test</body></html>", + kHttpsServer->getURIForPath("/style.css").data()); + soup_message_body_append(message->response_body, SOUP_MEMORY_TAKE, responseHTML, strlen(responseHTML)); + soup_message_body_complete(message->response_body); } else soup_message_set_status(message, SOUP_STATUS_NOT_FOUND); } @@ -310,12 +420,10 @@ void beforeAll() SSLTest::add("WebKitWebView", "ssl", testSSL); InsecureContentTest::add("WebKitWebView", "insecure-content", testInsecureContent); - // In this case the order of the tests does matter because tls-errors-policy tests the default policy, - // and expects that no exception will have been added for this certificate and host pair as is - // done in the tls-permission-request test. SSLTest::add("WebKitWebView", "tls-errors-policy", testTLSErrorsPolicy); SSLTest::add("WebKitWebView", "tls-errors-redirect-to-http", testTLSErrorsRedirect); SSLTest::add("WebKitWebView", "tls-http-auth", testTLSErrorsHTTPAuth); + TLSSubresourceTest::add("WebKitWebView", "tls-subresource", testSubresourceLoadFailedWithTLSErrors); TLSErrorsTest::add("WebKitWebView", "load-failed-with-tls-errors", testLoadFailedWithTLSErrors); } diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestUIClient.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestUIClient.cpp index d24edc601..4c44039cc 100644 --- a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestUIClient.cpp +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestUIClient.cpp @@ -20,13 +20,14 @@ #include "config.h" #include "WebViewTest.h" #include <wtf/HashSet.h> -#include <wtf/gobject/GRefPtr.h> +#include <wtf/glib/GRefPtr.h> #include <wtf/text/StringHash.h> static const char* kAlertDialogMessage = "WebKitGTK+ alert dialog message"; static const char* kConfirmDialogMessage = "WebKitGTK+ confirm dialog message"; static const char* kPromptDialogMessage = "WebKitGTK+ prompt dialog message"; static const char* kPromptDialogReturnedText = "WebKitGTK+ prompt dialog returned text"; +static const char* kBeforeUnloadConfirmDialogMessage = "WebKitGTK+ beforeunload dialog message"; class UIClientTest: public WebViewTest { public: @@ -117,9 +118,9 @@ public: test->m_windowPropertiesChanged.add(g_param_spec_get_name(paramSpec)); } - static GtkWidget* viewCreateCallback(WebKitWebView* webView, UIClientTest* test) + static GtkWidget* viewCreateCallback(WebKitWebView* webView, WebKitNavigationAction* navigation, UIClientTest* test) { - return test->viewCreate(webView); + return test->viewCreate(webView, navigation); } static void viewReadyToShowCallback(WebKitWebView* webView, UIClientTest* test) @@ -146,6 +147,9 @@ public: case WEBKIT_SCRIPT_DIALOG_PROMPT: g_assert_cmpstr(webkit_script_dialog_get_message(dialog), ==, kPromptDialogReturnedText); break; + case WEBKIT_SCRIPT_DIALOG_BEFORE_UNLOAD_CONFIRM: + g_assert_not_reached(); + break; } g_main_loop_quit(m_mainLoop); @@ -165,6 +169,13 @@ public: webkit_script_dialog_prompt_set_text(dialog, kPromptDialogReturnedText); } + void scriptBeforeUnloadConfirm(WebKitScriptDialog* dialog) + { + g_assert_cmpstr(webkit_script_dialog_get_message(dialog), ==, kBeforeUnloadConfirmDialogMessage); + m_scriptDialogConfirmed = true; + webkit_script_dialog_confirm_set_confirmed(dialog, m_scriptDialogConfirmed); + } + static gboolean scriptDialog(WebKitWebView*, WebKitScriptDialog* dialog, UIClientTest* test) { switch (webkit_script_dialog_get_dialog_type(dialog)) { @@ -177,6 +188,9 @@ public: case WEBKIT_SCRIPT_DIALOG_PROMPT: test->scriptPrompt(dialog); break; + case WEBKIT_SCRIPT_DIALOG_BEFORE_UNLOAD_CONFIRM: + test->scriptBeforeUnloadConfirm(dialog); + break; } return TRUE; @@ -197,6 +211,12 @@ public: g_assert(WEBKIT_IS_PERMISSION_REQUEST(request)); test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(request)); + if (test->m_verifyMediaTypes && WEBKIT_IS_USER_MEDIA_PERMISSION_REQUEST(request)) { + WebKitUserMediaPermissionRequest* userMediaRequest = WEBKIT_USER_MEDIA_PERMISSION_REQUEST(request); + g_assert(webkit_user_media_permission_is_for_audio_device(userMediaRequest) == test->m_expectedAudioMedia); + g_assert(webkit_user_media_permission_is_for_video_device(userMediaRequest) == test->m_expectedVideoMedia); + } + if (test->m_allowPermissionRequests) webkit_permission_request_allow(request); else @@ -205,10 +225,19 @@ public: return TRUE; } + static void permissionResultMessageReceivedCallback(WebKitUserContentManager* userContentManager, WebKitJavascriptResult* javascriptResult, UIClientTest* test) + { + test->m_permissionResult.reset(WebViewTest::javascriptResultToCString(javascriptResult)); + g_main_loop_quit(test->m_mainLoop); + } + UIClientTest() : m_scriptDialogType(WEBKIT_SCRIPT_DIALOG_ALERT) , m_scriptDialogConfirmed(true) , m_allowPermissionRequests(false) + , m_verifyMediaTypes(false) + , m_expectedAudioMedia(false) + , m_expectedVideoMedia(false) , m_mouseTargetModifiers(0) { webkit_settings_set_javascript_can_open_windows_automatically(webkit_web_view_get_settings(m_webView), TRUE); @@ -216,11 +245,33 @@ public: g_signal_connect(m_webView, "script-dialog", G_CALLBACK(scriptDialog), this); g_signal_connect(m_webView, "mouse-target-changed", G_CALLBACK(mouseTargetChanged), this); g_signal_connect(m_webView, "permission-request", G_CALLBACK(permissionRequested), this); + webkit_user_content_manager_register_script_message_handler(m_userContentManager.get(), "permission"); + g_signal_connect(m_userContentManager.get(), "script-message-received::permission", G_CALLBACK(permissionResultMessageReceivedCallback), this); } ~UIClientTest() { g_signal_handlers_disconnect_matched(m_webView, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this); + g_signal_handlers_disconnect_matched(m_userContentManager.get(), G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this); + webkit_user_content_manager_unregister_script_message_handler(m_userContentManager.get(), "permission"); + } + + static void tryWebViewCloseCallback(UIClientTest* test) + { + g_main_loop_quit(test->m_mainLoop); + } + + void tryCloseAndWaitUntilClosed() + { + gulong handler = g_signal_connect_swapped(m_webView, "close", G_CALLBACK(tryWebViewCloseCallback), this); + // Use an idle because webkit_web_view_try_close can emit the close signal in the + // current run loop iteration. + g_idle_add([](gpointer data) -> gboolean { + webkit_web_view_try_close(WEBKIT_WEB_VIEW(data)); + return G_SOURCE_REMOVE; + }, m_webView); + g_main_loop_run(m_mainLoop); + g_signal_handler_disconnect(m_webView, handler); } void waitUntilMainLoopFinishes() @@ -228,6 +279,13 @@ public: g_main_loop_run(m_mainLoop); } + const char* waitUntilPermissionResultMessageReceived() + { + m_permissionResult = nullptr; + g_main_loop_run(m_mainLoop); + return m_permissionResult.get(); + } + void setExpectedWindowProperties(const WindowProperties& windowProperties) { m_windowProperties = windowProperties; @@ -240,9 +298,19 @@ public: return m_mouseTargetHitTestResult.get(); } - virtual GtkWidget* viewCreate(WebKitWebView* webView) + void simulateUserInteraction() + { + runJavaScriptAndWaitUntilFinished("document.getElementById('testInput').focus()", nullptr); + keyStroke(GDK_KEY_a); + keyStroke(GDK_KEY_b); + while (gtk_events_pending()) + gtk_main_iteration(); + } + + virtual GtkWidget* viewCreate(WebKitWebView* webView, WebKitNavigationAction* navigation) { g_assert(webView == m_webView); + g_assert(navigation); GtkWidget* newWebView = webkit_web_view_new_with_context(webkit_web_view_get_context(webView)); g_object_ref_sink(newWebView); @@ -287,10 +355,14 @@ public: WebKitScriptDialogType m_scriptDialogType; bool m_scriptDialogConfirmed; bool m_allowPermissionRequests; + gboolean m_verifyMediaTypes; + gboolean m_expectedAudioMedia; + gboolean m_expectedVideoMedia; WindowProperties m_windowProperties; HashSet<WTF::String> m_windowPropertiesChanged; GRefPtr<WebKitHitTestResult> m_mouseTargetHitTestResult; unsigned m_mouseTargetModifiers; + GUniquePtr<char> m_permissionResult; }; static void testWebViewCreateReadyClose(UIClientTest* test, gconstpointer) @@ -305,6 +377,91 @@ static void testWebViewCreateReadyClose(UIClientTest* test, gconstpointer) g_assert_cmpint(events[2], ==, UIClientTest::Close); } +class CreateNavigationDataTest: public UIClientTest { +public: + MAKE_GLIB_TEST_FIXTURE(CreateNavigationDataTest); + + CreateNavigationDataTest() + : m_navigation(nullptr) + { + } + + ~CreateNavigationDataTest() + { + clearNavigation(); + } + + void clearNavigation() + { + if (m_navigation) + webkit_navigation_action_free(m_navigation); + m_navigation = nullptr; + } + + GtkWidget* viewCreate(WebKitWebView* webView, WebKitNavigationAction* navigation) + { + g_assert(navigation); + g_assert(!m_navigation); + m_navigation = webkit_navigation_action_copy(navigation); + g_main_loop_quit(m_mainLoop); + return nullptr; + } + + void loadHTML(const char* html) + { + clearNavigation(); + WebViewTest::loadHtml(html, nullptr); + } + + void clickAndWaitUntilMainLoopFinishes(int x, int y) + { + clearNavigation(); + clickMouseButton(x, y, 1); + g_main_loop_run(m_mainLoop); + } + + WebKitNavigationAction* m_navigation; +}; + +static void testWebViewCreateNavigationData(CreateNavigationDataTest* test, gconstpointer) +{ + test->showInWindowAndWaitUntilMapped(); + + test->loadHTML( + "<html><body>" + "<input style=\"position:absolute; left:0; top:0; margin:0; padding:0\" type=\"button\" value=\"click to show a popup\" onclick=\"window.open('data:foo');\"/>" + "<a style=\"position:absolute; left:20; top:20;\" href=\"data:bar\" target=\"_blank\">popup link</a>" + "</body></html>"); + test->waitUntilLoadFinished(); + + // Click on a button. + test->clickAndWaitUntilMainLoopFinishes(5, 5); + g_assert_cmpstr(webkit_uri_request_get_uri(webkit_navigation_action_get_request(test->m_navigation)), ==, "data:foo"); + g_assert_cmpuint(webkit_navigation_action_get_navigation_type(test->m_navigation), ==, WEBKIT_NAVIGATION_TYPE_OTHER); + // FIXME: This should be button 1. + g_assert_cmpuint(webkit_navigation_action_get_mouse_button(test->m_navigation), ==, 0); + g_assert_cmpuint(webkit_navigation_action_get_modifiers(test->m_navigation), ==, 0); + g_assert(webkit_navigation_action_is_user_gesture(test->m_navigation)); + + // Click on a link. + test->clickAndWaitUntilMainLoopFinishes(21, 21); + g_assert_cmpstr(webkit_uri_request_get_uri(webkit_navigation_action_get_request(test->m_navigation)), ==, "data:bar"); + g_assert_cmpuint(webkit_navigation_action_get_navigation_type(test->m_navigation), ==, WEBKIT_NAVIGATION_TYPE_LINK_CLICKED); + g_assert_cmpuint(webkit_navigation_action_get_mouse_button(test->m_navigation), ==, 1); + g_assert_cmpuint(webkit_navigation_action_get_modifiers(test->m_navigation), ==, 0); + g_assert(webkit_navigation_action_is_user_gesture(test->m_navigation)); + + // No user interaction. + test->loadHTML("<html><body onLoad=\"window.open();\"></html>"); + test->waitUntilMainLoopFinishes(); + + g_assert_cmpstr(webkit_uri_request_get_uri(webkit_navigation_action_get_request(test->m_navigation)), ==, ""); + g_assert_cmpuint(webkit_navigation_action_get_navigation_type(test->m_navigation), ==, WEBKIT_NAVIGATION_TYPE_OTHER); + g_assert_cmpuint(webkit_navigation_action_get_mouse_button(test->m_navigation), ==, 0); + g_assert_cmpuint(webkit_navigation_action_get_modifiers(test->m_navigation), ==, 0); + g_assert(!webkit_navigation_action_is_user_gesture(test->m_navigation)); +} + static gboolean checkMimeTypeForFilter(GtkFileFilter* filter, const gchar* mimeType) { GtkFileFilterInfo filterInfo; @@ -323,11 +480,11 @@ public: test->m_webViewEvents.append(RunAsModal); } - GtkWidget* viewCreate(WebKitWebView* webView) + GtkWidget* viewCreate(WebKitWebView* webView, WebKitNavigationAction* navigation) { g_assert(webView == m_webView); - GtkWidget* newWebView = UIClientTest::viewCreate(webView); + GtkWidget* newWebView = UIClientTest::viewCreate(webView, navigation); g_signal_connect(newWebView, "run-as-modal", G_CALLBACK(dialogRunAsModalCallback), this); return newWebView; } @@ -371,28 +528,80 @@ static void testWebViewDisallowModalDialogs(ModalDialogsTest* test, gconstpointe static void testWebViewJavaScriptDialogs(UIClientTest* test, gconstpointer) { + test->showInWindowAndWaitUntilMapped(GTK_WINDOW_TOPLEVEL); + static const char* htmlOnLoadFormat = "<html><body onLoad=\"%s\"></body></html>"; static const char* jsAlertFormat = "alert('%s')"; static const char* jsConfirmFormat = "do { confirmed = confirm('%s'); } while (!confirmed); alert('confirmed');"; static const char* jsPromptFormat = "alert(prompt('%s', 'default'));"; + static const char* htmlOnBeforeUnloadFormat = + "<html><body onbeforeunload=\"return beforeUnloadHandler();\"><input id=\"testInput\" type=\"text\"></input><script>function beforeUnloadHandler() { return \"%s\"; }</script></body></html>"; test->m_scriptDialogType = WEBKIT_SCRIPT_DIALOG_ALERT; GUniquePtr<char> alertDialogMessage(g_strdup_printf(jsAlertFormat, kAlertDialogMessage)); GUniquePtr<char> alertHTML(g_strdup_printf(htmlOnLoadFormat, alertDialogMessage.get())); test->loadHtml(alertHTML.get(), 0); test->waitUntilMainLoopFinishes(); + webkit_web_view_stop_loading(test->m_webView); + test->waitUntilLoadFinished(); test->m_scriptDialogType = WEBKIT_SCRIPT_DIALOG_CONFIRM; GUniquePtr<char> confirmDialogMessage(g_strdup_printf(jsConfirmFormat, kConfirmDialogMessage)); GUniquePtr<char> confirmHTML(g_strdup_printf(htmlOnLoadFormat, confirmDialogMessage.get())); test->loadHtml(confirmHTML.get(), 0); test->waitUntilMainLoopFinishes(); + webkit_web_view_stop_loading(test->m_webView); + test->waitUntilLoadFinished(); test->m_scriptDialogType = WEBKIT_SCRIPT_DIALOG_PROMPT; GUniquePtr<char> promptDialogMessage(g_strdup_printf(jsPromptFormat, kPromptDialogMessage)); GUniquePtr<char> promptHTML(g_strdup_printf(htmlOnLoadFormat, promptDialogMessage.get())); test->loadHtml(promptHTML.get(), 0); test->waitUntilMainLoopFinishes(); + webkit_web_view_stop_loading(test->m_webView); + test->waitUntilLoadFinished(); + + test->m_scriptDialogType = WEBKIT_SCRIPT_DIALOG_BEFORE_UNLOAD_CONFIRM; + GUniquePtr<char> beforeUnloadDialogHTML(g_strdup_printf(htmlOnBeforeUnloadFormat, kBeforeUnloadConfirmDialogMessage)); + test->loadHtml(beforeUnloadDialogHTML.get(), nullptr); + test->waitUntilLoadFinished(); + + // Reload should trigger onbeforeunload. +#if 0 + test->simulateUserInteraction(); + // FIXME: reloading HTML data doesn't emit finished load event. + // See https://bugs.webkit.org/show_bug.cgi?id=139089. + test->m_scriptDialogConfirmed = false; + webkit_web_view_reload(test->m_webView); + test->waitUntilLoadFinished(); + g_assert(test->m_scriptDialogConfirmed); +#endif + + // Navigation should trigger onbeforeunload. + test->simulateUserInteraction(); + test->m_scriptDialogConfirmed = false; + test->loadHtml("<html></html>", nullptr); + test->waitUntilLoadFinished(); + g_assert(test->m_scriptDialogConfirmed); + + // Try close should trigger onbeforeunload. + test->m_scriptDialogConfirmed = false; + test->loadHtml(beforeUnloadDialogHTML.get(), nullptr); + test->waitUntilLoadFinished(); + test->simulateUserInteraction(); + test->tryCloseAndWaitUntilClosed(); + g_assert(test->m_scriptDialogConfirmed); + + // Try close on a page with no unload handlers should not trigger onbeforeunload, + // but should actually close the page. + test->m_scriptDialogConfirmed = false; + test->loadHtml("<html><body></body></html>", nullptr); + test->waitUntilLoadFinished(); + // We got a onbeforeunload of the previous page. + g_assert(test->m_scriptDialogConfirmed); + test->m_scriptDialogConfirmed = false; + test->tryCloseAndWaitUntilClosed(); + g_assert(!test->m_scriptDialogConfirmed); } static void testWebViewWindowProperties(UIClientTest* test, gconstpointer) @@ -423,13 +632,22 @@ static void testWebViewMouseTarget(UIClientTest* test, gconstpointer) test->showInWindowAndWaitUntilMapped(GTK_WINDOW_TOPLEVEL); const char* linksHoveredHTML = - "<html><body>" + "<html><head>" + " <script>" + " window.onload = function () {" + " window.getSelection().removeAllRanges();" + " var select_range = document.createRange();" + " select_range.selectNodeContents(document.getElementById('text_to_select'));" + " window.getSelection().addRange(select_range);" + " }" + " </script>" + "</head><body>" " <a style='position:absolute; left:1; top:1' href='http://www.webkitgtk.org' title='WebKitGTK+ Title'>WebKitGTK+ Website</a>" " <img style='position:absolute; left:1; top:10' src='0xdeadbeef' width=5 height=5></img>" " <a style='position:absolute; left:1; top:20' href='http://www.webkitgtk.org/logo' title='WebKitGTK+ Logo'><img src='0xdeadbeef' width=5 height=5></img></a>" " <input style='position:absolute; left:1; top:30' size='10'></input>" - " <div style='position:absolute; left:1; top:50; width:30; height:30; overflow:scroll'> </div>" " <video style='position:absolute; left:1; top:100' width='300' height='300' controls='controls' preload='none'><source src='movie.ogg' type='video/ogg' /></video>" + " <p style='position:absolute; left:1; top:120' id='text_to_select'>Lorem ipsum.</p>" "</body></html>"; test->loadHtml(linksHoveredHTML, "file:///"); @@ -441,6 +659,7 @@ static void testWebViewMouseTarget(UIClientTest* test, gconstpointer) g_assert(!webkit_hit_test_result_context_is_image(hitTestResult)); g_assert(!webkit_hit_test_result_context_is_media(hitTestResult)); g_assert(!webkit_hit_test_result_context_is_editable(hitTestResult)); + g_assert(!webkit_hit_test_result_context_is_selection(hitTestResult)); g_assert_cmpstr(webkit_hit_test_result_get_link_uri(hitTestResult), ==, "http://www.webkitgtk.org/"); g_assert_cmpstr(webkit_hit_test_result_get_link_title(hitTestResult), ==, "WebKitGTK+ Title"); g_assert_cmpstr(webkit_hit_test_result_get_link_label(hitTestResult), ==, "WebKitGTK+ Website"); @@ -452,6 +671,7 @@ static void testWebViewMouseTarget(UIClientTest* test, gconstpointer) g_assert(!webkit_hit_test_result_context_is_image(hitTestResult)); g_assert(!webkit_hit_test_result_context_is_media(hitTestResult)); g_assert(!webkit_hit_test_result_context_is_editable(hitTestResult)); + g_assert(!webkit_hit_test_result_context_is_selection(hitTestResult)); g_assert(!test->m_mouseTargetModifiers); // Move over image with GDK_CONTROL_MASK. @@ -460,6 +680,7 @@ static void testWebViewMouseTarget(UIClientTest* test, gconstpointer) g_assert(webkit_hit_test_result_context_is_image(hitTestResult)); g_assert(!webkit_hit_test_result_context_is_media(hitTestResult)); g_assert(!webkit_hit_test_result_context_is_editable(hitTestResult)); + g_assert(!webkit_hit_test_result_context_is_selection(hitTestResult)); g_assert(!webkit_hit_test_result_context_is_scrollbar(hitTestResult)); g_assert_cmpstr(webkit_hit_test_result_get_image_uri(hitTestResult), ==, "file:///0xdeadbeef"); g_assert(test->m_mouseTargetModifiers & GDK_CONTROL_MASK); @@ -471,6 +692,7 @@ static void testWebViewMouseTarget(UIClientTest* test, gconstpointer) g_assert(!webkit_hit_test_result_context_is_media(hitTestResult)); g_assert(!webkit_hit_test_result_context_is_editable(hitTestResult)); g_assert(!webkit_hit_test_result_context_is_scrollbar(hitTestResult)); + g_assert(!webkit_hit_test_result_context_is_selection(hitTestResult)); g_assert_cmpstr(webkit_hit_test_result_get_link_uri(hitTestResult), ==, "http://www.webkitgtk.org/logo"); g_assert_cmpstr(webkit_hit_test_result_get_image_uri(hitTestResult), ==, "file:///0xdeadbeef"); g_assert_cmpstr(webkit_hit_test_result_get_link_title(hitTestResult), ==, "WebKitGTK+ Logo"); @@ -484,6 +706,7 @@ static void testWebViewMouseTarget(UIClientTest* test, gconstpointer) g_assert(webkit_hit_test_result_context_is_media(hitTestResult)); g_assert(!webkit_hit_test_result_context_is_editable(hitTestResult)); g_assert(!webkit_hit_test_result_context_is_scrollbar(hitTestResult)); + g_assert(!webkit_hit_test_result_context_is_selection(hitTestResult)); g_assert_cmpstr(webkit_hit_test_result_get_media_uri(hitTestResult), ==, "file:///movie.ogg"); g_assert(!test->m_mouseTargetModifiers); @@ -494,19 +717,32 @@ static void testWebViewMouseTarget(UIClientTest* test, gconstpointer) g_assert(!webkit_hit_test_result_context_is_media(hitTestResult)); g_assert(!webkit_hit_test_result_context_is_scrollbar(hitTestResult)); g_assert(webkit_hit_test_result_context_is_editable(hitTestResult)); + g_assert(!webkit_hit_test_result_context_is_selection(hitTestResult)); g_assert(!test->m_mouseTargetModifiers); // Move over scrollbar. - hitTestResult = test->moveMouseAndWaitUntilMouseTargetChanged(5, 75); + hitTestResult = test->moveMouseAndWaitUntilMouseTargetChanged(gtk_widget_get_allocated_width(GTK_WIDGET(test->m_webView)) - 4, 5); g_assert(!webkit_hit_test_result_context_is_link(hitTestResult)); g_assert(!webkit_hit_test_result_context_is_image(hitTestResult)); g_assert(!webkit_hit_test_result_context_is_media(hitTestResult)); g_assert(!webkit_hit_test_result_context_is_editable(hitTestResult)); g_assert(webkit_hit_test_result_context_is_scrollbar(hitTestResult)); + g_assert(!webkit_hit_test_result_context_is_selection(hitTestResult)); + g_assert(!test->m_mouseTargetModifiers); + + // Move over selection. + hitTestResult = test->moveMouseAndWaitUntilMouseTargetChanged(2, 145); + g_assert(!webkit_hit_test_result_context_is_link(hitTestResult)); + g_assert(!webkit_hit_test_result_context_is_image(hitTestResult)); + g_assert(!webkit_hit_test_result_context_is_media(hitTestResult)); + g_assert(!webkit_hit_test_result_context_is_editable(hitTestResult)); + g_assert(!webkit_hit_test_result_context_is_scrollbar(hitTestResult)); + g_assert(webkit_hit_test_result_context_is_selection(hitTestResult)); g_assert(!test->m_mouseTargetModifiers); + } -static void testWebViewPermissionRequests(UIClientTest* test, gconstpointer) +static void testWebViewGeolocationPermissionRequests(UIClientTest* test, gconstpointer) { // Some versions of geoclue give a runtime warning because it tries // to register the error quark twice. See https://bugs.webkit.org/show_bug.cgi?id=89858. @@ -518,35 +754,108 @@ static void testWebViewPermissionRequests(UIClientTest* test, gconstpointer) " <script>" " function runTest()" " {" - " navigator.geolocation.getCurrentPosition(function(p) { document.title = \"OK\" }," - " function(e) { document.title = e.code });" + " navigator.geolocation.getCurrentPosition(function(p) { window.webkit.messageHandlers.permission.postMessage('OK'); }," + " function(e) { window.webkit.messageHandlers.permission.postMessage(e.code.toString()); });" " }" " </script>" " <body onload='runTest();'></body>" "</html>"; - // Test denying a permission request. + // Geolocation is not allowed from insecure connections like HTTP, + // POSITION_UNAVAILABLE ('2') is returned in that case without even + // asking the API layer. test->m_allowPermissionRequests = false; - test->loadHtml(geolocationRequestHTML, 0); - test->waitUntilTitleChanged(); + test->loadHtml(geolocationRequestHTML, "http://foo.com/bar"); + const gchar* result = test->waitUntilPermissionResultMessageReceived(); + g_assert_cmpstr(result, ==, "2"); - // According to the Geolocation API specification, '1' is the - // error code returned for the PERMISSION_DENIED error. - // http://dev.w3.org/geo/api/spec-source.html#position_error_interface - const gchar* result = webkit_web_view_get_title(test->m_webView); + // Test denying a permission request. PERMISSION_DENIED ('1') is + // returned in this case. + test->m_allowPermissionRequests = false; + test->loadHtml(geolocationRequestHTML, "https://foo.com/bar"); + result = test->waitUntilPermissionResultMessageReceived(); g_assert_cmpstr(result, ==, "1"); - // Test allowing a permission request. + // Test allowing a permission request. Result should be different + // to PERMISSION_DENIED ('1'). test->m_allowPermissionRequests = true; - test->loadHtml(geolocationRequestHTML, 0); - test->waitUntilTitleChanged(); - - // Check that we did not get the PERMISSION_DENIED error now. - result = webkit_web_view_get_title(test->m_webView); + test->loadHtml(geolocationRequestHTML, "https://foo.com/bar"); + result = test->waitUntilPermissionResultMessageReceived(); g_assert_cmpstr(result, !=, "1"); test->addLogFatalFlag(G_LOG_LEVEL_WARNING); } +#if ENABLE(MEDIA_STREAM) +static void testWebViewUserMediaPermissionRequests(UIClientTest* test, gconstpointer) +{ + WebKitSettings* settings = webkit_web_view_get_settings(test->m_webView); + gboolean enabled = webkit_settings_get_enable_media_stream(settings); + webkit_settings_set_enable_media_stream(settings, TRUE); + + test->showInWindowAndWaitUntilMapped(); + static const char* userMediaRequestHTML = + "<html>" + " <script>" + " function runTest()" + " {" + " navigator.webkitGetUserMedia({audio: true, video: true}," + " function(s) { document.title = \"OK\" }," + " function(e) { document.title = e.name });" + " }" + " </script>" + " <body onload='runTest();'></body>" + "</html>"; + + test->m_verifyMediaTypes = TRUE; + test->m_expectedAudioMedia = TRUE; + test->m_expectedVideoMedia = TRUE; + + // Test denying a permission request. + test->m_allowPermissionRequests = false; + test->loadHtml(userMediaRequestHTML, nullptr); + test->waitUntilTitleChangedTo("PermissionDeniedError"); + + // Test allowing a permission request. + test->m_allowPermissionRequests = true; + test->loadHtml(userMediaRequestHTML, nullptr); + test->waitUntilTitleChangedTo("OK"); + + webkit_settings_set_enable_media_stream(settings, enabled); +} + +static void testWebViewAudioOnlyUserMediaPermissionRequests(UIClientTest* test, gconstpointer) +{ + WebKitSettings* settings = webkit_web_view_get_settings(test->m_webView); + gboolean enabled = webkit_settings_get_enable_media_stream(settings); + webkit_settings_set_enable_media_stream(settings, TRUE); + + test->showInWindowAndWaitUntilMapped(); + static const char* userMediaRequestHTML = + "<html>" + " <script>" + " function runTest()" + " {" + " navigator.webkitGetUserMedia({audio: true, video: false}," + " function(s) { document.title = \"OK\" }," + " function(e) { document.title = e.name });" + " }" + " </script>" + " <body onload='runTest();'></body>" + "</html>"; + + test->m_verifyMediaTypes = TRUE; + test->m_expectedAudioMedia = TRUE; + test->m_expectedVideoMedia = FALSE; + + // Test denying a permission request. + test->m_allowPermissionRequests = false; + test->loadHtml(userMediaRequestHTML, nullptr); + test->waitUntilTitleChangedTo("PermissionDeniedError"); + + webkit_settings_set_enable_media_stream(settings, enabled); +} +#endif // ENABLE(MEDIA_STREAM) + class FileChooserTest: public UIClientTest { public: MAKE_GLIB_TEST_FIXTURE(FileChooserTest); @@ -662,16 +971,124 @@ static void testWebViewFileChooserRequest(FileChooserTest* test, gconstpointer) webkit_file_chooser_request_cancel(fileChooserRequest); } +class ColorChooserTest: public WebViewTest { +public: + MAKE_GLIB_TEST_FIXTURE(ColorChooserTest); + + static gboolean runColorChooserCallback(WebKitWebView*, WebKitColorChooserRequest* request, ColorChooserTest* test) + { + test->runColorChooser(request); + return TRUE; + } + + static void requestFinishedCallback(WebKitColorChooserRequest* request, ColorChooserTest* test) + { + g_assert(test->m_request.get() == request); + test->m_request = nullptr; + if (g_main_loop_is_running(test->m_mainLoop)) + g_main_loop_quit(test->m_mainLoop); + } + + ColorChooserTest() + { + g_signal_connect(m_webView, "run-color-chooser", G_CALLBACK(runColorChooserCallback), this); + } + + void runColorChooser(WebKitColorChooserRequest* request) + { + g_assert(WEBKIT_IS_COLOR_CHOOSER_REQUEST(request)); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(request)); + m_request = request; + g_signal_connect(request, "finished", G_CALLBACK(requestFinishedCallback), this); + g_main_loop_quit(m_mainLoop); + } + + void finishRequest() + { + g_assert(m_request.get()); + webkit_color_chooser_request_finish(m_request.get()); + g_assert(!m_request); + } + + void cancelRequest() + { + g_assert(m_request.get()); + webkit_color_chooser_request_cancel(m_request.get()); + g_assert(!m_request); + } + + WebKitColorChooserRequest* clickMouseButtonAndWaitForColorChooserRequest(int x, int y) + { + clickMouseButton(x, y); + g_main_loop_run(m_mainLoop); + g_assert(m_request.get()); + return m_request.get(); + } + +private: + GRefPtr<WebKitColorChooserRequest> m_request; +}; + +static void testWebViewColorChooserRequest(ColorChooserTest* test, gconstpointer) +{ + static const char* colorChooserHTMLFormat = "<html><body><input style='position:absolute;left:1;top:1;margin:0;padding:0;width:45;height:25' type='color' %s/></body></html>"; + test->showInWindowAndWaitUntilMapped(); + + GUniquePtr<char> defaultColorHTML(g_strdup_printf(colorChooserHTMLFormat, "")); + test->loadHtml(defaultColorHTML.get(), nullptr); + test->waitUntilLoadFinished(); + WebKitColorChooserRequest* request = test->clickMouseButtonAndWaitForColorChooserRequest(5, 5); + + // Default color is black (#000000). + GdkRGBA rgba1; + GdkRGBA rgba2 = { 0., 0., 0., 1. }; + webkit_color_chooser_request_get_rgba(request, &rgba1); + g_assert(gdk_rgba_equal(&rgba1, &rgba2)); + + // Set a different color. + rgba2.green = 1; + webkit_color_chooser_request_set_rgba(request, &rgba2); + webkit_color_chooser_request_get_rgba(request, &rgba1); + g_assert(gdk_rgba_equal(&rgba1, &rgba2)); + + GdkRectangle rect; + webkit_color_chooser_request_get_element_rectangle(request, &rect); + g_assert_cmpint(rect.x, == , 1); + g_assert_cmpint(rect.y, == , 1); + g_assert_cmpint(rect.width, == , 45); + g_assert_cmpint(rect.height, == , 25); + + test->finishRequest(); + + // Use an initial color. + GUniquePtr<char> initialColorHTML(g_strdup_printf(colorChooserHTMLFormat, "value='#FF00FF'")); + test->loadHtml(initialColorHTML.get(), nullptr); + test->waitUntilLoadFinished(); + request = test->clickMouseButtonAndWaitForColorChooserRequest(5, 5); + + webkit_color_chooser_request_get_rgba(request, &rgba1); + GdkRGBA rgba3 = { 1., 0., 1., 1. }; + g_assert(gdk_rgba_equal(&rgba1, &rgba3)); + + test->cancelRequest(); +} + void beforeAll() { UIClientTest::add("WebKitWebView", "create-ready-close", testWebViewCreateReadyClose); + CreateNavigationDataTest::add("WebKitWebView", "create-navigation-data", testWebViewCreateNavigationData); ModalDialogsTest::add("WebKitWebView", "allow-modal-dialogs", testWebViewAllowModalDialogs); ModalDialogsTest::add("WebKitWebView", "disallow-modal-dialogs", testWebViewDisallowModalDialogs); UIClientTest::add("WebKitWebView", "javascript-dialogs", testWebViewJavaScriptDialogs); UIClientTest::add("WebKitWebView", "window-properties", testWebViewWindowProperties); UIClientTest::add("WebKitWebView", "mouse-target", testWebViewMouseTarget); - UIClientTest::add("WebKitWebView", "permission-requests", testWebViewPermissionRequests); + UIClientTest::add("WebKitWebView", "geolocation-permission-requests", testWebViewGeolocationPermissionRequests); +#if ENABLE(MEDIA_STREAM) + UIClientTest::add("WebKitWebView", "usermedia-permission-requests", testWebViewUserMediaPermissionRequests); + UIClientTest::add("WebKitWebView", "audio-usermedia-permission-request", testWebViewAudioOnlyUserMediaPermissionRequests); +#endif FileChooserTest::add("WebKitWebView", "file-chooser-request", testWebViewFileChooserRequest); + ColorChooserTest::add("WebKitWebView", "color-chooser-request", testWebViewColorChooserRequest); } void afterAll() diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebExtensions.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebExtensions.cpp index b02cdfd03..fa4ab2d6c 100644 --- a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebExtensions.cpp +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebExtensions.cpp @@ -21,19 +21,24 @@ #include "WebKitTestBus.h" #include "WebViewTest.h" -#include <wtf/gobject/GRefPtr.h> +#include <wtf/glib/GRefPtr.h> static const char* webExtensionsUserData = "Web Extensions user data"; static WebKitTestBus* bus; static GUniquePtr<char> scriptDialogResult; +#define INPUT_ID "input-id" +#define FORM_ID "form-id" +#define FORM2_ID "form2-id" + static void testWebExtensionGetTitle(WebViewTest* test, gconstpointer) { test->loadHtml("<html><head><title>WebKitGTK+ Web Extensions Test</title></head><body></body></html>", 0); test->waitUntilLoadFinished(); - GRefPtr<GDBusProxy> proxy = adoptGRef(bus->createProxy("org.webkit.gtk.WebExtensionTest", - "/org/webkit/gtk/WebExtensionTest" , "org.webkit.gtk.WebExtensionTest", test->m_mainLoop)); + GUniquePtr<char> extensionBusName(g_strdup_printf("org.webkit.gtk.WebExtensionTest%u", Test::s_webExtensionID)); + GRefPtr<GDBusProxy> proxy = adoptGRef(bus->createProxy(extensionBusName.get(), + "/org/webkit/gtk/WebExtensionTest", "org.webkit.gtk.WebExtensionTest", test->m_mainLoop)); GRefPtr<GVariant> result = adoptGRef(g_dbus_proxy_call_sync( proxy.get(), "GetTitle", @@ -54,7 +59,8 @@ static void documentLoadedCallback(GDBusConnection*, const char*, const char*, c static void testDocumentLoadedSignal(WebViewTest* test, gconstpointer) { - GRefPtr<GDBusProxy> proxy = adoptGRef(bus->createProxy("org.webkit.gtk.WebExtensionTest", + GUniquePtr<char> extensionBusName(g_strdup_printf("org.webkit.gtk.WebExtensionTest%u", Test::s_webExtensionID)); + GRefPtr<GDBusProxy> proxy = adoptGRef(bus->createProxy(extensionBusName.get(), "/org/webkit/gtk/WebExtensionTest", "org.webkit.gtk.WebExtensionTest", test->m_mainLoop)); GDBusConnection* connection = g_dbus_proxy_get_connection(proxy.get()); guint id = g_dbus_connection_signal_subscribe(connection, @@ -86,10 +92,13 @@ static void testWebKitWebViewProcessCrashed(WebViewTest* test, gconstpointer) test->loadHtml("<html></html>", 0); test->waitUntilLoadFinished(); - g_signal_connect(test->m_webView, "web-process-crashed", + g_signal_connect_after(test->m_webView, "web-process-crashed", G_CALLBACK(webProcessCrashedCallback), test); - GRefPtr<GDBusProxy> proxy = adoptGRef(bus->createProxy("org.webkit.gtk.WebExtensionTest", + test->m_expectedWebProcessCrash = true; + + GUniquePtr<char> extensionBusName(g_strdup_printf("org.webkit.gtk.WebExtensionTest%u", Test::s_webExtensionID)); + GRefPtr<GDBusProxy> proxy = adoptGRef(bus->createProxy(extensionBusName.get(), "/org/webkit/gtk/WebExtensionTest", "org.webkit.gtk.WebExtensionTest", test->m_mainLoop)); GRefPtr<GVariant> result = adoptGRef(g_dbus_proxy_call_sync( @@ -100,6 +109,7 @@ static void testWebKitWebViewProcessCrashed(WebViewTest* test, gconstpointer) -1, 0, 0)); g_assert(!result); g_main_loop_run(test->m_mainLoop); + test->m_expectedWebProcessCrash = false; } static void testWebExtensionWindowObjectCleared(WebViewTest* test, gconstpointer) @@ -152,7 +162,8 @@ static void testWebExtensionIsolatedWorld(WebViewTest* test, gconstpointer) "document.getElementById('console').innerHTML = top.foo;\n" "window.open = function () { alert('Isolated World'); }\n" "document.open(1, 2, 3);"; - GRefPtr<GDBusProxy> proxy = adoptGRef(bus->createProxy("org.webkit.gtk.WebExtensionTest", + GUniquePtr<char> extensionBusName(g_strdup_printf("org.webkit.gtk.WebExtensionTest%u", Test::s_webExtensionID)); + GRefPtr<GDBusProxy> proxy = adoptGRef(bus->createProxy(extensionBusName.get(), "/org/webkit/gtk/WebExtensionTest" , "org.webkit.gtk.WebExtensionTest", test->m_mainLoop)); g_dbus_proxy_call(proxy.get(), "RunJavaScriptInIsolatedWorld", @@ -173,49 +184,110 @@ static void testWebExtensionIsolatedWorld(WebViewTest* test, gconstpointer) g_signal_handler_disconnect(test->m_webView, scriptDialogID); } -static void testWebExtensionInitializationUserData(WebViewTest* test, gconstpointer) +static gboolean permissionRequestCallback(WebKitWebView*, WebKitPermissionRequest* request, WebViewTest* test) { - test->loadHtml("<html></html>", 0); - test->waitUntilLoadFinished(); + if (!WEBKIT_IS_INSTALL_MISSING_MEDIA_PLUGINS_PERMISSION_REQUEST(request)) + return FALSE; + + test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(request)); + WebKitInstallMissingMediaPluginsPermissionRequest* missingPluginsRequest = WEBKIT_INSTALL_MISSING_MEDIA_PLUGINS_PERMISSION_REQUEST(request); + g_assert(webkit_install_missing_media_plugins_permission_request_get_description(missingPluginsRequest)); + webkit_permission_request_deny(request); + test->quitMainLoop(); + + return TRUE; +} - GRefPtr<GDBusProxy> proxy = adoptGRef(bus->createProxy("org.webkit.gtk.WebExtensionTest", +static void testInstallMissingPluginsPermissionRequest(WebViewTest* test, gconstpointer) +{ + GUniquePtr<char> extensionBusName(g_strdup_printf("org.webkit.gtk.WebExtensionTest%u", Test::s_webExtensionID)); + GRefPtr<GDBusProxy> proxy = adoptGRef(bus->createProxy(extensionBusName.get(), "/org/webkit/gtk/WebExtensionTest", "org.webkit.gtk.WebExtensionTest", test->m_mainLoop)); + GRefPtr<GVariant> result = adoptGRef(g_dbus_proxy_call_sync(proxy.get(), "RemoveAVPluginsFromGSTRegistry", + nullptr, G_DBUS_CALL_FLAGS_NONE, -1, nullptr, nullptr)); - GRefPtr<GVariant> result = adoptGRef(g_dbus_proxy_call_sync( - proxy.get(), - "GetInitializationUserData", - nullptr, - G_DBUS_CALL_FLAGS_NONE, - -1, 0, 0)); - g_assert(result); + test->showInWindowAndWaitUntilMapped(); - const gchar* userData = nullptr; - g_variant_get(result.get(), "(&s)", &userData); - g_assert_cmpstr(userData, ==, webExtensionsUserData); + gulong permissionRequestSignalID = g_signal_connect(test->m_webView, "permission-request", G_CALLBACK(permissionRequestCallback), test); + // FIXME: the base URI needs to finish with / to work, that shouldn't happen. + GUniquePtr<char> baseURI(g_strconcat("file://", Test::getResourcesDir(Test::WebKit2Resources).data(), "/", nullptr)); + test->loadHtml("<html><body><video src=\"test.mp4\" autoplay></video></body></html>", baseURI.get()); + g_main_loop_run(test->m_mainLoop); + g_signal_handler_disconnect(test->m_webView, permissionRequestSignalID); } -static void initializeWebExtensions(WebKitWebContext* context, gpointer) +static void didAssociateFormControlsCallback(GDBusConnection*, const char*, const char*, const char*, const char*, GVariant* result, WebViewTest* test) { - webkit_web_context_set_web_extensions_directory(context, WEBKIT_TEST_WEB_EXTENSIONS_DIR); - webkit_web_context_set_web_extensions_initialization_user_data(context, - g_variant_new("&s", webExtensionsUserData)); + const char* formIds; + g_variant_get(result, "(&s)", &formIds); + g_assert(!g_strcmp0(formIds, FORM_ID FORM2_ID) || !g_strcmp0(formIds, INPUT_ID)); + + test->quitMainLoop(); } -void beforeAll() +static void testWebExtensionFormControlsAssociated(WebViewTest* test, gconstpointer) { - g_signal_connect(webkit_web_context_get_default(), - "initialize-web-extensions", G_CALLBACK(initializeWebExtensions), nullptr); + GUniquePtr<char> extensionBusName(g_strdup_printf("org.webkit.gtk.WebExtensionTest%u", Test::s_webExtensionID)); + GRefPtr<GDBusProxy> proxy = adoptGRef(bus->createProxy(extensionBusName.get(), + "/org/webkit/gtk/WebExtensionTest", "org.webkit.gtk.WebExtensionTest", test->m_mainLoop)); + GDBusConnection* connection = g_dbus_proxy_get_connection(proxy.get()); + guint id = g_dbus_connection_signal_subscribe(connection, + nullptr, + "org.webkit.gtk.WebExtensionTest", + "FormControlsAssociated", + "/org/webkit/gtk/WebExtensionTest", + nullptr, + G_DBUS_SIGNAL_FLAGS_NONE, + reinterpret_cast<GDBusSignalCallback>(didAssociateFormControlsCallback), + test, + nullptr); + g_assert(id); + + test->loadHtml("<!DOCTYPE html><head><title>WebKitGTK+ Web Extensions Test</title></head><div id=\"placeholder\"/>", 0); + test->waitUntilLoadFinished(); + + static const char* addFormScript = + "var input = document.createElement(\"input\");" + "input.id = \"" INPUT_ID "\";" + "input.type = \"password\";" + "var form = document.createElement(\"form\");" + "form.id = \"" FORM_ID "\";" + "form.appendChild(input);" + "var form2 = document.createElement(\"form\");" + "form2.id = \"" FORM2_ID "\";" + "var placeholder = document.getElementById(\"placeholder\");" + "placeholder.appendChild(form);" + "placeholder.appendChild(form2);"; + + webkit_web_view_run_javascript(test->m_webView, addFormScript, nullptr, nullptr, nullptr); + g_main_loop_run(test->m_mainLoop); + + static const char* moveFormElementScript = + "var form = document.getElementById(\"" FORM_ID "\");" + "var form2 = document.getElementById(\"" FORM2_ID "\");" + "var input = document.getElementById(\"" INPUT_ID "\");" + "form.removeChild(input);" + "form2.appendChild(input);"; + + webkit_web_view_run_javascript(test->m_webView, moveFormElementScript, nullptr, nullptr, nullptr); + g_main_loop_run(test->m_mainLoop); + g_dbus_connection_signal_unsubscribe(connection, id); +} + +void beforeAll() +{ bus = new WebKitTestBus(); if (!bus->run()) return; - WebViewTest::add("WebKitWebContext", "initialization-user-data", testWebExtensionInitializationUserData); WebViewTest::add("WebKitWebExtension", "dom-document-title", testWebExtensionGetTitle); WebViewTest::add("WebKitWebExtension", "document-loaded-signal", testDocumentLoadedSignal); WebViewTest::add("WebKitWebView", "web-process-crashed", testWebKitWebViewProcessCrashed); WebViewTest::add("WebKitWebExtension", "window-object-cleared", testWebExtensionWindowObjectCleared); WebViewTest::add("WebKitWebExtension", "isolated-world", testWebExtensionIsolatedWorld); + WebViewTest::add("WebKitWebView", "install-missing-plugins-permission-request", testInstallMissingPluginsPermissionRequest); + WebViewTest::add("WebKitWebExtension", "form-controls-associated-signal", testWebExtensionFormControlsAssociated); } void afterAll() diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitAccessibility.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitAccessibility.cpp index a5d8df9de..9c6bbdd7c 100644 --- a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitAccessibility.cpp +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitAccessibility.cpp @@ -32,9 +32,8 @@ extern "C" { #include <glib.h> #include <signal.h> #include <unistd.h> -#include <wtf/PassRefPtr.h> -#include <wtf/gobject/GRefPtr.h> -#include <wtf/gobject/GUniquePtr.h> +#include <wtf/glib/GRefPtr.h> +#include <wtf/glib/GUniquePtr.h> // Name of the test server application creating the webView object. static const char* kTestServerAppName = "AccessibilityTestServer"; diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitFaviconDatabase.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitFaviconDatabase.cpp index 0f5a82042..5a3b057fd 100644 --- a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitFaviconDatabase.cpp +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitFaviconDatabase.cpp @@ -23,21 +23,19 @@ #include "WebViewTest.h" #include <glib/gstdio.h> #include <libsoup/soup.h> -#include <wtf/gobject/GUniquePtr.h> +#include <wtf/glib/GUniquePtr.h> static WebKitTestServer* kServer; -static char* kTempDirectory; class FaviconDatabaseTest: public WebViewTest { public: MAKE_GLIB_TEST_FIXTURE(FaviconDatabaseTest); FaviconDatabaseTest() - : m_webContext(webkit_web_context_get_default()) - , m_favicon(0) + : m_favicon(nullptr) , m_faviconNotificationReceived(false) { - WebKitFaviconDatabase* database = webkit_web_context_get_favicon_database(m_webContext); + WebKitFaviconDatabase* database = webkit_web_context_get_favicon_database(m_webContext.get()); g_signal_connect(database, "favicon-changed", G_CALLBACK(faviconChangedCallback), this); } @@ -46,7 +44,7 @@ public: if (m_favicon) cairo_surface_destroy(m_favicon); - WebKitFaviconDatabase* database = webkit_web_context_get_favicon_database(m_webContext); + WebKitFaviconDatabase* database = webkit_web_context_get_favicon_database(m_webContext.get()); g_signal_handlers_disconnect_matched(database, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this); } @@ -67,7 +65,7 @@ public: static void getFaviconCallback(GObject* sourceObject, GAsyncResult* result, void* data) { FaviconDatabaseTest* test = static_cast<FaviconDatabaseTest*>(data); - WebKitFaviconDatabase* database = webkit_web_context_get_favicon_database(test->m_webContext); + WebKitFaviconDatabase* database = webkit_web_context_get_favicon_database(test->m_webContext.get()); test->m_favicon = webkit_favicon_database_get_favicon_finish(database, result, &test->m_error.outPtr()); test->quitMainLoop(); } @@ -87,12 +85,11 @@ public: m_favicon = 0; } - WebKitFaviconDatabase* database = webkit_web_context_get_favicon_database(m_webContext); + WebKitFaviconDatabase* database = webkit_web_context_get_favicon_database(m_webContext.get()); webkit_favicon_database_get_favicon(database, pageURI, 0, getFaviconCallback, this); g_main_loop_run(m_mainLoop); } - WebKitWebContext* m_webContext; cairo_surface_t* m_favicon; CString m_faviconURI; GUniqueOutPtr<GError> m_error; @@ -116,7 +113,7 @@ serverCallback(SoupServer* server, SoupMessage* message, const char* path, GHash char* contents; gsize length; if (g_str_equal(path, "/icon/favicon.ico")) { - GUniquePtr<char> pathToFavicon(g_build_filename(Test::getWebKit1TestResoucesDir().data(), "blank.ico", NULL)); + GUniquePtr<char> pathToFavicon(g_build_filename(Test::getResourcesDir().data(), "blank.ico", nullptr)); g_file_get_contents(pathToFavicon.get(), &contents, &length, 0); soup_message_body_append(message->response_body, SOUP_MEMORY_TAKE, contents, length); } else if (g_str_equal(path, "/nofavicon")) { @@ -142,19 +139,43 @@ static void testNotInitialized(FaviconDatabaseTest* test) static void testSetDirectory(FaviconDatabaseTest* test) { - webkit_web_context_set_favicon_database_directory(test->m_webContext, kTempDirectory); - g_assert_cmpstr(kTempDirectory, ==, webkit_web_context_get_favicon_database_directory(test->m_webContext)); + webkit_web_context_set_favicon_database_directory(test->m_webContext.get(), Test::dataDirectory()); + g_assert_cmpstr(Test::dataDirectory(), ==, webkit_web_context_get_favicon_database_directory(test->m_webContext.get())); } static void testClearDatabase(FaviconDatabaseTest* test) { - WebKitFaviconDatabase* database = webkit_web_context_get_favicon_database(test->m_webContext); + WebKitFaviconDatabase* database = webkit_web_context_get_favicon_database(test->m_webContext.get()); webkit_favicon_database_clear(database); GUniquePtr<char> iconURI(webkit_favicon_database_get_favicon_uri(database, kServer->getURIForPath("/foo").data())); g_assert(!iconURI); } +static void ephemeralViewLoadChanged(WebKitWebView* webView, WebKitLoadEvent loadEvent, WebViewTest* test) +{ + if (loadEvent != WEBKIT_LOAD_FINISHED) + return; + g_signal_handlers_disconnect_by_func(webView, reinterpret_cast<void*>(ephemeralViewLoadChanged), test); + test->quitMainLoop(); +} + +static void testPrivateBrowsing(FaviconDatabaseTest* test) +{ + GRefPtr<WebKitWebView> webView = WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW, + "web-context", test->m_webContext.get(), + "is-ephemeral", TRUE, + nullptr)); + g_signal_connect(webView.get(), "load-changed", G_CALLBACK(ephemeralViewLoadChanged), test); + webkit_web_view_load_uri(webView.get(), kServer->getURIForPath("/foo").data()); + g_main_loop_run(test->m_mainLoop); + + // An ephemeral web view should not write to the database. + test->getFaviconForPageURIAndWaitUntilReady(kServer->getURIForPath("/foo").data()); + g_assert(!test->m_favicon); + g_assert(test->m_error); +} + static void testGetFavicon(FaviconDatabaseTest* test) { // We need to load the page first to ensure the icon data will be @@ -198,7 +219,7 @@ static void testGetFavicon(FaviconDatabaseTest* test) static void testGetFaviconURI(FaviconDatabaseTest* test) { - WebKitFaviconDatabase* database = webkit_web_context_get_favicon_database(test->m_webContext); + WebKitFaviconDatabase* database = webkit_web_context_get_favicon_database(test->m_webContext.get()); CString baseURI = kServer->getURIForPath("/foo"); GUniquePtr<char> iconURI(webkit_favicon_database_get_favicon_uri(database, baseURI.data())); @@ -231,6 +252,7 @@ static void testFaviconDatabase(FaviconDatabaseTest* test, gconstpointer) // See https://bugs.webkit.org/show_bug.cgi?id=111434. testNotInitialized(test); testSetDirectory(test); + testPrivateBrowsing(test); testGetFavicon(test); testGetFaviconURI(test); testWebViewFavicon(test); @@ -243,30 +265,11 @@ void beforeAll() kServer = new WebKitTestServer(); kServer->run(serverCallback); - kTempDirectory = g_dir_make_tmp("WebKit2Tests-XXXXXX", 0); - g_assert(kTempDirectory); - // Add tests to the suite. FaviconDatabaseTest::add("WebKitFaviconDatabase", "favicon-database-test", testFaviconDatabase); } -static void webkitFaviconDatabaseFinalizedCallback(gpointer, GObject*) -{ - if (!g_file_test(kTempDirectory, G_FILE_TEST_IS_DIR)) - return; - - GUniquePtr<char> filename(g_build_filename(kTempDirectory, "WebpageIcons.db", nullptr)); - g_unlink(filename.get()); - - g_rmdir(kTempDirectory); -} - void afterAll() { delete kServer; - - // Delete the temporary files after the IconDatabase has been - // closed, that is, once WebKitFaviconDatabase is being destroyed. - WebKitFaviconDatabase* database = webkit_web_context_get_favicon_database(webkit_web_context_get_default()); - g_object_weak_ref(G_OBJECT(database), webkitFaviconDatabaseFinalizedCallback, 0); } diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitFindController.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitFindController.cpp index d2eef4ca4..7289fd37f 100644 --- a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitFindController.cpp +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitFindController.cpp @@ -22,7 +22,7 @@ #include "LoadTrackingTest.h" #include <gtk/gtk.h> #include <webkit2/webkit2.h> -#include <wtf/gobject/GRefPtr.h> +#include <wtf/glib/GRefPtr.h> static const char* testString = "<html><body>first testing second testing secondHalf</body></html>"; diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitPolicyClient.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitPolicyClient.cpp index 209ea045e..9f9515123 100644 --- a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitPolicyClient.cpp +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitPolicyClient.cpp @@ -21,7 +21,7 @@ #include "LoadTrackingTest.h" #include "WebKitTestServer.h" -#include <wtf/gobject/GRefPtr.h> +#include <wtf/glib/GRefPtr.h> #include <wtf/text/CString.h> static WebKitTestServer* kServer; @@ -117,11 +117,12 @@ static void testNavigationPolicy(PolicyClientTest* test, gconstpointer) // Ideally we'd like to have a more intensive test here, but it's still pretty tricky // to trigger different types of navigations with the GTK+ WebKit2 API. WebKitNavigationPolicyDecision* decision = WEBKIT_NAVIGATION_POLICY_DECISION(test->m_previousPolicyDecision.get()); - g_assert_cmpint(webkit_navigation_policy_decision_get_navigation_type(decision), ==, WEBKIT_NAVIGATION_TYPE_OTHER); - g_assert_cmpint(webkit_navigation_policy_decision_get_mouse_button(decision), ==, 0); - g_assert_cmpint(webkit_navigation_policy_decision_get_modifiers(decision), ==, 0); - g_assert_cmpstr(webkit_navigation_policy_decision_get_frame_name(decision), ==, 0); - WebKitURIRequest* request = webkit_navigation_policy_decision_get_request(decision); + WebKitNavigationAction* navigationAction = webkit_navigation_policy_decision_get_navigation_action(decision); + g_assert_cmpint(webkit_navigation_action_get_navigation_type(navigationAction), ==, WEBKIT_NAVIGATION_TYPE_OTHER); + g_assert_cmpint(webkit_navigation_action_get_mouse_button(navigationAction), ==, 0); + g_assert_cmpint(webkit_navigation_action_get_modifiers(navigationAction), ==, 0); + g_assert(!webkit_navigation_policy_decision_get_frame_name(decision)); + WebKitURIRequest* request = webkit_navigation_action_get_request(navigationAction); g_assert_cmpstr(webkit_uri_request_get_uri(request), ==, "http://webkitgtk.org/"); test->m_policyDecisionResponse = PolicyClientTest::Use; @@ -191,7 +192,7 @@ struct CreateCallbackData { GMainLoop* mainLoop; }; -static WebKitWebView* createCallback(WebKitWebView* webView, CreateCallbackData* data) +static WebKitWebView* createCallback(WebKitWebView* webView, WebKitNavigationAction*, CreateCallbackData* data) { data->triedToOpenWindow = true; g_main_loop_quit(data->mainLoop); diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitSecurityOrigin.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitSecurityOrigin.cpp new file mode 100644 index 000000000..1cc9e7709 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitSecurityOrigin.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2017 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2,1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#include "TestMain.h" +#include <webkit2/webkit2.h> +#include <wtf/glib/GUniquePtr.h> + +static void testSecurityOriginBasicConstructor(Test*, gconstpointer) +{ + WebKitSecurityOrigin* origin = webkit_security_origin_new("http", "127.0.0.1", 1234); + g_assert(origin); + GUniquePtr<char> asString(webkit_security_origin_to_string(origin)); + g_assert_cmpstr(asString.get(), ==, "http://127.0.0.1:1234"); + g_assert_cmpstr(webkit_security_origin_get_protocol(origin), ==, "http"); + g_assert_cmpstr(webkit_security_origin_get_host(origin), ==, "127.0.0.1"); + g_assert_cmpint(webkit_security_origin_get_port(origin), ==, 1234); + g_assert(!webkit_security_origin_is_opaque(origin)); + webkit_security_origin_unref(origin); +} + +static void testSecurityOriginURIConstructor(Test*, gconstpointer) +{ + WebKitSecurityOrigin* origin = webkit_security_origin_new_for_uri("http://127.0.0.1:1234"); + g_assert(origin); + GUniquePtr<char> asString(webkit_security_origin_to_string(origin)); + g_assert_cmpstr(asString.get(), ==, "http://127.0.0.1:1234"); + g_assert_cmpstr(webkit_security_origin_get_protocol(origin), ==, "http"); + g_assert_cmpstr(webkit_security_origin_get_host(origin), ==, "127.0.0.1"); + g_assert_cmpint(webkit_security_origin_get_port(origin), ==, 1234); + g_assert(!webkit_security_origin_is_opaque(origin)); + webkit_security_origin_unref(origin); + + origin = webkit_security_origin_new_for_uri("http://127.0.0.1:1234/this/path/?should=be#ignored"); + g_assert(origin); + asString.reset(webkit_security_origin_to_string(origin)); + g_assert_cmpstr(asString.get(), ==, "http://127.0.0.1:1234"); + g_assert_cmpstr(webkit_security_origin_get_protocol(origin), ==, "http"); + g_assert_cmpstr(webkit_security_origin_get_host(origin), ==, "127.0.0.1"); + g_assert_cmpint(webkit_security_origin_get_port(origin), ==, 1234); + g_assert(!webkit_security_origin_is_opaque(origin)); + webkit_security_origin_unref(origin); +} + +static void testSecurityOriginDefaultPort(Test*, gconstpointer) +{ + WebKitSecurityOrigin* origin = webkit_security_origin_new("http", "127.0.0.1", 0); + g_assert(origin); + GUniquePtr<char> asString(webkit_security_origin_to_string(origin)); + g_assert_cmpstr(asString.get(), ==, "http://127.0.0.1"); + g_assert_cmpstr(webkit_security_origin_get_protocol(origin), ==, "http"); + g_assert_cmpstr(webkit_security_origin_get_host(origin), ==, "127.0.0.1"); + g_assert_cmpint(webkit_security_origin_get_port(origin), ==, 0); + g_assert(!webkit_security_origin_is_opaque(origin)); + webkit_security_origin_unref(origin); + + origin = webkit_security_origin_new("http", "127.0.0.1", 80); + g_assert(origin); + asString.reset(webkit_security_origin_to_string(origin)); + g_assert_cmpstr(asString.get(), ==, "http://127.0.0.1"); + g_assert_cmpstr(webkit_security_origin_get_protocol(origin), ==, "http"); + g_assert_cmpstr(webkit_security_origin_get_host(origin), ==, "127.0.0.1"); + g_assert_cmpint(webkit_security_origin_get_port(origin), ==, 0); + g_assert(!webkit_security_origin_is_opaque(origin)); + webkit_security_origin_unref(origin); + + origin = webkit_security_origin_new_for_uri("http://127.0.0.1"); + g_assert(origin); + asString.reset(webkit_security_origin_to_string(origin)); + g_assert_cmpstr(asString.get(), ==, "http://127.0.0.1"); + g_assert_cmpstr(webkit_security_origin_get_protocol(origin), ==, "http"); + g_assert_cmpstr(webkit_security_origin_get_host(origin), ==, "127.0.0.1"); + g_assert_cmpint(webkit_security_origin_get_port(origin), ==, 0); + g_assert(!webkit_security_origin_is_opaque(origin)); + webkit_security_origin_unref(origin); + + origin = webkit_security_origin_new_for_uri("http://127.0.0.1:80"); + g_assert(origin); + asString.reset(webkit_security_origin_to_string(origin)); + g_assert_cmpstr(asString.get(), ==, "http://127.0.0.1"); + g_assert_cmpstr(webkit_security_origin_get_protocol(origin), ==, "http"); + g_assert_cmpstr(webkit_security_origin_get_host(origin), ==, "127.0.0.1"); + g_assert_cmpint(webkit_security_origin_get_port(origin), ==, 0); + g_assert(!webkit_security_origin_is_opaque(origin)); + webkit_security_origin_unref(origin); +} + +static void testSecurityOriginFileURI(Test*, gconstpointer) +{ + WebKitSecurityOrigin* origin = webkit_security_origin_new_for_uri("file:///abcdefg"); + g_assert(origin); + GUniquePtr<char> asString(webkit_security_origin_to_string(origin)); + g_assert_cmpstr(asString.get(), ==, "file://"); + g_assert_cmpstr(webkit_security_origin_get_protocol(origin), ==, "file"); + g_assert(!webkit_security_origin_get_host(origin)); + g_assert_cmpint(webkit_security_origin_get_port(origin), ==, 0); + g_assert(!webkit_security_origin_is_opaque(origin)); + webkit_security_origin_unref(origin); +} + +static void testOpaqueSecurityOrigin(Test*, gconstpointer) +{ + WebKitSecurityOrigin* origin = webkit_security_origin_new_for_uri("data:Lali ho!"); + g_assert(origin); + GUniquePtr<char> asString(webkit_security_origin_to_string(origin)); + g_assert(!asString); + g_assert(!webkit_security_origin_get_protocol(origin)); + g_assert(!webkit_security_origin_get_host(origin)); + g_assert_cmpint(webkit_security_origin_get_port(origin), ==, 0); + g_assert(webkit_security_origin_is_opaque(origin)); + webkit_security_origin_unref(origin); +} + +void beforeAll() +{ + Test::add("WebKitSecurityOrigin", "basic-constructor", testSecurityOriginBasicConstructor); + Test::add("WebKitSecurityOrigin", "uri-constructor", testSecurityOriginURIConstructor); + Test::add("WebKitSecruityOrigin", "default-port", testSecurityOriginDefaultPort); + Test::add("WebKitSecurityOrigin", "file-uri", testSecurityOriginFileURI); + Test::add("WebKitSecruityOrigin", "opaque-origin", testOpaqueSecurityOrigin); +} + +void afterAll() +{ +} diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitSettings.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitSettings.cpp index fb1091889..db60c2fe8 100644 --- a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitSettings.cpp +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitSettings.cpp @@ -35,7 +35,7 @@ #include "WebKitTestServer.h" #include <gtk/gtk.h> #include <webkit2/webkit2.h> -#include <wtf/gobject/GRefPtr.h> +#include <wtf/glib/GRefPtr.h> static WebKitTestServer* gServer; @@ -158,9 +158,11 @@ static void testWebKitSettings(Test*, gconstpointer) webkit_settings_set_default_charset(settings, "utf8"); g_assert_cmpstr(webkit_settings_get_default_charset(settings), ==, "utf8"); + G_GNUC_BEGIN_IGNORE_DEPRECATIONS; g_assert(!webkit_settings_get_enable_private_browsing(settings)); webkit_settings_set_enable_private_browsing(settings, TRUE); g_assert(webkit_settings_get_enable_private_browsing(settings)); + G_GNUC_END_IGNORE_DEPRECATIONS; g_assert(!webkit_settings_get_enable_developer_extras(settings)); webkit_settings_set_enable_developer_extras(settings, TRUE); @@ -273,6 +275,25 @@ static void testWebKitSettings(Test*, gconstpointer) webkit_settings_set_enable_mediasource(settings, TRUE); g_assert(webkit_settings_get_enable_mediasource(settings)); + // File access from file URLs is not allowed by default. + g_assert(!webkit_settings_get_allow_file_access_from_file_urls(settings)); + webkit_settings_set_allow_file_access_from_file_urls(settings, TRUE); + g_assert(webkit_settings_get_allow_file_access_from_file_urls(settings)); + + // Universal access from file URLs is not allowed by default. + g_assert(!webkit_settings_get_allow_universal_access_from_file_urls(settings)); + webkit_settings_set_allow_universal_access_from_file_urls(settings, TRUE); + g_assert(webkit_settings_get_allow_universal_access_from_file_urls(settings)); + + // Ondemand is the default hardware acceleration policy. + g_assert_cmpuint(webkit_settings_get_hardware_acceleration_policy(settings), ==, WEBKIT_HARDWARE_ACCELERATION_POLICY_ON_DEMAND); + webkit_settings_set_hardware_acceleration_policy(settings, WEBKIT_HARDWARE_ACCELERATION_POLICY_NEVER); + g_assert_cmpuint(webkit_settings_get_hardware_acceleration_policy(settings), ==, WEBKIT_HARDWARE_ACCELERATION_POLICY_NEVER); + webkit_settings_set_hardware_acceleration_policy(settings, WEBKIT_HARDWARE_ACCELERATION_POLICY_ALWAYS); + g_assert_cmpuint(webkit_settings_get_hardware_acceleration_policy(settings), ==, WEBKIT_HARDWARE_ACCELERATION_POLICY_ALWAYS); + webkit_settings_set_hardware_acceleration_policy(settings, WEBKIT_HARDWARE_ACCELERATION_POLICY_ON_DEMAND); + g_assert_cmpuint(webkit_settings_get_hardware_acceleration_policy(settings), ==, WEBKIT_HARDWARE_ACCELERATION_POLICY_ON_DEMAND); + g_object_unref(G_OBJECT(settings)); } diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitUserContentManager.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitUserContentManager.cpp new file mode 100644 index 000000000..134ad3837 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitUserContentManager.cpp @@ -0,0 +1,360 @@ +/* + * Copyright (C) 2013-2014 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2,1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#include "WebKitTestServer.h" +#include "WebViewTest.h" +#include <cstdarg> +#include <gtk/gtk.h> +#include <webkit2/webkit2.h> +#include <wtf/glib/GRefPtr.h> +#include <wtf/glib/GUniquePtr.h> + +static WebKitTestServer* kServer; + +// These are all here so that they can be changed easily, if necessary. +static const char* kStyleSheetHTML = "<html><div id=\"styledElement\">Sweet stylez!</div></html>"; +static const char* kInjectedStyleSheet = "#styledElement { font-weight: bold; }"; +static const char* kStyleSheetTestScript = "getComputedStyle(document.getElementById('styledElement'))['font-weight']"; +static const char* kStyleSheetTestScriptResult = "bold"; +static const char* kInjectedScript = "document.write('<div id=\"item\">Generated by a script</div>')"; +static const char* kScriptTestScript = "document.getElementById('item').innerText"; +static const char* kScriptTestScriptResult = "Generated by a script"; + +static void testWebViewNewWithUserContentManager(Test* test, gconstpointer) +{ + GRefPtr<WebKitUserContentManager> userContentManager1 = adoptGRef(webkit_user_content_manager_new()); + test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(userContentManager1.get())); + GRefPtr<WebKitWebView> webView1 = WEBKIT_WEB_VIEW(webkit_web_view_new_with_user_content_manager(userContentManager1.get())); + g_assert(webkit_web_view_get_user_content_manager(webView1.get()) == userContentManager1.get()); + + GRefPtr<WebKitWebView> webView2 = WEBKIT_WEB_VIEW(webkit_web_view_new()); + g_assert(webkit_web_view_get_user_content_manager(webView2.get()) != userContentManager1.get()); +} + +static bool isStyleSheetInjectedForURLAtPath(WebViewTest* test, const char* path) +{ + test->loadURI(kServer->getURIForPath(path).data()); + test->waitUntilLoadFinished(); + + GUniqueOutPtr<GError> error; + WebKitJavascriptResult* javascriptResult = test->runJavaScriptAndWaitUntilFinished(kStyleSheetTestScript, &error.outPtr()); + g_assert(javascriptResult); + g_assert(!error.get()); + + GUniquePtr<char> resultString(WebViewTest::javascriptResultToCString(javascriptResult)); + return !g_strcmp0(resultString.get(), kStyleSheetTestScriptResult); +} + +static bool isScriptInjectedForURLAtPath(WebViewTest* test, const char* path) +{ + test->loadURI(kServer->getURIForPath(path).data()); + test->waitUntilLoadFinished(); + + GUniqueOutPtr<GError> error; + if (WebKitJavascriptResult* javascriptResult = test->runJavaScriptAndWaitUntilFinished(kScriptTestScript, &error.outPtr())) { + g_assert(!error.get()); + + GUniquePtr<char> resultString(WebViewTest::javascriptResultToCString(javascriptResult)); + return !g_strcmp0(resultString.get(), kScriptTestScriptResult); + } + return false; +} + +static void fillURLListFromPaths(char** list, const char* path, ...) +{ + va_list argumentList; + va_start(argumentList, path); + + int i = 0; + while (path) { + // FIXME: We must use a wildcard for the host here until http://wkbug.com/112476 is fixed. + // Until that time patterns with port numbers in them will not properly match URLs with port numbers. + list[i++] = g_strdup_printf("http://*/%s*", path); + path = va_arg(argumentList, const char*); + } +} + +static void removeOldInjectedContentAndResetLists(WebKitUserContentManager* userContentManager, char** whitelist, char** blacklist) +{ + webkit_user_content_manager_remove_all_style_sheets(userContentManager); + webkit_user_content_manager_remove_all_scripts(userContentManager); + + while (*whitelist) { + g_free(*whitelist); + *whitelist = 0; + whitelist++; + } + + while (*blacklist) { + g_free(*blacklist); + *blacklist = 0; + blacklist++; + } +} + +static void testUserContentManagerInjectedStyleSheet(WebViewTest* test, gconstpointer) +{ + char* whitelist[3] = { 0, 0, 0 }; + char* blacklist[3] = { 0, 0, 0 }; + + removeOldInjectedContentAndResetLists(test->m_userContentManager.get(), whitelist, blacklist); + + // Without a whitelist or a blacklist all URLs should have the injected style sheet. + static const char* randomPath = "somerandompath"; + g_assert(!isStyleSheetInjectedForURLAtPath(test, randomPath)); + WebKitUserStyleSheet* styleSheet = webkit_user_style_sheet_new(kInjectedStyleSheet, WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES, WEBKIT_USER_STYLE_LEVEL_USER, nullptr, nullptr); + webkit_user_content_manager_add_style_sheet(test->m_userContentManager.get(), styleSheet); + webkit_user_style_sheet_unref(styleSheet); + g_assert(isStyleSheetInjectedForURLAtPath(test, randomPath)); + + removeOldInjectedContentAndResetLists(test->m_userContentManager.get(), whitelist, blacklist); + + fillURLListFromPaths(blacklist, randomPath, 0); + styleSheet = webkit_user_style_sheet_new(kInjectedStyleSheet, WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES, WEBKIT_USER_STYLE_LEVEL_USER, nullptr, blacklist); + webkit_user_content_manager_add_style_sheet(test->m_userContentManager.get(), styleSheet); + webkit_user_style_sheet_unref(styleSheet); + g_assert(!isStyleSheetInjectedForURLAtPath(test, randomPath)); + g_assert(isStyleSheetInjectedForURLAtPath(test, "someotherrandompath")); + + removeOldInjectedContentAndResetLists(test->m_userContentManager.get(), whitelist, blacklist); + + static const char* inTheWhiteList = "inthewhitelist"; + static const char* notInWhitelist = "notinthewhitelist"; + static const char* inTheWhiteListAndBlackList = "inthewhitelistandblacklist"; + + fillURLListFromPaths(whitelist, inTheWhiteList, inTheWhiteListAndBlackList, 0); + fillURLListFromPaths(blacklist, inTheWhiteListAndBlackList, 0); + styleSheet = webkit_user_style_sheet_new(kInjectedStyleSheet, WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES, WEBKIT_USER_STYLE_LEVEL_USER, whitelist, blacklist); + webkit_user_content_manager_add_style_sheet(test->m_userContentManager.get(), styleSheet); + webkit_user_style_sheet_unref(styleSheet); + g_assert(isStyleSheetInjectedForURLAtPath(test, inTheWhiteList)); + g_assert(!isStyleSheetInjectedForURLAtPath(test, inTheWhiteListAndBlackList)); + g_assert(!isStyleSheetInjectedForURLAtPath(test, notInWhitelist)); + + // It's important to clean up the environment before other tests. + removeOldInjectedContentAndResetLists(test->m_userContentManager.get(), whitelist, blacklist); +} + +static void testUserContentManagerInjectedScript(WebViewTest* test, gconstpointer) +{ + char* whitelist[3] = { 0, 0, 0 }; + char* blacklist[3] = { 0, 0, 0 }; + + removeOldInjectedContentAndResetLists(test->m_userContentManager.get(), whitelist, blacklist); + + // Without a whitelist or a blacklist all URLs should have the injected script. + static const char* randomPath = "somerandompath"; + g_assert(!isScriptInjectedForURLAtPath(test, randomPath)); + WebKitUserScript* script = webkit_user_script_new(kInjectedScript, WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES, WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_END, nullptr, nullptr); + webkit_user_content_manager_add_script(test->m_userContentManager.get(), script); + webkit_user_script_unref(script); + g_assert(isScriptInjectedForURLAtPath(test, randomPath)); + + removeOldInjectedContentAndResetLists(test->m_userContentManager.get(), whitelist, blacklist); + + fillURLListFromPaths(blacklist, randomPath, 0); + script = webkit_user_script_new(kInjectedScript, WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES, WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_END, nullptr, blacklist); + webkit_user_content_manager_add_script(test->m_userContentManager.get(), script); + webkit_user_script_unref(script); + g_assert(!isScriptInjectedForURLAtPath(test, randomPath)); + g_assert(isScriptInjectedForURLAtPath(test, "someotherrandompath")); + + removeOldInjectedContentAndResetLists(test->m_userContentManager.get(), whitelist, blacklist); + + static const char* inTheWhiteList = "inthewhitelist"; + static const char* notInWhitelist = "notinthewhitelist"; + static const char* inTheWhiteListAndBlackList = "inthewhitelistandblacklist"; + + fillURLListFromPaths(whitelist, inTheWhiteList, inTheWhiteListAndBlackList, 0); + fillURLListFromPaths(blacklist, inTheWhiteListAndBlackList, 0); + script = webkit_user_script_new(kInjectedScript, WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES, WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_END, whitelist, blacklist); + webkit_user_content_manager_add_script(test->m_userContentManager.get(), script); + webkit_user_script_unref(script); + g_assert(isScriptInjectedForURLAtPath(test, inTheWhiteList)); + g_assert(!isScriptInjectedForURLAtPath(test, inTheWhiteListAndBlackList)); + g_assert(!isScriptInjectedForURLAtPath(test, notInWhitelist)); + + // It's important to clean up the environment before other tests. + removeOldInjectedContentAndResetLists(test->m_userContentManager.get(), whitelist, blacklist); +} + +class UserScriptMessageTest : public WebViewTest { +public: + MAKE_GLIB_TEST_FIXTURE(UserScriptMessageTest); + + UserScriptMessageTest() + : m_userScriptMessage(nullptr) + { + } + + ~UserScriptMessageTest() + { + if (m_userScriptMessage) + webkit_javascript_result_unref(m_userScriptMessage); + } + + bool registerHandler(const char* handlerName) + { + return webkit_user_content_manager_register_script_message_handler(m_userContentManager.get(), handlerName); + } + + void unregisterHandler(const char* handlerName) + { + webkit_user_content_manager_unregister_script_message_handler(m_userContentManager.get(), handlerName); + } + + static void scriptMessageReceived(WebKitUserContentManager* userContentManager, WebKitJavascriptResult* jsResult, UserScriptMessageTest* test) + { + g_signal_handlers_disconnect_by_func(userContentManager, reinterpret_cast<gpointer>(scriptMessageReceived), test); + g_main_loop_quit(test->m_mainLoop); + + g_assert(!test->m_userScriptMessage); + test->m_userScriptMessage = webkit_javascript_result_ref(jsResult); + } + + WebKitJavascriptResult* waitUntilMessageReceived(const char* handlerName) + { + if (m_userScriptMessage) { + webkit_javascript_result_unref(m_userScriptMessage); + m_userScriptMessage = nullptr; + } + + GUniquePtr<char> signalName(g_strdup_printf("script-message-received::%s", handlerName)); + g_signal_connect(m_userContentManager.get(), signalName.get(), G_CALLBACK(scriptMessageReceived), this); + + g_main_loop_run(m_mainLoop); + g_assert(m_userScriptMessage); + return m_userScriptMessage; + } + + WebKitJavascriptResult* postMessageAndWaitUntilReceived(const char* handlerName, const char* javascriptValueAsText) + { + GUniquePtr<char> javascriptSnippet(g_strdup_printf("window.webkit.messageHandlers.%s.postMessage(%s);", handlerName, javascriptValueAsText)); + webkit_web_view_run_javascript(m_webView, javascriptSnippet.get(), nullptr, nullptr, nullptr); + return waitUntilMessageReceived(handlerName); + } + +private: + WebKitJavascriptResult* m_userScriptMessage; +}; + +static void testUserContentManagerScriptMessageReceived(UserScriptMessageTest* test, gconstpointer) +{ + g_assert(test->registerHandler("msg")); + + // Trying to register the same handler a second time must fail. + g_assert(!test->registerHandler("msg")); + + test->loadHtml("<html></html>", nullptr); + test->waitUntilLoadFinished(); + + // Check that the "window.webkit.messageHandlers" namespace exists. + GUniqueOutPtr<GError> error; + WebKitJavascriptResult* javascriptResult = test->runJavaScriptAndWaitUntilFinished("window.webkit.messageHandlers ? 'y' : 'n';", &error.outPtr()); + g_assert(javascriptResult); + g_assert(!error.get()); + GUniquePtr<char> valueString(WebViewTest::javascriptResultToCString(javascriptResult)); + g_assert_cmpstr(valueString.get(), ==, "y"); + + // Check that the "document.webkit.messageHandlers.msg" namespace exists. + javascriptResult = test->runJavaScriptAndWaitUntilFinished("window.webkit.messageHandlers.msg ? 'y' : 'n';", &error.outPtr()); + g_assert(javascriptResult); + g_assert(!error.get()); + valueString.reset(WebViewTest::javascriptResultToCString(javascriptResult)); + g_assert_cmpstr(valueString.get(), ==, "y"); + + valueString.reset(WebViewTest::javascriptResultToCString(test->postMessageAndWaitUntilReceived("msg", "'user message'"))); + g_assert_cmpstr(valueString.get(), ==, "user message"); + + // Messages should arrive despite of other handlers being registered. + g_assert(test->registerHandler("anotherHandler")); + + // Check that the "document.webkit.messageHandlers.msg" namespace still exists. + javascriptResult = test->runJavaScriptAndWaitUntilFinished("window.webkit.messageHandlers.msg ? 'y' : 'n';", &error.outPtr()); + g_assert(javascriptResult); + g_assert(!error.get()); + valueString.reset(WebViewTest::javascriptResultToCString(javascriptResult)); + g_assert_cmpstr(valueString.get(), ==, "y"); + + // Check that the "document.webkit.messageHandlers.anotherHandler" namespace exists. + javascriptResult = test->runJavaScriptAndWaitUntilFinished("window.webkit.messageHandlers.anotherHandler ? 'y' : 'n';", &error.outPtr()); + g_assert(javascriptResult); + g_assert(!error.get()); + valueString.reset(WebViewTest::javascriptResultToCString(javascriptResult)); + g_assert_cmpstr(valueString.get(), ==, "y"); + + valueString.reset(WebViewTest::javascriptResultToCString(test->postMessageAndWaitUntilReceived("msg", "'handler: msg'"))); + g_assert_cmpstr(valueString.get(), ==, "handler: msg"); + + valueString.reset(WebViewTest::javascriptResultToCString(test->postMessageAndWaitUntilReceived("anotherHandler", "'handler: anotherHandler'"))); + g_assert_cmpstr(valueString.get(), ==, "handler: anotherHandler"); + + // Unregistering a handler and re-registering again under the same name should work. + test->unregisterHandler("msg"); + + javascriptResult = test->runJavaScriptAndWaitUntilFinished("window.webkit.messageHandlers.msg.postMessage('42');", &error.outPtr()); + g_assert(!javascriptResult); + g_assert(error.get()); + + // Re-registering a handler that has been unregistered must work + g_assert(test->registerHandler("msg")); + valueString.reset(WebViewTest::javascriptResultToCString(test->postMessageAndWaitUntilReceived("msg", "'handler: msg'"))); + g_assert_cmpstr(valueString.get(), ==, "handler: msg"); + + test->unregisterHandler("anotherHandler"); +} + +static void testUserContentManagerScriptMessageFromDOMBindings(UserScriptMessageTest* test, gconstpointer) +{ + g_assert(test->registerHandler("dom")); + + test->loadHtml("<html>1</html>", nullptr); + WebKitJavascriptResult* javascriptResult = test->waitUntilMessageReceived("dom"); + g_assert(javascriptResult); + GUniquePtr<char> valueString(WebViewTest::javascriptResultToCString(javascriptResult)); + g_assert_cmpstr(valueString.get(), ==, "DocumentLoaded"); + + test->unregisterHandler("dom"); +} + +static void serverCallback(SoupServer* server, SoupMessage* message, const char* path, GHashTable*, SoupClientContext*, gpointer) +{ + soup_message_set_status(message, SOUP_STATUS_OK); + soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, kStyleSheetHTML, strlen(kStyleSheetHTML)); + soup_message_body_complete(message->response_body); +} + +void beforeAll() +{ + kServer = new WebKitTestServer(); + kServer->run(serverCallback); + + Test::add("WebKitWebView", "new-with-user-content-manager", testWebViewNewWithUserContentManager); + WebViewTest::add("WebKitUserContentManager", "injected-style-sheet", testUserContentManagerInjectedStyleSheet); + WebViewTest::add("WebKitUserContentManager", "injected-script", testUserContentManagerInjectedScript); + UserScriptMessageTest::add("WebKitUserContentManager", "script-message-received", testUserContentManagerScriptMessageReceived); + UserScriptMessageTest::add("WebKitUserContentManager", "script-message-from-dom-bindings", testUserContentManagerScriptMessageFromDOMBindings); +} + +void afterAll() +{ + delete kServer; +} diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitWebContext.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitWebContext.cpp index 74e8d6817..e7b03fe0d 100644 --- a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitWebContext.cpp +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitWebContext.cpp @@ -22,10 +22,13 @@ #include "LoadTrackingTest.h" #include "WebKitTestServer.h" #include <gtk/gtk.h> +#include <limits.h> +#include <stdlib.h> #include <webkit2/webkit2.h> #include <wtf/HashMap.h> -#include <wtf/gobject/GRefPtr.h> -#include <wtf/gobject/GUniquePtr.h> +#include <wtf/glib/GRefPtr.h> +#include <wtf/glib/GUniquePtr.h> +#include <wtf/text/StringBuilder.h> #include <wtf/text/StringHash.h> static WebKitTestServer* kServer; @@ -34,6 +37,45 @@ static void testWebContextDefault(Test* test, gconstpointer) { // Check there's a single instance of the default web context. g_assert(webkit_web_context_get_default() == webkit_web_context_get_default()); + g_assert(webkit_web_context_get_default() != test->m_webContext.get()); +} + +static void testWebContextEphemeral(Test* test, gconstpointer) +{ + // By default web contexts are not ephemeral. + g_assert(!webkit_web_context_is_ephemeral(webkit_web_context_get_default())); + g_assert(!webkit_web_context_is_ephemeral(test->m_webContext.get())); + + WebKitWebsiteDataManager* manager = webkit_web_context_get_website_data_manager(webkit_web_context_get_default()); + g_assert(WEBKIT_IS_WEBSITE_DATA_MANAGER(manager)); + g_assert(!webkit_website_data_manager_is_ephemeral(manager)); + manager = webkit_web_context_get_website_data_manager(test->m_webContext.get()); + g_assert(WEBKIT_IS_WEBSITE_DATA_MANAGER(manager)); + g_assert(!webkit_website_data_manager_is_ephemeral(manager)); + + GRefPtr<WebKitWebView> webView = WEBKIT_WEB_VIEW(webkit_web_view_new()); + g_assert(!webkit_web_view_is_ephemeral(webView.get())); + g_assert(webkit_web_view_get_website_data_manager(webView.get()) == webkit_web_context_get_website_data_manager(webkit_web_context_get_default())); + + webView = WEBKIT_WEB_VIEW(webkit_web_view_new_with_context(test->m_webContext.get())); + g_assert(!webkit_web_view_is_ephemeral(webView.get())); + g_assert(webkit_web_view_get_website_data_manager(webView.get()) == manager); + + GRefPtr<WebKitWebContext> context = adoptGRef(webkit_web_context_new_ephemeral()); + g_assert(webkit_web_context_is_ephemeral(context.get())); + manager = webkit_web_context_get_website_data_manager(context.get()); + g_assert(WEBKIT_IS_WEBSITE_DATA_MANAGER(manager)); + g_assert(webkit_website_data_manager_is_ephemeral(manager)); + g_assert(webkit_web_view_get_website_data_manager(webView.get()) != manager); + + webView = WEBKIT_WEB_VIEW(webkit_web_view_new_with_context(context.get())); + g_assert(webkit_web_view_is_ephemeral(webView.get())); + g_assert(webkit_web_view_get_website_data_manager(webView.get()) == manager); + + GRefPtr<WebKitWebsiteDataManager> ephemeralManager = adoptGRef(webkit_website_data_manager_new_ephemeral()); + g_assert(webkit_website_data_manager_is_ephemeral(ephemeralManager.get())); + context = adoptGRef(webkit_web_context_new_with_website_data_manager(ephemeralManager.get())); + g_assert(webkit_web_context_is_ephemeral(context.get())); } class PluginsTest: public Test { @@ -41,11 +83,10 @@ public: MAKE_GLIB_TEST_FIXTURE(PluginsTest); PluginsTest() - : m_context(webkit_web_context_get_default()) - , m_mainLoop(g_main_loop_new(0, TRUE)) - , m_plugins(0) + : m_mainLoop(g_main_loop_new(nullptr, TRUE)) + , m_plugins(nullptr) { - webkit_web_context_set_additional_plugins_directory(m_context, WEBKIT_TEST_PLUGIN_DIR); + webkit_web_context_set_additional_plugins_directory(m_webContext.get(), WEBKIT_TEST_PLUGIN_DIR); } ~PluginsTest() @@ -56,19 +97,18 @@ public: static void getPluginsAsyncReadyCallback(GObject*, GAsyncResult* result, PluginsTest* test) { - test->m_plugins = webkit_web_context_get_plugins_finish(test->m_context, result, 0); + test->m_plugins = webkit_web_context_get_plugins_finish(test->m_webContext.get(), result, nullptr); g_main_loop_quit(test->m_mainLoop); } GList* getPlugins() { g_list_free_full(m_plugins, g_object_unref); - webkit_web_context_get_plugins(m_context, 0, reinterpret_cast<GAsyncReadyCallback>(getPluginsAsyncReadyCallback), this); + webkit_web_context_get_plugins(m_webContext.get(), nullptr, reinterpret_cast<GAsyncReadyCallback>(getPluginsAsyncReadyCallback), this); g_main_loop_run(m_mainLoop); return m_plugins; } - WebKitWebContext* m_context; GMainLoop* m_mainLoop; GList* m_plugins; }; @@ -89,7 +129,9 @@ static void testWebContextGetPlugins(PluginsTest* test, gconstpointer) } g_assert(WEBKIT_IS_PLUGIN(testPlugin.get())); - GUniquePtr<char> pluginPath(g_build_filename(WEBKIT_TEST_PLUGIN_DIR, "libTestNetscapePlugin.so", nullptr)); + char normalizedPath[PATH_MAX]; + g_assert(realpath(WEBKIT_TEST_PLUGIN_DIR, normalizedPath)); + GUniquePtr<char> pluginPath(g_build_filename(normalizedPath, "libTestNetscapePlugIn.so", nullptr)); g_assert_cmpstr(webkit_plugin_get_path(testPlugin.get()), ==, pluginPath.get()); g_assert_cmpstr(webkit_plugin_get_description(testPlugin.get()), ==, "Simple Netscape® plug-in that handles test content for WebKit"); GList* mimeInfoList = webkit_plugin_get_mime_info_list(testPlugin.get()); @@ -116,7 +158,10 @@ static const char* kBarHTML = "<html><body>Bar</body></html>"; static const char* kEchoHTMLFormat = "<html><body>%s</body></html>"; static const char* errorDomain = "test"; static const int errorCode = 10; -static const char* errorMessage = "Error message."; + +static const char* genericErrorMessage = "Error message."; +static const char* beforeReceiveResponseErrorMessage = "Error before didReceiveResponse."; +static const char* afterInitialChunkErrorMessage = "Error after reading the initial chunk."; class URISchemeTest: public LoadTrackingTest { public: @@ -148,23 +193,38 @@ public: g_assert(webkit_uri_scheme_request_get_web_view(request) == test->m_webView); - GRefPtr<GInputStream> inputStream = adoptGRef(g_memory_input_stream_new()); - test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(inputStream.get())); - const char* scheme = webkit_uri_scheme_request_get_scheme(request); g_assert(scheme); g_assert(test->m_handlersMap.contains(String::fromUTF8(scheme))); + const URISchemeHandler& handler = test->m_handlersMap.get(String::fromUTF8(scheme)); + + GRefPtr<GInputStream> inputStream = adoptGRef(g_memory_input_stream_new()); + test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(inputStream.get())); + + const gchar* requestPath = webkit_uri_scheme_request_get_path(request); + if (!g_strcmp0(scheme, "error")) { - GUniquePtr<GError> error(g_error_new_literal(g_quark_from_string(errorDomain), errorCode, errorMessage)); - webkit_uri_scheme_request_finish_error(request, error.get()); + if (!g_strcmp0(requestPath, "before-response")) { + GUniquePtr<GError> error(g_error_new_literal(g_quark_from_string(errorDomain), errorCode, beforeReceiveResponseErrorMessage)); + // We call finish() and then finish_error() to make sure that not even + // the didReceiveResponse message is processed at the time of failing. + webkit_uri_scheme_request_finish(request, G_INPUT_STREAM(inputStream.get()), handler.replyLength, handler.mimeType.data()); + webkit_uri_scheme_request_finish_error(request, error.get()); + } else if (!g_strcmp0(requestPath, "after-first-chunk")) { + g_memory_input_stream_add_data(G_MEMORY_INPUT_STREAM(inputStream.get()), handler.reply.data(), handler.reply.length(), 0); + webkit_uri_scheme_request_finish(request, inputStream.get(), handler.replyLength, handler.mimeType.data()); + // We need to wait until we reach the load-committed state before calling webkit_uri_scheme_request_finish_error(), + // so we rely on the test using finishOnCommittedAndWaitUntilLoadFinished() to actually call it from loadCommitted(). + } else { + GUniquePtr<GError> error(g_error_new_literal(g_quark_from_string(errorDomain), errorCode, genericErrorMessage)); + webkit_uri_scheme_request_finish_error(request, error.get()); + } return; } - const URISchemeHandler& handler = test->m_handlersMap.get(String::fromUTF8(scheme)); - if (!g_strcmp0(scheme, "echo")) { - char* replyHTML = g_strdup_printf(handler.reply.data(), webkit_uri_scheme_request_get_path(request)); + char* replyHTML = g_strdup_printf(handler.reply.data(), requestPath); g_memory_input_stream_add_data(G_MEMORY_INPUT_STREAM(inputStream.get()), replyHTML, strlen(replyHTML), g_free); } else if (!g_strcmp0(scheme, "closed")) g_input_stream_close(inputStream.get(), 0, 0); @@ -177,13 +237,58 @@ public: void registerURISchemeHandler(const char* scheme, const char* reply, int replyLength, const char* mimeType) { m_handlersMap.set(String::fromUTF8(scheme), URISchemeHandler(reply, replyLength, mimeType)); - webkit_web_context_register_uri_scheme(webkit_web_context_get_default(), scheme, uriSchemeRequestCallback, this, 0); + webkit_web_context_register_uri_scheme(m_webContext.get(), scheme, uriSchemeRequestCallback, this, 0); + } + + void loadCommitted() override + { + if (m_finishOnCommitted) { + GUniquePtr<GError> error(g_error_new_literal(g_quark_from_string(errorDomain), errorCode, afterInitialChunkErrorMessage)); + webkit_uri_scheme_request_finish_error(m_uriSchemeRequest.get(), error.get()); + } + + LoadTrackingTest::loadCommitted(); + } + + void finishOnCommittedAndWaitUntilLoadFinished() + { + m_finishOnCommitted = true; + waitUntilLoadFinished(); + m_finishOnCommitted = false; } GRefPtr<WebKitURISchemeRequest> m_uriSchemeRequest; HashMap<String, URISchemeHandler> m_handlersMap; + bool m_finishOnCommitted { false }; }; +String generateHTMLContent(unsigned contentLength) +{ + String baseString("abcdefghijklmnopqrstuvwxyz0123457890"); + unsigned baseLength = baseString.length(); + + StringBuilder builder; + builder.append("<html><body>"); + + if (contentLength <= baseLength) + builder.append(baseString, 0, contentLength); + else { + unsigned currentLength = 0; + while (currentLength < contentLength) { + if ((currentLength + baseLength) <= contentLength) + builder.append(baseString); + else + builder.append(baseString, 0, contentLength - currentLength); + + // Account for the 12 characters of the '<html><body>' prefix. + currentLength = builder.length() - 12; + } + } + builder.append("</body></html>"); + + return builder.toString(); +} + static void testWebContextURIScheme(URISchemeTest* test, gconstpointer) { test->registerURISchemeHandler("foo", kBarHTML, strlen(kBarHTML), "text/html"); @@ -203,6 +308,16 @@ static void testWebContextURIScheme(URISchemeTest* test, gconstpointer) g_assert_cmpint(mainResourceDataSize, ==, strlen(echoHTML.get())); g_assert(!strncmp(mainResourceData, echoHTML.get(), mainResourceDataSize)); + test->loadURI("echo:with#fragment"); + test->waitUntilLoadFinished(); + g_assert_cmpstr(webkit_uri_scheme_request_get_path(test->m_uriSchemeRequest.get()), ==, "with"); + g_assert_cmpstr(webkit_uri_scheme_request_get_uri(test->m_uriSchemeRequest.get()), ==, "echo:with#fragment"); + echoHTML.reset(g_strdup_printf(kEchoHTMLFormat, webkit_uri_scheme_request_get_path(test->m_uriSchemeRequest.get()))); + mainResourceDataSize = 0; + mainResourceData = test->mainResourceData(mainResourceDataSize); + g_assert_cmpint(mainResourceDataSize, ==, strlen(echoHTML.get())); + g_assert(!strncmp(mainResourceData, echoHTML.get(), mainResourceDataSize)); + test->registerURISchemeHandler("nomime", kBarHTML, -1, 0); test->m_loadEvents.clear(); test->loadURI("nomime:foo-bar"); @@ -216,14 +331,35 @@ static void testWebContextURIScheme(URISchemeTest* test, gconstpointer) g_assert(!test->m_loadEvents.contains(LoadTrackingTest::ProvisionalLoadFailed)); g_assert(!test->m_loadEvents.contains(LoadTrackingTest::LoadFailed)); - test->registerURISchemeHandler("error", 0, 0, 0); + // Anything over 8192 bytes will get multiple calls to g_input_stream_read_async in + // WebKitURISchemeRequest when reading data, but we still need way more than that to + // ensure that we reach the load-committed state before failing, so we use an 8MB HTML. + String longHTMLContent = generateHTMLContent(8 * 1024 * 1024); + test->registerURISchemeHandler("error", longHTMLContent.utf8().data(), -1, "text/html"); test->m_loadEvents.clear(); test->loadURI("error:error"); test->waitUntilLoadFinished(); g_assert(test->m_loadEvents.contains(LoadTrackingTest::ProvisionalLoadFailed)); g_assert(test->m_loadFailed); g_assert_error(test->m_error.get(), g_quark_from_string(errorDomain), errorCode); - g_assert_cmpstr(test->m_error->message, ==, errorMessage); + g_assert_cmpstr(test->m_error->message, ==, genericErrorMessage); + + test->m_loadEvents.clear(); + test->loadURI("error:before-response"); + test->waitUntilLoadFinished(); + g_assert(test->m_loadEvents.contains(LoadTrackingTest::ProvisionalLoadFailed)); + g_assert(test->m_loadFailed); + g_assert_error(test->m_error.get(), g_quark_from_string(errorDomain), errorCode); + g_assert_cmpstr(test->m_error->message, ==, beforeReceiveResponseErrorMessage); + + test->m_loadEvents.clear(); + test->loadURI("error:after-first-chunk"); + test->finishOnCommittedAndWaitUntilLoadFinished(); + g_assert(!test->m_loadEvents.contains(LoadTrackingTest::ProvisionalLoadFailed)); + g_assert(test->m_loadEvents.contains(LoadTrackingTest::LoadFailed)); + g_assert(test->m_loadFailed); + g_assert_error(test->m_error.get(), g_quark_from_string(errorDomain), errorCode); + g_assert_cmpstr(test->m_error->message, ==, afterInitialChunkErrorMessage); test->registerURISchemeHandler("closed", 0, 0, 0); test->m_loadEvents.clear(); @@ -236,7 +372,7 @@ static void testWebContextURIScheme(URISchemeTest* test, gconstpointer) static void testWebContextSpellChecker(Test* test, gconstpointer) { - WebKitWebContext* webContext = webkit_web_context_get_default(); + WebKitWebContext* webContext = test->m_webContext.get(); // Check what happens if no spell checking language has been set. const gchar* const* currentLanguage = webkit_web_context_get_spell_checking_languages(webContext); @@ -289,7 +425,7 @@ static void testWebContextSpellChecker(Test* test, gconstpointer) static void testWebContextLanguages(WebViewTest* test, gconstpointer) { - static const char* expectedDefaultLanguage = "en"; + static const char* expectedDefaultLanguage = "en-US"; test->loadURI(kServer->getURIForPath("/").data()); test->waitUntilLoadFinished(); size_t mainResourceDataSize = 0; @@ -302,7 +438,7 @@ static void testWebContextLanguages(WebViewTest* test, gconstpointer) g_ptr_array_add(languages.get(), const_cast<gpointer>(static_cast<const void*>("ES_es"))); g_ptr_array_add(languages.get(), const_cast<gpointer>(static_cast<const void*>("dE"))); g_ptr_array_add(languages.get(), 0); - webkit_web_context_set_preferred_languages(webkit_web_context_get_default(), reinterpret_cast<const char* const*>(languages->pdata)); + webkit_web_context_set_preferred_languages(test->m_webContext.get(), reinterpret_cast<const char* const*>(languages->pdata)); static const char* expectedLanguages = "en, es-es;q=0.90, de;q=0.80"; test->loadURI(kServer->getURIForPath("/").data()); @@ -311,6 +447,32 @@ static void testWebContextLanguages(WebViewTest* test, gconstpointer) mainResourceData = test->mainResourceData(mainResourceDataSize); g_assert_cmpuint(mainResourceDataSize, ==, strlen(expectedLanguages)); g_assert(!strncmp(mainResourceData, expectedLanguages, mainResourceDataSize)); + + // When using the C locale, en-US should be used as default. + const char* cLanguage[] = { "C", nullptr }; + webkit_web_context_set_preferred_languages(test->m_webContext.get(), cLanguage); + GUniqueOutPtr<GError> error; + WebKitJavascriptResult* javascriptResult = test->runJavaScriptAndWaitUntilFinished("Intl.DateTimeFormat().resolvedOptions().locale", &error.outPtr()); + g_assert(javascriptResult); + g_assert(!error); + GUniquePtr<char> locale(WebViewTest::javascriptResultToCString(javascriptResult)); + g_assert_cmpstr(locale.get(), ==, expectedDefaultLanguage); + + // When using the POSIX locale, en-US should be used as default. + const char* posixLanguage[] = { "POSIX", nullptr }; + webkit_web_context_set_preferred_languages(test->m_webContext.get(), posixLanguage); + javascriptResult = test->runJavaScriptAndWaitUntilFinished("Intl.DateTimeFormat().resolvedOptions().locale", &error.outPtr()); + g_assert(javascriptResult); + g_assert(!error); + locale.reset(WebViewTest::javascriptResultToCString(javascriptResult)); + g_assert_cmpstr(locale.get(), ==, expectedDefaultLanguage); + + // An invalid locale should throw an exception. + const char* invalidLanguage[] = { "A", nullptr }; + webkit_web_context_set_preferred_languages(test->m_webContext.get(), invalidLanguage); + javascriptResult = test->runJavaScriptAndWaitUntilFinished("Intl.DateTimeFormat().resolvedOptions().locale", &error.outPtr()); + g_assert(!javascriptResult); + g_assert_error(error.get(), WEBKIT_JAVASCRIPT_ERROR, WEBKIT_JAVASCRIPT_ERROR_SCRIPT_FAILED); } static void serverCallback(SoupServer* server, SoupMessage* message, const char* path, GHashTable*, SoupClientContext*, gpointer) @@ -325,6 +487,16 @@ static void serverCallback(SoupServer* server, SoupMessage* message, const char* soup_message_set_status(message, SOUP_STATUS_OK); soup_message_body_append(message->response_body, SOUP_MEMORY_COPY, acceptLanguage, strlen(acceptLanguage)); soup_message_body_complete(message->response_body); + } else if (g_str_equal(path, "/empty")) { + const char* emptyHTML = "<html><body></body></html>"; + soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, emptyHTML, strlen(emptyHTML)); + soup_message_body_complete(message->response_body); + soup_message_set_status(message, SOUP_STATUS_OK); + } else if (g_str_equal(path, "/echoPort")) { + char* port = g_strdup_printf("%u", soup_server_get_port(server)); + soup_message_body_append(message->response_body, SOUP_MEMORY_TAKE, port, strlen(port)); + soup_message_body_complete(message->response_body); + soup_message_set_status(message, SOUP_STATUS_OK); } else soup_message_set_status(message, SOUP_STATUS_NOT_FOUND); } @@ -343,7 +515,7 @@ public: }; SecurityPolicyTest() - : m_manager(webkit_web_context_get_security_manager(webkit_web_context_get_default())) + : m_manager(webkit_web_context_get_security_manager(m_webContext.get())) { } @@ -406,17 +578,205 @@ static void testWebContextSecurityPolicy(SecurityPolicyTest* test, gconstpointer | SecurityPolicyTest::CORSEnabled | SecurityPolicyTest::EmptyDocument); } +static void consoleMessageReceivedCallback(WebKitUserContentManager*, WebKitJavascriptResult* message, WebKitJavascriptResult** result) +{ + g_assert(result); + g_assert(!*result); + *result = webkit_javascript_result_ref(message); +} + +static void testWebContextSecurityFileXHR(WebViewTest* test, gconstpointer) +{ + GUniquePtr<char> fileURL(g_strdup_printf("file://%s/simple.html", Test::getResourcesDir(Test::WebKit2Resources).data())); + test->loadURI(fileURL.get()); + test->waitUntilLoadFinished(); + + GUniquePtr<char> jsonURL(g_strdup_printf("file://%s/simple.json", Test::getResourcesDir().data())); + GUniquePtr<char> xhr(g_strdup_printf("var xhr = new XMLHttpRequest; xhr.open(\"GET\", \"%s\"); xhr.send();", jsonURL.get())); + + WebKitJavascriptResult* consoleMessage = nullptr; + webkit_user_content_manager_register_script_message_handler(test->m_userContentManager.get(), "console"); + g_signal_connect(test->m_userContentManager.get(), "script-message-received::console", G_CALLBACK(consoleMessageReceivedCallback), &consoleMessage); + + // By default file access is not allowed, this will show a console message with a cross-origin error. + GUniqueOutPtr<GError> error; + WebKitJavascriptResult* javascriptResult = test->runJavaScriptAndWaitUntilFinished(xhr.get(), &error.outPtr()); + g_assert(javascriptResult); + g_assert(!error); + g_assert(consoleMessage); + GUniquePtr<char> messageString(WebViewTest::javascriptResultToCString(consoleMessage)); + GRefPtr<GVariant> variant = g_variant_parse(G_VARIANT_TYPE("(uusus)"), messageString.get(), nullptr, nullptr, nullptr); + g_assert(variant.get()); + unsigned level; + const char* messageText; + g_variant_get(variant.get(), "(uu&su&s)", nullptr, &level, &messageText, nullptr, nullptr); + g_assert_cmpuint(level, ==, 3); // Console error message. + GUniquePtr<char> expectedErrorMessage(g_strdup_printf("XMLHttpRequest cannot load %s. Cross origin requests are only supported for HTTP.", jsonURL.get())); + g_assert_cmpstr(messageText, ==, expectedErrorMessage.get()); + webkit_javascript_result_unref(consoleMessage); + consoleMessage = nullptr; + level = 0; + messageText = nullptr; + variant = nullptr; + + // Allow file access from file URLs. + webkit_settings_set_allow_file_access_from_file_urls(webkit_web_view_get_settings(test->m_webView), TRUE); + test->loadURI(fileURL.get()); + test->waitUntilLoadFinished(); + javascriptResult = test->runJavaScriptAndWaitUntilFinished(xhr.get(), &error.outPtr()); + g_assert(javascriptResult); + g_assert(!error); + + // It isn't still possible to load file from an HTTP URL. + test->loadURI(kServer->getURIForPath("/").data()); + test->waitUntilLoadFinished(); + javascriptResult = test->runJavaScriptAndWaitUntilFinished(xhr.get(), &error.outPtr()); + g_assert(javascriptResult); + g_assert(!error); + g_assert(consoleMessage); + variant = g_variant_parse(G_VARIANT_TYPE("(uusus)"), messageString.get(), nullptr, nullptr, nullptr); + g_assert(variant.get()); + g_variant_get(variant.get(), "(uu&su&s)", nullptr, &level, &messageText, nullptr, nullptr); + g_assert_cmpuint(level, ==, 3); // Console error message. + g_assert_cmpstr(messageText, ==, expectedErrorMessage.get()); + webkit_javascript_result_unref(consoleMessage); + + g_signal_handlers_disconnect_matched(test->m_userContentManager.get(), G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, &consoleMessage); + webkit_user_content_manager_unregister_script_message_handler(test->m_userContentManager.get(), "console"); + + webkit_settings_set_allow_file_access_from_file_urls(webkit_web_view_get_settings(test->m_webView), FALSE); +} + +class ProxyTest : public WebViewTest { +public: + MAKE_GLIB_TEST_FIXTURE(ProxyTest); + + ProxyTest() + { + // This "proxy server" is actually just a different instance of the main + // test server (kServer), listening on a different port. Requests + // will not actually be proxied to kServer because proxyServer is not + // actually a proxy server. We're testing whether the proxy settings + // work, not whether we can write a soup proxy server. + m_proxyServer.run(serverCallback); + g_assert(m_proxyServer.baseURI()); + } + + CString loadURIAndGetMainResourceData(const char* uri) + { + loadURI(uri); + waitUntilLoadFinished(); + size_t dataSize = 0; + const char* data = mainResourceData(dataSize); + return CString(data, dataSize); + } + + GUniquePtr<char> proxyServerPortAsString() + { + GUniquePtr<char> port(g_strdup_printf("%u", soup_uri_get_port(m_proxyServer.baseURI()))); + return port; + } + + WebKitTestServer m_proxyServer; +}; + +static void ephemeralViewloadChanged(WebKitWebView* webView, WebKitLoadEvent loadEvent, WebViewTest* test) +{ + if (loadEvent != WEBKIT_LOAD_FINISHED) + return; + g_signal_handlers_disconnect_by_func(webView, reinterpret_cast<void*>(ephemeralViewloadChanged), test); + test->quitMainLoop(); +} + +static void testWebContextProxySettings(ProxyTest* test, gconstpointer) +{ + // Proxy URI is unset by default. Requests to kServer should be received by kServer. + GUniquePtr<char> serverPortAsString(g_strdup_printf("%u", soup_uri_get_port(kServer->baseURI()))); + auto mainResourceData = test->loadURIAndGetMainResourceData(kServer->getURIForPath("/echoPort").data()); + ASSERT_CMP_CSTRING(mainResourceData, ==, serverPortAsString.get()); + + // Set default proxy URI to point to proxyServer. Requests to kServer should be received by proxyServer instead. + GUniquePtr<char> proxyURI(soup_uri_to_string(test->m_proxyServer.baseURI(), FALSE)); + WebKitNetworkProxySettings* settings = webkit_network_proxy_settings_new(proxyURI.get(), nullptr); + webkit_web_context_set_network_proxy_settings(test->m_webContext.get(), WEBKIT_NETWORK_PROXY_MODE_CUSTOM, settings); + GUniquePtr<char> proxyServerPortAsString = test->proxyServerPortAsString(); + mainResourceData = test->loadURIAndGetMainResourceData(kServer->getURIForPath("/echoPort").data()); + ASSERT_CMP_CSTRING(mainResourceData, ==, proxyServerPortAsString.get()); + webkit_network_proxy_settings_free(settings); + + // Proxy settings also affect ephemeral web views. + GRefPtr<WebKitWebView> webView = WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW, + "web-context", test->m_webContext.get(), + "is-ephemeral", TRUE, + nullptr)); + g_assert(webkit_web_view_is_ephemeral(webView.get())); + g_assert(!webkit_web_context_is_ephemeral(webkit_web_view_get_context(webView.get()))); + + g_signal_connect(webView.get(), "load-changed", G_CALLBACK(ephemeralViewloadChanged), test); + webkit_web_view_load_uri(webView.get(), kServer->getURIForPath("/echoPort").data()); + g_main_loop_run(test->m_mainLoop); + WebKitWebResource* resource = webkit_web_view_get_main_resource(webView.get()); + g_assert(WEBKIT_IS_WEB_RESOURCE(resource)); + webkit_web_resource_get_data(resource, nullptr, [](GObject* object, GAsyncResult* result, gpointer userData) { + size_t dataSize; + GUniquePtr<char> data(reinterpret_cast<char*>(webkit_web_resource_get_data_finish(WEBKIT_WEB_RESOURCE(object), result, &dataSize, nullptr))); + g_assert(data); + auto* test = static_cast<ProxyTest*>(userData); + GUniquePtr<char> proxyServerPortAsString = test->proxyServerPortAsString(); + ASSERT_CMP_CSTRING(CString(data.get(), dataSize), ==, proxyServerPortAsString.get()); + test->quitMainLoop(); + }, test); + g_main_loop_run(test->m_mainLoop); + + // Remove the proxy. Requests to kServer should be received by kServer again. + webkit_web_context_set_network_proxy_settings(test->m_webContext.get(), WEBKIT_NETWORK_PROXY_MODE_NO_PROXY, nullptr); + mainResourceData = test->loadURIAndGetMainResourceData(kServer->getURIForPath("/echoPort").data()); + ASSERT_CMP_CSTRING(mainResourceData, ==, serverPortAsString.get()); + + // Use a default proxy uri, but ignoring requests to localhost. + static const char* ignoreHosts[] = { "localhost", nullptr }; + settings = webkit_network_proxy_settings_new(proxyURI.get(), ignoreHosts); + webkit_web_context_set_network_proxy_settings(test->m_webContext.get(), WEBKIT_NETWORK_PROXY_MODE_CUSTOM, settings); + mainResourceData = test->loadURIAndGetMainResourceData(kServer->getURIForPath("/echoPort").data()); + ASSERT_CMP_CSTRING(mainResourceData, ==, proxyServerPortAsString.get()); + GUniquePtr<char> localhostEchoPortURI(g_strdup_printf("http://localhost:%s/echoPort", serverPortAsString.get())); + mainResourceData = test->loadURIAndGetMainResourceData(localhostEchoPortURI.get()); + ASSERT_CMP_CSTRING(mainResourceData, ==, serverPortAsString.get()); + webkit_network_proxy_settings_free(settings); + + // Remove the proxy again to ensure next test is not using any previous values. + webkit_web_context_set_network_proxy_settings(test->m_webContext.get(), WEBKIT_NETWORK_PROXY_MODE_NO_PROXY, nullptr); + mainResourceData = test->loadURIAndGetMainResourceData(kServer->getURIForPath("/echoPort").data()); + ASSERT_CMP_CSTRING(mainResourceData, ==, serverPortAsString.get()); + + // Use scheme specific proxy instead of the default. + settings = webkit_network_proxy_settings_new(nullptr, nullptr); + webkit_network_proxy_settings_add_proxy_for_scheme(settings, "http", proxyURI.get()); + webkit_web_context_set_network_proxy_settings(test->m_webContext.get(), WEBKIT_NETWORK_PROXY_MODE_CUSTOM, settings); + mainResourceData = test->loadURIAndGetMainResourceData(kServer->getURIForPath("/echoPort").data()); + ASSERT_CMP_CSTRING(mainResourceData, ==, proxyServerPortAsString.get()); + webkit_network_proxy_settings_free(settings); + + // Reset to use the default resolver. + webkit_web_context_set_network_proxy_settings(test->m_webContext.get(), WEBKIT_NETWORK_PROXY_MODE_DEFAULT, nullptr); + mainResourceData = test->loadURIAndGetMainResourceData(kServer->getURIForPath("/echoPort").data()); + ASSERT_CMP_CSTRING(mainResourceData, ==, serverPortAsString.get()); +} + void beforeAll() { kServer = new WebKitTestServer(); kServer->run(serverCallback); Test::add("WebKitWebContext", "default-context", testWebContextDefault); + Test::add("WebKitWebContext", "ephemeral", testWebContextEphemeral); PluginsTest::add("WebKitWebContext", "get-plugins", testWebContextGetPlugins); URISchemeTest::add("WebKitWebContext", "uri-scheme", testWebContextURIScheme); Test::add("WebKitWebContext", "spell-checker", testWebContextSpellChecker); WebViewTest::add("WebKitWebContext", "languages", testWebContextLanguages); SecurityPolicyTest::add("WebKitSecurityManager", "security-policy", testWebContextSecurityPolicy); + WebViewTest::add("WebKitSecurityManager", "file-xhr", testWebContextSecurityFileXHR); + ProxyTest::add("WebKitWebContext", "proxy", testWebContextProxySettings); } void afterAll() diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitWebView.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitWebView.cpp index 334f8dafe..4e3d1d7e7 100644 --- a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitWebView.cpp +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitWebView.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2011 Igalia S.L. + * Copyright (C) 2014 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -18,38 +19,153 @@ */ #include "config.h" +#include "WebKitTestServer.h" #include "WebViewTest.h" #include <JavaScriptCore/JSStringRef.h> #include <JavaScriptCore/JSValueRef.h> #include <glib/gstdio.h> -#include <wtf/gobject/GRefPtr.h> +#include <wtf/glib/GRefPtr.h> -static void testWebViewDefaultContext(WebViewTest* test, gconstpointer) +class IsPlayingAudioWebViewTest : public WebViewTest { +public: + MAKE_GLIB_TEST_FIXTURE(IsPlayingAudioWebViewTest); + + static void isPlayingAudioChanged(GObject*, GParamSpec*, IsPlayingAudioWebViewTest* test) + { + g_signal_handlers_disconnect_by_func(test->m_webView, reinterpret_cast<void*>(isPlayingAudioChanged), test); + g_main_loop_quit(test->m_mainLoop); + } + + void waitUntilIsPlayingAudioChanged() + { + g_signal_connect(m_webView, "notify::is-playing-audio", G_CALLBACK(isPlayingAudioChanged), this); + g_main_loop_run(m_mainLoop); + } +}; + +static WebKitTestServer* gServer; + +static void testWebViewWebContext(WebViewTest* test, gconstpointer) { - g_assert(webkit_web_view_get_context(test->m_webView) == webkit_web_context_get_default()); + g_assert(webkit_web_view_get_context(test->m_webView) == test->m_webContext.get()); + g_assert(webkit_web_context_get_default() != test->m_webContext.get()); // Check that a web view created with g_object_new has the default context. - GRefPtr<WebKitWebView> webView = WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW, NULL)); + GRefPtr<WebKitWebView> webView = WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW, nullptr)); g_assert(webkit_web_view_get_context(webView.get()) == webkit_web_context_get_default()); + + // Check that a web view created with a related view has the related view context. + webView = WEBKIT_WEB_VIEW(webkit_web_view_new_with_related_view(test->m_webView)); + g_assert(webkit_web_view_get_context(webView.get()) == test->m_webContext.get()); + + // Check that a web context given as construct parameter is ignored if a related view is also provided. + webView = WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW, + "web-context", webkit_web_context_get_default(), "related-view", test->m_webView, nullptr)); + g_assert(webkit_web_view_get_context(webView.get()) == test->m_webContext.get()); +} + +static void testWebViewWebContextLifetime(WebViewTest* test, gconstpointer) +{ + WebKitWebContext* webContext = webkit_web_context_new(); + test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webContext)); + + GtkWidget* webView = webkit_web_view_new_with_context(webContext); + test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webView)); + + g_object_ref_sink(webView); + g_object_unref(webContext); + + // Check that the web view still has a valid context. + WebKitWebContext* tmpContext = webkit_web_view_get_context(WEBKIT_WEB_VIEW(webView)); + g_assert_true(WEBKIT_IS_WEB_CONTEXT(tmpContext)); + g_object_unref(webView); + + WebKitWebContext* webContext2 = webkit_web_context_new(); + test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webContext2)); + + GtkWidget* webView2 = webkit_web_view_new_with_context(webContext2); + test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webView2)); + + g_object_ref_sink(webView2); + g_object_unref(webView2); + + // Check that the context is still valid. + g_assert_true(WEBKIT_IS_WEB_CONTEXT(webContext2)); + g_object_unref(webContext2); +} + +static void ephemeralViewloadChanged(WebKitWebView* webView, WebKitLoadEvent loadEvent, WebViewTest* test) +{ + if (loadEvent != WEBKIT_LOAD_FINISHED) + return; + g_signal_handlers_disconnect_by_func(webView, reinterpret_cast<void*>(ephemeralViewloadChanged), test); + test->quitMainLoop(); +} + +static void testWebViewEphemeral(WebViewTest* test, gconstpointer) +{ + g_assert(!webkit_web_view_is_ephemeral(test->m_webView)); + g_assert(!webkit_web_context_is_ephemeral(webkit_web_view_get_context(test->m_webView))); + auto* manager = webkit_web_context_get_website_data_manager(test->m_webContext.get()); + g_assert(!webkit_website_data_manager_is_ephemeral(manager)); + g_assert(webkit_web_view_get_website_data_manager(test->m_webView) == manager); + webkit_website_data_manager_clear(manager, WEBKIT_WEBSITE_DATA_DISK_CACHE, 0, nullptr, [](GObject* manager, GAsyncResult* result, gpointer userData) { + webkit_website_data_manager_clear_finish(WEBKIT_WEBSITE_DATA_MANAGER(manager), result, nullptr); + static_cast<WebViewTest*>(userData)->quitMainLoop(); + }, test); + g_main_loop_run(test->m_mainLoop); + + // A WebView on a non ephemeral context can be ephemeral. + GRefPtr<WebKitWebView> webView = WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW, + "web-context", webkit_web_view_get_context(test->m_webView), + "is-ephemeral", TRUE, + nullptr)); + g_assert(webkit_web_view_is_ephemeral(webView.get())); + g_assert(!webkit_web_context_is_ephemeral(webkit_web_view_get_context(webView.get()))); + g_assert(webkit_web_view_get_website_data_manager(webView.get()) != manager); + + g_signal_connect(webView.get(), "load-changed", G_CALLBACK(ephemeralViewloadChanged), test); + webkit_web_view_load_uri(webView.get(), gServer->getURIForPath("/").data()); + g_main_loop_run(test->m_mainLoop); + + // Disk cache delays the storing of initial resources for 1 second to avoid + // affecting early page load. So, wait 1 second here to make sure resources + // have already been stored. + test->wait(1); + + webkit_website_data_manager_fetch(manager, WEBKIT_WEBSITE_DATA_DISK_CACHE, nullptr, [](GObject* manager, GAsyncResult* result, gpointer userData) { + auto* test = static_cast<WebViewTest*>(userData); + g_assert(!webkit_website_data_manager_fetch_finish(WEBKIT_WEBSITE_DATA_MANAGER(manager), result, nullptr)); + test->quitMainLoop(); + }, test); + g_main_loop_run(test->m_mainLoop); } static void testWebViewCustomCharset(WebViewTest* test, gconstpointer) { + test->loadURI(gServer->getURIForPath("/").data()); + test->waitUntilLoadFinished(); g_assert(!webkit_web_view_get_custom_charset(test->m_webView)); webkit_web_view_set_custom_charset(test->m_webView, "utf8"); + // Changing the charset reloads the page, so wait until reloaded. + test->waitUntilLoadFinished(); g_assert_cmpstr(webkit_web_view_get_custom_charset(test->m_webView), ==, "utf8"); - // Go back to the default charset. - webkit_web_view_set_custom_charset(test->m_webView, 0); + + // Go back to the default charset and wait until reloaded. + webkit_web_view_set_custom_charset(test->m_webView, nullptr); + test->waitUntilLoadFinished(); g_assert(!webkit_web_view_get_custom_charset(test->m_webView)); } static void testWebViewSettings(WebViewTest* test, gconstpointer) { WebKitSettings* defaultSettings = webkit_web_view_get_settings(test->m_webView); + test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(defaultSettings)); g_assert(defaultSettings); g_assert(webkit_settings_get_enable_javascript(defaultSettings)); GRefPtr<WebKitSettings> newSettings = adoptGRef(webkit_settings_new()); + test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(newSettings.get())); g_object_set(G_OBJECT(newSettings.get()), "enable-javascript", FALSE, NULL); webkit_web_view_set_settings(test->m_webView, newSettings.get()); @@ -63,10 +179,15 @@ static void testWebViewSettings(WebViewTest* test, gconstpointer) g_assert(webkit_web_view_get_settings(WEBKIT_WEB_VIEW(webView2.get())) == settings); GRefPtr<WebKitSettings> newSettings2 = adoptGRef(webkit_settings_new()); + test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(newSettings2.get())); webkit_web_view_set_settings(WEBKIT_WEB_VIEW(webView2.get()), newSettings2.get()); settings = webkit_web_view_get_settings(WEBKIT_WEB_VIEW(webView2.get())); g_assert(settings == newSettings2.get()); g_assert(webkit_settings_get_enable_javascript(settings)); + + GRefPtr<GtkWidget> webView3 = webkit_web_view_new_with_settings(newSettings2.get()); + test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webView3.get())); + g_assert(webkit_web_view_get_settings(WEBKIT_WEB_VIEW(webView3.get())) == newSettings2.get()); } static void testWebViewZoomLevel(WebViewTest* test, gconstpointer) @@ -407,7 +528,7 @@ static void testWebViewSave(SaveWebViewTest* test, gconstpointer) gchar buffer[512] = { 0 }; gssize readBytes = 0; gssize totalBytesFromStream = 0; - while (readBytes = g_input_stream_read(test->m_inputStream.get(), &buffer, 512, 0, &error.outPtr())) { + while ((readBytes = g_input_stream_read(test->m_inputStream.get(), &buffer, 512, 0, &error.outPtr()))) { g_assert(!error); totalBytesFromStream += readBytes; } @@ -417,28 +538,7 @@ static void testWebViewSave(SaveWebViewTest* test, gconstpointer) g_assert_cmpint(g_file_info_get_size(fileInfo.get()), ==, totalBytesFromStream); } -static void testWebViewMode(WebViewTest* test, gconstpointer) -{ - static const char* indexHTML = "<html><body><p>Test Web View Mode</p></body></html>"; - - // Web mode. - g_assert_cmpuint(webkit_web_view_get_view_mode(test->m_webView), ==, WEBKIT_VIEW_MODE_WEB); - test->loadHtml(indexHTML, 0); - test->waitUntilLoadFinished(); - WebKitJavascriptResult* javascriptResult = test->runJavaScriptAndWaitUntilFinished("window.document.body.textContent;", 0); - GUniquePtr<char> valueString(WebViewTest::javascriptResultToCString(javascriptResult)); - g_assert_cmpstr(valueString.get(), ==, "Test Web View Mode"); - - // Source mode. - webkit_web_view_set_view_mode(test->m_webView, WEBKIT_VIEW_MODE_SOURCE); - test->loadHtml(indexHTML, 0); - test->waitUntilLoadFinished(); - javascriptResult = test->runJavaScriptAndWaitUntilFinished("window.document.body.textContent;", 0); - valueString.reset(WebViewTest::javascriptResultToCString(javascriptResult)); - g_assert_cmpstr(valueString.get(), ==, indexHTML); -} - -// To test page visibility API. Currently only 'visible' and 'hidden' states are implemented fully in WebCore. +// To test page visibility API. Currently only 'visible', 'hidden' and 'prerender' states are implemented fully in WebCore. // See also http://www.w3.org/TR/2011/WD-page-visibility-20110602/ and https://developers.google.com/chrome/whitepapers/pagevisibility static void testWebViewPageVisibility(WebViewTest* test, gconstpointer) { @@ -453,7 +553,7 @@ static void testWebViewPageVisibility(WebViewTest* test, gconstpointer) "</body></html>", 0); - // Wait untill the page is loaded. Initial visibility should be 'hidden'. + // Wait until the page is loaded. Initial visibility should be 'prerender'. test->waitUntilLoadFinished(); GUniqueOutPtr<GError> error; @@ -461,7 +561,7 @@ static void testWebViewPageVisibility(WebViewTest* test, gconstpointer) g_assert(javascriptResult); g_assert(!error.get()); GUniquePtr<char> valueString(WebViewTest::javascriptResultToCString(javascriptResult)); - g_assert_cmpstr(valueString.get(), ==, "hidden"); + g_assert_cmpstr(valueString.get(), ==, "prerender"); javascriptResult = test->runJavaScriptAndWaitUntilFinished("document.hidden;", &error.outPtr()); g_assert(javascriptResult); @@ -564,15 +664,404 @@ static void testWebViewSnapshot(SnapshotWebViewTest* test, gconstpointer) g_assert_cmpint(cairo_image_surface_get_width(surface1), ==, cairo_image_surface_get_width(surface2)); g_assert_cmpint(cairo_image_surface_get_height(surface1), ==, cairo_image_surface_get_height(surface2)); g_assert(!Test::cairoSurfacesEqual(surface1, surface2)); + + // Get a snpashot with a transparent background, the result must be different. + surface2 = test->getSnapshotAndWaitUntilReady(WEBKIT_SNAPSHOT_REGION_VISIBLE, WEBKIT_SNAPSHOT_OPTIONS_TRANSPARENT_BACKGROUND); + g_assert_cmpuint(cairo_surface_get_type(surface2), ==, CAIRO_SURFACE_TYPE_IMAGE); + g_assert_cmpint(cairo_image_surface_get_width(surface1), ==, cairo_image_surface_get_width(surface2)); + g_assert_cmpint(cairo_image_surface_get_height(surface1), ==, cairo_image_surface_get_height(surface2)); + g_assert(!Test::cairoSurfacesEqual(surface1, surface2)); cairo_surface_destroy(surface1); // Test that cancellation works. g_assert(test->getSnapshotAndCancel()); } +class NotificationWebViewTest: public WebViewTest { +public: + MAKE_GLIB_TEST_FIXTURE_WITH_SETUP_TEARDOWN(NotificationWebViewTest, setup, teardown); + + static void setup() + { + WebViewTest::shouldInitializeWebViewInConstructor = false; + } + + static void teardown() + { + WebViewTest::shouldInitializeWebViewInConstructor = true; + } + + enum NotificationEvent { + None, + Permission, + Shown, + Clicked, + OnClicked, + Closed, + OnClosed, + }; + + static gboolean permissionRequestCallback(WebKitWebView*, WebKitPermissionRequest *request, NotificationWebViewTest* test) + { + g_assert(WEBKIT_IS_NOTIFICATION_PERMISSION_REQUEST(request)); + g_assert(test->m_isExpectingPermissionRequest); + test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(request)); + + test->m_event = Permission; + + webkit_permission_request_allow(request); + + g_main_loop_quit(test->m_mainLoop); + + return TRUE; + } + + static gboolean notificationClosedCallback(WebKitNotification* notification, NotificationWebViewTest* test) + { + g_assert(test->m_notification == notification); + test->m_notification = nullptr; + test->m_event = Closed; + if (g_main_loop_is_running(test->m_mainLoop)) + g_main_loop_quit(test->m_mainLoop); + return TRUE; + } + + static gboolean notificationClickedCallback(WebKitNotification* notification, NotificationWebViewTest* test) + { + g_assert(test->m_notification == notification); + test->m_event = Clicked; + return TRUE; + } + + static gboolean showNotificationCallback(WebKitWebView*, WebKitNotification* notification, NotificationWebViewTest* test) + { + g_assert(!test->m_notification); + test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(notification)); + test->m_notification = notification; + g_signal_connect(notification, "closed", G_CALLBACK(notificationClosedCallback), test); + g_signal_connect(notification, "clicked", G_CALLBACK(notificationClickedCallback), test); + test->m_event = Shown; + g_main_loop_quit(test->m_mainLoop); + return TRUE; + } + + static void notificationsMessageReceivedCallback(WebKitUserContentManager* userContentManager, WebKitJavascriptResult* javascriptResult, NotificationWebViewTest* test) + { + GUniquePtr<char> valueString(WebViewTest::javascriptResultToCString(javascriptResult)); + + if (g_str_equal(valueString.get(), "clicked")) + test->m_event = OnClicked; + else if (g_str_equal(valueString.get(), "closed")) + test->m_event = OnClosed; + + g_main_loop_quit(test->m_mainLoop); + } + + void initialize() + { + initializeWebView(); + g_signal_connect(m_webView, "permission-request", G_CALLBACK(permissionRequestCallback), this); + g_signal_connect(m_webView, "show-notification", G_CALLBACK(showNotificationCallback), this); + webkit_user_content_manager_register_script_message_handler(m_userContentManager.get(), "notifications"); + g_signal_connect(m_userContentManager.get(), "script-message-received::notifications", G_CALLBACK(notificationsMessageReceivedCallback), this); + } + + ~NotificationWebViewTest() + { + g_signal_handlers_disconnect_matched(m_webView, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this); + g_signal_handlers_disconnect_matched(m_userContentManager.get(), G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this); + webkit_user_content_manager_unregister_script_message_handler(m_userContentManager.get(), "notifications"); + } + + bool hasPermission() + { + auto* result = runJavaScriptAndWaitUntilFinished("Notification.permission;", nullptr); + g_assert(result); + GUniquePtr<char> value(javascriptResultToCString(result)); + return !g_strcmp0(value.get(), "granted"); + } + + void requestPermissionAndWaitUntilGiven() + { + m_event = None; + m_isExpectingPermissionRequest = true; + webkit_web_view_run_javascript(m_webView, "Notification.requestPermission();", nullptr, nullptr, nullptr); + g_main_loop_run(m_mainLoop); + } + + void requestNotificationAndWaitUntilShown(const char* title, const char* body) + { + m_event = None; + + GUniquePtr<char> jscode(g_strdup_printf("n = new Notification('%s', { body: '%s'});", title, body)); + webkit_web_view_run_javascript(m_webView, jscode.get(), nullptr, nullptr, nullptr); + + g_main_loop_run(m_mainLoop); + } + + void requestNotificationAndWaitUntilShown(const char* title, const char* body, const char* tag) + { + m_event = None; + + GUniquePtr<char> jscode(g_strdup_printf("n = new Notification('%s', { body: '%s', tag: '%s'});", title, body, tag)); + webkit_web_view_run_javascript(m_webView, jscode.get(), nullptr, nullptr, nullptr); + + g_main_loop_run(m_mainLoop); + } + + void clickNotificationAndWaitUntilClicked() + { + m_event = None; + runJavaScriptAndWaitUntilFinished("n.onclick = function() { window.webkit.messageHandlers.notifications.postMessage('clicked'); }", nullptr); + webkit_notification_clicked(m_notification); + g_assert(m_event == Clicked); + g_main_loop_run(m_mainLoop); + } + + void closeNotificationAndWaitUntilClosed() + { + m_event = None; + webkit_web_view_run_javascript(m_webView, "n.close()", nullptr, nullptr, nullptr); + g_main_loop_run(m_mainLoop); + } + + void closeNotificationAndWaitUntilOnClosed() + { + g_assert(m_notification); + m_event = None; + runJavaScriptAndWaitUntilFinished("n.onclose = function() { window.webkit.messageHandlers.notifications.postMessage('closed'); }", nullptr); + webkit_notification_close(m_notification); + g_assert(m_event == Closed); + g_main_loop_run(m_mainLoop); + } + + NotificationEvent m_event { None }; + WebKitNotification* m_notification { nullptr }; + bool m_isExpectingPermissionRequest { false }; + bool m_hasPermission { false }; +}; + +static void testWebViewNotification(NotificationWebViewTest* test, gconstpointer) +{ + test->initialize(); + + // Notifications don't work with local or special schemes. + test->loadURI(gServer->getURIForPath("/").data()); + test->waitUntilLoadFinished(); + g_assert(!test->hasPermission()); + + test->requestPermissionAndWaitUntilGiven(); + g_assert(test->m_event == NotificationWebViewTest::Permission); + g_assert(test->hasPermission()); + + static const char* title = "This is a notification"; + static const char* body = "This is the body."; + static const char* tag = "This is the tag."; + test->requestNotificationAndWaitUntilShown(title, body, tag); + + g_assert(test->m_event == NotificationWebViewTest::Shown); + g_assert(test->m_notification); + g_assert_cmpstr(webkit_notification_get_title(test->m_notification), ==, title); + g_assert_cmpstr(webkit_notification_get_body(test->m_notification), ==, body); + g_assert_cmpstr(webkit_notification_get_tag(test->m_notification), ==, tag); + + test->clickNotificationAndWaitUntilClicked(); + g_assert(test->m_event == NotificationWebViewTest::OnClicked); + + test->closeNotificationAndWaitUntilClosed(); + g_assert(test->m_event == NotificationWebViewTest::Closed); + + test->requestNotificationAndWaitUntilShown(title, body); + g_assert(test->m_event == NotificationWebViewTest::Shown); + g_assert_cmpstr(webkit_notification_get_tag(test->m_notification), ==, nullptr); + + test->closeNotificationAndWaitUntilOnClosed(); + g_assert(test->m_event == NotificationWebViewTest::OnClosed); + + // The first notification should be closed automatically because the tag is + // the same. It will crash in showNotificationCallback on failure. + test->requestNotificationAndWaitUntilShown(title, body, tag); + test->requestNotificationAndWaitUntilShown(title, body, tag); + g_assert(test->m_event == NotificationWebViewTest::Shown); + + // Notification should be closed when navigating to a different webpage. + test->loadURI(gServer->getURIForPath("/").data()); + test->waitUntilLoadFinished(); + g_assert(test->m_event == NotificationWebViewTest::Closed); +} + +static void setInitialNotificationPermissionsAllowedCallback(WebKitWebContext* context, NotificationWebViewTest* test) +{ + GUniquePtr<char> baseURI(soup_uri_to_string(gServer->baseURI(), FALSE)); + GList* allowedOrigins = g_list_prepend(nullptr, webkit_security_origin_new_for_uri(baseURI.get())); + webkit_web_context_initialize_notification_permissions(test->m_webContext.get(), allowedOrigins, nullptr); + g_list_free_full(allowedOrigins, reinterpret_cast<GDestroyNotify>(webkit_security_origin_unref)); +} + +static void setInitialNotificationPermissionsDisallowedCallback(WebKitWebContext* context, NotificationWebViewTest* test) +{ + GUniquePtr<char> baseURI(soup_uri_to_string(gServer->baseURI(), FALSE)); + GList* disallowedOrigins = g_list_prepend(nullptr, webkit_security_origin_new_for_uri(baseURI.get())); + webkit_web_context_initialize_notification_permissions(test->m_webContext.get(), nullptr, disallowedOrigins); + g_list_free_full(disallowedOrigins, reinterpret_cast<GDestroyNotify>(webkit_security_origin_unref)); +} + +static void testWebViewNotificationInitialPermissionAllowed(NotificationWebViewTest* test, gconstpointer) +{ + g_signal_connect(test->m_webContext.get(), "initialize-notification-permissions", G_CALLBACK(setInitialNotificationPermissionsAllowedCallback), test); + test->initialize(); + + test->loadURI(gServer->getURIForPath("/").data()); + test->waitUntilLoadFinished(); + g_assert(test->hasPermission()); + + test->requestNotificationAndWaitUntilShown("This is a notification", "This is the body."); + g_assert(test->m_event == NotificationWebViewTest::Shown); +} + +static void testWebViewNotificationInitialPermissionDisallowed(NotificationWebViewTest* test, gconstpointer) +{ + g_signal_connect(test->m_webContext.get(), "initialize-notification-permissions", G_CALLBACK(setInitialNotificationPermissionsDisallowedCallback), test); + test->initialize(); + + test->loadURI(gServer->getURIForPath("/").data()); + test->waitUntilLoadFinished(); + g_assert(!test->hasPermission()); +} + +static void testWebViewIsPlayingAudio(IsPlayingAudioWebViewTest* test, gconstpointer) +{ + // The web view must be realized for the video to start playback and + // trigger changes in WebKitWebView::is-playing-audio. + test->showInWindowAndWaitUntilMapped(GTK_WINDOW_TOPLEVEL); + + // Initially, web views should always report no audio being played. + g_assert(!webkit_web_view_is_playing_audio(test->m_webView)); + + GUniquePtr<char> resourcePath(g_build_filename(Test::getResourcesDir(Test::WebKit2Resources).data(), "file-with-video.html", nullptr)); + GUniquePtr<char> resourceURL(g_filename_to_uri(resourcePath.get(), nullptr, nullptr)); + webkit_web_view_load_uri(test->m_webView, resourceURL.get()); + test->waitUntilLoadFinished(); + g_assert(!webkit_web_view_is_playing_audio(test->m_webView)); + + test->runJavaScriptAndWaitUntilFinished("playVideo();", nullptr); + if (!webkit_web_view_is_playing_audio(test->m_webView)) + test->waitUntilIsPlayingAudioChanged(); + g_assert(webkit_web_view_is_playing_audio(test->m_webView)); + + // Pause the video, and check again. + test->runJavaScriptAndWaitUntilFinished("document.getElementById('test-video').pause();", nullptr); + if (webkit_web_view_is_playing_audio(test->m_webView)) + test->waitUntilIsPlayingAudioChanged(); + g_assert(!webkit_web_view_is_playing_audio(test->m_webView)); +} + +static void testWebViewBackgroundColor(WebViewTest* test, gconstpointer) +{ + // White is the default background. + GdkRGBA rgba; + webkit_web_view_get_background_color(test->m_webView, &rgba); + g_assert_cmpfloat(rgba.red, ==, 1); + g_assert_cmpfloat(rgba.green, ==, 1); + g_assert_cmpfloat(rgba.blue, ==, 1); + g_assert_cmpfloat(rgba.alpha, ==, 1); + + // Set a different (semi-transparent red). + rgba.red = 1; + rgba.green = 0; + rgba.blue = 0; + rgba.alpha = 0.5; + webkit_web_view_set_background_color(test->m_webView, &rgba); + g_assert_cmpfloat(rgba.red, ==, 1); + g_assert_cmpfloat(rgba.green, ==, 0); + g_assert_cmpfloat(rgba.blue, ==, 0); + g_assert_cmpfloat(rgba.alpha, ==, 0.5); + + // The actual rendering can't be tested using unit tests, use + // MiniBrowser --bg-color="<color-value>" for manually testing this API. +} + +static void testWebViewPreferredSize(WebViewTest* test, gconstpointer) +{ + test->loadHtml("<html style='width: 325px; height: 615px'></html>", nullptr); + test->waitUntilLoadFinished(); + test->showInWindowAndWaitUntilMapped(); + GtkRequisition minimunSize, naturalSize; + gtk_widget_get_preferred_size(GTK_WIDGET(test->m_webView), &minimunSize, &naturalSize); + g_assert_cmpint(minimunSize.width, ==, 0); + g_assert_cmpint(minimunSize.height, ==, 0); + g_assert_cmpint(naturalSize.width, ==, 325); + g_assert_cmpint(naturalSize.height, ==, 615); +} + +class WebViewTitleTest: public WebViewTest { +public: + MAKE_GLIB_TEST_FIXTURE(WebViewTitleTest); + + static void titleChangedCallback(WebKitWebView* view, GParamSpec*, WebViewTitleTest* test) + { + test->m_webViewTitles.append(webkit_web_view_get_title(view)); + } + + WebViewTitleTest() + { + g_signal_connect(m_webView, "notify::title", G_CALLBACK(titleChangedCallback), this); + } + + Vector<CString> m_webViewTitles; +}; + +static void testWebViewTitleChange(WebViewTitleTest* test, gconstpointer) +{ + g_assert_cmpint(test->m_webViewTitles.size(), ==, 0); + + test->loadHtml("<head><title>Page Title</title></head>", nullptr); + test->waitUntilLoadFinished(); + g_assert_cmpint(test->m_webViewTitles.size(), ==, 1); + g_assert_cmpstr(test->m_webViewTitles[0].data(), ==, "Page Title"); + + test->loadHtml("<head><title>Another Page Title</title></head>", nullptr); + test->waitUntilLoadFinished(); + g_assert_cmpint(test->m_webViewTitles.size(), ==, 3); + /* Page title should be immediately unset when loading a new page. */ + g_assert_cmpstr(test->m_webViewTitles[1].data(), ==, ""); + g_assert_cmpstr(test->m_webViewTitles[2].data(), ==, "Another Page Title"); + + test->loadHtml("<p>This page has no title!</p>", nullptr); + test->waitUntilLoadFinished(); + g_assert_cmpint(test->m_webViewTitles.size(), ==, 4); + g_assert_cmpstr(test->m_webViewTitles[3].data(), ==, ""); + + test->loadHtml("<script>document.title = 'one'; document.title = 'two'; document.title = 'three';</script>", nullptr); + test->waitUntilLoadFinished(); + g_assert_cmpint(test->m_webViewTitles.size(), ==, 7); + g_assert_cmpstr(test->m_webViewTitles[4].data(), ==, "one"); + g_assert_cmpstr(test->m_webViewTitles[5].data(), ==, "two"); + g_assert_cmpstr(test->m_webViewTitles[6].data(), ==, "three"); +} + +static void serverCallback(SoupServer* server, SoupMessage* message, const char* path, GHashTable*, SoupClientContext*, gpointer) +{ + if (message->method != SOUP_METHOD_GET) { + soup_message_set_status(message, SOUP_STATUS_NOT_IMPLEMENTED); + return; + } + + if (g_str_equal(path, "/")) { + soup_message_set_status(message, SOUP_STATUS_OK); + soup_message_body_complete(message->response_body); + } else + soup_message_set_status(message, SOUP_STATUS_NOT_FOUND); +} + void beforeAll() { - WebViewTest::add("WebKitWebView", "default-context", testWebViewDefaultContext); + gServer = new WebKitTestServer(); + gServer->run(serverCallback); + + WebViewTest::add("WebKitWebView", "web-context", testWebViewWebContext); + WebViewTest::add("WebKitWebView", "web-context-lifetime", testWebViewWebContextLifetime); + WebViewTest::add("WebKitWebView", "ephemeral", testWebViewEphemeral); WebViewTest::add("WebKitWebView", "custom-charset", testWebViewCustomCharset); WebViewTest::add("WebKitWebView", "settings", testWebViewSettings); WebViewTest::add("WebKitWebView", "zoom-level", testWebViewZoomLevel); @@ -581,9 +1070,15 @@ void beforeAll() WebViewTest::add("WebKitWebView", "can-show-mime-type", testWebViewCanShowMIMEType); FormClientTest::add("WebKitWebView", "submit-form", testWebViewSubmitForm); SaveWebViewTest::add("WebKitWebView", "save", testWebViewSave); - WebViewTest::add("WebKitWebView", "view-mode", testWebViewMode); SnapshotWebViewTest::add("WebKitWebView", "snapshot", testWebViewSnapshot); WebViewTest::add("WebKitWebView", "page-visibility", testWebViewPageVisibility); + NotificationWebViewTest::add("WebKitWebView", "notification", testWebViewNotification); + NotificationWebViewTest::add("WebKitWebView", "notification-initial-permission-allowed", testWebViewNotificationInitialPermissionAllowed); + NotificationWebViewTest::add("WebKitWebView", "notification-initial-permission-disallowed", testWebViewNotificationInitialPermissionDisallowed); + IsPlayingAudioWebViewTest::add("WebKitWebView", "is-playing-audio", testWebViewIsPlayingAudio); + WebViewTest::add("WebKitWebView", "background-color", testWebViewBackgroundColor); + WebViewTest::add("WebKitWebView", "preferred-size", testWebViewPreferredSize); + WebViewTitleTest::add("WebKitWebView", "title-change", testWebViewTitleChange); } void afterAll() diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitWebViewGroup.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitWebViewGroup.cpp deleted file mode 100644 index 448abd2fe..000000000 --- a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitWebViewGroup.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright (C) 2013 Igalia S.L. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2,1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include "config.h" - -#include "WebKitTestServer.h" -#include "WebViewTest.h" -#include <cstdarg> -#include <gtk/gtk.h> -#include <webkit2/webkit2.h> -#include <wtf/gobject/GRefPtr.h> - -static WebKitTestServer* kServer; - -// These are all here so that they can be changed easily, if necessary. -static const char* kStyleSheetHTML = "<html><div id=\"styledElement\">Sweet stylez!</div></html>"; -static const char* kInjectedStyleSheet = "#styledElement { font-weight: bold; }"; -static const char* kStyleSheetTestScript = "getComputedStyle(document.getElementById('styledElement'))['font-weight']"; -static const char* kStyleSheetTestScriptResult = "bold"; - -static void testWebViewGroupDefault(Test* test, gconstpointer) -{ - // Default group is shared by all WebViews by default. - GRefPtr<WebKitWebView> webView1 = WEBKIT_WEB_VIEW(webkit_web_view_new()); - GRefPtr<WebKitWebView> webView2 = WEBKIT_WEB_VIEW(webkit_web_view_new()); - g_assert(webkit_web_view_get_group(webView1.get()) == webkit_web_view_get_group(webView2.get())); - - // Settings are shared by all web view in the same group. - g_assert(webkit_web_view_get_settings(webView1.get()) == webkit_web_view_get_settings(webView2.get())); - g_assert(webkit_web_view_get_settings(webView1.get()) == webkit_web_view_group_get_settings(webkit_web_view_get_group(webView2.get()))); -} - -static void testWebViewGroupNewGroup(Test* test, gconstpointer) -{ - // Passing 0 as group name generates the name automatically. - GRefPtr<WebKitWebViewGroup> viewGroup1 = adoptGRef(webkit_web_view_group_new(0)); - test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(viewGroup1.get())); - g_assert(webkit_web_view_group_get_name(viewGroup1.get())); - - // New group with a given name. - GRefPtr<WebKitWebViewGroup> viewGroup2 = adoptGRef(webkit_web_view_group_new("TestGroup2")); - test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(viewGroup2.get())); - g_assert_cmpstr(webkit_web_view_group_get_name(viewGroup2.get()), ==, "TestGroup2"); - g_assert_cmpstr(webkit_web_view_group_get_name(viewGroup2.get()), !=, webkit_web_view_group_get_name(viewGroup1.get())); - - // Every group has its own settings. - g_assert(webkit_web_view_group_get_settings(viewGroup1.get()) != webkit_web_view_group_get_settings(viewGroup2.get())); -} - -static void testWebViewNewWithGroup(Test* test, gconstpointer) -{ - GRefPtr<WebKitWebViewGroup> viewGroup1 = adoptGRef(webkit_web_view_group_new("TestGroup1")); - test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(viewGroup1.get())); - GRefPtr<WebKitWebView> webView1 = WEBKIT_WEB_VIEW(webkit_web_view_new_with_group(viewGroup1.get())); - g_assert(webkit_web_view_get_group(webView1.get()) == viewGroup1.get()); - - GRefPtr<WebKitWebView> webView2 = WEBKIT_WEB_VIEW(webkit_web_view_new()); - g_assert(webkit_web_view_get_group(webView2.get()) != viewGroup1.get()); - - // Settings should be different for views in different groups. - g_assert(webkit_web_view_get_settings(webView1.get()) != webkit_web_view_get_settings(webView2.get())); -} - -static void testWebViewGroupSettings(Test* test, gconstpointer) -{ - GRefPtr<WebKitWebViewGroup> viewGroup1 = adoptGRef(webkit_web_view_group_new("TestGroup1")); - test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(viewGroup1.get())); - GRefPtr<WebKitSettings> newSettings = adoptGRef(webkit_settings_new_with_settings("enable-javascript", FALSE, nullptr)); - test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(newSettings.get())); - webkit_web_view_group_set_settings(viewGroup1.get(), newSettings.get()); - g_assert(webkit_web_view_group_get_settings(viewGroup1.get()) == newSettings.get()); - - GRefPtr<WebKitWebView> webView1 = WEBKIT_WEB_VIEW(webkit_web_view_new_with_group(viewGroup1.get())); - GRefPtr<WebKitWebView> webView2 = WEBKIT_WEB_VIEW(webkit_web_view_new()); - WebKitSettings* webView1Settings = webkit_web_view_get_settings(webView1.get()); - WebKitSettings* webView2Settings = webkit_web_view_get_settings(webView2.get()); - g_assert(webView1Settings != webView2Settings); - g_assert(webkit_settings_get_enable_javascript(webView1Settings) != webkit_settings_get_enable_javascript(webView2Settings)); - - webkit_web_view_set_settings(webView1.get(), webView2Settings); - g_assert(webkit_web_view_get_settings(webView1.get()) == webView2Settings); - g_assert(webkit_web_view_group_get_settings(webkit_web_view_get_group(webView1.get())) == webView2Settings); -} - -static bool isStyleSheetInjectedForURLAtPath(WebViewTest* test, const char* path) -{ - test->loadURI(kServer->getURIForPath(path).data()); - test->waitUntilLoadFinished(); - - GUniqueOutPtr<GError> error; - WebKitJavascriptResult* javascriptResult = test->runJavaScriptAndWaitUntilFinished(kStyleSheetTestScript, &error.outPtr()); - g_assert(javascriptResult); - g_assert(!error.get()); - - GUniquePtr<char> resultString(WebViewTest::javascriptResultToCString(javascriptResult)); - return !g_strcmp0(resultString.get(), kStyleSheetTestScriptResult); -} - -static void fillURLListFromPaths(char** list, const char* path, ...) -{ - va_list argumentList; - va_start(argumentList, path); - - int i = 0; - while (path) { - // FIXME: We must use a wildcard for the host here until http://wkbug.com/112476 is fixed. - // Until that time patterns with port numbers in them will not properly match URLs with port numbers. - list[i++] = g_strdup_printf("http://*/%s*", path); - path = va_arg(argumentList, const char*); - } -} - -static void removeOldInjectedStyleSheetsAndResetLists(WebKitWebViewGroup* group, char** whitelist, char** blacklist) -{ - webkit_web_view_group_remove_all_user_style_sheets(group); - - while (*whitelist) { - g_free(*whitelist); - *whitelist = 0; - whitelist++; - } - - while (*blacklist) { - g_free(*blacklist); - *blacklist = 0; - blacklist++; - } -} - -static void testWebViewGroupInjectedStyleSheet(WebViewTest* test, gconstpointer) -{ - WebKitWebViewGroup* group = webkit_web_view_get_group(test->m_webView); - char* whitelist[3] = { 0, 0, 0 }; - char* blacklist[3] = { 0, 0, 0 }; - - removeOldInjectedStyleSheetsAndResetLists(group, whitelist, blacklist); - - // Without a whitelist or a blacklist all URLs should have the injected style sheet. - static const char* randomPath = "somerandompath"; - g_assert(!isStyleSheetInjectedForURLAtPath(test, randomPath)); - webkit_web_view_group_add_user_style_sheet(group, kInjectedStyleSheet, 0, 0, 0, WEBKIT_INJECTED_CONTENT_FRAMES_ALL); - g_assert(isStyleSheetInjectedForURLAtPath(test, randomPath)); - - removeOldInjectedStyleSheetsAndResetLists(group, whitelist, blacklist); - - fillURLListFromPaths(blacklist, randomPath, 0); - webkit_web_view_group_add_user_style_sheet(group, kInjectedStyleSheet, 0, 0, blacklist, WEBKIT_INJECTED_CONTENT_FRAMES_ALL); - g_assert(!isStyleSheetInjectedForURLAtPath(test, randomPath)); - g_assert(isStyleSheetInjectedForURLAtPath(test, "someotherrandompath")); - - removeOldInjectedStyleSheetsAndResetLists(group, whitelist, blacklist); - - static const char* inTheWhiteList = "inthewhitelist"; - static const char* notInWhitelist = "notinthewhitelist"; - static const char* inTheWhiteListAndBlackList = "inthewhitelistandblacklist"; - - fillURLListFromPaths(whitelist, inTheWhiteList, inTheWhiteListAndBlackList, 0); - fillURLListFromPaths(blacklist, inTheWhiteListAndBlackList, 0); - webkit_web_view_group_add_user_style_sheet(group, kInjectedStyleSheet, 0, whitelist, blacklist, WEBKIT_INJECTED_CONTENT_FRAMES_ALL); - g_assert(isStyleSheetInjectedForURLAtPath(test, inTheWhiteList)); - g_assert(!isStyleSheetInjectedForURLAtPath(test, inTheWhiteListAndBlackList)); - g_assert(!isStyleSheetInjectedForURLAtPath(test, notInWhitelist)); - - // It's important to clean up the environment before other tests. - removeOldInjectedStyleSheetsAndResetLists(group, whitelist, blacklist); -} - -static void serverCallback(SoupServer* server, SoupMessage* message, const char* path, GHashTable*, SoupClientContext*, gpointer) -{ - soup_message_set_status(message, SOUP_STATUS_OK); - soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, kStyleSheetHTML, strlen(kStyleSheetHTML)); - soup_message_body_complete(message->response_body); -} - -void beforeAll() -{ - kServer = new WebKitTestServer(); - kServer->run(serverCallback); - - Test::add("WebKitWebViewGroup", "default-group", testWebViewGroupDefault); - Test::add("WebKitWebViewGroup", "new-group", testWebViewGroupNewGroup); - Test::add("WebKitWebView", "new-with-group", testWebViewNewWithGroup); - Test::add("WebKitWebViewGroup", "settings", testWebViewGroupSettings); - WebViewTest::add("WebKitWebViewGroup", "injected-style-sheet", testWebViewGroupInjectedStyleSheet); -} - -void afterAll() -{ - delete kServer; -} diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebViewEditor.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebViewEditor.cpp index 9e69bfa48..00c02aed2 100644 --- a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebViewEditor.cpp +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebViewEditor.cpp @@ -19,7 +19,7 @@ #include "config.h" #include "WebViewTest.h" -#include <wtf/gobject/GRefPtr.h> +#include <wtf/glib/GRefPtr.h> class EditorTest: public WebViewTest { public: @@ -32,6 +32,7 @@ public: : m_clipboard(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD)) , m_canExecuteEditingCommand(false) , m_triesCount(0) + , m_editorState(nullptr) { gtk_clipboard_clear(m_clipboard); } @@ -73,25 +74,71 @@ public: g_main_loop_run(m_mainLoop); } + gchar* cutSelection() + { + g_assert(canExecuteEditingCommand(WEBKIT_EDITING_COMMAND_CUT)); + g_assert(canExecuteEditingCommand(WEBKIT_EDITING_COMMAND_PASTE)); + + webkit_web_view_execute_editing_command(m_webView, WEBKIT_EDITING_COMMAND_CUT); + // There's no way to know when the selection has been cut to + // the clipboard, so use a timeout source to query the clipboard. + m_triesCount = 0; + g_timeout_add(kClipboardWaitTimeout, reinterpret_cast<GSourceFunc>(waitForClipboardText), this); + g_main_loop_run(m_mainLoop); + + return gtk_clipboard_wait_for_text(m_clipboard); + } + + WebKitEditorState* editorState() + { + if (m_editorState) + return m_editorState; + + m_editorState = webkit_web_view_get_editor_state(m_webView); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(m_editorState)); + return m_editorState; + } + + static void quitMainLoopInCallback(EditorTest* test) + { + g_main_loop_quit(test->m_mainLoop); + } + + unsigned typingAttributes() + { + return webkit_editor_state_get_typing_attributes(editorState()); + } + + unsigned waitUntilTypingAttributesChanged() + { + unsigned long handlerID = g_signal_connect_swapped(editorState(), "notify::typing-attributes", G_CALLBACK(quitMainLoopInCallback), this); + g_main_loop_run(m_mainLoop); + g_signal_handler_disconnect(m_editorState, handlerID); + return typingAttributes(); + } + GtkClipboard* m_clipboard; bool m_canExecuteEditingCommand; size_t m_triesCount; + WebKitEditorState* m_editorState; }; +static const char* selectedSpanHTMLFormat = + "<html><body contentEditable=\"%s\">" + "<span id=\"mainspan\">All work and no play <span id=\"subspan\">make Jack a dull</span> boy.</span>" + "<script>document.getSelection().removeAllRanges();\n" + "document.getSelection().selectAllChildren(document.getElementById('subspan'));\n" + "</script></body></html>"; + static void testWebViewEditorCutCopyPasteNonEditable(EditorTest* test, gconstpointer) { - static const char* selectedSpanHTML = "<html><body contentEditable=\"false\">" - "<span id=\"mainspan\">All work and no play <span id=\"subspan\">make Jack a dull</span> boy.</span>" - "<script>document.getSelection().collapse();\n" - "document.getSelection().selectAllChildren(document.getElementById('subspan'));\n" - "</script></body></html>"; - // Nothing loaded yet. g_assert(!test->canExecuteEditingCommand(WEBKIT_EDITING_COMMAND_CUT)); g_assert(!test->canExecuteEditingCommand(WEBKIT_EDITING_COMMAND_COPY)); g_assert(!test->canExecuteEditingCommand(WEBKIT_EDITING_COMMAND_PASTE)); - test->loadHtml(selectedSpanHTML, 0); + GUniquePtr<char> selectedSpanHTML(g_strdup_printf(selectedSpanHTMLFormat, "false")); + test->loadHtml(selectedSpanHTML.get(), nullptr); test->waitUntilLoadFinished(); g_assert(test->canExecuteEditingCommand(WEBKIT_EDITING_COMMAND_COPY)); @@ -107,18 +154,17 @@ static void testWebViewEditorCutCopyPasteNonEditable(EditorTest* test, gconstpoi static void testWebViewEditorCutCopyPasteEditable(EditorTest* test, gconstpointer) { - static const char* selectedSpanHTML = "<html><body contentEditable=\"true\">" - "<span id=\"mainspan\">All work and no play <span>make Jack a dull</span> boy.</span>" - "<script>document.getSelection().collapse();\n" - "document.getSelection().selectAllChildren(document.getElementById('mainspan'));\n" - "</script></body></html>"; - // Nothing loaded yet. g_assert(!test->canExecuteEditingCommand(WEBKIT_EDITING_COMMAND_CUT)); g_assert(!test->canExecuteEditingCommand(WEBKIT_EDITING_COMMAND_COPY)); g_assert(!test->canExecuteEditingCommand(WEBKIT_EDITING_COMMAND_PASTE)); - test->loadHtml(selectedSpanHTML, 0); + g_assert(!test->isEditable()); + test->setEditable(true); + g_assert(test->isEditable()); + + GUniquePtr<char> selectedSpanHTML(g_strdup_printf(selectedSpanHTMLFormat, "false")); + test->loadHtml(selectedSpanHTML.get(), nullptr); test->waitUntilLoadFinished(); // There's a selection. @@ -128,20 +174,15 @@ static void testWebViewEditorCutCopyPasteEditable(EditorTest* test, gconstpointe test->copyClipboard(); GUniquePtr<char> clipboardText(gtk_clipboard_wait_for_text(test->m_clipboard)); - g_assert_cmpstr(clipboardText.get(), ==, "All work and no play make Jack a dull boy."); + g_assert_cmpstr(clipboardText.get(), ==, "make Jack a dull"); } static void testWebViewEditorSelectAllNonEditable(EditorTest* test, gconstpointer) { - static const char* selectedSpanHTML = "<html><body contentEditable=\"false\">" - "<span id=\"mainspan\">All work and no play <span id=\"subspan\">make Jack a dull</span> boy.</span>" - "<script>document.getSelection().collapse();\n" - "document.getSelection().selectAllChildren(document.getElementById('subspan'));\n" - "</script></body></html>"; - g_assert(test->canExecuteEditingCommand(WEBKIT_EDITING_COMMAND_SELECT_ALL)); - test->loadHtml(selectedSpanHTML, 0); + GUniquePtr<char> selectedSpanHTML(g_strdup_printf(selectedSpanHTMLFormat, "false")); + test->loadHtml(selectedSpanHTML.get(), nullptr); test->waitUntilLoadFinished(); g_assert(test->canExecuteEditingCommand(WEBKIT_EDITING_COMMAND_SELECT_ALL)); @@ -162,15 +203,14 @@ static void testWebViewEditorSelectAllNonEditable(EditorTest* test, gconstpointe static void testWebViewEditorSelectAllEditable(EditorTest* test, gconstpointer) { - static const char* selectedSpanHTML = "<html><body contentEditable=\"true\">" - "<span id=\"mainspan\">All work and no play <span id=\"subspan\">make Jack a dull</span> boy.</span>" - "<script>document.getSelection().collapse();\n" - "document.getSelection().selectAllChildren(document.getElementById('subspan'));\n" - "</script></body></html>"; - g_assert(test->canExecuteEditingCommand(WEBKIT_EDITING_COMMAND_SELECT_ALL)); - test->loadHtml(selectedSpanHTML, 0); + g_assert(!test->isEditable()); + test->setEditable(true); + g_assert(test->isEditable()); + + GUniquePtr<char> selectedSpanHTML(g_strdup_printf(selectedSpanHTMLFormat, "false")); + test->loadHtml(selectedSpanHTML.get(), nullptr); test->waitUntilLoadFinished(); g_assert(test->canExecuteEditingCommand(WEBKIT_EDITING_COMMAND_SELECT_ALL)); @@ -189,12 +229,240 @@ static void testWebViewEditorSelectAllEditable(EditorTest* test, gconstpointer) g_assert_cmpstr(clipboardText.get(), ==, "All work and no play make Jack a dull boy."); } +static void loadContentsAndTryToCutSelection(EditorTest* test, bool contentEditable) +{ + // View is not editable by default. + g_assert(!test->isEditable()); + + GUniquePtr<char> selectedSpanHTML(g_strdup_printf(selectedSpanHTMLFormat, contentEditable ? "true" : "false")); + test->loadHtml(selectedSpanHTML.get(), nullptr); + test->waitUntilLoadFinished(); + + g_assert(!test->isEditable()); + test->setEditable(true); + g_assert(test->isEditable()); + + // Cut the selection to the clipboard to see if the view is indeed editable. + GUniquePtr<char> clipboardText(test->cutSelection()); + g_assert_cmpstr(clipboardText.get(), ==, "make Jack a dull"); + + // Reset the editable for next test. + test->setEditable(false); + g_assert(!test->isEditable()); +} + +static void testWebViewEditorNonEditable(EditorTest* test) +{ + GUniquePtr<char> selectedSpanHTML(g_strdup_printf(selectedSpanHTMLFormat, "false")); + test->loadHtml(selectedSpanHTML.get(), nullptr); + test->waitUntilLoadFinished(); + + g_assert(!test->isEditable()); + test->setEditable(true); + g_assert(test->isEditable()); + test->setEditable(false); + g_assert(!test->isEditable()); + + // Check if view is indeed non-editable. + g_assert(!test->canExecuteEditingCommand(WEBKIT_EDITING_COMMAND_CUT)); + g_assert(!test->canExecuteEditingCommand(WEBKIT_EDITING_COMMAND_PASTE)); +} + +static void testWebViewEditorEditable(EditorTest* test, gconstpointer) +{ + testWebViewEditorNonEditable(test); + + // Reset the editable for next test. + test->setEditable(false); + g_assert(!test->isEditable()); + + loadContentsAndTryToCutSelection(test, true); + + // Reset the editable for next test. + test->setEditable(false); + g_assert(!test->isEditable()); + + loadContentsAndTryToCutSelection(test, false); +} + +static void testWebViewEditorEditorStateTypingAttributes(EditorTest* test, gconstpointer) +{ + static const char* typingAttributesHTML = + "<html><body>" + "normal <b>bold </b><i>italic </i><u>underline </u><strike>strike </strike>" + "<b><i>boldanditalic </i></b>" + "</body></html>"; + + test->loadHtml(typingAttributesHTML, nullptr); + test->waitUntilLoadFinished(); + test->setEditable(true); + + unsigned typingAttributes = test->typingAttributes(); + g_assert_cmpuint(typingAttributes, ==, WEBKIT_EDITOR_TYPING_ATTRIBUTE_NONE); + + webkit_web_view_execute_editing_command(test->m_webView, "MoveWordForward"); + webkit_web_view_execute_editing_command(test->m_webView, "MoveForward"); + webkit_web_view_execute_editing_command(test->m_webView, "MoveForward"); + typingAttributes = test->waitUntilTypingAttributesChanged(); + g_assert(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_BOLD); + g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_ITALIC)); + g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_UNDERLINE)); + g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_STRIKETHROUGH)); + + webkit_web_view_execute_editing_command(test->m_webView, "MoveWordForward"); + webkit_web_view_execute_editing_command(test->m_webView, "MoveForward"); + webkit_web_view_execute_editing_command(test->m_webView, "MoveForward"); + typingAttributes = test->waitUntilTypingAttributesChanged(); + g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_BOLD)); + g_assert(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_ITALIC); + g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_UNDERLINE)); + g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_STRIKETHROUGH)); + + webkit_web_view_execute_editing_command(test->m_webView, "MoveWordForward"); + webkit_web_view_execute_editing_command(test->m_webView, "MoveForward"); + webkit_web_view_execute_editing_command(test->m_webView, "MoveForward"); + typingAttributes = test->waitUntilTypingAttributesChanged(); + g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_BOLD)); + g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_ITALIC)); + g_assert(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_UNDERLINE); + g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_STRIKETHROUGH)); + + webkit_web_view_execute_editing_command(test->m_webView, "MoveWordForward"); + webkit_web_view_execute_editing_command(test->m_webView, "MoveForward"); + webkit_web_view_execute_editing_command(test->m_webView, "MoveForward"); + typingAttributes = test->waitUntilTypingAttributesChanged(); + g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_BOLD)); + g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_ITALIC)); + g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_UNDERLINE)); + g_assert(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_STRIKETHROUGH); + + webkit_web_view_execute_editing_command(test->m_webView, "MoveWordForward"); + webkit_web_view_execute_editing_command(test->m_webView, "MoveForward"); + webkit_web_view_execute_editing_command(test->m_webView, "MoveForward"); + typingAttributes = test->waitUntilTypingAttributesChanged(); + g_assert(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_BOLD); + g_assert(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_ITALIC); + g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_UNDERLINE)); + g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_STRIKETHROUGH)); + + // Selections. + webkit_web_view_execute_editing_command(test->m_webView, "MoveToBeginningOfDocument"); + webkit_web_view_execute_editing_command(test->m_webView, "MoveWordForwardAndModifySelection"); + typingAttributes = test->waitUntilTypingAttributesChanged(); + g_assert_cmpuint(typingAttributes, ==, WEBKIT_EDITOR_TYPING_ATTRIBUTE_NONE); + + webkit_web_view_execute_editing_command(test->m_webView, "MoveForward"); + webkit_web_view_execute_editing_command(test->m_webView, "MoveForward"); + webkit_web_view_execute_editing_command(test->m_webView, "MoveWordForwardAndModifySelection"); + typingAttributes = test->waitUntilTypingAttributesChanged(); + g_assert(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_BOLD); + g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_ITALIC)); + g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_UNDERLINE)); + g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_STRIKETHROUGH)); + + webkit_web_view_execute_editing_command(test->m_webView, "MoveForward"); + webkit_web_view_execute_editing_command(test->m_webView, "MoveForward"); + webkit_web_view_execute_editing_command(test->m_webView, "MoveWordForwardAndModifySelection"); + typingAttributes = test->waitUntilTypingAttributesChanged(); + g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_BOLD)); + g_assert(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_ITALIC); + g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_UNDERLINE)); + g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_STRIKETHROUGH)); + + webkit_web_view_execute_editing_command(test->m_webView, "MoveForward"); + webkit_web_view_execute_editing_command(test->m_webView, "MoveForward"); + webkit_web_view_execute_editing_command(test->m_webView, "MoveWordForwardAndModifySelection"); + typingAttributes = test->waitUntilTypingAttributesChanged(); + g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_BOLD)); + g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_ITALIC)); + g_assert(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_UNDERLINE); + g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_STRIKETHROUGH)); + + webkit_web_view_execute_editing_command(test->m_webView, "MoveForward"); + webkit_web_view_execute_editing_command(test->m_webView, "MoveForward"); + webkit_web_view_execute_editing_command(test->m_webView, "MoveWordForwardAndModifySelection"); + typingAttributes = test->waitUntilTypingAttributesChanged(); + g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_BOLD)); + g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_ITALIC)); + g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_UNDERLINE)); + g_assert(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_STRIKETHROUGH); + + webkit_web_view_execute_editing_command(test->m_webView, "MoveForward"); + webkit_web_view_execute_editing_command(test->m_webView, "MoveForward"); + webkit_web_view_execute_editing_command(test->m_webView, "MoveWordForwardAndModifySelection"); + typingAttributes = test->waitUntilTypingAttributesChanged(); + g_assert(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_BOLD); + g_assert(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_ITALIC); + g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_UNDERLINE)); + g_assert(!(typingAttributes & WEBKIT_EDITOR_TYPING_ATTRIBUTE_STRIKETHROUGH)); + + webkit_web_view_execute_editing_command(test->m_webView, "SelectAll"); + typingAttributes = test->waitUntilTypingAttributesChanged(); + g_assert_cmpuint(typingAttributes, ==, WEBKIT_EDITOR_TYPING_ATTRIBUTE_NONE); +} + +static void testWebViewEditorInsertImage(EditorTest* test, gconstpointer) +{ + test->loadHtml("<html><body></body></html>", "file:///"); + test->waitUntilLoadFinished(); + test->setEditable(true); + + GUniquePtr<char> imagePath(g_build_filename(Test::getResourcesDir().data(), "blank.ico", nullptr)); + GUniquePtr<char> imageURI(g_filename_to_uri(imagePath.get(), nullptr, nullptr)); + webkit_web_view_execute_editing_command_with_argument(test->m_webView, WEBKIT_EDITING_COMMAND_INSERT_IMAGE, imageURI.get()); + GUniqueOutPtr<GError> error; + WebKitJavascriptResult* javascriptResult = test->runJavaScriptAndWaitUntilFinished("document.getElementsByTagName('IMG')[0].src", &error.outPtr()); + g_assert(javascriptResult); + g_assert(!error); + GUniquePtr<char> resultString(WebViewTest::javascriptResultToCString(javascriptResult)); + g_assert_cmpstr(resultString.get(), ==, imageURI.get()); +} + +static void testWebViewEditorCreateLink(EditorTest* test, gconstpointer) +{ + test->loadHtml("<html><body onload=\"document.getSelection().selectAllChildren(document.body);\">webkitgtk.org</body></html>", nullptr); + test->waitUntilLoadFinished(); + test->setEditable(true); + + static const char* webkitGTKURL = "http://www.webkitgtk.org/"; + webkit_web_view_execute_editing_command_with_argument(test->m_webView, WEBKIT_EDITING_COMMAND_CREATE_LINK, webkitGTKURL); + GUniqueOutPtr<GError> error; + WebKitJavascriptResult* javascriptResult = test->runJavaScriptAndWaitUntilFinished("document.getElementsByTagName('A')[0].href;", &error.outPtr()); + g_assert(javascriptResult); + g_assert(!error); + GUniquePtr<char> resultString(WebViewTest::javascriptResultToCString(javascriptResult)); + g_assert_cmpstr(resultString.get(), ==, webkitGTKURL); + javascriptResult = test->runJavaScriptAndWaitUntilFinished("document.getElementsByTagName('A')[0].innerText;", &error.outPtr()); + g_assert(javascriptResult); + g_assert(!error); + resultString.reset(WebViewTest::javascriptResultToCString(javascriptResult)); + g_assert_cmpstr(resultString.get(), ==, "webkitgtk.org"); + + // When there isn't text selected, the URL is used as link text. + webkit_web_view_execute_editing_command(test->m_webView, "MoveToEndOfLine"); + webkit_web_view_execute_editing_command_with_argument(test->m_webView, WEBKIT_EDITING_COMMAND_CREATE_LINK, webkitGTKURL); + javascriptResult = test->runJavaScriptAndWaitUntilFinished("document.getElementsByTagName('A')[1].href;", &error.outPtr()); + g_assert(javascriptResult); + g_assert(!error); + resultString.reset(WebViewTest::javascriptResultToCString(javascriptResult)); + g_assert_cmpstr(resultString.get(), ==, webkitGTKURL); + javascriptResult = test->runJavaScriptAndWaitUntilFinished("document.getElementsByTagName('A')[1].innerText;", &error.outPtr()); + g_assert(javascriptResult); + g_assert(!error); + resultString.reset(WebViewTest::javascriptResultToCString(javascriptResult)); + g_assert_cmpstr(resultString.get(), ==, webkitGTKURL); +} + void beforeAll() { + EditorTest::add("WebKitWebView", "editable/editable", testWebViewEditorEditable); EditorTest::add("WebKitWebView", "cut-copy-paste/non-editable", testWebViewEditorCutCopyPasteNonEditable); EditorTest::add("WebKitWebView", "cut-copy-paste/editable", testWebViewEditorCutCopyPasteEditable); EditorTest::add("WebKitWebView", "select-all/non-editable", testWebViewEditorSelectAllNonEditable); EditorTest::add("WebKitWebView", "select-all/editable", testWebViewEditorSelectAllEditable); + EditorTest::add("WebKitWebView", "editor-state/typing-attributes", testWebViewEditorEditorStateTypingAttributes); + EditorTest::add("WebKitWebView", "insert/image", testWebViewEditorInsertImage); + EditorTest::add("WebKitWebView", "insert/link", testWebViewEditorCreateLink); } void afterAll() diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebsiteData.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebsiteData.cpp new file mode 100644 index 000000000..4959e838e --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebsiteData.cpp @@ -0,0 +1,537 @@ +/* + * Copyright (C) 2017 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2,1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#include "WebKitTestServer.h" +#include "WebViewTest.h" + +static WebKitTestServer* kServer; + +static void serverCallback(SoupServer* server, SoupMessage* message, const char* path, GHashTable*, SoupClientContext*, gpointer) +{ + if (message->method != SOUP_METHOD_GET) { + soup_message_set_status(message, SOUP_STATUS_NOT_IMPLEMENTED); + return; + } + + if (g_str_equal(path, "/empty")) { + const char* emptyHTML = "<html><body></body></html>"; + soup_message_headers_replace(message->response_headers, "Set-Cookie", "foo=bar; Max-Age=60"); + soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, emptyHTML, strlen(emptyHTML)); + soup_message_body_complete(message->response_body); + soup_message_set_status(message, SOUP_STATUS_OK); + } else if (g_str_equal(path, "/appcache")) { + const char* appcacheHTML = "<html manifest=appcache.manifest><body></body></html>"; + soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, appcacheHTML, strlen(appcacheHTML)); + soup_message_body_complete(message->response_body); + soup_message_set_status(message, SOUP_STATUS_OK); + } else if (g_str_equal(path, "/appcache.manifest")) { + const char* appcacheManifest = "CACHE MANIFEST\nCACHE:\nappcache/foo.txt\n"; + soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, appcacheManifest, strlen(appcacheManifest)); + soup_message_body_complete(message->response_body); + soup_message_set_status(message, SOUP_STATUS_OK); + } else if (g_str_equal(path, "/appcache/foo.txt")) { + soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, "foo", 3); + soup_message_body_complete(message->response_body); + soup_message_set_status(message, SOUP_STATUS_OK); + } else if (g_str_equal(path, "/sessionstorage")) { + const char* sessionStorageHTML = "<html><body onload=\"sessionStorage.foo = 'bar';\"></body></html>"; + soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, sessionStorageHTML, strlen(sessionStorageHTML)); + soup_message_body_complete(message->response_body); + soup_message_set_status(message, SOUP_STATUS_OK); + } else if (g_str_equal(path, "/localstorage")) { + const char* localStorageHTML = "<html><body onload=\"localStorage.foo = 'bar';\"></body></html>"; + soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, localStorageHTML, strlen(localStorageHTML)); + soup_message_body_complete(message->response_body); + soup_message_set_status(message, SOUP_STATUS_OK); + } else + soup_message_set_status(message, SOUP_STATUS_NOT_FOUND); +} + +class WebsiteDataTest : public WebViewTest { +public: + MAKE_GLIB_TEST_FIXTURE(WebsiteDataTest); + + + WebsiteDataTest() + : m_manager(webkit_web_context_get_website_data_manager(webkit_web_view_get_context(m_webView))) + { + g_assert(WEBKIT_IS_WEBSITE_DATA_MANAGER(m_manager)); + assertObjectIsDeletedWhenTestFinishes(G_OBJECT(m_manager)); + // WebsiteDataStore creates a new WebProcessPool when used before any secondary process has been created. + // Ensure we have a web process by always loading about:blank here. + loadURI("about:blank"); + waitUntilLoadFinished(); + } + + ~WebsiteDataTest() + { + g_list_free_full(m_dataList, reinterpret_cast<GDestroyNotify>(webkit_website_data_unref)); + } + + GList* fetch(WebKitWebsiteDataTypes types) + { + if (m_dataList) { + g_list_free_full(m_dataList, reinterpret_cast<GDestroyNotify>(webkit_website_data_unref)); + m_dataList = nullptr; + } + webkit_website_data_manager_fetch(m_manager, types, nullptr, [](GObject*, GAsyncResult* result, gpointer userData) { + WebsiteDataTest* test = static_cast<WebsiteDataTest*>(userData); + test->m_dataList = webkit_website_data_manager_fetch_finish(test->m_manager, result, nullptr); + test->quitMainLoop(); + }, this); + g_main_loop_run(m_mainLoop); + return m_dataList; + } + + void remove(WebKitWebsiteDataTypes types, GList* dataList) + { + webkit_website_data_manager_remove(m_manager, types, dataList, nullptr, [](GObject*, GAsyncResult* result, gpointer userData) { + WebsiteDataTest* test = static_cast<WebsiteDataTest*>(userData); + g_assert(webkit_website_data_manager_remove_finish(test->m_manager, result, nullptr)); + test->quitMainLoop(); + }, this); + g_main_loop_run(m_mainLoop); + } + + void clear(WebKitWebsiteDataTypes types, GTimeSpan timeSpan) + { + webkit_website_data_manager_clear(m_manager, types, timeSpan, nullptr, [](GObject*, GAsyncResult* result, gpointer userData) { + WebsiteDataTest* test = static_cast<WebsiteDataTest*>(userData); + g_assert(webkit_website_data_manager_clear_finish(test->m_manager, result, nullptr)); + test->quitMainLoop(); + }, this); + g_main_loop_run(m_mainLoop); + } + + WebKitWebsiteDataManager* m_manager; + GList* m_dataList { nullptr }; +}; + +static void testWebsiteDataConfiguration(WebsiteDataTest* test, gconstpointer) +{ + // Base directories are not used by TestMain. + g_assert(!webkit_website_data_manager_get_base_data_directory(test->m_manager)); + g_assert(!webkit_website_data_manager_get_base_cache_directory(test->m_manager)); + + GUniquePtr<char> localStorageDirectory(g_build_filename(Test::dataDirectory(), "local-storage", nullptr)); + g_assert_cmpstr(localStorageDirectory.get(), ==, webkit_website_data_manager_get_local_storage_directory(test->m_manager)); + g_assert(g_file_test(localStorageDirectory.get(), G_FILE_TEST_IS_DIR)); + + test->loadURI(kServer->getURIForPath("/empty").data()); + test->waitUntilLoadFinished(); + test->runJavaScriptAndWaitUntilFinished("window.indexedDB.open('TestDatabase');", nullptr); + GUniquePtr<char> indexedDBDirectory(g_build_filename(Test::dataDirectory(), "indexeddb", nullptr)); + g_assert_cmpstr(indexedDBDirectory.get(), ==, webkit_website_data_manager_get_indexeddb_directory(test->m_manager)); + g_assert(g_file_test(indexedDBDirectory.get(), G_FILE_TEST_IS_DIR)); + + GUniquePtr<char> webSQLDirectory(g_build_filename(Test::dataDirectory(), "websql", nullptr)); + g_assert_cmpstr(webSQLDirectory.get(), ==, webkit_website_data_manager_get_websql_directory(test->m_manager)); + test->runJavaScriptAndWaitUntilFinished("db = openDatabase(\"TestDatabase\", \"1.0\", \"TestDatabase\", 1);", nullptr); + g_assert(g_file_test(webSQLDirectory.get(), G_FILE_TEST_IS_DIR)); + + test->loadURI(kServer->getURIForPath("/appcache").data()); + test->waitUntilLoadFinished(); + GUniquePtr<char> applicationCacheDirectory(g_build_filename(Test::dataDirectory(), "appcache", nullptr)); + g_assert_cmpstr(applicationCacheDirectory.get(), ==, webkit_website_data_manager_get_offline_application_cache_directory(test->m_manager)); + GUniquePtr<char> applicationCacheDatabase(g_build_filename(applicationCacheDirectory.get(), "ApplicationCache.db", nullptr)); + unsigned triesCount = 4; + while (!g_file_test(applicationCacheDatabase.get(), G_FILE_TEST_IS_REGULAR) && --triesCount) + test->wait(0.25); + g_assert(triesCount); + + GUniquePtr<char> diskCacheDirectory(g_build_filename(Test::dataDirectory(), "disk-cache", nullptr)); + g_assert_cmpstr(diskCacheDirectory.get(), ==, webkit_website_data_manager_get_disk_cache_directory(test->m_manager)); + g_assert(g_file_test(diskCacheDirectory.get(), G_FILE_TEST_IS_DIR)); + + // Clear all persistent caches, since the data dir is common to all test cases. + static const WebKitWebsiteDataTypes persistentCaches = static_cast<WebKitWebsiteDataTypes>(WEBKIT_WEBSITE_DATA_DISK_CACHE | WEBKIT_WEBSITE_DATA_LOCAL_STORAGE + | WEBKIT_WEBSITE_DATA_WEBSQL_DATABASES | WEBKIT_WEBSITE_DATA_INDEXEDDB_DATABASES | WEBKIT_WEBSITE_DATA_OFFLINE_APPLICATION_CACHE); + test->clear(persistentCaches, 0); + g_assert(!test->fetch(persistentCaches)); + + // The default context should have a different manager with different configuration. + WebKitWebsiteDataManager* defaultManager = webkit_web_context_get_website_data_manager(webkit_web_context_get_default()); + g_assert(WEBKIT_IS_WEBSITE_DATA_MANAGER(defaultManager)); + g_assert(test->m_manager != defaultManager); + g_assert_cmpstr(webkit_website_data_manager_get_local_storage_directory(test->m_manager), !=, webkit_website_data_manager_get_local_storage_directory(defaultManager)); + g_assert_cmpstr(webkit_website_data_manager_get_indexeddb_directory(test->m_manager), !=, webkit_website_data_manager_get_indexeddb_directory(defaultManager)); + g_assert_cmpstr(webkit_website_data_manager_get_disk_cache_directory(test->m_manager), !=, webkit_website_data_manager_get_disk_cache_directory(defaultManager)); + g_assert_cmpstr(webkit_website_data_manager_get_offline_application_cache_directory(test->m_manager), !=, webkit_website_data_manager_get_offline_application_cache_directory(defaultManager)); + g_assert_cmpstr(webkit_website_data_manager_get_websql_directory(test->m_manager), !=, webkit_website_data_manager_get_websql_directory(defaultManager)); + + // Using Test::dataDirectory() we get the default configuration but for a differrent prefix. + GRefPtr<WebKitWebsiteDataManager> baseDataManager = adoptGRef(webkit_website_data_manager_new("base-data-directory", Test::dataDirectory(), "base-cache-directory", Test::dataDirectory(), nullptr)); + g_assert(WEBKIT_IS_WEBSITE_DATA_MANAGER(baseDataManager.get())); + + localStorageDirectory.reset(g_build_filename(Test::dataDirectory(), "localstorage", nullptr)); + g_assert_cmpstr(webkit_website_data_manager_get_local_storage_directory(baseDataManager.get()), ==, localStorageDirectory.get()); + + indexedDBDirectory.reset(g_build_filename(Test::dataDirectory(), "databases", "indexeddb", nullptr)); + g_assert_cmpstr(webkit_website_data_manager_get_indexeddb_directory(baseDataManager.get()), ==, indexedDBDirectory.get()); + + applicationCacheDirectory.reset(g_build_filename(Test::dataDirectory(), "applications", nullptr)); + g_assert_cmpstr(webkit_website_data_manager_get_offline_application_cache_directory(baseDataManager.get()), ==, applicationCacheDirectory.get()); + + webSQLDirectory.reset(g_build_filename(Test::dataDirectory(), "databases", nullptr)); + g_assert_cmpstr(webkit_website_data_manager_get_websql_directory(baseDataManager.get()), ==, webSQLDirectory.get()); + + g_assert_cmpstr(webkit_website_data_manager_get_disk_cache_directory(baseDataManager.get()), ==, Test::dataDirectory()); + + // Any specific configuration provided takes precedence over base dirs. + indexedDBDirectory.reset(g_build_filename(Test::dataDirectory(), "mycustomindexeddb", nullptr)); + applicationCacheDirectory.reset(g_build_filename(Test::dataDirectory(), "mycustomappcache", nullptr)); + baseDataManager = adoptGRef(webkit_website_data_manager_new("base-data-directory", Test::dataDirectory(), "base-cache-directory", Test::dataDirectory(), + "indexeddb-directory", indexedDBDirectory.get(), "offline-application-cache-directory", applicationCacheDirectory.get(), nullptr)); + g_assert_cmpstr(webkit_website_data_manager_get_indexeddb_directory(baseDataManager.get()), ==, indexedDBDirectory.get()); + g_assert_cmpstr(webkit_website_data_manager_get_offline_application_cache_directory(baseDataManager.get()), ==, applicationCacheDirectory.get()); + // The result should be the same as previous manager. + g_assert_cmpstr(webkit_website_data_manager_get_local_storage_directory(baseDataManager.get()), ==, localStorageDirectory.get()); + g_assert_cmpstr(webkit_website_data_manager_get_websql_directory(baseDataManager.get()), ==, webSQLDirectory.get()); + g_assert_cmpstr(webkit_website_data_manager_get_disk_cache_directory(baseDataManager.get()), ==, Test::dataDirectory()); +} + +static void ephemeralViewloadChanged(WebKitWebView* webView, WebKitLoadEvent loadEvent, WebViewTest* test) +{ + if (loadEvent != WEBKIT_LOAD_FINISHED) + return; + g_signal_handlers_disconnect_by_func(webView, reinterpret_cast<void*>(ephemeralViewloadChanged), test); + test->quitMainLoop(); +} + +static void testWebsiteDataEphemeral(WebViewTest* test, gconstpointer) +{ + GRefPtr<WebKitWebsiteDataManager> manager = adoptGRef(webkit_website_data_manager_new_ephemeral()); + g_assert(webkit_website_data_manager_is_ephemeral(manager.get())); + g_assert(!webkit_website_data_manager_get_base_data_directory(manager.get())); + g_assert(!webkit_website_data_manager_get_base_cache_directory(manager.get())); + g_assert(!webkit_website_data_manager_get_local_storage_directory(manager.get())); + g_assert(!webkit_website_data_manager_get_disk_cache_directory(manager.get())); + g_assert(!webkit_website_data_manager_get_offline_application_cache_directory(manager.get())); + g_assert(!webkit_website_data_manager_get_indexeddb_directory(manager.get())); + g_assert(!webkit_website_data_manager_get_websql_directory(manager.get())); + + // Configuration is ignored when is-ephemeral is used. + manager = adoptGRef(WEBKIT_WEBSITE_DATA_MANAGER(g_object_new(WEBKIT_TYPE_WEBSITE_DATA_MANAGER, "base-data-directory", Test::dataDirectory(), "is-ephemeral", TRUE, nullptr))); + g_assert(webkit_website_data_manager_is_ephemeral(manager.get())); + g_assert(!webkit_website_data_manager_get_base_data_directory(manager.get())); + + // Non persistent data can be queried in an ephemeral manager. + GRefPtr<WebKitWebContext> webContext = adoptGRef(webkit_web_context_new_with_website_data_manager(manager.get())); + g_assert(webkit_web_context_is_ephemeral(webContext.get())); + GRefPtr<WebKitWebView> webView = WEBKIT_WEB_VIEW(webkit_web_view_new_with_context(webContext.get())); + g_assert(webkit_web_view_is_ephemeral(webView.get())); + g_assert(webkit_web_view_get_website_data_manager(webView.get()) == manager.get()); + + g_signal_connect(webView.get(), "load-changed", G_CALLBACK(ephemeralViewloadChanged), test); + webkit_web_view_load_uri(webView.get(), kServer->getURIForPath("/empty").data()); + g_main_loop_run(test->m_mainLoop); + + webkit_website_data_manager_fetch(manager.get(), WEBKIT_WEBSITE_DATA_MEMORY_CACHE, nullptr, [](GObject* manager, GAsyncResult* result, gpointer userData) { + auto* test = static_cast<WebViewTest*>(userData); + GList* dataList = webkit_website_data_manager_fetch_finish(WEBKIT_WEBSITE_DATA_MANAGER(manager), result, nullptr); + g_assert(dataList); + g_assert_cmpuint(g_list_length(dataList), ==, 1); + WebKitWebsiteData* data = static_cast<WebKitWebsiteData*>(dataList->data); + g_assert(data); + WebKitSecurityOrigin* origin = webkit_security_origin_new_for_uri(kServer->getURIForPath("/").data()); + g_assert_cmpstr(webkit_website_data_get_name(data), ==, webkit_security_origin_get_host(origin)); + webkit_security_origin_unref(origin); + g_list_free_full(dataList, reinterpret_cast<GDestroyNotify>(webkit_website_data_unref)); + test->quitMainLoop(); + }, test); + g_main_loop_run(test->m_mainLoop); +} + +static void testWebsiteDataCache(WebsiteDataTest* test, gconstpointer) +{ + static const WebKitWebsiteDataTypes cacheTypes = static_cast<WebKitWebsiteDataTypes>(WEBKIT_WEBSITE_DATA_MEMORY_CACHE | WEBKIT_WEBSITE_DATA_DISK_CACHE); + GList* dataList = test->fetch(cacheTypes); + g_assert(!dataList); + + test->loadURI(kServer->getURIForPath("/empty").data()); + test->waitUntilLoadFinished(); + + // Disk cache delays the storing of initial resources for 1 second to avoid + // affecting early page load. So, wait 1 second here to make sure resources + // have already been stored. + test->wait(1); + + dataList = test->fetch(cacheTypes); + g_assert(dataList); + g_assert_cmpuint(g_list_length(dataList), ==, 1); + WebKitWebsiteData* data = static_cast<WebKitWebsiteData*>(dataList->data); + g_assert(data); + WebKitSecurityOrigin* origin = webkit_security_origin_new_for_uri(kServer->getURIForPath("/").data()); + g_assert_cmpstr(webkit_website_data_get_name(data), ==, webkit_security_origin_get_host(origin)); + webkit_security_origin_unref(origin); + g_assert_cmpuint(webkit_website_data_get_types(data), ==, cacheTypes); + // Memory cache size is unknown. + g_assert_cmpuint(webkit_website_data_get_size(data, WEBKIT_WEBSITE_DATA_MEMORY_CACHE), ==, 0); + g_assert_cmpuint(webkit_website_data_get_size(data, WEBKIT_WEBSITE_DATA_DISK_CACHE), >, 0); + + // Try again but only getting disk cache. + dataList = test->fetch(WEBKIT_WEBSITE_DATA_DISK_CACHE); + g_assert(dataList); + g_assert_cmpuint(g_list_length(dataList), ==, 1); + data = static_cast<WebKitWebsiteData*>(dataList->data); + g_assert(data); + g_assert(webkit_website_data_get_types(data) & WEBKIT_WEBSITE_DATA_DISK_CACHE); + g_assert(!(webkit_website_data_get_types(data) & WEBKIT_WEBSITE_DATA_MEMORY_CACHE)); + + GUniquePtr<char> fileURL(g_strdup_printf("file://%s/simple.html", Test::getResourcesDir(Test::WebKit2Resources).data())); + test->loadURI(fileURL.get()); + test->waitUntilLoadFinished(); + + fileURL.reset(g_strdup_printf("file://%s/simple2.html", Test::getResourcesDir(Test::WebKit2Resources).data())); + test->loadURI(fileURL.get()); + test->waitUntilLoadFinished(); + + // Local files are grouped. + dataList = test->fetch(cacheTypes); + g_assert(dataList); + g_assert_cmpuint(g_list_length(dataList), ==, 2); + GList* itemList = g_list_find_custom(dataList, nullptr, [](gconstpointer item, gconstpointer) -> int { + WebKitWebsiteData* data = static_cast<WebKitWebsiteData*>(const_cast<gpointer>(item)); + return g_strcmp0(webkit_website_data_get_name(data), "Local files"); + }); + g_assert(itemList); + data = static_cast<WebKitWebsiteData*>(itemList->data); + g_assert(data); + g_assert(webkit_website_data_get_types(data) & WEBKIT_WEBSITE_DATA_MEMORY_CACHE); + // Local files are never stored in disk cache. + g_assert(!(webkit_website_data_get_types(data) & WEBKIT_WEBSITE_DATA_DISK_CACHE)); + + // Clear data modified since the last microsecond should not clear anything. + // Use disk-cache because memory cache ignores the modified since. + test->clear(WEBKIT_WEBSITE_DATA_DISK_CACHE, 1); + dataList = test->fetch(cacheTypes); + g_assert(dataList); + g_assert_cmpuint(g_list_length(dataList), ==, 2); + + // Remove memory cache only for local files. + itemList = g_list_find_custom(dataList, nullptr, [](gconstpointer item, gconstpointer) -> int { + WebKitWebsiteData* data = static_cast<WebKitWebsiteData*>(const_cast<gpointer>(item)); + return g_strcmp0(webkit_website_data_get_name(data), "Local files"); + }); + g_assert(itemList); + GList removeList = { itemList->data, nullptr, nullptr }; + test->remove(WEBKIT_WEBSITE_DATA_MEMORY_CACHE, &removeList); + dataList = test->fetch(cacheTypes); + g_assert(dataList); + g_assert_cmpuint(g_list_length(dataList), ==, 1); + data = static_cast<WebKitWebsiteData*>(dataList->data); + g_assert(webkit_website_data_get_types(data) & WEBKIT_WEBSITE_DATA_DISK_CACHE); + + // Clear all. + test->clear(cacheTypes, 0); + dataList = test->fetch(cacheTypes); + g_assert(!dataList); +} + +static void testWebsiteDataStorage(WebsiteDataTest* test, gconstpointer) +{ + static const WebKitWebsiteDataTypes storageTypes = static_cast<WebKitWebsiteDataTypes>(WEBKIT_WEBSITE_DATA_SESSION_STORAGE | WEBKIT_WEBSITE_DATA_LOCAL_STORAGE); + GList* dataList = test->fetch(storageTypes); + g_assert(!dataList); + + test->loadURI(kServer->getURIForPath("/sessionstorage").data()); + test->waitUntilLoadFinished(); + + test->loadURI(kServer->getURIForPath("/localstorage").data()); + test->waitUntilLoadFinished(); + + // Local storage uses a 1 second timer to update the database. + test->wait(1); + + dataList = test->fetch(storageTypes); + g_assert(dataList); + g_assert_cmpuint(g_list_length(dataList), ==, 1); + WebKitWebsiteData* data = static_cast<WebKitWebsiteData*>(dataList->data); + g_assert(data); + WebKitSecurityOrigin* origin = webkit_security_origin_new_for_uri(kServer->getURIForPath("/").data()); + g_assert_cmpstr(webkit_website_data_get_name(data), ==, webkit_security_origin_get_host(origin)); + webkit_security_origin_unref(origin); + g_assert_cmpuint(webkit_website_data_get_types(data), ==, storageTypes); + // Storage sizes are unknown. + g_assert_cmpuint(webkit_website_data_get_size(data, WEBKIT_WEBSITE_DATA_SESSION_STORAGE), ==, 0); + g_assert_cmpuint(webkit_website_data_get_size(data, WEBKIT_WEBSITE_DATA_LOCAL_STORAGE), ==, 0); + + // Get also cached data, and clear it. + static const WebKitWebsiteDataTypes cacheAndStorageTypes = static_cast<WebKitWebsiteDataTypes>(storageTypes | WEBKIT_WEBSITE_DATA_MEMORY_CACHE | WEBKIT_WEBSITE_DATA_DISK_CACHE); + dataList = test->fetch(cacheAndStorageTypes); + g_assert(dataList); + g_assert_cmpuint(g_list_length(dataList), ==, 1); + data = static_cast<WebKitWebsiteData*>(dataList->data); + g_assert(data); + g_assert_cmpuint(webkit_website_data_get_types(data), ==, cacheAndStorageTypes); + test->clear(static_cast<WebKitWebsiteDataTypes>(WEBKIT_WEBSITE_DATA_MEMORY_CACHE | WEBKIT_WEBSITE_DATA_DISK_CACHE), 0); + + // Get all types again, but only storage is retrieved now. + dataList = test->fetch(cacheAndStorageTypes); + g_assert(dataList); + g_assert_cmpuint(g_list_length(dataList), ==, 1); + data = static_cast<WebKitWebsiteData*>(dataList->data); + g_assert(data); + g_assert_cmpuint(webkit_website_data_get_types(data), ==, storageTypes); + + // Remove the session storage. + GList removeList = { data, nullptr, nullptr }; + test->remove(WEBKIT_WEBSITE_DATA_SESSION_STORAGE, &removeList); + dataList = test->fetch(storageTypes); + g_assert(dataList); + g_assert_cmpuint(g_list_length(dataList), ==, 1); + data = static_cast<WebKitWebsiteData*>(dataList->data); + g_assert(!(webkit_website_data_get_types(data) & WEBKIT_WEBSITE_DATA_SESSION_STORAGE)); + g_assert(webkit_website_data_get_types(data) & WEBKIT_WEBSITE_DATA_LOCAL_STORAGE); + + // Clear all. + test->clear(cacheAndStorageTypes, 0); + dataList = test->fetch(cacheAndStorageTypes); + g_assert(!dataList); +} + +static void testWebsiteDataDatabases(WebsiteDataTest* test, gconstpointer) +{ + static const WebKitWebsiteDataTypes databaseTypes = static_cast<WebKitWebsiteDataTypes>(WEBKIT_WEBSITE_DATA_WEBSQL_DATABASES | WEBKIT_WEBSITE_DATA_INDEXEDDB_DATABASES); + GList* dataList = test->fetch(databaseTypes); + g_assert(!dataList); + + test->loadURI(kServer->getURIForPath("/empty").data()); + test->waitUntilLoadFinished(); + test->runJavaScriptAndWaitUntilFinished("window.indexedDB.open('TestDatabase');", nullptr); + + dataList = test->fetch(databaseTypes); + g_assert(dataList); + g_assert_cmpuint(g_list_length(dataList), ==, 1); + WebKitWebsiteData* data = static_cast<WebKitWebsiteData*>(dataList->data); + g_assert(data); + WebKitSecurityOrigin* origin = webkit_security_origin_new_for_uri(kServer->getURIForPath("/").data()); + g_assert_cmpstr(webkit_website_data_get_name(data), ==, webkit_security_origin_get_host(origin)); + webkit_security_origin_unref(origin); + g_assert_cmpuint(webkit_website_data_get_types(data), ==, WEBKIT_WEBSITE_DATA_INDEXEDDB_DATABASES); + // Database sizes are unknown. + g_assert_cmpuint(webkit_website_data_get_size(data, WEBKIT_WEBSITE_DATA_INDEXEDDB_DATABASES), ==, 0); + + test->runJavaScriptAndWaitUntilFinished("db = openDatabase(\"TestDatabase\", \"1.0\", \"TestDatabase\", 1);", nullptr); + dataList = test->fetch(databaseTypes); + g_assert(dataList); + g_assert_cmpuint(g_list_length(dataList), ==, 1); + data = static_cast<WebKitWebsiteData*>(dataList->data); + g_assert(data); + g_assert_cmpuint(webkit_website_data_get_types(data), ==, databaseTypes); + // Database sizes are unknown. + g_assert_cmpuint(webkit_website_data_get_size(data, WEBKIT_WEBSITE_DATA_INDEXEDDB_DATABASES), ==, 0); + g_assert_cmpuint(webkit_website_data_get_size(data, WEBKIT_WEBSITE_DATA_WEBSQL_DATABASES), ==, 0); + + // Remove all databases at once. + GList removeList = { data, nullptr, nullptr }; + test->remove(databaseTypes, &removeList); + dataList = test->fetch(databaseTypes); + g_assert(!dataList); + + // Clear all. + static const WebKitWebsiteDataTypes cacheAndDatabaseTypes = static_cast<WebKitWebsiteDataTypes>(databaseTypes | WEBKIT_WEBSITE_DATA_MEMORY_CACHE | WEBKIT_WEBSITE_DATA_DISK_CACHE); + test->clear(cacheAndDatabaseTypes, 0); + dataList = test->fetch(cacheAndDatabaseTypes); + g_assert(!dataList); +} + +static void testWebsiteDataAppcache(WebsiteDataTest* test, gconstpointer) +{ + GList* dataList = test->fetch(WEBKIT_WEBSITE_DATA_OFFLINE_APPLICATION_CACHE); + g_assert(!dataList); + + test->loadURI(kServer->getURIForPath("/appcache").data()); + test->waitUntilLoadFinished(); + + test->wait(1); + dataList = test->fetch(WEBKIT_WEBSITE_DATA_OFFLINE_APPLICATION_CACHE); + g_assert(dataList); + g_assert_cmpuint(g_list_length(dataList), ==, 1); + WebKitWebsiteData* data = static_cast<WebKitWebsiteData*>(dataList->data); + g_assert(data); + WebKitSecurityOrigin* origin = webkit_security_origin_new_for_uri(kServer->getURIForPath("/").data()); + g_assert_cmpstr(webkit_website_data_get_name(data), ==, webkit_security_origin_get_host(origin)); + webkit_security_origin_unref(origin); + g_assert_cmpuint(webkit_website_data_get_types(data), ==, WEBKIT_WEBSITE_DATA_OFFLINE_APPLICATION_CACHE); + // Appcache size is unknown. + g_assert_cmpuint(webkit_website_data_get_size(data, WEBKIT_WEBSITE_DATA_OFFLINE_APPLICATION_CACHE), ==, 0); + + GList removeList = { data, nullptr, nullptr }; + test->remove(WEBKIT_WEBSITE_DATA_OFFLINE_APPLICATION_CACHE, &removeList); + dataList = test->fetch(WEBKIT_WEBSITE_DATA_OFFLINE_APPLICATION_CACHE); + g_assert(!dataList); + + // Clear all. + static const WebKitWebsiteDataTypes cacheAndAppcacheTypes = static_cast<WebKitWebsiteDataTypes>(WEBKIT_WEBSITE_DATA_OFFLINE_APPLICATION_CACHE | WEBKIT_WEBSITE_DATA_MEMORY_CACHE | WEBKIT_WEBSITE_DATA_DISK_CACHE); + test->clear(cacheAndAppcacheTypes, 0); + dataList = test->fetch(cacheAndAppcacheTypes); + g_assert(!dataList); +} + +static void testWebsiteDataCookies(WebsiteDataTest* test, gconstpointer) +{ + GList* dataList = test->fetch(WEBKIT_WEBSITE_DATA_COOKIES); + g_assert(!dataList); + + test->loadURI(kServer->getURIForPath("/empty").data()); + test->waitUntilLoadFinished(); + + dataList = test->fetch(WEBKIT_WEBSITE_DATA_COOKIES); + g_assert(dataList); + g_assert_cmpuint(g_list_length(dataList), ==, 1); + WebKitWebsiteData* data = static_cast<WebKitWebsiteData*>(dataList->data); + g_assert(data); + g_assert_cmpstr(webkit_website_data_get_name(data), ==, "127.0.0.1"); + g_assert_cmpuint(webkit_website_data_get_types(data), ==, WEBKIT_WEBSITE_DATA_COOKIES); + // Cookies size is unknown. + g_assert_cmpuint(webkit_website_data_get_size(data, WEBKIT_WEBSITE_DATA_COOKIES), ==, 0); + + GList removeList = { data, nullptr, nullptr }; + test->remove(WEBKIT_WEBSITE_DATA_COOKIES, &removeList); + dataList = test->fetch(WEBKIT_WEBSITE_DATA_COOKIES); + g_assert(!dataList); + + // Clear all. + static const WebKitWebsiteDataTypes cacheAndCookieTypes = static_cast<WebKitWebsiteDataTypes>(WEBKIT_WEBSITE_DATA_COOKIES | WEBKIT_WEBSITE_DATA_MEMORY_CACHE | WEBKIT_WEBSITE_DATA_DISK_CACHE); + test->clear(cacheAndCookieTypes, 0); + dataList = test->fetch(cacheAndCookieTypes); + g_assert(!dataList); +} + +void beforeAll() +{ + kServer = new WebKitTestServer(); + kServer->run(serverCallback); + + WebsiteDataTest::add("WebKitWebsiteData", "configuration", testWebsiteDataConfiguration); + WebViewTest::add("WebKitWebsiteData", "ephemeral", testWebsiteDataEphemeral); + WebsiteDataTest::add("WebKitWebsiteData", "cache", testWebsiteDataCache); + WebsiteDataTest::add("WebKitWebsiteData", "storage", testWebsiteDataStorage); + WebsiteDataTest::add("WebKitWebsiteData", "databases", testWebsiteDataDatabases); + WebsiteDataTest::add("WebKitWebsiteData", "appcache", testWebsiteDataAppcache); + WebsiteDataTest::add("WebKitWebsiteData", "cookies", testWebsiteDataCookies); +} + +void afterAll() +{ + delete kServer; +} diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/WebExtensionTest.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/WebExtensionTest.cpp index 757532f7a..f632bc371 100644 --- a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/WebExtensionTest.cpp +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/WebExtensionTest.cpp @@ -22,15 +22,14 @@ #include <JavaScriptCore/JSContextRef.h> #include <JavaScriptCore/JSRetainPtr.h> #include <gio/gio.h> +#include <gst/gst.h> #include <stdlib.h> #include <string.h> #include <webkit2/webkit-web-extension.h> #include <wtf/Deque.h> -#include <wtf/OwnPtr.h> -#include <wtf/PassOwnPtr.h> #include <wtf/ProcessID.h> -#include <wtf/gobject/GRefPtr.h> -#include <wtf/gobject/GUniquePtr.h> +#include <wtf/glib/GRefPtr.h> +#include <wtf/glib/GUniquePtr.h> #include <wtf/text/CString.h> static const char introspectionXML[] = @@ -46,25 +45,26 @@ static const char introspectionXML[] = " <arg type='t' name='pageID' direction='in'/>" " <arg type='s' name='script' direction='in'/>" " </method>" - " <method name='GetInitializationUserData'>" - " <arg type='s' name='userData' direction='out'/>" - " </method>" " <method name='GetProcessIdentifier'>" " <arg type='u' name='identifier' direction='out'/>" " </method>" + " <method name='RemoveAVPluginsFromGSTRegistry'>" + " </method>" " <signal name='DocumentLoaded'/>" + " <signal name='FormControlsAssociated'>" + " <arg type='s' name='formIds' direction='out'/>" + " </signal>" " <signal name='URIChanged'>" " <arg type='s' name='uri' direction='out'/>" " </signal>" " </interface>" "</node>"; -static GRefPtr<GVariant> initializationUserData; - typedef enum { DocumentLoadedSignal, URIChangedSignal, + FormControlsAssociatedSignal, } DelayedSignalType; struct DelayedSignal { @@ -73,17 +73,17 @@ struct DelayedSignal { { } - DelayedSignal(DelayedSignalType type, const char* uri) + DelayedSignal(DelayedSignalType type, const char* str) : type(type) - , uri(uri) + , str(str) { } DelayedSignalType type; - CString uri; + CString str; }; -Deque<OwnPtr<DelayedSignal>> delayedSignalsQueue; +Deque<DelayedSignal> delayedSignalsQueue; static void emitDocumentLoaded(GDBusConnection* connection) { @@ -98,13 +98,17 @@ static void emitDocumentLoaded(GDBusConnection* connection) g_assert(ok); } -static void documentLoadedCallback(WebKitWebPage*, WebKitWebExtension* extension) +static void documentLoadedCallback(WebKitWebPage* webPage, WebKitWebExtension* extension) { + WebKitDOMDocument* document = webkit_web_page_get_dom_document(webPage); + GRefPtr<WebKitDOMDOMWindow> window = adoptGRef(webkit_dom_document_get_default_view(document)); + webkit_dom_dom_window_webkit_message_handlers_post_message(window.get(), "dom", "DocumentLoaded"); + gpointer data = g_object_get_data(G_OBJECT(extension), "dbus-connection"); if (data) emitDocumentLoaded(G_DBUS_CONNECTION(data)); else - delayedSignalsQueue.append(adoptPtr(new DelayedSignal(DocumentLoadedSignal))); + delayedSignalsQueue.append(DelayedSignal(DocumentLoadedSignal)); } static void emitURIChanged(GDBusConnection* connection, const char* uri) @@ -126,11 +130,12 @@ static void uriChangedCallback(WebKitWebPage* webPage, GParamSpec* pspec, WebKit if (data) emitURIChanged(G_DBUS_CONNECTION(data), webkit_web_page_get_uri(webPage)); else - delayedSignalsQueue.append(adoptPtr(new DelayedSignal(URIChangedSignal, webkit_web_page_get_uri(webPage)))); + delayedSignalsQueue.append(DelayedSignal(URIChangedSignal, webkit_web_page_get_uri(webPage))); } static gboolean sendRequestCallback(WebKitWebPage*, WebKitURIRequest* request, WebKitURIResponse* redirectResponse, gpointer) { + gboolean returnValue = FALSE; const char* requestURI = webkit_uri_request_get_uri(request); g_assert(requestURI); @@ -153,17 +158,128 @@ static gboolean sendRequestCallback(WebKitWebPage*, WebKitURIRequest* request, W SoupMessageHeaders* headers = webkit_uri_request_get_http_headers(request); g_assert(headers); soup_message_headers_append(headers, "DNT", "1"); + } else if (g_str_has_suffix(requestURI, "/normal-change-request")) { + GUniquePtr<char> prefix(g_strndup(requestURI, strlen(requestURI) - strlen("/normal-change-request"))); + GUniquePtr<char> newURI(g_strdup_printf("%s/request-changed%s", prefix.get(), redirectResponse ? "-on-redirect" : "")); + webkit_uri_request_set_uri(request, newURI.get()); + } else if (g_str_has_suffix(requestURI, "/http-get-method")) { + g_assert_cmpstr(webkit_uri_request_get_http_method(request), ==, "GET"); + g_assert(webkit_uri_request_get_http_method(request) == SOUP_METHOD_GET); + } else if (g_str_has_suffix(requestURI, "/http-post-method")) { + g_assert_cmpstr(webkit_uri_request_get_http_method(request), ==, "POST"); + g_assert(webkit_uri_request_get_http_method(request) == SOUP_METHOD_POST); + returnValue = TRUE; } else if (g_str_has_suffix(requestURI, "/cancel-this.js")) + returnValue = TRUE; + + return returnValue; +} + +static GVariant* serializeContextMenu(WebKitContextMenu* menu) +{ + GVariantBuilder builder; + g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); + GList* items = webkit_context_menu_get_items(menu); + for (GList* it = items; it; it = g_list_next(it)) + g_variant_builder_add(&builder, "u", webkit_context_menu_item_get_stock_action(WEBKIT_CONTEXT_MENU_ITEM(it->data))); + return g_variant_builder_end(&builder); +} + +static GVariant* serializeNode(WebKitDOMNode* node) +{ + GVariantBuilder builder; + g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); + g_variant_builder_add(&builder, "{sv}", "Name", g_variant_new_take_string(webkit_dom_node_get_node_name(node))); + g_variant_builder_add(&builder, "{sv}", "Type", g_variant_new_uint32(webkit_dom_node_get_node_type(node))); + g_variant_builder_add(&builder, "{sv}", "Contents", g_variant_new_take_string(webkit_dom_node_get_text_content(node))); + WebKitDOMNode* parent = webkit_dom_node_get_parent_node(node); + g_variant_builder_add(&builder, "{sv}", "Parent", parent ? g_variant_new_take_string(webkit_dom_node_get_node_name(parent)) : g_variant_new_string("ROOT")); + return g_variant_builder_end(&builder); +} + +static gboolean contextMenuCallback(WebKitWebPage* page, WebKitContextMenu* menu, WebKitWebHitTestResult* hitTestResult, gpointer) +{ + const char* pageURI = webkit_web_page_get_uri(page); + if (!g_strcmp0(pageURI, "ContextMenuTestDefault")) { + webkit_context_menu_set_user_data(menu, serializeContextMenu(menu)); + return FALSE; + } + + if (!g_strcmp0(pageURI, "ContextMenuTestCustom")) { + // Remove Back and Forward, and add Inspector action. + webkit_context_menu_remove(menu, webkit_context_menu_first(menu)); + webkit_context_menu_remove(menu, webkit_context_menu_first(menu)); + webkit_context_menu_append(menu, webkit_context_menu_item_new_separator()); + webkit_context_menu_append(menu, webkit_context_menu_item_new_from_stock_action(WEBKIT_CONTEXT_MENU_ACTION_INSPECT_ELEMENT)); + webkit_context_menu_set_user_data(menu, serializeContextMenu(menu)); + return TRUE; + } + + if (!g_strcmp0(pageURI, "ContextMenuTestClear")) { + webkit_context_menu_remove_all(menu); + return TRUE; + } + + if (!g_strcmp0(pageURI, "ContextMenuTestNode")) { + WebKitDOMNode* node = webkit_web_hit_test_result_get_node(hitTestResult); + g_assert(WEBKIT_DOM_IS_NODE(node)); + webkit_context_menu_set_user_data(menu, serializeNode(node)); return TRUE; + } return FALSE; } +static void consoleMessageSentCallback(WebKitWebPage* webPage, WebKitConsoleMessage* consoleMessage) +{ + g_assert(consoleMessage); + GRefPtr<GVariant> variant = g_variant_new("(uusus)", webkit_console_message_get_source(consoleMessage), + webkit_console_message_get_level(consoleMessage), webkit_console_message_get_text(consoleMessage), + webkit_console_message_get_line(consoleMessage), webkit_console_message_get_source_id(consoleMessage)); + GUniquePtr<char> messageString(g_variant_print(variant.get(), FALSE)); + GRefPtr<WebKitDOMDOMWindow> window = adoptGRef(webkit_dom_document_get_default_view(webkit_web_page_get_dom_document(webPage))); + g_assert(WEBKIT_DOM_IS_DOM_WINDOW(window.get())); + webkit_dom_dom_window_webkit_message_handlers_post_message(window.get(), "console", messageString.get()); +} + + +static void emitFormControlsAssociated(GDBusConnection* connection, const char* formIds) +{ + bool ok = g_dbus_connection_emit_signal( + connection, + nullptr, + "/org/webkit/gtk/WebExtensionTest", + "org.webkit.gtk.WebExtensionTest", + "FormControlsAssociated", + g_variant_new("(s)", formIds), + nullptr); + g_assert(ok); +} + +static void formControlsAssociatedCallback(WebKitWebPage* webPage, GPtrArray* formElements, WebKitWebExtension* extension) +{ + GString* formIdsBuilder = g_string_new(nullptr); + for (int i = 0; i < formElements->len; ++i) { + g_assert(WEBKIT_DOM_IS_ELEMENT(g_ptr_array_index(formElements, i))); + auto domElement = WEBKIT_DOM_ELEMENT(g_ptr_array_index(formElements, i)); + g_string_append(formIdsBuilder, webkit_dom_element_get_id(domElement)); + } + GUniquePtr<char> formIds(g_string_free(formIdsBuilder, FALSE)); + gpointer data = g_object_get_data(G_OBJECT(extension), "dbus-connection"); + if (data) + emitFormControlsAssociated(G_DBUS_CONNECTION(data), formIds.get()); + else + delayedSignalsQueue.append(DelayedSignal(FormControlsAssociatedSignal, formIds.get())); +} + static void pageCreatedCallback(WebKitWebExtension* extension, WebKitWebPage* webPage, gpointer) { g_signal_connect(webPage, "document-loaded", G_CALLBACK(documentLoadedCallback), extension); g_signal_connect(webPage, "notify::uri", G_CALLBACK(uriChangedCallback), extension); - g_signal_connect(webPage, "send-request", G_CALLBACK(sendRequestCallback), 0); + g_signal_connect(webPage, "send-request", G_CALLBACK(sendRequestCallback), nullptr); + g_signal_connect(webPage, "context-menu", G_CALLBACK(contextMenuCallback), nullptr); + g_signal_connect(webPage, "console-message-sent", G_CALLBACK(consoleMessageSentCallback), nullptr); + g_signal_connect(webPage, "form-controls-associated", G_CALLBACK(formControlsAssociatedCallback), extension); } static JSValueRef echoCallback(JSContextRef jsContext, JSObjectRef, JSObjectRef, size_t argumentCount, const JSValueRef arguments[], JSValueRef*) @@ -233,14 +349,20 @@ static void methodCallCallback(GDBusConnection* connection, const char* sender, g_dbus_method_invocation_return_value(invocation, 0); } else if (!g_strcmp0(methodName, "AbortProcess")) { abort(); - } else if (!g_strcmp0(methodName, "GetInitializationUserData")) { - g_assert(initializationUserData); - g_assert(g_variant_is_of_type(initializationUserData.get(), G_VARIANT_TYPE_STRING)); - g_dbus_method_invocation_return_value(invocation, g_variant_new("(s)", - g_variant_get_string(initializationUserData.get(), nullptr))); } else if (!g_strcmp0(methodName, "GetProcessIdentifier")) { g_dbus_method_invocation_return_value(invocation, g_variant_new("(u)", static_cast<guint32>(getCurrentProcessID()))); + } else if (!g_strcmp0(methodName, "RemoveAVPluginsFromGSTRegistry")) { + gst_init(nullptr, nullptr); + static const char* avPlugins[] = { "libav", "omx", "vaapi", nullptr }; + GstRegistry* registry = gst_registry_get(); + for (unsigned i = 0; avPlugins[i]; ++i) { + if (GstPlugin* plugin = gst_registry_find_plugin(registry, avPlugins[i])) { + gst_registry_remove_plugin(registry, plugin); + gst_object_unref(plugin); + } + } + g_dbus_method_invocation_return_value(invocation, nullptr); } } @@ -268,38 +390,41 @@ static void busAcquiredCallback(GDBusConnection* connection, const char* name, g g_object_set_data(G_OBJECT(userData), "dbus-connection", connection); while (delayedSignalsQueue.size()) { - OwnPtr<DelayedSignal> delayedSignal = delayedSignalsQueue.takeFirst(); - switch (delayedSignal->type) { + DelayedSignal delayedSignal = delayedSignalsQueue.takeFirst(); + switch (delayedSignal.type) { case DocumentLoadedSignal: emitDocumentLoaded(connection); break; case URIChangedSignal: - emitURIChanged(connection, delayedSignal->uri.data()); + emitURIChanged(connection, delayedSignal.str.data()); + break; + case FormControlsAssociatedSignal: + emitFormControlsAssociated(connection, delayedSignal.str.data()); break; } } } -static GUniquePtr<char> makeBusName(GVariant* userData) +static void registerGResource(void) { - // When the web extension is used by TestMultiprocess, an uint32 - // identifier is passed as user data. It uniquely identifies - // the web process, and the UI side expects it added as suffix to - // the bus name. - if (userData && g_variant_is_of_type(userData, G_VARIANT_TYPE_UINT32)) - return GUniquePtr<char>(g_strdup_printf("org.webkit.gtk.WebExtensionTest%u", g_variant_get_uint32(userData))); - - return GUniquePtr<char>(g_strdup("org.webkit.gtk.WebExtensionTest")); + GUniquePtr<char> resourcesPath(g_build_filename(WEBKIT_EXEC_PATH, "TestWebKitAPI", "WebKit2Gtk", "resources", "webkit2gtk-tests-resources.gresource", nullptr)); + GResource* resource = g_resource_load(resourcesPath.get(), nullptr); + g_assert(resource); + + g_resources_register(resource); + g_resource_unref(resource); } extern "C" void webkit_web_extension_initialize_with_user_data(WebKitWebExtension* extension, GVariant* userData) { - initializationUserData = userData; - g_signal_connect(extension, "page-created", G_CALLBACK(pageCreatedCallback), extension); g_signal_connect(webkit_script_world_get_default(), "window-object-cleared", G_CALLBACK(windowObjectCleared), 0); - GUniquePtr<char> busName(makeBusName(userData)); + registerGResource(); + + g_assert(userData); + g_assert(g_variant_is_of_type(userData, G_VARIANT_TYPE_UINT32)); + GUniquePtr<char> busName(g_strdup_printf("org.webkit.gtk.WebExtensionTest%u", g_variant_get_uint32(userData))); g_bus_own_name( G_BUS_TYPE_SESSION, busName.get(), diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/WebProcessTest.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/WebProcessTest.cpp index 09d0785aa..bc058693d 100644 --- a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/WebProcessTest.cpp +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/WebProcessTest.cpp @@ -22,8 +22,11 @@ #include <JavaScriptCore/JSRetainPtr.h> #include <gio/gio.h> +#include <wtf/HashSet.h> #include <wtf/NeverDestroyed.h> -#include <wtf/gobject/GUniquePtr.h> +#include <wtf/glib/GUniquePtr.h> + +static HashSet<GObject*> s_watchedObjects; typedef HashMap<String, std::function<std::unique_ptr<WebProcessTest> ()>> TestsMap; static TestsMap& testsMap() @@ -34,7 +37,15 @@ static TestsMap& testsMap() void WebProcessTest::add(const String& testName, std::function<std::unique_ptr<WebProcessTest> ()> closure) { - testsMap().add(testName, std::move(closure)); + testsMap().add(testName, WTFMove(closure)); +} + +void WebProcessTest::assertObjectIsDeletedWhenTestFinishes(GObject* object) +{ + s_watchedObjects.add(object); + g_object_weak_ref(object, [](gpointer, GObject* finalizedObject) { + s_watchedObjects.remove(finalizedObject); + }, nullptr); } std::unique_ptr<WebProcessTest> WebProcessTest::create(const String& testName) @@ -53,6 +64,14 @@ static JSValueRef runTest(JSContextRef context, JSObjectRef function, JSObjectRe WebKitWebPage* webPage = WEBKIT_WEB_PAGE(JSObjectGetPrivate(thisObject)); g_assert(WEBKIT_IS_WEB_PAGE(webPage)); + // Test /WebKitDOMNode/dom-cache is an exception, because it's called 3 times, so + // the WebPage is destroyed after the third time. + if (g_str_equal(testPath.get(), "WebKitDOMNode/dom-cache")) { + static unsigned domCacheTestRunCount = 0; + if (++domCacheTestRunCount == 3) + WebProcessTest::assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webPage)); + } else + WebProcessTest::assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webPage)); std::unique_ptr<WebProcessTest> test = WebProcessTest::create(String::fromUTF8(testPath.get())); return JSValueMakeBoolean(context, test->runTest(g_strrstr(testPath.get(), "/") + 1, webPage)); @@ -67,6 +86,16 @@ static const JSStaticFunction webProcessTestRunnerStaticFunctions[] = static void webProcessTestRunnerFinalize(JSObjectRef object) { g_object_unref(JSObjectGetPrivate(object)); + + if (s_watchedObjects.isEmpty()) + return; + + g_print("Leaked objects in WebProcess:"); + for (const auto object : s_watchedObjects) + g_print(" %s(%p)", g_type_name_from_instance(reinterpret_cast<GTypeInstance*>(object)), object); + g_print("\n"); + + g_assert(s_watchedObjects.isEmpty()); } static void windowObjectClearedCallback(WebKitScriptWorld* world, WebKitWebPage* webPage, WebKitFrame* frame, WebKitWebExtension* extension) diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/WebProcessTest.h b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/WebProcessTest.h index fb4dd5d33..3715eb91e 100644 --- a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/WebProcessTest.h +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/WebProcessTest.h @@ -19,7 +19,7 @@ #include <webkit2/webkit-web-extension.h> #include <wtf/HashMap.h> -#include <wtf/gobject/GRefPtr.h> +#include <wtf/glib/GRefPtr.h> #include <wtf/text/StringHash.h> #include <wtf/text/WTFString.h> @@ -28,6 +28,8 @@ public: virtual ~WebProcessTest() { } virtual bool runTest(const char* testName, WebKitWebPage*) = 0; + static void assertObjectIsDeletedWhenTestFinishes(GObject*); + static void add(const String& testName, std::function<std::unique_ptr<WebProcessTest> ()>); static std::unique_ptr<WebProcessTest> create(const String& testName); }; diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/blank.ico b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/blank.ico Binary files differnew file mode 100644 index 000000000..ea848b991 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/blank.ico diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/boring.html b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/boring.html new file mode 100644 index 000000000..c0eeb49ee --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/boring.html @@ -0,0 +1 @@ +<p>This is a boring HTML file.</p> diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/link-title.js b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/link-title.js new file mode 100644 index 000000000..2c824da38 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/link-title.js @@ -0,0 +1 @@ +window.document.getElementById('WebKitLink').title; diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/silence.mpg b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/silence.mpg Binary files differnew file mode 100644 index 000000000..b6bbf2088 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/silence.mpg diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/simple.json b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/simple.json new file mode 100644 index 000000000..76519fa8c --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/simple.json @@ -0,0 +1 @@ +{"key": "value"} diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/test-cert.pem b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/test-cert.pem new file mode 100644 index 000000000..b34301f25 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/test-cert.pem @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIIB9jCCAV+gAwIBAgIJALeuXBo+vwz9MA0GCSqGSIb3DQEBBQUAMBQxEjAQBgNV +BAMMCTEyNy4wLjAuMTAeFw0xMjA3MTIxMjQ4MjRaFw0yMjA3MTAxMjQ4MjRaMBQx +EjAQBgNVBAMMCTEyNy4wLjAuMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA +0TUzOQxHBIKDD2mkuq+tU92mQvDZg73B0G+Nhr2T2G6MbcLqIwjg1QYtBZWJ83tZ +xMMEfiweHLF85Z9ohavAgxJlKG7YmvZO79KkFpmjV2W5CVRm0eYMPnzmxNCoaYqo +DLl0zsH6KZOLPKu/fX4eDX9XpAP1f83hWB1UFBmHKN8CAwEAAaNQME4wHQYDVR0O +BBYEFDHv5ZQ1BdmhzTsDUEoY55EXyUdKMB8GA1UdIwQYMBaAFDHv5ZQ1BdmhzTsD +UEoY55EXyUdKMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAh3qMBx7v +jSodMf3OyTqTLE7deLnmnCeBVpgzxRZEoizcGqYcjiqO27i5N5Z6KVQsnITnLiyC +mUtuR5KnF69uTKUw4m/ugZe5whjig5Mq2l410KVK6EeG4tdLlfXR+wi4U5K4KjP6 +p4nchQUXLa2zcbJn+VBexJn6/9wdhr+DUGY= +-----END CERTIFICATE----- diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/test-key.pem b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/test-key.pem new file mode 100644 index 000000000..9036222ce --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/test-key.pem @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBANE1MzkMRwSCgw9p +pLqvrVPdpkLw2YO9wdBvjYa9k9hujG3C6iMI4NUGLQWVifN7WcTDBH4sHhyxfOWf +aIWrwIMSZShu2Jr2Tu/SpBaZo1dluQlUZtHmDD585sTQqGmKqAy5dM7B+imTizyr +v31+Hg1/V6QD9X/N4VgdVBQZhyjfAgMBAAECgYB2QwOUsRsIMprRwJ9tJNfvO7G7 +z5i1/zOrlxPC4jHMPBnIBlICwgcOhLI4oOLdr5H8R12n0VqoT7DRwP396iwlJipF +iO1heDMn/8z8LPGwkCK/+ck04rMDksxWIdMwYKBXt9ahnJ/xRLzQ1/3AJiAGnoe5 +/QLXQweofd4mmfsjKQJBAO2CwT7uMP6nMjXgtVMJq5QP8UbeCS1sEOPJJbHuDxJB +/HePQHBjq4kzG6CL4oO7T+5fDv4g+fIIHzuXerZ0imsCQQDhfmiTIc9OucEIfg6/ +ms0JiKSmWc+qoiOCtrILuQvFoNwJRciQANqeJs6wpaDvevSUvBLGfG/7b3HvaE5X +iqBdAkBEQIvp2qcHtuJN60oQF7pPrRknxUyb2e8sljQX4pJAK+gyL19ULMAxiBdL +Vod8VYqNtJFpY+6Pp9fZ1xjzb6ALAkEA4JzrDAw0lQXA+3WduUw4ixOadr2ldyG0 +36KebcDwsfZO18m0Q4UmPz0Gy7zgN0wxzuochaw0W6+iPUiYKOlEXQJBAMWQrPlu +rrinoZS2f8doJ9BNNUa+RNpMug6UXc55qoUJlyiXEh+tu4AaMOtxuGIyC0sAcuw6 +XdAPVPXKd7Mne70= +-----END PRIVATE KEY----- diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/track.ogg b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/track.ogg Binary files differnew file mode 100644 index 000000000..c569c8f40 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/track.ogg diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/webkit2gtk-tests.gresource.xml b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/webkit2gtk-tests.gresource.xml new file mode 100644 index 000000000..87bc50158 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/webkit2gtk-tests.gresource.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<gresources> + <gresource prefix="/org/webkit/webkit2gtk/tests/"> + <file alias="boring.html">Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/boring.html</file> + <file alias="link-title.js">Tools/TestWebKitAPI/Tests/WebKit2Gtk/resources/link-title.js</file> + </gresource> +</gresources> |