Commit 895f39e1 authored by rmcilroy@chromium.org's avatar rmcilroy@chromium.org

Implementation of the requestIdleCallback API

Initial implementation of the requestIdleCallback API based on
the spec at https://w3c.github.io/requestidlecallback/.

This API is behind a flag and is not enabled by default.

BUG=514651
TBR=jochen@chromium.org
NOTRY=true

Review URL: https://codereview.chromium.org/1119683003

git-svn-id: svn://svn.chromium.org/blink/trunk@200992 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent f311b74a
...@@ -163,5 +163,10 @@ ...@@ -163,5 +163,10 @@
"prefix": "trustedeventsdefaultaction", "prefix": "trustedeventsdefaultaction",
"base": "fast/events", "base": "fast/events",
"args": ["--enable-blink-features=TrustedEventsDefaultAction"] "args": ["--enable-blink-features=TrustedEventsDefaultAction"]
},
{
"prefix": "threaded",
"base": "fast/idle-callback",
"args": ["--enable-threaded-compositing"]
} }
] ]
# This suite runs the tests in virtual/threaded/fast/idle-callback with
# --enable-threaded-compositing since the tests require threaded compositing
<!DOCTYPE html>
<title>window.requestIdleCallback exists</title>
<link rel="author" title="Ross McIlroy" href="mailto:rmcilroy@chromium.org" />
<link rel="help" href="http://www.w3.org/TR/requestidlecallback/"/>
<script src="../../../../resources/testharness.js"></script>
<script src="../../../../resources/testharnessreport.js"></script>
<link rel="stylesheet" href="../../resources/testharness.css" />
<script>
test(function() {
assert_equals(typeof window.requestIdleCallback, "function");
}, "window.requestIdleCallback is defined", {assert: "The window.requestIdleCallback function is used to request callbacks during browser-defined idle time."});
test(function() {
assert_equals(typeof window.cancelIdleCallback, "function");
}, "window.cancelIdleCallback is defined", {assert: "The window.cancelIdleCallback function is used to cancel callbacks scheduled via requestIdleCallback."});
test(function() {
assert_equals(typeof window.requestIdleCallback(function() {}), "number");
}, "window.requestIdleCallback() returns a number", {assert: "The requestIdleCallback method MUST return a long"});
test(function() {
assert_equals(typeof window.cancelIdleCallback(1), "undefined");
}, "window.cancelIdleCallback() returns undefined", {assert: "The cancelIdleCallback method MUST return void"});
async_test(function() {
// Check whether requestIdleCallback schedules a callback which gets executed
// and the deadline argument is passed correctly.
requestIdleCallback(this.step_func(function(deadline) {
assert_equals(typeof deadline, "object", "IdleDeadline MUST be passed to callback.");
assert_equals(typeof deadline.timeRemaining, "number", "IdleDeadline.timeRemaining MUST be a double time remaining in milliseconds");
assert_true(deadline.timeRemaining <= 50, "IdleDeadline.timeRemaining() MUST be less than or equal to 50ms in the future.");
assert_equals(typeof deadline.didTimeout, "boolean", "IdleDeadline.didTimeout MUST be a boolean");
assert_false(deadline.didTimeout, "IdleDeadline.didTimeout MUST be false if requestIdleCallback wasn't scheduled due to a timeout");
this.done();
}));
}, 'requestIdleCallback schedules callbacks');
async_test(function() {
// Check whether requestIdleCallback schedules a callback which gets executed
// and the deadline argument is passed correctly.
var handle = requestIdleCallback(this.step_func(function(deadline) {
assert_unreached("callback should not be called if canceled with cancelIdleCallback");
}));
cancelIdleCallback(handle);
setTimeout(this.step_func(function() {
this.done();
}), 200);
}, 'cancelIdleCallback cancels callbacks');
</script>
<h1>Description</h1>
<p>This test validates that window.requestIdleCallback and window.cancelIdleCallback exist and are a functions.</p>
<div id="log"></div>
<!DOCTYPE html>
<title>window.requestIdleCallback deals with timeouts correctly</title>
<link rel="author" title="Ross McIlroy" href="mailto:rmcilroy@chromium.org" />
<link rel="help" href="http://www.w3.org/TR/requestidlecallback/"/>
<script src="../../../../resources/testharness.js"></script>
<script src="../../../../resources/testharnessreport.js"></script>
<link rel="stylesheet" href="../../resources/testharness.css" />
<script>
async_test(function() {
// Check whether requestIdleCallback with a timeout works when the event loop
// is busy.
var busy_raf_loop_iterations_remaining = 10; // Should take 20 * 40 = 400ms
var idle_callback_scheduled;
var idle_callback = this.step_func(function(deadline) {
assert_false(deadline.didTimeout, "IdleDeadline.didTimeout MUST be false if requestIdleCallback wasn't scheduled due to a timeout");
assert_equals(busy_raf_loop_iterations_remaining, 0, "Busy rAF loop should be finished by the time we get scheduled");
this.done();
});
var busy_raf_loop_iterations_remaining = 10; // Should take 20 * 40 = 400ms
requestAnimationFrame(this.step_func(function busyRAFLoop() {
var start_time = performance.now();
if (!idle_callback_scheduled) {
idle_callback_scheduled = start_time;
requestIdleCallback(idle_callback);
}
// Use up the whole frame budget.
while (performance.now() - start_time < 40) { }
if (busy_raf_loop_iterations_remaining > 0) {
busy_raf_loop_iterations_remaining--;
requestAnimationFrame(busyRAFLoop);
}
}));
}, 'requestIdleCallback not scheduled when event loop is busy.');
async_test(function() {
// Check whether requestIdleCallback with a timeout works when the event loop
// is busy.
var busy_raf_loop_iterations_remaining = 10; // Should take 20 * 40 = 400ms
var timeout = 200;
var idle_callback_scheduled;
var idle_callback = this.step_func(function(deadline) {
var time_delta = performance.now() - idle_callback_scheduled;
assert_true(time_delta >= timeout, "Should only have been run after timeout");
assert_true(time_delta - timeout <= 80, "Should have been scheduled close to timeout");
assert_true(deadline.timeRemaining == 0, "IdleDeadline.timeRemaining MUST be equal to zero if requestIdleCallback was scheduled due to a timeout");
assert_true(deadline.didTimeout, "IdleDeadline.didTimeout MUST be true if requestIdleCallback was scheduled due to a timeout");
assert_true(busy_raf_loop_iterations_remaining > 0, "Busy rAF loop should still be going");
this.done();
});
requestAnimationFrame(this.step_func(function busyRAFLoop() {
var start_time = performance.now();
if (!idle_callback_scheduled) {
idle_callback_scheduled = start_time;
requestIdleCallback(idle_callback, timeout);
}
// Use up the whole frame budget.
while (performance.now() - start_time < 40) { }
if (busy_raf_loop_iterations_remaining > 0) {
busy_raf_loop_iterations_remaining--;
requestAnimationFrame(busyRAFLoop);
}
}));
}, 'requestIdleCallback scheduled with timeout when event loop is busy.');
</script>
<h1>Description</h1>
<p>This test validates that window.requestIdleCallback deals with timeouts correctly.</p>
<div id="log"></div>
...@@ -3054,6 +3054,10 @@ interface IDBVersionChangeEvent ...@@ -3054,6 +3054,10 @@ interface IDBVersionChangeEvent
getter newVersion getter newVersion
getter oldVersion getter oldVersion
method constructor method constructor
interface IdleCallbackDeadline
getter didTimeout
getter timeRemaining
method constructor
interface Image interface Image
method constructor method constructor
interface ImageBitmap interface ImageBitmap
...@@ -6296,6 +6300,7 @@ interface Window ...@@ -6296,6 +6300,7 @@ interface Window
method blur method blur
method btoa method btoa
method cancelAnimationFrame method cancelAnimationFrame
method cancelIdleCallback
method captureEvents method captureEvents
method clearInterval method clearInterval
method clearTimeout method clearTimeout
...@@ -6320,6 +6325,7 @@ interface Window ...@@ -6320,6 +6325,7 @@ interface Window
method prompt method prompt
method releaseEvents method releaseEvents
method requestAnimationFrame method requestAnimationFrame
method requestIdleCallback
method resizeBy method resizeBy
method resizeTo method resizeTo
method scroll method scroll
......
...@@ -69,6 +69,8 @@ ...@@ -69,6 +69,8 @@
'dom/Float32Array.idl', 'dom/Float32Array.idl',
'dom/Float64Array.idl', 'dom/Float64Array.idl',
'dom/FrameRequestCallback.idl', 'dom/FrameRequestCallback.idl',
'dom/IdleCallbackDeadline.idl',
'dom/IdleRequestCallback.idl',
'dom/Int16Array.idl', 'dom/Int16Array.idl',
'dom/Int32Array.idl', 'dom/Int32Array.idl',
'dom/Int8Array.idl', 'dom/Int8Array.idl',
...@@ -2310,6 +2312,9 @@ ...@@ -2310,6 +2312,9 @@
'dom/GlobalEventHandlers.h', 'dom/GlobalEventHandlers.h',
'dom/IconURL.cpp', 'dom/IconURL.cpp',
'dom/IconURL.h', 'dom/IconURL.h',
'dom/IdleCallbackDeadline.cpp',
'dom/IdleCallbackDeadline.h',
'dom/IdleRequestCallback.h',
'dom/IdTargetObserver.cpp', 'dom/IdTargetObserver.cpp',
'dom/IdTargetObserver.h', 'dom/IdTargetObserver.h',
'dom/IdTargetObserverRegistry.cpp', 'dom/IdTargetObserverRegistry.cpp',
...@@ -2390,6 +2395,8 @@ ...@@ -2390,6 +2395,8 @@
'dom/ScriptableDocumentParser.h', 'dom/ScriptableDocumentParser.h',
'dom/ScriptedAnimationController.cpp', 'dom/ScriptedAnimationController.cpp',
'dom/ScriptedAnimationController.h', 'dom/ScriptedAnimationController.h',
'dom/ScriptedIdleTaskController.cpp',
'dom/ScriptedIdleTaskController.h',
'dom/SecurityContext.cpp', 'dom/SecurityContext.cpp',
'dom/SecurityContext.h', 'dom/SecurityContext.h',
'dom/SelectorQuery.cpp', 'dom/SelectorQuery.cpp',
......
...@@ -91,6 +91,7 @@ ...@@ -91,6 +91,7 @@
#include "core/dom/ProcessingInstruction.h" #include "core/dom/ProcessingInstruction.h"
#include "core/dom/ScriptRunner.h" #include "core/dom/ScriptRunner.h"
#include "core/dom/ScriptedAnimationController.h" #include "core/dom/ScriptedAnimationController.h"
#include "core/dom/ScriptedIdleTaskController.h"
#include "core/dom/SelectorQuery.h" #include "core/dom/SelectorQuery.h"
#include "core/dom/StaticNodeList.h" #include "core/dom/StaticNodeList.h"
#include "core/dom/StyleEngine.h" #include "core/dom/StyleEngine.h"
...@@ -586,6 +587,8 @@ void Document::dispose() ...@@ -586,6 +587,8 @@ void Document::dispose()
m_scriptedAnimationController->clearDocumentPointer(); m_scriptedAnimationController->clearDocumentPointer();
m_scriptedAnimationController.clear(); m_scriptedAnimationController.clear();
m_scriptedIdleTaskController.clear();
if (svgExtensions()) if (svgExtensions())
accessSVGExtensions().pauseAnimations(); accessSVGExtensions().pauseAnimations();
...@@ -2136,6 +2139,8 @@ void Document::detach(const AttachContext& context) ...@@ -2136,6 +2139,8 @@ void Document::detach(const AttachContext& context)
m_scriptedAnimationController->clearDocumentPointer(); m_scriptedAnimationController->clearDocumentPointer();
m_scriptedAnimationController.clear(); m_scriptedAnimationController.clear();
m_scriptedIdleTaskController.clear();
if (svgExtensions()) if (svgExtensions())
accessSVGExtensions().pauseAnimations(); accessSVGExtensions().pauseAnimations();
...@@ -5130,6 +5135,25 @@ void Document::serviceScriptedAnimations(double monotonicAnimationStartTime) ...@@ -5130,6 +5135,25 @@ void Document::serviceScriptedAnimations(double monotonicAnimationStartTime)
m_scriptedAnimationController->serviceScriptedAnimations(monotonicAnimationStartTime); m_scriptedAnimationController->serviceScriptedAnimations(monotonicAnimationStartTime);
} }
ScriptedIdleTaskController& Document::ensureScriptedIdleTaskController()
{
if (!m_scriptedIdleTaskController)
m_scriptedIdleTaskController = ScriptedIdleTaskController::create(this, loader()->timing());
return *m_scriptedIdleTaskController;
}
int Document::requestIdleCallback(IdleRequestCallback* callback, double timeoutMillis)
{
return ensureScriptedIdleTaskController().registerCallback(callback, timeoutMillis);
}
void Document::cancelIdleCallback(int id)
{
if (!m_scriptedIdleTaskController)
return;
m_scriptedIdleTaskController->cancelCallback(id);
}
PassRefPtrWillBeRawPtr<Touch> Document::createTouch(DOMWindow* window, EventTarget* target, int identifier, double pageX, double pageY, double screenX, double screenY, double radiusX, double radiusY, float rotationAngle, float force) const PassRefPtrWillBeRawPtr<Touch> Document::createTouch(DOMWindow* window, EventTarget* target, int identifier, double pageX, double pageY, double screenX, double screenY, double radiusX, double radiusY, float rotationAngle, float force) const
{ {
// Match behavior from when these types were integers, and avoid surprises from someone explicitly // Match behavior from when these types were integers, and avoid surprises from someone explicitly
...@@ -5660,6 +5684,7 @@ DEFINE_TRACE(Document) ...@@ -5660,6 +5684,7 @@ DEFINE_TRACE(Document)
visitor->trace(m_documentTiming); visitor->trace(m_documentTiming);
visitor->trace(m_mediaQueryMatcher); visitor->trace(m_mediaQueryMatcher);
visitor->trace(m_scriptedAnimationController); visitor->trace(m_scriptedAnimationController);
visitor->trace(m_scriptedIdleTaskController);
visitor->trace(m_taskRunner); visitor->trace(m_taskRunner);
visitor->trace(m_textAutosizer); visitor->trace(m_textAutosizer);
visitor->trace(m_registrationContext); visitor->trace(m_registrationContext);
......
...@@ -122,6 +122,7 @@ class HTMLImportsController; ...@@ -122,6 +122,7 @@ class HTMLImportsController;
class HTMLLinkElement; class HTMLLinkElement;
class HTMLScriptElement; class HTMLScriptElement;
class HitTestRequest; class HitTestRequest;
class IdleRequestCallback;
class InputDeviceCapabilities; class InputDeviceCapabilities;
class LayoutPoint; class LayoutPoint;
class LiveNodeListBase; class LiveNodeListBase;
...@@ -146,6 +147,7 @@ class SVGUseElement; ...@@ -146,6 +147,7 @@ class SVGUseElement;
class ScriptRunner; class ScriptRunner;
class ScriptableDocumentParser; class ScriptableDocumentParser;
class ScriptedAnimationController; class ScriptedAnimationController;
class ScriptedIdleTaskController;
class SecurityOrigin; class SecurityOrigin;
class SegmentedString; class SegmentedString;
class SelectorQueryCache; class SelectorQueryCache;
...@@ -914,6 +916,9 @@ public: ...@@ -914,6 +916,9 @@ public:
void cancelAnimationFrame(int id); void cancelAnimationFrame(int id);
void serviceScriptedAnimations(double monotonicAnimationStartTime); void serviceScriptedAnimations(double monotonicAnimationStartTime);
int requestIdleCallback(IdleRequestCallback*, double timeoutMillis);
void cancelIdleCallback(int id);
EventTarget* errorEventTarget() final; EventTarget* errorEventTarget() final;
void logExceptionToConsole(const String& errorMessage, int scriptId, const String& sourceURL, int lineNumber, int columnNumber, PassRefPtrWillBeRawPtr<ScriptCallStack>) final; void logExceptionToConsole(const String& errorMessage, int scriptId, const String& sourceURL, int lineNumber, int columnNumber, PassRefPtrWillBeRawPtr<ScriptCallStack>) final;
...@@ -1065,6 +1070,7 @@ private: ...@@ -1065,6 +1070,7 @@ private:
bool isElementNode() const = delete; // This will catch anyone doing an unnecessary check. bool isElementNode() const = delete; // This will catch anyone doing an unnecessary check.
ScriptedAnimationController& ensureScriptedAnimationController(); ScriptedAnimationController& ensureScriptedAnimationController();
ScriptedIdleTaskController& ensureScriptedIdleTaskController();
SecurityContext& securityContext() final { return *this; } SecurityContext& securityContext() final { return *this; }
EventQueue* eventQueue() const final; EventQueue* eventQueue() const final;
...@@ -1333,6 +1339,7 @@ private: ...@@ -1333,6 +1339,7 @@ private:
unsigned m_writeRecursionDepth; unsigned m_writeRecursionDepth;
RefPtrWillBeMember<ScriptedAnimationController> m_scriptedAnimationController; RefPtrWillBeMember<ScriptedAnimationController> m_scriptedAnimationController;
RefPtrWillBeMember<ScriptedIdleTaskController> m_scriptedIdleTaskController;
OwnPtrWillBeMember<MainThreadTaskRunner> m_taskRunner; OwnPtrWillBeMember<MainThreadTaskRunner> m_taskRunner;
OwnPtrWillBeMember<TextAutosizer> m_textAutosizer; OwnPtrWillBeMember<TextAutosizer> m_textAutosizer;
......
// Copyright 2015 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 "config.h"
#include "core/dom/IdleCallbackDeadline.h"
#include "core/loader/DocumentLoadTiming.h"
#include "wtf/CurrentTime.h"
namespace blink {
IdleCallbackDeadline::IdleCallbackDeadline(double deadlineMillis, CallbackType callbackType, const DocumentLoadTiming& timing)
: m_deadlineMillis(deadlineMillis)
, m_callbackType(callbackType)
, m_timing(timing)
{
}
double IdleCallbackDeadline::timeRemaining() const
{
double timeRemaining = m_deadlineMillis - (1000 * m_timing.monotonicTimeToZeroBasedDocumentTime(monotonicallyIncreasingTime()));
if (timeRemaining < 0)
timeRemaining = 0;
return timeRemaining;
}
} // namespace blink
// Copyright 2015 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.
#ifndef IdleCallbackDeadline_h
#define IdleCallbackDeadline_h
#include "bindings/core/v8/ScriptWrappable.h"
#include "platform/heap/Handle.h"
namespace blink {
class DocumentLoadTiming;
class IdleCallbackDeadline : public GarbageCollected<IdleCallbackDeadline>, public ScriptWrappable {
DEFINE_WRAPPERTYPEINFO();
public:
enum class CallbackType {
CalledWhenIdle,
CalledByTimeout
};
DEFINE_INLINE_TRACE() {}
static IdleCallbackDeadline* create(double deadlineMillis, CallbackType callbackType, const DocumentLoadTiming& timing)
{
return new IdleCallbackDeadline(deadlineMillis, callbackType, timing);
}
double timeRemaining() const;
bool didTimeout() const
{
return m_callbackType == CallbackType::CalledByTimeout;
}
private:
IdleCallbackDeadline(double deadlineMillis, CallbackType, const DocumentLoadTiming&);
double m_deadlineMillis;
CallbackType m_callbackType;
const DocumentLoadTiming& m_timing;
};
}
#endif // IdleCallbackDeadline_h
// Copyright 2015 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.
[
GarbageCollected,
RuntimeEnabled=RequestIdleCallback,
] interface IdleCallbackDeadline {
readonly attribute double timeRemaining;
readonly attribute boolean didTimeout;
};
// Copyright 2015 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.
#ifndef IdleRequestCallback_h
#define IdleRequestCallback_h
#include "core/dom/ScriptedIdleTaskController.h"
#include "platform/Timer.h"
#include "platform/heap/Handle.h"
namespace blink {
class IdleCallbackDeadline;
class IdleRequestCallback : public GarbageCollectedFinalized<IdleRequestCallback> {
public:
DEFINE_INLINE_VIRTUAL_TRACE() {}
virtual ~IdleRequestCallback() {}
virtual void handleEvent(IdleCallbackDeadline*) = 0;
};
}
#endif // IdleRequestCallback_h
// Copyright 2015 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.
[
GarbageCollected,
RuntimeEnabled=RequestIdleCallback,
] callback interface IdleRequestCallback {
void handleEvent(IdleCallbackDeadline deadline);
};
// Copyright 2015 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 "config.h"
#include "core/dom/ScriptedIdleTaskController.h"
#include "core/dom/ExecutionContext.h"
#include "core/dom/IdleRequestCallback.h"
#include "core/loader/DocumentLoadTiming.h"
#include "platform/Logging.h"
#include "platform/TraceEvent.h"
#include "public/platform/Platform.h"
#include "public/platform/WebScheduler.h"
#include "public/platform/WebTraceLocation.h"
#include "wtf/CurrentTime.h"
#include "wtf/Functional.h"
namespace blink {
namespace internal {
class IdleRequestCallbackWrapper : public RefCounted<IdleRequestCallbackWrapper> {
public:
static PassRefPtr<IdleRequestCallbackWrapper> create(ScriptedIdleTaskController::CallbackId id, PassRefPtrWillBeRawPtr<ScriptedIdleTaskController> controller)
{
return adoptRef(new IdleRequestCallbackWrapper(id, controller));
}
virtual ~IdleRequestCallbackWrapper()
{
}
static void idleTaskFired(PassRefPtr<IdleRequestCallbackWrapper> callbackWrapper, double deadlineSeconds)
{
// TODO(rmcilroy): Implement clamping of deadline in some form.
callbackWrapper->controller()->callbackFired(callbackWrapper->id(), deadlineSeconds, IdleCallbackDeadline::CallbackType::CalledWhenIdle);
}
static void timeoutFired(PassRefPtr<IdleRequestCallbackWrapper> callbackWrapper)
{
callbackWrapper->controller()->callbackFired(callbackWrapper->id(), monotonicallyIncreasingTime(), IdleCallbackDeadline::CallbackType::CalledByTimeout);
}
ScriptedIdleTaskController::CallbackId id() const { return m_id; }
PassRefPtrWillBeRawPtr<ScriptedIdleTaskController> controller() const { return m_controller; }
private:
explicit IdleRequestCallbackWrapper(ScriptedIdleTaskController::CallbackId id, PassRefPtrWillBeRawPtr<ScriptedIdleTaskController> controller)
: m_id(id)
, m_controller(controller)
{
}
ScriptedIdleTaskController::CallbackId m_id;
RefPtrWillBePersistent<ScriptedIdleTaskController> m_controller;
};
} // namespace internal
ScriptedIdleTaskController::ScriptedIdleTaskController(ExecutionContext* context, const DocumentLoadTiming& timing)
: ActiveDOMObject(context)
, m_timing(timing)
, m_scheduler(Platform::current()->currentThread()->scheduler())
, m_nextCallbackId(0)
, m_suspended(false)
{
suspendIfNeeded();
}
DEFINE_EMPTY_DESTRUCTOR_WILL_BE_REMOVED(ScriptedIdleTaskController);
DEFINE_TRACE(ScriptedIdleTaskController)
{
ActiveDOMObject::trace(visitor);
visitor->trace(m_callbacks);
}
ScriptedIdleTaskController::CallbackId ScriptedIdleTaskController::registerCallback(IdleRequestCallback* callback, double timeoutMillis)
{
CallbackId id = ++m_nextCallbackId;
m_callbacks.set(id, callback);
RefPtr<internal::IdleRequestCallbackWrapper> callbackWrapper = internal::IdleRequestCallbackWrapper::create(id, this);
m_scheduler->postIdleTask(FROM_HERE, WTF::bind<double>(&internal::IdleRequestCallbackWrapper::idleTaskFired, callbackWrapper));
if (timeoutMillis > 0)
m_scheduler->postTimerTask(FROM_HERE, WTF::bind(&internal::IdleRequestCallbackWrapper::timeoutFired, callbackWrapper), static_cast<long long>(timeoutMillis));
// TODO(rmcilroy): Add devtools tracing.
return id;
}
void ScriptedIdleTaskController::cancelCallback(CallbackId id)
{
// TODO(rmcilroy): Add devtools tracing.
m_callbacks.remove(id);
}
void ScriptedIdleTaskController::callbackFired(CallbackId id, double deadlineSeconds, IdleCallbackDeadline::CallbackType callbackType)
{
if (!m_callbacks.contains(id))
return;
if (m_suspended) {
if (callbackType == IdleCallbackDeadline::CallbackType::CalledByTimeout) {
// Queue for execution when we are resumed.
m_pendingTimeouts.append(id);
}
// Just drop callbacks called while suspended, these will be reposted on the idle task queue when we are resumed.
return;
}
double deadlineMillis = 1000.0 * m_timing.monotonicTimeToZeroBasedDocumentTime(deadlineSeconds);
runCallback(id, deadlineMillis, callbackType);
}
void ScriptedIdleTaskController::runCallback(CallbackId id, double deadlineMillis, IdleCallbackDeadline::CallbackType callbackType)
{
ASSERT(!m_suspended);
auto callback = m_callbacks.take(id);
if (!callback)
return;
// TODO(rmcilroy): Add devtools tracing.
callback->handleEvent(IdleCallbackDeadline::create(deadlineMillis, callbackType, m_timing));
}
void ScriptedIdleTaskController::stop()
{
m_callbacks.clear();
}
void ScriptedIdleTaskController::suspend()
{
m_suspended = true;
}
void ScriptedIdleTaskController::resume()
{
ASSERT(m_suspended);
m_suspended = false;
// Run any pending timeouts.
Vector<CallbackId> pendingTimeouts;
m_pendingTimeouts.swap(pendingTimeouts);
for (auto& id : pendingTimeouts)
runCallback(id, monotonicallyIncreasingTime(), IdleCallbackDeadline::CallbackType::CalledByTimeout);
// Repost idle tasks for any remaining callbacks.
for (auto& callback : m_callbacks) {
RefPtr<internal::IdleRequestCallbackWrapper> callbackWrapper = internal::IdleRequestCallbackWrapper::create(callback.key, this);
m_scheduler->postIdleTask(FROM_HERE, WTF::bind<double>(&internal::IdleRequestCallbackWrapper::idleTaskFired, callbackWrapper));
}
}
bool ScriptedIdleTaskController::hasPendingActivity() const
{
return !m_callbacks.isEmpty();
}
} // namespace blink
// Copyright 2015 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.
#ifndef ScriptedIdleTaskController_h
#define ScriptedIdleTaskController_h
#include "core/dom/ActiveDOMObject.h"
#include "core/dom/IdleCallbackDeadline.h"
#include "platform/Timer.h"
#include "platform/heap/Handle.h"
#include "wtf/RefCounted.h"
#include "wtf/RefPtr.h"
#include "wtf/Vector.h"
namespace blink {
class ExecutionContext;
class IdleRequestCallback;
class DocumentLoadTiming;
class ScriptedIdleTaskController : public RefCountedWillBeGarbageCollected<ScriptedIdleTaskController>, public ActiveDOMObject {
DECLARE_EMPTY_DESTRUCTOR_WILL_BE_REMOVED(ScriptedIdleTaskController);
public:
static PassRefPtrWillBeRawPtr<ScriptedIdleTaskController> create(ExecutionContext* context, const DocumentLoadTiming& timing)
{
return adoptRefWillBeNoop(new ScriptedIdleTaskController(context, timing));
}
DECLARE_TRACE();
using CallbackId = int;
int registerCallback(IdleRequestCallback*, double timeoutMillis);
void cancelCallback(CallbackId);
// ActiveDOMObject interface.
void stop() override;
void suspend() override;
void resume() override;
bool hasPendingActivity() const override;
void callbackFired(CallbackId, double deadlineSeconds, IdleCallbackDeadline::CallbackType);
private:
ScriptedIdleTaskController(ExecutionContext*, const DocumentLoadTiming&);
void runCallback(CallbackId, double deadlineMillis, IdleCallbackDeadline::CallbackType);
const DocumentLoadTiming& m_timing;
WebScheduler* m_scheduler; // Not owned.
PersistentHeapHashMapWillBeHeapHashMap<CallbackId, Member<IdleRequestCallback>> m_callbacks;
Vector<CallbackId> m_pendingTimeouts;
CallbackId m_nextCallbackId;
bool m_suspended;
};
} // namespace blink
#endif // ScriptedIdleTaskController_h
...@@ -28,6 +28,7 @@ class Element; ...@@ -28,6 +28,7 @@ class Element;
class Frame; class Frame;
class FrameRequestCallback; class FrameRequestCallback;
class History; class History;
class IdleRequestCallback;
class LocalDOMWindow; class LocalDOMWindow;
class MediaQueryList; class MediaQueryList;
class Navigator; class Navigator;
...@@ -168,6 +169,10 @@ public: ...@@ -168,6 +169,10 @@ public:
virtual int webkitRequestAnimationFrame(FrameRequestCallback*) = 0; virtual int webkitRequestAnimationFrame(FrameRequestCallback*) = 0;
virtual void cancelAnimationFrame(int id) = 0; virtual void cancelAnimationFrame(int id) = 0;
// Idle callback extensions
virtual int requestIdleCallback(IdleRequestCallback*, double timeoutMillis) = 0;
virtual void cancelIdleCallback(int id) = 0;
void captureEvents() { } void captureEvents() { }
void releaseEvents() { } void releaseEvents() { }
......
...@@ -1264,23 +1264,36 @@ void LocalDOMWindow::resizeTo(int width, int height) const ...@@ -1264,23 +1264,36 @@ void LocalDOMWindow::resizeTo(int width, int height) const
int LocalDOMWindow::requestAnimationFrame(FrameRequestCallback* callback) int LocalDOMWindow::requestAnimationFrame(FrameRequestCallback* callback)
{ {
callback->m_useLegacyTimeBase = false; callback->m_useLegacyTimeBase = false;
if (Document* d = document()) if (Document* doc = document())
return d->requestAnimationFrame(callback); return doc->requestAnimationFrame(callback);
return 0; return 0;
} }
int LocalDOMWindow::webkitRequestAnimationFrame(FrameRequestCallback* callback) int LocalDOMWindow::webkitRequestAnimationFrame(FrameRequestCallback* callback)
{ {
callback->m_useLegacyTimeBase = true; callback->m_useLegacyTimeBase = true;
if (Document* d = document()) if (Document* document = this->document())
return d->requestAnimationFrame(callback); return document->requestAnimationFrame(callback);
return 0; return 0;
} }
void LocalDOMWindow::cancelAnimationFrame(int id) void LocalDOMWindow::cancelAnimationFrame(int id)
{ {
if (Document* d = document()) if (Document* document = this->document())
d->cancelAnimationFrame(id); document->cancelAnimationFrame(id);
}
int LocalDOMWindow::requestIdleCallback(IdleRequestCallback* callback, double timeoutMillis)
{
if (Document* document = this->document())
return document->requestIdleCallback(callback, timeoutMillis);
return 0;
}
void LocalDOMWindow::cancelIdleCallback(int id)
{
if (Document* document = this->document())
document->cancelIdleCallback(id);
} }
bool LocalDOMWindow::addEventListener(const AtomicString& eventType, PassRefPtrWillBeRawPtr<EventListener> prpListener, bool useCapture) bool LocalDOMWindow::addEventListener(const AtomicString& eventType, PassRefPtrWillBeRawPtr<EventListener> prpListener, bool useCapture)
......
...@@ -144,6 +144,8 @@ public: ...@@ -144,6 +144,8 @@ public:
int requestAnimationFrame(FrameRequestCallback*) override; int requestAnimationFrame(FrameRequestCallback*) override;
int webkitRequestAnimationFrame(FrameRequestCallback*) override; int webkitRequestAnimationFrame(FrameRequestCallback*) override;
void cancelAnimationFrame(int id) override; void cancelAnimationFrame(int id) override;
int requestIdleCallback(IdleRequestCallback*, double timeoutMillis) override;
void cancelIdleCallback(int id) override;
void schedulePostMessage(PassRefPtrWillBeRawPtr<MessageEvent>, LocalDOMWindow* source, SecurityOrigin* target, PassRefPtrWillBeRawPtr<ScriptCallStack> stackTrace); void schedulePostMessage(PassRefPtrWillBeRawPtr<MessageEvent>, LocalDOMWindow* source, SecurityOrigin* target, PassRefPtrWillBeRawPtr<ScriptCallStack> stackTrace);
void registerProperty(DOMWindowProperty*); void registerProperty(DOMWindowProperty*);
......
...@@ -328,6 +328,17 @@ void RemoteDOMWindow::cancelAnimationFrame(int id) ...@@ -328,6 +328,17 @@ void RemoteDOMWindow::cancelAnimationFrame(int id)
ASSERT_NOT_REACHED(); ASSERT_NOT_REACHED();
} }
int RemoteDOMWindow::requestIdleCallback(IdleRequestCallback*, double timeoutMillis)
{
ASSERT_NOT_REACHED();
return 0;
}
void RemoteDOMWindow::cancelIdleCallback(int id)
{
ASSERT_NOT_REACHED();
}
RemoteDOMWindow::RemoteDOMWindow(RemoteFrame& frame) RemoteDOMWindow::RemoteDOMWindow(RemoteFrame& frame)
: m_frame(&frame) : m_frame(&frame)
{ {
......
...@@ -77,6 +77,8 @@ public: ...@@ -77,6 +77,8 @@ public:
int requestAnimationFrame(FrameRequestCallback*) override; int requestAnimationFrame(FrameRequestCallback*) override;
int webkitRequestAnimationFrame(FrameRequestCallback*) override; int webkitRequestAnimationFrame(FrameRequestCallback*) override;
void cancelAnimationFrame(int id) override; void cancelAnimationFrame(int id) override;
int requestIdleCallback(IdleRequestCallback*, double timeoutMillis) override;
void cancelIdleCallback(int id) override;
private: private:
explicit RemoteDOMWindow(RemoteFrame&); explicit RemoteDOMWindow(RemoteFrame&);
......
...@@ -83,6 +83,9 @@ ...@@ -83,6 +83,9 @@
[MeasureAs=UnprefixedRequestAnimationFrame] long requestAnimationFrame(FrameRequestCallback callback); [MeasureAs=UnprefixedRequestAnimationFrame] long requestAnimationFrame(FrameRequestCallback callback);
void cancelAnimationFrame(long handle); void cancelAnimationFrame(long handle);
[RuntimeEnabled=RequestIdleCallback] long requestIdleCallback(IdleRequestCallback callback, optional double timeout = 0);
[RuntimeEnabled=RequestIdleCallback] void cancelIdleCallback(long handle);
[DoNotCheckSecurity, Custom, RaisesException] void postMessage(any message, DOMString targetOrigin, optional sequence<Transferable> transfer); [DoNotCheckSecurity, Custom, RaisesException] void postMessage(any message, DOMString targetOrigin, optional sequence<Transferable> transfer);
// HTML obsolete features // HTML obsolete features
......
...@@ -132,6 +132,7 @@ PushMessaging status=stable ...@@ -132,6 +132,7 @@ PushMessaging status=stable
PushMessagingData status=test PushMessagingData status=test
QuotaPromise status=experimental QuotaPromise status=experimental
ReducedReferrerGranularity ReducedReferrerGranularity
RequestIdleCallback status=experimental
RequestAutocomplete status=test RequestAutocomplete status=test
RestrictIFramePermissions RestrictIFramePermissions
SandboxBlocksModals status=stable SandboxBlocksModals status=stable
......
...@@ -59,4 +59,9 @@ void WebScheduler::postLoadingTask(const WebTraceLocation& location, PassOwnPtr< ...@@ -59,4 +59,9 @@ void WebScheduler::postLoadingTask(const WebTraceLocation& location, PassOwnPtr<
postLoadingTask(location, new blink::Task(task)); postLoadingTask(location, new blink::Task(task));
} }
void WebScheduler::postTimerTask(const WebTraceLocation& location, PassOwnPtr<Task> task, long long delayMs)
{
postTimerTask(location, new blink::Task(task), delayMs);
}
} // namespace blink } // namespace blink
...@@ -92,6 +92,7 @@ public: ...@@ -92,6 +92,7 @@ public:
void postNonNestableIdleTask(const WebTraceLocation&, PassOwnPtr<IdleTask>); void postNonNestableIdleTask(const WebTraceLocation&, PassOwnPtr<IdleTask>);
void postIdleTaskAfterWakeup(const WebTraceLocation&, PassOwnPtr<IdleTask>); void postIdleTaskAfterWakeup(const WebTraceLocation&, PassOwnPtr<IdleTask>);
void postLoadingTask(const WebTraceLocation&, PassOwnPtr<Task>); void postLoadingTask(const WebTraceLocation&, PassOwnPtr<Task>);
void postTimerTask(const WebTraceLocation&, PassOwnPtr<Task>, long long delayMs);
#endif #endif
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment