Browse files

Implement requestAnimationFrame

Cherry-pick from webkit-org branch on Code Aurora Forum:

requestAnimationFrame doesn't throttle on Mac
https://www.codeaurora.org/gitweb/quic/la/?p=platform/external/webkit.git;a=commit;h=d9dca741b4762c433ae18f7a1bc59a3a81861fc8

Timestamp parameter to requestAnimationFrame is busted in USE(REQUEST_ANIMATION_FRAME_TIMER) path
https://www.codeaurora.org/gitweb/quic/la/?p=platform/external/webkit.git;a=commit;h=f0909a46fa167c84062c63caffef340a6054bc1e

Rename webkitCancelRequestAnimationFrame to webkitCancelAnimationFrame to match spec change
https://www.codeaurora.org/gitweb/quic/la/?p=platform/external/webkit.git;a=commit;h=cd5d11d662d638b3e4dfda33f23cda907f007f12

Remove partially implemented per-Element visibility checks from requestAnimationFrame logic
https://www.codeaurora.org/gitweb/quic/la/?p=platform/external/webkit.git;a=commit;h=9fb90af3cebd0e595990cded0941d230cf77dcc1

Change-Id: Ib7e5a1c8138e2ca82c69fe904c1338a7b1fd48ef

Conflicts:

	Android.mk
  • Loading branch information...
