1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
6 #include "core/dom/ScriptedIdleTaskController.h"
8 #include "core/dom/ExecutionContext.h"
9 #include "core/dom/IdleRequestCallback.h"
10 #include "platform/Logging.h"
11 #include "platform/TraceEvent.h"
12 #include "public/platform/Platform.h"
13 #include "public/platform/WebScheduler.h"
14 #include "public/platform/WebTraceLocation.h"
15 #include "wtf/CurrentTime.h"
16 #include "wtf/Functional.h"
22 class IdleRequestCallbackWrapper
: public RefCounted
<IdleRequestCallbackWrapper
> {
24 static PassRefPtr
<IdleRequestCallbackWrapper
> create(ScriptedIdleTaskController::CallbackId id
, PassRefPtrWillBeRawPtr
<ScriptedIdleTaskController
> controller
)
26 return adoptRef(new IdleRequestCallbackWrapper(id
, controller
));
28 virtual ~IdleRequestCallbackWrapper()
32 static void idleTaskFired(PassRefPtr
<IdleRequestCallbackWrapper
> callbackWrapper
, double deadlineSeconds
)
34 // TODO(rmcilroy): Implement clamping of deadline in some form.
35 callbackWrapper
->controller()->callbackFired(callbackWrapper
->id(), deadlineSeconds
, IdleDeadline::CallbackType::CalledWhenIdle
);
38 static void timeoutFired(PassRefPtr
<IdleRequestCallbackWrapper
> callbackWrapper
)
40 callbackWrapper
->controller()->callbackFired(callbackWrapper
->id(), monotonicallyIncreasingTime(), IdleDeadline::CallbackType::CalledByTimeout
);
43 ScriptedIdleTaskController::CallbackId
id() const { return m_id
; }
44 PassRefPtrWillBeRawPtr
<ScriptedIdleTaskController
> controller() const { return m_controller
; }
47 IdleRequestCallbackWrapper(ScriptedIdleTaskController::CallbackId id
, PassRefPtrWillBeRawPtr
<ScriptedIdleTaskController
> controller
)
49 , m_controller(controller
)
53 ScriptedIdleTaskController::CallbackId m_id
;
54 RefPtrWillBePersistent
<ScriptedIdleTaskController
> m_controller
;
57 } // namespace internal
59 ScriptedIdleTaskController::ScriptedIdleTaskController(ExecutionContext
* context
)
60 : ActiveDOMObject(context
)
61 , m_scheduler(Platform::current()->currentThread()->scheduler())
68 ScriptedIdleTaskController::~ScriptedIdleTaskController()
72 DEFINE_TRACE(ScriptedIdleTaskController
)
74 visitor
->trace(m_callbacks
);
75 ActiveDOMObject::trace(visitor
);
78 ScriptedIdleTaskController::CallbackId
ScriptedIdleTaskController::registerCallback(IdleRequestCallback
* callback
, double timeoutMillis
)
80 CallbackId id
= ++m_nextCallbackId
;
81 m_callbacks
.set(id
, callback
);
83 RefPtr
<internal::IdleRequestCallbackWrapper
> callbackWrapper
= internal::IdleRequestCallbackWrapper::create(id
, this);
84 m_scheduler
->postIdleTask(FROM_HERE
, WTF::bind
<double>(&internal::IdleRequestCallbackWrapper::idleTaskFired
, callbackWrapper
));
85 if (timeoutMillis
> 0)
86 m_scheduler
->timerTaskRunner()->postDelayedTask(FROM_HERE
, WTF::bind(&internal::IdleRequestCallbackWrapper::timeoutFired
, callbackWrapper
), static_cast<long long>(timeoutMillis
));
88 // TODO(rmcilroy): Add devtools tracing.
92 void ScriptedIdleTaskController::cancelCallback(CallbackId id
)
94 // TODO(rmcilroy): Add devtools tracing.
95 m_callbacks
.remove(id
);
98 void ScriptedIdleTaskController::callbackFired(CallbackId id
, double deadlineSeconds
, IdleDeadline::CallbackType callbackType
)
100 if (!m_callbacks
.contains(id
))
104 if (callbackType
== IdleDeadline::CallbackType::CalledByTimeout
) {
105 // Queue for execution when we are resumed.
106 m_pendingTimeouts
.append(id
);
108 // Just drop callbacks called while suspended, these will be reposted on the idle task queue when we are resumed.
112 runCallback(id
, deadlineSeconds
, callbackType
);
115 void ScriptedIdleTaskController::runCallback(CallbackId id
, double deadlineSeconds
, IdleDeadline::CallbackType callbackType
)
117 ASSERT(!m_suspended
);
118 auto callback
= m_callbacks
.take(id
);
122 double allotedTimeMillis
= std::max((deadlineSeconds
- monotonicallyIncreasingTime()) * 1000, 0.0);
123 Platform::current()->histogramCustomCounts("WebCore.ScriptedIdleTaskController.IdleCallbackDeadline", allotedTimeMillis
, 0, 50, 50);
125 // TODO(rmcilroy): Add devtools tracing.
126 callback
->handleEvent(IdleDeadline::create(deadlineSeconds
, callbackType
));
128 double overrunMillis
= std::max((monotonicallyIncreasingTime() - deadlineSeconds
) * 1000, 0.0);
129 Platform::current()->histogramCustomCounts("WebCore.ScriptedIdleTaskController.IdleCallbackOverrun", overrunMillis
, 0, 10000, 50);
132 void ScriptedIdleTaskController::stop()
137 void ScriptedIdleTaskController::suspend()
142 void ScriptedIdleTaskController::resume()
147 // Run any pending timeouts.
148 Vector
<CallbackId
> pendingTimeouts
;
149 m_pendingTimeouts
.swap(pendingTimeouts
);
150 for (auto& id
: pendingTimeouts
)
151 runCallback(id
, monotonicallyIncreasingTime(), IdleDeadline::CallbackType::CalledByTimeout
);
153 // Repost idle tasks for any remaining callbacks.
154 for (auto& callback
: m_callbacks
) {
155 RefPtr
<internal::IdleRequestCallbackWrapper
> callbackWrapper
= internal::IdleRequestCallbackWrapper::create(callback
.key
, this);
156 m_scheduler
->postIdleTask(FROM_HERE
, WTF::bind
<double>(&internal::IdleRequestCallbackWrapper::idleTaskFired
, callbackWrapper
));
160 bool ScriptedIdleTaskController::hasPendingActivity() const
162 return !m_callbacks
.isEmpty();