Bug 1936278 - Prevent search mode chiclet from being dismissed when clicking in page...
[gecko.git] / dom / svg / SVGFragmentIdentifier.cpp
blob6652ff9bc18d6b05244d3528695afe7f3d5455e4
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->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() ||
67 NS_FAILED(
68 mSVGView->mViewBox.SetBaseValueString(aParams, mRoot, false))) {
69 return false;
71 } else if (IsMatchingParameter(aToken, u"preserveAspectRatio"_ns)) {
72 if (mSVGView->mPreserveAspectRatio.IsExplicitlySet() ||
73 NS_FAILED(mSVGView->mPreserveAspectRatio.SetBaseValueString(
74 aParams, mRoot, false))) {
75 return false;
77 } else if (IsMatchingParameter(aToken, u"transform"_ns)) {
78 if (mSVGView->mTransforms) {
79 return false;
81 mSVGView->mTransforms = MakeUnique<SVGAnimatedTransformList>();
82 if (NS_FAILED(
83 mSVGView->mTransforms->SetBaseValueString(aParams, mRoot))) {
84 return false;
86 } else if (IsMatchingParameter(aToken, u"zoomAndPan"_ns)) {
87 if (mSVGView->mZoomAndPan.IsExplicitlySet()) {
88 return false;
90 nsAtom* valAtom = NS_GetStaticAtom(aParams);
91 if (!valAtom || !mSVGView->mZoomAndPan.SetBaseValueAtom(valAtom, mRoot)) {
92 return false;
94 } else {
95 return false;
97 return true;
100 void SetValid() { mValid = true; }
102 private:
103 SVGSVGElement* mRoot;
104 UniquePtr<SVGView> mSVGView;
105 bool mValid;
106 bool mWasOverridden;
109 bool SVGFragmentIdentifier::ProcessSVGViewSpec(const nsAString& aViewSpec,
110 SVGSVGElement* aRoot) {
111 AutoSVGViewHandler viewHandler(aRoot);
113 if (!IsMatchingParameter(aViewSpec, u"svgView"_ns)) {
114 return false;
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()) {
124 return false;
126 viewHandler.CreateSVGView();
128 do {
129 nsAutoString token(tokenizer.nextToken());
131 bracketPos = token.FindChar('(');
132 if (bracketPos < 1 || token.Last() != ')') {
133 // invalid SVGViewAttribute syntax
134 return false;
137 const nsAString& params =
138 Substring(token, bracketPos + 1, token.Length() - bracketPos - 2);
140 if (!viewHandler.ProcessAttr(token, params)) {
141 return false;
144 } while (tokenizer.hasMoreTokens());
146 viewHandler.SetValid();
147 return true;
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));
160 if (viewElement) {
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
174 return false;
177 return ProcessSVGViewSpec(aAnchorName, rootElement);
180 } // namespace mozilla