2015-05-03 22:32:37 +03:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
2012-05-21 15:12:37 +04:00
|
|
|
/* 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/. */
|
2012-05-17 14:02:41 +04:00
|
|
|
|
|
|
|
#include "SVGFragmentIdentifier.h"
|
2013-08-14 10:56:21 +04:00
|
|
|
|
2013-01-10 03:02:45 +04:00
|
|
|
#include "mozilla/dom/SVGSVGElement.h"
|
2013-01-08 07:22:41 +04:00
|
|
|
#include "mozilla/dom/SVGViewElement.h"
|
2013-08-14 10:56:21 +04:00
|
|
|
#include "nsContentUtils.h" // for nsCharSeparatedTokenizerTemplate
|
2013-04-15 02:56:34 +04:00
|
|
|
#include "nsSVGAnimatedTransformList.h"
|
2013-08-21 23:28:26 +04:00
|
|
|
#include "nsCharSeparatedTokenizer.h"
|
2012-05-17 14:02:41 +04:00
|
|
|
|
2015-12-03 01:36:23 +03:00
|
|
|
namespace mozilla {
|
|
|
|
|
|
|
|
using namespace dom;
|
2012-05-17 14:02:41 +04:00
|
|
|
|
|
|
|
static bool
|
2013-05-05 11:20:25 +04:00
|
|
|
IsMatchingParameter(const nsAString& aString, const nsAString& aParameterName)
|
2012-05-17 14:02:41 +04:00
|
|
|
{
|
2012-05-18 14:41:30 +04:00
|
|
|
// The first two tests ensure aString.Length() > aParameterName.Length()
|
|
|
|
// so it's then safe to do the third test
|
2012-05-17 14:02:41 +04:00
|
|
|
return StringBeginsWith(aString, aParameterName) &&
|
2012-05-18 14:41:30 +04:00
|
|
|
aString.Last() == ')' &&
|
|
|
|
aString.CharAt(aParameterName.Length()) == '(';
|
2012-05-17 14:02:41 +04:00
|
|
|
}
|
|
|
|
|
2012-08-25 21:02:34 +04:00
|
|
|
inline bool
|
2014-01-04 19:02:17 +04:00
|
|
|
IgnoreWhitespace(char16_t aChar)
|
2012-08-25 21:02:34 +04:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-12-03 01:36:23 +03:00
|
|
|
static SVGViewElement*
|
2013-05-05 11:20:25 +04:00
|
|
|
GetViewElement(nsIDocument* aDocument, const nsAString& aId)
|
2012-05-17 14:02:41 +04:00
|
|
|
{
|
2015-12-03 01:36:23 +03:00
|
|
|
Element* element = aDocument->GetElementById(aId);
|
2015-03-03 14:08:59 +03:00
|
|
|
return (element && element->IsSVGElement(nsGkAtoms::view)) ?
|
2015-12-03 01:36:23 +03:00
|
|
|
static_cast<SVGViewElement*>(element) : nullptr;
|
2012-05-17 14:02:41 +04:00
|
|
|
}
|
|
|
|
|
2015-12-14 03:58:01 +03:00
|
|
|
// Handles setting/clearing the root's mSVGView pointer.
|
|
|
|
class MOZ_RAII AutoSVGViewHandler
|
2012-05-17 14:02:41 +04:00
|
|
|
{
|
2015-12-14 03:58:01 +03:00
|
|
|
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;
|
2012-05-17 14:02:41 +04:00
|
|
|
}
|
|
|
|
|
2015-12-14 03:58:01 +03:00
|
|
|
~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();
|
2012-05-17 14:02:41 +04:00
|
|
|
}
|
|
|
|
|
2015-12-14 03:58:01 +03:00
|
|
|
void CreateSVGView() {
|
|
|
|
MOZ_ASSERT(!mSVGView, "CreateSVGView should not be called multiple times");
|
|
|
|
mSVGView = new SVGView();
|
2012-05-17 14:02:41 +04:00
|
|
|
}
|
|
|
|
|
2015-12-14 03:58:01 +03:00
|
|
|
bool ProcessAttr(const nsAString& aToken, const nsAString &aParams) {
|
2012-05-17 14:02:41 +04:00
|
|
|
|
2015-12-14 03:58:01 +03:00
|
|
|
MOZ_ASSERT(mSVGView, "CreateSVGView should have been called");
|
2012-05-17 14:02:41 +04:00
|
|
|
|
2015-12-14 03:58:01 +03:00
|
|
|
// 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.
|
2013-05-05 11:20:25 +04:00
|
|
|
|
2015-12-14 03:58:01 +03:00
|
|
|
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;
|
2013-05-05 11:20:25 +04:00
|
|
|
}
|
|
|
|
|
2015-12-14 03:58:01 +03:00
|
|
|
void SetValid() {
|
|
|
|
mValid = true;
|
2013-05-05 11:20:25 +04:00
|
|
|
}
|
2015-12-14 03:58:01 +03:00
|
|
|
|
|
|
|
private:
|
|
|
|
SVGSVGElement* mRoot;
|
|
|
|
nsAutoPtr<SVGView> mSVGView;
|
|
|
|
bool mValid;
|
|
|
|
bool mWasOverridden;
|
|
|
|
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
|
|
|
};
|
2012-10-22 13:13:24 +04:00
|
|
|
|
2012-05-17 14:02:41 +04:00
|
|
|
bool
|
2013-05-05 11:20:25 +04:00
|
|
|
SVGFragmentIdentifier::ProcessSVGViewSpec(const nsAString& aViewSpec,
|
2015-12-14 03:58:01 +03:00
|
|
|
SVGSVGElement* aRoot)
|
2012-05-17 14:02:41 +04:00
|
|
|
{
|
2015-12-14 03:58:01 +03:00
|
|
|
AutoSVGViewHandler viewHandler(aRoot);
|
|
|
|
|
2012-05-17 14:02:41 +04:00
|
|
|
if (!IsMatchingParameter(aViewSpec, NS_LITERAL_STRING("svgView"))) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Each token is a SVGViewAttribute
|
2012-08-22 19:56:38 +04:00
|
|
|
int32_t bracketPos = aViewSpec.FindChar('(');
|
2012-08-25 21:02:34 +04:00
|
|
|
uint32_t lengthOfViewSpec = aViewSpec.Length() - bracketPos - 2;
|
|
|
|
nsCharSeparatedTokenizerTemplate<IgnoreWhitespace> tokenizer(
|
|
|
|
Substring(aViewSpec, bracketPos + 1, lengthOfViewSpec), ';');
|
2012-05-17 14:02:41 +04:00
|
|
|
|
2012-05-31 14:39:12 +04:00
|
|
|
if (!tokenizer.hasMoreTokens()) {
|
|
|
|
return false;
|
|
|
|
}
|
2015-12-14 03:58:01 +03:00
|
|
|
viewHandler.CreateSVGView();
|
|
|
|
|
2012-05-31 14:39:12 +04:00
|
|
|
do {
|
2012-05-17 14:02:41 +04:00
|
|
|
|
|
|
|
nsAutoString token(tokenizer.nextToken());
|
|
|
|
|
|
|
|
bracketPos = token.FindChar('(');
|
|
|
|
if (bracketPos < 1 || token.Last() != ')') {
|
|
|
|
// invalid SVGViewAttribute syntax
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const nsAString ¶ms =
|
|
|
|
Substring(token, bracketPos + 1, token.Length() - bracketPos - 2);
|
|
|
|
|
2015-12-14 03:58:01 +03:00
|
|
|
if (!viewHandler.ProcessAttr(token, params)) {
|
2012-05-17 14:02:41 +04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-12-14 03:58:01 +03:00
|
|
|
} while (tokenizer.hasMoreTokens());
|
2012-05-17 14:02:41 +04:00
|
|
|
|
2015-12-14 03:58:01 +03:00
|
|
|
viewHandler.SetValid();
|
2012-05-17 14:02:41 +04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2013-05-05 11:20:25 +04:00
|
|
|
SVGFragmentIdentifier::ProcessFragmentIdentifier(nsIDocument* aDocument,
|
|
|
|
const nsAString& aAnchorName)
|
2012-05-17 14:02:41 +04:00
|
|
|
{
|
2015-03-03 14:08:59 +03:00
|
|
|
MOZ_ASSERT(aDocument->GetRootElement()->IsSVGElement(nsGkAtoms::svg),
|
2015-02-10 01:34:50 +03:00
|
|
|
"expecting an SVG root element");
|
2012-05-17 14:02:41 +04:00
|
|
|
|
2015-12-03 01:36:23 +03:00
|
|
|
SVGSVGElement* rootElement =
|
|
|
|
static_cast<SVGSVGElement*>(aDocument->GetRootElement());
|
2012-05-17 14:02:41 +04:00
|
|
|
|
2015-12-03 01:36:23 +03:00
|
|
|
const SVGViewElement* viewElement = GetViewElement(aDocument, aAnchorName);
|
2012-05-17 14:02:41 +04:00
|
|
|
|
|
|
|
if (viewElement) {
|
2012-09-09 15:44:03 +04:00
|
|
|
if (!rootElement->mCurrentViewID) {
|
|
|
|
rootElement->mCurrentViewID = new nsString();
|
2012-05-17 14:02:41 +04:00
|
|
|
}
|
2012-09-09 15:44:03 +04:00
|
|
|
*rootElement->mCurrentViewID = aAnchorName;
|
2015-12-14 03:58:01 +03:00
|
|
|
rootElement->mSVGView = nullptr;
|
2012-09-09 15:44:03 +04:00
|
|
|
rootElement->InvalidateTransformNotifyFrame();
|
2015-03-08 20:34:47 +03:00
|
|
|
// not an svgView()-style fragment identifier, return false so the caller
|
|
|
|
// continues processing to match any :target pseudo elements
|
|
|
|
return false;
|
2012-05-17 14:02:41 +04:00
|
|
|
}
|
|
|
|
|
2015-12-14 03:58:01 +03:00
|
|
|
return ProcessSVGViewSpec(aAnchorName, rootElement);
|
2012-05-17 14:02:41 +04:00
|
|
|
}
|
2015-12-03 01:36:23 +03:00
|
|
|
|
|
|
|
} // namespace mozilla
|