Changeset 214503 in webkit
- Timestamp:
- Mar 28, 2017, 4:11:35 PM (8 years ago)
- Location:
- trunk
- Files:
-
- 5 added
- 21 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r214501 r214503 1 2017-03-28 Chris Dumez <cdumez@apple.com> 2 3 Animated SVG images are not paused when outside viewport 4 https://bugs.webkit.org/show_bug.cgi?id=170155 5 <rdar://problem/31288893> 6 7 Reviewed by Antti Koivisto. 8 9 Add layout test coverage. 10 11 * platform/mac-wk1/TestExpectations: 12 * svg/animations/animated-svg-image-outside-viewport-paused-expected.txt: Added. 13 * svg/animations/animated-svg-image-outside-viewport-paused.html: Added. 14 * svg/animations/animated-svg-image-removed-from-document-paused-expected.txt: Added. 15 * svg/animations/animated-svg-image-removed-from-document-paused.html: Added. 16 * svg/animations/resources/smilAnimation.svg: Added. 17 1 18 2017-03-28 Antti Koivisto <antti@apple.com> 2 19 -
trunk/LayoutTests/platform/mac-wk1/TestExpectations
r214423 r214503 124 124 fast/images/animated-gif-window-resizing.html [ Skip ] 125 125 fast/images/animated-gif-zooming.html [ Skip ] 126 svg/animations/animated-svg-image-outside-viewport-paused.html [ Skip ] 127 svg/animations/animated-svg-image-removed-from-document-paused.html [ Skip ] 126 128 127 129 # WK1 uses the native scrollview for scrolling by page. -
trunk/Source/WebCore/ChangeLog
r214501 r214503 1 2017-03-28 Chris Dumez <cdumez@apple.com> 2 3 Animated SVG images are not paused when outside viewport 4 https://bugs.webkit.org/show_bug.cgi?id=170155 5 <rdar://problem/31288893> 6 7 Reviewed by Antti Koivisto. 8 9 Make sure animated SVG images get paused when outside the viewport, 10 similarly to what was already done for animated GIF images. Also 11 make sure they are paused when they no longer have any renderers 12 using them. 13 14 Tests: svg/animations/animated-svg-image-outside-viewport-paused.html 15 svg/animations/animated-svg-image-removed-from-document-paused.html 16 17 * loader/cache/CachedImage.cpp: 18 (WebCore::CachedImage::didAddClient): 19 Restart the animation whenever a new CachedImage client is added. This 20 will cause us the re-evaluate if the animation should run. The animation 21 will pause again if the new renderer is not inside the viewport. 22 23 (WebCore::CachedImage::animationAdvanced): 24 Add a flag to newImageAnimationFrameAvailable() so that the renderers can 25 let us know if we can pause the animation. Pause the animation if all no 26 renderer requires it (i.e. they are all outside the viewport, or there 27 are no renderers). 28 29 * loader/cache/CachedImageClient.h: 30 (WebCore::CachedImageClient::newImageAnimationFrameAvailable): 31 By default, the CachedImageClients allow pausing. Only renderer will 32 potentially prevent pausing if they are inside the viewport. 33 34 * platform/graphics/BitmapImage.cpp: 35 (WebCore::BitmapImage::isAnimating): 36 * platform/graphics/BitmapImage.h: 37 * platform/graphics/Image.h: 38 (WebCore::Image::isAnimating): 39 Add isAnimating() flag on Image for layout testing purposes. 40 41 * rendering/RenderElement.cpp: 42 (WebCore::RenderElement::newImageAnimationFrameAvailable): 43 Set canPause flag to true if the renderer is not inside the viewport. 44 45 (WebCore::RenderElement::repaintForPausedImageAnimationsIfNeeded): 46 Call startAnimation() if the renderer is now visible to resume SVG 47 animations. Repainting is enough for GIF animations but not for SVG 48 animations, we have to explicitly resume them. 49 50 * rendering/RenderElement.h: 51 * rendering/RenderView.cpp: 52 (WebCore::RenderView::addRendererWithPausedImageAnimations): 53 (WebCore::RenderView::removeRendererWithPausedImageAnimations): 54 (WebCore::RenderView::resumePausedImageAnimationsIfNeeded): 55 * rendering/RenderView.h: 56 Store CachedImages with the renderers that have paused animations. 57 This is required for SVG where we need to explicitly resume the 58 animation on the CachedImage when the renderer becomes visible 59 again. Having access to the Image will also allow us to do smarter 60 visibility checks in RenderElement's shouldRepaintForImageAnimation(), 61 in the future. 62 63 * svg/SVGSVGElement.cpp: 64 (WebCore::SVGSVGElement::hasActiveAnimation): 65 * svg/SVGSVGElement.h: 66 Add hasActiveAnimation() method. 67 68 * svg/graphics/SVGImage.cpp: 69 (WebCore::SVGImage::startAnimation): 70 Check that animations are paused before starting them. This avoid 71 jumping due to unnecessary calls to rootElement->setCurrentTime(0). 72 73 (WebCore::SVGImage::isAnimating): 74 Add isAnimating() method for layout tests purposes. 75 76 * svg/graphics/SVGImage.h: 77 * svg/graphics/SVGImageClients.h: 78 Call animationAdvanced() on the observer instead of the generic 79 changedInRect() when the SVGImage is animating. This way, we go 80 through the same code path as GIF animations and we end up calling 81 CachedImage::animationAdvanced() which calls newImageAnimationFrameAvailable() 82 on RenderElement, which determines if the animation should keep 83 running or not. 84 85 * testing/Internals.cpp: 86 (WebCore::Internals::isImageAnimating): 87 * testing/Internals.h: 88 * testing/Internals.idl: 89 Add layout testing infrastructure. 90 1 91 2017-03-28 Antti Koivisto <antti@apple.com> 2 92 -
trunk/Source/WebCore/loader/cache/CachedImage.cpp
r210697 r214503 118 118 static_cast<CachedImageClient&>(client).imageChanged(this); 119 119 120 if (m_image) 121 m_image->startAnimation(); 122 120 123 CachedResource::didAddClient(client); 121 124 } … … 515 518 if (!image || image != m_image) 516 519 return; 520 521 bool shouldPauseAnimation = true; 522 517 523 CachedResourceClientWalker<CachedImageClient> clientWalker(m_clients); 518 while (CachedImageClient* client = clientWalker.next()) 519 client->newImageAnimationFrameAvailable(*this); 524 while (CachedImageClient* client = clientWalker.next()) { 525 bool canPause = false; 526 client->newImageAnimationFrameAvailable(*this, canPause); 527 if (!canPause) 528 shouldPauseAnimation = false; 529 } 530 531 if (shouldPauseAnimation) 532 m_image->stopAnimation(); 520 533 } 521 534 -
trunk/Source/WebCore/loader/cache/CachedImageClient.h
r208646 r214503 41 41 42 42 // Called when GIF animation progresses. 43 virtual void newImageAnimationFrameAvailable(CachedImage& image ) { imageChanged(&image); }43 virtual void newImageAnimationFrameAvailable(CachedImage& image, bool& canPause) { imageChanged(&image); canPause = true; } 44 44 }; 45 45 -
trunk/Source/WebCore/platform/graphics/BitmapImage.cpp
r214450 r214503 407 407 } 408 408 409 bool BitmapImage::isAnimating() const 410 { 411 return !!m_frameTimer; 412 } 413 409 414 void BitmapImage::stopAnimation() 410 415 { -
trunk/Source/WebCore/platform/graphics/BitmapImage.h
r214450 r214503 170 170 void advanceAnimation(); 171 171 void internalAdvanceAnimation(); 172 bool isAnimating() const final; 172 173 173 174 // It may look unusual that there is no start animation call as public API. This is because -
trunk/Source/WebCore/platform/graphics/Image.h
r214450 r214503 132 132 virtual void resetAnimation() {} 133 133 virtual void newFrameNativeImageAvailableAtIndex(size_t) { } 134 virtual bool isAnimating() const { return false; } 134 135 135 136 // Typically the CachedImage that owns us. -
trunk/Source/WebCore/rendering/RenderElement.cpp
r214443 r214503 1495 1495 } 1496 1496 1497 void RenderElement::newImageAnimationFrameAvailable(CachedImage& image )1497 void RenderElement::newImageAnimationFrameAvailable(CachedImage& image, bool& canPause) 1498 1498 { 1499 1499 auto& frameView = view().frameView(); 1500 1500 auto visibleRect = frameView.windowToContents(frameView.windowClipRect()); 1501 1501 if (!shouldRepaintForImageAnimation(*this, visibleRect)) { 1502 // FIXME: It would be better to pass the image along with the renderer 1503 // so that we can be smarter about detecting if the image is inside the 1504 // viewport in repaintForPausedImageAnimationsIfNeeded(). 1505 view().addRendererWithPausedImageAnimations(*this); 1502 view().addRendererWithPausedImageAnimations(*this, image); 1503 canPause = true; 1506 1504 return; 1507 1505 } … … 1509 1507 } 1510 1508 1511 bool RenderElement::repaintForPausedImageAnimationsIfNeeded(const IntRect& visibleRect )1509 bool RenderElement::repaintForPausedImageAnimationsIfNeeded(const IntRect& visibleRect, CachedImage& cachedImage) 1512 1510 { 1513 1511 ASSERT(m_hasPausedImageAnimations); … … 1516 1514 1517 1515 repaint(); 1516 1517 if (auto* image = cachedImage.image()) 1518 image->startAnimation(); 1518 1519 1519 1520 // For directly-composited animated GIFs it does not suffice to call repaint() to resume animation. We need to mark the image as changed. -
trunk/Source/WebCore/rendering/RenderElement.h
r214443 r214503 196 196 virtual void visibleInViewportStateChanged(); 197 197 198 bool repaintForPausedImageAnimationsIfNeeded(const IntRect& visibleRect );198 bool repaintForPausedImageAnimationsIfNeeded(const IntRect& visibleRect, CachedImage&); 199 199 bool hasPausedImageAnimations() const { return m_hasPausedImageAnimations; } 200 200 void setHasPausedImageAnimations(bool b) { m_hasPausedImageAnimations = b; } … … 318 318 void invalidateCachedFirstLineStyle(); 319 319 320 void newImageAnimationFrameAvailable(CachedImage& ) final;320 void newImageAnimationFrameAvailable(CachedImage&, bool& canPause) final; 321 321 322 322 bool getLeadingCorner(FloatPoint& output, bool& insideFixed) const; -
trunk/Source/WebCore/rendering/RenderView.cpp
r214443 r214503 1396 1396 } 1397 1397 1398 void RenderView::addRendererWithPausedImageAnimations(RenderElement& renderer) 1399 { 1400 if (renderer.hasPausedImageAnimations()) { 1401 ASSERT(m_renderersWithPausedImageAnimation.contains(&renderer)); 1402 return; 1403 } 1398 void RenderView::addRendererWithPausedImageAnimations(RenderElement& renderer, CachedImage& image) 1399 { 1400 ASSERT(!renderer.hasPausedImageAnimations() || m_renderersWithPausedImageAnimation.contains(&renderer)); 1401 1404 1402 renderer.setHasPausedImageAnimations(true); 1405 m_renderersWithPausedImageAnimation.add(&renderer); 1403 auto& images = m_renderersWithPausedImageAnimation.ensure(&renderer, [] { 1404 return Vector<CachedImage*>(); 1405 }).iterator->value; 1406 if (!images.contains(&image)) 1407 images.append(&image); 1406 1408 } 1407 1409 … … 1415 1417 } 1416 1418 1419 void RenderView::removeRendererWithPausedImageAnimations(RenderElement& renderer, CachedImage& image) 1420 { 1421 ASSERT(renderer.hasPausedImageAnimations()); 1422 1423 auto it = m_renderersWithPausedImageAnimation.find(&renderer); 1424 ASSERT(it != m_renderersWithPausedImageAnimation.end()); 1425 1426 auto& images = it->value; 1427 if (!images.contains(&image)) 1428 return; 1429 1430 if (images.size() == 1) 1431 removeRendererWithPausedImageAnimations(renderer); 1432 else 1433 images.removeFirst(&image); 1434 } 1435 1417 1436 void RenderView::resumePausedImageAnimationsIfNeeded(IntRect visibleRect) 1418 1437 { 1419 Vector<RenderElement*, 10> toRemove; 1420 for (auto* renderer : m_renderersWithPausedImageAnimation) { 1421 if (renderer->repaintForPausedImageAnimationsIfNeeded(visibleRect)) 1422 toRemove.append(renderer); 1423 } 1424 for (auto& renderer : toRemove) 1425 removeRendererWithPausedImageAnimations(*renderer); 1438 Vector<std::pair<RenderElement*, CachedImage*>, 10> toRemove; 1439 for (auto& it : m_renderersWithPausedImageAnimation) { 1440 auto* renderer = it.key; 1441 for (auto* image : it.value) { 1442 if (renderer->repaintForPausedImageAnimationsIfNeeded(visibleRect, *image)) 1443 toRemove.append(std::make_pair(renderer, image)); 1444 } 1445 } 1446 for (auto& pair : toRemove) 1447 removeRendererWithPausedImageAnimations(*pair.first, *pair.second); 1426 1448 } 1427 1449 -
trunk/Source/WebCore/rendering/RenderView.h
r213897 r214503 230 230 void unregisterForVisibleInViewportCallback(RenderElement&); 231 231 void resumePausedImageAnimationsIfNeeded(IntRect visibleRect); 232 void addRendererWithPausedImageAnimations(RenderElement& );232 void addRendererWithPausedImageAnimations(RenderElement&, CachedImage&); 233 233 void removeRendererWithPausedImageAnimations(RenderElement&); 234 void removeRendererWithPausedImageAnimations(RenderElement&, CachedImage&); 234 235 235 236 class RepaintRegionAccumulator { … … 390 391 #endif 391 392 392 Hash Set<RenderElement*> m_renderersWithPausedImageAnimation;393 HashMap<RenderElement*, Vector<CachedImage*>> m_renderersWithPausedImageAnimation; 393 394 HashSet<RenderElement*> m_visibleInViewportRenderers; 394 395 Vector<RefPtr<RenderWidget>> m_protectedRenderWidgets; -
trunk/Source/WebCore/rendering/style/StyleCachedImage.cpp
r210758 r214503 30 30 #include "CachedImage.h" 31 31 #include "RenderElement.h" 32 #include "RenderView.h" 32 33 33 34 namespace WebCore { … … 187 188 return; 188 189 ASSERT(renderer); 190 191 if (renderer->hasPausedImageAnimations()) 192 renderer->view().removeRendererWithPausedImageAnimations(*renderer, *m_cachedImage); 193 189 194 m_cachedImage->removeClient(*renderer); 190 195 } -
trunk/Source/WebCore/svg/SVGSVGElement.cpp
r214327 r214503 508 508 } 509 509 510 bool SVGSVGElement::hasActiveAnimation() const 511 { 512 return m_timeContainer->isActive(); 513 } 514 510 515 float SVGSVGElement::getCurrentTime() const 511 516 { -
trunk/Source/WebCore/svg/SVGSVGElement.h
r208828 r214503 86 86 void unpauseAnimations(); 87 87 bool animationsPaused() const; 88 bool hasActiveAnimation() const; 88 89 89 90 float getCurrentTime() const; -
trunk/Source/WebCore/svg/graphics/SVGImage.cpp
r214450 r214503 375 375 { 376 376 SVGSVGElement* rootElement = this->rootElement(); 377 if (!rootElement )377 if (!rootElement || !rootElement->animationsPaused()) 378 378 return; 379 379 rootElement->unpauseAnimations(); … … 392 392 { 393 393 stopAnimation(); 394 } 395 396 bool SVGImage::isAnimating() const 397 { 398 SVGSVGElement* rootElement = this->rootElement(); 399 if (!rootElement) 400 return false; 401 return rootElement->hasActiveAnimation(); 394 402 } 395 403 -
trunk/Source/WebCore/svg/graphics/SVGImage.h
r214450 r214503 64 64 void stopAnimation() final; 65 65 void resetAnimation() final; 66 bool isAnimating() const final; 66 67 67 68 #if USE(CAIRO) -
trunk/Source/WebCore/svg/graphics/SVGImageClients.h
r208668 r214503 30 30 31 31 #include "EmptyClients.h" 32 #include "SVGImage.h" 32 33 33 34 namespace WebCore { … … 53 54 { 54 55 // If m_image->m_page is null, we're being destructed, don't fire changedInRect() in that case. 55 if (m_image && m_image->imageObserver() && m_image->m_page) 56 m_image->imageObserver()->changedInRect(m_image, &r); 56 if (!m_image || !m_image->m_page) 57 return; 58 59 auto* imageObserver = m_image->imageObserver(); 60 if (!imageObserver) 61 return; 62 63 if (m_image->isAnimating()) 64 imageObserver->animationAdvanced(m_image); 65 else 66 imageObserver->changedInRect(m_image, &r); 57 67 } 58 68 -
trunk/Source/WebCore/testing/Internals.cpp
r214322 r214503 745 745 } 746 746 747 bool Internals::isImageAnimating(HTMLImageElement& element) 748 { 749 auto* cachedImage = element.cachedImage(); 750 if (!cachedImage) 751 return false; 752 753 auto* image = cachedImage->image(); 754 if (!image) 755 return false; 756 757 return image->isAnimating(); 758 } 759 747 760 void Internals::setClearDecoderAfterAsyncFrameRequestForTesting(HTMLImageElement& element, bool value) 748 761 { -
trunk/Source/WebCore/testing/Internals.h
r214322 r214503 116 116 void setImageFrameDecodingDuration(HTMLImageElement&, float duration); 117 117 void resetImageAnimation(HTMLImageElement&); 118 bool isImageAnimating(HTMLImageElement&); 118 119 void setClearDecoderAfterAsyncFrameRequestForTesting(HTMLImageElement&, bool); 119 120 -
trunk/Source/WebCore/testing/Internals.idl
r214322 r214503 245 245 void setImageFrameDecodingDuration(HTMLImageElement element, unrestricted float duration); 246 246 void resetImageAnimation(HTMLImageElement element); 247 boolean isImageAnimating(HTMLImageElement element); 247 248 void setClearDecoderAfterAsyncFrameRequestForTesting(HTMLImageElement element, boolean value); 248 249
Note:
See TracChangeset
for help on using the changeset viewer.