From 980e7156686d9684a247a48e203b8cd9f1b93374 Mon Sep 17 00:00:00 2001 From: Jelle Raaijmakers Date: Wed, 15 Oct 2025 09:15:48 +0200 Subject: [PATCH 1/3] CI: Run js-benchmarks with optional wasm binary If we run js-benchmarks against older builds that did not yet include the wasm repl, make sure we skip extracting the file and passing the `--wasm-executable` argument. --- .github/workflows/js-and-wasm-benchmarks.yml | 25 +++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/.github/workflows/js-and-wasm-benchmarks.yml b/.github/workflows/js-and-wasm-benchmarks.yml index d954d1814e1..27bf1c128e2 100644 --- a/.github/workflows/js-and-wasm-benchmarks.yml +++ b/.github/workflows/js-and-wasm-benchmarks.yml @@ -53,7 +53,6 @@ jobs: sudo apt-get install -y python3-venv - name: 'Download JS repl artifact' - id: download-js-artifact uses: dawidd6/action-download-artifact@v11 with: run_id: ${{ github.event.workflow_run.id }} @@ -73,15 +72,16 @@ jobs: commit_hash=$(cat js-repl/COMMIT) echo "sha=${commit_hash}" >> "${GITHUB_OUTPUT}" - - name: 'Download Wasm repl artifact' - id: download-wasm-artifact + - name: 'Download wasm repl artifact' uses: dawidd6/action-download-artifact@v11 with: run_id: ${{ github.event.workflow_run.id }} name: ladybird-wasm-${{ matrix.package_type }} path: wasm-repl + if_no_artifact_found: warn - - name: 'Extract Wasm repl' + - name: 'Extract wasm repl' + if: ${{ hashFiles('wasm-repl/*.tar.gz') != '' }} shell: bash run: | cd wasm-repl @@ -95,17 +95,24 @@ jobs: source .venv/bin/activate python3 -m pip install -r requirements.txt - run_options="--iterations=5" + run_options='--iterations=5' + + js_path="${{ github.workspace }}/js-repl/bin/js" + run_options="${run_options} --executable=${js_path}" + + wasm_path="${{ github.workspace }}/wasm-repl/bin/wasm" + if [ -x "${wasm_path}" ]; then + run_options="${run_options} --wasm-executable=${wasm_path}" + else + echo "Wasm repl not found; skipping wasm benchmarks." + fi if [ "${{ github.event.workflow_run.event }}" = "workflow_dispatch" ]; then # Upstream was run manually; we might run into failing benchmarks as a result of older builds. run_options="${run_options} --continue-on-failure" fi - ./run.py \ - --executable=${{ github.workspace }}/js-repl/bin/js \ - --wasm-executable=${{ github.workspace }}/wasm-repl/bin/wasm \ - ${run_options} + ./run.py ${run_options} - name: 'Save results as an artifact' uses: actions/upload-artifact@v4 From 373b2838dbd2c582f2a00a58390fb8fa8ba03bb5 Mon Sep 17 00:00:00 2001 From: Jelle Raaijmakers Date: Wed, 15 Oct 2025 10:34:29 +0200 Subject: [PATCH 2/3] CI: Don't run merge conflict labeler on forks --- .github/workflows/merge-conflict-labeler.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/merge-conflict-labeler.yml b/.github/workflows/merge-conflict-labeler.yml index 7cb4f9f253b..639ac431ac8 100644 --- a/.github/workflows/merge-conflict-labeler.yml +++ b/.github/workflows/merge-conflict-labeler.yml @@ -13,9 +13,12 @@ on: jobs: auto-labeler: runs-on: blacksmith-2vcpu-ubuntu-2404 + if: github.repository == 'LadybirdBrowser/ladybird' + permissions: contents: read pull-requests: write + steps: - uses: eps1lon/actions-label-merge-conflict@v3 with: From d4df0e1db985ab44cce747e0e3aaf4129c774654 Mon Sep 17 00:00:00 2001 From: mikiubo Date: Mon, 6 Oct 2025 01:32:55 +0200 Subject: [PATCH 3/3] LibWeb: Make Event.currentTarget return WindowProxy instead of Window Make WindowProxy implement the EventTarget interface. Add a new method current_target_for_bindings() that returns a WindowProxy object instead of directly exposing the Window object. These changes fixes several WPT tests that contain `window === currentTarget`. --- Libraries/LibWeb/DOM/Event.cpp | 11 ++++ Libraries/LibWeb/DOM/Event.h | 2 + Libraries/LibWeb/DOM/Event.idl | 2 +- Libraries/LibWeb/DOM/EventDispatcher.cpp | 2 +- Libraries/LibWeb/HTML/WindowProxy.cpp | 3 +- Libraries/LibWeb/HTML/WindowProxy.h | 9 +-- .../events/Event-dispatch-bubbles-false.txt | 5 +- .../events/Event-dispatch-bubbles-true.txt | 5 +- .../Event-dispatch-handlers-changed.txt | 4 +- .../Event-dispatch-multiple-cancelBubble.txt | 4 +- ...vent-dispatch-multiple-stopPropagation.txt | 4 +- .../events/Event-dispatch-omitted-capture.txt | 4 +- .../dom/events/Event-dispatch-reenter.txt | 4 +- .../events/Event-dispatch-target-moved.txt | 4 +- .../events/Event-dispatch-target-removed.txt | 4 +- .../dom/window-extends-event-target.txt | 8 +++ .../dom/window-extends-event-target.html | 55 +++++++++++++++++++ 17 files changed, 103 insertions(+), 27 deletions(-) create mode 100644 Tests/LibWeb/Text/expected/wpt-import/dom/window-extends-event-target.txt create mode 100644 Tests/LibWeb/Text/input/wpt-import/dom/window-extends-event-target.html diff --git a/Libraries/LibWeb/DOM/Event.cpp b/Libraries/LibWeb/DOM/Event.cpp index 2d6e8fcc810..f123f12e72d 100644 --- a/Libraries/LibWeb/DOM/Event.cpp +++ b/Libraries/LibWeb/DOM/Event.cpp @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include namespace Web::DOM { @@ -263,4 +265,13 @@ Vector> Event::composed_path() const return composed_path; } +// AD-HOC: Needed to return a WindowProxy when the current target is a Window, +// ensuring that JavaScript sees the correct global object instead of the internal Window. +GC::Ptr Event::current_target_for_bindings() const +{ + if (auto* window = as_if(m_current_target.ptr())) + return window->window(); + return m_current_target; +} + } diff --git a/Libraries/LibWeb/DOM/Event.h b/Libraries/LibWeb/DOM/Event.h index 3559de4e154..6f8bc428791 100644 --- a/Libraries/LibWeb/DOM/Event.h +++ b/Libraries/LibWeb/DOM/Event.h @@ -101,6 +101,8 @@ public: GC::Ptr current_target() const { return m_current_target; } void set_current_target(EventTarget* current_target) { m_current_target = current_target; } + GC::Ptr current_target_for_bindings() const; + bool return_value() const { return !m_cancelled; } void set_return_value(bool return_value) { diff --git a/Libraries/LibWeb/DOM/Event.idl b/Libraries/LibWeb/DOM/Event.idl index b081fcb918c..ebf666c1496 100644 --- a/Libraries/LibWeb/DOM/Event.idl +++ b/Libraries/LibWeb/DOM/Event.idl @@ -9,7 +9,7 @@ interface Event { readonly attribute DOMString type; readonly attribute EventTarget? target; readonly attribute EventTarget? srcElement; // legacy - readonly attribute EventTarget? currentTarget; + [ImplementedAs=current_target_for_bindings] readonly attribute EventTarget? currentTarget; sequence composedPath(); const unsigned short NONE = 0; diff --git a/Libraries/LibWeb/DOM/EventDispatcher.cpp b/Libraries/LibWeb/DOM/EventDispatcher.cpp index 54d2aa0ff69..e21fec36444 100644 --- a/Libraries/LibWeb/DOM/EventDispatcher.cpp +++ b/Libraries/LibWeb/DOM/EventDispatcher.cpp @@ -89,7 +89,7 @@ bool EventDispatcher::inner_invoke(Event& event, Vector #include #include +#include #include #include #include @@ -24,7 +25,7 @@ GC_DEFINE_ALLOCATOR(WindowProxy); // 7.4 The WindowProxy exotic object, https://html.spec.whatwg.org/multipage/window-object.html#the-windowproxy-exotic-object WindowProxy::WindowProxy(JS::Realm& realm) - : JS::Object(realm, nullptr, MayInterfereWithIndexedPropertyAccess::Yes) + : DOM::EventTarget(realm, MayInterfereWithIndexedPropertyAccess::Yes) { } diff --git a/Libraries/LibWeb/HTML/WindowProxy.h b/Libraries/LibWeb/HTML/WindowProxy.h index 1a3a27a62b4..050acc64a07 100644 --- a/Libraries/LibWeb/HTML/WindowProxy.h +++ b/Libraries/LibWeb/HTML/WindowProxy.h @@ -10,13 +10,14 @@ #include #include #include +#include #include #include namespace Web::HTML { -class WEB_API WindowProxy final : public JS::Object { - JS_OBJECT(WindowProxy, JS::Object); +class WEB_API WindowProxy final : public DOM::EventTarget { + WEB_PLATFORM_OBJECT(WindowProxy, DOM::EventTarget) GC_DECLARE_ALLOCATOR(WindowProxy); public: @@ -33,8 +34,6 @@ public: virtual JS::ThrowCompletionOr internal_delete(JS::PropertyKey const&) override; virtual JS::ThrowCompletionOr> internal_own_property_keys() const override; - virtual bool eligible_for_own_property_enumeration_fast_path() const override final { return false; } - GC::Ptr window() const { return m_window; } void set_window(GC::Ref); @@ -43,6 +42,8 @@ public: private: explicit WindowProxy(JS::Realm&); + virtual bool is_window_or_worker_global_scope_mixin() const final { return true; } + virtual bool is_html_window_proxy() const override { return true; } virtual void visit_edges(JS::Cell::Visitor&) override; diff --git a/Tests/LibWeb/Text/expected/wpt-import/dom/events/Event-dispatch-bubbles-false.txt b/Tests/LibWeb/Text/expected/wpt-import/dom/events/Event-dispatch-bubbles-false.txt index 98379d24da5..aa6ce5f3be6 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/dom/events/Event-dispatch-bubbles-false.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/dom/events/Event-dispatch-bubbles-false.txt @@ -2,9 +2,8 @@ Harness status: OK Found 5 tests -4 Pass -1 Fail -Fail In window.document with click event +5 Pass +Pass In window.document with click event Pass In window.document with load event Pass In window.document.cloneNode(true) Pass In new Document() diff --git a/Tests/LibWeb/Text/expected/wpt-import/dom/events/Event-dispatch-bubbles-true.txt b/Tests/LibWeb/Text/expected/wpt-import/dom/events/Event-dispatch-bubbles-true.txt index 98379d24da5..aa6ce5f3be6 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/dom/events/Event-dispatch-bubbles-true.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/dom/events/Event-dispatch-bubbles-true.txt @@ -2,9 +2,8 @@ Harness status: OK Found 5 tests -4 Pass -1 Fail -Fail In window.document with click event +5 Pass +Pass In window.document with click event Pass In window.document with load event Pass In window.document.cloneNode(true) Pass In new Document() diff --git a/Tests/LibWeb/Text/expected/wpt-import/dom/events/Event-dispatch-handlers-changed.txt b/Tests/LibWeb/Text/expected/wpt-import/dom/events/Event-dispatch-handlers-changed.txt index e6cfcfd4e38..7ff5c18c2cc 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/dom/events/Event-dispatch-handlers-changed.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/dom/events/Event-dispatch-handlers-changed.txt @@ -2,5 +2,5 @@ Harness status: OK Found 1 tests -1 Fail -Fail Dispatch additional events inside an event listener \ No newline at end of file +1 Pass +Pass Dispatch additional events inside an event listener \ No newline at end of file diff --git a/Tests/LibWeb/Text/expected/wpt-import/dom/events/Event-dispatch-multiple-cancelBubble.txt b/Tests/LibWeb/Text/expected/wpt-import/dom/events/Event-dispatch-multiple-cancelBubble.txt index eb312829e47..e6f43f37c5b 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/dom/events/Event-dispatch-multiple-cancelBubble.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/dom/events/Event-dispatch-multiple-cancelBubble.txt @@ -2,5 +2,5 @@ Harness status: OK Found 1 tests -1 Fail -Fail Multiple dispatchEvent() and cancelBubble \ No newline at end of file +1 Pass +Pass Multiple dispatchEvent() and cancelBubble \ No newline at end of file diff --git a/Tests/LibWeb/Text/expected/wpt-import/dom/events/Event-dispatch-multiple-stopPropagation.txt b/Tests/LibWeb/Text/expected/wpt-import/dom/events/Event-dispatch-multiple-stopPropagation.txt index 73c29f3fefe..7baca83a1b7 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/dom/events/Event-dispatch-multiple-stopPropagation.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/dom/events/Event-dispatch-multiple-stopPropagation.txt @@ -2,5 +2,5 @@ Harness status: OK Found 1 tests -1 Fail -Fail Multiple dispatchEvent() and stopPropagation() \ No newline at end of file +1 Pass +Pass Multiple dispatchEvent() and stopPropagation() \ No newline at end of file diff --git a/Tests/LibWeb/Text/expected/wpt-import/dom/events/Event-dispatch-omitted-capture.txt b/Tests/LibWeb/Text/expected/wpt-import/dom/events/Event-dispatch-omitted-capture.txt index 7e15ac0adb9..c91ebd87dc6 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/dom/events/Event-dispatch-omitted-capture.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/dom/events/Event-dispatch-omitted-capture.txt @@ -2,5 +2,5 @@ Harness status: OK Found 1 tests -1 Fail -Fail EventTarget.addEventListener with the capture argument omitted \ No newline at end of file +1 Pass +Pass EventTarget.addEventListener with the capture argument omitted \ No newline at end of file diff --git a/Tests/LibWeb/Text/expected/wpt-import/dom/events/Event-dispatch-reenter.txt b/Tests/LibWeb/Text/expected/wpt-import/dom/events/Event-dispatch-reenter.txt index e6cfcfd4e38..7ff5c18c2cc 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/dom/events/Event-dispatch-reenter.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/dom/events/Event-dispatch-reenter.txt @@ -2,5 +2,5 @@ Harness status: OK Found 1 tests -1 Fail -Fail Dispatch additional events inside an event listener \ No newline at end of file +1 Pass +Pass Dispatch additional events inside an event listener \ No newline at end of file diff --git a/Tests/LibWeb/Text/expected/wpt-import/dom/events/Event-dispatch-target-moved.txt b/Tests/LibWeb/Text/expected/wpt-import/dom/events/Event-dispatch-target-moved.txt index 3b2afaf0784..6f691400770 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/dom/events/Event-dispatch-target-moved.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/dom/events/Event-dispatch-target-moved.txt @@ -2,5 +2,5 @@ Harness status: OK Found 1 tests -1 Fail -Fail Event propagation path when an element in it is moved within the DOM \ No newline at end of file +1 Pass +Pass Event propagation path when an element in it is moved within the DOM \ No newline at end of file diff --git a/Tests/LibWeb/Text/expected/wpt-import/dom/events/Event-dispatch-target-removed.txt b/Tests/LibWeb/Text/expected/wpt-import/dom/events/Event-dispatch-target-removed.txt index 43ccb7fa6b2..6cf6f508990 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/dom/events/Event-dispatch-target-removed.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/dom/events/Event-dispatch-target-removed.txt @@ -2,5 +2,5 @@ Harness status: OK Found 1 tests -1 Fail -Fail Event propagation path when an element in it is removed from the DOM \ No newline at end of file +1 Pass +Pass Event propagation path when an element in it is removed from the DOM \ No newline at end of file diff --git a/Tests/LibWeb/Text/expected/wpt-import/dom/window-extends-event-target.txt b/Tests/LibWeb/Text/expected/wpt-import/dom/window-extends-event-target.txt new file mode 100644 index 00000000000..2f840058a14 --- /dev/null +++ b/Tests/LibWeb/Text/expected/wpt-import/dom/window-extends-event-target.txt @@ -0,0 +1,8 @@ +Harness status: OK + +Found 3 tests + +3 Pass +Pass EventTarget methods on Window instances are inherited from the EventTarget prototype +Pass window.addEventListener respects custom `this` +Pass window.addEventListener treats nullish `this` as `window` \ No newline at end of file diff --git a/Tests/LibWeb/Text/input/wpt-import/dom/window-extends-event-target.html b/Tests/LibWeb/Text/input/wpt-import/dom/window-extends-event-target.html new file mode 100644 index 00000000000..5ebc76c7acb --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/dom/window-extends-event-target.html @@ -0,0 +1,55 @@ + + +Window extends EventTarget + + + + + +