586b8fdaf77169a2283da05bd0e78579c5428a13
[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 "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
45 namespace WebCore {
46
47 static inline SVGCursorElement* resourceReferencedByCursorElement(const String& url, Document& document)
48 {
49     Element* element = SVGURIReference::targetElementFromIRIString(url, document);
50     if (element && is<SVGCursorElement>(element))
51         return downcast<SVGCursorElement>(element);
52
53     return nullptr;
54 }
55
56 CSSCursorImageValue::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
65 inline void CSSCursorImageValue::detachPendingImage()
66 {
67     if (m_image && m_image->isPendingImage())
68         toStylePendingImage(*m_image).detachFromCSSValue();
69 }
70
71 CSSCursorImageValue::~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
89 String 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
102 bool 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 = downcast<SVGElement>(*element);
124         m_referencedElements.add(&svgElement);
125         svgElement.setCursorImageValue(this);
126         cursorElement->addClient(&svgElement);
127         return true;
128     }
129
130     return false;
131 }
132
133 StyleImage* 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
169 StyleImage* 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
185 bool 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
194 String CSSCursorImageValue::cachedImageURL()
195 {
196     if (!m_image || !m_image->isCachedImage())
197         return String();
198     return toStyleCachedImage(*m_image).cachedImage()->url();
199 }
200
201 void CSSCursorImageValue::clearCachedImage()
202 {
203     detachPendingImage();
204     m_image = nullptr;
205     m_accessedImage = false;
206 }
207
208 void CSSCursorImageValue::removeReferencedElement(SVGElement* element)
209 {
210     m_referencedElements.remove(element);
211 }
212
213 bool 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