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 | |