1 parent 5285073 commit c79e317b772ee522d37c2c5855d136b59674c90d Yida Wang committed with Whitehawkx Aug 23, 2012
View
2 Android.mk
@@ -270,6 +270,8 @@ ifeq ($(ENABLE_WTF_USE_ACCELERATED_COMPOSITING),true)
LOCAL_CFLAGS += -DWTF_USE_ACCELERATED_COMPOSITING=1
endif
+LOCAL_CFLAGS += -DENABLE_REQUEST_ANIMATION_FRAME=1
+
# LOCAL_LDLIBS is used in simulator builds only and simulator builds are only
# valid on Linux
LOCAL_LDLIBS += -lpthread -ldl
View
11 LayoutTests/fast/animation/request-animation-frame-timestamps-advance-expected.txt
@@ -0,0 +1,11 @@
+Tests the timestamps provided to requestAnimationFrame callbacks advance
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS firstTimestamp is defined.
+PASS secondTimestamp is defined.
+PASS secondTimestamp > firstTimestamp is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
View
15 LayoutTests/fast/animation/request-animation-frame-timestamps-advance.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="stylesheet" href="../js/resources/js-test-style.css">
+<script src="../js/resources/js-test-pre.js"></script>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<span id="e"></span>
+<span id="f"></span>
+<script src="script-tests/request-animation-frame-timestamps-advance.js"></script>
+<script src="../js/resources/js-test-post-function.js"></script>
+</body>
+</html>
View
37 LayoutTests/fast/animation/script-tests/request-animation-frame-timestamps-advance.js
@@ -0,0 +1,37 @@
+description("Tests the timestamps provided to requestAnimationFrame callbacks advance");
+
+function busyWait(millis) {
+ var start = Date.now();
+ while (Date.now()-start < millis) {}
+}
+
+var firstTimestamp = undefined;
+var secondTimestamp = undefined;
+
+window.webkitRequestAnimationFrame(function(timestamp) {
+ firstTimestamp = timestamp;
+ shouldBeDefined("firstTimestamp");
+ window.webkitRequestAnimationFrame(function(timestamp) {
+ secondTimestamp = timestamp;
+ shouldBeDefined("secondTimestamp");
+ shouldBeTrue("secondTimestamp > firstTimestamp");
+ isSuccessfullyParsed();
+ if (window.layoutTestController)
+ layoutTestController.notifyDone();
+ });
+ busyWait(10);
+ if (window.layoutTestController)
+ layoutTestController.display();
+});
+
+
+if (window.layoutTestController)
+ window.setTimeout(function() {
+ layoutTestController.display();
+ });
+
+
+if (window.layoutTestController)
+ layoutTestController.waitUntilDone();
+
+var successfullyParsed = true;
View
13 Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,16 @@
+2011-09-09 Chris Marrin <cmarrin@apple.com>
+
+ requestAnimationFrame doesn't throttle on Mac
+ https://bugs.webkit.org/show_bug.cgi?id=67171
+
+ Reviewed by Simon Fraser.
+
+ Added WTF_USE_REQUEST_ANIMATION_FRAME_TIMER to allow any platform to run
+ requestAnimationFrame callbacks on a Timer defined in ScriptedAnimationController.
+ Currently only enabled for PLATFORM(MAC)
+
+ * wtf/Platform.h:
+
2011-04-19 Oliver Hunt <oliver@apple.com>
Reviewed by Gavin Barraclough.
View
4 Source/JavaScriptCore/wtf/Platform.h
@@ -1305,4 +1305,8 @@
#define WTF_USE_UNIX_DOMAIN_SOCKETS 1
#endif
+#if PLATFORM(MAC) || PLATFORM(ANDROID)
+#define WTF_USE_REQUEST_ANIMATION_FRAME_TIMER 1
+#endif
+
#endif /* WTF_Platform_h */
View
3 Source/WebCore/Android.derived.v8bindings.mk
@@ -39,6 +39,8 @@ ifeq ($(ENABLE_SVG), true)
FEATURE_DEFINES += ENABLE_SVG=1
endif
+FEATURE_DEFINES += ENABLE_REQUEST_ANIMATION_FRAME
+
# CSS
GEN := \
$(intermediates)/bindings/V8CSSCharsetRule.h \
@@ -125,6 +127,7 @@ GEN := \
$(intermediates)/bindings/V8ProgressEvent.h \
$(intermediates)/bindings/V8Range.h \
$(intermediates)/bindings/V8RangeException.h \
+ $(intermediates)/bindings/V8RequestAnimationFrameCallback.h \
$(intermediates)/bindings/V8Text.h \
$(intermediates)/bindings/V8TextEvent.h \
$(intermediates)/bindings/V8Touch.h \
View
96 Source/WebCore/ChangeLog
@@ -1,5 +1,99 @@
-2011-05-11 Antoine Labour <piman@chromium.org>
+2012-04-09 James Robinson <jamesr@chromium.org>
+
+ Remove partially implemented per-Element visibility checks from requestAnimationFrame logic
+ https://bugs.webkit.org/show_bug.cgi?id=74232
+
+ Reviewed by Dean Jackson.
+
+ The initial requestAnimationFrame implementation had an Element parameter as the second argument to the
+ function. This element was intended to convey the element associated with the animation so that when the element
+ was not visible the animation callback would not be run. The checked in implementation does a very limited check
+ - testing for display:none and being detached from the tree - but does it in a way that does not work correctly
+ if an element's visibility is manipulated by a callback running from a different document. It also adds
+ significant complexity to the code, making it less hackable and easy to introduce subtle security bugs or
+ infinite loops.
+
+ This patch removes the parameter. Since it has always been marked optional, there is no web compat risk.
+
+ If this functionality is added back in the future it needs to be implemented in a way that considers all
+ callbacks within a Page and not only those within a single Document.
+ * dom/Document.cpp:
+ (WebCore::Document::webkitRequestAnimationFrame):
+ * dom/Document.h:
+ * dom/RequestAnimationFrameCallback.h:
+ * dom/ScriptedAnimationController.cpp:
+ (WebCore::ScriptedAnimationController::registerCallback):
+ (WebCore::ScriptedAnimationController::serviceScriptedAnimations):
+ * dom/ScriptedAnimationController.h:
+ * page/DOMWindow.cpp:
+ (WebCore::DOMWindow::webkitRequestAnimationFrame):
+ * page/DOMWindow.h:
+ * page/DOMWindow.idl:
+
+2011-12-12 James Robinson <jamesr@chromium.org>
+
+ Rename webkitCancelRequestAnimationFrame to webkitCancelAnimationFrame to match spec change
+ https://bugs.webkit.org/show_bug.cgi?id=74231
+
+ Reviewed by Simon Fraser.
+
+ The RequestAnimationFrame spec has renamed cancelRequestAnimationFrame to cancelAnimationFrame in response to
+ feedback from Mozilla and Microsoft that the old name was too long and didn't parallel setTimeout/clearTimeout
+ and setInterval/clearInterval very well. This updates our IDL to match, while preserving the old name as an
+ alias to be compatible with current content.
+
+ * dom/Document.cpp:
+ (WebCore::Document::webkitCancelAnimationFrame):
+ * dom/Document.h:
+ * page/DOMWindow.cpp:
+ (WebCore::DOMWindow::webkitCancelAnimationFrame):
+ * page/DOMWindow.h:
+ (WebCore::DOMWindow::webkitCancelRequestAnimationFrame):
+ * page/DOMWindow.idl:
+
+2011-09-26 James Robinson <jamesr@chromium.org>
+
+ [mac] Timestamp parameter to requestAnimationFrame is busted in USE(REQUEST_ANIMATION_FRAME_TIMER) path
+ https://bugs.webkit.org/show_bug.cgi?id=68769
+
+ Reviewed by Simon Fraser.
+
+ Convert the time parameter from double to DOMTimeStamp using convertSecondsToDOMTimeStamp rather than relying on
+ implicit double->long conversion, which ignores the units of the value.
+
+ Test: fast/animation/request-animation-frame-timestamps-advance.html
+
+ * dom/ScriptedAnimationController.cpp:
+ (WebCore::ScriptedAnimationController::animationTimerFired):
+
+2011-09-09 Chris Marrin <cmarrin@apple.com>
+
+ requestAnimationFrame doesn't throttle on Mac
+ https://bugs.webkit.org/show_bug.cgi?id=67171
+
+ Reviewed by Simon Fraser.
+
+ Changed requestAnimationFrame to use a Timer in ScriptedAnimationController
+ on Mac, rather than runLoopObservers. The Timer is throttled to fire no
+ faster than every 15ms. It is behind a WTF_USE_REQUEST_ANIMATION_FRAME_TIMER
+ flag and can be used by any implementation, but currently it is only enabled
+ by PLATFORM(MAC).
+
+ * dom/ScriptedAnimationController.cpp:
+ (WebCore::ScriptedAnimationController::ScriptedAnimationController):
+ (WebCore::ScriptedAnimationController::resume):
+ (WebCore::ScriptedAnimationController::registerCallback):
+ (WebCore::ScriptedAnimationController::serviceScriptedAnimations):
+ (WebCore::ScriptedAnimationController::scheduleAnimation):
+ (WebCore::ScriptedAnimationController::animationTimerFired):
+ * dom/ScriptedAnimationController.h:
+ * loader/EmptyClients.h:
+ * page/Chrome.cpp:
+ (WebCore::Chrome::scheduleAnimation):
+ * page/ChromeClient.h:
+
+2011-05-11 Antoine Labour <piman@chromium.org>
Reviewed by David Levin.
Expose shouldBufferData to ThreadableLoaderOptions to be able to disable buffering of the
View
6 Source/WebCore/dom/Document.cpp
@@ -5034,15 +5034,15 @@ void Document::loadEventDelayTimerFired(Timer<Document>*)
}
#if ENABLE(REQUEST_ANIMATION_FRAME)
-int Document::webkitRequestAnimationFrame(PassRefPtr<RequestAnimationFrameCallback> callback, Element* animationElement)
+int Document::webkitRequestAnimationFrame(PassRefPtr<RequestAnimationFrameCallback> callback)
{
if (!m_scriptedAnimationController)
m_scriptedAnimationController = ScriptedAnimationController::create(this);
- return m_scriptedAnimationController->registerCallback(callback, animationElement);
+ return m_scriptedAnimationController->registerCallback(callback);
}
-void Document::webkitCancelRequestAnimationFrame(int id)
+void Document::webkitCancelAnimationFrame(int id)
{
if (!m_scriptedAnimationController)
return;
View
4 Source/WebCore/dom/Document.h
@@ -1090,8 +1090,8 @@ class Document : public TreeScope, public ScriptExecutionContext {
const DocumentTiming* timing() const { return &m_documentTiming; }
#if ENABLE(REQUEST_ANIMATION_FRAME)
- int webkitRequestAnimationFrame(PassRefPtr<RequestAnimationFrameCallback>, Element*);
- void webkitCancelRequestAnimationFrame(int id);
+ int webkitRequestAnimationFrame(PassRefPtr<RequestAnimationFrameCallback>);
+ void webkitCancelAnimationFrame(int id);
void serviceScriptedAnimations(DOMTimeStamp);
#endif
View
4 Source/WebCore/dom/RequestAnimationFrameCallback.h
@@ -31,8 +31,7 @@
#ifndef RequestAnimationFrameCallback_h
#define RequestAnimationFrameCallback_h
-#include "Element.h"
-#include <wtf/PassRefPtr.h>
+#include "DOMTimeStamp.h"
#include <wtf/RefCounted.h>
namespace WebCore {
@@ -42,7 +41,6 @@ class RequestAnimationFrameCallback : public RefCounted<RequestAnimationFrameCal
virtual ~RequestAnimationFrameCallback() { }
virtual bool handleEvent(DOMTimeStamp) = 0;
- RefPtr<Element> m_element;
int m_id;
bool m_firedOrCancelled;
};
View
78 Source/WebCore/dom/ScriptedAnimationController.cpp
@@ -29,16 +29,29 @@
#if ENABLE(REQUEST_ANIMATION_FRAME)
#include "Document.h"
-#include "Element.h"
#include "FrameView.h"
#include "RequestAnimationFrameCallback.h"
+#if USE(REQUEST_ANIMATION_FRAME_TIMER)
+#include <algorithm>
+#include <wtf/CurrentTime.h>
+
+using namespace std;
+
+// Allow a little more than 60fps to make sure we can at least hit that frame rate.
+#define MinimumAnimationInterval 0.015
+#endif
+
namespace WebCore {
ScriptedAnimationController::ScriptedAnimationController(Document* document)
: m_document(document)
, m_nextCallbackId(0)
, m_suspendCount(0)
+#if USE(REQUEST_ANIMATION_FRAME_TIMER)
+ , m_animationTimer(this, &ScriptedAnimationController::animationTimerFired)
+ , m_lastAnimationFrameTime(0)
+#endif
{
}
@@ -51,20 +64,17 @@ void ScriptedAnimationController::resume()
{
--m_suspendCount;
if (!m_suspendCount && m_callbacks.size())
- if (FrameView* fv = m_document->view())
- fv->scheduleAnimation();
+ scheduleAnimation();
}
-ScriptedAnimationController::CallbackId ScriptedAnimationController::registerCallback(PassRefPtr<RequestAnimationFrameCallback> callback, Element* animationElement)
+ScriptedAnimationController::CallbackId ScriptedAnimationController::registerCallback(PassRefPtr<RequestAnimationFrameCallback> callback)
{
ScriptedAnimationController::CallbackId id = m_nextCallbackId++;
callback->m_firedOrCancelled = false;
callback->m_id = id;
- callback->m_element = animationElement;
m_callbacks.append(callback);
if (!m_suspendCount)
- if (FrameView* view = m_document->view())
- view->scheduleAnimation();
+ scheduleAnimation();
return id;
}
@@ -83,37 +93,19 @@ void ScriptedAnimationController::serviceScriptedAnimations(DOMTimeStamp time)
{
if (!m_callbacks.size() || m_suspendCount)
return;
- // We want to run the callback for all elements in the document that have registered
- // for a callback and that are visible. Running the callbacks can cause new callbacks
- // to be registered, existing callbacks to be cancelled, and elements to gain or lose
- // visibility so this code has to iterate carefully.
-
- // FIXME: Currently, this code doesn't do any visibility tests beyond checking display:
// First, generate a list of callbacks to consider. Callbacks registered from this point
// on are considered only for the "next" frame, not this one.
CallbackList callbacks(m_callbacks);
- // Firing the callback may cause the visibility of other elements to change. To avoid
- // missing any callbacks, we keep iterating through the list of candiate callbacks and firing
- // them until nothing new becomes visible.
- bool firedCallback;
- do {
- firedCallback = false;
- // A previous iteration may have invalidated style (or layout). Update styles for each iteration
- // for now since all we check is the existence of a renderer.
- m_document->updateStyleIfNeeded();
- for (size_t i = 0; i < callbacks.size(); ++i) {
- RequestAnimationFrameCallback* callback = callbacks[i].get();
- if (!callback->m_firedOrCancelled && (!callback->m_element || callback->m_element->renderer())) {
- callback->m_firedOrCancelled = true;
- callback->handleEvent(time);
- firedCallback = true;
- callbacks.remove(i);
- break;
- }
+ for (size_t i = 0; i < callbacks.size(); ++i) {
+ RequestAnimationFrameCallback* callback = callbacks[i].get();
+ if (!callback->m_firedOrCancelled) {
+ callback->m_firedOrCancelled = true;
+ callback->handleEvent(time);
}
- } while (firedCallback);
+ }
+ m_document->updateStyleIfNeeded();
// Remove any callbacks we fired from the list of pending callbacks.
for (size_t i = 0; i < m_callbacks.size();) {
@@ -124,10 +116,28 @@ void ScriptedAnimationController::serviceScriptedAnimations(DOMTimeStamp time)
}
if (m_callbacks.size())
- if (FrameView* view = m_document->view())
- view->scheduleAnimation();
+ scheduleAnimation();
}
+void ScriptedAnimationController::scheduleAnimation()
+{
+#if USE(REQUEST_ANIMATION_FRAME_TIMER)
+ double scheduleDelay = max<double>(MinimumAnimationInterval - (currentTime() - m_lastAnimationFrameTime), 0);
+ m_animationTimer.startOneShot(scheduleDelay);
+#else
+ if (FrameView* frameView = m_document->view())
+ frameView->scheduleAnimation();
+#endif
+}
+
+#if USE(REQUEST_ANIMATION_FRAME_TIMER)
+void ScriptedAnimationController::animationTimerFired(Timer<ScriptedAnimationController>*)
+{
+ m_lastAnimationFrameTime = currentTime();
+ serviceScriptedAnimations(convertSecondsToDOMTimeStamp(m_lastAnimationFrameTime));
+}
+#endif
+
}
#endif
View
15 Source/WebCore/dom/ScriptedAnimationController.h
@@ -28,6 +28,9 @@
#if ENABLE(REQUEST_ANIMATION_FRAME)
#include "DOMTimeStamp.h"
+#if USE(REQUEST_ANIMATION_FRAME_TIMER)
+#include "Timer.h"
+#endif
#include <wtf/Noncopyable.h>
#include <wtf/PassOwnPtr.h>
#include <wtf/RefPtr.h>
@@ -36,7 +39,6 @@
namespace WebCore {
class Document;
-class Element;
class RequestAnimationFrameCallback;
class ScriptedAnimationController {
@@ -49,7 +51,7 @@ WTF_MAKE_NONCOPYABLE(ScriptedAnimationController);
typedef int CallbackId;
- CallbackId registerCallback(PassRefPtr<RequestAnimationFrameCallback>, Element*);
+ CallbackId registerCallback(PassRefPtr<RequestAnimationFrameCallback>);
void cancelCallback(CallbackId);
void serviceScriptedAnimations(DOMTimeStamp);
@@ -58,12 +60,21 @@ WTF_MAKE_NONCOPYABLE(ScriptedAnimationController);
private:
explicit ScriptedAnimationController(Document*);
+
typedef Vector<RefPtr<RequestAnimationFrameCallback> > CallbackList;
CallbackList m_callbacks;
Document* m_document;
CallbackId m_nextCallbackId;
int m_suspendCount;
+
+ void scheduleAnimation();
+
+#if USE(REQUEST_ANIMATION_FRAME_TIMER)
+ void animationTimerFired(Timer<ScriptedAnimationController>*);
+ Timer<ScriptedAnimationController> m_animationTimer;
+ double m_lastAnimationFrameTime;
+#endif
};
}
View
2 Source/WebCore/loader/EmptyClients.h
@@ -168,7 +168,7 @@ class EmptyChromeClient : public ChromeClient {
#if ENABLE(TILED_BACKING_STORE)
virtual void delegatedScrollRequested(const IntPoint&) { }
#endif
-#if ENABLE(REQUEST_ANIMATION_FRAME)
+#if ENABLE(REQUEST_ANIMATION_FRAME) && !USE(REQUEST_ANIMATION_FRAME_TIMER)
virtual void scheduleAnimation() { }
#endif
View
2 Source/WebCore/page/Chrome.cpp
@@ -474,7 +474,9 @@ void Chrome::setCursor(const Cursor& cursor)
#if ENABLE(REQUEST_ANIMATION_FRAME)
void Chrome::scheduleAnimation()
{
+#if !USE(REQUEST_ANIMATION_FRAME_TIMER)
m_client->scheduleAnimation();
+#endif
}
#endif
View
2 Source/WebCore/page/ChromeClient.h
@@ -153,7 +153,7 @@ namespace WebCore {
virtual PlatformPageClient platformPageClient() const = 0;
virtual void scrollbarsModeDidChange() const = 0;
virtual void setCursor(const Cursor&) = 0;
-#if ENABLE(REQUEST_ANIMATION_FRAME)
+#if ENABLE(REQUEST_ANIMATION_FRAME) && !USE(REQUEST_ANIMATION_FRAME_TIMER)
virtual void scheduleAnimation() = 0;
#endif
// End methods used by HostWindow.
View
8 Source/WebCore/page/DOMWindow.cpp
@@ -1517,17 +1517,17 @@ void DOMWindow::clearInterval(int timeoutId)
}
#if ENABLE(REQUEST_ANIMATION_FRAME)
-int DOMWindow::webkitRequestAnimationFrame(PassRefPtr<RequestAnimationFrameCallback> callback, Element* e)
+int DOMWindow::webkitRequestAnimationFrame(PassRefPtr<RequestAnimationFrameCallback> callback)
{
if (Document* d = document())
- return d->webkitRequestAnimationFrame(callback, e);
+ return d->webkitRequestAnimationFrame(callback);
return 0;
}
-void DOMWindow::webkitCancelRequestAnimationFrame(int id)
+void DOMWindow::webkitCancelAnimationFrame(int id)
{
if (Document* d = document())
- d->webkitCancelRequestAnimationFrame(id);
+ d->webkitCancelAnimationFrame(id);
}
#endif
View
5 Source/WebCore/page/DOMWindow.h
@@ -249,8 +249,9 @@ namespace WebCore {
// WebKit animation extensions
#if ENABLE(REQUEST_ANIMATION_FRAME)
- int webkitRequestAnimationFrame(PassRefPtr<RequestAnimationFrameCallback>, Element*);
- void webkitCancelRequestAnimationFrame(int id);
+ int webkitRequestAnimationFrame(PassRefPtr<RequestAnimationFrameCallback>);
+ void webkitCancelAnimationFrame(int id);
+ void webkitCancelRequestAnimationFrame(int id) { webkitCancelAnimationFrame(id); }
#endif
// Events
View
7 Source/WebCore/page/DOMWindow.idl
@@ -234,9 +234,10 @@ module window {
void clearInterval(in long handle);
#if defined(ENABLE_REQUEST_ANIMATION_FRAME)
- // WebKit animation extensions
- long webkitRequestAnimationFrame(in [Callback] RequestAnimationFrameCallback callback, in Element element);
- void webkitCancelRequestAnimationFrame(in long id);
+ // WebKit animation extensions, being standardized in the WebPerf WG
+ long webkitRequestAnimationFrame(in [Callback] RequestAnimationFrameCallback callback);
+ void webkitCancelAnimationFrame(in long id);
+ void webkitCancelRequestAnimationFrame(in long id); // This is a deprecated alias for webkitCancelAnimationFrame(). Remove this when removing vendor prefix.
#endif
// Base64
View
4 Source/WebKit/android/WebCoreSupport/ChromeClientAndroid.h
@@ -200,6 +200,10 @@ namespace android {
virtual void* webView() const { return 0; }
+#if ENABLE(REQUEST_ANIMATION_FRAME) && !USE(REQUEST_ANIMATION_FRAME_TIMER)
+ virtual void scheduleAnimation() { }
+#endif
+
private:
android::WebFrame* m_webFrame;
// The Geolocation permissions manager.

0 comments on commit c79e317

Please sign in to comment.