d25222c1e1ad3e5faf207d5a600b843bb16e6667
[WebKit-https.git] / Source / WebCore / css / CSSCursorImageValue.cpp
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
48 namespace WebCore {
49
50 #if ENABLE(SVG)
51 static 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
61 CSSCursorImageValue::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
70 CSSCursorImageValue::~CSSCursorImageValue()
71 {
72 #if ENABLE(SVG)
73     if (!isSVGCursor())
74         return;
75
76     HashSet<SVGElement*>::const_iterator it = m_referencedElements.begin();
77     HashSet<SVGElement*>::const_iterator end = m_referencedElements.end();
78     String url = static_cast<CSSImageValue*>(m_imageValue.get())->url();
79
80     for (; it != end; ++it) {
81         SVGElement* referencedElement = *it;
82         referencedElement->cursorImageValueRemoved();
83         if (SVGCursorElement* cursorElement = resourceReferencedByCursorElement(url, &referencedElement->document()))
84             cursorElement->removeClient(referencedElement);
85     }
86 #endif
87 }
88
89 String CSSCursorImageValue::customCSSText() const
90 {
91     StringBuilder result;
92     result.append(m_imageValue->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
102 bool CSSCursorImageValue::updateIfSVGCursorIsUsed(Element* element)
103 {
104 #if !ENABLE(SVG)
105     UNUSED_PARAM(element);
106 #else
107     if (!element || !element->isSVGElement())
108         return false;
109
110     if (!isSVGCursor())
111         return false;
112
113     String url = static_cast<CSSImageValue*>(m_imageValue.get())->url();
114     if (SVGCursorElement* cursorElement = resourceReferencedByCursorElement(url, &element->document())) {
115         // FIXME: This will override hot spot specified in CSS, which is probably incorrect.
116         SVGLengthContext lengthContext(0);
117         m_hasHotSpot = true;
118         float x = roundf(cursorElement->x().value(lengthContext));
119         m_hotSpot.setX(static_cast<int>(x));
120
121         float y = roundf(cursorElement->y().value(lengthContext));
122         m_hotSpot.setY(static_cast<int>(y));
123
124         if (cachedImageURL() != element->document().completeURL(cursorElement->href()))
125             clearCachedImage();
126
127         SVGElement* svgElement = toSVGElement(element);
128         m_referencedElements.add(svgElement);
129         svgElement->setCursorImageValue(this);
130         cursorElement->addClient(svgElement);
131         return true;
132     }
133 #endif
134
135     return false;
136 }
137
138 StyleImage* CSSCursorImageValue::cachedImage(CachedResourceLoader* loader)
139 {
140 #if ENABLE(CSS_IMAGE_SET)
141     if (m_imageValue->isImageSetValue())
142         return static_cast<CSSImageSetValue*>(m_imageValue.get())->cachedImageSet(loader);
143 #endif
144
145     if (!m_accessedImage) {
146         m_accessedImage = true;
147
148 #if ENABLE(SVG)
149         // For SVG images we need to lazily substitute in the correct URL. Rather than attempt
150         // to change the URL of the CSSImageValue (which would then change behavior like cssText),
151         // we create an alternate CSSImageValue to use.
152         if (isSVGCursor() && loader && loader->document()) {
153             RefPtr<CSSImageValue> imageValue = static_cast<CSSImageValue*>(m_imageValue.get());
154             // FIXME: This will fail if the <cursor> element is in a shadow DOM (bug 59827)
155             if (SVGCursorElement* cursorElement = resourceReferencedByCursorElement(imageValue->url(), loader->document())) {
156                 RefPtr<CSSImageValue> svgImageValue = CSSImageValue::create(cursorElement->href());
157                 StyleCachedImage* cachedImage = svgImageValue->cachedImage(loader);
158                 m_image = cachedImage;
159                 return cachedImage;
160             }
161         }
162 #endif
163
164         if (m_imageValue->isImageValue())
165             m_image = static_cast<CSSImageValue*>(m_imageValue.get())->cachedImage(loader);
166     }
167
168     if (m_image && m_image->isCachedImage())
169         return static_cast<StyleCachedImage*>(m_image.get());
170
171     return 0;
172 }
173
174 StyleImage* CSSCursorImageValue::cachedOrPendingImage(Document* document)
175 {
176 #if ENABLE(CSS_IMAGE_SET)
177     // Need to delegate completely so that changes in device scale factor can be handled appropriately.
178     if (m_imageValue->isImageSetValue())
179         return static_cast<CSSImageSetValue*>(m_imageValue.get())->cachedOrPendingImageSet(document);
180 #endif
181
182     if (!m_image)
183         m_image = StylePendingImage::create(this);
184
185     return m_image.get();
186 }
187
188 #if ENABLE(SVG)
189 bool CSSCursorImageValue::isSVGCursor() const
190 {
191     if (m_imageValue->isImageValue()) {
192         RefPtr<CSSImageValue> imageValue = static_cast<CSSImageValue*>(m_imageValue.get());
193         KURL kurl(ParsedURLString, imageValue->url());
194         return kurl.hasFragmentIdentifier();
195     }
196     return false;
197 }
198
199 String CSSCursorImageValue::cachedImageURL()
200 {
201     if (!m_image || !m_image->isCachedImage())
202         return String();
203     return static_cast<StyleCachedImage*>(m_image.get())->cachedImage()->url();
204 }
205
206 void CSSCursorImageValue::clearCachedImage()
207 {
208     m_image = 0;
209     m_accessedImage = false;
210 }
211
212 void CSSCursorImageValue::removeReferencedElement(SVGElement* element)
213 {
214     m_referencedElements.remove(element);
215 }
216 #endif
217
218 bool CSSCursorImageValue::equals(const CSSCursorImageValue& other) const
219 {
220     return m_hasHotSpot ? other.m_hasHotSpot && m_hotSpot == other.m_hotSpot : !other.m_hasHotSpot
221         && compareCSSValuePtr(m_imageValue, other.m_imageValue);
222 }
223
224 } // namespace WebCore