/* * Copyright (C) 2008, 2011, 2012, 2013 Apple Inc. All rights reserved. * Copyright (C) 2013 Adobe Systems Incorporated. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "CSSImageGeneratorValue.h" #include "CSSCanvasValue.h" #include "CSSCrossfadeValue.h" #include "CSSFilterImageValue.h" #include "CSSGradientValue.h" #include "CSSImageValue.h" #include "CSSNamedImageValue.h" #include "CSSPaintImageValue.h" #include "GeneratedImage.h" #include "HTMLCanvasElement.h" #include "InspectorInstrumentation.h" #include "RenderElement.h" namespace WebCore { static const Seconds timeToKeepCachedGeneratedImages { 3_s }; class CSSImageGeneratorValue::CachedGeneratedImage { WTF_MAKE_FAST_ALLOCATED; public: CachedGeneratedImage(CSSImageGeneratorValue&, FloatSize, GeneratedImage&); GeneratedImage& image() const { return m_image; } void puntEvictionTimer() { m_evictionTimer.restart(); } private: void evictionTimerFired(); CSSImageGeneratorValue& m_owner; const FloatSize m_size; const Ref m_image; DeferrableOneShotTimer m_evictionTimer; }; CSSImageGeneratorValue::CSSImageGeneratorValue(ClassType classType) : CSSValue(classType) { } CSSImageGeneratorValue::~CSSImageGeneratorValue() = default; void CSSImageGeneratorValue::addClient(RenderElement& renderer) { if (m_clients.isEmpty()) ref(); m_clients.add(&renderer); if (is(this)) { if (HTMLCanvasElement* canvasElement = downcast(this)->element()) InspectorInstrumentation::didChangeCSSCanvasClientNodes(*canvasElement); } } void CSSImageGeneratorValue::removeClient(RenderElement& renderer) { ASSERT(m_clients.contains(&renderer)); if (!m_clients.remove(&renderer)) return; if (is(this)) { if (HTMLCanvasElement* canvasElement = downcast(this)->element()) InspectorInstrumentation::didChangeCSSCanvasClientNodes(*canvasElement); } if (m_clients.isEmpty()) deref(); } GeneratedImage* CSSImageGeneratorValue::cachedImageForSize(FloatSize size) { if (size.isEmpty()) return nullptr; auto* cachedGeneratedImage = m_images.get(size); if (!cachedGeneratedImage) return nullptr; cachedGeneratedImage->puntEvictionTimer(); return &cachedGeneratedImage->image(); } void CSSImageGeneratorValue::saveCachedImageForSize(FloatSize size, GeneratedImage& image) { ASSERT(!m_images.contains(size)); m_images.add(size, makeUnique(*this, size, image)); } void CSSImageGeneratorValue::evictCachedGeneratedImage(FloatSize size) { ASSERT(m_images.contains(size)); m_images.remove(size); } inline CSSImageGeneratorValue::CachedGeneratedImage::CachedGeneratedImage(CSSImageGeneratorValue& owner, FloatSize size, GeneratedImage& image) : m_owner(owner) , m_size(size) , m_image(image) , m_evictionTimer(*this, &CSSImageGeneratorValue::CachedGeneratedImage::evictionTimerFired, timeToKeepCachedGeneratedImages) { m_evictionTimer.restart(); } void CSSImageGeneratorValue::CachedGeneratedImage::evictionTimerFired() { // NOTE: This is essentially a "delete this", the object is no longer valid after this line. m_owner.evictCachedGeneratedImage(m_size); } RefPtr CSSImageGeneratorValue::image(RenderElement& renderer, const FloatSize& size) { switch (classType()) { case CanvasClass: return downcast(*this).image(renderer, size); case NamedImageClass: return downcast(*this).image(renderer, size); case CrossfadeClass: return downcast(*this).image(renderer, size); case FilterImageClass: return downcast(*this).image(renderer, size); case LinearGradientClass: return downcast(*this).image(renderer, size); case RadialGradientClass: return downcast(*this).image(renderer, size); case ConicGradientClass: return downcast(*this).image(renderer, size); #if ENABLE(CSS_PAINTING_API) case PaintImageClass: return downcast(*this).image(renderer, size); #endif default: ASSERT_NOT_REACHED(); } return nullptr; } bool CSSImageGeneratorValue::isFixedSize() const { switch (classType()) { case CanvasClass: return downcast(*this).isFixedSize(); case NamedImageClass: return downcast(*this).isFixedSize(); case CrossfadeClass: return downcast(*this).isFixedSize(); case FilterImageClass: return downcast(*this).isFixedSize(); case LinearGradientClass: return downcast(*this).isFixedSize(); case RadialGradientClass: return downcast(*this).isFixedSize(); case ConicGradientClass: return downcast(*this).isFixedSize(); #if ENABLE(CSS_PAINTING_API) case PaintImageClass: return downcast(*this).isFixedSize(); #endif default: ASSERT_NOT_REACHED(); } return false; } FloatSize CSSImageGeneratorValue::fixedSize(const RenderElement& renderer) { switch (classType()) { case CanvasClass: return downcast(*this).fixedSize(renderer); case CrossfadeClass: return downcast(*this).fixedSize(renderer); case FilterImageClass: return downcast(*this).fixedSize(renderer); case LinearGradientClass: return downcast(*this).fixedSize(renderer); case RadialGradientClass: return downcast(*this).fixedSize(renderer); case ConicGradientClass: return downcast(*this).fixedSize(renderer); #if ENABLE(CSS_PAINTING_API) case PaintImageClass: return downcast(*this).fixedSize(renderer); #endif default: ASSERT_NOT_REACHED(); } return FloatSize(); } bool CSSImageGeneratorValue::isPending() const { switch (classType()) { case CrossfadeClass: return downcast(*this).isPending(); case CanvasClass: return downcast(*this).isPending(); case NamedImageClass: return downcast(*this).isPending(); case FilterImageClass: return downcast(*this).isPending(); case LinearGradientClass: return downcast(*this).isPending(); case RadialGradientClass: return downcast(*this).isPending(); case ConicGradientClass: return downcast(*this).isPending(); #if ENABLE(CSS_PAINTING_API) case PaintImageClass: return downcast(*this).isPending(); #endif default: ASSERT_NOT_REACHED(); } return false; } bool CSSImageGeneratorValue::knownToBeOpaque(const RenderElement& renderer) const { switch (classType()) { case CrossfadeClass: return downcast(*this).knownToBeOpaque(renderer); case CanvasClass: return false; case NamedImageClass: return false; case FilterImageClass: return downcast(*this).knownToBeOpaque(renderer); case LinearGradientClass: return downcast(*this).knownToBeOpaque(renderer); case RadialGradientClass: return downcast(*this).knownToBeOpaque(renderer); case ConicGradientClass: return downcast(*this).knownToBeOpaque(renderer); #if ENABLE(CSS_PAINTING_API) case PaintImageClass: return downcast(*this).knownToBeOpaque(renderer); #endif default: ASSERT_NOT_REACHED(); } return false; } void CSSImageGeneratorValue::loadSubimages(CachedResourceLoader& cachedResourceLoader, const ResourceLoaderOptions& options) { switch (classType()) { case CrossfadeClass: downcast(*this).loadSubimages(cachedResourceLoader, options); break; case CanvasClass: downcast(*this).loadSubimages(cachedResourceLoader, options); break; case FilterImageClass: downcast(*this).loadSubimages(cachedResourceLoader, options); break; case LinearGradientClass: downcast(*this).loadSubimages(cachedResourceLoader, options); break; case RadialGradientClass: downcast(*this).loadSubimages(cachedResourceLoader, options); break; case ConicGradientClass: downcast(*this).loadSubimages(cachedResourceLoader, options); break; #if ENABLE(CSS_PAINTING_API) case PaintImageClass: downcast(*this).loadSubimages(cachedResourceLoader, options); break; #endif default: ASSERT_NOT_REACHED(); } } bool CSSImageGeneratorValue::operator==(const CSSImageGeneratorValue& other) const { if (classType() != other.classType()) return false; switch (classType()) { case CrossfadeClass: return downcast(*this).equals(downcast(other)); case CanvasClass: return downcast(*this).equals(downcast(other)); case FilterImageClass: return downcast(*this).equals(downcast(other)); case LinearGradientClass: return downcast(*this).equals(downcast(other)); case RadialGradientClass: return downcast(*this).equals(downcast(other)); case ConicGradientClass: return downcast(*this).equals(downcast(other)); #if ENABLE(CSS_PAINTING_API) case PaintImageClass: return downcast(*this).equals(downcast(other)); #endif default: ASSERT_NOT_REACHED(); } return false; } bool CSSImageGeneratorValue::subimageIsPending(const CSSValue& value) { if (is(value)) return downcast(value).isPending(); if (is(value)) return downcast(value).isPending(); if (is(value) && downcast(value).valueID() == CSSValueNone) return false; ASSERT_NOT_REACHED(); return false; } CachedImage* CSSImageGeneratorValue::cachedImageForCSSValue(CSSValue& value, CachedResourceLoader& cachedResourceLoader, const ResourceLoaderOptions& options) { if (is(value)) { auto& imageValue = downcast(value); return imageValue.loadImage(cachedResourceLoader, options); } if (is(value)) { downcast(value).loadSubimages(cachedResourceLoader, options); // FIXME: Handle CSSImageGeneratorValue (and thus cross-fades with gradients and canvas). return nullptr; } if (is(value) && downcast(value).valueID() == CSSValueNone) return nullptr; ASSERT_NOT_REACHED(); return nullptr; } } // namespace WebCore