Bug 512525 - Implement SVG Fragment Identifier parsing and animation hyperlinking - Part 2 main implementation. r=jwatt
authorRobert Longson <longsonr@gmail.com>
Thu, 17 May 2012 11:02:41 +0100
changeset 98726 3597432da4d9be566ff3c59984775cb13dcc14b8
parent 98725 e288dfb36d73fd9a25d71f38fcbf2af973c71472
child 98727 d51f702f5cbeda21530bdd259b377e8685901dde
push id173
push userlsblakk@mozilla.com
push dateFri, 24 Aug 2012 15:39:16 +0000
treeherdermozilla-release@bcc45eb1fb41 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwatt
bugs512525
milestone15.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 512525 - Implement SVG Fragment Identifier parsing and animation hyperlinking - Part 2 main implementation. r=jwatt
content/base/src/nsGkAtomList.h
content/base/src/nsTreeSanitizer.cpp
content/svg/content/src/Makefile.in
content/svg/content/src/SVGAnimatedPreserveAspectRatio.cpp
content/svg/content/src/SVGAnimatedPreserveAspectRatio.h
content/svg/content/src/SVGFragmentIdentifier.cpp
content/svg/content/src/SVGFragmentIdentifier.h
content/svg/content/src/nsSVGElementFactory.cpp
content/svg/content/src/nsSVGSVGElement.cpp
content/svg/content/src/nsSVGSVGElement.h
content/svg/content/src/nsSVGViewBox.cpp
content/svg/content/src/nsSVGViewBox.h
content/svg/content/src/nsSVGViewElement.cpp
content/svg/content/src/nsSVGViewElement.h
dom/base/nsDOMClassInfo.cpp
dom/base/nsDOMClassInfoClasses.h
dom/interfaces/svg/Makefile.in
dom/interfaces/svg/nsIDOMSVGViewElement.idl
layout/base/nsPresShell.cpp
layout/reftests/svg/fragmentIdentifier-01.xhtml
layout/reftests/svg/fragmentIdentifier-rect-01.svg
layout/reftests/svg/reftest.list
layout/svg/base/src/Makefile.in
layout/svg/base/src/nsSVGOuterSVGFrame.cpp
layout/svg/base/src/nsSVGUtils.cpp
layout/svg/base/src/nsSVGUtils.h
xpcom/ds/Makefile.in
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -1359,16 +1359,17 @@ GK_ATOM(text_rendering, "text-rendering"
 GK_ATOM(textPath, "textPath")
 GK_ATOM(tref, "tref")
 GK_ATOM(tspan, "tspan")
 GK_ATOM(turbulence, "turbulence")
 GK_ATOM(unicode_bidi, "unicode-bidi")
 GK_ATOM(userSpaceOnUse, "userSpaceOnUse")
 GK_ATOM(view, "view")
 GK_ATOM(viewBox, "viewBox")
+GK_ATOM(viewTarget, "viewTarget")
 GK_ATOM(vkern, "vkern")
 GK_ATOM(word_spacing, "word-spacing")
 GK_ATOM(x, "x")
 GK_ATOM(x1, "x1")
 GK_ATOM(x2, "x2")
 GK_ATOM(xChannelSelector, "xChannelSelector")
 GK_ATOM(xor_, "xor")
 GK_ATOM(y, "y")
--- a/content/base/src/nsTreeSanitizer.cpp
+++ b/content/base/src/nsTreeSanitizer.cpp
@@ -627,18 +627,18 @@ nsIAtom** const kAttributesSVG[] = {
   // v-hanging
   // v-ideographic
   // v-mathematical
   &nsGkAtoms::values, // values
   // vert-adv-y
   // vert-origin-x
   // vert-origin-y
   &nsGkAtoms::viewBox, // viewBox
+  &nsGkAtoms::viewTarget, // viewTarget
   &nsGkAtoms::visibility, // visibility
-  // viewTarget
   &nsGkAtoms::width, // width
   // widths
   &nsGkAtoms::word_spacing, // word-spacing
   // writing-mode
   &nsGkAtoms::x, // x
   // x-height
   &nsGkAtoms::x1, // x1
   &nsGkAtoms::x2, // x2
--- a/content/svg/content/src/Makefile.in
+++ b/content/svg/content/src/Makefile.in
@@ -119,22 +119,24 @@ CPPSRCS		= \
 		nsSVGTextContentElement.cpp \
 		nsSVGTextElement.cpp \
 		nsSVGTextPathElement.cpp \
 		nsSVGTextPositioningElement.cpp \
 		nsSVGTitleElement.cpp \
 		nsSVGUnknownElement.cpp \
 		nsSVGUseElement.cpp \
 		nsSVGViewBox.cpp \
+		nsSVGViewElement.cpp \
 		SVGAnimatedLengthList.cpp \
 		SVGAnimatedNumberList.cpp \
 		SVGAnimatedPathSegList.cpp \
 		SVGAnimatedPointList.cpp \
 		SVGAnimatedPreserveAspectRatio.cpp \
 		SVGAnimatedTransformList.cpp \
+		SVGFragmentIdentifier.cpp \
 		SVGLength.cpp \
 		SVGLengthList.cpp \
 		SVGNumberList.cpp \
 		SVGPathData.cpp \
 		SVGPathSegUtils.cpp \
 		SVGPointList.cpp \
 		SVGStringList.cpp \
 		SVGTransform.cpp \
--- a/content/svg/content/src/SVGAnimatedPreserveAspectRatio.cpp
+++ b/content/svg/content/src/SVGAnimatedPreserveAspectRatio.cpp
@@ -144,16 +144,24 @@ GetMeetOrSliceString(nsAString& aMeetOrS
     aMeetOrSlice <= nsIDOMSVGPreserveAspectRatio::SVG_MEETORSLICE_SLICE,
     "Unknown meetOrSlice");
 
   aMeetOrSliceString.AssignASCII(
     sMeetOrSliceStrings[aMeetOrSlice -
                         nsIDOMSVGPreserveAspectRatio::SVG_MEETORSLICE_MEET]);
 }
 
+bool
+SVGPreserveAspectRatio::operator==(const SVGPreserveAspectRatio& aOther) const
+{
+  return mAlign == aOther.mAlign &&
+    mMeetOrSlice == aOther.mMeetOrSlice &&
+    mDefer == aOther.mDefer;
+}
+
 nsresult
 SVGAnimatedPreserveAspectRatio::ToDOMBaseVal(
   nsIDOMSVGPreserveAspectRatio **aResult,
   nsSVGElement *aSVGElement)
 {
   *aResult = new DOMBaseVal(this, aSVGElement);
   if (!*aResult)
     return NS_ERROR_OUT_OF_MEMORY;
@@ -268,58 +276,35 @@ SVGAnimatedPreserveAspectRatio::GetBaseV
       nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_NONE) {
 
     aValueAsString.AppendLiteral(" ");
     GetMeetOrSliceString(tmpString, mBaseVal.mMeetOrSlice);
     aValueAsString.Append(tmpString);
   }
 }
 
