1 /* 2 * Copyright (C) 2006 Rob Buis <buis (at) kde.org> 3 * (C) 2008 Nikolas Zimmermann <zimmermann (at) 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 39 namespace WebCore { 40 41 static 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 50 CSSCursorImageValue::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 59 CSSCursorImageValue::~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 76 String 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 89 bool 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 121 StyleImage* 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 152 StyleImage* 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 164 bool 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 174 String CSSCursorImageValue::cachedImageURL() 175 { 176 if (!m_image || !m_image->isImageResource()) 177 return String(); 178 return toStyleFetchedImage(m_image)->cachedImage()->url().string(); 179 } 180 181 void CSSCursorImageValue::clearImageResource() 182 { 183 m_image = 0; 184 m_accessedImage = false; 185 } 186 187 void CSSCursorImageValue::removeReferencedElement(SVGElement* element) 188 { 189 m_referencedElements.remove(element); 190 } 191 192 bool 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