1/* 2 * Copyright (C) 2006 Rob Buis <buis@kde.org> 3 * (C) 2008 Nikolas Zimmermann <zimmermann@kde.org> 4 * Copyright (C) 2008 Apple Inc. All rights reserved. 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Library General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Library General Public License for more details. 15 * 16 * You should have received a copy of the GNU Library General Public License 17 * along with this library; see the file COPYING.LIB. If not, write to 18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA. 20 */ 21 22#include "config.h" 23#include "CSSCursorImageValue.h" 24 25#include "CSSImageValue.h" 26#include "CachedImage.h" 27#include "CachedResourceLoader.h" 28#include "StyleCachedImage.h" 29#include "StyleImage.h" 30#include "StylePendingImage.h" 31#include "TreeScope.h" 32#include <wtf/MathExtras.h> 33#include <wtf/text/StringBuilder.h> 34#include <wtf/text/WTFString.h> 35 36#if ENABLE(SVG) 37#include "SVGCursorElement.h" 38#include "SVGLengthContext.h" 39#include "SVGNames.h" 40#include "SVGURIReference.h" 41#endif 42 43#if ENABLE(CSS_IMAGE_SET) 44#include "CSSImageSetValue.h" 45#include "StyleCachedImageSet.h" 46#endif 47 48namespace WebCore { 49 50#if ENABLE(SVG) 51static inline SVGCursorElement* resourceReferencedByCursorElement(const String& url, Document* document) 52{ 53 Element* element = SVGURIReference::targetElementFromIRIString(url, document); 54 if (element && element->hasTagName(SVGNames::cursorTag)) 55 return static_cast<SVGCursorElement*>(element); 56 57 return 0; 58} 59#endif 60 61CSSCursorImageValue::CSSCursorImageValue(PassRefPtr<CSSValue> imageValue, bool hasHotSpot, const IntPoint& hotSpot) 62 : CSSValue(CursorImageClass) 63 , m_imageValue(imageValue) 64 , m_hasHotSpot(hasHotSpot) 65 , m_hotSpot(hotSpot) 66 , m_accessedImage(false) 67{ 68} 69 70inline void CSSCursorImageValue::detachPendingImage() 71{ 72 if (m_image && m_image->isPendingImage()) 73 static_cast<StylePendingImage&>(*m_image).detachFromCSSValue(); 74} 75 76CSSCursorImageValue::~CSSCursorImageValue() 77{ 78 detachPendingImage(); 79 80#if ENABLE(SVG) 81 if (!isSVGCursor()) 82 return; 83 84 HashSet<SVGElement*>::const_iterator it = m_referencedElements.begin(); 85 HashSet<SVGElement*>::const_iterator end = m_referencedElements.end(); 86 String url = static_cast<CSSImageValue*>(m_imageValue.get())->url(); 87 88 for (; it != end; ++it) { 89 SVGElement* referencedElement = *it; 90 referencedElement->cursorImageValueRemoved(); 91 if (SVGCursorElement* cursorElement = resourceReferencedByCursorElement(url, referencedElement->document())) 92 cursorElement->removeClient(referencedElement); 93 } 94#endif 95} 96 97String CSSCursorImageValue::customCssText() const 98{ 99 StringBuilder result; 100 result.append(m_imageValue->cssText()); 101 if (m_hasHotSpot) { 102 result.append(' '); 103 result.appendNumber(m_hotSpot.x()); 104 result.append(' '); 105 result.appendNumber(m_hotSpot.y()); 106 } 107 return result.toString(); 108} 109 110bool CSSCursorImageValue::updateIfSVGCursorIsUsed(Element* element) 111{ 112#if !ENABLE(SVG) 113 UNUSED_PARAM(element); 114#else 115 if (!element || !element->isSVGElement()) 116 return false; 117 118 if (!isSVGCursor()) 119 return false; 120 121 String url = static_cast<CSSImageValue*>(m_imageValue.get())->url(); 122 if (SVGCursorElement* cursorElement = resourceReferencedByCursorElement(url, element->document())) { 123 // FIXME: This will override hot spot specified in CSS, which is probably incorrect. 124 SVGLengthContext lengthContext(0); 125 m_hasHotSpot = true; 126 float x = roundf(cursorElement->x().value(lengthContext)); 127 m_hotSpot.setX(static_cast<int>(x)); 128 129 float y = roundf(cursorElement->y().value(lengthContext)); 130 m_hotSpot.setY(static_cast<int>(y)); 131 132 if (cachedImageURL() != element->document()->completeURL(cursorElement->href())) 133 clearCachedImage(); 134 135 SVGElement* svgElement = toSVGElement(element); 136 m_referencedElements.add(svgElement); 137 svgElement->setCursorImageValue(this); 138 cursorElement->addClient(svgElement); 139 return true; 140 } 141#endif 142 143 return false; 144} 145 146StyleImage* CSSCursorImageValue::cachedImage(CachedResourceLoader* loader) 147{ 148#if ENABLE(CSS_IMAGE_SET) 149 if (m_imageValue->isImageSetValue()) 150 return static_cast<CSSImageSetValue*>(m_imageValue.get())->cachedImageSet(loader); 151#endif 152 153 if (!m_accessedImage) { 154 m_accessedImage = true; 155 156#if ENABLE(SVG) 157 // For SVG images we need to lazily substitute in the correct URL. Rather than attempt 158 // to change the URL of the CSSImageValue (which would then change behavior like cssText), 159 // we create an alternate CSSImageValue to use. 160 if (isSVGCursor() && loader && loader->document()) { 161 RefPtr<CSSImageValue> imageValue = static_cast<CSSImageValue*>(m_imageValue.get()); 162 // FIXME: This will fail if the <cursor> element is in a shadow DOM (bug 59827) 163 if (SVGCursorElement* cursorElement = resourceReferencedByCursorElement(imageValue->url(), loader->document())) { 164 detachPendingImage(); 165 RefPtr<CSSImageValue> svgImageValue = CSSImageValue::create(cursorElement->href()); 166 StyleCachedImage* cachedImage = svgImageValue->cachedImage(loader); 167 m_image = cachedImage; 168 return cachedImage; 169 } 170 } 171#endif 172 173 if (m_imageValue->isImageValue()) { 174 detachPendingImage(); 175 m_image = static_cast<CSSImageValue*>(m_imageValue.get())->cachedImage(loader); 176 } 177 } 178 179 if (m_image && m_image->isCachedImage()) 180 return static_cast<StyleCachedImage*>(m_image.get()); 181 182 return 0; 183} 184 185StyleImage* CSSCursorImageValue::cachedOrPendingImage(Document* document) 186{ 187#if ENABLE(CSS_IMAGE_SET) 188 // Need to delegate completely so that changes in device scale factor can be handled appropriately. 189 if (m_imageValue->isImageSetValue()) 190 return static_cast<CSSImageSetValue*>(m_imageValue.get())->cachedOrPendingImageSet(document); 191#endif 192 193 if (!m_image) 194 m_image = StylePendingImage::create(this); 195 196 return m_image.get(); 197} 198 199#if ENABLE(SVG) 200bool CSSCursorImageValue::isSVGCursor() const 201{ 202 if (m_imageValue->isImageValue()) { 203 RefPtr<CSSImageValue> imageValue = static_cast<CSSImageValue*>(m_imageValue.get()); 204 KURL kurl(ParsedURLString, imageValue->url()); 205 return kurl.hasFragmentIdentifier(); 206 } 207 return false; 208} 209 210String CSSCursorImageValue::cachedImageURL() 211{ 212 if (!m_image || !m_image->isCachedImage()) 213 return String(); 214 return static_cast<StyleCachedImage*>(m_image.get())->cachedImage()->url(); 215} 216 217void CSSCursorImageValue::clearCachedImage() 218{ 219 detachPendingImage(); 220 m_image = nullptr; 221 m_accessedImage = false; 222} 223 224void CSSCursorImageValue::removeReferencedElement(SVGElement* element) 225{ 226 m_referencedElements.remove(element); 227} 228#endif 229 230bool CSSCursorImageValue::equals(const CSSCursorImageValue& other) const 231{ 232 return m_hasHotSpot ? other.m_hasHotSpot && m_hotSpot == other.m_hotSpot : !other.m_hasHotSpot 233 && compareCSSValuePtr(m_imageValue, other.m_imageValue); 234} 235 236} // namespace WebCore 237