blob: 97bbc8779773203c9f9f874d67edfb4deea98519 [file] [log] [blame]
/*
* Copyright (C) 1999 Lars Knoll (knoll@kde.org)
* Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc.
* All rights reserved.
* Copyright (C) 2013 Google Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#include "core/css/resolver/ElementStyleResources.h"
#include "core/CSSPropertyNames.h"
#include "core/css/CSSCursorImageValue.h"
#include "core/css/CSSGradientValue.h"
#include "core/css/CSSImageValue.h"
#include "core/css/CSSURIValue.h"
#include "core/dom/Document.h"
#include "core/fetch/ResourceFetcher.h"
#include "core/layout/svg/ReferenceFilterBuilder.h"
#include "core/style/ComputedStyle.h"
#include "core/style/ContentData.h"
#include "core/style/CursorData.h"
#include "core/style/FillLayer.h"
#include "core/style/FilterOperation.h"
#include "core/style/StyleFetchedImage.h"
#include "core/style/StyleFetchedImageSet.h"
#include "core/style/StyleGeneratedImage.h"
#include "core/style/StyleImage.h"
#include "core/style/StyleInvalidImage.h"
#include "core/style/StylePendingImage.h"
namespace blink {
ElementStyleResources::ElementStyleResources(Document& document,
float deviceScaleFactor)
: m_document(&document), m_deviceScaleFactor(deviceScaleFactor) {}
StyleImage* ElementStyleResources::styleImage(CSSPropertyID property,
const CSSValue& value) {
if (value.isImageValue())
return cachedOrPendingFromValue(property, toCSSImageValue(value));
if (value.isImageGeneratorValue())
return generatedOrPendingFromValue(property,
toCSSImageGeneratorValue(value));
if (value.isImageSetValue())
return setOrPendingFromValue(property, toCSSImageSetValue(value));
if (value.isCursorImageValue())
return cursorOrPendingFromValue(property, toCSSCursorImageValue(value));
return nullptr;
}
StyleImage* ElementStyleResources::generatedOrPendingFromValue(
CSSPropertyID property,
const CSSImageGeneratorValue& value) {
if (value.isPending()) {
m_pendingImageProperties.add(property);
return StylePendingImage::create(value);
}
return StyleGeneratedImage::create(value);
}
StyleImage* ElementStyleResources::setOrPendingFromValue(
CSSPropertyID property,
const CSSImageSetValue& value) {
if (value.isCachePending(m_deviceScaleFactor)) {
m_pendingImageProperties.add(property);
return StylePendingImage::create(value);
}
return value.cachedImage(m_deviceScaleFactor);
}
StyleImage* ElementStyleResources::cachedOrPendingFromValue(
CSSPropertyID property,
const CSSImageValue& value) {
if (value.isCachePending()) {
m_pendingImageProperties.add(property);
return StylePendingImage::create(value);
}
value.restoreCachedResourceIfNeeded(*m_document);
return value.cachedImage();
}
StyleImage* ElementStyleResources::cursorOrPendingFromValue(
CSSPropertyID property,
const CSSCursorImageValue& value) {
if (value.isCachePending(m_deviceScaleFactor)) {
m_pendingImageProperties.add(property);
return StylePendingImage::create(value);
}
return value.cachedImage(m_deviceScaleFactor);
}
void ElementStyleResources::addPendingSVGDocument(
FilterOperation* filterOperation,
const CSSURIValue* cssUriValue) {
m_pendingSVGDocuments.set(filterOperation, cssUriValue);
}
void ElementStyleResources::loadPendingSVGDocuments(
ComputedStyle* computedStyle) {
if (!computedStyle->hasFilter() || m_pendingSVGDocuments.isEmpty())
return;
FilterOperations::FilterOperationVector& filterOperations =
computedStyle->mutableFilter().operations();
for (unsigned i = 0; i < filterOperations.size(); ++i) {
FilterOperation* filterOperation = filterOperations.at(i);
if (filterOperation->type() == FilterOperation::REFERENCE) {
ReferenceFilterOperation* referenceFilter =
toReferenceFilterOperation(filterOperation);
const CSSURIValue* value = m_pendingSVGDocuments.get(referenceFilter);
if (!value)
continue;
DocumentResource* resource = value->load(*m_document);
if (!resource)
continue;
// Stash the DocumentResource on the reference filter.
ReferenceFilterBuilder::setDocumentResourceReference(
referenceFilter, new DocumentResourceReference(resource));
}
}
}
StyleImage* ElementStyleResources::loadPendingImage(
ComputedStyle* style,
StylePendingImage* pendingImage,
CrossOriginAttributeValue crossOrigin) {
if (CSSImageValue* imageValue = pendingImage->cssImageValue())
return imageValue->cacheImage(*m_document, crossOrigin);
if (CSSPaintValue* paintValue = pendingImage->cssPaintValue()) {
StyleGeneratedImage* image = StyleGeneratedImage::create(*paintValue);
style->addPaintImage(image);
return image;
}
if (CSSImageGeneratorValue* imageGeneratorValue =
pendingImage->cssImageGeneratorValue()) {
imageGeneratorValue->loadSubimages(*m_document);
return StyleGeneratedImage::create(*imageGeneratorValue);
}
if (CSSCursorImageValue* cursorImageValue =
pendingImage->cssCursorImageValue())
return cursorImageValue->cacheImage(*m_document, m_deviceScaleFactor);
if (CSSImageSetValue* imageSetValue = pendingImage->cssImageSetValue())
return imageSetValue->cacheImage(*m_document, m_deviceScaleFactor,
crossOrigin);
ASSERT_NOT_REACHED();
return nullptr;
}
void ElementStyleResources::loadPendingImages(ComputedStyle* style) {
// We must loop over the properties and then look at the style to see if
// a pending image exists, and only load that image. For example:
//
// <style>
// div { background-image: url(a.png); }
// div { background-image: url(b.png); }
// div { background-image: none; }
// </style>
// <div></div>
//
// We call styleImage() for both a.png and b.png adding the
// CSSPropertyBackgroundImage property to the m_pendingImageProperties set,
// then we null out the background image because of the "none".
//
// If we eagerly loaded the images we'd fetch a.png, even though it's not
// used. If we didn't null check below we'd crash since the none actually
// removed all background images.
for (CSSPropertyID property : m_pendingImageProperties) {
switch (property) {
case CSSPropertyBackgroundImage: {
for (FillLayer* backgroundLayer = &style->accessBackgroundLayers();
backgroundLayer; backgroundLayer = backgroundLayer->next()) {
if (backgroundLayer->image() &&
backgroundLayer->image()->isPendingImage())
backgroundLayer->setImage(loadPendingImage(
style, toStylePendingImage(backgroundLayer->image())));
}
break;
}
case CSSPropertyContent: {
for (ContentData* contentData =
const_cast<ContentData*>(style->contentData());
contentData; contentData = contentData->next()) {
if (contentData->isImage()) {
StyleImage* image = toImageContentData(contentData)->image();
if (image->isPendingImage())
toImageContentData(contentData)
->setImage(
loadPendingImage(style, toStylePendingImage(image)));
}
}
break;
}
case CSSPropertyCursor: {
if (CursorList* cursorList = style->cursors()) {
for (size_t i = 0; i < cursorList->size(); ++i) {
CursorData& currentCursor = cursorList->at(i);
if (StyleImage* image = currentCursor.image()) {
if (image->isPendingImage())
currentCursor.setImage(
loadPendingImage(style, toStylePendingImage(image)));
}
}
}
break;
}
case CSSPropertyListStyleImage: {
if (style->listStyleImage() &&
style->listStyleImage()->isPendingImage())
style->setListStyleImage(loadPendingImage(
style, toStylePendingImage(style->listStyleImage())));
break;
}
case CSSPropertyBorderImageSource: {
if (style->borderImageSource() &&
style->borderImageSource()->isPendingImage())
style->setBorderImageSource(loadPendingImage(
style, toStylePendingImage(style->borderImageSource())));
break;
}
case CSSPropertyWebkitBoxReflect: {
if (StyleReflection* reflection = style->boxReflect()) {
const NinePieceImage& maskImage = reflection->mask();
if (maskImage.image() && maskImage.image()->isPendingImage()) {
StyleImage* loadedImage =
loadPendingImage(style, toStylePendingImage(maskImage.image()));
reflection->setMask(NinePieceImage(
loadedImage, maskImage.imageSlices(), maskImage.fill(),
maskImage.borderSlices(), maskImage.outset(),
maskImage.horizontalRule(), maskImage.verticalRule()));
}
}
break;
}
case CSSPropertyWebkitMaskBoxImageSource: {
if (style->maskBoxImageSource() &&
style->maskBoxImageSource()->isPendingImage())
style->setMaskBoxImageSource(loadPendingImage(
style, toStylePendingImage(style->maskBoxImageSource())));
break;
}
case CSSPropertyWebkitMaskImage: {
for (FillLayer* maskLayer = &style->accessMaskLayers(); maskLayer;
maskLayer = maskLayer->next()) {
if (maskLayer->image() && maskLayer->image()->isPendingImage())
maskLayer->setImage(loadPendingImage(
style, toStylePendingImage(maskLayer->image())));
}
break;
}
case CSSPropertyShapeOutside:
if (style->shapeOutside() && style->shapeOutside()->image() &&
style->shapeOutside()->image()->isPendingImage())
style->shapeOutside()->setImage(loadPendingImage(
style, toStylePendingImage(style->shapeOutside()->image()),
CrossOriginAttributeAnonymous));
break;
default:
ASSERT_NOT_REACHED();
}
}
}
void ElementStyleResources::loadPendingResources(ComputedStyle* computedStyle) {
loadPendingImages(computedStyle);
loadPendingSVGDocuments(computedStyle);
}
} // namespace blink