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
30static HashSet<GObject*> s_watchedObjects;
31
32typedef HashMap<String, std::function<std::unique_ptr<WebProcessTest> ()>> TestsMap;
33static TestsMap& testsMap()
34{
35 static NeverDestroyed<TestsMap> s_testsMap;
36 return s_testsMap;
37}
38
39static 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
52void WebProcessTest::add(const String& testName, std::function<std::unique_ptr<WebProcessTest> ()> closure)
53{
54 testsMap().add(testName, WTFMove(closure));
55}
56
57void 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
65std::unique_ptr<WebProcessTest> WebProcessTest::create(const String& testName)
66{
67 g_assert_true(testsMap().contains(testName));
68 return testsMap().get(testName)();
69}
70
71static 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
81static void webProcessTestRunnerFinalize(WebKitWebPage* webPage)
82{
83 g_object_unref(webPage);
84 checkLeaks();
85}
86
87static 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
102extern "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
108static void __attribute__((destructor)) checkLeaksAtExit()
109{
110 checkLeaks();
111}
112