1 /* 2 * Copyright (C) 2007, 2010 Rob Buis <buis (at) kde.org> 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Library General Public 6 * License as published by the Free Software Foundation; either 7 * version 2 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Library General Public License for more details. 13 * 14 * You should have received a copy of the GNU Library General Public License 15 * along with this library; see the file COPYING.LIB. If not, write to 16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 17 * Boston, MA 02110-1301, USA. 18 */ 19 20 #include "config.h" 21 #include "core/svg/SVGViewSpec.h" 22 23 #include "SVGNames.h" 24 #include "bindings/v8/ExceptionState.h" 25 #include "core/dom/Document.h" 26 #include "core/svg/SVGAnimatedTransformList.h" 27 #include "core/svg/SVGFitToViewBox.h" 28 #include "core/svg/SVGParserUtilities.h" 29 #include "core/svg/SVGSVGElement.h" 30 #include "core/svg/SVGTransformable.h" 31 32 namespace WebCore { 33 34 // Define custom animated property 'viewBox'. 35 const SVGPropertyInfo* SVGViewSpec::viewBoxPropertyInfo() 36 { 37 static const SVGPropertyInfo* s_propertyInfo = 0; 38 if (!s_propertyInfo) { 39 s_propertyInfo = new SVGPropertyInfo(AnimatedRect, 40 PropertyIsReadOnly, 41 SVGNames::viewBoxAttr, 42 viewBoxIdentifier(), 43 0, 44 0); 45 } 46 return s_propertyInfo; 47 } 48 49 // Define custom animated property 'preserveAspectRatio'. 50 const SVGPropertyInfo* SVGViewSpec::preserveAspectRatioPropertyInfo() 51 { 52 static const SVGPropertyInfo* s_propertyInfo = 0; 53 if (!s_propertyInfo) { 54 s_propertyInfo = new SVGPropertyInfo(AnimatedPreserveAspectRatio, 55 PropertyIsReadOnly, 56 SVGNames::preserveAspectRatioAttr, 57 preserveAspectRatioIdentifier(), 58 0, 59 0); 60 } 61 return s_propertyInfo; 62 } 63 64 65 // Define custom non-animated property 'transform'. 66 const SVGPropertyInfo* SVGViewSpec::transformPropertyInfo() 67 { 68 static const SVGPropertyInfo* s_propertyInfo = 0; 69 if (!s_propertyInfo) { 70 s_propertyInfo = new SVGPropertyInfo(AnimatedTransformList, 71 PropertyIsReadOnly, 72 SVGNames::transformAttr, 73 transformIdentifier(), 74 0, 75 0); 76 } 77 return s_propertyInfo; 78 } 79 80 SVGViewSpec::SVGViewSpec(SVGElement* contextElement) 81 : m_contextElement(contextElement) 82 , m_zoomAndPan(SVGZoomAndPanMagnify) 83 { 84 ASSERT(m_contextElement); 85 ScriptWrappable::init(this); 86 } 87 88 const AtomicString& SVGViewSpec::viewBoxIdentifier() 89 { 90 DEFINE_STATIC_LOCAL(AtomicString, s_identifier, ("SVGViewSpecViewBoxAttribute", AtomicString::ConstructFromLiteral)); 91 return s_identifier; 92 } 93 94 const AtomicString& SVGViewSpec::preserveAspectRatioIdentifier() 95 { 96 DEFINE_STATIC_LOCAL(AtomicString, s_identifier, ("SVGViewSpecPreserveAspectRatioAttribute", AtomicString::ConstructFromLiteral)); 97 return s_identifier; 98 } 99 100 const AtomicString& SVGViewSpec::transformIdentifier() 101 { 102 DEFINE_STATIC_LOCAL(AtomicString, s_identifier, ("SVGViewSpecTransformAttribute", AtomicString::ConstructFromLiteral)); 103 return s_identifier; 104 } 105 106 void SVGViewSpec::setZoomAndPan(unsigned short, ExceptionState& es) 107 { 108 // SVGViewSpec and all of its content is read-only. 109 es.throwDOMException(NoModificationAllowedError); 110 } 111 112 void SVGViewSpec::setTransformString(const String& transform) 113 { 114 if (!m_contextElement) 115 return; 116 117 SVGTransformList newList; 118 newList.parse(transform); 119 120 if (SVGAnimatedProperty* wrapper = SVGAnimatedProperty::lookupWrapper<SVGElement, SVGAnimatedTransformList>(m_contextElement, transformPropertyInfo())) 121 static_cast<SVGAnimatedTransformList*>(wrapper)->detachListWrappers(newList.size()); 122 123 m_transform = newList; 124 } 125 126 String SVGViewSpec::transformString() const 127 { 128 return SVGPropertyTraits<SVGTransformList>::toString(m_transform); 129 } 130 131 String SVGViewSpec::viewBoxString() const 132 { 133 return SVGPropertyTraits<FloatRect>::toString(viewBoxBaseValue()); 134 } 135 136 String SVGViewSpec::preserveAspectRatioString() const 137 { 138 return SVGPropertyTraits<SVGPreserveAspectRatio>::toString(preserveAspectRatioBaseValue()); 139 } 140 141 SVGElement* SVGViewSpec::viewTarget() const 142 { 143 if (!m_contextElement) 144 return 0; 145 Element* element = m_contextElement->treeScope()->getElementById(m_viewTargetString); 146 if (!element || !element->isSVGElement()) 147 return 0; 148 return toSVGElement(element); 149 } 150 151 SVGTransformListPropertyTearOff* SVGViewSpec::transform() 152 { 153 if (!m_contextElement) 154 return 0; 155 // Return the animVal here, as its readonly by default - which is exactly what we want here. 156 return static_cast<SVGTransformListPropertyTearOff*>(static_pointer_cast<SVGAnimatedTransformList>(lookupOrCreateTransformWrapper(this))->animVal()); 157 } 158 159 PassRefPtr<SVGAnimatedRect> SVGViewSpec::viewBox() 160 { 161 if (!m_contextElement) 162 return 0; 163 return static_pointer_cast<SVGAnimatedRect>(lookupOrCreateViewBoxWrapper(this)); 164 } 165 166 PassRefPtr<SVGAnimatedPreserveAspectRatio> SVGViewSpec::preserveAspectRatio() 167 { 168 if (!m_contextElement) 169 return 0; 170 return static_pointer_cast<SVGAnimatedPreserveAspectRatio>(lookupOrCreatePreserveAspectRatioWrapper(this)); 171 } 172 173 PassRefPtr<SVGAnimatedProperty> SVGViewSpec::lookupOrCreateViewBoxWrapper(SVGViewSpec* ownerType) 174 { 175 ASSERT(ownerType); 176 ASSERT(ownerType->contextElement()); 177 return SVGAnimatedProperty::lookupOrCreateWrapper<SVGElement, SVGAnimatedRect, FloatRect>(ownerType->contextElement(), viewBoxPropertyInfo(), ownerType->m_viewBox); 178 } 179 180 PassRefPtr<SVGAnimatedProperty> SVGViewSpec::lookupOrCreatePreserveAspectRatioWrapper(SVGViewSpec* ownerType) 181 { 182 ASSERT(ownerType); 183 ASSERT(ownerType->contextElement()); 184 return SVGAnimatedProperty::lookupOrCreateWrapper<SVGElement, SVGAnimatedPreserveAspectRatio, SVGPreserveAspectRatio>(ownerType->contextElement(), preserveAspectRatioPropertyInfo(), ownerType->m_preserveAspectRatio); 185 } 186 187 PassRefPtr<SVGAnimatedProperty> SVGViewSpec::lookupOrCreateTransformWrapper(SVGViewSpec* ownerType) 188 { 189 ASSERT(ownerType); 190 ASSERT(ownerType->contextElement()); 191 return SVGAnimatedProperty::lookupOrCreateWrapper<SVGElement, SVGAnimatedTransformList, SVGTransformList>(ownerType->contextElement(), transformPropertyInfo(), ownerType->m_transform); 192 } 193 194 void SVGViewSpec::reset() 195 { 196 m_zoomAndPan = SVGZoomAndPanMagnify; 197 m_transform.clear(); 198 m_viewBox = FloatRect(); 199 m_preserveAspectRatio = SVGPreserveAspectRatio(); 200 m_viewTargetString = emptyString(); 201 } 202 203 static const LChar svgViewSpec[] = {'s', 'v', 'g', 'V', 'i', 'e', 'w'}; 204 static const LChar viewBoxSpec[] = {'v', 'i', 'e', 'w', 'B', 'o', 'x'}; 205 static const LChar preserveAspectRatioSpec[] = {'p', 'r', 'e', 's', 'e', 'r', 'v', 'e', 'A', 's', 'p', 'e', 'c', 't', 'R', 'a', 't', 'i', 'o'}; 206 static const LChar transformSpec[] = {'t', 'r', 'a', 'n', 's', 'f', 'o', 'r', 'm'}; 207 static const LChar zoomAndPanSpec[] = {'z', 'o', 'o', 'm', 'A', 'n', 'd', 'P', 'a', 'n'}; 208 static const LChar viewTargetSpec[] = {'v', 'i', 'e', 'w', 'T', 'a', 'r', 'g', 'e', 't'}; 209 210 template<typename CharType> 211 bool SVGViewSpec::parseViewSpecInternal(const CharType* ptr, const CharType* end) 212 { 213 if (!skipString(ptr, end, svgViewSpec, WTF_ARRAY_LENGTH(svgViewSpec))) 214 return false; 215 216 if (ptr >= end || *ptr != '(') 217 return false; 218 ptr++; 219 220 while (ptr < end && *ptr != ')') { 221 if (*ptr == 'v') { 222 if (skipString(ptr, end, viewBoxSpec, WTF_ARRAY_LENGTH(viewBoxSpec))) { 223 if (ptr >= end || *ptr != '(') 224 return false; 225 ptr++; 226 FloatRect viewBox; 227 if (!SVGFitToViewBox::parseViewBox(m_contextElement->document(), ptr, end, viewBox, false)) 228 return false; 229 setViewBoxBaseValue(viewBox); 230 if (ptr >= end || *ptr != ')') 231 return false; 232 ptr++; 233 } else if (skipString(ptr, end, viewTargetSpec, WTF_ARRAY_LENGTH(viewTargetSpec))) { 234 if (ptr >= end || *ptr != '(') 235 return false; 236 const CharType* viewTargetStart = ++ptr; 237 while (ptr < end && *ptr != ')') 238 ptr++; 239 if (ptr >= end) 240 return false; 241 setViewTargetString(String(viewTargetStart, ptr - viewTargetStart)); 242 ptr++; 243 } else 244 return false; 245 } else if (*ptr == 'z') { 246 if (!skipString(ptr, end, zoomAndPanSpec, WTF_ARRAY_LENGTH(zoomAndPanSpec))) 247 return false; 248 if (ptr >= end || *ptr != '(') 249 return false; 250 ptr++; 251 if (!parseZoomAndPan(ptr, end, m_zoomAndPan)) 252 return false; 253 if (ptr >= end || *ptr != ')') 254 return false; 255 ptr++; 256 } else if (*ptr == 'p') { 257 if (!skipString(ptr, end, preserveAspectRatioSpec, WTF_ARRAY_LENGTH(preserveAspectRatioSpec))) 258 return false; 259 if (ptr >= end || *ptr != '(') 260 return false; 261 ptr++; 262 SVGPreserveAspectRatio preserveAspectRatio; 263 if (!preserveAspectRatio.parse(ptr, end, false)) 264 return false; 265 setPreserveAspectRatioBaseValue(preserveAspectRatio); 266 if (ptr >= end || *ptr != ')') 267 return false; 268 ptr++; 269 } else if (*ptr == 't') { 270 if (!skipString(ptr, end, transformSpec, WTF_ARRAY_LENGTH(transformSpec))) 271 return false; 272 if (ptr >= end || *ptr != '(') 273 return false; 274 ptr++; 275 SVGTransformable::parseTransformAttribute(m_transform, ptr, end, SVGTransformable::DoNotClearList); 276 if (ptr >= end || *ptr != ')') 277 return false; 278 ptr++; 279 } else 280 return false; 281 282 if (ptr < end && *ptr == ';') 283 ptr++; 284 } 285 286 if (ptr >= end || *ptr != ')') 287 return false; 288 289 return true; 290 } 291 292 bool SVGViewSpec::parseViewSpec(const String& spec) 293 { 294 if (spec.isEmpty() || !m_contextElement) 295 return false; 296 if (spec.is8Bit()) { 297 const LChar* ptr = spec.characters8(); 298 const LChar* end = ptr + spec.length(); 299 return parseViewSpecInternal(ptr, end); 300 } 301 const UChar* ptr = spec.characters16(); 302 const UChar* end = ptr + spec.length(); 303 return parseViewSpecInternal(ptr, end); 304 } 305 306 } 307