blob: 7a8e19f230f4a200a52b51158302e1cdce55fc6e [file] [log] [blame]
/*
* 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 "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<GeneratedImage> 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<CSSCanvasValue>(this)) {
if (HTMLCanvasElement* canvasElement = downcast<CSSCanvasValue>(this)->element())
InspectorInstrumentation::didChangeCSSCanvasClientNodes(*canvasElement);
}
}
void CSSImageGeneratorValue::removeClient(RenderElement& renderer)
{
ASSERT(m_clients.contains(&renderer));
if (!m_clients.remove(&renderer))
return;
if (is<CSSCanvasValue>(this)) {
if (HTMLCanvasElement* canvasElement = downcast<CSSCanvasValue>(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, std::make_unique<CachedGeneratedImage>(*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<Image> CSSImageGeneratorValue::image(RenderElement& renderer, const FloatSize& size)
{
switch (classType()) {
case CanvasClass:
return downcast<CSSCanvasValue>(*this).image(&renderer, size);
case NamedImageClass:
return downcast<CSSNamedImageValue>(*this).image(&renderer, size);
case CrossfadeClass:
return downcast<CSSCrossfadeValue>(*this).image(renderer, size);
case FilterImageClass:
return downcast<CSSFilterImageValue>(*this).image(&renderer, size);
case LinearGradientClass:
return downcast<CSSLinearGradientValue>(*this).image(renderer, size);
case RadialGradientClass:
return downcast<CSSRadialGradientValue>(*this).image(renderer, size);
case ConicGradientClass:
return downcast<CSSConicGradientValue>(*this).image(renderer, size);
default:
ASSERT_NOT_REACHED();
}
return nullptr;
}
bool CSSImageGeneratorValue::isFixedSize() const
{
switch (classType()) {
case CanvasClass:
return downcast<CSSCanvasValue>(*this).isFixedSize();
case NamedImageClass:
return downcast<CSSNamedImageValue>(*this).isFixedSize();
case CrossfadeClass:
return downcast<CSSCrossfadeValue>(*this).isFixedSize();
case FilterImageClass:
return downcast<CSSFilterImageValue>(*this).isFixedSize();
case LinearGradientClass:
return downcast<CSSLinearGradientValue>(*this).isFixedSize();
case RadialGradientClass:
return downcast<CSSRadialGradientValue>(*this).isFixedSize();
case ConicGradientClass:
return downcast<CSSConicGradientValue>(*this).isFixedSize();
default:
ASSERT_NOT_REACHED();
}
return false;
}
FloatSize CSSImageGeneratorValue::fixedSize(const RenderElement& renderer)
{
switch (classType()) {
case CanvasClass:
return downcast<CSSCanvasValue>(*this).fixedSize(&renderer);
case CrossfadeClass:
return downcast<CSSCrossfadeValue>(*this).fixedSize(renderer);
case FilterImageClass:
return downcast<CSSFilterImageValue>(*this).fixedSize(&renderer);
case LinearGradientClass:
return downcast<CSSLinearGradientValue>(*this).fixedSize(renderer);
case RadialGradientClass:
return downcast<CSSRadialGradientValue>(*this).fixedSize(renderer);
case ConicGradientClass:
return downcast<CSSConicGradientValue>(*this).fixedSize(renderer);
default:
ASSERT_NOT_REACHED();
}
return FloatSize();
}
bool CSSImageGeneratorValue::isPending() const
{
switch (classType()) {
case CrossfadeClass:
return downcast<CSSCrossfadeValue>(*this).isPending();
case CanvasClass:
return downcast<CSSCanvasValue>(*this).isPending();
case NamedImageClass:
return downcast<CSSNamedImageValue>(*this).isPending();
case FilterImageClass:
return downcast<CSSFilterImageValue>(*this).isPending();
case LinearGradientClass:
return downcast<CSSLinearGradientValue>(*this).isPending();
case RadialGradientClass:
return downcast<CSSRadialGradientValue>(*this).isPending();
case ConicGradientClass:
return downcast<CSSConicGradientValue>(*this).isPending();
default:
ASSERT_NOT_REACHED();
}
return false;
}
bool CSSImageGeneratorValue::knownToBeOpaque(const RenderElement& renderer) const
{
switch (classType()) {
case CrossfadeClass:
return downcast<CSSCrossfadeValue>(*this).knownToBeOpaque(renderer);
case CanvasClass:
return false;
case NamedImageClass:
return false;
case FilterImageClass:
return downcast<CSSFilterImageValue>(*this).knownToBeOpaque(renderer);
case LinearGradientClass:
return downcast<CSSLinearGradientValue>(*this).knownToBeOpaque(renderer);
case RadialGradientClass:
return downcast<CSSRadialGradientValue>(*this).knownToBeOpaque(renderer);
case ConicGradientClass:
return downcast<CSSConicGradientValue>(*this).knownToBeOpaque(renderer);
default:
ASSERT_NOT_REACHED();
}
return false;
}
void CSSImageGeneratorValue::loadSubimages(CachedResourceLoader& cachedResourceLoader, const ResourceLoaderOptions& options)
{
switch (classType()) {
case CrossfadeClass:
downcast<CSSCrossfadeValue>(*this).loadSubimages(cachedResourceLoader, options);
break;
case CanvasClass:
downcast<CSSCanvasValue>(*this).loadSubimages(cachedResourceLoader, options);
break;
case FilterImageClass:
downcast<CSSFilterImageValue>(*this).loadSubimages(cachedResourceLoader, options);
break;
case LinearGradientClass:
downcast<CSSLinearGradientValue>(*this).loadSubimages(cachedResourceLoader, options);
break;
case RadialGradientClass:
downcast<CSSRadialGradientValue>(*this).loadSubimages(cachedResourceLoader, options);
break;
case ConicGradientClass:
downcast<CSSConicGradientValue>(*this).loadSubimages(cachedResourceLoader, options);
break;
default:
ASSERT_NOT_REACHED();
}
}
bool CSSImageGeneratorValue::subimageIsPending(const CSSValue& value)
{
if (is<CSSImageValue>(value))
return downcast<CSSImageValue>(value).isPending();
if (is<CSSImageGeneratorValue>(value))
return downcast<CSSImageGeneratorValue>(value).isPending();
if (is<CSSPrimitiveValue>(value) && downcast<CSSPrimitiveValue>(value).valueID() == CSSValueNone)
return false;
ASSERT_NOT_REACHED();
return false;
}
CachedImage* CSSImageGeneratorValue::cachedImageForCSSValue(CSSValue& value, CachedResourceLoader& cachedResourceLoader, const ResourceLoaderOptions& options)
{
if (is<CSSImageValue>(value)) {
auto& imageValue = downcast<CSSImageValue>(value);
return imageValue.loadImage(cachedResourceLoader, options);
}
if (is<CSSImageGeneratorValue>(value)) {
downcast<CSSImageGeneratorValue>(value).loadSubimages(cachedResourceLoader, options);
// FIXME: Handle CSSImageGeneratorValue (and thus cross-fades with gradients and canvas).
return nullptr;
}
if (is<CSSPrimitiveValue>(value) && downcast<CSSPrimitiveValue>(value).valueID() == CSSValueNone)
return nullptr;
ASSERT_NOT_REACHED();
return nullptr;
}
} // namespace WebCore