-nsresult
-SVGAnimatedPreserveAspectRatio::SetBaseAlign(PRUint16 aAlign,
+void
+SVGAnimatedPreserveAspectRatio::SetBaseValue(const SVGPreserveAspectRatio &aValue,
                                              nsSVGElement *aSVGElement)
 {
-  if (mIsBaseSet && mBaseVal.GetAlign() == aAlign) {
-    return NS_OK;
+  if (mIsBaseSet && mBaseVal == aValue) {
+    return;
   }
 
   nsAttrValue emptyOrOldValue = aSVGElement->WillChangePreserveAspectRatio();
-  nsresult rv = mBaseVal.SetAlign(aAlign);
-  NS_ENSURE_SUCCESS(rv, rv);
+  mBaseVal = aValue;
   mIsBaseSet = true;
 
-  mAnimVal.mAlign = mBaseVal.mAlign;
+  if (!mIsAnimated) {
+    mAnimVal = mBaseVal;
+  }
   aSVGElement->DidChangePreserveAspectRatio(emptyOrOldValue);
   if (mIsAnimated) {
     aSVGElement->AnimationNeedsResample();
   }
-  
-  return NS_OK;
-}
-
-nsresult
-SVGAnimatedPreserveAspectRatio::SetBaseMeetOrSlice(PRUint16 aMeetOrSlice,
-                                                   nsSVGElement *aSVGElement)
-{
-  if (mIsBaseSet && mBaseVal.GetMeetOrSlice() == aMeetOrSlice) {
-    return NS_OK;
-  }
-
-  nsAttrValue emptyOrOldValue = aSVGElement->WillChangePreserveAspectRatio();
-  nsresult rv = mBaseVal.SetMeetOrSlice(aMeetOrSlice);
-  NS_ENSURE_SUCCESS(rv, rv);
-  mIsBaseSet = true;
-
-  mAnimVal.mMeetOrSlice = mBaseVal.mMeetOrSlice;
-  aSVGElement->DidChangePreserveAspectRatio(emptyOrOldValue);
-  if (mIsAnimated) {
-    aSVGElement->AnimationNeedsResample();
-  }
-  
-  return NS_OK;
 }
 
 void
 SVGAnimatedPreserveAspectRatio::SetAnimValue(PRUint64 aPackedValue,
                                              nsSVGElement *aSVGElement)
 {
   mAnimVal.SetDefer(((aPackedValue & 0xff0000) >> 16) ? true : false);
   mAnimVal.SetAlign(PRUint16((aPackedValue & 0xff00) >> 8));
--- a/content/svg/content/src/SVGAnimatedPreserveAspectRatio.h
+++ b/content/svg/content/src/SVGAnimatedPreserveAspectRatio.h
@@ -55,22 +55,30 @@ namespace mozilla {
 
 class SVGAnimatedPreserveAspectRatio;
 
 class SVGPreserveAspectRatio
 {
   friend class SVGAnimatedPreserveAspectRatio;
 
 public:
+  SVGPreserveAspectRatio(PRUint16 aAlign, PRUint16 aMeetOrSlice, bool aDefer = false)
+    : mAlign(aAlign)
+    , mMeetOrSlice(aMeetOrSlice)
+    , mDefer(aDefer)
+  {};
+
   SVGPreserveAspectRatio()
     : mAlign(0)
     , mMeetOrSlice(0)
     , mDefer(false)
   {};
 
+  bool operator==(const SVGPreserveAspectRatio& aOther) const;
+
   nsresult SetAlign(PRUint16 aAlign) {
     if (aAlign < nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_NONE ||
         aAlign > nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX)
       return NS_ERROR_FAILURE;
     mAlign = static_cast<PRUint8>(aAlign);
     return NS_OK;
   };
 
@@ -115,18 +123,38 @@ public:
     mIsAnimated = false;
     mIsBaseSet = false;
   }
 
   nsresult SetBaseValueString(const nsAString& aValue,
                               nsSVGElement *aSVGElement);
   void GetBaseValueString(nsAString& aValue) const;
 
-  nsresult SetBaseAlign(PRUint16 aAlign, nsSVGElement *aSVGElement);
-  nsresult SetBaseMeetOrSlice(PRUint16 aMeetOrSlice, nsSVGElement *aSVGElement);
+  void SetBaseValue(const SVGPreserveAspectRatio &aValue,
+                    nsSVGElement *aSVGElement);
+  nsresult SetBaseAlign(PRUint16 aAlign, nsSVGElement *aSVGElement) {
+    if (aAlign < nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_NONE ||
+        aAlign > nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX) {
+      return NS_ERROR_FAILURE;
+    }
+    SetBaseValue(SVGPreserveAspectRatio(
+                   aAlign, mBaseVal.GetMeetOrSlice(), mBaseVal.GetDefer()),
+                 aSVGElement);
+    return NS_OK;
+  }
+  nsresult SetBaseMeetOrSlice(PRUint16 aMeetOrSlice, nsSVGElement *aSVGElement) {
+    if (aMeetOrSlice < nsIDOMSVGPreserveAspectRatio::SVG_MEETORSLICE_MEET ||
+        aMeetOrSlice > nsIDOMSVGPreserveAspectRatio::SVG_MEETORSLICE_SLICE) {
+      return NS_ERROR_FAILURE;
+    }
+    SetBaseValue(SVGPreserveAspectRatio(
+                   mBaseVal.GetAlign(), aMeetOrSlice, mBaseVal.GetDefer()),
+                 aSVGElement);
+    return NS_OK;
+  }
   void SetAnimValue(PRUint64 aPackedValue, nsSVGElement *aSVGElement);
 
   const SVGPreserveAspectRatio &GetBaseValue() const
     { return mBaseVal; }
   const SVGPreserveAspectRatio &GetAnimValue() const
     { return mAnimVal; }
   bool IsAnimated() const
     { return mIsAnimated; }
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/SVGFragmentIdentifier.cpp
@@ -0,0 +1,260 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Mozilla SVG project.
+ *
+ * The Initial Developer of the Original Code is
+ * Robert Longson <longsonr@gmail.com>
+ * Portions created by the Initial Developer are Copyright (C) 2012
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "SVGFragmentIdentifier.h"
+#include "CharTokenizer.h"
+#include "nsIDOMSVGDocument.h"
+#include "nsSVGSVGElement.h"
+#include "nsSVGViewElement.h"
+
+using namespace mozilla;
+
+static nsSVGEnumMapping sZoomAndPanMap[] = {
+  {&nsGkAtoms::disable, nsIDOMSVGZoomAndPan::SVG_ZOOMANDPAN_DISABLE},
+  {&nsGkAtoms::magnify, nsIDOMSVGZoomAndPan::SVG_ZOOMANDPAN_MAGNIFY},
+  {nsnull, 0}
+};
+
+static bool
+IsMatchingParameter(const nsAString &aString, const nsAString &aParameterName)
+{
+  return StringBeginsWith(aString, aParameterName) &&
+         aString.CharAt(aParameterName.Length()) == '(' &&
+         aString.Last() == ')';
+}
+
+static nsSVGViewElement*
+GetViewElement(nsIDocument *aDocument, const nsAString &aId)
+{
+  dom::Element* element = aDocument->GetElementById(aId);
+  return (element && element->IsSVG(nsGkAtoms::view)) ?
+            static_cast<nsSVGViewElement*>(element) : nsnull;
+}
+
+void 
+SVGFragmentIdentifier::SaveOldPreserveAspectRatio(nsSVGSVGElement *root)
+{
+  const SVGPreserveAspectRatio *oldPARPtr = root->GetPreserveAspectRatioProperty();
+  if (!oldPARPtr) {
+    root->SetPreserveAspectRatioProperty(root->mPreserveAspectRatio.GetBaseValue());
+  }
+}
+
+void 
+SVGFragmentIdentifier::RestoreOldPreserveAspectRatio(nsSVGSVGElement *root)
+{
+  const SVGPreserveAspectRatio *oldPARPtr = root->GetPreserveAspectRatioProperty();
+  if (oldPARPtr) {
+    root->mPreserveAspectRatio.SetBaseValue(*oldPARPtr, root);
+    root->ClearPreserveAspectRatioProperty();
+  }
+}
+
+void 
+SVGFragmentIdentifier::SaveOldViewBox(nsSVGSVGElement *root)
+{
+  const nsSVGViewBoxRect *oldViewBoxPtr = root->GetViewBoxProperty();
+  if (!oldViewBoxPtr) {
+    root->SetViewBoxProperty(root->mViewBox.GetBaseValue());
+  }
+}
+
+void 
+SVGFragmentIdentifier::RestoreOldViewBox(nsSVGSVGElement *root)
+{
+  const nsSVGViewBoxRect *oldViewBoxPtr = root->GetViewBoxProperty();
+  if (oldViewBoxPtr) {
+    root->mViewBox.SetBaseValue(*oldViewBoxPtr, root);
+    root->ClearViewBoxProperty();
+  }
+}
+
+void 
+SVGFragmentIdentifier::SaveOldZoomAndPan(nsSVGSVGElement *root)
+{
+  const PRUint16 *oldZoomAndPanPtr = root->GetZoomAndPanProperty();
+  if (!oldZoomAndPanPtr) {
+    root->SetZoomAndPanProperty(root->mEnumAttributes[nsSVGSVGElement::ZOOMANDPAN].GetBaseValue());
+  }
+}
+
+void 
+SVGFragmentIdentifier::RestoreOldZoomAndPan(nsSVGSVGElement *root)
+{
+  const PRUint16 *oldZoomAndPanPtr = root->GetZoomAndPanProperty();
+  if (oldZoomAndPanPtr) {
+    root->mEnumAttributes[nsSVGSVGElement::ZOOMANDPAN].SetBaseValue(*oldZoomAndPanPtr, root);
+    root->ClearZoomAndPanProperty();
+  }
+}
+
+bool
+SVGFragmentIdentifier::ProcessSVGViewSpec(const nsAString &aViewSpec,
+                                          nsSVGSVGElement *root)
+{
+  if (!IsMatchingParameter(aViewSpec, NS_LITERAL_STRING("svgView"))) {
+    return false;
+  }
+
+  // SVGViewAttribute may occur in any order, but each type may only occur at most one time
+  // in a correctly formed SVGViewSpec.
+  // If we encounter any element more than once or get any syntax errors we're going to
+  // return without updating the root element
+
+  const nsAString *viewBoxParams = nsnull;
+  const nsAString *preserveAspectRatioParams = nsnull;
+  const nsAString *zoomAndPanParams = nsnull;
+
+  // Each token is a SVGViewAttribute
+  PRInt32 bracketPos = aViewSpec.FindChar('(');
+  CharTokenizer<';'>tokenizer(
+    Substring(aViewSpec, bracketPos + 1, aViewSpec.Length() - bracketPos - 2));
+
+  while (tokenizer.hasMoreTokens()) {
+
+    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 (IsMatchingParameter(token, NS_LITERAL_STRING("viewBox"))) {
+      if (viewBoxParams) {
+        return false;
+      }
+      viewBoxParams = &params;
+    } else if (IsMatchingParameter(token, NS_LITERAL_STRING("preserveAspectRatio"))) {
+      if (preserveAspectRatioParams) {
+        return false;
+      }
+      preserveAspectRatioParams = &params;
+    } else if (IsMatchingParameter(token, NS_LITERAL_STRING("zoomAndPan"))) {
+      if (zoomAndPanParams) {
+        return false;
+      }
+      zoomAndPanParams = &params;
+    } else {
+      // We don't support transform or viewTarget currently
+      return false;
+    }
+  }
+
+  const nsSVGViewBoxRect *oldViewBoxPtr = root->GetViewBoxProperty();
+  if (viewBoxParams) {
+    SaveOldViewBox(root);
+    root->mViewBox.SetBaseValueString(*viewBoxParams, root);
+  } else {
+    RestoreOldViewBox(root);
+  }
+
+  const SVGPreserveAspectRatio *oldPARPtr = root->GetPreserveAspectRatioProperty();
+  if (preserveAspectRatioParams) {
+    SaveOldPreserveAspectRatio(root);
+    root->mPreserveAspectRatio.SetBaseValueString(*preserveAspectRatioParams, root);
+  } else {
+    RestoreOldPreserveAspectRatio(root);
+  }
+
+  const PRUint16 *oldZoomAndPanPtr = root->GetZoomAndPanProperty();
+  if (zoomAndPanParams) {
+    SaveOldZoomAndPan(root);
+    nsCOMPtr<nsIAtom> valAtom = do_GetAtom(*zoomAndPanParams);
+    const nsSVGEnumMapping *mapping = root->sZoomAndPanMap;
+    while (mapping->mKey) {
+      if (valAtom == *(mapping->mKey)) {
+        root->mEnumAttributes[nsSVGSVGElement::ZOOMANDPAN].SetBaseValue(mapping->mVal, root);
+        break;
+      }
+      mapping++;
+    }
+  } else {
+    RestoreOldZoomAndPan(root);
+  }
+
+  return true;
+}
+
+bool
+SVGFragmentIdentifier::ProcessFragmentIdentifier(nsIDocument *aDocument,
+                                                 const nsAString &aAnchorName)
+{
+  NS_ABORT_IF_FALSE(aDocument->GetRootElement()->IsSVG(nsGkAtoms::svg),
+                    "expecting an SVG root element");
+
+  nsSVGSVGElement *rootElement =
+    static_cast<nsSVGSVGElement*>(aDocument->GetRootElement());
+
+  const nsSVGViewElement *viewElement = GetViewElement(aDocument, aAnchorName);
+
+  if (viewElement) {
+    if (viewElement->mViewBox.IsExplicitlySet()) {
+      SaveOldViewBox(rootElement);
+      rootElement->mViewBox.SetBaseValue(
+        viewElement->mViewBox.GetBaseValue(), rootElement);
+    } else {
+      RestoreOldViewBox(rootElement);
+    }
+    if (viewElement->mPreserveAspectRatio.IsExplicitlySet()) {
+      SaveOldPreserveAspectRatio(rootElement);
+      rootElement->mPreserveAspectRatio.SetBaseValue(
+        viewElement->mPreserveAspectRatio.GetBaseValue(), rootElement);
+    } else {
+      RestoreOldPreserveAspectRatio(rootElement);
+    }
+    if (viewElement->mEnumAttributes[nsSVGViewElement::ZOOMANDPAN].IsExplicitlySet()) {
+      SaveOldZoomAndPan(rootElement);
+      rootElement->mEnumAttributes[nsSVGSVGElement::ZOOMANDPAN].SetBaseValue(
+        viewElement->mEnumAttributes[nsSVGViewElement::ZOOMANDPAN].GetBaseValue(), rootElement);
+    } else {
+      RestoreOldZoomAndPan(rootElement);
+    }
+    return true;
+  }
+
+  if (ProcessSVGViewSpec(aAnchorName, rootElement)) {
+    return true;
+  }
+  RestoreOldViewBox(rootElement);
+  RestoreOldPreserveAspectRatio(rootElement);
+  RestoreOldZoomAndPan(rootElement);
+  return false;
+}
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/SVGFragmentIdentifier.h
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Mozilla SVG project.
+ *
+ * The Initial Developer of the Original Code is
+ * Robert Longson <longsonr@gmail.com>
+ * Portions created by the Initial Developer are Copyright (C) 2012
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef MOZILLA_SVGFRAGMENTIDENTIFIER_H__
+#define MOZILLA_SVGFRAGMENTIDENTIFIER_H__
+
+#include "nsString.h"
+
+class nsIDocument;
+class nsSVGSVGElement;
+class nsSVGViewElement;
+
+namespace mozilla {
+
+/**
+ * Implements support for parsing SVG fragment identifiers
+ * http://www.w3.org/TR/SVG/linking.html#SVGFragmentIdentifiers
+ */
+class SVGFragmentIdentifier
+{
+  // To prevent the class being instantiated
+  SVGFragmentIdentifier() MOZ_DELETE;
+
+public:
+  /**
+   * Process the SVG fragment identifier, if there is one.
+   * @return true if we found something we recognised
+   */
+  static bool ProcessFragmentIdentifier(nsIDocument *aDocument,
+                                        const nsAString &aAnchorName);
+
+private:
+ /**
+  * Parse an SVG ViewSpec and set applicable attributes on the root element.
+  * @return true if there is a valid ViewSpec
+  */
+  static bool ProcessSVGViewSpec(const nsAString &aViewSpec, nsSVGSVGElement *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(nsSVGSVGElement *root);
+  static void RestoreOldPreserveAspectRatio(nsSVGSVGElement *root);
+  static void SaveOldViewBox(nsSVGSVGElement *root);
+  static void RestoreOldViewBox(nsSVGSVGElement *root);
+  static void SaveOldZoomAndPan(nsSVGSVGElement *root);
+  static void RestoreOldZoomAndPan(nsSVGSVGElement *root);
+};
+
+} // namespace mozilla
+
+#endif // MOZILLA_SVGFRAGMENTIDENTIFIER_H__
--- a/content/svg/content/src/nsSVGElementFactory.cpp
+++ b/content/svg/content/src/nsSVGElementFactory.cpp
@@ -217,16 +217,19 @@ nsresult
 NS_NewSVGFESpecularLightingElement(nsIContent **aResult,
                                    already_AddRefed<nsINodeInfo> aNodeInfo);
 nsresult
 NS_NewSVGFEImageElement(nsIContent **aResult,
                         already_AddRefed<nsINodeInfo> aNodeInfo);
 nsresult
 NS_NewSVGFEDisplacementMapElement(nsIContent **aResult,
                                   already_AddRefed<nsINodeInfo> aNodeInfo);
+nsresult
+NS_NewSVGViewElement(nsIContent **aResult,
+                     already_AddRefed<nsINodeInfo> aNodeInfo);
 
 nsresult
 NS_NewSVGAnimateElement(nsIContent **aResult,
                         already_AddRefed<nsINodeInfo> aNodeInfo);
 nsresult
 NS_NewSVGAnimateTransformElement(nsIContent **aResult,
                                  already_AddRefed<nsINodeInfo> aNodeInfo);
 nsresult
@@ -365,16 +368,18 @@ NS_NewSVGElement(nsIContent** aResult, a
   if (name == nsGkAtoms::feDisplacementMap)
     return NS_NewSVGFEDisplacementMapElement(aResult, aNodeInfo);
   if (name == nsGkAtoms::pattern)
     return NS_NewSVGPatternElement(aResult, aNodeInfo);
   if (name == nsGkAtoms::mask)
     return NS_NewSVGMaskElement(aResult, aNodeInfo);
   if (name == nsGkAtoms::svgSwitch)
     return NS_NewSVGSwitchElement(aResult, aNodeInfo);
+  if (name == nsGkAtoms::view)
+    return NS_NewSVGViewElement(aResult, aNodeInfo);
   if (NS_SMILEnabled()) {
     if (name == nsGkAtoms::animate)
       return NS_NewSVGAnimateElement(aResult, aNodeInfo);
     if (name == nsGkAtoms::animateTransform)
       return NS_NewSVGAnimateTransformElement(aResult, aNodeInfo);
     if (name == nsGkAtoms::animateMotion)
       return NS_NewSVGAnimateMotionElement(aResult, aNodeInfo);
     if (name == nsGkAtoms::mpath)
--- a/content/svg/content/src/nsSVGSVGElement.cpp
+++ b/content/svg/content/src/nsSVGSVGElement.cpp
@@ -951,81 +951,42 @@ ComputeSynthesizedViewBoxDimension(const
 }
 
 //----------------------------------------------------------------------
 // public helpers:
 
 gfxMatrix
 nsSVGSVGElement::GetViewBoxTransform() const
 {
-  // Do we have an override preserveAspectRatio value?
-  const SVGPreserveAspectRatio* overridePARPtr =
-    GetImageOverridePreserveAspectRatio();
-
-  // May assign this to overridePARPtr if we have no viewBox but are faking one:
-  SVGPreserveAspectRatio tmpPAR;
-
   float viewportWidth, viewportHeight;
   if (IsInner()) {
     nsSVGSVGElement *ctx = GetCtx();
     viewportWidth = mLengthAttributes[WIDTH].GetAnimValue(ctx);
     viewportHeight = mLengthAttributes[HEIGHT].GetAnimValue(ctx);
   } else {
     viewportWidth = mViewportWidth;
     viewportHeight = mViewportHeight;
   }
 
   if (viewportWidth <= 0.0f || viewportHeight <= 0.0f) {
     return gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
   }
 
-  nsSVGViewBoxRect viewBox;
-  if (HasViewBox()) {
-    viewBox = mViewBox.GetAnimValue();
-  } else {
-    viewBox.x = viewBox.y = 0.0f;
-    if (ShouldSynthesizeViewBox()) {
-      // Special case -- fake a viewBox, using height & width attrs.
-      // (Use |this| as context, since if we get here, we're outermost <svg>.)
-      viewBox.width =
-        ComputeSynthesizedViewBoxDimension(mLengthAttributes[WIDTH],
-                                           mViewportWidth, this);
-      viewBox.height =
-        ComputeSynthesizedViewBoxDimension(mLengthAttributes[HEIGHT],
-                                           mViewportHeight, this);
-      NS_ABORT_IF_FALSE(!overridePARPtr,
-                        "shouldn't have overridePAR if we're "
-                        "synthesizing a viewBox");
-
-      // If we're synthesizing a viewBox, use preserveAspectRatio="none";
-      tmpPAR.SetAlign(nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_NONE);
-
-      // (set the other pAR attributes too, just so they're initialized):
-      tmpPAR.SetDefer(false);
-      tmpPAR.SetMeetOrSlice(nsIDOMSVGPreserveAspectRatio::SVG_MEETORSLICE_SLICE);
-
-      overridePARPtr = &tmpPAR;
-    } else {
-      // No viewBox attribute, so we shouldn't auto-scale. This is equivalent
-      // to having a viewBox that exactly matches our viewport size.
-      viewBox.width  = viewportWidth;
-      viewBox.height = viewportHeight;
-    }
-  }
+  nsSVGViewBoxRect viewBox =
+    GetViewBoxWithSynthesis(viewportWidth, viewportHeight);
 
   if (viewBox.width <= 0.0f || viewBox.height <= 0.0f) {
     return gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
   }
 
   return nsSVGUtils::GetViewBoxTransform(this,
                                          viewportWidth, viewportHeight,
                                          viewBox.x, viewBox.y,
                                          viewBox.width, viewBox.height,
-                                         overridePARPtr ? *overridePARPtr :
-                                         mPreserveAspectRatio.GetAnimValue());
+                                         GetPreserveAspectRatioWithOverride());
 }
 
 void
 nsSVGSVGElement::ChildrenOnlyTransformChanged()
 {
   // Avoid wasteful calls:
   NS_ABORT_IF_FALSE(!(GetPrimaryFrame()->GetStateBits() &
                       NS_STATE_SVG_NONDISPLAY_CHILD),
@@ -1144,16 +1105,60 @@ nsSVGSVGElement::InvalidateTransformNoti
 
 bool
 nsSVGSVGElement::HasPreserveAspectRatio()
 {
   return HasAttr(kNameSpaceID_None, nsGkAtoms::preserveAspectRatio) ||
     mPreserveAspectRatio.IsAnimated();
 }
 
+nsSVGViewBoxRect
+nsSVGSVGElement::GetViewBoxWithSynthesis(
+  float aViewportWidth, float aViewportHeight) const
+{
+  if (HasViewBox()) {
+    return mViewBox.GetAnimValue();
+  }
+
+  if (ShouldSynthesizeViewBox()) {
+    // Special case -- fake a viewBox, using height & width attrs.
+    // (Use |this| as context, since if we get here, we're outermost <svg>.)
+    return nsSVGViewBoxRect(0, 0,
+              ComputeSynthesizedViewBoxDimension(mLengthAttributes[WIDTH],
+                                                 mViewportWidth, this),
+              ComputeSynthesizedViewBoxDimension(mLengthAttributes[HEIGHT],
+                                                 mViewportHeight, this));
+
+  }
+
+  // No viewBox attribute, so we shouldn't auto-scale. This is equivalent
+  // to having a viewBox that exactly matches our viewport size.
+  return nsSVGViewBoxRect(0, 0, aViewportWidth, aViewportHeight);
+}
+
+SVGPreserveAspectRatio
+nsSVGSVGElement::GetPreserveAspectRatioWithOverride() const
+{
+  if (GetCurrentDoc()->IsBeingUsedAsImage()) {
+    const SVGPreserveAspectRatio *pAROverridePtr = GetPreserveAspectRatioProperty();
+    if (pAROverridePtr) {
+      return *pAROverridePtr;
+    }
+  }
+
+  if (!HasViewBox() && ShouldSynthesizeViewBox()) {
+    // If we're synthesizing a viewBox, use preserveAspectRatio="none";
+    return SVGPreserveAspectRatio(
+         nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_NONE,
+         nsIDOMSVGPreserveAspectRatio::SVG_MEETORSLICE_SLICE);
+  }
+
+  return mPreserveAspectRatio.GetAnimValue();
+}
+
 //----------------------------------------------------------------------
 // nsSVGSVGElement
 
 float
 nsSVGSVGElement::GetLength(PRUint8 aCtxType)
 {
   float h, w;
 
@@ -1296,16 +1301,53 @@ ReleasePreserveAspectRatioPropertyValue(
                                         void*    aPropertyValue,
                                         void*    aData          /* unused */)
 {
   SVGPreserveAspectRatio* valPtr =
     static_cast<SVGPreserveAspectRatio*>(aPropertyValue);
   delete valPtr;
 }
 
+bool
+nsSVGSVGElement::
+  SetPreserveAspectRatioProperty(const SVGPreserveAspectRatio& aPAR)
+{
+  SVGPreserveAspectRatio* pAROverridePtr = new SVGPreserveAspectRatio(aPAR);
+  nsresult rv = SetProperty(nsGkAtoms::overridePreserveAspectRatio,
+                            pAROverridePtr,
+                            ReleasePreserveAspectRatioPropertyValue);
+  NS_ABORT_IF_FALSE(rv != NS_PROPTABLE_PROP_OVERWRITTEN,
+                    "Setting override value when it's already set...?"); 
+
+  if (NS_UNLIKELY(NS_FAILED(rv))) {
+    // property-insertion failed (e.g. OOM in property-table code)
+    delete pAROverridePtr;
+    return false;
+  }
+  return true;
+}
+
+const SVGPreserveAspectRatio*
+nsSVGSVGElement::GetPreserveAspectRatioProperty() const
+{
+  void* valPtr = GetProperty(nsGkAtoms::overridePreserveAspectRatio);
+  if (valPtr) {
+    return static_cast<SVGPreserveAspectRatio*>(valPtr);
+  }
+  return nsnull;
+}
+
+bool
+nsSVGSVGElement::ClearPreserveAspectRatioProperty()
+{
+  void* valPtr = UnsetProperty(nsGkAtoms::overridePreserveAspectRatio);
+  delete static_cast<SVGPreserveAspectRatio*>(valPtr);
+  return valPtr;
+}
+
 void
 nsSVGSVGElement::
   SetImageOverridePreserveAspectRatio(const SVGPreserveAspectRatio& aPAR)
 {
 #ifdef DEBUG
   NS_ABORT_IF_FALSE(GetCurrentDoc()->IsBeingUsedAsImage(),
                     "should only override preserveAspectRatio in images");
 #endif
@@ -1321,72 +1363,121 @@ nsSVGSVGElement::
   if (!HasViewBox()) {
     return; // preserveAspectRatio irrelevant (only matters if we have viewBox)
   }
 
   if (aPAR.GetDefer() && HasPreserveAspectRatio()) {
     return; // Referring element defers to my own preserveAspectRatio value.
   }
 
-  SVGPreserveAspectRatio* pAROverridePtr = new SVGPreserveAspectRatio(aPAR);
-  nsresult rv = SetProperty(nsGkAtoms::overridePreserveAspectRatio,
-                            pAROverridePtr,
-                            ReleasePreserveAspectRatioPropertyValue);
-  NS_ABORT_IF_FALSE(rv != NS_PROPTABLE_PROP_OVERWRITTEN,
-                    "Setting override value when it's already set...?"); 
-
-  if (NS_LIKELY(NS_SUCCEEDED(rv))) {
+  if (SetPreserveAspectRatioProperty(aPAR)) {
     mImageNeedsTransformInvalidation = true;
-  } else {
-    // property-insertion failed (e.g. OOM in property-table code)
-    delete pAROverridePtr;
   }
 }
 
 void
 nsSVGSVGElement::ClearImageOverridePreserveAspectRatio()
 {
 #ifdef DEBUG
   NS_ABORT_IF_FALSE(GetCurrentDoc()->IsBeingUsedAsImage(),
-                    "should only override preserveAspectRatio in images");
+                    "should only override image preserveAspectRatio in images");
 #endif
 
   mIsPaintingSVGImageElement = false;
   if (!HasViewBox() && ShouldSynthesizeViewBox()) {
     // My non-<svg:image> clients will want to paint me with a synthesized
     // viewBox, but my <svg:image> client that just painted me did NOT
     // use that.  Need to tell ourselves to flush our transform.
     mImageNeedsTransformInvalidation = true;
   }
 
-  void* valPtr = UnsetProperty(nsGkAtoms::overridePreserveAspectRatio);
-  if (valPtr) {
+  if (ClearPreserveAspectRatioProperty()) {
     mImageNeedsTransformInvalidation = true;
-    delete static_cast<SVGPreserveAspectRatio*>(valPtr);
   }
 }
 
-const SVGPreserveAspectRatio*
-nsSVGSVGElement::GetImageOverridePreserveAspectRatio() const
-{
-  void* valPtr = GetProperty(nsGkAtoms::overridePreserveAspectRatio);
-#ifdef DEBUG
-  if (valPtr) {
-    NS_ABORT_IF_FALSE(GetCurrentDoc()->IsBeingUsedAsImage(),
-                      "should only override preserveAspectRatio in images");
-  }
-#endif
-
-  return static_cast<SVGPreserveAspectRatio*>(valPtr);
-}
-
 void
 nsSVGSVGElement::FlushImageTransformInvalidation()
 {
   NS_ABORT_IF_FALSE(!GetParent(), "Should only be called on root node");
   NS_ABORT_IF_FALSE(GetCurrentDoc()->IsBeingUsedAsImage(),
                     "Should only be called on image documents");
 
   if (mImageNeedsTransformInvalidation) {
     InvalidateTransformNotifyFrame();
     mImageNeedsTransformInvalidation = false;
   }
 }
+
+// Callback function, for freeing PRUint64 values stored in property table
+static void
+ReleaseViewBoxPropertyValue(void*    aObject,       /* unused */
+                            nsIAtom* aPropertyName, /* unused */
+                            void*    aPropertyValue,
+                            void*    aData          /* unused */)
+{
+  nsSVGViewBoxRect* valPtr =
+    static_cast<nsSVGViewBoxRect*>(aPropertyValue);
+  delete valPtr;
+}
+
+bool
+nsSVGSVGElement::SetViewBoxProperty(const nsSVGViewBoxRect& aViewBox)
+{
+  nsSVGViewBoxRect* pViewBoxOverridePtr = new nsSVGViewBoxRect(aViewBox);
+  nsresult rv = SetProperty(nsGkAtoms::viewBox,
+                            pViewBoxOverridePtr,
+                            ReleaseViewBoxPropertyValue);
+  NS_ABORT_IF_FALSE(rv != NS_PROPTABLE_PROP_OVERWRITTEN,
+                    "Setting override value when it's already set...?"); 
+
+  if (NS_UNLIKELY(NS_FAILED(rv))) {
+    // property-insertion failed (e.g. OOM in property-table code)
+    delete pViewBoxOverridePtr;
+    return false;
+  }
+  return true;
+}
+
+const nsSVGViewBoxRect*
+nsSVGSVGElement::GetViewBoxProperty() const
+{
+  void* valPtr = GetProperty(nsGkAtoms::viewBox);
+  if (valPtr) {
+    return static_cast<nsSVGViewBoxRect*>(valPtr);
+  }
+  return nsnull;
+}
+
+bool
+nsSVGSVGElement::ClearViewBoxProperty()
+{
+  void* valPtr = UnsetProperty(nsGkAtoms::viewBox);
+  delete static_cast<nsSVGViewBoxRect*>(valPtr);
+  return valPtr;
+}
+
+bool
+nsSVGSVGElement::SetZoomAndPanProperty(PRUint16 aValue)
+{
+  nsresult rv = SetProperty(nsGkAtoms::zoomAndPan, reinterpret_cast<void*>(aValue));
+  NS_ABORT_IF_FALSE(rv != NS_PROPTABLE_PROP_OVERWRITTEN,
+                    "Setting override value when it's already set...?"); 
+
+  return NS_SUCCEEDED(rv);
+}
+
+const PRUint16*
+nsSVGSVGElement::GetZoomAndPanProperty() const
+{
+  void* valPtr = GetProperty(nsGkAtoms::zoomAndPan);
+  if (valPtr) {
+    return reinterpret_cast<PRUint16*>(valPtr);
+  }
+  return nsnull;
+}
+
+bool
+nsSVGSVGElement::ClearZoomAndPanProperty()
+{
+  return UnsetProperty(nsGkAtoms::viewBox);
+}
+
--- a/content/svg/content/src/nsSVGSVGElement.h
+++ b/content/svg/content/src/nsSVGSVGElement.h
@@ -50,16 +50,20 @@
 #include "nsSVGEnum.h"
 #include "nsSVGLength2.h"
 #include "nsSVGStylableElement.h"
 #include "nsSVGViewBox.h"
 #include "SVGAnimatedPreserveAspectRatio.h"
 
 class nsIDOMSVGMatrix;
 class nsSMILTimeContainer;
+class nsSVGViewElement;
+namespace mozilla {
+  class SVGFragmentIdentifier;
+}
 
 typedef nsSVGStylableElement nsSVGSVGElementBase;
 
 class nsSVGSVGElement;
 
 class nsSVGTranslatePoint {
 public:
   nsSVGTranslatePoint()
@@ -133,16 +137,17 @@ class nsSVGSVGElement : public nsSVGSVGE
                         public DOMSVGTests,
                         public nsIDOMSVGFitToViewBox,
                         public nsIDOMSVGLocatable,
                         public nsIDOMSVGZoomAndPan
 {
   friend class nsSVGOuterSVGFrame;
   friend class nsSVGInnerSVGFrame;
   friend class nsSVGImageFrame;
+  friend class mozilla::SVGFragmentIdentifier;
 
   friend nsresult NS_NewSVGSVGElement(nsIContent **aResult,
                                       already_AddRefed<nsINodeInfo> aNodeInfo,
                                       mozilla::dom::FromParser aFromParser);
   nsSVGSVGElement(already_AddRefed<nsINodeInfo> aNodeInfo,
                   mozilla::dom::FromParser aFromParser);
   
 public:
@@ -268,33 +273,43 @@ public:
     mViewportHeight = aSize.height;
   }
 
   virtual nsXPCClassInfo* GetClassInfo();
 
   virtual nsIDOMNode* AsDOMNode() { return this; }
 
 private:
-  // Methods for <image> elements to override my "PreserveAspectRatio" value.
-  // These are private so that only our friends (nsSVGImageFrame in
-  // particular) have access.
-  void SetImageOverridePreserveAspectRatio(const SVGPreserveAspectRatio& aPAR);
-  void ClearImageOverridePreserveAspectRatio();
-  const SVGPreserveAspectRatio* GetImageOverridePreserveAspectRatio() const;
-
   // nsSVGElement overrides
   bool IsEventName(nsIAtom* aName);
 
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                               nsIContent* aBindingParent,
                               bool aCompileEventHandlers);
   virtual void UnbindFromTree(bool aDeep, bool aNullParent);
 
   // implementation helpers:
 
+  // Methods for <image> elements to override my "PreserveAspectRatio" value.
+  // These are private so that only our friends (nsSVGImageFrame in
+  // particular) have access.
+  void SetImageOverridePreserveAspectRatio(const SVGPreserveAspectRatio& aPAR);
+  void ClearImageOverridePreserveAspectRatio();
+
+  // Set/Clear properties to hold old or override versions of attributes
+  bool SetPreserveAspectRatioProperty(const SVGPreserveAspectRatio& aPAR);
+  const SVGPreserveAspectRatio* GetPreserveAspectRatioProperty() const;
+  bool ClearPreserveAspectRatioProperty();
+  bool SetViewBoxProperty(const nsSVGViewBoxRect& aViewBox);
+  const nsSVGViewBoxRect* GetViewBoxProperty() const;
+  bool ClearViewBoxProperty();
+  bool SetZoomAndPanProperty(PRUint16 aValue);
+  const PRUint16* GetZoomAndPanProperty() const;
+  bool ClearZoomAndPanProperty();
+
   bool IsRoot() const {
     NS_ASSERTION((IsInDoc() && !GetParent()) ==
                  (OwnerDoc() && (OwnerDoc()->GetRootElement() == this)),
                  "Can't determine if we're root");
     return IsInDoc() && !GetParent();
   }
 
   /**
@@ -312,26 +327,39 @@ private:
    * <svg> element _before_ the children are bound (as they want to know what
    * timed document root to register with) and therefore _before_ our parent is
    * set (both actions are performed by nsGenericElement::BindToTree) so we
    * can't use GetOwnerSVGElement() as it relies on GetParent(). This code is
    * basically a simplified version of GetOwnerSVGElement that uses the parent
    * parameters passed in instead.
    */
   bool WillBeOutermostSVG(nsIContent* aParent,
-                            nsIContent* aBindingParent) const;
+                          nsIContent* aBindingParent) const;
 
   // invalidate viewbox -> viewport xform & inform frames
   void InvalidateTransformNotifyFrame();
 
   // Returns true if we have at least one of the following:
   // - a (valid or invalid) value for the preserveAspectRatio attribute
   // - a SMIL-animated value for the preserveAspectRatio attribute
   bool HasPreserveAspectRatio();
 
+ /**
+  * Returns the explicit viewBox rect, if specified, or else a synthesized
+  * viewBox, if appropriate, or else a viewBox matching the dimensions of the
+  * SVG viewport.
+  */
+  nsSVGViewBoxRect GetViewBoxWithSynthesis(
+      float aViewportWidth, float aViewportHeight) const;
+  /**
+   * Returns the explicit or default preserveAspectRatio, unless we're
+   * synthesizing a viewBox, in which case it returns the "none" value.
+   */
+  SVGPreserveAspectRatio GetPreserveAspectRatioWithOverride() const;
+
   virtual LengthAttributesInfo GetLengthInfo();
 
   enum { X, Y, WIDTH, HEIGHT };
   nsSVGLength2 mLengthAttributes[4];
   static LengthInfo sLengthInfo[4];
 
   virtual EnumAttributesInfo GetEnumInfo();
 
--- a/content/svg/content/src/nsSVGViewBox.cpp
+++ b/content/svg/content/src/nsSVGViewBox.cpp
@@ -119,26 +119,26 @@ nsSVGViewBox::SetAnimValue(float aX, flo
     mAnimVal->y = aY;
     mAnimVal->width = aWidth;
     mAnimVal->height = aHeight;
   }
   aSVGElement->DidAnimateViewBox();
 }
 
 void
-nsSVGViewBox::SetBaseValue(float aX, float aY, float aWidth, float aHeight,
+nsSVGViewBox::SetBaseValue(const nsSVGViewBoxRect& aRect,
                            nsSVGElement *aSVGElement)
 {
-  if (mHasBaseVal && mBaseVal == nsSVGViewBoxRect(aX, aY, aWidth, aHeight)) {
+  if (mHasBaseVal && mBaseVal == aRect) {
     return;
   }
 
   nsAttrValue emptyOrOldValue = aSVGElement->WillChangeViewBox();
 
-  mBaseVal = nsSVGViewBoxRect(aX, aY, aWidth, aHeight);
+  mBaseVal = aRect;
   mHasBaseVal = true;
 
   aSVGElement->DidChangeViewBox(emptyOrOldValue);
   if (mAnimVal) {
     aSVGElement->AnimationNeedsResample();
   }
 }
 
--- a/content/svg/content/src/nsSVGViewBox.h
+++ b/content/svg/content/src/nsSVGViewBox.h
@@ -81,19 +81,21 @@ public:
    * positive, so callers must check whether the viewBox rect is valid where
    * necessary!
    */
   bool IsExplicitlySet() const
     { return (mHasBaseVal || mAnimVal); }
 
   const nsSVGViewBoxRect& GetBaseValue() const
     { return mBaseVal; }
-  void SetBaseValue(float aX, float aY, float aWidth, float aHeight,
+  void SetBaseValue(const nsSVGViewBoxRect& aRect,
                     nsSVGElement *aSVGElement);
-
+  void SetBaseValue(float aX, float aY, float aWidth, float aHeight,
+                    nsSVGElement *aSVGElement)
+    { SetBaseValue(nsSVGViewBoxRect(aX, aY, aWidth, aHeight), aSVGElement); }
   const nsSVGViewBoxRect& GetAnimValue() const
     { return mAnimVal ? *mAnimVal : mBaseVal; }
   void SetAnimValue(float aX, float aY, float aWidth, float aHeight,
                     nsSVGElement *aSVGElement);
 
   nsresult SetBaseValueString(const nsAString& aValue,
                               nsSVGElement *aSVGElement);
   void GetBaseValueString(nsAString& aValue) const;
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/nsSVGViewElement.cpp
@@ -0,0 +1,169 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Mozilla SVG project.
+ *
+ * The Initial Developer of the Original Code is Robert Longson.
+ * Portions created by the Initial Developer are Copyright (C) 2012
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsSVGViewElement.h"
+#include "DOMSVGStringList.h"
+
+using namespace mozilla;
+
+nsSVGElement::StringListInfo nsSVGViewElement::sStringListInfo[1] =
+{
+  { &nsGkAtoms::viewTarget }
+};
+
+nsSVGEnumMapping nsSVGViewElement::sZoomAndPanMap[] = {
+  {&nsGkAtoms::disable, nsIDOMSVGZoomAndPan::SVG_ZOOMANDPAN_DISABLE},
+  {&nsGkAtoms::magnify, nsIDOMSVGZoomAndPan::SVG_ZOOMANDPAN_MAGNIFY},
+  {nsnull, 0}
+};
+
+nsSVGElement::EnumInfo nsSVGViewElement::sEnumInfo[1] =
+{
+  { &nsGkAtoms::zoomAndPan,
+    sZoomAndPanMap,
+    nsIDOMSVGZoomAndPan::SVG_ZOOMANDPAN_MAGNIFY
+  }
+};
+
+NS_IMPL_NS_NEW_SVG_ELEMENT(View)
+
+//----------------------------------------------------------------------
+// nsISupports methods
+
+NS_IMPL_ADDREF_INHERITED(nsSVGViewElement,nsSVGViewElementBase)
+NS_IMPL_RELEASE_INHERITED(nsSVGViewElement,nsSVGViewElementBase)
+
+DOMCI_NODE_DATA(SVGViewElement, nsSVGViewElement)
+
+NS_INTERFACE_TABLE_HEAD(nsSVGViewElement)
+  NS_NODE_INTERFACE_TABLE6(nsSVGViewElement, nsIDOMNode, nsIDOMElement,
+                           nsIDOMSVGElement, nsIDOMSVGViewElement,
+                           nsIDOMSVGFitToViewBox,
+                           nsIDOMSVGZoomAndPan)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGViewElement)
+NS_INTERFACE_MAP_END_INHERITING(nsSVGViewElementBase)
+
+//----------------------------------------------------------------------
+// Implementation
+
+nsSVGViewElement::nsSVGViewElement(already_AddRefed<nsINodeInfo> aNodeInfo)
+  : nsSVGViewElementBase(aNodeInfo)
+{
+}
+
+//----------------------------------------------------------------------
+// nsIDOMNode methods
+
+NS_IMPL_ELEMENT_CLONE_WITH_INIT(nsSVGViewElement)
+
+//----------------------------------------------------------------------
+// nsIDOMSVGZoomAndPan methods
+
+/* attribute unsigned short zoomAndPan; */
+NS_IMETHODIMP
+nsSVGViewElement::GetZoomAndPan(PRUint16 *aZoomAndPan)
+{
+  *aZoomAndPan = mEnumAttributes[ZOOMANDPAN].GetAnimValue();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSVGViewElement::SetZoomAndPan(PRUint16 aZoomAndPan)
+{
+  if (aZoomAndPan == nsIDOMSVGZoomAndPan::SVG_ZOOMANDPAN_DISABLE ||
+      aZoomAndPan == nsIDOMSVGZoomAndPan::SVG_ZOOMANDPAN_MAGNIFY) {
+    mEnumAttributes[ZOOMANDPAN].SetBaseValue(aZoomAndPan, this);
+    return NS_OK;
+  }
+
+  return NS_ERROR_DOM_SVG_INVALID_VALUE_ERR;
+}
+
+//----------------------------------------------------------------------
+// nsIDOMSVGFitToViewBox methods
+
+/* readonly attribute nsIDOMSVGAnimatedRect viewBox; */
+NS_IMETHODIMP
+nsSVGViewElement::GetViewBox(nsIDOMSVGAnimatedRect * *aViewBox)
+{
+  return mViewBox.ToDOMAnimatedRect(aViewBox, this);
+}
+
+/* readonly attribute nsIDOMSVGAnimatedPreserveAspectRatio preserveAspectRatio; */
+NS_IMETHODIMP
+nsSVGViewElement::GetPreserveAspectRatio(nsIDOMSVGAnimatedPreserveAspectRatio
+                                         **aPreserveAspectRatio)
+{
+  return mPreserveAspectRatio.ToDOMAnimatedPreserveAspectRatio(aPreserveAspectRatio, this);
+}
+
+//----------------------------------------------------------------------
+// nsIDOMSVGViewElement methods
+
+/* readonly attribute nsIDOMSVGStringList viewTarget; */
+NS_IMETHODIMP nsSVGViewElement::GetViewTarget(nsIDOMSVGStringList * *aViewTarget)
+{
+  *aViewTarget = DOMSVGStringList::GetDOMWrapper(
+                   &mStringListAttributes[VIEW_TARGET], this, false, VIEW_TARGET).get();
+  return NS_OK;
+}
+
+//----------------------------------------------------------------------
+// nsSVGElement methods
+
+nsSVGElement::EnumAttributesInfo
+nsSVGViewElement::GetEnumInfo()
+{
+  return EnumAttributesInfo(mEnumAttributes, sEnumInfo,
+                            ArrayLength(sEnumInfo));
+}
+
+nsSVGViewBox *
+nsSVGViewElement::GetViewBox()
+{
+  return &mViewBox;
+}
+
+SVGAnimatedPreserveAspectRatio *
+nsSVGViewElement::GetPreserveAspectRatio()
+{
+  return &mPreserveAspectRatio;
+}
+
+nsSVGElement::StringListAttributesInfo
+nsSVGViewElement::GetStringListInfo()
+{
+  return StringListAttributesInfo(mStringListAttributes, sStringListInfo,
+                                  ArrayLength(sStringListInfo));
+}
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/nsSVGViewElement.h
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Mozilla SVG project.
+ *
+ * The Initial Developer of the Original Code is Robert Longson.
+ * Portions created by the Initial Developer are Copyright (C) 2012
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef __NS_SVGVIEWELEMENT_H__
+#define __NS_SVGVIEWELEMENT_H__
+
+#include "nsIDOMSVGViewElement.h"
+#include "nsIDOMSVGFitToViewBox.h"
+#include "nsIDOMSVGZoomAndPan.h"
+#include "nsSVGElement.h"
+#include "nsSVGEnum.h"
+#include "nsSVGViewBox.h"
+#include "SVGAnimatedPreserveAspectRatio.h"
+#include "SVGStringList.h"
+
+namespace mozilla {
+  class SVGFragmentIdentifier;
+}
+
+typedef nsSVGElement nsSVGViewElementBase;
+
+class nsSVGViewElement : public nsSVGViewElementBase,
+                         public nsIDOMSVGViewElement,
+                         public nsIDOMSVGFitToViewBox,
+                         public nsIDOMSVGZoomAndPan
+{
+  friend class mozilla::SVGFragmentIdentifier;
+  friend nsresult NS_NewSVGViewElement(nsIContent **aResult,
+                                       already_AddRefed<nsINodeInfo> aNodeInfo);
+  nsSVGViewElement(already_AddRefed<nsINodeInfo> aNodeInfo);
+  
+public:
+  // interfaces:
+  
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_NSIDOMSVGVIEWELEMENT
+  NS_DECL_NSIDOMSVGFITTOVIEWBOX
+  NS_DECL_NSIDOMSVGZOOMANDPAN
+
+  // xxx If xpcom allowed virtual inheritance we wouldn't need to
+  // forward here :-(
+  NS_FORWARD_NSIDOMNODE(nsSVGViewElementBase::)
+  NS_FORWARD_NSIDOMELEMENT(nsSVGViewElementBase::)
+  NS_FORWARD_NSIDOMSVGELEMENT(nsSVGViewElementBase::)
+
+  virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
+
+  virtual nsXPCClassInfo* GetClassInfo();
+
+  virtual nsIDOMNode* AsDOMNode() { return this; }
+private:
+
+  // nsSVGElement overrides
+
+  virtual EnumAttributesInfo GetEnumInfo();
+
+  enum { ZOOMANDPAN };
+  nsSVGEnum mEnumAttributes[1];
+  static nsSVGEnumMapping sZoomAndPanMap[];
+  static EnumInfo sEnumInfo[1];
+
+  virtual nsSVGViewBox *GetViewBox();
+  virtual SVGAnimatedPreserveAspectRatio *GetPreserveAspectRatio();
+
+  nsSVGViewBox                   mViewBox;
+  SVGAnimatedPreserveAspectRatio mPreserveAspectRatio;
+
+  virtual StringListAttributesInfo GetStringListInfo();
+
+  enum { VIEW_TARGET };
+  SVGStringList mStringListAttributes[1];
+  static StringListInfo sStringListInfo[1];
+};
+
+#endif
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -431,16 +431,17 @@
 #include "nsIDOMSVGTitleElement.h"
 #include "nsIDOMSVGTransform.h"
 #include "nsIDOMSVGTransformable.h"
 #include "nsIDOMSVGTransformList.h"
 #include "nsIDOMSVGTSpanElement.h"
 #include "nsIDOMSVGUnitTypes.h"
 #include "nsIDOMSVGURIReference.h"
 #include "nsIDOMSVGUseElement.h"
+#include "nsIDOMSVGViewElement.h"
 #include "nsIDOMSVGZoomAndPan.h"
 #include "nsIDOMSVGZoomEvent.h"
 
 #include "nsIDOMCanvasRenderingContext2D.h"
 #include "nsIDOMWebGLRenderingContext.h"
 
 #include "nsIImageDocument.h"
 
@@ -1210,16 +1211,18 @@ static nsDOMClassInfoData sClassInfoData
   NS_DEFINE_CLASSINFO_DATA(SVGTitleElement, nsElementSH,
                            ELEMENT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(SVGTSpanElement, nsElementSH,
                            ELEMENT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA_WITH_NAME(SVGUnknownElement, SVGElement, nsElementSH,
                                      ELEMENT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(SVGUseElement, nsElementSH,
                            ELEMENT_SCRIPTABLE_FLAGS)
+  NS_DEFINE_CLASSINFO_DATA(SVGViewElement, nsElementSH,
+                           ELEMENT_SCRIPTABLE_FLAGS)
 
   // other SVG classes
   NS_DEFINE_CLASSINFO_DATA(SVGAngle, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(SVGAnimatedAngle, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(SVGAnimatedBoolean, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
@@ -3672,16 +3675,22 @@ nsDOMClassInfo::Init()
 
   DOM_CLASSINFO_MAP_BEGIN(SVGUseElement, nsIDOMSVGUseElement)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMSVGUseElement)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMSVGTests)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMSVGURIReference)
     DOM_CLASSINFO_SVG_GRAPHIC_ELEMENT_MAP_ENTRIES
   DOM_CLASSINFO_MAP_END
 
+  DOM_CLASSINFO_MAP_BEGIN(SVGViewElement, nsIDOMSVGViewElement)
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMSVGFitToViewBox)
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMSVGZoomAndPan)
+    DOM_CLASSINFO_SVG_ELEMENT_MAP_ENTRIES
+  DOM_CLASSINFO_MAP_END
+
   // other SVG classes
 
   DOM_CLASSINFO_MAP_BEGIN(SVGAngle, nsIDOMSVGAngle)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMSVGAngle)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(SVGAnimatedAngle, nsIDOMSVGAnimatedAngle)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMSVGAnimatedAngle)
--- a/dom/base/nsDOMClassInfoClasses.h
+++ b/dom/base/nsDOMClassInfoClasses.h
@@ -291,16 +291,17 @@ DOMCI_CLASS(SVGSVGElement)
 DOMCI_CLASS(SVGSwitchElement)
 DOMCI_CLASS(SVGSymbolElement)
 DOMCI_CLASS(SVGTextElement)
 DOMCI_CLASS(SVGTextPathElement)
 DOMCI_CLASS(SVGTitleElement)
 DOMCI_CLASS(SVGTSpanElement)
 DOMCI_CLASS(SVGUnknownElement)
 DOMCI_CLASS(SVGUseElement)
+DOMCI_CLASS(SVGViewElement)
 
 // other SVG classes
 DOMCI_CLASS(SVGAngle)
 DOMCI_CLASS(SVGAnimatedAngle)
 DOMCI_CLASS(SVGAnimatedBoolean)
 DOMCI_CLASS(SVGAnimatedEnumeration)
 DOMCI_CLASS(SVGAnimatedInteger)
 DOMCI_CLASS(SVGAnimatedLength)
--- a/dom/interfaces/svg/Makefile.in
+++ b/dom/interfaces/svg/Makefile.in
@@ -126,14 +126,15 @@ XPIDLSRCS	= \
 		nsIDOMSVGTitleElement.idl \
 		nsIDOMSVGTransform.idl \
 		nsIDOMSVGTransformList.idl \
 		nsIDOMSVGTransformable.idl \
 		nsIDOMSVGTSpanElement.idl \
 		nsIDOMSVGURIReference.idl \
 		nsIDOMSVGUnitTypes.idl \
 		nsIDOMSVGUseElement.idl \
+		nsIDOMSVGViewElement.idl \
 		nsIDOMSVGViewSpec.idl \
 		nsIDOMSVGZoomAndPan.idl \
 		nsIDOMSVGZoomEvent.idl \
 		$(NULL)
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/dom/interfaces/svg/nsIDOMSVGViewElement.idl
@@ -0,0 +1,57 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Mozilla SVG project.
+ *
+ * The Initial Developer of the Original Code is
+ * Robert Longson
+ * Portions created by the Initial Developer are Copyright (C) 2012
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsIDOMSVGElement.idl"
+#include "nsIDOMSVGStringList.idl"
+
+[scriptable, uuid(4b02f970-a916-4b9d-b83f-eecef658266b)]
+interface nsIDOMSVGViewElement
+  : nsIDOMSVGElement
+/*
+        The SVG DOM makes use of multiple interface inheritance.
+        Since XPCOM only supports single interface inheritance,
+        the best thing that we can do is to promise that whenever
+        an object implements _this_ interface it will also
+        implement the following interfaces. (We then have to QI to
+        hop between them.)
+
+    nsIDOMSVGExternalResourcesRequired,
+    nsIDOMSVGFitToViewBox,
+    nsIDOMSVGZoomAndPan
+*/
+{
+  readonly attribute nsIDOMSVGStringList viewTarget;
+};
+
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -150,16 +150,18 @@
 #include "nsThreadUtils.h"
 #include "nsStyleSheetService.h"
 #include "gfxImageSurface.h"
 #include "gfxContext.h"
 #ifdef MOZ_MEDIA
 #include "nsHTMLMediaElement.h"
 #endif
 #include "nsSMILAnimationController.h"
+#include "nsSVGUtils.h"
+#include "SVGFragmentIdentifier.h"
 
 #include "nsRefreshDriver.h"
 
 // Drag & Drop, Clipboard
 #include "nsWidgetsCID.h"
 #include "nsIClipboard.h"
 #include "nsIClipboardHelper.h"
 #include "nsIDocShellTreeItem.h"
@@ -2743,16 +2745,25 @@ PresShell::GetReferenceRenderingContext(
 
 nsresult
 PresShell::GoToAnchor(const nsAString& aAnchorName, bool aScroll)
 {
   if (!mDocument) {
     return NS_ERROR_FAILURE;
   }
   
+  const Element *root = mDocument->GetRootElement();
+  if (root && root->IsSVG(nsGkAtoms::svg)) {
+    // We need to execute this even if there is an empty anchor name
+    // so that any existing SVG fragment identifier effect is removed
+    if (SVGFragmentIdentifier::ProcessFragmentIdentifier(mDocument, aAnchorName)) {
+      return NS_OK;
+    }
+  }
+
   // Hold a reference to the ESM in case event dispatch tears us down.
   nsRefPtr<nsEventStateManager> esm = mPresContext->EventStateManager();
 
   if (aAnchorName.IsEmpty()) {
     NS_ASSERTION(!aScroll, "can't scroll to empty anchor name");
     esm->SetContentState(nsnull, NS_EVENT_STATE_URLTARGET);
     return NS_OK;
   }
@@ -2875,16 +2886,21 @@ PresShell::GoToAnchor(const nsAString& a
     nsIFocusManager* fm = nsFocusManager::GetFocusManager();
     if (fm && win) {
       nsCOMPtr<nsIDOMWindow> focusedWindow;
       fm->GetFocusedWindow(getter_AddRefs(focusedWindow));
       if (SameCOMIdentity(win, focusedWindow)) {
         fm->ClearFocus(focusedWindow);
       }
     }
+
+    // If the target is an animation element, activate the animation
+    if (content->IsNodeOfType(nsINode::eANIMATION)) {
+      nsSVGUtils::ActivateByHyperlink(content.get());
+    }
   } else {
     rv = NS_ERROR_FAILURE;
     NS_NAMED_LITERAL_STRING(top, "top");
     if (nsContentUtils::EqualsIgnoreASCIICase(aAnchorName, top)) {
       // Scroll to the top/left if aAnchorName is "top" and there is no element
       // with such a name or id.
       rv = NS_OK;
       nsIScrollableFrame* sf = GetRootScrollFrameAsScrollable();
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/fragmentIdentifier-01.xhtml
@@ -0,0 +1,11 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <title>Testcases for SVG fragment identifiers</title>
+  </head>
+  <body style="background-color: lime;">
+    <div>
+      <object type="image/svg+xml" width="100" height="100" data="fragmentIdentifier-rect-01.svg#limeView" />
+      <object type="image/svg+xml" width="100" height="100" data="fragmentIdentifier-rect-01.svg#svgView(viewBox(0,200,100,100))" />
+    </div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/fragmentIdentifier-rect-01.svg
@@ -0,0 +1,9 @@
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+
+  <view id="limeView" viewBox="0 200 100 100"/>
+
+  <rect fill="red" height="100%" width="100%"/>
+  <rect fill="lime" x="0" y="200" height="100" width="100"/>
+
+</svg>
--- a/layout/reftests/svg/reftest.list
+++ b/layout/reftests/svg/reftest.list
@@ -106,16 +106,17 @@ random-if(/^Windows\x20NT\x205\.1/.test(
 == dynamic-use-01.svg pass.svg
 == dynamic-use-02.svg pass.svg
 == dynamic-use-03.svg pass.svg
 == dynamic-use-04.svg pass.svg
 == dynamic-use-05.svg pass.svg
 == dynamic-use-06.svg pass.svg
 random == dynamic-use-nested-01.svg dynamic-use-nested-01-ref.svg # bug 467498
 == dynamic-use-remove-width.svg dynamic-use-remove-width-ref.svg
+== fragmentIdentifier-01.xhtml pass.svg
 == linked-filter-01.svg pass.svg
 == linked-pattern-01.svg pass.svg
 == use-01.svg pass.svg
 == use-01-extref.svg pass.svg
 == use-02-extref.svg use-02-extref-ref.svg
 == use-extref-dataURI-01.svg pass.svg
 == use-children.svg pass.svg
 == fallback-color-01a.svg pass.svg
--- a/layout/svg/base/src/Makefile.in
+++ b/layout/svg/base/src/Makefile.in
@@ -101,16 +101,17 @@ include $(topsrcdir)/config/rules.mk
 
 LOCAL_INCLUDES	= \
 		-I$(srcdir)/../../../base \
 		-I$(srcdir)/../../../generic \
 		-I$(srcdir)/../../../style \
 		-I$(srcdir)/../../../xul/base/src \
 		-I$(srcdir)/../../../../content/svg/content/src \
 		-I$(srcdir)/../../../../content/base/src \
+		-I$(srcdir)/../../../../content/smil \
 		$(NULL)
 
 libs::
 	$(INSTALL) $(srcdir)/svg.css $(DIST)/bin/res
 
 install::
 	$(SYSINSTALL) $(IFLAGS1) $(srcdir)/svg.css $(DESTDIR)$(mozappdir)/res
 
--- a/layout/svg/base/src/nsSVGOuterSVGFrame.cpp
+++ b/layout/svg/base/src/nsSVGOuterSVGFrame.cpp
@@ -765,27 +765,19 @@ nsSVGOuterSVGFrame::GetCanvasTM()
 {
   if (!mCanvasTM) {
     nsSVGSVGElement *content = static_cast<nsSVGSVGElement*>(mContent);
 
     float devPxPerCSSPx =
       1.0f / PresContext()->AppUnitsToFloatCSSPixels(
                                 PresContext()->AppUnitsPerDevPixel());
 
-    gfxMatrix viewBoxTM = content->GetViewBoxTransform();
-
-    gfxMatrix zoomPanTM;
-    if (mIsRootContent) {
-      const nsSVGTranslatePoint& translate = content->GetCurrentTranslate();
-      zoomPanTM.Translate(gfxPoint(translate.GetX(), translate.GetY()));
-      zoomPanTM.Scale(content->GetCurrentScale(), content->GetCurrentScale());
-    }
-
-    gfxMatrix TM = viewBoxTM * zoomPanTM * gfxMatrix().Scale(devPxPerCSSPx, devPxPerCSSPx);
-    mCanvasTM = new gfxMatrix(TM);
+    gfxMatrix tm = content->PrependLocalTransformsTo(
+                     gfxMatrix().Scale(devPxPerCSSPx, devPxPerCSSPx));
+    mCanvasTM = new gfxMatrix(tm);
   }
   return *mCanvasTM;
 }
 
 bool
 nsSVGOuterSVGFrame::HasChildrenOnlyTransform(gfxMatrix *aTransform) const
 {
   nsSVGSVGElement *content = static_cast<nsSVGSVGElement*>(mContent);
--- a/layout/svg/base/src/nsSVGUtils.cpp
+++ b/layout/svg/base/src/nsSVGUtils.cpp
@@ -60,16 +60,17 @@
 #include "nsINameSpaceManager.h"
 #include "nsIPresShell.h"
 #include "nsIScriptError.h"
 #include "nsISVGChildFrame.h"
 #include "nsPresContext.h"
 #include "nsRenderingContext.h"
 #include "nsStyleCoord.h"
 #include "nsStyleStruct.h"
+#include "nsSVGAnimationElement.h"
 #include "nsSVGClipPathFrame.h"
 #include "nsSVGContainerFrame.h"
 #include "nsSVGEffects.h"
 #include "nsSVGFilterFrame.h"
 #include "nsSVGFilterPaintCallback.h"
 #include "nsSVGForeignObjectFrame.h"
 #include "nsSVGGeometryFrame.h"
 #include "nsSVGInnerSVGFrame.h"
@@ -260,16 +261,25 @@ nsSVGUtils::GetOuterSVGElement(nsSVGElem
   }
 
   if (element && element->Tag() == nsGkAtoms::svg) {
     return static_cast<nsSVGSVGElement*>(element);
   }
   return nsnull;
 }
 
+void
+nsSVGUtils::ActivateByHyperlink(nsIContent *aContent)
+{
+  NS_ABORT_IF_FALSE(aContent->IsNodeOfType(nsINode::eANIMATION),
+                    "Expecting an animation element");
+
+  static_cast<nsSVGAnimationElement*>(aContent)->ActivateByHyperlink();
+}
+
 float
 nsSVGUtils::GetFontSize(Element *aElement)
 {
   if (!aElement)
     return 1.0f;
 
   nsRefPtr<nsStyleContext> styleContext = 
     nsComputedDOMStyle::GetStyleContextForElementNoFlush(aElement,
--- a/layout/svg/base/src/nsSVGUtils.h
+++ b/layout/svg/base/src/nsSVGUtils.h
@@ -236,16 +236,26 @@ public:
    */
   static mozilla::dom::Element *GetParentElement(nsIContent *aContent);
 
   /*
    * Get the outer SVG element of an nsIContent
    */
   static nsSVGSVGElement *GetOuterSVGElement(nsSVGElement *aSVGElement);
 
+  /**
+   * Activates the animation element aContent as a result of navigation to the
+   * fragment identifier that identifies aContent. aContent must be an instance
+   * of nsSVGAnimationElement.
+   *
+   * This is just a shim to allow nsSVGAnimationElement::ActivateByHyperlink to
+   * be called from layout/base without adding to that directory's include paths.
+   */
+  static void ActivateByHyperlink(nsIContent *aContent);
+
   /*
    * Get the number of CSS px (user units) per em (i.e. the em-height in user
    * units) for an nsIContent
    *
    * XXX document the conditions under which these may fail, and what they
    * return in those cases.
    */
   static float GetFontSize(mozilla::dom::Element *aElement);
--- a/xpcom/ds/Makefile.in
+++ b/xpcom/ds/Makefile.in
@@ -105,16 +105,17 @@ EXPORTS		= \
 		nsStaticAtom.h \
 		nsSupportsArray.h \
 		nsSupportsPrimitives.h \
 		nsVariant.h \
 		nsStringEnumerator.h \
 		nsHashPropertyBag.h \
 		nsWhitespaceTokenizer.h \
 		nsCharSeparatedTokenizer.h \
+		CharTokenizer.h \
 		$(NULL)			
 
 XPIDLSRCS	= \
 		nsIAtom.idl \
 		nsIAtomService.idl \
 		nsICollection.idl \
 		nsIEnumerator.idl \
 		nsIINIParser.idl \