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"
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
{
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.
44 mRoot
->mSVGView
= std::move(mSVGView
);
46 mRoot
->DidChangeSVGView();
47 if (SVGOuterSVGFrame
* osf
= do_QueryFrame(mRoot
->GetPrimaryFrame())) {
48 osf
->MaybeSendIntrinsicSizeAndRatioToEmbedder();
52 void CreateSVGView() {
53 MOZ_ASSERT(!mSVGView
, "CreateSVGView should not be called multiple times");
54 mSVGView
= MakeUnique
<SVGView
>();
57 bool ProcessAttr(const nsAString
& aToken
, const nsAString
& aParams
) {
58 MOZ_ASSERT(mSVGView
, "CreateSVGView should have been called");
60 // SVGViewAttributes may occur in any order, but each type may only occur
61 // at most one time in a correctly formed SVGViewSpec.
62 // If we encounter any attribute more than once or get any syntax errors
63 // we're going to return false and cancel any changes.
65 if (IsMatchingParameter(aToken
, u
"viewBox"_ns
)) {
66 if (mSVGView
->mViewBox
.IsExplicitlySet() ||
68 mSVGView
->mViewBox
.SetBaseValueString(aParams
, mRoot
, false))) {
71 } else if (IsMatchingParameter(aToken
, u
"preserveAspectRatio"_ns
)) {
72 if (mSVGView
->mPreserveAspectRatio
.IsExplicitlySet() ||
73 NS_FAILED(mSVGView
->mPreserveAspectRatio
.SetBaseValueString(
74 aParams
, mRoot
, false))) {
77 } else if (IsMatchingParameter(aToken
, u
"transform"_ns
)) {
78 if (mSVGView
->mTransforms
) {
81 mSVGView
->mTransforms
= MakeUnique
<SVGAnimatedTransformList
>();
83 mSVGView
->mTransforms
->SetBaseValueString(aParams
, mRoot
))) {
86 } else if (IsMatchingParameter(aToken
, u
"zoomAndPan"_ns
)) {
87 if (mSVGView
->mZoomAndPan
.IsExplicitlySet()) {
90 nsAtom
* valAtom
= NS_GetStaticAtom(aParams
);
91 if (!valAtom
|| !mSVGView
->mZoomAndPan
.SetBaseValueAtom(valAtom
, mRoot
)) {
100 void SetValid() { mValid
= true; }
103 SVGSVGElement
* mRoot
;
104 UniquePtr
<SVGView
> mSVGView
;
109 bool SVGFragmentIdentifier::ProcessSVGViewSpec(const nsAString
& aViewSpec
,
110 SVGSVGElement
* aRoot
) {
111 AutoSVGViewHandler
viewHandler(aRoot
);
113 if (!IsMatchingParameter(aViewSpec
, u
"svgView"_ns
)) {
117 // Each token is a SVGViewAttribute
118 int32_t bracketPos
= aViewSpec
.FindChar('(');
119 uint32_t lengthOfViewSpec
= aViewSpec
.Length() - bracketPos
- 2;
120 nsCharSeparatedTokenizerTemplate
<NS_TokenizerIgnoreNothing
> tokenizer(
121 Substring(aViewSpec
, bracketPos
+ 1, lengthOfViewSpec
), ';');
123 if (!tokenizer
.hasMoreTokens()) {
126 viewHandler
.CreateSVGView();
129 nsAutoString
token(tokenizer
.nextToken());
131 bracketPos
= token
.FindChar('(');
132 if (bracketPos
< 1 || token
.Last() != ')') {
133 // invalid SVGViewAttribute syntax
137 const nsAString
& params
=
138 Substring(token
, bracketPos
+ 1, token
.Length() - bracketPos
- 2);
140 if (!viewHandler
.ProcessAttr(token
, params
)) {
144 } while (tokenizer
.hasMoreTokens());
146 viewHandler
.SetValid();
150 bool SVGFragmentIdentifier::ProcessFragmentIdentifier(
151 Document
* aDocument
, const nsAString
& aAnchorName
) {
152 MOZ_ASSERT(aDocument
->GetRootElement()->IsSVGElement(nsGkAtoms::svg
),
153 "expecting an SVG root element");
155 auto* rootElement
= SVGSVGElement::FromNode(aDocument
->GetRootElement());
157 const auto* viewElement
=
158 SVGViewElement::FromNodeOrNull(aDocument
->GetElementById(aAnchorName
));
161 if (!rootElement
->mCurrentViewID
) {
162 rootElement
->mCurrentViewID
= MakeUnique
<nsString
>();
164 *rootElement
->mCurrentViewID
= aAnchorName
;
165 rootElement
->mSVGView
= nullptr;
166 rootElement
->InvalidateTransformNotifyFrame();
167 if (nsIFrame
* f
= rootElement
->GetPrimaryFrame()) {
168 if (SVGOuterSVGFrame
* osf
= do_QueryFrame(f
)) {
169 osf
->MaybeSendIntrinsicSizeAndRatioToEmbedder();
172 // not an svgView()-style fragment identifier, return false so the caller
173 // continues processing to match any :target pseudo elements
177 return ProcessSVGViewSpec(aAnchorName
, rootElement
);
180 } // namespace mozilla