Merge last PGO-green inbound changeset to m-c.
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 17 May 2012 14:01:53 -0400
changeset 94221 895e12563245f4b082279e59e2b2fdd34ad0ca1e
parent 94201 f2b2b99108a20379283763389aae595f463aa1a4 (current diff)
parent 94220 b98aee2cdf4fd32116e726f77c4b2a397c97167e (diff)
child 94222 e34babb3039307fc7904fc13982874b3838e3a7c
push id22702
push userryanvm@gmail.com
push dateThu, 17 May 2012 18:02:05 +0000
treeherdermozilla-central@895e12563245 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
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
Merge last PGO-green inbound changeset to m-c.
js/src/jit-test/tests/basic/testCrossGlobalInvokeSession.js
js/src/jit-test/tests/basic/testReconstructImacroPCStack.js
--- a/accessible/src/base/nsAccessible.cpp
+++ b/accessible/src/base/nsAccessible.cpp
@@ -741,23 +741,31 @@ nsAccessible::NativeState()
   if (frame && (frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW))
     state |= states::FLOATING;
 
   // Check if a XUL element has the popup attribute (an attached popup menu).
   if (mContent->IsXUL())
     if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::popup))
       state |= states::HASPOPUP;
 
-  // Add 'linked' state for simple xlink.
-  if (nsCoreUtils::IsXLink(mContent))
-    state |= states::LINKED;
+  // Bypass the link states specialization for non links.
+  if (!mRoleMapEntry || mRoleMapEntry->roleRule == kUseNativeRole ||
+      mRoleMapEntry->role == roles::LINK)
+    state |= NativeLinkState();
 
   return state;
 }
 
+PRUint64
+nsAccessible::NativeLinkState() const
+{
+  // Expose linked state for simple xlink.
+  return nsCoreUtils::IsXLink(mContent) ? states::LINKED : 0;
+}
+
   /* readonly attribute boolean focusedChild; */
 NS_IMETHODIMP
 nsAccessible::GetFocusedChild(nsIAccessible** aChild)
 {
   NS_ENSURE_ARG_POINTER(aChild);
   *aChild = nsnull;
 
   if (IsDefunct())
@@ -1613,17 +1621,17 @@ nsAccessible::State()
       state |= states::HORIZONTAL;
     }
   }
 
   return state;
 }
 
 void
-nsAccessible::ApplyARIAState(PRUint64* aState)
+nsAccessible::ApplyARIAState(PRUint64* aState) const
 {
   if (!mContent->IsElement())
     return;
 
   dom::Element* element = mContent->AsElement();
 
   // Test for universal states first
   *aState |= nsARIAMap::UniversalStatesFor(element);
--- a/accessible/src/base/nsAccessible.h
+++ b/accessible/src/base/nsAccessible.h
@@ -180,17 +180,17 @@ public:
 
   /**
    * Maps ARIA state attributes to state of accessible. Note the given state
    * argument should hold states for accessible before you pass it into this
    * method.
    *
    * @param  [in/out] where to fill the states into.
    */
-  virtual void ApplyARIAState(PRUint64* aState);
+  virtual void ApplyARIAState(PRUint64* aState) const;
 
   /**
    * Returns the accessible name provided by native markup. It doesn't take
    * into account ARIA markup used to specify the name.
    *
    * @param  aName             [out] the accessible name
    *
    * @return NS_OK_EMPTY_NAME  points empty name was specified by native markup
@@ -223,22 +223,37 @@ public:
   virtual mozilla::a11y::role NativeRole();
 
   /**
    * Return all states of accessible (including ARIA states).
    */
   virtual PRUint64 State();
 
   /**
+   * Return link states present on the accessible.
+   */
+  PRUint64 LinkState() const
+  {
+    PRUint64 state = NativeLinkState();
+    ApplyARIAState(&state);
+    return state;
+  }
+
+  /**
    * Return the states of accessible, not taking into account ARIA states.
    * Use State() to get complete set of states.
    */
   virtual PRUint64 NativeState();
 
   /**
+   * Return native link states present on the accessible.
+   */
+  virtual PRUint64 NativeLinkState() const;
+
+  /**
    * Return bit set of invisible and offscreen states.
    */
   PRUint64 VisibilityState();
 
   /**
    * Returns attributes for accessible without explicitly setted ARIA
    * attributes.
    */
--- a/accessible/src/base/nsBaseWidgetAccessible.cpp
+++ b/accessible/src/base/nsBaseWidgetAccessible.cpp
@@ -108,26 +108,22 @@ NS_IMPL_ISUPPORTS_INHERITED0(nsLinkableA
 
 NS_IMETHODIMP
 nsLinkableAccessible::TakeFocus()
 {
   return mActionAcc ? mActionAcc->TakeFocus() : nsAccessibleWrap::TakeFocus();
 }
 
 PRUint64
-nsLinkableAccessible::NativeState()
+nsLinkableAccessible::NativeLinkState() const
 {
-  PRUint64 states = nsAccessibleWrap::NativeState();
-  if (mIsLink) {
-    states |= states::LINKED;
-    if (mActionAcc->State() & states::TRAVERSED)
-      states |= states::TRAVERSED;
-  }
+  if (mIsLink)
+    return states::LINKED | (mActionAcc->LinkState() & states::TRAVERSED);
 
-  return states;
+  return 0;
 }
 
 void
 nsLinkableAccessible::Value(nsString& aValue)
 {
   aValue.Truncate();
 
   nsAccessible::Value(aValue);
@@ -230,21 +226,20 @@ nsLinkableAccessible::BindToParent(nsAcc
     return;
   }
 
   // XXX: The logic looks broken since the click listener may be registered
   // on non accessible node in parent chain but this node is skipped when tree
   // is traversed.
   nsAccessible* walkUpAcc = this;
   while ((walkUpAcc = walkUpAcc->Parent()) && !walkUpAcc->IsDoc()) {
-    if (walkUpAcc->Role() == roles::LINK &&
-        walkUpAcc->State() & states::LINKED) {
-        mIsLink = true;
-        mActionAcc = walkUpAcc;
-        return;
+    if (walkUpAcc->LinkState() & states::LINKED) {
+      mIsLink = true;
+      mActionAcc = walkUpAcc;
+      return;
     }
 
     if (nsCoreUtils::HasClickListener(walkUpAcc->GetContent())) {
       mActionAcc = walkUpAcc;
       mIsOnclick = true;
       return;
     }
   }
--- a/accessible/src/base/nsBaseWidgetAccessible.h
+++ b/accessible/src/base/nsBaseWidgetAccessible.h
@@ -91,17 +91,17 @@ public:
   NS_IMETHOD DoAction(PRUint8 index);
   NS_IMETHOD TakeFocus();
 
   // nsAccessNode
   virtual void Shutdown();
 
   // nsAccessible
   virtual void Value(nsString& aValue);
-  virtual PRUint64 NativeState();
+  virtual PRUint64 NativeLinkState() const;
 
   // ActionAccessible
   virtual PRUint8 ActionCount();
   virtual KeyBinding AccessKey() const;
 
   // HyperLinkAccessible
   virtual already_AddRefed<nsIURI> AnchorURIAt(PRUint32 aAnchorIndex);
 
--- a/accessible/src/base/nsDocAccessible.cpp
+++ b/accessible/src/base/nsDocAccessible.cpp
@@ -336,17 +336,17 @@ nsDocAccessible::NativeState()
   nsCOMPtr<nsIEditor> editor = GetEditor();
   state |= editor ? states::EDITABLE : states::READONLY;
 
   return state;
 }
 
 // nsAccessible public method
 void
-nsDocAccessible::ApplyARIAState(PRUint64* aState)
+nsDocAccessible::ApplyARIAState(PRUint64* aState) const
 {
   // Combine with states from outer doc
   // 
   nsAccessible::ApplyARIAState(aState);
 
   // Allow iframe/frame etc. to have final state override via ARIA
   if (mParent)
     mParent->ApplyARIAState(aState);
--- a/accessible/src/base/nsDocAccessible.h
+++ b/accessible/src/base/nsDocAccessible.h
@@ -110,17 +110,17 @@ public:
   virtual nsIDocument* GetDocumentNode() const { return mDocument; }
 
   // nsAccessible
   virtual mozilla::a11y::ENameValueFlag Name(nsString& aName);
   virtual void Description(nsString& aDescription);
   virtual nsAccessible* FocusedChild();
   virtual mozilla::a11y::role NativeRole();
   virtual PRUint64 NativeState();
-  virtual void ApplyARIAState(PRUint64* aState);
+  virtual void ApplyARIAState(PRUint64* aState) const;
 
   virtual void SetRoleMapEntry(nsRoleMapEntry* aRoleMapEntry);
 
 #ifdef DEBUG_ACCDOCMGR
   virtual nsresult HandleAccEvent(AccEvent* aAccEvent);
 #endif
 
   virtual void GetBoundsRect(nsRect& aRect, nsIFrame** aRelativeFrame);
--- a/accessible/src/generic/ARIAGridAccessible.cpp
+++ b/accessible/src/generic/ARIAGridAccessible.cpp
@@ -1040,17 +1040,17 @@ ARIAGridCellAccessible::IsSelected(bool*
   *aIsSelected = true;
   return NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccessible
 
 void
-ARIAGridCellAccessible::ApplyARIAState(PRUint64* aState)
+ARIAGridCellAccessible::ApplyARIAState(PRUint64* aState) const
 {
   nsHyperTextAccessibleWrap::ApplyARIAState(aState);
 
   // Return if the gridcell has aria-selected="true".
   if (*aState & states::SELECTED)
     return;
 
   // Check aria-selected="true" on the row.
--- a/accessible/src/generic/ARIAGridAccessible.h
+++ b/accessible/src/generic/ARIAGridAccessible.h
@@ -133,16 +133,16 @@ public:
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIAccessibleTableCell
   NS_DECL_NSIACCESSIBLETABLECELL
 
   // nsAccessible
-  virtual void ApplyARIAState(PRUint64* aState);
+  virtual void ApplyARIAState(PRUint64* aState) const;
   virtual nsresult GetAttributesInternal(nsIPersistentProperties *aAttributes);
 };
 
 } // namespace a11y
 } // namespace mozilla
 
 #endif
--- a/accessible/src/generic/ApplicationAccessible.cpp
+++ b/accessible/src/generic/ApplicationAccessible.cpp
@@ -328,17 +328,17 @@ ApplicationAccessible::IsPrimaryForNode(
 {
   return false;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccessible public methods
 
 void
-ApplicationAccessible::ApplyARIAState(PRUint64* aState)
+ApplicationAccessible::ApplyARIAState(PRUint64* aState) const
 {
 }
 
 role
 ApplicationAccessible::NativeRole()
 {
   return roles::APP_ROOT;
 }
--- a/accessible/src/generic/ApplicationAccessible.h
+++ b/accessible/src/generic/ApplicationAccessible.h
@@ -99,17 +99,17 @@ public:
 
   // nsAccessNode
   virtual bool Init();
   virtual void Shutdown();
   virtual bool IsPrimaryForNode() const;
 
   // nsAccessible
   virtual mozilla::a11y::ENameValueFlag Name(nsString& aName);
-  virtual void ApplyARIAState(PRUint64* aState);
+  virtual void ApplyARIAState(PRUint64* aState) const;
   virtual void Description(nsString& aDescription);
   virtual void Value(nsString& aValue);
   virtual mozilla::a11y::role NativeRole();
   virtual PRUint64 State();
   virtual PRUint64 NativeState();
   virtual Relation RelationByType(PRUint32 aRelType);
 
   virtual nsAccessible* ChildAtPoint(PRInt32 aX, PRInt32 aY,
--- a/accessible/src/html/HTMLFormControlAccessible.cpp
+++ b/accessible/src/html/HTMLFormControlAccessible.cpp
@@ -432,17 +432,17 @@ HTMLTextFieldAccessible::Value(nsString&
   
   nsCOMPtr<nsIDOMHTMLInputElement> inputElement(do_QueryInterface(mContent));
   if (inputElement) {
     inputElement->GetValue(aValue);
   }
 }
 
 void
-HTMLTextFieldAccessible::ApplyARIAState(PRUint64* aState)
+HTMLTextFieldAccessible::ApplyARIAState(PRUint64* aState) const
 {
   nsHyperTextAccessibleWrap::ApplyARIAState(aState);
 
   aria::MapToState(aria::eARIAAutoComplete, mContent->AsElement(), aState);
 }
 
 PRUint64
 HTMLTextFieldAccessible::State()
--- a/accessible/src/html/HTMLFormControlAccessible.h
+++ b/accessible/src/html/HTMLFormControlAccessible.h
@@ -140,17 +140,17 @@ public:
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
   NS_IMETHOD DoAction(PRUint8 index);
 
   // nsHyperTextAccessible
   virtual already_AddRefed<nsIEditor> GetEditor() const;
 
   // nsAccessible
   virtual void Value(nsString& aValue);
-  virtual void ApplyARIAState(PRUint64* aState);
+  virtual void ApplyARIAState(PRUint64* aState) const;
   virtual nsresult GetNameInternal(nsAString& aName);
   virtual mozilla::a11y::role NativeRole();
   virtual PRUint64 State();
   virtual PRUint64 NativeState();
 
   // ActionAccessible
   virtual PRUint8 ActionCount();
 
--- a/accessible/src/html/nsHTMLImageMapAccessible.cpp
+++ b/accessible/src/html/nsHTMLImageMapAccessible.cpp
@@ -226,29 +226,16 @@ nsHTMLAreaAccessible::IsPrimaryForNode()
   // Make HTML area DOM element not accessible. HTML image map accessible
   // manages its tree itself.
   return false;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsHTMLAreaAccessible: nsAccessible public
 
-PRUint64
-nsHTMLAreaAccessible::NativeState()
-{
-  // Bypass the link states specialization for non links.
-  if (mRoleMapEntry &&
-      mRoleMapEntry->role != roles::NOTHING &&
-      mRoleMapEntry->role != roles::LINK) {
-    return nsAccessible::NativeState();
-  }
-
-  return nsHTMLLinkAccessible::NativeState();
-}
-
 nsAccessible*
 nsHTMLAreaAccessible::ChildAtPoint(PRInt32 aX, PRInt32 aY,
                                    EWhichChildAtPoint aWhichChild)
 {
   // Don't walk into area accessibles.
   return this;
 }
 
--- a/accessible/src/html/nsHTMLImageMapAccessible.h
+++ b/accessible/src/html/nsHTMLImageMapAccessible.h
@@ -97,17 +97,16 @@ public:
   nsHTMLAreaAccessible(nsIContent* aContent, nsDocAccessible* aDoc);
 
   // nsAccessNode
   virtual bool IsPrimaryForNode() const;
 
   // nsAccessible
   virtual void Description(nsString& aDescription);
   virtual nsresult GetNameInternal(nsAString& aName);
-  virtual PRUint64 NativeState();
   virtual nsAccessible* ChildAtPoint(PRInt32 aX, PRInt32 aY,
                                      EWhichChildAtPoint aWhichChild);
   virtual void GetBoundsRect(nsRect& aBounds, nsIFrame** aBoundingFrame);
 
   // HyperLinkAccessible
   virtual PRUint32 StartOffset();
   virtual PRUint32 EndOffset();
 
--- a/accessible/src/html/nsHTMLLinkAccessible.cpp
+++ b/accessible/src/html/nsHTMLLinkAccessible.cpp
@@ -81,34 +81,33 @@ nsHTMLLinkAccessible::NativeState()
 
   if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::name)) {
     // This is how we indicate it is a named anchor
     // In other words, this anchor can be selected as a location :)
     // There is no other better state to use to indicate this.
     states |= states::SELECTABLE;
   }
 
-  nsEventStates state = mContent->AsElement()->State();
-  if (state.HasAtLeastOneOfStates(NS_EVENT_STATE_VISITED |
-                                  NS_EVENT_STATE_UNVISITED)) {
-    states |= states::LINKED;
+  return states;
+}
 
-    if (state.HasState(NS_EVENT_STATE_VISITED))
-      states |= states::TRAVERSED;
+PRUint64
+nsHTMLLinkAccessible::NativeLinkState() const
+{
+  nsEventStates eventState = mContent->AsElement()->State();
+  if (eventState.HasState(NS_EVENT_STATE_UNVISITED))
+    return states::LINKED;
 
-    return states;
-  }
+  if (eventState.HasState(NS_EVENT_STATE_VISITED))
+    return states::LINKED | states::TRAVERSED;
 
   // This is a either named anchor (a link with also a name attribute) or
   // it doesn't have any attributes. Check if 'click' event handler is
   // registered, otherwise bail out.
-  if (nsCoreUtils::HasClickListener(mContent))
-    states |= states::LINKED;
-
-  return states;
+  return nsCoreUtils::HasClickListener(mContent) ? states::LINKED : 0;
 }
 
 void
 nsHTMLLinkAccessible::Value(nsString& aValue)
 {
   aValue.Truncate();
 
   nsHyperTextAccessible::Value(aValue);
--- a/accessible/src/html/nsHTMLLinkAccessible.h
+++ b/accessible/src/html/nsHTMLLinkAccessible.h
@@ -52,16 +52,17 @@ public:
   // nsIAccessible
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
   NS_IMETHOD DoAction(PRUint8 aIndex);
 
   // nsAccessible
   virtual void Value(nsString& aValue);
   virtual mozilla::a11y::role NativeRole();
   virtual PRUint64 NativeState();
+  virtual PRUint64 NativeLinkState() const;
 
   // ActionAccessible
   virtual PRUint8 ActionCount();
 
   // HyperLinkAccessible
   virtual bool IsLink();
   virtual already_AddRefed<nsIURI> AnchorURIAt(PRUint32 aAnchorIndex);
 
--- a/accessible/src/xul/XULFormControlAccessible.cpp
+++ b/accessible/src/xul/XULFormControlAccessible.cpp
@@ -749,22 +749,21 @@ XULTextFieldAccessible::Value(nsString& 
   }
 
   nsCOMPtr<nsIDOMXULMenuListElement> menuList(do_QueryInterface(mContent));
   if (menuList)
     menuList->GetLabel(aValue);
 }
 
 void
-XULTextFieldAccessible::ApplyARIAState(PRUint64* aState)
+XULTextFieldAccessible::ApplyARIAState(PRUint64* aState) const
 {
   nsHyperTextAccessibleWrap::ApplyARIAState(aState);
 
   aria::MapToState(aria::eARIAAutoComplete, mContent->AsElement(), aState);
-
 }
 
 PRUint64
 XULTextFieldAccessible::NativeState()
 {
   PRUint64 state = nsHyperTextAccessibleWrap::NativeState();
 
   nsCOMPtr<nsIContent> inputField(GetInputField());
--- a/accessible/src/xul/XULFormControlAccessible.h
+++ b/accessible/src/xul/XULFormControlAccessible.h
@@ -260,17 +260,17 @@ public:
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
   NS_IMETHOD DoAction(PRUint8 index);
 
   // nsHyperTextAccessible
   virtual already_AddRefed<nsIEditor> GetEditor() const;
 
   // nsAccessible
   virtual void Value(nsString& aValue);
-  virtual void ApplyARIAState(PRUint64* aState);
+  virtual void ApplyARIAState(PRUint64* aState) const;
   virtual mozilla::a11y::role NativeRole();
   virtual PRUint64 NativeState();
   virtual bool CanHaveAnonChildren();
 
   // ActionAccessible
   virtual PRUint8 ActionCount();
 
 protected:
--- a/accessible/src/xul/nsXULTextAccessible.cpp
+++ b/accessible/src/xul/nsXULTextAccessible.cpp
@@ -173,19 +173,19 @@ nsXULLinkAccessible::GetNameInternal(nsA
 role
 nsXULLinkAccessible::NativeRole()
 {
   return roles::LINK;
 }
 
 
 PRUint64
-nsXULLinkAccessible::NativeState()
+nsXULLinkAccessible::NativeLinkState() const
 {
-  return nsHyperTextAccessible::NativeState() | states::LINKED;
+  return states::LINKED;
 }
 
 PRUint8
 nsXULLinkAccessible::ActionCount()
 {
   return 1;
 }
 
--- a/accessible/src/xul/nsXULTextAccessible.h
+++ b/accessible/src/xul/nsXULTextAccessible.h
@@ -83,17 +83,17 @@ public:
   // nsIAccessible
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
   NS_IMETHOD DoAction(PRUint8 aIndex);
 
   // nsAccessible
   virtual void Value(nsString& aValue);
   virtual nsresult GetNameInternal(nsAString& aName);
   virtual mozilla::a11y::role NativeRole();
-  virtual PRUint64 NativeState();
+  virtual PRUint64 NativeLinkState() const;
 
   // ActionAccessible
   virtual PRUint8 ActionCount();
 
   // HyperLinkAccessible
   virtual bool IsLink();
   virtual PRUint32 StartOffset();
   virtual PRUint32 EndOffset();
--- a/accessible/tests/mochitest/states/test_link.html
+++ b/accessible/tests/mochitest/states/test_link.html
@@ -3,54 +3,99 @@
 <head>
   <title>HTML link states testing</title>
 
   <link rel="stylesheet" type="text/css"
         href="chrome://mochikit/content/tests/SimpleTest/test.css" />
 
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
 
   <script type="application/javascript"
           src="../common.js"></script>
   <script type="application/javascript"
           src="../role.js"></script>
   <script type="application/javascript"
           src="../states.js"></script>
+  <script type="application/javascript"
+          src="../events.js"></script>
 
   <script type="application/javascript">
     function doTest()
     {
-      testStates("link1", STATE_LINKED);
-      testStates("link2", STATE_LINKED);
-      testStates("link3", STATE_LINKED);
-      testStates("link4", STATE_LINKED);
-      testStates("link5", 0, 0, STATE_LINKED);
+      // a@href and its text node
+      testStates("link_href", STATE_LINKED);
+      testStates(getAccessible("link_href").firstChild, STATE_LINKED);
+
+      // a@onclick
+      testStates("link_click", STATE_LINKED);
+
+      // a@onmousedown
+      testStates("link_mousedown", STATE_LINKED);
+
+      // a@onmouseup
+      testStates("link_mouseup", STATE_LINKED);
+
+      // a@role="link"
+      testStates("link_arialink", STATE_LINKED);
+
+      // a@role="button"
+      testStates("link_ariabutton", 0, 0, STATE_LINKED);
 
-      SimpleTest.finish();
+      // a (no @href, no click event listener)
+      testStates("link_notlink", 0, 0, STATE_LINKED);
+
+      // a: traversal state
+      testStates("link_traversed", 0, 0, STATE_TRAVERSED);
+      registerA11yEventListener(EVENT_DOCUMENT_LOAD_COMPLETE,
+                                traversedLinkTester);
+
+      synthesizeMouse(getNode("link_traversed"), 1, 1, { shiftKey: true });
     }
 
+    var traversedLinkTester = {
+      handleEvent: function traversedLinkTester_handleEvent(aEvent) {
+        unregisterA11yEventListener(EVENT_DOCUMENT_LOAD_COMPLETE,
+                                    traversedLinkTester);
+        aEvent.accessible.rootDocument.window.close();
+
+        testStates("link_traversed", STATE_TRAVERSED);
+        SimpleTest.finish();
+      }
+    };
+
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 
 </head>
 
 <body>
 
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=423409"
      title="Expose click action if mouseup and mousedown are registered">
     Mozilla Bug 423409
   </a>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=754830"
+     title="Calculate link states separately">
+    Mozilla Bug 754830
+  </a>
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
-  <a id="link1" href="http://mozilla.org">link</a>
-  <a id="link2" onclick="">link</a>
-  <a id="link3" onmousedown="">link</a>
-  <a id="link4" onmouseup="">link</a>
-  <a id="link5">not link</a>
+  <a id="link_href" href="http://mozilla.org">link</a>
+  <a id="link_click" onclick="">link</a>
+  <a id="link_mousedown" onmousedown="">link</a>
+  <a id="link_mouseup" onmouseup="">link</a>
+  <a id="link_arialink" role="link">aria link</a>
+  <a id="link_ariabutton" role="button">aria button</a>
+  <a id="link_notlink">not link</a>
+
+  <a id="link_traversed" href="http://www.example.com" target="_top">example.com</a>
 
 </body>
 </html>
--- 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/events/public/Makefile.in
+++ b/content/events/public/Makefile.in
@@ -49,16 +49,17 @@ EXPORTS		= \
 		nsMutationEvent.h \
 		nsIPrivateDOMEvent.h \
 		nsIPrivateTextEvent.h \
 		nsIPrivateTextRange.h \
 		nsAsyncDOMEvent.h \
 		nsEventDispatcher.h \
 		nsEventStates.h \
 		nsEventNameList.h \
+		nsVKList.h \
 		$(NULL)
 
 XPIDLSRCS	= \
 		nsIEventListenerService.idl \
 		$(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
new file mode 100644
--- /dev/null
+++ b/content/events/public/nsVKList.h
@@ -0,0 +1,191 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * This header file defines all DOM keys which are defined in nsIDOMKeyEvent.
+ * You must define NS_DEFINE_VK macro before including this.
+ *
+ * It must have two arguments, (aDOMKeyName, aDOMKeyCode)
+ * aDOMKeyName is a key name in DOM.
+ * aDOMKeyCode is one of nsIDOMKeyEvent::DOM_VK_*.
+ */
+
+#define DEFINE_VK_INTERNAL(aKeyName) \
+  NS_DEFINE_VK(VK##aKeyName, nsIDOMKeyEvent::DOM_VK##aKeyName)
+
+// Some keycode may have different name in nsIDOMKeyEvent from its key name.
+#define DEFINE_VK_INTERNAL2(aKeyName, aKeyCodeName) \
+  NS_DEFINE_VK(VK##aKeyName, nsIDOMKeyEvent::DOM_VK##aKeyCodeName)
+
+DEFINE_VK_INTERNAL(_CANCEL),
+DEFINE_VK_INTERNAL(_HELP),
+DEFINE_VK_INTERNAL2(_BACK, _BACK_SPACE),
+DEFINE_VK_INTERNAL(_TAB),
+DEFINE_VK_INTERNAL(_CLEAR),
+DEFINE_VK_INTERNAL(_RETURN),
+DEFINE_VK_INTERNAL(_ENTER),
+DEFINE_VK_INTERNAL(_SHIFT),
+DEFINE_VK_INTERNAL(_CONTROL),
+DEFINE_VK_INTERNAL(_ALT),
+DEFINE_VK_INTERNAL(_PAUSE),
+DEFINE_VK_INTERNAL(_CAPS_LOCK),
+DEFINE_VK_INTERNAL(_KANA),
+DEFINE_VK_INTERNAL(_HANGUL),
+DEFINE_VK_INTERNAL(_EISU),
+DEFINE_VK_INTERNAL(_JUNJA),
+DEFINE_VK_INTERNAL(_FINAL),
+DEFINE_VK_INTERNAL(_HANJA),
+DEFINE_VK_INTERNAL(_KANJI),
+DEFINE_VK_INTERNAL(_ESCAPE),
+DEFINE_VK_INTERNAL(_CONVERT),
+DEFINE_VK_INTERNAL(_NONCONVERT),
+DEFINE_VK_INTERNAL(_ACCEPT),
+DEFINE_VK_INTERNAL(_MODECHANGE),
+DEFINE_VK_INTERNAL(_SPACE),
+DEFINE_VK_INTERNAL(_PAGE_UP),
+DEFINE_VK_INTERNAL(_PAGE_DOWN),
+DEFINE_VK_INTERNAL(_END),
+DEFINE_VK_INTERNAL(_HOME),
+DEFINE_VK_INTERNAL(_LEFT),
+DEFINE_VK_INTERNAL(_UP),
+DEFINE_VK_INTERNAL(_RIGHT),
+DEFINE_VK_INTERNAL(_DOWN),
+DEFINE_VK_INTERNAL(_SELECT),
+DEFINE_VK_INTERNAL(_PRINT),
+DEFINE_VK_INTERNAL(_EXECUTE),
+DEFINE_VK_INTERNAL(_PRINTSCREEN),
+DEFINE_VK_INTERNAL(_INSERT),
+DEFINE_VK_INTERNAL(_DELETE),
+
+DEFINE_VK_INTERNAL(_0),
+DEFINE_VK_INTERNAL(_1),
+DEFINE_VK_INTERNAL(_2),
+DEFINE_VK_INTERNAL(_3),
+DEFINE_VK_INTERNAL(_4),
+DEFINE_VK_INTERNAL(_5),
+DEFINE_VK_INTERNAL(_6),
+DEFINE_VK_INTERNAL(_7),
+DEFINE_VK_INTERNAL(_8),
+DEFINE_VK_INTERNAL(_9),
+
+DEFINE_VK_INTERNAL(_COLON),
+DEFINE_VK_INTERNAL(_SEMICOLON),
+DEFINE_VK_INTERNAL(_LESS_THAN),
+DEFINE_VK_INTERNAL(_EQUALS),
+DEFINE_VK_INTERNAL(_GREATER_THAN),
+DEFINE_VK_INTERNAL(_QUESTION_MARK),
+DEFINE_VK_INTERNAL(_AT),
+
+DEFINE_VK_INTERNAL(_A),
+DEFINE_VK_INTERNAL(_B),
+DEFINE_VK_INTERNAL(_C),
+DEFINE_VK_INTERNAL(_D),
+DEFINE_VK_INTERNAL(_E),
+DEFINE_VK_INTERNAL(_F),
+DEFINE_VK_INTERNAL(_G),
+DEFINE_VK_INTERNAL(_H),
+DEFINE_VK_INTERNAL(_I),
+DEFINE_VK_INTERNAL(_J),
+DEFINE_VK_INTERNAL(_K),
+DEFINE_VK_INTERNAL(_L),
+DEFINE_VK_INTERNAL(_M),
+DEFINE_VK_INTERNAL(_N),
+DEFINE_VK_INTERNAL(_O),
+DEFINE_VK_INTERNAL(_P),
+DEFINE_VK_INTERNAL(_Q),
+DEFINE_VK_INTERNAL(_R),
+DEFINE_VK_INTERNAL(_S),
+DEFINE_VK_INTERNAL(_T),
+DEFINE_VK_INTERNAL(_U),
+DEFINE_VK_INTERNAL(_V),
+DEFINE_VK_INTERNAL(_W),
+DEFINE_VK_INTERNAL(_X),
+DEFINE_VK_INTERNAL(_Y),
+DEFINE_VK_INTERNAL(_Z),
+
+DEFINE_VK_INTERNAL(_WIN),
+DEFINE_VK_INTERNAL(_CONTEXT_MENU),
+DEFINE_VK_INTERNAL(_SLEEP),
+
+DEFINE_VK_INTERNAL(_NUMPAD0),
+DEFINE_VK_INTERNAL(_NUMPAD1),
+DEFINE_VK_INTERNAL(_NUMPAD2),
+DEFINE_VK_INTERNAL(_NUMPAD3),
+DEFINE_VK_INTERNAL(_NUMPAD4),
+DEFINE_VK_INTERNAL(_NUMPAD5),
+DEFINE_VK_INTERNAL(_NUMPAD6),
+DEFINE_VK_INTERNAL(_NUMPAD7),
+DEFINE_VK_INTERNAL(_NUMPAD8),
+DEFINE_VK_INTERNAL(_NUMPAD9),
+DEFINE_VK_INTERNAL(_MULTIPLY),
+DEFINE_VK_INTERNAL(_ADD),
+DEFINE_VK_INTERNAL(_SEPARATOR),
+DEFINE_VK_INTERNAL(_SUBTRACT),
+DEFINE_VK_INTERNAL(_DECIMAL),
+DEFINE_VK_INTERNAL(_DIVIDE),
+
+DEFINE_VK_INTERNAL(_F1),
+DEFINE_VK_INTERNAL(_F2),
+DEFINE_VK_INTERNAL(_F3),
+DEFINE_VK_INTERNAL(_F4),
+DEFINE_VK_INTERNAL(_F5),
+DEFINE_VK_INTERNAL(_F6),
+DEFINE_VK_INTERNAL(_F7),
+DEFINE_VK_INTERNAL(_F8),
+DEFINE_VK_INTERNAL(_F9),
+DEFINE_VK_INTERNAL(_F10),
+DEFINE_VK_INTERNAL(_F11),
+DEFINE_VK_INTERNAL(_F12),
+DEFINE_VK_INTERNAL(_F13),
+DEFINE_VK_INTERNAL(_F14),
+DEFINE_VK_INTERNAL(_F15),
+DEFINE_VK_INTERNAL(_F16),
+DEFINE_VK_INTERNAL(_F17),
+DEFINE_VK_INTERNAL(_F18),
+DEFINE_VK_INTERNAL(_F19),
+DEFINE_VK_INTERNAL(_F20),
+DEFINE_VK_INTERNAL(_F21),
+DEFINE_VK_INTERNAL(_F22),
+DEFINE_VK_INTERNAL(_F23),
+DEFINE_VK_INTERNAL(_F24),
+
+DEFINE_VK_INTERNAL(_NUM_LOCK),
+DEFINE_VK_INTERNAL(_SCROLL_LOCK),
+
+DEFINE_VK_INTERNAL(_CIRCUMFLEX),
+DEFINE_VK_INTERNAL(_EXCLAMATION),
+DEFINE_VK_INTERNAL(_DOUBLE_QUOTE),
+DEFINE_VK_INTERNAL(_HASH),
+DEFINE_VK_INTERNAL(_DOLLAR),
+DEFINE_VK_INTERNAL(_PERCENT),
+DEFINE_VK_INTERNAL(_AMPERSAND),
+DEFINE_VK_INTERNAL(_UNDERSCORE),
+DEFINE_VK_INTERNAL(_OPEN_PAREN),
+DEFINE_VK_INTERNAL(_CLOSE_PAREN),
+DEFINE_VK_INTERNAL(_ASTERISK),
+DEFINE_VK_INTERNAL(_PLUS),
+DEFINE_VK_INTERNAL(_PIPE),
+DEFINE_VK_INTERNAL(_HYPHEN_MINUS),
+
+DEFINE_VK_INTERNAL(_OPEN_CURLY_BRACKET),
+DEFINE_VK_INTERNAL(_CLOSE_CURLY_BRACKET),
+
+DEFINE_VK_INTERNAL(_TILDE),
+
+DEFINE_VK_INTERNAL(_COMMA),
+DEFINE_VK_INTERNAL(_PERIOD),
+DEFINE_VK_INTERNAL(_SLASH),
+DEFINE_VK_INTERNAL(_BACK_QUOTE),
+DEFINE_VK_INTERNAL(_OPEN_BRACKET),
+DEFINE_VK_INTERNAL(_BACK_SLASH),
+DEFINE_VK_INTERNAL(_CLOSE_BRACKET),
+DEFINE_VK_INTERNAL(_QUOTE),
+
+DEFINE_VK_INTERNAL(_META),
+DEFINE_VK_INTERNAL(_ALTGR)
+
+#undef DEFINE_VK_INTERNAL
+#undef DEFINE_VK_INTERNAL2
--- a/content/smil/nsSMILTimedElement.cpp
+++ b/content/smil/nsSMILTimedElement.cpp
@@ -350,16 +350,33 @@ nsSMILTimeValue
 nsSMILTimedElement::GetStartTime() const
 {
   return mElementState == STATE_WAITING || mElementState == STATE_ACTIVE
          ? mCurrentInterval->Begin()->Time()
          : nsSMILTimeValue();
 }
 
 //----------------------------------------------------------------------
+// Hyperlinking support
+
+nsSMILTimeValue
+nsSMILTimedElement::GetHyperlinkTime() const
+{
+  nsSMILTimeValue hyperlinkTime; // Default ctor creates unresolved time
+
+  if (mElementState == STATE_ACTIVE) {
+    hyperlinkTime = mCurrentInterval->Begin()->Time();
+  } else if (!mBeginInstances.IsEmpty()) {
+    hyperlinkTime = mBeginInstances[0]->Time();
+  }
+
+  return hyperlinkTime;
+}
+
+//----------------------------------------------------------------------
 // nsSMILTimedElement
 
 void
 nsSMILTimedElement::AddInstanceTime(nsSMILInstanceTime* aInstanceTime,
                                     bool aIsBegin)
 {
   NS_ABORT_IF_FALSE(aInstanceTime, "Attempting to add null instance time");
 
@@ -1487,39 +1504,50 @@ nsSMILTimedElement::FilterHistory()
 }
 
 void
 nsSMILTimedElement::FilterIntervals()
 {
   // We can filter old intervals that:
   //
   // a) are not the previous interval; AND
-  // b) are not in the middle of a dependency chain
+  // b) are not in the middle of a dependency chain; AND
+  // c) are not the first interval
   //
   // Condition (a) is necessary since the previous interval is used for applying
   // fill effects and updating the current interval.
   //
   // Condition (b) is necessary since even if this interval itself is not
   // active, it may be part of a dependency chain that includes active
   // intervals. Such chains are used to establish priorities within the
   // animation sandwich.
   //
+  // Condition (c) is necessary to support hyperlinks that target animations
+  // since in some cases the defined behavior is to seek the document back to
+  // the first resolved begin time. Presumably the intention here is not
+  // actually to use the first resolved begin time, the
+  // _the_first_resolved_begin_time_that_produced_an_interval. That is,
+  // if we have begin="-5s; -3s; 1s; 3s" with a duration on 1s, we should seek
+  // to 1s. The spec doesn't say this but I'm pretty sure that is the intention.
+  // It seems negative times were simply not considered.
+  //
   // Although the above conditions allow us to safely filter intervals for most
   // scenarios they do not cover all cases and there will still be scenarios
   // that generate intervals indefinitely. In such a case we simply set
   // a maximum number of intervals and drop any intervals beyond that threshold.
 
   PRUint32 threshold = mOldIntervals.Length() > sMaxNumIntervals ?
                        mOldIntervals.Length() - sMaxNumIntervals :
                        0;
   IntervalList filteredList;
   for (PRUint32 i = 0; i < mOldIntervals.Length(); ++i)
   {
     nsSMILInterval* interval = mOldIntervals[i].get();
-    if (i + 1 < mOldIntervals.Length() /*skip previous interval*/ &&
+    if (i != 0 && /*skip first interval*/
+        i + 1 < mOldIntervals.Length() && /*skip previous interval*/
         (i < threshold || !interval->IsDependencyChainLink())) {
       interval->Unlink(true /*filtered, not deleted*/);
     } else {
       filteredList.AppendElement(mOldIntervals[i].forget());
     }
   }
   mOldIntervals.Clear();
   mOldIntervals.SwapElements(filteredList);
@@ -1579,24 +1607,28 @@ nsSMILTimedElement::FilterInstanceTimes(
   // they're unpredictable due to the possibility of seeking the document which
   // may prevent some events from being generated). Therefore we introduce
   // a hard cutoff at which point we just drop the oldest instance times.
   if (aList.Length() > sMaxNumInstanceTimes) {
     PRUint32 threshold = aList.Length() - sMaxNumInstanceTimes;
     // There are a few instance times we should keep though, notably:
     // - the current interval begin time,
     // - the previous interval end time (see note in RemoveInstanceTimes)
+    // - the first interval begin time (see note in FilterIntervals)
     nsTArray<const nsSMILInstanceTime *> timesToKeep;
     if (mCurrentInterval) {
       timesToKeep.AppendElement(mCurrentInterval->Begin());
     }
     const nsSMILInterval* prevInterval = GetPreviousInterval();
     if (prevInterval) {
       timesToKeep.AppendElement(prevInterval->End());
     }
+    if (!mOldIntervals.IsEmpty()) {
+      timesToKeep.AppendElement(mOldIntervals[0]->Begin());
+    }
     RemoveBelowThreshold removeBelowThreshold(threshold, timesToKeep);
     RemoveInstanceTimes(aList, removeBelowThreshold);
   }
 }
 
 //
 // This method is based on the pseudocode given in the SMILANIM spec.
 //
--- a/content/smil/nsSMILTimedElement.h
+++ b/content/smil/nsSMILTimedElement.h
@@ -135,20 +135,39 @@ public:
    * @return the simple duration in milliseconds or INDEFINITE.
    */
   nsSMILTimeValue GetSimpleDuration() const
   {
     return mSimpleDur;
   }
 
   /**
+   * Methods for supporting hyperlinking
+   */
+
+  /**
    * Internal SMIL methods
    */
 
   /**
+   * Returns the time to seek the document to when this element is targetted by
+   * a hyperlink.
+   *
+   * The behavior is defined here:
+   *   http://www.w3.org/TR/smil-animation/#HyperlinkSemantics
+   *
+   * It is very similar to GetStartTime() with the exception that when the
+   * element is not active, the begin time of the *first* interval is returned.
+   *
+   * @return the time to seek the documen to in milliseconds or an unresolved
+   * time if there is no resolved interval.
+   */
+  nsSMILTimeValue GetHyperlinkTime() const;
+
+  /**
    * Adds an instance time object this element's list of instance times.
    * These instance times are used when creating intervals.
    *
    * This method is typically called by an nsSMILTimeValueSpec.
    *
    * @param aInstanceTime   The time to add, expressed in container time.
    * @param aIsBegin        true if the time to be added represents a begin
    *                        time or false if it represents an end time.
--- a/content/smil/test/Makefile.in
+++ b/content/smil/test/Makefile.in
@@ -73,16 +73,17 @@ include $(topsrcdir)/config/rules.mk
 	  test_smilMappedAttrFromBy.xhtml \
 	  test_smilMappedAttrPaced.xhtml \
 	  test_smilReset.xhtml \
 	  test_smilRestart.xhtml \
 	  test_smilExtDoc.xhtml \
 	  test_smilFillMode.xhtml \
 	  test_smilGetStartTime.xhtml \
 	  test_smilGetSimpleDuration.xhtml \
+	  test_smilHyperlinking.xhtml \
 	  test_smilKeySplines.xhtml \
 	  test_smilKeyTimes.xhtml \
 	  test_smilKeyTimesPacedMode.xhtml \
 	  test_smilRepeatTiming.xhtml \
 	  test_smilSetCurrentTime.xhtml \
 	  test_smilSync.xhtml \
 	  test_smilSyncbaseTarget.xhtml \
 	  test_smilSyncTransform.xhtml \
new file mode 100644
--- /dev/null
+++ b/content/smil/test/test_smilHyperlinking.xhtml
@@ -0,0 +1,233 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+  <title>Test for hyperlinking</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display:none">
+<svg id="svg" xmlns="http://www.w3.org/2000/svg" width="120px" height="120px"
+     onload="this.pauseAnimations()">
+  <circle cx="-100" cy="20" r="15" fill="blue" id="circle"/>
+</svg>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+<![CDATA[
+/** Test for SMIL keySplines **/
+
+/* Global Variables */
+const SVGNS="http://www.w3.org/2000/svg";
+var gSvg = document.getElementById("svg");
+var gAnim;
+
+var gTestStages =
+  [ testActive,
+    testSeekToFirst,
+    testKickStart,
+    testKickStartWithUnresolved,
+    testFiltering
+  ];
+
+SimpleTest.waitForExplicitFinish();
+
+function continueTest()
+{
+  if (gTestStages.length == 0) {
+    SimpleTest.finish();
+    return;
+  }
+
+  window.location.hash = "";
+  if (gAnim) {
+    gAnim.parentNode.removeChild(gAnim);
+  }
+  gAnim = createAnim();
+  gSvg.setCurrentTime(0);
+  gTestStages.shift()();
+}
+
+function createAnim() {
+  var anim = document.createElementNS(SVGNS,'animate');
+  anim.setAttribute('attributeName','cx');
+  anim.setAttribute('from','0');
+  anim.setAttribute('to','100');
+  anim.setAttribute('dur','1s');
+  anim.setAttribute('begin','indefinite');
+  anim.setAttribute('id','anim');
+  return document.getElementById('circle').appendChild(anim);
+}
+
+// Traversing a hyperlink, condition 1:
+// 
+// "If the target element is active, seek the document time back to the
+// (current) begin time of the element. If there are multiple begin times, use
+// the begin time that corresponds to the current "begin instance"."
+//
+function testActive() {
+  gAnim.setAttribute('begin','2s; 4s');
+  gSvg.setCurrentTime(2.5);
+  fireLink(rewindActiveInterval1);
+}
+
+function rewindActiveInterval1() {
+  is(gSvg.getCurrentTime(), 2,
+     "Unexpected time after activating link to animation in the middle of " +
+     "first active interval");
+
+  // Seek to second interval
+  gSvg.setCurrentTime(4.5);
+  fireLink(rewindActiveInterval2);
+}
+
+function rewindActiveInterval2() {
+  is(gSvg.getCurrentTime(), 4,
+     "Unexpected time after activating link to animation in the middle of " +
+     "second active interval");
+
+  // Try a negative time
+  gAnim.setAttribute("begin", "-0.5");
+  gSvg.setCurrentTime(0.2);
+  fireLink(rewindActiveIntervalAtZero);
+}
+
+function rewindActiveIntervalAtZero() {
+  is(gSvg.getCurrentTime(), 0,
+     "Unexpected time after activating link to animation in the middle of " +
+     "an active interval that overlaps zero");
+
+  continueTest();
+}
+
+// Traversing a hyperlink, condition 2:
+// 
+// "Else if the target element begin time is resolved (i.e., there is any
+// resolved time in the list of begin times, or if the begin time was forced by
+// an earlier hyperlink or a beginElement() method call), seek the document time
+// (forward or back, as needed) to the earliest resolved begin time of the
+// target element. Note that the begin time may be resolved as a result of an
+// earlier hyperlink, DOM or event activation. Once the begin time is resolved,
+// hyperlink traversal always seeks."
+//
+function testSeekToFirst() {
+  // Seek forwards
+  gAnim.setAttribute('begin','2s');
+  gSvg.setCurrentTime(0);
+  fireLink(forwardToInterval1);
+}
+
+function forwardToInterval1() {
+  is(gSvg.getCurrentTime(), 2,
+     "Unexpected time after activating link to animation scheduled to start " +
+     "the future");
+
+  // Seek backwards
+  gSvg.setCurrentTime(3.5);
+  fireLink(backwardToInterval1);
+}
+
+function backwardToInterval1() {
+  is(gSvg.getCurrentTime(), 2,
+     "Unexpected time after activating link to animation that ran in the past");
+
+  // What if the first begin instance is negative?
+  gAnim.setAttribute('begin','-0.5s');
+  gSvg.setCurrentTime(1);
+  fireLink(backwardToZero);
+}
+
+function backwardToZero() {
+  is(gSvg.getCurrentTime(), 0,
+     "Unexpected time after activating link to animation that ran in the " +
+     "past with a negative time");
+
+  continueTest();
+}
+
+// Traversing a hyperlink, condition 3:
+// 
+// "Else (animation begin time is unresolved) just resolve the target animation
+// begin time at current document time. Disregard the sync-base or event base of
+// the animation, and do not "back-propagate" any timing logic to resolve the
+// child, but rather treat it as though it were defined with begin="indefinite"
+// and just resolve begin time to the current document time."
+//
+function testKickStart() {
+  gSvg.setCurrentTime(1);
+  fireLink(startedAt1s);
+}
+
+function startedAt1s() {
+  is(gSvg.getCurrentTime(), 1,
+     "Unexpected time after kick-starting animation with indefinite start " +
+     "by hyperlink");
+  is(gAnim.getStartTime(), 1,
+     "Unexpected start time for kick-started animation");
+
+  continueTest();
+}
+
+function testKickStartWithUnresolved() {
+  gAnim.setAttribute("begin", "circle.click");
+  gSvg.setCurrentTime(3);
+  fireLink(startedAt3s);
+}
+
+function startedAt3s() {
+  is(gSvg.getCurrentTime(), 3,
+     "Unexpected time after kick-starting animation with unresolved start " +
+     "by hyperlink");
+  is(gAnim.getStartTime(), 3,
+     "Unexpected start time for kick-started animation with unresolved begin " +
+     "condition");
+
+  continueTest();
+}
+
+function testFiltering() {
+  gAnim.setAttribute('begin','-3s; 1s; 2s; 3s; 4s; 5s; 6s; 7s; 8s; 9s; 10s');
+  gSvg.setCurrentTime(12);
+  fireLink(rewindToFirst);
+}
+
+function rewindToFirst() {
+  is(gSvg.getCurrentTime(), 1,
+     "Unexpected time after triggering animation with a hyperlink after " +
+     "numerous intervals have passed");
+
+  continueTest();
+}
+
+function fireLink(callback) {
+  // First we need to reset the hash because otherwise the redundant hashchange
+  // events will be suppressed
+  if (window.location.hash === '') {
+    fireLinkPart2(callback);
+  } else {
+    window.location.hash = '';
+    window.addEventListener("hashchange",
+      function clearHash() {
+        window.removeEventListener("hashchange", clearHash, false);
+        window.setTimeout(fireLinkPart2, 0, callback);
+      },
+      false);
+  }
+}
+
+function fireLinkPart2(callback) {
+  window.addEventListener("hashchange",
+    function triggerCallback() {
+      window.removeEventListener("hashchange", triggerCallback, false);
+      window.setTimeout(callback, 0);
+    },
+    false);
+  window.location.hash = '#anim';
+}
+
+window.addEventListener("load", continueTest, false);
+]]>
+</script>
+</pre>
+</body>
+</html>
--- 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/nsSVGAnimationElement.cpp
+++ b/content/svg/content/src/nsSVGAnimationElement.cpp
@@ -418,16 +418,44 @@ nsSVGAnimationElement::UnsetAttr(PRInt32
 
 bool
 nsSVGAnimationElement::IsNodeOfType(PRUint32 aFlags) const
 {
   return !(aFlags & ~(eCONTENT | eANIMATION));
 }
 
 //----------------------------------------------------------------------
+// SVG utility methods
+
+void
+nsSVGAnimationElement::ActivateByHyperlink()
+{
+  FlushAnimations();
+
+  // The behavior for when the target is an animation element is defined in
+  // SMIL Animation:
+  //   http://www.w3.org/TR/smil-animation/#HyperlinkSemantics
+  nsSMILTimeValue seekTime = mTimedElement.GetHyperlinkTime();
+  if (seekTime.IsDefinite()) {
+    nsSMILTimeContainer* timeContainer = GetTimeContainer();
+    if (timeContainer) {
+      timeContainer->SetCurrentTime(seekTime.GetMillis());
+      AnimationNeedsResample();
+      // As with nsSVGSVGElement::SetCurrentTime, we need to trigger
+      // a synchronous sample now.
+      FlushAnimations();
+    }
+    // else, silently fail. We mustn't be part of an SVG document fragment that
+    // is attached to the document tree so there's nothing we can do here
+  } else {
+    BeginElement();
+  }
+}
+
+//----------------------------------------------------------------------
 // Implementation helpers
 
 nsSMILTimeContainer*
 nsSVGAnimationElement::GetTimeContainer()
 {
   nsSVGSVGElement *element = nsSVGUtils::GetOuterSVGElement(this);
 
   if (element) {
--- a/content/svg/content/src/nsSVGAnimationElement.h
+++ b/content/svg/content/src/nsSVGAnimationElement.h
@@ -95,16 +95,19 @@ public:
   virtual bool HasAnimAttr(nsIAtom* aAttName) const;
   virtual Element* GetTargetElementContent();
   virtual bool GetTargetAttributeName(PRInt32* aNamespaceID,
                                         nsIAtom** aLocalName) const;
   virtual nsSMILTargetAttrType GetTargetAttributeType() const;
   virtual nsSMILTimedElement& TimedElement();
   virtual nsSMILTimeContainer* GetTimeContainer();
 
+  // Utility methods for within SVG
+  void ActivateByHyperlink();
+
 protected:
   // nsSVGElement overrides
   bool IsEventName(nsIAtom* aName);
 
   void UpdateHrefTarget(nsIContent* aNodeForContext,
                         const nsAString& aHrefStr);
   void AnimationTargetChanged();
 
--- 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/nsSVGGraphicElement.cpp
+++ b/content/svg/content/src/nsSVGGraphicElement.cpp
@@ -40,16 +40,17 @@
 #include "mozilla/Util.h"
 
 #include "nsSVGGraphicElement.h"
 #include "nsSVGSVGElement.h"
 #include "DOMSVGAnimatedTransformList.h"
 #include "DOMSVGMatrix.h"
 #include "nsGkAtoms.h"
 #include "nsIDOMEventTarget.h"
+#include "nsIDOMMutationEvent.h"
 #include "nsIFrame.h"
 #include "nsISVGChildFrame.h"
 #include "nsIDOMSVGPoint.h"
 #include "nsSVGUtils.h"
 #include "nsDOMError.h"
 #include "nsSVGRect.h"
 #include "nsContentUtils.h"
 
@@ -174,16 +175,53 @@ nsSVGGraphicElement::IsAttributeMapped(c
     sFillStrokeMap,
     sGraphicsMap
   };
   
   return FindAttributeDependence(name, map) ||
     nsSVGGraphicElementBase::IsAttributeMapped(name);
 }
 
+nsChangeHint
+nsSVGGraphicElement::GetAttributeChangeHint(const nsIAtom* aAttribute,
+                                            PRInt32 aModType) const
+{
+  nsChangeHint retval =
+    nsSVGGraphicElementBase::GetAttributeChangeHint(aAttribute, aModType);
+  if (aAttribute == nsGkAtoms::transform) {
+    // We add nsChangeHint_UpdateOverflow so that nsFrame::UpdateOverflow()
+    // will be called on us and our ancestors.
+    nsIFrame* frame =
+      const_cast<nsSVGGraphicElement*>(this)->GetPrimaryFrame();
+    if (frame && frame->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD) {
+      // No need to do anything.
+    } else if (aModType == nsIDOMMutationEvent::ADDITION ||
+               aModType == nsIDOMMutationEvent::REMOVAL) {
+      // In order to handle view creation/destruction and stacking context
+      // changes, the code in nsStyleDisplay::CalcDifference uses
+      // nsChangeHint_ReconstructFrame if the transform was added/removed.
+      // XXXSDL Currently we don't need to reconstruct SVG frames when their
+      // transform is set/unset since we don't currently create GFX layers for
+      // SVG transforms, but we will after bug 614732 is fixed. Also change the
+      // assertion in ApplyRenderingChangeToTree when we do that.
+      NS_UpdateHint(retval, nsChangeHint_UpdateOverflow);
+    } else {
+      NS_ABORT_IF_FALSE(aModType == nsIDOMMutationEvent::MODIFICATION,
+                        "Unknown modification type.");
+      // We just assume the old and new transforms are different.
+      // XXXSDL Once we use GFX layers for SVG transforms, we will need to pass
+      // the nsChangeHint_UpdateTransformLayer hint too. Note that the
+      // assertion in ApplyRenderingChangeToTree will fail if that hint is
+      // passed on nsIDOMMutationEvent::REMOVAL though.
+      NS_UpdateHint(retval, nsChangeHint_UpdateOverflow);
+    }
+  }
+  return retval;
+}
+
 //----------------------------------------------------------------------
 // nsSVGElement overrides
 
 bool
 nsSVGGraphicElement::IsEventName(nsIAtom* aName)
 {
   return nsContentUtils::IsEventAttributeName(aName, EventNameType_SVGGraphic);
 }
--- a/content/svg/content/src/nsSVGGraphicElement.h
+++ b/content/svg/content/src/nsSVGGraphicElement.h
@@ -57,16 +57,19 @@ public:
   // interfaces:  
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIDOMSVGLOCATABLE
   NS_DECL_NSIDOMSVGTRANSFORMABLE
 
   // nsIContent interface
   NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const;
 
+  nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute,
+                                      PRInt32 aModType) const;
+
   virtual gfxMatrix PrependLocalTransformsTo(const gfxMatrix &aMatrix,
                       TransformTypes aWhich = eAllTransforms) const;
   virtual void SetAnimateMotionTransform(const gfxMatrix* aMatrix);
 
   virtual mozilla::SVGAnimatedTransformList* GetAnimatedTransformList();
   virtual nsIAtom* GetTransformListAttrName() const {
     return nsGkAtoms::transform;
   }
--- a/content/svg/content/src/nsSVGSVGElement.cpp
+++ b/content/svg/content/src/nsSVGSVGElement.cpp
@@ -36,16 +36,17 @@
  * 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 "mozilla/Util.h"
 
 #include "nsGkAtoms.h"
+#include "nsLayoutUtils.h"
 #include "DOMSVGNumber.h"
 #include "DOMSVGLength.h"
 #include "nsSVGAngle.h"
 #include "nsCOMPtr.h"
 #include "nsIPresShell.h"
 #include "nsContentUtils.h"
 #include "nsIDocument.h"
 #include "nsPresContext.h"
@@ -199,17 +200,18 @@ nsSVGSVGElement::nsSVGSVGElement(already
     mViewportWidth(0),
     mViewportHeight(0),
     mCurrentTranslate(0.0f, 0.0f),
     mCurrentScale(1.0f),
     mPreviousTranslate(0.0f, 0.0f),
     mPreviousScale(1.0f),
     mStartAnimationOnBindToTree(!aFromParser),
     mImageNeedsTransformInvalidation(false),
-    mIsPaintingSVGImageElement(false)
+    mIsPaintingSVGImageElement(false),
+    mHasChildrenOnlyTransform(false)
 {
 }
 
 //----------------------------------------------------------------------
 // nsIDOMNode methods
 
 // From NS_IMPL_ELEMENT_CLONE_WITH_INIT(nsSVGSVGElement)
 nsresult
@@ -949,81 +951,69 @@ 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),
+                    "Non-display SVG frames don't maintain overflow rects");
+
+  bool hasChildrenOnlyTransform = HasViewBoxOrSyntheticViewBox() ||
+    (IsRoot() && (mCurrentTranslate != nsSVGTranslatePoint(0.0f, 0.0f) ||
+                  mCurrentScale != 1.0f));
+
+  // XXXSDL Currently we don't destroy frames if
+  // hasChildrenOnlyTransform != mHasChildrenOnlyTransform
+  // but we should once we start using GFX layers for SVG transforms
+  // (see the comment in nsSVGGraphicElement::GetAttributeChangeHint).
+
+  nsChangeHint changeHint =
+    nsChangeHint(nsChangeHint_RepaintFrame |
+                 nsChangeHint_UpdateOverflow |
+                 nsChangeHint_ChildrenOnlyTransform);
+
+  nsLayoutUtils::PostRestyleEvent(this, nsRestyleHint(0), changeHint);
+
+  mHasChildrenOnlyTransform = hasChildrenOnlyTransform;
 }
 
 nsresult
 nsSVGSVGElement::BindToTree(nsIDocument* aDocument,
                             nsIContent* aParent,
                             nsIContent* aBindingParent,
                             bool aCompileEventHandlers)
 {
@@ -1115,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;
 
@@ -1267,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
@@ -1292,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,37 +50,52 @@
 #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(float aX, float aY) :
-    mX(aX), mY(aY) {}
+  nsSVGTranslatePoint()
+    : mX(0.0f)
+    , mY(0.0f)
+  {}
+
+  nsSVGTranslatePoint(float aX, float aY)
+    : mX(aX)
+    , mY(aY)
+  {}
 
   void SetX(float aX)
     { mX = aX; }
   void SetY(float aY)
     { mY = aY; }
   float GetX() const
     { return mX; }
   float GetY() const
     { return mY; }
 
   nsresult ToDOMVal(nsSVGSVGElement *aElement, nsIDOMSVGPoint **aResult);
 
+  bool operator!=(const nsSVGTranslatePoint &rhs) const {
+    return mX != rhs.mX || mY != rhs.mY;
+  }
+
 private:
 
   struct DOMVal : public nsIDOMSVGPoint {
     NS_DECL_CYCLE_COLLECTING_ISUPPORTS
     NS_DECL_CYCLE_COLLECTION_CLASS(DOMVal)
 
     DOMVal(nsSVGTranslatePoint* aVal, nsSVGSVGElement *aElement)
       : mVal(aVal), mElement(aElement) {}
@@ -122,18 +137,18 @@ 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;
 
-protected:
   friend nsresult NS_NewSVGSVGElement(nsIContent **aResult,
                                       already_AddRefed<nsINodeInfo> aNodeInfo,
                                       mozilla::dom::FromParser aFromParser);
   nsSVGSVGElement(already_AddRefed<nsINodeInfo> aNodeInfo,
                   mozilla::dom::FromParser aFromParser);
   
 public:
   typedef mozilla::SVGAnimatedPreserveAspectRatio SVGAnimatedPreserveAspectRatio;
@@ -215,18 +230,38 @@ public:
    * Returns true if we should synthesize a viewBox for ourselves (that is, if
    * we're the root element in an image document, and we're not currently being
    * painted for an <svg:image> element).
    *
    * Only call this method if HasViewBox() returns false.
    */
   bool ShouldSynthesizeViewBox() const;
 
+  bool HasViewBoxOrSyntheticViewBox() const {
+    return HasViewBox() || ShouldSynthesizeViewBox();
+  }
+
   gfxMatrix GetViewBoxTransform() const;
 
+  bool HasChildrenOnlyTransform() const {
+    return mHasChildrenOnlyTransform;
+  }
+
+  /**
+   * This method notifies the style system that the overflow rects of our
+   * immediate childrens' frames need to be updated. It is called by our own
+   * frame when changes (e.g. to currentScale) cause our children-only
+   * transform to change.
+   *
+   * The reason we have this method instead of overriding
+   * GetAttributeChangeHint is because we need to act on non-attribute (e.g.
+   * currentScale) changes in addition to attribute (e.g. viewBox) changes.
+   */
+  void ChildrenOnlyTransformChanged();
+
   // This services any pending notifications for the transform on on this root
   // <svg> node needing to be recalculated.  (Only applicable in
   // SVG-as-an-image documents.)
   virtual void FlushImageTransformInvalidation();
 
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
 
   svgFloatSize GetViewportSize() const {
@@ -238,34 +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;
-
-protected:
   // 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();
   }
 
   /**
@@ -283,26 +327,39 @@ protected:
    * <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();
 
@@ -344,11 +401,12 @@ protected:
 
   // For outermost <svg> elements created from parsing, animation is started by
   // the onload event in accordance with the SVG spec, but for <svg> elements
   // created by script or promoted from inner <svg> to outermost <svg> we need
   // to manually kick off animation when they are bound to the tree.
   bool                              mStartAnimationOnBindToTree;
   bool                              mImageNeedsTransformInvalidation;
   bool                              mIsPaintingSVGImageElement;
+  bool                              mHasChildrenOnlyTransform;
 };
 
 #endif
--- 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/content/xbl/src/nsXBLPrototypeHandler.cpp
+++ b/content/xbl/src/nsXBLPrototypeHandler.cpp
@@ -642,136 +642,20 @@ struct keyCodeData {
   PRUint32 keycode;
 };
 
 // All of these must be uppercase, since the function below does
 // case-insensitive comparison by converting to uppercase.
 // XXX: be sure to check this periodically for new symbol additions!
 static const keyCodeData gKeyCodes[] = {
 
-#define KEYCODE_ENTRY(str) {#str, sizeof(#str) - 1, nsIDOMKeyEvent::DOM_##str}
-#define KEYCODE_ENTRY2(str, code) {str, sizeof(str) - 1, code}
-
-  KEYCODE_ENTRY(VK_CANCEL),
-  KEYCODE_ENTRY2("VK_BACK", nsIDOMKeyEvent::DOM_VK_BACK_SPACE),
-  KEYCODE_ENTRY(VK_TAB),
-  KEYCODE_ENTRY(VK_CLEAR),
-  KEYCODE_ENTRY(VK_RETURN),
-  KEYCODE_ENTRY(VK_ENTER),
-  KEYCODE_ENTRY(VK_SHIFT),
-  KEYCODE_ENTRY(VK_CONTROL),
-  KEYCODE_ENTRY(VK_ALT),
-  KEYCODE_ENTRY(VK_PAUSE),
-  KEYCODE_ENTRY(VK_CAPS_LOCK),
-  KEYCODE_ENTRY(VK_ESCAPE),
-  KEYCODE_ENTRY(VK_SPACE),
-  KEYCODE_ENTRY(VK_PAGE_UP),
-  KEYCODE_ENTRY(VK_PAGE_DOWN),
-  KEYCODE_ENTRY(VK_END),
-  KEYCODE_ENTRY(VK_HOME),
-  KEYCODE_ENTRY(VK_LEFT),
-  KEYCODE_ENTRY(VK_UP),
-  KEYCODE_ENTRY(VK_RIGHT),
-  KEYCODE_ENTRY(VK_DOWN),
-  KEYCODE_ENTRY(VK_PRINTSCREEN),
-  KEYCODE_ENTRY(VK_INSERT),
-  KEYCODE_ENTRY(VK_HELP),
-  KEYCODE_ENTRY(VK_DELETE),
-  KEYCODE_ENTRY(VK_0),
-  KEYCODE_ENTRY(VK_1),
-  KEYCODE_ENTRY(VK_2),
-  KEYCODE_ENTRY(VK_3),
-  KEYCODE_ENTRY(VK_4),
-  KEYCODE_ENTRY(VK_5),
-  KEYCODE_ENTRY(VK_6),
-  KEYCODE_ENTRY(VK_7),
-  KEYCODE_ENTRY(VK_8),
-  KEYCODE_ENTRY(VK_9),
-  KEYCODE_ENTRY(VK_SEMICOLON),
-  KEYCODE_ENTRY(VK_EQUALS),
-  KEYCODE_ENTRY(VK_A),
-  KEYCODE_ENTRY(VK_B),
-  KEYCODE_ENTRY(VK_C),
-  KEYCODE_ENTRY(VK_D),
-  KEYCODE_ENTRY(VK_E),
-  KEYCODE_ENTRY(VK_F),
-  KEYCODE_ENTRY(VK_G),
-  KEYCODE_ENTRY(VK_H),
-  KEYCODE_ENTRY(VK_I),
-  KEYCODE_ENTRY(VK_J),
-  KEYCODE_ENTRY(VK_K),
-  KEYCODE_ENTRY(VK_L),
-  KEYCODE_ENTRY(VK_M),
-  KEYCODE_ENTRY(VK_N),
-  KEYCODE_ENTRY(VK_O),
-  KEYCODE_ENTRY(VK_P),
-  KEYCODE_ENTRY(VK_Q),
-  KEYCODE_ENTRY(VK_R),
-  KEYCODE_ENTRY(VK_S),
-  KEYCODE_ENTRY(VK_T),
-  KEYCODE_ENTRY(VK_U),
-  KEYCODE_ENTRY(VK_V),
-  KEYCODE_ENTRY(VK_W),
-  KEYCODE_ENTRY(VK_X),
-  KEYCODE_ENTRY(VK_Y),
-  KEYCODE_ENTRY(VK_Z),
-  KEYCODE_ENTRY(VK_NUMPAD0),
-  KEYCODE_ENTRY(VK_NUMPAD1),
-  KEYCODE_ENTRY(VK_NUMPAD2),
-  KEYCODE_ENTRY(VK_NUMPAD3),
-  KEYCODE_ENTRY(VK_NUMPAD4),
-  KEYCODE_ENTRY(VK_NUMPAD5),
-  KEYCODE_ENTRY(VK_NUMPAD6),
-  KEYCODE_ENTRY(VK_NUMPAD7),
-  KEYCODE_ENTRY(VK_NUMPAD8),
-  KEYCODE_ENTRY(VK_NUMPAD9),
-  KEYCODE_ENTRY(VK_MULTIPLY),
-  KEYCODE_ENTRY(VK_ADD),
-  KEYCODE_ENTRY(VK_SEPARATOR),
-  KEYCODE_ENTRY(VK_SUBTRACT),
-  KEYCODE_ENTRY(VK_DECIMAL),
-  KEYCODE_ENTRY(VK_DIVIDE),
-  KEYCODE_ENTRY(VK_F1),
-  KEYCODE_ENTRY(VK_F2),
-  KEYCODE_ENTRY(VK_F3),
-  KEYCODE_ENTRY(VK_F4),
-  KEYCODE_ENTRY(VK_F5),
-  KEYCODE_ENTRY(VK_F6),
-  KEYCODE_ENTRY(VK_F7),
-  KEYCODE_ENTRY(VK_F8),
-  KEYCODE_ENTRY(VK_F9),
-  KEYCODE_ENTRY(VK_F10),
-  KEYCODE_ENTRY(VK_F11),
-  KEYCODE_ENTRY(VK_F12),
-  KEYCODE_ENTRY(VK_F13),
-  KEYCODE_ENTRY(VK_F14),
-  KEYCODE_ENTRY(VK_F15),
-  KEYCODE_ENTRY(VK_F16),
-  KEYCODE_ENTRY(VK_F17),
-  KEYCODE_ENTRY(VK_F18),
-  KEYCODE_ENTRY(VK_F19),
-  KEYCODE_ENTRY(VK_F20),
-  KEYCODE_ENTRY(VK_F21),
-  KEYCODE_ENTRY(VK_F22),
-  KEYCODE_ENTRY(VK_F23),
-  KEYCODE_ENTRY(VK_F24),
-  KEYCODE_ENTRY(VK_NUM_LOCK),
-  KEYCODE_ENTRY(VK_SCROLL_LOCK),
-  KEYCODE_ENTRY(VK_COMMA),
-  KEYCODE_ENTRY(VK_PERIOD),
-  KEYCODE_ENTRY(VK_SLASH),
-  KEYCODE_ENTRY(VK_BACK_QUOTE),
-  KEYCODE_ENTRY(VK_OPEN_BRACKET),
-  KEYCODE_ENTRY(VK_BACK_SLASH),
-  KEYCODE_ENTRY(VK_CLOSE_BRACKET),
-  KEYCODE_ENTRY(VK_QUOTE)
-
-#undef KEYCODE_ENTRY
-#undef KEYCODE_ENTRY2
-
+#define NS_DEFINE_VK(aDOMKeyName, aDOMKeyCode) \
+  { #aDOMKeyName, sizeof(#aDOMKeyName) - 1, aDOMKeyCode }
+#include "nsVKList.h"
+#undef NS_DEFINE_VK
 };
 
 PRInt32 nsXBLPrototypeHandler::GetMatchingKeyCode(const nsAString& aKeyName)
 {
   nsCAutoString keyName;
   keyName.AssignWithConversion(aKeyName);
   ToUpperCase(keyName); // We want case-insensitive comparison with data
                         // stored as uppercase.
--- 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/events/nsIDOMKeyEvent.idl
+++ b/dom/interfaces/events/nsIDOMKeyEvent.idl
@@ -51,16 +51,17 @@ interface nsIDOMKeyEvent : nsIDOMUIEvent
   const unsigned long DOM_VK_ENTER          = 0x0E;
   const unsigned long DOM_VK_SHIFT          = 0x10;
   const unsigned long DOM_VK_CONTROL        = 0x11;
   const unsigned long DOM_VK_ALT            = 0x12;
   const unsigned long DOM_VK_PAUSE          = 0x13;
   const unsigned long DOM_VK_CAPS_LOCK      = 0x14;
   const unsigned long DOM_VK_KANA           = 0x15;
   const unsigned long DOM_VK_HANGUL         = 0x15;
+  const unsigned long DOM_VK_EISU           = 0x16; // Japanese Mac keyboard only
   const unsigned long DOM_VK_JUNJA          = 0x17;
   const unsigned long DOM_VK_FINAL          = 0x18;
   const unsigned long DOM_VK_HANJA          = 0x19;
   const unsigned long DOM_VK_KANJI          = 0x19;
   const unsigned long DOM_VK_ESCAPE         = 0x1B;
   const unsigned long DOM_VK_CONVERT        = 0x1C;
   const unsigned long DOM_VK_NONCONVERT     = 0x1D;
   const unsigned long DOM_VK_ACCEPT         = 0x1E;
@@ -88,18 +89,23 @@ interface nsIDOMKeyEvent : nsIDOMUIEvent
   const unsigned long DOM_VK_3              = 0x33;
   const unsigned long DOM_VK_4              = 0x34;
   const unsigned long DOM_VK_5              = 0x35;
   const unsigned long DOM_VK_6              = 0x36;
   const unsigned long DOM_VK_7              = 0x37;
   const unsigned long DOM_VK_8              = 0x38;
   const unsigned long DOM_VK_9              = 0x39;
 
+  const unsigned long DOM_VK_COLON          = 0x3A;
   const unsigned long DOM_VK_SEMICOLON      = 0x3B;
+  const unsigned long DOM_VK_LESS_THAN      = 0x3C;
   const unsigned long DOM_VK_EQUALS         = 0x3D;
+  const unsigned long DOM_VK_GREATER_THAN   = 0x3E;
+  const unsigned long DOM_VK_QUESTION_MARK  = 0x3F;
+  const unsigned long DOM_VK_AT             = 0x40;
 
   // DOM_VK_A - DOM_VK_Z match their ascii values
   const unsigned long DOM_VK_A              = 0x41;
   const unsigned long DOM_VK_B              = 0x42;
   const unsigned long DOM_VK_C              = 0x43;
   const unsigned long DOM_VK_D              = 0x44;
   const unsigned long DOM_VK_E              = 0x45;
   const unsigned long DOM_VK_F              = 0x46;
@@ -119,35 +125,38 @@ interface nsIDOMKeyEvent : nsIDOMUIEvent
   const unsigned long DOM_VK_T              = 0x54;
   const unsigned long DOM_VK_U              = 0x55;
   const unsigned long DOM_VK_V              = 0x56;
   const unsigned long DOM_VK_W              = 0x57;
   const unsigned long DOM_VK_X              = 0x58;
   const unsigned long DOM_VK_Y              = 0x59;
   const unsigned long DOM_VK_Z              = 0x5A;
 
+  const unsigned long DOM_VK_WIN            = 0x5B;
   const unsigned long DOM_VK_CONTEXT_MENU   = 0x5D;
   const unsigned long DOM_VK_SLEEP          = 0x5F;
 
+  // Numpad keys
   const unsigned long DOM_VK_NUMPAD0        = 0x60;
   const unsigned long DOM_VK_NUMPAD1        = 0x61;
   const unsigned long DOM_VK_NUMPAD2        = 0x62;
   const unsigned long DOM_VK_NUMPAD3        = 0x63;
   const unsigned long DOM_VK_NUMPAD4        = 0x64;
   const unsigned long DOM_VK_NUMPAD5        = 0x65;
   const unsigned long DOM_VK_NUMPAD6        = 0x66;
   const unsigned long DOM_VK_NUMPAD7        = 0x67;
   const unsigned long DOM_VK_NUMPAD8        = 0x68;
   const unsigned long DOM_VK_NUMPAD9        = 0x69;
   const unsigned long DOM_VK_MULTIPLY       = 0x6A;
   const unsigned long DOM_VK_ADD            = 0x6B;
   const unsigned long DOM_VK_SEPARATOR      = 0x6C;
   const unsigned long DOM_VK_SUBTRACT       = 0x6D;
   const unsigned long DOM_VK_DECIMAL        = 0x6E;
   const unsigned long DOM_VK_DIVIDE         = 0x6F;
+
   const unsigned long DOM_VK_F1             = 0x70;
   const unsigned long DOM_VK_F2             = 0x71;
   const unsigned long DOM_VK_F3             = 0x72;
   const unsigned long DOM_VK_F4             = 0x73;
   const unsigned long DOM_VK_F5             = 0x74;
   const unsigned long DOM_VK_F6             = 0x75;
   const unsigned long DOM_VK_F7             = 0x76;
   const unsigned long DOM_VK_F8             = 0x77;
@@ -166,26 +175,47 @@ interface nsIDOMKeyEvent : nsIDOMUIEvent
   const unsigned long DOM_VK_F21            = 0x84;
   const unsigned long DOM_VK_F22            = 0x85;
   const unsigned long DOM_VK_F23            = 0x86;
   const unsigned long DOM_VK_F24            = 0x87;
 
   const unsigned long DOM_VK_NUM_LOCK       = 0x90;
   const unsigned long DOM_VK_SCROLL_LOCK    = 0x91;
 
+  const unsigned long DOM_VK_CIRCUMFLEX     = 0xA0;
+  const unsigned long DOM_VK_EXCLAMATION    = 0xA1;
+  const unsigned long DOM_VK_DOUBLE_QUOTE   = 0xA2;
+  const unsigned long DOM_VK_HASH           = 0xA3;
+  const unsigned long DOM_VK_DOLLAR         = 0xA4;
+  const unsigned long DOM_VK_PERCENT        = 0xA5;
+  const unsigned long DOM_VK_AMPERSAND      = 0xA6;
+  const unsigned long DOM_VK_UNDERSCORE     = 0xA7;
+  const unsigned long DOM_VK_OPEN_PAREN     = 0xA8;
+  const unsigned long DOM_VK_CLOSE_PAREN    = 0xA9;
+  const unsigned long DOM_VK_ASTERISK       = 0xAA;
+  const unsigned long DOM_VK_PLUS           = 0xAB;
+  const unsigned long DOM_VK_PIPE           = 0xAC;
+  const unsigned long DOM_VK_HYPHEN_MINUS   = 0xAD;
+
+  const unsigned long DOM_VK_OPEN_CURLY_BRACKET  = 0xAE;
+  const unsigned long DOM_VK_CLOSE_CURLY_BRACKET = 0xAF;
+
+  const unsigned long DOM_VK_TILDE          = 0xB0;
+
   const unsigned long DOM_VK_COMMA          = 0xBC;
   const unsigned long DOM_VK_PERIOD         = 0xBE;
   const unsigned long DOM_VK_SLASH          = 0xBF;
   const unsigned long DOM_VK_BACK_QUOTE     = 0xC0;
-  const unsigned long DOM_VK_OPEN_BRACKET   = 0xDB;
+  const unsigned long DOM_VK_OPEN_BRACKET   = 0xDB; // square bracket
   const unsigned long DOM_VK_BACK_SLASH     = 0xDC;
-  const unsigned long DOM_VK_CLOSE_BRACKET  = 0xDD;
-  const unsigned long DOM_VK_QUOTE          = 0xDE;
+  const unsigned long DOM_VK_CLOSE_BRACKET  = 0xDD; // square bracket
+  const unsigned long DOM_VK_QUOTE          = 0xDE; // Apostrophe
 
   const unsigned long DOM_VK_META           = 0xE0;
+  const unsigned long DOM_VK_ALTGR          = 0xE1;
 
   readonly attribute unsigned long    charCode;
   readonly attribute unsigned long    keyCode;
 
   readonly attribute boolean          altKey;
   readonly attribute boolean          ctrlKey;
   readonly attribute boolean          shiftKey;
   readonly attribute boolean          metaKey;
--- 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/js/src/jit-test/tests/basic/testCrossCompartmentTransparency2.js
+++ b/js/src/jit-test/tests/basic/testCrossCompartmentTransparency2.js
@@ -8,18 +8,8 @@ var number = g.eval("new Number(42)");
 var bool = g.eval("new Boolean(false)");
 var string = g.eval("new String('ponies')");
 assertEq(JSON.stringify({n:number, b:bool, s:string}), "{\"n\":42,\"b\":false,\"s\":\"ponies\"}");
 assertEq(JSON.stringify({arr:array}), "{\"arr\":[1,2,3]}");
 assertEq(JSON.stringify({2:'ponies', unicorns:'not real'}, array), "{\"2\":\"ponies\"}");
 assertEq(JSON.stringify({42:true, ponies:true, unicorns:'sad'}, [number, string]), "{\"42\":true,\"ponies\":true}");
 assertEq(JSON.stringify({a:true,b:false}, undefined, number), "{\n          \"a\": true,\n          \"b\": false\n}");
 assertEq(JSON.stringify({a:true,b:false}, undefined, string), "{\nponies\"a\": true,\nponies\"b\": false\n}");
-
-var o = Proxy.create({getPropertyDescriptor:function(name) {}}, Object.prototype);
-var threw = false;
-try {
-    print([].concat(o).toString());
-} catch(e) {
-    assertEq(e instanceof TypeError, true);
-    threw = true;
-}
-assertEq(threw, true);
deleted file mode 100644
--- a/js/src/jit-test/tests/basic/testCrossGlobalInvokeSession.js
+++ /dev/null
@@ -1,12 +0,0 @@
-otherGlobal = newGlobal("same-compartment");
-otherGlobal.poison = Proxy.create({});
-callee = new otherGlobal.Function("return this.poison;");
-
-var caught = false;
-try {
-    [1,2,3,4,5,6,7,8].sort(callee);
-} catch(e) {
-    assertEq(e instanceof Error, true);
-    caught = true;
-}
-assertEq(caught, true);
deleted file mode 100644
--- a/js/src/jit-test/tests/basic/testReconstructImacroPCStack.js
+++ /dev/null
@@ -1,28 +0,0 @@
-x = Proxy.create((function () {
-    return {
-        get: function () {}
-    }
-}()), Object.e)
-
-var hit = false;
-
-try {
-    Function("\
-      for(var a = 0; a < 2; ++a) {\
-        if (a == 0) {}\
-        else {\
-          x > x\
-        }\
-      }\
-    ")()
-} catch (e) {
-    hit = true;
-
-    var str = String(e);
-    var match = (str == "TypeError: x is not a function" ||
-                 str == "TypeError: can't convert x to number");
-
-    assertEq(match, true);
-}
-
-assertEq(hit, true);
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -63,17 +63,17 @@
 #include "gc/Heap.h"
 
 #include "vm/ObjectImpl.h"
 #include "vm/String.h"
 
 namespace js {
 
 class AutoPropDescArrayRooter;
-class ProxyHandler;
+class BaseProxyHandler;
 class CallObject;
 struct GCMarker;
 struct NativeIterator;
 
 namespace mjit { class Compiler; }
 
 inline JSObject *
 CastAsObject(PropertyOp op)
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -40,22 +40,23 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include <string.h>
 #include "jsapi.h"
 #include "jscntxt.h"
 #include "jsgc.h"
 #include "jsprvtd.h"
 #include "jsnum.h"
-#include "jsobj.h"
+#include "jsobjinlines.h"
 #include "jsproxy.h"
 #include "jsscope.h"
 
 #include "gc/Marking.h"
 #include "vm/MethodGuard.h"
+#include "vm/RegExpObject-inl.h"
 
 #include "jsatominlines.h"
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
 
 using namespace js;
 using namespace js::gc;
 
@@ -91,48 +92,48 @@ OperationInProgress(JSContext *cx, JSObj
         if (op->object == proxy)
             return true;
         op = op->next;
     }
     return false;
 }
 #endif
 
-ProxyHandler::ProxyHandler(void *family) : mFamily(family)
+BaseProxyHandler::BaseProxyHandler(void *family) : mFamily(family)
 {
 }
 
-ProxyHandler::~ProxyHandler()
+BaseProxyHandler::~BaseProxyHandler()
 {
 }
 
 bool
-ProxyHandler::has(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
+BaseProxyHandler::has(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
 {
     JS_ASSERT(OperationInProgress(cx, proxy));
     AutoPropertyDescriptorRooter desc(cx);
     if (!getPropertyDescriptor(cx, proxy, id, false, &desc))
         return false;
     *bp = !!desc.obj;
     return true;
 }
 
 bool
-ProxyHandler::hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
+BaseProxyHandler::hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
 {
     JS_ASSERT(OperationInProgress(cx, proxy));
     AutoPropertyDescriptorRooter desc(cx);
     if (!getOwnPropertyDescriptor(cx, proxy, id, false, &desc))
         return false;
     *bp = !!desc.obj;
     return true;
 }
 
 bool
-ProxyHandler::get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, Value *vp)
+BaseProxyHandler::get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, Value *vp)
 {
     JS_ASSERT(OperationInProgress(cx, proxy));
     AutoPropertyDescriptorRooter desc(cx);
     if (!getPropertyDescriptor(cx, proxy, id, false, &desc))
         return false;
     if (!desc.obj) {
         vp->setUndefined();
         return true;
@@ -149,17 +150,17 @@ ProxyHandler::get(JSContext *cx, JSObjec
     else
         vp->setUndefined();
     if (desc.attrs & JSPROP_SHORTID)
         id = INT_TO_JSID(desc.shortid);
     return CallJSPropertyOp(cx, desc.getter, receiver, id, vp);
 }
 
 bool
-ProxyHandler::getElementIfPresent(JSContext *cx, JSObject *proxy_, JSObject *receiver_, uint32_t index, Value *vp, bool *present)
+BaseProxyHandler::getElementIfPresent(JSContext *cx, JSObject *proxy_, JSObject *receiver_, uint32_t index, Value *vp, bool *present)
 {
     RootedVarObject proxy(cx, proxy_);
     RootedVarObject receiver(cx, receiver_);
 
     jsid id;
     if (!IndexToId(cx, index, &id))
         return false;
 
@@ -170,17 +171,17 @@ ProxyHandler::getElementIfPresent(JSCont
         Debug_SetValueRangeToCrashOnTouch(vp, 1);
         return true;
     }
 
     return get(cx, proxy, receiver, id, vp);
 }   
 
 bool
-ProxyHandler::set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, bool strict,
+BaseProxyHandler::set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, bool strict,
                   Value *vp)
 {
     JS_ASSERT(OperationInProgress(cx, proxy));
     AutoPropertyDescriptorRooter desc(cx);
     if (!getOwnPropertyDescriptor(cx, proxy, id, true, &desc))
         return false;
     /* The control-flow here differs from ::get() because of the fall-through case below. */
     if (desc.obj) {
@@ -240,17 +241,17 @@ ProxyHandler::set(JSContext *cx, JSObjec
     desc.attrs = JSPROP_ENUMERATE;
     desc.shortid = 0;
     desc.getter = NULL;
     desc.setter = NULL; // Pick up the class getter/setter.
     return defineProperty(cx, receiver, id, &desc);
 }
 
 bool
-ProxyHandler::keys(JSContext *cx, JSObject *proxy, AutoIdVector &props)
+BaseProxyHandler::keys(JSContext *cx, JSObject *proxy, AutoIdVector &props)
 {
     JS_ASSERT(OperationInProgress(cx, proxy));
     JS_ASSERT(props.length() == 0);
 
     if (!getOwnPropertyNames(cx, proxy, props))
         return false;
 
     /* Select only the enumerable properties through in-place iteration. */
@@ -267,137 +268,307 @@ ProxyHandler::keys(JSContext *cx, JSObje
 
     JS_ASSERT(i <= props.length());
     props.resize(i);
 
     return true;
 }
 
 bool
-ProxyHandler::iterate(JSContext *cx, JSObject *proxy_, unsigned flags, Value *vp)
+BaseProxyHandler::iterate(JSContext *cx, JSObject *proxy_, unsigned flags, Value *vp)
 {
     RootedVarObject proxy(cx, proxy_);
 
     JS_ASSERT(OperationInProgress(cx, proxy));
     AutoIdVector props(cx);
     if ((flags & JSITER_OWNONLY)
         ? !keys(cx, proxy, props)
         : !enumerate(cx, proxy, props)) {
         return false;
     }
     return EnumeratedIdVectorToIterator(cx, proxy, flags, props, vp);
 }
 
-JSString *
-ProxyHandler::obj_toString(JSContext *cx, JSObject *proxy)
+bool
+BaseProxyHandler::call(JSContext *cx, JSObject *proxy, unsigned argc,
+                       Value *vp)
 {
-    JS_ASSERT(proxy->isProxy());
+    Value v = UndefinedValue();
+    js_ReportIsNotFunction(cx, &v, 0);
+    return false;
+}
 
-    return JS_NewStringCopyZ(cx, IsFunctionProxy(proxy)
-                                 ? "[object Function]"
-                                 : "[object Object]");
+bool
+BaseProxyHandler::construct(JSContext *cx, JSObject *proxy, unsigned argc,
+                            Value *argv, Value *rval)
+{
+    Value v = UndefinedValue();
+    js_ReportIsNotFunction(cx, &v, JSV2F_CONSTRUCT);
+    return false;
 }
 
 JSString *
-ProxyHandler::fun_toString(JSContext *cx, JSObject *proxy, unsigned indent)
+BaseProxyHandler::obj_toString(JSContext *cx, JSObject *proxy)
 {
-    JS_ASSERT(proxy->isProxy());
-    Value fval = GetCall(proxy);
-    if (IsFunctionProxy(proxy) &&
-        (fval.isPrimitive() || !fval.toObject().isFunction())) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
-                             JSMSG_INCOMPATIBLE_PROTO,
-                             js_Function_str, js_toString_str,
-                             "object");
-        return NULL;
-    }
-    return fun_toStringHelper(cx, &fval.toObject(), indent);
+    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
+                         JSMSG_INCOMPATIBLE_PROTO,
+                         js_Object_str, js_toString_str,
+                         "object");
+    return NULL;
+}
+
+JSString *
+BaseProxyHandler::fun_toString(JSContext *cx, JSObject *proxy, unsigned indent)
+{
+    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
+                         JSMSG_INCOMPATIBLE_PROTO,
+                         js_Function_str, js_toString_str,
+                         "object");
+    return NULL;
 }
 
 bool
-ProxyHandler::regexp_toShared(JSContext *cx, JSObject *proxy, RegExpGuard *g)
+BaseProxyHandler::regexp_toShared(JSContext *cx, JSObject *proxy,
+                                  RegExpGuard *g)
 {
     JS_NOT_REACHED("This should have been a wrapped regexp");
     return false;
 }
 
 bool
-ProxyHandler::defaultValue(JSContext *cx, JSObject *proxy, JSType hint, Value *vp)
+BaseProxyHandler::defaultValue(JSContext *cx, JSObject *proxy, JSType hint,
+                               Value *vp)
 {
     return DefaultValue(cx, RootedVarObject(cx, proxy), hint, vp);
 }
 
 bool
-ProxyHandler::iteratorNext(JSContext *cx, JSObject *proxy, Value *vp)
+BaseProxyHandler::iteratorNext(JSContext *cx, JSObject *proxy, Value *vp)
 {
     vp->setMagic(JS_NO_ITER_VALUE);
     return true;
 }
 
 bool
-ProxyHandler::call(JSContext *cx, JSObject *proxy, unsigned argc, Value *vp)
+BaseProxyHandler::nativeCall(JSContext *cx, JSObject *proxy, Class *clasp, Native native, CallArgs args)
+{
+    JS_ASSERT(OperationInProgress(cx, proxy));
+    ReportIncompatibleMethod(cx, args, clasp);
+    return false;
+}
+
+bool
+BaseProxyHandler::hasInstance(JSContext *cx, JSObject *proxy, const Value *vp, bool *bp)
+{
+    JS_ASSERT(OperationInProgress(cx, proxy));
+    js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS,
+                        JSDVG_SEARCH_STACK, ObjectValue(*proxy), NULL);
+    return false;
+}
+
+JSType
+BaseProxyHandler::typeOf(JSContext *cx, JSObject *proxy)
+{
+    JS_ASSERT(OperationInProgress(cx, proxy));
+    return IsFunctionProxy(proxy) ? JSTYPE_FUNCTION : JSTYPE_OBJECT;
+}
+
+bool
+BaseProxyHandler::objectClassIs(JSObject *proxy, ESClassValue classValue, JSContext *cx)
 {
     JS_ASSERT(OperationInProgress(cx, proxy));
-    RootedVarValue rval(cx);
-    JSBool ok = Invoke(cx, vp[1], GetCall(proxy), argc, JS_ARGV(cx, vp), rval.address());
+    return false;
+}
+
+void
+BaseProxyHandler::finalize(JSFreeOp *fop, JSObject *proxy)
+{
+}
+
+void
+BaseProxyHandler::trace(JSTracer *trc, JSObject *proxy)
+{
+}
+
+IndirectProxyHandler::IndirectProxyHandler(void *family) : BaseProxyHandler(family)
+{
+}
+
+bool
+IndirectProxyHandler::getPropertyDescriptor(JSContext *cx, JSObject *proxy,
+                                            jsid id, bool set,
+                                            PropertyDescriptor *desc)
+{
+    return JS_GetPropertyDescriptorById(cx, GetProxyTargetObject(proxy), id,
+                                        JSRESOLVE_QUALIFIED, desc);
+}
+
+static bool
+GetOwnPropertyDescriptor(JSContext *cx, JSObject *obj, jsid id, unsigned flags, JSPropertyDescriptor *desc)
+{
+    // If obj is a proxy, we can do better than just guessing. This is
+    // important for certain types of wrappers that wrap other wrappers.
+    if (obj->isProxy())
+        return Proxy::getOwnPropertyDescriptor(cx, obj, id, flags & JSRESOLVE_ASSIGNING, desc);
+
+    if (!JS_GetPropertyDescriptorById(cx, obj, id, flags, desc))
+        return false;
+    if (desc->obj != obj)
+        desc->obj = NULL;
+    return true;
+}
+
+bool
+IndirectProxyHandler::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy,
+                                               jsid id, bool set,
+                                               PropertyDescriptor *desc)
+{
+    return GetOwnPropertyDescriptor(cx, GetProxyTargetObject(proxy), id,
+                                    JSRESOLVE_QUALIFIED, desc);
+}
+
+bool
+IndirectProxyHandler::defineProperty(JSContext *cx, JSObject *proxy, jsid id,
+                                     PropertyDescriptor *desc)
+{
+    return JS_DefinePropertyById(cx, GetProxyTargetObject(proxy), id,
+                                 desc->value, desc->getter, desc->setter,
+                                 desc->attrs);
+}
+
+bool
+IndirectProxyHandler::getOwnPropertyNames(JSContext *cx, JSObject *proxy,
+                                          AutoIdVector &props)
+{
+    return GetPropertyNames(cx, GetProxyTargetObject(proxy),
+                            JSITER_OWNONLY | JSITER_HIDDEN, &props);
+}
+
+bool
+IndirectProxyHandler::delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
+{
+    Value v;
+    if (!JS_DeletePropertyById2(cx, GetProxyTargetObject(proxy), id, &v))
+        return false;
+    JSBool b;
+    if (!JS_ValueToBoolean(cx, v, &b))
+        return false;
+    *bp = !!b;
+    return true;
+}
+
+bool
+IndirectProxyHandler::enumerate(JSContext *cx, JSObject *proxy,
+                                AutoIdVector &props)
+{
+    return GetPropertyNames(cx, GetProxyTargetObject(proxy), 0, &props);
+}
+
+bool
+IndirectProxyHandler::call(JSContext *cx, JSObject *proxy, unsigned argc,
+                   Value *vp)
+{
+    JS_ASSERT(OperationInProgress(cx, proxy));
+    AutoValueRooter rval(cx);
+    JSBool ok = Invoke(cx, vp[1], GetCall(proxy), argc, JS_ARGV(cx, vp), rval.addr());
     if (ok)
-        JS_SET_RVAL(cx, vp, rval);
+        JS_SET_RVAL(cx, vp, rval.value());
     return ok;
 }
 
 bool
-ProxyHandler::construct(JSContext *cx, JSObject *proxy,
-                        unsigned argc, Value *argv, Value *rval)
+IndirectProxyHandler::construct(JSContext *cx, JSObject *proxy, unsigned argc,
+                                Value *argv, Value *rval)
 {
     JS_ASSERT(OperationInProgress(cx, proxy));
     Value fval = GetConstruct(proxy);
     if (fval.isUndefined())
         return InvokeConstructor(cx, GetCall(proxy), argc, argv, rval);
     return Invoke(cx, UndefinedValue(), fval, argc, argv, rval);
 }
 
 bool
-ProxyHandler::nativeCall(JSContext *cx, JSObject *proxy, Class *clasp, Native native, CallArgs args)
+IndirectProxyHandler::nativeCall(JSContext *cx, JSObject *proxy, Class *clasp,
+                                 Native native, CallArgs args)
 {
-    JS_ASSERT(OperationInProgress(cx, proxy));
-    ReportIncompatibleMethod(cx, args, clasp);
-    return false;
+    return CallJSNative(cx, native, args);
 }
 
 bool
-ProxyHandler::hasInstance(JSContext *cx, JSObject *proxy, const Value *vp, bool *bp)
+IndirectProxyHandler::hasInstance(JSContext *cx, JSObject *proxy, const Value *vp,
+                                  bool *bp)
 {
-    JS_ASSERT(OperationInProgress(cx, proxy));
-    js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS,
-                        JSDVG_SEARCH_STACK, ObjectValue(*proxy), NULL);
-    return false;
+    JSBool b;
+    if (!JS_HasInstance(cx, GetProxyTargetObject(proxy), *vp, &b))
+        return false;
+    *bp = !!b;
+    return true;
 }
 
 JSType
-ProxyHandler::typeOf(JSContext *cx, JSObject *proxy)
+IndirectProxyHandler::typeOf(JSContext *cx, JSObject *proxy) 
+{
+    return TypeOfValue(cx, ObjectValue(*GetProxyTargetObject(proxy)));
+}
+
+bool
+IndirectProxyHandler::objectClassIs(JSObject *proxy, ESClassValue classValue,
+                                    JSContext *cx)
 {
-    JS_ASSERT(OperationInProgress(cx, proxy));
-    return IsFunctionProxy(proxy) ? JSTYPE_FUNCTION : JSTYPE_OBJECT;
+    return ObjectClassIs(*GetProxyTargetObject(proxy), classValue, cx);
+}
+
+JSString *
+IndirectProxyHandler::obj_toString(JSContext *cx, JSObject *proxy)
+{
+    return obj_toStringHelper(cx, GetProxyTargetObject(proxy));
+}
+
+JSString *
+IndirectProxyHandler::fun_toString(JSContext *cx, JSObject *proxy,
+                                   unsigned indent)
+{
+    return fun_toStringHelper(cx, GetProxyTargetObject(proxy), indent);
 }
 
 bool
-ProxyHandler::objectClassIs(JSObject *proxy, ESClassValue classValue, JSContext *cx)
+IndirectProxyHandler::regexp_toShared(JSContext *cx, JSObject *proxy,
+                                      RegExpGuard *g)
+{
+    return RegExpToShared(cx, *GetProxyTargetObject(proxy), g);
+}
+
+bool
+IndirectProxyHandler::defaultValue(JSContext *cx, JSObject *proxy, JSType hint,
+                                   Value *vp)
 {
-    JS_ASSERT(OperationInProgress(cx, proxy));
-    return false;
+    *vp = ObjectValue(*GetProxyTargetObject(proxy));
+    if (hint == JSTYPE_VOID)
+        return ToPrimitive(cx, vp);
+    return ToPrimitive(cx, hint, vp);
+}
+
+bool
+IndirectProxyHandler::iteratorNext(JSContext *cx, JSObject *proxy, Value *vp)
+{
+    if (!js_IteratorMore(cx, RootedVarObject(cx, GetProxyTargetObject(proxy)),
+                         vp))
+        return false;
+    if (vp->toBoolean()) {
+        *vp = cx->iterValue;
+        cx->iterValue.setUndefined();
+    } else 
+        vp->setMagic(JS_NO_ITER_VALUE);
+    return true;
 }
 
 void
-ProxyHandler::finalize(JSFreeOp *fop, JSObject *proxy)
+IndirectProxyHandler::trace(JSTracer *trc, JSObject *proxy)
 {
-}
-
-void
-ProxyHandler::trace(JSTracer *trc, JSObject *proxy)
-{
+    MarkSlot(trc, &proxy->getReservedSlotRef(JSSLOT_PROXY_PRIVATE), "targetObject");
 }
 
 static bool
 GetTrap(JSContext *cx, JSObject *handler, PropertyName *name, Value *fvalp)
 {
     JS_CHECK_RECURSION(cx, return false);
 
     return handler->getGeneric(cx, NameToId(name), fvalp);
@@ -516,17 +687,17 @@ ArrayToIdVector(JSContext *cx, const Val
         if (!props.append(id))
             return false;
     }
 
     return true;
 }
 
 /* Derived class for all scripted proxy handlers. */
-class ScriptedProxyHandler : public ProxyHandler {
+class ScriptedProxyHandler : public IndirectProxyHandler {
   public:
     ScriptedProxyHandler();
     virtual ~ScriptedProxyHandler();
 
     /* ES5 Harmony fundamental proxy traps. */
     virtual bool getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
                                        PropertyDescriptor *desc);
     virtual bool getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
@@ -541,22 +712,24 @@ class ScriptedProxyHandler : public Prox
     virtual bool has(JSContext *cx, JSObject *proxy, jsid id, bool *bp);
     virtual bool hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp);
     virtual bool get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, Value *vp);
     virtual bool set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, bool strict,
                      Value *vp);
     virtual bool keys(JSContext *cx, JSObject *proxy, AutoIdVector &props);
     virtual bool iterate(JSContext *cx, JSObject *proxy, unsigned flags, Value *vp);
 
+    virtual JSType typeOf(JSContext *cx, JSObject *proxy);
+
     static ScriptedProxyHandler singleton;
 };
 
 static int sScriptedProxyHandlerFamily = 0;
 
-ScriptedProxyHandler::ScriptedProxyHandler() : ProxyHandler(&sScriptedProxyHandlerFamily)
+ScriptedProxyHandler::ScriptedProxyHandler() : IndirectProxyHandler(&sScriptedProxyHandlerFamily)
 {
 }
 
 ScriptedProxyHandler::~ScriptedProxyHandler()
 {
 }
 
 static bool
@@ -656,31 +829,31 @@ bool
 ScriptedProxyHandler::has(JSContext *cx, JSObject *proxy_, jsid id, bool *bp)
 {
     RootedVarObject proxy(cx, proxy_);
     RootedVarObject handler(cx, GetProxyHandlerObject(cx, proxy));
     RootedVarValue fval(cx), value(cx);
     if (!GetDerivedTrap(cx, handler, ATOM(has), fval.address()))
         return false;
     if (!js_IsCallable(fval))
-        return ProxyHandler::has(cx, proxy, id, bp);
+        return BaseProxyHandler::has(cx, proxy, id, bp);
     return Trap1(cx, handler, fval, id, value.address()) &&
            ValueToBool(cx, value, bp);
 }
 
 bool
 ScriptedProxyHandler::hasOwn(JSContext *cx, JSObject *proxy_, jsid id, bool *bp)
 {
     RootedVarObject proxy(cx, proxy_);
     RootedVarObject handler(cx, GetProxyHandlerObject(cx, proxy));
     RootedVarValue fval(cx), value(cx);
     if (!GetDerivedTrap(cx, handler, ATOM(hasOwn), fval.address()))
         return false;
     if (!js_IsCallable(fval))
-        return ProxyHandler::hasOwn(cx, proxy, id, bp);
+        return BaseProxyHandler::hasOwn(cx, proxy, id, bp);
     return Trap1(cx, handler, fval, id, value.address()) &&
            ValueToBool(cx, value, bp);
 }
 
 bool
 ScriptedProxyHandler::get(JSContext *cx, JSObject *proxy_, JSObject *receiver, jsid id_, Value *vp)
 {
     RootedVarId id(cx, id_);
@@ -690,17 +863,17 @@ ScriptedProxyHandler::get(JSContext *cx,
     if (!str)
         return false;
     RootedVarValue value(cx, StringValue(str));
     Value argv[] = { ObjectOrNullValue(receiver), value };
     RootedVarValue fval(cx);
     if (!GetDerivedTrap(cx, handler, ATOM(get), fval.address()))
         return false;
     if (!js_IsCallable(fval))
-        return ProxyHandler::get(cx, proxy, receiver, id, vp);
+        return BaseProxyHandler::get(cx, proxy, receiver, id, vp);
     return Trap(cx, handler, fval, 2, argv, vp);
 }
 
 bool
 ScriptedProxyHandler::set(JSContext *cx, JSObject *proxy_, JSObject *receiver, jsid id_, bool strict,
                           Value *vp)
 {
     RootedVarId id(cx, id_);
@@ -710,48 +883,59 @@ ScriptedProxyHandler::set(JSContext *cx,
     if (!str)
         return false;
     RootedVarValue value(cx, StringValue(str));
     Value argv[] = { ObjectOrNullValue(receiver), value, *vp };
     RootedVarValue fval(cx);
     if (!GetDerivedTrap(cx, handler, ATOM(set), fval.address()))
         return false;
     if (!js_IsCallable(fval))
-        return ProxyHandler::set(cx, proxy, receiver, id, strict, vp);
+        return BaseProxyHandler::set(cx, proxy, receiver, id, strict, vp);
     return Trap(cx, handler, fval, 3, argv, value.address());
 }
 
 bool
 ScriptedProxyHandler::keys(JSContext *cx, JSObject *proxy_, AutoIdVector &props)
 {
     RootedVarObject proxy(cx, proxy_);
     RootedVarObject handler(cx, GetProxyHandlerObject(cx, proxy));
     RootedVarValue value(cx);
     if (!GetDerivedTrap(cx, handler, ATOM(keys), value.address()))
         return false;
     if (!js_IsCallable(value))
-        return ProxyHandler::keys(cx, proxy, props);
+        return BaseProxyHandler::keys(cx, proxy, props);
     return Trap(cx, handler, value, 0, NULL, value.address()) &&
            ArrayToIdVector(cx, value, props);
 }
 
 bool
 ScriptedProxyHandler::iterate(JSContext *cx, JSObject *proxy_, unsigned flags, Value *vp)
 {
     RootedVarObject proxy(cx, proxy_);
     RootedVarObject handler(cx, GetProxyHandlerObject(cx, proxy));
     RootedVarValue value(cx);
     if (!GetDerivedTrap(cx, handler, ATOM(iterate), value.address()))
         return false;
     if (!js_IsCallable(value))
-        return ProxyHandler::iterate(cx, proxy, flags, vp);
+        return BaseProxyHandler::iterate(cx, proxy, flags, vp);
     return Trap(cx, handler, value, 0, NULL, vp) &&
            ReturnedValueMustNotBePrimitive(cx, proxy, ATOM(iterate), *vp);
 }
 
+JSType
+ScriptedProxyHandler::typeOf(JSContext *cx, JSObject *proxy) 
+{
+    /*
+     * This function is only here to prevent a regression in
+     * js1_8_5/extensions/scripted-proxies.js. It will be removed when the
+     * direct proxy refactor is complete.
+     */
+    return IsFunctionProxy(proxy) ? JSTYPE_FUNCTION : JSTYPE_OBJECT;
+}
+
 ScriptedProxyHandler ScriptedProxyHandler::singleton;
 
 class AutoPendingProxyOperation {
     JSRuntime               *rt;
     PendingProxyOperation   op;
   public:
     AutoPendingProxyOperation(JSContext *cx, JSObject *proxy)
         : rt(cx->runtime), op(cx, proxy)
@@ -1482,17 +1666,17 @@ JS_FRIEND_DATA(Class) js::FunctionProxyC
         NULL,                /* enumerate       */
         proxy_TypeOf,
         NULL,                /* thisObject      */
         NULL,                /* clear           */
     }
 };
 
 JS_FRIEND_API(JSObject *)
-js::NewProxyObject(JSContext *cx, ProxyHandler *handler, const Value &priv_, JSObject *proto_,
+js::NewProxyObject(JSContext *cx, BaseProxyHandler *handler, const Value &priv_, JSObject *proto_,
                    JSObject *parent_, JSObject *call_, JSObject *construct_)
 {
     RootedVarValue priv(cx, priv_);
     RootedVarObject proto(cx, proto_), parent(cx, parent_), call(cx, call_), construct(cx, construct_);
 
     JS_ASSERT_IF(proto, cx->compartment == proto->compartment());
     JS_ASSERT_IF(parent, cx->compartment == parent->compartment());
     bool fun = call || construct;
--- a/js/src/jsproxy.h
+++ b/js/src/jsproxy.h
@@ -43,21 +43,21 @@
 #define jsproxy_h___
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
 
 namespace js {
 
 /* Base class for all C++ proxy handlers. */
-class JS_FRIEND_API(ProxyHandler) {
+class JS_FRIEND_API(BaseProxyHandler) {
     void *mFamily;
   public:
-    explicit ProxyHandler(void *family);
-    virtual ~ProxyHandler();
+    explicit BaseProxyHandler(void *family);
+    virtual ~BaseProxyHandler();
 
     /* ES5 Harmony fundamental proxy traps. */
     virtual bool getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
                                        PropertyDescriptor *desc) = 0;
     virtual bool getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
                                           PropertyDescriptor *desc) = 0;
     virtual bool defineProperty(JSContext *cx, JSObject *proxy, jsid id,
                                 PropertyDescriptor *desc) = 0;
@@ -95,16 +95,62 @@ class JS_FRIEND_API(ProxyHandler) {
         return false;
     }
 
     inline void *family() {
         return mFamily;
     }
 };
 
+class JS_PUBLIC_API(IndirectProxyHandler) : public BaseProxyHandler {
+  public:
+    explicit IndirectProxyHandler(void *family);
+
+    /* ES5 Harmony fundamental proxy traps. */
+    virtual bool getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id,
+                                       bool set,
+                                       PropertyDescriptor *desc) MOZ_OVERRIDE;
+    virtual bool getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy,
+                                          jsid id, bool set,
+                                          PropertyDescriptor *desc) MOZ_OVERRIDE;
+    virtual bool defineProperty(JSContext *cx, JSObject *proxy, jsid id,
+                                PropertyDescriptor *desc) MOZ_OVERRIDE;
+    virtual bool getOwnPropertyNames(JSContext *cx, JSObject *proxy,
+                                     AutoIdVector &props) MOZ_OVERRIDE;
+    virtual bool delete_(JSContext *cx, JSObject *proxy, jsid id,
+                         bool *bp) MOZ_OVERRIDE;
+    virtual bool enumerate(JSContext *cx, JSObject *proxy,
+                           AutoIdVector &props) MOZ_OVERRIDE;
+
+    /* Derived traps are implemented in terms of fundamental traps */
+
+    /* Spidermonkey extensions. */
+    virtual bool call(JSContext *cx, JSObject *proxy, unsigned argc,
+                      Value *vp) MOZ_OVERRIDE;
+    virtual bool construct(JSContext *cx, JSObject *proxy, unsigned argc,
+                           Value *argv, Value *rval) MOZ_OVERRIDE;
+    virtual bool nativeCall(JSContext *cx, JSObject *proxy, Class *clasp,
+                            Native native, CallArgs args) MOZ_OVERRIDE;
+    virtual bool hasInstance(JSContext *cx, JSObject *proxy, const Value *vp,
+                             bool *bp) MOZ_OVERRIDE;
+    virtual JSType typeOf(JSContext *cx, JSObject *proxy) MOZ_OVERRIDE;
+    virtual bool objectClassIs(JSObject *obj, ESClassValue classValue,
+                               JSContext *cx) MOZ_OVERRIDE;
+    virtual JSString *obj_toString(JSContext *cx, JSObject *proxy) MOZ_OVERRIDE;
+    virtual JSString *fun_toString(JSContext *cx, JSObject *proxy,
+                                   unsigned indent) MOZ_OVERRIDE;
+    virtual bool regexp_toShared(JSContext *cx, JSObject *proxy,
+                                 RegExpGuard *g) MOZ_OVERRIDE;
+    virtual bool defaultValue(JSContext *cx, JSObject *obj, JSType hint,
+                              Value *vp) MOZ_OVERRIDE;
+    virtual bool iteratorNext(JSContext *cx, JSObject *proxy,
+                              Value *vp) MOZ_OVERRIDE;
+    virtual void trace(JSTracer *trc, JSObject *proxy) MOZ_OVERRIDE;
+};
+
 /* Dispatch point for handlers that executes the appropriate C++ or scripted traps. */
 class Proxy {
   public:
     /* ES5 Harmony fundamental proxy traps. */
     static bool getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
                                       PropertyDescriptor *desc);
     static bool getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set, Value *vp);
     static bool getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
@@ -171,39 +217,46 @@ inline bool IsProxy(const JSObject *obj)
 /* Shared between object and function proxies. */
 const uint32_t JSSLOT_PROXY_HANDLER = 0;
 const uint32_t JSSLOT_PROXY_PRIVATE = 1;
 const uint32_t JSSLOT_PROXY_EXTRA   = 2;
 /* Function proxies only. */
 const uint32_t JSSLOT_PROXY_CALL = 4;
 const uint32_t JSSLOT_PROXY_CONSTRUCT = 5;
 
-inline ProxyHandler *
+inline BaseProxyHandler *
 GetProxyHandler(const JSObject *obj)
 {
     JS_ASSERT(IsProxy(obj));
-    return (ProxyHandler *) GetReservedSlot(obj, JSSLOT_PROXY_HANDLER).toPrivate();
+    return (BaseProxyHandler *) GetReservedSlot(obj, JSSLOT_PROXY_HANDLER).toPrivate();
 }
 
 inline const Value &
 GetProxyPrivate(const JSObject *obj)
 {
     JS_ASSERT(IsProxy(obj));
     return GetReservedSlot(obj, JSSLOT_PROXY_PRIVATE);
 }
 
+inline JSObject *
+GetProxyTargetObject(const JSObject *obj)
+{
+    JS_ASSERT(IsProxy(obj));
+    return GetProxyPrivate(obj).toObjectOrNull();
+}
+
 inline const Value &
 GetProxyExtra(const JSObject *obj, size_t n)
 {
     JS_ASSERT(IsProxy(obj));
     return GetReservedSlot(obj, JSSLOT_PROXY_EXTRA + n);
 }
 
 inline void
-SetProxyHandler(JSObject *obj, ProxyHandler *handler)
+SetProxyHandler(JSObject *obj, BaseProxyHandler *handler)
 {
     JS_ASSERT(IsProxy(obj));
     SetReservedSlot(obj, JSSLOT_PROXY_HANDLER, PrivateValue(handler));
 }
 
 inline void
 SetProxyPrivate(JSObject *obj, const Value &value)
 {
@@ -215,17 +268,17 @@ inline void
 SetProxyExtra(JSObject *obj, size_t n, const Value &extra)
 {
     JS_ASSERT(IsProxy(obj));
     JS_ASSERT(n <= 1);
     SetReservedSlot(obj, JSSLOT_PROXY_EXTRA + n, extra);
 }
 
 JS_FRIEND_API(JSObject *)
-NewProxyObject(JSContext *cx, ProxyHandler *handler, const Value &priv,
+NewProxyObject(JSContext *cx, BaseProxyHandler *handler, const Value &priv,
                JSObject *proto, JSObject *parent,
                JSObject *call = NULL, JSObject *construct = NULL);
 
 } /* namespace js */
 
 JS_BEGIN_EXTERN_C
 
 extern JS_FRIEND_API(JSObject *)
--- a/js/src/jsprvtd.h
+++ b/js/src/jsprvtd.h
@@ -177,17 +177,17 @@ struct SharedContext;
 class TokenStream;
 struct Token;
 struct TokenPos;
 struct TokenPtr;
 struct TreeContext;
 class UpvarCookie;
 
 class Proxy;
-class ProxyHandler;
+class BaseProxyHandler;
 class Wrapper;
 class CrossCompartmentWrapper;
 
 class TempAllocPolicy;
 class RuntimeAllocPolicy;
 
 class GlobalObject;
 
--- a/js/src/jswrapper.cpp
+++ b/js/src/jswrapper.cpp
@@ -107,17 +107,17 @@ js::UnwrapObjectChecked(JSContext *cx, J
 bool
 js::IsCrossCompartmentWrapper(const JSObject *wrapper)
 {
     return wrapper->isWrapper() &&
            !!(Wrapper::wrapperHandler(wrapper)->flags() & Wrapper::CROSS_COMPARTMENT);
 }
 
 AbstractWrapper::AbstractWrapper(unsigned flags) :
-    ProxyHandler(&sWrapperFamily),
+    IndirectProxyHandler(&sWrapperFamily),
     mFlags(flags)
 {
 }
 
 Wrapper::Wrapper(unsigned flags) : AbstractWrapper(flags)
 {
 }
 
@@ -138,81 +138,55 @@ Wrapper::~Wrapper()
 #define SET(action) CHECKED(action, SET)
 #define GET(action) CHECKED(action, GET)
 
 bool
 AbstractWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, bool set,
                                        PropertyDescriptor *desc)
 {
     desc->obj = NULL; // default result if we refuse to perform this action
-    CHECKED(JS_GetPropertyDescriptorById(cx, wrappedObject(wrapper), id, JSRESOLVE_QUALIFIED, desc),
+    CHECKED(IndirectProxyHandler::getPropertyDescriptor(cx, wrapper, id, set, desc),
             set ? SET : GET);
 }
 
-static bool
-GetOwnPropertyDescriptor(JSContext *cx, JSObject *obj, jsid id, unsigned flags, JSPropertyDescriptor *desc)
-{
-    // If obj is a proxy, we can do better than just guessing. This is
-    // important for certain types of wrappers that wrap other wrappers.
-    if (obj->isProxy())
-        return Proxy::getOwnPropertyDescriptor(cx, obj, id, flags & JSRESOLVE_ASSIGNING, desc);
-
-    if (!JS_GetPropertyDescriptorById(cx, obj, id, flags, desc))
-        return false;
-    if (desc->obj != obj)
-        desc->obj = NULL;
-    return true;
-}
-
 bool
 AbstractWrapper::getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, bool set,
                                           PropertyDescriptor *desc)
 {
     desc->obj = NULL; // default result if we refuse to perform this action
-    CHECKED(GetOwnPropertyDescriptor(cx, wrappedObject(wrapper), id, JSRESOLVE_QUALIFIED, desc),
-            set ? SET : GET);
+    CHECKED(IndirectProxyHandler::getOwnPropertyDescriptor(cx, wrapper, id, set, desc), GET);
 }
 
 bool
 AbstractWrapper::defineProperty(JSContext *cx, JSObject *wrapper, jsid id, PropertyDescriptor *desc)
 {
-    SET(JS_DefinePropertyById(cx, wrappedObject(wrapper), id, desc->value,
-                              desc->getter, desc->setter, desc->attrs));
+    SET(IndirectProxyHandler::defineProperty(cx, wrapper, id, desc));
 }
 
 bool
 AbstractWrapper::getOwnPropertyNames(JSContext *cx, JSObject *wrapper, AutoIdVector &props)
 {
     // if we refuse to perform this action, props remains empty
     jsid id = JSID_VOID;
-    GET(GetPropertyNames(cx, wrappedObject(wrapper), JSITER_OWNONLY | JSITER_HIDDEN, &props));
-}
-
-static bool
-ValueToBoolean(Value *vp, bool *bp)
-{
-    *bp = js_ValueToBoolean(*vp);
-    return true;
+    GET(IndirectProxyHandler::getOwnPropertyNames(cx, wrapper, props));
 }
 
 bool
 AbstractWrapper::delete_(JSContext *cx, JSObject *wrapper, jsid id, bool *bp)
 {
     *bp = true; // default result if we refuse to perform this action
-    Value v;
-    SET(JS_DeletePropertyById2(cx, wrappedObject(wrapper), id, &v) &&
-        ValueToBoolean(&v, bp));
+    SET(IndirectProxyHandler::delete_(cx, wrapper, id, bp));
 }
 
 bool
 AbstractWrapper::enumerate(JSContext *cx, JSObject *wrapper, AutoIdVector &props)
 {
     // if we refuse to perform this action, props remains empty
     static jsid id = JSID_VOID;
-    GET(GetPropertyNames(cx, wrappedObject(wrapper), 0, &props));
+    GET(IndirectProxyHandler::enumerate(cx, wrapper, props));
 }
 
 static bool
 Cond(JSBool b, bool *bp)
 {
     *bp = !!b;
     return true;
 }
@@ -266,67 +240,54 @@ Wrapper::iterate(JSContext *cx, JSObject
     GET(GetIterator(cx, RootedVarObject(cx, wrappedObject(wrapper)), flags, vp));
 }
 
 bool
 Wrapper::call(JSContext *cx, JSObject *wrapper, unsigned argc, Value *vp)
 {
     vp->setUndefined(); // default result if we refuse to perform this action
     const jsid id = JSID_VOID;
-    CHECKED(ProxyHandler::call(cx, wrapper, argc, vp), CALL);
+    CHECKED(IndirectProxyHandler::call(cx, wrapper, argc, vp), CALL);
 }
 
 bool
 Wrapper::construct(JSContext *cx, JSObject *wrapper, unsigned argc, Value *argv, Value *vp)
 {
     vp->setUndefined(); // default result if we refuse to perform this action
     const jsid id = JSID_VOID;
-    GET(ProxyHandler::construct(cx, wrapper, argc, argv, vp));
+    GET(IndirectProxyHandler::construct(cx, wrapper, argc, argv, vp));
 }
 
 bool
 Wrapper::nativeCall(JSContext *cx, JSObject *wrapper, Class *clasp, Native native, CallArgs args)
 {
     const jsid id = JSID_VOID;
-    CHECKED(CallJSNative(cx, native, args), CALL);
+    CHECKED(IndirectProxyHandler::nativeCall(cx, wrapper, clasp, native, args), CALL);
 }
 
 bool
 Wrapper::hasInstance(JSContext *cx, JSObject *wrapper, const Value *vp, bool *bp)
 {
     *bp = false; // default result if we refuse to perform this action
     const jsid id = JSID_VOID;
-    JSBool b = JS_FALSE;
-    GET(JS_HasInstance(cx, wrappedObject(wrapper), *vp, &b) && Cond(b, bp));
-}
-
-JSType
-Wrapper::typeOf(JSContext *cx, JSObject *wrapper)
-{
-    return TypeOfValue(cx, ObjectValue(*wrappedObject(wrapper)));
-}
-
-bool
-Wrapper::objectClassIs(JSObject *wrapper, ESClassValue classValue, JSContext *cx)
-{
-    return ObjectClassIs(*wrappedObject(wrapper), classValue, cx);
+    GET(IndirectProxyHandler::hasInstance(cx, wrapper, vp, bp));
 }
 
 JSString *
 Wrapper::obj_toString(JSContext *cx, JSObject *wrapper)
 {
     bool status;
     if (!enter(cx, wrapper, JSID_VOID, GET, &status)) {
         if (status) {
             // Perform some default behavior that doesn't leak any information.
             return JS_NewStringCopyZ(cx, "[object Object]");
         }
         return NULL;
     }
-    JSString *str = obj_toStringHelper(cx, wrappedObject(wrapper));
+    JSString *str = IndirectProxyHandler::obj_toString(cx, wrapper);
     leave(cx, wrapper);
     return str;
 }
 
 JSString *
 Wrapper::fun_toString(JSContext *cx, JSObject *wrapper, unsigned indent)
 {
     bool status;
@@ -336,57 +297,21 @@ Wrapper::fun_toString(JSContext *cx, JSO
             if (wrapper->isCallable())
                 return JS_NewStringCopyZ(cx, "function () {\n    [native code]\n}");
             js::Value v = ObjectValue(*wrapper);
             js_ReportIsNotFunction(cx, &v, 0);
             return NULL;
         }
         return NULL;
     }
-    JSString *str = ProxyHandler::fun_toString(cx, wrapper, indent);
+    JSString *str = IndirectProxyHandler::fun_toString(cx, wrapper, indent);
     leave(cx, wrapper);
     return str;
 }
 
-bool
-Wrapper::regexp_toShared(JSContext *cx, JSObject *wrapper, RegExpGuard *g)
-{
-    return RegExpToShared(cx, *wrappedObject(wrapper), g);
-}
-
-bool
-Wrapper::defaultValue(JSContext *cx, JSObject *wrapper, JSType hint, Value *vp)
-{
-    *vp = ObjectValue(*wrappedObject(wrapper));
-    if (hint == JSTYPE_VOID)
-        return ToPrimitive(cx, vp);
-    return ToPrimitive(cx, hint, vp);
-}
-
-bool
-Wrapper::iteratorNext(JSContext *cx, JSObject *wrapper, Value *vp)
-{
-    if (!js_IteratorMore(cx, RootedVarObject(cx, wrappedObject(wrapper)), vp))
-        return false;
-
-    if (vp->toBoolean()) {
-        *vp = cx->iterValue;
-        cx->iterValue.setUndefined();
-    } else {
-        vp->setMagic(JS_NO_ITER_VALUE);
-    }
-    return true;
-}
-
-void
-Wrapper::trace(JSTracer *trc, JSObject *wrapper)
-{
-    MarkSlot(trc, &wrapper->getReservedSlotRef(JSSLOT_PROXY_PRIVATE), "wrappedObject");
-}
-
 JSObject *
 AbstractWrapper::wrappedObject(const JSObject *wrapper)
 {
     return GetProxyPrivate(wrapper).toObjectOrNull();
 }
 
 AbstractWrapper *
 AbstractWrapper::wrapperHandler(const JSObject *wrapper)
@@ -872,37 +797,37 @@ CrossCompartmentWrapper::fun_toString(JS
 
 bool
 CrossCompartmentWrapper::defaultValue(JSContext *cx, JSObject *wrapper, JSType hint, Value *vp)
 {
     AutoCompartment call(cx, wrappedObject(wrapper));
     if (!call.enter())
         return false;
 
-    if (!Wrapper::defaultValue(cx, wrapper, hint, vp))
+    if (!IndirectProxyHandler::defaultValue(cx, wrapper, hint, vp))
         return false;
 
     call.leave();
     return call.origin->wrap(cx, vp);
 }
 
 bool
 CrossCompartmentWrapper::iteratorNext(JSContext *cx, JSObject *wrapper, Value *vp)
 {
     PIERCE(cx, wrapper, GET,
            NOTHING,
-           Wrapper::iteratorNext(cx, wrapper, vp),
+           IndirectProxyHandler::iteratorNext(cx, wrapper, vp),
            call.origin->wrap(cx, vp));
 }
 
 void
 CrossCompartmentWrapper::trace(JSTracer *trc, JSObject *wrapper)
 {
     MarkCrossCompartmentSlot(trc, &wrapper->getReservedSlotRef(JSSLOT_PROXY_PRIVATE),
-                             "wrappedObject");
+                             "targetObject");
 }
 
 CrossCompartmentWrapper CrossCompartmentWrapper::singleton(0u);
 
 /* Security wrappers. */
 
 template <class Base>
 SecurityWrapper<Base>::SecurityWrapper(unsigned flags)
@@ -938,17 +863,17 @@ SecurityWrapper<Base>::regexp_toShared(J
 {
     return Base::regexp_toShared(cx, obj, g);
 }
 
 
 template class js::SecurityWrapper<Wrapper>;
 template class js::SecurityWrapper<CrossCompartmentWrapper>;
 
-class JS_FRIEND_API(DeadObjectProxy) : public ProxyHandler
+class JS_FRIEND_API(DeadObjectProxy) : public BaseProxyHandler
 {
   private:
     static int sDeadObjectFamily;
   public:
 
     explicit DeadObjectProxy();
 
     /* ES5 Harmony fundamental wrapper traps. */
@@ -976,17 +901,17 @@ class JS_FRIEND_API(DeadObjectProxy) : p
     virtual bool getElementIfPresent(JSContext *cx, JSObject *obj, JSObject *receiver,
                                      uint32_t index, Value *vp, bool *present);
 
 
     static DeadObjectProxy singleton;
 };
 
 DeadObjectProxy::DeadObjectProxy()
-  : ProxyHandler(&sDeadObjectFamily)
+  : BaseProxyHandler(&sDeadObjectFamily)
 {
 }
 
 bool
 DeadObjectProxy::getPropertyDescriptor(JSContext *cx, JSObject *wrapper,
                                        jsid id, bool set,
                                        PropertyDescriptor *desc)
 {
--- a/js/src/jswrapper.h
+++ b/js/src/jswrapper.h
@@ -51,17 +51,17 @@ namespace js {
 
 class DummyFrameGuard;
 
 /* Base class that just implements no-op forwarding methods for fundamental
  * traps. This is meant to be used as a base class for ProxyHandlers that
  * want transparent forwarding behavior but don't want to use the derived
  * traps and other baggage of js::Wrapper.
  */
-class JS_FRIEND_API(AbstractWrapper) : public ProxyHandler
+class JS_FRIEND_API(AbstractWrapper) : public IndirectProxyHandler
 {
     unsigned mFlags;
   public:
     unsigned flags() const { return mFlags; }
 
     explicit AbstractWrapper(unsigned flags);
 
     /* ES5 Harmony fundamental wrapper traps. */
@@ -130,25 +130,18 @@ class JS_FRIEND_API(Wrapper) : public Ab
     virtual bool keys(JSContext *cx, JSObject *wrapper, AutoIdVector &props) MOZ_OVERRIDE;
     virtual bool iterate(JSContext *cx, JSObject *wrapper, unsigned flags, Value *vp) MOZ_OVERRIDE;
 
     /* Spidermonkey extensions. */
     virtual bool call(JSContext *cx, JSObject *wrapper, unsigned argc, Value *vp) MOZ_OVERRIDE;
     virtual bool construct(JSContext *cx, JSObject *wrapper, unsigned argc, Value *argv, Value *rval) MOZ_OVERRIDE;
     virtual bool nativeCall(JSContext *cx, JSObject *wrapper, Class *clasp, Native native, CallArgs args) MOZ_OVERRIDE;
     virtual bool hasInstance(JSContext *cx, JSObject *wrapper, const Value *vp, bool *bp) MOZ_OVERRIDE;
-    virtual JSType typeOf(JSContext *cx, JSObject *proxy) MOZ_OVERRIDE;
-    virtual bool objectClassIs(JSObject *obj, ESClassValue classValue, JSContext *cx) MOZ_OVERRIDE;
     virtual JSString *obj_toString(JSContext *cx, JSObject *wrapper) MOZ_OVERRIDE;
     virtual JSString *fun_toString(JSContext *cx, JSObject *wrapper, unsigned indent) MOZ_OVERRIDE;
-    virtual bool regexp_toShared(JSContext *cx, JSObject *proxy, RegExpGuard *g) MOZ_OVERRIDE;
-    virtual bool defaultValue(JSContext *cx, JSObject *wrapper, JSType hint, Value *vp) MOZ_OVERRIDE;
-    virtual bool iteratorNext(JSContext *cx, JSObject *wrapper, Value *vp) MOZ_OVERRIDE;
-
-    virtual void trace(JSTracer *trc, JSObject *wrapper) MOZ_OVERRIDE;
 
     using AbstractWrapper::Action;
 
     static Wrapper singleton;
 
     static JSObject *New(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent,
                          Wrapper *handler);
 
@@ -195,17 +188,16 @@ class JS_FRIEND_API(CrossCompartmentWrap
     virtual bool call(JSContext *cx, JSObject *wrapper, unsigned argc, Value *vp) MOZ_OVERRIDE;
     virtual bool construct(JSContext *cx, JSObject *wrapper, unsigned argc, Value *argv, Value *rval) MOZ_OVERRIDE;
     virtual bool nativeCall(JSContext *cx, JSObject *wrapper, Class *clasp, Native native, CallArgs args) MOZ_OVERRIDE;
     virtual bool hasInstance(JSContext *cx, JSObject *wrapper, const Value *vp, bool *bp) MOZ_OVERRIDE;
     virtual JSString *obj_toString(JSContext *cx, JSObject *wrapper) MOZ_OVERRIDE;
     virtual JSString *fun_toString(JSContext *cx, JSObject *wrapper, unsigned indent) MOZ_OVERRIDE;
     virtual bool defaultValue(JSContext *cx, JSObject *wrapper, JSType hint, Value *vp) MOZ_OVERRIDE;
     virtual bool iteratorNext(JSContext *cx, JSObject *wrapper, Value *vp);
-
     virtual void trace(JSTracer *trc, JSObject *wrapper) MOZ_OVERRIDE;
 
     static CrossCompartmentWrapper singleton;
 };
 
 /*
  * Base class for security wrappers. A security wrapper is potentially hiding
  * all or part of some wrapped object thus SecurityWrapper defaults to denying
--- a/js/xpconnect/src/dombindings.h
+++ b/js/xpconnect/src/dombindings.h
@@ -44,19 +44,19 @@
 #include "jsproxy.h"
 #include "xpcpublic.h"
 #include "nsString.h"
 
 namespace mozilla {
 namespace dom {
 namespace binding {
 
-class ProxyHandler : public js::ProxyHandler {
+class ProxyHandler : public js::BaseProxyHandler {
 protected:
-    ProxyHandler() : js::ProxyHandler(ProxyFamily())
+    ProxyHandler() : js::BaseProxyHandler(ProxyFamily())
     {
     }
 
 public:
     virtual bool isInstanceOf(JSObject *prototype) = 0;
 };
 
 class NoType;
@@ -225,17 +225,17 @@ public:
     bool keys(JSContext *cx, JSObject *proxy, JS::AutoIdVector &props);
     bool iterate(JSContext *cx, JSObject *proxy, unsigned flags, JS::Value *vp);
 
     /* Spidermonkey extensions. */
     bool hasInstance(JSContext *cx, JSObject *proxy, const JS::Value *vp, bool *bp);
     JSString *obj_toString(JSContext *cx, JSObject *proxy);
     void finalize(JSFreeOp *fop, JSObject *proxy);
 
-    static bool proxyHandlerIsList(js::ProxyHandler *handler) {
+    static bool proxyHandlerIsList(js::BaseProxyHandler *handler) {
         return handler == &instance;
     }
     static bool objIsList(JSObject *obj) {
         return js::IsProxy(obj) && proxyHandlerIsList(js::GetProxyHandler(obj));
     }
     static inline bool instanceIsListObject(JSContext *cx, JSObject *obj, JSObject *callee);
     virtual bool isInstanceOf(JSObject *prototype)
     {
--- a/js/xpconnect/src/dombindingsgen.py
+++ b/js/xpconnect/src/dombindingsgen.py
@@ -470,26 +470,26 @@ listTemplate = (
 
 derivedClassTemplate = (
 "template<>\n"
 "bool\n"
 "${name}Wrapper::objIsList(JSObject *obj)\n"
 "{\n"
 "    if (!js::IsProxy(obj))\n"
 "        return false;\n"
-"    js::ProxyHandler *handler = js::GetProxyHandler(obj);\n"
+"    js::BaseProxyHandler *handler = js::GetProxyHandler(obj);\n"
 "    return proxyHandlerIsList(handler) ||\n"
 "${checkproxyhandlers};\n"
 "}\n"
 "\n"
 "template<>\n"
 "${nativeClass}*\n"
 "${name}Wrapper::getNative(JSObject *obj)\n"
 "{\n"
-"    js::ProxyHandler *handler = js::GetProxyHandler(obj);\n"
+"    js::BaseProxyHandler *handler = js::GetProxyHandler(obj);\n"
 "    if (proxyHandlerIsList(handler))\n"
 "        return static_cast<${nativeClass}*>(js::GetProxyPrivate(obj).toPrivate());\n"
 "${castproxyhandlers}"
 "\n"
 "    NS_RUNTIMEABORT(\"Unknown list type!\");\n"
 "    return NULL;\n"
 "}\n"
 "\n")
--- a/js/xpconnect/wrappers/FilteringWrapper.cpp
+++ b/js/xpconnect/wrappers/FilteringWrapper.cpp
@@ -112,17 +112,17 @@ FilteringWrapper<Base, Policy>::keys(JSC
 template <typename Base, typename Policy>
 bool
 FilteringWrapper<Base, Policy>::iterate(JSContext *cx, JSObject *wrapper, unsigned flags, Value *vp)
 {
     // We refuse to trigger the iterator hook across chrome wrappers because
     // we don't know how to censor custom iterator objects. Instead we trigger
     // the default proxy iterate trap, which will ask enumerate() for the list
     // of (consored) ids.
-    return js::ProxyHandler::iterate(cx, wrapper, flags, vp);
+    return js::BaseProxyHandler::iterate(cx, wrapper, flags, vp);
 }
 
 template <typename Base, typename Policy>
 bool
 FilteringWrapper<Base, Policy>::enter(JSContext *cx, JSObject *wrapper, jsid id,
                                       Wrapper::Action act, bool *bp)
 {
     Permission perm;
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -1331,61 +1331,61 @@ XrayWrapper<Base, Traits>::enumerate(JSC
 template <typename Base, typename Traits>
 bool
 XrayWrapper<Base, Traits>::get(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id,
                                js::Value *vp)
 {
     // Skip our Base if it isn't already ProxyHandler.
     // NB: None of the functions we call are prepared for the receiver not
     // being the wrapper, so ignore the receiver here.
-    return ProxyHandler::get(cx, wrapper, wrapper, id, vp);
+    return BaseProxyHandler::get(cx, wrapper, wrapper, id, vp);
 }
 
 template <typename Base, typename Traits>
 bool
 XrayWrapper<Base, Traits>::set(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id,
                                bool strict, js::Value *vp)
 {
-    // Skip our Base if it isn't already ProxyHandler.
+    // Skip our Base if it isn't already BaseProxyHandler.
     // NB: None of the functions we call are prepared for the receiver not
     // being the wrapper, so ignore the receiver here.
-    return ProxyHandler::set(cx, wrapper, wrapper, id, strict, vp);
+    return BaseProxyHandler::set(cx, wrapper, wrapper, id, strict, vp);
 }
 
 template <typename Base, typename Traits>
 bool
 XrayWrapper<Base, Traits>::has(JSContext *cx, JSObject *wrapper, jsid id, bool *bp)
 {
     // Skip our Base if it isn't already ProxyHandler.
-    return ProxyHandler::has(cx, wrapper, id, bp);
+    return BaseProxyHandler::has(cx, wrapper, id, bp);
 }
 
 template <typename Base, typename Traits>
 bool
 XrayWrapper<Base, Traits>::hasOwn(JSContext *cx, JSObject *wrapper, jsid id, bool *bp)
 {
     // Skip our Base if it isn't already ProxyHandler.
-    return ProxyHandler::hasOwn(cx, wrapper, id, bp);
+    return BaseProxyHandler::hasOwn(cx, wrapper, id, bp);
 }
 
 template <typename Base, typename Traits>
 bool
 XrayWrapper<Base, Traits>::keys(JSContext *cx, JSObject *wrapper, JS::AutoIdVector &props)
 {
     // Skip our Base if it isn't already ProxyHandler.
-    return ProxyHandler::keys(cx, wrapper, props);
+    return BaseProxyHandler::keys(cx, wrapper, props);
 }
 
 template <typename Base, typename Traits>
 bool
 XrayWrapper<Base, Traits>::iterate(JSContext *cx, JSObject *wrapper, unsigned flags,
                                    js::Value *vp)
 {
     // Skip our Base if it isn't already ProxyHandler.
-    return ProxyHandler::iterate(cx, wrapper, flags, vp);
+    return BaseProxyHandler::iterate(cx, wrapper, flags, vp);
 }
 
 template <typename Base, typename Traits>
 bool
 XrayWrapper<Base, Traits>::call(JSContext *cx, JSObject *wrapper, unsigned argc, js::Value *vp)
 {
     JSObject *holder = GetHolder(wrapper);
     XPCWrappedNative *wn = GetWrappedNativeFromHolder(holder);
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -7668,19 +7668,24 @@ DoApplyRenderingChangeToTree(nsIFrame* a
   }
 }
 
 static void
 ApplyRenderingChangeToTree(nsPresContext* aPresContext,
                            nsIFrame* aFrame,
                            nsChangeHint aChange)
 {
+  // We check GetStyleDisplay()->HasTransform() in addition to checking
+  // IsTransformed() since we can get here for some frames that don't have the
+  // NS_FRAME_MAY_BE_TRANSFORMED bit set (e.g. nsTableFrame; for a transformed
+  // table that bit is only set on the nsTableOuterFrame).
   NS_ASSERTION(!(aChange & nsChangeHint_UpdateTransformLayer) ||
+               aFrame->IsTransformed() ||
                aFrame->GetStyleDisplay()->HasTransform(),
-               "Only transform style should give a UpdateTransformLayer hint");
+               "Unexpected UpdateTransformLayer hint");
 
   nsIPresShell *shell = aPresContext->PresShell();
   if (shell->IsPaintingSuppressed()) {
     // Don't allow synchronous rendering changes when painting is turned off.
     aChange = NS_SubtractHint(aChange, nsChangeHint_RepaintFrame);
     if (!aChange) {
       return;
     }
@@ -7943,17 +7948,50 @@ nsCSSFrameConstructor::ProcessRestyledFr
         StyleChangeReflow(frame, hint);
         didReflow = true;
       }
       if (hint & (nsChangeHint_RepaintFrame | nsChangeHint_SyncFrameView |
                   nsChangeHint_UpdateOpacityLayer | nsChangeHint_UpdateTransformLayer)) {
         ApplyRenderingChangeToTree(presContext, frame, hint);
         didInvalidate = true;
       }
+      NS_ASSERTION(!(hint & nsChangeHint_ChildrenOnlyTransform) ||
+                   (hint & nsChangeHint_UpdateOverflow),
+                   "nsChangeHint_UpdateOverflow should be passed too");
       if ((hint & nsChangeHint_UpdateOverflow) && !didReflow) {
+        if (hint & nsChangeHint_ChildrenOnlyTransform) {
+          // When we process restyle events starting from the root of the frame
+          // tree, we start at a ViewportFrame and traverse down the tree from
+          // there. When we reach its nsHTMLScrollFrame child, that frame's
+          // GetContent() returns the root element of the document, even though
+          // that frame is not the root element's primary frame. The result of
+          // this quirk is that we remove any pending change hints for the
+          // root element and process them for the nsHTMLScrollFrame instead of
+          // the root element's primary frame. For most change hints this is
+          // not a problem, but for nsChangeHint_ChildrenOnlyTransform it is,
+          // since the children that we want to call UpdateOverflow on are the
+          // frames for the children of the root element, not the nsCanvasFrame
+          // child of the nsHTMLScrollFrame. As a result we need to ignore
+          // |frame| here and use frame->GetContent()->GetPrimaryFrame().
+          nsIFrame *f = frame->GetContent()->GetPrimaryFrame();
+          NS_ABORT_IF_FALSE(f->IsFrameOfType(nsIFrame::eSVG |
+                                             nsIFrame::eSVGContainer),
+                            "Children-only transforms only expected on SVG frames");
+          // Update overflow areas of children first:
+          nsIFrame* childFrame = f->GetFirstPrincipalChild();
+          for ( ; childFrame; childFrame = childFrame->GetNextSibling()) {
+            NS_ABORT_IF_FALSE(childFrame->IsFrameOfType(nsIFrame::eSVG),
+                              "Not expecting non-SVG children");
+            childFrame->UpdateOverflow();
+            NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrSpecialSibling(childFrame),
+                         "SVG frames should not have continuations or special siblings");
+            NS_ASSERTION(childFrame->GetParent() == frame,
+                         "SVG child frame not expected to have different parent");
+          }
+        }
         while (frame) {
           nsOverflowAreas* pre = static_cast<nsOverflowAreas*>
             (frame->Properties().Get(frame->PreTransformOverflowAreasProperty()));
           if (pre) {
             // FinishAndStoreOverflow will change the overflow areas passed in,
             // so make a copy.
             nsOverflowAreas overflowAreas = *pre;
             frame->FinishAndStoreOverflow(overflowAreas, frame->GetSize());
--- a/layout/base/nsChangeHint.h
+++ b/layout/base/nsChangeHint.h
@@ -108,17 +108,23 @@ enum nsChangeHint {
    */
   nsChangeHint_ReconstructFrame = 0x400,
 
   /**
    * The frame's effect on its ancestors' overflow areas has changed,
    * either through a change in its transform or a change in its position.
    * Does not update any descendant frames.
    */
-  nsChangeHint_UpdateOverflow = 0x800
+  nsChangeHint_UpdateOverflow = 0x800,
+
+  /**
+   * The children-only transform of an SVG frame changed, requiring the
+   * overflow rects of the frame's immediate children to be updated.
+   */
+  nsChangeHint_ChildrenOnlyTransform = 0x1000
 };
 
 // Redefine these operators to return nothing. This will catch any use
 // of these operators on hints. We should not be using these operators
 // on nsChangeHints
 inline void operator<(nsChangeHint s1, nsChangeHint s2) {}
 inline void operator>(nsChangeHint s1, nsChangeHint s2) {}
 inline void operator!=(nsChangeHint s1, nsChangeHint s2) {}
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -2517,28 +2517,39 @@ bool nsDisplayZoom::ComputeVisibility(ns
  * coordinate space.
  */
 #ifndef UNIFIED_CONTINUATIONS
 
 nsRect
 nsDisplayTransform::GetFrameBoundsForTransform(const nsIFrame* aFrame)
 {
   NS_PRECONDITION(aFrame, "Can't get the bounds of a nonexistent frame!");
+
+  if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
+    // TODO: SVG needs to define what percentage translations resolve against.
+    return nsRect();
+  }
+
   return nsRect(nsPoint(0, 0), aFrame->GetSize());
 }
 
 #else
 
 nsRect
 nsDisplayTransform::GetFrameBoundsForTransform(const nsIFrame* aFrame)
 {
   NS_PRECONDITION(aFrame, "Can't get the bounds of a nonexistent frame!");
 
   nsRect result;
   
+  if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
+    // TODO: SVG needs to define what percentage translations resolve against.
+    return result;
+  }
+
   /* Iterate through the continuation list, unioning together all the
    * bounding rects.
    */
   for (const nsIFrame *currFrame = aFrame->GetFirstContinuation();
        currFrame != nsnull;
        currFrame = currFrame->GetNextContinuation())
     {
       /* Get the frame rect in local coordinates, then translate back to the
@@ -2558,18 +2569,18 @@ nsDisplayTransform::GetFrameBoundsForTra
  * to get from (0, 0) of the frame to the transform origin.
  */
 static
 gfxPoint3D GetDeltaToMozTransformOrigin(const nsIFrame* aFrame,
                                         float aAppUnitsPerPixel,
                                         const nsRect* aBoundsOverride)
 {
   NS_PRECONDITION(aFrame, "Can't get delta for a null frame!");
-  NS_PRECONDITION(aFrame->GetStyleDisplay()->HasTransform(),
-                  "Can't get a delta for an untransformed frame!");
+  NS_PRECONDITION(aFrame->IsTransformed(),
+                  "Shouldn't get a delta for an untransformed frame!");
 
   /* For both of the coordinates, if the value of -moz-transform is a
    * percentage, it's relative to the size of the frame.  Otherwise, if it's
    * a distance, it's already computed for us!
    */
   const nsStyleDisplay* display = aFrame->GetStyleDisplay();
   nsRect boundingRect = (aBoundsOverride ? *aBoundsOverride :
                          nsDisplayTransform::GetFrameBoundsForTransform(aFrame));
@@ -2595,16 +2606,24 @@ gfxPoint3D GetDeltaToMozTransformOrigin(
       *coords[index] =
         NSAppUnitsToFloatPixels(*dimensions[index], aAppUnitsPerPixel) *
         coord.GetPercentValue();
     } else {
       NS_ABORT_IF_FALSE(coord.GetUnit() == eStyleUnit_Coord, "unexpected unit");
       *coords[index] =
         NSAppUnitsToFloatPixels(coord.GetCoordValue(), aAppUnitsPerPixel);
     }
+    if ((aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) &&
+        coord.GetUnit() != eStyleUnit_Percent) {
+      // <length> values represent offsets from the origin of the SVG element's
+      // user space, not the top left of its bounds, so we must adjust for that:
+      nscoord offset =
+        (index == 0) ? aFrame->GetPosition().x : aFrame->GetPosition().y;
+      *coords[index] -= NSAppUnitsToFloatPixels(offset, aAppUnitsPerPixel);
+    }
   }
 
   *coords[2] = NSAppUnitsToFloatPixels(display->mTransformOrigin[2].GetCoordValue(),
                                        aAppUnitsPerPixel);
   /* Adjust based on the origin of the rectangle. */
   result.x += NSAppUnitsToFloatPixels(boundingRect.x, aAppUnitsPerPixel);
   result.y += NSAppUnitsToFloatPixels(boundingRect.y, aAppUnitsPerPixel);
 
@@ -2615,18 +2634,18 @@ gfxPoint3D GetDeltaToMozTransformOrigin(
  * This is a positive delta, meaning that it indicates the direction to move
  * to get from (0, 0) of the frame to the perspective origin.
  */
 static
 gfxPoint3D GetDeltaToMozPerspectiveOrigin(const nsIFrame* aFrame,
                                           float aAppUnitsPerPixel)
 {
   NS_PRECONDITION(aFrame, "Can't get delta for a null frame!");
-  NS_PRECONDITION(aFrame->GetStyleDisplay()->HasTransform(),
-                  "Can't get a delta for an untransformed frame!");
+  NS_PRECONDITION(aFrame->IsTransformed(),
+                  "Shouldn't get a delta for an untransformed frame!");
   NS_PRECONDITION(aFrame->GetParentStyleContextFrame(), 
                   "Can't get delta without a style parent!");
 
   /* For both of the coordinates, if the value of -moz-perspective-origin is a
    * percentage, it's relative to the size of the frame.  Otherwise, if it's
    * a distance, it's already computed for us!
    */
 
@@ -2706,28 +2725,39 @@ nsDisplayTransform::GetResultingTransfor
    */
   const nsStyleDisplay* disp = aFrame->GetStyleDisplay();
   nsRect bounds = (aBoundsOverride ? *aBoundsOverride :
                    nsDisplayTransform::GetFrameBoundsForTransform(aFrame));
 
   /* Get the matrix, then change its basis to factor in the origin. */
   bool dummy;
   gfx3DMatrix result;
+  // Call IsSVGTransformed() regardless of the value of
+  // disp->mSpecifiedTransform, since we still need any transformFromSVGParent.
+  gfxMatrix svgTransform, transformFromSVGParent;
+  bool hasSVGTransforms =
+    aFrame->IsSVGTransformed(&svgTransform, &transformFromSVGParent);
   /* Transformed frames always have a transform, or are preserving 3d (and might still have perspective!) */
   if (disp->mSpecifiedTransform) {
     result = nsStyleTransformMatrix::ReadTransforms(disp->mSpecifiedTransform,
                                                     aFrame->GetStyleContext(),
                                                     aFrame->PresContext(),
                                                     dummy, bounds, aAppUnitsPerPixel);
+  } else if (hasSVGTransforms) {
+    result = gfx3DMatrix::From2D(svgTransform);
   } else {
      NS_ASSERTION(aFrame->GetStyleDisplay()->mTransformStyle == NS_STYLE_TRANSFORM_STYLE_PRESERVE_3D ||
                   aFrame->GetStyleDisplay()->mBackfaceVisibility == NS_STYLE_BACKFACE_VISIBILITY_HIDDEN,
                   "If we don't have a transform, then we must have another reason to have an nsDisplayTransform created");
   }
 
+  if (hasSVGTransforms && !transformFromSVGParent.IsIdentity()) {
+    result = result * gfx3DMatrix::From2D(transformFromSVGParent);
+  }
+
   const nsStyleDisplay* parentDisp = nsnull;
   nsStyleContext* parentStyleContext = aFrame->GetStyleContext()->GetParent();
   if (parentStyleContext) {
     parentDisp = parentStyleContext->GetStyleDisplay();
   }
   if (nsLayoutUtils::Are3DTransformsEnabled() &&
       parentDisp && parentDisp->mChildPerspective.GetUnit() == eStyleUnit_Coord &&
       parentDisp->mChildPerspective.GetCoordValue() > 0.0) {
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -2335,18 +2335,18 @@ public:
    * frame's local coordinate space.
    *
    * @param aFrame The frame to get the bounding rect for.
    * @return The frame's bounding rect, as described above.
    */
   static nsRect GetFrameBoundsForTransform(const nsIFrame* aFrame);
 
   /**
-   * Given a frame with the -moz-transform property, returns the
-   * transformation matrix for that frame.
+   * Given a frame with the -moz-transform property or an SVG transform,
+   * returns the transformation matrix for that frame.
    *
    * @param aFrame The frame to get the matrix from.
    * @param aOrigin Relative to which point this transform should be applied.
    * @param aAppUnitsPerPixel The number of app units per graphics unit.
    * @param aBoundsOverride [optional] If this is nsnull (the default), the
    *        computation will use the value of GetFrameBoundsForTransform(aFrame)
    *        for the frame's bounding rectangle. Otherwise, it will use the
    *        value of aBoundsOverride.  This is mostly for internal use and in
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -4601,16 +4601,32 @@ nsLayoutUtils::DeregisterImageRequest(ns
 
       if (aRequestRegistered) {
         *aRequestRegistered = false;
       }
     }
   }
 }
 
+/* static */
+void
+nsLayoutUtils::PostRestyleEvent(Element* aElement,
+                                nsRestyleHint aRestyleHint,
+                                nsChangeHint aMinChangeHint)
+{
+  nsIDocument* doc = aElement->GetCurrentDoc();
+  if (doc) {
+    nsCOMPtr<nsIPresShell> presShell = doc->GetShell();
+    if (presShell) {
+      presShell->FrameConstructor()->PostRestyleEvent(
+        aElement, aRestyleHint, aMinChangeHint);
+    }
+  }
+}
+
 nsSetAttrRunnable::nsSetAttrRunnable(nsIContent* aContent, nsIAtom* aAttrName,
                                      const nsAString& aValue)
   : mContent(aContent),
     mAttrName(aAttrName),
     mValue(aValue)
 {
   NS_ASSERTION(aContent && aAttrName, "Missing stuff, prepare to crash");
 }
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -50,16 +50,17 @@ class nsIDOMEvent;
 class nsRegion;
 class nsDisplayListBuilder;
 class nsDisplayItem;
 class nsFontMetrics;
 class nsClientRectList;
 class nsFontFaceList;
 
 #include "prtypes.h"
+#include "nsChangeHint.h"
 #include "nsStyleContext.h"
 #include "nsAutoPtr.h"
 #include "nsStyleSet.h"
 #include "nsIView.h"
 #include "nsIFrame.h"
 #include "nsThreadUtils.h"
 #include "nsIPresShell.h"
 #include "nsIPrincipal.h"
@@ -1647,16 +1648,25 @@ public:
    *        deregistered. If the request is deregistered by this function,
    *        then *aRequestRegistered will be set to false upon the completion of
    *        this function.
    */
   static void DeregisterImageRequest(nsPresContext* aPresContext,
                                      imgIRequest* aRequest,
                                      bool* aRequestRegistered);
 
+  /**
+   * Shim to nsCSSFrameConstructor::PostRestyleEvent. Exists so that we
+   * can avoid including nsCSSFrameConstructor.h and all its dependencies
+   * in content files.
+   */
+  static void PostRestyleEvent(mozilla::dom::Element* aElement,
+                               nsRestyleHint aRestyleHint,
+                               nsChangeHint aMinChangeHint);
+
 #ifdef DEBUG
   /**
    * Assert that there are no duplicate continuations of the same frame
    * within aFrameList.  Optimize the tests by assuming that all frames
    * in aFrameList have parent aContainer.
    */
   static void
   AssertNoDuplicateContinuations(nsIFrame* aContainer,
--- 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();
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -265,16 +265,18 @@ nsLayoutStatics::Initialize()
   nsCORSListenerProxy::Startup();
 
   nsFrameList::Init();
 
   NS_SealStaticAtomTable();
 
   nsWindowMemoryReporter::Init();
 
+  nsSVGUtils::Init();
+
   return NS_OK;
 }
 
 void
 nsLayoutStatics::Shutdown()
 {
   // Don't need to shutdown nsWindowMemoryReporter, that will be done by the
   // memory reporter manager.
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -956,17 +956,25 @@ nsIFrame::GetPaddingRect() const
 {
   return GetPaddingRectRelativeToSelf() + GetPosition();
 }
 
 bool
 nsIFrame::IsTransformed() const
 {
   return (mState & NS_FRAME_MAY_BE_TRANSFORMED) &&
-    GetStyleDisplay()->HasTransform();
+          (GetStyleDisplay()->HasTransform() ||
+           IsSVGTransformed());
+}
+
+bool
+nsIFrame::IsSVGTransformed(gfxMatrix *aOwnTransforms,
+                           gfxMatrix *aFromParentTransforms) const
+{
+  return false;
 }
 
 bool
 nsIFrame::Preserves3DChildren() const
 {
   if (GetStyleDisplay()->mTransformStyle != NS_STYLE_TRANSFORM_STYLE_PRESERVE_3D || !IsTransformed())
       return false;
 
@@ -4638,19 +4646,21 @@ nsIFrame::InvalidateInternalAfterResize(
         aDamageRect + nsPoint(aX, aY));
     // Don't need to invalidate any more Thebes layers
     aFlags |= INVALIDATE_NO_THEBES_LAYERS;
     if (aFlags & INVALIDATE_ONLY_THEBES_LAYERS) {
       return;
     }
   }
   if (IsTransformed() && !rectIsTransformed) {
-    nsRect newDamageRect;
-    newDamageRect.UnionRect(nsDisplayTransform::TransformRectOut
-                            (aDamageRect, this, nsPoint(-aX, -aY)), aDamageRect);
+    nsRect newDamageRect = nsDisplayTransform::TransformRectOut
+                             (aDamageRect, this, nsPoint(-aX, -aY));
+    if (!(GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
+      newDamageRect.UnionRect(newDamageRect, aDamageRect);
+    }
 
     // If we are preserving 3d, then our computed transform includes that of any
     // ancestor frames that also preserve 3d. Mark the rectangle as already being
     // transformed into the parent's coordinate space.
     if (Preserves3D()) {
       aFlags |= INVALIDATE_ALREADY_TRANSFORMED;
     }
 
@@ -4698,16 +4708,17 @@ nsIFrame::GetTransformMatrix(nsIFrame* a
      */
     NS_ASSERTION(nsLayoutUtils::GetCrossDocParentFrame(this),
 	             "Cannot transform the viewport frame!");
     PRInt32 scaleFactor = PresContext()->AppUnitsPerDevPixel();
 
     gfx3DMatrix result =
       nsDisplayTransform::GetResultingTransformMatrix(this, nsPoint(0, 0),
                                                       scaleFactor, nsnull, aOutAncestor);
+    // XXXjwatt: seems like this will double count offsets in the face of preserve-3d:
     nsPoint delta = GetOffsetToCrossDoc(*aOutAncestor);
     /* Combine the raw transform with a translation to our parent. */
     result *= gfx3DMatrix::Translation
       (NSAppUnitsToFloatPixels(delta.x, scaleFactor),
        NSAppUnitsToFloatPixels(delta.y, scaleFactor),
        0.0f);
     return result;
   }
@@ -4830,16 +4841,31 @@ nsIFrame::EndDeferringInvalidatesForDisp
 static nsRect
 ComputeOutlineAndEffectsRect(nsIFrame* aFrame, bool* aAnyOutlineOrEffects,
                              const nsRect& aOverflowRect,
                              const nsSize& aNewSize,
                              bool aStoreRectProperties) {
   nsRect r = aOverflowRect;
   *aAnyOutlineOrEffects = false;
 
+  if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
+    // For SVG frames, we only need to account for filters.
+    // TODO: We could also take account of clipPath and mask to reduce the
+    // visual overflow, but that's not essential.
+    if (aFrame->GetStyleSVGReset()->mFilter) {
+      *aAnyOutlineOrEffects = true;
+      if (aStoreRectProperties) {
+        aFrame->Properties().
+          Set(nsIFrame::PreEffectsBBoxProperty(), new nsRect(r));
+      }
+      r = nsSVGUtils::GetPostFilterVisualOverflowRect(aFrame, aOverflowRect);
+    }
+    return r;
+  }
+
   // box-shadow
   nsCSSShadowArray* boxShadows = aFrame->GetStyleBorder()->mBoxShadow;
   if (boxShadows) {
     nsRect shadows;
     PRInt32 A2D = aFrame->PresContext()->AppUnitsPerDevPixel();
     for (PRUint32 i = 0; i < boxShadows->Length(); ++i) {
       nsRect tmpRect(nsPoint(0, 0), aNewSize);
       nsCSSShadowItem* shadow = boxShadows->ShadowAt(i);
@@ -6736,20 +6762,23 @@ nsIFrame::FinishAndStoreOverflow(nsOverf
   }
 
   // This is now called FinishAndStoreOverflow() instead of 
   // StoreOverflow() because frame-generic ways of adding overflow
   // can happen here, e.g. CSS2 outline and native theme.
   // If the overflow area width or height is nscoord_MAX, then a
   // saturating union may have encounted an overflow, so the overflow may not
   // contain the frame border-box. Don't warn in that case.
+  // Don't warn for SVG either, since SVG doesn't need the overflow area
+  // to contain the frame bounds.
   NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
     DebugOnly<nsRect*> r = &aOverflowAreas.Overflow(otype);
     NS_ASSERTION(aNewSize.width == 0 || aNewSize.height == 0 ||
                  r->width == nscoord_MAX || r->height == nscoord_MAX ||
+                 (mState & NS_FRAME_SVG_LAYOUT) ||
                  r->Contains(nsRect(nsPoint(0,0), aNewSize)),
                  "Computed overflow area must contain frame bounds");
   }
 
   // If we clip our children, clear accumulated overflow area. The
   // children are actually clipped to the padding-box, but since the
   // overflow area should include the entire border-box, just set it to
   // the border-box here.
@@ -6758,20 +6787,23 @@ nsIFrame::FinishAndStoreOverflow(nsOverf
                (disp->mOverflowX == NS_STYLE_OVERFLOW_CLIP),
                "If one overflow is clip, the other should be too");
   if (nsFrame::ApplyOverflowClipping(this, disp)) {
     // The contents are actually clipped to the padding area 
     aOverflowAreas.SetAllTo(bounds);
   }
 
   // Overflow area must always include the frame's top-left and bottom-right,
-  // even if the frame rect is empty.
+  // even if the frame rect is empty (so we can scroll to those positions).
   // Pending a real fix for bug 426879, don't do this for inline frames
   // with zero width.
-  if (aNewSize.width != 0 || !IsInlineFrame(this)) {
+  // Do not do this for SVG either, since it will usually massively increase
+  // the area unnecessarily.
+  if ((aNewSize.width != 0 || !IsInlineFrame(this)) &&
+      !(GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
     NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
       nsRect& o = aOverflowAreas.Overflow(otype);
       o.UnionRectEdges(o, bounds);
     }
   }
 
   // Note that NS_STYLE_OVERFLOW_CLIP doesn't clip the frame background,
   // so we add theme background overflow here so it's not clipped.
@@ -6865,17 +6897,20 @@ nsIFrame::FinishAndStoreOverflow(nsOverf
       // overflow area changes, it might be because the clipping changed.
       // The nsChangeHint_RepaintFrame for the style change will only
       // repaint the old overflow area, so if the overflow area has
       // changed (in particular, if it grows), we have to repaint the
       // new area here.
       Invalidate(aOverflowAreas.VisualOverflow());
     }
   }
-  if (anyOverflowChanged && hasTransform) {
+  // XXXSDL For SVG the invalidation happens in UpdateBounds for now, so we
+  // don't currently invalidate SVG here:
+  if (anyOverflowChanged && hasTransform &&
+      !(GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
     // When there's a transform, changes to that style might require
     // repainting of the old and new overflow areas in the widget.
     // Repainting of the frame itself will not be required if there's
     // a retained layer, so we can call InvalidateLayer here
     // which will avoid repainting ThebesLayers if possible.
     // nsCSSFrameConstructor::DoApplyRenderingChangeToTree repaints
     // the old overflow area in the widget in response to
     // nsChangeHint_UpdateTransformLayer. But since the new overflow
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -237,18 +237,19 @@ typedef PRUint64 nsFrameState;
 
 // If this bit is set, the frame is "special" (lame term, I know),
 // which means that it is part of the mangled frame hierarchy that
 // results when an inline has been split because of a nested block.
 // See the comments in nsCSSFrameConstructor::ConstructInline for
 // more details.
 #define NS_FRAME_IS_SPECIAL                         NS_FRAME_STATE_BIT(15)
 
-// If this bit is set, the frame may have a transform that it applies
-// to its coordinate system (e.g. CSS transform, SVG foreignObject).
+// If this bit is set, then transforms (e.g. CSS or SVG transforms) are allowed
+// to affect the frame, and a transform may currently be in affect. If this bit
+// is not set, then any transforms on the frame will be ignored.
 // This is used primarily in GetTransformMatrix to optimize for the
 // common case.
 #define  NS_FRAME_MAY_BE_TRANSFORMED                NS_FRAME_STATE_BIT(16)
 
 #ifdef IBMBIDI
 // If this bit is set, the frame itself is a bidi continuation,
 // or is incomplete (its next sibling is a bidi continuation)
 #define NS_FRAME_IS_BIDI                            NS_FRAME_STATE_BIT(17)
@@ -306,16 +307,21 @@ typedef PRUint64 nsFrameState;
 // everything whose nearest ancestor container for this frame?
 #define NS_FRAME_FONT_INFLATION_CONTAINER           NS_FRAME_STATE_BIT(41)
 
 // Does this frame manage a region in which we do font size inflation,
 // i.e., roughly, is it an element establishing a new block formatting
 // context?
 #define NS_FRAME_FONT_INFLATION_FLOW_ROOT           NS_FRAME_STATE_BIT(42)
 
+// This bit is set on SVG frames that are laid out using SVG's coordinate
+// system based layout (as opposed to any of the CSS layout models). Note that
+// this does not include nsSVGOuterSVGFrame since it takes part is CSS layout.
+#define NS_FRAME_SVG_LAYOUT                         NS_FRAME_STATE_BIT(43)
+
 // Box layout bits
 #define NS_STATE_IS_HORIZONTAL                      NS_FRAME_STATE_BIT(22)
 #define NS_STATE_IS_DIRECTION_NORMAL                NS_FRAME_STATE_BIT(31)
 
 // Helper macros
 #define NS_SUBTREE_DIRTY(_frame)  \
   (((_frame)->GetStateBits() &      \
     (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN)) != 0)
@@ -1224,20 +1230,33 @@ public:
                                           const nsDisplayListSet& aToLists);
 
   /**
    * Does this frame need a view?
    */
   virtual bool NeedsView() { return false; }
 
   /**
-   * Returns whether this frame has a transform matrix applied to it.  This is true
-   * if we have the -moz-transform property or if we're an SVGForeignObjectFrame.
+   * Returns true if this frame is transformed (e.g. has CSS or SVG transforms)
+   * or if its parent is an SVG frame that has children-only transforms (e.g.
+   * an SVG viewBox attribute).
    */
-  virtual bool IsTransformed() const;
+  bool IsTransformed() const;
+
+  /**
+   * Returns true if this frame is an SVG frame that has SVG transforms applied
+   * to it, or if its parent frame is an SVG frame that has children-only
+   * transforms (e.g. an SVG viewBox attribute).
+   * If aOwnTransforms is non-null and the frame has its own SVG transforms,
+   * aOwnTransforms will be set to these transforms. If aFromParentTransforms
+   * is non-null and the frame has an SVG parent with children-only transforms,
+   * then aFromParentTransforms will be set to these transforms.
+   */
+  virtual bool IsSVGTransformed(gfxMatrix *aOwnTransforms = nsnull,
+                                gfxMatrix *aFromParentTransforms = nsnull) const;
 
   /**
    * Returns whether this frame will attempt to preserve the 3d transforms of its
    * children. This is a direct indicator of -moz-transform-style: preserve-3d.
    */
   bool Preserves3DChildren() const;
 
   /**
@@ -1964,18 +1983,18 @@ public:
    *
    * @param aStopAtAncestor don't look further than aStopAtAncestor. If null,
    *   all ancestors (including across documents) will be traversed.
    * @param aOutAncestor [out] The ancestor frame the frame has chosen.  If
    *   this frame has no ancestor, *aOutAncestor will be set to null.
    * @return A gfxMatrix that converts points in this frame's coordinate space
    *   into points in aOutAncestor's coordinate space.
    */
-  virtual gfx3DMatrix GetTransformMatrix(nsIFrame* aStopAtAncestor,
-                                         nsIFrame **aOutAncestor);
+  gfx3DMatrix GetTransformMatrix(nsIFrame* aStopAtAncestor,
+                                 nsIFrame **aOutAncestor);
 
   /**
    * Bit-flags to pass to IsFrameOfType()
    */
   enum {
     eMathML =                           1 << 0,
     eSVG =                              1 << 1,
     eSVGForeignObject =                 1 << 2,
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/nsISVGChildFrame.h
+++ b/layout/svg/base/src/nsISVGChildFrame.h
@@ -105,17 +105,18 @@ public:
   //                           NS_STATE_SVG_NONDISPLAY_CHILD frame) immediately
   //                           prior to painting that frame's descendants.
   // TRANSFORM_CHANGED     - the current transform matrix for this frame has changed
   // COORD_CONTEXT_CHANGED - the dimensions of this frame's coordinate context has
   //                           changed (percentage lengths must be reevaluated)
   enum SVGChangedFlags {
     DO_NOT_NOTIFY_RENDERING_OBSERVERS = 0x01,
     TRANSFORM_CHANGED     = 0x02,
-    COORD_CONTEXT_CHANGED = 0x04
+    COORD_CONTEXT_CHANGED = 0x04,
+    FULL_ZOOM_CHANGED     = 0x08
   };
   virtual void NotifySVGChanged(PRUint32 aFlags)=0;
 
   /**
    * Get this frame's contribution to the rect returned by a GetBBox() call
    * that occurred either on this element, or on one of its ancestors.
    *
    * SVG defines an element's bbox to be the element's fill bounds in the
@@ -135,15 +136,12 @@ public:
    * @param aFlags Flags indicating whether, stroke, for example, should be
    *   included in the bbox calculation.
    */
   virtual SVGBBox GetBBoxContribution(const gfxMatrix &aToBBoxUserspace,
                                       PRUint32 aFlags) = 0;
 
   // Are we a container frame?
   NS_IMETHOD_(bool) IsDisplayContainer()=0;
-
-  // Does this frame have an current covered region in mRect (aka GetRect())?
-  NS_IMETHOD_(bool) HasValidCoveredRect()=0;
 };
 
 #endif // __NS_ISVGCHILDFRAME_H__
 
--- a/layout/svg/base/src/nsSVGContainerFrame.cpp
+++ b/layout/svg/base/src/nsSVGContainerFrame.cpp
@@ -33,18 +33,22 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 // Main header first:
 #include "nsSVGContainerFrame.h"
 
 // Keep others in (case-insensitive) order:
+#include "nsSVGEffects.h"
 #include "nsSVGElement.h"
 #include "nsSVGUtils.h"
+#include "SVGAnimatedTransformList.h"
+
+using namespace mozilla;
 
 NS_QUERYFRAME_HEAD(nsSVGContainerFrame)
   NS_QUERYFRAME_ENTRY(nsSVGContainerFrame)
 NS_QUERYFRAME_TAIL_INHERITING(nsSVGContainerFrameBase)
 
 NS_QUERYFRAME_HEAD(nsSVGDisplayContainerFrame)
   NS_QUERYFRAME_ENTRY(nsSVGDisplayContainerFrame)
   NS_QUERYFRAME_ENTRY(nsISVGChildFrame)
@@ -91,16 +95,27 @@ nsSVGContainerFrame::RemoveFrame(ChildLi
                                  nsIFrame* aOldFrame)
 {
   NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
 
   mFrames.DestroyFrame(aOldFrame);
   return NS_OK;
 }
 
+bool
+nsSVGContainerFrame::UpdateOverflow()
+{
+  if (mState & NS_STATE_SVG_NONDISPLAY_CHILD) {
+    // We don't maintain overflow rects.
+    // XXX It would have be better if the restyle request hadn't even happened.
+    return false;
+  }
+  return nsSVGContainerFrameBase::UpdateOverflow();
+}
+
 NS_IMETHODIMP
 nsSVGDisplayContainerFrame::Init(nsIContent* aContent,
                                  nsIFrame* aParent,
                                  nsIFrame* aPrevInFlow)
 {
   if (!(GetStateBits() & NS_STATE_IS_OUTER_SVG)) {
     AddStateBits(aParent->GetStateBits() &
       (NS_STATE_SVG_NONDISPLAY_CHILD | NS_STATE_SVG_CLIPPATH_CHILD));
@@ -158,16 +173,42 @@ nsSVGDisplayContainerFrame::RemoveFrame(
 
   if (!(GetStateBits() & (NS_STATE_SVG_NONDISPLAY_CHILD | NS_STATE_IS_OUTER_SVG))) {
     nsSVGUtils::NotifyAncestorsOfFilterRegionChange(this);
   }
 
   return rv;
 }
 
+bool
+nsSVGDisplayContainerFrame::IsSVGTransformed(gfxMatrix *aOwnTransform,
+                                             gfxMatrix *aFromParentTransform) const
+{
+  bool foundTransform = false;
+
+  // Check if our parent has children-only transforms:
+  nsIFrame *parent = GetParent();
+  if (parent &&
+      parent->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer)) {
+    foundTransform = static_cast<nsSVGContainerFrame*>(parent)->
+                       HasChildrenOnlyTransform(aFromParentTransform);
+  }
+
+  nsSVGElement *content = static_cast<nsSVGElement*>(mContent);
+  const SVGAnimatedTransformList *list = content->GetAnimatedTransformList();
+  if (list && !list->GetAnimValue().IsEmpty()) {
+    if (aOwnTransform) {
+      *aOwnTransform = content->PrependLocalTransformsTo(gfxMatrix(),
+                                  nsSVGElement::eUserSpaceToParent);
+    }
+    foundTransform = true;
+  }
+  return foundTransform;
+}
+
 //----------------------------------------------------------------------
 // nsISVGChildFrame methods
 
 NS_IMETHODIMP
 nsSVGDisplayContainerFrame::PaintSVG(nsRenderingContext* aContext,
                                      const nsIntRect *aDirtyRect)
 {
   const nsStyleDisplay *display = mStyleContext->GetStyleDisplay();
@@ -198,16 +239,19 @@ void
 nsSVGDisplayContainerFrame::UpdateBounds()
 {
   NS_ASSERTION(nsSVGUtils::OuterSVGIsCallingUpdateBounds(this),
                "This call is probaby a wasteful mistake");
 
   NS_ABORT_IF_FALSE(!(GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD),
                     "UpdateBounds mechanism not designed for this");
 
+  NS_ABORT_IF_FALSE(GetType() != nsGkAtoms::svgOuterSVGFrame,
+                    "Do not call on outer-<svg>");
+
   if (!nsSVGUtils::NeedsUpdatedBounds(this)) {
     return;
   }
 
   // If the NS_FRAME_FIRST_REFLOW bit has been removed from our parent frame,
   // then our outer-<svg> has previously had its initial reflow. In that case
   // we need to make sure that that bit has been removed from ourself _before_
   // recursing over our children to ensure that they know too. Otherwise, we
@@ -216,31 +260,62 @@ nsSVGDisplayContainerFrame::UpdateBounds
 
   bool outerSVGHasHadFirstReflow =
     (GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW) == 0;
 
   if (outerSVGHasHadFirstReflow) {
     mState &= ~NS_FRAME_FIRST_REFLOW; // tell our children
   }
 
+  nsOverflowAreas overflowRects;
+
   for (nsIFrame* kid = mFrames.FirstChild(); kid;
        kid = kid->GetNextSibling()) {
     nsISVGChildFrame* SVGFrame = do_QueryFrame(kid);
     if (SVGFrame) {
       NS_ABORT_IF_FALSE(!(kid->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD),
                         "Check for this explicitly in the |if|, then");
       SVGFrame->UpdateBounds();
+
+      // We build up our child frame overflows here instead of using
+      // nsLayoutUtils::UnionChildOverflow since SVG frame's all use the same
+      // frame list, and we're iterating over that list now anyway.
+      ConsiderChildOverflow(overflowRects, kid);
     }
   }
 
+  // <svg> can create an SVG viewport with an offset due to its
+  // x/y/width/height attributes, and <use> can introduce an offset with an
+  // empty mRect (any width/height is copied to an anonymous <svg> child).
+  // Other than that containers should not set mRect since all other offsets
+  // come from transforms, which are accounted for by nsDisplayTransform.
+  // Note that we rely on |overflow:visible| to allow display list items to be
+  // created for our children.
+  NS_ABORT_IF_FALSE(mContent->Tag() == nsGkAtoms::svg ||
+                    (mContent->Tag() == nsGkAtoms::use &&
+                     mRect.Size() == nsSize(0,0)) ||
+                    mRect.IsEqualEdges(nsRect()),
+                    "Only inner-<svg>/<use> is expected to have mRect set");
+
+  if (mState & NS_FRAME_FIRST_REFLOW) {
+    // Make sure we have our filter property (if any) before calling
+    // FinishAndStoreOverflow (subsequent filter changes are handled off
+    // nsChangeHint_UpdateEffects):
+    nsSVGEffects::UpdateEffects(this);
+  }
+
+  FinishAndStoreOverflow(overflowRects, mRect.Size());
+
+  // Remove state bits after FinishAndStoreOverflow so that it doesn't
+  // invalidate on first reflow:
   mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
               NS_FRAME_HAS_DIRTY_CHILDREN);
 
-  // XXXsvgreflow once we store bounds on containers, do...
-  // nsSVGUtils::InvalidateBounds(this);
+  // XXXSDL Make Invalidate() call nsSVGUtils::InvalidateBounds(this)
+  // so that we invalidate under FinishAndStoreOverflow().
 }  
 
 void
 nsSVGDisplayContainerFrame::NotifySVGChanged(PRUint32 aFlags)
 {
   NS_ABORT_IF_FALSE(!(aFlags & DO_NOT_NOTIFY_RENDERING_OBSERVERS) ||
                     (GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD),
                     "Must be NS_STATE_SVG_NONDISPLAY_CHILD!");
--- a/layout/svg/base/src/nsSVGContainerFrame.h
+++ b/layout/svg/base/src/nsSVGContainerFrame.h
@@ -63,75 +63,96 @@ typedef nsContainerFrame nsSVGContainerF
  * display their contents directly (such as the frames for inner-<svg> or
  * <g>) inherit our nsDisplayContainerFrame sub-class.
  */
 class nsSVGContainerFrame : public nsSVGContainerFrameBase
 {
   friend nsIFrame* NS_NewSVGContainerFrame(nsIPresShell* aPresShell,
                                            nsStyleContext* aContext);
 protected:
-  nsSVGContainerFrame(nsStyleContext* aContext) :
-    nsSVGContainerFrameBase(aContext) {}
+  nsSVGContainerFrame(nsStyleContext* aContext)
+    : nsSVGContainerFrameBase(aContext)
+  {
+    AddStateBits(NS_FRAME_SVG_LAYOUT);
+  }
 
 public:
   NS_DECL_QUERYFRAME_TARGET(nsSVGContainerFrame)
   NS_DECL_QUERYFRAME
   NS_DECL_FRAMEARENA_HELPERS
 
   // Returns the transform to our gfxContext (to device pixels, not CSS px)
   virtual gfxMatrix GetCanvasTM() { return gfxMatrix(); }
 
+  /**
+   * Returns true if the frame's content has a transform that applies only to
+   * its children, and not to the frame itself. For example, an implicit
+   * transform introduced by a 'viewBox' attribute, or an explicit transform
+   * due to a root-<svg> having its currentScale/currentTransform properties
+   * set. If aTransform is non-null, then it will be set to the transform.
+   */
+  virtual bool HasChildrenOnlyTransform(gfxMatrix *aTransform) const {
+    return false;
+  }
+
   // nsIFrame:
   NS_IMETHOD AppendFrames(ChildListID     aListID,
                           nsFrameList&    aFrameList);
   NS_IMETHOD InsertFrames(ChildListID     aListID,
                           nsIFrame*       aPrevFrame,
                           nsFrameList&    aFrameList);
   NS_IMETHOD RemoveFrame(ChildListID     aListID,
                          nsIFrame*       aOldFrame);
 
   virtual bool IsFrameOfType(PRUint32 aFlags) const
   {
     return nsSVGContainerFrameBase::IsFrameOfType(
             aFlags & ~(nsIFrame::eSVG | nsIFrame::eSVGContainer));
   }
+
+  virtual bool UpdateOverflow();
 };
 
 /**
  * Frame class or base-class for SVG containers that can or do display their
  * contents directly.
  */
 class nsSVGDisplayContainerFrame : public nsSVGContainerFrame,
                                    public nsISVGChildFrame
 {
 protected:
-  nsSVGDisplayContainerFrame(nsStyleContext* aContext) :
-    nsSVGContainerFrame(aContext) {}
+  nsSVGDisplayContainerFrame(nsStyleContext* aContext)
+    : nsSVGContainerFrame(aContext)
+  {
+     AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
+  }
 
 public:
   NS_DECL_QUERYFRAME_TARGET(nsSVGDisplayContainerFrame)
   NS_DECL_QUERYFRAME
   NS_DECL_FRAMEARENA_HELPERS
 
   // nsIFrame:
   NS_IMETHOD InsertFrames(ChildListID     aListID,
                           nsIFrame*       aPrevFrame,
                           nsFrameList&    aFrameList);
   NS_IMETHOD RemoveFrame(ChildListID     aListID,
                          nsIFrame*       aOldFrame);
   NS_IMETHOD Init(nsIContent*      aContent,
                   nsIFrame*        aParent,
                   nsIFrame*        aPrevInFlow);
 
+  virtual bool IsSVGTransformed(gfxMatrix *aOwnTransform = nsnull,
+                                gfxMatrix *aFromParentTransform = nsnull) const;
+
   // nsISVGChildFrame interface:
   NS_IMETHOD PaintSVG(nsRenderingContext* aContext,
                       const nsIntRect *aDirtyRect);
   NS_IMETHOD_(nsIFrame*) GetFrameForPoint(const nsPoint &aPoint);
   NS_IMETHOD_(nsRect) GetCoveredRegion();
   virtual void UpdateBounds();
   virtual void NotifySVGChanged(PRUint32 aFlags);
   virtual SVGBBox GetBBoxContribution(const gfxMatrix &aToBBoxUserspace,
                                       PRUint32 aFlags);
   NS_IMETHOD_(bool) IsDisplayContainer() { return true; }
-  NS_IMETHOD_(bool) HasValidCoveredRect() { return false; }
 };
 
 #endif
--- a/layout/svg/base/src/nsSVGFilterFrame.cpp
+++ b/layout/svg/base/src/nsSVGFilterFrame.cpp
@@ -94,33 +94,35 @@ private:
 
 class NS_STACK_CLASS nsAutoFilterInstance {
 public:
   nsAutoFilterInstance(nsIFrame *aTarget,
                        nsSVGFilterFrame *aFilterFrame,
                        nsSVGFilterPaintCallback *aPaint,
                        const nsIntRect *aDirtyOutputRect,
                        const nsIntRect *aDirtyInputRect,
-                       const nsIntRect *aOverrideSourceBBox);
+                       const nsIntRect *aOverrideSourceBBox,
+                       const gfxMatrix *aOverrideUserToDeviceSpace = nsnull);
   ~nsAutoFilterInstance() {}
 
   // If this returns null, then draw nothing. Either the filter draws
   // nothing or it is "in error".
   nsSVGFilterInstance* get() { return mInstance; }
 
 private:
   nsAutoPtr<nsSVGFilterInstance> mInstance;
 };
 
 nsAutoFilterInstance::nsAutoFilterInstance(nsIFrame *aTarget,
                                            nsSVGFilterFrame *aFilterFrame,
                                            nsSVGFilterPaintCallback *aPaint,
                                            const nsIntRect *aDirtyOutputRect,
                                            const nsIntRect *aDirtyInputRect,
-                                           const nsIntRect *aOverrideSourceBBox)
+                                           const nsIntRect *aOverrideSourceBBox,
+                                           const gfxMatrix *aOverrideUserToDeviceSpace)
 {
   const nsSVGFilterElement *filter = aFilterFrame->GetFilterContent();
 
   PRUint16 filterUnits =
     aFilterFrame->GetEnumValue(nsSVGFilterElement::FILTERUNITS);
   PRUint16 primitiveUnits =
     aFilterFrame->GetEnumValue(nsSVGFilterElement::PRIMITIVEUNITS);
 
@@ -156,20 +158,21 @@ nsAutoFilterInstance::nsAutoFilterInstan
     XYWH, bbox, aTarget);
 
   if (filterRegion.Width() <= 0 || filterRegion.Height() <= 0) {
     // 0 disables rendering, < 0 is error. dispatch error console warning
     // or error as appropriate.
     return;
   }
 
-  gfxMatrix userToDeviceSpace = nsSVGUtils::GetCanvasTM(aTarget);
-  if (userToDeviceSpace.IsSingular()) {
-    // nothing to draw
-    return;
+  gfxMatrix userToDeviceSpace;
+  if (aOverrideUserToDeviceSpace) {
+    userToDeviceSpace = *aOverrideUserToDeviceSpace;
+  } else {
+    userToDeviceSpace = nsSVGUtils::GetCanvasTM(aTarget);
   }
   
   // Calculate filterRes (the width and height of the pixel buffer of the
   // temporary offscreen surface that we'll paint into):
 
   gfxIntSize filterRes;
   const nsSVGIntegerPair* filterResAttrs =
     aFilterFrame->GetIntegerPairValue(nsSVGFilterElement::FILTERRES);
@@ -190,17 +193,22 @@ nsAutoFilterInstance::nsAutoFilterInstan
     filterRes =
       nsSVGUtils::ConvertToSurfaceSize(gfxSize(filterResX, filterResY),
                                        &overflow);
     // XXX we could send a warning to the error console if the author specified
     // filterRes doesn't align well with our outer 'svg' device space.
   } else {
     // Match filterRes as closely as possible to the pixel density of the nearest
     // outer 'svg' device space:
-    float scale = nsSVGUtils::MaxExpansion(userToDeviceSpace);
+    gfxMatrix canvasTM = nsSVGUtils::GetCanvasTM(aTarget);
+    if (canvasTM.IsSingular()) {
+      // nothing to draw
+      return;
+    }
+    float scale = nsSVGUtils::MaxExpansion(canvasTM);
 
     filterRegion.Scale(scale);
     filterRegion.RoundOut();
     // We don't care if this overflows, because we can handle upscaling/
     // downscaling to filterRes
     bool overflow;
     filterRes = nsSVGUtils::ConvertToSurfaceSize(filterRegion.Size(),
                                                  &overflow);
@@ -223,18 +231,30 @@ nsAutoFilterInstance::nsAutoFilterInstan
 
   nsIntRect dirtyOutputRect =
     MapDeviceRectToFilterSpace(deviceToFilterSpace, filterRes, aDirtyOutputRect);
   nsIntRect dirtyInputRect =
     MapDeviceRectToFilterSpace(deviceToFilterSpace, filterRes, aDirtyInputRect);
   nsIntRect targetBoundsDeviceSpace;
   nsISVGChildFrame* svgTarget = do_QueryFrame(aTarget);
   if (svgTarget) {
-    targetBoundsDeviceSpace.UnionRect(targetBoundsDeviceSpace,
-      svgTarget->GetCoveredRegion().ToOutsidePixels(aTarget->PresContext()->AppUnitsPerDevPixel()));
+    if (aOverrideUserToDeviceSpace) {
+      // If aOverrideUserToDeviceSpace is specified, it is a simple
+      // CSS-px-to-dev-px transform passed by nsSVGFilterFrame::GetFilterBBox()
+      // when requesting the filter expansion of the overflow rects in frame
+      // space. In this case GetCoveredRegion() is not what we want since it is
+      // in outer-<svg> space, GetFilterBBox passes in the pre-filter bounds of
+      // the frame in frame space for us to use instead.
+      NS_ASSERTION(aDirtyInputRect, "Who passed aOverrideUserToDeviceSpace?");
+      targetBoundsDeviceSpace = *aDirtyInputRect;
+    } else {
+      targetBoundsDeviceSpace =
+        svgTarget->GetCoveredRegion().ToOutsidePixels(aTarget->
+          PresContext()->AppUnitsPerDevPixel());
+    }
   }
   nsIntRect targetBoundsFilterSpace =
     MapDeviceRectToFilterSpace(deviceToFilterSpace, filterRes, &targetBoundsDeviceSpace);
 
   // Setup instance data
   mInstance = new nsSVGFilterInstance(aTarget, aPaint, filter, bbox, filterRegion,
                                       nsIntSize(filterRes.width, filterRes.height),
                                       filterToDeviceSpace, targetBoundsFilterSpace,
@@ -466,19 +486,37 @@ nsSVGFilterFrame::GetSourceForInvalidAre
     if (NS_SUCCEEDED(rv))
       return neededRect;
   }
 
   return nsIntRect();
 }
 
 nsIntRect
-nsSVGFilterFrame::GetFilterBBox(nsIFrame *aTarget, const nsIntRect *aSourceBBox)
+nsSVGFilterFrame::GetFilterBBox(nsIFrame *aTarget,
+                                const nsIntRect *aOverrideBBox,
+                                const nsIntRect *aPreFilterBounds)
 {
-  nsAutoFilterInstance instance(aTarget, this, nsnull, nsnull, nsnull, aSourceBBox);
+  bool overrideCTM = false;
+  gfxMatrix ctm;
+
+  if (aTarget->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
+    // For most filter operations on SVG frames we want information in
+    // outer-<svg> device space, but in this case we want the visual overflow
+    // rect relative to aTarget itself. For that we need to prevent the filter
+    // code using GetCanvasTM().
+    overrideCTM = true;
+    PRInt32 appUnitsPerDevPixel = aTarget->PresContext()->AppUnitsPerDevPixel();
+    float devPxPerCSSPx =
+      1 / nsPresContext::AppUnitsToFloatCSSPixels(appUnitsPerDevPixel);
+    ctm.Scale(devPxPerCSSPx, devPxPerCSSPx);
+  }
+
+  nsAutoFilterInstance instance(aTarget, this, nsnull, nsnull, aPreFilterBounds,
+                                aOverrideBBox, overrideCTM ? &ctm : nsnull);
   if (!instance.get())
     return nsIntRect();
 
   // We've passed in the source's bounding box so the instance knows about
   // it. Now we can ask the instance to compute the bounding box of
   // the filter output.
   nsIntRect bbox;
   nsresult rv = instance.get()->ComputeOutputBBox(&bbox);
--- a/layout/svg/base/src/nsSVGFilterFrame.h
+++ b/layout/svg/base/src/nsSVGFilterFrame.h
@@ -94,19 +94,21 @@ public:
    * relative to aTarget itself otherwise, in device pixels.
    */
   nsIntRect GetSourceForInvalidArea(nsIFrame *aTarget, const nsIntRect& aRect);
 
   /**
    * Returns the bounding box of the post-filter area of aTarget.
    * The rectangles are relative to the origin of the outer svg, if aTarget is SVG,
    * relative to aTarget itself otherwise, in device pixels.
-   * @param aSourceBBox overrides the normal bbox for the source, if non-null
+   * @param aOverrideBBox overrides the normal bbox for the source, if non-null
    */
-  nsIntRect GetFilterBBox(nsIFrame *aTarget, const nsIntRect *aSourceBBox);
+  nsIntRect GetFilterBBox(nsIFrame *aTarget,
+                          const nsIntRect *aOverrideBBox = nsnull,
+                          const nsIntRect *aPreFilterBounds = nsnull);
 
 #ifdef DEBUG
   NS_IMETHOD Init(nsIContent*      aContent,
                   nsIFrame*        aParent,
                   nsIFrame*        aPrevInFlow);
 #endif
 
   /**
--- a/layout/svg/base/src/nsSVGForeignObjectFrame.cpp
+++ b/layout/svg/base/src/nsSVGForeignObjectFrame.cpp
@@ -68,17 +68,18 @@ NS_NewSVGForeignObjectFrame(nsIPresShell
 }
 
 NS_IMPL_FRAMEARENA_HELPERS(nsSVGForeignObjectFrame)
 
 nsSVGForeignObjectFrame::nsSVGForeignObjectFrame(nsStyleContext* aContext)
   : nsSVGForeignObjectFrameBase(aContext),
     mInReflow(false)
 {
-  AddStateBits(NS_FRAME_REFLOW_ROOT | NS_FRAME_MAY_BE_TRANSFORMED);
+  AddStateBits(NS_FRAME_REFLOW_ROOT | NS_FRAME_MAY_BE_TRANSFORMED |
+               NS_FRAME_SVG_LAYOUT);
 }
 
 //----------------------------------------------------------------------
 // nsIFrame methods
 
 NS_QUERYFRAME_HEAD(nsSVGForeignObjectFrame)
   NS_QUERYFRAME_ENTRY(nsISVGChildFrame)
 NS_QUERYFRAME_TAIL_INHERITING(nsSVGForeignObjectFrameBase)
@@ -300,34 +301,16 @@ nsSVGForeignObjectFrame::PaintSVG(nsRend
   nsresult rv = nsLayoutUtils::PaintFrame(aContext, kid, nsRegion(kidDirtyRect),
                                           NS_RGBA(0,0,0,0), flags);
 
   gfx->Restore();
 
   return rv;
 }
 
-gfx3DMatrix
-nsSVGForeignObjectFrame::GetTransformMatrix(nsIFrame* aAncestor,
-                                            nsIFrame **aOutAncestor)
-{
-  NS_PRECONDITION(aOutAncestor, "We need an ancestor to write to!");
-
-  /* Set the ancestor to be the outer frame. */
-  *aOutAncestor = nsSVGUtils::GetOuterSVGFrame(this);
-  NS_ASSERTION(*aOutAncestor, "How did we end up without an outer frame?");
-
-  if (GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD) {
-    return gfx3DMatrix::From2D(gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
-  }
-
-  /* Return the matrix back to the root, factoring in the x and y offsets. */
-  return gfx3DMatrix::From2D(GetCanvasTMForChildren());
-}
- 
 NS_IMETHODIMP_(nsIFrame*)
 nsSVGForeignObjectFrame::GetFrameForPoint(const nsPoint &aPoint)
 {
   if (IsDisabled() || (GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD))
     return nsnull;
 
   nsIFrame* kid = GetFirstPrincipalChild();
   if (!kid)
@@ -410,24 +393,38 @@ nsSVGForeignObjectFrame::UpdateBounds()
   nsIFrame* kid = GetFirstPrincipalChild();
   kid->AddStateBits(NS_FRAME_IS_DIRTY);
 
   // Make sure to not allow interrupts if we're not being reflown as a root:
   nsPresContext::InterruptPreventer noInterrupts(PresContext());
 
   DoReflow();
 
+  if (mState & NS_FRAME_FIRST_REFLOW) {
+    // Make sure we have our filter property (if any) before calling
+    // FinishAndStoreOverflow (subsequent filter changes are handled off
+    // nsChangeHint_UpdateEffects):
+    nsSVGEffects::UpdateEffects(this);
+  }
+
+  // TODO: once we support |overflow:visible| on foreignObject, then we will
+  // need to take account of our descendants here.
+  nsRect overflow = nsRect(nsPoint(0,0), mRect.Size());
+  nsOverflowAreas overflowAreas(overflow, overflow);
+  FinishAndStoreOverflow(overflowAreas, mRect.Size());
+
   // Now unset the various reflow bits:
   mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
               NS_FRAME_HAS_DIRTY_CHILDREN);
 
   if (!(GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
     // We only invalidate if our outer-<svg> has already had its
     // initial reflow (since if it hasn't, its entire area will be
     // invalidated when it gets that initial reflow):
+    // XXXSDL Let FinishAndStoreOverflow do this.
     nsSVGUtils::InvalidateBounds(this, true);
   }
 }
 
 void
 nsSVGForeignObjectFrame::NotifySVGChanged(PRUint32 aFlags)
 {
   NS_ABORT_IF_FALSE(!(aFlags & DO_NOT_NOTIFY_RENDERING_OBSERVERS) ||
--- a/layout/svg/base/src/nsSVGForeignObjectFrame.h
+++ b/layout/svg/base/src/nsSVGForeignObjectFrame.h
@@ -77,30 +77,16 @@ public:
   }
 
   NS_IMETHOD Reflow(nsPresContext*           aPresContext,
                     nsHTMLReflowMetrics&     aDesiredSize,
                     const nsHTMLReflowState& aReflowState,
                     nsReflowStatus&          aStatus);
 
   /**
-   * Foreign objects are always transformed.
-   */
-  virtual bool IsTransformed() const
-  {
-    return true;
-  }
-
-  /**
-   * Foreign objects can return a transform matrix.
-   */
-  virtual gfx3DMatrix GetTransformMatrix(nsIFrame* aAncestor,
-                                         nsIFrame **aOutAncestor);
-
-  /**
    * Get the "type" of the frame
    *
    * @see nsGkAtoms::svgForeignObjectFrame
    */
   virtual nsIAtom* GetType() const;
 
   virtual bool IsFrameOfType(PRUint32 aFlags) const
   {
@@ -124,19 +110,16 @@ public:
                       const nsIntRect *aDirtyRect);
   NS_IMETHOD_(nsIFrame*) GetFrameForPoint(const nsPoint &aPoint);
   NS_IMETHOD_(nsRect) GetCoveredRegion();
   virtual void UpdateBounds();
   virtual void NotifySVGChanged(PRUint32 aFlags);
   virtual SVGBBox GetBBoxContribution(const gfxMatrix &aToBBoxUserspace,
                                       PRUint32 aFlags);
   NS_IMETHOD_(bool) IsDisplayContainer() { return true; }
-  NS_IMETHOD_(bool) HasValidCoveredRect() {
-    return !(GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD);
-  }
 
   gfxMatrix GetCanvasTM();
 
 protected:
   // implementation helpers:
   void DoReflow();
   void RequestReflow(nsIPresShell::IntrinsicDirty aType);
 
--- a/layout/svg/base/src/nsSVGGeometryFrame.h
+++ b/layout/svg/base/src/nsSVGGeometryFrame.h
@@ -64,17 +64,21 @@ typedef nsFrame nsSVGGeometryFrameBase;
  * cairo context information and stores the fill/stroke paint
  * servers. */
 
 class nsSVGGeometryFrame : public nsSVGGeometryFrameBase
 {
 protected:
   NS_DECL_FRAMEARENA_HELPERS
 
-  nsSVGGeometryFrame(nsStyleContext *aContext) : nsSVGGeometryFrameBase(aContext) {}
+  nsSVGGeometryFrame(nsStyleContext *aContext)
+    : nsSVGGeometryFrameBase(aContext)
+  {
+    AddStateBits(NS_FRAME_SVG_LAYOUT);
+  }
 
 public:
   // nsIFrame interface:
   NS_IMETHOD Init(nsIContent* aContent,
                   nsIFrame* aParent,
                   nsIFrame* aPrevInFlow);
 
   virtual bool IsFrameOfType(PRUint32 aFlags) const
--- a/layout/svg/base/src/nsSVGGlyphFrame.cpp
+++ b/layout/svg/base/src/nsSVGGlyphFrame.cpp
@@ -507,23 +507,28 @@ nsSVGGlyphFrame::UpdateBounds()
     mRect = nsLayoutUtils::RoundGfxRectToAppRect(extent, 
               PresContext()->AppUnitsPerCSSPixel());
   }
 
   // See bug 614732 comment 32.
   mCoveredRegion = nsSVGUtils::TransformFrameRectToOuterSVG(
     mRect, GetCanvasTM(), PresContext());
 
+  nsRect overflow = nsRect(nsPoint(0,0), mRect.Size());
+  nsOverflowAreas overflowAreas(overflow, overflow);
+  FinishAndStoreOverflow(overflowAreas, mRect.Size());
+
   mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
               NS_FRAME_HAS_DIRTY_CHILDREN);
 
   if (!(GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
     // We only invalidate if our outer-<svg> has already had its
     // initial reflow (since if it hasn't, its entire area will be
     // invalidated when it gets that initial reflow):
+    // XXXSDL Let FinishAndStoreOverflow do this.
     nsSVGUtils::InvalidateBounds(this, true);
   }
 }  
 
 void
 nsSVGGlyphFrame::NotifySVGChanged(PRUint32 aFlags)
 {
   NS_ABORT_IF_FALSE(!(aFlags & DO_NOT_NOTIFY_RENDERING_OBSERVERS) ||
--- a/layout/svg/base/src/nsSVGGlyphFrame.h
+++ b/layout/svg/base/src/nsSVGGlyphFrame.h
@@ -180,19 +180,16 @@ public:
   NS_IMETHOD_(nsIFrame*) GetFrameForPoint(const nsPoint &aPoint);
   virtual SVGBBox GetBBoxContribution(const gfxMatrix &aToBBoxUserspace,
                                       PRUint32 aFlags);
 
   NS_IMETHOD_(nsRect) GetCoveredRegion();
   virtual void UpdateBounds();
   virtual void NotifySVGChanged(PRUint32 aFlags);
   NS_IMETHOD_(bool) IsDisplayContainer() { return false; }
-  NS_IMETHOD_(bool) HasValidCoveredRect() {
-    return !(GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD);
-  }
 
   // nsSVGGeometryFrame methods
   gfxMatrix GetCanvasTM();
 
   // nsISVGGlyphFragmentNode interface:
   // These do not use the global transform if NS_STATE_NONDISPLAY_CHILD
   virtual PRUint32 GetNumberOfChars();
   virtual float GetComputedTextLength();
--- a/layout/svg/base/src/nsSVGImageFrame.cpp
+++ b/layout/svg/base/src/nsSVGImageFrame.cpp
@@ -509,23 +509,35 @@ nsSVGImageFrame::UpdateBounds()
   } else {
     mRect.SetEmpty();
   }
 
   // See bug 614732 comment 32.
   mCoveredRegion = nsSVGUtils::TransformFrameRectToOuterSVG(
     mRect, GetCanvasTM(), PresContext());
 
+  if (mState & NS_FRAME_FIRST_REFLOW) {
+    // Make sure we have our filter property (if any) before calling
+    // FinishAndStoreOverflow (subsequent filter changes are handled off
+    // nsChangeHint_UpdateEffects):
+    nsSVGEffects::UpdateEffects(this);
+  }
+
+  nsRect overflow = nsRect(nsPoint(0,0), mRect.Size());
+  nsOverflowAreas overflowAreas(overflow, overflow);
+  FinishAndStoreOverflow(overflowAreas, mRect.Size());
+
   mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
               NS_FRAME_HAS_DIRTY_CHILDREN);
 
   if (!(GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
     // We only invalidate if our outer-<svg> has already had its
     // initial reflow (since if it hasn't, its entire area will be
     // invalidated when it gets that initial reflow):
+    // XXXSDL Let FinishAndStoreOverflow do this.
     nsSVGUtils::InvalidateBounds(this, true);
   }
 }
 
 PRUint16
 nsSVGImageFrame::GetHitTestFlags()
 {
   PRUint16 flags = 0;
--- a/layout/svg/base/src/nsSVGInnerSVGFrame.cpp
+++ b/layout/svg/base/src/nsSVGInnerSVGFrame.cpp
@@ -109,16 +109,30 @@ nsSVGInnerSVGFrame::PaintSVG(nsRendering
       nsSVGUtils::GetClipRectForFrame(this, x, y, width, height);
     nsSVGUtils::SetClipRect(gfx, clipTransform, clipRect);
   }
 
   return nsSVGInnerSVGFrameBase::PaintSVG(aContext, aDirtyRect);
 }
 
 void
+nsSVGInnerSVGFrame::UpdateBounds()
+{
+  // mRect must be set before FinishAndStoreOverflow is called in order
+  // for our overflow areas to be clipped correctly.
+  float x, y, width, height;
+  static_cast<nsSVGSVGElement*>(mContent)->
+    GetAnimatedLengthValues(&x, &y, &width, &height, nsnull);
+  mRect = nsLayoutUtils::RoundGfxRectToAppRect(
+                           gfxRect(x, y, width, height),
+                           PresContext()->AppUnitsPerCSSPixel());
+  nsSVGInnerSVGFrameBase::UpdateBounds();
+}
+
+void
 nsSVGInnerSVGFrame::NotifySVGChanged(PRUint32 aFlags)
 {
   NS_ABORT_IF_FALSE(!(aFlags & DO_NOT_NOTIFY_RENDERING_OBSERVERS) ||
                     (GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD),
                     "Must be NS_STATE_SVG_NONDISPLAY_CHILD!");
 
   NS_ABORT_IF_FALSE(aFlags & (TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED),
                     "Invalidation logic may need adjusting");
@@ -166,50 +180,53 @@ nsSVGInnerSVGFrame::NotifySVGChanged(PRU
 }
 
 NS_IMETHODIMP
 nsSVGInnerSVGFrame::AttributeChanged(PRInt32  aNameSpaceID,
                                      nsIAtom* aAttribute,
                                      PRInt32  aModType)
 {
   if (aNameSpaceID == kNameSpaceID_None) {
+
+    nsSVGSVGElement* content = static_cast<nsSVGSVGElement*>(mContent);
+
     if (aAttribute == nsGkAtoms::width ||
         aAttribute == nsGkAtoms::height) {
 
-      nsSVGSVGElement* svg = static_cast<nsSVGSVGElement*>(mContent);
-      if (svg->HasViewBox()) {
-
+      if (content->HasViewBoxOrSyntheticViewBox()) {
         // make sure our cached transform matrix gets (lazily) updated
         mCanvasTM = nsnull;
-
+        content->ChildrenOnlyTransformChanged();
         nsSVGUtils::NotifyChildrenOfSVGChange(this, TRANSFORM_CHANGED);
       } else {
-
         PRUint32 flags = COORD_CONTEXT_CHANGED;
-
         if (mCanvasTM && mCanvasTM->IsSingular()) {
-
           mCanvasTM = nsnull;
-
           flags |= TRANSFORM_CHANGED;
         }
         nsSVGUtils::NotifyChildrenOfSVGChange(this, flags);
       }
 
     } else if (aAttribute == nsGkAtoms::transform ||
                aAttribute == nsGkAtoms::preserveAspectRatio ||
                aAttribute == nsGkAtoms::viewBox ||
                aAttribute == nsGkAtoms::x ||
                aAttribute == nsGkAtoms::y) {
       // make sure our cached transform matrix gets (lazily) updated
       mCanvasTM = nsnull;
 
       nsSVGUtils::NotifyChildrenOfSVGChange(
           this, aAttribute == nsGkAtoms::viewBox ?
                   TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED : TRANSFORM_CHANGED);
+
+      if (aAttribute == nsGkAtoms::viewBox ||
+          (aAttribute == nsGkAtoms::preserveAspectRatio &&
+           content->HasViewBoxOrSyntheticViewBox())) {
+        content->ChildrenOnlyTransformChanged();
+      }
     }
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP_(nsIFrame*)
 nsSVGInnerSVGFrame::GetFrameForPoint(const nsPoint &aPoint)
@@ -260,8 +277,22 @@ nsSVGInnerSVGFrame::GetCanvasTM()
 
     gfxMatrix tm = content->PrependLocalTransformsTo(parent->GetCanvasTM());
 
     mCanvasTM = new gfxMatrix(tm);
   }
   return *mCanvasTM;
 }
 
+bool
+nsSVGInnerSVGFrame::HasChildrenOnlyTransform(gfxMatrix *aTransform) const
+{
+  nsSVGSVGElement *content = static_cast<nsSVGSVGElement*>(mContent);
+
+  if (content->HasViewBoxOrSyntheticViewBox()) {
+    // XXX Maybe return false if the transform is the identity transform?
+    if (aTransform) {
+      *aTransform = content->GetViewBoxTransform();
+    }
+    return true;
+  }
+  return false;
+}
--- a/layout/svg/base/src/nsSVGInnerSVGFrame.h
+++ b/layout/svg/base/src/nsSVGInnerSVGFrame.h
@@ -79,21 +79,24 @@ public:
 #endif
 
   NS_IMETHOD  AttributeChanged(PRInt32         aNameSpaceID,
                                nsIAtom*        aAttribute,
                                PRInt32         aModType);
 
   // nsISVGChildFrame interface:
   NS_IMETHOD PaintSVG(nsRenderingContext *aContext, const nsIntRect *aDirtyRect);
+  virtual void UpdateBounds();
   virtual void NotifySVGChanged(PRUint32 aFlags);
   NS_IMETHOD_(nsIFrame*) GetFrameForPoint(const nsPoint &aPoint);
 
   // nsSVGContainerFrame methods:
   virtual gfxMatrix GetCanvasTM();
 
+  virtual bool HasChildrenOnlyTransform(gfxMatrix *aTransform) const;
+
   // nsISVGSVGFrame interface:
   virtual void NotifyViewportOrTransformChanged(PRUint32 aFlags);
 
 protected:
 
   nsAutoPtr<gfxMatrix> mCanvasTM;
 };
--- a/layout/svg/base/src/nsSVGOuterSVGFrame.cpp
+++ b/layout/svg/base/src/nsSVGOuterSVGFrame.cpp
@@ -141,16 +141,18 @@ nsSVGOuterSVGFrame::nsSVGOuterSVGFrame(n
     : nsSVGOuterSVGFrameBase(aContext)
     , mFullZoom(0)
     , mViewportInitialized(false)
 #ifdef XP_MACOSX
     , mEnableBitmapFallback(false)
 #endif
     , mIsRootContent(false)
 {
+  // Outer-<svg> has CSS layout, so remove this bit:
+  RemoveStateBits(NS_FRAME_SVG_LAYOUT);
 }
 
 NS_IMETHODIMP
 nsSVGOuterSVGFrame::Init(nsIContent* aContent,
                          nsIFrame* aParent,
                          nsIFrame* aPrevInFlow)
 {
 #ifdef DEBUG
@@ -411,17 +413,17 @@ nsSVGOuterSVGFrame::Reflow(nsPresContext
   nsSVGSVGElement *svgElem = static_cast<nsSVGSVGElement*>(mContent);
 
   PRUint32 changeBits = 0;
   if (newViewportSize != svgElem->GetViewportSize()) {
     changeBits |= COORD_CONTEXT_CHANGED;
     svgElem->SetViewportSize(newViewportSize);
   }
   if (mFullZoom != PresContext()->GetFullZoom()) {
-    changeBits |= TRANSFORM_CHANGED;
+    changeBits |= FULL_ZOOM_CHANGED;
     mFullZoom = PresContext()->GetFullZoom();
   }
   mViewportInitialized = true;
   if (changeBits) {
     NotifyViewportOrTransformChanged(changeBits);
   }
 
   // Now that we've marked the necessary children as dirty, call
@@ -612,19 +614,24 @@ nsSVGOuterSVGFrame::AttributeChanged(PRI
 
       // make sure our cached transform matrix gets (lazily) updated
       mCanvasTM = nsnull;
 
       nsSVGUtils::NotifyChildrenOfSVGChange(
           this, aAttribute == nsGkAtoms::viewBox ?
                   TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED : TRANSFORM_CHANGED);
 
+      static_cast<nsSVGSVGElement*>(mContent)->ChildrenOnlyTransformChanged();
+
     } else if (aAttribute == nsGkAtoms::width ||
                aAttribute == nsGkAtoms::height) {
 
+      // Don't call ChildrenOnlyTransformChanged() here, since we call it
+      // under Reflow if the width/height actually changed.
+
       nsIFrame* embeddingFrame;
       if (IsRootOfReplacedElementSubDoc(&embeddingFrame) && embeddingFrame) {
         if (DependsOnIntrinsicSize(embeddingFrame)) {
           // Tell embeddingFrame's presShell it needs to be reflowed (which takes
           // care of reflowing us too).
           embeddingFrame->PresContext()->PresShell()->
             FrameNeedsReflow(embeddingFrame, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
         }
@@ -698,17 +705,18 @@ nsSVGOuterSVGFrame::GetType() const
 
 //----------------------------------------------------------------------
 // nsISVGSVGFrame methods:
 
 void
 nsSVGOuterSVGFrame::NotifyViewportOrTransformChanged(PRUint32 aFlags)
 {
   NS_ABORT_IF_FALSE(aFlags &&
-                    !(aFlags & ~(COORD_CONTEXT_CHANGED | TRANSFORM_CHANGED)),
+                    !(aFlags & ~(COORD_CONTEXT_CHANGED | TRANSFORM_CHANGED |
+                                 FULL_ZOOM_CHANGED)),
                     "Unexpected aFlags value");
 
   // No point in doing anything when were not init'ed yet:
   if (!mViewportInitialized) {
     return;
   }
 
   nsSVGSVGElement *content = static_cast<nsSVGSVGElement*>(mContent);
@@ -724,19 +732,31 @@ nsSVGOuterSVGFrame::NotifyViewportOrTran
       // A width/height of zero will result in us having a singular mCanvasTM
       // even when we don't have a viewBox. So we also want to recompute our
       // mCanvasTM for this width/height change even though we don't have a
       // viewBox.
       aFlags |= TRANSFORM_CHANGED;
     }
   }
 
+  bool haveNonFulLZoomTransformChange = (aFlags & TRANSFORM_CHANGED);
+
+  if (aFlags & FULL_ZOOM_CHANGED) {
+    // Convert FULL_ZOOM_CHANGED to TRANSFORM_CHANGED:
+    aFlags = (aFlags & ~FULL_ZOOM_CHANGED) | TRANSFORM_CHANGED;
+  }
+
   if (aFlags & TRANSFORM_CHANGED) {
     // Make sure our canvas transform matrix gets (lazily) recalculated:
     mCanvasTM = nsnull;
+
+    if (haveNonFulLZoomTransformChange &&
+        !(mState & NS_STATE_SVG_NONDISPLAY_CHILD)) {
+      content->ChildrenOnlyTransformChanged();
+    }
   }
 
   nsSVGUtils::NotifyChildrenOfSVGChange(this, aFlags);
 }
 
 //----------------------------------------------------------------------
 // nsSVGContainerFrame methods:
 
@@ -745,31 +765,41 @@ 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);
+
+  bool hasTransform = content->HasChildrenOnlyTransform();
+
+  if (hasTransform && aTransform) {
+    // Outer-<svg> doesn't use x/y, so we can pass eChildToUserSpace here.
+    gfxMatrix identity;
+    *aTransform =
+      content->PrependLocalTransformsTo(identity,
+                                        nsSVGElement::eChildToUserSpace);
+  }
+
+  return hasTransform;
+}
+
 //----------------------------------------------------------------------
 // Implementation helpers
 
 bool
 nsSVGOuterSVGFrame::IsRootOfReplacedElementSubDoc(nsIFrame **aEmbeddingFrame)
 {
   if (!mContent->GetParent()) {
     // Our content is the document element
--- a/layout/svg/base/src/nsSVGOuterSVGFrame.h
+++ b/layout/svg/base/src/nsSVGOuterSVGFrame.h
@@ -108,22 +108,31 @@ public:
     return MakeFrameName(NS_LITERAL_STRING("SVGOuterSVG"), aResult);
   }
 #endif
 
   NS_IMETHOD  AttributeChanged(PRInt32         aNameSpaceID,
                                nsIAtom*        aAttribute,
                                PRInt32         aModType);
 
+  virtual bool IsSVGTransformed(gfxMatrix *aOwnTransform,
+                                gfxMatrix *aFromParentTransform) const {
+    // Outer-<svg> can transform its children with viewBox, currentScale and
+    // currentTranslate, but it itself is not transformed by SVG transforms.
+    return false;
+  }
+
   // nsISVGSVGFrame interface:
   virtual void NotifyViewportOrTransformChanged(PRUint32 aFlags);
 
   // nsSVGContainerFrame methods:
   virtual gfxMatrix GetCanvasTM();
 
+  virtual bool HasChildrenOnlyTransform(gfxMatrix *aTransform) const;
+
 #ifdef XP_MACOSX
   bool BitmapFallbackEnabled() const {
     return mEnableBitmapFallback;
   }
   void SetBitmapFallbackEnabled(bool aVal) {
     NS_NOTREACHED("don't think me need this any more"); // comment in bug 732429 if we do
     mEnableBitmapFallback = aVal;
   }
--- a/layout/svg/base/src/nsSVGPathGeometryFrame.cpp
+++ b/layout/svg/base/src/nsSVGPathGeometryFrame.cpp
@@ -44,16 +44,19 @@
 #include "gfxPlatform.h"
 #include "nsGkAtoms.h"
 #include "nsRenderingContext.h"
 #include "nsSVGEffects.h"
 #include "nsSVGGraphicElement.h"
 #include "nsSVGMarkerFrame.h"
 #include "nsSVGPathGeometryElement.h"
 #include "nsSVGUtils.h"
+#include "SVGAnimatedTransformList.h"
+
+using namespace mozilla;
 
 //----------------------------------------------------------------------
 // Implementation
 
 nsIFrame*
 NS_NewSVGPathGeometryFrame(nsIPresShell* aPresShell,
                            nsStyleContext* aContext)
 {
@@ -101,16 +104,42 @@ nsSVGPathGeometryFrame::DidSetStyleConte
 }
 
 nsIAtom *
 nsSVGPathGeometryFrame::GetType() const
 {
   return nsGkAtoms::svgPathGeometryFrame;
 }
 
+bool
+nsSVGPathGeometryFrame::IsSVGTransformed(gfxMatrix *aOwnTransform,
+                                         gfxMatrix *aFromParentTransform) const
+{
+  bool foundTransform = false;
+
+  // Check if our parent has children-only transforms:
+  nsIFrame *parent = GetParent();
+  if (parent &&
+      parent->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer)) {
+    foundTransform = static_cast<nsSVGContainerFrame*>(parent)->
+                       HasChildrenOnlyTransform(aFromParentTransform);
+  }
+
+  nsSVGElement *content = static_cast<nsSVGElement*>(mContent);
+  const SVGAnimatedTransformList *list = content->GetAnimatedTransformList();
+  if (list && !list->GetAnimValue().IsEmpty()) {
+    if (aOwnTransform) {
+      *aOwnTransform = content->PrependLocalTransformsTo(gfxMatrix(),
+                                  nsSVGElement::eUserSpaceToParent);
+    }
+    foundTransform = true;
+  }
+  return foundTransform;
+}
+
 //----------------------------------------------------------------------
 // nsISVGChildFrame methods
 
 NS_IMETHODIMP
 nsSVGPathGeometryFrame::PaintSVG(nsRenderingContext *aContext,
                                  const nsIntRect *aDirtyRect)
 {
   if (!GetStyleVisibility()->IsVisible())
@@ -230,19 +259,32 @@ nsSVGPathGeometryFrame::UpdateBounds()
     nsSVGUtils::eBBoxIncludeMarkers);
   mRect = nsLayoutUtils::RoundGfxRectToAppRect(extent,
             PresContext()->AppUnitsPerCSSPixel());
 
   // See bug 614732 comment 32.
   mCoveredRegion = nsSVGUtils::TransformFrameRectToOuterSVG(
     mRect, GetCanvasTM(), PresContext());
 
+  if (mState & NS_FRAME_FIRST_REFLOW) {
+    // Make sure we have our filter property (if any) before calling
+    // FinishAndStoreOverflow (subsequent filter changes are handled off
+    // nsChangeHint_UpdateEffects):
+    nsSVGEffects::UpdateEffects(this);
+  }
+
+  nsRect overflow = nsRect(nsPoint(0,0), mRect.Size());
+  nsOverflowAreas overflowAreas(overflow, overflow);
+  FinishAndStoreOverflow(overflowAreas, mRect.Size());
+
   mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
               NS_FRAME_HAS_DIRTY_CHILDREN);
 
+  // XXXSDL get rid of this in favor of the invalidate call in
+  // FinishAndStoreOverflow?
   if (!(GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
     // We only invalidate if our outer-<svg> has already had its
     // initial reflow (since if it hasn't, its entire area will be
     // invalidated when it gets that initial reflow):
     nsSVGUtils::InvalidateBounds(this, true);
   }
 }
 
--- a/layout/svg/base/src/nsSVGPathGeometryFrame.h
+++ b/layout/svg/base/src/nsSVGPathGeometryFrame.h
@@ -63,18 +63,21 @@ struct nsPoint;
 typedef nsSVGGeometryFrame nsSVGPathGeometryFrameBase;
 
 class nsSVGPathGeometryFrame : public nsSVGPathGeometryFrameBase,
                                public nsISVGChildFrame
 {
   friend nsIFrame*
   NS_NewSVGPathGeometryFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 protected:
-  nsSVGPathGeometryFrame(nsStyleContext* aContext) :
-    nsSVGPathGeometryFrameBase(aContext) {}
+  nsSVGPathGeometryFrame(nsStyleContext* aContext)
+    : nsSVGPathGeometryFrameBase(aContext)
+  {
+     AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
+  }
 
 public:
   NS_DECL_QUERYFRAME
   NS_DECL_FRAMEARENA_HELPERS
 
   // nsIFrame interface:
   NS_IMETHOD  AttributeChanged(PRInt32         aNameSpaceID,
                                nsIAtom*        aAttribute,
@@ -84,16 +87,19 @@ public:
 
   /**
    * Get the "type" of the frame
    *
    * @see nsGkAtoms::svgPathGeometryFrame
    */
   virtual nsIAtom* GetType() const;
 
+  virtual bool IsSVGTransformed(gfxMatrix *aOwnTransforms = nsnull,
+                                gfxMatrix *aFromParentTransforms = nsnull) const;
+
 #ifdef DEBUG
   NS_IMETHOD GetFrameName(nsAString& aResult) const
   {
     return MakeFrameName(NS_LITERAL_STRING("SVGPathGeometry"), aResult);
   }
 #endif
 
   // nsSVGGeometryFrame methods
@@ -105,19 +111,16 @@ protected:
                       const nsIntRect *aDirtyRect);
   NS_IMETHOD_(nsIFrame*) GetFrameForPoint(const nsPoint &aPoint);
   NS_IMETHOD_(nsRect) GetCoveredRegion();
   virtual void UpdateBounds();
   virtual void NotifySVGChanged(PRUint32 aFlags);
   virtual SVGBBox GetBBoxContribution(const gfxMatrix &aToBBoxUserspace,
                                       PRUint32 aFlags);
   NS_IMETHOD_(bool) IsDisplayContainer() { return false; }
-  NS_IMETHOD_(bool) HasValidCoveredRect() {
-    return !(GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD);
-  }
 
 protected:
   void GeneratePath(gfxContext *aContext,
                     const gfxMatrix *aOverrideTransform = nsnull);
 
 private:
   void Render(nsRenderingContext *aContext);
 
--- a/layout/svg/base/src/nsSVGSwitchFrame.cpp
+++ b/layout/svg/base/src/nsSVGSwitchFrame.cpp
@@ -34,16 +34,17 @@
  * 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 ***** */
 
 // Keep in (case-insensitive) order:
 #include "gfxMatrix.h"
 #include "gfxRect.h"
+#include "nsSVGEffects.h"
 #include "nsSVGGFrame.h"
 #include "nsSVGSwitchElement.h"
 #include "nsSVGUtils.h"
 
 class nsRenderingContext;
 
 typedef nsSVGGFrame nsSVGSwitchFrameBase;
 
@@ -77,16 +78,17 @@ public:
     return MakeFrameName(NS_LITERAL_STRING("SVGSwitch"), aResult);
   }
 #endif
 
   // nsISVGChildFrame interface:
   NS_IMETHOD PaintSVG(nsRenderingContext* aContext, const nsIntRect *aDirtyRect);
   NS_IMETHODIMP_(nsIFrame*) GetFrameForPoint(const nsPoint &aPoint);
   NS_IMETHODIMP_(nsRect) GetCoveredRegion();
+  virtual void UpdateBounds();
   virtual SVGBBox GetBBoxContribution(const gfxMatrix &aToBBoxUserspace,
                                       PRUint32 aFlags);
 
 private:
   nsIFrame *GetActiveChildFrame();
 };
 
 //----------------------------------------------------------------------
@@ -159,16 +161,78 @@ nsSVGSwitchFrame::GetCoveredRegion()
     nsISVGChildFrame* child = do_QueryFrame(kid);
     if (child) {
       rect = child->GetCoveredRegion();
     }
   }
   return rect;
 }
 
+void
+nsSVGSwitchFrame::UpdateBounds()
+{
+  NS_ASSERTION(nsSVGUtils::OuterSVGIsCallingUpdateBounds(this),
+               "This call is probaby a wasteful mistake");
+
+  NS_ABORT_IF_FALSE(!(GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD),
+                    "UpdateBounds mechanism not designed for this");
+
+  if (!nsSVGUtils::NeedsUpdatedBounds(this)) {
+    return;
+  }
+
+  // If the NS_FRAME_FIRST_REFLOW bit has been removed from our parent frame,
+  // then our outer-<svg> has previously had its initial reflow. In that case
+  // we need to make sure that that bit has been removed from ourself _before_
+  // recursing over our children to ensure that they know too. Otherwise, we
+  // need to remove it _after_ recursing over our children so that they know
+  // the initial reflow is currently underway.
+
+  bool outerSVGHasHadFirstReflow =
+    (GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW) == 0;
+
+  if (outerSVGHasHadFirstReflow) {
+    mState &= ~NS_FRAME_FIRST_REFLOW; // tell our children
+  }
+
+  nsOverflowAreas overflowRects;
+
+  nsIFrame *child = GetActiveChildFrame();
+  if (child) {
+    nsISVGChildFrame* svgChild = do_QueryFrame(child);
+    if (svgChild) {
+      NS_ABORT_IF_FALSE(!(child->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD),
+                        "Check for this explicitly in the |if|, then");
+      svgChild->UpdateBounds();
+
+      // We build up our child frame overflows here instead of using
+      // nsLayoutUtils::UnionChildOverflow since SVG frame's all use the same
+      // frame list, and we're iterating over that list now anyway.
+      ConsiderChildOverflow(overflowRects, child);
+    }
+  }
+
+  if (mState & NS_FRAME_FIRST_REFLOW) {
+    // Make sure we have our filter property (if any) before calling
+    // FinishAndStoreOverflow (subsequent filter changes are handled off
+    // nsChangeHint_UpdateEffects):
+    nsSVGEffects::UpdateEffects(this);
+  }
+
+  FinishAndStoreOverflow(overflowRects, mRect.Size());
+
+  // Remove state bits after FinishAndStoreOverflow so that it doesn't
+  // invalidate on first reflow:
+  mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
+              NS_FRAME_HAS_DIRTY_CHILDREN);
+
+  // XXXSDL Make Invalidate() call nsSVGUtils::InvalidateBounds(this)
+  // so that we invalidate under FinishAndStoreOverflow().
+}
+
 SVGBBox
 nsSVGSwitchFrame::GetBBoxContribution(const gfxMatrix &aToBBoxUserspace,
                                       PRUint32 aFlags)
 {
   nsIFrame* kid = GetActiveChildFrame();
   if (kid) {
     nsISVGChildFrame* svgKid = do_QueryFrame(kid);
     if (svgKid) {
--- a/layout/svg/base/src/nsSVGTextFrame.cpp
+++ b/layout/svg/base/src/nsSVGTextFrame.cpp
@@ -252,17 +252,17 @@ nsSVGTextFrame::UpdateBounds()
   mPositioningDirty = true;
 
   UpdateGlyphPositioning(false);
 
   // With glyph positions updated, our descendants can invalidate their new
   // areas correctly:
   nsSVGTextFrameBase::UpdateBounds();
 
-  // XXXsvgreflow once we store bounds on containers, call
+  // XXXSDL once we store bounds on containers, call
   // nsSVGUtils::InvalidateBounds(this) if not first reflow.
 }
 
 SVGBBox
 nsSVGTextFrame::GetBBoxContribution(const gfxMatrix &aToBBoxUserspace,
                                     PRUint32 aFlags)
 {
   UpdateGlyphPositioning(true);
--- a/layout/svg/base/src/nsSVGUseFrame.cpp
+++ b/layout/svg/base/src/nsSVGUseFrame.cpp
@@ -82,16 +82,17 @@ public:
 #ifdef DEBUG
   NS_IMETHOD GetFrameName(nsAString& aResult) const
   {
     return MakeFrameName(NS_LITERAL_STRING("SVGUse"), aResult);
   }
 #endif
 
   // nsISVGChildFrame interface:
+  virtual void UpdateBounds();
   virtual void NotifySVGChanged(PRUint32 aFlags);
 
   // nsIAnonymousContentCreator
   virtual nsresult CreateAnonymousContent(nsTArray<ContentInfo>& aElements);
   virtual void AppendAnonymousContentTo(nsBaseContentList& aElements,
                                         PRUint32 aFilter);
 
 private:
@@ -191,16 +192,31 @@ nsSVGUseFrame::IsLeaf() const
   return true;
 }
 
 
 //----------------------------------------------------------------------
 // nsISVGChildFrame methods
 
 void
+nsSVGUseFrame::UpdateBounds()
+{
+  // We only handle x/y offset here, since any width/height that is in force is
+  // handled by the nsSVGOuterSVGFrame for the anonymous <svg> that will be
+  // created for that purpose.
+  float x, y;
+  static_cast<nsSVGUseElement*>(mContent)->
+    GetAnimatedLengthValues(&x, &y, nsnull);
+  mRect.MoveTo(nsLayoutUtils::RoundGfxRectToAppRect(
+                 gfxRect(x, y, 0.0, 0.0),
+                 PresContext()->AppUnitsPerCSSPixel()).TopLeft());
+  nsSVGUseFrameBase::UpdateBounds();
+}
+
+void
 nsSVGUseFrame::NotifySVGChanged(PRUint32 aFlags)
 {
   if (aFlags & COORD_CONTEXT_CHANGED &&
       !(aFlags & TRANSFORM_CHANGED)) {
     // Coordinate context changes affect mCanvasTM if we have a
     // percentage 'x' or 'y'
     nsSVGUseElement *use = static_cast<nsSVGUseElement*>(mContent);
     if (use->mLengthAttributes[nsSVGUseElement::X].IsPercentage() ||
--- a/layout/svg/base/src/nsSVGUtils.cpp
+++ b/layout/svg/base/src/nsSVGUtils.cpp
@@ -42,16 +42,17 @@
 #include "gfxContext.h"
 #include "gfxImageSurface.h"
 #include "gfxMatrix.h"
 #include "gfxPlatform.h"
 #include "gfxRect.h"
 #include "gfxUtils.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/Preferences.h"
+#include "nsCSSFrameConstructor.h"
 #include "nsComputedDOMStyle.h"
 #include "nsContentUtils.h"
 #include "nsFrameList.h"
 #include "nsGkAtoms.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "nsIDOMSVGElement.h"
 #include "nsIDOMSVGUnitTypes.h"
@@ -59,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"
@@ -155,41 +157,36 @@ 147, 149, 151, 152, 154, 156, 157, 159,
 161, 163, 164, 166, 168, 170, 171, 173,
 175, 177, 179, 181, 183, 184, 186, 188,
 190, 192, 194, 196, 198, 200, 202, 204,
 206, 208, 210, 212, 214, 216, 218, 220,
 222, 224, 226, 229, 231, 233, 235, 237,
 239, 242, 244, 246, 248, 250, 253, 255
 };
 
-static bool gSMILEnabled;
-static const char SMIL_PREF_STR[] = "svg.smil.enabled";
-
-static int
-SMILPrefChanged(const char *aPref, void *aClosure)
-{
-  bool prefVal = Preferences::GetBool(SMIL_PREF_STR);
-  gSMILEnabled = prefVal;
-  return 0;
-}
+static bool sSMILEnabled;
+static bool sSVGDisplayListHitTestingEnabled;
+static bool sSVGDisplayListPaintingEnabled;
 
 bool
 NS_SMILEnabled()
 {
-  static bool sInitialized = false;
-  
-  if (!sInitialized) {
-    /* check and register ourselves with the pref */
-    gSMILEnabled = Preferences::GetBool(SMIL_PREF_STR);
-    Preferences::RegisterCallback(SMILPrefChanged, SMIL_PREF_STR);
+  return sSMILEnabled;
+}
 
-    sInitialized = true;
-  }
+bool
+NS_SVGDisplayListHitTestingEnabled()
+{
+  return sSVGDisplayListHitTestingEnabled;
+}
 
-  return gSMILEnabled;
+bool
+NS_SVGDisplayListPaintingEnabled()
+{
+  return sSVGDisplayListPaintingEnabled;
 }
 
 // we only take the address of this:
 static mozilla::gfx::UserDataKey sSVGAutoRenderStateKey;
 
 SVGAutoRenderState::SVGAutoRenderState(nsRenderingContext *aContext,
                                        RenderMode aMode)
   : mContext(aContext)
@@ -232,16 +229,30 @@ SVGAutoRenderState::IsPaintingToWindow(n
 {
   void *state = aContext->GetUserData(&sSVGAutoRenderStateKey);
   if (state) {
     return static_cast<SVGAutoRenderState*>(state)->mPaintingToWindow;
   }
   return false;
 }
 
+void
+nsSVGUtils::Init()
+{
+  Preferences::AddBoolVarCache(&sSMILEnabled,
+                               "svg.smil.enabled",
+                               true);
+
+  Preferences::AddBoolVarCache(&sSVGDisplayListHitTestingEnabled,
+                               "svg.display-lists.hit-testing.enabled");
+
+  Preferences::AddBoolVarCache(&sSVGDisplayListPaintingEnabled,
+                               "svg.display-lists.painting.enabled");
+}
+
 nsSVGSVGElement*
 nsSVGUtils::GetOuterSVGElement(nsSVGElement *aSVGElement)
 {
   nsIContent *element = nsnull;
   nsIContent *ancestor = aSVGElement->GetFlattenedTreeParent();
 
   while (ancestor && ancestor->IsSVG() &&
                      ancestor->Tag() != nsGkAtoms::foreignObject) {
@@ -250,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,
@@ -574,16 +594,36 @@ nsSVGUtils::GetNearestSVGViewport(nsIFra
       return do_QueryFrame(aFrame);
     }
   }
   NS_NOTREACHED("This is not reached. It's only needed to compile.");
   return nsnull;
 }
 
 nsRect
+nsSVGUtils::GetPostFilterVisualOverflowRect(nsIFrame *aFrame,
+                                            const nsRect &aUnfilteredRect)
+{
+  NS_ABORT_IF_FALSE(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT,
+                    "Called on invalid frame type");
+
+  nsSVGFilterFrame *filter = nsSVGEffects::GetFilterFrame(aFrame);
+  if (!filter) {
+    return aUnfilteredRect;
+  }
+
+  PRInt32 appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
+  nsIntRect unfilteredRect =
+    aUnfilteredRect.ToOutsidePixels(appUnitsPerDevPixel);
+  nsIntRect rect = filter->GetFilterBBox(aFrame, nsnull, &unfilteredRect);
+  nsRect r = rect.ToAppUnits(appUnitsPerDevPixel) - aFrame->GetPosition();
+  return r;
+}
+
+nsRect
 nsSVGUtils::FindFilterInvalidation(nsIFrame *aFrame, const nsRect& aRect)
 {
   PRInt32 appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
   nsIntRect rect = aRect.ToOutsidePixels(appUnitsPerDevPixel);
 
   while (aFrame) {
     if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG)
       break;
@@ -682,17 +722,17 @@ nsSVGUtils::InvalidateBounds(nsIFrame *a
   // XXXjwatt: can this come before InvalidateRenderingObservers?
   if (aFrame->GetStateBits() &
       (NS_FRAME_IS_DIRTY | NS_FRAME_FIRST_REFLOW)) {
     // Nothing to do if we're already dirty, or if the outer-<svg>
     // hasn't yet had its initial reflow.
     return;
   }
 
-  // XXXsvgreflow we want to reduce the bounds when passing through inner-<svg>
+  // XXXSDL we want to reduce the bounds when passing through inner-<svg>
   // and <use>, etc.
 
   nsSVGOuterSVGFrame* outerSVGFrame = GetOuterSVGFrame(aFrame);
   NS_ASSERTION(outerSVGFrame, "no outer svg frame");
   if (outerSVGFrame) {
     nsISVGChildFrame *svgFrame = do_QueryFrame(aFrame);
     if (!svgFrame)
       return;
@@ -748,17 +788,17 @@ nsSVGUtils::ScheduleBoundsUpdate(nsIFram
 
   if (aFrame->GetStateBits() &
       (NS_FRAME_IS_DIRTY | NS_FRAME_FIRST_REFLOW)) {
     // Nothing to do if we're already dirty, or if the outer-<svg>
     // hasn't yet had its initial reflow.
     return;
   }
 
-  // XXXsvgreflow once we store bounds on containers, we will not need to
+  // XXXSDL once we store bounds on containers, we will not need to
   // mark our descendants dirty.
   MarkDirtyBitsOnDescendants(aFrame);
 
   nsSVGOuterSVGFrame *outerSVGFrame = nsnull;
 
   // We must not add dirty bits to the nsSVGOuterSVGFrame or else
   // PresShell::FrameNeedsReflow won't work when we pass it in below.
   if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG) {
@@ -1130,30 +1170,43 @@ nsSVGUtils::PaintFrameWithEffects(nsRend
      so make sure all applicable ones are set again. */
 
   nsSVGEffects::EffectProperties effectProperties =
     nsSVGEffects::GetEffectProperties(aFrame);
 
   bool isOK = true;
   nsSVGFilterFrame *filterFrame = effectProperties.GetFilterFrame(&isOK);
 
-  /* Check if we need to draw anything. HasValidCoveredRect only returns
-   * true for path geometry and glyphs, so basically we're traversing
-   * all containers and we can only skip leaves here.
-   */
-  if (aDirtyRect && svgChildFrame->HasValidCoveredRect()) {
-    if (filterFrame) {
-      if (!aDirtyRect->Intersects(filterFrame->GetFilterBBox(aFrame, nsnull)))
-        return;
-    } else {
-      nsRect leafBounds = nsSVGUtils::TransformFrameRectToOuterSVG(
-        aFrame->GetRect(), GetCanvasTM(aFrame), aFrame->PresContext());
-      nsRect rect = aDirtyRect->ToAppUnits(aFrame->PresContext()->AppUnitsPerDevPixel());
-      if (!rect.Intersects(leafBounds))
-        return;
+  if (aDirtyRect &&
+      !(aFrame->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)) {
+    // Here we convert aFrame's paint bounds to outer-<svg> device space,
+    // compare it to aDirtyRect, and return early if they don't intersect.
+    // We don't do this optimization for nondisplay SVG since nondisplay
+    // SVG doesn't maintain bounds/overflow rects.
+    nsRect overflowRect = aFrame->GetVisualOverflowRectRelativeToSelf();
+    if (aFrame->IsFrameOfType(nsIFrame::eSVGGeometry)) {
+      // Unlike containers, leaf frames do not include GetPosition() in
+      // GetCanvasTM().
+      overflowRect = overflowRect + aFrame->GetPosition();
+    }
+    PRUint32 appUnitsPerDevPx = aFrame->PresContext()->AppUnitsPerDevPixel();
+    gfxMatrix tm = GetCanvasTM(aFrame);
+    if (aFrame->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer)) {
+      gfxMatrix childrenOnlyTM;
+      if (static_cast<nsSVGContainerFrame*>(aFrame)->
+            HasChildrenOnlyTransform(&childrenOnlyTM)) {
+        // Undo the children-only transform:
+        tm = childrenOnlyTM.Invert() * tm;
+      }
+    }
+    nsIntRect bounds = nsSVGUtils::TransformFrameRectToOuterSVG(overflowRect,
+                         tm, aFrame->PresContext()).
+                           ToOutsidePixels(appUnitsPerDevPx);
+    if (!aDirtyRect->Intersects(bounds)) {
+      return;
     }
   }
 
   /* SVG defines the following rendering model:
    *
    *  1. Render fill
    *  2. Render stroke
    *  3. Render markers
--- a/layout/svg/base/src/nsSVGUtils.h
+++ b/layout/svg/base/src/nsSVGUtils.h
@@ -40,16 +40,17 @@
 // include math.h to pick up definition of M_SQRT1_2 if the platform defines it
 #define _USE_MATH_DEFINES
 #include <math.h>
 
 #include "gfxMatrix.h"
 #include "gfxPoint.h"
 #include "gfxRect.h"
 #include "nsAlgorithm.h"
+#include "nsChangeHint.h"
 #include "nsColor.h"
 #include "nsCOMPtr.h"
 #include "nsID.h"
 #include "nsISupportsBase.h"
 #include "nsMathUtils.h"
 #include "nsPoint.h"
 #include "nsRect.h"
 
@@ -138,16 +139,19 @@ IsSVGWhitespace(PRUnichar aChar)
 }
 
 /*
  * Checks the smil enabled preference.  Declared as a function to match
  * NS_SVGEnabled().
  */
 bool NS_SMILEnabled();
 
+bool NS_SVGDisplayListHitTestingEnabled();
+bool NS_SVGDisplayListPaintingEnabled();
+
 /**
  * Sometimes we need to distinguish between an empty box and a box
  * that contains an element that has no size e.g. a point at the origin.
  */
 class SVGBBox {
 public:
   SVGBBox() 
     : mIsEmpty(true) {}
@@ -220,26 +224,38 @@ public:
 NS_DEFINE_STATIC_IID_ACCESSOR(nsISVGFilterProperty, NS_ISVGFILTERPROPERTY_IID)
 
 class nsSVGUtils
 {
 public:
   typedef mozilla::SVGAnimatedPreserveAspectRatio SVGAnimatedPreserveAspectRatio;
   typedef mozilla::SVGPreserveAspectRatio SVGPreserveAspectRatio;
 
+  static void Init();
+
   /*
    * Get the parent element of an nsIContent
    */
   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);
@@ -310,17 +326,25 @@ public:
   GetNearestViewportElement(nsIContent *aContent);
 
   /**
    * Gets the nearest nsSVGInnerSVGFrame or nsSVGOuterSVGFrame frame. aFrame
    * must be an SVG frame. If aFrame is of type nsGkAtoms::svgOuterSVGFrame,
    * returns nsnull.
    */
   static nsSVGDisplayContainerFrame* GetNearestSVGViewport(nsIFrame *aFrame);
-  
+
+  /**
+   * Returns the frame's post-filter visual overflow rect when passed the
+   * frame's pre-filter visual overflow rect. If the frame is not currently
+   * being filtered, this function simply returns aUnfilteredRect.
+   */
+  static nsRect GetPostFilterVisualOverflowRect(nsIFrame *aFrame,
+                                                const nsRect &aUnfilteredRect);
+
   /**
    * Figures out the worst case invalidation area for a frame, taking
    * filters into account.
    * Note that the caller is responsible for making sure that any cached
    * covered regions in the frame tree rooted at aFrame are up to date.
    * @param aRect the area in app units that needs to be invalidated in aFrame
    * @return the rect in app units that should be invalidated, taking
    * filters into account. Will return aRect when no filters are present.
--- a/layout/svg/base/src/svg.css
+++ b/layout/svg/base/src/svg.css
@@ -63,8 +63,17 @@ foreignObject {
 }
 
 *|*::-moz-svg-foreign-content {
   display: block !important;
   /* We need to be an absolute and fixed container */
   -moz-transform: translate(0) !important;
   text-indent: 0;
 }
+
+/* Set |transform-origin:0% 0%;| for all SVG elements except outer-<svg>,
+   noting that 'svg' as a child of 'foreignObject' counts as outer-<svg>.
+*/
+*:not(svg),
+*:not(foreignObject) > svg {
+  -moz-transform-origin:0 0;
+}
+
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -49,17 +49,16 @@ import org.mozilla.gecko.gfx.IntSize;
 import org.mozilla.gecko.gfx.Layer;
 import org.mozilla.gecko.gfx.LayerController;
 import org.mozilla.gecko.gfx.LayerView;
 import org.mozilla.gecko.gfx.PluginLayer;
 import org.mozilla.gecko.gfx.RectUtils;
 import org.mozilla.gecko.gfx.SurfaceTextureLayer;
 import org.mozilla.gecko.gfx.ViewportMetrics;
 import org.mozilla.gecko.gfx.ImmutableViewportMetrics;
-import org.mozilla.gecko.Tab.HistoryEntry;
 
 import java.io.*;
 import java.util.*;
 import java.util.regex.Pattern;
 import java.util.regex.Matcher;
 import java.util.zip.*;
 import java.net.URL;
 import java.nio.*;
--- a/mobile/android/base/GeckoInputConnection.java
+++ b/mobile/android/base/GeckoInputConnection.java
@@ -138,29 +138,33 @@ public class GeckoInputConnection
 
     @Override
     public boolean commitCompletion(CompletionInfo text) {
         return commitText(text.getText(), 1);
     }
 
     @Override
     public boolean commitText(CharSequence text, int newCursorPosition) {
+        if (mCommittingText)
+            Log.e(LOGTAG, "Please report this bug:", new IllegalStateException("commitText, but already committing text?!"));
+
         mCommittingText = true;
         replaceText(text, newCursorPosition, false);
         mCommittingText = false;
 
         if (hasCompositionString()) {
             if (DEBUG) Log.d(LOGTAG, ". . . commitText: endComposition");
             endComposition();
         }
         return true;
     }
 
     @Override
     public boolean finishComposingText() {
+        // finishComposingText() is sometimes called even when we are not composing text.
         if (hasCompositionString()) {
             if (DEBUG) Log.d(LOGTAG, ". . . finishComposingText: endComposition");
             endComposition();
         }
 
         final Editable content = getEditable();
         if (content != null) {
             beginBatchEdit();
@@ -284,16 +288,17 @@ public class GeckoInputConnection
     @Override
     public CharSequence getTextAfterCursor(int length, int flags) {
         clampSelection();
         return super.getTextAfterCursor(length, flags);
     }
 
     @Override
     public boolean setComposingText(CharSequence text, int newCursorPosition) {
+        // setComposingText will likely be called multiple times while we are composing text.
         clampSelection();
         return super.setComposingText(text, newCursorPosition);
     }
 
     // Android's BaseInputConnection.java is vulnerable to IndexOutOfBoundsExceptions because it
     // does not adequately protect against stale indexes for selections exceeding the content length
     // when the Editable content changes. We must clamp the indexes to be safe.
     private void clampSelection() {
@@ -587,18 +592,18 @@ public class GeckoInputConnection
             // we can send Gecko keydown/keyup events instead of composition events.
             if (mCommittingText && !hasCompositionString() && synthesizeKeyEvents(changedChar)) {
                 // Block this thread until all pending events are processed
                 GeckoAppShell.geckoEventSync();
                 return;
             }
         }
 
-        boolean needCompositionString = !hasCompositionString();
-        if (needCompositionString) {
+        boolean startCompositionString = !hasCompositionString();
+        if (startCompositionString) {
             if (DEBUG) Log.d(LOGTAG, ". . . onTextChanged: IME_COMPOSITION_BEGIN");
             GeckoAppShell.sendEventToGecko(
                 GeckoEvent.createIMEEvent(GeckoEvent.IME_COMPOSITION_BEGIN, 0, 0));
             mCompositionStart = start;
 
             if (DEBUG) {
                 Log.d(LOGTAG, ". . . onTextChanged: IME_SET_SELECTION, start=" + start + ", len="
                               + before);
@@ -615,17 +620,17 @@ public class GeckoInputConnection
                           + ", 0");
         }
 
         GeckoAppShell.sendEventToGecko(
             GeckoEvent.createIMEEvent(GeckoEvent.IME_SET_SELECTION, start + count, 0));
 
         // End composition if all characters in the word have been deleted.
         // This fixes autocomplete results not appearing.
-        if (count == 0 || needCompositionString)
+        if (count == 0 || (startCompositionString && mCommittingText))
             endComposition();
 
         // Block this thread until all pending events are processed
         GeckoAppShell.geckoEventSync();
     }
 
     private boolean synthesizeKeyEvents(char inputChar) {
         if (mKeyCharacterMap == null) {
@@ -658,18 +663,23 @@ public class GeckoInputConnection
             }
         }
 
         return sentKeyEvents;
     }
 
     private void endComposition() {
         if (DEBUG) Log.d(LOGTAG, "IME: endComposition: IME_COMPOSITION_END");
+
+        if (!hasCompositionString())
+           Log.e(LOGTAG, "Please report this bug:", new IllegalStateException("endComposition, but not composing text?!"));
+
         GeckoAppShell.sendEventToGecko(
             GeckoEvent.createIMEEvent(GeckoEvent.IME_COMPOSITION_END, 0, 0));
+
         mCompositionStart = NO_COMPOSITION_STRING;
     }
 
     private void sendTextToGecko(CharSequence text, int caretPos) {
         if (DEBUG) Log.d(LOGTAG, "IME: sendTextToGecko(\"" + text + "\")");
 
         // Handle composition text styles
         if (text != null && text instanceof Spanned) {
--- a/mobile/android/base/Tab.java
+++ b/mobile/android/base/Tab.java
@@ -71,18 +71,18 @@ public final class Tab {
     private static Pattern sColorPattern;
     private int mId;
     private String mUrl;
     private String mTitle;
     private Drawable mFavicon;
     private String mFaviconUrl;
     private JSONObject mIdentityData;
     private Drawable mThumbnail;
-    private List<HistoryEntry> mHistory;
     private int mHistoryIndex;
+    private int mHistorySize;
     private int mParentId;
     private boolean mExternal;
     private boolean mBookmark;
     private HashMap<String, DoorHanger> mDoorHangers;
     private long mFaviconLoadId;
     private String mDocumentURI;
     private String mContentType;
     private boolean mHasTouchListeners;
@@ -93,38 +93,28 @@ public final class Tab {
     private int mCheckerboardColor = Color.WHITE;
     private int mState;
 
     public static final int STATE_DELAYED = 0;
     public static final int STATE_LOADING = 1;
     public static final int STATE_SUCCESS = 2;
     public static final int STATE_ERROR = 3;
 
-    public static final class HistoryEntry {
-        public String mUri;         // must never be null
-        public String mTitle;       // must never be null
-
-        public HistoryEntry(String uri, String title) {
-            mUri = uri;
-            mTitle = title;
-        }
-    }
-
     public Tab(int id, String url, boolean external, int parentId, String title) {
         mId = id;
         mUrl = url;
         mExternal = external;
         mParentId = parentId;
         mTitle = title;
         mFavicon = null;
         mFaviconUrl = null;
         mIdentityData = null;
         mThumbnail = null;
-        mHistory = new ArrayList<HistoryEntry>();
         mHistoryIndex = -1;
+        mHistorySize = 0;
         mBookmark = false;
         mDoorHangers = new HashMap<String, DoorHanger>();
         mFaviconLoadId = 0;
         mDocumentURI = "";
         mContentType = "";
         mPluginViews = new ArrayList<View>();
         mPluginLayers = new HashMap<Object, Layer>();
         mState = "about:home".equals(url) ? STATE_SUCCESS : STATE_LOADING;
@@ -246,17 +236,17 @@ public final class Tab {
         return mExternal;
     }
 
     public void updateURL(String url) {
         if (url != null && url.length() > 0) {
             mUrl = url;
             Log.i(LOGTAG, "Updated url: " + url + " for tab with id: " + mId);
             updateBookmark();
-            updateHistoryEntry(mUrl, mTitle);
+            updateHistory(mUrl, mTitle);
         }
     }
 
     public void setDocumentURI(String documentURI) {
         mDocumentURI = documentURI;
     }
 
     public String getDocumentURI() {
@@ -270,32 +260,25 @@ public final class Tab {
     public String getContentType() {
         return mContentType;
     }
 
     public void updateTitle(String title) {
         mTitle = (title == null ? "" : title);
 
         Log.i(LOGTAG, "Updated title: " + mTitle + " for tab with id: " + mId);
-        updateHistoryEntry(mUrl, mTitle);
+        updateHistory(mUrl, mTitle);
     }
 
-    private void updateHistoryEntry(final String uri, final String title) {
-        final HistoryEntry he = getLastHistoryEntry();
-        if (he != null) {
-            he.mUri = uri;
-            he.mTitle = title;
-            GeckoAppShell.getHandler().post(new Runnable() {
-                public void run() {
-                    GlobalHistory.getInstance().update(uri, title);
-                }
-            });
-        } else {
-            Log.e(LOGTAG, "Requested title update on empty history stack");
-        }
+    private void updateHistory(final String uri, final String title) {
+        GeckoAppShell.getHandler().post(new Runnable() {
+            public void run() {
+                GlobalHistory.getInstance().update(uri, title);
+            }
+        });
     }
 
     public void setState(int state) {
         mState = state;
     }
 
     public int getState() {
         return mState;
@@ -312,22 +295,16 @@ public final class Tab {
     public void setFaviconLoadId(long faviconLoadId) {
         mFaviconLoadId = faviconLoadId;
     }
 
     public long getFaviconLoadId() {
         return mFaviconLoadId;
     }
 
-    public HistoryEntry getLastHistoryEntry() {
-        if (mHistory.isEmpty())
-            return null;
-        return mHistory.get(mHistoryIndex);
-    }
-
     public void updateFavicon(Drawable favicon) {
         mFavicon = favicon;
         Log.i(LOGTAG, "Updated favicon for tab with id: " + mId);
     }
 
     public void updateFaviconURL(String faviconUrl) {
         mFaviconUrl = faviconUrl;
         Log.i(LOGTAG, "Updated favicon URL for tab with id: " + mId);
@@ -394,21 +371,21 @@ public final class Tab {
 
     public boolean doStop() {
         GeckoEvent e = GeckoEvent.createBroadcastEvent("Session:Stop", "");
         GeckoAppShell.sendEventToGecko(e);
         return true;
     }
 
     public boolean canDoForward() {
-        return (mHistoryIndex + 1 < mHistory.size());
+        return (mHistoryIndex + 1 < mHistorySize);
     }
 
     public boolean doForward() {
-        if (mHistoryIndex + 1 >= mHistory.size()) {
+        if (mHistoryIndex + 1 >= mHistorySize) {
             return false;
         }
         GeckoEvent e = GeckoEvent.createBroadcastEvent("Session:Forward", "");
         GeckoAppShell.sendEventToGecko(e);
         return true;
     }
 
     public void addDoorHanger(String value, DoorHanger dh) {
@@ -447,48 +424,50 @@ public final class Tab {
     public HashMap<String, DoorHanger> getDoorHangers() {
         return mDoorHangers;
     }
 
     void handleSessionHistoryMessage(String event, JSONObject message) throws JSONException {
         if (event.equals("New")) {
             final String uri = message.getString("uri");
             mHistoryIndex++;
-            while (mHistory.size() > mHistoryIndex) {
-                mHistory.remove(mHistoryIndex);
-            }
-            HistoryEntry he = new HistoryEntry(uri, "");
-            mHistory.add(he);
+            mHistorySize = mHistoryIndex + 1;
             GeckoAppShell.getHandler().post(new Runnable() {
                 public void run() {
                     GlobalHistory.getInstance().add(uri);
                 }
             });
         } else if (event.equals("Back")) {
             if (mHistoryIndex - 1 < 0) {
                 Log.e(LOGTAG, "Received unexpected back notification");
                 return;
             }
             mHistoryIndex--;
         } else if (event.equals("Forward")) {
-            if (mHistoryIndex + 1 >= mHistory.size()) {
+            if (mHistoryIndex + 1 >= mHistorySize) {
                 Log.e(LOGTAG, "Received unexpected forward notification");
                 return;
             }
             mHistoryIndex++;
         } else if (event.equals("Goto")) {
             int index = message.getInt("index");
-            if (index < 0 || index >= mHistory.size()) {
+            if (index < 0 || index >= mHistorySize) {
                 Log.e(LOGTAG, "Received unexpected history-goto notification");
                 return;
             }
             mHistoryIndex = index;
         } else if (event.equals("Purge")) {
-            mHistory.clear();
-            mHistoryIndex = -1;
+            int numEntries = message.getInt("index");
+            mHistorySize -= numEntries;
+            mHistoryIndex -= numEntries;
+            if (mHistorySize < 0 || mHistoryIndex < -1) {
+                Log.e(LOGTAG, "Unexpected history state: index = " + mHistoryIndex + ", size = " + mHistorySize);
+                mHistorySize = 0;
+                mHistoryIndex = -1;
+            }
         }
     }
 
     private void saveThumbnailToDB(BitmapDrawable thumbnail) {
         try {
             String url = getURL();
             if (url == null)
                 return;
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -2335,17 +2335,17 @@ Tab.prototype = {
   },
 
   OnHistoryGotoIndex: function(aIndex, aUri) {
     this._sendHistoryEvent("Goto", aIndex, null);
     return true;
   },
 
   OnHistoryPurge: function(aNumEntries) {
-    this._sendHistoryEvent("Purge", -1, null);
+    this._sendHistoryEvent("Purge", aNumEntries, null);
     return true;
   },
 
   get metadata() {
     return ViewportHandler.getMetadataForDocument(this.browser.contentDocument);
   },
 
   /** Update viewport when the metadata changes. */
--- a/widget/cocoa/GfxInfo.mm
+++ b/widget/cocoa/GfxInfo.mm
@@ -39,17 +39,17 @@
 #include <OpenGL/OpenGL.h>
 #include <OpenGL/CGLRenderers.h>
 
 #include "mozilla/Util.h"
 
 #include "GfxInfo.h"
 #include "nsUnicharUtils.h"
 #include "mozilla/FunctionTimer.h"
-#include "nsToolkit.h"
+#include "nsCocoaFeatures.h"
 #include "mozilla/Preferences.h"
 
 #import <Foundation/Foundation.h>
 #import <IOKit/IOKitLib.h>
 #import <Cocoa/Cocoa.h>
 
 #if defined(MOZ_CRASHREPORTER)
 #include "nsExceptionHandler.h"
@@ -171,17 +171,17 @@ GfxInfo::Init()
 
   CGLDestroyRendererInfo(renderer);
 #endif
 
   GetDeviceInfo();
 
   AddCrashReportAnnotations();
 
-  mOSXVersion = nsToolkit::OSXVersion();
+  mOSXVersion = nsCocoaFeatures::OSXVersion();
 
   return rv;
 }
 
 NS_IMETHODIMP
 GfxInfo::GetD2DEnabled(bool *aEnabled)
 {
   return NS_ERROR_FAILURE;
@@ -399,17 +399,17 @@ GfxInfo::GetFeatureStatusImpl(PRInt32 aF
     *aOS = os;
 
   // Don't evaluate special cases when we're evaluating the downloaded blocklist.
   if (!aDriverInfo.Length()) {
     // Many WebGL issues on 10.5, especially:
     //   * bug 631258: WebGL shader paints using textures belonging to other processes on Mac OS 10.5
     //   * bug 618848: Post process shaders and texture mapping crash OS X 10.5
     if (aFeature == nsIGfxInfo::FEATURE_WEBGL_OPENGL &&
-        !nsToolkit::OnSnowLeopardOrLater()) {
+        !nsCocoaFeatures::OnSnowLeopardOrLater()) {
       *aStatus = nsIGfxInfo::FEATURE_BLOCKED_OS_VERSION;
       return NS_OK;
     }
 
     // The code around the following has been moved into the global blocklist.
 #if 0
       // CGL reports a list of renderers, some renderers are slow (e.g. software)
       // and AFAIK we can't decide which one will be used among them, so let's implement this by returning NO_INFO
--- a/widget/cocoa/Makefile.in
+++ b/widget/cocoa/Makefile.in
@@ -133,15 +133,16 @@ export::
 endif
 
 export::
 	$(INSTALL) $(srcdir)/cursors $(DIST)/bin/res
 
 LOCAL_INCLUDES	= \
 	$(TK_CFLAGS) \
 	-I$(srcdir)/../xpwidgets \
+	-I$(srcdir)/../shared \
 	$(NULL)
 
 LDFLAGS	+= \
 	-framework QuickTime \
 	-framework IOKit \
 	-F/System/Library/PrivateFrameworks -framework CoreUI \
 	$(NULL)
--- a/widget/cocoa/TextInputHandler.h
+++ b/widget/cocoa/TextInputHandler.h
@@ -56,26 +56,28 @@ class nsChildView;
 struct nsTextRange;
 
 namespace mozilla {
 namespace widget {
 
 // Key code constants
 enum
 {
+  kSpaceKeyCode       = 0x31,
   kEscapeKeyCode      = 0x35,
   kRCommandKeyCode    = 0x36, // right command key
   kCommandKeyCode     = 0x37,
   kShiftKeyCode       = 0x38,
   kCapsLockKeyCode    = 0x39,
   kOptionkeyCode      = 0x3A,
   kControlKeyCode     = 0x3B,
   kRShiftKeyCode      = 0x3C, // right shift key
   kROptionKeyCode     = 0x3D, // right option key
   kRControlKeyCode    = 0x3E, // right control key
+
   kClearKeyCode       = 0x47,
 
   // function keys
   kF1KeyCode          = 0x7A,
   kF2KeyCode          = 0x78,
   kF3KeyCode          = 0x63,
   kF4KeyCode          = 0x76,
   kF5KeyCode          = 0x60,
@@ -84,16 +86,20 @@ enum
   kF8KeyCode          = 0x64,
   kF9KeyCode          = 0x65,
   kF10KeyCode         = 0x6D,
   kF11KeyCode         = 0x67,
   kF12KeyCode         = 0x6F,
   kF13KeyCode         = 0x69,
   kF14KeyCode         = 0x6B,
   kF15KeyCode         = 0x71,
+  kF16KeyCode         = 0x6A,
+  kF17KeyCode         = 0x40,
+  kF18KeyCode         = 0x4F,
+  kF19KeyCode         = 0x50,
 
   kPrintScreenKeyCode = kF13KeyCode,
   kScrollLockKeyCode  = kF14KeyCode,
   kPauseKeyCode       = kF15KeyCode,
 
   // keypad
   kKeypad0KeyCode     = 0x52,
   kKeypad1KeyCode     = 0x53,
@@ -107,20 +113,25 @@ enum
   kKeypad9KeyCode     = 0x5C,
 
   kKeypadMultiplyKeyCode  = 0x43,
   kKeypadAddKeyCode       = 0x45,
   kKeypadSubtractKeyCode  = 0x4E,
   kKeypadDecimalKeyCode   = 0x41,
   kKeypadDivideKeyCode    = 0x4B,
   kKeypadEqualsKeyCode    = 0x51, // no correpsonding gecko key code
+
   kEnterKeyCode           = 0x4C,
   kReturnKeyCode          = 0x24,
   kPowerbookEnterKeyCode  = 0x34, // Enter on Powerbook's keyboard is different
 
+  // IME keys
+  kJapanese_Eisu          = 0x66,
+  kJapanese_Kana          = 0x68,
+
   kInsertKeyCode          = 0x72, // also help key
   kDeleteKeyCode          = 0x75, // also forward delete key
   kTabKeyCode             = 0x30,
   kTildeKeyCode           = 0x32,
   kBackspaceKeyCode       = 0x33,
   kHomeKeyCode            = 0x73, 
   kEndKeyCode             = 0x77,
   kPageUpKeyCode          = 0x74,
@@ -282,16 +293,29 @@ public:
    *
    * @param aNativeKeyEvent       A native key event for which you want to
    *                              dispatch a Gecko key event.
    * @param aKeyEvent             The result -- a Gecko key event initialized
    *                              from the native key event.
    */
   void InitKeyEvent(NSEvent *aNativeKeyEvent, nsKeyEvent& aKeyEvent);
 
+  /**
+   * ComputeGeckoKeyCode() returns Gecko keycode for aNativeKeyCode on current
+   * keyboard layout.
+   *
+   * @param aNativeKeyCode        A native keycode.
+   * @param aKbType               A native Keyboard Type value.  Typically,
+   *                              this is a result of ::LMGetKbdType().
+   * @param aCmdIsPressed         TRUE if Cmd key is pressed.  Otherwise, FALSE.
+   * @return                      The computed Gecko keycode.
+   */
+  PRUint32 ComputeGeckoKeyCode(UInt32 aNativeKeyCode, UInt32 aKbType,
+                               bool aCmdIsPressed);
+
 protected:
   /**
    * TranslateToString() computes the inputted text from the native keyCode,
    * modifier flags and keyboard type.
    *
    * @param aKeyCode              A native keyCode.
    * @param aModifiers            Combination of native modifier flags.
    * @param aKbType               A native Keyboard Type value.  Typically,
@@ -312,29 +336,32 @@ protected:
    * @param aKeyCode              A native keyCode.
    * @param aModifiers            Combination of native modifier flags.
    * @param aKbType               A native Keyboard Type value.  Typically,
    *                              this is a result of ::LMGetKbdType().
    * @return                      If succeeded and the result is one character,
    *                              returns the charCode of it.  Otherwise,
    *                              returns 0.
    */
-  PRUint32 TranslateToChar(UInt32 aKeyCode, UInt32 aModifiers, UInt32 aKbdType);
+  PRUint32 TranslateToChar(UInt32 aKeyCode, UInt32 aModifiers, UInt32 aKbType);
 
   /**
    * InitKeyPressEvent() initializes aKeyEvent for aNativeKeyEvent.
    * Don't call this method when aKeyEvent isn't NS_KEY_PRESS.
    *
    * @param aNativeKeyEvent       A native key event for which you want to
    *                              dispatch a Gecko key event.
    * @param aKeyEvent             The result -- a Gecko key event initialized
    *                              from the native key event.  This must be
    *                              NS_KEY_PRESS event.
+   * @param aKbType               A native Keyboard Type value.  Typically,
+   *                              this is a result of ::LMGetKbdType().
    */
-  void InitKeyPressEvent(NSEvent *aNativeKeyEvent, nsKeyEvent& aKeyEvent);
+  void InitKeyPressEvent(NSEvent *aNativeKeyEvent, nsKeyEvent& aKeyEvent,
+                         UInt32 aKbType);
 
   bool GetBoolProperty(const CFStringRef aKey);
   bool GetStringProperty(const CFStringRef aKey, CFStringRef &aStr);
   bool GetStringProperty(const CFStringRef aKey, nsAString &aStr);
 
   TISInputSourceRef mInputSource;
   CFArrayRef mInputSourceList;
   const UCKeyboardLayout* mUCKeyboardLayout;
@@ -398,35 +425,16 @@ public:
    */
   nsresult SynthesizeNativeKeyEvent(PRInt32 aNativeKeyboardLayout,
                                     PRInt32 aNativeKeyCode,
                                     PRUint32 aModifierFlags,
                                     const nsAString& aCharacters,
                                     const nsAString& aUnmodifiedCharacters);
 
   /**
-   * ComputeGeckoKeyCode() computes Gecko defined keyCode from the native
-   * keyCode or the characters.
-   *
-   * @param aNativeKeyCode        A native keyCode.
-   * @param aCharacters           Characters from the native key event (obtained
-   *                              using charactersIgnoringModifiers).  If the
-   *                              native event contains one or more characters,
-   *                              the result is computed from this.
-   * @return                      Gecko keyCode value for aNativeKeyCode (if
-   *                              aCharacters is empty), otherwise for
-   *                              aCharacters (if aCharacters is non-empty).
-   *                              Or zero if the aCharacters contains one or
-   *                              more Unicode characters, or if aNativeKeyCode
-   *                              cannot be mapped to a Gecko keyCode.
-   */
-  static PRUint32 ComputeGeckoKeyCode(UInt32 aNativeKeyCode,
-                                      NSString *aCharacters);
-
-  /**
    * IsSpecialGeckoKey() checks whether aNativeKeyCode is mapped to a special
    * Gecko keyCode.  A key is "special" if it isn't used for text input.
    *
    * @param aNativeKeyCode        A native keycode.
    * @return                      If the keycode is mapped to a special key,
    *                              TRUE.  Otherwise, FALSE.
    */
   static bool IsSpecialGeckoKey(UInt32 aNativeKeyCode);
@@ -624,26 +632,16 @@ protected:
    * @return                      TRUE if aChar is a printable ASCII character
    *                              or a unicode character.  Otherwise, i.e,
    *                              if aChar is a non-printable ASCII character,
    *                              FALSE.
    */
   static bool IsPrintableChar(PRUnichar aChar);
 
   /**
-   * ComputeGeckoKeyCodeFromChar() computes Gecko defined keyCode value from
-   * aChar.  If aChar is not an ASCII character, this always returns FALSE.
-   *
-   * @param aChar                 A unicode character.
-   * @return                      A Gecko defined keyCode.  Or zero if aChar
-   *                              is a unicode character.
-   */
-  static PRUint32 ComputeGeckoKeyCodeFromChar(PRUnichar aChar);
-
-  /**
    * IsNormalCharInputtingEvent() checks whether aKeyEvent causes text input.
    *
    * @param aKeyEvent             A key event.
    * @return                      TRUE if the key event causes text input.
    *                              Otherwise, FALSE.
    */
   static bool IsNormalCharInputtingEvent(const nsKeyEvent& aKeyEvent);
 
--- a/widget/cocoa/TextInputHandler.mm
+++ b/widget/cocoa/TextInputHandler.mm
@@ -46,16 +46,17 @@
 #endif // MOZ_LOGGING
 #include "prlog.h"
 
 #include "nsChildView.h"
 #include "nsObjCExceptions.h"
 #include "nsBidiUtils.h"
 #include "nsToolkit.h"
 #include "nsCocoaUtils.h"
+#include "WidgetUtils.h"
 #include "nsPrintfCString.h"
 
 #ifdef __LP64__
 #include "ComplexTextInputPanel.h"
 #endif // __LP64__
 
 #ifndef NP_NO_CARBON
 #include <objc/runtime.h>
@@ -657,35 +658,38 @@ TISInputSourceWrapper::Clear()
 
 void
 TISInputSourceWrapper::InitKeyEvent(NSEvent *aNativeKeyEvent,
                                     nsKeyEvent& aKeyEvent)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   PR_LOG(gLog, PR_LOG_ALWAYS,
-    ("%p TISInputSourceWrapper::InitKeyEvent, aNativeKeyEvent=%p"
+    ("%p TISInputSourceWrapper::InitKeyEvent, aNativeKeyEvent=%p, "
      "aKeyEvent.message=%s",
      this, aNativeKeyEvent, GetGeckoKeyEventType(aKeyEvent)));
 
   NS_ENSURE_TRUE(aNativeKeyEvent, );
 
   aKeyEvent.time = PR_IntervalNow();
 
   nsCocoaUtils::InitInputEvent(aKeyEvent, aNativeKeyEvent);
 
   aKeyEvent.refPoint = nsIntPoint(0, 0);
   aKeyEvent.isChar = false; // XXX not used in XP level
 
-  NSString* str = nil;
-  if ([aNativeKeyEvent type] != NSFlagsChanged) {
-    str = [aNativeKeyEvent charactersIgnoringModifiers];
-  }
+  // If a keyboard layout override is set, we also need to force the keyboard
+  // type to something ANSI to avoid test failures on machines with JIS
+  // keyboards (since the pair of keyboard layout and physical keyboard type
+  // form the actual key layout).  This assumes that the test setting the
+  // override was written assuming an ANSI keyboard.
+  UInt32 kbType = mOverrideKeyboard ? eKbdType_ANSI : ::LMGetKbdType();
+
   aKeyEvent.keyCode =
-    TextInputHandler::ComputeGeckoKeyCode([aNativeKeyEvent keyCode], str);
+    ComputeGeckoKeyCode([aNativeKeyEvent keyCode], kbType, aKeyEvent.IsMeta());
 
   switch ([aNativeKeyEvent keyCode]) {
     case kCommandKeyCode:
     case kShiftKeyCode:
     case kOptionkeyCode:
     case kControlKeyCode:
       aKeyEvent.location = nsIDOMKeyEvent::DOM_KEY_LOCATION_LEFT;
       break;
@@ -726,46 +730,51 @@ TISInputSourceWrapper::InitKeyEvent(NSEv
   PR_LOG(gLog, PR_LOG_ALWAYS,
     ("%p TISInputSourceWrapper::InitKeyEvent, "
      "shift=%s, ctrl=%s, alt=%s, meta=%s",
      this, OnOrOff(aKeyEvent.IsShift()), OnOrOff(aKeyEvent.IsControl()),
      OnOrOff(aKeyEvent.IsAlt()), OnOrOff(aKeyEvent.IsMeta())));
 
   if (aKeyEvent.message == NS_KEY_PRESS &&
       !TextInputHandler::IsSpecialGeckoKey([aNativeKeyEvent keyCode])) {
-    InitKeyPressEvent(aNativeKeyEvent, aKeyEvent);
+    InitKeyPressEvent(aNativeKeyEvent, aKeyEvent, kbType);
     return;
   }
 
   aKeyEvent.charCode = 0;
 
   PR_LOG(gLog, PR_LOG_ALWAYS,
     ("%p TISInputSourceWrapper::InitKeyEvent, keyCode=0x%X charCode=0x0",
      this, aKeyEvent.keyCode));
 
   NS_OBJC_END_TRY_ABORT_BLOCK
 }
 
 void
 TISInputSourceWrapper::InitKeyPressEvent(NSEvent *aNativeKeyEvent,
-                                         nsKeyEvent& aKeyEvent)
+                                         nsKeyEvent& aKeyEvent,
+                                         UInt32 aKbType)
 {
   NS_ASSERTION(aKeyEvent.message == NS_KEY_PRESS,
                "aKeyEvent must be NS_KEY_PRESS event");
 
   PR_LOG(gLog, PR_LOG_ALWAYS,
     ("%p TISInputSourceWrapper::InitKeyPressEvent, aNativeKeyEvent=%p"
-     "aKeyEvent.message=%s",
-     this, aNativeKeyEvent, GetGeckoKeyEventType(aKeyEvent)));
+     "aKeyEvent.message=%s, aKbType=0x%X, IsOpenedIMEMode()=%s",
+     this, aNativeKeyEvent, GetGeckoKeyEventType(aKeyEvent), aKbType,
+     TrueOrFalse(IsOpenedIMEMode())));
 
   aKeyEvent.isChar = true; // this is not a special key  XXX not used in XP
 
   aKeyEvent.charCode = 0;
   NSString* chars = [aNativeKeyEvent characters];
   if ([chars length] > 0) {
+    // XXX This is wrong at Hiragana or Katakana with Kana-Nyuryoku mode or
+    //     Chinese or Koran IME modes.  We should use ASCII characters for the
+    //     charCode.
     aKeyEvent.charCode = [chars characterAtIndex:0];
   }
 
   // convert control-modified charCode to raw charCode (with appropriate case)
   if (aKeyEvent.IsControl() && aKeyEvent.charCode <= 26) {
     aKeyEvent.charCode += (aKeyEvent.IsShift()) ? ('A' - 1) : ('a' - 1);
   }
 
@@ -780,59 +789,52 @@ TISInputSourceWrapper::InitKeyPressEvent
 
   if (!aKeyEvent.IsControl() && !aKeyEvent.IsMeta() && !aKeyEvent.IsAlt()) {
     return;
   }
 
   TISInputSourceWrapper USLayout("com.apple.keylayout.US");
   bool isRomanKeyboardLayout = IsASCIICapable();
 
-  // If a keyboard layout override is set, we also need to force the
-  // keyboard type to something ANSI to avoid test failures on machines
-  // with JIS keyboards (since the pair of keyboard layout and physical
-  // keyboard type form the actual key layout).  This assumes that the
-  // test setting the override was written assuming an ANSI keyboard.
-  UInt32 kbType = mOverrideKeyboard ? eKbdType_ANSI : ::LMGetKbdType();
-
   UInt32 key = [aNativeKeyEvent keyCode];
 
   // Caps lock and num lock modifier state:
   UInt32 lockState = 0;
   if ([aNativeKeyEvent modifierFlags] & NSAlphaShiftKeyMask) {
     lockState |= alphaLock;
   }
   if ([aNativeKeyEvent modifierFlags] & NSNumericPadKeyMask) {
     lockState |= kEventKeyModifierNumLockMask;
   }
 
   PR_LOG(gLog, PR_LOG_ALWAYS,
     ("%p TISInputSourceWrapper::InitKeyPressEvent, "
-     "isRomanKeyboardLayout=%s, kbType=0x%X, key=0x%X",
-     this, TrueOrFalse(isRomanKeyboardLayout), kbType, key));
+     "isRomanKeyboardLayout=%s, key=0x%X",
+     this, TrueOrFalse(isRomanKeyboardLayout), aKbType, key));
 
   nsString str;
 
   // normal chars
-  PRUint32 unshiftedChar = TranslateToChar(key, lockState, kbType);
+  PRUint32 unshiftedChar = TranslateToChar(key, lockState, aKbType);
   UInt32 shiftLockMod = shiftKey | lockState;
-  PRUint32 shiftedChar = TranslateToChar(key, shiftLockMod, kbType);
+  PRUint32 shiftedChar = TranslateToChar(key, shiftLockMod, aKbType);
 
   // characters generated with Cmd key
   // XXX we should remove CapsLock state, which changes characters from
   //     Latin to Cyrillic with Russian layout on 10.4 only when Cmd key
   //     is pressed.
   UInt32 numState = (lockState & ~alphaLock); // only num lock state
-  PRUint32 uncmdedChar = TranslateToChar(key, numState, kbType);
+  PRUint32 uncmdedChar = TranslateToChar(key, numState, aKbType);
   UInt32 shiftNumMod = numState | shiftKey;
-  PRUint32 uncmdedShiftChar = TranslateToChar(key, shiftNumMod, kbType);
-  PRUint32 uncmdedUSChar = USLayout.TranslateToChar(key, numState, kbType);
+  PRUint32 uncmdedShiftChar = TranslateToChar(key, shiftNumMod, aKbType);
+  PRUint32 uncmdedUSChar = USLayout.TranslateToChar(key, numState, aKbType);
   UInt32 cmdNumMod = cmdKey | numState;
-  PRUint32 cmdedChar = TranslateToChar(key, cmdNumMod, kbType);
+  PRUint32 cmdedChar = TranslateToChar(key, cmdNumMod, aKbType);
   UInt32 cmdShiftNumMod = shiftKey | cmdNumMod;
-  PRUint32 cmdedShiftChar = TranslateToChar(key, cmdShiftNumMod, kbType);
+  PRUint32 cmdedShiftChar = TranslateToChar(key, cmdShiftNumMod, aKbType);
 
   // Is the keyboard layout changed by Cmd key?
   // E.g., Arabic, Russian, Hebrew, Greek and Dvorak-QWERTY.
   bool isCmdSwitchLayout = uncmdedChar != cmdedChar;
   // Is the keyboard layout for Latin, but Cmd key switches the layout?
   // I.e., Dvorak-QWERTY
   bool isDvorakQWERTY = isCmdSwitchLayout && isRomanKeyboardLayout;
 
@@ -881,21 +883,21 @@ TISInputSourceWrapper::InitKeyPressEvent
       cmdedChar = unshiftedChar;
     }
     if (shiftedChar) {
       cmdedShiftChar = shiftedChar;
     }
   } else if (uncmdedUSChar == cmdedChar) {
     // It looks like characters from a US layout are provided when Command
     // is down.
-    PRUint32 ch = USLayout.TranslateToChar(key, lockState, kbType);
+    PRUint32 ch = USLayout.TranslateToChar(key, lockState, aKbType);
     if (ch) {
       cmdedChar = ch;
     }
-    ch = USLayout.TranslateToChar(key, shiftLockMod, kbType);
+    ch = USLayout.TranslateToChar(key, shiftLockMod, aKbType);
     if (ch) {
       cmdedShiftChar = ch;
     }
   }
 
   // Only charCode (not alternativeCharCodes) is available to javascript,
   // so attempt to set this to the most likely intended (or most useful)
   // character.  Note that cmdedChar and cmdedShiftChar are usually
@@ -941,16 +943,173 @@ TISInputSourceWrapper::InitKeyPressEvent
     aKeyEvent.alternativeCharCodes.AppendElement(altCharCodes);
   }
   PR_LOG(gLog, PR_LOG_ALWAYS,
     ("%p TISInputSourceWrapper::InitKeyPressEvent, "
      "hasCmdShiftOnlyChar=%s, originalCmdedShiftChar=U+%X",
      this, TrueOrFalse(hasCmdShiftOnlyChar), originalCmdedShiftChar));
 }
 
+PRUint32
+TISInputSourceWrapper::ComputeGeckoKeyCode(UInt32 aNativeKeyCode,
+                                           UInt32 aKbType,
+                                           bool aCmdIsPressed)
+{
+  PR_LOG(gLog, PR_LOG_ALWAYS,
+    ("%p TISInputSourceWrapper::ComputeGeckoKeyCode, aNativeKeyCode=0x%X, "
+     "aKbType=0x%X, aCmdIsPressed=%s, IsOpenedIMEMode()=%s, "
+     "IsASCIICapable()=%s",
+     this, aNativeKeyCode, aKbType, TrueOrFalse(aCmdIsPressed),
+     TrueOrFalse(IsOpenedIMEMode()), TrueOrFalse(IsASCIICapable())));
+
+  switch (aNativeKeyCode) {
+    case kSpaceKeyCode:         return NS_VK_SPACE;
+    case kEscapeKeyCode:        return NS_VK_ESCAPE;
+
+    // modifiers
+    case kRCommandKeyCode:
+    case kCommandKeyCode:       return NS_VK_META;
+    case kRShiftKeyCode:
+    case kShiftKeyCode:         return NS_VK_SHIFT;
+    case kCapsLockKeyCode:      return NS_VK_CAPS_LOCK;
+    case kRControlKeyCode:
+    case kControlKeyCode:       return NS_VK_CONTROL;
+    case kROptionKeyCode:
+    case kOptionkeyCode:        return NS_VK_ALT;
+
+    case kClearKeyCode:         return NS_VK_CLEAR;
+
+    // function keys
+    case kF1KeyCode:            return NS_VK_F1;
+    case kF2KeyCode:            return NS_VK_F2;
+    case kF3KeyCode:            return NS_VK_F3;
+    case kF4KeyCode:            return NS_VK_F4;
+    case kF5KeyCode:            return NS_VK_F5;
+    case kF6KeyCode:            return NS_VK_F6;
+    case kF7KeyCode:            return NS_VK_F7;
+    case kF8KeyCode:            return NS_VK_F8;
+    case kF9KeyCode:            return NS_VK_F9;
+    case kF10KeyCode:           return NS_VK_F10;
+    case kF11KeyCode:           return NS_VK_F11;
+    case kF12KeyCode:           return NS_VK_F12;
+    // case kF13KeyCode:           return NS_VK_F13;  // clash with the 3 below
+    // case kF14KeyCode:           return NS_VK_F14;
+    // case kF15KeyCode:           return NS_VK_F15;
+    case kF16KeyCode:           return NS_VK_F16;
+    case kF17KeyCode:           return NS_VK_F17;
+    case kF18KeyCode:           return NS_VK_F18;
+    case kF19KeyCode:           return NS_VK_F19;
+
+    case kPauseKeyCode:         return NS_VK_PAUSE;
+    case kScrollLockKeyCode:    return NS_VK_SCROLL_LOCK;
+    case kPrintScreenKeyCode:   return NS_VK_PRINTSCREEN;
+
+    // keypad
+    case kKeypad0KeyCode:       return NS_VK_NUMPAD0;
+    case kKeypad1KeyCode:       return NS_VK_NUMPAD1;
+    case kKeypad2KeyCode:       return NS_VK_NUMPAD2;
+    case kKeypad3KeyCode:       return NS_VK_NUMPAD3;
+    case kKeypad4KeyCode:       return NS_VK_NUMPAD4;
+    case kKeypad5KeyCode:       return NS_VK_NUMPAD5;
+    case kKeypad6KeyCode:       return NS_VK_NUMPAD6;
+    case kKeypad7KeyCode:       return NS_VK_NUMPAD7;
+    case kKeypad8KeyCode:       return NS_VK_NUMPAD8;
+    case kKeypad9KeyCode:       return NS_VK_NUMPAD9;
+
+    case kKeypadMultiplyKeyCode:  return NS_VK_MULTIPLY;
+    case kKeypadAddKeyCode:       return NS_VK_ADD;
+    case kKeypadSubtractKeyCode:  return NS_VK_SUBTRACT;
+    case kKeypadDecimalKeyCode:   return NS_VK_DECIMAL;
+    case kKeypadDivideKeyCode:    return NS_VK_DIVIDE;
+
+    // IME keys
+    case kJapanese_Eisu:        return NS_VK_EISU;
+    case kJapanese_Kana:        return NS_VK_KANA;
+
+    // these may clash with forward delete and help
+    case kInsertKeyCode:        return NS_VK_INSERT;
+    case kDeleteKeyCode:        return NS_VK_DELETE;
+
+    case kBackspaceKeyCode:     return NS_VK_BACK;
+    case kTabKeyCode:           return NS_VK_TAB;
+
+    case kHomeKeyCode:          return NS_VK_HOME;
+    case kEndKeyCode:           return NS_VK_END;
+
+    case kPageUpKeyCode:        return NS_VK_PAGE_UP;
+    case kPageDownKeyCode:      return NS_VK_PAGE_DOWN;
+
+    case kLeftArrowKeyCode:     return NS_VK_LEFT;
+    case kRightArrowKeyCode:    return NS_VK_RIGHT;
+    case kUpArrowKeyCode:       return NS_VK_UP;
+    case kDownArrowKeyCode:     return NS_VK_DOWN;
+
+    case kVK_ANSI_1:            return NS_VK_1;
+    case kVK_ANSI_2:            return NS_VK_2;
+    case kVK_ANSI_3:            return NS_VK_3;
+    case kVK_ANSI_4:            return NS_VK_4;
+    case kVK_ANSI_5:            return NS_VK_5;
+    case kVK_ANSI_6:            return NS_VK_6;
+    case kVK_ANSI_7:            return NS_VK_7;
+    case kVK_ANSI_8:            return NS_VK_8;
+    case kVK_ANSI_9:            return NS_VK_9;
+    case kVK_ANSI_0:            return NS_VK_0;
+
+    case kEnterKeyCode:
+    case kReturnKeyCode:
+    case kPowerbookEnterKeyCode: return NS_VK_RETURN;
+  }
+
+  // If Cmd key is pressed, that causes switching keyboard layout temporarily.
+  // E.g., Dvorak-QWERTY.  Therefore, if Cmd key is pressed, we should honor it.
+  UInt32 modifiers = aCmdIsPressed ? cmdKey : 0;
+
+  PRUint32 charCode = TranslateToChar(aNativeKeyCode, modifiers, aKbType);
+
+  // Special case for Mac.  Mac inputs Yen sign (U+00A5) directly instead of
+  // Back slash (U+005C).  We should return NS_VK_BACK_SLASH for compatibility
+  // with other platforms.
+  // XXX How about Won sign (U+20A9) which has same problem as Yen sign?
+  if (charCode == 0x00A5) {
+    return NS_VK_BACK_SLASH;
+  }
+
+  PRUint32 keyCode = WidgetUtils::ComputeKeyCodeFromChar(charCode);
+  if (keyCode) {
+    return keyCode;
+  }
+
+  // If the unshifed char isn't an ASCII character, use shifted char.
+  charCode = TranslateToChar(aNativeKeyCode, modifiers | shiftKey, aKbType);
+  keyCode = WidgetUtils::ComputeKeyCodeFromChar(charCode);
+  if (keyCode) {
+    return keyCode;
+  }
+
+  // If this is ASCII capable, give up to compute it.
+  if (IsASCIICapable()) {
+    return 0;
+  }
+
+  // Retry with ASCII capable keyboard layout.
+  TISInputSourceWrapper currentKeyboardLayout;
+  currentKeyboardLayout.InitByCurrentASCIICapableKeyboardLayout();
+  NS_ENSURE_TRUE(mInputSource != currentKeyboardLayout.mInputSource, 0);
+  keyCode = currentKeyboardLayout.ComputeGeckoKeyCode(aNativeKeyCode, aKbType,
+                                                      aCmdIsPressed);
+
+  // However, if keyCode isn't for an alphabet keys or a numeric key, we should
+  // ignore it.  For example, comma key of Thai layout is same as close-square-
+  // bracket key of US layout and an unicode character key of Thai layout is
+  // same as comma key of US layout.  If we return NS_VK_COMMA for latter key,
+  // web application developers cannot distinguish with the former key.
+  return ((keyCode >= NS_VK_A && keyCode <= NS_VK_Z) ||
+          (keyCode >= NS_VK_0 && keyCode <= NS_VK_9)) ? keyCode : 0;
+}
+
 
 #pragma mark -
 
 
 /******************************************************************************
  *
  *  TextInputHandler implementation (static methods)
  *
@@ -1370,60 +1529,53 @@ TextInputHandler::InsertText(NSAttribute
   if (currentKeyEvent && currentKeyEvent->mKeyPressDispatched) {
     return;
   }
 
   nsRefPtr<nsChildView> kungFuDeathGrip(mWidget);
 
   // Dispatch keypress event with char instead of textEvent
   nsKeyEvent keypressEvent(true, NS_KEY_PRESS, mWidget);
-  keypressEvent.charCode  = str.CharAt(0);
-  keypressEvent.keyCode   = 0;
-  keypressEvent.isChar    = true;
 
   // Don't set other modifiers from the current event, because here in
   // -insertText: they've already been taken into account in creating
   // the input string.
 
   // create event for use by plugins
 #ifndef NP_NO_CARBON
   EventRecord carbonEvent;
 #endif // #ifndef NP_NO_CARBON
 
   if (currentKeyEvent) {
     NSEvent* keyEvent = currentKeyEvent->mKeyEvent;
-    nsCocoaUtils::InitInputEvent(keypressEvent, keyEvent);
+    InitKeyEvent(keyEvent, keypressEvent);
 
     // XXX The ASCII characters inputting mode of egbridge (Japanese IME)
     // might send the keyDown event with wrong keyboard layout if other
     // keyboard layouts are already loaded. In that case, the native event
     // doesn't match to this gecko event...
 #ifndef NP_NO_CARBON
     if ([mView pluginEventModel] == NPEventModelCarbon) {
       ConvertCocoaKeyEventToCarbonEvent(keyEvent, carbonEvent, true);
       keypressEvent.pluginEvent = &carbonEvent;
     }
 #endif // #ifndef NP_NO_CARBON
 
     if (currentKeyEvent->mKeyDownHandled) {
       keypressEvent.flags |= NS_EVENT_FLAG_NO_DEFAULT;
     }
-
-    if (!IsPrintableChar(keypressEvent.charCode)) {
-      keypressEvent.keyCode =
-        ComputeGeckoKeyCode([keyEvent keyCode],
-                            [keyEvent charactersIgnoringModifiers]);
-      keypressEvent.charCode = 0;
-    }
   } else {
     nsCocoaUtils::InitInputEvent(keypressEvent, static_cast<NSEvent*>(nsnull));
+    keypressEvent.charCode = str.CharAt(0);
+    keypressEvent.keyCode = 0;
+    keypressEvent.isChar = true;
     // Note that insertText is not called only at key pressing.
     if (!IsPrintableChar(keypressEvent.charCode)) {
       keypressEvent.keyCode =
-        ComputeGeckoKeyCodeFromChar(keypressEvent.charCode);
+        WidgetUtils::ComputeKeyCodeFromChar(keypressEvent.charCode);
       keypressEvent.charCode = 0;
     }
   }
 
   // Remove basic modifiers from keypress event because if they are included,
   // nsPlaintextEditor ignores the event.
   keypressEvent.modifiers &= ~(widget::MODIFIER_CONTROL |
                                widget::MODIFIER_ALT |
@@ -3132,35 +3284,41 @@ PluginTextInputHandler::HandleCarbonPlug
   }
 
   UInt32 macKeyCode;
   status = ::GetEventParameter(aKeyEvent, kEventParamKeyCode,
                                typeUInt32, NULL, sizeof(macKeyCode), NULL,
                                &macKeyCode);
   NS_ENSURE_TRUE(status == noErr, );
 
+  TISInputSourceWrapper currentKeyboardLayout;
+  currentKeyboardLayout.InitByCurrentKeyboardLayout();
+
   EventRef cloneEvent = ::CopyEvent(aKeyEvent);
   for (PRUint32 i = 0; i < numCharCodes; ++i) {
     status = ::SetEventParameter(cloneEvent, kEventParamKeyMacCharCodes,
                                  typeChar, 1, charCodes.Elements() + i);
     NS_ENSURE_TRUE(status == noErr, );
 
     EventRecord eventRec;
     if (::ConvertEventRefToEventRecord(cloneEvent, &eventRec)) {
       nsKeyEvent keydownEvent(true, NS_KEY_DOWN, mWidget);
       nsCocoaUtils::InitInputEvent(keydownEvent, cocoaModifiers);
 
-      PRUint32 keyCode = ComputeGeckoKeyCode(macKeyCode, @"");
+      PRUint32 keyCode =
+        currentKeyboardLayout.ComputeGeckoKeyCode(macKeyCode, ::LMGetKbdType(),
+                                                  keydownEvent.IsMeta());
       PRUint32 charCode(charCodes.ElementAt(i));
 
       keydownEvent.time = PR_IntervalNow();
       keydownEvent.pluginEvent = &eventRec;
       if (IsSpecialGeckoKey(macKeyCode)) {
         keydownEvent.keyCode = keyCode;
       } else {
+        // XXX This is wrong. charCode must be 0 for keydown event.
         keydownEvent.charCode = charCode;
         keydownEvent.isChar   = true;
       }
       DispatchEvent(keydownEvent);
       if (Destroyed()) {
         break;
       }
     }
@@ -3652,166 +3810,16 @@ TextInputHandlerBase::SynthesizeNativeKe
 }
 
 /* static */ bool
 TextInputHandlerBase::IsPrintableChar(PRUnichar aChar)
 {
   return (aChar >= 0x20 && aChar <= 0x7E) || aChar >= 0xA0;
 }
 
-/* static */ PRUint32
-TextInputHandlerBase::ComputeGeckoKeyCodeFromChar(PRUnichar aChar)
-{
-  // We don't support the key code for non-ASCII characters
-  if (aChar > 0x7E) {
-    return 0;
-  }
-
-  // lowercase
-  if (aChar >= 'a' && aChar <= 'z') {
-    return PRUint32(toupper(aChar));
-  }
-  // uppercase
-  if (aChar >= 'A' && aChar <= 'Z') {
-    return PRUint32(aChar);
-  }
-  // numeric
-  if (aChar >= '0' && aChar <= '9') {
-    return PRUint32(aChar - '0' + NS_VK_0);
-  }
-
-  switch (aChar) {
-    case kReturnCharCode:
-    case kEnterCharCode:
-    case '\n':
-      return NS_VK_RETURN;
-    case '{':
-    case '[':
-      return NS_VK_OPEN_BRACKET;
-    case '}':
-    case ']':
-      return NS_VK_CLOSE_BRACKET;
-    case '\'':
-    case '"':
-      return NS_VK_QUOTE;
-
-    case '\\':                  return NS_VK_BACK_SLASH;
-    case ' ':                   return NS_VK_SPACE;
-    case ';':                   return NS_VK_SEMICOLON;
-    case '=':                   return NS_VK_EQUALS;
-    case ',':                   return NS_VK_COMMA;
-    case '.':                   return NS_VK_PERIOD;
-    case '/':                   return NS_VK_SLASH;
-    case '`':                   return NS_VK_BACK_QUOTE;
-    case '\t':                  return NS_VK_TAB;
-    case '-':                   return NS_VK_SUBTRACT;
-    case '+':                   return NS_VK_ADD;
-
-    default:
-      if (!IsPrintableChar(aChar)) {
-        NS_WARNING("ComputeGeckoKeyCodeFromChar() has failed.");
-      }
-      return 0;
-  }
-}
-
-/* static */ PRUint32
-TextInputHandlerBase::ComputeGeckoKeyCode(UInt32 aNativeKeyCode,
-                                          NSString *aCharacters)
-{
-  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
-
-  switch (aNativeKeyCode) {
-    // modifiers. We don't get separate events for these
-    case kEscapeKeyCode:        return NS_VK_ESCAPE;
-    case kRCommandKeyCode:
-    case kCommandKeyCode:       return NS_VK_META;
-    case kRShiftKeyCode:
-    case kShiftKeyCode:         return NS_VK_SHIFT;
-    case kCapsLockKeyCode:      return NS_VK_CAPS_LOCK;
-    case kRControlKeyCode:
-    case kControlKeyCode:       return NS_VK_CONTROL;
-    case kROptionKeyCode:
-    case kOptionkeyCode:        return NS_VK_ALT;
-    case kClearKeyCode:         return NS_VK_CLEAR;
-
-    // function keys
-    case kF1KeyCode:            return NS_VK_F1;
-    case kF2KeyCode:            return NS_VK_F2;
-    case kF3KeyCode:            return NS_VK_F3;
-    case kF4KeyCode:            return NS_VK_F4;
-    case kF5KeyCode:            return NS_VK_F5;
-    case kF6KeyCode:            return NS_VK_F6;
-    case kF7KeyCode:            return NS_VK_F7;
-    case kF8KeyCode:            return NS_VK_F8;
-    case kF9KeyCode:            return NS_VK_F9;
-    case kF10KeyCode:           return NS_VK_F10;
-    case kF11KeyCode:           return NS_VK_F11;
-    case kF12KeyCode:           return NS_VK_F12;
-    // case kF13KeyCode:           return NS_VK_F13;  // clash with the 3 below
-    // case kF14KeyCode:           return NS_VK_F14;
-    // case kF15KeyCode:           return NS_VK_F15;
-    case kPauseKeyCode:         return NS_VK_PAUSE;
-    case kScrollLockKeyCode:    return NS_VK_SCROLL_LOCK;
-    case kPrintScreenKeyCode:   return NS_VK_PRINTSCREEN;
-
-    // keypad
-    case kKeypad0KeyCode:       return NS_VK_NUMPAD0;
-    case kKeypad1KeyCode:       return NS_VK_NUMPAD1;
-    case kKeypad2KeyCode:       return NS_VK_NUMPAD2;
-    case kKeypad3KeyCode:       return NS_VK_NUMPAD3;
-    case kKeypad4KeyCode:       return NS_VK_NUMPAD4;
-    case kKeypad5KeyCode:       return NS_VK_NUMPAD5;
-    case kKeypad6KeyCode:       return NS_VK_NUMPAD6;
-    case kKeypad7KeyCode:       return NS_VK_NUMPAD7;
-    case kKeypad8KeyCode:       return NS_VK_NUMPAD8;
-    case kKeypad9KeyCode:       return NS_VK_NUMPAD9;
-
-    case kKeypadMultiplyKeyCode:  return NS_VK_MULTIPLY;
-    case kKeypadAddKeyCode:       return NS_VK_ADD;
-    case kKeypadSubtractKeyCode:  return NS_VK_SUBTRACT;
-    case kKeypadDecimalKeyCode:   return NS_VK_DECIMAL;
-    case kKeypadDivideKeyCode:    return NS_VK_DIVIDE;
-
-    // these may clash with forward delete and help
-    case kInsertKeyCode:        return NS_VK_INSERT;
-    case kDeleteKeyCode:        return NS_VK_DELETE;
-
-    case kBackspaceKeyCode:     return NS_VK_BACK;
-    case kTabKeyCode:           return NS_VK_TAB;
-    case kHomeKeyCode:          return NS_VK_HOME;
-    case kEndKeyCode:           return NS_VK_END;
-    case kPageUpKeyCode:        return NS_VK_PAGE_UP;
-    case kPageDownKeyCode:      return NS_VK_PAGE_DOWN;
-    case kLeftArrowKeyCode:     return NS_VK_LEFT;
-    case kRightArrowKeyCode:    return NS_VK_RIGHT;
-    case kUpArrowKeyCode:       return NS_VK_UP;
-    case kDownArrowKeyCode:     return NS_VK_DOWN;
-    case kVK_ANSI_1:            return NS_VK_1;
-    case kVK_ANSI_2:            return NS_VK_2;
-    case kVK_ANSI_3:            return NS_VK_3;
-    case kVK_ANSI_4:            return NS_VK_4;
-    case kVK_ANSI_5:            return NS_VK_5;
-    case kVK_ANSI_6:            return NS_VK_6;
-    case kVK_ANSI_7:            return NS_VK_7;
-    case kVK_ANSI_8:            return NS_VK_8;
-    case kVK_ANSI_9:            return NS_VK_9;
-    case kVK_ANSI_0:            return NS_VK_0;
-
-    default:
-      // if we haven't gotten the key code already, look at the char code
-      if ([aCharacters length] > 0) {
-        return ComputeGeckoKeyCodeFromChar([aCharacters characterAtIndex:0]);
-      }
-  }
-
-  return 0;
-
-  NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0);
-}
 
 /* static */ bool
 TextInputHandlerBase::IsSpecialGeckoKey(UInt32 aNativeKeyCode)
 {
   // this table is used to determine which keys are special and should not
   // generate a charCode
   switch (aNativeKeyCode) {
     // modifiers - we don't get separate events for these yet
@@ -3838,22 +3846,29 @@ TextInputHandlerBase::IsSpecialGeckoKey(
     case kF8KeyCode:
     case kF9KeyCode:
     case kF10KeyCode:
     case kF11KeyCode:
     case kF12KeyCode:
     case kPauseKeyCode:
     case kScrollLockKeyCode:
     case kPrintScreenKeyCode:
+    case kF16KeyCode:
+    case kF17KeyCode:
+    case kF18KeyCode:
+    case kF19KeyCode:
 
     case kInsertKeyCode:
     case kDeleteKeyCode:
     case kTabKeyCode:
     case kBackspaceKeyCode:
 
+    case kJapanese_Eisu:
+    case kJapanese_Kana:
+
     case kHomeKeyCode:
     case kEndKeyCode:
     case kPageUpKeyCode:
     case kPageDownKeyCode:
     case kLeftArrowKeyCode:
     case kRightArrowKeyCode:
     case kUpArrowKeyCode:
     case kDownArrowKeyCode:
--- a/widget/cocoa/nsAppShell.mm
+++ b/widget/cocoa/nsAppShell.mm
@@ -53,16 +53,17 @@
 #include "nsIRollupListener.h"
 #include "nsIWidget.h"
 #include "nsThreadUtils.h"
 #include "nsIWindowMediator.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIWebBrowserChrome.h"
 #include "nsObjCExceptions.h"
+#include "nsCocoaFeatures.h"
 #include "nsCocoaUtils.h"
 #include "nsChildView.h"
 #include "nsToolkit.h"
 #include "TextInputHandler.h"
 #include "mozilla/HangMonitor.h"
 #include "sampler.h"
 
 #include "npapi.h"
@@ -366,17 +367,17 @@ nsAppShell::Init()
     nsToolkit::SwizzleMethods([NSApplication class], @selector(endModalSession:),
                               @selector(nsAppShell_NSApplication_endModalSession:));
     // We should only replace the original terminate: method if we're not
     // running in a Cocoa embedder (like Camino).  See bug 604901.
     if (!mRunningCocoaEmbedded) {
       nsToolkit::SwizzleMethods([NSApplication class], @selector(terminate:),
                                 @selector(nsAppShell_NSApplication_terminate:));
     }
-    if (!nsToolkit::OnSnowLeopardOrLater()) {
+    if (!nsCocoaFeatures::OnSnowLeopardOrLater()) {
       dlopen("/System/Library/Frameworks/Carbon.framework/Frameworks/Print.framework/Versions/Current/Plugins/PrintCocoaUI.bundle/Contents/MacOS/PrintCocoaUI",
              RTLD_LAZY);
       Class PDEPluginCallbackClass = ::NSClassFromString(@"PDEPluginCallback");
       nsresult rv1 = nsToolkit::SwizzleMethods(PDEPluginCallbackClass, @selector(initWithPrintWindowController:),
                                                @selector(nsAppShell_PDEPluginCallback_initWithPrintWindowController:));
       if (NS_SUCCEEDED(rv1)) {
         nsToolkit::SwizzleMethods(PDEPluginCallbackClass, @selector(dealloc),
                                   @selector(nsAppShell_PDEPluginCallback_dealloc));
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -69,16 +69,17 @@
 #include "nsIDOMSimpleGestureEvent.h"
 #include "nsNPAPIPluginInstance.h"
 #include "nsThemeConstants.h"
 
 #include "nsDragService.h"
 #include "nsClipboard.h"
 #include "nsCursorManager.h"
 #include "nsWindowMap.h"
+#include "nsCocoaFeatures.h"
 #include "nsCocoaUtils.h"
 #include "nsMenuUtilsX.h"
 #include "nsMenuBarX.h"
 #ifdef __LP64__
 #include "ComplexTextInputPanel.h"
 #endif
 
 #include "gfxContext.h"
@@ -301,17 +302,17 @@ nsresult nsChildView::Create(nsIWidget *
   // (see bug 559075).
   nsAutoreleasePool localPool;
 
   // See NSView (MethodSwizzling) below.
   if (!gChildViewMethodsSwizzled) {
     nsToolkit::SwizzleMethods([NSView class], @selector(mouseDownCanMoveWindow),
                               @selector(nsChildView_NSView_mouseDownCanMoveWindow));
 #ifdef __LP64__
-    if (nsToolkit::OnLionOrLater()) {
+    if (nsCocoaFeatures::OnLionOrLater()) {
       nsToolkit::SwizzleMethods([NSEvent class], @selector(addLocalMonitorForEventsMatchingMask:handler:),
                                 @selector(nsChildView_NSEvent_addLocalMonitorForEventsMatchingMask:handler:),
                                 true);
       nsToolkit::SwizzleMethods([NSEvent class], @selector(removeMonitor:),
                                 @selector(nsChildView_NSEvent_removeMonitor:), true);
     }
 #endif
 #ifndef NP_NO_CARBON
@@ -3048,17 +3049,17 @@ NSEvent* gLastDragMouseDownEvent = nil;
 // now swipe animation is unsupported (e.g. no bounces).  This method is
 // partly based on Apple sample code available at
 // http://developer.apple.com/library/mac/#releasenotes/Cocoa/AppKit.html
 // (under Fluid Swipe Tracking API).
 #ifdef __LP64__
 - (void)maybeTrackScrollEventAsSwipe:(NSEvent *)anEvent
                       scrollOverflow:(PRInt32)overflow
 {
-  if (!nsToolkit::OnLionOrLater()) {
+  if (!nsCocoaFeatures::OnLionOrLater()) {
     return;
   }
   // This method checks whether the AppleEnableSwipeNavigateWithScrolls global
   // preference is set.  If it isn't, fluid swipe tracking is disabled, and a
   // horizontal two-finger gesture is always a scroll (even in Safari).  This
   // preference can't (currently) be set from the Preferences UI -- only using
   // 'defaults write'.
   if (![NSEvent isSwipeTrackingFromScrollEventsEnabled]) {
@@ -3955,17 +3956,17 @@ NSEvent* gLastDragMouseDownEvent = nil;
   NSPoint localPoint = [self convertPoint:locationInWindow fromView:nil];
   outGeckoEvent->refPoint.x = static_cast<nscoord>(localPoint.x);
   outGeckoEvent->refPoint.y = static_cast<nscoord>(localPoint.y);
 
   nsMouseEvent_base* mouseEvent =
     static_cast<nsMouseEvent_base*>(outGeckoEvent);
   mouseEvent->buttons = 0;
   NSUInteger mouseButtons =
-    nsToolkit::OnSnowLeopardOrLater() ? [NSEvent pressedMouseButtons] : 0;
+    nsCocoaFeatures::OnSnowLeopardOrLater() ? [NSEvent pressedMouseButtons] : 0;
 
   if (mouseButtons & 0x01) {
     mouseEvent->buttons |= nsMouseEvent::eLeftButtonFlag;
   }
   if (mouseButtons & 0x02) {
     mouseEvent->buttons |= nsMouseEvent::eRightButtonFlag;
   }
   if (mouseButtons & 0x04) {
--- a/widget/cocoa/nsCocoaFeatures.h
+++ b/widget/cocoa/nsCocoaFeatures.h
@@ -39,17 +39,16 @@
 #ifndef nsCocoaFeatures_h_
 #define nsCocoaFeatures_h_
 
 #include "prtypes.h"
 
 class nsCocoaFeatures {
 public:
   static PRInt32 OSXVersion();
-
   static bool OnSnowLeopardOrLater();
   static bool OnLionOrLater();
+  static bool SupportCoreAnimationPlugins();
 
-  static bool SupportCoreAnimationPlugins();
 private:
   static PRInt32 mOSXVersion;
 };
 #endif // nsCocoaFeatures_h_
--- a/widget/cocoa/nsCocoaFeatures.mm
+++ b/widget/cocoa/nsCocoaFeatures.mm
@@ -40,36 +40,41 @@
 #define MAC_OS_X_VERSION_10_5_HEX 0x00001050
 #define MAC_OS_X_VERSION_10_6_HEX 0x00001060
 #define MAC_OS_X_VERSION_10_7_HEX 0x00001070
 
 // This API will not work for OS X 10.10, see Gestalt.h.
 
 #include "nsCocoaFeatures.h"
 #include "nsDebug.h"
+#include "nsObjCExceptions.h"
 
 #import <Cocoa/Cocoa.h>
 
 PRInt32 nsCocoaFeatures::mOSXVersion = 0;
 
 /* static */ PRInt32
 nsCocoaFeatures::OSXVersion()
 {
+    NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
+
     if (!mOSXVersion) {
         // minor version is not accurate, use gestaltSystemVersionMajor, 
         // gestaltSystemVersionMinor, gestaltSystemVersionBugFix for these
         OSErr err = ::Gestalt(gestaltSystemVersion, reinterpret_cast<SInt32*>(&mOSXVersion));
         if (err != noErr) {
             // This should probably be changed when our minimum version changes
             NS_ERROR("Couldn't determine OS X version, assuming 10.5");
             mOSXVersion = MAC_OS_X_VERSION_10_5_HEX;
         }
         mOSXVersion &= MAC_OS_X_VERSION_MASK;
     }
     return mOSXVersion;
+
+    NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0);
 }
 
 /* static */ bool
 nsCocoaFeatures::SupportCoreAnimationPlugins()
 {
     // Disallow Core Animation on 10.5 because of crashes.
     // See Bug 711564.
     return (OSXVersion() >= MAC_OS_X_VERSION_10_6_HEX);
--- a/widget/cocoa/nsCocoaUtils.mm
+++ b/widget/cocoa/nsCocoaUtils.mm
@@ -479,17 +479,17 @@ nsCocoaUtils::InitInputEvent(nsInputEven
 }
 
 // static
 NSUInteger
 nsCocoaUtils::GetCurrentModifiers()
 {
   // NOTE: [[NSApp currentEvent] modifiers] isn't useful because it sometime 0
   //       and we cannot check if it's actual state.
-  if (nsToolkit::OnSnowLeopardOrLater()) {
+  if (nsCocoaFeatures::OnSnowLeopardOrLater()) {
     // XXX [NSEvent modifierFlags] returns "current" modifier state, so,
     //     it's not event-queue-synchronized.  GetCurrentEventKeyModifiers()
     //     might be better, but it's Carbon API, we shouldn't use it as far as
     //     possible.
     return [NSEvent modifierFlags];
   }
 
   // If [NSEvent modifierFlags] isn't available, use carbon API.
--- a/widget/cocoa/nsCocoaWindow.mm
+++ b/widget/cocoa/nsCocoaWindow.mm
@@ -56,16 +56,17 @@
 #include "nsPIDOMWindow.h"
 #include "nsIDOMElement.h"
 #include "nsThreadUtils.h"
 #include "nsMenuBarX.h"
 #include "nsMenuUtilsX.h"
 #include "nsStyleConsts.h"
 #include "nsNativeThemeColors.h"
 #include "nsChildView.h"
+#include "nsCocoaFeatures.h"
 
 #include "gfxPlatform.h"
 #include "qcms.h"
 
 #include "mozilla/Preferences.h"
 
 namespace mozilla {
 namespace layers {
@@ -2379,17 +2380,17 @@ static const NSString* kStateShowsToolba
   // text at all is selected.  (This always happens when accessibility is off.
   // It doesn't happen in Firefox releases because Apple has (on OS X 10.7)
   // special-cased the handling of apps whose CFBundleIdentifier is
   // org.mozilla.firefox.)
   //
   // We work around this problem by only returning AXChildren that are
   // mozAccessible object or are one of the titlebar's buttons (which
   // instantiate subclasses of NSButtonCell).
-  if (nsToolkit::OnLionOrLater() && [retval isKindOfClass:[NSArray class]] &&
+  if (nsCocoaFeatures::OnLionOrLater() && [retval isKindOfClass:[NSArray class]] &&
       [attribute isEqualToString:@"AXChildren"]) {
     NSMutableArray *holder = [NSMutableArray arrayWithCapacity:10];
     [holder addObjectsFromArray:(NSArray *)retval];
     NSUInteger count = [holder count];
     for (NSInteger i = count - 1; i >= 0; --i) {
       id item = [holder objectAtIndex:i];
       // Remove anything from holder that isn't one of the titlebar's buttons
       // (which instantiate subclasses of NSButtonCell) or a mozAccessible
@@ -2684,17 +2685,17 @@ DrawNativeTitlebar(CGContextRef aContext
             @"kCUIWidgetWindowFrame", @"widget",
             @"regularwin", @"windowtype",
             (aIsMain ? @"normal" : @"inactive"), @"state",
             [NSNumber numberWithInt:unifiedHeight], @"kCUIWindowFrameUnifiedTitleBarHeightKey",
             [NSNumber numberWithBool:YES], @"kCUIWindowFrameDrawTitleSeparatorKey",
             nil],
           nil);
 
-  if (nsToolkit::OnLionOrLater()) {
+  if (nsCocoaFeatures::OnLionOrLater()) {
     // On Lion the call to CUIDraw doesn't draw the top pixel strip at some
     // window widths. We don't want to have a flickering transparent line, so
     // we overdraw it.
     CGContextSetRGBFillColor(aContext, 0.95, 0.95, 0.95, 1);
     CGContextFillRect(aContext, CGRectMake(0, CGRectGetMaxY(aTitlebarRect) - 1,
                                            aTitlebarRect.size.width, 1));
   }
 }
--- a/widget/cocoa/nsFilePicker.mm
+++ b/widget/cocoa/nsFilePicker.mm
@@ -48,18 +48,18 @@
 #include "nsReadableUtils.h"
 #include "nsNetUtil.h"
 #include "nsIComponentManager.h"
 #include "nsILocalFile.h"
 #include "nsILocalFileMac.h"
 #include "nsIURL.h"
 #include "nsArrayEnumerator.h"
 #include "nsIStringBundle.h"
+#include "nsCocoaFeatures.h"
 #include "nsCocoaUtils.h"
-#include "nsToolkit.h"
 #include "mozilla/Preferences.h"
 
 using namespace mozilla;
 
 const float kAccessoryViewPadding = 5;
 const int   kSaveTypeControlTag = 1;
 
 static bool gCallSecretHiddenFileAPI = false;
@@ -351,17 +351,17 @@ nsFilePicker::GetLocalFiles(const nsStri
       [(NSString *)[filters objectAtIndex:0] isEqualToString:@"app"]) {
     theDir = @"/Applications/";
   }
 
   // On 10.6+, we let users change the filters. Unfortunately, some methods
   // are not available on 10.5 and without using them it happens to be buggy.
   int result;
   nsCocoaUtils::PrepareForNativeAppModalDialog();
-  if (mFilters.Length() > 1 && nsToolkit::OnSnowLeopardOrLater()) {
+  if (mFilters.Length() > 1 && nsCocoaFeatures::OnSnowLeopardOrLater()) {
     // [NSURL initWithString:] (below) throws an exception if URLString is nil.
     if (!theDir) {
       theDir = @"";
     }
 
     NSPopUpButtonObserver* observer = [[NSPopUpButtonObserver alloc] init];
 
     NSView* accessoryView = GetAccessoryView();
--- a/widget/cocoa/nsLookAndFeel.mm
+++ b/widget/cocoa/nsLookAndFeel.mm
@@ -32,16 +32,17 @@
  * 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 "nsLookAndFeel.h"
+#include "nsCocoaFeatures.h"
 #include "nsObjCExceptions.h"
 #include "nsIServiceManager.h"
 #include "nsNativeThemeColors.h"
 #include "nsStyleConsts.h"
 #include "gfxFont.h"
 
 #import <Cocoa/Cocoa.h>
 
@@ -255,17 +256,17 @@ nsLookAndFeel::NativeGetColor(ColorID aI
       break;
     case eColorID__moz_mac_focusring:
       aColor = GetColorFromNSColor([NSColor keyboardFocusIndicatorColor]);
       break;
     case eColorID__moz_mac_menushadow:
       aColor = NS_RGB(0xA3,0xA3,0xA3);
       break;          
     case eColorID__moz_mac_menutextdisable:
-      aColor = nsToolkit::OnSnowLeopardOrLater() ?
+      aColor = nsCocoaFeatures::OnSnowLeopardOrLater() ?
                  NS_RGB(0x88,0x88,0x88) : NS_RGB(0x98,0x98,0x98);
       break;      
     case eColorID__moz_mac_menutextselect:
       aColor = GetColorFromNSColor([NSColor selectedMenuItemTextColor]);
       break;      
     case eColorID__moz_mac_disabledtoolbartext:
       aColor = NS_RGB(0x3F,0x3F,0x3F);
       break;
@@ -344,17 +345,17 @@ nsLookAndFeel::GetIntImpl(IntID aID, PRI
     case eIntID_SkipNavigatingDisabledMenuItem:
       aResult = 1;
       break;
     case eIntID_DragThresholdX:
     case eIntID_DragThresholdY:
       aResult = 4;
       break;
     case eIntID_ScrollArrowStyle:
-      if (nsToolkit::OnLionOrLater()) {
+      if (nsCocoaFeatures::OnLionOrLater()) {
         // OS X Lion's scrollbars have no arrows
         aResult = eScrollArrow_None;
       } else {
         NSString *buttonPlacement = [[NSUserDefaults standardUserDefaults] objectForKey:@"AppleScrollBarVariant"];
         if ([buttonPlacement isEqualToString:@"Single"]) {
           aResult = eScrollArrowStyle_Single;
         } else if ([buttonPlacement isEqualToString:@"DoubleMin"]) {
           aResult = eScrollArrowStyle_BothAtTop;
@@ -391,17 +392,17 @@ nsLookAndFeel::GetIntImpl(IntID aID, PRI
     case eIntID_WindowsThemeIdentifier:
       aResult = 0;
       res = NS_ERROR_NOT_IMPLEMENTED;
       break;
     case eIntID_MacGraphiteTheme:
       aResult = [NSColor currentControlTint] == NSGraphiteControlTint;
       break;
     case eIntID_MacLionTheme:
-      aResult = nsToolkit::OnLionOrLater();
+      aResult = nsCocoaFeatures::OnLionOrLater();
       break;
     case eIntID_TabFocusModel:
     {
       // we should probably cache this
       CFPropertyListRef fullKeyboardAccessProperty;
       fullKeyboardAccessProperty = ::CFPreferencesCopyValue(CFSTR("AppleKeyboardUIMode"),
                                                             kCFPreferencesAnyApplication,
                                                             kCFPreferencesCurrentUser,
--- a/widget/cocoa/nsMenuBarX.mm
+++ b/widget/cocoa/nsMenuBarX.mm
@@ -38,19 +38,19 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include <objc/objc-runtime.h>
 
 #include "nsMenuBarX.h"
 #include "nsMenuX.h"
 #include "nsMenuItemX.h"
 #include "nsMenuUtilsX.h"
+#include "nsCocoaFeatures.h"
 #include "nsCocoaUtils.h"
 #include "nsCocoaWindow.h"
-#include "nsToolkit.h"
 #include "nsChildView.h"
 
 #include "nsCOMPtr.h"
 #include "nsString.h"
 #include "nsGkAtoms.h"
 #include "nsGUIEvent.h"
 #include "nsObjCExceptions.h"
 #include "nsHashtable.h"
@@ -360,17 +360,17 @@ nsMenuX* nsMenuBarX::GetXULHelpMenu()
 }
 
 // On SnowLeopard and later we must tell the OS which is our Help menu.
 // Otherwise it will only add Spotlight for Help (the Search item) to our
 // Help menu if its label/title is "Help" -- i.e. if the menu is in English.
 // This resolves bugs 489196 and 539317.
 void nsMenuBarX::SetSystemHelpMenu()
 {
-  if (!nsToolkit::OnSnowLeopardOrLater())
+  if (!nsCocoaFeatures::OnSnowLeopardOrLater())
     return;
   nsMenuX* xulHelpMenu = GetXULHelpMenu();
   if (xulHelpMenu) {
     NSMenu* helpMenu = (NSMenu*)xulHelpMenu->NativeData();
     if (helpMenu)
       [NSApp setHelpMenu:helpMenu];
   }
 }
--- a/widget/cocoa/nsMenuX.mm
+++ b/widget/cocoa/nsMenuX.mm
@@ -42,16 +42,17 @@
 #include "nsMenuItemX.h"
 #include "nsMenuUtilsX.h"
 #include "nsMenuItemIconX.h"
 #include "nsStandaloneNativeMenu.h"
 
 #include "nsObjCExceptions.h"
 
 #include "nsToolkit.h"
+#include "nsCocoaFeatures.h"
 #include "nsCocoaUtils.h"
 #include "nsCOMPtr.h"
 #include "prinrval.h"
 #include "nsString.h"
 #include "nsReadableUtils.h"
 #include "nsUnicharUtils.h"
 #include "plstr.h"
 #include "nsGkAtoms.h"
@@ -134,17 +135,17 @@ nsMenuX::nsMenuX()
     nsToolkit::SwizzleMethods([NSMenu class], @selector(_addItem:toTable:),
                               @selector(nsMenuX_NSMenu_addItem:toTable:), true);
     nsToolkit::SwizzleMethods([NSMenu class], @selector(_removeItem:fromTable:),
                               @selector(nsMenuX_NSMenu_removeItem:fromTable:), true);
     // On SnowLeopard the Shortcut framework (which contains the
     // SCTGRLIndex class) is loaded on demand, whenever the user first opens
     // a menu (which normally hasn't happened yet).  So we need to load it
     // here explicitly.
-    if (nsToolkit::OnSnowLeopardOrLater())
+    if (nsCocoaFeatures::OnSnowLeopardOrLater())
       dlopen("/System/Library/PrivateFrameworks/Shortcut.framework/Shortcut", RTLD_LAZY);
     Class SCTGRLIndexClass = ::NSClassFromString(@"SCTGRLIndex");
     nsToolkit::SwizzleMethods(SCTGRLIndexClass, @selector(indexMenuBarDynamically),
                               @selector(nsMenuX_SCTGRLIndex_indexMenuBarDynamically));
 
     gMenuMethodsSwizzled = true;
   }
 
--- a/widget/cocoa/nsNativeThemeCocoa.mm
+++ b/widget/cocoa/nsNativeThemeCocoa.mm
@@ -51,17 +51,17 @@
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "nsIFrame.h"
 #include "nsIAtom.h"
 #include "nsEventStates.h"
 #include "nsINameSpaceManager.h"
 #include "nsPresContext.h"
 #include "nsGkAtoms.h"
-#include "nsToolkit.h"
+#include "nsCocoaFeatures.h"
 #include "nsCocoaWindow.h"
 #include "nsNativeThemeColors.h"
 #include "nsIScrollableFrame.h"
 #include "nsIDOMHTMLProgressElement.h"
 
 #include "gfxContext.h"
 #include "gfxQuartzSurface.h"
 #include "gfxQuartzNativeDrawing.h"
@@ -1620,17 +1620,17 @@ nsNativeThemeCocoa::GetScrollbarDrawInfo
 
   aTdi.trackInfo.scrollbar.pressState = 0;
 
   // Only go get these scrollbar button states if we need it. For example,
   // there's no reason to look up scrollbar button states when we're only
   // creating a TrackDrawInfo to determine the size of the thumb. There's
   // also no reason to do this on Lion or later, whose scrollbars have no
   // arrow buttons.
-  if (aShouldGetButtonStates && !nsToolkit::OnLionOrLater()) {
+  if (aShouldGetButtonStates && !nsCocoaFeatures::OnLionOrLater()) {
     nsEventStates buttonStates[4];
     GetScrollbarPressStates(aFrame, buttonStates);
     NSString *buttonPlacement = [[NSUserDefaults standardUserDefaults] objectForKey:@"AppleScrollBarVariant"];
     // It seems that unless all four buttons are showing, kThemeTopOutsideArrowPressed is the correct constant for
     // the up scrollbar button.
     if ([buttonPlacement isEqualToString:@"DoubleBoth"]) {
       aTdi.trackInfo.scrollbar.pressState = ConvertToPressState(buttonStates[0], kThemeTopOutsideArrowPressed) |
                                             ConvertToPressState(buttonStates[1], kThemeTopInsideArrowPressed) |
@@ -2320,17 +2320,17 @@ nsNativeThemeCocoa::GetWidgetBorder(nsDe
       aResult->SizeTo(frameOutset, frameOutset, frameOutset, frameOutset);
       break;
     }
 
     case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
     case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
     {
       // On Lion and later, scrollbars have no arrows.
-      if (!nsToolkit::OnLionOrLater()) {
+      if (!nsCocoaFeatures::OnLionOrLater()) {
         // There's only an endcap to worry about when both arrows are on the bottom
         NSString *buttonPlacement = [[NSUserDefaults standardUserDefaults] objectForKey:@"AppleScrollBarVariant"];
         if (!buttonPlacement || [buttonPlacement isEqualToString:@"DoubleMax"]) {
           bool isHorizontal = (aWidgetType == NS_THEME_SCROLLBAR_TRACK_HORIZONTAL);
 
           nsIFrame *scrollbarFrame = GetParentScrollbarFrame(aFrame);
           if (!scrollbarFrame) return NS_ERROR_FAILURE;
           bool isSmall = (scrollbarFrame->GetStyleDisplay()->mAppearance == NS_THEME_SCROLLBAR_SMALL);
--- a/widget/cocoa/nsNativeThemeColors.h
+++ b/widget/cocoa/nsNativeThemeColors.h
@@ -33,17 +33,17 @@
  * 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 nsNativeThemeColors_h_
 #define nsNativeThemeColors_h_
 
-#include "nsToolkit.h"
+#include "nsCocoaFeatures.h"
 #import <Cocoa/Cocoa.h>
 
 extern "C" {
   typedef CFTypeRef CUIRendererRef;
   void CUIDraw(CUIRendererRef r, CGRect rect, CGContextRef ctx, CFDictionaryRef options, CFDictionaryRef* result);
 }
 
 @interface NSWindow(CoreUIRendererPrivate)
@@ -78,20 +78,20 @@ static const int sLionThemeColors[][2] =
   { 0xD0, 0xF0 }, // top separator line
   { 0xB2, 0xE1 }, // fill color
   { 0x59, 0x87 }, // bottom separator line
 };
 
 __attribute__((unused))
 static int NativeGreyColorAsInt(ColorName name, BOOL isMain)
 {
-  if (nsToolkit::OnLionOrLater())
+  if (nsCocoaFeatures::OnLionOrLater())
     return sLionThemeColors[name][isMain ? 0 : 1];
 
-  if (nsToolkit::OnSnowLeopardOrLater())
+  if (nsCocoaFeatures::OnSnowLeopardOrLater())
     return sSnowLeopardThemeColors[name][isMain ? 0 : 1];
 
   return sLeopardThemeColors[name][isMain ? 0 : 1];
 }
 
 __attribute__((unused))
 static float NativeGreyColorAsFloat(ColorName name, BOOL isMain)
 {
--- a/widget/cocoa/nsToolkit.h
+++ b/widget/cocoa/nsToolkit.h
@@ -35,59 +35,52 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsToolkit_h_
 #define nsToolkit_h_
 
 #include "nscore.h"
+#include "nsCocoaFeatures.h"
 
 #import <Carbon/Carbon.h>
 #import <Cocoa/Cocoa.h>
 #import <objc/Object.h>
 #import <IOKit/IOKitLib.h>
 
-#define MAC_OS_X_VERSION_10_5_HEX 0x00001050
-#define MAC_OS_X_VERSION_10_6_HEX 0x00001060
-#define MAC_OS_X_VERSION_10_7_HEX 0x00001070
-
 class nsToolkit
 {
 public:
                      nsToolkit();
   virtual            ~nsToolkit();
 
-  static nsToolkit* GetToolkit();
+  static nsToolkit*  GetToolkit();
 
   static void Shutdown() {
     delete gToolkit;
     gToolkit = nsnull;
   }
 
-  static PRInt32     OSXVersion();
-  static bool        OnSnowLeopardOrLater();
-  static bool        OnLionOrLater();
-
   static void        PostSleepWakeNotification(const char* aNotification);
 
   static nsresult    SwizzleMethods(Class aClass, SEL orgMethod, SEL posedMethod,
                                     bool classMethods = false);
 
 protected:
 
   nsresult           RegisterForSleepWakeNotifcations();
   void               RemoveSleepWakeNotifcations();
 
   void               RegisterForAllProcessMouseEvents();
   void               UnregisterAllProcessMouseEventHandlers();
 
 protected:
 
-  static nsToolkit* gToolkit;
+  static nsToolkit*  gToolkit;
 
   CFRunLoopSourceRef mSleepWakeNotificationRLS;
   io_object_t        mPowerNotifier;
 
   CFMachPortRef      mEventTapPort;
   CFRunLoopSourceRef mEventTapRLS;
 };
 
--- a/widget/cocoa/nsToolkit.mm
+++ b/widget/cocoa/nsToolkit.mm
@@ -309,44 +309,16 @@ nsToolkit* nsToolkit::GetToolkit()
     gToolkit = new nsToolkit();
   }
 
   return gToolkit;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(nsnull);
 }
 
-PRInt32 nsToolkit::OSXVersion()
-{
-  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
-
-  static PRInt32 gOSXVersion = 0x0;
-  if (gOSXVersion == 0x0) {
-    OSErr err = ::Gestalt(gestaltSystemVersion, (SInt32*)&gOSXVersion);
-    if (err != noErr) {
-      // This should probably be changed when our minimum version changes
-      NS_ERROR("Couldn't determine OS X version, assuming 10.5");
-      gOSXVersion = MAC_OS_X_VERSION_10_5_HEX;
-    }
-  }
-  return gOSXVersion;
-
-  NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0);
-}
-
-bool nsToolkit::OnSnowLeopardOrLater()
-{
-  return (OSXVersion() >= MAC_OS_X_VERSION_10_6_HEX);
-}
-
-bool nsToolkit::OnLionOrLater()
-{
-  return (OSXVersion() >= MAC_OS_X_VERSION_10_7_HEX);
-}
-
 // An alternative to [NSObject poseAsClass:] that isn't deprecated on OS X
 // Leopard and is available to 64-bit binaries on Leopard and above.  Based on
 // ideas and code from http://www.cocoadev.com/index.pl?MethodSwizzling.
 // Since the Method type becomes an opaque type as of Objective-C 2.0, we'll
 // have to switch to using accessor methods like method_exchangeImplementations()
 // when we build 64-bit binaries that use Objective-C 2.0 (on and for Leopard
 // and above).  But these accessor methods aren't available in Objective-C 1
 // (or on Tiger).  So we need to access Method's members directly for (Tiger-
@@ -511,17 +483,17 @@ OSStatus Hooked_InstallEventLoopIdleTime
 // Try to hook (or "subclass") the dynamically bound functions specified in
 // gHookedFunctions.  We don't hook these functions at their "original"
 // addresses, so we can only "subclass" calls to them from modules other than
 // the one in which they're defined.  Of course, this only works for globally
 // accessible functions.
 void HookImportedFunctions()
 {
   // We currently only need to do anything on Tiger or Leopard.
-  if (nsToolkit::OnSnowLeopardOrLater())
+  if (nsCocoaFeatures::OnSnowLeopardOrLater())
     return;
 
   // _dyld_register_func_for_add_image() makes the dynamic linker runtime call
   // ScanImportedFunctions() "once for each of the images that are currently
   // loaded into the program" (including the main image, i.e. firefox-bin).
   // When a new image is added (e.g. a plugin), ScanImportedFunctions() is
   // called again with data for that image.
   //
--- a/widget/gtk2/nsGtkKeyUtils.cpp
+++ b/widget/gtk2/nsGtkKeyUtils.cpp
@@ -47,16 +47,17 @@
 #define GDK_Sleep 0x1008ff2f
 #endif
 
 #include <gdk/gdk.h>
 #ifdef MOZ_X11
 #include <gdk/gdkx.h>
 #endif /* MOZ_X11 */
 #include "nsGUIEvent.h"
+#include "WidgetUtils.h"
 #include "keysym2ucs.h"
 
 #ifdef PR_LOGGING
 PRLogModuleInfo* gKeymapWrapperLog = nsnull;
 #endif // PR_LOGGING
 
 #include "mozilla/Util.h"
 
@@ -83,18 +84,41 @@ static const KeyPair kKeyPairs[] = {
     { NS_VK_CLEAR,      GDK_Clear },
     { NS_VK_RETURN,     GDK_Return },
     { NS_VK_SHIFT,      GDK_Shift_L },
     { NS_VK_SHIFT,      GDK_Shift_R },
     { NS_VK_CONTROL,    GDK_Control_L },
     { NS_VK_CONTROL,    GDK_Control_R },
     { NS_VK_ALT,        GDK_Alt_L },
     { NS_VK_ALT,        GDK_Alt_R },
-    { NS_VK_META,       GDK_Meta_L },
-    { NS_VK_META,       GDK_Meta_R },
+
+    // NS_VK_META is used for Command key of Mac.  It's a modifier key for
+    // shortcut keys like Ctrl key on GTK or Windows.  So, it's really different
+    // from GTK's META key, we shouldn't use it on GTK.
+    // { NS_VK_META,       GDK_Meta_L },
+    // { NS_VK_META,       GDK_Meta_R },
+
+    // Assume that Super or Hyper is always mapped to physical Win key.
+    { NS_VK_WIN,        GDK_Super_L },
+    { NS_VK_WIN,        GDK_Super_R },
+    { NS_VK_WIN,        GDK_Hyper_L },
+    { NS_VK_WIN,        GDK_Hyper_R },
+
+    // GTK's AltGraph key is similar to Mac's Option (Alt) key.  However,
+    // unfortunately, browsers on Mac are using NS_VK_ALT for it even though
+    // it's really different from Alt key on Windows.
+    // On the other hand, GTK's AltGrapsh keys are really different from
+    // Alt key.  However, there is no AltGrapsh key on Windows.  On Windows,
+    // both Ctrl and Alt keys are pressed internally when AltGr key is pressed.
+    // For some languages' users, AltGraph key is important, so, web
+    // applications on such locale may want to know AltGraph key press.
+    // Therefore, we should map AltGr keycode for them only on GTK.
+    { NS_VK_ALTGR,      GDK_ISO_Level3_Shift },
+    { NS_VK_ALTGR,      GDK_Mode_switch },
+
     { NS_VK_PAUSE,      GDK_Pause },
     { NS_VK_CAPS_LOCK,  GDK_Caps_Lock },
     { NS_VK_KANA,       GDK_Kana_Lock },
     { NS_VK_KANA,       GDK_Kana_Shift },
     { NS_VK_HANGUL,     GDK_Hangul },
     // { NS_VK_JUNJA,      GDK_XXX },
     // { NS_VK_FINAL,      GDK_XXX },
     { NS_VK_HANJA,      GDK_Hangul_Hanja },
@@ -125,77 +149,57 @@ static const KeyPair kKeyPairs[] = {
     { NS_VK_LEFT,       GDK_KP_Left },
     { NS_VK_RIGHT,      GDK_KP_Right },
     { NS_VK_UP,         GDK_KP_Up },
     { NS_VK_DOWN,       GDK_KP_Down },
     { NS_VK_PAGE_UP,    GDK_KP_Page_Up },
     // Not sure what these are
     //{ NS_VK_,       GDK_KP_Prior },
     //{ NS_VK_,        GDK_KP_Next },
-    // GDK_KP_Begin is the 5 on the non-numlock keypad
-    //{ NS_VK_,        GDK_KP_Begin },
+    { NS_VK_CLEAR,      GDK_KP_Begin }, // Num-unlocked 5
     { NS_VK_PAGE_DOWN,  GDK_KP_Page_Down },
     { NS_VK_HOME,       GDK_KP_Home },
     { NS_VK_END,        GDK_KP_End },
     { NS_VK_INSERT,     GDK_KP_Insert },
     { NS_VK_DELETE,     GDK_KP_Delete },
+    { NS_VK_RETURN,     GDK_KP_Enter },
 
-    { NS_VK_MULTIPLY,   GDK_KP_Multiply },
-    { NS_VK_ADD,        GDK_KP_Add },
-    { NS_VK_SEPARATOR,  GDK_KP_Separator },
-    { NS_VK_SUBTRACT,   GDK_KP_Subtract },
-    { NS_VK_DECIMAL,    GDK_KP_Decimal },
-    { NS_VK_DIVIDE,     GDK_KP_Divide },
-    { NS_VK_RETURN,     GDK_KP_Enter },
     { NS_VK_NUM_LOCK,   GDK_Num_Lock },
     { NS_VK_SCROLL_LOCK,GDK_Scroll_Lock },
 
-    { NS_VK_COMMA,      GDK_comma },
-    { NS_VK_PERIOD,     GDK_period },
-    { NS_VK_SLASH,      GDK_slash },
-    { NS_VK_BACK_SLASH, GDK_backslash },
-    { NS_VK_BACK_QUOTE, GDK_grave },
-    { NS_VK_OPEN_BRACKET, GDK_bracketleft },
-    { NS_VK_CLOSE_BRACKET, GDK_bracketright },
-    { NS_VK_SEMICOLON, GDK_colon },
-    { NS_VK_QUOTE, GDK_apostrophe },
+    // Function keys
+    { NS_VK_F1,         GDK_F1 },
+    { NS_VK_F2,         GDK_F2 },
+    { NS_VK_F3,         GDK_F3 },
+    { NS_VK_F4,         GDK_F4 },
+    { NS_VK_F5,         GDK_F5 },
+    { NS_VK_F6,         GDK_F6 },
+    { NS_VK_F7,         GDK_F7 },
+    { NS_VK_F8,         GDK_F8 },
+    { NS_VK_F9,         GDK_F9 },
+    { NS_VK_F10,        GDK_F10 },
+    { NS_VK_F11,        GDK_F11 },
+    { NS_VK_F12,        GDK_F12 },
+    { NS_VK_F13,        GDK_F13 },
+    { NS_VK_F14,        GDK_F14 },
+    { NS_VK_F15,        GDK_F15 },
+    { NS_VK_F16,        GDK_F16 },
+    { NS_VK_F17,        GDK_F17 },
+    { NS_VK_F18,        GDK_F18 },
+    { NS_VK_F19,        GDK_F19 },
+    { NS_VK_F20,        GDK_F20 },
+    { NS_VK_F21,        GDK_F21 },
+    { NS_VK_F22,        GDK_F22 },
+    { NS_VK_F23,        GDK_F23 },
+    { NS_VK_F24,        GDK_F24 },
 
     // context menu key, keysym 0xff67, typically keycode 117 on 105-key (Microsoft) 
     // x86 keyboards, located between right 'Windows' key and right Ctrl key
     { NS_VK_CONTEXT_MENU, GDK_Menu },
     { NS_VK_SLEEP,      GDK_Sleep },
-
-    // NS doesn't have dash or equals distinct from the numeric keypad ones,
-    // so we'll use those for now.  See bug 17008:
-    { NS_VK_SUBTRACT, GDK_minus },
-    { NS_VK_EQUALS, GDK_equal },
-
-    // Some shifted keys, see bug 15463 as well as 17008.
-    // These should be subject to different keyboard mappings.
-    { NS_VK_QUOTE, GDK_quotedbl },
-    { NS_VK_OPEN_BRACKET, GDK_braceleft },
-    { NS_VK_CLOSE_BRACKET, GDK_braceright },
-    { NS_VK_BACK_SLASH, GDK_bar },
-    { NS_VK_SEMICOLON, GDK_semicolon },
-    { NS_VK_BACK_QUOTE, GDK_asciitilde },
-    { NS_VK_COMMA, GDK_less },
-    { NS_VK_PERIOD, GDK_greater },
-    { NS_VK_SLASH,      GDK_question },
-    { NS_VK_1, GDK_exclam },
-    { NS_VK_2, GDK_at },
-    { NS_VK_3, GDK_numbersign },
-    { NS_VK_4, GDK_dollar },
-    { NS_VK_5, GDK_percent },
-    { NS_VK_6, GDK_asciicircum },
-    { NS_VK_7, GDK_ampersand },
-    { NS_VK_8, GDK_asterisk },
-    { NS_VK_9, GDK_parenleft },
-    { NS_VK_0, GDK_parenright },
-    { NS_VK_SUBTRACT, GDK_underscore },
-    { NS_VK_EQUALS, GDK_plus }
 };
 
 // map Sun Keyboard special keysyms on to NS_VK keys
 static const KeyPair kSunKeyPairs[] = {
     {NS_VK_F11, 0x1005ff10 }, //Sun F11 key generates SunF36(0x1005ff10) keysym
     {NS_VK_F12, 0x1005ff11 }  //Sun F12 key generates SunF37(0x1005ff11) keysym
 };
 
@@ -627,80 +631,147 @@ KeymapWrapper::InitInputEvent(nsInputEve
          GetBoolName(mouseEvent.buttons & nsMouseEvent::eMiddleButtonFlag),
          GetBoolName(mouseEvent.buttons & nsMouseEvent::e4thButtonFlag),
          GetBoolName(mouseEvent.buttons & nsMouseEvent::e5thButtonFlag)));
 }
 
 /* static */ PRUint32
 KeymapWrapper::ComputeDOMKeyCode(const GdkEventKey* aGdkKeyEvent)
 {
+    // If the keyval indicates it's a modifier key, we should use unshifted
+    // key's modifier keyval.
     guint keyval = aGdkKeyEvent->keyval;
-
-    // First, try to handle alphanumeric input, not listed in nsKeycodes:
-    // most likely, more letters will be getting typed in than things in
-    // the key list, so we will look through these first.
-
-    // since X has different key symbols for upper and lowercase letters and
-    // mozilla does not, convert gdk's to mozilla's
-    if (keyval >= GDK_a && keyval <= GDK_z) {
-        return keyval - GDK_a + NS_VK_A;
-    }
-    if (keyval >= GDK_A && keyval <= GDK_Z) {
-        return keyval - GDK_A + NS_VK_A;
+    if (GetModifierForGDKKeyval(keyval)) {
+        // But if the keyval without modifiers isn't a modifier key, we
+        // shouldn't use it.  E.g., Japanese keyboard layout's
+        // Shift + Eisu-Toggle key is CapsLock.  This is an actual rare case,
+        // Windows uses different keycode for a physical key for different
+        // shift key state.
+        guint keyvalWithoutModifier = GetGDKKeyvalWithoutModifier(aGdkKeyEvent);
+        if (GetModifierForGDKKeyval(keyvalWithoutModifier)) {
+            keyval = keyvalWithoutModifier;
+        }
+        return GetDOMKeyCodeFromKeyPairs(keyval);
     }
 
-    // numbers
-    if (keyval >= GDK_0 && keyval <= GDK_9) {
-        return keyval - GDK_0 + NS_VK_0;
+    // If the key isn't printable, let's look at the key pairs.
+    PRUint32 charCode = GetCharCodeFor(aGdkKeyEvent);
+    if (!charCode) {
+        // Always use unshifted keycode for the non-printable key.
+        // XXX It might be better to decide DOM keycode from all keyvals of
+        //     the hardware keycode.  However, I think that it's too excessive.
+        guint keyvalWithoutModifier = GetGDKKeyvalWithoutModifier(aGdkKeyEvent);
+        PRUint32 DOMKeyCode = GetDOMKeyCodeFromKeyPairs(keyvalWithoutModifier);
+        if (!DOMKeyCode) {
+            // If the unshifted keyval couldn't be mapped to a DOM keycode,
+            // we should fallback to legacy logic, so, we should recompute with
+            // the keyval with aGdkKeyEvent.
+            DOMKeyCode = GetDOMKeyCodeFromKeyPairs(keyval);
+        }
+        return DOMKeyCode;
+    }
+
+    // printable numpad keys should be resolved here.
+    switch (keyval) {
+        case GDK_KP_Multiply:  return NS_VK_MULTIPLY;
+        case GDK_KP_Add:       return NS_VK_ADD;
+        case GDK_KP_Separator: return NS_VK_SEPARATOR;
+        case GDK_KP_Subtract:  return NS_VK_SUBTRACT;
+        case GDK_KP_Decimal:   return NS_VK_DECIMAL;
+        case GDK_KP_Divide:    return NS_VK_DIVIDE;
+        case GDK_KP_0:         return NS_VK_NUMPAD0;
+        case GDK_KP_1:         return NS_VK_NUMPAD1;
+        case GDK_KP_2:         return NS_VK_NUMPAD2;
+        case GDK_KP_3:         return NS_VK_NUMPAD3;
+        case GDK_KP_4:         return NS_VK_NUMPAD4;
+        case GDK_KP_5:         return NS_VK_NUMPAD5;
+        case GDK_KP_6:         return NS_VK_NUMPAD6;
+        case GDK_KP_7:         return NS_VK_NUMPAD7;
+        case GDK_KP_8:         return NS_VK_NUMPAD8;
+        case GDK_KP_9:         return NS_VK_NUMPAD9;
     }
 
-    // keypad numbers
-    if (keyval >= GDK_KP_0 && keyval <= GDK_KP_9) {
-        return keyval - GDK_KP_0 + NS_VK_NUMPAD0;
+    KeymapWrapper* keymapWrapper = GetInstance();
+
+    // Ignore all modifier state except NumLock.
+    guint baseState =
+        (aGdkKeyEvent->state & keymapWrapper->GetModifierMask(NUM_LOCK));
+
+    // Basically, we should use unmodified character for deciding our keyCode.
+    PRUint32 unmodifiedChar =
+        keymapWrapper->GetCharCodeFor(aGdkKeyEvent, baseState,
+                                      aGdkKeyEvent->group);
+    if (IsBasicLatinLetterOrNumeral(unmodifiedChar)) {
+        // If the unmodified character is an ASCII alphabet or an ASCII
+        // numeric, it's the best hint for deciding our keyCode.
+        return WidgetUtils::ComputeKeyCodeFromChar(unmodifiedChar);
+    }
+
+    // If the unmodified character is not an ASCII character, that means we
+    // couldn't find the hint. We should reset it.
+    if (unmodifiedChar > 0x7F) {
+        unmodifiedChar = 0;
     }
 
-    // If the keyval indicates it's a modifier key, we should use unshifted
-    // key's modifier keyval.
-    if (GetModifierForGDKKeyval(keyval)) {
-        KeymapWrapper* keymapWrapper = GetInstance();
-        GdkKeymapKey key;
-        key.keycode = aGdkKeyEvent->hardware_keycode;
-        key.group = aGdkKeyEvent->group;
-        key.level = 0;
-        guint unshiftedKeyval =
-            gdk_keymap_lookup_key(keymapWrapper->mGdkKeymap, &key);
-        // But if the unshifted keyval isn't a modifier key, we shouldn't use
-        // it.  E.g., Japanese keyboard layout's Shift + Eisu-Toggle key is
-        // CapsLock.  This is an actual rare case, Windows uses different
-        // keycode for a physical key for different shift key state.
-        if (GetModifierForGDKKeyval(unshiftedKeyval)) {
-            keyval = unshiftedKeyval;
+    // Retry with shifted keycode.
+    guint shiftState = (baseState | keymapWrapper->GetModifierMask(SHIFT));
+    PRUint32 shiftedChar =
+        keymapWrapper->GetCharCodeFor(aGdkKeyEvent, shiftState,
+                                      aGdkKeyEvent->group);
+    if (IsBasicLatinLetterOrNumeral(shiftedChar)) {
+        // A shifted character can be an ASCII alphabet on Hebrew keyboard
+        // layout. And also shifted character can be an ASCII numeric on
+        // AZERTY keyboad layout.  Then, it's a good hint for deciding our
+        // keyCode.
+        return WidgetUtils::ComputeKeyCodeFromChar(shiftedChar);
+    }
+
+    // If the shifted unmodified character isn't an ASCII character, we should
+    // discard it too.
+    if (shiftedChar > 0x7F) {
+        shiftedChar = 0;
+    }
+
+    // If current keyboard layout isn't ASCII alphabet inputtable layout,
+    // look for ASCII alphabet inputtable keyboard layout.  If the key
+    // inputs an ASCII alphabet or an ASCII numeric, we should use it
+    // for deciding our keyCode.
+    // Note that it's important not to use alternative keyboard layout for ASCII
+    // alphabet inputabble keyboard layout because the keycode for the key with
+    // alternative keyboard layout may conflict with another key on current
+    // keyboard layout.
+    if (!keymapWrapper->IsLatinGroup(aGdkKeyEvent->group)) {
+        gint minGroup = keymapWrapper->GetFirstLatinGroup();
+        if (minGroup >= 0) {
+            PRUint32 unmodCharLatin =
+                keymapWrapper->GetCharCodeFor(aGdkKeyEvent, baseState,
+                                              minGroup);
+            if (IsBasicLatinLetterOrNumeral(unmodCharLatin)) {
+                // If the unmodified character is an ASCII alphabet or
+                // an ASCII numeric, we should use it for the keyCode.
+                return WidgetUtils::ComputeKeyCodeFromChar(unmodCharLatin);
+            }
+            PRUint32 shiftedCharLatin =
+                keymapWrapper->GetCharCodeFor(aGdkKeyEvent, shiftState,
+                                              minGroup);
+            if (IsBasicLatinLetterOrNumeral(shiftedCharLatin)) {
+                // If the shifted character is an ASCII alphabet or an ASCII
+                // numeric, we should use it for the keyCode.
+                return WidgetUtils::ComputeKeyCodeFromChar(shiftedCharLatin);
+            }
         }
     }
 
-    // map Sun Keyboard special keysyms
-    for (PRUint32 i = 0; i < ArrayLength(kSunKeyPairs); i++) {
-        if (kSunKeyPairs[i].GDKKeyval == keyval) {
-            return kSunKeyPairs[i].DOMKeyCode;
-        }
+    // If unmodified character is in ASCII range, use it.  Otherwise, use
+    // shifted character.
+    if (!unmodifiedChar && !shiftedChar) {
+        return 0;
     }
-
-    // misc other things
-    for (PRUint32 i = 0; i < ArrayLength(kKeyPairs); i++) {
-        if (kKeyPairs[i].GDKKeyval == keyval) {
-            return kKeyPairs[i].DOMKeyCode;
-        }
-    }
-
-    // function keys
-    if (keyval >= GDK_F1 && keyval <= GDK_F24) {
-        return keyval - GDK_F1 + NS_VK_F1;
-    }
-
-    return 0;
+    return WidgetUtils::ComputeKeyCodeFromChar(
+                unmodifiedChar ? unmodifiedChar : shiftedChar);
 }
 
 /* static */ guint
 KeymapWrapper::GuessGDKKeyval(PRUint32 aDOMKeyCode)
 {
     // First, try to handle alphanumeric input, not listed in nsKeycodes:
     // most likely, more letters will be getting typed in than things in
     // the key list, so we will look through these first.
@@ -711,33 +782,77 @@ KeymapWrapper::GuessGDKKeyval(PRUint32 a
     }
 
     // numbers
     if (aDOMKeyCode >= NS_VK_0 && aDOMKeyCode <= NS_VK_9) {
         // gdk and DOM both use the ASCII codes for these keys.
         return aDOMKeyCode - NS_VK_0 + GDK_0;
     }
 
-    // keypad numbers
-    if (aDOMKeyCode >= NS_VK_NUMPAD0 && aDOMKeyCode <= NS_VK_NUMPAD9) {
-        return aDOMKeyCode - NS_VK_NUMPAD0 + GDK_KP_0;
+    switch (aDOMKeyCode) {
+        // keys in numpad
+        case NS_VK_MULTIPLY:  return GDK_KP_Multiply;
+        case NS_VK_ADD:       return GDK_KP_Add;
+        case NS_VK_SEPARATOR: return GDK_KP_Separator;
+        case NS_VK_SUBTRACT:  return GDK_KP_Subtract;
+        case NS_VK_DECIMAL:   return GDK_KP_Decimal;
+        case NS_VK_DIVIDE:    return GDK_KP_Divide;
+        case NS_VK_NUMPAD0:   return GDK_KP_0;
+        case NS_VK_NUMPAD1:   return GDK_KP_1;
+        case NS_VK_NUMPAD2:   return GDK_KP_2;
+        case NS_VK_NUMPAD3:   return GDK_KP_3;
+        case NS_VK_NUMPAD4:   return GDK_KP_4;
+        case NS_VK_NUMPAD5:   return GDK_KP_5;
+        case NS_VK_NUMPAD6:   return GDK_KP_6;
+        case NS_VK_NUMPAD7:   return GDK_KP_7;
+        case NS_VK_NUMPAD8:   return GDK_KP_8;
+        case NS_VK_NUMPAD9:   return GDK_KP_9;
+        // other prinable keys
+        case NS_VK_SPACE:               return GDK_space;
+        case NS_VK_COLON:               return GDK_comma;
+        case NS_VK_SEMICOLON:           return GDK_semicolon;
+        case NS_VK_LESS_THAN:           return GDK_less;
+        case NS_VK_EQUALS:              return GDK_equal;
+        case NS_VK_GREATER_THAN:        return GDK_greater;
+        case NS_VK_QUESTION_MARK:       return GDK_question;
+        case NS_VK_AT:                  return GDK_at;
+        case NS_VK_CIRCUMFLEX:          return GDK_asciicircum;
+        case NS_VK_EXCLAMATION:         return GDK_exclam;
+        case NS_VK_DOUBLE_QUOTE:        return GDK_quotedbl;
+        case NS_VK_HASH:                return GDK_numbersign;
+        case NS_VK_DOLLAR:              return GDK_dollar;
+        case NS_VK_PERCENT:             return GDK_percent;
+        case NS_VK_AMPERSAND:           return GDK_ampersand;
+        case NS_VK_UNDERSCORE:          return GDK_underscore;
+        case NS_VK_OPEN_PAREN:          return GDK_parenleft;
+        case NS_VK_CLOSE_PAREN:         return GDK_parenright;
+        case NS_VK_ASTERISK:            return GDK_asterisk;
+        case NS_VK_PLUS:                return GDK_plus;
+        case NS_VK_PIPE:                return GDK_bar;
+        case NS_VK_HYPHEN_MINUS:        return GDK_minus;
+        case NS_VK_OPEN_CURLY_BRACKET:  return GDK_braceleft;
+        case NS_VK_CLOSE_CURLY_BRACKET: return GDK_braceright;
+        case NS_VK_TILDE:               return GDK_asciitilde;
+        case NS_VK_COMMA:               return GDK_comma;
+        case NS_VK_PERIOD:              return GDK_period;
+        case NS_VK_SLASH:               return GDK_slash;
+        case NS_VK_BACK_QUOTE:          return GDK_grave;
+        case NS_VK_OPEN_BRACKET:        return GDK_bracketleft;
+        case NS_VK_BACK_SLASH:          return GDK_backslash;
+        case NS_VK_CLOSE_BRACKET:       return GDK_bracketright;
+        case NS_VK_QUOTE:               return GDK_apostrophe;
     }
 
     // misc other things
     for (PRUint32 i = 0; i < ArrayLength(kKeyPairs); ++i) {
         if (kKeyPairs[i].DOMKeyCode == aDOMKeyCode) {
             return kKeyPairs[i].GDKKeyval;
         }
     }
 
-    // function keys
-    if (aDOMKeyCode >= NS_VK_F1 && aDOMKeyCode <= NS_VK_F9) {
-        return aDOMKeyCode - NS_VK_F1 + GDK_F1;
-    }
-
     return 0;
 }
 
 /* static */ void
 KeymapWrapper::InitKeyEvent(nsKeyEvent& aKeyEvent,
                             GdkEventKey* aGdkKeyEvent)
 {
     KeymapWrapper* keymapWrapper = GetInstance();
@@ -935,24 +1050,102 @@ KeymapWrapper::GetKeyLevel(GdkEventKey *
              aGdkKeyEvent->hardware_keycode,
              GdkModifierType(aGdkKeyEvent->state),
              aGdkKeyEvent->group, NULL, NULL, &level, NULL)) {
         return -1;
     }
     return level;
 }
 
+gint
+KeymapWrapper::GetFirstLatinGroup()
+{
+    GdkKeymapKey *keys;
+    gint count;
+    gint minGroup = -1;
+    if (gdk_keymap_get_entries_for_keyval(mGdkKeymap, GDK_a, &keys, &count)) {
+        // find the minimum number group for latin inputtable layout
+        for (gint i = 0; i < count && minGroup != 0; ++i) {
+            if (keys[i].level != 0 && keys[i].level != 1) {
+                continue;
+            }
+            if (minGroup >= 0 && keys[i].group > minGroup) {
+                continue;
+            }
+            minGroup = keys[i].group;
+        }
+        g_free(keys);
+    }
+    return minGroup;
+}
+
+bool
+KeymapWrapper::IsLatinGroup(guint8 aGroup)
+{
+    GdkKeymapKey *keys;
+    gint count;
+    bool result = false;
+    if (gdk_keymap_get_entries_for_keyval(mGdkKeymap, GDK_a, &keys, &count)) {
+        for (gint i = 0; i < count; ++i) {
+            if (keys[i].level != 0 && keys[i].level != 1) {
+                continue;
+            }
+            if (keys[i].group == aGroup) {
+                result = true;
+                break;
+            }
+        }
+        g_free(keys);
+    }
+    return result;
+}
+
 /* static */ bool
 KeymapWrapper::IsBasicLatinLetterOrNumeral(PRUint32 aCharCode)
 {
     return (aCharCode >= 'a' && aCharCode <= 'z') ||
            (aCharCode >= 'A' && aCharCode <= 'Z') ||
            (aCharCode >= '0' && aCharCode <= '9');
 }
 
+/* static */ guint
+KeymapWrapper::GetGDKKeyvalWithoutModifier(const GdkEventKey *aGdkKeyEvent)
+{
+    KeymapWrapper* keymapWrapper = GetInstance();
+    guint state =
+        (aGdkKeyEvent->state & keymapWrapper->GetModifierMask(NUM_LOCK));
+    guint keyval;
+    if (!gdk_keymap_translate_keyboard_state(keymapWrapper->mGdkKeymap,
+             aGdkKeyEvent->hardware_keycode, GdkModifierType(state),
+             aGdkKeyEvent->group, &keyval, NULL, NULL, NULL)) {
+        return 0;
+    }
+    return keyval;
+}
+
+/* static */ PRUint32
+KeymapWrapper::GetDOMKeyCodeFromKeyPairs(guint aGdkKeyval)
+{
+    // map Sun Keyboard special keysyms first.
+    for (PRUint32 i = 0; i < ArrayLength(kSunKeyPairs); i++) {
+        if (kSunKeyPairs[i].GDKKeyval == aGdkKeyval) {
+            return kSunKeyPairs[i].DOMKeyCode;
+        }
+    }
+
+    // misc other things
+    for (PRUint32 i = 0; i < ArrayLength(kKeyPairs); i++) {
+        if (kKeyPairs[i].GDKKeyval == aGdkKeyval) {
+            return kKeyPairs[i].DOMKeyCode;
+        }
+    }
+
+    return 0;
+}
+
 void
 KeymapWrapper::InitKeypressEvent(nsKeyEvent& aKeyEvent,
                                  GdkEventKey* aGdkKeyEvent)
 {
     NS_ENSURE_TRUE(aKeyEvent.message == NS_KEY_PRESS, );
 
     aKeyEvent.charCode = GetCharCodeFor(aGdkKeyEvent);
     if (!aKeyEvent.charCode) {
@@ -1023,33 +1216,17 @@ KeymapWrapper::InitKeypressEvent(nsKeyEv
              "charCode=0x%08X, level=%d, altCharCodes={ "
              "mUnshiftedCharCode=0x%08X, mShiftedCharCode=0x%08X }",
              this, aKeyEvent.keyCode, aKeyEvent.charCode, level,
              altCharCodes.mUnshiftedCharCode, altCharCodes.mShiftedCharCode));
         return;
     }
 
     // Next, find Latin inputtable keyboard layout.
-    GdkKeymapKey *keys;
-    gint count;
-    gint minGroup = -1;
-    if (gdk_keymap_get_entries_for_keyval(mGdkKeymap, GDK_a, &keys, &count)) {
-        // find the minimum number group for latin inputtable layout
-        for (gint i = 0; i < count && minGroup != 0; ++i) {
-            if (keys[i].level != 0 && keys[i].level != 1) {
-                continue;
-            }
-            if (minGroup >= 0 && keys[i].group > minGroup) {
-                continue;
-            }
-            minGroup = keys[i].group;
-        }
-        g_free(keys);
-    }
-
+    gint minGroup = GetFirstLatinGroup();
     if (minGroup < 0) {
         PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS,
             ("KeymapWrapper(%p): InitKeypressEvent, "
              "Latin keyboard layout isn't found: "
              "keyCode=0x%02X, charCode=0x%08X, level=%d, "
              "altCharCodes={ mUnshiftedCharCode=0x%08X, "
              "mShiftedCharCode=0x%08X }",
              this, aKeyEvent.keyCode, aKeyEvent.charCode, level,
@@ -1102,18 +1279,19 @@ KeymapWrapper::InitKeypressEvent(nsKeyEv
 
 /* static */ bool
 KeymapWrapper::IsKeyPressEventNecessary(GdkEventKey* aGdkKeyEvent)
 {
     // If this is a modifier key event, we shouldn't send keypress event.
     switch (ComputeDOMKeyCode(aGdkKeyEvent)) {
         case NS_VK_SHIFT:
         case NS_VK_CONTROL:
-        case NS_VK_META:
         case NS_VK_ALT:
+        case NS_VK_ALTGR:
+        case NS_VK_WIN:
         case NS_VK_CAPS_LOCK:
         case NS_VK_NUM_LOCK:
         case NS_VK_SCROLL_LOCK:
             return false;
         default:
             return true;
     }
 }
--- a/widget/gtk2/nsGtkKeyUtils.h
+++ b/widget/gtk2/nsGtkKeyUtils.h
@@ -262,26 +262,57 @@ protected:
      *
      * @param aGdkKeyEvent      Native key event, must not be NULL.
      * @return                  Using level.  Typically, this is 0 or 1.
      *                          If failed, this returns -1.
      */
     gint GetKeyLevel(GdkEventKey *aGdkKeyEvent);
 
     /**
+     * GetFirstLatinGroup() returns group of mGdkKeymap which can input an
+     * ASCII character by GDK_A.
+     *
+     * @return                  group value of GdkEventKey.
+     */
+    gint GetFirstLatinGroup();
+
+    /**
+     * IsLatinGroup() checkes whether the keyboard layout of aGroup is
+     * ASCII alphabet inputtable or not.
+     *
+     * @param aGroup            The group value of GdkEventKey.
+     * @return                  TRUE if the keyboard layout can input
+     *                          ASCII alphabet.  Otherwise, FALSE.
+     */
+    bool IsLatinGroup(guint8 aGroup);
+
+    /**
      * IsBasicLatinLetterOrNumeral() Checks whether the aCharCode is an
      * alphabet or a numeric character in ASCII.
      *
      * @param aCharCode         Charcode which you want to test.
      * @return                  TRUE if aCharCode is an alphabet or a numeric
      *                          in ASCII range.  Otherwise, FALSE.
      */
     static bool IsBasicLatinLetterOrNumeral(PRUint32 aCharCode);
 
     /**
+     * GetGDKKeyvalWithoutModifier() returns the keyval for aGdkKeyEvent when
+     * ignoring the modifier state except NumLock. (NumLock is a key to change
+     * some key's meaning.)
+     */
+    static guint GetGDKKeyvalWithoutModifier(const GdkEventKey *aGdkKeyEvent);
+
+    /**
+     * GetDOMKeyCodeFromKeyPairs() returns DOM keycode for aGdkKeyval if
+     * it's in KeyPair table.
+     */
+    static PRUint32 GetDOMKeyCodeFromKeyPairs(guint aGdkKeyval);
+
+    /**
      * InitKeypressEvent() intializes keyCode, charCode and
      * alternativeCharCodes of keypress event.
      *
      * @param aKeyEvent         An NS_KEY_PRESS event, must not be NULL.
      *                          The modifier related members and keyCode must
      *                          be initialized already.
      * @param aGdkKeyEvent      A native key event which causes dispatching
      *                          aKeyEvent.
--- a/widget/nsGUIEvent.h
+++ b/widget/nsGUIEvent.h
@@ -1927,155 +1927,21 @@ enum nsDragDropEventStatus {
 
 /*
  * Virtual key bindings for keyboard events.
  * These come from nsIDOMKeyEvent.h, which is generated from MouseKeyEvent.idl.
  * Really, it would be better if we phased out the NS_VK symbols altogether
  * in favor of the DOM ones, but at least this way they'll be in sync.
  */
 
-#define NS_VK_CANCEL         nsIDOMKeyEvent::DOM_VK_CANCEL
-#define NS_VK_HELP           nsIDOMKeyEvent::DOM_VK_HELP
-#define NS_VK_BACK           nsIDOMKeyEvent::DOM_VK_BACK_SPACE
-#define NS_VK_TAB            nsIDOMKeyEvent::DOM_VK_TAB
-#define NS_VK_CLEAR          nsIDOMKeyEvent::DOM_VK_CLEAR
-#define NS_VK_RETURN         nsIDOMKeyEvent::DOM_VK_RETURN
-#define NS_VK_ENTER          nsIDOMKeyEvent::DOM_VK_ENTER
-#define NS_VK_SHIFT          nsIDOMKeyEvent::DOM_VK_SHIFT
-#define NS_VK_CONTROL        nsIDOMKeyEvent::DOM_VK_CONTROL
-#define NS_VK_ALT            nsIDOMKeyEvent::DOM_VK_ALT
-#define NS_VK_PAUSE          nsIDOMKeyEvent::DOM_VK_PAUSE
-#define NS_VK_CAPS_LOCK      nsIDOMKeyEvent::DOM_VK_CAPS_LOCK
-#define NS_VK_KANA           nsIDOMKeyEvent::DOM_VK_KANA
-#define NS_VK_HANGUL         nsIDOMKeyEvent::DOM_VK_HANGUL
-#define NS_VK_JUNJA          nsIDOMKeyEvent::DOM_VK_JUNJA
-#define NS_VK_FINAL          nsIDOMKeyEvent::DOM_VK_FINAL
-#define NS_VK_HANJA          nsIDOMKeyEvent::DOM_VK_HANJA
-#define NS_VK_KANJI          nsIDOMKeyEvent::DOM_VK_KANJI
-#define NS_VK_ESCAPE         nsIDOMKeyEvent::DOM_VK_ESCAPE
-#define NS_VK_CONVERT        nsIDOMKeyEvent::DOM_VK_CONVERT
-#define NS_VK_NONCONVERT     nsIDOMKeyEvent::DOM_VK_NONCONVERT
-#define NS_VK_ACCEPT         nsIDOMKeyEvent::DOM_VK_ACCEPT
-#define NS_VK_MODECHANGE     nsIDOMKeyEvent::DOM_VK_MODECHANGE
-#define NS_VK_SPACE          nsIDOMKeyEvent::DOM_VK_SPACE
-#define NS_VK_PAGE_UP        nsIDOMKeyEvent::DOM_VK_PAGE_UP
-#define NS_VK_PAGE_DOWN      nsIDOMKeyEvent::DOM_VK_PAGE_DOWN
-#define NS_VK_END            nsIDOMKeyEvent::DOM_VK_END
-#define NS_VK_HOME           nsIDOMKeyEvent::DOM_VK_HOME
-#define NS_VK_LEFT           nsIDOMKeyEvent::DOM_VK_LEFT
-#define NS_VK_UP             nsIDOMKeyEvent::DOM_VK_UP
-#define NS_VK_RIGHT          nsIDOMKeyEvent::DOM_VK_RIGHT
-#define NS_VK_DOWN           nsIDOMKeyEvent::DOM_VK_DOWN
-#define NS_VK_SELECT         nsIDOMKeyEvent::DOM_VK_SELECT
-#define NS_VK_PRINT          nsIDOMKeyEvent::DOM_VK_PRINT
-#define NS_VK_EXECUTE        nsIDOMKeyEvent::DOM_VK_EXECUTE
-#define NS_VK_PRINTSCREEN    nsIDOMKeyEvent::DOM_VK_PRINTSCREEN
-#define NS_VK_INSERT         nsIDOMKeyEvent::DOM_VK_INSERT
-#define NS_VK_DELETE         nsIDOMKeyEvent::DOM_VK_DELETE
-
-// NS_VK_0 - NS_VK_9 match their ascii values
-#define NS_VK_0              nsIDOMKeyEvent::DOM_VK_0
-#define NS_VK_1              nsIDOMKeyEvent::DOM_VK_1
-#define NS_VK_2              nsIDOMKeyEvent::DOM_VK_2
-#define NS_VK_3              nsIDOMKeyEvent::DOM_VK_3
-#define NS_VK_4              nsIDOMKeyEvent::DOM_VK_4
-#define NS_VK_5              nsIDOMKeyEvent::DOM_VK_5
-#define NS_VK_6              nsIDOMKeyEvent::DOM_VK_6
-#define NS_VK_7              nsIDOMKeyEvent::DOM_VK_7
-#define NS_VK_8              nsIDOMKeyEvent::DOM_VK_8
-#define NS_VK_9              nsIDOMKeyEvent::DOM_VK_9
-
-#define NS_VK_SEMICOLON      nsIDOMKeyEvent::DOM_VK_SEMICOLON
-#define NS_VK_EQUALS         nsIDOMKeyEvent::DOM_VK_EQUALS
-
-// NS_VK_A - NS_VK_Z match their ascii values
-#define NS_VK_A              nsIDOMKeyEvent::DOM_VK_A
-#define NS_VK_B              nsIDOMKeyEvent::DOM_VK_B
-#define NS_VK_C              nsIDOMKeyEvent::DOM_VK_C
-#define NS_VK_D              nsIDOMKeyEvent::DOM_VK_D
-#define NS_VK_E              nsIDOMKeyEvent::DOM_VK_E
-#define NS_VK_F              nsIDOMKeyEvent::DOM_VK_F
-#define NS_VK_G              nsIDOMKeyEvent::DOM_VK_G
-#define NS_VK_H              nsIDOMKeyEvent::DOM_VK_H
-#define NS_VK_I              nsIDOMKeyEvent::DOM_VK_I
-#define NS_VK_J              nsIDOMKeyEvent::DOM_VK_J
-#define NS_VK_K              nsIDOMKeyEvent::DOM_VK_K
-#define NS_VK_L              nsIDOMKeyEvent::DOM_VK_L
-#define NS_VK_M              nsIDOMKeyEvent::DOM_VK_M
-#define NS_VK_N              nsIDOMKeyEvent::DOM_VK_N
-#define NS_VK_O              nsIDOMKeyEvent::DOM_VK_O
-#define NS_VK_P              nsIDOMKeyEvent::DOM_VK_P
-#define NS_VK_Q              nsIDOMKeyEvent::DOM_VK_Q
-#define NS_VK_R              nsIDOMKeyEvent::DOM_VK_R
-#define NS_VK_S              nsIDOMKeyEvent::DOM_VK_S
-#define NS_VK_T              nsIDOMKeyEvent::DOM_VK_T
-#define NS_VK_U              nsIDOMKeyEvent::DOM_VK_U
-#define NS_VK_V              nsIDOMKeyEvent::DOM_VK_V
-#define NS_VK_W              nsIDOMKeyEvent::DOM_VK_W
-#define NS_VK_X              nsIDOMKeyEvent::DOM_VK_X
-#define NS_VK_Y              nsIDOMKeyEvent::DOM_VK_Y
-#define NS_VK_Z              nsIDOMKeyEvent::DOM_VK_Z
-
-#define NS_VK_CONTEXT_MENU   nsIDOMKeyEvent::DOM_VK_CONTEXT_MENU
-#define NS_VK_SLEEP          nsIDOMKeyEvent::DOM_VK_SLEEP
-
-#define NS_VK_NUMPAD0        nsIDOMKeyEvent::DOM_VK_NUMPAD0
-#define NS_VK_NUMPAD1        nsIDOMKeyEvent::DOM_VK_NUMPAD1
-#define NS_VK_NUMPAD2        nsIDOMKeyEvent::DOM_VK_NUMPAD2
-#define NS_VK_NUMPAD3        nsIDOMKeyEvent::DOM_VK_NUMPAD3
-#define NS_VK_NUMPAD4        nsIDOMKeyEvent::DOM_VK_NUMPAD4
-#define NS_VK_NUMPAD5        nsIDOMKeyEvent::DOM_VK_NUMPAD5
-#define NS_VK_NUMPAD6        nsIDOMKeyEvent::DOM_VK_NUMPAD6
-#define NS_VK_NUMPAD7        nsIDOMKeyEvent::DOM_VK_NUMPAD7
-#define NS_VK_NUMPAD8        nsIDOMKeyEvent::DOM_VK_NUMPAD8
-#define NS_VK_NUMPAD9        nsIDOMKeyEvent::DOM_VK_NUMPAD9
-#define NS_VK_MULTIPLY       nsIDOMKeyEvent::DOM_VK_MULTIPLY
-#define NS_VK_ADD            nsIDOMKeyEvent::DOM_VK_ADD
-#define NS_VK_SEPARATOR      nsIDOMKeyEvent::DOM_VK_SEPARATOR
-#define NS_VK_SUBTRACT       nsIDOMKeyEvent::DOM_VK_SUBTRACT
-#define NS_VK_DECIMAL        nsIDOMKeyEvent::DOM_VK_DECIMAL
-#define NS_VK_DIVIDE         nsIDOMKeyEvent::DOM_VK_DIVIDE
-#define NS_VK_F1             nsIDOMKeyEvent::DOM_VK_F1
-#define NS_VK_F2             nsIDOMKeyEvent::DOM_VK_F2
-#define NS_VK_F3             nsIDOMKeyEvent::DOM_VK_F3
-#define NS_VK_F4             nsIDOMKeyEvent::DOM_VK_F4
-#define NS_VK_F5             nsIDOMKeyEvent::DOM_VK_F5
-#define NS_VK_F6             nsIDOMKeyEvent::DOM_VK_F6
-#define NS_VK_F7             nsIDOMKeyEvent::DOM_VK_F7
-#define NS_VK_F8             nsIDOMKeyEvent::DOM_VK_F8
-#define NS_VK_F9             nsIDOMKeyEvent::DOM_VK_F9
-#define NS_VK_F10            nsIDOMKeyEvent::DOM_VK_F10
-#define NS_VK_F11            nsIDOMKeyEvent::DOM_VK_F11
-#define NS_VK_F12            nsIDOMKeyEvent::DOM_VK_F12
-#define NS_VK_F13            nsIDOMKeyEvent::DOM_VK_F13
-#define NS_VK_F14            nsIDOMKeyEvent::DOM_VK_F14
-#define NS_VK_F15            nsIDOMKeyEvent::DOM_VK_F15
-#define NS_VK_F16            nsIDOMKeyEvent::DOM_VK_F16
-#define NS_VK_F17            nsIDOMKeyEvent::DOM_VK_F17
-#define NS_VK_F18            nsIDOMKeyEvent::DOM_VK_F18
-#define NS_VK_F19            nsIDOMKeyEvent::DOM_VK_F19
-#define NS_VK_F20            nsIDOMKeyEvent::DOM_VK_F20
-#define NS_VK_F21            nsIDOMKeyEvent::DOM_VK_F21
-#define NS_VK_F22            nsIDOMKeyEvent::DOM_VK_F22
-#define NS_VK_F23            nsIDOMKeyEvent::DOM_VK_F23
-#define NS_VK_F24            nsIDOMKeyEvent::DOM_VK_F24
-
-#define NS_VK_NUM_LOCK       nsIDOMKeyEvent::DOM_VK_NUM_LOCK
-#define NS_VK_SCROLL_LOCK    nsIDOMKeyEvent::DOM_VK_SCROLL_LOCK
-
-#define NS_VK_COMMA          nsIDOMKeyEvent::DOM_VK_COMMA
-#define NS_VK_PERIOD         nsIDOMKeyEvent::DOM_VK_PERIOD
-#define NS_VK_SLASH          nsIDOMKeyEvent::DOM_VK_SLASH
-#define NS_VK_BACK_QUOTE     nsIDOMKeyEvent::DOM_VK_BACK_QUOTE
-#define NS_VK_OPEN_BRACKET   nsIDOMKeyEvent::DOM_VK_OPEN_BRACKET
-#define NS_VK_BACK_SLASH     nsIDOMKeyEvent::DOM_VK_BACK_SLASH
-#define NS_VK_CLOSE_BRACKET  nsIDOMKeyEvent::DOM_VK_CLOSE_BRACKET
-#define NS_VK_QUOTE          nsIDOMKeyEvent::DOM_VK_QUOTE
-
-#define NS_VK_META           nsIDOMKeyEvent::DOM_VK_META
+enum {
+#define NS_DEFINE_VK(aDOMKeyName, aDOMKeyCode) NS_##aDOMKeyName = aDOMKeyCode
+#include "nsVKList.h"
+#undef NS_DEFINE_VK
+};
 
 // IME Constants  -- keep in synch with nsIPrivateTextRange.h
 #define NS_TEXTRANGE_CARETPOSITION         0x01
 #define NS_TEXTRANGE_RAWINPUT              0x02
 #define NS_TEXTRANGE_SELECTEDRAWTEXT       0x03
 #define NS_TEXTRANGE_CONVERTEDTEXT         0x04
 #define NS_TEXTRANGE_SELECTEDCONVERTEDTEXT 0x05
 
--- a/widget/shared/WidgetUtils.cpp
+++ b/widget/shared/WidgetUtils.cpp
@@ -35,16 +35,18 @@
  * 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 "WidgetUtils.h"
 
+#include "nsGUIEvent.h"
+
 #include "nsIBaseWindow.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIDocShell.h"
 #include "nsIInterfaceRequestorUtils.h"
 
 namespace mozilla {
 namespace widget {
 
@@ -75,10 +77,128 @@ WidgetUtils::DOMWindowToWidget(nsIDOMWin
         baseWin = do_QueryInterface(window->GetDocShell());
       }
     }
   }
 
   return widget.forget();
 }
 
+// static
+PRUint32
+WidgetUtils::ComputeKeyCodeFromChar(PRUint32 aCharCode)
+{
+  if (aCharCode >= 'A' && aCharCode <= 'Z') {
+    return aCharCode - 'A' + NS_VK_A;
+  }
+  if (aCharCode >= 'a' && aCharCode <= 'z') {
+    return aCharCode - 'a' + NS_VK_A;
+  }
+  if (aCharCode >= '0' && aCharCode <= '9') {
+    return aCharCode - '0' + NS_VK_0;
+  }
+  switch (aCharCode) {
+    case ' ': return NS_VK_SPACE;
+    case '\t': return NS_VK_TAB;
+    case ':': return NS_VK_COLON;
+    case ';': return NS_VK_SEMICOLON;
+    case '<': return NS_VK_LESS_THAN;
+    case '=': return NS_VK_EQUALS;
+    case '>': return NS_VK_GREATER_THAN;
+    case '?': return NS_VK_QUESTION_MARK;
+    case '@': return NS_VK_AT;
+    case '^': return NS_VK_CIRCUMFLEX;
+    case '!': return NS_VK_EXCLAMATION;
+    case '"': return NS_VK_DOUBLE_QUOTE;
+    case '#': return NS_VK_HASH;
+    case '$': return NS_VK_DOLLAR;
+    case '%': return NS_VK_PERCENT;
+    case '&': return NS_VK_AMPERSAND;
+    case '_': return NS_VK_UNDERSCORE;
+    case '(': return NS_VK_OPEN_PAREN;
+    case ')': return NS_VK_CLOSE_PAREN;
+    case '*': return NS_VK_ASTERISK;
+    case '+': return NS_VK_PLUS;
+    case '|': return NS_VK_PIPE;
+    case '-': return NS_VK_HYPHEN_MINUS;
+    case '{': return NS_VK_OPEN_CURLY_BRACKET;
+    case '}': return NS_VK_CLOSE_CURLY_BRACKET;
+    case '~': return NS_VK_TILDE;
+    case ',': return NS_VK_COMMA;
+    case '.': return NS_VK_PERIOD;
+    case '/': return NS_VK_SLASH;
+    case '`': return NS_VK_BACK_QUOTE;
+    case '[': return NS_VK_OPEN_BRACKET;
+    case '\\': return NS_VK_BACK_SLASH;
+    case ']': return NS_VK_CLOSE_BRACKET;
+    case '\'': return NS_VK_QUOTE;
+  }
+  return 0;