Bug 534566. Use separate nsPropertyTables for different categories so we don't pollute the property name list with unrelated names. r=sicking
authorRobert O'Callahan <robert@ocallahan.org>
Fri, 23 Apr 2010 14:41:38 +1200
changeset 41182 ca34c53987b070ab52098b2241973e7baecd8070
parent 41181 4b48094881e12b820918dc5c4130a55c8de94950
child 41183 7432d20f5b86f018824ebc40286a8eaf60c1ee89
push id12938
push userrocallahan@mozilla.com
push dateFri, 23 Apr 2010 03:36:56 +0000
treeherdermozilla-central@a9ec36359a4c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssicking
bugs534566
milestone1.9.3a5pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
Bug 534566. Use separate nsPropertyTables for different categories so we don't pollute the property name list with unrelated names. r=sicking
content/base/public/nsIDocument.h
content/base/public/nsINode.h
content/base/src/nsDOMAttribute.cpp
content/base/src/nsDocument.cpp
content/base/src/nsGenericElement.cpp
content/base/src/nsNodeUtils.cpp
content/base/src/nsPropertyTable.cpp
content/base/src/nsPropertyTable.h
content/svg/content/src/nsSVGElement.cpp
layout/generic/nsGfxScrollFrame.cpp
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -811,17 +811,26 @@ public:
   }
 
   /**
    * Returns the default namespace ID used for elements created in this
    * document.
    */
   virtual PRInt32 GetDefaultNamespaceID() const = 0;
 
-  nsPropertyTable* PropertyTable() { return &mPropertyTable; }
+  void DeleteAllProperties();
+  void DeleteAllPropertiesFor(nsINode* aNode);
+
+  nsPropertyTable* PropertyTable(PRUint16 aCategory) {
+    if (aCategory == 0)
+      return &mPropertyTable;
+    return GetExtraPropertyTable(aCategory);
+  }
+  PRUint32 GetPropertyTableCount()
+  { return mExtraPropertyTables.Length() + 1; }
 
   /**
    * Sets the ID used to identify this part of the multipart document
    */
   void SetPartID(PRUint32 aID) {
     mPartID = aID;
   }
 
@@ -1351,16 +1360,18 @@ protected:
   ~nsIDocument()
   {
     // XXX The cleanup of mNodeInfoManager (calling DropDocumentReference and
     //     releasing it) happens in the nsDocument destructor. We'd prefer to
     //     do it here but nsNodeInfoManager is a concrete class that we don't
     //     want to expose to users of the nsIDocument API outside of Gecko.
   }
 
+  nsPropertyTable* GetExtraPropertyTable(PRUint16 aCategory);
+
   // Never ever call this. Only call GetInnerWindow!
   virtual nsPIDOMWindow *GetInnerWindowInternal() = 0;
 
   /**
    * These methods should be called before and after dispatching
    * a mutation event.
    * To make this easy and painless, use the mozAutoSubtreeModified helper class.
    */
@@ -1400,16 +1411,17 @@ protected:
 
 #ifdef MOZ_SMIL
   // SMIL Animation Controller, lazily-initialized in GetAnimationController
   nsRefPtr<nsSMILAnimationController> mAnimationController;
 #endif // MOZ_SMIL
 
   // Table of element properties for this document.
   nsPropertyTable mPropertyTable;
+  nsTArray<nsAutoPtr<nsPropertyTable> > mExtraPropertyTables;
 
   // Compatibility mode
   nsCompatibility mCompatMode;
 
   // True if BIDI is enabled.
   PRPackedBool mBidiEnabled;
   // True if a MathML element has ever been owned by this document.
   PRPackedBool mMathMLEnabled;
--- a/content/base/public/nsINode.h
+++ b/content/base/public/nsINode.h
@@ -244,16 +244,24 @@ private:
 
   
   // sMutationCount is a global mutation counter which is decreased by one at
   // every mutation. It is capped at 0 to avoid wrapping.
   // Its value is always between 0 and 300, inclusive.
   static PRUint32 sMutationCount;
 };
 
