/src/mozilla-central/dom/svg/SVGFragmentIdentifier.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "SVGFragmentIdentifier.h" |
8 | | |
9 | | #include "mozilla/dom/SVGSVGElement.h" |
10 | | #include "mozilla/dom/SVGViewElement.h" |
11 | | #include "nsContentUtils.h" // for nsCharSeparatedTokenizerTemplate |
12 | | #include "nsSVGAnimatedTransformList.h" |
13 | | #include "nsCharSeparatedTokenizer.h" |
14 | | |
15 | | namespace mozilla { |
16 | | |
17 | | using namespace dom; |
18 | | |
19 | | static bool |
20 | | IsMatchingParameter(const nsAString& aString, const nsAString& aParameterName) |
21 | 0 | { |
22 | 0 | // The first two tests ensure aString.Length() > aParameterName.Length() |
23 | 0 | // so it's then safe to do the third test |
24 | 0 | return StringBeginsWith(aString, aParameterName) && |
25 | 0 | aString.Last() == ')' && |
26 | 0 | aString.CharAt(aParameterName.Length()) == '('; |
27 | 0 | } |
28 | | |
29 | | inline bool |
30 | | IgnoreWhitespace(char16_t aChar) |
31 | 0 | { |
32 | 0 | return false; |
33 | 0 | } |
34 | | |
35 | | static SVGViewElement* |
36 | | GetViewElement(nsIDocument* aDocument, const nsAString& aId) |
37 | 0 | { |
38 | 0 | Element* element = aDocument->GetElementById(aId); |
39 | 0 | return (element && element->IsSVGElement(nsGkAtoms::view)) ? |
40 | 0 | static_cast<SVGViewElement*>(element) : nullptr; |
41 | 0 | } |
42 | | |
43 | | // Handles setting/clearing the root's mSVGView pointer. |
44 | | class MOZ_RAII AutoSVGViewHandler |
45 | | { |
46 | | public: |
47 | | explicit AutoSVGViewHandler(SVGSVGElement* aRoot |
48 | | MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
49 | 0 | : mRoot(aRoot), mValid(false) { |
50 | 0 | MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
51 | 0 | mWasOverridden = mRoot->UseCurrentView(); |
52 | 0 | mRoot->mSVGView = nullptr; |
53 | 0 | mRoot->mCurrentViewID = nullptr; |
54 | 0 | } |
55 | | |
56 | 0 | ~AutoSVGViewHandler() { |
57 | 0 | if (!mWasOverridden && !mValid) { |
58 | 0 | // we weren't overridden before and we aren't |
59 | 0 | // overridden now so nothing has changed. |
60 | 0 | return; |
61 | 0 | } |
62 | 0 | if (mValid) { |
63 | 0 | mRoot->mSVGView = mSVGView; |
64 | 0 | } |
65 | 0 | mRoot->InvalidateTransformNotifyFrame(); |
66 | 0 | } |
67 | | |
68 | 0 | void CreateSVGView() { |
69 | 0 | MOZ_ASSERT(!mSVGView, "CreateSVGView should not be called multiple times"); |
70 | 0 | mSVGView = new SVGView(); |
71 | 0 | } |
72 | | |
73 | 0 | bool ProcessAttr(const nsAString& aToken, const nsAString &aParams) { |
74 | 0 |
|
75 | 0 | MOZ_ASSERT(mSVGView, "CreateSVGView should have been called"); |
76 | 0 |
|
77 | 0 | // SVGViewAttributes may occur in any order, but each type may only occur |
78 | 0 | // at most one time in a correctly formed SVGViewSpec. |
79 | 0 | // If we encounter any attribute more than once or get any syntax errors |
80 | 0 | // we're going to return false and cancel any changes. |
81 | 0 |
|
82 | 0 | if (IsMatchingParameter(aToken, NS_LITERAL_STRING("viewBox"))) { |
83 | 0 | if (mSVGView->mViewBox.IsExplicitlySet() || |
84 | 0 | NS_FAILED(mSVGView->mViewBox.SetBaseValueString( |
85 | 0 | aParams, mRoot, false))) { |
86 | 0 | return false; |
87 | 0 | } |
88 | 0 | } else if (IsMatchingParameter(aToken, NS_LITERAL_STRING("preserveAspectRatio"))) { |
89 | 0 | if (mSVGView->mPreserveAspectRatio.IsExplicitlySet() || |
90 | 0 | NS_FAILED(mSVGView->mPreserveAspectRatio.SetBaseValueString( |
91 | 0 | aParams, mRoot, false))) { |
92 | 0 | return false; |
93 | 0 | } |
94 | 0 | } else if (IsMatchingParameter(aToken, NS_LITERAL_STRING("transform"))) { |
95 | 0 | if (mSVGView->mTransforms) { |
96 | 0 | return false; |
97 | 0 | } |
98 | 0 | mSVGView->mTransforms = new nsSVGAnimatedTransformList(); |
99 | 0 | if (NS_FAILED(mSVGView->mTransforms->SetBaseValueString(aParams, mRoot))) { |
100 | 0 | return false; |
101 | 0 | } |
102 | 0 | } else if (IsMatchingParameter(aToken, NS_LITERAL_STRING("zoomAndPan"))) { |
103 | 0 | if (mSVGView->mZoomAndPan.IsExplicitlySet()) { |
104 | 0 | return false; |
105 | 0 | } |
106 | 0 | nsAtom* valAtom = NS_GetStaticAtom(aParams); |
107 | 0 | if (!valAtom || |
108 | 0 | NS_FAILED(mSVGView->mZoomAndPan.SetBaseValueAtom( |
109 | 0 | valAtom, mRoot))) { |
110 | 0 | return false; |
111 | 0 | } |
112 | 0 | } else { |
113 | 0 | return false; |
114 | 0 | } |
115 | 0 | return true; |
116 | 0 | } |
117 | | |
118 | 0 | void SetValid() { |
119 | 0 | mValid = true; |
120 | 0 | } |
121 | | |
122 | | private: |
123 | | SVGSVGElement* mRoot; |
124 | | nsAutoPtr<SVGView> mSVGView; |
125 | | bool mValid; |
126 | | bool mWasOverridden; |
127 | | MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
128 | | }; |
129 | | |
130 | | bool |
131 | | SVGFragmentIdentifier::ProcessSVGViewSpec(const nsAString& aViewSpec, |
132 | | SVGSVGElement* aRoot) |
133 | 0 | { |
134 | 0 | AutoSVGViewHandler viewHandler(aRoot); |
135 | 0 |
|
136 | 0 | if (!IsMatchingParameter(aViewSpec, NS_LITERAL_STRING("svgView"))) { |
137 | 0 | return false; |
138 | 0 | } |
139 | 0 | |
140 | 0 | // Each token is a SVGViewAttribute |
141 | 0 | int32_t bracketPos = aViewSpec.FindChar('('); |
142 | 0 | uint32_t lengthOfViewSpec = aViewSpec.Length() - bracketPos - 2; |
143 | 0 | nsCharSeparatedTokenizerTemplate<IgnoreWhitespace> tokenizer( |
144 | 0 | Substring(aViewSpec, bracketPos + 1, lengthOfViewSpec), ';'); |
145 | 0 |
|
146 | 0 | if (!tokenizer.hasMoreTokens()) { |
147 | 0 | return false; |
148 | 0 | } |
149 | 0 | viewHandler.CreateSVGView(); |
150 | 0 |
|
151 | 0 | do { |
152 | 0 |
|
153 | 0 | nsAutoString token(tokenizer.nextToken()); |
154 | 0 |
|
155 | 0 | bracketPos = token.FindChar('('); |
156 | 0 | if (bracketPos < 1 || token.Last() != ')') { |
157 | 0 | // invalid SVGViewAttribute syntax |
158 | 0 | return false; |
159 | 0 | } |
160 | 0 | |
161 | 0 | const nsAString ¶ms = |
162 | 0 | Substring(token, bracketPos + 1, token.Length() - bracketPos - 2); |
163 | 0 |
|
164 | 0 | if (!viewHandler.ProcessAttr(token, params)) { |
165 | 0 | return false; |
166 | 0 | } |
167 | 0 | |
168 | 0 | } while (tokenizer.hasMoreTokens()); |
169 | 0 |
|
170 | 0 | viewHandler.SetValid(); |
171 | 0 | return true; |
172 | 0 | } |
173 | | |
174 | | bool |
175 | | SVGFragmentIdentifier::ProcessFragmentIdentifier(nsIDocument* aDocument, |
176 | | const nsAString& aAnchorName) |
177 | 0 | { |
178 | 0 | MOZ_ASSERT(aDocument->GetRootElement()->IsSVGElement(nsGkAtoms::svg), |
179 | 0 | "expecting an SVG root element"); |
180 | 0 |
|
181 | 0 | SVGSVGElement* rootElement = |
182 | 0 | static_cast<SVGSVGElement*>(aDocument->GetRootElement()); |
183 | 0 |
|
184 | 0 | const SVGViewElement* viewElement = GetViewElement(aDocument, aAnchorName); |
185 | 0 |
|
186 | 0 | if (viewElement) { |
187 | 0 | if (!rootElement->mCurrentViewID) { |
188 | 0 | rootElement->mCurrentViewID = new nsString(); |
189 | 0 | } |
190 | 0 | *rootElement->mCurrentViewID = aAnchorName; |
191 | 0 | rootElement->mSVGView = nullptr; |
192 | 0 | rootElement->InvalidateTransformNotifyFrame(); |
193 | 0 | // not an svgView()-style fragment identifier, return false so the caller |
194 | 0 | // continues processing to match any :target pseudo elements |
195 | 0 | return false; |
196 | 0 | } |
197 | 0 |
|
198 | 0 | return ProcessSVGViewSpec(aAnchorName, rootElement); |
199 | 0 | } |
200 | | |
201 | | } // namespace mozilla |