Forráskód Böngészése

LibWeb: Add a simple `internals` objects only available during testing

This object is available as `window.internals` (or just `internals`) and
is only accessible while running in "test mode".

This first version only has one API: gc(), which triggers a garbage
collection immediately.

In the future, we can add more APIs here to help us test parts of the
engine that are hard or impossible to reach via public web APIs.
Andreas Kling 2 éve
szülő
commit
ec24d7555a

+ 3 - 0
Ladybird/WebContent/main.cpp

@@ -23,6 +23,7 @@
 #include <LibJS/Bytecode/Interpreter.h>
 #include <LibJS/Bytecode/Interpreter.h>
 #include <LibMain/Main.h>
 #include <LibMain/Main.h>
 #include <LibWeb/Bindings/MainThreadVM.h>
 #include <LibWeb/Bindings/MainThreadVM.h>
+#include <LibWeb/HTML/Window.h>
 #include <LibWeb/Loader/ContentFilter.h>
 #include <LibWeb/Loader/ContentFilter.h>
 #include <LibWeb/Loader/FrameLoader.h>
 #include <LibWeb/Loader/FrameLoader.h>
 #include <LibWeb/Loader/ResourceLoader.h>
 #include <LibWeb/Loader/ResourceLoader.h>
@@ -89,6 +90,8 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
         Web::WebSockets::WebSocketClientManager::initialize(Ladybird::WebSocketClientManagerQt::create());
         Web::WebSockets::WebSocketClientManager::initialize(Ladybird::WebSocketClientManagerQt::create());
     }
     }
 
 
+    Web::HTML::Window::set_internals_object_exposed(is_layout_test_mode);
+
     JS::Bytecode::Interpreter::set_enabled(use_javascript_bytecode);
     JS::Bytecode::Interpreter::set_enabled(use_javascript_bytecode);
 
 
     VERIFY(webcontent_fd_passing_socket >= 0);
     VERIFY(webcontent_fd_passing_socket >= 0);

+ 5 - 0
Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp

@@ -3126,6 +3126,7 @@ using namespace Web::FileAPI;
 using namespace Web::Geometry;
 using namespace Web::Geometry;
 using namespace Web::HighResolutionTime;
 using namespace Web::HighResolutionTime;
 using namespace Web::HTML;
 using namespace Web::HTML;
+using namespace Web::Internals;
 using namespace Web::IntersectionObserver;
 using namespace Web::IntersectionObserver;
 using namespace Web::RequestIdleCallback;
 using namespace Web::RequestIdleCallback;
 using namespace Web::ResizeObserver;
 using namespace Web::ResizeObserver;
@@ -3342,6 +3343,7 @@ using namespace Web::FileAPI;
 using namespace Web::Geometry;
 using namespace Web::Geometry;
 using namespace Web::HighResolutionTime;
 using namespace Web::HighResolutionTime;
 using namespace Web::HTML;
 using namespace Web::HTML;
+using namespace Web::Internals;
 using namespace Web::IntersectionObserver;
 using namespace Web::IntersectionObserver;
 using namespace Web::PerformanceTimeline;
 using namespace Web::PerformanceTimeline;
 using namespace Web::RequestIdleCallback;
 using namespace Web::RequestIdleCallback;
@@ -3723,6 +3725,7 @@ using namespace Web::FileAPI;
 using namespace Web::Geometry;
 using namespace Web::Geometry;
 using namespace Web::HighResolutionTime;
 using namespace Web::HighResolutionTime;
 using namespace Web::HTML;
 using namespace Web::HTML;
+using namespace Web::Internals;
 using namespace Web::IntersectionObserver;
 using namespace Web::IntersectionObserver;
 using namespace Web::NavigationTiming;
 using namespace Web::NavigationTiming;
 using namespace Web::PerformanceTimeline;
 using namespace Web::PerformanceTimeline;
@@ -3858,6 +3861,7 @@ using namespace Web::FileAPI;
 using namespace Web::Geometry;
 using namespace Web::Geometry;
 using namespace Web::HighResolutionTime;
 using namespace Web::HighResolutionTime;
 using namespace Web::HTML;
 using namespace Web::HTML;
