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