blob: a820c7a0eefa705eb34db835ed0f24921042644c [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/dom/abort_signal.h"
#include <tuple>
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
#include "third_party/blink/renderer/core/dom/abort_controller.h"
#include "third_party/blink/renderer/core/dom/abort_signal_registry.h"
#include "third_party/blink/renderer/core/dom/events/native_event_listener.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/testing/page_test_base.h"
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
#include "third_party/blink/renderer/platform/heap/thread_state.h"
#include "third_party/blink/renderer/platform/wtf/functional.h"
namespace blink {
namespace {
class TestEventListener : public NativeEventListener {
public:
TestEventListener() = default;
void Invoke(ExecutionContext*, Event*) override {}
};
} // namespace
class AbortSignalTest : public PageTestBase {
public:
AbortSignalTest() = default;
void SetUp() override {
PageTestBase::SetUp();
ScriptState* script_state = ToScriptStateForMainWorld(&GetFrame());
controller_ = AbortController::Create(script_state);
signal_ = controller_->signal();
}
void SignalAbort() {
ScriptState* script_state = ToScriptStateForMainWorld(&GetFrame());
ScriptState::Scope script_scope(script_state);
controller_->abort(script_state);
}
AbortSignalRegistry* GetRegistry() const {
return AbortSignalRegistry::From(*GetFrame().DomWindow());
}
protected:
Persistent<AbortController> controller_;
Persistent<AbortSignal> signal_;
Persistent<AbortSignal::AlgorithmHandle> abort_handle_;
};
TEST_F(AbortSignalTest, AbortAlgorithmRuns) {
int count = 0;
abort_handle_ = signal_->AddAlgorithm(
WTF::BindOnce([](int* count) { ++(*count); }, WTF::Unretained(&count)));
// GC should not affect whether or not the algorithm runs.
ThreadState::Current()->CollectAllGarbageForTesting();
SignalAbort();
EXPECT_EQ(count, 1);
// Subsequent aborts are no-ops.
SignalAbort();
EXPECT_EQ(count, 1);
}
TEST_F(AbortSignalTest, AbortAlgorithmHandleRemoved) {
int count = 0;
abort_handle_ = signal_->AddAlgorithm(
WTF::BindOnce([](int* count) { ++(*count); }, WTF::Unretained(&count)));
signal_->RemoveAlgorithm(abort_handle_.Get());
SignalAbort();
EXPECT_EQ(count, 0);
}
TEST_F(AbortSignalTest, AbortAlgorithmHandleGCed) {
int count = 0;
abort_handle_ = signal_->AddAlgorithm(
WTF::BindOnce([](int* count) { ++(*count); }, WTF::Unretained(&count)));
abort_handle_.Clear();
ThreadState::Current()->CollectAllGarbageForTesting();
SignalAbort();
EXPECT_EQ(count, 0);
}
TEST_F(AbortSignalTest, RegisteredSignalAlgorithmRuns) {
int count = 0;
Persistent<TestEventListener> listener =
MakeGarbageCollected<TestEventListener>();
{
auto* handle = signal_->AddAlgorithm(
WTF::BindOnce([](int* count) { ++(*count); }, WTF::Unretained(&count)));
GetRegistry()->RegisterAbortAlgorithm(listener.Get(), handle);
}
// GC should not affect whether or not the algorithm runs.
ThreadState::Current()->CollectAllGarbageForTesting();
SignalAbort();
EXPECT_EQ(count, 1);
}
TEST_F(AbortSignalTest, RegisteredSignalAlgorithmListenerGCed) {
int count = 0;
Persistent<TestEventListener> listener =
MakeGarbageCollected<TestEventListener>();
{
auto* handle = signal_->AddAlgorithm(
WTF::BindOnce([](int* count) { ++(*count); }, WTF::Unretained(&count)));
GetRegistry()->RegisterAbortAlgorithm(listener.Get(), handle);
}
listener.Clear();
ThreadState::Current()->CollectAllGarbageForTesting();
SignalAbort();
EXPECT_EQ(count, 0);
}
TEST_F(AbortSignalTest, CanAbort) {
EXPECT_TRUE(signal_->CanAbort());
SignalAbort();
EXPECT_FALSE(signal_->CanAbort());
}
TEST_F(AbortSignalTest, CanAbortAfterGC) {
controller_.Clear();
ThreadState::Current()->CollectAllGarbageForTesting();
EXPECT_FALSE(signal_->CanAbort());
}
} // namespace blink