From df2771aab84e1829c930271375bb04bbe6229bf9 Mon Sep 17 00:00:00 2001 From: Robert Longson Date: Mon, 14 Dec 2015 00:58:01 +0000 Subject: [PATCH] Bug 1145195 part 2 - SVGFragmentIdentifier::ProcessSVGViewSpec() shouldn't actually let #svgView() affect attribute values r=dholbert --- dom/svg/SVGFragmentIdentifier.cpp | 273 ++++++++++-------------------- dom/svg/SVGFragmentIdentifier.h | 11 -- dom/svg/SVGSVGElement.cpp | 147 ++++------------ dom/svg/SVGSVGElement.h | 35 ++-- dom/svg/test/test_fragments.html | 103 ++++++----- 5 files changed, 215 insertions(+), 354 deletions(-) diff --git a/dom/svg/SVGFragmentIdentifier.cpp b/dom/svg/SVGFragmentIdentifier.cpp index 0e20ee68ae9..3adad54447c 100644 --- a/dom/svg/SVGFragmentIdentifier.cpp +++ b/dom/svg/SVGFragmentIdentifier.cpp @@ -40,109 +40,104 @@ GetViewElement(nsIDocument* aDocument, const nsAString& aId) static_cast(element) : nullptr; } -void -SVGFragmentIdentifier::SaveOldPreserveAspectRatio(SVGSVGElement* root) +// Handles setting/clearing the root's mSVGView pointer. +class MOZ_RAII AutoSVGViewHandler { - if (root->mPreserveAspectRatio.IsExplicitlySet()) { - root->SetPreserveAspectRatioProperty(root->mPreserveAspectRatio.GetBaseValue()); +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; } -} -void -SVGFragmentIdentifier::RestoreOldPreserveAspectRatio(SVGSVGElement* root) -{ - const SVGPreserveAspectRatio* oldPARPtr = root->GetPreserveAspectRatioProperty(); - if (oldPARPtr) { - root->mPreserveAspectRatio.SetBaseValue(*oldPARPtr, root); - } else if (root->mPreserveAspectRatio.IsExplicitlySet()) { - ErrorResult error; - root->RemoveAttribute(NS_LITERAL_STRING("preserveAspectRatio"), error); - } -} - -void -SVGFragmentIdentifier::SaveOldViewBox(SVGSVGElement* root) -{ - if (root->mViewBox.IsExplicitlySet()) { - root->SetViewBoxProperty(root->mViewBox.GetBaseValue()); - } -} - -void -SVGFragmentIdentifier::RestoreOldViewBox(SVGSVGElement* root) -{ - const nsSVGViewBoxRect* oldViewBoxPtr = root->GetViewBoxProperty(); - if (oldViewBoxPtr) { - root->mViewBox.SetBaseValue(*oldViewBoxPtr, root); - } else if (root->mViewBox.IsExplicitlySet()) { - ErrorResult error; - root->RemoveAttribute(NS_LITERAL_STRING("viewBox"), error); - } -} - -void -SVGFragmentIdentifier::SaveOldZoomAndPan(SVGSVGElement* root) -{ - if (root->mEnumAttributes[SVGSVGElement::ZOOMANDPAN].IsExplicitlySet()) { - root->SetZoomAndPanProperty(root->mEnumAttributes[SVGSVGElement::ZOOMANDPAN].GetBaseValue()); - } -} - -void -SVGFragmentIdentifier::RestoreOldZoomAndPan(SVGSVGElement* root) -{ - uint16_t oldZoomAndPan = root->GetZoomAndPanProperty(); - if (oldZoomAndPan != SVG_ZOOMANDPAN_UNKNOWN) { - root->mEnumAttributes[SVGSVGElement::ZOOMANDPAN].SetBaseValue(oldZoomAndPan, root); - } else if (root->mEnumAttributes[SVGSVGElement::ZOOMANDPAN].IsExplicitlySet()) { - ErrorResult error; - root->RemoveAttribute(NS_LITERAL_STRING("zoomAndPan"), error); - } -} - -void -SVGFragmentIdentifier::SaveOldTransform(SVGSVGElement* root) -{ - nsSVGAnimatedTransformList* transformList = root->GetAnimatedTransformList(); - - if (transformList && transformList->IsExplicitlySet()) { - root->SetTransformProperty(transformList->GetBaseValue()); - } -} - -void -SVGFragmentIdentifier::RestoreOldTransform(SVGSVGElement* root) -{ - const SVGTransformList* oldTransformPtr = root->GetTransformProperty(); - if (oldTransformPtr) { - root->GetAnimatedTransformList(nsSVGElement::DO_ALLOCATE)->SetBaseValue(*oldTransformPtr); - } else { - nsSVGAnimatedTransformList* transformList = root->GetAnimatedTransformList(); - if (transformList && transformList->IsExplicitlySet()) { - ErrorResult error; - root->RemoveAttribute(NS_LITERAL_STRING("transform"), error); + ~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 mSVGView; + bool mValid; + bool mWasOverridden; + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER +}; bool SVGFragmentIdentifier::ProcessSVGViewSpec(const nsAString& aViewSpec, - SVGSVGElement* root) + SVGSVGElement* aRoot) { + AutoSVGViewHandler viewHandler(aRoot); + if (!IsMatchingParameter(aViewSpec, NS_LITERAL_STRING("svgView"))) { return false; } - // 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. - - bool viewBoxFound = false; - bool preserveAspectRatioFound = false; - bool transformFound = false; - bool zoomAndPanFound = false; - // Each token is a SVGViewAttribute int32_t bracketPos = aViewSpec.FindChar('('); uint32_t lengthOfViewSpec = aViewSpec.Length() - bracketPos - 2; @@ -152,6 +147,8 @@ SVGFragmentIdentifier::ProcessSVGViewSpec(const nsAString& aViewSpec, if (!tokenizer.hasMoreTokens()) { return false; } + viewHandler.CreateSVGView(); + do { nsAutoString token(tokenizer.nextToken()); @@ -165,75 +162,13 @@ SVGFragmentIdentifier::ProcessSVGViewSpec(const nsAString& aViewSpec, const nsAString ¶ms = Substring(token, bracketPos + 1, token.Length() - bracketPos - 2); - if (IsMatchingParameter(token, NS_LITERAL_STRING("viewBox"))) { - if (viewBoxFound || - NS_FAILED(root->mViewBox.SetBaseValueString( - params, root, true))) { - return false; - } - viewBoxFound = true; - } else if (IsMatchingParameter(token, NS_LITERAL_STRING("preserveAspectRatio"))) { - if (preserveAspectRatioFound || - NS_FAILED(root->mPreserveAspectRatio.SetBaseValueString( - params, root, true))) { - return false; - } - preserveAspectRatioFound = true; - } else if (IsMatchingParameter(token, NS_LITERAL_STRING("transform"))) { - if (transformFound || - NS_FAILED(root->GetAnimatedTransformList(nsSVGElement::DO_ALLOCATE)-> - SetBaseValueString(params))) { - return false; - } - transformFound = true; - } else if (IsMatchingParameter(token, NS_LITERAL_STRING("zoomAndPan"))) { - if (zoomAndPanFound) { - return false; - } - nsIAtom* valAtom = NS_GetStaticAtom(params); - if (!valAtom) { - return false; - } - const nsSVGEnumMapping* mapping = SVGSVGElement::sZoomAndPanMap; - while (mapping->mKey) { - if (valAtom == *(mapping->mKey)) { - // If we've got a valid zoomAndPan value, then set it on our root element. - if (NS_FAILED(root->mEnumAttributes[SVGSVGElement::ZOOMANDPAN].SetBaseValue( - mapping->mVal, root))) { - return false; - } - break; - } - mapping++; - } - if (!mapping->mKey) { - // Unrecognised zoomAndPan value - return false; - } - zoomAndPanFound = true; - } else { - // We don't support viewTarget currently + if (!viewHandler.ProcessAttr(token, params)) { return false; } + } while (tokenizer.hasMoreTokens()); - if (root->mUseCurrentView) { - // A previous SVGViewSpec may have overridden some attributes. - // If they are no longer overridden we need to restore the old values. - if (!transformFound) { - RestoreOldTransform(root); - } - if (!viewBoxFound) { - RestoreOldViewBox(root); - } - if (!preserveAspectRatioFound) { - RestoreOldPreserveAspectRatio(root); - } - if (!zoomAndPanFound) { - RestoreOldZoomAndPan(root); - } - } - + viewHandler.SetValid(); return true; } @@ -247,12 +182,6 @@ SVGFragmentIdentifier::ProcessFragmentIdentifier(nsIDocument* aDocument, SVGSVGElement* rootElement = static_cast(aDocument->GetRootElement()); - if (!rootElement->mUseCurrentView) { - SaveOldViewBox(rootElement); - SaveOldPreserveAspectRatio(rootElement); - SaveOldZoomAndPan(rootElement); - } - const SVGViewElement* viewElement = GetViewElement(aDocument, aAnchorName); if (viewElement) { @@ -260,32 +189,14 @@ SVGFragmentIdentifier::ProcessFragmentIdentifier(nsIDocument* aDocument, rootElement->mCurrentViewID = new nsString(); } *rootElement->mCurrentViewID = aAnchorName; - rootElement->mUseCurrentView = true; + 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; } - bool wasOverridden = !!rootElement->mCurrentViewID; - rootElement->mCurrentViewID = nullptr; - - rootElement->mUseCurrentView = ProcessSVGViewSpec(aAnchorName, rootElement); - if (rootElement->mUseCurrentView) { - return true; - } - RestoreOldViewBox(rootElement); - rootElement->ClearViewBoxProperty(); - RestoreOldPreserveAspectRatio(rootElement); - rootElement->ClearPreserveAspectRatioProperty(); - RestoreOldZoomAndPan(rootElement); - rootElement->ClearZoomAndPanProperty(); - RestoreOldTransform(rootElement); - rootElement->ClearTransformProperty(); - if (wasOverridden) { - rootElement->InvalidateTransformNotifyFrame(); - } - return false; + return ProcessSVGViewSpec(aAnchorName, rootElement); } } // namespace mozilla diff --git a/dom/svg/SVGFragmentIdentifier.h b/dom/svg/SVGFragmentIdentifier.h index 2591af698bd..0fb6fb1a789 100644 --- a/dom/svg/SVGFragmentIdentifier.h +++ b/dom/svg/SVGFragmentIdentifier.h @@ -42,17 +42,6 @@ private: * @return true if there is a valid ViewSpec */ static bool ProcessSVGViewSpec(const nsAString &aViewSpec, dom::SVGSVGElement *root); - - // Save and restore things we override in case we want to go back e.g. the - // user presses the back button - static void SaveOldPreserveAspectRatio(dom::SVGSVGElement *root); - static void RestoreOldPreserveAspectRatio(dom::SVGSVGElement *root); - static void SaveOldViewBox(dom::SVGSVGElement *root); - static void RestoreOldViewBox(dom::SVGSVGElement *root); - static void SaveOldZoomAndPan(dom::SVGSVGElement *root); - static void RestoreOldZoomAndPan(dom::SVGSVGElement *root); - static void SaveOldTransform(dom::SVGSVGElement *root); - static void RestoreOldTransform(dom::SVGSVGElement *root); }; } // namespace mozilla diff --git a/dom/svg/SVGSVGElement.cpp b/dom/svg/SVGSVGElement.cpp index 9b72bc5760c..0bf009865e9 100644 --- a/dom/svg/SVGSVGElement.cpp +++ b/dom/svg/SVGSVGElement.cpp @@ -111,6 +111,14 @@ DOMSVGTranslatePoint::MatrixTransform(SVGMatrix& matrix) return point.forget(); } +SVGView::SVGView() +{ + mZoomAndPan.Init(SVGSVGElement::ZOOMANDPAN, + SVG_ZOOMANDPAN_MAGNIFY); + mViewBox.Init(); + mPreserveAspectRatio.Init(); +} + nsSVGElement::LengthInfo SVGSVGElement::sLengthInfo[4] = { { &nsGkAtoms::x, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::X }, @@ -176,8 +184,7 @@ SVGSVGElement::SVGSVGElement(already_AddRefed& aNodeInfo aFromParser == FROM_PARSER_XSLT), mImageNeedsTransformInvalidation(false), mIsPaintingSVGImageElement(false), - mHasChildrenOnlyTransform(false), - mUseCurrentView(false) + mHasChildrenOnlyTransform(false) { } @@ -257,7 +264,7 @@ SVGSVGElement::ScreenPixelToMillimeterY() bool SVGSVGElement::UseCurrentView() { - return mUseCurrentView; + return mSVGView || mCurrentViewID; } float @@ -452,12 +459,6 @@ SVGSVGElement::PreserveAspectRatio() uint16_t SVGSVGElement::ZoomAndPan() { - SVGViewElement* viewElement = GetCurrentViewElement(); - if (viewElement && viewElement->mEnumAttributes[ - SVGViewElement::ZOOMANDPAN].IsExplicitlySet()) { - return viewElement->mEnumAttributes[ - SVGViewElement::ZOOMANDPAN].GetAnimValue(); - } return mEnumAttributes[ZOOMANDPAN].GetAnimValue(); } @@ -841,6 +842,9 @@ SVGSVGElement::GetViewBoxWithSynthesis( if (viewElement && viewElement->mViewBox.HasRect()) { return viewElement->mViewBox.GetAnimValue(); } + if (mSVGView && mSVGView->mViewBox.HasRect()) { + return mSVGView->mViewBox.GetAnimValue(); + } if (mViewBox.HasRect()) { return mViewBox.GetAnimValue(); } @@ -878,6 +882,7 @@ SVGSVGElement::GetPreserveAspectRatioWithOverride() const // We're just holding onto the viewElement that HasViewBoxRect() would look up, // so that we don't have to look it up again later. if (!((viewElement && viewElement->mViewBox.HasRect()) || + (mSVGView && mSVGView->mViewBox.HasRect()) || mViewBox.HasRect()) && ShouldSynthesizeViewBox()) { // If we're synthesizing a viewBox, use preserveAspectRatio="none"; @@ -887,6 +892,9 @@ SVGSVGElement::GetPreserveAspectRatioWithOverride() const if (viewElement && viewElement->mPreserveAspectRatio.IsExplicitlySet()) { return viewElement->mPreserveAspectRatio.GetAnimValue(); } + if (mSVGView && mSVGView->mPreserveAspectRatio.IsExplicitlySet()) { + return mSVGView->mPreserveAspectRatio.GetAnimValue(); + } return mPreserveAspectRatio.GetAnimValue(); } @@ -904,6 +912,8 @@ SVGSVGElement::GetLength(uint8_t aCtxType) // The logic here should match HasViewBoxRect(). if (viewElement && viewElement->mViewBox.HasRect()) { viewbox = &viewElement->mViewBox.GetAnimValue(); + } else if (mSVGView && mSVGView->mViewBox.HasRect()) { + viewbox = &mSVGView->mViewBox.GetAnimValue(); } else if (mViewBox.HasRect()) { viewbox = &mViewBox.GetAnimValue(); } @@ -946,9 +956,12 @@ SVGSVGElement::GetLength(uint8_t aCtxType) SVGSVGElement::PrependLocalTransformsTo( const gfxMatrix &aMatrix, SVGTransformTypes aWhich) const { - // 'transform' attribute: + // 'transform' attribute (or an override from a fragment identifier): gfxMatrix fromUserSpace = - SVGSVGElementBase::PrependLocalTransformsTo(aMatrix, aWhich); + SVGContentUtils::PrependLocalTransformsTo( + aMatrix, aWhich, mAnimateMotionTransform, + mSVGView && mSVGView->mTransforms ? mSVGView->mTransforms : mTransforms); + if (aWhich == eUserSpaceToParent) { return fromUserSpace; } @@ -975,6 +988,15 @@ SVGSVGElement::PrependLocalTransformsTo( return ThebesMatrix(GetViewBoxTransform()) * fromUserSpace; } +nsSVGAnimatedTransformList* +SVGSVGElement::GetAnimatedTransformList(uint32_t aFlags) +{ + if (!(aFlags & DO_ALLOCATE) && mSVGView && mSVGView->mTransforms) { + return mSVGView->mTransforms; + } + return SVGSVGElementBase::GetAnimatedTransformList(aFlags); +} + /* virtual */ bool SVGSVGElement::HasValidDimensions() const { @@ -1015,7 +1037,8 @@ bool SVGSVGElement::HasViewBoxRect() const { SVGViewElement* viewElement = GetCurrentViewElement(); - if (viewElement && viewElement->mViewBox.HasRect()) { + if ((viewElement && viewElement->mViewBox.HasRect()) || + (mSVGView && mSVGView->mViewBox.HasRect())) { return true; } return mViewBox.HasRect(); @@ -1136,106 +1159,6 @@ SVGSVGElement::FlushImageTransformInvalidation() } } -bool -SVGSVGElement::SetViewBoxProperty(const nsSVGViewBoxRect& aViewBox) -{ - nsSVGViewBoxRect* pViewBoxOverridePtr = new nsSVGViewBoxRect(aViewBox); - nsresult rv = SetProperty(nsGkAtoms::viewBox, - pViewBoxOverridePtr, - nsINode::DeleteProperty, - true); - MOZ_ASSERT(rv != NS_PROPTABLE_PROP_OVERWRITTEN, - "Setting override value when it's already set...?"); - - if (MOZ_UNLIKELY(NS_FAILED(rv))) { - // property-insertion failed (e.g. OOM in property-table code) - delete pViewBoxOverridePtr; - return false; - } - return true; -} - -const nsSVGViewBoxRect* -SVGSVGElement::GetViewBoxProperty() const -{ - void* valPtr = GetProperty(nsGkAtoms::viewBox); - if (valPtr) { - return static_cast(valPtr); - } - return nullptr; -} - -bool -SVGSVGElement::ClearViewBoxProperty() -{ - void* valPtr = UnsetProperty(nsGkAtoms::viewBox); - delete static_cast(valPtr); - return valPtr; -} - -bool -SVGSVGElement::SetZoomAndPanProperty(uint16_t aValue) -{ - nsresult rv = SetProperty(nsGkAtoms::zoomAndPan, - reinterpret_cast(aValue), - nullptr, true); - MOZ_ASSERT(rv != NS_PROPTABLE_PROP_OVERWRITTEN, - "Setting override value when it's already set...?"); - - return NS_SUCCEEDED(rv); -} - -uint16_t -SVGSVGElement::GetZoomAndPanProperty() const -{ - void* valPtr = GetProperty(nsGkAtoms::zoomAndPan); - if (valPtr) { - return reinterpret_cast(valPtr); - } - return SVG_ZOOMANDPAN_UNKNOWN; -} - -bool -SVGSVGElement::ClearZoomAndPanProperty() -{ - return UnsetProperty(nsGkAtoms::zoomAndPan); -} - -bool -SVGSVGElement::SetTransformProperty(const SVGTransformList& aTransform) -{ - SVGTransformList* pTransformOverridePtr = new SVGTransformList(aTransform); - nsresult rv = SetProperty(nsGkAtoms::transform, - pTransformOverridePtr, - nsINode::DeleteProperty, - true); - MOZ_ASSERT(rv != NS_PROPTABLE_PROP_OVERWRITTEN, - "Setting override value when it's already set...?"); - - if (MOZ_UNLIKELY(NS_FAILED(rv))) { - // property-insertion failed (e.g. OOM in property-table code) - delete pTransformOverridePtr; - return false; - } - return true; -} - -const SVGTransformList* -SVGSVGElement::GetTransformProperty() const -{ - void* valPtr = GetProperty(nsGkAtoms::transform); - if (valPtr) { - return static_cast(valPtr); - } - return nullptr; -} - -bool -SVGSVGElement::ClearTransformProperty() -{ - return UnsetProperty(nsGkAtoms::transform); -} - int32_t SVGSVGElement::GetIntrinsicWidth() { diff --git a/dom/svg/SVGSVGElement.h b/dom/svg/SVGSVGElement.h index 146097c4434..0a011ffd611 100644 --- a/dom/svg/SVGSVGElement.h +++ b/dom/svg/SVGSVGElement.h @@ -33,6 +33,7 @@ class DOMSVGLength; class DOMSVGNumber; class EventChainPreVisitor; class SVGFragmentIdentifier; +class AutoSVGViewHandler; namespace dom { class SVGAngle; @@ -85,13 +86,29 @@ public: float height; }; +// Stores svgView arguments of SVG fragment identifiers. +class SVGView { + friend class mozilla::AutoSVGViewHandler; + friend class mozilla::dom::SVGSVGElement; +public: + SVGView(); + +private: + nsSVGEnum mZoomAndPan; + nsSVGViewBox mViewBox; + SVGAnimatedPreserveAspectRatio mPreserveAspectRatio; + nsAutoPtr mTransforms; +}; + typedef SVGGraphicsElement SVGSVGElementBase; class SVGSVGElement final : public SVGSVGElementBase { friend class ::nsSVGOuterSVGFrame; friend class ::nsSVGInnerSVGFrame; + friend class mozilla::dom::SVGView; friend class mozilla::SVGFragmentIdentifier; + friend class mozilla::AutoSVGViewHandler; friend class mozilla::AutoSVGRenderingState; SVGSVGElement(already_AddRefed& aNodeInfo, @@ -141,6 +158,8 @@ public: virtual gfxMatrix PrependLocalTransformsTo( const gfxMatrix &aMatrix, SVGTransformTypes aWhich = eAllTransforms) const override; + virtual nsSVGAnimatedTransformList* + GetAnimatedTransformList(uint32_t aFlags = 0) override; virtual bool HasValidDimensions() const override; // SVGSVGElement methods: @@ -286,19 +305,11 @@ private: void SetImageOverridePreserveAspectRatio(const SVGPreserveAspectRatio& aPAR); void ClearImageOverridePreserveAspectRatio(); - // Set/Clear properties to hold old or override versions of attributes + // Set/Clear properties to hold old version of preserveAspectRatio + // when it's being overridden by an element that we are inside of. bool SetPreserveAspectRatioProperty(const SVGPreserveAspectRatio& aPAR); const SVGPreserveAspectRatio* GetPreserveAspectRatioProperty() const; bool ClearPreserveAspectRatioProperty(); - bool SetViewBoxProperty(const nsSVGViewBoxRect& aViewBox); - const nsSVGViewBoxRect* GetViewBoxProperty() const; - bool ClearViewBoxProperty(); - bool SetZoomAndPanProperty(uint16_t aValue); - uint16_t GetZoomAndPanProperty() const; - bool ClearZoomAndPanProperty(); - bool SetTransformProperty(const SVGTransformList& aValue); - const SVGTransformList* GetTransformProperty() const; - bool ClearTransformProperty(); bool IsRoot() const { NS_ASSERTION((IsInDoc() && !GetParent()) == @@ -369,7 +380,10 @@ private: nsSVGViewBox mViewBox; SVGAnimatedPreserveAspectRatio mPreserveAspectRatio; + // mCurrentViewID and mSVGView are mutually exclusive; we can have + // at most one non-null. nsAutoPtr mCurrentViewID; + nsAutoPtr mSVGView; // The size of the rectangular SVG viewport into which we render. This is // not (necessarily) the same as the content area. See: @@ -401,7 +415,6 @@ private: bool mImageNeedsTransformInvalidation; bool mIsPaintingSVGImageElement; bool mHasChildrenOnlyTransform; - bool mUseCurrentView; }; } // namespace dom diff --git a/dom/svg/test/test_fragments.html b/dom/svg/test/test_fragments.html index 970e7a0b95e..eba83332dee 100644 --- a/dom/svg/test/test_fragments.html +++ b/dom/svg/test/test_fragments.html @@ -24,67 +24,92 @@ function Test(svgFragmentIdentifier, valid, viewBoxString, { this.svgFragmentIdentifier = svgFragmentIdentifier; this.valid = valid; - this.viewBoxString = viewBoxString; - this.preserveAspectRatioString = preserveAspectRatioString; - this.zoomAndPanString = zoomAndPanString; } function runTests() { var svg = $("svg"); var doc = svg.contentWindow.document; - + var rootElement = doc.rootElement; + var tests = [ - new Test("unknown", false, null, null, null), - new Test("svgView(viewBox(0,0,200,200))", true, "0 0 200 200", null, null), - new Test("svgView(preserveAspectRatio(xMaxYMin slice))", true, null, "xMaxYMin slice", null), - new Test("svgView(viewBox(1,2,3,4);preserveAspectRatio(xMinYMax))", true, "1 2 3 4", "xMinYMax meet", null), - new Test("svgView(viewBox(none))", true, "none", null, null), - new Test("svgView(zoomAndPan(disable))", true, null, null, "disable"), - new Test("svgView(transform(translate(-10,-20) scale(2) rotate(45) translate(5,10)))", true, null, null, null), + new Test("unknown", false), + new Test("svgView(viewBox(0,0,200,200))", true), + new Test("svgView(preserveAspectRatio(xMaxYMin slice))", true), + new Test("svgView(viewBox(1,2,3,4);preserveAspectRatio(xMinYMax))", true), + new Test("svgView(viewBox(none))", true), + new Test("svgView(zoomAndPan(disable))", true), + new Test("svgView(transform(translate(-10,-20) scale(2) rotate(45) translate(5,10)))", true), // No duplicates allowed - new Test("svgView(zoomAndPan(disable);zoomAndPan(disable))", false, null, null, null), - new Test("svgView(viewBox(0,0,200,200);viewBox(0,0,200,200))", false, null, null, null), - new Test("svgView(preserveAspectRatio(xMaxYMin);preserveAspectRatio(xMaxYMin))", false, null, null, null), - new Test("svgView(transform(translate(0,200));transform(translate(0,200)))", false, null, null, null), + new Test("svgView(zoomAndPan(disable);zoomAndPan(disable))", false), + new Test("svgView(viewBox(0,0,200,200);viewBox(0,0,200,200))", false), + new Test("svgView(preserveAspectRatio(xMaxYMin);preserveAspectRatio(xMaxYMin))", false), + new Test("svgView(transform(translate(0,200));transform(translate(0,200)))", false), // No invalid values allowed - new Test("svgView(viewBox(bad)", false, null, null, null), - new Test("svgView(preserveAspectRatio(bad))", false, null, null, null), - new Test("svgView(zoomAndPan(bad))", false, null, null, null), - new Test("svgView(transform(bad))", false, null, null, null), - new Test("svgView", false, null, null, null), - new Test("svgView(", false, null, null, null), - new Test("svgView()", false, null, null, null), + new Test("svgView(viewBox(bad)", false), + new Test("svgView(preserveAspectRatio(bad))", false), + new Test("svgView(zoomAndPan(bad))", false), + new Test("svgView(transform(bad))", false), + new Test("svgView", false), + new Test("svgView(", false), + new Test("svgView()", false), // Be sure we verify that there's a closing paren for svgView() // (and not too many closing parens) - new Test("svgView(zoomAndPan(disable)", false, null, null, null), - new Test("svgView(zoomAndPan(disable) ", false, null, null, null), - new Test("svgView(zoomAndPan(disable)]", false, null, null, null), - new Test("svgView(zoomAndPan(disable)))", false, null, null, null) + new Test("svgView(zoomAndPan(disable)", false), + new Test("svgView(zoomAndPan(disable) ", false), + new Test("svgView(zoomAndPan(disable)]", false), + new Test("svgView(zoomAndPan(disable)))", false) ]; var src = svg.getAttribute("src"); - for (var i = 0; i < tests.length; i++) { - var test = tests[i]; - svg.setAttribute("src", src + "#" + test.svgFragmentIdentifier); - is(doc.rootElement.useCurrentView, test.valid, - "Expected " + test.svgFragmentIdentifier + " to be " + - (test.valid ? "valid" : "invalid")); - is(doc.rootElement.getAttribute("viewBox"), - test.viewBoxString, "unexpected viewBox"); + is(false, rootElement.hasAttribute("viewBox"), + "expecting to start without a viewBox set"); + is(false, rootElement.hasAttribute("preserveAspectRatio"), + "expecting to start without preserveAspectRatio set"); + is(false, rootElement.hasAttribute("zoomAndPan"), + "expecting to start without zoomAndPan set"); - is(doc.rootElement.getAttribute("preserveAspectRatio"), - test.preserveAspectRatioString, "unexpected preserveAspectRatio"); + for (var j = 0; j < 2; j++) { + var initialViewBox = rootElement.getAttribute("viewBox"); + var initialPreserveAspectRatio = + rootElement.getAttribute("preserveAspectRatio"); + var initialZoomAndPan = rootElement.getAttribute("zoomAndPan"); + var initialTransform = rootElement.getAttribute("transform"); - is(doc.rootElement.getAttribute("zoomAndPan"), - test.zoomAndPanString, "unexpected zoomAndPan"); + for (var i = 0; i < tests.length; i++) { + var test = tests[i]; + svg.setAttribute("src", src + "#" + test.svgFragmentIdentifier); + is(rootElement.useCurrentView, test.valid, + "Expected " + test.svgFragmentIdentifier + " to be " + + (test.valid ? "valid" : "invalid")); + + // check that assigning a viewSpec does not modify the underlying + // attribute values. + is(rootElement.getAttribute("viewBox"), + initialViewBox, "unexpected viewBox"); + + is(rootElement.getAttribute("preserveAspectRatio"), + initialPreserveAspectRatio, "unexpected preserveAspectRatio"); + + is(rootElement.getAttribute("zoomAndPan"), + initialZoomAndPan, "unexpected zoomAndPan"); + + is(rootElement.getAttribute("transform"), + initialTransform, "unexpected transform"); + } + + // repeat tests with underlying attributes set to values + rootElement.setAttribute("viewBox", "0 0 100 100"); + rootElement.setAttribute("preserveAspectRatio", "none"); + rootElement.setAttribute("zoomAndPan", "disable"); + rootElement.setAttribute("transform", "translate(10,10)"); } SimpleTest.finish(); } -window.addEventListener("load", runTests, false); +$(svg).addEventListener("load", runTests, false);