+using namespace Web::Internals;
 using namespace Web::IntersectionObserver;
 using namespace Web::IntersectionObserver;
 using namespace Web::NavigationTiming;
 using namespace Web::NavigationTiming;
 using namespace Web::PerformanceTimeline;
 using namespace Web::PerformanceTimeline;
@@ -3990,6 +3994,7 @@ using namespace Web::FileAPI;
 using namespace Web::Geometry;
 using namespace Web::Geometry;
 using namespace Web::HighResolutionTime;
 using namespace Web::HighResolutionTime;
 using namespace Web::HTML;
 using namespace Web::HTML;
+using namespace Web::Internals;
 using namespace Web::IntersectionObserver;
 using namespace Web::IntersectionObserver;
 using namespace Web::NavigationTiming;
 using namespace Web::NavigationTiming;
 using namespace Web::PerformanceTimeline;
 using namespace Web::PerformanceTimeline;

+ 1 - 0
Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/Namespaces.h

@@ -23,6 +23,7 @@ static constexpr Array libweb_interface_namespaces = {
     "Geometry"sv,
     "Geometry"sv,
     "HTML"sv,
     "HTML"sv,
     "HighResolutionTime"sv,
     "HighResolutionTime"sv,
+    "Internals"sv,
     "IntersectionObserver"sv,
     "IntersectionObserver"sv,
     "NavigationTiming"sv,
     "NavigationTiming"sv,
     "RequestIdleCallback"sv,
     "RequestIdleCallback"sv,

+ 4 - 4
Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateWindowOrWorkerInterfaces.cpp

@@ -178,7 +178,7 @@ void Intrinsics::create_web_namespace<@namespace_class@>(JS::Realm& realm)
 )~~~");
 )~~~");
     };
     };
 
 
