Skip to content

Commit

Permalink
Simplify timer creation
Browse files Browse the repository at this point in the history
* Several methods on WindowOrWorkerGlobalScope, including
  setTimeout/setInternval, take an EventTarget& when what they
  really want is an ExecutionContext&. All of the classes being
  passed in that parameter slot are both, so switch to
  ExecutionContext& and pass ExecutionContext as a reference
  thorughout.
* Make DOMTimerCoordinator a Supplement of ExecutionContext and
  a private helper for DOMTimer. Tweak when exactly timeout id
  creation happens so that DOMTimerCoordinator doesn't have to
  be the one to construct the DOMTimer.

Change-Id: I48971ab220757251184badef1b41dad97344ff24
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4545787
Reviewed-by: Andrey Kosyakov <caseq@chromium.org>
Commit-Queue: Nate Chapin <japhet@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1146789}
  • Loading branch information
natechapin authored and Chromium LUCI CQ committed May 19, 2023
1 parent 586484c commit 74f189d
Show file tree
Hide file tree
Showing 12 changed files with 157 additions and 225 deletions.
Expand Up @@ -51,15 +51,15 @@
namespace blink {

ScheduledAction::ScheduledAction(ScriptState* script_state,
ExecutionContext* target,
ExecutionContext& target,
V8Function* handler,
const HeapVector<ScriptValue>& arguments)
: script_state_(
MakeGarbageCollected<ScriptStateProtectingContext>(script_state)) {
if (script_state->World().IsWorkerWorld() ||
BindingSecurity::ShouldAllowAccessTo(
EnteredDOMWindow(script_state->GetIsolate()),
To<LocalDOMWindow>(target))) {
To<LocalDOMWindow>(&target))) {
function_ = handler;
arguments_ = arguments;
auto* tracker = ThreadScheduler::Current()->GetTaskAttributionTracker();
Expand All @@ -73,14 +73,14 @@ ScheduledAction::ScheduledAction(ScriptState* script_state,
}

ScheduledAction::ScheduledAction(ScriptState* script_state,
ExecutionContext* target,
ExecutionContext& target,
const String& handler)
: script_state_(
MakeGarbageCollected<ScriptStateProtectingContext>(script_state)) {
if (script_state->World().IsWorkerWorld() ||
BindingSecurity::ShouldAllowAccessTo(
EnteredDOMWindow(script_state->GetIsolate()),
To<LocalDOMWindow>(target))) {
To<LocalDOMWindow>(&target))) {
code_ = handler;
auto* tracker = ThreadScheduler::Current()->GetTaskAttributionTracker();
if (tracker && script_state->World().IsMainWorld()) {
Expand Down
Expand Up @@ -53,11 +53,11 @@ class ScheduledAction final : public GarbageCollected<ScheduledAction>,

public:
ScheduledAction(ScriptState*,
ExecutionContext* target,
ExecutionContext& target,
V8Function* handler,
const HeapVector<ScriptValue>& arguments);
ScheduledAction(ScriptState*,
ExecutionContext* target,
ExecutionContext& target,
const String& handler);

ScheduledAction(const ScheduledAction&) = delete;
Expand Down
Expand Up @@ -548,7 +548,6 @@ void ExecutionContext::Trace(Visitor* visitor) const {
visitor->Trace(public_url_manager_);
visitor->Trace(pending_exceptions_);
visitor->Trace(csp_delegate_);
visitor->Trace(timers_);
visitor->Trace(origin_trial_context_);
visitor->Trace(content_security_policy_);
visitor->Trace(runtime_feature_state_override_context_);
Expand Down
Expand Up @@ -46,7 +46,6 @@
#include "third_party/blink/renderer/bindings/core/v8/sanitize_script_errors.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/execution_context/security_context.h"
#include "third_party/blink/renderer/core/frame/dom_timer_coordinator.h"
#include "third_party/blink/renderer/core/frame/web_feature_forward.h"
#include "third_party/blink/renderer/platform/heap/collection_support/heap_vector.h"
#include "third_party/blink/renderer/platform/heap_observer_set.h"
Expand Down Expand Up @@ -221,15 +220,6 @@ class CORE_EXPORT ExecutionContext : public Supplementable<ExecutionContext>,

virtual HttpsState GetHttpsState() const = 0;

// Gets the DOMTimerCoordinator which maintains the "active timer
// list" of tasks created by setTimeout and setInterval. The
// DOMTimerCoordinator is owned by the ExecutionContext and should
// not be used after the ExecutionContext is destroyed.
DOMTimerCoordinator* Timers() {
DCHECK(!IsWorkletGlobalScope());
return &timers_;
}

virtual ResourceFetcher* Fetcher() = 0;

SecurityContext& GetSecurityContext() { return security_context_; }
Expand Down Expand Up @@ -528,8 +518,6 @@ class CORE_EXPORT ExecutionContext : public Supplementable<ExecutionContext>,

const Member<ContentSecurityPolicyDelegate> csp_delegate_;

DOMTimerCoordinator timers_;

// Counter that keeps track of how many window interaction calls are allowed
// for this ExecutionContext. Callers are expected to call
// |allowWindowInteraction()| and |consumeWindowInteraction()| in order to
Expand Down
2 changes: 0 additions & 2 deletions third_party/blink/renderer/core/frame/build.gni
Expand Up @@ -55,8 +55,6 @@ blink_core_sources_frame = [
"document_policy_violation_report_body.h",
"dom_timer.cc",
"dom_timer.h",
"dom_timer_coordinator.cc",
"dom_timer_coordinator.h",
"dom_visual_viewport.cc",
"dom_visual_viewport.h",
"dom_window.cc",
Expand Down
126 changes: 102 additions & 24 deletions third_party/blink/renderer/core/frame/dom_timer.cc
Expand Up @@ -51,38 +51,116 @@ constexpr int kMaxTimerNestingLevel = 5;
constexpr base::TimeDelta kMinimumInterval = base::Milliseconds(4);
constexpr base::TimeDelta kMaxHighResolutionInterval = base::Milliseconds(32);

// Maintains a set of DOMTimers for a given ExecutionContext. Assigns IDs to
// timers; these IDs are the ones returned to web authors from setTimeout or
// setInterval. It also tracks recursive creation or iterative scheduling of
// timers, which is used as a signal for throttling repetitive timers.
class DOMTimerCoordinator : public GarbageCollected<DOMTimerCoordinator>,
public Supplement<ExecutionContext> {
public:
constexpr static const char kSupplementName[] = "DOMTimerCoordinator";

static DOMTimerCoordinator& From(ExecutionContext& context) {
CHECK(!context.IsWorkletGlobalScope());
auto* coordinator =
Supplement<ExecutionContext>::From<DOMTimerCoordinator>(context);
if (!coordinator) {
coordinator = MakeGarbageCollected<DOMTimerCoordinator>(context);
Supplement<ExecutionContext>::ProvideTo(context, coordinator);
}
return *coordinator;
}

explicit DOMTimerCoordinator(ExecutionContext& context)
: Supplement<ExecutionContext>(context) {}

int Install(DOMTimer* timer) {
int timeout_id = NextID();
timers_.insert(timeout_id, timer);
return timeout_id;
}

// Removes and disposes the timer with the specified ID, if any. This may
// destroy the timer.
DOMTimer* RemoveTimeoutByID(int timeout_id) {
if (timeout_id <= 0) {
return nullptr;
}
DOMTimer* removed_timer = timers_.Take(timeout_id);
if (removed_timer) {
removed_timer->Stop();
}
return removed_timer;
}

// Timers created during the execution of other timers, and
// repeating timers, are throttled. Timer nesting level tracks the
// number of linked timers or repetitions of a timer. See
// https://html.spec.whatwg.org/C/#timers
int TimerNestingLevel() { return timer_nesting_level_; }

// Sets the timer nesting level. Set when a timer executes so that
// any timers created while the timer is executing will incur a
// deeper timer nesting level, see DOMTimer::DOMTimer.
void SetTimerNestingLevel(int level) { timer_nesting_level_ = level; }

void Trace(Visitor* visitor) const final {
visitor->Trace(timers_);
Supplement<ExecutionContext>::Trace(visitor);
}

private:
int NextID() {
while (true) {
++circular_sequential_id_;

if (circular_sequential_id_ <= 0) {
circular_sequential_id_ = 1;
}

if (!timers_.Contains(circular_sequential_id_)) {
return circular_sequential_id_;
}
}
}

HeapHashMap<int, Member<DOMTimer>> timers_;
int circular_sequential_id_ = 0;
int timer_nesting_level_ = 0;
};

} // namespace

int DOMTimer::Install(ExecutionContext* context,
int DOMTimer::Install(ExecutionContext& context,
ScheduledAction* action,
base::TimeDelta timeout,
bool single_shot) {
int timeout_id = context->Timers()->InstallNewTimeout(context, action,
timeout, single_shot);
return timeout_id;
return MakeGarbageCollected<DOMTimer>(context, action, timeout, single_shot)
->timeout_id_;
}

void DOMTimer::RemoveByID(ExecutionContext* context, int timeout_id) {
DOMTimer* timer = context->Timers()->RemoveTimeoutByID(timeout_id);
void DOMTimer::RemoveByID(ExecutionContext& context, int timeout_id) {
DEVTOOLS_TIMELINE_TRACE_EVENT_INSTANT(
"TimerRemove", inspector_timer_remove_event::Data, context, timeout_id);
"TimerRemove", inspector_timer_remove_event::Data, &context, timeout_id);
// Eagerly unregister as ExecutionContext observer.
if (timer)
if (DOMTimer* timer =
DOMTimerCoordinator::From(context).RemoveTimeoutByID(timeout_id)) {
// Eagerly unregister as ExecutionContext observer.
timer->SetExecutionContext(nullptr);
}
}

DOMTimer::DOMTimer(ExecutionContext* context,
DOMTimer::DOMTimer(ExecutionContext& context,
ScheduledAction* action,
base::TimeDelta timeout,
bool single_shot,
int timeout_id)
: ExecutionContextLifecycleObserver(context),
bool single_shot)
: ExecutionContextLifecycleObserver(&context),
TimerBase(nullptr),
timeout_id_(timeout_id),
timeout_id_(DOMTimerCoordinator::From(context).Install(this)),
// Step 9:
nesting_level_(context->Timers()->TimerNestingLevel()),
nesting_level_(DOMTimerCoordinator::From(context).TimerNestingLevel()),
action_(action) {
DCHECK_GT(timeout_id, 0);
DCHECK_GT(timeout_id_, 0);

// Step 10:
if (timeout.is_negative())
Expand Down Expand Up @@ -120,7 +198,7 @@ DOMTimer::DOMTimer(ExecutionContext* context,
} else {
task_type = TaskType::kJavascriptTimerDelayedLowNesting;
}
MoveToNewTaskRunner(context->GetTaskRunner(task_type));
MoveToNewTaskRunner(context.GetTaskRunner(task_type));

// Clamping up to 1ms for historical reasons crbug.com/402694.
// Removing clamp for single_shot behind a feature flag.
Expand All @@ -133,11 +211,11 @@ DOMTimer::DOMTimer(ExecutionContext* context,
StartRepeating(timeout, FROM_HERE, precise);

DEVTOOLS_TIMELINE_TRACE_EVENT_INSTANT(
"TimerInstall", inspector_timer_install_event::Data, context, timeout_id,
timeout, single_shot);
"TimerInstall", inspector_timer_install_event::Data, &context,
timeout_id_, timeout, single_shot);
const char* name = single_shot ? "setTimeout" : "setInterval";
async_task_context_.Schedule(context, name);
probe::BreakableLocation(context, name);
async_task_context_.Schedule(&context, name);
probe::BreakableLocation(&context, name);
}

DOMTimer::~DOMTimer() = default;
Expand Down Expand Up @@ -171,7 +249,7 @@ void DOMTimer::ContextDestroyed() {
void DOMTimer::Fired() {
ExecutionContext* context = GetExecutionContext();
DCHECK(context);
context->Timers()->SetTimerNestingLevel(nesting_level_);
DOMTimerCoordinator::From(*context).SetTimerNestingLevel(nesting_level_);
DCHECK(!context->IsContextPaused());
// Only the first execution of a multi-shot timer should get an affirmative
// user gesture indicator.
Expand Down Expand Up @@ -223,15 +301,15 @@ void DOMTimer::Fired() {
// No access to member variables after this point, it can delete the timer.
action_->Execute(context);

context->Timers()->SetTimerNestingLevel(0);
DOMTimerCoordinator::From(*context).SetTimerNestingLevel(0);

return;
}

// Unregister the timer from ExecutionContext before executing the action
// for one-shot timers.
ScheduledAction* action = action_.Release();
context->Timers()->RemoveTimeoutByID(timeout_id_);
DOMTimerCoordinator::From(*context).RemoveTimeoutByID(timeout_id_);

action->Execute(context);

Expand All @@ -243,7 +321,7 @@ void DOMTimer::Fired() {
if (!execution_context)
return;

execution_context->Timers()->SetTimerNestingLevel(0);
DOMTimerCoordinator::From(*execution_context).SetTimerNestingLevel(0);
// Eagerly unregister as ExecutionContext observer.
SetExecutionContext(nullptr);
}
Expand Down
9 changes: 4 additions & 5 deletions third_party/blink/renderer/core/frame/dom_timer.h
Expand Up @@ -50,17 +50,16 @@ class CORE_EXPORT DOMTimer final : public GarbageCollected<DOMTimer>,
public:
// Creates a new timer owned by the ExecutionContext, starts it and returns
// its ID.
static int Install(ExecutionContext*,
static int Install(ExecutionContext&,
ScheduledAction*,
base::TimeDelta timeout,
bool single_shot);
static void RemoveByID(ExecutionContext*, int timeout_id);
static void RemoveByID(ExecutionContext&, int timeout_id);

DOMTimer(ExecutionContext*,
DOMTimer(ExecutionContext&,
ScheduledAction*,
base::TimeDelta timeout,
bool single_shot,
int timeout_id);
bool single_shot);
~DOMTimer() override;

// ExecutionContextLifecycleObserver
Expand Down
54 changes: 0 additions & 54 deletions third_party/blink/renderer/core/frame/dom_timer_coordinator.cc

This file was deleted.

0 comments on commit 74f189d

Please sign in to comment.