| 1 | /* |
| 2 | * Copyright (C) 2007, 2010 Rob Buis <buis@kde.org> |
| 3 | * Copyright (C) 2018-2019 Apple Inc. All rights reserved. |
| 4 | * |
| 5 | * This library is free software; you can redistribute it and/or |
| 6 | * modify it under the terms of the GNU Library General Public |
| 7 | * License as published by the Free Software Foundation; either |
| 8 | * version 2 of the License, or (at your option) any later version. |
| 9 | * |
| 10 | * This library is distributed in the hope that it will be useful, |
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 13 | * Library General Public License for more details. |
| 14 | * |
| 15 | * You should have received a copy of the GNU Library General Public License |
| 16 | * along with this library; see the file COPYING.LIB. If not, write to |
| 17 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| 18 | * Boston, MA 02110-1301, USA. |
| 19 | */ |
| 20 | |
| 21 | #include "config.h" |
| 22 | #include "SVGViewSpec.h" |
| 23 | |
| 24 | #include "Document.h" |
| 25 | #include "SVGElement.h" |
| 26 | #include "SVGFitToViewBox.h" |
| 27 | #include "SVGNames.h" |
| 28 | #include "SVGParserUtilities.h" |
| 29 | #include "SVGTransformList.h" |
| 30 | #include "SVGTransformable.h" |
| 31 | |
| 32 | namespace WebCore { |
| 33 | |
| 34 | SVGViewSpec::SVGViewSpec(SVGElement& contextElement) |
| 35 | : SVGFitToViewBox(&contextElement, SVGPropertyAccess::ReadOnly) |
| 36 | , m_contextElement(makeWeakPtr(contextElement)) |
| 37 | , m_transform(SVGTransformList::create(&contextElement, SVGPropertyAccess::ReadOnly)) |
| 38 | { |
| 39 | static std::once_flag onceFlag; |
| 40 | std::call_once(onceFlag, [] { |
| 41 | PropertyRegistry::registerProperty<SVGNames::transformAttr, &SVGViewSpec::m_transform>(); |
| 42 | }); |
| 43 | } |
| 44 | |
| 45 | SVGElement* SVGViewSpec::viewTarget() const |
| 46 | { |
| 47 | if (!m_contextElement) |
| 48 | return nullptr; |
| 49 | auto* element = m_contextElement->treeScope().getElementById(m_viewTargetString); |
| 50 | if (!is<SVGElement>(element)) |
| 51 | return nullptr; |
| 52 | return downcast<SVGElement>(element); |
| 53 | } |
| 54 | |
| 55 | void SVGViewSpec::reset() |
| 56 | { |
| 57 | m_viewTargetString = emptyString(); |
| 58 | m_transform->clearItems(); |
| 59 | SVGFitToViewBox::reset(); |
| 60 | SVGZoomAndPan::reset(); |
| 61 | } |
| 62 | |
| 63 | static const UChar svgViewSpec[] = {'s', 'v', 'g', 'V', 'i', 'e', 'w'}; |
| 64 | static const UChar viewBoxSpec[] = {'v', 'i', 'e', 'w', 'B', 'o', 'x'}; |
| 65 | static const UChar preserveAspectRatioSpec[] = {'p', 'r', 'e', 's', 'e', 'r', 'v', 'e', 'A', 's', 'p', 'e', 'c', 't', 'R', 'a', 't', 'i', 'o'}; |
| 66 | static const UChar transformSpec[] = {'t', 'r', 'a', 'n', 's', 'f', 'o', 'r', 'm'}; |
| 67 | static const UChar zoomAndPanSpec[] = {'z', 'o', 'o', 'm', 'A', 'n', 'd', 'P', 'a', 'n'}; |
| 68 | static const UChar viewTargetSpec[] = {'v', 'i', 'e', 'w', 'T', 'a', 'r', 'g', 'e', 't'}; |
| 69 | |
| 70 | bool SVGViewSpec::parseViewSpec(const String& viewSpec) |
| 71 | { |
| 72 | auto upconvertedCharacters = StringView(viewSpec).upconvertedCharacters(); |
| 73 | const UChar* currViewSpec = upconvertedCharacters; |
| 74 | const UChar* end = currViewSpec + viewSpec.length(); |
| 75 | |
| 76 | if (currViewSpec >= end || !m_contextElement) |
| 77 | return false; |
| 78 | |
| 79 | if (!skipString(currViewSpec, end, svgViewSpec, WTF_ARRAY_LENGTH(svgViewSpec))) |
| 80 | return false; |
| 81 | |
| 82 | if (currViewSpec >= end || *currViewSpec != '(') |
| 83 | return false; |
| 84 | currViewSpec++; |
| 85 | |
| 86 | while (currViewSpec < end && *currViewSpec != ')') { |
| 87 | if (*currViewSpec == 'v') { |
| 88 | if (skipString(currViewSpec, end, viewBoxSpec, WTF_ARRAY_LENGTH(viewBoxSpec))) { |
| 89 | if (currViewSpec >= end || *currViewSpec != '(') |
| 90 | return false; |
| 91 | currViewSpec++; |
| 92 | FloatRect viewBox; |
| 93 | if (!SVGFitToViewBox::parseViewBox(currViewSpec, end, viewBox, false)) |
| 94 | return false; |
| 95 | setViewBox(viewBox); |
| 96 | if (currViewSpec >= end || *currViewSpec != ')') |
| 97 | return false; |
| 98 | currViewSpec++; |
| 99 | } else if (skipString(currViewSpec, end, viewTargetSpec, WTF_ARRAY_LENGTH(viewTargetSpec))) { |
| 100 | if (currViewSpec >= end || *currViewSpec != '(') |
| 101 | return false; |
| 102 | const UChar* viewTargetStart = ++currViewSpec; |
| 103 | while (currViewSpec < end && *currViewSpec != ')') |
| 104 | currViewSpec++; |
| 105 | if (currViewSpec >= end) |
| 106 | return false; |
| 107 | m_viewTargetString = String(viewTargetStart, currViewSpec - viewTargetStart); |
| 108 | currViewSpec++; |
| 109 | } else |
| 110 | return false; |
| 111 | } else if (*currViewSpec == 'z') { |
| 112 | if (!skipString(currViewSpec, end, zoomAndPanSpec, WTF_ARRAY_LENGTH(zoomAndPanSpec))) |
| 113 | return false; |
| 114 | if (currViewSpec >= end || *currViewSpec != '(') |
| 115 | return false; |
| 116 | currViewSpec++; |
| 117 | if (!SVGZoomAndPan::parseZoomAndPan(currViewSpec, end)) |
| 118 | return false; |
| 119 | if (currViewSpec >= end || *currViewSpec != ')') |
| 120 | return false; |
| 121 | currViewSpec++; |
| 122 | } else if (*currViewSpec == 'p') { |
| 123 | if (!skipString(currViewSpec, end, preserveAspectRatioSpec, WTF_ARRAY_LENGTH(preserveAspectRatioSpec))) |
| 124 | return false; |
| 125 | if (currViewSpec >= end || *currViewSpec != '(') |
| 126 | return false; |
| 127 | currViewSpec++; |
| 128 | SVGPreserveAspectRatioValue preserveAspectRatio; |
| 129 | if (!preserveAspectRatio.parse(currViewSpec, end, false)) |
| 130 | return false; |
| 131 | setPreserveAspectRatio(preserveAspectRatio); |
| 132 | if (currViewSpec >= end || *currViewSpec != ')') |
| 133 | return false; |
| 134 | currViewSpec++; |
| 135 | } else if (*currViewSpec == 't') { |
| 136 | if (!skipString(currViewSpec, end, transformSpec, WTF_ARRAY_LENGTH(transformSpec))) |
| 137 | return false; |
| 138 | if (currViewSpec >= end || *currViewSpec != '(') |
| 139 | return false; |
| 140 | currViewSpec++; |
| 141 | m_transform->parse(currViewSpec, end); |
| 142 | if (currViewSpec >= end || *currViewSpec != ')') |
| 143 | return false; |
| 144 | currViewSpec++; |
| 145 | } else |
| 146 | return false; |
| 147 | |
| 148 | if (currViewSpec < end && *currViewSpec == ';') |
| 149 | currViewSpec++; |
| 150 | } |
| 151 | |
| 152 | if (currViewSpec >= end || *currViewSpec != ')') |
| 153 | return false; |
| 154 | |
| 155 | return true; |
| 156 | } |
| 157 | |
| 158 | } |
| 159 | |