no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / dom / svg / SVGFragmentIdentifier.cpp
blob0932afd8cafda63612a3b674b8c8d408d5535dea
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/. */
7 #include "SVGFragmentIdentifier.h"
9 #include "mozilla/dom/SVGSVGElement.h"
10 #include "mozilla/dom/SVGViewElement.h"
11 #include "mozilla/SVGOuterSVGFrame.h"
12 #include "nsCharSeparatedTokenizer.h"
13 #include "SVGAnimatedTransformList.h"
15 namespace mozilla {
17 using namespace dom;
19 static bool IsMatchingParameter(const nsAString& aString,
20 const nsAString& aParameterName) {
21 // The first two tests ensure aString.Length() > aParameterName.Length()
22 // so it's then safe to do the third test
23 return StringBeginsWith(aString, aParameterName) && aString.Last() == ')' &&
24 aString.CharAt(aParameterName.Length()) == '(';
27 // Handles setting/clearing the root's mSVGView pointer.
28 class MOZ_RAII AutoSVGViewHandler {
29 public:
30 explicit AutoSVGViewHandler(SVGSVGElement* aRoot)
31 : mRoot(aRoot), mValid(false) {
32 mWasOverridden = mRoot->UseCurrentView();
33 mRoot->mSVGView = nullptr;
34 mRoot->mCurrentViewID = nullptr;
37 ~AutoSVGViewHandler() {
38 if (!mWasOverridden && !mValid) {
39 // we weren't overridden before and we aren't
40 // overridden now so nothing has changed.
41 return;
43 if (mValid) {
44 mRoot->mSVGView = std::move(mSVGView);
46 mRoot->InvalidateTransformNotifyFrame();
47 if (nsIFrame* f = mRoot->GetPrimaryFrame()) {
48 if (SVGOuterSVGFrame* osf = do_QueryFrame(f)) {
49 osf->MaybeSendIntrinsicSizeAndRatioToEmbedder();
54 void CreateSVGView() {
55 MOZ_ASSERT(!mSVGView, "CreateSVGView should not be called multiple times");
56 mSVGView = MakeUnique<SVGView>();
59 bool ProcessAttr(const nsAString& aToken, const nsAString& aParams) {
60 MOZ_ASSERT(mSVGView, "CreateSVGView should have been called");
62 // SVGViewAttributes may occur in any order, but each type may only occur
63 // at most one time in a correctly formed SVGViewSpec.
64 // If we encounter any attribute more than once or get any syntax errors
65 // we're going to return false and cancel any changes.
67 if (IsMatchingParameter(aToken, u"viewBox"_ns)) {
68 if (mSVGView->mViewBox.IsExplicitlySet() ||
69 NS_FAILED(
70 mSVGView->mViewBox.SetBaseValueString(aParams, mRoot, false))) {
71 return false;
73 } else if (IsMatchingParameter(aToken, u"preserveAspectRatio"_ns)) {
74 if (mSVGView->mPreserveAspectRatio.IsExplicitlySet() ||
75 NS_FAILED(mSVGView->mPreserveAspectRatio.SetBaseValueString(
76 aParams, mRoot, false))) {
77 return false;
79 } else if (IsMatchingParameter(aToken, u"transform"_ns)) {
80 if (mSVGView->mTransforms) {
81 return false;
83 mSVGView->mTransforms = MakeUnique<SVGAnimatedTransformList>();
84 if (NS_FAILED(
85 mSVGView->mTransforms->SetBaseValueString(aParams, mRoot))) {
86 return false;
88 } else if (IsMatchingParameter(aToken, u"zoomAndPan"_ns)) {
89 if (mSVGView->mZoomAndPan.IsExplicitlySet()) {
90 return false;
92 nsAtom* valAtom = NS_GetStaticAtom(aParams);
93 if (!valAtom || !mSVGView->mZoomAndPan.SetBaseValueAtom(valAtom, mRoot)) {
94 return false;
96 } else {
97 return false;
99 return true;
102 void SetValid() { mValid = true; }
104 private:
105 SVGSVGElement* mRoot;
106 UniquePtr<SVGView> mSVGView;
107 bool mValid;
108 bool mWasOverridden;
111 bool SVGFragmentIdentifier::ProcessSVGViewSpec(const nsAString& aViewSpec,
112 SVGSVGElement* aRoot) {
113 AutoSVGViewHandler viewHandler(aRoot);
115 if (!IsMatchingParameter(aViewSpec, u"svgView"_ns)) {
116 return false;
119 // Each token is a SVGViewAttribute
120 int32_t bracketPos = aViewSpec.FindChar('(');
121 uint32_t lengthOfViewSpec = aViewSpec.Length() - bracketPos - 2;
122 nsCharSeparatedTokenizerTemplate<NS_TokenizerIgnoreNothing> tokenizer(
123 Substring(aViewSpec, bracketPos + 1, lengthOfViewSpec), ';');
125 if (!tokenizer.hasMoreTokens()) {
126 return false;
128 viewHandler.CreateSVGView();
130 do {
131 nsAutoString token(tokenizer.nextToken());
133 bracketPos = token.FindChar('(');
134 if (bracketPos < 1 || token.Last() != ')') {
135 // invalid SVGViewAttribute syntax
136 return false;
139 const nsAString& params =
140 Substring(token, bracketPos + 1, token.Length() - bracketPos - 2);
142 if (!viewHandler.ProcessAttr(token, params)) {
143 return false;
146 } while (tokenizer.hasMoreTokens());
148 viewHandler.SetValid();
149 return true;
152 bool SVGFragmentIdentifier::ProcessFragmentIdentifier(
153 Document* aDocument, const nsAString& aAnchorName) {
154 MOZ_ASSERT(aDocument->GetRootElement()->IsSVGElement(nsGkAtoms::svg),
155 "expecting an SVG root element");
157 auto* rootElement = SVGSVGElement::FromNode(aDocument->GetRootElement());
159 const auto* viewElement =
160 SVGViewElement::FromNodeOrNull(aDocument->GetElementById(aAnchorName));
162 if (viewElement) {
163 if (!rootElement->mCurrentViewID) {
164 rootElement->mCurrentViewID = MakeUnique<nsString>();
166 *rootElement->mCurrentViewID = aAnchorName;
167 rootElement->mSVGView = nullptr;
168 rootElement->InvalidateTransformNotifyFrame();
169 if (nsIFrame* f = rootElement->GetPrimaryFrame()) {
170 if (SVGOuterSVGFrame* osf = do_QueryFrame(f)) {
171 osf->MaybeSendIntrinsicSizeAndRatioToEmbedder();
174 // not an svgView()-style fragment identifier, return false so the caller
175 // continues processing to match any :target pseudo elements
176 return false;
179 return ProcessSVGViewSpec(aAnchorName, rootElement);
182 } // namespace mozilla