/* * 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 #include #include #include #include class UserContentManagerTest : public WebViewTest { public: MAKE_GLIB_TEST_FIXTURE(UserContentManagerTest); UserContentManagerTest() : WebViewTest(webkit_user_content_manager_new()) { // A reference is leaked when passing the result of webkit_user_content_manager_new() // directly to webkit_web_view_new_with_user_content_manager() above. Adopting the // reference here avoids the leak. m_userContentManager = adoptGRef(webkit_web_view_get_user_content_manager(m_webView)); assertObjectIsDeletedWhenTestFinishes(G_OBJECT(m_userContentManager.get())); } GRefPtr m_userContentManager; }; static WebKitTestServer* kServer; // These are all here so that they can be changed easily, if necessary. static const char* kStyleSheetHTML = "
Sweet stylez!
"; 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('
Generated by a script
')"; static const char* kScriptTestScript = "document.getElementById('item').innerText"; static const char* kScriptTestScriptResult = "Generated by a script"; static void testWebViewNewWithUserContentManager(Test* test, gconstpointer) { GRefPtr userContentManager1 = adoptGRef(webkit_user_content_manager_new()); test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(userContentManager1.get())); GRefPtr 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 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 error; WebKitJavascriptResult* javascriptResult = test->runJavaScriptAndWaitUntilFinished(kStyleSheetTestScript, &error.outPtr()); g_assert(javascriptResult); g_assert(!error.get()); GUniquePtr 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 error; if (WebKitJavascriptResult* javascriptResult = test->runJavaScriptAndWaitUntilFinished(kScriptTestScript, &error.outPtr())) { g_assert(!error.get()); GUniquePtr 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(UserContentManagerTest* 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(UserContentManagerTest* 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 UserContentManagerTest { public: MAKE_GLIB_TEST_FIXTURE(UserScriptMessageTest); UserScriptMessageTest() : UserContentManagerTest() , 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(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 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 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("", nullptr); test->waitUntilLoadFinished(); // Check that the "window.webkit.messageHandlers" namespace exists. GUniqueOutPtr error; WebKitJavascriptResult* javascriptResult = test->runJavaScriptAndWaitUntilFinished("window.webkit.messageHandlers ? 'y' : 'n';", &error.outPtr()); g_assert(javascriptResult); g_assert(!error.get()); GUniquePtr 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("1", nullptr); WebKitJavascriptResult* javascriptResult = test->waitUntilMessageReceived("dom"); g_assert(javascriptResult); GUniquePtr valueString(WebViewTest::javascriptResultToCString(javascriptResult)); g_assert_cmpstr(valueString.get(), ==, "DocumentLoaded"); test->unregisterHandler("dom"); g_assert(test->registerHandler("dom-convenience")); test->loadHtml("2", nullptr); javascriptResult = test->waitUntilMessageReceived("dom-convenience"); g_assert(javascriptResult); valueString.reset(WebViewTest::javascriptResultToCString(javascriptResult)); g_assert_cmpstr(valueString.get(), ==, "DocumentLoaded"); test->unregisterHandler("dom-convenience"); } 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); UserContentManagerTest::add("WebKitUserContentManager", "injected-style-sheet", testUserContentManagerInjectedStyleSheet); UserContentManagerTest::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; }