-    auto add_interface = [&](SourceGenerator& gen, StringView name, StringView prototype_class, StringView constructor_class, Optional<LegacyConstructor> const& legacy_constructor) {
+    auto add_interface = [](SourceGenerator& gen, StringView name, StringView prototype_class, StringView constructor_class, Optional<LegacyConstructor> const& legacy_constructor) {
         gen.set("interface_name", name);
         gen.set("interface_name", name);
         gen.set("prototype_class", prototype_class);
         gen.set("prototype_class", prototype_class);
         gen.set("constructor_class", constructor_class);
         gen.set("constructor_class", constructor_class);
@@ -434,6 +434,8 @@ static ErrorOr<ExposedTo> parse_exposure_set(IDL::Interface& interface)
     auto exposed = maybe_exposed.value().trim_whitespace();
     auto exposed = maybe_exposed.value().trim_whitespace();
     if (exposed == "*"sv)
     if (exposed == "*"sv)
         return ExposedTo::All;
         return ExposedTo::All;
+    if (exposed == "Nobody"sv)
+        return ExposedTo::Nobody;
     if (exposed == "Window"sv)
     if (exposed == "Window"sv)
         return ExposedTo::Window;
         return ExposedTo::Window;
     if (exposed == "Worker"sv)
     if (exposed == "Worker"sv)
@@ -477,10 +479,8 @@ ErrorOr<void> add_to_interface_sets(IDL::Interface& interface, Vector<IDL::Inter
 {
 {
     // TODO: Add service worker exposed and audio worklet exposed
     // TODO: Add service worker exposed and audio worklet exposed
     auto whom = TRY(parse_exposure_set(interface));
     auto whom = TRY(parse_exposure_set(interface));
-    VERIFY(whom != ExposedTo::Nobody);
 
 
-    if ((whom & ExposedTo::Window) || (whom & ExposedTo::DedicatedWorker) || (whom & ExposedTo::SharedWorker))
-        intrinsics.append(interface);
+    intrinsics.append(interface);
 
 
     if (whom & ExposedTo::Window)
     if (whom & ExposedTo::Window)
         window_exposed.append(interface);
         window_exposed.append(interface);

+ 3 - 0
Tests/LibWeb/Text/expected/internals.txt

@@ -0,0 +1,3 @@
+[object Internals]
+function gc() { [native code] }
+OK

+ 9 - 0
Tests/LibWeb/Text/input/internals.html

@@ -0,0 +1,9 @@
+<script src="include.js"></script>
+<script>
+    test(() => {
+        println(window.internals);
+        println(internals.gc);
+        internals.gc();
+        println("OK");
+    });
+</script>

+ 1 - 0
Userland/Libraries/LibWeb/CMakeLists.txt

@@ -397,6 +397,7 @@ set(SOURCES
     Infra/ByteSequences.cpp
     Infra/ByteSequences.cpp
     Infra/JSON.cpp
     Infra/JSON.cpp
     Infra/Strings.cpp
     Infra/Strings.cpp
+    Internals/Internals.cpp
     IntersectionObserver/IntersectionObserver.cpp
     IntersectionObserver/IntersectionObserver.cpp
     IntersectionObserver/IntersectionObserverEntry.cpp
     IntersectionObserver/IntersectionObserverEntry.cpp
     Layout/AudioBox.cpp
     Layout/AudioBox.cpp

+ 4 - 0
Userland/Libraries/LibWeb/Forward.h

@@ -454,6 +454,10 @@ namespace Web::HighResolutionTime {
 class Performance;
 class Performance;
 }
 }
 
 
+namespace Web::Internals {
+class Internals;
+}
+
 namespace Web::IntersectionObserver {
 namespace Web::IntersectionObserver {
 class IntersectionObserver;
 class IntersectionObserver;
 class IntersectionObserverEntry;
 class IntersectionObserverEntry;

+ 12 - 1
Userland/Libraries/LibWeb/HTML/Window.cpp

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2020-2022, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2020-2023, Andreas Kling <kling@serenityos.org>
  * Copyright (c) 2021-2022, Sam Atkins <atkinssj@serenityos.org>
  * Copyright (c) 2021-2022, Sam Atkins <atkinssj@serenityos.org>
  * Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
  * Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
  *
  *
@@ -51,6 +51,7 @@
 #include <LibWeb/HighResolutionTime/TimeOrigin.h>
 #include <LibWeb/HighResolutionTime/TimeOrigin.h>
 #include <LibWeb/Infra/Base64.h>
 #include <LibWeb/Infra/Base64.h>
 #include <LibWeb/Infra/CharacterTypes.h>
 #include <LibWeb/Infra/CharacterTypes.h>
+#include <LibWeb/Internals/Internals.h>
 #include <LibWeb/Layout/Viewport.h>
 #include <LibWeb/Layout/Viewport.h>
 #include <LibWeb/Page/Page.h>
 #include <LibWeb/Page/Page.h>
 #include <LibWeb/RequestIdleCallback/IdleDeadline.h>
 #include <LibWeb/RequestIdleCallback/IdleDeadline.h>
@@ -805,6 +806,13 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::CallbackType>> Window::byte_length_
     return JS::NonnullGCPtr { *m_byte_length_queuing_strategy_size_function };
     return JS::NonnullGCPtr { *m_byte_length_queuing_strategy_size_function };
 }
 }
 
 
+static bool s_internals_object_exposed = false;
+
+void Window::set_internals_object_exposed(bool exposed)
+{
+    s_internals_object_exposed = exposed;
+}
+
 WebIDL::ExceptionOr<void> Window::initialize_web_interfaces(Badge<WindowEnvironmentSettingsObject>)
 WebIDL::ExceptionOr<void> Window::initialize_web_interfaces(Badge<WindowEnvironmentSettingsObject>)
 {
 {
     auto& realm = this->realm();
     auto& realm = this->realm();
@@ -815,6 +823,9 @@ WebIDL::ExceptionOr<void> Window::initialize_web_interfaces(Badge<WindowEnvironm
     MUST_OR_THROW_OOM(Bindings::WindowGlobalMixin::initialize(realm, *this));
     MUST_OR_THROW_OOM(Bindings::WindowGlobalMixin::initialize(realm, *this));
     MUST_OR_THROW_OOM(WindowOrWorkerGlobalScopeMixin::initialize(realm));
     MUST_OR_THROW_OOM(WindowOrWorkerGlobalScopeMixin::initialize(realm));
 
 
+    if (s_internals_object_exposed)
+        define_direct_property("internals", MUST_OR_THROW_OOM(heap().allocate<Internals::Internals>(realm, realm)), JS::default_attributes);
+
     return {};
     return {};
 }
 }
 
 

+ 2 - 0
Userland/Libraries/LibWeb/HTML/Window.h

@@ -190,6 +190,8 @@ public:
     HighResolutionTime::DOMHighResTimeStamp get_last_activation_timestamp() const { return m_last_activation_timestamp; }
     HighResolutionTime::DOMHighResTimeStamp get_last_activation_timestamp() const { return m_last_activation_timestamp; }
     void set_last_activation_timestamp(HighResolutionTime::DOMHighResTimeStamp timestamp) { m_last_activation_timestamp = timestamp; }
     void set_last_activation_timestamp(HighResolutionTime::DOMHighResTimeStamp timestamp) { m_last_activation_timestamp = timestamp; }
 
 
+    static void set_internals_object_exposed(bool);
+
 private:
 private:
     explicit Window(JS::Realm&);
     explicit Window(JS::Realm&);
 
 

+ 33 - 0
Userland/Libraries/LibWeb/Internals/Internals.cpp

@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2023, Andreas Kling <kling@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <LibJS/Runtime/VM.h>
+#include <LibWeb/Bindings/InternalsPrototype.h>
+#include <LibWeb/Bindings/Intrinsics.h>
+#include <LibWeb/Internals/Internals.h>
+
+namespace Web::Internals {
+
+Internals::Internals(JS::Realm& realm)
+    : Bindings::PlatformObject(realm)
+{
+}
+
+Internals::~Internals() = default;
+
+JS::ThrowCompletionOr<void> Internals::initialize(JS::Realm& realm)
+{
+    TRY(Base::initialize(realm));
+    Object::set_prototype(&Bindings::ensure_web_prototype<Bindings::InternalsPrototype>(realm, "Internals"));
+    return {};
+}
+
+void Internals::gc()
+{
+    vm().heap().collect_garbage();
+}
+
+}

+ 26 - 0
Userland/Libraries/LibWeb/Internals/Internals.h

@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2023, Andreas Kling <kling@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <LibWeb/Bindings/PlatformObject.h>
+
+namespace Web::Internals {
+
+class Internals final : public Bindings::PlatformObject {
+    WEB_PLATFORM_OBJECT(Internals, Bindings::PlatformObject);
+
+public:
+    virtual ~Internals() override;
+
+    void gc();
+
+private:
+    explicit Internals(JS::Realm&);
+    virtual JS::ThrowCompletionOr<void> initialize(JS::Realm&) override;
+};
+
+}

+ 5 - 0
Userland/Libraries/LibWeb/Internals/Internals.idl

@@ -0,0 +1,5 @@
+[Exposed=Nobody] interface Internals {
+
+    undefined gc();
+
+};

+ 1 - 0
Userland/Libraries/LibWeb/idl_files.cmake

@@ -183,6 +183,7 @@ libweb_js_bindings(HTML/WorkerGlobalScope)
 libweb_js_bindings(HTML/WorkerLocation)
 libweb_js_bindings(HTML/WorkerLocation)
 libweb_js_bindings(HTML/WorkerNavigator)
 libweb_js_bindings(HTML/WorkerNavigator)
 libweb_js_bindings(HighResolutionTime/Performance)
 libweb_js_bindings(HighResolutionTime/Performance)
+libweb_js_bindings(Internals/Internals)
 libweb_js_bindings(IntersectionObserver/IntersectionObserver)
 libweb_js_bindings(IntersectionObserver/IntersectionObserver)
 libweb_js_bindings(IntersectionObserver/IntersectionObserverEntry)
 libweb_js_bindings(IntersectionObserver/IntersectionObserverEntry)
 libweb_js_bindings(NavigationTiming/PerformanceTiming)
 libweb_js_bindings(NavigationTiming/PerformanceTiming)