blob: dae1d10c17f385ad197cf5476eba06424df727f9 [file] [log] [blame]
// Copyright 2018 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/core/display_lock/display_lock_context.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_display_lock_callback.h"
#include "third_party/blink/renderer/platform/bindings/microtask.h"
namespace blink {
DisplayLockContext::DisplayLockContext(ExecutionContext* context)
: ContextLifecycleObserver(context) {}
DisplayLockContext::~DisplayLockContext() {
DCHECK(!resolver_);
DCHECK(callbacks_.IsEmpty());
}
void DisplayLockContext::Trace(blink::Visitor* visitor) {
ScriptWrappable::Trace(visitor);
ContextLifecycleObserver::Trace(visitor);
visitor->Trace(callbacks_);
visitor->Trace(resolver_);
}
void DisplayLockContext::Dispose() {
RejectAndCleanUp();
}
void DisplayLockContext::ContextDestroyed(ExecutionContext*) {
RejectAndCleanUp();
}
bool DisplayLockContext::HasPendingActivity() const {
// If we don't have a task scheduled, then we should already be resolved.
// TODO(vmpstr): This should also be kept alive if we're doing co-operative
// work.
DCHECK(process_queue_task_scheduled_ || !resolver_);
return process_queue_task_scheduled_;
}
void DisplayLockContext::ScheduleTask(V8DisplayLockCallback* callback,
ScriptState* script_state) {
callbacks_.push_back(callback);
if (!resolver_) {
DCHECK(script_state);
resolver_ = ScriptPromiseResolver::Create(script_state);
}
if (!process_queue_task_scheduled_) {
DCHECK(GetExecutionContext());
DCHECK(GetExecutionContext()->GetTaskRunner(TaskType::kMiscPlatformAPI));
GetExecutionContext()
->GetTaskRunner(TaskType::kMiscPlatformAPI)
->PostTask(FROM_HERE, WTF::Bind(&DisplayLockContext::ProcessQueue,
WrapWeakPersistent(this)));
process_queue_task_scheduled_ = true;
}
}
void DisplayLockContext::schedule(V8DisplayLockCallback* callback) {
ScheduleTask(callback);
}
void DisplayLockContext::ProcessQueue() {
// It's important to clear this before running the tasks, since the tasks can
// call ScheduleTask() which will re-schedule a PostTask() for us to continue
// the work.
process_queue_task_scheduled_ = false;
// We might have cleaned up already due to exeuction context being destroyed.
if (callbacks_.IsEmpty()) {
DCHECK(!resolver_);
return;
}
// Get a local copy of all the tasks we will run.
// TODO(vmpstr): This should possibly be subject to a budget instead.
HeapVector<Member<V8DisplayLockCallback>> callbacks;
callbacks.swap(callbacks_);
for (auto& callback : callbacks) {
DCHECK(callback);
{
// A re-implementation of InvokeAndReportException, in order for us to
// be able to query |try_catch| to determine whether or not we need to
// reject our promise.
v8::TryCatch try_catch(callback->GetIsolate());
try_catch.SetVerbose(true);
auto result = callback->Invoke(nullptr, this);
ALLOW_UNUSED_LOCAL(result);
if (try_catch.HasCaught()) {
RejectAndCleanUp();
return;
}
}
Microtask::PerformCheckpoint(callback->GetIsolate());
}
// TODO(vmpstr): This should be resolved after all of the co-operative work
// finishes, not here.
if (callbacks_.IsEmpty()) {
DCHECK(!process_queue_task_scheduled_);
resolver_->Resolve();
resolver_ = nullptr;
}
}
void DisplayLockContext::RejectAndCleanUp() {
if (resolver_) {
resolver_->Reject();
resolver_ = nullptr;
}
callbacks_.clear();
}
} // namespace blink