blob: e170073772dc7f8b46e73ae71bb3bb35696701dd [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// 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/bindings/core/v8/script_promise_resolver.h"
#include "third_party/blink/public/platform/task_type.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/probe/core_probes.h"
#include "third_party/blink/renderer/platform/wtf/functional.h"
#if DCHECK_IS_ON()
#include "base/debug/alias.h"
#include "components/crash/core/common/crash_key.h"
#endif
namespace blink {
ScriptPromiseResolver::ScriptPromiseResolver(ScriptState* script_state)
: ExecutionContextLifecycleObserver(ExecutionContext::From(script_state)),
state_(kPending),
script_state_(script_state),
resolver_(script_state),
keep_alive_(PERSISTENT_FROM_HERE) {
if (GetExecutionContext()->IsContextDestroyed()) {
state_ = kDetached;
resolver_.Clear();
}
}
ScriptPromiseResolver::~ScriptPromiseResolver() = default;
void ScriptPromiseResolver::Dispose() {
#if DCHECK_IS_ON()
// This assertion fails if:
// - promise() is called at least once and
// - this resolver is destructed before it is resolved, rejected,
// detached, the V8 isolate is terminated or the associated
// ExecutionContext is stopped.
const bool is_properly_detached =
state_ == kDetached || !is_promise_called_ ||
!GetScriptState()->ContextIsValid() || !GetExecutionContext() ||
GetExecutionContext()->IsContextDestroyed();
if (!is_properly_detached && !suppress_detach_check_) {
// This is here to make it easier to track down which promise resolvers are
// being abandoned. See https://crbug.com/873980.
static crash_reporter::CrashKeyString<1024> trace_key(
"scriptpromiseresolver-trace");
crash_reporter::SetCrashKeyStringToStackTrace(&trace_key,
create_stack_trace_);
DCHECK(false)
<< "ScriptPromiseResolver was not properly detached; created at\n"
<< create_stack_trace_.ToString();
}
#endif
deferred_resolve_task_.Cancel();
}
void ScriptPromiseResolver::Reject(ExceptionState& exception_state) {
DCHECK(exception_state.HadException());
Reject(exception_state.GetException());
exception_state.ClearException();
}
void ScriptPromiseResolver::Detach() {
if (state_ == kDetached)
return;
deferred_resolve_task_.Cancel();
state_ = kDetached;
resolver_.Clear();
value_.Clear();
keep_alive_.Clear();
}
void ScriptPromiseResolver::KeepAliveWhilePending() {
// keepAliveWhilePending() will be called twice if the resolver
// is created in a suspended execution context and the resolver
// is then resolved/rejected while in that suspended state.
if (state_ == kDetached || keep_alive_)
return;
// Keep |this| around while the promise is Pending;
// see detach() for the dual operation.
keep_alive_ = this;
}
void ScriptPromiseResolver::ResolveOrRejectImmediately() {
DCHECK(!GetExecutionContext()->IsContextDestroyed());
DCHECK(!GetExecutionContext()->IsContextPaused());
{
if (state_ == kResolving) {
resolver_.Resolve(value_.NewLocal(script_state_->GetIsolate()));
} else {
DCHECK_EQ(state_, kRejecting);
resolver_.Reject(value_.NewLocal(script_state_->GetIsolate()));
}
}
Detach();
}
void ScriptPromiseResolver::ScheduleResolveOrReject() {
deferred_resolve_task_ = PostCancellableTask(
*GetExecutionContext()->GetTaskRunner(TaskType::kMicrotask), FROM_HERE,
WTF::Bind(&ScriptPromiseResolver::ResolveOrRejectDeferred,
WrapPersistent(this)));
}
void ScriptPromiseResolver::ResolveOrRejectDeferred() {
DCHECK(state_ == kResolving || state_ == kRejecting);
if (!GetScriptState()->ContextIsValid()) {
Detach();
return;
}
ScriptState::Scope scope(script_state_);
ResolveOrRejectImmediately();
}
void ScriptPromiseResolver::Trace(Visitor* visitor) const {
visitor->Trace(script_state_);
visitor->Trace(resolver_);
visitor->Trace(value_);
ExecutionContextLifecycleObserver::Trace(visitor);
}
} // namespace blink