+// Categories of node properties
+// 0 is global.
+#define DOM_USER_DATA         1
+#define DOM_USER_DATA_HANDLER 2
+#ifdef MOZ_SMIL
+#define SMIL_MAPPED_ATTR_ANIMVAL 3
+#endif // MOZ_SMIL
+
 // IID for the nsINode interface
 #define NS_INODE_IID \
 { 0xbc347b50, 0xa9b8, 0x419e, \
  { 0xb1, 0x21, 0x76, 0x8f, 0x90, 0x05, 0x8d, 0xbf } } 
 
 /**
  * An internal interface that abstracts some DOMNode-related parts that both
  * nsIContent and nsIDocument share.  An instance of this interface has a list
--- a/content/base/src/nsDOMAttribute.cpp
+++ b/content/base/src/nsDOMAttribute.cpp
@@ -155,17 +155,17 @@ nsDOMAttribute::GetContent() const
 nsresult
 nsDOMAttribute::SetOwnerDocument(nsIDocument* aDocument)
 {
   NS_ASSERTION(aDocument, "Missing document");
 
   nsIDocument *doc = GetOwnerDoc();
   NS_ASSERTION(doc != aDocument, "bad call to nsDOMAttribute::SetOwnerDocument");
   if (doc) {
-    doc->PropertyTable()->DeleteAllPropertiesFor(this);
+    doc->DeleteAllPropertiesFor(this);
   }
 
   nsCOMPtr<nsINodeInfo> newNodeInfo;
   newNodeInfo = aDocument->NodeInfoManager()->
     GetNodeInfo(mNodeInfo->NameAtom(), mNodeInfo->GetPrefixAtom(),
                 mNodeInfo->NamespaceID());
   NS_ENSURE_TRUE(newNodeInfo, NS_ERROR_OUT_OF_MEMORY);
   NS_ASSERTION(newNodeInfo, "GetNodeInfo lies");
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -1781,16 +1781,42 @@ nsDocument::Init()
   NS_ASSERTION(GetOwnerDoc() == this, "Our nodeinfo is busted!");
 
   mScriptLoader = new nsScriptLoader(this);
   NS_ENSURE_TRUE(mScriptLoader, NS_ERROR_OUT_OF_MEMORY);
 
   return NS_OK;
 }
 
+void 
+nsIDocument::DeleteAllProperties()
+{
+  for (PRUint32 i = 0; i < GetPropertyTableCount(); ++i) {
+    PropertyTable(i)->DeleteAllProperties();
+  }
+}
+
+void
+nsIDocument::DeleteAllPropertiesFor(nsINode* aNode)
+{
+  for (PRUint32 i = 0; i < GetPropertyTableCount(); ++i) {
+    PropertyTable(i)->DeleteAllPropertiesFor(aNode);
+  }
+}
+
+nsPropertyTable*
+nsIDocument::GetExtraPropertyTable(PRUint16 aCategory)
+{
+  NS_ASSERTION(aCategory > 0, "Category 0 should have already been handled");
+  while (aCategory - 1 >= mExtraPropertyTables.Length()) {
+    mExtraPropertyTables.AppendElement(new nsPropertyTable());
+  }
+  return mExtraPropertyTables[aCategory - 1];
+}
+
 nsresult
 nsDocument::AddXMLEventsContent(nsIContent *aXMLEventsElement)
 {
   if (!mXMLEventsManager) {
     mXMLEventsManager = new nsXMLEventsManager();
     NS_ENSURE_TRUE(mXMLEventsManager, NS_ERROR_OUT_OF_MEMORY);
     AddObserver(mXMLEventsManager);
   }
@@ -6115,45 +6141,50 @@ nsDocument::AdoptNode(nsIDOMNode *aAdopt
   rv = nsNodeUtils::Adopt(adoptedNode, sameDocument ? nsnull : mNodeInfoManager,
                           cx, oldScope, newScope, nodesWithProperties);
   if (NS_FAILED(rv)) {
     // Disconnect all nodes from their parents, since some have the old document
     // as their ownerDocument and some have this as their ownerDocument.
     BlastSubtreeToPieces(adoptedNode);
 
     if (!sameDocument && oldDocument) {
-      PRUint32 i, count = nodesWithProperties.Count();
-      for (i = 0; i < count; ++i) {
-        // Remove all properties.
-        oldDocument->PropertyTable()->
-          DeleteAllPropertiesFor(nodesWithProperties[i]);
+      PRUint32 count = nodesWithProperties.Count();
+      for (PRUint32 j = 0; j < oldDocument->GetPropertyTableCount(); ++j) {
+        for (PRUint32 i = 0; i < count; ++i) {
+          // Remove all properties.
+          oldDocument->PropertyTable(j)->
+            DeleteAllPropertiesFor(nodesWithProperties[i]);
+        }
       }
     }
 
     return rv;
   }
 
-  PRUint32 i, count = nodesWithProperties.Count();
+  PRUint32 count = nodesWithProperties.Count();
   if (!sameDocument && oldDocument) {
-    nsPropertyTable *oldTable = oldDocument->PropertyTable();
-    nsPropertyTable *newTable = PropertyTable();
-    for (i = 0; i < count; ++i) {
-      rv = oldTable->TransferOrDeleteAllPropertiesFor(nodesWithProperties[i],
-                                                      newTable);
-      if (NS_FAILED(rv)) {
-        while (++i < count) {
+    for (PRUint32 j = 0; j < oldDocument->GetPropertyTableCount(); ++j) {
+      nsPropertyTable *oldTable = oldDocument->PropertyTable(j);
+      nsPropertyTable *newTable = PropertyTable(j);
+      for (PRUint32 i = 0; i < count; ++i) {
+        if (NS_SUCCEEDED(rv)) {
+          rv = oldTable->TransferOrDeleteAllPropertiesFor(nodesWithProperties[i],
+                                                          newTable);
+        } else {
           oldTable->DeleteAllPropertiesFor(nodesWithProperties[i]);
         }
-
-        // Disconnect all nodes from their parents.
-        BlastSubtreeToPieces(adoptedNode);
-
-        return rv;
       }
     }
+
+    if (NS_FAILED(rv)) {
+      // Disconnect all nodes from their parents.
+      BlastSubtreeToPieces(adoptedNode);
+
+      return rv;
+    }
   }
 
   rv = nsNodeUtils::CallUserDataHandlers(nodesWithProperties, this,
                                          nsIDOMUserDataHandler::NODE_ADOPTED,
                                          PR_FALSE);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (adoptedNode->GetOwnerDoc() != this) {
--- a/content/base/src/nsGenericElement.cpp
+++ b/content/base/src/nsGenericElement.cpp
@@ -204,59 +204,59 @@ nsINode::~nsINode()
 void*
 nsINode::GetProperty(PRUint16 aCategory, nsIAtom *aPropertyName,
                      nsresult *aStatus) const
 {
   nsIDocument *doc = GetOwnerDoc();
   if (!doc)
     return nsnull;
 
-  return doc->PropertyTable()->GetProperty(this, aCategory, aPropertyName,
-                                           aStatus);
+  return doc->PropertyTable(aCategory)->GetProperty(this, aPropertyName,
+                                                    aStatus);
 }
 
 nsresult
 nsINode::SetProperty(PRUint16 aCategory, nsIAtom *aPropertyName, void *aValue,
                      NSPropertyDtorFunc aDtor, PRBool aTransfer,
                      void **aOldValue)
 {
   nsIDocument *doc = GetOwnerDoc();
   if (!doc)
     return NS_ERROR_FAILURE;
 
-  nsresult rv = doc->PropertyTable()->SetProperty(this, aCategory,
-                                                  aPropertyName, aValue, aDtor,
-                                                  nsnull, aTransfer, aOldValue);
+  nsresult rv = doc->PropertyTable(aCategory)->SetProperty(this,
+                                                           aPropertyName, aValue, aDtor,
+                                                           nsnull, aTransfer, aOldValue);
   if (NS_SUCCEEDED(rv)) {
     SetFlags(NODE_HAS_PROPERTIES);
   }
 
   return rv;
 }
 
 nsresult
 nsINode::DeleteProperty(PRUint16 aCategory, nsIAtom *aPropertyName)
 {
   nsIDocument *doc = GetOwnerDoc();
   if (!doc)
     return nsnull;
 
-  return doc->PropertyTable()->DeleteProperty(this, aCategory, aPropertyName);
+  return doc->PropertyTable(aCategory)->DeleteProperty(this, aPropertyName);
 }
 
 void*
 nsINode::UnsetProperty(PRUint16 aCategory, nsIAtom *aPropertyName,
                        nsresult *aStatus)
 {
   nsIDocument *doc = GetOwnerDoc();
   if (!doc)
     return nsnull;
 
-  return doc->PropertyTable()->UnsetProperty(this, aCategory, aPropertyName,
-                                             aStatus);
+  return doc->PropertyTable(aCategory)->UnsetProperty(this, aPropertyName,
+                                                      aStatus);
 }
 
 nsIEventListenerManager*
 nsGenericElement::GetListenerManager(PRBool aCreateIfNotFound)
 {
   return nsContentUtils::GetListenerManager(this, aCreateIfNotFound);
 }
 
--- a/content/base/src/nsNodeUtils.cpp
+++ b/content/base/src/nsNodeUtils.cpp
@@ -218,25 +218,25 @@ nsNodeUtils::LastRelease(nsINode* aNode)
   }
 
   // Kill properties first since that may run external code, so we want to
   // be in as complete state as possible at that time.
   if (aNode->IsNodeOfType(nsINode::eDOCUMENT)) {
     // Delete all properties before tearing down the document. Some of the
     // properties are bound to nsINode objects and the destructor functions of
     // the properties may want to use the owner document of the nsINode.
-    static_cast<nsIDocument*>(aNode)->PropertyTable()->DeleteAllProperties();
+    static_cast<nsIDocument*>(aNode)->DeleteAllProperties();
   }
   else {
     if (aNode->HasProperties()) {
       // Strong reference to the document so that deleting properties can't
       // delete the document.
       nsCOMPtr<nsIDocument> document = aNode->GetOwnerDoc();
       if (document) {
-        document->PropertyTable()->DeleteAllPropertiesFor(aNode);
+        document->DeleteAllPropertiesFor(aNode);
       }
     }
 
     // I wonder whether it's faster to do the HasFlag check first....
     if (aNode->IsNodeOfType(nsINode::eHTML_FORM_CONTROL) &&
         aNode->HasFlag(ADDED_TO_FORM)) {
       // Tell the form (if any) this node is going away.  Don't
       // notify, since we're being destroyed in any case.
@@ -381,17 +381,17 @@ nsresult
 nsNodeUtils::CallUserDataHandlers(nsCOMArray<nsINode> &aNodesWithProperties,
                                   nsIDocument *aOwnerDocument,
                                   PRUint16 aOperation, PRBool aCloned)
 {
   NS_PRECONDITION(!aCloned || (aNodesWithProperties.Count() % 2 == 0),
                   "Expected aNodesWithProperties to contain original and "
                   "cloned nodes.");
 
-  nsPropertyTable *table = aOwnerDocument->PropertyTable();
+  nsPropertyTable *table = aOwnerDocument->PropertyTable(DOM_USER_DATA_HANDLER);
 
   // Keep the document alive, just in case one of the handlers causes it to go
   // away.
   nsCOMPtr<nsIDocument> ownerDoc = aOwnerDocument;
 
   nsHandlerData handlerData;
   handlerData.mOperation = aOperation;
 
@@ -403,18 +403,17 @@ nsNodeUtils::CallUserDataHandlers(nsCOMA
     handlerData.mSource = do_QueryInterface(nodeWithProperties, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (aCloned) {
       handlerData.mDest = do_QueryInterface(aNodesWithProperties[++i], &rv);
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
-    table->Enumerate(nodeWithProperties, DOM_USER_DATA_HANDLER, CallHandler,
-                     &handlerData);
+    table->Enumerate(nodeWithProperties, CallHandler, &handlerData);
   }
 
   return NS_OK;
 }
 
 static void
 NoteUserData(void *aObject, nsIAtom *aKey, void *aXPCOMChild, void *aData)
 {
@@ -429,20 +428,18 @@ void
 nsNodeUtils::TraverseUserData(nsINode* aNode,
                               nsCycleCollectionTraversalCallback &aCb)
 {
   nsIDocument* ownerDoc = aNode->GetOwnerDoc();
   if (!ownerDoc) {
     return;
   }
 
-  nsPropertyTable *table = ownerDoc->PropertyTable();
-
-  table->Enumerate(aNode, DOM_USER_DATA, NoteUserData, &aCb);
-  table->Enumerate(aNode, DOM_USER_DATA_HANDLER, NoteUserData, &aCb);
+  ownerDoc->PropertyTable(DOM_USER_DATA)->Enumerate(aNode, NoteUserData, &aCb);
+  ownerDoc->PropertyTable(DOM_USER_DATA_HANDLER)->Enumerate(aNode, NoteUserData, &aCb);
 }
 
 /* static */
 nsresult
 nsNodeUtils::CloneNodeImpl(nsINode *aNode, PRBool aDeep, nsIDOMNode **aResult)
 {
   *aResult = nsnull;
 
@@ -750,13 +747,12 @@ void
 nsNodeUtils::UnlinkUserData(nsINode *aNode)
 {
   NS_ASSERTION(aNode->HasProperties(), "Call to UnlinkUserData not needed.");
 
   // Strong reference to the document so that deleting properties can't
   // delete the document.
   nsCOMPtr<nsIDocument> document = aNode->GetOwnerDoc();
   if (document) {
-    document->PropertyTable()->DeleteAllPropertiesFor(aNode, DOM_USER_DATA);
-    document->PropertyTable()->DeleteAllPropertiesFor(aNode,
-                                                      DOM_USER_DATA_HANDLER);
+    document->PropertyTable(DOM_USER_DATA)->DeleteAllPropertiesFor(aNode);
+    document->PropertyTable(DOM_USER_DATA_HANDLER)->DeleteAllPropertiesFor(aNode);
   }
 }
--- a/content/base/src/nsPropertyTable.cpp
+++ b/content/base/src/nsPropertyTable.cpp
@@ -61,40 +61,38 @@ struct PropertyListMapEntry : public PLD
   const void  *key;
   void        *value;
 };
 
 //----------------------------------------------------------------------
 
 class nsPropertyTable::PropertyList {
 public:
-  PropertyList(PRUint16           aCategory,
-               nsIAtom*           aName,
+  PropertyList(nsIAtom*           aName,
                NSPropertyDtorFunc aDtorFunc,
                void*              aDtorData,
                PRBool             aTransfer) NS_HIDDEN;
   ~PropertyList() NS_HIDDEN;
 
   // Removes the property associated with the given object, and destroys
   // the property value
   NS_HIDDEN_(PRBool) DeletePropertyFor(nsPropertyOwner aObject);
 
   // Destroy all remaining properties (without removing them)
   NS_HIDDEN_(void) Destroy();
 
-  NS_HIDDEN_(PRBool) Equals(PRUint16 aCategory, nsIAtom *aPropertyName)
+  NS_HIDDEN_(PRBool) Equals(nsIAtom *aPropertyName)
   {
-    return mCategory == aCategory && mName == aPropertyName;
+    return mName == aPropertyName;
   }
 
   nsCOMPtr<nsIAtom>  mName;           // property name
   PLDHashTable       mObjectValueMap; // map of object/value pairs
   NSPropertyDtorFunc mDtorFunc;       // property specific value dtor function
   void*              mDtorData;       // pointer to pass to dtor
-  PRUint16           mCategory;       // category
   PRPackedBool       mTransfer;       // whether to transfer in
                                       // TransferOrDeleteAllPropertiesFor
   
   PropertyList*      mNext;
 };
 
 void
 nsPropertyTable::DeleteAllProperties()
@@ -111,38 +109,28 @@ nsPropertyTable::DeleteAllProperties()
 void
 nsPropertyTable::DeleteAllPropertiesFor(nsPropertyOwner aObject)
 {
   for (PropertyList* prop = mPropertyList; prop; prop = prop->mNext) {
     prop->DeletePropertyFor(aObject);
   }
 }
 
-void
-nsPropertyTable::DeleteAllPropertiesFor(nsPropertyOwner aObject,
-                                        PRUint16 aCategory)
-{
-  for (PropertyList* prop = mPropertyList; prop; prop = prop->mNext) {
-    if (prop->mCategory == aCategory)
-      prop->DeletePropertyFor(aObject);
-  }
-}
-
 nsresult
 nsPropertyTable::TransferOrDeleteAllPropertiesFor(nsPropertyOwner aObject,
                                                   nsPropertyTable *aOtherTable)
 {
   nsresult rv = NS_OK;
   for (PropertyList* prop = mPropertyList; prop; prop = prop->mNext) {
     if (prop->mTransfer) {
       PropertyListMapEntry *entry = static_cast<PropertyListMapEntry*>
                                                (PL_DHashTableOperate(&prop->mObjectValueMap, aObject,
                                PL_DHASH_LOOKUP));
       if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
-        rv = aOtherTable->SetProperty(aObject, prop->mCategory, prop->mName,
+        rv = aOtherTable->SetProperty(aObject, prop->mName,
                                       entry->value, prop->mDtorFunc,
                                       prop->mDtorData, prop->mTransfer);
         if (NS_FAILED(rv)) {
           DeleteAllPropertiesFor(aObject);
           aOtherTable->DeleteAllPropertiesFor(aObject);
 
           break;
         }
@@ -154,45 +142,41 @@ nsPropertyTable::TransferOrDeleteAllProp
       prop->DeletePropertyFor(aObject);
     }
   }
 
   return rv;
 }
 
 void
-nsPropertyTable::Enumerate(nsPropertyOwner aObject, PRUint16 aCategory,
+nsPropertyTable::Enumerate(nsPropertyOwner aObject,
                            NSPropertyFunc aCallback, void *aData)
 {
   PropertyList* prop;
   for (prop = mPropertyList; prop; prop = prop->mNext) {
-    if (prop->mCategory == aCategory) {
-      PropertyListMapEntry *entry = static_cast<PropertyListMapEntry*>
-                                               (PL_DHashTableOperate(&prop->mObjectValueMap, aObject,
-                               PL_DHASH_LOOKUP));
-      if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
-        aCallback(const_cast<void*>(aObject.get()), prop->mName, entry->value,
-                  aData);
-      }
+    PropertyListMapEntry *entry = static_cast<PropertyListMapEntry*>
+      (PL_DHashTableOperate(&prop->mObjectValueMap, aObject, PL_DHASH_LOOKUP));
+    if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
+      aCallback(const_cast<void*>(aObject.get()), prop->mName, entry->value,
+                 aData);
     }
   }
 }
 
 void*
 nsPropertyTable::GetPropertyInternal(nsPropertyOwner aObject,
-                                     PRUint16    aCategory,
                                      nsIAtom    *aPropertyName,
                                      PRBool      aRemove,
                                      nsresult   *aResult)
 {
   NS_PRECONDITION(aPropertyName && aObject, "unexpected null param");
   nsresult rv = NS_PROPTABLE_PROP_NOT_THERE;
   void *propValue = nsnull;
 
-  PropertyList* propertyList = GetPropertyListFor(aCategory, aPropertyName);
+  PropertyList* propertyList = GetPropertyListFor(aPropertyName);
   if (propertyList) {
     PropertyListMapEntry *entry = static_cast<PropertyListMapEntry*>
                                              (PL_DHashTableOperate(&propertyList->mObjectValueMap, aObject,
                              PL_DHASH_LOOKUP));
     if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
       propValue = entry->value;
       if (aRemove) {
         // don't call propertyList->mDtorFunc.  That's the caller's job now.
@@ -205,39 +189,38 @@ nsPropertyTable::GetPropertyInternal(nsP
   if (aResult)
     *aResult = rv;
 
   return propValue;
 }
 
 nsresult
 nsPropertyTable::SetPropertyInternal(nsPropertyOwner     aObject,
-                                     PRUint16            aCategory,
                                      nsIAtom            *aPropertyName,
                                      void               *aPropertyValue,
                                      NSPropertyDtorFunc  aPropDtorFunc,
                                      void               *aPropDtorData,
                                      PRBool              aTransfer,
                                      void              **aOldValue)
 {
   NS_PRECONDITION(aPropertyName && aObject, "unexpected null param");
 
-  PropertyList* propertyList = GetPropertyListFor(aCategory, aPropertyName);
+  PropertyList* propertyList = GetPropertyListFor(aPropertyName);
 
   if (propertyList) {
     // Make sure the dtor function and data and the transfer flag match
     if (aPropDtorFunc != propertyList->mDtorFunc ||
         aPropDtorData != propertyList->mDtorData ||
         aTransfer != propertyList->mTransfer) {
       NS_WARNING("Destructor/data mismatch while setting property");
       return NS_ERROR_INVALID_ARG;
     }
 
   } else {
-    propertyList = new PropertyList(aCategory, aPropertyName, aPropDtorFunc,
+    propertyList = new PropertyList(aPropertyName, aPropDtorFunc,
                                     aPropDtorData, aTransfer);
     if (!propertyList || !propertyList->mObjectValueMap.ops) {
       delete propertyList;
       return NS_ERROR_OUT_OF_MEMORY;
     }
 
     propertyList->mNext = mPropertyList;
     mPropertyList = propertyList;
@@ -266,56 +249,52 @@ nsPropertyTable::SetPropertyInternal(nsP
   entry->key = aObject;
   entry->value = aPropertyValue;
 
   return result;
 }
 
 nsresult
 nsPropertyTable::DeleteProperty(nsPropertyOwner aObject,
-                                PRUint16    aCategory,
                                 nsIAtom    *aPropertyName)
 {
   NS_PRECONDITION(aPropertyName && aObject, "unexpected null param");
 
-  PropertyList* propertyList = GetPropertyListFor(aCategory, aPropertyName);
+  PropertyList* propertyList = GetPropertyListFor(aPropertyName);
   if (propertyList) {
     if (propertyList->DeletePropertyFor(aObject))
       return NS_OK;
   }
 
   return NS_PROPTABLE_PROP_NOT_THERE;
 }
 
 nsPropertyTable::PropertyList*
-nsPropertyTable::GetPropertyListFor(PRUint16 aCategory,
-                                    nsIAtom* aPropertyName) const
+nsPropertyTable::GetPropertyListFor(nsIAtom* aPropertyName) const
 {
   PropertyList* result;
 
   for (result = mPropertyList; result; result = result->mNext) {
-    if (result->Equals(aCategory, aPropertyName)) {
+    if (result->Equals(aPropertyName)) {
       break;
     }
   }
 
   return result;
 }
 
 //----------------------------------------------------------------------
     
-nsPropertyTable::PropertyList::PropertyList(PRUint16            aCategory,
-                                            nsIAtom            *aName,
+nsPropertyTable::PropertyList::PropertyList(nsIAtom            *aName,
                                             NSPropertyDtorFunc  aDtorFunc,
                                             void               *aDtorData,
                                             PRBool              aTransfer)
   : mName(aName),
     mDtorFunc(aDtorFunc),
     mDtorData(aDtorData),
-    mCategory(aCategory),
     mTransfer(aTransfer),
     mNext(nsnull)
 {
   PL_DHashTableInit(&mObjectValueMap, PL_DHashGetStubOps(), this,
                     sizeof(PropertyListMapEntry), 16);
 }
 
 nsPropertyTable::PropertyList::~PropertyList()
--- a/content/base/src/nsPropertyTable.h
+++ b/content/base/src/nsPropertyTable.h
@@ -89,49 +89,32 @@ public:
 
   operator const void*() { return mObject; }
   const void* get() { return mObject; }
 
 private:
   const void* mObject;
 };
 
-// Categories of properties
-// 0 is global.
-#define DOM_USER_DATA         1
-#define DOM_USER_DATA_HANDLER 2
-#ifdef MOZ_SMIL
-#define SMIL_MAPPED_ATTR_ANIMVAL 3
-#endif // MOZ_SMIL
-
 class nsPropertyTable
 {
  public:
   /**
    * Get the value of the property |aPropertyName| for node |aObject|.
    * |aResult|, if supplied, is filled in with a return status code.
    **/
   void* GetProperty(nsPropertyOwner aObject,
                     nsIAtom    *aPropertyName,
                     nsresult   *aResult = nsnull)
   {
-    return GetPropertyInternal(aObject, 0, aPropertyName, PR_FALSE, aResult);
-  }
-
-  void* GetProperty(nsPropertyOwner aObject,
-                    PRUint16    aCategory,
-                    nsIAtom    *aPropertyName,
-                    nsresult   *aResult = nsnull)
-  {
-    return GetPropertyInternal(aObject, aCategory, aPropertyName, PR_FALSE,
-                               aResult);
+    return GetPropertyInternal(aObject, aPropertyName, PR_FALSE, aResult);
   }
 
   /**
-   * Set the value of the property |aPropertyName| in the global category to
+   * Set the value of the property |aPropertyName| to
    * |aPropertyValue| for node |aObject|.  |aDtor| is a destructor for the
    * property value to be called if the property is removed.  It can be null
    * if no destructor is required.  |aDtorData| is an optional pointer to an
    * opaque context to be passed to the property destructor.  Note that the
    * destructor is global for each property name regardless of node; it is an
    * error to set a given property with a different destructor than was used
    * before (this will return NS_ERROR_INVALID_ARG). If aOldValue is non-null
    * it will contain the old value after the function returns (the destructor
@@ -145,158 +128,93 @@ class nsPropertyTable
   NS_HIDDEN_(nsresult) SetProperty(nsPropertyOwner     aObject,
                                    nsIAtom            *aPropertyName,
                                    void               *aPropertyValue,
                                    NSPropertyDtorFunc  aDtor,
                                    void               *aDtorData,
                                    PRBool              aTransfer = PR_FALSE,
                                    void              **aOldValue = nsnull)
   {
-    return SetPropertyInternal(aObject, 0, aPropertyName, aPropertyValue,
+    return SetPropertyInternal(aObject, aPropertyName, aPropertyValue,
                                aDtor, aDtorData, aTransfer, aOldValue);
   }
 
   /**
-   * Set the value of the property |aPropertyName| in the category |aCategory|
-   * to |aPropertyValue| for node |aObject|.  |aDtor| is a destructor for the
-   * property value to be called if the property is removed.  It can be null
-   * if no destructor is required.  |aDtorData| is an optional pointer to an
-   * opaque context to be passed to the property destructor.  Note that the
-   * destructor is global for  each property name regardless of node; it is an
-   * error to set a given property with a different destructor than was used
-   * before (this will return NS_ERROR_INVALID_ARG). If aOldValue is non-null
-   * it will contain the old value after the function returns (the destructor
-   * for the old value will not be run in that case). If aTransfer is PR_TRUE
-   * the property will be transfered to the new table when the property table
-   * for |aObject| changes (currently the tables for nodes are owned by their
-   * ownerDocument, so if the ownerDocument for a node changes, its property
-   * table changes too). If |aTransfer| is PR_FALSE the property will just be
-   * deleted instead.
-   */
-  NS_HIDDEN_(nsresult) SetProperty(nsPropertyOwner     aObject,
-                                   PRUint16            aCategory,
-                                   nsIAtom            *aPropertyName,
-                                   void               *aPropertyValue,
-                                   NSPropertyDtorFunc  aDtor,
-                                   void               *aDtorData,
-                                   PRBool              aTransfer = PR_FALSE,
-                                   void              **aOldValue = nsnull)
-  {
-    return SetPropertyInternal(aObject, aCategory, aPropertyName,
-                               aPropertyValue, aDtor, aDtorData, aTransfer,
-                               aOldValue);
-  }
-
-  /**
    * Delete the property |aPropertyName| in the global category for object
    * |aObject|. The property's destructor function will be called.
    */
   NS_HIDDEN_(nsresult) DeleteProperty(nsPropertyOwner aObject,
-                                      nsIAtom    *aPropertyName)
-  {
-    return DeleteProperty(aObject, 0, aPropertyName);
-  }
-
-  /**
-   * Delete the property |aPropertyName| in category |aCategory| for object
-   * |aObject|. The property's destructor function will be called.
-   */
-  NS_HIDDEN_(nsresult) DeleteProperty(nsPropertyOwner aObject,
-                                      PRUint16    aCategory,
                                       nsIAtom    *aPropertyName);
 
   /**
    * Unset the property |aPropertyName| in the global category for object
    * |aObject|, but do not call the property's destructor function.  The
    * property value is returned.
    */
   void* UnsetProperty(nsPropertyOwner aObject,
                       nsIAtom    *aPropertyName,
                       nsresult   *aStatus = nsnull)
   {
-    return GetPropertyInternal(aObject, 0, aPropertyName, PR_TRUE, aStatus);
-  }
-
-  /**
-   * Unset the property |aPropertyName| in category |aCategory| for object
-   * |aObject|, but do not call the property's destructor function.  The
-   * property value is returned.
-   */
-  void* UnsetProperty(nsPropertyOwner aObject,
-                      PRUint16    aCategory,
-                      nsIAtom    *aPropertyName,
-                      nsresult   *aStatus = nsnull)
-  {
-    return GetPropertyInternal(aObject, aCategory, aPropertyName, PR_TRUE,
-                               aStatus);
+    return GetPropertyInternal(aObject, aPropertyName, PR_TRUE, aStatus);
   }
 
   /**
    * Deletes all of the properties for object |aObject|, calling the
    * destructor function for each property.
    */
   NS_HIDDEN_(void) DeleteAllPropertiesFor(nsPropertyOwner aObject);
 
   /**
-   * Deletes all of the properties in category |aCategory| for object |aObject|,
-   * calling the destructor function for each property.
-   */
-  NS_HIDDEN_(void) DeleteAllPropertiesFor(nsPropertyOwner aObject,
-                                          PRUint16 aCategory);
-
-  /**
    * Transfers all properties for object |aObject| that were set with the
    * |aTransfer| argument as PR_TRUE to |aTable|. Deletes the other properties
    * for object |aObject|, calling the destructor function for each property.
    * If transfering a property fails, this deletes all the properties for
    * object |aObject|.
    */
   NS_HIDDEN_(nsresult)
     TransferOrDeleteAllPropertiesFor(nsPropertyOwner aObject,
                                      nsPropertyTable *aOtherTable);
 
   /**
-   * Enumerate the properties in category |aCategory| for object |aObject|.
+   * Enumerate the properties for object |aObject|.
    * For every property |aCallback| will be called with as arguments |aObject|,
    * the property name, the property value and |aData|.
    */
-  NS_HIDDEN_(void) Enumerate(nsPropertyOwner aObject, PRUint16 aCategory,
+  NS_HIDDEN_(void) Enumerate(nsPropertyOwner aObject,
                              NSPropertyFunc aCallback, void *aData);
 
   /**
    * Deletes all of the properties for all objects in the property
    * table, calling the destructor function for each property.
    */
   NS_HIDDEN_(void) DeleteAllProperties();
-  
+
+  nsPropertyTable() : mPropertyList(nsnull) {}  
   ~nsPropertyTable() {
     DeleteAllProperties();
   }
 
   /**
    * Function useable as destructor function for property data that is
    * XPCOM objects. The function will call NS_IF_RELASE on the value
    * to destroy it.
    */
   static void SupportsDtorFunc(void *aObject, nsIAtom *aPropertyName,
                                void *aPropertyValue, void *aData);
 
   class PropertyList;
 
  private:
   NS_HIDDEN_(void) DestroyPropertyList();
-  NS_HIDDEN_(PropertyList*) GetPropertyListFor(PRUint16 aCategory,
-                                               nsIAtom *aPropertyName) const;
+  NS_HIDDEN_(PropertyList*) GetPropertyListFor(nsIAtom *aPropertyName) const;
   NS_HIDDEN_(void*) GetPropertyInternal(nsPropertyOwner aObject,
-                                        PRUint16    aCategory,
                                         nsIAtom    *aPropertyName,
                                         PRBool      aRemove,
                                         nsresult   *aStatus);
   NS_HIDDEN_(nsresult) SetPropertyInternal(nsPropertyOwner     aObject,
-                                           PRUint16            aCategory,
                                            nsIAtom            *aPropertyName,
                                            void               *aPropertyValue,
                                            NSPropertyDtorFunc  aDtor,
                                            void               *aDtorData,
                                            PRBool              aTransfer,
                                            void              **aOldValue);
 
   PropertyList *mPropertyList;
--- a/content/svg/content/src/nsSVGElement.cpp
+++ b/content/svg/content/src/nsSVGElement.cpp
@@ -1245,19 +1245,18 @@ nsSVGElement::UpdateAnimatedContentStyle
   nsIDocument* doc = GetOwnerDoc();
   if (!doc) {
     NS_ERROR("SVG element without owner document");
     return;
   }
 
   MappedAttrParser mappedAttrParser(doc->CSSLoader(), doc->GetDocumentURI(),
                                     GetBaseURI(), NodePrincipal());
-  doc->PropertyTable()->Enumerate(this, SMIL_MAPPED_ATTR_ANIMVAL,
-                                  ParseMappedAttrAnimValueCallback,
-                                  &mappedAttrParser);
+  doc->PropertyTable(SMIL_MAPPED_ATTR_ANIMVAL)->
+    Enumerate(this, ParseMappedAttrAnimValueCallback, &mappedAttrParser);
  
   nsRefPtr<nsICSSStyleRule>
     animContentStyleRule(mappedAttrParser.CreateStyleRule());
 
   if (animContentStyleRule) {
     nsresult rv = SetProperty(SMIL_MAPPED_ATTR_ANIMVAL,
                               SMIL_MAPPED_ATTR_STYLERULE_ATOM,
                               animContentStyleRule.get(), ReleaseStyleRule);
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -1615,28 +1615,44 @@ SortBlitRectsForCopy(nsIntPoint aPixDelt
     // Rectangle i has no rectangles to the right or below.
     // Flip it back before saving the result.
     aRects->AppendElement(FlipRect(rects[i], aPixDelta));
     rects.RemoveElementAt(i);
   }
 }
 
 static PRBool
-CanScrollWithBlitting(nsIFrame* aFrame)
+CanScrollWithBlitting(nsIFrame* aFrame, nsIFrame* aDisplayRoot, nsRect* aClippedScrollPort)
 {
-  for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetCrossDocParentFrame(f)) {
+  nsPoint offset(0, 0);
+  *aClippedScrollPort = nsRect(nsPoint(0, 0), aFrame->GetSize());
+
+  for (nsIFrame* f = aFrame; f;
+       f = nsLayoutUtils::GetCrossDocParentFrame(f, &offset)) {
     if (f->GetStyleDisplay()->HasTransform()) {
       return PR_FALSE;
     }
 #ifdef MOZ_SVG
     if (nsSVGIntegrationUtils::UsingEffectsForFrame(f) ||
         f->IsFrameOfType(nsIFrame::eSVG)) {
       return PR_FALSE;
     }
 #endif
+
+    nsIScrollableFrame* sf = do_QueryFrame(f);
+    if (sf) {
+      // Note that this will always happen on the first iteration of the loop,
+      // ensuring that we clip to our own scrollframe's scrollport
+      aClippedScrollPort->IntersectRect(*aClippedScrollPort,
+                                        sf->GetScrollPortRect() - offset);
+    }
+
+    offset += f->GetPosition();
+    if (f == aDisplayRoot)
+      break;
   }
   return PR_TRUE;
 }
 
 void nsGfxScrollFrameInner::ScrollVisual(nsIntPoint aPixDelta)
 {
   nsRootPresContext* rootPresContext =
     mOuter->PresContext()->GetRootPresContext();
@@ -1653,40 +1669,41 @@ void nsGfxScrollFrameInner::ScrollVisual
   nsTArray<nsIWidget::Configuration> configurations;
   // Only update plugin configurations if we're going to scroll the
   // root widget. Otherwise we must be in a popup or some other situation
   // where we don't actually support windows plugins.
   if (rootPresContext->FrameManager()->GetRootFrame()->GetWindow() == nearestWidget) {
     rootPresContext->GetPluginGeometryUpdates(mOuter, &configurations);
   }
 
+  nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(mOuter);
+  nsRect clippedScrollPort;
   if (!nearestWidget ||
       nearestWidget->GetTransparencyMode() == eTransparencyTransparent ||
-      !CanScrollWithBlitting(mOuter)) {
+      !CanScrollWithBlitting(mOuter, displayRoot, &clippedScrollPort)) {
     // Just invalidate the frame and adjust child widgets
     // Recall that our widget's origin is at our bounds' top-left
     if (nearestWidget) {
       nearestWidget->ConfigureChildren(configurations);
     }
     AdjustViewsAndWidgets(mScrolledFrame, PR_FALSE);
     // We need to call this after fixing up the widget and view positions
     // to be consistent with the view and frame hierarchy.
     mOuter->InvalidateWithFlags(mScrollPort,
                                 nsIFrame::INVALIDATE_REASON_SCROLL_REPAINT);
   } else {
-    nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(mOuter);
     nsRegion blitRegion;
     nsRegion repaintRegion;
     nsPresContext* presContext = mOuter->PresContext();
     nsPoint delta(presContext->DevPixelsToAppUnits(aPixDelta.x),
                   presContext->DevPixelsToAppUnits(aPixDelta.y));
     nsPoint offsetToDisplayRoot = mOuter->GetOffsetTo(displayRoot);
     nscoord appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
     nsRect scrollPort =
-      (mScrollPort + offsetToDisplayRoot).ToNearestPixels(appUnitsPerDevPixel).
+      (clippedScrollPort + offsetToDisplayRoot).ToNearestPixels(appUnitsPerDevPixel).
       ToAppUnits(appUnitsPerDevPixel);
     nsresult rv =
       nsLayoutUtils::ComputeRepaintRegionForCopy(displayRoot, mScrolledFrame,
                                                  delta, scrollPort,
                                                  &blitRegion,
                                                  &repaintRegion);
     if (NS_FAILED(rv)) {
       nearestWidget->ConfigureChildren(configurations);