Merge last green changeset from mozilla-inbound to mozilla-central
authorMarco Bonardo <mbonardo@mozilla.com>
Wed, 03 Aug 2011 10:46:22 +0200
changeset 73748 c35c69e1ce99c824b8eb18695b599251d3996dbb
parent 73696 3735fb1cd5ef45a6b2f41388856aa85c769dd17f (current diff)
parent 73747 07a247973487eaaa904f96d8c5b9c751d76c1a50 (diff)
child 73760 101411c3ca1c8c0f4570501f1f12b5c40e44d610
child 73768 1d1c771a3aba1dfafe8f8f66525af5c87d5820f5
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
milestone8.0a1
Merge last green changeset from mozilla-inbound to mozilla-central
browser/components/sessionstore/src/nsSessionStore.js
content/base/src/nsDocument.cpp
docshell/shistory/src/nsSHistory.cpp
dom/base/nsDOMClassInfo.cpp
extensions/spellcheck/hunspell/tests/suggestiontest/Makefile.orig
layout/generic/nsFrame.cpp
modules/libpref/src/init/all.js
--- a/content/base/public/nsIMozWebSocket.idl
+++ b/content/base/public/nsIMozWebSocket.idl
@@ -46,17 +46,17 @@ interface nsPIDOMWindow;
 
 /**
  * The nsIMozWebSocket interface enables Web applications to maintain
  * bidirectional communications with server-side processes as described in:
  *
  * http://dev.w3.org/html5/websockets/
  *
  */
