summaryrefslogtreecommitdiffstats
path: root/dom/svg/SVGFragmentIdentifier.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/svg/SVGFragmentIdentifier.cpp')
-rw-r--r--dom/svg/SVGFragmentIdentifier.cpp202
1 files changed, 202 insertions, 0 deletions
diff --git a/dom/svg/SVGFragmentIdentifier.cpp b/dom/svg/SVGFragmentIdentifier.cpp
new file mode 100644
index 000000000..3adad5444
--- /dev/null
+++ b/dom/svg/SVGFragmentIdentifier.cpp
@@ -0,0 +1,202 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SVGFragmentIdentifier.h"
+
+#include "mozilla/dom/SVGSVGElement.h"
+#include "mozilla/dom/SVGViewElement.h"
+#include "nsContentUtils.h" // for nsCharSeparatedTokenizerTemplate
+#include "nsSVGAnimatedTransformList.h"
+#include "nsCharSeparatedTokenizer.h"
+
+namespace mozilla {
+
+using namespace dom;
+
+static bool
+IsMatchingParameter(const nsAString& aString, const nsAString& aParameterName)
+{
+ // The first two tests ensure aString.Length() > aParameterName.Length()
+ // so it's then safe to do the third test
+ return StringBeginsWith(aString, aParameterName) &&
+ aString.Last() == ')' &&
+ aString.CharAt(aParameterName.Length()) == '(';
+}
+
+inline bool
+IgnoreWhitespace(char16_t aChar)
+{
+ return false;
+}
+
+static SVGViewElement*
+GetViewElement(nsIDocument* aDocument, const nsAString& aId)
+{
+ Element* element = aDocument->GetElementById(aId);
+ return (element && element->IsSVGElement(nsGkAtoms::view)) ?
+ static_cast<SVGViewElement*>(element) : nullptr;
+}
+
+// Handles setting/clearing the root's mSVGView pointer.
+class MOZ_RAII AutoSVGViewHandler
+{
+public:
+ explicit AutoSVGViewHandler(SVGSVGElement* aRoot
+ MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+ : mRoot(aRoot), mValid(false) {
+ MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+ mWasOverridden = mRoot->UseCurrentView();
+ mRoot->mSVGView = nullptr;
+ mRoot->mCurrentViewID = nullptr;
+ }
+
+ ~AutoSVGViewHandler() {
+ if (!mWasOverridden && !mValid) {
+ // we weren't overridden before and we aren't
+ // overridden now so nothing has changed.
+ return;
+ }
+ if (mValid) {
+ mRoot->mSVGView = mSVGView;
+ }
+ mRoot->InvalidateTransformNotifyFrame();
+ }
+
+ void CreateSVGView() {
+ MOZ_ASSERT(!mSVGView, "CreateSVGView should not be called multiple times");
+ mSVGView = new SVGView();
+ }
+
+ bool ProcessAttr(const nsAString& aToken, const nsAString &aParams) {
+
+ MOZ_ASSERT(mSVGView, "CreateSVGView should have been called");
+
+ // SVGViewAttributes may occur in any order, but each type may only occur
+ // at most one time in a correctly formed SVGViewSpec.
+ // If we encounter any attribute more than once or get any syntax errors
+ // we're going to return false and cancel any changes.
+
+ if (IsMatchingParameter(aToken, NS_LITERAL_STRING("viewBox"))) {
+ if (mSVGView->mViewBox.IsExplicitlySet() ||
+ NS_FAILED(mSVGView->mViewBox.SetBaseValueString(
+ aParams, mRoot, false))) {
+ return false;
+ }
+ } else if (IsMatchingParameter(aToken, NS_LITERAL_STRING("preserveAspectRatio"))) {
+ if (mSVGView->mPreserveAspectRatio.IsExplicitlySet() ||
+ NS_FAILED(mSVGView->mPreserveAspectRatio.SetBaseValueString(
+ aParams, mRoot, false))) {
+ return false;
+ }
+ } else if (IsMatchingParameter(aToken, NS_LITERAL_STRING("transform"))) {
+ if (mSVGView->mTransforms) {
+ return false;
+ }
+ mSVGView->mTransforms = new nsSVGAnimatedTransformList();
+ if (NS_FAILED(mSVGView->mTransforms->SetBaseValueString(aParams))) {
+ return false;
+ }
+ } else if (IsMatchingParameter(aToken, NS_LITERAL_STRING("zoomAndPan"))) {
+ if (mSVGView->mZoomAndPan.IsExplicitlySet()) {
+ return false;
+ }
+ nsIAtom* valAtom = NS_GetStaticAtom(aParams);
+ if (!valAtom ||
+ NS_FAILED(mSVGView->mZoomAndPan.SetBaseValueAtom(
+ valAtom, mRoot))) {
+ return false;
+ }
+ } else {
+ // We don't support viewTarget currently
+ return false;
+ }
+ return true;
+ }
+
+ void SetValid() {
+ mValid = true;
+ }
+
+private:
+ SVGSVGElement* mRoot;
+ nsAutoPtr<SVGView> mSVGView;
+ bool mValid;
+ bool mWasOverridden;
+ MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+};
+
+bool
+SVGFragmentIdentifier::ProcessSVGViewSpec(const nsAString& aViewSpec,
+ SVGSVGElement* aRoot)
+{
+ AutoSVGViewHandler viewHandler(aRoot);
+
+ if (!IsMatchingParameter(aViewSpec, NS_LITERAL_STRING("svgView"))) {
+ return false;
+ }
+
+ // Each token is a SVGViewAttribute
+ int32_t bracketPos = aViewSpec.FindChar('(');
+ uint32_t lengthOfViewSpec = aViewSpec.Length() - bracketPos - 2;
+ nsCharSeparatedTokenizerTemplate<IgnoreWhitespace> tokenizer(
+ Substring(aViewSpec, bracketPos + 1, lengthOfViewSpec), ';');
+
+ if (!tokenizer.hasMoreTokens()) {
+ return false;
+ }
+ viewHandler.CreateSVGView();
+
+ do {
+
+ nsAutoString token(tokenizer.nextToken());
+
+ bracketPos = token.FindChar('(');
+ if (bracketPos < 1 || token.Last() != ')') {
+ // invalid SVGViewAttribute syntax
+ return false;
+ }
+
+ const nsAString &params =
+ Substring(token, bracketPos + 1, token.Length() - bracketPos - 2);
+
+ if (!viewHandler.ProcessAttr(token, params)) {
+ return false;
+ }
+
+ } while (tokenizer.hasMoreTokens());
+
+ viewHandler.SetValid();
+ return true;
+}
+
+bool
+SVGFragmentIdentifier::ProcessFragmentIdentifier(nsIDocument* aDocument,
+ const nsAString& aAnchorName)
+{
+ MOZ_ASSERT(aDocument->GetRootElement()->IsSVGElement(nsGkAtoms::svg),
+ "expecting an SVG root element");
+
+ SVGSVGElement* rootElement =
+ static_cast<SVGSVGElement*>(aDocument->GetRootElement());
+
+ const SVGViewElement* viewElement = GetViewElement(aDocument, aAnchorName);
+
+ if (viewElement) {
+ if (!rootElement->mCurrentViewID) {
+ rootElement->mCurrentViewID = new nsString();
+ }
+ *rootElement->mCurrentViewID = aAnchorName;
+ rootElement->mSVGView = nullptr;
+ rootElement->InvalidateTransformNotifyFrame();
+ // not an svgView()-style fragment identifier, return false so the caller
+ // continues processing to match any :target pseudo elements
+ return false;
+ }
+
+ return ProcessSVGViewSpec(aAnchorName, rootElement);
+}
+
+} // namespace mozilla