blob: 67af85cd0d9937758461dba3bbd5396d93518df2 [file] [log] [blame]
/*
* Copyright (C) 2014 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 THE COPYRIGHT HOLDER "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 THE COPYRIGHT HOLDER 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 "MaskImageOperation.h"
#include "CachedImage.h"
#include "CachedSVGDocument.h"
#include "RenderBoxModelObject.h"
#include "RenderSVGResourceMasker.h"
#include "SVGDocument.h"
#include "SVGMaskElement.h"
#include "SVGSVGElement.h"
#include "StyleCachedImage.h"
#include "SubresourceLoader.h"
#include "WebKitCSSResourceValue.h"
namespace WebCore {
PassRefPtr<MaskImageOperation> MaskImageOperation::create(PassRefPtr<WebKitCSSResourceValue> cssValue, const String& url, const String& fragment, bool isExternalDocument, PassRefPtr<CachedResourceLoader> cachedResourceLoader)
{
return adoptRef(new MaskImageOperation(cssValue, url, fragment, isExternalDocument, cachedResourceLoader));
}
PassRefPtr<MaskImageOperation> MaskImageOperation::create(PassRefPtr<StyleImage> generatedImage)
{
return adoptRef(new MaskImageOperation(generatedImage));
}
PassRefPtr<MaskImageOperation> MaskImageOperation::create()
{
return adoptRef(new MaskImageOperation());
}
MaskImageOperation::MaskImageOperation(PassRefPtr<WebKitCSSResourceValue> cssValue, const String& url, const String& fragment, bool isExternalDocument, PassRefPtr<CachedResourceLoader> cachedResourceLoader)
: m_url(url)
, m_fragment(fragment)
, m_isExternalDocument(isExternalDocument)
, m_renderLayerImageClient(nullptr)
, m_cssMaskImageValue(cssValue)
, m_cachedResourceLoader(cachedResourceLoader)
{
ASSERT(m_cssMaskImageValue.get());
}
MaskImageOperation::MaskImageOperation(PassRefPtr<StyleImage> generatedImage)
: m_isExternalDocument(false)
, m_styleImage(generatedImage)
, m_renderLayerImageClient(nullptr)
{
}
MaskImageOperation::MaskImageOperation()
: m_isExternalDocument(false)
, m_renderLayerImageClient(nullptr)
{
m_cssMaskImageValue = WebKitCSSResourceValue::create(CSSPrimitiveValue::createIdentifier(CSSValueNone));
}
MaskImageOperation::~MaskImageOperation()
{
}
bool MaskImageOperation::isCSSValueNone() const
{
if (image())
return false;
ASSERT(m_cssMaskImageValue.get());
return m_cssMaskImageValue->isCSSValueNone();
}
PassRefPtr<CSSValue> MaskImageOperation::cssValue()
{
if (image())
return image()->cssValue();
if (isCSSValueNone())
return m_cssMaskImageValue->innerValue();
ASSERT(m_cssMaskImageValue.get());
return m_cssMaskImageValue.get();
}
bool MaskImageOperation::isMaskLoaded() const
{
if (!m_isExternalDocument)
return true;
if (image())
return (image()->cachedImage() && image()->cachedImage()->image());
if (m_cachedSVGDocumentReference.get()) {
if (CachedSVGDocument* cachedSVGDocument = m_cachedSVGDocumentReference->document())
return (cachedSVGDocument->document() != nullptr);
}
return false;
}
void MaskImageOperation::setRenderLayerImageClient(CachedImageClient* client)
{
if (m_renderLayerImageClient == client)
return;
if (CachedImage* cachedImage = (image() ? image()->cachedImage() : nullptr)) {
if (m_renderLayerImageClient)
cachedImage->removeClient(m_renderLayerImageClient);
if (client)
cachedImage->addClient(client);
}
m_renderLayerImageClient = client;
}
void MaskImageOperation::addRendererImageClient(RenderElement* client)
{
ASSERT(client);
if (m_styleImage.get())
m_styleImage->addClient(client);
m_rendererImageClients.add(client);
}
void MaskImageOperation::removeRendererImageClient(RenderElement* client)
{
ASSERT(client && m_rendererImageClients.contains(client));
if (m_styleImage.get())
m_styleImage->removeClient(client);
m_rendererImageClients.remove(client);
}
CachedSVGDocumentReference* MaskImageOperation::ensureCachedSVGDocumentReference()
{
// If we ended up loading the data into an Image, then the SVG document was not valid.
if (image())
return nullptr;
if (!m_cachedSVGDocumentReference.get())
m_cachedSVGDocumentReference = std::make_unique<CachedSVGDocumentReference>(m_url, this, false);
return m_cachedSVGDocumentReference.get();
}
void MaskImageOperation::notifyFinished(CachedResource* resource)
{
// The only one notifying us should be the SVG document we hold.
CachedSVGDocument* cachedSVGDocument = ensureCachedSVGDocumentReference()->document();
if ((CachedResource*)cachedSVGDocument != resource || !resource) {
ASSERT_NOT_REACHED();
return;
}
// Check if we find a valid masking element in this SVG document.
SVGDocument* svgDocument = cachedSVGDocument->document();
bool validMaskFound = false;
if (svgDocument && svgDocument->rootElement()) {
// Are we looking for a specific element in the SVG document?
if (fragment().length()) {
if (Element* maskingElement = svgDocument->rootElement()->getElementById(fragment())) {
if (is<SVGMaskElement>(maskingElement))
validMaskFound = true;
}
}
}
// If no valid mask was found, this is not a valid SVG document or it specified an invalid fragment identifier.
// Fallback to the normal way of loading the document in an Image object.
if (!validMaskFound) {
// Get the resource loader, acquire the resource buffer and load it into an image.
ASSERT(cachedSVGDocument->loader());
if (SubresourceLoader* loader = cachedSVGDocument->loader()) {
if (SharedBuffer* dataBuffer = loader->resourceData()) {
m_styleImage = StyleCachedImage::create(new CachedImage(cachedSVGDocument->resourceRequest(), cachedSVGDocument->sessionID()));
if (m_renderLayerImageClient)
m_styleImage->cachedImage()->addClient(m_renderLayerImageClient);
for (auto itClient : m_rendererImageClients)
m_styleImage->addClient(itClient.key);
m_styleImage->cachedImage()->setResponse(cachedSVGDocument->response());
m_styleImage->cachedImage()->finishLoading(dataBuffer);
// Let the cached resource loader of the document which requested this mask keep a handle to this
// cached image to ensure it only gets deleted when it should.
if (m_cachedResourceLoader.get())
m_cachedResourceLoader->addCachedResource(m_styleImage->cachedImage());
}
// Destroy the current SVG document as its no longer needed
m_cachedSVGDocumentReference = nullptr;
}
}
}
bool MaskImageOperation::drawMask(RenderElement& renderer, BackgroundImageGeometry& geometry, GraphicsContext* context, CompositeOperator compositeOp)
{
// This method only handles custom masks.
if (image())
return false;
if (RenderSVGResourceMasker* svgMasker = getSVGMasker(renderer)) {
svgMasker->drawMaskForRenderer(renderer, geometry, context, compositeOp);
return true;
}
return false;
}
RenderSVGResourceMasker* MaskImageOperation::getSVGMasker(RenderElement& renderer)
{
if (image())
return nullptr;
// Identify the element referenced by the fragmentId.
CachedSVGDocumentReference* svgDocumentReference = cachedSVGDocumentReference();
Element* elementForMasking = nullptr;
RenderObject* svgResourceForMasking = nullptr;
if (svgDocumentReference && svgDocumentReference->document()) {
SVGDocument* svgDocument = svgDocumentReference->document()->document();
if (svgDocument && svgDocument->rootElement())
elementForMasking = svgDocument->rootElement()->getElementById(fragment());
} else
elementForMasking = renderer.document().getElementById(fragment());
if (elementForMasking)
svgResourceForMasking = elementForMasking->renderer();
// Check if it's the correct type
if (svgResourceForMasking && svgResourceForMasking->isSVGResourceContainer() && downcast<RenderSVGResourceContainer>(svgResourceForMasking)->resourceType() == MaskerResourceType)
return static_cast<RenderSVGResourceMasker*>(svgResourceForMasking);
return nullptr;
}
} // namespace WebCore