Coverage Report

Created: 2018-09-25 14:53

/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 &params =
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