-[scriptable, uuid(eae76132-e4a4-4765-8494-5e136521c846)]
+[scriptable, uuid(662691db-2b99-4461-801b-fbb72d99a4b9)]
 interface nsIMozWebSocket : nsISupports
 {
   readonly attribute DOMString url;
   readonly attribute DOMString protocol;
 
   //ready state
   const unsigned short CONNECTING = 0;
   const unsigned short OPEN = 1;
@@ -74,17 +74,17 @@ interface nsIMozWebSocket : nsISupports
 
   /**
    * Transmits data using the connection.
    *
    * @param data The data to be transmited.
    * @return if the connection is still established (and the data was queued or
    *         sent successfully).
    */
-  boolean send(in DOMString data);
+  void send(in DOMString data);
 
   /**
    * Closes the Web Socket connection or connection attempt, if any.
    * If the connection is already closed, it does nothing.
    */
   void close();
 
   /**
--- a/content/base/public/nsTreeSanitizer.h
+++ b/content/base/public/nsTreeSanitizer.h
@@ -132,20 +132,21 @@ class NS_STACK_CLASS nsTreeSanitizer {
 
     /**
      * Remove the named URL attribute from the element if the URL fails a
      * security check.
      *
      * @param aElement the element whose attribute to possibly modify
      * @param aNamespace the namespace of the URL attribute
      * @param aLocalName the local name of the URL attribute
+     * @return true if the attribute was removed and false otherwise
      */
-    void SanitizeURL(mozilla::dom::Element* aElement,
-                     PRInt32 aNamespace,
-                     nsIAtom* aLocalName);
+    PRBool SanitizeURL(mozilla::dom::Element* aElement,
+                       PRInt32 aNamespace,
+                       nsIAtom* aLocalName);
 
     /**
      * Checks a style rule for the presence of the 'binding' CSS property and
      * removes that property from the rule and reserializes in case the
      * property was found.
      *
      * @param aRule The style rule to check
      * @param aRuleText the serialized mutated rule if the method returns true
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -1575,16 +1575,18 @@ nsDocument::~nsDocument()
     PR_LOG(gDocumentLeakPRLog, PR_LOG_DEBUG,
            ("DOCUMENT %p destroyed", this));
 #endif
 
 #ifdef DEBUG
   nsCycleCollector_DEBUG_wasFreed(static_cast<nsIDocument*>(this));
 #endif
 
+  NS_ASSERTION(!mIsShowing, "Destroying a currently-showing document");
+
   mInDestructor = PR_TRUE;
   mInUnlinkOrDeletion = PR_TRUE;
 
   // Clear mObservers to keep it in sync with the mutationobserver list
   mObservers.Clear();
 
   if (mStyleSheetSetList) {
     mStyleSheetSetList->Disconnect();
--- a/content/base/src/nsTreeSanitizer.cpp
+++ b/content/base/src/nsTreeSanitizer.cpp
@@ -1222,17 +1222,22 @@ nsTreeSanitizer::SanitizeAttributes(mozi
           }
         }
         continue;
       }
       if (aAllowDangerousSrc && nsGkAtoms::src == attrLocal) {
         continue;
       }
       if (IsURL(aURLs, attrLocal)) {
-        SanitizeURL(aElement, attrNs, attrLocal);
+        if (SanitizeURL(aElement, attrNs, attrLocal)) {
+          // in case the attribute removal shuffled the attribute order, start
+          // the loop again.
+          --ac;
+          i = ac; // i will be decremented immediately thanks to the for loop
+        }
         continue;
       }
       if (aAllowed->GetEntry(attrLocal) &&
           !(attrLocal == nsGkAtoms::rel &&
             aElement->IsHTML(nsGkAtoms::link)) &&
           !(attrLocal == nsGkAtoms::name &&
             aElement->IsHTML(nsGkAtoms::meta))) {
         // name="" and rel="" are whitelisted, but treat them as blacklisted
@@ -1247,26 +1252,36 @@ nsTreeSanitizer::SanitizeAttributes(mozi
       if (*localStr == '_' || (attrLocal->GetLength() > 5 && localStr[0] == 'd'
           && localStr[1] == 'a' && localStr[2] == 't' && localStr[3] == 'a'
           && localStr[4] == '-')) {
         continue;
       }
       // else not allowed
     } else if (kNameSpaceID_XML == attrNs) {
       if (nsGkAtoms::base == attrLocal) {
-        SanitizeURL(aElement, attrNs, attrLocal);
+        if (SanitizeURL(aElement, attrNs, attrLocal)) {
+          // in case the attribute removal shuffled the attribute order, start
+          // the loop again.
+          --ac;
+          i = ac; // i will be decremented immediately thanks to the for loop
+        }
         continue;
       }
       if (nsGkAtoms::lang == attrLocal || nsGkAtoms::space == attrLocal) {
         continue;
       }
       // else not allowed
     } else if (aAllowXLink && kNameSpaceID_XLink == attrNs) {
       if (nsGkAtoms::href == attrLocal) {
-        SanitizeURL(aElement, attrNs, attrLocal);
+        if (SanitizeURL(aElement, attrNs, attrLocal)) {
+          // in case the attribute removal shuffled the attribute order, start
+          // the loop again.
+          --ac;
+          i = ac; // i will be decremented immediately thanks to the for loop
+        }
         continue;
       }
       if (nsGkAtoms::type == attrLocal || nsGkAtoms::title == attrLocal
           || nsGkAtoms::show == attrLocal || nsGkAtoms::actuate == attrLocal) {
         continue;
       }
       // else not allowed
     }
@@ -1283,17 +1298,17 @@ nsTreeSanitizer::SanitizeAttributes(mozi
       aElement->IsHTML(nsGkAtoms::audio)) {
     aElement->SetAttr(kNameSpaceID_None,
                       nsGkAtoms::controls,
                       EmptyString(),
                       PR_FALSE);
   }
 }
 
-void
+PRBool
 nsTreeSanitizer::SanitizeURL(mozilla::dom::Element* aElement,
                              PRInt32 aNamespace,
                              nsIAtom* aLocalName)
 {
   nsAutoString value;
   aElement->GetAttr(aNamespace, aLocalName, value);
 
   // Get value and remove mandatory quotes
@@ -1307,17 +1322,19 @@ nsTreeSanitizer::SanitizeURL(mozilla::do
   nsCOMPtr<nsIURI> baseURI = aElement->GetBaseURI();
   nsCOMPtr<nsIURI> attrURI;
   nsresult rv = NS_NewURI(getter_AddRefs(attrURI), v, nsnull, baseURI);
   if (NS_SUCCEEDED(rv)) {
     rv = secMan->CheckLoadURIWithPrincipal(sNullPrincipal, attrURI, flags);
   }
   if (NS_FAILED(rv)) {
     aElement->UnsetAttr(aNamespace, aLocalName, PR_FALSE);
+    return PR_TRUE;
   }
+  return PR_FALSE;
 }
 
 void
 nsTreeSanitizer::Sanitize(nsIContent* aFragment) {
   // If you want to relax these preconditions, be sure to check the code in
   // here that notifies / does not notify or that fires mutation events if
   // in tree.
   NS_PRECONDITION(aFragment->IsNodeOfType(nsINode::eDOCUMENT_FRAGMENT),
--- a/content/base/src/nsWebSocket.cpp
+++ b/content/base/src/nsWebSocket.cpp
@@ -370,17 +370,16 @@ nsWebSocketEstablishedConnection::Close(
   if (!mOwner)
     return NS_OK;
 
   // Disconnect() can release this object, so we keep a
   // reference until the end of the method
   nsRefPtr<nsWebSocketEstablishedConnection> kungfuDeathGrip = this;
 
   if (mOwner->mReadyState == nsIMozWebSocket::CONNECTING) {
-    mOwner->SetReadyState(nsIMozWebSocket::CLOSING);
     mOwner->SetReadyState(nsIMozWebSocket::CLOSED);
     Disconnect();
     return NS_OK;
   }
 
   mOwner->SetReadyState(nsIMozWebSocket::CLOSING);
 
   if (mStatus == CONN_CLOSED) {
@@ -412,17 +411,17 @@ nsWebSocketEstablishedConnection::Consol
                           NS_LITERAL_STRING("connectionFailure").get(),
                           formatStrings, NS_ARRAY_LENGTH(formatStrings));
     } else {
       PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
                           NS_LITERAL_STRING("netInterrupt").get(),
                           formatStrings, NS_ARRAY_LENGTH(formatStrings));
     }
   }
-  /// todo some sepcific errors - like for message too large
+  /// todo some specific errors - like for message too large
   return rv;
 }
 
 
 nsresult
 nsWebSocketEstablishedConnection::FailConnection()
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
@@ -760,19 +759,22 @@ nsWebSocket::EstablishConnection()
   NS_ABORT_IF_FALSE(!mConnection, "mConnection should be null");
 
   nsresult rv;
 
   nsRefPtr<nsWebSocketEstablishedConnection> conn =
     new nsWebSocketEstablishedConnection();
 
   rv = conn->Init(this);
-  NS_ENSURE_SUCCESS(rv, rv);
-
   mConnection = conn;
+  if (NS_FAILED(rv)) {
+    Close();
+    mConnection = nsnull;
+    return rv;
+  }
 
   return NS_OK;
 }
 
 class nsWSCloseEvent : public nsRunnable
 {
 public:
   nsWSCloseEvent(nsWebSocket *aWebSocket, PRBool aWasClean)
@@ -1231,20 +1233,19 @@ nsWebSocket::GetBufferedAmount(PRUint32 
   }
 
 NS_WEBSOCKET_IMPL_DOMEVENTLISTENER(open, mOnOpenListener)
 NS_WEBSOCKET_IMPL_DOMEVENTLISTENER(error, mOnErrorListener)
 NS_WEBSOCKET_IMPL_DOMEVENTLISTENER(message, mOnMessageListener)
 NS_WEBSOCKET_IMPL_DOMEVENTLISTENER(close, mOnCloseListener)
 
 NS_IMETHODIMP
-nsWebSocket::Send(const nsAString& aData, PRBool *aRet)
+nsWebSocket::Send(const nsAString& aData)
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
-  *aRet = PR_FALSE;
 
   if (mReadyState == nsIMozWebSocket::CONNECTING) {
     return NS_ERROR_DOM_INVALID_STATE_ERR;
   }
 
   // Check for unpaired surrogates.
   PRUint32 i, length = aData.Length();
   for (i = 0; i < length; ++i) {
@@ -1261,18 +1262,17 @@ nsWebSocket::Send(const nsAString& aData
   }
 
   if (mReadyState == nsIMozWebSocket::CLOSING ||
       mReadyState == nsIMozWebSocket::CLOSED) {
     mOutgoingBufferedAmount += NS_ConvertUTF16toUTF8(aData).Length();
     return NS_OK;
   }
 
-  nsresult rv = mConnection->PostMessage(PromiseFlatString(aData));
-  *aRet = NS_SUCCEEDED(rv);
+  mConnection->PostMessage(PromiseFlatString(aData));
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsWebSocket::Close()
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
--- a/content/media/ogg/nsOggCodecState.cpp
+++ b/content/media/ogg/nsOggCodecState.cpp
@@ -673,16 +673,22 @@ nsresult nsVorbisState::ReconstructVorbi
       blockSize = 0;
       mPrevVorbisBlockSize = 0;
     }
     long samples = mPrevVorbisBlockSize / 4 + blockSize / 4;
     mPrevVorbisBlockSize = blockSize;
     if (packet->granulepos == -1) {
       packet->granulepos = mGranulepos + samples;
     }
+
+    // Account for a partial last frame
+    if (packet->e_o_s && packet->granulepos >= mGranulepos) {
+       samples = packet->granulepos - mGranulepos;
+    }
+ 
     mGranulepos = packet->granulepos;
     RecordVorbisPacketSamples(packet, samples);
     return NS_OK;
   }
 
   PRBool unknownGranulepos = last->granulepos == -1;
   int totalSamples = 0;
   for (PRInt32 i = mUnstamped.Length() - 1; i > 0; i--) {
--- a/content/smil/nsSMILAnimationController.cpp
+++ b/content/smil/nsSMILAnimationController.cpp
@@ -89,18 +89,18 @@ nsSMILAnimationController::~nsSMILAnimat
 }
 
 void
 nsSMILAnimationController::Disconnect()
 {
   NS_ABORT_IF_FALSE(mDocument, "disconnecting when we weren't connected...?");
   NS_ABORT_IF_FALSE(mRefCnt.get() == 1,
                     "Expecting to disconnect when doc is sole remaining owner");
-  NS_ABORT_IF_FALSE(mPauseState & nsSMILTimeContainer::PAUSE_PAGEHIDE,
-                    "Expecting to be paused for pagehide before disconnect");
+  NS_ASSERTION(mPauseState & nsSMILTimeContainer::PAUSE_PAGEHIDE,
+               "Expecting to be paused for pagehide before disconnect");
 
   StopSampling(GetRefreshDriver());
 
   mDocument = nsnull; // (raw pointer)
 }
 
 //----------------------------------------------------------------------
 // nsSMILTimeContainer methods:
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -89,16 +89,17 @@
 #include "nsIDOMKeyEvent.h"
 #include "nsIDOMEventListener.h"
 #include "nsIDOMPopStateEvent.h"
 #include "nsIDOMHashChangeEvent.h"
 #include "nsContentUtils.h"
 #include "nsDOMWindowUtils.h"
 #include "nsIDOMGlobalPropertyInitializer.h"
 #include "mozilla/Preferences.h"
+#include "nsLocation.h"
 
 // Window scriptable helper includes
 #include "nsIDocShell.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIDocShellTreeNode.h"
 #include "nsIScriptExternalNameSet.h"
 #include "nsJSUtils.h"
 #include "nsIInterfaceRequestor.h"
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -237,16 +237,17 @@
 
 #include "mozilla/dom/indexedDB/IDBFactory.h"
 #include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
 
 #include "nsRefreshDriver.h"
 #include "mozAutoDocUpdate.h"
 
 #include "mozilla/Telemetry.h"
+#include "nsLocation.h"
 
 #ifdef PR_LOGGING
 static PRLogModuleInfo* gDOMLeakPRLog;
 #endif
 
 static const char kStorageEnabled[] = "dom.storage.enabled";
 
 using namespace mozilla;
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -61,17 +61,16 @@
 #include "nsIBrowserDOMWindow.h"
 #include "nsIDocShellTreeOwner.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIDOMClientInformation.h"
 #include "nsIDOMEventTarget.h"
 #include "nsIDOMNavigator.h"
 #include "nsIDOMNavigatorGeolocation.h"
 #include "nsIDOMNavigatorDesktopNotification.h"
-#include "nsIDOMLocation.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIDOMJSWindow.h"
 #include "nsIDOMChromeWindow.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIScriptContext.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIScriptTimeoutHandler.h"
@@ -1085,55 +1084,14 @@ protected:
   nsIDocShell* mDocShell; // weak reference
 };
 
 nsresult NS_GetNavigatorUserAgent(nsAString& aUserAgent);
 nsresult NS_GetNavigatorPlatform(nsAString& aPlatform);
 nsresult NS_GetNavigatorAppVersion(nsAString& aAppVersion);
 nsresult NS_GetNavigatorAppName(nsAString& aAppName);
 
-class nsIURI;
-
-//*****************************************************************************
-// nsLocation: Script "location" object
-//*****************************************************************************
-
-class nsLocation : public nsIDOMLocation
-{
-public:
-  nsLocation(nsIDocShell *aDocShell);
-  virtual ~nsLocation();
-
-  NS_DECL_ISUPPORTS
-
-  void SetDocShell(nsIDocShell *aDocShell);
-  nsIDocShell *GetDocShell();
-
-  // nsIDOMLocation
-  NS_DECL_NSIDOMLOCATION
-
-protected:
-  // In the case of jar: uris, we sometimes want the place the jar was
-  // fetched from as the URI instead of the jar: uri itself.  Pass in
-  // PR_TRUE for aGetInnermostURI when that's the case.
-  nsresult GetURI(nsIURI** aURL, PRBool aGetInnermostURI = PR_FALSE);
-  nsresult GetWritableURI(nsIURI** aURL);
-  nsresult SetURI(nsIURI* aURL, PRBool aReplace = PR_FALSE);
-  nsresult SetHrefWithBase(const nsAString& aHref, nsIURI* aBase,
-                           PRBool aReplace);
-  nsresult SetHrefWithContext(JSContext* cx, const nsAString& aHref,
-                              PRBool aReplace);
-
-  nsresult GetSourceBaseURL(JSContext* cx, nsIURI** sourceURL);
-  nsresult GetSourceDocument(JSContext* cx, nsIDocument** aDocument);
-
-  nsresult CheckURL(nsIURI *url, nsIDocShellLoadInfo** aLoadInfo);
-
-  nsString mCachedHash;
-  nsWeakPtr mDocShell;
-};
-
 /* factory function */
 nsresult
 NS_NewScriptGlobalObject(PRBool aIsChrome, PRBool aIsModalContentWindow,
                          nsIScriptGlobalObject **aResult);
 
 #endif /* nsGlobalWindow_h___ */
--- a/dom/base/nsLocation.cpp
+++ b/dom/base/nsLocation.cpp
@@ -33,17 +33,17 @@
  * 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 "nsGlobalWindow.h"
+#include "nsLocation.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIScriptContext.h"
 #include "nsIDocShell.h"
 #include "nsIDocShellLoadInfo.h"
 #include "nsIWebNavigation.h"
 #include "nsCDefaultURIFixup.h"
 #include "nsIURIFixup.h"
 #include "nsIURL.h"
@@ -51,32 +51,32 @@
 #include "nsIIOService.h"
 #include "nsIServiceManager.h"
 #include "nsNetUtil.h"
 #include "plstr.h"
 #include "prprf.h"
 #include "prmem.h"
 #include "nsCOMPtr.h"
 #include "nsEscape.h"
-#include "nsJSUtils.h"
 #include "nsIDOMWindow.h"
 #include "nsIDOMDocument.h"
 #include "nsIDocument.h"
 #include "nsIPresShell.h"
 #include "nsPresContext.h"
 #include "nsIJSContextStack.h"
 #include "nsXPIDLString.h"
 #include "nsDOMError.h"
 #include "nsDOMClassInfo.h"
 #include "nsCRT.h"
 #include "nsIProtocolHandler.h"
 #include "nsReadableUtils.h"
 #include "nsITextToSubURI.h"
 #include "nsContentUtils.h"
 #include "nsJSUtils.h"
+#include "jsdbgapi.h"
 
 static nsresult
 GetContextFromStack(nsIJSContextStack *aStack, JSContext **aContext)
 {
   nsCOMPtr<nsIJSContextStackIterator>
     iterator(do_CreateInstance("@mozilla.org/js/xpc/ContextStackIterator;1"));
   NS_ENSURE_TRUE(iterator, NS_ERROR_FAILURE);
 
new file mode 100644
--- /dev/null
+++ b/dom/base/nsLocation.h
@@ -0,0 +1,97 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sw=2 et tw=80: */
+/* ***** 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 mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Travis Bogard <travis@netscape.com>
+ *   Dan Rosen <dr@netscape.com>
+ *   Vidur Apparao <vidur@netscape.com>
+ *   Johnny Stenback <jst@netscape.com>
+ *
+ * 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 nsLocation_h__
+#define nsLocation_h__
+
+#include "nsIDOMLocation.h"
+#include "nsString.h"
+#include "nsWeakReference.h"
+
+
+class nsIURI;
+class nsIDocShell;
+struct JSContext;
+class nsIDocument;
+class nsIDocShellLoadInfo;
+
+//*****************************************************************************
+// nsLocation: Script "location" object
+//*****************************************************************************
+
+class nsLocation : public nsIDOMLocation
+{
+public:
+  nsLocation(nsIDocShell *aDocShell);
+  virtual ~nsLocation();
+
+  NS_DECL_ISUPPORTS
+
+  void SetDocShell(nsIDocShell *aDocShell);
+  nsIDocShell *GetDocShell();
+
+  // nsIDOMLocation
+  NS_DECL_NSIDOMLOCATION
+
+protected:
+  // In the case of jar: uris, we sometimes want the place the jar was
+  // fetched from as the URI instead of the jar: uri itself.  Pass in
+  // PR_TRUE for aGetInnermostURI when that's the case.
+  nsresult GetURI(nsIURI** aURL, PRBool aGetInnermostURI = PR_FALSE);
+  nsresult GetWritableURI(nsIURI** aURL);
+  nsresult SetURI(nsIURI* aURL, PRBool aReplace = PR_FALSE);
+  nsresult SetHrefWithBase(const nsAString& aHref, nsIURI* aBase,
+                           PRBool aReplace);
+  nsresult SetHrefWithContext(JSContext* cx, const nsAString& aHref,
+                              PRBool aReplace);
+
+  nsresult GetSourceBaseURL(JSContext* cx, nsIURI** sourceURL);
+  nsresult GetSourceDocument(JSContext* cx, nsIDocument** aDocument);
+
+  nsresult CheckURL(nsIURI *url, nsIDocShellLoadInfo** aLoadInfo);
+
+  nsString mCachedHash;
+  nsWeakPtr mDocShell;
+};
+
+#endif // nsLocation_h__
+
--- a/dom/interfaces/css/nsIDOMCSS2Properties.idl
+++ b/dom/interfaces/css/nsIDOMCSS2Properties.idl
@@ -46,17 +46,17 @@
  * The nsIDOMCSS2Properties interface is a datatype for additional
  * reflection of data already provided in nsIDOMCSSStyleDeclaration in
  * the Document Object Model.
  *
  * For more information on this interface please see
  * http://www.w3.org/TR/DOM-Level-2-Style
  */
 
-[scriptable, uuid(7cf11a5f-4be5-4e31-b427-58d82746b5f5)]
+[scriptable, uuid(10f43750-b379-11e0-aff2-0800200c9a66)]
 interface nsIDOMCSS2Properties : nsISupports
 {
            attribute DOMString        background;
                                         // raises(DOMException) on setting
 
            attribute DOMString        backgroundAttachment;
                                         // raises(DOMException) on setting
 
@@ -676,16 +676,25 @@ interface nsIDOMCSS2Properties : nsISupp
                                         // raises(DOMException) on setting
 
            attribute DOMString        MozTransform;
                                         // raises(DOMException) on setting
 
            attribute DOMString        MozTransformOrigin;
                                         // raises(DOMException) on setting 
 
+           attribute DOMString        MozPerspective;
+                                        // raises(DOMException) on setting
+
+           attribute DOMString        MozPerspectiveOrigin;
+                                        // raises(DOMException) on setting
+
+           attribute DOMString        MozBackfaceVisibility;
+                                        // raises(DOMException) on setting 
+
            attribute DOMString        MozWindowShadow;
                                         // raises(DOMException) on setting
 
            attribute DOMString        backgroundSize;
                                         // raises(DOMException) on setting
 
            attribute DOMString        MozTextBlink;
                                         // raises(DOMException) on setting
--- a/dom/tests/mochitest/bugs/test_resize_move_windows.html
+++ b/dom/tests/mochitest/bugs/test_resize_move_windows.html
@@ -77,96 +77,85 @@ function hitEventLoop(condition, test, t
   }
 
   setTimeout(hitEventLoop, 0, condition, test, times - 1, next);
 }
 
 function checkChangeIsDisabled(aWindow, aNext)
 {
   // We want to check that nothing has changed. Having a high value would take
-  // too much time.
-  var hits = 10;
+  // too much time. Worse thing that could happen is random green.
+  var hits = 5;
 
   var originalWidth = aWindow.innerWidth;
   var originalHeight = aWindow.innerHeight;
 
   var originalX = aWindow.screenX;
   var originalY = aWindow.screenY;
 
   var oWidth = aWindow.outerWidth;
   var oHeight = aWindow.outerHeight;
 
-  function sizeChangeCondition() {
-    return aWindow.innerWidth != originalWidth || aWindow.innerHeight != originalHeight;
-  }
-
-  function sizeChangeTest() {
-    is(aWindow.innerWidth, originalWidth, "Window width shouldn't have changed");
-    is(aWindow.innerHeight, originalHeight, "Window height shouldn't have changed");
+  function changeCondition() {
+    return aWindow.innerWidth != originalWidth ||
+           aWindow.innerHeight != originalHeight ||
+           aWindow.screenX != originalX || aWindow.screenY != originalY ||
+           aWindow.outerWidth != oWidth || aWindow.outerHeight != oHeight;
   }
 
-  function posChangeCondition() {
-    return aWindow.screenX != originalX || aWindow.screenY != originalY;
-  }
-
-  function posChangeTest() {
+  function changeTest() {
+    is(aWindow.innerWidth, originalWidth, "Window width shouldn't have changed");
+    is(aWindow.innerHeight, originalHeight, "Window height shouldn't have changed");
     is(aWindow.screenX, originalX, "Window x position shouldn't have changed");
     is(aWindow.screenY, originalY, "Window y position shouldn't have changed");
-  }
-
-  function outerChangeCondition() {
-    return aWindow.outerWidth != oWidth || aWindow.outerHeight != oHeight;
-  }
-
-  function outerChangeTest() {
     is(aWindow.outerWidth, oWidth, "Window outerWidth shouldn't have changed");
     is(aWindow.outerHeight, oHeight, "Window outerHeight shouldn't have changed");
   }
 
   /**
-   * Size checks.
+   * Size changes.
    */
-  aWindow.innerWidth = getNewWidth(aWindow);
-  aWindow.innerHeight = getNewHeight(aWindow);
-  hitEventLoop(sizeChangeCondition, sizeChangeTest, hits, function () {
-    aWindow.resizeTo(getNewWidth(aWindow), getNewHeight(aWindow));
+  var newWidth = getNewWidth(aWindow);
+  var newHeight = getNewHeight(aWindow);
+
+  aWindow.innerWidth = newWidth;
+  aWindow.innerHeight = newHeight;
 
-  hitEventLoop(sizeChangeCondition, sizeChangeTest, hits, function () {
-    aWindow.resizeBy(getNewWidth(aWindow) - aWindow.innerWidth,
-                     getNewHeight(aWindow) - aWindow.innerHeight);
+  aWindow.resizeTo(newWidth, newHeight);
 
-  hitEventLoop(sizeChangeCondition, sizeChangeTest, hits, function () {
-    aWindow.sizeToContent();
+  aWindow.resizeBy(newWidth - aWindow.innerWidth,
+                   newHeight - aWindow.innerHeight);
 
-  hitEventLoop(sizeChangeCondition, sizeChangeTest, hits, function () {
+  aWindow.sizeToContent();
+
   /**
    * Position checks.
    */
-    aWindow.screenX = getNewX(aWindow);
-    aWindow.screenY = getNewY(aWindow);
-  hitEventLoop(posChangeCondition, posChangeTest, hits, function () {
-    aWindow.moveTo(getNewX(aWindow), getNewY(aWindow));
-  hitEventLoop(posChangeCondition, posChangeTest, hits, function () {
-    aWindow.moveBy(getNewX(aWindow) - aWindow.screenX,
-                   getNewY(aWindow) - aWindow.screenY);
-  hitEventLoop(posChangeCondition, posChangeTest, hits, function () {
+  var newX = getNewX(aWindow);
+  var newY = getNewY(aWindow);
+
+  aWindow.screenX = newX;
+  aWindow.screenY = newY;
+
+  aWindow.moveTo(newX, newY);
+
+  aWindow.moveBy(newX - aWindow.screenX,
+                 newY - aWindow.screenY);
+
   /**
    * Outer width/height checks.
    */
-    aWindow.outerWidth *= 2;
-    aWindow.outerHeight *= 2;
+  aWindow.outerWidth *= 2;
+  aWindow.outerHeight *= 2;
 
-  hitEventLoop(outerChangeCondition, outerChangeTest, hits, aNext);
-  });
-  });
-  });
-  });
-  });
-  });
-  });
+  // We did a lot of changes. Now, we are going to wait and see if something
+  // happens.
+  // NOTE: if this happens to fail, you will have to check manually which
+  // operation has been accepted.
+  hitEventLoop(changeCondition, changeTest, hits, aNext);
 }
 
 function checkChangeIsEnabled(aWindow, aNext)
 {
   // Something should happen. We are not going to go to the next test until
   // it does.
   var hits = -1;
 
--- a/editor/libeditor/html/nsHTMLEditRules.cpp
+++ b/editor/libeditor/html/nsHTMLEditRules.cpp
@@ -6766,41 +6766,48 @@ nsHTMLEditRules::ReturnInListItem(nsISel
   nsresult res = NS_OK;
   
   nsCOMPtr<nsIDOMNode> listitem;
   
   // sanity check
   NS_PRECONDITION(PR_TRUE == nsHTMLEditUtils::IsListItem(aListItem),
                   "expected a list item and didn't get one");
   
+  // get the listitem parent and the active editing host.
+  nsIContent* rootContent = mHTMLEditor->GetActiveEditingHost();
+  nsCOMPtr<nsIDOMNode> rootNode = do_QueryInterface(rootContent);
+  nsCOMPtr<nsIDOMNode> list;
+  PRInt32 itemOffset;
+  res = nsEditor::GetNodeLocation(aListItem, address_of(list), &itemOffset);
+  NS_ENSURE_SUCCESS(res, res);
+
   // if we are in an empty listitem, then we want to pop up out of the list
+  // but only if prefs says it's ok and if the parent isn't the active editing host.
   PRBool isEmpty;
   res = IsEmptyBlock(aListItem, &isEmpty, PR_TRUE, PR_FALSE);
   NS_ENSURE_SUCCESS(res, res);
-  if (isEmpty && mReturnInEmptyLIKillsList)   // but only if prefs says it's ok
-  {
-    nsCOMPtr<nsIDOMNode> list, listparent;
-    PRInt32 offset, itemOffset;
-    res = nsEditor::GetNodeLocation(aListItem, address_of(list), &itemOffset);
-    NS_ENSURE_SUCCESS(res, res);
-    res = nsEditor::GetNodeLocation(list, address_of(listparent), &offset);
-    NS_ENSURE_SUCCESS(res, res);
-    
+  if (isEmpty && (rootNode != list) && mReturnInEmptyLIKillsList)
+  {
     // are we the last list item in the list?
     PRBool bIsLast;
     res = mHTMLEditor->IsLastEditableChild(aListItem, &bIsLast);
     NS_ENSURE_SUCCESS(res, res);
     if (!bIsLast)
     {
       // we need to split the list!
       nsCOMPtr<nsIDOMNode> tempNode;
       res = mHTMLEditor->SplitNode(list, itemOffset, getter_AddRefs(tempNode));
       NS_ENSURE_SUCCESS(res, res);
     }
+
     // are we in a sublist?
+    nsCOMPtr<nsIDOMNode> listparent;
+    PRInt32 offset;
+    res = nsEditor::GetNodeLocation(list, address_of(listparent), &offset);
+    NS_ENSURE_SUCCESS(res, res);
     if (nsHTMLEditUtils::IsList(listparent))  //in a sublist
     {
       // if so, move this list item out of this list and into the grandparent list
       res = mHTMLEditor->MoveNode(aListItem,listparent,offset+1);
       NS_ENSURE_SUCCESS(res, res);
       res = aSelection->Collapse(aListItem,0);
     }
     else
--- a/editor/libeditor/html/tests/Makefile.in
+++ b/editor/libeditor/html/tests/Makefile.in
@@ -50,17 +50,17 @@ include $(topsrcdir)/config/rules.mk
 		test_bug332636.html^headers^ \
 		test_bug366682.html \
 		test_bug372345.html \
 		test_bug410986.html \
 		test_bug414526.html \
 		test_bug417418.html \
 		test_bug432225.html \
 		test_bug439808.html \
- 		test_bug449243.html \
+		test_bug449243.html \
 		test_bug455992.html \
 		test_bug456244.html \
 		test_bug460740.html \
 		test_bug478725.html \
 		test_bug480972.html \
 		test_bug484181.html \
 		test_bug487524.html \
 		test_bug520189.html \
@@ -76,16 +76,17 @@ include $(topsrcdir)/config/rules.mk
 		test_bug607584.html \
 		test_bug611182.html \
 		test_bug612447.html \
 		test_bug620906.html \
 		test_bug622371.html \
 		test_bug629845.html \
 		test_bug640321.html \
 		test_bug668599.html \
+		test_bug674861.html \
 		test_CF_HTML_clipboard.html \
 		test_contenteditable_focus.html \
 		test_htmleditor_keyevent_handling.html \
 		test_select_all_without_body.html \
 		file_select_all_without_body.html \
 		test_root_element_replacement.html \
 		$(NULL)
 
new file mode 100644
--- /dev/null
+++ b/editor/libeditor/html/tests/test_bug674861.html
@@ -0,0 +1,120 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=674861
+-->
+<head>
+  <title>Test for Bug 674861</title>
+  <script type="application/javascript" src="/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=674861">Mozilla Bug 674861</a>
+<p id="display"></p>
+<div id="content">
+  <h2> Bullet List </h2>
+  <ul contenteditable>
+    <li> item 1 </li>
+    <li> item 2 </li>
+    <li> item 3 </li>
+  </ul>
+
+  <h2> Ordered List </h2>
+  <ol contenteditable>
+    <li> item 1 </li>
+    <li> item 2 </li>
+    <li> item 3 </li>
+  </ol>
+
+  <h2> Definition List </h2>
+  <dl contenteditable>
+    <dt> term 1 </dt>
+    <dd> definition 1 </dd>
+    <dt> term 2 </dt>
+    <dd> definition 2 </dd>
+    <dt> term 3 </dt>
+    <dd> definition 3 </dd>
+  </dl>
+</div>
+
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 674861 **/
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(runTests);
+
+const CARET_BEGIN  = 0;
+const CARET_MIDDLE = 1;
+const CARET_END    = 2;
+
+function try2split(element, caretPos) {
+  // compute the requested position
+  var len = element.textContent.length;
+  var pos = -1;
+  switch (caretPos) {
+    case CARET_BEGIN:
+      pos = 0;
+      break;
+    case CARET_MIDDLE:
+      pos = Math.floor(len/2);
+      break;
+    case CARET_END:
+      pos = len;
+      break;
+  }
+
+  // put the caret on the requested position
+  var sel = window.getSelection();
+  for (var i = 0; i < sel.rangeCount; i++) {
+    var range = sel.getRangeAt(i);
+    sel.removeRange(range);
+  }
+  range = document.createRange();
+  range.setStart(element.firstChild, pos);
+  range.setEnd(element.firstChild, pos);
+  sel.addRange(range);
+  
+  // simulates two [Return] keypresses
+  synthesizeKey("VK_RETURN", {});
+  synthesizeKey("VK_RETURN", {});
+}
+
+function runTests() {
+  const ul = document.querySelector("#content ul");
+  const ol = document.querySelector("#content ol");
+  const dl = document.querySelector("#content dl");
+
+  // bullet list
+  ul.focus();
+  try2split(ul.querySelector("li"), CARET_END);
+  is(document.querySelectorAll("#content ul").length, 1,
+    "The <ul> list should not be splittable.");
+  is(ul.querySelectorAll("li").length, 5,
+    "Two new <li> elements should have been created.");
+
+  // ordered list
+  ol.focus();
+  try2split(ol.querySelector("li"), CARET_END);
+  is(document.querySelectorAll("#content ol").length, 1,
+    "The <ol> list should not be splittable.");
+  is(ol.querySelectorAll("li").length, 5,
+    "Two new <li> elements should have been created.");
+
+  // definition list
+  dl.focus();
+  try2split(dl.querySelector("dd"), CARET_END);
+  is(document.querySelectorAll("#content dl").length, 1,
+    "The <dl> list should not be splittable.");
+  is(dl.querySelectorAll("dt").length, 5,
+    "Two new <dt> elements should have been created.");
+
+  // done
+  SimpleTest.finish();
+}
+</script>
+</pre>
+</body>
+</html>
--- a/embedding/android/GeckoAppShell.java
+++ b/embedding/android/GeckoAppShell.java
@@ -1162,17 +1162,17 @@ public class GeckoAppShell
             appearance.recycle();
         }
 
         return result;
     }
 
     public static void putChildInBackground() {
         try {
-            File cgroupFile = new File("/proc" + android.os.Process.myPid() + "/cgroup");
+            File cgroupFile = new File("/proc/" + android.os.Process.myPid() + "/cgroup");
             BufferedReader br = new BufferedReader(new FileReader(cgroupFile));
             String[] cpuLine = br.readLine().split("/");
             br.close();
             final String backgroundGroup = cpuLine.length == 2 ? cpuLine[1] : "";
             GeckoProcessesVisitor visitor = new GeckoProcessesVisitor() {
                 public boolean callback(int pid) {
                     if (pid != android.os.Process.myPid()) {
                         try {
deleted file mode 100644
--- a/extensions/spellcheck/hunspell/tests/suggestiontest/Makefile.orig
+++ /dev/null
@@ -1,11 +0,0 @@
-all:
-	./prepare
-	./test
-
-single:
-	./prepare2
-	./test
-
-clean:
-	rm *.[1-5] result.*
-
new file mode 100644
--- /dev/null
+++ b/gfx/2d/BasePoint3D.h
@@ -0,0 +1,137 @@
+/* -*- 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 Mozilla Corporation code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Matt Woodrow <mwoodrow@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either 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_BASEPOINT3D_H_
+#define MOZILLA_BASEPOINT3D_H_
+
+namespace mozilla {
+namespace gfx {
+
+/**
+ * Do not use this class directly. Subclass it, pass that subclass as the
+ * Sub parameter, and only use that subclass. This allows methods to safely
+ * cast 'this' to 'Sub*'.
+ */
+template <class T, class Sub>
+struct BasePoint3D {
+  T x, y, z;
+
+  // Constructors
+  BasePoint3D() : x(0), y(0), z(0) {}
+  BasePoint3D(T aX, T aY, T aZ) : x(aX), y(aY), z(aZ) {}
+
+  void MoveTo(T aX, T aY, T aZ) { x = aX; y = aY; z = aZ; }
+  void MoveBy(T aDx, T aDy, T aDz) { x += aDx; y += aDy; z += aDz; }
+
+  // Note that '=' isn't defined so we'll get the
+  // compiler generated default assignment operator
+
+  bool operator==(const Sub& aPoint) const {
+    return x == aPoint.x && y == aPoint.y && z == aPoint.z;
+  }
+  bool operator!=(const Sub& aPoint) const {
+    return x != aPoint.x || y != aPoint.y || z != aPoint.z;
+  }
+
+  Sub operator+(const Sub& aPoint) const {
+    return Sub(x + aPoint.x, y + aPoint.y, z + aPoint.z);
+  }
+  Sub operator-(const Sub& aPoint) const {
+    return Sub(x - aPoint.x, y - aPoint.y, z - aPoint.z);
+  }
+  Sub& operator+=(const Sub& aPoint) {
+    x += aPoint.x;
+    y += aPoint.y;
+    z += aPoint.z;
+    return *static_cast<Sub*>(this);
+  }
+  Sub& operator-=(const Sub& aPoint) {
+    x -= aPoint.x;
+    y -= aPoint.y;
+    z -= aPoint.z;
+    return *static_cast<Sub*>(this);
+  }
+
+  Sub operator*(T aScale) const {
+    return Sub(x * aScale, y * aScale, z * aScale);
+  }
+  Sub operator/(T aScale) const {
+    return Sub(x / aScale, y / aScale, z / aScale);
+  }
+
+  Sub& operator*=(T aScale) {
+    x *= aScale;
+    y *= aScale;
+    z *= aScale;
+    return *static_cast<Sub*>(this);
+  }
+
+  Sub& operator/=(T aScale) {
+      x /= aScale;
+      y /= aScale;
+      z /= aScale;
+      return *static_cast<Sub*>(this);
+  }
+
+  Sub operator-() const {
+    return Sub(-x, -y, -z);
+  }
+
+  Sub CrossProduct(const Sub& aPoint) const {
+      return Sub(y * aPoint.z - aPoint.y * z,
+                 z * aPoint.x - aPoint.z * x,
+                 x * aPoint.y - aPoint.x * y);
+  }
+
+  T DotProduct(const Sub& aPoint) const {
+      return x * aPoint.x + y * aPoint.y + z * aPoint.z;
+  }
+
+  T Length() const {
+      return sqrt(x*x + y*y + z*z);
+  }
+
+  // Invalid for points with distance from origin of 0.
+  void Normalize() {
+      *this /= Length();
+  }
+};
+
+}
+}
+
+#endif /* MOZILLA_BASEPOINT3D_H_ */
--- a/gfx/2d/Makefile.in
+++ b/gfx/2d/Makefile.in
@@ -46,16 +46,17 @@ MODULE		= gfx2d
 LIBRARY_NAME	= gfx2d
 LIBXUL_LIBRARY	= 1
 EXPORT_LIBRARY	= 1
 
 EXPORTS_NAMESPACES = mozilla/gfx
 EXPORTS_mozilla/gfx	= \
         2D.h \
         BasePoint.h \
+	BasePoint3D.h \
         BaseMargin.h \
         BaseRect.h \
         BaseSize.h \
         Point.h \
         Matrix.h \
         Rect.h \
         Types.h \
 	$(NULL)
--- a/gfx/cairo/cairo/src/cairo-quartz-surface.c
+++ b/gfx/cairo/cairo/src/cairo-quartz-surface.c
@@ -1101,16 +1101,17 @@ typedef struct {
     void *image_extra;
 } quartz_source_image_t;
 
 static void
 DataProviderReleaseCallback (void *info, const void *data, size_t size)
 {
     quartz_source_image_t *source_img = info;
     _cairo_surface_release_source_image (source_img->surface, source_img->image_out, source_img->image_extra);
+    cairo_surface_destroy (source_img->surface);
     free (source_img);
 }
 
 static cairo_status_t
 _cairo_surface_to_cgimage (cairo_surface_t *source,
 			   CGImageRef *image_out)
 {
     cairo_status_t status;
@@ -1140,20 +1141,21 @@ static cairo_status_t
             }
 	}
     }
 
     source_img = malloc (sizeof (quartz_source_image_t));
     if (source_img == NULL)
 	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
-    source_img->surface = source;
+    source_img->surface = cairo_surface_reference(source);
 
     status = _cairo_surface_acquire_source_image (source_img->surface, &source_img->image_out, &source_img->image_extra);
     if (status) {
+	cairo_surface_destroy (source_img->surface);
 	free (source_img);
 	return status;
     }
 
     if (source_img->image_out->width == 0 || source_img->image_out->height == 0) {
 	*image_out = NULL;
 	DataProviderReleaseCallback (source_img,
 				     source_img->image_out->data,
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -416,17 +416,19 @@ ContainerLayer::DefaultComputeEffectiveT
 
   PRBool useIntermediateSurface;
   float opacity = GetEffectiveOpacity();
   if (opacity != 1.0f && HasMultipleChildren()) {
     useIntermediateSurface = PR_TRUE;
   } else {
     useIntermediateSurface = PR_FALSE;
     gfxMatrix contTransform;
-    if (!mEffectiveTransform.Is2D(&contTransform) ||
+    if (!mEffectiveTransform.Is2D(&contTransform)) {
+     useIntermediateSurface = PR_TRUE;   
+    } else if (
 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
         !contTransform.PreservesAxisAlignedRectangles()) {
 #else
         contTransform.HasNonIntegerTranslation()) {
 #endif
       for (Layer* child = GetFirstChild(); child; child = child->GetNextSibling()) {
         const nsIntRect *clipRect = child->GetEffectiveClipRect();
         /* We can't (easily) forward our transform to children with a non-empty clip
--- a/gfx/layers/d3d10/LayerManagerD3D10.cpp
+++ b/gfx/layers/d3d10/LayerManagerD3D10.cpp
@@ -485,17 +485,17 @@ LayerManagerD3D10::SetViewport(const nsI
 
   gfx3DMatrix projection;
   /*
    * Matrix to transform to viewport space ( <-1.0, 1.0> topleft,
    * <1.0, -1.0> bottomright)
    */
   projection._11 = 2.0f / aViewport.width;
   projection._22 = -2.0f / aViewport.height;
-  projection._33 = 1.0f;
+  projection._33 = 0.0f;
   projection._41 = -1.0f;
   projection._42 = 1.0f;
   projection._44 = 1.0f;
 
   HRESULT hr = mEffect->GetVariableByName("mProjection")->
     SetRawValue(&projection._11, 0, 64);
 
   if (FAILED(hr)) {
--- a/gfx/layers/d3d9/LayerManagerD3D9.cpp
+++ b/gfx/layers/d3d9/LayerManagerD3D9.cpp
@@ -366,16 +366,17 @@ LayerManagerD3D9::SetupPipeline()
 
   gfx3DMatrix viewMatrix;
   /*
    * Matrix to transform to viewport space ( <-1.0, 1.0> topleft,
    * <1.0, -1.0> bottomright)
    */
   viewMatrix._11 = 2.0f / rect.width;
   viewMatrix._22 = -2.0f / rect.height;
+  viewMatrix._33 = 0.0f;
   viewMatrix._41 = -1.0f;
   viewMatrix._42 = 1.0f;
 
   HRESULT hr = device()->SetVertexShaderConstantF(CBmProjection,
                                                   &viewMatrix._11, 4);
 
   if (FAILED(hr)) {
     NS_WARNING("Failed to set projection shader constant!");
--- a/gfx/layers/opengl/LayerManagerOGL.cpp
+++ b/gfx/layers/opengl/LayerManagerOGL.cpp
@@ -940,17 +940,20 @@ LayerManagerOGL::SetupPipeline(int aWidt
   viewMatrix.Translate(-gfxPoint(1.0, -1.0));
   viewMatrix.Scale(2.0f / float(aWidth), 2.0f / float(aHeight));
   viewMatrix.Scale(1.0f, -1.0f);
 
   if (aTransformPolicy == ApplyWorldTransform) {
     viewMatrix = mWorldMatrix * viewMatrix;
   }
 
-  SetLayerProgramProjectionMatrix(gfx3DMatrix::From2D(viewMatrix));
+  gfx3DMatrix matrix3d = gfx3DMatrix::From2D(viewMatrix);
+  matrix3d._33 = 0.0f;
+
+  SetLayerProgramProjectionMatrix(matrix3d);
 }
 
 void
 LayerManagerOGL::SetupBackBuffer(int aWidth, int aHeight)
 {
   if (mGLContext->IsDoubleBuffered()) {
     mGLContext->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
     return;
--- a/gfx/tests/reftest/reftest.list
+++ b/gfx/tests/reftest/reftest.list
@@ -1,3 +1,3 @@
 # 468496-1 will also detect bugs in video drivers.
-fails-if(/Mac\x20OS\x20X\x2010\.5/.test(http.oscpu)) fails-if(Android) == 468496-1.html 468496-1-ref.html # bug 486761, 660740
+fails-if(/Mac\x20OS\x20X\x2010\.5/.test(http.oscpu)) == 468496-1.html 468496-1-ref.html # bug 486761, 660740
 == 611498-1.html 611498-ref.html
--- a/gfx/thebes/Makefile.in
+++ b/gfx/thebes/Makefile.in
@@ -27,16 +27,17 @@ EXPORTS	= \
 	gfxFontUtils.h \
 	gfxFontTest.h \
 	gfxImageSurface.h \
 	gfxMatrix.h \
 	gfxPath.h \
 	gfxPattern.h \
 	gfxPlatform.h \
 	gfxPoint.h \
+	gfxPoint3D.h \
 	gfxRect.h \
 	gfxSkipChars.h \
 	gfxTeeSurface.h \
 	gfxTypes.h \
 	gfxTextRunCache.h \
 	gfxTextRunWordCache.h \
 	gfxUnicodeProperties.h \
 	gfxUtils.h \
--- a/gfx/thebes/gfx3DMatrix.cpp
+++ b/gfx/thebes/gfx3DMatrix.cpp
@@ -143,16 +143,27 @@ gfx3DMatrix::Translation(float aX, float
 
   matrix._41 = aX;
   matrix._42 = aY;
   matrix._43 = aZ;
   return matrix;
 }
 
 gfx3DMatrix
+gfx3DMatrix::Translation(const gfxPoint3D& aPoint)
+{
+  gfx3DMatrix matrix;
+
+  matrix._41 = aPoint.x;
+  matrix._42 = aPoint.y;
+  matrix._43 = aPoint.z;
+  return matrix;
+}
+
+gfx3DMatrix
 gfx3DMatrix::Scale(float aFactor)
 {
   gfx3DMatrix matrix;
 
   matrix._11 = matrix._22 = matrix._33 = aFactor;
   return matrix;
 }
 
@@ -264,23 +275,34 @@ gfx3DMatrix::Invert()
 
   *this /= det;
   return *this;
 }
 
 gfxPoint
 gfx3DMatrix::Transform(const gfxPoint& point) const
 {
-  gfxFloat x = point.x * _11 + point.y * _21 + _41;
-  gfxFloat y = point.x * _12 + point.y * _22 + _42;
-  gfxFloat w = point.x * _14 + point.y * _24 + _44;
+  gfxPoint3D vec3d(point.x, point.y, 0);
+  vec3d = Transform3D(vec3d);
+  return gfxPoint(vec3d.x, vec3d.y);
+}
+
+gfxPoint3D
+gfx3DMatrix::Transform3D(const gfxPoint3D& point) const
+{
+  gfxFloat x = point.x * _11 + point.y * _21 + point.z * _31 + _41;
+  gfxFloat y = point.x * _12 + point.y * _22 + point.z * _32 + _42;
+  gfxFloat z = point.x * _13 + point.y * _23 + point.z * _33 + _43;
+  gfxFloat w = point.x * _14 + point.y * _24 + point.z * _34 + _44;
+
   x /= w;
   y /= w;
-  /* Assume z is 0! */
-  return gfxPoint(x, y);
+  z /= w;
+
+  return gfxPoint3D(x, y, z);
 }
 
 gfxRect
 gfx3DMatrix::TransformBounds(const gfxRect& rect) const
 {
   gfxPoint points[4];
 
   points[0] = Transform(rect.TopLeft());
@@ -320,8 +342,78 @@ gfx3DMatrix::Is2D(gfxMatrix* aMatrix) co
     aMatrix->xy = _21;
     aMatrix->yy = _22;
     aMatrix->x0 = _41;
     aMatrix->y0 = _42;
   }
   return PR_TRUE;
 }
 
+gfxPoint gfx3DMatrix::ProjectPoint(const gfxPoint& aPoint) const
+{
+  // Define a ray of the form P + Ut where t is a real number
+  // w is assumed to always be 1 when transforming 3d points with our
+  // 4x4 matrix.
+  // p is our click point, q is another point on the same ray.
+  // 
+  // Note: since the transformation is a general projective transformation and is not
+  // necessarily affine, we can't just take a unit vector u, back-transform it, and use
+  // it as unit vector on the back-transformed ray. Instead, we really must take two points
+  // on the ray and back-transform them.
+  gfxPoint3D p(aPoint.x, aPoint.y, 0);
+  gfxPoint3D q(aPoint.x, aPoint.y, 1);
+
+  // Back transform the vectors (using w = 1) and normalize
+  // back into 3d vectors by dividing by the w component.
+  gfxPoint3D pback = Transform3D(p);
+  gfxPoint3D qback = Transform3D(q);
+  gfxPoint3D uback = qback - pback;
+
+  // Find the point where the back transformed line intersects z=0
+  // and find t.
+  
+  float t = -pback.z / uback.z;
+
+  gfxPoint result(pback.x + t*uback.x, pback.y + t*uback.y);
+
+  return result;
+}
+
+gfxRect gfx3DMatrix::ProjectRectBounds(const gfxRect& aRect) const
+{
+  gfxPoint points[4];
+
+  points[0] = ProjectPoint(aRect.TopLeft());
+  points[1] = ProjectPoint(gfxPoint(aRect.X() + aRect.Width(), aRect.Y()));
+  points[2] = ProjectPoint(gfxPoint(aRect.X(), aRect.Y() + aRect.Height()));
+  points[3] = ProjectPoint(gfxPoint(aRect.X() + aRect.Width(),
+                                    aRect.Y() + aRect.Height()));
+
+  gfxFloat min_x, max_x;
+  gfxFloat min_y, max_y;
+
+  min_x = max_x = points[0].x;
+  min_y = max_y = points[0].y;
+
+  for (int i=1; i<4; i++) {
+    min_x = min(points[i].x, min_x);
+    max_x = max(points[i].x, max_x);
+    min_y = min(points[i].y, min_y);
+    max_y = max(points[i].y, max_y);
+  }
+
+  return gfxRect(min_x, min_y, max_x - min_x, max_y - min_y);
+}
+
+gfxPoint3D gfx3DMatrix::GetNormalVector() const
+{
+    // Define a plane in transformed space as the transformations
+    // of 3 points on the z=0 screen plane.
+    gfxPoint3D a = Transform3D(gfxPoint3D(0, 0, 0));
+    gfxPoint3D b = Transform3D(gfxPoint3D(0, 1, 0));
+    gfxPoint3D c = Transform3D(gfxPoint3D(1, 0, 0));
+
+    // Convert to two vectors on the surface of the plane.
+    gfxPoint3D ab = b - a;
+    gfxPoint3D ac = c - a;
+
+    return ac.CrossProduct(ab);
+}
--- a/gfx/thebes/gfx3DMatrix.h
+++ b/gfx/thebes/gfx3DMatrix.h
@@ -35,16 +35,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef GFX_3DMATRIX_H
 #define GFX_3DMATRIX_H
 
 #include <gfxTypes.h>
+#include <gfxPoint3D.h>
 #include <gfxMatrix.h>
 
 /**
  * This class represents a 3D transformation. The matrix is laid
  * out as follows:
  *
  * _11 _12 _13 _14
  * _21 _22 _23 _24
@@ -106,35 +107,58 @@ public:
    */
   gfxPoint Transform(const gfxPoint& point) const;
 
   /**
    * Transforms a rectangle according to this matrix
    */
   gfxRect TransformBounds(const gfxRect& rect) const;
 
+  /** 
+   * Transforms a 3D vector according to this matrix.
+   */
+  gfxPoint3D Transform3D(const gfxPoint3D& point) const;
+
+  gfxPoint ProjectPoint(const gfxPoint& aPoint) const;
+  gfxRect ProjectRectBounds(const gfxRect& aRect) const;
+
+
   /**
    * Inverts this matrix, if possible. Otherwise, the matrix is left
    * unchanged.
    */
   gfx3DMatrix& Invert();
 
+  inline gfx3DMatrix Inverse() const
+  {
+    gfx3DMatrix temp = *this;
+    temp.Invert();
+    return temp;
+  }
+
+  /**
+   * Returns a unit vector that is perpendicular to the plane formed
+   * by transform the screen plane (z=0) by this matrix.
+   */
+  gfxPoint3D GetNormalVector() const;
+
   /**
    * Check if matrix is singular (no inverse exists).
    */
   PRBool IsSingular() const;
 
   /**
    * Create a translation matrix.
    *
    * \param aX Translation on X-axis.
    * \param aY Translation on Y-axis.
    * \param aZ Translation on Z-axis.
    */
   static gfx3DMatrix Translation(float aX, float aY, float aZ);
+  static gfx3DMatrix Translation(const gfxPoint3D& aPoint);
 
   /**
    * Create a scale matrix. Scales uniformly along all axes.
    *
    * \param aScale Scale factor
    */
   static gfx3DMatrix Scale(float aFactor);
 
--- a/gfx/thebes/gfxDrawable.cpp
+++ b/gfx/thebes/gfxDrawable.cpp
@@ -153,21 +153,16 @@ gfxSurfaceDrawable::Draw(gfxContext* aCo
           filter = gfxPattern::FILTER_FAST;
         }
         nsRefPtr<gfxASurface> currentTarget = aContext->CurrentSurface();
         gfxMatrix deviceSpaceToImageSpace =
             DeviceToImageTransform(aContext, aTransform);
         PreparePatternForUntiledDrawing(pattern, deviceSpaceToImageSpace,
                                         currentTarget, filter);
     }
-#ifdef MOZ_GFX_OPTIMIZE_MOBILE
-    if (!mozilla::supports_neon()) {
-        pattern->SetFilter(gfxPattern::FILTER_FAST);
-    }
-#endif
     pattern->SetMatrix(gfxMatrix(aTransform).Multiply(mTransform));
     aContext->NewPath();
     aContext->SetPattern(pattern);
     aContext->Rectangle(aFillRect);
     aContext->Fill();
     return PR_TRUE;
 }
 
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/gfxPoint3D.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * ***** 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 Oracle Corporation code.
+ *
+ * The Initial Developer of the Original Code is Oracle Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2005
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Matt Woodrow <mwoodrow@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either 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 GFX_POINT3D_H
+#define GFX_POINT3D_H
+
+#include "mozilla/gfx/BasePoint3D.h"
+#include "gfxTypes.h"
+
+struct THEBES_API gfxPoint3D : public mozilla::gfx::BasePoint3D<gfxFloat, gfxPoint3D> {
+    typedef mozilla::gfx::BasePoint3D<gfxFloat, gfxPoint3D> Super;
+
+    gfxPoint3D() : Super() {}
+    gfxPoint3D(gfxFloat aX, gfxFloat aY, gfxFloat aZ) : Super(aX, aY, aZ) {}
+};
+
+#endif /* GFX_POINT3D_H */ 
--- a/intl/locale/src/charsetalias.properties
+++ b/intl/locale/src/charsetalias.properties
@@ -108,16 +108,17 @@ utf-7=UTF-7
 shift_jis=Shift_JIS
 big5=Big5
 euc-jp=EUC-JP
 euc-kr=EUC-KR
 gb2312=GB2312
 gb18030=gb18030
 viscii=VISCII
 koi8-r=KOI8-R
+koi8_r=KOI8-R
 koi8-u=KOI8-U
 tis-620=TIS-620
 t.61-8bit=T.61-8bit
 hz-gb-2312=HZ-GB-2312
 big5-hkscs=Big5-HKSCS
 gbk=gbk
 cns11643=x-euc-tw
 #
--- a/js/src/jit-test/tests/basic/testBug663789-2.js
+++ b/js/src/jit-test/tests/basic/testBug663789-2.js
@@ -1,11 +1,6 @@
 // |jit-test| debug;mjit
 
-/*
- * NOTE: this evalInFrame is explicitly exposing an optimization artifact that
- * InvokeSessionGuard leaves the callee frame on the stack between invocations.
- * If this ever gets fixed or InvokeSessionGuard gets removed, this test will
- * fail and it can be removed.
- */
-o = { toString:function() { return evalInFrame(1, "arguments; x") } }
+o = { toString:function() { return evalInFrame(1, "x") } }
+var x = 'C';
 var s = "aaaaaaaaaa".replace(/a/g, function() { var x = 'B'; return o });
-assertEq(s, "BBBBBBBBBB");
+assertEq(s, "CCCCCCCCCC");
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -4468,17 +4468,17 @@ CompileUCScriptForPrincipalsCommon(JSCon
 
     uint32 tcflags = JS_OPTIONS_TO_TCFLAGS(cx) | TCF_NEED_MUTABLE_SCRIPT;
     JSScript *script = Compiler::compileScript(cx, obj, NULL, principals, tcflags,
                                                chars, length, filename, lineno, version);
     JSObject *scriptObj = NULL;
     if (script) {
         scriptObj = js_NewScriptObject(cx, script);
         if (!scriptObj)
-            js_DestroyScript(cx, script);
+            js_DestroyScript(cx, script, 3);
     }
     LAST_FRAME_CHECKS(cx, scriptObj);
     return scriptObj;
 }
 
 extern JS_PUBLIC_API(JSObject *)
 JS_CompileUCScriptForPrincipalsVersion(JSContext *cx, JSObject *obj,
                                        JSPrincipals *principals,
@@ -4655,17 +4655,17 @@ CompileFileHelper(JSContext *cx, JSObjec
     script = Compiler::compileScript(cx, obj, NULL, principals, tcflags, buf, len, filename, 1,
                                      cx->findVersion());
     cx->free_(buf);
     if (!script)
         return NULL;
 
     JSObject *scriptObj = js_NewScriptObject(cx, script);
     if (!scriptObj)
-        js_DestroyScript(cx, script);
+        js_DestroyScript(cx, script, 4);
 
     return scriptObj;
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_CompileFile(JSContext *cx, JSObject *obj, const char *filename)
 {
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
@@ -4962,17 +4962,17 @@ EvaluateUCScriptForPrincipalsCommon(JSCo
     if (!script) {
         LAST_FRAME_CHECKS(cx, script);
         return false;
     }
     JS_ASSERT(script->getVersion() == compileVersion);
 
     bool ok = ExternalExecute(cx, script, *obj, Valueify(rval));
     LAST_FRAME_CHECKS(cx, ok);
-    js_DestroyScript(cx, script);
+    js_DestroyScript(cx, script, 5);
     return ok;
 
 }
 
 JS_PUBLIC_API(JSBool)
 JS_EvaluateUCScriptForPrincipalsVersion(JSContext *cx, JSObject *obj,
                                         JSPrincipals *principals,
                                         const jschar *chars, uintN length,
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -1120,17 +1120,17 @@ JS_EvaluateUCInStackFrame(JSContext *cx,
                                                filename, lineno, cx->findVersion(),
                                                NULL, UpvarCookie::UPVAR_LEVEL_LIMIT);
 
     if (!script)
         return false;
 
     bool ok = Execute(cx, script, *scobj, fp->thisValue(), EXECUTE_DEBUG, fp, Valueify(rval));
 
-    js_DestroyScript(cx, script);
+    js_DestroyScript(cx, script, 6);
     return ok;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_EvaluateInStackFrame(JSContext *cx, JSStackFrame *fp,
                         const char *bytes, uintN length,
                         const char *filename, uintN lineno,
                         jsval *rval)
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -1581,18 +1581,24 @@ js_XDRFunctionObject(JSXDRState *xdr, JS
     if (xdr->mode == JSXDR_DECODE) {
         fun->nargs = flagsword >> 16;
         JS_ASSERT((flagsword & JSFUN_KINDMASK) >= JSFUN_INTERPRETED);
         fun->flags = uint16(flagsword);
         fun->u.i.skipmin = uint16(firstword >> 2);
         fun->u.i.wrapper = JSPackedBool((firstword >> 1) & 1);
     }
 
-    if (!js_XDRScript(xdr, &fun->u.i.script))
+    /*
+     * Don't directly store into fun->u.i.script because we want this to happen
+     * at the same time as we set the script's owner.
+     */
+    JSScript *script = fun->u.i.script;
+    if (!js_XDRScript(xdr, &script))
         return false;
+    fun->u.i.script = script;
 
     if (xdr->mode == JSXDR_DECODE) {
         *objp = FUN_OBJECT(fun);
         fun->u.i.script->setOwnerObject(fun);
 #ifdef CHECK_SCRIPT_OWNER
         fun->script()->owner = NULL;
 #endif
         JS_ASSERT(fun->nargs == fun->script()->bindings.countArgs());
@@ -2469,19 +2475,20 @@ js_CloneFunctionObject(JSContext *cx, JS
         clone->setPrivate(cfun);
         if (cfun->isInterpreted()) {
             JSScript *script = cfun->script();
             JS_ASSERT(script);
             JS_ASSERT(script->compartment == fun->compartment());
             JS_ASSERT(script->compartment != cx->compartment);
             JS_OPT_ASSERT(script->ownerObject == fun);
 
-            cfun->u.i.script = js_CloneScript(cx, script);
-            if (!cfun->script())
+            JSScript *cscript = js_CloneScript(cx, script);
+            if (!cscript)
                 return NULL;
+            cfun->u.i.script = cscript;
             cfun->script()->setOwnerObject(cfun);
 #ifdef CHECK_SCRIPT_OWNER
             cfun->script()->owner = NULL;
 #endif
             js_CallNewScriptHook(cx, cfun->script(), cfun);
         }
     }
     return clone;
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -704,16 +704,19 @@ InvokeSessionGuard::start(JSContext *cx,
     ContextStack &stack = cx->stack;
     if (!stack.pushInvokeArgs(cx, argc, &args_))
         return false;
 
     /* Callees may clobber 'this' or 'callee'. */
     savedCallee_ = args_.calleev() = calleev;
     savedThis_ = args_.thisv() = thisv;
 
+    /* If anyone (through jsdbgapi) finds this frame, make it safe. */
+    MakeRangeGCSafe(args_.argv(), args_.argc());
+
     do {
         /* Hoist dynamic checks from scripted Invoke. */
         if (!calleev.isObject())
             break;
         JSObject &callee = calleev.toObject();
         if (callee.getClass() != &js_FunctionClass)
             break;
         JSFunction *fun = callee.getFunctionPrivate();
--- a/js/src/jsparse.cpp
+++ b/js/src/jsparse.cpp
@@ -1137,17 +1137,17 @@ Compiler::compileScript(JSContext *cx, J
     return script;
 
   too_many_slots:
     parser.reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_TOO_MANY_LOCALS);
     /* Fall through. */
 
   late_error:
     if (script) {
-        js_DestroyScript(cx, script);
+        js_DestroyScript(cx, script, 7);
         script = NULL;
     }
     goto out;
 }
 
 bool
 Compiler::defineGlobals(JSContext *cx, GlobalScope &globalScope, JSScript *script)
 {
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -749,17 +749,17 @@ js_XDRScript(JSXDRState *xdr, JSScript *
             goto error;
     }
 
     xdr->script = oldscript;
     return JS_TRUE;
 
   error:
     if (xdr->mode == JSXDR_DECODE) {
-        js_DestroyScript(cx, script);
+        js_DestroyScript(cx, script, 1);
         *scriptp = NULL;
     }
     xdr->script = oldscript;
     return JS_FALSE;
 }
 
 #endif /* JS_HAS_XDR */
 
@@ -1265,17 +1265,17 @@ JSScript::NewScriptFromCG(JSContext *cx,
     }
 
     /* Tell the debugger about this compiled script. */
     js_CallNewScriptHook(cx, script, fun);
 
     return script;
 
 bad:
-    js_DestroyScript(cx, script);
+    js_DestroyScript(cx, script, 2);
     return NULL;
 }
 
 size_t
 JSScript::totalSize()
 {
     return code +
            length * sizeof(jsbytecode) +
@@ -1340,17 +1340,17 @@ CheckCompartmentScripts(JSCompartment *c
     {
         CheckScript(script, prev);
     }
 }
 
 } /* namespace js */
 
 static void
-DestroyScript(JSContext *cx, JSScript *script, JSObject *owner)
+DestroyScript(JSContext *cx, JSScript *script, JSObject *owner, uint32 caller)
 {
     CheckScript(script, NULL);
     CheckScriptOwner(script, owner);
 
     if (script->principals)
         JSPRINCIPALS_DROP(cx, script->principals);
 
     GSNCache *gsnCache = GetGSNCache(cx);
@@ -1403,41 +1403,42 @@ DestroyScript(JSContext *cx, JSScript *s
 #endif
     JS_REMOVE_LINK(&script->links);
 
     script->pcCounters.destroy(cx);
 
     if (script->sourceMap)
         cx->free_(script->sourceMap);
 
-    memset(script, JS_FREE_PATTERN, script->totalSize());
+    memset(script, 0xdb, script->totalSize());
+    *(uint32 *)script = caller;
     cx->free_(script);
 }
 
 void
-js_DestroyScript(JSContext *cx, JSScript *script)
+js_DestroyScript(JSContext *cx, JSScript *script, uint32 caller)
 {
     JS_ASSERT(!cx->runtime->gcRunning);
     js_CallDestroyScriptHook(cx, script);
-    DestroyScript(cx, script, JS_NEW_SCRIPT);
+    DestroyScript(cx, script, JS_NEW_SCRIPT, caller);
 }
 
 void
 js_DestroyScriptFromGC(JSContext *cx, JSScript *script, JSObject *owner)
 {
     JS_ASSERT(cx->runtime->gcRunning);
     js_CallDestroyScriptHook(cx, script);
-    DestroyScript(cx, script, owner);
+    DestroyScript(cx, script, owner, 100);
 }
 
 void
 js_DestroyCachedScript(JSContext *cx, JSScript *script)
 {
     JS_ASSERT(cx->runtime->gcRunning);
-    DestroyScript(cx, script, JS_CACHED_SCRIPT);
+    DestroyScript(cx, script, JS_CACHED_SCRIPT, 101);
 }
 
 void
 js_TraceScript(JSTracer *trc, JSScript *script, JSObject *owner)
 {
     CheckScript(script, NULL);
     if (owner)
         CheckScriptOwner(script, owner);
@@ -1466,16 +1467,20 @@ js_TraceScript(JSTracer *trc, JSScript *
 
     if (script->u.object)
         MarkObject(trc, *script->u.object, "object");
 
     if (IS_GC_MARKING_TRACER(trc) && script->filename)
         js_MarkScriptFilename(script->filename);
 
     script->bindings.trace(trc);
+
+#ifdef JS_METHODJIT
+    mjit::TraceScript(trc, script);
+#endif
 }
 
 JSObject *
 js_NewScriptObject(JSContext *cx, JSScript *script)
 {
     AutoScriptRooter root(cx, script);
 
     JS_ASSERT(!script->u.object);
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -728,17 +728,17 @@ js_CallNewScriptHook(JSContext *cx, JSSc
 extern void
 js_CallDestroyScriptHook(JSContext *cx, JSScript *script);
 
 /*
  * The function must be used only outside the GC for a script that was run
  * only on the current thread.
  */
 extern void
-js_DestroyScript(JSContext *cx, JSScript *script);
+js_DestroyScript(JSContext *cx, JSScript *script, uint32 caller);
 
 extern void
 js_DestroyScriptFromGC(JSContext *cx, JSScript *script, JSObject *owner);
 
 /*
  * Script objects may be cached and reused, in which case their JSD-visible
  * lifetimes may be shorter than their actual lifetimes. Destroy one such
  * script for real as part of a GC pass. From JSD's point of view, the script
--- a/js/src/jsutil.cpp
+++ b/js/src/jsutil.cpp
@@ -65,29 +65,36 @@ JS_PUBLIC_DATA(JSUint32) OOM_counter = 0
  * Checks the assumption that JS_FUNC_TO_DATA_PTR and JS_DATA_TO_FUNC_PTR
  * macros uses to implement casts between function and data pointers.
  */
 JS_STATIC_ASSERT(sizeof(void *) == sizeof(void (*)()));
 
 static JS_NEVER_INLINE void
 CrashInJS()
 {
+    /*
+     * We write 123 here so that the machine code for this function is
+     * unique. Otherwise the linker, trying to be smart, might use the
+     * same code for CrashInJS and for some other function. That
+     * messes up the signature in minidumps.
+     */
+
 #if defined(WIN32)
     /*
      * We used to call DebugBreak() on Windows, but amazingly, it causes
      * the MSVS 2010 debugger not to be able to recover a call stack.
      */
-    *((int *) NULL) = 0;
+    *((int *) NULL) = 123;
     exit(3);
 #elif defined(__APPLE__)
     /*
      * On Mac OS X, Breakpad ignores signals. Only real Mach exceptions are
      * trapped.
      */
-    *((int *) NULL) = 0;  /* To continue from here in GDB: "return" then "continue". */
+    *((int *) NULL) = 123;  /* To continue from here in GDB: "return" then "continue". */
     raise(SIGABRT);  /* In case above statement gets nixed by the optimizer. */
 #else
     raise(SIGABRT);  /* To continue from here in GDB: "signal 0". */
 #endif
 }
 
 JS_PUBLIC_API(void) JS_Assert(const char *s, const char *file, JSIntn ln)
 {
--- a/js/src/jsxdrapi.cpp
+++ b/js/src/jsxdrapi.cpp
@@ -715,17 +715,17 @@ JS_XDRScriptObject(JSXDRState *xdr, JSOb
 
     if (!js_XDRScript(xdr, &script))
         return false;
 
     if (xdr->mode == JSXDR_DECODE) {
         js_CallNewScriptHook(xdr->cx, script, NULL);
         *scriptObjp = js_NewScriptObject(xdr->cx, script);
         if (!*scriptObjp) {
-            js_DestroyScript(xdr->cx, script);
+            js_DestroyScript(xdr->cx, script, 8);
             return false;
         }
     }
 
     return true;
 }
 
 #define CLASS_REGISTRY_MIN      8
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -108,16 +108,17 @@ mjit::Compiler::Compiler(JSContext *cx, 
     getElemICs(CompilerAllocPolicy(cx, *thisFromCtor())),
     setElemICs(CompilerAllocPolicy(cx, *thisFromCtor())),
 #endif
     callPatches(CompilerAllocPolicy(cx, *thisFromCtor())),
     callSites(CompilerAllocPolicy(cx, *thisFromCtor())), 
     doubleList(CompilerAllocPolicy(cx, *thisFromCtor())),
     jumpTables(CompilerAllocPolicy(cx, *thisFromCtor())),
     jumpTableOffsets(CompilerAllocPolicy(cx, *thisFromCtor())),
+    rootedObjects(CompilerAllocPolicy(cx, *thisFromCtor())),
     stubcc(cx, *thisFromCtor(), frame, script),
     debugMode_(cx->compartment->debugMode),
 #if defined JS_TRACER
     addTraceHints(cx->traceJitEnabled),
 #endif
     oomInVector(false),
     applyTricks(NoApplyTricks)
 {
@@ -452,17 +453,18 @@ mjit::Compiler::finishThisUp(JITScript *
                       sizeof(ic::EqualityICInfo) * equalityICs.length() +
                       sizeof(ic::TraceICInfo) * traceICs.length() +
 #endif
 #if defined JS_POLYIC
                        sizeof(ic::PICInfo) * pics.length() +
                        sizeof(ic::GetElementIC) * getElemICs.length() +
                        sizeof(ic::SetElementIC) * setElemICs.length() +
 #endif
-                       sizeof(CallSite) * callSites.length();
+                       sizeof(CallSite) * callSites.length() +
+                       sizeof(JSObject *) * rootedObjects.length();
 
     uint8 *cursor = (uint8 *)cx->calloc_(dataSize);
     if (!cursor) {
         execPool->release();
         js_ReportOutOfMemory(cx);
         return Compile_Error;
     }
 
@@ -815,16 +817,23 @@ mjit::Compiler::finishThisUp(JITScript *
         CallSite &to = jitCallSites[i];
         InternalCallSite &from = callSites[i];
         uint32 codeOffset = from.ool
                             ? masm.size() + from.returnOffset
                             : from.returnOffset;
         to.initialize(codeOffset, from.pc - script->code, from.id);
     }
 
+    /* Build the list of objects rooted by the script. */
+    JSObject **jitRooted = (JSObject **)cursor;
+    jit->nRootedObjects = rootedObjects.length();
+    cursor += sizeof(JSObject *) * jit->nRootedObjects;
+    for (size_t i = 0; i < jit->nRootedObjects; i++)
+        jitRooted[i] = rootedObjects[i];
+
     JS_ASSERT(size_t(cursor - (uint8*)jit) == dataSize);
 
     *jitp = jit;
 
     return Compile_Okay;
 }
 
 class SrcNoteLineScanner {
@@ -3236,16 +3245,22 @@ mjit::Compiler::jsop_callprop_str(JSAtom
      * We must pass an explicit scope chain only because JSD calls into
      * here via the recompiler with a dummy context, and we need to use
      * the global object for the script we are now compiling.
      */
     JSObject *obj;
     if (!js_GetClassPrototype(cx, &fp->scopeChain(), JSProto_String, &obj))
         return false;
 
+    /*
+     * Root the proto, since JS_ClearScope might overwrite the global object's
+     * copy.
+     */
+    rootedObjects.append(obj);
+
     /* Force into a register because getprop won't expect a constant. */
     RegisterID reg = frame.allocReg();
 
     masm.move(ImmPtr(obj), reg);
     frame.pushTypedPayload(JSVAL_TYPE_OBJECT, reg);
 
     /* Get the property. */
     if (!jsop_getprop(atom))
--- a/js/src/methodjit/Compiler.h
+++ b/js/src/methodjit/Compiler.h
@@ -353,16 +353,17 @@ class Compiler : public BaseCompiler
     js::Vector<GetElementICInfo, 16, CompilerAllocPolicy> getElemICs;
     js::Vector<SetElementICInfo, 16, CompilerAllocPolicy> setElemICs;
 #endif
     js::Vector<CallPatchInfo, 64, CompilerAllocPolicy> callPatches;
     js::Vector<InternalCallSite, 64, CompilerAllocPolicy> callSites;
     js::Vector<DoublePatch, 16, CompilerAllocPolicy> doubleList;
     js::Vector<JumpTable, 16> jumpTables;
     js::Vector<uint32, 16> jumpTableOffsets;
+    js::Vector<JSObject *, 0, CompilerAllocPolicy> rootedObjects;
     StubCompiler stubcc;
     Label invokeLabel;
     Label arityLabel;
     bool debugMode_;
     bool addTraceHints;
     bool oomInVector;       // True if we have OOM'd appending to a vector. 
     enum { NoApplyTricks, LazyArgsObj } applyTricks;
 #ifdef DEBUG
--- a/js/src/methodjit/MethodJIT.cpp
+++ b/js/src/methodjit/MethodJIT.cpp
@@ -35,16 +35,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "MethodJIT.h"
 #include "Logging.h"
 #include "assembler/jit/ExecutableAllocator.h"
 #include "jstracer.h"
+#include "jsgcmark.h"
 #include "BaseAssembler.h"
 #include "Compiler.h"
 #include "MonoIC.h"
 #include "PolyIC.h"
 #include "TrampolineCompiler.h"
 #include "jscntxtinlines.h"
 #include "jscompartment.h"
 #include "jsscope.h"
@@ -747,17 +748,17 @@ NativeMapEntry *
 JITScript::nmap() const
 {
     return (NativeMapEntry *)((char*)this + sizeof(JITScript));
 }
 
 char *
 JITScript::nmapSectionLimit() const
 {
-    return (char *)nmap() + sizeof(NativeMapEntry) * nNmapPairs;
+    return (char *)&nmap()[nNmapPairs];
 }
 
 #ifdef JS_MONOIC
 ic::GetGlobalNameIC *
 JITScript::getGlobalNames() const
 {
     return (ic::GetGlobalNameIC *)nmapSectionLimit();
 }
@@ -767,36 +768,35 @@ JITScript::setGlobalNames() const
 {
     return (ic::SetGlobalNameIC *)((char *)nmapSectionLimit() +
             sizeof(ic::GetGlobalNameIC) * nGetGlobalNames);
 }
 
 ic::CallICInfo *
 JITScript::callICs() const
 {
-    return (ic::CallICInfo *)((char *)setGlobalNames() +
-            sizeof(ic::SetGlobalNameIC) * nSetGlobalNames);
+    return (ic::CallICInfo *)&setGlobalNames()[nSetGlobalNames];
 }
 
 ic::EqualityICInfo *
 JITScript::equalityICs() const
 {
-    return (ic::EqualityICInfo *)((char *)callICs() + sizeof(ic::CallICInfo) * nCallICs);
+    return (ic::EqualityICInfo *)&callICs()[nCallICs];
 }
 
 ic::TraceICInfo *
 JITScript::traceICs() const
 {
-    return (ic::TraceICInfo *)((char *)equalityICs() + sizeof(ic::EqualityICInfo) * nEqualityICs);
+    return (ic::TraceICInfo *)&equalityICs()[nEqualityICs];
 }
 
 char *
 JITScript::monoICSectionsLimit() const
 {
-    return (char *)traceICs() + sizeof(ic::TraceICInfo) * nTraceICs;
+    return (char *)&traceICs()[nTraceICs];
 }
 #else   // JS_MONOIC
 char *
 JITScript::monoICSectionsLimit() const
 {
     return nmapSectionLimit();
 }
 #endif  // JS_MONOIC
@@ -834,16 +834,22 @@ JITScript::polyICSectionsLimit() const
 #endif  // JS_POLYIC
 
 js::mjit::CallSite *
 JITScript::callSites() const
 {
     return (js::mjit::CallSite *)polyICSectionsLimit();
 }
 
+JSObject **
+JITScript::rootedObjects() const
+{
+    return (JSObject **)&callSites()[nCallSites];
+}
+
 template <typename T>
 static inline void Destroy(T &t)
 {
     t.~T();
 }
 
 mjit::JITScript::~JITScript()
 {
@@ -870,16 +876,23 @@ mjit::JITScript::~JITScript()
     }
     
     ic::CallICInfo *callICs_ = callICs();
     for (uint32 i = 0; i < nCallICs; i++)
         callICs_[i].releasePools();
 #endif
 }
 
+void
+mjit::JITScript::trace(JSTracer *trc)
+{
+    for (uint32 i = 0; i < nRootedObjects; ++i)
+        MarkObject(trc, *rootedObjects()[i], "mjit rooted object");
+}
+
 size_t
 JSScript::jitDataSize()
 {
     size_t n = 0;
     if (jitNormal)
         n += jitNormal->scriptDataSize(); 
     if (jitCtor)
         n += jitCtor->scriptDataSize(); 
@@ -925,16 +938,26 @@ mjit::ReleaseScriptCode(JSContext *cx, J
     if ((jscr = script->jitCtor)) {
         jscr->~JITScript();
         cx->free_(jscr);
         script->jitCtor = NULL;
         script->jitArityCheckCtor = NULL;
     }
 }
 
+void
+mjit::TraceScript(JSTracer *trc, JSScript *script)
+{
+    if (JITScript *jit = script->jitNormal)
+        jit->trace(trc);
+
+    if (JITScript *jit = script->jitCtor)
+        jit->trace(trc);
+}
+
 #ifdef JS_METHODJIT_PROFILE_STUBS
 void JS_FASTCALL
 mjit::ProfileStubCall(VMFrame &f)
 {
     JSOp op = JSOp(*f.regs.pc);
     StubCallsForOp[op]++;
 }
 #endif
--- a/js/src/methodjit/MethodJIT.h
+++ b/js/src/methodjit/MethodJIT.h
@@ -377,16 +377,17 @@ struct JITScript {
     uint32          nTraceICs;
 #endif
 #ifdef JS_POLYIC
     uint32          nGetElems;
     uint32          nSetElems;
     uint32          nPICs;
 #endif
     uint32          nCallSites;
+    uint32          nRootedObjects;
 
 #ifdef JS_MONOIC
     // Additional ExecutablePools that IC stubs were generated into.
     typedef Vector<JSC::ExecutablePool *, 0, SystemAllocPolicy> ExecPoolVector;
     ExecPoolVector execPools;
 #endif
 
     NativeMapEntry *nmap() const;
@@ -398,30 +399,33 @@ struct JITScript {
     ic::TraceICInfo *traceICs() const;
 #endif
 #ifdef JS_POLYIC
     ic::GetElementIC *getElems() const;
     ic::SetElementIC *setElems() const;
     ic::PICInfo     *pics() const;
 #endif
     js::mjit::CallSite *callSites() const;
+    JSObject **rootedObjects() const;
 
     ~JITScript();
 
     bool isValidCode(void *ptr) {
         char *jitcode = (char *)code.m_code.executableAddress();
         char *jcheck = (char *)ptr;
         return jcheck >= jitcode && jcheck < jitcode + code.m_size;
     }
 
     void nukeScriptDependentICs();
     void sweepCallICs(JSContext *cx, bool purgeAll);
     void purgeMICs();
     void purgePICs();
 
+    void trace(JSTracer *trc);
+
     size_t scriptDataSize();
     jsbytecode *nativeToPC(void *returnAddress) const;
 
   private:
     /* Helpers used to navigate the variable-length sections. */
     char *nmapSectionLimit() const;
     char *monoICSectionsLimit() const;
     char *polyICSectionsLimit() const;
@@ -451,16 +455,19 @@ void JS_FASTCALL
 ProfileStubCall(VMFrame &f);
 
 CompileStatus JS_NEVER_INLINE
 TryCompile(JSContext *cx, StackFrame *fp);
 
 void
 ReleaseScriptCode(JSContext *cx, JSScript *script);
 
+void
+TraceScript(JSTracer *trc, JSScript *script);
+
 struct CallSite
 {
     uint32 codeOffset;
     uint32 pcOffset;
     uint32 id;
 
     // Normally, callsite ID is the __LINE__ in the program that added the
     // callsite. Since traps can be removed, we make sure they carry over
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -906,16 +906,26 @@ void
 StackIter::startOnSegment(StackSegment *seg)
 {
     seg_ = seg;
     fp_ = seg_->maybefp();
     calls_ = seg_->maybeCalls();
     settleOnNewSegment();
 }
 
+static void JS_NEVER_INLINE
+CrashIfInvalidSlot(StackFrame *fp, Value *vp)
+{
+    if (vp < fp->slots() || vp >= fp->slots() + fp->script()->nslots) {
+        JS_ASSERT(false && "About to dereference invalid slot");
+        *(int *)0xbad = 0;  // show up nicely in crash-stats
+        JS_Assert("About to dereference invalid slot", __FILE__, __LINE__);
+    }
+}
+
 void
 StackIter::settleOnNewState()
 {
     /*
      * There are elements of the calls_ and fp_ chains that we want to skip
      * over so iterate until we settle on one or until there are no more.
      */
     while (true) {
@@ -958,16 +968,22 @@ StackIter::settleOnNewState()
          */
         if (containsFrame && (!containsCall || (Value *)fp_ >= calls_->argv())) {
             /* Nobody wants to see dummy frames. */
             if (fp_->isDummyFrame()) {
                 popFrame();
                 continue;
             }
 
+            /* Censor pushed-but-not-active frames from InvokeSessionGuard. */
+            if (containsCall && !calls_->active() && calls_->argv() == fp_->actualArgs()) {
+                popFrame();
+                continue;
+            }
+
             /*
              * As an optimization, there is no CallArgsList element pushed for
              * natives called directly by a script (compiled or interpreted).
              * We catch these by inspecting the bytecode and stack. This check
              * relies on the property that, at a call opcode,
              *
              *   regs.sp == vp + 2 + argc
              *
@@ -992,28 +1008,30 @@ StackIter::settleOnNewState()
 #ifdef DEBUG
                 if (cx_->stackIterAssertionEnabled) {
                     JS_ASSERT_IF(!fp_->hasImacropc(),
                                  spoff == js_ReconstructStackDepth(cx_, fp_->script(), pc_));
                 }
 #endif
                 Value *vp = sp_ - (2 + argc);
 
+                CrashIfInvalidSlot(fp_, vp);
                 if (IsNativeFunction(*vp)) {
                     state_ = IMPLICIT_NATIVE;
                     args_ = CallArgsFromVp(argc, vp);
                     return;
                 }
             } else if (op == JSOP_FUNAPPLY) {
                 JS_ASSERT(!fp_->hasImacropc());
                 uintN argc = GET_ARGC(pc_);
                 uintN spoff = js_ReconstructStackDepth(cx_, fp_->script(), pc_);
                 Value *sp = fp_->base() + spoff;
                 Value *vp = sp - (2 + argc);
 
+                CrashIfInvalidSlot(fp_, vp);
                 if (IsNativeFunction(*vp)) {
                     if (sp_ != sp) {
                         JS_ASSERT(argc == 2);
                         JS_ASSERT(vp[0].toObject().getFunctionPrivate()->native() == js_fun_apply);
                         JS_ASSERT(sp_ >= vp + 3);
                         argc = sp_ - (vp + 2);
                     }
                     state_ = IMPLICIT_NATIVE;
--- a/js/src/vm/String.cpp
+++ b/js/src/vm/String.cpp
@@ -111,40 +111,41 @@ JSString::charsHeapSize()
     /* JSStaticAtom: the chars are static and so not part of the heap. */
     if (isStaticAtom())
         return 0;
 
     /* JSAtom, JSFixedString: count the chars. */
     return length() * sizeof(jschar);
 }
 
-static JS_ALWAYS_INLINE size_t
-RopeCapacityFor(size_t length)
+static JS_ALWAYS_INLINE bool
+AllocChars(JSContext *maybecx, size_t length, jschar **chars, size_t *capacity)
 {
-    static const size_t ROPE_DOUBLING_MAX = 1024 * 1024;
+    /*
+     * String length doesn't include the null char, so include it here before
+     * doubling. Adding the null char after doubling would interact poorly with
+     * round-up malloc schemes.
+     */
+    size_t numChars = length + 1;
 
     /*
      * Grow by 12.5% if the buffer is very large. Otherwise, round up to the
      * next power of 2. This is similar to what we do with arrays; see
      * JSObject::ensureDenseArrayElements.
      */
-    if (length > ROPE_DOUBLING_MAX)
-        return length + (length / 8);
-    return RoundUpPow2(length);
-}
+    static const size_t DOUBLING_MAX = 1024 * 1024;
+    numChars = numChars > DOUBLING_MAX ? numChars + (numChars / 8) : RoundUpPow2(numChars);
 
-static JS_ALWAYS_INLINE jschar *
-AllocChars(JSContext *maybecx, size_t wholeCapacity)
-{
-    /* +1 for the null char at the end. */
+    /* Like length, capacity does not include the null char, so take it out. */
+    *capacity = numChars - 1;
+
     JS_STATIC_ASSERT(JSString::MAX_LENGTH * sizeof(jschar) < UINT32_MAX);
-    size_t bytes = (wholeCapacity + 1) * sizeof(jschar);
-    if (maybecx)
-        return (jschar *)maybecx->malloc_(bytes);
-    return (jschar *)OffTheBooks::malloc_(bytes);
+    size_t bytes = numChars * sizeof(jschar);
+    *chars = (jschar *)(maybecx ? maybecx->malloc_(bytes) : OffTheBooks::malloc_(bytes));
+    return *chars != NULL;
 }
 
 JSFlatString *
 JSRope::flatten(JSContext *maybecx)
 {
     /*
      * Perform a depth-first dag traversal, splatting each node's characters
      * into a contiguous buffer. Visit each rope node three times:
@@ -192,19 +193,17 @@ JSRope::flatten(JSContext *maybecx)
             size_t bits = left.d.lengthAndFlags;
             pos = wholeChars + (bits >> LENGTH_SHIFT);
             left.d.lengthAndFlags = bits ^ (EXTENSIBLE_FLAGS | DEPENDENT_BIT);
             left.d.s.u2.base = (JSLinearString *)this;  /* will be true on exit */
             goto visit_right_child;
         }
     }
 
-    wholeCapacity = RopeCapacityFor(wholeLength);
-    wholeChars = AllocChars(maybecx, wholeCapacity);
-    if (!wholeChars)
+    if (!AllocChars(maybecx, wholeLength, &wholeChars, &wholeCapacity))
         return NULL;
 
     pos = wholeChars;
     first_visit_node: {
         JSString &left = *str->d.u1.left;
         str->d.u1.chars = pos;
         if (left.isRope()) {
             left.d.s.u3.parent = str;          /* Return to this when 'left' done, */
--- a/js/src/vm/String.h
+++ b/js/src/vm/String.h
@@ -50,18 +50,21 @@ class JSLinearString;
 class JSFixedString;
 class JSStaticAtom;
 class JSRope;
 class JSAtom;
 
 /*
  * JavaScript strings
  *
- * Conceptually, a JS string is just an array of chars and a length. To improve
- * performance of common string operations, the following optimizations are
+ * Conceptually, a JS string is just an array of chars and a length. This array
+ * of chars may or may not be null-terminated and, if it is, the null character
+ * is not included in the length.
+ *
+ * To improve performance of common operations, the following optimizations are
  * made which affect the engine's representation of strings:
  *
  *  - The plain vanilla representation is a "flat" string which consists of a
  *    string header in the GC heap and a malloc'd null terminated char array.
  *
  *  - To avoid copying a substring of an existing "base" string , a "dependent"
  *    string (JSDependentString) can be created which points into the base
  *    string's char array.
@@ -105,17 +108,17 @@ class JSAtom;
  * JSString (abstract)          getCharsZ, getChars, length / -
  *  | \
  *  | JSRope                    leftChild, rightChild / -
  *  |
  * JSLinearString (abstract)    chars / not null-terminated
  *  | \
  *  | JSDependentString         base / -
  *  |
- * JSFlatString (abstract)      chars / not null-terminated
+ * JSFlatString (abstract)      chars / null-terminated
  *  | \
  *  | JSExtensibleString        capacity / no external pointers into char array
  *  |
  * JSFixedString                - / may have external pointers into char array
  *  | \  \
  *  |  \ JSExternalString       - / char array memory managed by embedding
  *  |   \
  *  |   JSInlineString          - / chars stored in header
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -916,26 +916,22 @@ AppUnitsPerDevPixel(nsDisplayItem* aItem
  * about whether CONTENT_OPAQUE is set; if layer was opauqe in the old
  * visible region, it will still be opaque in the new one.
  * @param aItemVisible the visible region of the display item (that is,
  * after any layer transform has been applied)
  */
 static void
 RestrictVisibleRegionForLayer(Layer* aLayer, const nsIntRect& aItemVisible)
 {
-  gfxMatrix transform;
-  if (!aLayer->GetTransform().Is2D(&transform))
-    return;
+  gfx3DMatrix transform = aLayer->GetTransform();
 
   // if 'transform' is not invertible, then nothing will be displayed
   // for the layer, so it doesn't really matter what we do here
-  gfxMatrix inverse = transform;
-  inverse.Invert();
   gfxRect itemVisible(aItemVisible.x, aItemVisible.y, aItemVisible.width, aItemVisible.height);
-  gfxRect layerVisible = inverse.TransformBounds(itemVisible);
+  gfxRect layerVisible = transform.Inverse().ProjectRectBounds(itemVisible);
   layerVisible.RoundOut();
 
   nsIntRect visibleRect;
   if (!gfxUtils::GfxRectToIntRect(layerVisible, &visibleRect))
     return;
 
   nsIntRegion rgn = aLayer->GetVisibleRegion();
   if (!visibleRect.Contains(rgn.GetBounds())) {
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -446,29 +446,26 @@ TreatAsOpaque(nsDisplayItem* aItem, nsDi
   return opaque;
 }
 
 static nsRect GetDisplayPortBounds(nsDisplayListBuilder* aBuilder,
                                    nsDisplayItem* aItem,
                                    PRBool aIgnoreTransform)
 {
   nsIFrame* frame = aItem->GetUnderlyingFrame();
-  nscoord auPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
-  gfx3DMatrix transform;
-
-  if (!aIgnoreTransform) {
-    transform = nsLayoutUtils::GetTransformToAncestor(frame,
-                  aBuilder->ReferenceFrame());
-    transform.Invert();
+  const nsRect* displayport = aBuilder->GetDisplayPort();
+
+  if (aIgnoreTransform) {
+    return *displayport;
   }
 
-  const nsRect* displayport = aBuilder->GetDisplayPort();
-  return nsLayoutUtils::MatrixTransformRect(
+  return nsLayoutUtils::TransformRectToBoundsInAncestor(
+           frame,
            nsRect(0, 0, displayport->width, displayport->height),
-           transform, auPerDevPixel);
+           aBuilder->ReferenceFrame());
 }
 
 PRBool
 nsDisplayList::ComputeVisibilityForSublist(nsDisplayListBuilder* aBuilder,
                                            nsRegion* aVisibleRegion,
                                            const nsRect& aListVisibleBounds,
                                            const nsRect& aAllowVisibleRegionExpansion) {
 #ifdef DEBUG
@@ -2296,35 +2293,35 @@ nsDisplayTransform::GetFrameBoundsForTra
 
 #endif
 
 /* Returns the delta specified by the -moz-transform-origin property.
  * This is a positive delta, meaning that it indicates the direction to move
  * to get from (0, 0) of the frame to the transform origin.
  */
 static
-gfxPoint GetDeltaToMozTransformOrigin(const nsIFrame* aFrame,
-                                      float aFactor,
-                                      const nsRect* aBoundsOverride)
+gfxPoint3D GetDeltaToMozTransformOrigin(const nsIFrame* aFrame,
+                                        float aFactor,
+                                        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!");
 
   /* 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));
 
   /* Allows us to access named variables by index. */
-  gfxPoint result;
-  gfxFloat* coords[2] = {&result.x, &result.y};
+  gfxPoint3D result;
+  gfxFloat* coords[3] = {&result.x, &result.y, &result.z};
   const nscoord* dimensions[2] =
     {&boundingRect.width, &boundingRect.height};
 
   for (PRUint8 index = 0; index < 2; ++index) {
     /* If the -moz-transform-origin specifies a percentage, take the percentage
      * of the size of the box.
      */
     const nsStyleCoord &coord = display->mTransformOrigin[index];
@@ -2336,24 +2333,86 @@ gfxPoint GetDeltaToMozTransformOrigin(co
     } else if (coord.GetUnit() == eStyleUnit_Percent) {
       *coords[index] = NSAppUnitsToFloatPixels(*dimensions[index], aFactor) *
         coord.GetPercentValue();
     } else {
       NS_ABORT_IF_FALSE(coord.GetUnit() == eStyleUnit_Coord, "unexpected unit");
       *coords[index] = NSAppUnitsToFloatPixels(coord.GetCoordValue(), aFactor);
     }
   }
+
+  *coords[2] = NSAppUnitsToFloatPixels(display->mTransformOrigin[2].GetCoordValue(), aFactor);
   
   /* Adjust based on the origin of the rectangle. */
   result.x += NSAppUnitsToFloatPixels(boundingRect.x, aFactor);
   result.y += NSAppUnitsToFloatPixels(boundingRect.y, aFactor);
 
   return result;
 }
 
+/* Returns the delta specified by the -moz-perspective-origin property.
+ * 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 aFactor,
+                                          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!");
+
+  /* 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!
+   */
+
+  //TODO: Should this be using our bounds or the parent's bounds?
+  // How do we handle aBoundsOverride in the latter case?
+  const nsStyleDisplay* display = aFrame->GetParent()->GetStyleDisplay();
+  nsRect boundingRect = (aBoundsOverride ? *aBoundsOverride :
+                         nsDisplayTransform::GetFrameBoundsForTransform(aFrame));
+
+  /* Allows us to access named variables by index. */
+  gfxPoint3D result;
+  result.z = 0.0f;
+  gfxFloat* coords[2] = {&result.x, &result.y};
+  const nscoord* dimensions[2] =
+    {&boundingRect.width, &boundingRect.height};
+
+  for (PRUint8 index = 0; index < 2; ++index) {
+    /* If the -moz-transform-origin specifies a percentage, take the percentage
+     * of the size of the box.
+     */
+    const nsStyleCoord &coord = display->mPerspectiveOrigin[index];
+    if (coord.GetUnit() == eStyleUnit_Calc) {
+      const nsStyleCoord::Calc *calc = coord.GetCalcValue();
+      *coords[index] = NSAppUnitsToFloatPixels(*dimensions[index], aFactor) *
+                         calc->mPercent +
+                       NSAppUnitsToFloatPixels(calc->mLength, aFactor);
+    } else if (coord.GetUnit() == eStyleUnit_Percent) {
+      *coords[index] = NSAppUnitsToFloatPixels(*dimensions[index], aFactor) *
+        coord.GetPercentValue();
+    } else {
+      NS_ABORT_IF_FALSE(coord.GetUnit() == eStyleUnit_Coord, "unexpected unit");
+      *coords[index] = NSAppUnitsToFloatPixels(coord.GetCoordValue(), aFactor);
+    }
+  }
+  
+  /**
+   * An offset of (0,0) results in the perspective-origin being at the centre of the element,
+   * so include a shift of the centre point to (0,0).
+   */
+  result.x -= NSAppUnitsToFloatPixels(boundingRect.width, aFactor)/2;
+  result.y -= NSAppUnitsToFloatPixels(boundingRect.height, aFactor)/2;
+
+  return result;
+}
+
 /* Wraps up the -moz-transform matrix in a change-of-basis matrix pair that
  * translates from local coordinate space to transform coordinate space, then
  * hands it back.
  */
 gfx3DMatrix
 nsDisplayTransform::GetResultingTransformMatrix(const nsIFrame* aFrame,
                                                 const nsPoint &aOrigin,
                                                 float aFactor,
@@ -2361,69 +2420,92 @@ nsDisplayTransform::GetResultingTransfor
 {
   NS_PRECONDITION(aFrame, "Cannot get transform matrix for a null frame!");
   NS_PRECONDITION(aFrame->GetStyleDisplay()->HasTransform(),
                   "Cannot get transform matrix if frame isn't transformed!");
 
   /* Account for the -moz-transform-origin property by translating the
    * coordinate space to the new origin.
    */
-  gfxPoint toMozOrigin = GetDeltaToMozTransformOrigin(aFrame, aFactor, aBoundsOverride);
-  gfxPoint newOrigin = gfxPoint(NSAppUnitsToFloatPixels(aOrigin.x, aFactor),
-                                NSAppUnitsToFloatPixels(aOrigin.y, aFactor));
+  gfxPoint3D toMozOrigin = GetDeltaToMozTransformOrigin(aFrame, aFactor, aBoundsOverride);
+  gfxPoint3D toPerspectiveOrigin = GetDeltaToMozPerspectiveOrigin(aFrame, aFactor, aBoundsOverride);
+  gfxPoint3D newOrigin = gfxPoint3D(NSAppUnitsToFloatPixels(aOrigin.x, aFactor),
+                                    NSAppUnitsToFloatPixels(aOrigin.y, aFactor),
+                                    0.0f);
 
   /* Get the underlying transform matrix.  This requires us to get the
    * bounds of the frame.
    */
   const nsStyleDisplay* disp = aFrame->GetStyleDisplay();
   nsRect bounds = (aBoundsOverride ? *aBoundsOverride :
                    nsDisplayTransform::GetFrameBoundsForTransform(aFrame));
 
   /* Get the matrix, then change its basis to factor in the origin. */
   PRBool dummy;
+  gfx3DMatrix result =
+    nsStyleTransformMatrix::ReadTransforms(disp->mSpecifiedTransform,
+                                           aFrame->GetStyleContext(),
+                                           aFrame->PresContext(),
+                                           dummy, bounds, aFactor);
+
+  const nsStyleDisplay* parentDisp = nsnull;
+  if (aFrame->GetParent()) {
+    parentDisp = aFrame->GetParent()->GetStyleDisplay();
+  }
+  if (nsLayoutUtils::Are3DTransformsEnabled() &&
+      parentDisp && parentDisp->mChildPerspective.GetUnit() == eStyleUnit_Coord &&
+      parentDisp->mChildPerspective.GetCoordValue() > 0.0) {
+    gfx3DMatrix perspective;
+    perspective._34 =
+      -1.0 / NSAppUnitsToFloatPixels(parentDisp->mChildPerspective.GetCoordValue(),
+                                     aFactor);
+    result = result * nsLayoutUtils::ChangeMatrixBasis(toPerspectiveOrigin, perspective);
+  }
   return nsLayoutUtils::ChangeMatrixBasis
-    (newOrigin + toMozOrigin, 
-     nsStyleTransformMatrix::ReadTransforms(disp->mSpecifiedTransform,
-                                            aFrame->GetStyleContext(),
-                                            aFrame->PresContext(),
-                                            dummy, bounds, aFactor));
-}
-
-const gfx3DMatrix&
-nsDisplayTransform::GetTransform(float aFactor)
-{
-  if (mTransform.IsIdentity() || mCachedFactor != aFactor) {
-    mTransform =
-      GetResultingTransformMatrix(mFrame, ToReferenceFrame(),
-                                  aFactor,
-                                  nsnull);
-    mCachedFactor = aFactor;
-  }
+    (newOrigin + toMozOrigin, result);
+}
+
+const gfx3DMatrix&
+nsDisplayTransform::GetTransform(float aFactor)
+{
+  if (mTransform.IsIdentity() || mCachedFactor != aFactor) {
+    mTransform =
+      GetResultingTransformMatrix(mFrame, ToReferenceFrame(),
+                                  aFactor,
+                                  nsnull);
+    mCachedFactor = aFactor;
+  }
   return mTransform;
 }
 
 already_AddRefed<Layer> nsDisplayTransform::BuildLayer(nsDisplayListBuilder *aBuilder,
                                                        LayerManager *aManager,
                                                        const ContainerParameters& aContainerParameters)
 {
   const gfx3DMatrix& newTransformMatrix = 
     GetTransform(mFrame->PresContext()->AppUnitsPerDevPixel());
-  if (newTransformMatrix.IsSingular())
+
+  if (newTransformMatrix.IsSingular() ||
+      (mFrame->GetStyleDisplay()->mBackfaceVisibility == NS_STYLE_BACKFACE_VISIBILITY_HIDDEN &&
+       newTransformMatrix.GetNormalVector().z <= 0.0)) {
     return nsnull;
+  }
 
   return aBuilder->LayerBuilder()->
     BuildContainerLayerFor(aBuilder, aManager, mFrame, this, *mStoredList.GetList(),
                            aContainerParameters, &newTransformMatrix);
 }
 
 nsDisplayItem::LayerState
 nsDisplayTransform::GetLayerState(nsDisplayListBuilder* aBuilder,
                                   LayerManager* aManager) {
   if (mFrame->AreLayersMarkedActive(nsChangeHint_UpdateTransformLayer))
     return LAYER_ACTIVE;
+  if (!GetTransform(mFrame->PresContext()->AppUnitsPerDevPixel()).Is2D())
+    return LAYER_ACTIVE;
   nsIFrame* activeScrolledRoot =
     nsLayoutUtils::GetActiveScrolledRootFor(mFrame, nsnull);
   return !mStoredList.ChildrenCanBeInactive(aBuilder, 
                                              aManager, 
                                              *mStoredList.GetList(), 
                                              activeScrolledRoot)
       ? LAYER_ACTIVE : LAYER_INACTIVE;
 }
@@ -2468,42 +2550,46 @@ void nsDisplayTransform::HitTest(nsDispl
    *    anything).
    * 2. Invert the matrix.
    * 3. Use it to transform the rect into the correct space.
    * 4. Pass that rect down through to the list's version of HitTest.
    */
   float factor = nsPresContext::AppUnitsPerCSSPixel();
   gfx3DMatrix matrix = GetTransform(factor);
 
-  if (matrix.IsSingular())
-    return;
+  /* If the matrix is singular, or a hidden backface is shown, we didn't hit anything. */
+  if (matrix.IsSingular() ||
+      (mFrame->GetStyleDisplay()->mBackfaceVisibility == NS_STYLE_BACKFACE_VISIBILITY_HIDDEN &&
+       matrix.GetNormalVector().z <= 0.0)) {
+          return;
+  }
 
   /* We want to go from transformed-space to regular space.
    * Thus we have to invert the matrix, which normally does
    * the reverse operation (e.g. regular->transformed)
    */
-  matrix.Invert();
 
   /* Now, apply the transform and pass it down the channel. */
   nsRect resultingRect;
   if (aRect.width == 1 && aRect.height == 1) {
-    gfxPoint point = matrix.Transform(gfxPoint(NSAppUnitsToFloatPixels(aRect.x, factor),
-                                               NSAppUnitsToFloatPixels(aRect.y, factor)));
+    gfxPoint point = matrix.Inverse().ProjectPoint(
+                       gfxPoint(NSAppUnitsToFloatPixels(aRect.x, factor),
+                                NSAppUnitsToFloatPixels(aRect.y, factor)));
 
     resultingRect = nsRect(NSFloatPixelsToAppUnits(float(point.x), factor),
                            NSFloatPixelsToAppUnits(float(point.y), factor),
                            1, 1);
 
   } else {
     gfxRect originalRect(NSAppUnitsToFloatPixels(aRect.x, factor),
                          NSAppUnitsToFloatPixels(aRect.y, factor),
                          NSAppUnitsToFloatPixels(aRect.width, factor),
                          NSAppUnitsToFloatPixels(aRect.height, factor));
 
-    gfxRect rect = matrix.TransformBounds(originalRect);
+    gfxRect rect = matrix.Inverse().ProjectRectBounds(originalRect);;
 
     resultingRect = nsRect(NSFloatPixelsToAppUnits(float(rect.X()), factor),
                            NSFloatPixelsToAppUnits(float(rect.Y()), factor),
                            NSFloatPixelsToAppUnits(float(rect.Width()), factor),
                            NSFloatPixelsToAppUnits(float(rect.Height()), factor));
   }
   
 
@@ -2686,24 +2772,31 @@ PRBool nsDisplayTransform::UntransformRe
                   "Cannot transform a rectangle if there's no transformation!");
 
 
   /* Grab the matrix.  If the transform is degenerate, just hand back the
    * empty rect.
    */
   float factor = nsPresContext::AppUnitsPerCSSPixel();
   gfx3DMatrix matrix = GetResultingTransformMatrix(aFrame, aOrigin, factor, nsnull);
-  if (matrix.IsSingular() || !matrix.Is2D())
+  if (matrix.IsSingular())
     return PR_FALSE;
 
+  gfxRect result(NSAppUnitsToFloatPixels(aUntransformedBounds.x, factor),
+                 NSAppUnitsToFloatPixels(aUntransformedBounds.y, factor),
+                 NSAppUnitsToFloatPixels(aUntransformedBounds.width, factor),
+                 NSAppUnitsToFloatPixels(aUntransformedBounds.height, factor));
+
   /* We want to untransform the matrix, so invert the transformation first! */
-  matrix.Invert();
-
-  *aOutRect = nsLayoutUtils::MatrixTransformRect(aUntransformedBounds, matrix,
-                                                 factor);
+  result = matrix.Inverse().ProjectRectBounds(result);
+
+  *aOutRect = nsRect(NSFloatPixelsToAppUnits(float(result.x), factor),
+                     NSFloatPixelsToAppUnits(float(result.y), factor),
+                     NSFloatPixelsToAppUnits(float(result.width), factor),
+                     NSFloatPixelsToAppUnits(float(result.height), factor));
 
   return PR_TRUE;
 }
 
 nsDisplaySVGEffects::nsDisplaySVGEffects(nsDisplayListBuilder* aBuilder,
                                          nsIFrame* aFrame, nsDisplayList* aList)
     : nsDisplayWrapList(aBuilder, aFrame, aList), mEffectsFrame(aFrame),
       mBounds(aFrame->GetVisualOverflowRectRelativeToSelf())
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -72,16 +72,17 @@
 #include "nsContentUtils.h"
 #include "nsThemeConstants.h"
 #include "nsPIDOMWindow.h"
 #include "nsIBaseWindow.h"
 #include "nsIDocShell.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIWidget.h"
 #include "gfxMatrix.h"
+#include "gfxPoint3D.h"
 #include "gfxTypes.h"
 #include "gfxUserFontSet.h"
 #include "nsTArray.h"
 #include "nsHTMLCanvasElement.h"
 #include "nsICanvasRenderingContextInternal.h"
 #include "gfxPlatform.h"
 #include "nsClientRect.h"
 #ifdef MOZ_MEDIA
@@ -102,16 +103,18 @@
 #include "nsTextFrame.h"
 #include "nsFontFaceList.h"
 
 #include "nsSVGUtils.h"
 #include "nsSVGIntegrationUtils.h"
 #include "nsSVGForeignObjectFrame.h"
 #include "nsSVGOuterSVGFrame.h"
 
+#include "mozilla/Preferences.h"
+
 #ifdef MOZ_XUL
 #include "nsXULPopupManager.h"
 #endif
 
 using namespace mozilla::layers;
 using namespace mozilla::dom;
 namespace css = mozilla::css;
 
@@ -134,16 +137,32 @@ static ContentMap& GetContentMap() {
     nsresult rv =
 #endif
     sContentMap->Init();
     NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv), "Could not initialize map.");
   }
   return *sContentMap;
 }
 
+
+PRBool
+nsLayoutUtils::Are3DTransformsEnabled()
+{
+  static PRBool s3DTransformsEnabled;
+  static PRBool s3DTransformPrefCached = PR_FALSE;
+
+  if (!s3DTransformPrefCached) {
+    s3DTransformPrefCached = PR_TRUE;
+    mozilla::Preferences::AddBoolVarCache(&s3DTransformsEnabled, 
+                                          "layout.3d-transforms.enabled");
+  }
+
+  return s3DTransformsEnabled;
+}
+
 static void DestroyViewID(void* aObject, nsIAtom* aPropertyName,
                           void* aPropertyValue, void* aData)
 {
   ViewID* id = static_cast<ViewID*>(aPropertyValue);
   GetContentMap().Remove(*id);
   delete id;
 }
 
@@ -978,26 +997,24 @@ nsLayoutUtils::GetPopupFrameForEventCoor
       return popup;
     }
   }
 #endif
   return nsnull;
 }
 
 gfx3DMatrix
-nsLayoutUtils::ChangeMatrixBasis(const gfxPoint &aOrigin,
+nsLayoutUtils::ChangeMatrixBasis(const gfxPoint3D &aOrigin,
                                  const gfx3DMatrix &aMatrix)
 {
   /* These are translation matrices from world-to-origin of relative frame and
-   * vice-versa.  Although I could use the gfxMatrix::Translate function to
-   * accomplish this, I'm hoping to reduce the overall number of matrix
-   * operations by hardcoding as many of the matrices as possible.
+   * vice-versa.
    */
-  gfx3DMatrix worldToOrigin = gfx3DMatrix::From2D(gfxMatrix(1.0, 0.0, 0.0, 1.0, -aOrigin.x, -aOrigin.y));
-  gfx3DMatrix originToWorld = gfx3DMatrix::From2D(gfxMatrix(1.0, 0.0, 0.0, 1.0,  aOrigin.x,  aOrigin.y));
+  gfx3DMatrix worldToOrigin = gfx3DMatrix::Translation(-aOrigin);
+  gfx3DMatrix originToWorld = gfx3DMatrix::Translation(aOrigin);
 
   /* Multiply all three to get the transform! */
   return worldToOrigin * aMatrix * originToWorld;
 }
 
 /**
  * Given a gfxFloat, constrains its value to be between nscoord_MIN and nscoord_MAX.
  *
@@ -1088,53 +1105,91 @@ nsLayoutUtils::MatrixTransformPoint(cons
                                     const gfx3DMatrix &aMatrix, float aFactor)
 {
   gfxPoint image = aMatrix.Transform(gfxPoint(NSAppUnitsToFloatPixels(aPoint.x, aFactor),
                                               NSAppUnitsToFloatPixels(aPoint.y, aFactor)));
   return nsPoint(NSFloatPixelsToAppUnits(float(image.x), aFactor),
                  NSFloatPixelsToAppUnits(float(image.y), aFactor));
 }
 
-gfx3DMatrix nsLayoutUtils::GetTransformToAncestor(nsIFrame *aFrame,
-                                                  nsIFrame* aStopAtAncestor)
+static gfxPoint 
+InvertTransformsToAncestor(nsIFrame *aFrame,
+                           const gfxPoint &aPoint,
+                           nsIFrame *aStopAtAncestor = nsnull)
 {
-  gfx3DMatrix ctm;
-
-  /* Starting at the specified frame, we'll use the GetTransformMatrix
-   * function of the frame, which gives us a matrix from this frame up
-   * to some other ancestor frame. If aStopAtAncestor frame is not reached, 
-   * we stop at root. We get the CTM by simply accumulating all of these
-   * matrices together.
+  NS_PRECONDITION(aFrame, "Why are you inverting transforms when there is no frame?");
+
+  /* To invert everything to the root, we'll get the CTM, invert it, and use it to transform
+   * the point.
    */
-  while (aFrame && aFrame != aStopAtAncestor) {
-    ctm *= aFrame->GetTransformMatrix(&aFrame);
+  nsIFrame *parent = nsnull;
+  gfx3DMatrix ctm = aFrame->GetTransformMatrix(&parent);
+  gfxPoint result = aPoint;
+  
+  if (parent && parent != aStopAtAncestor) {
+      result = InvertTransformsToAncestor(parent, aPoint, aStopAtAncestor);
   }
-  NS_ASSERTION(aFrame == aStopAtAncestor, "How did we manage to miss the ancestor?");
-  return ctm;
+
+  result = ctm.Inverse().ProjectPoint(result);
+  return result;
+}
+
+static gfxRect
+InvertGfxRectToAncestor(nsIFrame *aFrame,
+                     const gfxRect &aRect,
+                     nsIFrame *aStopAtAncestor = nsnull)
+{
+  NS_PRECONDITION(aFrame, "Why are you inverting transforms when there is no frame?");
+
+  /* To invert everything to the root, we'll get the CTM, invert it, and use it to transform
+   * the point.
+   */
+  nsIFrame *parent = nsnull;
+  gfx3DMatrix ctm = aFrame->GetTransformMatrix(&parent);
+  gfxRect result = aRect;
+  
+  if (parent && parent != aStopAtAncestor) {
+      result = InvertGfxRectToAncestor(parent, aRect, aStopAtAncestor);
+  }
+
+  result = ctm.Inverse().ProjectRectBounds(result);
+  return result;
 }
 
 nsPoint
 nsLayoutUtils::InvertTransformsToRoot(nsIFrame *aFrame,
                                       const nsPoint &aPoint)
 {
-  NS_PRECONDITION(aFrame, "Why are you inverting transforms when there is no frame?");
-
-  /* To invert everything to the root, we'll get the CTM, invert it, and use it to transform
-   * the point.
-   */
-  gfx3DMatrix ctm = GetTransformToAncestor(aFrame);
-
-  /* If the ctm is singular, hand back (0, 0) as a sentinel. */
-  if (ctm.IsSingular())
-    return nsPoint(0, 0);
-
-  /* TODO: Correctly handle 3d transforms when they start being used */
-
-  /* Otherwise, invert the CTM and use it to transform the point. */
-  return MatrixTransformPoint(aPoint, ctm.Invert(), aFrame->PresContext()->AppUnitsPerDevPixel());
+    float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
+    gfxPoint result(NSAppUnitsToFloatPixels(aPoint.x, factor),
+                    NSAppUnitsToFloatPixels(aPoint.y, factor));
+    
+    result = InvertTransformsToAncestor(aFrame, result);
+   
+    return nsPoint(NSFloatPixelsToAppUnits(float(result.x), factor),
+                   NSFloatPixelsToAppUnits(float(result.y), factor));
+}
+
+nsRect 
+nsLayoutUtils::TransformRectToBoundsInAncestor(nsIFrame* aFrame,
+                                               const nsRect &aRect,
+                                               nsIFrame* aStopAtAncestor)
+{
+    float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
+    gfxRect result(NSAppUnitsToFloatPixels(aRect.x, factor),
+                   NSAppUnitsToFloatPixels(aRect.y, factor),
+                   NSAppUnitsToFloatPixels(aRect.width, factor),
+                   NSAppUnitsToFloatPixels(aRect.height, factor));
+
+    result = InvertGfxRectToAncestor(aFrame, result, aStopAtAncestor);
+
+    return nsRect(NSFloatPixelsToAppUnits(float(result.x), factor),
+                  NSFloatPixelsToAppUnits(float(result.y), factor),
+                  NSFloatPixelsToAppUnits(float(result.width), factor),
+                  NSFloatPixelsToAppUnits(float(result.height), factor));
 }
 
 static nsIntPoint GetWidgetOffset(nsIWidget* aWidget, nsIWidget*& aRootWidget) {
   nsIntPoint offset(0, 0);
   nsIWidget* parent = aWidget->GetParent();
   while (parent) {
     nsIntRect bounds;
     aWidget->GetBounds(bounds);
@@ -3155,21 +3210,16 @@ nsLayoutUtils::GetClosestLayer(nsIFrame*
     return layer;
   return aFrame->PresContext()->PresShell()->FrameManager()->GetRootFrame();
 }
 
 GraphicsFilter
 nsLayoutUtils::GetGraphicsFilterForFrame(nsIFrame* aForFrame)
 {
   GraphicsFilter defaultFilter = gfxPattern::FILTER_GOOD;
-#ifdef MOZ_GFX_OPTIMIZE_MOBILE
-  if (!mozilla::supports_neon()) {
-    defaultFilter = gfxPattern::FILTER_NEAREST;
-  }
-#endif
   nsIFrame *frame = nsCSSRendering::IsCanvasFrame(aForFrame) ?
     nsCSSRendering::FindBackgroundStyleFrame(aForFrame) : aForFrame;
 
   switch (frame->GetStyleSVG()->mImageRendering) {
   case NS_STYLE_IMAGE_RENDERING_OPTIMIZESPEED:
     return gfxPattern::FILTER_FAST;
   case NS_STYLE_IMAGE_RENDERING_OPTIMIZEQUALITY:
     return gfxPattern::FILTER_BEST;
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -456,17 +456,17 @@ public:
    * that's equivalent to aMatrix but in the coordinate space that treats aOrigin
    * as the origin.
    *
    * @param aOrigin The origin to translate to.
    * @param aMatrix The matrix to change the basis of.
    * @return A matrix equivalent to aMatrix, but operating in the coordinate system with
    *         origin aOrigin.
    */
-  static gfx3DMatrix ChangeMatrixBasis(const gfxPoint &aOrigin, const gfx3DMatrix &aMatrix);
+  static gfx3DMatrix ChangeMatrixBasis(const gfxPoint3D &aOrigin, const gfx3DMatrix &aMatrix);
 
   /**
    * Find IDs corresponding to a scrollable content element in the child process.
    * In correspondence with the shadow layer tree, you can use this to perform a
    * hit test that corresponds to a specific shadow layer that you can then perform
    * transformations on to do parent-side scrolling.
    *
    * @param aFrame The root frame of a stack context
@@ -505,41 +505,34 @@ public:
    * @param aIgnoreRootScrollFrame whether or not the display list builder
    * should ignore the root scroll frame.
    */
   static nsresult GetFramesForArea(nsIFrame* aFrame, const nsRect& aRect,
                                    nsTArray<nsIFrame*> &aOutFrames,
                                    PRBool aShouldIgnoreSuppression = PR_FALSE,
                                    PRBool aIgnoreRootScrollFrame = PR_FALSE);
 
-  /**
-   * Returns the CTM at the specified frame. This matrix can be used to map
-   * coordinates from aFrame's to aStopAtAncestor's coordinate system.
-   *
-   * @param aFrame The frame at which we should calculate the CTM.
-   * @param aStopAtAncestor is an ancestor frame to stop at. If it's nsnull,
-   * matrix accumulating stops at root.
-   * @return The CTM at the specified frame.
-   */
-  static gfx3DMatrix GetTransformToAncestor(nsIFrame *aFrame,
-                                            nsIFrame* aStopAtAncestor = nsnull);
+  
+
+  static nsRect TransformRectToBoundsInAncestor(nsIFrame* aFrame,
+                                                const nsRect& aRect,
+                                                nsIFrame* aStopAtAncestor);
 
   /**
    * Given a point in the global coordinate space, returns that point expressed
    * in the coordinate system of aFrame.  This effectively inverts all transforms
    * between this point and the root frame.
    *
    * @param aFrame The frame that acts as the coordinate space container.
    * @param aPoint The point, in the global space, to get in the frame-local space.
    * @return aPoint, expressed in aFrame's canonical coordinate space.
    */
   static nsPoint InvertTransformsToRoot(nsIFrame* aFrame,
                                         const nsPoint &aPt);
 
-
   /**
    * Helper function that, given a rectangle and a matrix, returns the smallest
    * rectangle containing the image of the source rectangle.
    *
    * @param aBounds The rectangle to transform.
    * @param aMatrix The matrix to transform it with.
    * @param aFactor The number of app units per graphics unit.
    * @return The smallest rect that contains the image of aBounds.
@@ -1383,16 +1376,21 @@ public:
    * entire text is to be considered.
    */
   static nsresult GetFontFacesForText(nsIFrame* aFrame,
                                       PRInt32 aStartOffset,
                                       PRInt32 aEndOffset,
                                       PRBool aFollowContinuations,
                                       nsFontFaceList* aFontFaceList);
 
+  /**
+   * Checks if CSS 3D transforms are currently enabled.
+   */
+  static PRBool Are3DTransformsEnabled();
+
   static void Shutdown();
 
 #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.
    */
--- a/layout/base/nsStyleConsts.h
+++ b/layout/base/nsStyleConsts.h
@@ -853,16 +853,20 @@ static inline mozilla::css::Side operato
 #define NS_STYLE_TEXT_RENDERING_OPTIMIZELEGIBILITY 2
 #define NS_STYLE_TEXT_RENDERING_GEOMETRICPRECISION 3
 
 // color-interpolation and color-interpolation-filters
 #define NS_STYLE_COLOR_INTERPOLATION_AUTO           0
 #define NS_STYLE_COLOR_INTERPOLATION_SRGB           1
 #define NS_STYLE_COLOR_INTERPOLATION_LINEARRGB      2
 
+// 3d Transforms - Backface visibility
+#define NS_STYLE_BACKFACE_VISIBILITY_VISIBLE        1
+#define NS_STYLE_BACKFACE_VISIBILITY_HIDDEN         0
+
 /*****************************************************************************
  * Constants for media features.                                             *
  *****************************************************************************/
 
 // orientation
 #define NS_STYLE_ORIENTATION_PORTRAIT           0
 #define NS_STYLE_ORIENTATION_LANDSCAPE          1
 
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -4209,17 +4209,17 @@ nsIFrame::GetTransformMatrix(nsIFrame **
     NS_ASSERTION(*aOutAncestor, "Cannot transform the viewport frame!");
     nsPoint delta = GetOffsetToCrossDoc(*aOutAncestor);
     PRInt32 scaleFactor = PresContext()->AppUnitsPerDevPixel();
 
     gfx3DMatrix result =
       nsDisplayTransform::GetResultingTransformMatrix(this, nsPoint(0, 0),
                                                       scaleFactor);
     /* Combine the raw transform with a translation to our parent. */
-    result = result * gfx3DMatrix::Translation
+    result *= gfx3DMatrix::Translation
       (NSAppUnitsToFloatPixels(delta.x, scaleFactor),
        NSAppUnitsToFloatPixels(delta.y, scaleFactor),
        0.0f);
     return result;
   }
   
   /* Otherwise, we're not transformed.  In that case, we'll walk up the frame
    * tree until we either hit the root frame or something that may be
--- a/layout/reftests/backgrounds/reftest.list
+++ b/layout/reftests/backgrounds/reftest.list
@@ -94,17 +94,17 @@ fails-if(Android) == viewport-translucen
 == background-size-monster-px.html background-size-monster-ref.html
 == background-size-monster-rem.html background-size-monster-ref.html
 
 # There seems to be a pixel-snapping problem here, where the repeated background
 # image isn't being snapped at its boundaries.  Note that the boundaries within
 # the image aren't the issue, because they're being obscured to avoid sampling
 # algorithm dependencies (at least assuming the sampling algorithm in use
 # doesn't sample too far astray from the boundaries).
-fails-if(!Android) == background-size-zoom-repeat.html background-size-zoom-repeat-ref.html
+fails == background-size-zoom-repeat.html background-size-zoom-repeat-ref.html
 
 # background-size affects images without intrinsic dimensions specially; we may
 # not support such image formats right now, but when we do, we want
 # background-size to be changed accordingly, and hopefully these tests should
 # start failing when we do.
 fails == background-size-no-intrinsic-width-image.html background-size-no-intrinsic-width-image-ref.html
 fails == background-size-no-intrinsic-height-image.html background-size-no-intrinsic-height-image-ref.html
 
--- a/layout/style/nsCSSKeywordList.h
+++ b/layout/style/nsCSSKeywordList.h
@@ -328,16 +328,17 @@ CSS_KEY(lower-alpha, lower_alpha)
 CSS_KEY(lower-greek, lower_greek)
 CSS_KEY(lower-latin, lower_latin)
 CSS_KEY(lower-roman, lower_roman)
 CSS_KEY(lowercase, lowercase)
 CSS_KEY(ltr, ltr)
 CSS_KEY(manual, manual)
 CSS_KEY(margin-box, margin_box)
 CSS_KEY(matrix, matrix)
+CSS_KEY(matrix3d, matrix3d)
 CSS_KEY(medium, medium)
 CSS_KEY(menu, menu)
 CSS_KEY(menutext, menutext)
 CSS_KEY(message-box, message_box)
 CSS_KEY(middle, middle)
 CSS_KEY(mix, mix)
 CSS_KEY(mm, mm)
 CSS_KEY(move, move)
@@ -361,16 +362,17 @@ CSS_KEY(oblique, oblique)
 CSS_KEY(open-quote, open_quote)
 CSS_KEY(outset, outset)
 CSS_KEY(outside, outside)
 CSS_KEY(overline, overline)
 CSS_KEY(padding-box, padding_box)
 CSS_KEY(painted, painted)
 CSS_KEY(paused, paused)
 CSS_KEY(pc, pc)
+CSS_KEY(perspective, perspective)
 CSS_KEY(physical, physical)
 CSS_KEY(pointer, pointer)
 CSS_KEY(portrait, portrait)
 CSS_KEY(pre, pre)
 CSS_KEY(pre-wrap, pre_wrap)
 CSS_KEY(pre-line, pre_line)
 CSS_KEY(progress, progress)
 CSS_KEY(progressive, progressive)
@@ -382,25 +384,31 @@ CSS_KEY(read-write, read_write)
 CSS_KEY(relative, relative)
 CSS_KEY(repeat, repeat)
 CSS_KEY(repeat-x, repeat_x)
 CSS_KEY(repeat-y, repeat_y)
 CSS_KEY(reverse, reverse)
 CSS_KEY(ridge, ridge)
 CSS_KEY(right, right)
 CSS_KEY(rotate, rotate)
+CSS_KEY(rotate3d, rotate3d)
+CSS_KEY(rotatex, rotatex)
+CSS_KEY(rotatey, rotatey)
+CSS_KEY(rotatez, rotatez)
 CSS_KEY(round, round)
 CSS_KEY(row-resize, row_resize)
 CSS_KEY(rtl, rtl)
 CSS_KEY(running, running)
 CSS_KEY(s, s)
 CSS_KEY(s-resize, s_resize)
 CSS_KEY(scale, scale)
+CSS_KEY(scale3d, scale3d)
 CSS_KEY(scalex, scalex)
 CSS_KEY(scaley, scaley)
+CSS_KEY(scalez, scalez)
 CSS_KEY(scroll, scroll)
 CSS_KEY(scrollbar, scrollbar)
 CSS_KEY(scrollbar-small, scrollbar_small)
 CSS_KEY(se-resize, se_resize)
 CSS_KEY(select-after, select_after)
 CSS_KEY(select-all, select_all)
 CSS_KEY(select-before, select_before)
 CSS_KEY(select-menu, select_menu)
@@ -448,18 +456,20 @@ CSS_KEY(threeddarkshadow, threeddarkshad
 CSS_KEY(threedface, threedface)
 CSS_KEY(threedhighlight, threedhighlight)
 CSS_KEY(threedlightshadow, threedlightshadow)
 CSS_KEY(threedshadow, threedshadow)
 CSS_KEY(toggle, toggle)
 CSS_KEY(top, top)
 CSS_KEY(top-outside, top_outside)
 CSS_KEY(translate, translate)
+CSS_KEY(translate3d, translate3d)
 CSS_KEY(translatex, translatex)
 CSS_KEY(translatey, translatey)
+CSS_KEY(translatez, translatez)
 CSS_KEY(transparent, transparent) // for nsComputedDOMStyle only
 CSS_KEY(tri-state, tri_state)
 CSS_KEY(ultra-condensed, ultra_condensed)
 CSS_KEY(ultra-expanded, ultra_expanded)
 CSS_KEY(underline, underline)
 CSS_KEY(upper-alpha, upper_alpha)
 CSS_KEY(upper-latin, upper_latin)
 CSS_KEY(upper-roman, upper_roman)
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -82,16 +82,17 @@
 #include "math.h"
 #include "nsContentUtils.h"
 #include "nsDOMError.h"
 #include "nsAutoPtr.h"
 #include "nsTArray.h"
 #include "prlog.h"
 #include "CSSCalc.h"
 #include "nsMediaFeatures.h"
+#include "nsLayoutUtils.h"
 
 namespace css = mozilla::css;
 
 // Flags for ParseVariant method
 #define VARIANT_KEYWORD         0x000001  // K
 #define VARIANT_LENGTH          0x000002  // L
 #define VARIANT_PERCENT         0x000004  // P
 #define VARIANT_COLOR           0x000008  // C eCSSUnit_Color, eCSSUnit_Ident (e.g.  "red")
@@ -115,16 +116,17 @@ namespace css = mozilla::css;
 #define VARIANT_GRADIENT        0x200000  // eCSSUnit_Gradient
 #define VARIANT_TIMING_FUNCTION 0x400000  // cubic-bezier() and steps()
 #define VARIANT_ALL             0x800000  //
 #define VARIANT_IMAGE_RECT    0x01000000  // eCSSUnit_Function
 // This is an extra bit that says that a VARIANT_ANGLE allows unitless zero:
 #define VARIANT_ZERO_ANGLE    0x02000000  // unitless zero for angles
 #define VARIANT_CALC          0x04000000  // eCSSUnit_Calc
 #define VARIANT_ELEMENT       0x08000000  // eCSSUnit_Element
+#define VARIANT_POSITIVE_LENGTH 0x10000000 // Only lengths greater than 0.0
 
 // Common combinations of variants
 #define VARIANT_AL   (VARIANT_AUTO | VARIANT_LENGTH)
 #define VARIANT_LP   (VARIANT_LENGTH | VARIANT_PERCENT)
 #define VARIANT_AH   (VARIANT_AUTO | VARIANT_INHERIT)
 #define VARIANT_AHLP (VARIANT_AH | VARIANT_LP)
 #define VARIANT_AHI  (VARIANT_AH | VARIANT_INTEGER)
 #define VARIANT_AHK  (VARIANT_AH | VARIANT_KEYWORD)
@@ -580,28 +582,27 @@ protected:
     NS_ASSERTION(aBool == PR_TRUE || aBool == PR_FALSE, "bad PRBool value");
     mParsingCompoundProperty = aBool;
   }
   PRBool IsParsingCompoundProperty(void) const {
     return mParsingCompoundProperty;
   }
 
   /* Functions for -moz-transform Parsing */
-  PRBool ParseSingleTransform(nsCSSValue& aValue);
+  PRBool ParseSingleTransform(nsCSSValue& aValue, PRBool& aIs3D);
   PRBool ParseFunction(const nsString &aFunction, const PRInt32 aAllowedTypes[],
                        PRUint16 aMinElems, PRUint16 aMaxElems,
                        nsCSSValue &aValue);
   PRBool ParseFunctionInternals(const PRInt32 aVariantMask[],
                                 PRUint16 aMinElems,
                                 PRUint16 aMaxElems,
                                 nsTArray<nsCSSValue>& aOutput);
 
-  /* Functions for -moz-transform-origin Parsing */
-  PRBool ParseMozTransformOrigin();
-
+  /* Functions for -moz-transform-origin/-moz-perspective-origin Parsing */
+  PRBool ParseMozTransformOrigin(PRBool aPerspective);
 
   /* Find and return the namespace ID associated with aPrefix.
      If aPrefix has not been declared in an @namespace rule, returns
      kNameSpaceID_Unknown and sets mFoundUnresolvablePrefix to true. */
   PRInt32 GetNamespaceIdForPrefix(const nsString& aPrefix);
 
   /* Find the correct default namespace, and set it on aSelector. */
   void SetDefaultNamespaceOnSelector(nsCSSSelector& aSelector);
@@ -4539,16 +4540,22 @@ CSSParserImpl::ParseVariant(nsCSSValue& 
     return PR_TRUE;
   }
   if (((aVariantMask & (VARIANT_LENGTH | VARIANT_ANGLE |
                         VARIANT_FREQUENCY | VARIANT_TIME)) != 0 &&
        eCSSToken_Dimension == tk->mType) ||
       ((aVariantMask & (VARIANT_LENGTH | VARIANT_ZERO_ANGLE)) != 0 &&
        eCSSToken_Number == tk->mType &&
        tk->mNumber == 0.0f)) {
+    if ((aVariantMask & VARIANT_POSITIVE_LENGTH) != 0 && 
+        eCSSToken_Number == tk->mType &&
+        tk->mNumber <= 0.0) {
+        UngetToken();
+        return PR_FALSE;
+    }
     if (TranslateDimension(aValue, aVariantMask, tk->mNumber, tk->mIdent)) {
       return PR_TRUE;
     }
     // Put the token back; we didn't parse it, so we shouldn't consume it
     UngetToken();
     return PR_FALSE;
   }
   if (((aVariantMask & VARIANT_PERCENT) != 0) &&
@@ -5536,17 +5543,19 @@ CSSParserImpl::ParsePropertyByFunction(n
     return ParseQuotes();
   case eCSSProperty_size:
     return ParseSize();
   case eCSSProperty_text_decoration:
     return ParseTextDecoration();
   case eCSSProperty__moz_transform:
     return ParseMozTransform();
   case eCSSProperty__moz_transform_origin:
-    return ParseMozTransformOrigin();
+    return ParseMozTransformOrigin(PR_FALSE);
+  case eCSSProperty_perspective_origin:
+    return ParseMozTransformOrigin(PR_TRUE);
   case eCSSProperty_transition:
     return ParseTransition();
   case eCSSProperty_animation:
     return ParseAnimation();
   case eCSSProperty_transition_property:
     return ParseTransitionProperty();
   case eCSSProperty_fill:
   case eCSSProperty_stroke:
@@ -7258,74 +7267,120 @@ CSSParserImpl::ParseFunction(const nsStr
  * @param aMinElems [out] The minimum number of elements to read.
  * @param aMaxElems [out] The maximum number of elements to read
  * @param aVariantMask [out] The variant mask to use during parsing
  * @return Whether the information was loaded successfully.
  */
 static PRBool GetFunctionParseInformation(nsCSSKeyword aToken,
                                           PRUint16 &aMinElems,
                                           PRUint16 &aMaxElems,
-                                          const PRInt32 *& aVariantMask)
+                                          const PRInt32 *& aVariantMask,
+                                          PRBool &aIs3D)
 {
 /* These types represent the common variant masks that will be used to
    * parse out the individual functions.  The order in the enumeration
    * must match the order in which the masks are declared.
    */
   enum { eLengthPercentCalc,
+         eLengthCalc,
          eTwoLengthPercentCalcs,
+         eTwoLengthPercentCalcsOneLengthCalc,
          eAngle,
          eTwoAngles,
          eNumber,
+         ePositiveLength,
          eTwoNumbers,
+         eThreeNumbers,
+         eThreeNumbersOneAngle,
          eMatrix,
+         eMatrix3d,
          eNumVariantMasks };
-  static const PRInt32 kMaxElemsPerFunction = 6;
+  static const PRInt32 kMaxElemsPerFunction = 16;
   static const PRInt32 kVariantMasks[eNumVariantMasks][kMaxElemsPerFunction] = {
     {VARIANT_TRANSFORM_LPCALC},
+    {VARIANT_LENGTH|VARIANT_CALC},
     {VARIANT_TRANSFORM_LPCALC, VARIANT_TRANSFORM_LPCALC},
+    {VARIANT_TRANSFORM_LPCALC, VARIANT_TRANSFORM_LPCALC, VARIANT_LENGTH|VARIANT_CALC},
     {VARIANT_ANGLE_OR_ZERO},
     {VARIANT_ANGLE_OR_ZERO, VARIANT_ANGLE_OR_ZERO},
     {VARIANT_NUMBER},
+    {VARIANT_LENGTH|VARIANT_POSITIVE_LENGTH},
     {VARIANT_NUMBER, VARIANT_NUMBER},
+    {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER},
+    {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_ANGLE_OR_ZERO},
     {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
-     VARIANT_TRANSFORM_LPCALC, VARIANT_TRANSFORM_LPCALC}};
+     VARIANT_TRANSFORM_LPCALC, VARIANT_TRANSFORM_LPCALC},
+    {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
+     VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
+     VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
+     VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER}};
 
 #ifdef DEBUG
   static const PRUint8 kVariantMaskLengths[eNumVariantMasks] =
-    {1, 2, 1, 2, 1, 2, 6};
+    {1, 1, 2, 3, 1, 2, 1, 1, 2, 3, 4, 6, 16};
 #endif
 
   PRInt32 variantIndex = eNumVariantMasks;
 
+  aIs3D = PR_FALSE;
+
   switch (aToken) {
   case eCSSKeyword_translatex:
   case eCSSKeyword_translatey:
     /* Exactly one length or percent. */
     variantIndex = eLengthPercentCalc;
     aMinElems = 1U;
     aMaxElems = 1U;
     break;
-  case eCSSKeyword_scalex:
-    /* Exactly one scale factor. */
-    variantIndex = eNumber;
+  case eCSSKeyword_translatez:
+    /* Exactly one length */
+    variantIndex = eLengthCalc;
     aMinElems = 1U;
     aMaxElems = 1U;
+    aIs3D = PR_TRUE;
     break;
+  case eCSSKeyword_translate3d:
+    /* Exactly two lengthds or percents and a number */
+    variantIndex = eTwoLengthPercentCalcsOneLengthCalc;
+    aMinElems = 3U;
+    aMaxElems = 3U;
+    aIs3D = PR_TRUE;
+    break;
+  case eCSSKeyword_scalez:
+    aIs3D = PR_TRUE;
+  case eCSSKeyword_scalex:
   case eCSSKeyword_scaley:
     /* Exactly one scale factor. */
     variantIndex = eNumber;
     aMinElems = 1U;
     aMaxElems = 1U;
     break;
+  case eCSSKeyword_scale3d:
+    /* Exactly three scale factors. */
+    variantIndex = eThreeNumbers;
+    aMinElems = 3U;
+    aMaxElems = 3U;
+    aIs3D = PR_TRUE;
+    break;
+  case eCSSKeyword_rotatex:
+  case eCSSKeyword_rotatey:
+    aIs3D = PR_TRUE;
   case eCSSKeyword_rotate:
+  case eCSSKeyword_rotatez:
     /* Exactly one angle. */
     variantIndex = eAngle;
     aMinElems = 1U;
     aMaxElems = 1U;
     break;
+  case eCSSKeyword_rotate3d:
+    variantIndex = eThreeNumbersOneAngle;
+    aMinElems = 4U;
+    aMaxElems = 4U;
+    aIs3D = PR_TRUE;
+    break;
   case eCSSKeyword_translate:
     /* One or two lengths or percents. */
     variantIndex = eTwoLengthPercentCalcs;
     aMinElems = 1U;
     aMaxElems = 2U;
     break;
   case eCSSKeyword_skew:
     /* Exactly one or two angles. */
@@ -7351,17 +7406,31 @@ static PRBool GetFunctionParseInformatio
     aMinElems = 1U;
     aMaxElems = 1U;
     break;
   case eCSSKeyword_matrix:
     /* Six values, which can be numbers, lengths, or percents. */
     variantIndex = eMatrix;
     aMinElems = 6U;
     aMaxElems = 6U;
-    break;    
+    break;
+  case eCSSKeyword_matrix3d:
+    /* 16 matrix values, all numbers */
+    variantIndex = eMatrix3d;
+    aMinElems = 16U;
+    aMaxElems = 16U;
+    aIs3D = PR_TRUE;
+    break;
+  case eCSSKeyword_perspective:
+    /* Exactly one scale number. */
+    variantIndex = ePositiveLength;
+    aMinElems = 1U;
+    aMaxElems = 1U;
+    aIs3D = PR_TRUE;
+    break;
   default:
     /* Oh dear, we didn't match.  Report an error. */
     return PR_FALSE;
   }
 
   NS_ASSERTION(aMinElems > 0, "Didn't update minimum elements!");
   NS_ASSERTION(aMaxElems > 0, "Didn't update maximum elements!");
   NS_ASSERTION(aMinElems <= aMaxElems, "aMinElems > aMaxElems!");
@@ -7377,30 +7446,32 @@ static PRBool GetFunctionParseInformatio
 
   return PR_TRUE;
 }
 
 /* Reads a single transform function from the tokenizer stream, reporting an
  * error if something goes wrong.
  */
 PRBool
-CSSParserImpl::ParseSingleTransform(nsCSSValue& aValue)
+CSSParserImpl::ParseSingleTransform(nsCSSValue& aValue, PRBool& aIs3D)
 {
   if (!GetToken(PR_TRUE))
     return PR_FALSE;
 
   if (mToken.mType != eCSSToken_Function) {
     UngetToken();
     return PR_FALSE;
   }
 
   const PRInt32* variantMask;
   PRUint16 minElems, maxElems;
-  if (!GetFunctionParseInformation(nsCSSKeywords::LookupKeyword(mToken.mIdent),
-                                   minElems, maxElems, variantMask))
+  nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
+
+  if (!GetFunctionParseInformation(keyword,
+                                   minElems, maxElems, variantMask, aIs3D))
     return PR_FALSE;
 
   return ParseFunction(mToken.mIdent, variantMask, minElems, maxElems, aValue);
 }
 
 /* Parses a -moz-transform property list by continuously reading in properties
  * and constructing a matrix from it.
  */
@@ -7410,48 +7481,70 @@ PRBool CSSParserImpl::ParseMozTransform(
   if (ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nsnull)) {
     // 'inherit', 'initial', and 'none' must be alone
     if (!ExpectEndProperty()) {
       return PR_FALSE;
     }
   } else {
     nsCSSValueList* cur = value.SetListValue();
     for (;;) {
-      if (!ParseSingleTransform(cur->mValue)) {
+      PRBool is3D;
+      if (!ParseSingleTransform(cur->mValue, is3D)) {
+        return PR_FALSE;
+      }
+      if (is3D && !nsLayoutUtils::Are3DTransformsEnabled()) {
         return PR_FALSE;
       }
       if (CheckEndProperty()) {
         break;
       }
       cur->mNext = new nsCSSValueList;
       cur = cur->mNext;
     }
   }
   AppendValue(eCSSProperty__moz_transform, value);
   return PR_TRUE;
 }
 
-PRBool CSSParserImpl::ParseMozTransformOrigin()
+PRBool CSSParserImpl::ParseMozTransformOrigin(PRBool aPerspective)
 {
   nsCSSValuePair position;
-  if (!ParseBoxPositionValues(position, PR_TRUE) || !ExpectEndProperty())
-    return PR_FALSE;
+  if (!ParseBoxPositionValues(position, PR_TRUE))
+    return PR_FALSE;
+
+  nsCSSProperty prop = eCSSProperty__moz_transform_origin;
+  if (aPerspective) {
+    if (!ExpectEndProperty()) {
+      return PR_FALSE;
+    }
+    prop = eCSSProperty_perspective_origin;
+  }
 
   // Unlike many other uses of pairs, this position should always be stored
   // as a pair, even if the values are the same, so it always serializes as
   // a pair, and to keep the computation code simple.
   if (position.mXValue.GetUnit() == eCSSUnit_Inherit ||
       position.mXValue.GetUnit() == eCSSUnit_Initial) {
     NS_ABORT_IF_FALSE(position.mXValue == position.mYValue,
                       "inherit/initial only half?");
-    AppendValue(eCSSProperty__moz_transform_origin, position.mXValue);
+    AppendValue(prop, position.mXValue);
   } else {
-    nsCSSValue pair;
-    pair.SetPairValue(&position);
-    AppendValue(eCSSProperty__moz_transform_origin, pair);
+    nsCSSValue value;
+    if (aPerspective) {
+      value.SetPairValue(position.mXValue, position.mYValue);
+    } else {
+      nsCSSValue depth;
+      if (!ParseVariant(depth, VARIANT_LENGTH | VARIANT_CALC, nsnull) ||
+          !nsLayoutUtils::Are3DTransformsEnabled()) {
+        depth.Reset();
+      }
+      value.SetTripletValue(position.mXValue, position.mYValue, depth);
+    }
+
+    AppendValue(prop, value);
   }
   return PR_TRUE;
 }
 
 PRBool
 CSSParserImpl::ParseFamily(nsCSSValue& aValue)
 {
   if (!GetToken(PR_TRUE))
--- a/layout/style/nsCSSPropList.h
+++ b/layout/style/nsCSSPropList.h
@@ -2253,16 +2253,44 @@ CSS_PROP_DISPLAY(
     _moz_transform_origin,
     CSS_PROP_DOMPROP_PREFIXED(TransformOrigin),
     CSS_PROPERTY_PARSE_FUNCTION |
         CSS_PROPERTY_STORES_CALC,
     0,
     kBackgroundPositionKTable,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_Custom)
+CSS_PROP_DISPLAY(
+    -moz-perspective-origin,
+    perspective_origin,
+    CSS_PROP_DOMPROP_PREFIXED(PerspectiveOrigin),
+    CSS_PROPERTY_PARSE_FUNCTION |
+        CSS_PROPERTY_STORES_CALC,
+    0,
+    kBackgroundPositionKTable,
+    CSS_PROP_NO_OFFSET,
+    eStyleAnimType_Custom)
+CSS_PROP_DISPLAY(
+    -moz-perspective,
+    perspective,
+    CSS_PROP_DOMPROP_PREFIXED(Perspective),
+    CSS_PROPERTY_PARSE_VALUE,
+    VARIANT_NONE | VARIANT_INHERIT | VARIANT_LENGTH,
+    nsnull,
+    offsetof(nsStyleDisplay, mChildPerspective),
+    eStyleAnimType_Coord)
+CSS_PROP_DISPLAY(
+    -moz-backface-visibility,
+    backface_visibility,
+    CSS_PROP_DOMPROP_PREFIXED(BackfaceVisibility),
+    CSS_PROPERTY_PARSE_VALUE,
+    VARIANT_HK,
+    kBackfaceVisibilityKTable,
+    offsetof(nsStyleDisplay, mBackfaceVisibility),
+    eStyleAnimType_None)
 CSS_PROP_POSITION(
     top,
     top,
     Top,
     CSS_PROPERTY_PARSE_VALUE |
         CSS_PROPERTY_STORES_CALC,
     VARIANT_AHLP | VARIANT_CALC,
     nsnull,
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -576,16 +576,21 @@ const PRInt32 nsCSSProps::kAppearanceKTa
   eCSSKeyword__moz_window_button_maximize,    NS_THEME_WINDOW_BUTTON_MAXIMIZE,
   eCSSKeyword__moz_window_button_restore,     NS_THEME_WINDOW_BUTTON_RESTORE,
   eCSSKeyword__moz_window_button_box,         NS_THEME_WINDOW_BUTTON_BOX,
   eCSSKeyword__moz_window_button_box_maximized, NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED,
   eCSSKeyword__moz_win_exclude_glass,         NS_THEME_WIN_EXCLUDE_GLASS,
   eCSSKeyword_UNKNOWN,-1
 };
 
+const PRInt32 nsCSSProps::kBackfaceVisibilityKTable[] = {
+  eCSSKeyword_visible, NS_STYLE_BACKFACE_VISIBILITY_VISIBLE,
+  eCSSKeyword_hidden, NS_STYLE_BACKFACE_VISIBILITY_HIDDEN
+};
+
 const PRInt32 nsCSSProps::kBackgroundAttachmentKTable[] = {
   eCSSKeyword_fixed, NS_STYLE_BG_ATTACHMENT_FIXED,
   eCSSKeyword_scroll, NS_STYLE_BG_ATTACHMENT_SCROLL,
   eCSSKeyword_UNKNOWN,-1
 };
 
 const PRInt32 nsCSSProps::kBackgroundInlinePolicyKTable[] = {
   eCSSKeyword_each_box,     NS_STYLE_BG_INLINE_POLICY_EACH_BOX,
--- a/layout/style/nsCSSProps.h
+++ b/layout/style/nsCSSProps.h
@@ -328,16 +328,17 @@ public:
   // Keyword/Enum value tables
   static const PRInt32 kAnimationDirectionKTable[];
   static const PRInt32 kAnimationFillModeKTable[];
   static const PRInt32 kAnimationIterationCountKTable[];
   static const PRInt32 kAnimationPlayStateKTable[];
   static const PRInt32 kAnimationTimingFunctionKTable[];
   static const PRInt32 kAppearanceKTable[];
   static const PRInt32 kAzimuthKTable[];
+  static const PRInt32 kBackfaceVisibilityKTable[];
   static const PRInt32 kBackgroundAttachmentKTable[];
   static const PRInt32 kBackgroundInlinePolicyKTable[];
   static const PRInt32 kBackgroundOriginKTable[];
   static const PRInt32 kBackgroundPositionKTable[];
   static const PRInt32 kBackgroundRepeatKTable[];
   static const PRInt32 kBackgroundSizeKTable[];
   static const PRInt32 kBorderCollapseKTable[];
   static const PRInt32 kBorderColorKTable[];
--- a/layout/style/nsCSSValue.cpp
+++ b/layout/style/nsCSSValue.cpp
@@ -158,16 +158,20 @@ nsCSSValue::nsCSSValue(const nsCSSValue&
   else if (eCSSUnit_Gradient == mUnit) {
     mValue.mGradient = aCopy.mValue.mGradient;
     mValue.mGradient->AddRef();
   }
   else if (eCSSUnit_Pair == mUnit) {
     mValue.mPair = aCopy.mValue.mPair;
     mValue.mPair->AddRef();
   }
+  else if (eCSSUnit_Triplet == mUnit) {
+    mValue.mTriplet = aCopy.mValue.mTriplet;
+    mValue.mTriplet->AddRef();
+  }
   else if (eCSSUnit_Rect == mUnit) {
     mValue.mRect = aCopy.mValue.mRect;
     mValue.mRect->AddRef();
   }
   else if (eCSSUnit_List == mUnit) {
     mValue.mList = aCopy.mValue.mList;
     mValue.mList->AddRef();
   }
@@ -227,16 +231,19 @@ PRBool nsCSSValue::operator==(const nsCS
       return *mValue.mImage == *aOther.mValue.mImage;
     }
     else if (eCSSUnit_Gradient == mUnit) {
       return *mValue.mGradient == *aOther.mValue.mGradient;
     }
     else if (eCSSUnit_Pair == mUnit) {
       return *mValue.mPair == *aOther.mValue.mPair;
     }
+    else if (eCSSUnit_Triplet == mUnit) {
+      return *mValue.mTriplet == *aOther.mValue.mTriplet;
+    }
     else if (eCSSUnit_Rect == mUnit) {
       return *mValue.mRect == *aOther.mValue.mRect;
     }
     else if (eCSSUnit_List == mUnit) {
       return *mValue.mList == *aOther.mValue.mList;
     }
     else if (eCSSUnit_PairList == mUnit) {
       return *mValue.mPairList == *aOther.mValue.mPairList;
@@ -307,16 +314,18 @@ void nsCSSValue::DoReset()
   } else if (eCSSUnit_URL == mUnit) {
     mValue.mURL->Release();
   } else if (eCSSUnit_Image == mUnit) {
     mValue.mImage->Release();
   } else if (eCSSUnit_Gradient == mUnit) {
     mValue.mGradient->Release();
   } else if (eCSSUnit_Pair == mUnit) {
     mValue.mPair->Release();
+  } else if (eCSSUnit_Triplet == mUnit) {
+    mValue.mTriplet->Release();
   } else if (eCSSUnit_Rect == mUnit) {
     mValue.mRect->Release();
   } else if (eCSSUnit_List == mUnit) {
     mValue.mList->Release();
   } else if (eCSSUnit_PairList == mUnit) {
     mValue.mPairList->Release();
   }
   mUnit = eCSSUnit_Null;
@@ -436,16 +445,56 @@ void nsCSSValue::SetPairValue(const nsCS
                     yValue.GetUnit() != eCSSUnit_Initial,
                     "inappropriate pair value");
   Reset();
   mUnit = eCSSUnit_Pair;
   mValue.mPair = new nsCSSValuePair_heap(xValue, yValue);
   mValue.mPair->AddRef();
 }
 
+void nsCSSValue::SetTripletValue(const nsCSSValueTriplet* aValue)
+{
+    // triplet should not be used for null/inherit/initial values
+    // Only allow Null for the z component
+    NS_ABORT_IF_FALSE(aValue &&
+                      aValue->mXValue.GetUnit() != eCSSUnit_Null &&
+                      aValue->mYValue.GetUnit() != eCSSUnit_Null &&
+                      aValue->mXValue.GetUnit() != eCSSUnit_Inherit &&
+                      aValue->mYValue.GetUnit() != eCSSUnit_Inherit &&
+                      aValue->mZValue.GetUnit() != eCSSUnit_Inherit &&
+                      aValue->mXValue.GetUnit() != eCSSUnit_Initial &&
+                      aValue->mYValue.GetUnit() != eCSSUnit_Initial &&
+                      aValue->mZValue.GetUnit() != eCSSUnit_Initial,
+                      "missing or inappropriate triplet value");
+    Reset();
+    mUnit = eCSSUnit_Triplet;
+    mValue.mTriplet = new nsCSSValueTriplet_heap(aValue->mXValue, aValue->mYValue, aValue->mZValue);
+    mValue.mTriplet->AddRef();
+}
+
+void nsCSSValue::SetTripletValue(const nsCSSValue& xValue,
+                                 const nsCSSValue& yValue,
+                                 const nsCSSValue& zValue)
+{
+    // Only allow Null for the z component
+    NS_ABORT_IF_FALSE(xValue.GetUnit() != eCSSUnit_Null &&
+                      yValue.GetUnit() != eCSSUnit_Null &&
+                      xValue.GetUnit() != eCSSUnit_Inherit &&
+                      yValue.GetUnit() != eCSSUnit_Inherit &&
+                      zValue.GetUnit() != eCSSUnit_Inherit &&
+                      xValue.GetUnit() != eCSSUnit_Initial &&
+                      yValue.GetUnit() != eCSSUnit_Initial &&
+                      zValue.GetUnit() != eCSSUnit_Initial,
+                      "inappropriate triplet value");
+    Reset();
+    mUnit = eCSSUnit_Triplet;
+    mValue.mTriplet = new nsCSSValueTriplet_heap(xValue, yValue, zValue);
+    mValue.mTriplet->AddRef();
+}
+
 nsCSSRect& nsCSSValue::SetRectValue()
 {
   Reset();
   mUnit = eCSSUnit_Rect;
   mValue.mRect = new nsCSSRect_heap;
   mValue.mRect->AddRef();
   return *mValue.mRect;
 }
@@ -955,16 +1004,18 @@ nsCSSValue::AppendToString(nsCSSProperty
         break;
       }
       aResult.AppendLiteral(", ");
     }
 
     aResult.AppendLiteral(")");
   } else if (eCSSUnit_Pair == unit) {
     GetPairValue().AppendToString(aProperty, aResult);
+  } else if (eCSSUnit_Triplet == unit) {
+    GetTripletValue().AppendToString(aProperty, aResult);
   } else if (eCSSUnit_Rect == unit) {
     GetRectValue().AppendToString(aProperty, aResult);
   } else if (eCSSUnit_List == unit || eCSSUnit_ListDep == unit) {
     GetListValue()->AppendToString(aProperty, aResult);
   } else if (eCSSUnit_PairList == unit || eCSSUnit_PairListDep == unit) {
     GetPairListValue()->AppendToString(aProperty, aResult);
   }
 
@@ -1006,16 +1057,17 @@ nsCSSValue::AppendToString(nsCSSProperty
     case eCSSUnit_Integer:      break;
     case eCSSUnit_Enumerated:   break;
     case eCSSUnit_EnumColor:    break;
     case eCSSUnit_Color:        break;
     case eCSSUnit_Percent:      aResult.Append(PRUnichar('%'));    break;
     case eCSSUnit_Number:       break;
     case eCSSUnit_Gradient:     break;
     case eCSSUnit_Pair:         break;
+    case eCSSUnit_Triplet:      break;
     case eCSSUnit_Rect:         break;
     case eCSSUnit_List:         break;
     case eCSSUnit_ListDep:      break;
     case eCSSUnit_PairList:     break;
     case eCSSUnit_PairListDep:  break;
 
     case eCSSUnit_Inch:         aResult.AppendLiteral("in");   break;
     case eCSSUnit_Millimeter:   aResult.AppendLiteral("mm");   break;
@@ -1172,16 +1224,33 @@ nsCSSValuePair::AppendToString(nsCSSProp
 {
   mXValue.AppendToString(aProperty, aResult);
   if (mYValue.GetUnit() != eCSSUnit_Null) {
     aResult.Append(PRUnichar(' '));
     mYValue.AppendToString(aProperty, aResult);
   }
 }
 
+// --- nsCSSValueTriple -----------------
+
+void
+nsCSSValueTriplet::AppendToString(nsCSSProperty aProperty,
+                               nsAString& aResult) const
+{
+    mXValue.AppendToString(aProperty, aResult);
+    if (mYValue.GetUnit() != eCSSUnit_Null) {
+        aResult.Append(PRUnichar(' '));
+        mYValue.AppendToString(aProperty, aResult);
+        if (mZValue.GetUnit() != eCSSUnit_Null) {
+            aResult.Append(PRUnichar(' '));
+            mZValue.AppendToString(aProperty, aResult);
+        }
+    }
+}
+
 // --- nsCSSValuePairList -----------------
 
 nsCSSValuePairList::~nsCSSValuePairList()
 {
   MOZ_COUNT_DTOR(nsCSSValuePairList);
   NS_CSS_DELETE_LIST_MEMBER(nsCSSValuePairList, this, mNext);
 }
 
--- a/layout/style/nsCSSValue.h
+++ b/layout/style/nsCSSValue.h
@@ -137,22 +137,23 @@ enum nsCSSUnit {
   eCSSUnit_Calc_Times_R = 34,     // (nsCSSValue::Array*) val * num within calc
   eCSSUnit_Calc_Divided = 35,     // (nsCSSValue::Array*) / within calc
 
   eCSSUnit_URL          = 40,     // (nsCSSValue::URL*) value
   eCSSUnit_Image        = 41,     // (nsCSSValue::Image*) value
   eCSSUnit_Gradient     = 42,     // (nsCSSValueGradient*) value
 
   eCSSUnit_Pair         = 50,     // (nsCSSValuePair*) pair of values
-  eCSSUnit_Rect         = 51,     // (nsCSSRect*) rectangle (four values)
-  eCSSUnit_List         = 52,     // (nsCSSValueList*) list of values
-  eCSSUnit_ListDep      = 53,     // (nsCSSValueList*) same as List
+  eCSSUnit_Triplet      = 51,     // (nsCSSValueTriplet*) triplet of values
+  eCSSUnit_Rect         = 52,     // (nsCSSRect*) rectangle (four values)
+  eCSSUnit_List         = 53,     // (nsCSSValueList*) list of values
+  eCSSUnit_ListDep      = 54,     // (nsCSSValueList*) same as List
                                   //   but does not own the list
-  eCSSUnit_PairList     = 54,     // (nsCSSValuePairList*) list of value pairs
-  eCSSUnit_PairListDep  = 55,     // (nsCSSValuePairList*) same as PairList
+  eCSSUnit_PairList     = 55,     // (nsCSSValuePairList*) list of value pairs
+  eCSSUnit_PairListDep  = 56,     // (nsCSSValuePairList*) same as PairList
                                   //   but does not own the list
 
   eCSSUnit_Integer      = 70,     // (int) simple value
   eCSSUnit_Enumerated   = 71,     // (int) value has enumerated meaning
 
   eCSSUnit_EnumColor    = 80,     // (int) enumerated color (kColorKTable)
   eCSSUnit_Color        = 81,     // (nscolor) an RGBA value
 
@@ -195,16 +196,18 @@ struct nsCSSValueGradient;
 struct nsCSSValuePair;
 struct nsCSSValuePair_heap;
 struct nsCSSRect;
 struct nsCSSRect_heap;
 struct nsCSSValueList;
 struct nsCSSValueList_heap;
 struct nsCSSValuePairList;
 struct nsCSSValuePairList_heap;
+struct nsCSSValueTriplet;
+struct nsCSSValueTriplet_heap;
 
 class nsCSSValue {
 public:
   struct Array;
   friend struct Array;
 
   struct URL;
   friend struct URL;
@@ -365,16 +368,19 @@ public:
   inline const nsCSSRect& GetRectValue() const;
 
   inline nsCSSValueList* GetListValue();
   inline const nsCSSValueList* GetListValue() const;
 
   inline nsCSSValuePairList* GetPairListValue();
   inline const nsCSSValuePairList* GetPairListValue() const;
 
+  inline nsCSSValueTriplet& GetTripletValue();
+  inline const nsCSSValueTriplet& GetTripletValue() const;
+
   URL* GetURLStructValue() const
   {
     // Not allowing this for Image values, because if the caller takes
     // a ref to them they won't be able to delete them properly.
     NS_ABORT_IF_FALSE(mUnit == eCSSUnit_URL, "not a URL value");
     return mValue.mURL;
   }
 
@@ -412,16 +418,18 @@ public:
   void SetArrayValue(nsCSSValue::Array* aArray, nsCSSUnit aUnit);
   void SetURLValue(nsCSSValue::URL* aURI);
   void SetImageValue(nsCSSValue::Image* aImage);
   void SetGradientValue(nsCSSValueGradient* aGradient);
   void SetPairValue(const nsCSSValuePair* aPair);
   void SetPairValue(const nsCSSValue& xValue, const nsCSSValue& yValue);
   void SetDependentListValue(nsCSSValueList* aList);
   void SetDependentPairListValue(nsCSSValuePairList* aList);
+  void SetTripletValue(const nsCSSValueTriplet* aTriplet);
+  void SetTripletValue(const nsCSSValue& xValue, const nsCSSValue& yValue, const nsCSSValue& zValue);
   void SetAutoValue();
   void SetInheritValue();
   void SetInitialValue();
   void SetNoneValue();
   void SetAllValue();
   void SetNormalValue();
   void SetSystemFontValue();
   void SetDummyValue();
@@ -526,16 +534,17 @@ protected:
     nsStringBuffer* mString;
     nscolor    mColor;
     Array*     mArray;
     URL*       mURL;
     Image*     mImage;
     nsCSSValueGradient* mGradient;
     nsCSSValuePair_heap* mPair;
     nsCSSRect_heap* mRect;
+    nsCSSValueTriplet_heap* mTriplet;
     nsCSSValueList_heap* mList;
     nsCSSValueList* mListDependent;
     nsCSSValuePairList_heap* mPairList;
     nsCSSValuePairList* mPairListDependent;
   }         mValue;
 };
 
 struct nsCSSValue::Array {
@@ -833,22 +842,98 @@ struct nsCSSValuePair {
   nsCSSValue mXValue;
   nsCSSValue mYValue;
 };
 
 // nsCSSValuePair_heap differs from nsCSSValuePair only in being
 // refcounted.  It should not be necessary to use this class directly;
 // it's an implementation detail of nsCSSValue.
 struct nsCSSValuePair_heap : public nsCSSValuePair {
+    // forward constructor
+    nsCSSValuePair_heap(const nsCSSValue& aXValue, const nsCSSValue& aYValue)
+        : nsCSSValuePair(aXValue, aYValue)
+    {}
+
+    NS_INLINE_DECL_REFCOUNTING(nsCSSValuePair_heap)
+};
+
+struct nsCSSValueTriplet {
+    nsCSSValueTriplet()
+    {
+        MOZ_COUNT_CTOR(nsCSSValueTriplet);
+    }
+    nsCSSValueTriplet(nsCSSUnit aUnit)
+        : mXValue(aUnit), mYValue(aUnit), mZValue(aUnit)
+    {
+        MOZ_COUNT_CTOR(nsCSSValueTriplet);
+    }
+    nsCSSValueTriplet(const nsCSSValue& aXValue, 
+                      const nsCSSValue& aYValue, 
+                      const nsCSSValue& aZValue)
+        : mXValue(aXValue), mYValue(aYValue), mZValue(aZValue)
+    {
+        MOZ_COUNT_CTOR(nsCSSValueTriplet);
+    }
+    nsCSSValueTriplet(const nsCSSValueTriplet& aCopy)
+        : mXValue(aCopy.mXValue), mYValue(aCopy.mYValue), mZValue(aCopy.mZValue)
+    {
+        MOZ_COUNT_CTOR(nsCSSValueTriplet);
+    }
+    ~nsCSSValueTriplet()
+    {
+        MOZ_COUNT_DTOR(nsCSSValueTriplet);
+    }
+
+    PRBool operator==(const nsCSSValueTriplet& aOther) const {
+        return mXValue == aOther.mXValue &&
+               mYValue == aOther.mYValue &&
+               mZValue == aOther.mZValue;
+    }
+
+    PRBool operator!=(const nsCSSValueTriplet& aOther) const {
+        return mXValue != aOther.mXValue ||
+               mYValue != aOther.mYValue ||
+               mZValue != aOther.mZValue;
+    }
+
+    void SetAllValuesTo(const nsCSSValue& aValue) {
+        mXValue = aValue;
+        mYValue = aValue;
+        mZValue = aValue;
+    }
+
+    void Reset() {
+        mXValue.Reset();
+        mYValue.Reset();
+        mZValue.Reset();
+    }
+
+    PRBool HasValue() const {
+        return mXValue.GetUnit() != eCSSUnit_Null ||
+               mYValue.GetUnit() != eCSSUnit_Null ||
+               mZValue.GetUnit() != eCSSUnit_Null;
+    }
+
+    void AppendToString(nsCSSProperty aProperty, nsAString& aResult) const;
+
+    nsCSSValue mXValue;
+    nsCSSValue mYValue;
+    nsCSSValue mZValue;
+};
+
+// nsCSSValueTriplet_heap differs from nsCSSValueTriplet only in being
+// refcounted.  It should not be necessary to use this class directly;
+// it's an implementation detail of nsCSSValue.
+struct nsCSSValueTriplet_heap : public nsCSSValueTriplet {
   // forward constructor
-  nsCSSValuePair_heap(const nsCSSValue& aXValue, const nsCSSValue& aYValue)
-    : nsCSSValuePair(aXValue, aYValue)
+  nsCSSValueTriplet_heap(const nsCSSValue& aXValue, const nsCSSValue& aYValue, const nsCSSValue& aZValue)
+    : nsCSSValueTriplet(aXValue, aYValue, aZValue)
   {}
 
-  NS_INLINE_DECL_REFCOUNTING(nsCSSValuePair_heap)
+  NS_INLINE_DECL_REFCOUNTING(nsCSSValueTriplet_heap)
 };
 
 // This has to be here so that the relationship between nsCSSValuePair
 // and nsCSSValuePair_heap is visible.
 inline nsCSSValuePair&
 nsCSSValue::GetPairValue()
 {
   NS_ABORT_IF_FALSE(mUnit == eCSSUnit_Pair, "not a pair value");
@@ -857,16 +942,30 @@ nsCSSValue::GetPairValue()
 
 inline const nsCSSValuePair&
 nsCSSValue::GetPairValue() const
 {
   NS_ABORT_IF_FALSE(mUnit == eCSSUnit_Pair, "not a pair value");
   return *mValue.mPair;
 }
 
+inline nsCSSValueTriplet&
+nsCSSValue::GetTripletValue()
+{
+    NS_ABORT_IF_FALSE(mUnit == eCSSUnit_Triplet, "not a triplet value");
+    return *mValue.mTriplet;
+}
+
+inline const nsCSSValueTriplet&
+nsCSSValue::GetTripletValue() const
+{
+    NS_ABORT_IF_FALSE(mUnit == eCSSUnit_Triplet, "not a triplet value");
+    return *mValue.mTriplet;
+}
+
 // Maybe should be replaced with nsCSSValueList and nsCSSValue::Array?
 struct nsCSSValuePairList {
   nsCSSValuePairList() : mNext(nsnull) { MOZ_COUNT_CTOR(nsCSSValuePairList); }
   ~nsCSSValuePairList();
 
   nsCSSValuePairList* Clone() const; // makes a deep copy
   void AppendToString(nsCSSProperty aProperty, nsAString& aResult) const;
 
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -933,19 +933,79 @@ nsComputedDOMStyle::DoGetMozTransformOri
                   &nsComputedDOMStyle::GetFrameBoundsWidthForTransform);
   valueList->AppendCSSValue(width);
 
   nsROCSSPrimitiveValue* height = GetROCSSPrimitiveValue();
   SetValueToCoord(height, display->mTransformOrigin[1], PR_FALSE,
                   &nsComputedDOMStyle::GetFrameBoundsHeightForTransform);
   valueList->AppendCSSValue(height);
 
+  if (display->mTransformOrigin[2].GetUnit() != eStyleUnit_Coord ||
+      display->mTransformOrigin[2].GetCoordValue() != 0) {
+    nsROCSSPrimitiveValue* depth = GetROCSSPrimitiveValue();
+    SetValueToCoord(depth, display->mTransformOrigin[2], PR_FALSE,
+                    nsnull);
+    valueList->AppendCSSValue(depth);
+  }
+
   return valueList;
 }
 
+/* Convert the stored representation into a list of two values and then hand
+ * it back.
+ */
+nsIDOMCSSValue*
+nsComputedDOMStyle::DoGetMozPerspectiveOrigin()
+{
+  /* We need to build up a list of two values.  We'll call them
+   * width and height.
+   */
+
+  /* Store things as a value list */
+  nsDOMCSSValueList* valueList = GetROCSSValueList(PR_FALSE);
+
+  /* Now, get the values. */
+  const nsStyleDisplay* display = GetStyleDisplay();
+
+  nsROCSSPrimitiveValue* width = GetROCSSPrimitiveValue();
+  SetValueToCoord(width, display->mPerspectiveOrigin[0], PR_FALSE,
+                  &nsComputedDOMStyle::GetFrameBoundsWidthForTransform);
+  valueList->AppendCSSValue(width);
+
+  nsROCSSPrimitiveValue* height = GetROCSSPrimitiveValue();
+  SetValueToCoord(height, display->mPerspectiveOrigin[1], PR_FALSE,
+                  &nsComputedDOMStyle::GetFrameBoundsHeightForTransform);
+  valueList->AppendCSSValue(height);
+
+  return valueList;
+}
+
+nsIDOMCSSValue*
+nsComputedDOMStyle::DoGetMozPerspective()
+{
+    nsROCSSPrimitiveValue* val = GetROCSSPrimitiveValue();
+    if (GetStyleDisplay()->mChildPerspective.GetUnit() == eStyleUnit_Coord &&
+        GetStyleDisplay()->mChildPerspective.GetCoordValue() == 0.0) {
+        val->SetIdent(eCSSKeyword_none);
+    } else {
+        SetValueToCoord(val, GetStyleDisplay()->mChildPerspective, PR_FALSE);
+    }
+    return val;
+}
+
+nsIDOMCSSValue*
+nsComputedDOMStyle::DoGetMozBackfaceVisibility()
+{
+    nsROCSSPrimitiveValue* val = GetROCSSPrimitiveValue();
+    val->SetIdent(
+        nsCSSProps::ValueToKeywordEnum(GetStyleDisplay()->mBackfaceVisibility,
+                                       nsCSSProps::kBackfaceVisibilityKTable));
+    return val;
+}
+
 /* If the property is "none", hand back "none" wrapped in a value.
  * Otherwise, compute the aggregate transform matrix and hands it back in a
  * "matrix" wrapper.
  */
 nsIDOMCSSValue*
 nsComputedDOMStyle::DoGetMozTransform()
 {
   /* First, get the display data.  We'll need it. */
@@ -4360,16 +4420,17 @@ nsComputedDOMStyle::GetQueryableProperty
     COMPUTED_STYLE_MAP_ENTRY(animation_direction,           AnimationDirection),
     COMPUTED_STYLE_MAP_ENTRY(animation_duration,            AnimationDuration),
     COMPUTED_STYLE_MAP_ENTRY(animation_fill_mode,           AnimationFillMode),
     COMPUTED_STYLE_MAP_ENTRY(animation_iteration_count,     AnimationIterationCount),
     COMPUTED_STYLE_MAP_ENTRY(animation_name,                AnimationName),
     COMPUTED_STYLE_MAP_ENTRY(animation_play_state,          AnimationPlayState),
     COMPUTED_STYLE_MAP_ENTRY(animation_timing_function,     AnimationTimingFunction),
     COMPUTED_STYLE_MAP_ENTRY(appearance,                    Appearance),
+    COMPUTED_STYLE_MAP_ENTRY(backface_visibility,           MozBackfaceVisibility),
     COMPUTED_STYLE_MAP_ENTRY(_moz_background_inline_policy, BackgroundInlinePolicy),
     COMPUTED_STYLE_MAP_ENTRY(binding,                       Binding),
     COMPUTED_STYLE_MAP_ENTRY(border_bottom_colors,          BorderBottomColors),
     COMPUTED_STYLE_MAP_ENTRY(border_image,                  BorderImage),
     COMPUTED_STYLE_MAP_ENTRY(border_left_colors,            BorderLeftColors),
     COMPUTED_STYLE_MAP_ENTRY(border_right_colors,           BorderRightColors),
     COMPUTED_STYLE_MAP_ENTRY(border_top_colors,             BorderTopColors),
     COMPUTED_STYLE_MAP_ENTRY(box_align,                     BoxAlign),
@@ -4392,16 +4453,18 @@ nsComputedDOMStyle::GetQueryableProperty
     COMPUTED_STYLE_MAP_ENTRY(force_broken_image_icon,       ForceBrokenImageIcon),
     COMPUTED_STYLE_MAP_ENTRY(hyphens,                       Hyphens),
     COMPUTED_STYLE_MAP_ENTRY(image_region,                  ImageRegion),
     COMPUTED_STYLE_MAP_ENTRY(orient,                        Orient),
     COMPUTED_STYLE_MAP_ENTRY_LAYOUT(_moz_outline_radius_bottomLeft, OutlineRadiusBottomLeft),
     COMPUTED_STYLE_MAP_ENTRY_LAYOUT(_moz_outline_radius_bottomRight,OutlineRadiusBottomRight),
     COMPUTED_STYLE_MAP_ENTRY_LAYOUT(_moz_outline_radius_topLeft,    OutlineRadiusTopLeft),
     COMPUTED_STYLE_MAP_ENTRY_LAYOUT(_moz_outline_radius_topRight,   OutlineRadiusTopRight),
+    COMPUTED_STYLE_MAP_ENTRY(perspective,                   MozPerspective),
+    COMPUTED_STYLE_MAP_ENTRY_LAYOUT(perspective_origin,     MozPerspectiveOrigin),
     COMPUTED_STYLE_MAP_ENTRY(stack_sizing,                  StackSizing),
     COMPUTED_STYLE_MAP_ENTRY(_moz_tab_size,                 MozTabSize),
     COMPUTED_STYLE_MAP_ENTRY(text_blink,                    MozTextBlink),
     COMPUTED_STYLE_MAP_ENTRY(text_decoration_color,         MozTextDecorationColor),
     COMPUTED_STYLE_MAP_ENTRY(text_decoration_line,          MozTextDecorationLine),
     COMPUTED_STYLE_MAP_ENTRY(text_decoration_style,         MozTextDecorationStyle),
     COMPUTED_STYLE_MAP_ENTRY_LAYOUT(_moz_transform,         MozTransform),
     COMPUTED_STYLE_MAP_ENTRY_LAYOUT(_moz_transform_origin,  MozTransformOrigin),
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -342,16 +342,19 @@ private:
   nsIDOMCSSValue* DoGetOverflow();
   nsIDOMCSSValue* DoGetOverflowX();
   nsIDOMCSSValue* DoGetOverflowY();
   nsIDOMCSSValue* DoGetResize();
   nsIDOMCSSValue* DoGetPageBreakAfter();
   nsIDOMCSSValue* DoGetPageBreakBefore();
   nsIDOMCSSValue* DoGetMozTransform();
   nsIDOMCSSValue* DoGetMozTransformOrigin();
+  nsIDOMCSSValue* DoGetMozPerspective();
+  nsIDOMCSSValue* DoGetMozBackfaceVisibility();
+  nsIDOMCSSValue* DoGetMozPerspectiveOrigin();
   nsIDOMCSSValue* DoGetOrient();
 
   /* User interface properties */
   nsIDOMCSSValue* DoGetCursor();
   nsIDOMCSSValue* DoGetForceBrokenImageIcon();
   nsIDOMCSSValue* DoGetIMEMode();
   nsIDOMCSSValue* DoGetUserFocus();
   nsIDOMCSSValue* DoGetUserInput();
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -4465,29 +4465,81 @@ nsRuleNode::ComputeDisplayData(void* aSt
   default:
     NS_ABORT_IF_FALSE(false, "unrecognized transform unit");
   }
 
   /* Convert -moz-transform-origin. */
   const nsCSSValue* transformOriginValue =
     aRuleData->ValueForTransformOrigin();
   if (transformOriginValue->GetUnit() != eCSSUnit_Null) {
-#ifdef DEBUG
-    PRBool result =
-#endif
-      SetPairCoords(*transformOriginValue,
-                    display->mTransformOrigin[0],
-                    display->mTransformOrigin[1],
-                    parentDisplay->mTransformOrigin[0],
-                    parentDisplay->mTransformOrigin[1],
+    const nsCSSValue& valX =
+      transformOriginValue->GetUnit() == eCSSUnit_Triplet ?
+        transformOriginValue->GetTripletValue().mXValue : *transformOriginValue;
+    const nsCSSValue& valY =
+      transformOriginValue->GetUnit() == eCSSUnit_Triplet ?
+        transformOriginValue->GetTripletValue().mYValue : *transformOriginValue;
+    const nsCSSValue& valZ =
+      transformOriginValue->GetUnit() == eCSSUnit_Triplet ?
+        transformOriginValue->GetTripletValue().mZValue : *transformOriginValue;
+
+    mozilla::DebugOnly<PRBool> cX =
+       SetCoord(valX, display->mTransformOrigin[0],
+                parentDisplay->mTransformOrigin[0],
+                SETCOORD_LPH | SETCOORD_INITIAL_HALF |
+                  SETCOORD_BOX_POSITION | SETCOORD_STORE_CALC,
+                aContext, mPresContext, canStoreInRuleTree);
+
+     mozilla::DebugOnly<PRBool> cY =
+       SetCoord(valY, display->mTransformOrigin[1],
+                parentDisplay->mTransformOrigin[1],
+                SETCOORD_LPH | SETCOORD_INITIAL_HALF |
+                  SETCOORD_BOX_POSITION | SETCOORD_STORE_CALC,
+                aContext, mPresContext, canStoreInRuleTree);
+
+     if (valZ.GetUnit() == eCSSUnit_Null) {
+       // Null for the z component means a 0 translation, not
+       // unspecified, as we have already checked the triplet
+       // value for Null.
+       display->mTransformOrigin[2].SetCoordValue(0);
+     } else {
+       mozilla::DebugOnly<PRBool> cZ =
+         SetCoord(valZ, display->mTransformOrigin[2],
+                  parentDisplay->mTransformOrigin[2],
+                  SETCOORD_LH | SETCOORD_INITIAL_ZERO | SETCOORD_STORE_CALC,
+                  aContext, mPresContext, canStoreInRuleTree);
+       NS_ABORT_IF_FALSE(cY == cZ, "changed one but not the other");
+     }
+     NS_ABORT_IF_FALSE(cX == cY, "changed one but not the other");
+     NS_ASSERTION(cX, "Malformed -moz-transform-origin parse!");
+  }
+
+  const nsCSSValue* perspectiveOriginValue =
+    aRuleData->ValueForPerspectiveOrigin();
+  if (perspectiveOriginValue->GetUnit() != eCSSUnit_Null) {
+    mozilla::DebugOnly<PRBool> result =
+      SetPairCoords(*perspectiveOriginValue,
+                    display->mPerspectiveOrigin[0],
+                    display->mPerspectiveOrigin[1],
+                    parentDisplay->mPerspectiveOrigin[0],
+                    parentDisplay->mPerspectiveOrigin[1],
                     SETCOORD_LPH | SETCOORD_INITIAL_HALF |
                     SETCOORD_BOX_POSITION | SETCOORD_STORE_CALC,
                     aContext, mPresContext, canStoreInRuleTree);
-    NS_ASSERTION(result, "Malformed -moz-transform-origin parse!");
-  }
+    NS_ASSERTION(result, "Malformed -moz-perspective-origin parse!");
+  }
+
+  SetCoord(*aRuleData->ValueForPerspective(), 
+           display->mChildPerspective, parentDisplay->mChildPerspective,
+           SETCOORD_LAH | SETCOORD_INITIAL_ZERO | SETCOORD_NONE,
+           aContext, mPresContext, canStoreInRuleTree);
+
+  SetDiscrete(*aRuleData->ValueForBackfaceVisibility(),
+              display->mBackfaceVisibility, canStoreInRuleTree,
+              SETDSC_ENUMERATED, parentDisplay->mBackfaceVisibility,
+              NS_STYLE_BACKFACE_VISIBILITY_VISIBLE, 0, 0, 0, 0);
 
   // orient: enum, inherit, initial
   SetDiscrete(*aRuleData->ValueForOrient(),
               display->mOrient, canStoreInRuleTree,
               SETDSC_ENUMERATED, parentDisplay->mOrient,
               NS_STYLE_ORIENT_HORIZONTAL, 0, 0, 0, 0);
 
   COMPUTE_END_RESET(Display, display)
--- a/layout/style/nsStyleAnimation.cpp
+++ b/layout/style/nsStyleAnimation.cpp
@@ -412,16 +412,71 @@ nsStyleAnimation::ComputeDistance(nsCSSP
             return PR_FALSE;
         }
         squareDistance += diffsquared;
       }
 
       aDistance = sqrt(squareDistance);
       return PR_TRUE;
     }
+    case eUnit_CSSValueTriplet: {
+      const nsCSSValueTriplet *triplet1 = aStartValue.GetCSSValueTripletValue();
+      const nsCSSValueTriplet *triplet2 = aEndValue.GetCSSValueTripletValue();
+      nsCSSUnit unit[3];
+      unit[0] = GetCommonUnit(aProperty, triplet1->mXValue.GetUnit(),
+                              triplet2->mXValue.GetUnit());
+      unit[1] = GetCommonUnit(aProperty, triplet1->mYValue.GetUnit(),
+                              triplet2->mYValue.GetUnit());
+      unit[2] = GetCommonUnit(aProperty, triplet1->mZValue.GetUnit(),
+                              triplet2->mZValue.GetUnit());
+      if (unit[0] == eCSSUnit_Null || unit[1] == eCSSUnit_Null ||
+          unit[0] == eCSSUnit_URL) {
+        return PR_FALSE;
+      }
+
+      double squareDistance = 0.0;
+      static nsCSSValue nsCSSValueTriplet::* const pairValues[3] = {
+        &nsCSSValueTriplet::mXValue, &nsCSSValueTriplet::mYValue, &nsCSSValueTriplet::mZValue
+      };
+      for (PRUint32 i = 0; i < 3; ++i) {
+        nsCSSValue nsCSSValueTriplet::*member = pairValues[i];
+        double diffsquared;
+        switch (unit[i]) {
+          case eCSSUnit_Pixel: {
+            float diff = (triplet1->*member).GetFloatValue() -
+                         (triplet2->*member).GetFloatValue();
+            diffsquared = diff * diff;
+            break;
+          }
+          case eCSSUnit_Percent: {
+            float diff = (triplet1->*member).GetPercentValue() -
+                         (triplet2->*member).GetPercentValue();
+             diffsquared = diff * diff;
+             break;
+          }
+          case eCSSUnit_Calc: {
+            CalcValue v1 = ExtractCalcValue(triplet1->*member);
+            CalcValue v2 = ExtractCalcValue(triplet2->*member);
+            float difflen = v2.mLength - v1.mLength;
+            float diffpct = v2.mPercent - v1.mPercent;
+            diffsquared = difflen * difflen + diffpct * diffpct;
+            break;
+          }
+          case eCSSUnit_Null:
+            break;
+          default:
+            NS_ABORT_IF_FALSE(PR_FALSE, "unexpected unit");
+            return PR_FALSE;
+        }
+        squareDistance += diffsquared;
+      }
+
+      aDistance = sqrt(squareDistance);
+      return PR_TRUE;
+    }
     case eUnit_CSSRect: {
       const nsCSSRect *rect1 = aStartValue.GetCSSRectValue();
       const nsCSSRect *rect2 = aEndValue.GetCSSRectValue();
       if (rect1->mTop.GetUnit() != rect2->mTop.GetUnit() ||
           rect1->mRight.GetUnit() != rect2->mRight.GetUnit() ||
           rect1->mBottom.GetUnit() != rect2->mBottom.GetUnit() ||
           rect1->mLeft.GetUnit() != rect2->mLeft.GetUnit()) {
         // At least until we have calc()
@@ -1486,16 +1541,76 @@ nsStyleAnimation::AddWeighted(nsCSSPrope
             return PR_FALSE;
         }
       }
 
       aResultValue.SetAndAdoptCSSValuePairValue(result.forget(),
                                                 eUnit_CSSValuePair);
       return PR_TRUE;
     }
+    case eUnit_CSSValueTriplet: {
+      nsCSSValueTriplet triplet1(*aValue1.GetCSSValueTripletValue());
+      nsCSSValueTriplet triplet2(*aValue2.GetCSSValueTripletValue());
+
+      if (triplet1.mZValue.GetUnit() == eCSSUnit_Null) {
+        triplet1.mZValue.SetFloatValue(0.0, eCSSUnit_Pixel);
+      }
+      if (triplet2.mZValue.GetUnit() == eCSSUnit_Null) {
+          triplet2.mZValue.SetFloatValue(0.0, eCSSUnit_Pixel);
+      }
+
+      nsCSSUnit unit[3];
+      unit[0] = GetCommonUnit(aProperty, triplet1.mXValue.GetUnit(),
+                              triplet2.mXValue.GetUnit());
+      unit[1] = GetCommonUnit(aProperty, triplet1.mYValue.GetUnit(),
+                               triplet2.mYValue.GetUnit());
+      unit[2] = GetCommonUnit(aProperty, triplet1.mZValue.GetUnit(),
+                              triplet2.mZValue.GetUnit());
+      if (unit[0] == eCSSUnit_Null || unit[1] == eCSSUnit_Null ||
+          unit[0] == eCSSUnit_Null || unit[0] == eCSSUnit_URL) {
+        return PR_FALSE;
+      }
+
+      nsAutoPtr<nsCSSValueTriplet> result(new nsCSSValueTriplet);
+      static nsCSSValue nsCSSValueTriplet::* const tripletValues[3] = {
+        &nsCSSValueTriplet::mXValue, &nsCSSValueTriplet::mYValue, &nsCSSValueTriplet::mZValue
+      };
+      PRUint32 restrictions = nsCSSProps::ValueRestrictions(aProperty);
+      for (PRUint32 i = 0; i < 3; ++i) {
+        nsCSSValue nsCSSValueTriplet::*member = tripletValues[i];
+        switch (unit[i]) {
+          case eCSSUnit_Pixel:
+            AddCSSValuePixel(aCoeff1, &triplet1->*member, aCoeff2, &triplet2->*member,
+                             result->*member, restrictions);
+            break;
+          case eCSSUnit_Percent:
+            AddCSSValuePercent(aCoeff1, &triplet1->*member,
+                               aCoeff2, &triplet2->*member,
+                               result->*member, restrictions);
+            break;
+          case eCSSUnit_Calc:
+            AddCSSValueCanonicalCalc(aCoeff1, &triplet1->*member,
+                                     aCoeff2, &triplet2->*member,
+                                     result->*member);
+            break;
+          default:
+            NS_ABORT_IF_FALSE(PR_FALSE, "unexpected unit");
+            return PR_FALSE;
+        }
+      }
+
+      if (result->mZValue.GetUnit() == eCSSUnit_Pixel &&
+          result->mZValue.GetFloatValue() == 0.0f) {
+        result->mZValue.Reset();
+      }
+
+      aResultValue.SetAndAdoptCSSValueTripletValue(result.forget(),
+                                                   eUnit_CSSValueTriplet);
+      return PR_TRUE;
+    }
     case eUnit_CSSRect: {
       NS_ABORT_IF_FALSE(nsCSSProps::ValueRestrictions(aProperty) == 0,
                         "must add code for handling value restrictions");
       const nsCSSRect *rect1 = aValue1.GetCSSRectValue();
       const nsCSSRect *rect2 = aValue2.GetCSSRectValue();
       if (rect1->mTop.GetUnit() != rect2->mTop.GetUnit() ||
           rect1->mRight.GetUnit() != rect2->mRight.GetUnit() ||
           rect1->mBottom.GetUnit() != rect2->mBottom.GetUnit() ||
@@ -1953,16 +2068,28 @@ nsStyleAnimation::UncomputeValue(nsCSSPr
       // use pairs do expect collapsing.
       const nsCSSValuePair* pair = aComputedValue.GetCSSValuePairValue();
       if (pair->mXValue == pair->mYValue) {
         aSpecifiedValue = pair->mXValue;
       } else {
         aSpecifiedValue.SetPairValue(pair);
       }
     } break;
+    case eUnit_CSSValueTriplet: {
+      // Rule node processing expects triplet values to be collapsed to a
+      // single value if both halves would be equal, for most but not
+      // all properties.  At present, all animatable properties that
+      // use pairs do expect collapsing.
+      const nsCSSValueTriplet* triplet = aComputedValue.GetCSSValueTripletValue();
+      if (triplet->mXValue == triplet->mYValue && triplet->mYValue == triplet->mZValue) {
+        aSpecifiedValue = triplet->mXValue;
+      } else {
+        aSpecifiedValue.SetTripletValue(triplet);
+      }
+    } break;
     case eUnit_CSSRect: {
       nsCSSRect& rect = aSpecifiedValue.SetRectValue();
       rect = *aComputedValue.GetCSSRectValue();
     } break;
     case eUnit_Dasharray:
     case eUnit_Shadow:
     case eUnit_Transform:
       aSpecifiedValue.
@@ -2263,21 +2390,43 @@ nsStyleAnimation::ExtractComputedValue(n
           aComputedValue.SetAndAdoptCSSValuePairValue(pair.forget(),
                                                       eUnit_CSSValuePair);
           break;
         }
 
         case eCSSProperty__moz_transform_origin: {
           const nsStyleDisplay *styleDisplay =
             static_cast<const nsStyleDisplay*>(styleStruct);
+          nsAutoPtr<nsCSSValueTriplet> triplet(new nsCSSValueTriplet);
+          if (!triplet ||
+              !StyleCoordToCSSValue(styleDisplay->mTransformOrigin[0],
+                                    triplet->mXValue) ||
+              !StyleCoordToCSSValue(styleDisplay->mTransformOrigin[1],
+                                    triplet->mYValue) ||
+              !StyleCoordToCSSValue(styleDisplay->mTransformOrigin[2],
+                                    triplet->mZValue)) {
+            return PR_FALSE;
+          }
+          if (triplet->mZValue.GetUnit() == eCSSUnit_Pixel &&
+              triplet->mZValue.GetFloatValue() == 0.0f) {
+            triplet->mZValue.Reset();
+          }
+          aComputedValue.SetAndAdoptCSSValueTripletValue(triplet.forget(),
+                                                         eUnit_CSSValueTriplet);
+          break;
+        }
+
+        case eCSSProperty_perspective_origin: {
+          const nsStyleDisplay *styleDisplay =
+            static_cast<const nsStyleDisplay*>(styleStruct);
           nsAutoPtr<nsCSSValuePair> pair(new nsCSSValuePair);
           if (!pair ||
-              !StyleCoordToCSSValue(styleDisplay->mTransformOrigin[0],
+              !StyleCoordToCSSValue(styleDisplay->mPerspectiveOrigin[0],
                                     pair->mXValue) ||
-              !StyleCoordToCSSValue(styleDisplay->mTransformOrigin[1],
+              !StyleCoordToCSSValue(styleDisplay->mPerspectiveOrigin[1],
                                     pair->mYValue)) {
             return PR_FALSE;
           }
           aComputedValue.SetAndAdoptCSSValuePairValue(pair.forget(),
                                                       eUnit_CSSValuePair);
           break;
         }
 
@@ -2771,16 +2920,24 @@ nsStyleAnimation::Value::operator=(const
     case eUnit_CSSValuePair:
       NS_ABORT_IF_FALSE(aOther.mValue.mCSSValuePair,
                         "value pairs may not be null");
       mValue.mCSSValuePair = new nsCSSValuePair(*aOther.mValue.mCSSValuePair);
       if (!mValue.mCSSValuePair) {
         mUnit = eUnit_Null;
       }
       break;
+    case eUnit_CSSValueTriplet:
+      NS_ABORT_IF_FALSE(aOther.mValue.mCSSValueTriplet,
+                        "value triplets may not be null");
+      mValue.mCSSValueTriplet = new nsCSSValueTriplet(*aOther.mValue.mCSSValueTriplet);
+      if (!mValue.mCSSValueTriplet) {
+        mUnit = eUnit_Null;
+      }
+      break;
     case eUnit_CSSRect:
       NS_ABORT_IF_FALSE(aOther.mValue.mCSSRect, "rects may not be null");
       mValue.mCSSRect = new nsCSSRect(*aOther.mValue.mCSSRect);
       if (!mValue.mCSSRect) {
         mUnit = eUnit_Null;
       }
       break;
     case eUnit_Dasharray:
@@ -2908,16 +3065,27 @@ nsStyleAnimation::Value::SetAndAdoptCSSV
   FreeValue();
   NS_ABORT_IF_FALSE(IsCSSValuePairUnit(aUnit), "bad unit");
   NS_ABORT_IF_FALSE(aValuePair != nsnull, "value pairs may not be null");
   mUnit = aUnit;
   mValue.mCSSValuePair = aValuePair; // take ownership
 }
 
 void
+nsStyleAnimation::Value::SetAndAdoptCSSValueTripletValue(
+                           nsCSSValueTriplet *aValueTriplet, Unit aUnit)
+{
+    FreeValue();
+    NS_ABORT_IF_FALSE(IsCSSValueTripletUnit(aUnit), "bad unit");
+    NS_ABORT_IF_FALSE(aValueTriplet != nsnull, "value pairs may not be null");
+    mUnit = aUnit;
+    mValue.mCSSValueTriplet = aValueTriplet; // take ownership
+}
+
+void
 nsStyleAnimation::Value::SetAndAdoptCSSRectValue(nsCSSRect *aRect, Unit aUnit)
 {
   FreeValue();
   NS_ABORT_IF_FALSE(IsCSSRectUnit(aUnit), "bad unit");
   NS_ABORT_IF_FALSE(aRect != nsnull, "value pairs may not be null");
   mUnit = aUnit;
   mValue.mCSSRect = aRect; // take ownership
 }
@@ -2948,16 +3116,18 @@ void
 nsStyleAnimation::Value::FreeValue()
 {
   if (IsCSSValueUnit(mUnit)) {
     delete mValue.mCSSValue;
   } else if (IsCSSValueListUnit(mUnit)) {
     delete mValue.mCSSValueList;
   } else if (IsCSSValuePairUnit(mUnit)) {
     delete mValue.mCSSValuePair;
+  } else if (IsCSSValueTripletUnit(mUnit)) {
+    delete mValue.mCSSValueTriplet;
   } else if (IsCSSRectUnit(mUnit)) {
     delete mValue.mCSSRect;
   } else if (IsCSSValuePairListUnit(mUnit)) {
     delete mValue.mCSSValuePairList;
   } else if (IsStringUnit(mUnit)) {
     NS_ABORT_IF_FALSE(mValue.mString, "expecting non-null string");
     mValue.mString->Release();
   }
@@ -2986,16 +3156,18 @@ nsStyleAnimation::Value::operator==(cons
     case eUnit_Float:
       return mValue.mFloat == aOther.mValue.mFloat;
     case eUnit_Color:
       return mValue.mColor == aOther.mValue.mColor;
     case eUnit_Calc:
       return *mValue.mCSSValue == *aOther.mValue.mCSSValue;
     case eUnit_CSSValuePair:
       return *mValue.mCSSValuePair == *aOther.mValue.mCSSValuePair;
+    case eUnit_CSSValueTriplet:
+      return *mValue.mCSSValueTriplet == *aOther.mValue.mCSSValueTriplet;
     case eUnit_CSSRect:
       return *mValue.mCSSRect == *aOther.mValue.mCSSRect;
     case eUnit_Dasharray:
     case eUnit_Shadow:
     case eUnit_Transform:
       return *mValue.mCSSValueList == *aOther.mValue.mCSSValueList;
     case eUnit_CSSValuePairList:
       return *mValue.mCSSValuePairList == *aOther.mValue.mCSSValuePairList;
--- a/layout/style/nsStyleAnimation.h
+++ b/layout/style/nsStyleAnimation.h
@@ -50,16 +50,17 @@
 #include "nsCoord.h"
 #include "nsColor.h"
 
 class nsPresContext;
 class nsStyleContext;
 class nsCSSValue;
 struct nsCSSValueList;
 struct nsCSSValuePair;
+struct nsCSSValueTriplet;
 struct nsCSSValuePairList;
 struct nsCSSRect;
 struct gfxMatrix;
 
 namespace mozilla {
 namespace dom {
 class Element;
 } // namespace dom
@@ -244,16 +245,17 @@ public:
     eUnit_Integer,
     eUnit_Coord,
     eUnit_Percent,
     eUnit_Float,
     eUnit_Color,
     eUnit_Calc, // nsCSSValue* (never null), always with a single
                 // calc() expression that's either length or length+percent
     eUnit_CSSValuePair, // nsCSSValuePair* (never null)
+    eUnit_CSSValueTriplet, // nsCSSValueTriplet* (never null)
     eUnit_CSSRect, // nsCSSRect* (never null)
     eUnit_Dasharray, // nsCSSValueList* (never null)
     eUnit_Shadow, // nsCSSValueList* (may be null)
     eUnit_Transform, // nsCSSValueList* (never null)
     eUnit_CSSValuePairList, // nsCSSValuePairList* (never null)
     eUnit_UnparsedString // nsStringBuffer* (never null)
   };
 
@@ -262,16 +264,17 @@ public:
     Unit mUnit;
     union {
       PRInt32 mInt;
       nscoord mCoord;
       float mFloat;
       nscolor mColor;
       nsCSSValue* mCSSValue;
       nsCSSValuePair* mCSSValuePair;
+      nsCSSValueTriplet* mCSSValueTriplet;
       nsCSSRect* mCSSRect;
       nsCSSValueList* mCSSValueList;
       nsCSSValuePairList* mCSSValuePairList;
       nsStringBuffer* mString;
     } mValue;
   public:
     Unit GetUnit() const {
       NS_ASSERTION(mUnit != eUnit_Null, "uninitialized");
@@ -307,16 +310,20 @@ public:
     nsCSSValue* GetCSSValueValue() const {
       NS_ASSERTION(IsCSSValueUnit(mUnit), "unit mismatch");
       return mValue.mCSSValue;
     }
     nsCSSValuePair* GetCSSValuePairValue() const {
       NS_ASSERTION(IsCSSValuePairUnit(mUnit), "unit mismatch");
       return mValue.mCSSValuePair;
     }
+    nsCSSValueTriplet* GetCSSValueTripletValue() const {
+      NS_ASSERTION(IsCSSValueTripletUnit(mUnit), "unit mismatch");
+      return mValue.mCSSValueTriplet;
+    }
     nsCSSRect* GetCSSRectValue() const {
       NS_ASSERTION(IsCSSRectUnit(mUnit), "unit mismatch");
       return mValue.mCSSRect;
     }
     nsCSSValueList* GetCSSValueListValue() const {
       NS_ASSERTION(IsCSSValueListUnit(mUnit), "unit mismatch");
       return mValue.mCSSValueList;
     }
@@ -364,16 +371,17 @@ public:
     void SetFloatValue(float aFloat);
     void SetColorValue(nscolor aColor);
     void SetUnparsedStringValue(const nsString& aString);
 
     // These setters take ownership of |aValue|, and are therefore named
     // "SetAndAdopt*".
     void SetAndAdoptCSSValueValue(nsCSSValue *aValue, Unit aUnit);
     void SetAndAdoptCSSValuePairValue(nsCSSValuePair *aValue, Unit aUnit);
+    void SetAndAdoptCSSValueTripletValue(nsCSSValueTriplet *aValue, Unit aUnit);
     void SetAndAdoptCSSRectValue(nsCSSRect *aValue, Unit aUnit);
     void SetAndAdoptCSSValueListValue(nsCSSValueList *aValue, Unit aUnit);
     void SetAndAdoptCSSValuePairListValue(nsCSSValuePairList *aValue);
 
     Value& operator=(const Value& aOther);
 
     PRBool operator==(const Value& aOther) const;
     PRBool operator!=(const Value& aOther) const
@@ -391,16 +399,19 @@ public:
              aUnit == eUnit_Integer;
     }
     static PRBool IsCSSValueUnit(Unit aUnit) {
       return aUnit == eUnit_Calc;
     }
     static PRBool IsCSSValuePairUnit(Unit aUnit) {
       return aUnit == eUnit_CSSValuePair;
     }
+    static PRBool IsCSSValueTripletUnit(Unit aUnit) {
+      return aUnit == eUnit_CSSValueTriplet;
+    }
     static PRBool IsCSSRectUnit(Unit aUnit) {
       return aUnit == eUnit_CSSRect;
     }
     static PRBool IsCSSValueListUnit(Unit aUnit) {
       return aUnit == eUnit_Dasharray || aUnit == eUnit_Shadow ||
              aUnit == eUnit_Transform;
     }
     static PRBool IsCSSValuePairListUnit(Unit aUnit) {
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -2027,17 +2027,22 @@ nsStyleDisplay::nsStyleDisplay()
   mOverflowX = NS_STYLE_OVERFLOW_VISIBLE;
   mOverflowY = NS_STYLE_OVERFLOW_VISIBLE;
   mResize = NS_STYLE_RESIZE_NONE;
   mClipFlags = NS_STYLE_CLIP_AUTO;
   mClip.SetRect(0,0,0,0);
   mOpacity = 1.0f;
   mSpecifiedTransform = nsnull;
   mTransformOrigin[0].SetPercentValue(0.5f); // Transform is centered on origin
-  mTransformOrigin[1].SetPercentValue(0.5f); 
+  mTransformOrigin[1].SetPercentValue(0.5f);
+  mTransformOrigin[2].SetCoordValue(0);
+  mPerspectiveOrigin[0].SetPercentValue(0.5f);
+  mPerspectiveOrigin[1].SetPercentValue(0.5f);
+  mChildPerspective.SetCoordValue(0);
+  mBackfaceVisibility = NS_STYLE_BACKFACE_VISIBILITY_VISIBLE;
   mOrient = NS_STYLE_ORIENT_HORIZONTAL;
 
   mTransitions.AppendElement();
   NS_ABORT_IF_FALSE(mTransitions.Length() == 1,
                     "appending within auto buffer should never fail");
   mTransitions[0].SetInitialValues();
   mTransitionTimingFunctionCount = 1;
   mTransitionDurationCount = 1;
@@ -2093,16 +2098,21 @@ nsStyleDisplay::nsStyleDisplay(const nsS
   mOrient = aSource.mOrient;
 
   /* Copy over the transformation information. */
   mSpecifiedTransform = aSource.mSpecifiedTransform;
   
   /* Copy over transform origin. */
   mTransformOrigin[0] = aSource.mTransformOrigin[0];
   mTransformOrigin[1] = aSource.mTransformOrigin[1];
+  mTransformOrigin[2] = aSource.mTransformOrigin[2];
+  mPerspectiveOrigin[0] = aSource.mPerspectiveOrigin[0];
+  mPerspectiveOrigin[1] = aSource.mPerspectiveOrigin[1];
+  mChildPerspective = aSource.mChildPerspective;
+  mBackfaceVisibility = aSource.mBackfaceVisibility;
 }
 
 nsChangeHint nsStyleDisplay::CalcDifference(const nsStyleDisplay& aOther) const
 {
   nsChangeHint hint = nsChangeHint(0);
 
   if (!EqualURIs(mBinding, aOther.mBinding)
       || mPosition != aOther.mPosition
@@ -2148,22 +2158,36 @@ nsChangeHint nsStyleDisplay::CalcDiffere
      * the overflow rect (which probably changed if the transform changed)
      * and to redraw within the bounds of that new overflow rect.
      */
     if (!mSpecifiedTransform != !aOther.mSpecifiedTransform ||
         (mSpecifiedTransform && *mSpecifiedTransform != *aOther.mSpecifiedTransform))
       NS_UpdateHint(hint, NS_CombineHint(nsChangeHint_ReflowFrame,
                                          nsChangeHint_UpdateTransformLayer));
     
-    for (PRUint8 index = 0; index < 2; ++index)
+    for (PRUint8 index = 0; index < 3; ++index)
       if (mTransformOrigin[index] != aOther.mTransformOrigin[index]) {
         NS_UpdateHint(hint, NS_CombineHint(nsChangeHint_ReflowFrame,
                                            nsChangeHint_RepaintFrame));
         break;
       }
+    
+    for (PRUint8 index = 0; index < 2; ++index)
+      if (mPerspectiveOrigin[index] != aOther.mPerspectiveOrigin[index]) {
+        NS_UpdateHint(hint, NS_CombineHint(nsChangeHint_ReflowFrame,
+                                           nsChangeHint_RepaintFrame));
+        break;
+      }
+
+    if (mChildPerspective != aOther.mChildPerspective)
+      NS_UpdateHint(hint, NS_CombineHint(nsChangeHint_ReflowFrame,
+                                         nsChangeHint_RepaintFrame));
+
+    if (mBackfaceVisibility != aOther.mBackfaceVisibility)
+      NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
   }
 
   // Note:  Our current behavior for handling changes to the
   // transition-duration, transition-delay, and transition-timing-function
   // properties is to do nothing.  In other words, the transition
   // property that matters is what it is when the transition begins, and
   // we don't stop a transition later because the transition property
   // changed.
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -1509,17 +1509,20 @@ struct nsStyleDisplay {
   PRUint8   mClipFlags;         // [reset] see nsStyleConsts.h
   PRUint8 mOrient;              // [reset] see nsStyleConsts.h
 
   // mSpecifiedTransform is the list of transform functions as
   // specified, or null to indicate there is no transform.  (inherit or
   // initial are replaced by an actual list of transform functions, or
   // null, as appropriate.) (owned by the style rule)
   const nsCSSValueList *mSpecifiedTransform; // [reset]
-  nsStyleCoord mTransformOrigin[2]; // [reset] percent, coord, calc
+  nsStyleCoord mTransformOrigin[3]; // [reset] percent, coord, calc, 3rd param is coord, calc only
+  nsStyleCoord mChildPerspective; // [reset] coord
+  nsStyleCoord mPerspectiveOrigin[2]; // [reset] percent, coord, calc
+  PRUint8 mBackfaceVisibility;
 
   nsAutoTArray<nsTransition, 1> mTransitions; // [reset]
   // The number of elements in mTransitions that are not from repeating
   // a list due to another property being longer.
   PRUint32 mTransitionTimingFunctionCount,
            mTransitionDurationCount,
            mTransitionDelayCount,
            mTransitionPropertyCount;
--- a/layout/style/nsStyleTransformMatrix.cpp
+++ b/layout/style/nsStyleTransformMatrix.cpp
@@ -167,16 +167,42 @@ nsStyleTransformMatrix::ProcessMatrix(co
                        aBounds.Width(), aAppUnitsPerMatrixUnit);
   ProcessTranslatePart(result._42, aData->Item(6),
                        aContext, aPresContext, aCanStoreInRuleTree,
                        aBounds.Height(), aAppUnitsPerMatrixUnit);
 
   return result;
 }
 
+/*static */ gfx3DMatrix
+nsStyleTransformMatrix::ProcessMatrix3D(const nsCSSValue::Array* aData)
+{
+  NS_PRECONDITION(aData->Count() == 17, "Invalid array!");
+
+  gfx3DMatrix temp;
+
+  temp._11 = aData->Item(1).GetFloatValue();
+  temp._12 = aData->Item(2).GetFloatValue();
+  temp._13 = aData->Item(3).GetFloatValue();
+  temp._14 = aData->Item(4).GetFloatValue();
+  temp._21 = aData->Item(5).GetFloatValue();
+  temp._22 = aData->Item(6).GetFloatValue();
+  temp._23 = aData->Item(7).GetFloatValue();
+  temp._24 = aData->Item(8).GetFloatValue();
+  temp._31 = aData->Item(9).GetFloatValue();
+  temp._32 = aData->Item(10).GetFloatValue();
+  temp._33 = aData->Item(11).GetFloatValue();
+  temp._34 = aData->Item(12).GetFloatValue();
+  temp._41 = aData->Item(13).GetFloatValue();
+  temp._42 = aData->Item(14).GetFloatValue();
+  temp._43 = aData->Item(15).GetFloatValue();
+  temp._44 = aData->Item(16).GetFloatValue();
+  return temp;
+}
+
 /* Helper function to process two matrices that we need to interpolate between */
 /* static */ gfx3DMatrix
 nsStyleTransformMatrix::ProcessInterpolateMatrix(const nsCSSValue::Array* aData,
                                                  nsStyleContext* aContext,
                                                  nsPresContext* aPresContext,
                                                  PRBool& aCanStoreInRuleTree,
                                                  nsRect& aBounds, float aAppUnitsPerMatrixUnit)
 {
@@ -265,49 +291,84 @@ nsStyleTransformMatrix::ProcessTranslate
    * to the percent.
    */
   ProcessTranslatePart(temp._42, aData->Item(1),
                        aContext, aPresContext, aCanStoreInRuleTree,
                        aBounds.Height(), aAppUnitsPerMatrixUnit);
   return temp;
 }
 
+/* static */ gfx3DMatrix
+nsStyleTransformMatrix::ProcessTranslateZ(const nsCSSValue::Array* aData,
+                                          nsStyleContext* aContext,
+                                          nsPresContext* aPresContext,
+                                          PRBool& aCanStoreInRuleTree,
+                                          float aAppUnitsPerMatrixUnit)
+{
+  NS_PRECONDITION(aData->Count() == 2, "Invalid array!");
+
+  gfx3DMatrix temp;
+
+  ProcessTranslatePart(temp._43, aData->Item(1),
+                       aContext, aPresContext, aCanStoreInRuleTree,
+                       0, aAppUnitsPerMatrixUnit);
+  return temp;
+}
+
 /* Helper function to process a translate function. */
 /* static */ gfx3DMatrix
 nsStyleTransformMatrix::ProcessTranslate(const nsCSSValue::Array* aData,
                                          nsStyleContext* aContext,
                                          nsPresContext* aPresContext,
                                          PRBool& aCanStoreInRuleTree,
                                          nsRect& aBounds, float aAppUnitsPerMatrixUnit)
 {
   NS_PRECONDITION(aData->Count() == 2 || aData->Count() == 3, "Invalid array!");
 
   gfx3DMatrix temp;
 
-  /* There are several cases to consider.
-   * First, we might have one value, or we might have two.  If we have
-   * two, we need to consider both dX and dY components.
-   * Next, the values might be lengths, or they might be percents.  If they're
-   * percents, store them in the dX and dY components.  Otherwise, store them in
-   * the main matrix.
-   */
-
   ProcessTranslatePart(temp._41, aData->Item(1),
                        aContext, aPresContext, aCanStoreInRuleTree,
                        aBounds.Width(), aAppUnitsPerMatrixUnit);
 
   /* If we read in a Y component, set it appropriately */
   if (aData->Count() == 3) {
     ProcessTranslatePart(temp._42, aData->Item(2),
                          aContext, aPresContext, aCanStoreInRuleTree,
                          aBounds.Height(), aAppUnitsPerMatrixUnit);
   }
   return temp;
 }
 
+/* static */ gfx3DMatrix
+nsStyleTransformMatrix::ProcessTranslate3D(const nsCSSValue::Array* aData,
+                                           nsStyleContext* aContext,
+                                           nsPresContext* aPresContext,
+                                           PRBool& aCanStoreInRuleTree,
+                                           nsRect& aBounds, float aAppUnitsPerMatrixUnit)
+{
+  NS_PRECONDITION(aData->Count() == 4, "Invalid array!");
+
+  gfx3DMatrix temp;
+
+  ProcessTranslatePart(temp._41, aData->Item(1),
+                       aContext, aPresContext, aCanStoreInRuleTree,
+                       aBounds.Width(), aAppUnitsPerMatrixUnit);
+
+  ProcessTranslatePart(temp._42, aData->Item(2),
+                       aContext, aPresContext, aCanStoreInRuleTree,
+                       aBounds.Height(), aAppUnitsPerMatrixUnit);
+
+  ProcessTranslatePart(temp._43, aData->Item(3),
+                       aContext, aPresContext, aCanStoreInRuleTree,
+                       0, aAppUnitsPerMatrixUnit);
+
+  return temp;
+}
+
 /* Helper function to set up a scale matrix. */
 /* static */ gfx3DMatrix
 nsStyleTransformMatrix::ProcessScaleHelper(float aXScale, float aYScale, float aZScale)
 {
   /* We want our matrix to look like this:
    * | dx  0   0 0 |
    * |  0 dy   0 0 |
    * |  0  0  dz 0 |
@@ -332,16 +393,32 @@ nsStyleTransformMatrix::ProcessScaleX(co
 /* Process a scaley function. */
 /* static */ gfx3DMatrix
 nsStyleTransformMatrix::ProcessScaleY(const nsCSSValue::Array* aData)
 {
   NS_PRECONDITION(aData->Count() == 2, "Bad array!");
   return ProcessScaleHelper(1.0f, aData->Item(1).GetFloatValue(), 1.0f);
 }
 
+/* static */ gfx3DMatrix
+nsStyleTransformMatrix::ProcessScaleZ(const nsCSSValue::Array* aData)
+{
+  NS_PRECONDITION(aData->Count() == 2, "Bad array!");
+  return ProcessScaleHelper(1.0f, 1.0f, aData->Item(1).GetFloatValue());
+}
+
+/* static */ gfx3DMatrix
+nsStyleTransformMatrix::ProcessScale3D(const nsCSSValue::Array* aData)
+{
+  NS_PRECONDITION(aData->Count() == 4, "Bad array!");
+  return ProcessScaleHelper(aData->Item(1).GetFloatValue(),
+                            aData->Item(2).GetFloatValue(),
+                            aData->Item(3).GetFloatValue());
+}
+
 /* Process a scale function. */
 /* static */ gfx3DMatrix
 nsStyleTransformMatrix::ProcessScale(const nsCSSValue::Array* aData)
 {
   NS_PRECONDITION(aData->Count() == 2 || aData->Count() == 3, "Bad array!");
   /* We either have one element or two.  If we have one, it's for both X and Y.
    * Otherwise it's one for each.
    */
@@ -424,16 +501,145 @@ nsStyleTransformMatrix::ProcessRotateZ(c
 
   temp._11 = cosTheta;
   temp._12 = sinTheta;
   temp._21 = -sinTheta;
   temp._22 = cosTheta;
   return temp;
 }
 
+/* static */ gfx3DMatrix
+nsStyleTransformMatrix::ProcessRotateX(const nsCSSValue::Array* aData)
+{
+  NS_PRECONDITION(aData->Count() == 2, "Invalid array!");
+
+  /* We want our matrix to look like this:
+   * | 1           0           0 0 |
+   * | 0  cos(theta)  sin(theta) 0 |
+   * | 0 -sin(theta)  cos(theta) 0 |
+   * | 0           0           0 1 |
+   * (see http://www.w3.org/TR/SVG/coords.html#RotationDefined)
+   */
+  double theta = aData->Item(1).GetAngleValueInRadians();
+  float cosTheta = FlushToZero(cos(theta));
+  float sinTheta = FlushToZero(sin(theta));
+
+  gfx3DMatrix temp;
+
+  temp._22 = cosTheta;
+  temp._23 = sinTheta;
+  temp._32 = -sinTheta;
+  temp._33 = cosTheta;
+  return temp;
+}
+
+/* static */ gfx3DMatrix 
+nsStyleTransformMatrix::ProcessRotateY(const nsCSSValue::Array* aData)
+{
+  NS_PRECONDITION(aData->Count() == 2, "Invalid array!");
+
+  /* We want our matrix to look like this:
+   * | cos(theta) 0 -sin(theta) 0 |
+   * |          0 1           0 0 |
+   * | sin(theta) 0  cos(theta) 0 |
+   * | 0          0           0 1 |
+   * (see http://www.w3.org/TR/SVG/coords.html#RotationDefined)
+   */
+  double theta = aData->Item(1).GetAngleValueInRadians();
+  float cosTheta = FlushToZero(cos(theta));
+  float sinTheta = FlushToZero(sin(theta));
+
+  gfx3DMatrix temp;
+
+  temp._11 = cosTheta;
+  temp._13 = -sinTheta;
+  temp._31 = sinTheta;
+  temp._33 = cosTheta;
+  return temp;
+}
+
+/* static */ gfx3DMatrix
+nsStyleTransformMatrix::ProcessRotate3D(const nsCSSValue::Array* aData)
+{
+  NS_PRECONDITION(aData->Count() == 5, "Invalid array!");
+
+  /* We want our matrix to look like this:
+   * |       1 + (1-cos(angle))*(x*x-1)   -z*sin(angle)+(1-cos(angle))*x*y   y*sin(angle)+(1-cos(angle))*x*z   0 |
+   * |  z*sin(angle)+(1-cos(angle))*x*y         1 + (1-cos(angle))*(y*y-1)  -x*sin(angle)+(1-cos(angle))*y*z   0 |
+   * | -y*sin(angle)+(1-cos(angle))*x*z    x*sin(angle)+(1-cos(angle))*y*z        1 + (1-cos(angle))*(z*z-1)   0 |
+   * |                                0                                  0                                 0   1 |
+   * (see http://www.w3.org/TR/css3-3d-transforms/#transform-functions)
+   */
+  double theta = aData->Item(4).GetAngleValueInRadians();
+  float cosTheta = FlushToZero(cos(theta));
+  float sinTheta = FlushToZero(sin(theta));
+
+  float x = aData->Item(1).GetFloatValue();
+  float y = aData->Item(2).GetFloatValue();
+  float z = aData->Item(3).GetFloatValue();
+
+  /* Normalize [x,y,z] */
+  float length = sqrt(x*x + y*y + z*z);
+  if (length == 0.0) {
+    return gfx3DMatrix();
+  }
+  x /= length;
+  y /= length;
+  z /= length;
+
+  gfx3DMatrix temp;
+
+  /* Create our matrix */
+  temp._11 = 1 + (1 - cosTheta) * (x * x - 1);
+  temp._12 = -z * sinTheta + (1 - cosTheta) * x * y;
+  temp._13 = y * sinTheta + (1 - cosTheta) * x * z;
+  temp._14 = 0.0f;
+  temp._21 = z * sinTheta + (1 - cosTheta) * x * y;
+  temp._22 = 1 + (1 - cosTheta) * (y * y - 1);
+  temp._23 = -x * sinTheta + (1 - cosTheta) * y * z;
+  temp._24 = 0.0f;
+  temp._31 = -y * sinTheta + (1 - cosTheta) * x * z;
+  temp._32 = x * sinTheta + (1 - cosTheta) * y * z;
+  temp._33 = 1 + (1 - cosTheta) * (z * z - 1);
+  temp._34 = 0.0f;
+  temp._41 = 0.0f;
+  temp._42 = 0.0f;
+  temp._43 = 0.0f;
+  temp._44 = 1.0f;
+  return temp;
+}
+
+/* static */ gfx3DMatrix
+nsStyleTransformMatrix::ProcessPerspective(const nsCSSValue::Array* aData,
+                                           nsStyleContext *aContext,
+                                           nsPresContext *aPresContext,
+                                           PRBool &aCanStoreInRuleTree,
+                                           float aAppUnitsPerMatrixUnit)
+{
+  NS_PRECONDITION(aData->Count() == 2, "Invalid array!");
+
+  /* We want our matrix to look like this:
+   * | 1 0 0        0 |
+   * | 0 1 0        0 |
+   * | 0 0 1 -1/depth |
+   * | 0 0 0        1 |
+   */
+
+  gfx3DMatrix temp;
+
+  float depth;
+  ProcessTranslatePart(depth, aData->Item(1), aContext,
+                       aPresContext, aCanStoreInRuleTree,
+                       0, aAppUnitsPerMatrixUnit);
+  NS_ASSERTION(depth > 0.0, "Perspective must be positive!");
+  temp._34 = -1.0/depth;
+
+  return temp;
+}
+
 /**
  * Return the transform function, as an nsCSSKeyword, for the given
  * nsCSSValue::Array from a transform list.
  */
 /* static */ nsCSSKeyword
 nsStyleTransformMatrix::TransformFunctionOf(const nsCSSValue::Array* aData)
 {
   nsAutoString keyword;
@@ -462,39 +668,61 @@ nsStyleTransformMatrix::MatrixForTransfo
   /* Get the keyword for the transform. */
   switch (TransformFunctionOf(aData)) {
   case eCSSKeyword_translatex:
     return ProcessTranslateX(aData, aContext, aPresContext,
                              aCanStoreInRuleTree, aBounds, aAppUnitsPerMatrixUnit);
   case eCSSKeyword_translatey:
     return ProcessTranslateY(aData, aContext, aPresContext,
                              aCanStoreInRuleTree, aBounds, aAppUnitsPerMatrixUnit);
+  case eCSSKeyword_translatez:
+    return ProcessTranslateZ(aData, aContext, aPresContext,
+                             aCanStoreInRuleTree, aAppUnitsPerMatrixUnit);
   case eCSSKeyword_translate:
     return ProcessTranslate(aData, aContext, aPresContext,
                             aCanStoreInRuleTree, aBounds, aAppUnitsPerMatrixUnit);
+  case eCSSKeyword_translate3d:
+    return ProcessTranslate3D(aData, aContext, aPresContext,
+                              aCanStoreInRuleTree, aBounds, aAppUnitsPerMatrixUnit);
   case eCSSKeyword_scalex:
     return ProcessScaleX(aData);
   case eCSSKeyword_scaley:
     return ProcessScaleY(aData);
+  case eCSSKeyword_scalez:
+    return ProcessScaleZ(aData);
   case eCSSKeyword_scale:
-    return ProcessScale(aData);
+      return ProcessScale(aData);
+  case eCSSKeyword_scale3d:
+    return ProcessScale3D(aData);
   case eCSSKeyword_skewx:
     return ProcessSkewX(aData);
   case eCSSKeyword_skewy:
     return ProcessSkewY(aData);
   case eCSSKeyword_skew:
     return ProcessSkew(aData);
+  case eCSSKeyword_rotatex:
+    return ProcessRotateX(aData);
+  case eCSSKeyword_rotatey:
+    return ProcessRotateY(aData);
+  case eCSSKeyword_rotatez:
   case eCSSKeyword_rotate:
-    return ProcessRotateZ(aData);
+      return ProcessRotateZ(aData);
+  case eCSSKeyword_rotate3d:
+    return ProcessRotate3D(aData);
   case eCSSKeyword_matrix:
     return ProcessMatrix(aData, aContext, aPresContext,
                          aCanStoreInRuleTree, aBounds, aAppUnitsPerMatrixUnit);
+  case eCSSKeyword_matrix3d:
+    return ProcessMatrix3D(aData);
   case eCSSKeyword_interpolatematrix:
     return ProcessInterpolateMatrix(aData, aContext, aPresContext,
                                     aCanStoreInRuleTree, aBounds, aAppUnitsPerMatrixUnit);
+  case eCSSKeyword_perspective:
+    return ProcessPerspective(aData, aContext, aPresContext, 
+                              aCanStoreInRuleTree, aAppUnitsPerMatrixUnit);
   default:
     NS_NOTREACHED("Unknown transform function!");
   }
   return gfx3DMatrix();
 }
 
 /* static */ gfx3DMatrix
 nsStyleTransformMatrix::ReadTransforms(const nsCSSValueList* aList,
--- a/layout/style/nsStyleTransformMatrix.h
+++ b/layout/style/nsStyleTransformMatrix.h
@@ -100,40 +100,61 @@ class nsStyleTransformMatrix
  private:
   static gfx3DMatrix ProcessMatrix(const nsCSSValue::Array *aData,
                                    nsStyleContext *aContext,
                                    nsPresContext *aPresContext,
                                    PRBool &aCanStoreInRuleTree,
                                    nsRect& aBounds, float aAppUnitsPerMatrixUnit,
                                    PRBool *aPercentX = nsnull, 
                                    PRBool *aPercentY = nsnull);
+  static gfx3DMatrix ProcessMatrix3D(const nsCSSValue::Array *aData);
   static gfx3DMatrix ProcessInterpolateMatrix(const nsCSSValue::Array *aData,
                                               nsStyleContext *aContext,
                                               nsPresContext *aPresContext,
                                               PRBool &aCanStoreInRuleTree,
                                               nsRect& aBounds, float aAppUnitsPerMatrixUnit);
   static gfx3DMatrix ProcessTranslateX(const nsCSSValue::Array *aData,
                                        nsStyleContext *aContext,
                                        nsPresContext *aPresContext,
                                        PRBool &aCanStoreInRuleTree,
                                        nsRect& aBounds, float aAppUnitsPerMatrixUnit);
   static gfx3DMatrix ProcessTranslateY(const nsCSSValue::Array *aData,
                                        nsStyleContext *aContext,
                                        nsPresContext *aPresContext,
                                        PRBool &aCanStoreInRuleTree,
                                        nsRect& aBounds, float aAppUnitsPerMatrixUnit);
+  static gfx3DMatrix ProcessTranslateZ(const nsCSSValue::Array *aData,
+                                       nsStyleContext *aContext,
+                                       nsPresContext *aPresContext,
+                                       PRBool &aCanStoreInRuleTree,
+                                       float aAppUnitsPerMatrixUnit);
   static gfx3DMatrix ProcessTranslate(const nsCSSValue::Array *aData,
                                       nsStyleContext *aContext,
                                       nsPresContext *aPresContext,
                                       PRBool &aCanStoreInRuleTree,
                                       nsRect& aBounds, float aAppUnitsPerMatrixUnit);
+  static gfx3DMatrix ProcessTranslate3D(const nsCSSValue::Array *aData,
+                                        nsStyleContext *aContext,
+                                        nsPresContext *aPresContext,
+                                        PRBool &aCanStoreInRuleTree,
+                                        nsRect& aBounds, float aAppUnitsPerMatrixUnit);
   static gfx3DMatrix ProcessScaleHelper(float aXScale, float aYScale, float aZScale);
   static gfx3DMatrix ProcessScaleX(const nsCSSValue::Array *aData);
   static gfx3DMatrix ProcessScaleY(const nsCSSValue::Array *aData);
+  static gfx3DMatrix ProcessScaleZ(const nsCSSValue::Array *aData);
   static gfx3DMatrix ProcessScale(const nsCSSValue::Array *aData);
+  static gfx3DMatrix ProcessScale3D(const nsCSSValue::Array *aData);
   static gfx3DMatrix ProcessSkewHelper(double aXAngle, double aYAngle);
   static gfx3DMatrix ProcessSkewX(const nsCSSValue::Array *aData);
   static gfx3DMatrix ProcessSkewY(const nsCSSValue::Array *aData);
   static gfx3DMatrix ProcessSkew(const nsCSSValue::Array *aData);
+  static gfx3DMatrix ProcessRotateX(const nsCSSValue::Array *aData);
+  static gfx3DMatrix ProcessRotateY(const nsCSSValue::Array *aData);
   static gfx3DMatrix ProcessRotateZ(const nsCSSValue::Array *aData);
+  static gfx3DMatrix ProcessRotate3D(const nsCSSValue::Array *aData);
+  static gfx3DMatrix ProcessPerspective(const nsCSSValue::Array *aData,
+                                        nsStyleContext *aContext,
+                                        nsPresContext *aPresContext,
+                                        PRBool &aCanStoreInRuleTree,
+                                        float aAppUnitsPerMatrixUnit);
 };
 
 #endif
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -962,16 +962,59 @@ var gCSSProperties = {
 			"-moz-calc(20px + 50%) -moz-calc(50% - 10px)",
 			"-moz-calc(-20px) -moz-calc(-50%)",
 			"-moz-calc(-20%) -moz-calc(-50%)"
 		],
 		invalid_values: ["red", "auto", "none", "0.5 0.5", "40px #0000ff",
 						 "border", "center red", "right diagonal",
 						 "#00ffff bottom"]
 	},
+    "-moz-perspective-origin": {
+        domProp: "MozPerspectiveOrigin",
+        inherited: false,
+        type: CSS_TYPE_LONGHAND,
+        /* no subproperties */
+        prerequisites: { "width": "10px", "height": "10px", "display": "block"},
+        initial_values: [ "50% 50%", "center", "center center" ],
+        other_values: [ "25% 25%", "5px 5px", "20% 3em", "0 0", "0in 1in",
+                        "top", "bottom","top left", "top right",
+                        "top center", "center left", "center right",
+                        "bottom left", "bottom right", "bottom center",
+                        "20% center", "5px center", "13in bottom",
+                        "left 50px", "right 13%", "center 40px",
+                        "-moz-calc(20px)",
+                        "-moz-calc(20px) 10px",
+                        "10px -moz-calc(20px)",
+                        "-moz-calc(20px) 25%",
+                        "25% -moz-calc(20px)",
+                        "-moz-calc(20px) -moz-calc(20px)",
+                        "-moz-calc(20px + 1em) -moz-calc(20px / 2)",
+                        "-moz-calc(20px + 50%) -moz-calc(50% - 10px)",
+                        "-moz-calc(-20px) -moz-calc(-50%)",
+                        "-moz-calc(-20%) -moz-calc(-50%)" ],
+        invalid_values: [ "red", "auto", "none", "0.5 0.5", "40px #0000ff",
+                          "border", "center red", "right diagonal",
+                          "#00ffff bottom"]
+    },
+    "-moz-perspective": {
+		domProp: "MozPerspective",
+		inherited: false,
+		type: CSS_TYPE_LONGHAND,
+		initial_values: [ "none", "0" ],
+		other_values: [ "1000px", "500.2px", "-100px", "-27.2em" ],
+		invalid_values: [ "pants", "200" ]
+	},
+    "-moz-backface-visibility": {
+        domProp: "MozBackfaceVisibility",
+        inherited: false,
+        type: CSS_TYPE_LONGHAND,
+        initial_values: [ "visible" ],
+        other_values: [ "hidden" ],
+        invalid_values: [ "collapse" ]
+    },
 	"-moz-user-focus": {
 		domProp: "MozUserFocus",
 		inherited: true,
 		type: CSS_TYPE_LONGHAND,
 		initial_values: [ "none" ],
 		other_values: [ "normal", "ignore", "select-all", "select-before", "select-after", "select-same", "select-menu" ],
 		invalid_values: []
 	},
--- a/layout/style/test/test_transitions_per_property.html
+++ b/layout/style/test/test_transitions_per_property.html
@@ -76,16 +76,19 @@ var supported_properties = {
     "-moz-outline-radius-topleft": [ test_radius_transition ],
     "-moz-outline-radius-topright": [ test_radius_transition ],
     "-moz-text-decoration-color": [ test_color_transition,
                                     test_border_color_transition ],
     "-moz-transform": [ test_transform_transition ],
     "-moz-transform-origin": [ test_length_pair_transition,
                                test_length_percent_pair_transition,
                                test_length_percent_pair_unclamped ],
+    "-moz-perspective-origin": [ test_length_pair_transition,
+                                 test_length_percent_pair_transition,
+                                 test_length_percent_pair_unclamped ],
     "background-color": [ test_color_transition ],
     "background-position": [ test_background_position_transition,
                              // FIXME: We don't currently test clamping,
                              // since background-position uses calc() as
                              // an intermediate form.
                              /* test_length_percent_pair_unclamped */ ],
     "background-size": [ test_background_size_transition,
                          // FIXME: We don't currently test clamping,
@@ -186,16 +189,17 @@ var supported_properties = {
                       test_length_percent_calc_transition,
                       test_length_clamped, test_percent_clamped ],
     "padding-right": [ test_length_transition, test_percent_transition,
                        test_length_percent_calc_transition,
                        test_length_clamped, test_percent_clamped ],
     "padding-top": [ test_length_transition, test_percent_transition,
                      test_length_percent_calc_transition,
                      test_length_clamped, test_percent_clamped ],
+    "-moz-perspective": [ test_length_transition ],
     "right": [ test_length_transition, test_percent_transition,
                test_length_percent_calc_transition,
                test_length_unclamped, test_percent_unclamped ],
     "stop-color": [ test_color_transition ],
     "stop-opacity" : [ test_float_zeroToOne_transition,
                        // opacity is clamped in computed style
                        // (not parsing/interpolation)
                        test_float_zeroToOne_clamped ],
--- a/media/libsydneyaudio/src/sydney_audio_alsa.c
+++ b/media/libsydneyaudio/src/sydney_audio_alsa.c
@@ -244,39 +244,40 @@ sa_stream_write(sa_stream_t *s, const vo
   if (s == NULL || s->output_unit == NULL) {
     return SA_ERROR_NO_INIT;
   }
   if (nbytes == 0) {
     return SA_SUCCESS;
   }
 
   nframes = snd_pcm_bytes_to_frames(s->output_unit, nbytes);
-
   while(nframes>0) {
     if (s->resumed) {
       avail = snd_pcm_avail_update(s->output_unit);
       frames = snd_pcm_writei(s->output_unit, data, nframes > avail ? avail : nframes);
       avail = snd_pcm_avail_update(s->output_unit);
       s->resumed = avail != 0;
     } else {
-      frames = snd_pcm_writei(s->output_unit, data, nframes);
+      avail = snd_pcm_avail_update(s->output_unit);
+      avail = avail < 64 ? 64 : avail;
+      frames = snd_pcm_writei(s->output_unit, data, nframes > avail ? avail : nframes);
     }
     if (frames < 0) {
       int r = snd_pcm_recover(s->output_unit, frames, 1);
       if (r < 0) {
         return SA_ERROR_SYSTEM;
       }
     } else {
+      size_t bytes = snd_pcm_frames_to_bytes(s->output_unit, frames);
       nframes -= frames;
-      data = ((unsigned char *)data) + snd_pcm_frames_to_bytes(s->output_unit, frames);
+      data = ((unsigned char *)data) + bytes;
+      s->bytes_written += bytes;
     }
   }
 
-  s->bytes_written += nbytes;
-
   return SA_SUCCESS;
 }
 
 /*
  * -----------------------------------------------------------------------------
  * General query and support functions
  * -----------------------------------------------------------------------------
  */
--- a/mobile/app/mobile.js
+++ b/mobile/app/mobile.js
@@ -646,8 +646,18 @@ pref("browser.safebrowsing.malware.repor
 #endif
 
 // True if this is the first time we are showing about:firstrun
 pref("browser.firstrun.show.uidiscovery", true);
 pref("browser.firstrun.show.localepicker", true);
 
 // initiated by a user
 pref("content.ime.strict_policy", true);
+
+// True if you always want dump() to work
+//
+// On Android, you also need to do the following for the output
+// to show up in logcat:
+//
+// $ adb shell stop
+// $ adb shell setprop log.redirect-stdio true
+// $ adb shell start
+pref("browser.dom.window.dump.enabled", false);
--- a/mobile/themes/core/jar.mn
+++ b/mobile/themes/core/jar.mn
@@ -126,17 +126,17 @@ chrome.jar:
   skin/images/pause-hdpi.png                (images/pause-hdpi.png)
   skin/images/mute-hdpi.png                 (images/mute-hdpi.png)
   skin/images/unmute-hdpi.png               (images/unmute-hdpi.png)
   skin/images/scrubber-hdpi.png             (images/scrubber-hdpi.png)
   skin/images/handle-start.png              (images/handle-start.png)
   skin/images/handle-end.png                (images/handle-end.png)
 
 chrome.jar:
-% skin browser classic/1.0 %skin/gingerbread/ os=Android osversion=2.3 osversion=2.3.3 osversion=2.3.4
+% skin browser classic/1.0 %skin/gingerbread/ os=Android osversion=2.3 osversion=2.3.3 osversion=2.3.4 osversion=2.3.5 osversion=2.3.6 osversion=2.3.7
 % skin browser gingerbread/1.0 %skin/gingerbread/
   skin/gingerbread/aboutPage.css                        (aboutPage.css)
   skin/gingerbread/about.css                            (about.css)
   skin/gingerbread/aboutHome.css                        (aboutHome.css)
 * skin/gingerbread/browser.css                          (gingerbread/browser.css)
 * skin/gingerbread/content.css                          (gingerbread/content.css)
   skin/gingerbread/config.css                           (config.css)
 * skin/gingerbread/forms.css                            (gingerbread/forms.css)
--- a/modules/libpr0n/src/RasterImage.cpp
+++ b/modules/libpr0n/src/RasterImage.cpp
@@ -837,18 +837,17 @@ RasterImage::InternalAddFrame(PRUint32 f
 
   if (mFrames.Length() == 0) {
     return InternalAddFrameHelper(framenum, frame.forget(), imageData, imageLength, 
                                   paletteData, paletteLength);
   }
 
   if (mFrames.Length() == 1) {
     // Since we're about to add our second frame, initialize animation stuff
-    if (!ensureAnimExists())
-      return NS_ERROR_OUT_OF_MEMORY;
+    EnsureAnimExists();
     
     // If we dispose of the first frame by clearing it, then the
     // First Frame's refresh area is all of itself.
     // RESTORE_PREVIOUS is invalid (assumed to be DISPOSE_CLEAR)
     PRInt32 frameDisposalMethod = mFrames[0]->GetFrameDisposalMethod();
     if (frameDisposalMethod == kDisposeClear ||
         frameDisposalMethod == kDisposeRestorePrevious)
       mAnim->firstFrameRefreshArea = mFrames[0]->GetRect();
@@ -1108,18 +1107,17 @@ RasterImage::DecodingComplete()
 nsresult
 RasterImage::StartAnimation()
 {
   if (mError)
     return NS_ERROR_FAILURE;
 
   NS_ABORT_IF_FALSE(ShouldAnimate(), "Should not animate!");
 
-  if (!ensureAnimExists())
-    return NS_ERROR_OUT_OF_MEMORY;
+  EnsureAnimExists();
 
   NS_ABORT_IF_FALSE(mAnim && !mAnim->timer, "Anim must exist and not have a timer yet");
   
   // Default timeout to 100: the timer notify code will do the right
   // thing, so just get that started.
   PRInt32 timeout = 100;
   imgFrame *currentFrame = GetCurrentImgFrame();
   if (currentFrame) {
--- a/modules/libpr0n/src/RasterImage.h
+++ b/modules/libpr0n/src/RasterImage.h
@@ -377,17 +377,17 @@ private:
 
   imgFrame* GetImgFrameNoDecode(PRUint32 framenum);
   imgFrame* GetImgFrame(PRUint32 framenum);
   imgFrame* GetDrawableImgFrame(PRUint32 framenum);
   imgFrame* GetCurrentImgFrame();
   imgFrame* GetCurrentDrawableImgFrame();
   PRUint32 GetCurrentImgFrameIndex() const;
   
-  inline Anim* ensureAnimExists()
+  inline void EnsureAnimExists()
   {
     if (!mAnim) {
 
       // Create the animation context
       mAnim = new Anim();
 
       // We don't support discarding animated images (See bug 414259).
       // Lock the image and throw away the key.
@@ -395,17 +395,16 @@ private:
       // Note that this is inefficient, since we could get rid of the source
       // data too. However, doing this is actually hard, because we're probably
       // calling ensureAnimExists mid-decode, and thus we're decoding out of
       // the source buffer. Since we're going to fix this anyway later, and
       // since we didn't kill the source data in the old world either, locking
       // is acceptable for the moment.
       LockImage();
     }
-    return mAnim;
   }
   
   /** Function for doing the frame compositing of animations
    *
    * @param aFrameToUse Set by DoComposite
    *                   (aNextFrame, compositingFrame, or compositingPrevFrame)
    * @param aDirtyRect  Area that the display will need to update
    * @param aPrevFrame  Last Frame seen/processed
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -3320,8 +3320,11 @@ pref("browser.history.maxStateObjectSize
 pref("xpinstall.whitelist.required", true);
 pref("extensions.alwaysUnpack", false);
 
 pref("network.buffer.cache.count", 24);
 pref("network.buffer.cache.size",  32768);
 
 // Desktop Notification
 pref("notification.feature.enabled", false);
+
+//3D Transforms
+pref("layout.3d-transforms.enabled, false);
--- a/netwerk/cache/nsDeleteDir.cpp
+++ b/netwerk/cache/nsDeleteDir.cpp
@@ -16,16 +16,17 @@
  * The Original Code is mozilla.org code.
  *
  * The Initial Developer of the Original Code is Google Inc.
  * Portions created by the Initial Developer are Copyright (C) 2005
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *  Darin Fisher <darin@meer.net>
+ *  Jason Duell <jduell.mcbugs@gmail.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either 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
@@ -36,28 +37,39 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsDeleteDir.h"
 #include "nsIFile.h"
 #include "nsString.h"
 #include "prthread.h"
 #include "mozilla/Telemetry.h"
+#include "nsITimer.h"
 
 using namespace mozilla;
 
 static void DeleteDirThreadFunc(void *arg)
 {
   Telemetry::AutoTimer<Telemetry::NETWORK_DISK_CACHE_DELETEDIR> timer;
   nsIFile *dir = static_cast<nsIFile *>(arg);
   dir->Remove(PR_TRUE);
   NS_RELEASE(dir);
 }
 
-nsresult DeleteDir(nsIFile *dirIn, PRBool moveToTrash, PRBool sync)
+static void CreateDeleterThread(nsITimer *aTimer, void *arg)
+{
+  nsIFile *dir = static_cast<nsIFile *>(arg);
+
+  // create the worker thread
+  PR_CreateThread(PR_USER_THREAD, DeleteDirThreadFunc, dir, PR_PRIORITY_LOW,
+                  PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD, 0);
+}
+
+nsresult DeleteDir(nsIFile *dirIn, PRBool moveToTrash, PRBool sync,
+                   PRUint32 delay)
 {
   Telemetry::AutoTimer<Telemetry::NETWORK_DISK_CACHE_TRASHRENAME> timer;
   nsresult rv;
   nsCOMPtr<nsIFile> trash, dir;
 
   // Need to make a clone of this since we don't want to modify the input
   // file object.
   rv = dirIn->Clone(getter_AddRefs(dir));
@@ -77,42 +89,45 @@ nsresult DeleteDir(nsIFile *dirIn, PRBoo
     // NTFS we'll wait (with cache lock) while nsIFile's ACL reset walks file
     // tree: was hanging GUI for *minutes* on large cache dirs.
     rv = dir->MoveToNative(nsnull, leaf);
     if (NS_FAILED(rv)) {
       nsresult rvMove = rv;
       // TrashDir may already exist (if we crashed while deleting it, etc.)
       // In that case current Cache dir should be small--just move it to
       // subdirectory of TrashDir and eat the NTFS ACL overhead.
+      leaf.AppendInt(rand()); // support this happening multiple times
       rv = dir->MoveToNative(trash, leaf);
       if (NS_FAILED(rv))
         return rvMove;
+      // Be paranoid and delete immediately if we're seeing old trash when
+      // we're creating a new one
+      delay = 0;
     }
   } else {
     // we want to pass a clone of the original off to the worker thread.
     trash.swap(dir);
   }
 
   // Steal ownership of trash directory; let the thread release it.
   nsIFile *trashRef = nsnull;
   trash.swap(trashRef);
 
   if (sync) {
     DeleteDirThreadFunc(trashRef);
   } else {
-    // now, invoke the worker thread
-    PRThread *thread = PR_CreateThread(PR_USER_THREAD,
-                                       DeleteDirThreadFunc,
-                                       trashRef,
-                                       PR_PRIORITY_LOW,
-                                       PR_GLOBAL_THREAD,
-                                       PR_UNJOINABLE_THREAD,
-                                       0);
-    if (!thread)
-      return NS_ERROR_UNEXPECTED;
+    if (delay) {
+      nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1", &rv);
+      if (NS_FAILED(rv))
+        return NS_ERROR_UNEXPECTED;
+      timer->InitWithFuncCallback(CreateDeleterThread, trashRef, delay,
+                                  nsITimer::TYPE_ONE_SHOT);
+    } else {
+      CreateDeleterThread(nsnull, trashRef);
+    }
   }
 
   return NS_OK;
 }
 
 nsresult GetTrashDir(nsIFile *target, nsCOMPtr<nsIFile> *result)
 {
   nsresult rv = target->Clone(getter_AddRefs(*result));
--- a/netwerk/cache/nsDeleteDir.h
+++ b/netwerk/cache/nsDeleteDir.h
@@ -54,18 +54,25 @@ class nsIFile;
  * trash folder (moving individual files if necessary).  Next, it proceeds to
  * delete each file in the trash folder on a low-priority background thread.
  *
  * If the moveToTrash parameter is false, then the given directory is deleted
  * directly.
  *
  * If the sync flag is true, then the delete operation runs to completion
  * before this function returns.  Otherwise, deletion occurs asynchronously.
+ *
+ * If 'delay' is non-zero, the directory will not be deleted until the
+ * specified number of milliseconds have passed. (The directory is still
+ * renamed immediately if 'moveToTrash' is passed, so upon return it is safe
+ * to create a directory with the same name). This parameter is ignored if
+ * 'sync' is true.
  */
-NS_HIDDEN_(nsresult) DeleteDir(nsIFile *dir, PRBool moveToTrash, PRBool sync);
+NS_HIDDEN_(nsresult) DeleteDir(nsIFile *dir, PRBool moveToTrash, PRBool sync, 
+                               PRUint32 delay = 0);
 
 /**
  * This routine returns the trash directory corresponding to the given 
  * directory.
  */
 NS_HIDDEN_(nsresult) GetTrashDir(nsIFile *dir, nsCOMPtr<nsIFile> *result);
 
 #endif  // nsDeleteDir_h__
--- a/netwerk/cache/nsDiskCacheDevice.cpp
+++ b/netwerk/cache/nsDiskCacheDevice.cpp
@@ -989,17 +989,18 @@ nsDiskCacheDevice::OpenDiskCache()
         return rv;
 
     PRBool trashing = PR_FALSE;
     if (exists) {
         // Try opening cache map file.
         rv = mCacheMap.Open(mCacheDirectory);        
         // move "corrupt" caches to trash
         if (rv == NS_ERROR_FILE_CORRUPTED) {
-            rv = DeleteDir(mCacheDirectory, PR_TRUE, PR_FALSE);
+            // delay delete by 1 minute to avoid IO thrash at startup
+            rv = DeleteDir(mCacheDirectory, PR_TRUE, PR_FALSE, 60000);
             if (NS_FAILED(rv))
                 return rv;
             exists = PR_FALSE;
             trashing = PR_TRUE;
         }
         else if (NS_FAILED(rv))
             return rv;
     }
@@ -1019,18 +1020,20 @@ nsDiskCacheDevice::OpenDiskCache()
     }
 
     if (!trashing) {
         // delete any trash files leftover from a previous run
         nsCOMPtr<nsIFile> trashDir;
         GetTrashDir(mCacheDirectory, &trashDir);
         if (trashDir) {
             PRBool exists;
-            if (NS_SUCCEEDED(trashDir->Exists(&exists)) && exists)
+            if (NS_SUCCEEDED(trashDir->Exists(&exists)) && exists) {
+                // be paranoid and delete immediately if leftover
                 DeleteDir(trashDir, PR_FALSE, PR_FALSE);
+            }
         }
     }
 
     return NS_OK;
 }
 
 
 nsresult
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -1290,30 +1290,30 @@ NS_IMETHODIMP
 HttpBaseChannel::GetEntityID(nsACString& aEntityID)
 {
   // Don't return an entity ID for Non-GET requests which require
   // additional data
   if (mRequestHead.Method() != nsHttp::Get) {
     return NS_ERROR_NOT_RESUMABLE;
   }
 
-  // Don't return an entity if the server sent the following header:
-  // Accept-Ranges: none
-  // Not sending the Accept-Ranges header means we can still try
-  // sending range requests.
-  const char* acceptRanges =
-      mResponseHead->PeekHeader(nsHttp::Accept_Ranges);
-  if (acceptRanges &&
-      !nsHttp::FindToken(acceptRanges, "bytes", HTTP_HEADER_VALUE_SEPS)) {
-    return NS_ERROR_NOT_RESUMABLE;
-  }
-
   PRUint64 size = LL_MAXUINT;
   nsCAutoString etag, lastmod;
   if (mResponseHead) {
+    // Don't return an entity if the server sent the following header:
+    // Accept-Ranges: none
+    // Not sending the Accept-Ranges header means we can still try
+    // sending range requests.
+    const char* acceptRanges =
+        mResponseHead->PeekHeader(nsHttp::Accept_Ranges);
+    if (acceptRanges &&
+        !nsHttp::FindToken(acceptRanges, "bytes", HTTP_HEADER_VALUE_SEPS)) {
+      return NS_ERROR_NOT_RESUMABLE;
+    }
+
     size = mResponseHead->TotalEntitySize();
     const char* cLastMod = mResponseHead->PeekHeader(nsHttp::Last_Modified);
     if (cLastMod)
       lastmod = cLastMod;
     const char* cEtag = mResponseHead->PeekHeader(nsHttp::ETag);
     if (cEtag)
       etag = cEtag;
   }
--- a/netwerk/protocol/websocket/PWebSocket.ipdl
+++ b/netwerk/protocol/websocket/PWebSocket.ipdl
@@ -65,17 +65,14 @@ child:
   // Forwarded notifications corresponding to the nsIWebSocketListener interface
   OnStart(nsCString aProtocol);
   OnStop(nsresult aStatusCode);
   OnMessageAvailable(nsCString aMsg);
   OnBinaryMessageAvailable(nsCString aMsg);
   OnAcknowledge(PRUint32 aSize);
   OnServerClose();
 
-  // Only sent in the event that AsyncOpen fails
-  AsyncOpenFailed();
-
   __delete__();
 
 };
 
 } //namespace net
 } //namespace mozilla
--- a/netwerk/protocol/websocket/WebSocketChannel.cpp
+++ b/netwerk/protocol/websocket/WebSocketChannel.cpp
@@ -1539,32 +1539,32 @@ WebSocketChannel::SetupRequest()
   char* b64 = PL_Base64Encode((const char *)secKey, 16, nsnull);
   NS_Free(secKey);
   if (!b64)
     return NS_ERROR_OUT_OF_MEMORY;
   secKeyString.Assign(b64);
   PR_Free(b64);
   mHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Sec-WebSocket-Key"),
                                  secKeyString, PR_FALSE);
-  LOG(("WebSocketChannel::AsyncOpen(): client key %s\n", secKeyString.get()));
+  LOG(("WebSocketChannel::SetupRequest: client key %s\n", secKeyString.get()));
 
   // prepare the value we expect to see in
   // the sec-websocket-accept response header
   secKeyString.AppendLiteral("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
   nsCOMPtr<nsICryptoHash> hasher =
     do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = hasher->Init(nsICryptoHash::SHA1);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = hasher->Update((const PRUint8 *) secKeyString.BeginWriting(),
                       secKeyString.Length());
   NS_ENSURE_SUCCESS(rv, rv);
   rv = hasher->Finish(PR_TRUE, mHashedSecret);
   NS_ENSURE_SUCCESS(rv, rv);
-  LOG(("WebSocketChannel::AsyncOpen(): expected server key %s\n",
+  LOG(("WebSocketChannel::SetupRequest: expected server key %s\n",
        mHashedSecret.get()));
 
   return NS_OK;
 }
 
 nsresult
 WebSocketChannel::ApplyForAdmission()
 {
@@ -1578,17 +1578,17 @@ WebSocketChannel::ApplyForAdmission()
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCString hostName;
   rv = mURI->GetHost(hostName);
   NS_ENSURE_SUCCESS(rv, rv);
   mAddress = hostName;
 
   // expect the callback in ::OnLookupComplete
-  LOG(("WebSocketChannel::AsyncOpen(): checking for concurrent open\n"));
+  LOG(("WebSocketChannel::ApplyForAdmission: checking for concurrent open\n"));
   nsCOMPtr<nsIThread> mainThread;
   NS_GetMainThread(getter_AddRefs(mainThread));
   dns->AsyncResolve(hostName, 0, this, mainThread, getter_AddRefs(mDNSRequest));
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
@@ -1983,16 +1983,23 @@ WebSocketChannel::AsyncOpen(nsIURI *aURI
   return ApplyForAdmission();
 }
 
 NS_IMETHODIMP
 WebSocketChannel::Close()
 {
   LOG(("WebSocketChannel::Close() %p\n", this));
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
+
+  if (!mTransport) {
+    LOG(("WebSocketChannel::Close() without transport - aborting."));
+    AbortSession(NS_ERROR_NOT_CONNECTED);
+    return NS_ERROR_NOT_CONNECTED;
+  }
+
   if (mRequestedClose) {
     LOG(("WebSocketChannel:: Double close error\n"));
     return NS_ERROR_UNEXPECTED;
   }
 
   mRequestedClose = 1;
 
   return mSocketThread->Dispatch(new nsPostMessage(this, kFinMessage, -1),
--- a/netwerk/protocol/websocket/WebSocketChannelChild.cpp
+++ b/netwerk/protocol/websocket/WebSocketChannelChild.cpp
@@ -72,17 +72,16 @@ NS_INTERFACE_MAP_BEGIN(WebSocketChannelC
   NS_INTERFACE_MAP_ENTRY(nsIWebSocketChannel)
   NS_INTERFACE_MAP_ENTRY(nsIProtocolHandler)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWebSocketChannel)
 NS_INTERFACE_MAP_END
 
 WebSocketChannelChild::WebSocketChannelChild(bool aSecure)
 : mEventQ(static_cast<nsIWebSocketChannel*>(this))
 , mIPCOpen(false)
-, mCancelled(false)
 {
   LOG(("WebSocketChannelChild::WebSocketChannelChild() %p\n", this));
   BaseWebSocketChannel::mEncrypted = aSecure;
 }
 
 WebSocketChannelChild::~WebSocketChannelChild()
 {
   LOG(("WebSocketChannelChild::~WebSocketChannelChild() %p\n", this));
@@ -320,51 +319,16 @@ WebSocketChannelChild::OnServerClose()
 {
   LOG(("WebSocketChannelChild::RecvOnServerClose() %p\n", this));
   if (mListener) {
     AutoEventEnqueuer ensureSerialDispatch(mEventQ);;
     mListener->OnServerClose(mContext);
   }
 }
 
-class AsyncOpenFailedEvent : public ChannelEvent
-{
- public:
-  AsyncOpenFailedEvent(WebSocketChannelChild* aChild)
-  : mChild(aChild)
-  {}
-
-  void Run()
-  {
-    mChild->AsyncOpenFailed();
-  }
- private:
-  WebSocketChannelChild* mChild;
-};
-
-bool
-WebSocketChannelChild::RecvAsyncOpenFailed()
-{
-  if (mEventQ.ShouldEnqueue()) {
-    mEventQ.Enqueue(new AsyncOpenFailedEvent(this));
-  } else {
-    AsyncOpenFailed();
-  }
-  return true;
-}
-
-void
-WebSocketChannelChild::AsyncOpenFailed()
-{
-  LOG(("WebSocketChannelChild::RecvAsyncOpenFailed() %p\n", this));
-  mCancelled = true;
-  if (mIPCOpen)
-    SendDeleteSelf();
-}
-
 NS_IMETHODIMP
 WebSocketChannelChild::AsyncOpen(nsIURI *aURI,
                                  const nsACString &aOrigin,
                                  nsIWebSocketListener *aListener,
                                  nsISupports *aContext)
 {
   LOG(("WebSocketChannelChild::AsyncOpen() %p\n", this));
 
@@ -396,45 +360,36 @@ WebSocketChannelChild::AsyncOpen(nsIURI 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 WebSocketChannelChild::Close()
 {
   LOG(("WebSocketChannelChild::Close() %p\n", this));
 
-  if (mCancelled)
-    return NS_ERROR_UNEXPECTED;
-
   if (!mIPCOpen || !SendClose())
     return NS_ERROR_UNEXPECTED;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 WebSocketChannelChild::SendMsg(const nsACString &aMsg)
 {
   LOG(("WebSocketChannelChild::SendMsg() %p\n", this));
 
-  if (mCancelled)
-    return NS_ERROR_UNEXPECTED;
-
   if (!mIPCOpen || !SendSendMsg(nsCString(aMsg)))
     return NS_ERROR_UNEXPECTED;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 WebSocketChannelChild::SendBinaryMsg(const nsACString &aMsg)
 {
   LOG(("WebSocketChannelChild::SendBinaryMsg() %p\n", this));
 
-  if (mCancelled)
-    return NS_ERROR_UNEXPECTED;
-
   if (!mIPCOpen || !SendSendBinaryMsg(nsCString(aMsg)))
     return NS_ERROR_UNEXPECTED;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 WebSocketChannelChild::GetSecurityInfo(nsISupports **aSecurityInfo)
 {
--- a/netwerk/protocol/websocket/WebSocketChannelChild.h
+++ b/netwerk/protocol/websocket/WebSocketChannelChild.h
@@ -86,17 +86,16 @@ class WebSocketChannelChild : public Bas
   void OnMessageAvailable(const nsCString& aMsg);
   void OnBinaryMessageAvailable(const nsCString& aMsg);
   void OnAcknowledge(const PRUint32& aSize);
   void OnServerClose();
   void AsyncOpenFailed();  
 
   ChannelEventQueue mEventQ;
   bool mIPCOpen;
-  bool mCancelled;
 
   friend class StartEvent;
   friend class StopEvent;
   friend class MessageEvent;
   friend class AcknowledgeEvent;
   friend class ServerCloseEvent;
   friend class AsyncOpenFailedEvent;
 };
--- a/netwerk/protocol/websocket/WebSocketChannelParent.cpp
+++ b/netwerk/protocol/websocket/WebSocketChannelParent.cpp
@@ -78,31 +78,35 @@ WebSocketChannelParent::RecvAsyncOpen(co
   if (aSecure) {
     mChannel =
       do_CreateInstance("@mozilla.org/network/protocol;1?name=wss", &rv);
   } else {
     mChannel =
       do_CreateInstance("@mozilla.org/network/protocol;1?name=ws", &rv);
   }
   if (NS_FAILED(rv))
-    return CancelEarly();
+    goto fail;
 
   rv = mChannel->SetNotificationCallbacks(this);
   if (NS_FAILED(rv))
-    return CancelEarly();
+    goto fail;
 
   rv = mChannel->SetProtocol(aProtocol);
   if (NS_FAILED(rv))
-    return CancelEarly();
+    goto fail;
 
   rv = mChannel->AsyncOpen(aURI, aOrigin, this, nsnull);
   if (NS_FAILED(rv))
-    return CancelEarly();
+    goto fail;
 
   return true;
+
+fail:
+  mChannel = nsnull;
+  return SendOnStop(rv);
 }
 
 bool
 WebSocketChannelParent::RecvClose()
 {
   LOG(("WebSocketChannelParent::RecvClose() %p\n", this));
   if (mChannel) {
     nsresult rv = mChannel->Close();
@@ -128,23 +132,16 @@ WebSocketChannelParent::RecvSendBinaryMs
   LOG(("WebSocketChannelParent::RecvSendBinaryMsg() %p\n", this));
   if (mChannel) {
     nsresult rv = mChannel->SendBinaryMsg(aMsg);
     NS_ENSURE_SUCCESS(rv, true);
   }
   return true;
 }
 
-bool
-WebSocketChannelParent::CancelEarly()
-{
-  LOG(("WebSocketChannelParent::CancelEarly() %p\n", this));
-  return mIPCOpen ? SendAsyncOpenFailed() : true;
-}
-
 NS_IMETHODIMP
 WebSocketChannelParent::GetInterface(const nsIID & iid, void **result NS_OUTPARAM)
 {
   LOG(("WebSocketChannelParent::GetInterface() %p\n", this));
   if (mAuthProvider && iid.Equals(NS_GET_IID(nsIAuthPromptProvider)))
     return mAuthProvider->GetAuthPrompt(nsIAuthPromptProvider::PROMPT_NORMAL,
                                         iid, result);
 
--- a/netwerk/protocol/websocket/WebSocketChannelParent.h
+++ b/netwerk/protocol/websocket/WebSocketChannelParent.h
@@ -66,17 +66,16 @@ class WebSocketChannelParent : public PW
   bool RecvAsyncOpen(const IPC::URI& aURI,
                      const nsCString& aOrigin,
                      const nsCString& aProtocol,
                      const bool& aSecure);
   bool RecvClose();
   bool RecvSendMsg(const nsCString& aMsg);
   bool RecvSendBinaryMsg(const nsCString& aMsg);
   bool RecvDeleteSelf();
-  bool CancelEarly();
 
   void ActorDestroy(ActorDestroyReason why);
 
   nsCOMPtr<nsIAuthPromptProvider> mAuthProvider;
   nsCOMPtr<nsIWebSocketChannel> mChannel;
   bool mIPCOpen;
 };
 
--- a/parser/html/nsHtml5Speculation.h
+++ b/parser/html/nsHtml5Speculation.h
@@ -95,9 +95,9 @@ class nsHtml5Speculation : public nsAHtm
      */
     PRInt32                             mStartLineNumber;
     
     nsAutoPtr<nsAHtml5TreeBuilderState> mSnapshot;
 
     nsTArray<nsHtml5TreeOperation>      mOpQueue;
 };
 
-#endif // nsHtml5Speculation_h__
\ No newline at end of file
+#endif // nsHtml5Speculation_h__
--- a/parser/html/nsHtml5SpeculativeLoad.cpp
+++ b/parser/html/nsHtml5SpeculativeLoad.cpp
@@ -53,27 +53,37 @@ nsHtml5SpeculativeLoad::~nsHtml5Speculat
                "Uninitialized speculative load.");
 }
 
 void
 nsHtml5SpeculativeLoad::Perform(nsHtml5TreeOpExecutor* aExecutor)
 {
   switch (mOpCode) {
     case eSpeculativeLoadBase:
-        aExecutor->SetSpeculationBase(mUrl);
+      aExecutor->SetSpeculationBase(mUrl);
       break;
     case eSpeculativeLoadImage:
-        aExecutor->PreloadImage(mUrl, mCharsetOrCrossOrigin);
+      aExecutor->PreloadImage(mUrl, mCharsetOrCrossOrigin);
       break;
     case eSpeculativeLoadScript:
-        aExecutor->PreloadScript(mUrl, mCharsetOrCrossOrigin, mType);
+      aExecutor->PreloadScript(mUrl, mCharsetOrCrossOrigin, mTypeOrCharsetSource);
       break;
     case eSpeculativeLoadStyle:
-        aExecutor->PreloadStyle(mUrl, mCharsetOrCrossOrigin);
+      aExecutor->PreloadStyle(mUrl, mCharsetOrCrossOrigin);
       break;
     case eSpeculativeLoadManifest:  
-        aExecutor->ProcessOfflineManifest(mUrl);
+      aExecutor->ProcessOfflineManifest(mUrl);
+      break;
+    case eSpeculativeLoadSetDocumentCharset: {
+        nsCAutoString narrowName;
+        CopyUTF16toUTF8(mCharsetOrCrossOrigin, narrowName);
+        NS_ASSERTION(mTypeOrCharsetSource.Length() == 1,
+            "Unexpected charset source string");
+        PRInt32 intSource = (PRInt32)mTypeOrCharsetSource.First();
+        aExecutor->SetDocumentCharsetAndSource(narrowName,
+                                               intSource);
+      }
       break;
     default:
       NS_NOTREACHED("Bogus speculative load.");
       break;
   }
 }
--- a/parser/html/nsHtml5SpeculativeLoad.h
+++ b/parser/html/nsHtml5SpeculativeLoad.h
@@ -45,17 +45,18 @@ class nsHtml5TreeOpExecutor;
 enum eHtml5SpeculativeLoad {
 #ifdef DEBUG
   eSpeculativeLoadUninitialized,
 #endif
   eSpeculativeLoadBase,
   eSpeculativeLoadImage,
   eSpeculativeLoadScript,
   eSpeculativeLoadStyle,
-  eSpeculativeLoadManifest  
+  eSpeculativeLoadManifest,
+  eSpeculativeLoadSetDocumentCharset
 };
 
 class nsHtml5SpeculativeLoad {
   public:
     nsHtml5SpeculativeLoad();
     ~nsHtml5SpeculativeLoad();
     
     inline void InitBase(const nsAString& aUrl) {
@@ -77,17 +78,17 @@ class nsHtml5SpeculativeLoad {
     inline void InitScript(const nsAString& aUrl,
                       const nsAString& aCharset,
                       const nsAString& aType) {
       NS_PRECONDITION(mOpCode == eSpeculativeLoadUninitialized,
                       "Trying to reinitialize a speculative load!");
       mOpCode = eSpeculativeLoadScript;
       mUrl.Assign(aUrl);
       mCharsetOrCrossOrigin.Assign(aCharset);
-      mType.Assign(aType);
+      mTypeOrCharsetSource.Assign(aType);
     }
     
     inline void InitStyle(const nsAString& aUrl, const nsAString& aCharset) {
       NS_PRECONDITION(mOpCode == eSpeculativeLoadUninitialized,
                       "Trying to reinitialize a speculative load!");
       mOpCode = eSpeculativeLoadStyle;
       mUrl.Assign(aUrl);
       mCharsetOrCrossOrigin.Assign(aCharset);
@@ -95,33 +96,62 @@ class nsHtml5SpeculativeLoad {
 
     /**
      * "Speculative" manifest loads aren't truly speculative--if a manifest
      * gets loaded, we are committed to it. There can never be a <script>
      * before the manifest, so the situation of having to undo a manifest due
      * to document.write() never arises. The reason why a parser
      * thread-discovered manifest gets loaded via the speculative load queue
      * as opposed to tree operation queue is that the manifest must get
-     * processed before any actually speculative loads such as scripts. Thus,
+     * processed before any actual speculative loads such as scripts. Thus,
      * manifests seen by the parser thread have to maintain the queue order
      * relative to true speculative loads. See bug 541079.
      */
     inline void InitManifest(const nsAString& aUrl) {
       NS_PRECONDITION(mOpCode == eSpeculativeLoadUninitialized,
                       "Trying to reinitialize a speculative load!");
       mOpCode = eSpeculativeLoadManifest;
       mUrl.Assign(aUrl);
     }
 
+    /**
+     * "Speculative" charset setting isn't truly speculative. If the charset
+     * is set via this operation, we are committed to it unless chardet or
+     * a late meta cause a reload. The reason why a parser
+     * thread-discovered charset gets communicated via the speculative load
+     * queue as opposed to tree operation queue is that the charset change
+     * must get processed before any actual speculative loads such as style
+     * sheets. Thus, encoding decisions by the parser thread have to maintain
+     * the queue order relative to true speculative loads. See bug 675499.
+     */
+    inline void InitSetDocumentCharset(nsACString& aCharset,
+                                       PRInt32 aCharsetSource) {
+      NS_PRECONDITION(mOpCode == eSpeculativeLoadUninitialized,
+                      "Trying to reinitialize a speculative load!");
+      mOpCode = eSpeculativeLoadSetDocumentCharset;
+      CopyUTF8toUTF16(aCharset, mCharsetOrCrossOrigin);
+      mTypeOrCharsetSource.Assign((PRUnichar)aCharsetSource);
+    }
+
     void Perform(nsHtml5TreeOpExecutor* aExecutor);
     
   private:
     eHtml5SpeculativeLoad mOpCode;
     nsString mUrl;
-    // If mOpCode is eSpeculativeLoadImage, this is the value of the
-    // "crossorigin" attribute.  If mOpCode is eSpeculativeLoadStyle
-    // or eSpeculativeLoadScript then this is the value of the
-    // "charset" attribute.  Otherwise it's empty.
+    /**
+     * If mOpCode is eSpeculativeLoadImage, this is the value of the
+     * "crossorigin" attribute.  If mOpCode is eSpeculativeLoadStyle
+     * or eSpeculativeLoadScript then this is the value of the
+     * "charset" attribute. For eSpeculativeLoadSetDocumentCharset it is
+     * the charset that the document's charset is being set to. Otherwise
+     * it's empty.
+     */
     nsString mCharsetOrCrossOrigin;
-    nsString mType;
+    /**
+     * If mOpCode is eSpeculativeLoadSetDocumentCharset, this is a
+     * one-character string whose single character's code point is to be
+     * interpreted as a charset source integer. Otherwise, it is empty or
+     * the value of the type attribute.
+     */
+    nsString mTypeOrCharsetSource;
 };
 
 #endif // nsHtml5SpeculativeLoad_h_
--- a/parser/html/nsHtml5TreeBuilderCppSupplement.h
+++ b/parser/html/nsHtml5TreeBuilderCppSupplement.h
@@ -638,19 +638,18 @@ nsHtml5TreeBuilder::FlushLoads()
     mSpeculativeLoadStage->MoveSpeculativeLoadsFrom(mSpeculativeLoadQueue);
   }
 }
 
 void
 nsHtml5TreeBuilder::SetDocumentCharset(nsACString& aCharset, 
                                        PRInt32 aCharsetSource)
 {
-  nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
-  NS_ASSERTION(treeOp, "Tree op allocation failed.");
-  treeOp->Init(eTreeOpSetDocumentCharset, aCharset, aCharsetSource);  
+  mSpeculativeLoadQueue.AppendElement()->InitSetDocumentCharset(aCharset,
+                                                                aCharsetSource);
 }
 
 void
 nsHtml5TreeBuilder::StreamEnded()
 {
   // The fragment mode calls DidBuildModel from nsHtml5Parser. 
   // Letting DidBuildModel be called from the executor in the fragment case
   // confuses the EndLoad logic of nsHTMLDocument, since nsHTMLDocument
--- a/parser/html/nsHtml5TreeOperation.cpp
+++ b/parser/html/nsHtml5TreeOperation.cpp
@@ -122,17 +122,16 @@ nsHtml5TreeOperation::~nsHtml5TreeOperat
       delete mTwo.stringPair;
       break;
     case eTreeOpFosterParentText:
     case eTreeOpAppendText:
     case eTreeOpAppendComment:
     case eTreeOpAppendCommentToDocument:
       delete[] mTwo.unicharPtr;
       break;
-    case eTreeOpSetDocumentCharset:
     case eTreeOpNeedsCharsetSwitchTo:
       delete[] mOne.charPtr;
       break;
     case eTreeOpProcessOfflineManifest:
       nsMemory::Free(mOne.unicharPtr);
       break;
     default: // keep the compiler happy
       break;
@@ -625,23 +624,16 @@ nsHtml5TreeOperation::Perform(nsHtml5Tre
       nsIContent* node = *(mOne.node);
       node->DoneCreatingElement();
       return rv;
     }
     case eTreeOpFlushPendingAppendNotifications: {
       aBuilder->FlushPendingAppendNotifications();
       return rv;
     }
-    case eTreeOpSetDocumentCharset: {
-      char* str = mOne.charPtr;
-      PRInt32 charsetSource = mInt;
-      nsDependentCString dependentString(str);
-      aBuilder->SetDocumentCharsetAndSource(dependentString, charsetSource);
-      return rv;
-    }
     case eTreeOpNeedsCharsetSwitchTo: {
       char* str = mOne.charPtr;
       PRInt32 charsetSource = mInt;
       aBuilder->NeedsCharsetSwitchTo(str, charsetSource);
       return rv;    
     }
     case eTreeOpUpdateStyleSheet: {
       nsIContent* node = *(mOne.node);
--- a/parser/html/nsHtml5TreeOperation.h
+++ b/parser/html/nsHtml5TreeOperation.h
@@ -68,17 +68,16 @@ enum eHtml5TreeOperation {
   eTreeOpAppendCommentToDocument,
   eTreeOpAppendDoctypeToDocument,
   // Gecko-specific on-pop ops
   eTreeOpRunScript,
   eTreeOpRunScriptAsyncDefer,
   eTreeOpDoneAddingChildren,
   eTreeOpDoneCreatingElement,
   eTreeOpFlushPendingAppendNotifications,
-  eTreeOpSetDocumentCharset,
   eTreeOpNeedsCharsetSwitchTo,
   eTreeOpUpdateStyleSheet,
   eTreeOpProcessMeta,
   eTreeOpProcessOfflineManifest,
   eTreeOpMarkMalformedIfScript,
   eTreeOpStreamEnded,
   eTreeOpSetStyleLineNumber,
   eTreeOpSetScriptLineNumberAndFreeze,
--- a/toolkit/components/feeds/Makefile.in
+++ b/toolkit/components/feeds/Makefile.in
@@ -37,16 +37,20 @@
 
 DEPTH = ../../..
 topsrcdir = @top_srcdir@
 srcdir = @srcdir@
 VPATH = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
+ifdef ENABLE_TESTS
+TOOL_DIRS += test
+endif
+
 MODULE = feeds
 LIBRARY_NAME = feed_s
 MOZILLA_INTERNAL_API = 1
 FORCE_STATIC_LIB = 1
 LIBXUL_LIBRARY = 1
 
 XPIDLSRCS = \
   nsIFeed.idl \
--- a/toolkit/components/feeds/nsScriptableUnescapeHTML.cpp
+++ b/toolkit/components/feeds/nsScriptableUnescapeHTML.cpp
@@ -183,29 +183,31 @@ nsScriptableUnescapeHTML::ParseFragment(
       fragment = do_QueryInterface(*aReturn);
       nsContentUtils::ParseFragmentHTML(aFragment,
                                         fragment,
                                         nsGkAtoms::body,
                                         kNameSpaceID_XHTML,
                                         PR_FALSE,
                                         PR_TRUE);
       // Now, set the base URI on all subtree roots.
-      aBaseURI->GetSpec(spec);
-      nsAutoString spec16;
-      CopyUTF8toUTF16(spec, spec16);
-      nsIContent* node = fragment->GetFirstChild();
-      while (node) {
-        if (node->IsElement()) {
-          node->SetAttr(kNameSpaceID_XML,
-                        nsGkAtoms::base,
-                        nsGkAtoms::xml,
-                        spec16,
-                        PR_FALSE);
+      if (aBaseURI) {
+        aBaseURI->GetSpec(spec);
+        nsAutoString spec16;
+        CopyUTF8toUTF16(spec, spec16);
+        nsIContent* node = fragment->GetFirstChild();
+        while (node) {
+          if (node->IsElement()) {
+            node->SetAttr(kNameSpaceID_XML,
+                          nsGkAtoms::base,
+                          nsGkAtoms::xml,
+                          spec16,
+                          PR_FALSE);
+          }
+          node = node->GetNextSibling();
         }
-        node = node->GetNextSibling();
       }
     }
     if (fragment) {
       nsTreeSanitizer sanitizer(PR_FALSE, PR_FALSE);
       sanitizer.Sanitize(fragment);
     }
   }
 
new file mode 100644
--- /dev/null
+++ b/toolkit/components/feeds/test/Makefile.in
@@ -0,0 +1,52 @@
+#
+# ***** 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 mozilla.org code.
+#
+# The Initial Developer of the Original Code is
+# Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2009
+# 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 *****
+
+DEPTH		= ../../../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+relativesrcdir  = toolkit/components/feeds/test
+
+include $(DEPTH)/config/autoconf.mk
+include $(topsrcdir)/config/rules.mk
+
+_TEST_FILES = \
+		test_bug675492.xul    \
+		$(NULL)
+
+libs:: $(_TEST_FILES)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/toolkit/components/feeds/test/test_bug675492.xul
@@ -0,0 +1,32 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=675492
+-->
+<window title="Mozilla Bug 675492"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript" src="chrome://mochikit/content/MochiKit/packed.js"/>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+  <script type="application/javascript" src="chrome://mochikit/content/chrome-harness.js"/>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=675492"
+     target="_blank">Mozilla Bug 675492</a>
+  </body>
+
+  <!-- test code goes here -->
+  <script type="application/javascript">
+  <![CDATA[
+
+  /** Test for Bug 675492 **/
+
+  Components
+    .classes["@mozilla.org/feed-unescapehtml;1"]
+    .getService(Components.interfaces.nsIScriptableUnescapeHTML)
+    .parseFragment("<p>test</p>", false, null, document.createElementNS("http://www.w3.org/1999/xhtml", "body"));
+  ok(true, "No crash!");
+  ]]>
+  </script>
+</window>
--- a/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator.h
+++ b/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator.h
@@ -38,17 +38,17 @@
 
 #include "client/minidump_file_writer.h"
 #include "common/memory.h"
 #include "common/mac/macho_utilities.h"
 #include "google_breakpad/common/minidump_format.h"
 
 #include "dynamic_images.h"
 
-#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7
+#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
   #define HAS_PPC_SUPPORT
 #endif
 
 namespace google_breakpad {
 
 using std::string;
 
 const u_int64_t TOP_OF_THREAD0_STACK_64BIT = 0x00007fff5fbff000LL;