@@ -151,8 +151,12 @@ void ResolveFrameCallbacksClass(JEnv& env) {
151151using EntryId = uintptr_t ;
152152
153153struct FrameCallbackEntry {
154- FrameCallbackEntry (Isolate* isolate, Local<Function> callback, EntryId id)
155- : isolate_(isolate), callback_(isolate, callback), id_(id) {
154+ FrameCallbackEntry (Isolate* isolate, Local<Function> callback, EntryId id,
155+ bool isAnimationFrame = false )
156+ : isolate_(isolate),
157+ callback_ (isolate, callback),
158+ id_(id),
159+ isAnimationFrame_(isAnimationFrame) {
156160 }
157161
158162 ~FrameCallbackEntry () {
@@ -200,6 +204,13 @@ struct FrameCallbackEntry {
200204 Isolate* isolate_;
201205 Global<Function> callback_;
202206 EntryId id_;
207+ /*
208+ * requestAnimationFrame entries are anonymous one-shots addressed only by
209+ * their returned handle: they never dedupe by function, take the single
210+ * spec-mandated timestamp argument, and cancelAnimationFrame may only
211+ * touch entries carrying this flag.
212+ */
213+ bool isAnimationFrame_;
203214 jobject javaCallback_ = nullptr ;
204215
205216private:
@@ -273,15 +284,23 @@ void Dispatch(EntryId id, int64_t frameTimeNanos) {
273284
274285 entry->MarkUnscheduled ();
275286
287+ Local<Value> timelineMillis =
288+ Number::New (isolate, Performance::MonotonicNanosToTimelineMillis (
289+ isolate, frameTimeNanos));
276290 Local<Value> args[2 ] = {
277291 Number::New (isolate, (double ) frameTimeNanos),
278- Number::New (isolate, Performance::MonotonicNanosToTimelineMillis (
279- isolate, frameTimeNanos)),
292+ timelineMillis,
280293 };
281294
282295 TryCatch tc (isolate);
283296
284- cb->Call (context, context->Global (), 2 , args); // ignore JS return value
297+ if (entry->isAnimationFrame_ ) {
298+ // spec signature: a single DOMHighResTimeStamp on this isolate's
299+ // performance timeline
300+ cb->Call (context, context->Global (), 1 , &timelineMillis);
301+ } else {
302+ cb->Call (context, context->Global (), 2 , args); // ignore JS return value
303+ }
285304
286305 // Re-resolve: the callback may have rescheduled or removed itself.
287306 entry = FindEntryById (id);
@@ -436,6 +455,58 @@ void FrameCallbacks::RemoveFrameCallback(const FunctionCallbackInfo<Value>& args
436455 }
437456}
438457
458+ void FrameCallbacks::RequestAnimationFrame (const FunctionCallbackInfo<Value>& args) {
459+ Isolate* isolate = args.GetIsolate ();
460+ Locker locker (isolate);
461+ Isolate::Scope isolateScope (isolate);
462+ HandleScope handleScope (isolate);
463+ Local<Context> context = isolate->GetCurrentContext ();
464+ Context::Scope contextScope (context);
465+
466+ if (args.Length () < 1 || !args[0 ]->IsFunction ()) {
467+ isolate->ThrowException (Exception::TypeError (String::NewFromUtf8Literal (
468+ isolate, " Animation frame callback argument is not a function" )));
469+ return ;
470+ }
471+
472+ // Every call registers its own entry: the spec runs a function as many
473+ // times as it was requested, so no dedupe by function here.
474+ EntryId id = ++entryCount_;
475+ FrameCallbackEntry* entry;
476+ {
477+ std::lock_guard<std::mutex> lock (entriesMutex_);
478+ auto inserted = entries_.emplace (
479+ id, std::make_unique<FrameCallbackEntry>(
480+ isolate, args[0 ].As <Function>(), id,
481+ /* isAnimationFrame */ true ));
482+ NS_DCHECK (inserted.second && " Frame callback ID should not be duplicated" );
483+ entry = inserted.first ->second .get ();
484+ }
485+
486+ entry->MarkScheduled ();
487+ Post (entry, 0 );
488+ args.GetReturnValue ().Set (Number::New (isolate, (double ) id));
489+ }
490+
491+ void FrameCallbacks::CancelAnimationFrame (const FunctionCallbackInfo<Value>& args) {
492+ Isolate* isolate = args.GetIsolate ();
493+ Locker locker (isolate);
494+ Isolate::Scope isolateScope (isolate);
495+ HandleScope handleScope (isolate);
496+ Local<Context> context = isolate->GetCurrentContext ();
497+ Context::Scope contextScope (context);
498+
499+ // per spec an unknown or malformed handle is a silent no-op
500+ if (args.Length () < 1 || !args[0 ]->IsNumber ()) {
501+ return ;
502+ }
503+ EntryId id = (EntryId) args[0 ]->IntegerValue (context).FromMaybe (0 );
504+ FrameCallbackEntry* entry = FindEntryById (id);
505+ if (entry != nullptr && entry->isAnimationFrame_ ) {
506+ entry->MarkRemoved ();
507+ }
508+ }
509+
439510void FrameCallbacks::RemoveIsolateEntries (Isolate* isolate) {
440511 // Detached first, destroyed after the mutex is released: the destructors
441512 // call into Java.
@@ -454,6 +525,15 @@ void FrameCallbacks::RemoveIsolateEntries(Isolate* isolate) {
454525}
455526
456527void FrameCallbacks::Init (Isolate* isolate, Local<ObjectTemplate> globalTemplate) {
528+ globalTemplate->Set (
529+ ArgConverter::ConvertToV8String (isolate, " requestAnimationFrame" ),
530+ FunctionTemplate::New (isolate, RequestAnimationFrame));
531+ globalTemplate->Set (
532+ ArgConverter::ConvertToV8String (isolate, " cancelAnimationFrame" ),
533+ FunctionTemplate::New (isolate, CancelAnimationFrame));
534+ // kept for backwards compatibility with callers that predate
535+ // requestAnimationFrame; unlike it, these dedupe by function, take an
536+ // optional delay, and hand the callback the raw frame time as well
457537 globalTemplate->Set (
458538 ArgConverter::ConvertToV8String (isolate, " __postFrameCallback" ),
459539 FunctionTemplate::New (isolate, PostFrameCallback));
0 commit comments