| 1 | /* |
| 2 | * Copyright (C) 2013 Igalia S.L. |
| 3 | * |
| 4 | * This library is free software; you can redistribute it and/or |
| 5 | * modify it under the terms of the GNU Library General Public |
| 6 | * License as published by the Free Software Foundation; either |
| 7 | * version 2 of the License, or (at your option) any later version. |
| 8 | * |
| 9 | * This library is distributed in the hope that it will be useful, |
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 12 | * Library General Public License for more details. |
| 13 | * |
| 14 | * You should have received a copy of the GNU Library General Public License |
| 15 | * along with this library; see the file COPYING.LIB. If not, write to |
| 16 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| 17 | * Boston, MA 02110-1301, USA. |
| 18 | */ |
| 19 | |
| 20 | #include "config.h" |
| 21 | #include "WebProcessTest.h" |
| 22 | |
| 23 | #include "WebKitWebExtensionPrivate.h" |
| 24 | #include <gio/gio.h> |
| 25 | #include <jsc/jsc.h> |
| 26 | #include <wtf/HashSet.h> |
| 27 | #include <wtf/NeverDestroyed.h> |
| 28 | #include <wtf/glib/GUniquePtr.h> |
| 29 | |
| 30 | static HashSet<GObject*> s_watchedObjects; |
| 31 | |
| 32 | typedef HashMap<String, std::function<std::unique_ptr<WebProcessTest> ()>> TestsMap; |
| 33 | static TestsMap& testsMap() |
| 34 | { |
| 35 | static NeverDestroyed<TestsMap> s_testsMap; |
| 36 | return s_testsMap; |
| 37 | } |
| 38 | |
| 39 | static void checkLeaks() |
| 40 | { |
| 41 | if (s_watchedObjects.isEmpty()) |
| 42 | return; |
| 43 | |
| 44 | g_print("Leaked objects in WebProcess:" ); |
| 45 | for (const auto object : s_watchedObjects) |
| 46 | g_print(" %s(%p)" , g_type_name_from_instance(reinterpret_cast<GTypeInstance*>(object)), object); |
| 47 | g_print("\n" ); |
| 48 | |
| 49 | g_assert_true(s_watchedObjects.isEmpty()); |
| 50 | } |
| 51 | |
| 52 | void WebProcessTest::add(const String& testName, std::function<std::unique_ptr<WebProcessTest> ()> closure) |
| 53 | { |
| 54 | testsMap().add(testName, WTFMove(closure)); |
| 55 | } |
| 56 | |
| 57 | void WebProcessTest::assertObjectIsDeletedWhenTestFinishes(GObject* object) |
| 58 | { |
| 59 | s_watchedObjects.add(object); |
| 60 | g_object_weak_ref(object, [](gpointer, GObject* finalizedObject) { |
| 61 | s_watchedObjects.remove(finalizedObject); |
| 62 | }, nullptr); |
| 63 | } |
| 64 | |
| 65 | std::unique_ptr<WebProcessTest> WebProcessTest::create(const String& testName) |
| 66 | { |
| 67 | g_assert_true(testsMap().contains(testName)); |
| 68 | return testsMap().get(testName)(); |
| 69 | } |
| 70 | |
| 71 | static gboolean runTest(WebKitWebPage* webPage, const char* testPath) |
| 72 | { |
| 73 | g_assert_true(WEBKIT_IS_WEB_PAGE(webPage)); |
| 74 | WebProcessTest::assertObjectIsDeletedWhenTestFinishes(G_OBJECT(webPage)); |
| 75 | g_assert_nonnull(testPath); |
| 76 | |
| 77 | std::unique_ptr<WebProcessTest> test = WebProcessTest::create(String::fromUTF8(testPath)); |
| 78 | return test->runTest(g_strrstr(testPath, "/" ) + 1, webPage); |
| 79 | } |
| 80 | |
| 81 | static void webProcessTestRunnerFinalize(WebKitWebPage* webPage) |
| 82 | { |
| 83 | g_object_unref(webPage); |
| 84 | checkLeaks(); |
| 85 | } |
| 86 | |
| 87 | static void windowObjectClearedCallback(WebKitScriptWorld* world, WebKitWebPage* webPage, WebKitFrame* frame, WebKitWebExtension* extension) |
| 88 | { |
| 89 | if (g_strcmp0(webkit_web_page_get_uri(webPage), "webprocess://test" )) |
| 90 | return; |
| 91 | |
| 92 | GRefPtr<JSCContext> context = adoptGRef(webkit_frame_get_js_context_for_script_world(frame, world)); |
| 93 | WebProcessTest::assertObjectIsDeletedWhenTestFinishes(G_OBJECT(context.get())); |
| 94 | auto* jsClass = jsc_context_register_class(context.get(), "WebProcessTestRunner" , nullptr, nullptr, reinterpret_cast<GDestroyNotify>(webProcessTestRunnerFinalize)); |
| 95 | WebProcessTest::assertObjectIsDeletedWhenTestFinishes(G_OBJECT(jsClass)); |
| 96 | jsc_class_add_method(jsClass, "runTest" , G_CALLBACK(runTest), NULL, NULL, G_TYPE_BOOLEAN, 1, G_TYPE_STRING); |
| 97 | GRefPtr<JSCValue> testRunner = adoptGRef(jsc_value_new_object(context.get(), g_object_ref(webPage), jsClass)); |
| 98 | WebProcessTest::assertObjectIsDeletedWhenTestFinishes(G_OBJECT(testRunner.get())); |
| 99 | jsc_context_set_value(context.get(), "WebProcessTestRunner" , testRunner.get()); |
| 100 | } |
| 101 | |
| 102 | extern "C" void webkit_web_extension_initialize(WebKitWebExtension* extension) |
| 103 | { |
| 104 | webkitWebExtensionSetGarbageCollectOnPageDestroy(extension); |
| 105 | g_signal_connect(webkit_script_world_get_default(), "window-object-cleared" , G_CALLBACK(windowObjectClearedCallback), extension); |
| 106 | } |
| 107 | |
| 108 | static void __attribute__((destructor)) checkLeaksAtExit() |
| 109 | { |
| 110 | checkLeaks(); |
| 111 | } |
| 112 | |