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