Home | History | Annotate | Download | only in css
      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