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 "core/css/CSSCursorImageValue.h" 24 25#include "SVGNames.h" 26#include "core/css/CSSImageSetValue.h" 27#include "core/fetch/ImageResource.h" 28#include "core/fetch/ResourceFetcher.h" 29#include "core/rendering/style/StyleFetchedImage.h" 30#include "core/rendering/style/StyleFetchedImageSet.h" 31#include "core/rendering/style/StyleImage.h" 32#include "core/rendering/style/StylePendingImage.h" 33#include "core/svg/SVGCursorElement.h" 34#include "core/svg/SVGLengthContext.h" 35#include "core/svg/SVGURIReference.h" 36#include "wtf/MathExtras.h" 37#include "wtf/text/WTFString.h" 38 39namespace WebCore { 40 41static inline SVGCursorElement* resourceReferencedByCursorElement(const String& url, Document& document) 42{ 43 Element* element = SVGURIReference::targetElementFromIRIString(url, document); 44 if (element && element->hasTagName(SVGNames::cursorTag)) 45 return static_cast<SVGCursorElement*>(element); 46 47 return 0; 48} 49 50CSSCursorImageValue::CSSCursorImageValue(PassRefPtr<CSSValue> imageValue, bool hasHotSpot, const IntPoint& hotSpot) 51 : CSSValue(CursorImageClass) 52 , m_imageValue(imageValue) 53 , m_hasHotSpot(hasHotSpot) 54 , m_hotSpot(hotSpot) 55 , m_accessedImage(false) 56{ 57} 58 59CSSCursorImageValue::~CSSCursorImageValue() 60{ 61 if (!isSVGCursor()) 62 return; 63 64 HashSet<SVGElement*>::const_iterator it = m_referencedElements.begin(); 65 HashSet<SVGElement*>::const_iterator end = m_referencedElements.end(); 66 String url = toCSSImageValue(m_imageValue.get())->url(); 67 68 for (; it != end; ++it) { 69 SVGElement* referencedElement = *it; 70 referencedElement->cursorImageValueRemoved(); 71 if (SVGCursorElement* cursorElement = resourceReferencedByCursorElement(url, referencedElement->document())) 72 cursorElement->removeClient(referencedElement); 73 } 74} 75 76String CSSCursorImageValue::customCSSText() const 77{ 78 StringBuilder result; 79 result.append(m_imageValue->cssText()); 80 if (m_hasHotSpot) { 81 result.append(' '); 82 result.appendNumber(m_hotSpot.x()); 83 result.append(' '); 84 result.appendNumber(m_hotSpot.y()); 85 } 86 return result.toString(); 87} 88 89bool CSSCursorImageValue::updateIfSVGCursorIsUsed(Element* element) 90{ 91 if (!element || !element->isSVGElement()) 92 return false; 93 94 if (!isSVGCursor()) 95 return false; 96 97 String url = toCSSImageValue(m_imageValue.get())->url(); 98 if (SVGCursorElement* cursorElement = resourceReferencedByCursorElement(url, element->document())) { 99 // FIXME: This will override hot spot specified in CSS, which is probably incorrect. 100 SVGLengthContext lengthContext(0); 101 m_hasHotSpot = true; 102 float x = roundf(cursorElement->xCurrentValue().value(lengthContext)); 103 m_hotSpot.setX(static_cast<int>(x)); 104 105 float y = roundf(cursorElement->yCurrentValue().value(lengthContext)); 106 m_hotSpot.setY(static_cast<int>(y)); 107 108 if (cachedImageURL() != element->document().completeURL(cursorElement->hrefCurrentValue())) 109 clearImageResource(); 110 111 SVGElement* svgElement = toSVGElement(element); 112 m_referencedElements.add(svgElement); 113 svgElement->setCursorImageValue(this); 114 cursorElement->addClient(svgElement); 115 return true; 116 } 117 118 return false; 119} 120 121StyleImage* CSSCursorImageValue::cachedImage(ResourceFetcher* loader, float deviceScaleFactor) 122{ 123 if (m_imageValue->isImageSetValue()) 124 return toCSSImageSetValue(m_imageValue.get())->cachedImageSet(loader, deviceScaleFactor); 125 126 if (!m_accessedImage) { 127 m_accessedImage = true; 128 129 // For SVG images we need to lazily substitute in the correct URL. Rather than attempt 130 // to change the URL of the CSSImageValue (which would then change behavior like cssText), 131 // we create an alternate CSSImageValue to use. 132 if (isSVGCursor() && loader && loader->document()) { 133 RefPtr<CSSImageValue> imageValue = toCSSImageValue(m_imageValue.get()); 134 // FIXME: This will fail if the <cursor> element is in a shadow DOM (bug 59827) 135 if (SVGCursorElement* cursorElement = resourceReferencedByCursorElement(imageValue->url(), *loader->document())) { 136 RefPtr<CSSImageValue> svgImageValue = CSSImageValue::create(cursorElement->hrefCurrentValue()); 137 StyleFetchedImage* cachedImage = svgImageValue->cachedImage(loader); 138 m_image = cachedImage; 139 return cachedImage; 140 } 141 } 142 143 if (m_imageValue->isImageValue()) 144 m_image = toCSSImageValue(m_imageValue.get())->cachedImage(loader); 145 } 146 147 if (m_image && m_image->isImageResource()) 148 return toStyleFetchedImage(m_image); 149 return 0; 150} 151 152StyleImage* CSSCursorImageValue::cachedOrPendingImage(float deviceScaleFactor) 153{ 154 // Need to delegate completely so that changes in device scale factor can be handled appropriately. 155 if (m_imageValue->isImageSetValue()) 156 return toCSSImageSetValue(m_imageValue.get())->cachedOrPendingImageSet(deviceScaleFactor); 157 158 if (!m_image) 159 m_image = StylePendingImage::create(this); 160 161 return m_image.get(); 162} 163 164bool CSSCursorImageValue::isSVGCursor() const 165{ 166 if (m_imageValue->isImageValue()) { 167 RefPtr<CSSImageValue> imageValue = toCSSImageValue(m_imageValue.get()); 168 KURL kurl(ParsedURLString, imageValue->url()); 169 return kurl.hasFragmentIdentifier(); 170 } 171 return false; 172} 173 174String CSSCursorImageValue::cachedImageURL() 175{ 176 if (!m_image || !m_image->isImageResource()) 177 return String(); 178 return toStyleFetchedImage(m_image)->cachedImage()->url().string(); 179} 180 181void CSSCursorImageValue::clearImageResource() 182{ 183 m_image = 0; 184 m_accessedImage = false; 185} 186 187void CSSCursorImageValue::removeReferencedElement(SVGElement* element) 188{ 189 m_referencedElements.remove(element); 190} 191 192bool CSSCursorImageValue::equals(const CSSCursorImageValue& other) const 193{ 194 return m_hasHotSpot ? other.m_hasHotSpot && m_hotSpot == other.m_hotSpot : !other.m_hasHotSpot 195 && compareCSSValuePtr(m_imageValue, other.m_imageValue); 196} 197 198} // namespace WebCore 199