Commit 3a4d52a5 authored by pdr@chromium.org's avatar pdr@chromium.org

Add a new API for testing animated images

This patch adds internals.advanceImageAnimationFrame([img]) for
advancing an image's animation in tests. With this patch, 2.5 seconds
of unnecessary and flaky setTimeout calls have been removed.

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

git-svn-id: svn://svn.chromium.org/blink/trunk@201942 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent f929bb82
......@@ -37,8 +37,6 @@ crbug.com/515250 [ Mac Debug ] plugins/attach-during-destroy.html [ Timeout ]
crbug.com/344239 [ Release ] inspector-protocol/heap-profiler/heap-objects-tracking.html [ Slow ]
crbug.com/356742 [ Win ] svg/as-image/animated-svg-animation-control.html [ Pass ImageOnlyFailure ]
crbug.com/501229 fast/forms/associatedFormControls-leak-nodes.html [ Failure ]
crbug.com/506186 accessibility/axobjectcache-leaks-node.html [ Failure ]
......
......@@ -1344,7 +1344,6 @@ crbug.com/521730 [ Win10 ] inspector/file-reader-with-network-panel.html [ Failu
crbug.com/521730 [ Win10 ] inspector/sources/debugger-step/debugger-step-out-document-write.html [ Pass Timeout ]
crbug.com/521730 [ Win10 ] media/W3C/audio/error/error_onerror_called_on_bogus_source.html [ Pass Timeout ]
crbug.com/521730 [ Win10 ] paint/inline/outline-offset.html [ ImageOnlyFailure Timeout ]
crbug.com/521730 [ Win10 ] svg/as-image/animated-svg-animation-control.html [ ImageOnlyFailure Pass Timeout ]
crbug.com/521730 [ Win10 ] svg/as-image/svg-object-intrinsic-size.html [ Missing Timeout ]
crbug.com/521730 [ Win10 ] svg/as-object/nested-embedded-svg-size-changes.html [ Pass Timeout ]
crbug.com/521730 [ Win10 ] svg/custom/rounded-rect-update-to-empty.html [ Pass Timeout ]
......
......@@ -13,11 +13,14 @@
_addTest(function(canvas, ctx) {
deferTest();
setTimeout(wrapFunction(function () {
// Advance the animation one frame to ensure we only draw the first frame.
window.internals.advanceImageAnimation(document.getElementById('anim-gr.gif'));
requestAnimationFrame(wrapFunction(function () {
ctx.drawImage(document.getElementById('anim-gr.gif'), 0, 0);
_assertPixelApprox(canvas, 50,25, 0,255,0,255, "50,25", "0,255,0,255", 2);
}), 500);
}));
});
</script>
......
......@@ -20,22 +20,20 @@
var ctx = canvas.getContext("2d");
var img = new Image();
img.src = 'resources/green-red-animated.gif';
img.onload = imageLoaded;
img.src = 'resources/green-red-animated.gif';
function imageLoaded() {
// The gif switches from green to red in 10ms. We wait 50ms to ensure that the gif will have changed colors.
// If the ImageBitmap is green, we know that it is a snapshot of the gif's 0th frame.
window.setTimeout(function() {
createImageBitmap(img).then(function (imageBitmap) {
ctx.drawImage(imageBitmap, 0, 0);
shouldBeGreen(100, 100);
finishJSTest();
}, function() {
testFailed("Promise was rejected.");
finishJSTest();
});
}, 50);
window.internals.advanceImageAnimation(img);
createImageBitmap(img).then(function (imageBitmap) {
ctx.drawImage(imageBitmap, 0, 0);
shouldBeGreen(100, 100);
finishJSTest();
}, function() {
testFailed("Promise was rejected.");
finishJSTest();
});
}
</script>
</body>
......
<!DOCTYPE HTML>
<style>
#a {
display: inline-block;
background-color: green;
width: 25px;
height: 25px;
}
#b {
display: inline-block;
background-color: red;
width: 25px;
height: 25px;
}
#c {
display: inline-block;
background-color: blue;
width: 25px;
height: 25px;
}
#d {
display: inline-block;
background-color: yellow;
width: 25px;
height: 25px;
}
#e {
display: inline-block;
background-color: green;
width: 25px;
height: 25px;
}
</style>
<div id="a"></div>
<div id="b"></div>
<div id="c"></div>
<div id="d"></div>
<div id="e"></div>
\ No newline at end of file
<!DOCTYPE HTML>
<img id="a" width="25" height="25" src="resources/green-red-blue-yellow-animated.gif?a">
<img id="b" width="25" height="25" src="resources/green-red-blue-yellow-animated.gif?b">
<img id="c" width="25" height="25" src="resources/green-red-blue-yellow-animated.gif?c">
<img id="d" width="25" height="25" src="resources/green-red-blue-yellow-animated.gif?d">
<img id="e" width="25" height="25" src="resources/green-red-blue-yellow-animated.gif?e">
<script>
if (window.testRunner)
testRunner.waitUntilDone();
window.onload = function() {
// Jump to the 2nd frame.
window.internals.advanceImageAnimation(b);
// Jump to the 3rd frame.
for (var i = 0; i < 2; i++)
window.internals.advanceImageAnimation(c);
// Jump to the 4th frame.
for (var i = 0; i < 3; i++)
window.internals.advanceImageAnimation(d);
// Ensure the animation can loop and get back to the first frame.
for (var i = 0; i < 4; i++)
window.internals.advanceImageAnimation(e);
requestAnimationFrame(function() {
if (window.testRunner)
testRunner.notifyDone();
});
}
</script>
\ No newline at end of file
......@@ -8,26 +8,15 @@
<script>
window.testIsAsync = true;
// Steps:
// 1. Load the image
// 2. Start tracking paint invalidation rects
// 3. Call layoutAndPaintAsyncThen (via runRepaintTest, which does this for us) to draw a frame
// 4. Wait longer than 100ms, so that the timer between the first and second animated GIF
// frame expires and a paint invalidation is issued on the animated GIF. (100ms is the
// length of the first frame of the animation in the GIF file.)
// 5. Stop tracking paint invalidation rects (finishRepaintTest does this for us)
// These steps differ from a standard repaint test in that step 2 precedes step 3.
// This is critical because layoutAndPaintAsyncThen may take longer than the 100ms of the timer,
// so we may miss it.
function repaintTest() {
setTimeout(finishRepaintTest, 150);
window.internals.advanceImageAnimation(testTarget);
requestAnimationFrame(function() {
finishRepaintTest();
});
}
function targetImageOnload() {
if (window.internals)
window.internals.startTrackingRepaints(document);
window.internals.startTrackingRepaints(document);
runRepaintTest();
}
......
......@@ -7,26 +7,15 @@
<script>
window.testIsAsync = true;
// Steps:
// 1. Load the image
// 2. Start tracking paint invalidation rects
// 3. Call layoutAndPaintAsyncThen (via runRepaintTest, which does this for us) to draw a frame
// 4. Wait longer than 100ms, so that the timer between the first and second animated GIF
// frame expires and a paint invalidation is issued on the animated GIF. (100ms is the
// length of the first frame of the animation in the GIF file.)
// 5. Stop tracking paint invalidation rects (finishRepaintTest does this for us)
// These steps differ from a standard repaint test in that step 2 precedes step 3.
// This is critical because layoutAndPaintAsyncThen may take longer than the 100ms of the timer,
// so we may miss it.
function repaintTest() {
setTimeout(finishRepaintTest, 150);
window.internals.advanceImageAnimation(targetImage);
requestAnimationFrame(function() {
finishRepaintTest();
});
}
function targetImageOnload() {
if (window.internals)
window.internals.startTrackingRepaints(document);
window.internals.startTrackingRepaints(document);
runRepaintTest();
}
......
......@@ -9,7 +9,7 @@
[0, 10, 22, 22]
],
"paintInvalidationClients": [
"LayoutImage IMG"
"LayoutImage IMG id='targetImage'"
]
}
]
......
......@@ -9,7 +9,7 @@
[0, 10, 22, 22]
],
"paintInvalidationClients": [
"LayoutImage IMG"
"LayoutImage IMG id='targetImage'"
]
}
]
......
<!DOCTYPE html>
<div style="width: 100px; height: 100px; background-color: green"></div>
<!DOCTYPE html>
<title>SVG-in-&lt;img&gt;: Animation resets when all references are removed.</title>
<img></img>
<script>
if (window.testRunner)
testRunner.waitUntilDone();
var img = document.querySelector('img');
var imgurl = "resources/animated-rect-color.svg";
img.src = imgurl;
setTimeout(function() {
img.src = "";
setTimeout(function() {
img.src = imgurl;
setTimeout(function() {
if (window.testRunner)
testRunner.notifyDone();
}, 10);
}, 0);
}, 100);
</script>
......@@ -10,10 +10,13 @@
if (!window.testRunner)
return;
// The animation lasts 100ms. Wait 200ms for the repaint.
setTimeout(function() {
// The animation lasts 100ms so skip 12 (12*1s/120=100ms) frames to be sure.
for (var i = 0; i < 12; i++)
window.internals.advanceImageAnimation(image);
window.requestAnimationFrame(function() {
finishRepaintTest();
}, 200);
});
}
</script>
<style type="text/css" media="screen">
......@@ -24,6 +27,6 @@
</head>
<body onload="runRepaintAndPixelTest()">
<p>Images should redraw correctly when SVG animation runs</p>
<img src="resources/animated-rect-fixed-size.svg">
<img id="image" src="resources/animated-rect-fixed-size.svg">
</body>
</html>
......@@ -10,10 +10,15 @@
if (!window.testRunner)
return;
// The animation lasts 100ms. Wait 200ms for the repaint.
setTimeout(function() {
// The animation lasts 100ms so skip 12 (12*1s/120=100ms) frames to be sure.
for (var i = 0; i < 12; i++) {
window.internals.advanceImageAnimation(imageA);
window.internals.advanceImageAnimation(imageB);
}
window.requestAnimationFrame(function() {
finishRepaintTest();
}, 200);
});
}
</script>
<style type="text/css" media="screen">
......@@ -24,7 +29,7 @@
</head>
<body onload="runRepaintAndPixelTest()">
<p>Images should redraw correctly when SVG animation runs</p>
<img height="250px" width="350px" border="2" src="resources/animated-rect-same-image.svg"><br>
<img height="250px" width="350px" border="2" src="resources/animated-rect-same-image.svg">
<img id="imageA" height="250px" width="350px" border="2" src="resources/animated-rect-same-image.svg"><br>
<img id="imageB" height="250px" width="350px" border="2" src="resources/animated-rect-same-image.svg">
</body>
</html>
......@@ -10,10 +10,15 @@
if (!window.testRunner)
return;
// The animation lasts 100ms. Wait 200ms for the repaint.
setTimeout(function() {
// The animation lasts 100ms so skip 12 frames (12*1s/120=100ms) to be sure.
for (var i = 0; i < 12; i++) {
window.internals.advanceImageAnimation(imageA);
window.internals.advanceImageAnimation(imageB);
}
window.requestAnimationFrame(function() {
finishRepaintTest();
}, 200);
});
}
</script>
<style type="text/css" media="screen">
......@@ -25,8 +30,8 @@
<body onload="runRepaintAndPixelTest()">
<p>Images should redraw correctly when SVG animation runs</p>
<!-- Don't load animated-rect-fixed-size.svg, it will already be loaded, and the animation already ran if animated-svg-as-image-no-fixed-intrinsic-size.html runs before this test! -->
<img height="250px" width="350px" border="2" src="resources/animated-rect-fixed-size-2.svg"><br>
<img id="imageA" height="250px" width="350px" border="2" src="resources/animated-rect-fixed-size-2.svg"><br>
<!-- animated-rect-relative-size.svg is not loaded by any other test, so there's no problem here -->
<img height="250px" width="350px" border="2" src="resources/animated-rect-relative-size.svg">
<img id="imageB" height="250px" width="350px" border="2" src="resources/animated-rect-relative-size.svg">
</body>
</html>
<!DOCTYPE html>
<html>
<p>Test passes if loaded into Chromium with Asan enabled and does not crash.</p>
<img src='resources/animated-href-on-use.svg'></img>
<img id="image" src='resources/animated-href-on-use.svg'></img>
<script type="text/javascript">
if (window.testRunner) {
testRunner.waitUntilDone();
testRunner.dumpAsText();
}
setTimeout(function() { if (window.testRunner) testRunner.notifyDone(); }, 500);
window.onload = function() {
window.internals.advanceImageAnimation(image);
window.requestAnimationFrame(function() {
testRunner.notifyDone()
});
}
</script>
</html>
......@@ -9,7 +9,7 @@
[0, 9, 22, 22]
],
"paintInvalidationClients": [
"LayoutImage IMG"
"LayoutImage IMG id='targetImage'"
]
}
]
......
......@@ -15,10 +15,10 @@ window.testIsAsync = true;
window.onload = runRepaintTest;
function repaintTest() {
setTimeout(function() {
if (window.testRunner)
finishRepaintTest();
}, 220);
window.internals.advanceImageAnimation(targetImage);
window.requestAnimationFrame(function() {
finishRepaintTest();
});
};
</script>
<img src="resources/animate-center.svg" width="10px" height="10px">
\ No newline at end of file
<img id="targetImage" src="resources/animate-center.svg" width="10px" height="10px">
\ No newline at end of file
......@@ -549,6 +549,11 @@ SMILTime SMILTimeContainer::updateAnimations(SMILTime elapsed, bool seekToTime)
return earliestFireTime;
}
void SMILTimeContainer::advanceFrameForTesting()
{
setElapsed(elapsed().value() + initialFrameDelay);
}
DEFINE_TRACE(SMILTimeContainer)
{
#if ENABLE(OILPAN)
......
......@@ -69,6 +69,9 @@ public:
void setDocumentOrderIndexesDirty() { m_documentOrderIndexesDirty = true; }
// Advance the animation timeline a single frame.
void advanceFrameForTesting();
DECLARE_TRACE();
private:
......
......@@ -395,6 +395,20 @@ bool SVGImage::hasAnimations() const
return rootElement->timeContainer()->hasAnimations() || toLocalFrame(m_page->mainFrame())->document()->timeline().hasPendingUpdates();
}
void SVGImage::advanceAnimationForTesting()
{
if (SVGSVGElement* rootElement = svgRootElement(m_page.get())) {
rootElement->timeContainer()->advanceFrameForTesting();
// The following triggers animation updates which can issue a new draw
// but will not permanently change the animation timeline.
// TODO(pdr): Actually advance the document timeline so CSS animations
// can be properly tested.
rootElement->document().page()->animator().serviceScriptedAnimations(rootElement->getCurrentTime());
imageObserver()->animationAdvanced(this);
}
}
void SVGImage::updateUseCounters(Document& document) const
{
if (SVGSVGElement* rootElement = svgRootElement(m_page.get())) {
......
......@@ -63,6 +63,10 @@ public:
void stopAnimation() override;
void resetAnimation() override;
// Advances an animated image. This will trigger an animation update for CSS
// and advance the SMIL timeline by one frame.
void advanceAnimationForTesting() override;
PassRefPtr<SkImage> imageForCurrentFrame() override;
// Returns the SVG image document's frame.
......
......@@ -512,6 +512,29 @@ void Internals::advanceTimeForImage(Element* image, double deltaTimeInSeconds, E
imageData->advanceTime(deltaTimeInSeconds);
}
void Internals::advanceImageAnimation(Element* image, ExceptionState& exceptionState)
{
ASSERT(image);
ImageResource* resource = nullptr;
if (isHTMLImageElement(*image)) {
resource = toHTMLImageElement(*image).cachedImage();
} else if (isSVGImageElement(*image)) {
resource = toSVGImageElement(*image).cachedImage();
} else {
exceptionState.throwDOMException(InvalidAccessError, "The element provided is not a image element.");
return;
}
if (!resource || !resource->hasImage()) {
exceptionState.throwDOMException(InvalidAccessError, "The image resource is not available.");
return;
}
Image* imageData = resource->image();
imageData->advanceAnimationForTesting();
}
bool Internals::hasShadowInsertionPoint(const Node* root, ExceptionState& exceptionState) const
{
ASSERT(root);
......
......@@ -116,6 +116,11 @@ public:
// for testing whether animated images work properly.
void advanceTimeForImage(Element* image, double deltaTimeInSeconds, ExceptionState&);
// Advances an animated image. For BitmapImage (e.g., animated gifs) this
// will advance to the next frame. For SVGImage, this will trigger an
// animation update for CSS and advance the SMIL timeline by one frame.
void advanceImageAnimation(Element* image, ExceptionState&);
bool isValidContentSelect(Element* insertionPoint, ExceptionState&);
Node* treeScopeRootNode(Node*);
Node* parentTreeScope(Node*);
......
......@@ -78,6 +78,11 @@
// for testing whether animated images work properly.
[RaisesException] void advanceTimeForImage(Element image, double deltaTimeInSeconds);
// Advances an animated image. For BitmapImage (e.g., animated gifs) this
// will advance to the next frame. For SVGImage, this will trigger an
// animation update for CSS and advance the SMIL timeline by one frame.
[RaisesException, TypeChecking=Interface] void advanceImageAnimation(Element image);
[RaisesException, TypeChecking=Interface] Node nextSiblingInComposedTree(Node node);
[RaisesException, TypeChecking=Interface] Node firstChildInComposedTree(Node node);
[RaisesException, TypeChecking=Interface] Node lastChildInComposedTree(Node node);
......
......@@ -84,6 +84,9 @@ public:
ImageAnimationPolicy animationPolicy() override { return m_animationPolicy; }
void advanceTime(double deltaTimeInSeconds) override;
// Advance the image animation by one frame.
void advanceAnimationForTesting() override { internalAdvanceAnimation(false); }
PassRefPtr<SkImage> imageForCurrentFrame() override;
PassRefPtr<Image> imageForDefaultFrame() override;
bool currentFrameKnownToBeOpaque() override;
......
......@@ -121,6 +121,11 @@ public:
virtual ImageAnimationPolicy animationPolicy() { return ImageAnimationPolicyAllowed; }
virtual void advanceTime(double deltaTimeInSeconds) { }
// Advances an animated image. For BitmapImage (e.g., animated gifs) this
// will advance to the next frame. For SVGImage, this will trigger an
// animation update for CSS and advance the SMIL timeline by one frame.
virtual void advanceAnimationForTesting() { }
// Typically the ImageResource that owns us.
ImageObserver* imageObserver() const { return m_imageObserver; }
void setImageObserver(ImageObserver* observer) { m_imageObserver = observer; }
......
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