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 "core/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/StringBuilder.h" 38 #include "wtf/text/WTFString.h" 39 40 namespace blink { 41 42 static inline SVGCursorElement* resourceReferencedByCursorElement(const String& url, TreeScope& treeScope) 43 { 44 Element* element = SVGURIReference::targetElementFromIRIString(url, treeScope); 45 return isSVGCursorElement(element) ? toSVGCursorElement(element) : 0; 46 } 47 48 CSSCursorImageValue::CSSCursorImageValue(PassRefPtrWillBeRawPtr<CSSValue> imageValue, bool hasHotSpot, const IntPoint& hotSpot) 49 : CSSValue(CursorImageClass) 50 , m_imageValue(imageValue) 51 , m_hasHotSpot(hasHotSpot) 52 , m_hotSpot(hotSpot) 53 , m_accessedImage(false) 54 { 55 } 56 57 CSSCursorImageValue::~CSSCursorImageValue() 58 { 59 // The below teardown is all handled by weak pointer processing in oilpan. 60 #if !ENABLE(OILPAN) 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->treeScope())) 72 cursorElement->removeClient(referencedElement); 73 } 74 #endif 75 } 76 77 String CSSCursorImageValue::customCSSText() const 78 { 79 StringBuilder result; 80 result.append(m_imageValue->cssText()); 81 if (m_hasHotSpot) { 82 result.append(' '); 83 result.appendNumber(m_hotSpot.x()); 84 result.append(' '); 85 result.appendNumber(m_hotSpot.y()); 86 } 87 return result.toString(); 88 } 89 90 bool CSSCursorImageValue::updateIfSVGCursorIsUsed(Element* element) 91 { 92 if (!element || !element->isSVGElement()) 93 return false; 94 95 if (!isSVGCursor()) 96 return false; 97 98 String url = toCSSImageValue(m_imageValue.get())->url(); 99 if (SVGCursorElement* cursorElement = resourceReferencedByCursorElement(url, element->treeScope())) { 100 // FIXME: This will override hot spot specified in CSS, which is probably incorrect. 101 SVGLengthContext lengthContext(0); 102 m_hasHotSpot = true; 103 float x = roundf(cursorElement->x()->currentValue()->value(lengthContext)); 104 m_hotSpot.setX(static_cast<int>(x)); 105 106 float y = roundf(cursorElement->y()->currentValue()->value(lengthContext)); 107 m_hotSpot.setY(static_cast<int>(y)); 108 109 if (cachedImageURL() != element->document().completeURL(cursorElement->href()->currentValue()->value())) 110 clearImageResource(); 111 112 SVGElement* svgElement = toSVGElement(element); 113 #if !ENABLE(OILPAN) 114 m_referencedElements.add(svgElement); 115 #endif 116 svgElement->setCursorImageValue(this); 117 cursorElement->addClient(svgElement); 118 return true; 119 } 120 121 return false; 122 } 123 124 StyleImage* CSSCursorImageValue::cachedImage(ResourceFetcher* loader, float deviceScaleFactor) 125 { 126 if (m_imageValue->isImageSetValue()) 127 return toCSSImageSetValue(m_imageValue.get())->cachedImageSet(loader, deviceScaleFactor); 128 129 if (!m_accessedImage) { 130 m_accessedImage = true; 131 132 // For SVG images we need to lazily substitute in the correct URL. Rather than attempt 133 // to change the URL of the CSSImageValue (which would then change behavior like cssText), 134 // we create an alternate CSSImageValue to use. 135 if (isSVGCursor() && loader && loader->document()) { 136 RefPtrWillBeRawPtr<CSSImageValue> imageValue = toCSSImageValue(m_imageValue.get()); 137 // FIXME: This will fail if the <cursor> element is in a shadow DOM (bug 59827) 138 if (SVGCursorElement* cursorElement = resourceReferencedByCursorElement(imageValue->url(), *loader->document())) { 139 RefPtrWillBeRawPtr<CSSImageValue> svgImageValue = CSSImageValue::create(loader->document()->completeURL(cursorElement->href()->currentValue()->value())); 140 svgImageValue->setReferrer(imageValue->referrer()); 141 StyleFetchedImage* cachedImage = svgImageValue->cachedImage(loader); 142 m_image = cachedImage; 143 return cachedImage; 144 } 145 } 146 147 if (m_imageValue->isImageValue()) 148 m_image = toCSSImageValue(m_imageValue.get())->cachedImage(loader); 149 } 150 151 if (m_image && m_image->isImageResource()) 152 return toStyleFetchedImage(m_image); 153 return 0; 154 } 155 156 StyleImage* CSSCursorImageValue::cachedOrPendingImage(float deviceScaleFactor) 157 { 158 // Need to delegate completely so that changes in device scale factor can be handled appropriately. 159 if (m_imageValue->isImageSetValue()) 160 return toCSSImageSetValue(m_imageValue.get())->cachedOrPendingImageSet(deviceScaleFactor); 161 162 if (!m_image) 163 m_image = StylePendingImage::create(this); 164 165 return m_image.get(); 166 } 167 168 bool CSSCursorImageValue::isSVGCursor() const 169 { 170 if (m_imageValue->isImageValue()) { 171 RefPtrWillBeRawPtr<CSSImageValue> imageValue = toCSSImageValue(m_imageValue.get()); 172 KURL kurl(ParsedURLString, imageValue->url()); 173 return kurl.hasFragmentIdentifier(); 174 } 175 return false; 176 } 177 178 String CSSCursorImageValue::cachedImageURL() 179 { 180 if (!m_image || !m_image->isImageResource()) 181 return String(); 182 return toStyleFetchedImage(m_image)->cachedImage()->url().string(); 183 } 184 185 void CSSCursorImageValue::clearImageResource() 186 { 187 m_image = nullptr; 188 m_accessedImage = false; 189 } 190 191 #if !ENABLE(OILPAN) 192 void CSSCursorImageValue::removeReferencedElement(SVGElement* element) 193 { 194 m_referencedElements.remove(element); 195 } 196 #endif 197 198 bool CSSCursorImageValue::equals(const CSSCursorImageValue& other) const 199 { 200 return m_hasHotSpot ? other.m_hasHotSpot && m_hotSpot == other.m_hotSpot : !other.m_hasHotSpot 201 && compareCSSValuePtr(m_imageValue, other.m_imageValue); 202 } 203 204 void CSSCursorImageValue::traceAfterDispatch(Visitor* visitor) 205 { 206 visitor->trace(m_imageValue); 207 CSSValue::traceAfterDispatch(visitor); 208 } 209 210 } // namespace blink 211