Bug 238072 backout
authorRobert O'Callahan <robert@ocallahan.org>
Thu, 07 Aug 2008 22:22:08 +1200
changeset 16477 90a8c3f3899bca9c065b66ebba7b017372f6813f
parent 16476 b7bcbaf5bef2549e2c2fe5f0dbeeed7edfc848ab
child 16478 aa0cb953b8f70cd0bb67035c5ef53bb88143fc85
push idunknown
push userunknown
push dateunknown
bugs238072
milestone1.9.1a2pre
Bug 238072 backout
content/base/src/nsGenConImageContent.cpp
content/base/src/nsGkAtomList.h
content/base/src/nsTextNode.cpp
layout/base/crashtests/crashtests.list
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsCSSFrameConstructor.h
layout/base/nsCounterManager.cpp
layout/base/nsCounterManager.h
layout/base/nsGenConList.cpp
layout/base/nsGenConList.h
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
layout/base/nsQuoteList.cpp
layout/base/nsQuoteList.h
layout/generic/nsHTMLParts.h
layout/generic/nsTextFrameThebes.cpp
layout/reftests/bugs/374193-1xbl.xml
layout/reftests/bugs/380842-1-ref.html
layout/reftests/bugs/reftest.list
layout/reftests/reftest.list
layout/style/nsRuleNode.cpp
--- a/content/base/src/nsGenConImageContent.cpp
+++ b/content/base/src/nsGenConImageContent.cpp
@@ -97,17 +97,17 @@ nsGenConImageContent::~nsGenConImageCont
 }
 
 PRInt32
 nsGenConImageContent::IntrinsicState() const
 {
   PRInt32 state = nsXMLElement::IntrinsicState();
 
   PRInt32 imageState = nsImageLoadingContent::ImageState();
-  if (imageState & (NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_USERDISABLED)) {
+  if (imageState & NS_EVENT_STATE_BROKEN) {
     // We should never be in an error state; if the image fails to load, we
     // just go to the suppressed state.
     imageState |= NS_EVENT_STATE_SUPPRESSED;
     imageState &= ~NS_EVENT_STATE_BROKEN;
   }
   imageState &= ~NS_EVENT_STATE_LOADING;
   return state | imageState;
 }
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -58,18 +58,16 @@
 #endif 
 
 //---------------------------------------------------------------------------
 // Generic atoms
 //---------------------------------------------------------------------------
 
 GK_ATOM(_empty, "")
 GK_ATOM(mozdirty, "_moz_dirty")
-GK_ATOM(mozgeneratedcontentbefore, "_moz_generated_content_before")
-GK_ATOM(mozgeneratedcontentafter, "_moz_generated_content_after")
 GK_ATOM(_moz_target, "_moz_target")
 GK_ATOM(menuactive, "_moz-menuactive")
 GK_ATOM(_poundDefault, "#default")
 GK_ATOM(_asterix, "*")
 GK_ATOM(a, "a")
 GK_ATOM(abbr, "abbr")
 GK_ATOM(abort, "abort")
 GK_ATOM(above, "above")
--- a/content/base/src/nsTextNode.cpp
+++ b/content/base/src/nsTextNode.cpp
@@ -88,26 +88,25 @@ class nsAttributeTextNode : public nsTex
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   
   nsAttributeTextNode(nsINodeInfo *aNodeInfo,
                       PRInt32 aNameSpaceID,
                       nsIAtom* aAttrName) :
     nsTextNode(aNodeInfo),
-    mGrandparent(nsnull),
     mNameSpaceID(aNameSpaceID),
     mAttrName(aAttrName)
   {
     NS_ASSERTION(mNameSpaceID != kNameSpaceID_Unknown, "Must know namespace");
     NS_ASSERTION(mAttrName, "Must have attr name");
   }
 
   virtual ~nsAttributeTextNode() {
-    NS_ASSERTION(!mGrandparent, "We were not unbound!");
+    DetachListener();
   }
 
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                               nsIContent* aBindingParent,
                               PRBool aCompileEventHandlers);
   virtual void UnbindFromTree(PRBool aDeep = PR_TRUE,
                               PRBool aNullParent = PR_TRUE);
 
@@ -130,21 +129,19 @@ public:
   void UpdateText() {
     UpdateText(PR_TRUE);
   }
 
 private:
   // Update our text to our parent's current attr value
   void UpdateText(PRBool aNotify);
 
-  // This doesn't need to be a strong pointer because it's only non-null
-  // while we're bound to the document tree, and it points to an ancestor
-  // so the ancestor must be bound to the document tree the whole time
-  // and can't be deleted.
-  nsIContent* mGrandparent;
+  // Detach ourselves as an attribute listener.
+  void DetachListener();
+
   // What attribute we're showing
   PRInt32 mNameSpaceID;
   nsCOMPtr<nsIAtom> mAttrName;
 };
 
 nsresult
 NS_NewTextNode(nsIContent** aInstancePtrResult,
                nsNodeInfoManager *aNodeInfoManager)
@@ -299,71 +296,77 @@ NS_NewAttributeContent(nsNodeInfoManager
 NS_IMPL_ISUPPORTS_INHERITED1(nsAttributeTextNode, nsTextNode,
                              nsIMutationObserver)
 
 nsresult
 nsAttributeTextNode::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                                 nsIContent* aBindingParent,
                                 PRBool aCompileEventHandlers)
 {
-  NS_PRECONDITION(aParent && aParent->GetParent(),
-                  "This node can't be a child of the document or of the document root");
+  NS_PRECONDITION(aParent, "This node can't be a child of the document");
 
   nsresult rv = nsTextNode::BindToTree(aDocument, aParent,
                                        aBindingParent, aCompileEventHandlers);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  NS_ASSERTION(!mGrandparent, "We were already bound!");
-  mGrandparent = aParent->GetParent();
-  mGrandparent->AddMutationObserver(this);
+  nsINode* parent = GetNodeParent();
+  NS_ENSURE_TRUE(parent, NS_ERROR_UNEXPECTED);
+
+  parent->AddMutationObserver(this);
 
   // Note that there is no need to notify here, since we have no
   // frame yet at this point.
   UpdateText(PR_FALSE);
 
   return NS_OK;
 }
 
 void
 nsAttributeTextNode::UnbindFromTree(PRBool aDeep, PRBool aNullParent)
 {
-  // UnbindFromTree can be called anytime so we have to be safe.
-  if (mGrandparent) {
-    // aNullParent might not be true here, but we want to remove the
-    // mutation observer anyway since we only need it while we're
-    // in the document.
-    mGrandparent->RemoveMutationObserver(this);
-    mGrandparent = nsnull;
+  // Detach as listener while we know who our parent is!
+  if (aNullParent) {
+    DetachListener();
   }
   nsTextNode::UnbindFromTree(aDeep, aNullParent);
 }
 
 void
 nsAttributeTextNode::AttributeChanged(nsIDocument* aDocument,
                                       nsIContent* aContent,
                                       PRInt32 aNameSpaceID,
                                       nsIAtom* aAttribute,
                                       PRInt32 aModType,
                                       PRUint32 aStateMask)
 {
   if (aNameSpaceID == mNameSpaceID && aAttribute == mAttrName &&
-      aContent == mGrandparent) {
+      aContent == GetNodeParent()) {
     // Since UpdateText notifies, do it asynchronously.  Note that if we get
-    // unbound while the event is up that's ok -- we'll just have no
-    // grandparent when it fires, and will do nothing.    
+    // unbound while the event is up that's ok -- we'll just have no parent
+    // when it fires, and will do nothing.    
     // XXXbz ideally we'd either process this on layout flushes or do it right
     // after nsIMutationObserver notifications are over or something, instead
     // of doing it fully async.
     nsCOMPtr<nsIRunnable> ev = new nsRunnableMethod<nsAttributeTextNode>(
             this, &nsAttributeTextNode::UpdateText);
     NS_DispatchToCurrentThread(ev);
   }
 }
 
 void
 nsAttributeTextNode::UpdateText(PRBool aNotify)
 {
-  if (mGrandparent) {
+  nsIContent* parent = GetParent();
+  if (parent) {
     nsAutoString attrValue;
-    mGrandparent->GetAttr(mNameSpaceID, mAttrName, attrValue);
+    parent->GetAttr(mNameSpaceID, mAttrName, attrValue);
     SetText(attrValue, aNotify);
   }  
 }
+
+void
+nsAttributeTextNode::DetachListener()
+{
+  nsINode* parent = GetNodeParent();
+  if (parent) {
+    parent->RemoveMutationObserver(this);
+  }
+}
--- a/layout/base/crashtests/crashtests.list
+++ b/layout/base/crashtests/crashtests.list
@@ -51,17 +51,16 @@ load 366271-1.html
 load 366967-1.html
 load 367015-1.html
 load 367243-1.html
 load 369945-1.xhtml
 load 371681-1.xhtml
 load 372237-1.html
 load 372475-1.xhtml
 load 372550-1.html
-load 374193-1.xhtml
 load 374297-1.html
 load 374297-2.html
 load 376223-1.xhtml
 load 379105-1.xhtml
 load 379419-1.xhtml
 load 379768-1.html
 load 379799-1.html
 load 379920-1.svg
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -1111,19 +1111,16 @@ public:
   // Containing block information for out-of-flow frames.
   nsAbsoluteItems           mFixedItems;
   nsAbsoluteItems           mAbsoluteItems;
   nsAbsoluteItems           mFloatedItems;
   PRBool                    mFirstLetterStyle;
   PRBool                    mFirstLineStyle;
   nsCOMPtr<nsILayoutHistoryState> mFrameState;
   nsPseudoFrames            mPseudoFrames;
-  // These bits will be added to the state bits of any frame we construct
-  // using this state.
-  nsFrameState              mAdditionalStateBits; 
 
   // Constructor
   // Use the passed-in history state.
   nsFrameConstructorState(nsIPresShell*          aPresShell,
                           nsIFrame*              aFixedContainingBlock,
                           nsIFrame*              aAbsoluteContainingBlock,
                           nsIFrame*              aFloatContainingBlock,
                           nsILayoutHistoryState* aHistoryState);
@@ -1218,18 +1215,17 @@ nsFrameConstructorState::nsFrameConstruc
     mPopupItems(mRootBox ? mRootBox->GetPopupSetFrame() : nsnull),
 #endif
     mFixedItems(aFixedContainingBlock),
     mAbsoluteItems(aAbsoluteContainingBlock),
     mFloatedItems(aFloatContainingBlock),
     mFirstLetterStyle(PR_FALSE),
     mFirstLineStyle(PR_FALSE),
     mFrameState(aHistoryState),
-    mPseudoFrames(),
-    mAdditionalStateBits(0)
+    mPseudoFrames()
 {
   MOZ_COUNT_CTOR(nsFrameConstructorState);
 }
 
 nsFrameConstructorState::nsFrameConstructorState(nsIPresShell* aPresShell,
                                                  nsIFrame*     aFixedContainingBlock,
                                                  nsIFrame*     aAbsoluteContainingBlock,
                                                  nsIFrame*     aFloatContainingBlock)
@@ -1240,18 +1236,17 @@ nsFrameConstructorState::nsFrameConstruc
     mRootBox(nsIRootBox::GetRootBox(aPresShell)),
     mPopupItems(mRootBox ? mRootBox->GetPopupSetFrame() : nsnull),
 #endif
     mFixedItems(aFixedContainingBlock),
     mAbsoluteItems(aAbsoluteContainingBlock),
     mFloatedItems(aFloatContainingBlock),
     mFirstLetterStyle(PR_FALSE),
     mFirstLineStyle(PR_FALSE),
-    mPseudoFrames(),
-    mAdditionalStateBits(0)
+    mPseudoFrames()
 {
   MOZ_COUNT_CTOR(nsFrameConstructorState);
   mFrameState = aPresShell->GetDocument()->GetLayoutHistoryState();
 }
 
 nsFrameConstructorState::~nsFrameConstructorState()
 {
   // Frame order comparison functions only work properly when the placeholders
@@ -1432,17 +1427,16 @@ nsFrameConstructorState::AddChild(nsIFra
       // hence already set as the primary frame.  So we have to clean up here.
       // But it shouldn't have any out-of-flow kids.
       // XXXbz Maybe add a utility function to assert that?
       CleanupFrameReferences(mFrameManager, aNewFrame);
       aNewFrame->Destroy();
       return rv;
     }
 
-    placeholderFrame->AddStateBits(mAdditionalStateBits);
     // Add the placeholder frame to the flow
     aFrameItems.AddChild(placeholderFrame);
   }
 #ifdef DEBUG
   else {
     NS_ASSERTION(aNewFrame->GetParent() == aParentFrame,
                  "In-flow frame has wrong parent");
   }
@@ -1880,350 +1874,449 @@ nsIXBLService * nsCSSFrameConstructor::G
     nsresult rv = CallGetService("@mozilla.org/xbl;1", &gXBLService);
     if (NS_FAILED(rv))
       gXBLService = nsnull;
   }
   
   return gXBLService;
 }
 
-// XXX it would be great if we could use a state bit for this ... or a fast
-// property
-static PRBool
-HasCounterIncrementOrReset(nsIFrame* aFrame)
-{
-  const nsStyleContent *styleContent = aFrame->GetStyleContent();
-  return styleContent->CounterIncrementCount() ||
-         styleContent->CounterResetCount();
-}
-
 void
 nsCSSFrameConstructor::NotifyDestroyingFrame(nsIFrame* aFrame)
 {
   NS_PRECONDITION(mUpdateCount != 0,
                   "Should be in an update while destroying frames");
 
-  nsIFrame* parentFrame = aFrame->GetParent();
-  if (!parentFrame) {
-    NS_ASSERTION(!aFrame->IsGeneratedContentFrame(),
-                 "Root frame was generated?");
-    // The root frame can't be generated...
-    return;
-  }
-
-  if ((aFrame->IsGeneratedContentFrame() &&
-       !parentFrame->IsGeneratedContentFrame()) ||
-      (!mCounterManager.IsEmpty() && HasCounterIncrementOrReset(aFrame))) {
-    // Destroying outermost generated content frame OR a frame with
-    // counter increments/resets
-    nsIContent* content;
-    nsIAtom* pseudo = aFrame->GetStyleContext()->GetPseudoType();
-    // The frame may be a pseudo that's not 'before' or 'after'; if so,
-    // treat it as no pseudo since the frame may be non-generated content
-    // associated with a counter, and the counter pseudo is always only
-    // 'before', 'after' or null.
-    if (pseudo == nsCSSPseudoElements::before ||
-        pseudo == nsCSSPseudoElements::after) {
-      content = parentFrame->GetContent();
-    } else {
-      pseudo = nsnull;
-      content = aFrame->GetContent();
-    }
-
-    if (mQuoteList.DestroyNodesFor(content, pseudo)) {
+  if (aFrame->GetStateBits() & NS_FRAME_GENERATED_CONTENT) {
+    if (mQuoteList.DestroyNodesFor(aFrame))
       QuotesDirty();
-    }
-    // Note that we might call DestroyNodesFor multiple times for the same
-    // content+pseudo pair: once for the outermost generated content frame
-    // and once for an inner frame that actually has the content
-    // increments/resets on it (e.g. an inner table frame). That's OK though.
-    if (mCounterManager.DestroyNodesFor(content, pseudo)) {
-      // Technically we don't need to update anything if we destroyed only
-      // USE nodes.  However, this is unlikely to happen in the real world
-      // since USE nodes generally go along with INCREMENT nodes.
-      CountersDirty();
-    }
-  }
-}
-
-already_AddRefed<nsIContent>
-nsCSSFrameConstructor::CreateTextNode(const nsString& aString,
-                                      nsCOMPtr<nsIDOMCharacterData>* aText)
-{
+  }
+
+  if (mCounterManager.DestroyNodesFor(aFrame)) {
+    // Technically we don't need to update anything if we destroyed only
+    // USE nodes.  However, this is unlikely to happen in the real world
+    // since USE nodes generally go along with INCREMENT nodes.
+    CountersDirty();
+  }
+}
+
+nsresult
+nsCSSFrameConstructor::CreateAttributeContent(nsIContent* aParentContent,
+                                              nsIFrame* aParentFrame,
+                                              PRInt32 aAttrNamespace,
+                                              nsIAtom* aAttrName,
+                                              nsStyleContext* aStyleContext,
+                                              nsCOMArray<nsIContent>& aGeneratedContent,
+                                              nsIContent** aNewContent,
+                                              nsIFrame** aNewFrame)
+{
+  *aNewFrame = nsnull;
+  *aNewContent = nsnull;
   nsCOMPtr<nsIContent> content;
-  NS_NewTextNode(getter_AddRefs(content), mDocument->NodeInfoManager());
-  if (!content) {
-    // XXX The quotes/counters code doesn't like the text pointer
-    // being null in case of dynamic changes!
-    NS_ASSERTION(!aText, "this OOM case isn't handled very well");
-    return nsnull;
-  }
-  content->SetText(aString, PR_FALSE);
-  if (aText) {
-    *aText = do_QueryInterface(content);
-  }
-  return content.forget();
-}
-
-already_AddRefed<nsIContent>
-nsCSSFrameConstructor::CreateGeneratedContent(nsIContent*     aParentContent,
-                                              nsStyleContext* aStyleContext,
-                                              PRUint32        aContentIndex)
-{
+  nsresult rv = NS_NewAttributeContent(mDocument->NodeInfoManager(),
+                                       aAttrNamespace, aAttrName,
+                                       getter_AddRefs(content));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  content->SetNativeAnonymous();
+
+  // Set aParentContent as the parent content so that event handling works.
+  // It is also the binding parent.
+  rv = content->BindToTree(mDocument, aParentContent, aParentContent, PR_TRUE);
+  if (NS_FAILED(rv)) {
+    content->UnbindFromTree();
+    return rv;
+  }
+
+  // Create a text frame and initialize it
+  nsIFrame* textFrame = NS_NewTextFrame(mPresShell, aStyleContext);
+  rv = textFrame->Init(content, aParentFrame, nsnull);
+  if (NS_SUCCEEDED(rv)) {
+    if (NS_UNLIKELY(!aGeneratedContent.AppendObject(content))) {
+      rv = NS_ERROR_OUT_OF_MEMORY;
+    }
+  }
+
+  if (NS_FAILED(rv)) {
+    content->UnbindFromTree();
+    textFrame->Destroy();
+    textFrame = nsnull;
+    content = nsnull;
+  }
+
+  *aNewFrame = textFrame;
+  content.swap(*aNewContent);
+  return rv;
+}
+
+nsresult
+nsCSSFrameConstructor::CreateGeneratedFrameFor(nsIFrame*             aParentFrame,
+                                               nsIContent*           aContent,
+                                               nsStyleContext*       aStyleContext,
+                                               const nsStyleContent* aStyleContent,
+                                               PRUint32              aContentIndex,
+                                               nsCOMArray<nsIContent>& aGeneratedContent,
+                                               nsIFrame**            aFrame)
+{
+  *aFrame = nsnull;  // initialize OUT parameter
+
+  // The QuoteList needs the content attached to the frame.
+  nsCOMPtr<nsIDOMCharacterData>* textPtr = nsnull;
+
   // Get the content value
-  const nsStyleContentData &data =
-    aStyleContext->GetStyleContent()->ContentAt(aContentIndex);
-  nsStyleContentType type = data.mType;
+  const nsStyleContentData &data = aStyleContent->ContentAt(aContentIndex);
+  nsStyleContentType  type = data.mType;
+
+  nsCOMPtr<nsIContent> content;
 
   if (eStyleContentType_Image == type) {
     if (!data.mContent.mImage) {
       // CSS had something specified that couldn't be converted to an
       // image object
-      return nsnull;
+      return NS_ERROR_FAILURE;
     }
     
     // Create an image content object and pass it the image request.
     // XXX Check if it's an image type we can handle...
 
     nsCOMPtr<nsINodeInfo> nodeInfo;
     mDocument->NodeInfoManager()->GetNodeInfo(nsGkAtoms::img, nsnull,
-                                              kNameSpaceID_XHTML,
+                                              kNameSpaceID_None,
                                               getter_AddRefs(nodeInfo));
 
-    nsCOMPtr<nsIContent> content;
-    NS_NewGenConImageContent(getter_AddRefs(content), nodeInfo,
-                             data.mContent.mImage);
-    return content.forget();
-  }
-
-  switch (type) {
-  case eStyleContentType_String:
-    return CreateTextNode(nsDependentString(data.mContent.mString), nsnull);
-
-  case eStyleContentType_Attr:
-    {
-      nsCOMPtr<nsIAtom> attrName;
-      PRInt32 attrNameSpace = kNameSpaceID_None;
-      nsAutoString contentString(data.mContent.mString);
-      PRInt32 barIndex = contentString.FindChar('|'); // CSS namespace delimiter
-      if (-1 != barIndex) {
-        nsAutoString  nameSpaceVal;
-        contentString.Left(nameSpaceVal, barIndex);
-        PRInt32 error;
-        attrNameSpace = nameSpaceVal.ToInteger(&error, 10);
-        contentString.Cut(0, barIndex + 1);
-        if (contentString.Length()) {
+    nsresult rv = NS_NewGenConImageContent(getter_AddRefs(content), nodeInfo,
+                                           data.mContent.mImage);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    content->SetNativeAnonymous();
+  
+    // Set aContent as the parent content and set the document object. This
+    // way event handling works.  It is also the binding parent.
+    rv = content->BindToTree(mDocument, aContent, aContent, PR_TRUE);
+    if (NS_FAILED(rv)) {
+      content->UnbindFromTree();
+      return rv;
+    }
+    
+    // Create an image frame and initialize it
+    nsIFrame* imageFrame = NS_NewImageFrame(mPresShell, aStyleContext);
+    if (NS_UNLIKELY(!imageFrame)) {
+      content->UnbindFromTree();
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+
+    rv = imageFrame->Init(content, aParentFrame, nsnull);
+    if (NS_FAILED(rv) || NS_UNLIKELY(!aGeneratedContent.AppendObject(content))) {
+      content->UnbindFromTree();
+      imageFrame->Destroy();
+      return NS_FAILED(rv) ? rv : NS_ERROR_OUT_OF_MEMORY;
+    }
+
+    // Return the image frame
+    *aFrame = imageFrame;
+
+  } else {
+
+    nsAutoString contentString;
+
+    switch (type) {
+    case eStyleContentType_String:
+      contentString = data.mContent.mString;
+      break;
+  
+    case eStyleContentType_Attr:
+      {
+        nsCOMPtr<nsIAtom> attrName;
+        PRInt32 attrNameSpace = kNameSpaceID_None;
+        contentString = data.mContent.mString;
+        PRInt32 barIndex = contentString.FindChar('|'); // CSS namespace delimiter
+        if (-1 != barIndex) {
+          nsAutoString  nameSpaceVal;
+          contentString.Left(nameSpaceVal, barIndex);
+          PRInt32 error;
+          attrNameSpace = nameSpaceVal.ToInteger(&error, 10);
+          contentString.Cut(0, barIndex + 1);
+          if (contentString.Length()) {
+            attrName = do_GetAtom(contentString);
+          }
+        }
+        else {
           attrName = do_GetAtom(contentString);
         }
-      }
-      else {
-        attrName = do_GetAtom(contentString);
-      }
-
-      if (!attrName) {
-        return nsnull;
-      }
-
-      nsCOMPtr<nsIContent> content;
-      NS_NewAttributeContent(mDocument->NodeInfoManager(),
-                             attrNameSpace, attrName, getter_AddRefs(content));
-      return content.forget();
-    }
-  
-  case eStyleContentType_Counter:
-  case eStyleContentType_Counters:
-    {
-      nsCSSValue::Array *counters = data.mContent.mCounters;
-      nsCounterList *counterList = mCounterManager.CounterListFor(
-          nsDependentString(counters->Item(0).GetStringBufferValue()));
-      if (!counterList)
-        return nsnull;
-
-      nsCounterUseNode* node =
-        new nsCounterUseNode(counters, aParentContent, aStyleContext,
-                             aContentIndex, type == eStyleContentType_Counters);
-      if (!node)
-        return nsnull;
-
-      nsAutoString contentString;
-
-      // Note that if there are increments or resets for this generated
-      // content, they will be added *later* when we create the frame subtree
-      // for the generated content. That's OK correctness-wise since
-      // the counter manager will insert them before this "use" node. It's
-      // not so great for performance though. This !dirty optimization here
-      // isn't going to be much use since adding the increments and resets
-      // later will almost certainly dirty everything.
-      counterList->Insert(node);
-      PRBool dirty = counterList->IsDirty();
-      if (!dirty) {
-        if (counterList->IsLast(node)) {
-          node->Calc(counterList);
-          node->GetText(contentString);
+
+        if (!attrName) {
+          return NS_ERROR_OUT_OF_MEMORY;
         }
-        // In all other cases (list already dirty or node not at the end),
-        // just start with an empty string for now and when we recalculate
-        // the list we'll change the value to the right one.
-        else {
-          counterList->SetDirty();
-          CountersDirty();
+
+        nsresult rv =
+          CreateAttributeContent(aContent, aParentFrame, attrNameSpace,
+                                 attrName, aStyleContext, aGeneratedContent,
+                                 getter_AddRefs(content), aFrame);
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
+      break;
+  
+    case eStyleContentType_Counter:
+    case eStyleContentType_Counters:
+      {
+        nsCSSValue::Array *counters = data.mContent.mCounters;
+        nsCounterList *counterList = mCounterManager.CounterListFor(
+            nsDependentString(counters->Item(0).GetStringBufferValue()));
+        if (!counterList)
+            return NS_ERROR_OUT_OF_MEMORY;
+
+        nsCounterUseNode* node =
+          new nsCounterUseNode(counters, aParentFrame, aContentIndex,
+                               type == eStyleContentType_Counters);
+        if (!node)
+          return NS_ERROR_OUT_OF_MEMORY;
+
+        counterList->Insert(node);
+        PRBool dirty = counterList->IsDirty();
+        if (!dirty) {
+          if (counterList->IsLast(node)) {
+            node->Calc(counterList);
+            node->GetText(contentString);
+          }
+          // In all other cases (list already dirty or node not at the end),
+          // just start with an empty string for now and when we recalculate
+          // the list we'll change the value to the right one.
+          else {
+            counterList->SetDirty();
+            CountersDirty();
+          }
         }
-      }
-
-      return CreateTextNode(contentString, &node->mText);
-    }
-
-  case eStyleContentType_Image:
-    NS_NOTREACHED("handled by if above");
-    return nsnull;
-
-  case eStyleContentType_OpenQuote:
-  case eStyleContentType_CloseQuote:
-  case eStyleContentType_NoOpenQuote:
-  case eStyleContentType_NoCloseQuote:
-    {
-      nsQuoteNode* node =
-        new nsQuoteNode(type, aParentContent, aContentIndex, aStyleContext);
-      if (!node)
-        return nsnull;
-      mQuoteList.Insert(node);
-      if (mQuoteList.IsLast(node))
-        mQuoteList.Calc(node);
-      else
-        QuotesDirty();
-
-      // Don't generate a text node or any text for 'no-open-quote' and
-      // 'no-close-quote'.
-      if (node->IsHiddenQuote())
-        return nsnull;
-
-      return CreateTextNode(*node->Text(), &node->mText);
-    }
-  
-  case eStyleContentType_AltContent:
-    {
-      // Use the "alt" attribute; if that fails and the node is an HTML
-      // <input>, try the value attribute and then fall back to some default
-      // localized text we have.
-      // XXX what if the 'alt' attribute is added later, how will we
-      // detect that and do the right thing here?
-      if (aParentContent->HasAttr(kNameSpaceID_None, nsGkAtoms::alt)) {
-        nsCOMPtr<nsIContent> content;
-        NS_NewAttributeContent(mDocument->NodeInfoManager(),
-                               kNameSpaceID_None, nsGkAtoms::alt, getter_AddRefs(content));
-        return content.forget();
-      }
-
-      if (aParentContent->IsNodeOfType(nsINode::eHTML) &&
-          aParentContent->NodeInfo()->Equals(nsGkAtoms::input)) {
-        if (aParentContent->HasAttr(kNameSpaceID_None, nsGkAtoms::value)) {
-          nsCOMPtr<nsIContent> content;
-          NS_NewAttributeContent(mDocument->NodeInfoManager(),
-                                 kNameSpaceID_None, nsGkAtoms::value, getter_AddRefs(content));
-          return content.forget();
+
+        textPtr = &node->mText; // text node assigned below
+      }
+      break;
+
+    case eStyleContentType_Image:
+      NS_NOTREACHED("handled by if above");
+      return NS_ERROR_UNEXPECTED;
+  
+    case eStyleContentType_OpenQuote:
+    case eStyleContentType_CloseQuote:
+    case eStyleContentType_NoOpenQuote:
+    case eStyleContentType_NoCloseQuote:
+      {
+        nsQuoteNode* node = new nsQuoteNode(type, aParentFrame, aContentIndex);
+        if (!node)
+          return NS_ERROR_OUT_OF_MEMORY;
+        mQuoteList.Insert(node);
+        if (mQuoteList.IsLast(node))
+          mQuoteList.Calc(node);
+        else
+          QuotesDirty();
+
+        // Don't generate a text node or any text for 'no-open-quote' and
+        // 'no-close-quote'.
+        if (node->IsHiddenQuote())
+          return NS_OK;
+
+        textPtr = &node->mText; // text node assigned below
+        contentString = *node->Text();
+      }
+      break;
+  
+    case eStyleContentType_AltContent:
+      {
+        // Use the "alt" attribute; if that fails and the node is an HTML
+        // <input>, try the value attribute and then fall back to some default
+        // localized text we have.
+        nsresult rv = NS_OK;
+        if (aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::alt)) {
+          rv = CreateAttributeContent(aContent, aParentFrame,
+                                      kNameSpaceID_None, nsGkAtoms::alt,
+                                      aStyleContext, aGeneratedContent,
+                                      getter_AddRefs(content), aFrame);
+        } else if (aContent->IsNodeOfType(nsINode::eHTML) &&
+                   aContent->NodeInfo()->Equals(nsGkAtoms::input)) {
+          if (aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::value)) {
+            rv = CreateAttributeContent(aContent, aParentFrame,
+                                        kNameSpaceID_None, nsGkAtoms::value,
+                                        aStyleContext, aGeneratedContent,
+                                        getter_AddRefs(content), aFrame);
+          } else {
+            nsXPIDLString temp;
+            rv = nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
+                                                    "Submit", temp);
+            contentString = temp;
+          }
+        } else {
+          *aFrame = nsnull;
+          rv = NS_ERROR_NOT_AVAILABLE;
+          return rv; // Don't fall through to the warning below.
         }
-
-        nsXPIDLString temp;
-        nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
-                                           "Submit", temp);
-        return CreateTextNode(temp, nsnull);
-      }
-
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
       break;
-    }
-  } // switch
-
-  return nsnull;
-}
-
-static void DestroyContent(void *aObject,
-                           nsIAtom *aPropertyName,
-                           void *aPropertyValue,
-                           void *aData)
-{
-  nsIContent* content = static_cast<nsIContent*>(aPropertyValue);
-  content->UnbindFromTree();
-  NS_RELEASE(content);
+    } // switch
+  
+
+    if (!content) {
+      // Create a text content node
+      nsIFrame* textFrame = nsnull;
+      nsCOMPtr<nsIContent> textContent;
+      NS_NewTextNode(getter_AddRefs(textContent),
+                     mDocument->NodeInfoManager());
+      if (textContent) {
+        // Set the text
+        textContent->SetText(contentString, PR_TRUE);
+
+        if (textPtr) {
+          *textPtr = do_QueryInterface(textContent);
+          NS_ASSERTION(*textPtr, "must implement nsIDOMCharacterData");
+        }
+
+        textContent->SetNativeAnonymous();
+
+        // Set aContent as the parent content so that event handling works.
+        nsresult rv = textContent->BindToTree(mDocument, aContent, aContent,
+                                              PR_TRUE);
+        if (NS_FAILED(rv)) {
+          textContent->UnbindFromTree();
+          return rv;
+        }
+
+        // Create a text frame and initialize it
+        textFrame = NS_NewTextFrame(mPresShell, aStyleContext);
+        if (!textFrame) {
+          // XXX The quotes/counters code doesn't like the text pointer
+          // being null in case of dynamic changes!
+          NS_NOTREACHED("this OOM case isn't handled very well");
+          return NS_ERROR_OUT_OF_MEMORY;
+        }
+
+        textFrame->Init(textContent, aParentFrame, nsnull);
+
+        content = textContent;
+        if (NS_UNLIKELY(!aGeneratedContent.AppendObject(content))) {
+          NS_NOTREACHED("this OOM case isn't handled very well");
+          return NS_ERROR_OUT_OF_MEMORY;
+        }
+      } else {
+        // XXX The quotes/counters code doesn't like the text pointer
+        // being null in case of dynamic changes!
+        NS_NOTREACHED("this OOM case isn't handled very well");
+      }
+
+      // Return the text frame
+      *aFrame = textFrame;
+    }
+  }
+
+  return NS_OK;
 }
 
 /*
- * aParentFrame - the frame that should be the parent of the generated
+ *
+ * aFrame - the frame that should be the parent of the generated
  *   content.  This is the frame for the corresponding content node,
  *   which must not be a leaf frame.
- * 
- * Any frames created are added to aFrameItems (or possibly left
- * in the table pseudoframe state in aState).
- * 
- * We create an XML element (tag _moz_generated_content_before or
- * _moz_generated_content_after) representing the pseudoelement. We
- * create a DOM node for each 'content' item and make those nodes the
- * children of the XML element. Then we create a frame subtree for
- * the XML element as if it were a regular child of
- * aParentFrame/aParentContent, giving the XML element the ::before or
- * ::after style.
  */
-void
+PRBool
 nsCSSFrameConstructor::CreateGeneratedContentFrame(nsFrameConstructorState& aState,
-                                                   nsIFrame*        aParentFrame,
-                                                   nsIContent*      aParentContent,
+                                                   nsIFrame*        aFrame,
+                                                   nsIContent*      aContent,
                                                    nsStyleContext*  aStyleContext,
                                                    nsIAtom*         aPseudoElement,
-                                                   nsFrameItems&    aFrameItems)
-{
-  if (!aParentContent->IsNodeOfType(nsINode::eELEMENT))
-    return;
+                                                   nsIFrame**       aResult)
+{
+  *aResult = nsnull; // initialize OUT parameter
+
+  if (!aContent->IsNodeOfType(nsINode::eELEMENT))
+    return PR_FALSE;
 
   nsStyleSet *styleSet = mPresShell->StyleSet();
 
   // Probe for the existence of the pseudo-element
   nsRefPtr<nsStyleContext> pseudoStyleContext;
-  pseudoStyleContext = styleSet->ProbePseudoStyleFor(aParentContent,
+  pseudoStyleContext = styleSet->ProbePseudoStyleFor(aContent,
                                                      aPseudoElement,
                                                      aStyleContext);
-  if (!pseudoStyleContext)
-    return;
-  // |ProbePseudoStyleFor| checked the 'display' property and the
-  // |ContentCount()| of the 'content' property for us.
-  nsCOMPtr<nsINodeInfo> nodeInfo;
-  nsIAtom* elemName = aPseudoElement == nsCSSPseudoElements::before ?
-    nsGkAtoms::mozgeneratedcontentbefore : nsGkAtoms::mozgeneratedcontentafter;
-  mDocument->NodeInfoManager()->GetNodeInfo(elemName, nsnull,
-                                            kNameSpaceID_None,
-                                            getter_AddRefs(nodeInfo));
-  nsIContent* container;
-  nsresult rv = NS_NewXMLElement(&container, nodeInfo);
-  if (NS_FAILED(rv))
-    return;
-  container->SetNativeAnonymous();
-  // Transfer ownership to the frame
-  aParentFrame->SetProperty(aPseudoElement, container, DestroyContent);
-
-  rv = container->BindToTree(mDocument, aParentContent, aParentContent, PR_TRUE);
-  if (NS_FAILED(rv)) {
-    container->UnbindFromTree();
-    return;
-  }
-
-  PRUint32 contentCount = pseudoStyleContext->GetStyleContent()->ContentCount();
-  for (PRUint32 contentIndex = 0; contentIndex < contentCount; contentIndex++) {
-    nsCOMPtr<nsIContent> content =
-      CreateGeneratedContent(aParentContent, pseudoStyleContext, contentIndex);
-    if (content) {
-      container->AppendChildTo(content, PR_FALSE);
-    }
-  }
-
-  nsFrameState savedStateBits = aState.mAdditionalStateBits;
-  // Ensure that frames created here are all tagged with
-  // NS_FRAME_GENERATED_CONTENT.
-  aState.mAdditionalStateBits |= NS_FRAME_GENERATED_CONTENT;
-
-  ConstructFrameInternal(aState, container, aParentFrame,
-    elemName, kNameSpaceID_None, pseudoStyleContext, aFrameItems, PR_TRUE);
-  aState.mAdditionalStateBits = savedStateBits;
+
+  if (pseudoStyleContext) {
+    // |ProbePseudoStyleContext| checks the 'display' property and the
+    // |ContentCount()| of the 'content' property for us.
+
+    // Create a block box or an inline box depending on the value of
+    // the 'display' property
+    nsIFrame*     containerFrame;
+    nsFrameItems  childFrames;
+    nsresult rv;
+
+    const PRUint8 disp = pseudoStyleContext->GetStyleDisplay()->mDisplay;
+    if (disp == NS_STYLE_DISPLAY_BLOCK ||
+        disp == NS_STYLE_DISPLAY_INLINE_BLOCK) {
+      PRUint32 flags = 0;
+      if (disp == NS_STYLE_DISPLAY_INLINE_BLOCK) {
+        flags = NS_BLOCK_SPACE_MGR | NS_BLOCK_MARGIN_ROOT;
+      }
+      containerFrame = NS_NewBlockFrame(mPresShell, pseudoStyleContext, flags);
+    } else {
+      containerFrame = NS_NewInlineFrame(mPresShell, pseudoStyleContext);
+    }
+
+    if (NS_UNLIKELY(!containerFrame)) {
+      return PR_FALSE;
+    }
+    InitAndRestoreFrame(aState, aContent, aFrame, nsnull, containerFrame);
+    // XXXbz should we be passing in a non-null aContentParentFrame?
+    nsHTMLContainerFrame::CreateViewForFrame(containerFrame, nsnull, PR_FALSE);
+
+    // Mark the frame as being associated with generated content
+    containerFrame->AddStateBits(NS_FRAME_GENERATED_CONTENT);
+
+    // Create an array to hold all the generated content created for this
+    // frame below in CreateGeneratedFrameFor. No destructor function is
+    // specified because the property is only set here and is removed in
+    // a single place - nsContainerFrame::Destroy.
+    nsCOMArray<nsIContent>* generatedContent = new nsCOMArray<nsIContent>;
+    rv = containerFrame->SetProperty(nsGkAtoms::generatedContent,
+                                     generatedContent);
+    if (NS_UNLIKELY(!generatedContent) || NS_FAILED(rv)) {
+      containerFrame->Destroy(); // this also destroys the created view
+      delete generatedContent;
+      return PR_FALSE;
+    }
+
+    // Create another pseudo style context to use for all the generated child
+    // frames
+    nsRefPtr<nsStyleContext> textStyleContext;
+    textStyleContext = styleSet->ResolveStyleForNonElement(pseudoStyleContext);
+
+    // Now create content objects (and child frames) for each value of the
+    // 'content' property
+
+    const nsStyleContent* styleContent = pseudoStyleContext->GetStyleContent();
+    PRUint32 contentCount = styleContent->ContentCount();
+    for (PRUint32 contentIndex = 0; contentIndex < contentCount; contentIndex++) {
+      nsIFrame* frame;
+
+      // Create a frame
+      rv = CreateGeneratedFrameFor(containerFrame,
+                                   aContent, textStyleContext,
+                                   styleContent, contentIndex,
+                                   *generatedContent, &frame);
+      // Non-elements can't possibly have a view, so don't bother checking
+      if (NS_SUCCEEDED(rv) && frame) {
+        // Add it to the list of child frames
+        childFrames.AddChild(frame);
+      }
+    }
+
+    if (childFrames.childList) {
+      containerFrame->SetInitialChildList(nsnull, childFrames.childList);
+    }
+    *aResult = containerFrame;
+    return PR_TRUE;
+  }
+
+  return PR_FALSE;
 }
 
 nsresult
 nsCSSFrameConstructor::CreateInputFrame(nsFrameConstructorState& aState,
                                         nsIContent*              aContent,
                                         nsIFrame*                aParentFrame,
                                         nsIAtom*                 aTag,
                                         nsStyleContext*          aStyleContext,
@@ -3537,17 +3630,17 @@ nsCSSFrameConstructor::ConstructTableFra
 
     rv = aState.AddChild(aNewOuterFrame, *frameItems, aContent,
                          aStyleContext, parentFrame);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     nsFrameItems childItems;
-    rv = ProcessChildren(aState, aContent, aNewInnerFrame, PR_TRUE, childItems,
+    rv = ProcessChildren(aState, aContent, aNewInnerFrame, PR_FALSE, childItems,
                          PR_FALSE);
     // XXXbz what about cleaning up?
     if (NS_FAILED(rv)) return rv;
 
     // if there are any anonymous children for the table, create frames for them
     CreateAnonymousFrames(nsnull, aState, aContent, aNewInnerFrame,
                           PR_FALSE, childItems);
 
@@ -3666,17 +3759,17 @@ nsCSSFrameConstructor::ConstructTableRow
     }
     InitAndRestoreFrame(aState, aContent, parentFrame, nsnull, aNewFrame);
     // XXXbz should we be passing in a non-null aContentParentFrame?
     nsHTMLContainerFrame::CreateViewForFrame(aNewFrame, nsnull, PR_FALSE);
   }
 
   if (!aIsPseudo) {
     nsFrameItems childItems;
-    rv = ProcessChildren(aState, aContent, aNewFrame, PR_TRUE, childItems,
+    rv = ProcessChildren(aState, aContent, aNewFrame, PR_FALSE, childItems,
                          PR_FALSE);
     
     if (NS_FAILED(rv)) return rv;
 
     // if there are any anonymous children for the table, create frames for them
     CreateAnonymousFrames(nsnull, aState, aContent, aNewFrame,
                           PR_FALSE, childItems);
 
@@ -3727,17 +3820,17 @@ nsCSSFrameConstructor::ConstructTableCol
   aNewFrame = NS_NewTableColGroupFrame(mPresShell, aStyleContext);
   if (NS_UNLIKELY(!aNewFrame)) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
   InitAndRestoreFrame(aState, aContent, parentFrame, nsnull, aNewFrame);
 
   if (!aIsPseudo) {
     nsFrameItems childItems;
-    rv = ProcessChildren(aState, aContent, aNewFrame, PR_TRUE, childItems,
+    rv = ProcessChildren(aState, aContent, aNewFrame, PR_FALSE, childItems,
                          PR_FALSE);
     if (NS_FAILED(rv)) return rv;
     aNewFrame->SetInitialChildList(nsnull, childItems.childList);
     if (aIsPseudoParent) {
       aState.mPseudoFrames.mTableInner.mChildList.AddChild(aNewFrame);
     }
   }
 
@@ -3783,17 +3876,17 @@ nsCSSFrameConstructor::ConstructTableRow
   if (NS_UNLIKELY(!aNewFrame)) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
   InitAndRestoreFrame(aState, aContent, parentFrame, nsnull, aNewFrame);
   // XXXbz should we be passing in a non-null aContentParentFrame?
   nsHTMLContainerFrame::CreateViewForFrame(aNewFrame, nsnull, PR_FALSE);
   if (!aIsPseudo) {
     nsFrameItems childItems;
-    rv = ProcessChildren(aState, aContent, aNewFrame, PR_TRUE, childItems,
+    rv = ProcessChildren(aState, aContent, aNewFrame, PR_FALSE, childItems,
                          PR_FALSE);
     if (NS_FAILED(rv)) return rv;
     // if there are any anonymous children for the table, create frames for them
     CreateAnonymousFrames(nsnull, aState, aContent, aNewFrame,
                           PR_FALSE, childItems);
 
     aNewFrame->SetInitialChildList(nsnull, childItems.childList);
     if (aIsPseudoParent) {
@@ -3987,25 +4080,19 @@ nsCSSFrameConstructor::ConstructTableCel
 
   return rv;
 }
 
 static PRBool 
 NeedFrameFor(nsIFrame*   aParentFrame,
              nsIContent* aChildContent) 
 {
-  // don't create a whitespace frame if aParentFrame doesn't want it.
-  // always create frames for children in generated content. counter(),
-  // quotes, and attr() content can easily change dynamically and we don't
-  // want to be reconstructing frames. It's not even clear that these
-  // should be considered ignorable just because they evaluate to
-  // whitespace.
-  return !aParentFrame->IsFrameOfType(nsIFrame::eExcludesIgnorableWhitespace)
-    || !TextIsOnlyWhitespace(aChildContent)
-    || aParentFrame->IsGeneratedContentFrame();
+  // don't create a whitespace frame if aParentFrame doesn't want it
+  return !aParentFrame->IsFrameOfType(nsIFrame::eExcludesIgnorableWhitespace) ||
+         !TextIsOnlyWhitespace(aChildContent);
 }
 
 const nsStyleDisplay* 
 nsCSSFrameConstructor::GetDisplay(nsIFrame* aFrame)
 {
   if (nsnull == aFrame) {
     return nsnull;
   }
@@ -6667,17 +6754,16 @@ nsCSSFrameConstructor::InitAndRestoreFra
   nsresult rv = NS_OK;
   
   NS_ASSERTION(aNewFrame, "Null frame cannot be initialized");
   if (!aNewFrame)
     return NS_ERROR_NULL_POINTER;
 
   // Initialize the frame
   rv = aNewFrame->Init(aContent, aParentFrame, aPrevInFlow);
-  aNewFrame->AddStateBits(aState.mAdditionalStateBits);
 
   if (aState.mFrameState && aState.mFrameManager) {
     // Restore frame state for just the newly created frame.
     aState.mFrameManager->RestoreFrameStateFor(aNewFrame, aState.mFrameState);
   }
 
   if (aAllowCounters && !aPrevInFlow &&
       mCounterManager.AddCounterResetsAndIncrements(aNewFrame)) {
@@ -8496,20 +8582,23 @@ nsCSSFrameConstructor::ContentAppended(n
   nsresult result = NS_OK;
 
   // Notify the parent frame passing it the list of new frames
   if (NS_SUCCEEDED(result) &&
       (frameItems.childList || captionItems.childList)) {
     // Perform special check for diddling around with the frames in
     // a special inline frame.
 
-    // If we're appending before :after content, then we're not really
-    // appending, so let WipeContainingBlock know that.
+    // We can't have a block ::after inside an inline, so it's safe to ignore
+    // the fact that we're not really appending if there's ::after content.
+    // Indeed, if we're inserting before the ::after content that means the
+    // ::after content is not the last child of the block in the {ib} split,
+    // which is the only case in which we care whether we're appending.
     if (WipeContainingBlock(state, containingBlock, parentFrame, frameItems,
-                            !parentAfterFrame, nsnull)) {
+                            PR_TRUE, nsnull)) {
       return NS_OK;
     }
 
     // Append the flowed frames to the principal child list, tables need special treatment
     if (nsGkAtoms::tableFrame == frameType) {
       if (captionItems.childList) { // append the caption to the outer table
         nsIFrame* outerTable = parentFrame->GetParent();
         if (outerTable) { 
@@ -8899,21 +8988,23 @@ nsCSSFrameConstructor::ContentInserted(n
       ::AdjustAppendParentForAfterContent(mPresShell->GetPresContext(),
                                           aContainer,
                                           frameItems.childList->GetParent(),
                                           &appendAfterFrame);
   }
 
   // Perform special check for diddling around with the frames in
   // a special inline frame.
-
-  // If we're appending before :after content, then we're not really
-  // appending, so let WipeContainingBlock know that.
+  // We can't have a block ::after inside an inline, so it's safe to ignore
+  // the fact that we're not really appending if there's ::after content.
+  // Indeed, if we're inserting before the ::after content that means the
+  // ::after content is not the last child of the block in the {ib} split,
+  // which is the only case in which we care whether we're appending.
   if (WipeContainingBlock(state, containingBlock, parentFrame, frameItems,
-                          isAppend && !appendAfterFrame, prevSibling))
+                          isAppend, prevSibling))
     return NS_OK;
 
   if (haveFirstLineStyle && parentFrame == containingBlock) {
     // It's possible that the new frame goes into a first-line
     // frame. Look at it and see...
     if (isAppend) {
       // Use append logic when appending
       AppendFirstLineFrames(state, containingBlock->GetContent(),
@@ -10136,17 +10227,17 @@ nsCSSFrameConstructor::CreateContinuingT
                                         GetAbsoluteContainingBlock(newFrame),
                                         nsnull);
 
           headerFooterFrame = static_cast<nsTableRowGroupFrame*>
                                          (NS_NewTableRowGroupFrame(aPresShell, rowGroupFrame->GetStyleContext()));
           nsIContent* headerFooter = rowGroupFrame->GetContent();
           headerFooterFrame->Init(headerFooter, newFrame, nsnull);
           ProcessChildren(state, headerFooter, headerFooterFrame,
-                          PR_TRUE, childItems, PR_FALSE);
+                          PR_FALSE, childItems, PR_FALSE);
           NS_ASSERTION(!state.mFloatedItems.childList, "unexpected floated element");
           headerFooterFrame->SetInitialChildList(nsnull, childItems.childList);
           headerFooterFrame->SetRepeatable(PR_TRUE);
 
           // Table specific initialization
           headerFooterFrame->InitRepeatedFrame(aPresContext, rowGroupFrame);
 
           // XXX Deal with absolute and fixed frames...
@@ -11128,52 +11219,61 @@ nsCSSFrameConstructor::ProcessChildren(n
   // XXXbz ideally, this would do all the pushing of various
   // containing blocks as needed, so callers don't have to do it...
   nsresult rv = NS_OK;
   // :before/:after content should have the same style context parent
   // as normal kids.
   nsStyleContext* styleContext =
     nsFrame::CorrectStyleParentFrame(aFrame, nsnull)->GetStyleContext();
     
+  if (aCanHaveGeneratedContent) {
+    // Probe for generated content before
+    nsIFrame* generatedFrame;
+    if (CreateGeneratedContentFrame(aState, aFrame, aContent,
+                                    styleContext, nsCSSPseudoElements::before,
+                                    &generatedFrame)) {
+      // Add the generated frame to the child list
+      aFrameItems.AddChild(generatedFrame);
+    }
+  }
+
+ 
   // save the incoming pseudo frame state
   nsPseudoFrames priorPseudoFrames;
   aState.mPseudoFrames.Reset(&priorPseudoFrames);
 
-  if (aCanHaveGeneratedContent) {
-    // Probe for generated content before
-    CreateGeneratedContentFrame(aState, aFrame, aContent,
-                                styleContext, nsCSSPseudoElements::before,
-                                aFrameItems);
-  }
-
   ChildIterator iter, last;
   for (ChildIterator::Init(aContent, &iter, &last);
        iter != last;
        ++iter) {
     rv = ConstructFrame(aState, nsCOMPtr<nsIContent>(*iter),
                         aFrame, aFrameItems);
     if (NS_FAILED(rv))
       return rv;
   }
 
-  if (aCanHaveGeneratedContent) {
-    // Probe for generated content after
-    CreateGeneratedContentFrame(aState, aFrame, aContent,
-                                styleContext, nsCSSPseudoElements::after,
-                                aFrameItems);
-  }
-
   // process the current pseudo frame state
   if (!aState.mPseudoFrames.IsEmpty()) {
     ProcessPseudoFrames(aState, aFrameItems);
   }
 
   // restore the incoming pseudo frame state
   aState.mPseudoFrames = priorPseudoFrames;
 
+  if (aCanHaveGeneratedContent) {
+    // Probe for generated content after
+    nsIFrame* generatedFrame;
+    if (CreateGeneratedContentFrame(aState, aFrame, aContent,
+                                    styleContext, nsCSSPseudoElements::after,
+                                    &generatedFrame)) {
+      // Add the generated frame to the child list
+      aFrameItems.AddChild(generatedFrame);
+    }
+  }
+
   if (aParentIsBlock) {
     if (aState.mFirstLetterStyle) {
       rv = WrapFramesInFirstLetterFrame(aState, aContent, aFrame, aFrameItems);
     }
     if (aState.mFirstLineStyle) {
       rv = WrapFramesInFirstLineFrame(aState, aContent, aFrame, aFrameItems);
     }
   }
@@ -12501,20 +12601,24 @@ nsCSSFrameConstructor::ProcessInlineChil
   nsStyleContext* styleContext = nsnull;
 
   // save the pseudo frame state 
   nsPseudoFrames prevPseudoFrames; 
   aState.mPseudoFrames.Reset(&prevPseudoFrames);
 
   if (aCanHaveGeneratedContent) {
     // Probe for generated content before
+    nsIFrame* generatedFrame;
     styleContext = aFrame->GetStyleContext();
-    CreateGeneratedContentFrame(aState, aFrame, aContent,
-                                styleContext, nsCSSPseudoElements::before,
-                                aFrameItems);
+    if (CreateGeneratedContentFrame(aState, aFrame, aContent,
+                                    styleContext, nsCSSPseudoElements::before,
+                                    &generatedFrame)) {
+      // Add the generated frame to the child list
+      aFrameItems.AddChild(generatedFrame);
+    }
   }
 
   // Iterate the child content objects and construct frames
   PRBool allKidsInline = PR_TRUE;
   ChildIterator iter, last;
   for (ChildIterator::Init(aContent, &iter, &last);
        iter != last;
        ++iter) {
@@ -12545,19 +12649,23 @@ nsCSSFrameConstructor::ProcessInlineChil
         }
         kid = kid->GetNextSibling();
       }
     }
   }
 
   if (aCanHaveGeneratedContent) {
     // Probe for generated content after
-    CreateGeneratedContentFrame(aState, aFrame, aContent,
-                                styleContext, nsCSSPseudoElements::after,
-                                aFrameItems);
+    nsIFrame* generatedFrame;
+    if (CreateGeneratedContentFrame(aState, aFrame, aContent,
+                                    styleContext, nsCSSPseudoElements::after,
+                                    &generatedFrame)) {
+      // Add the generated frame to the child list
+      aFrameItems.AddChild(generatedFrame);
+    }
   }
 
   // process the current pseudo frame state
   if (!aState.mPseudoFrames.IsEmpty()) {
     ProcessPseudoFrames(aState, aFrameItems);
     // recompute allKidsInline to take into account new child frames
     // XXX we DON'T do this yet because anonymous table children should
     // be accepted as inline children, until we turn on inline-table.
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -318,43 +318,31 @@ private:
   nsresult CreateAttributeContent(nsIContent* aParentContent,
                                   nsIFrame* aParentFrame,
                                   PRInt32 aAttrNamespace,
                                   nsIAtom* aAttrName,
                                   nsStyleContext* aStyleContext,
                                   nsCOMArray<nsIContent>& aGeneratedContent,
                                   nsIContent** aNewContent,
                                   nsIFrame** aNewFrame);
-
-  /**
-   * Create a text node containing the given string. If aText is non-null
-   * then we also set aText to the returned node.
-   */
-  already_AddRefed<nsIContent> CreateTextNode(const nsString& aString,  
-                                              nsCOMPtr<nsIDOMCharacterData>* aText);
+  
+  nsresult CreateGeneratedFrameFor(nsIFrame*             aParentFrame,
+                                   nsIContent*           aContent,
+                                   nsStyleContext*       aStyleContext,
+                                   const nsStyleContent* aStyleContent,
+                                   PRUint32              aContentIndex,
+                                   nsCOMArray<nsIContent>& aGeneratedContent,
+                                   nsIFrame**            aFrame);
 
-  /**
-   * Create a content node for the given generated content style.
-   * The caller takes care of making it SetNativeAnonymous, binding it
-   * to the document, and creating frames for it.
-   * @param aParentContent is the node that has the before/after style
-   * @param aStyleContext is the 'before' or 'after' pseudo-element
-   * style context
-   * @param aContentIndex is the index of the content item to create
-   */
-  already_AddRefed<nsIContent> CreateGeneratedContent(nsIContent*     aParentContent,
-                                                      nsStyleContext* aStyleContext,
-                                                      PRUint32        aContentIndex);
-
-  void CreateGeneratedContentFrame(nsFrameConstructorState& aState,
-                                   nsIFrame*                aFrame,
-                                   nsIContent*              aContent,
-                                   nsStyleContext*          aStyleContext,
-                                   nsIAtom*                 aPseudoElement,
-                                   nsFrameItems&            aFrameItems);
+  PRBool CreateGeneratedContentFrame(nsFrameConstructorState& aState,
+                                     nsIFrame*                aFrame,
+                                     nsIContent*              aContent,
+                                     nsStyleContext*          aStyleContext,
+                                     nsIAtom*                 aPseudoElement,
+                                     nsIFrame**               aResult);
 
   // This method can change aFrameList: it can chop off the end and
   // put it in a special sibling of aParentFrame.  It can also change
   // aState by moving some floats out of it.
   nsresult AppendFrames(nsFrameConstructorState&       aState,
                         nsIContent*                    aContainer,
                         nsIFrame*                      aParentFrame,
                         nsFrameItems&                  aFrameList,
--- a/layout/base/nsCounterManager.cpp
+++ b/layout/base/nsCounterManager.cpp
@@ -34,21 +34,18 @@
  * 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 ***** */
 
 /* implementation of CSS counters (for numbering things) */
 
 #include "nsCounterManager.h"
-
-#include "nsIFrame.h"
 #include "nsBulletFrame.h" // legacy location for list style type to text code
 #include "nsContentUtils.h"
-#include "nsCSSPseudoElements.h"
 
 // assign the correct |mValueAfter| value to a node that has been inserted
 // Should be called immediately after calling |Insert|.
 void nsCounterUseNode::Calc(nsCounterList *aList)
 {
     NS_ASSERTION(!aList->IsDirty(),
                  "Why are we calculating with a dirty list?");
     mValueAfter = aList->ValueBefore(this);
@@ -91,29 +88,16 @@ nsCounterUseNode::GetText(nsString& aRes
         nsBulletFrame::AppendCounterText(style, n->mValueAfter, aResult);
         if (i == 0)
             break;
         NS_ASSERTION(mAllCounters, "yikes, separator is uninitialized");
         aResult.Append(separator);
     }
 }
 
-// Get the content that determines the scope for aNode. For non-generated
-// content, this is the parent of the element associated with aNode,
-// otherwise it's the mParentContent stored in aNode.
-static nsIContent *
-GetScopeContent(nsCounterNode *aNode)
-{
-  nsIContent *content = aNode->mParentContent;
-  if (!aNode->mPseudoType) {
-    content = content->GetParent();
-  }
-  return content;
-}
-
 void
 nsCounterList::SetScope(nsCounterNode *aNode)
 {
     // This function is responsible for setting |mScopeStart| and
     // |mScopePrev| (whose purpose is described in nsCounterManager.h).
     // We do this by starting from the node immediately preceding
     // |aNode| in content tree order, which is reasonably likely to be
     // the previous element in our scope (or, for a reset, the previous
@@ -123,30 +107,42 @@ nsCounterList::SetScope(nsCounterNode *a
     // appropriate.
 
     if (aNode == First()) {
         aNode->mScopeStart = nsnull;
         aNode->mScopePrev = nsnull;
         return;
     }
 
-    nsIContent *nodeContent = GetScopeContent(aNode);
+    // Get the content node for aNode's rendering object's *parent*,
+    // since scope includes siblings, so we want a descendant check on
+    // parents.  If aNode is for a pseudo-element, then the parent
+    // rendering object is the frame's content; if aNode is for an
+    // element, then the parent rendering object is the frame's
+    // content's parent.
+    nsIContent *nodeContent = aNode->mPseudoFrame->GetContent();
+    if (!aNode->mPseudoFrame->GetStyleContext()->GetPseudoType()) {
+        nodeContent = nodeContent->GetParent();
+    }
 
     for (nsCounterNode *prev = Prev(aNode), *start;
          prev; prev = start->mScopePrev) {
         // If |prev| starts a scope (because it's a real or implied
         // reset), we want it as the scope start rather than the start
         // of its enclosing scope.  Otherwise, there's no enclosing
         // scope, so the next thing in prev's scope shares its scope
         // start.
         start = (prev->mType == nsCounterNode::RESET || !prev->mScopeStart)
                   ? prev : prev->mScopeStart;
 
         // |startContent| is analogous to |nodeContent| (see above).
-        nsIContent *startContent = GetScopeContent(start);
+        nsIContent *startContent = start->mPseudoFrame->GetContent();
+        if (!start->mPseudoFrame->GetStyleContext()->GetPseudoType()) {
+            startContent = startContent->GetParent();
+        }
         NS_ASSERTION(nodeContent || !startContent,
                      "null check on startContent should be sufficient to "
                      "null check nodeContent as well, since if nodeContent "
                      "is for the root, startContent (which is before it) "
                      "must be too");
 
              // A reset's outer scope can't be a scope created by a sibling.
         if (!(aNode->mType == nsCounterNode::RESET &&
@@ -196,63 +192,43 @@ nsCounterList::RecalcAll()
 nsCounterManager::nsCounterManager()
 {
     mNames.Init(16);
 }
 
 PRBool
 nsCounterManager::AddCounterResetsAndIncrements(nsIFrame *aFrame)
 {
-    nsStyleContext *styleContext = aFrame->GetStyleContext();
-    const nsStyleContent *styleContent = styleContext->GetStyleContent();
+    const nsStyleContent *styleContent = aFrame->GetStyleContent();
     if (!styleContent->CounterIncrementCount() &&
         !styleContent->CounterResetCount())
         return PR_FALSE;
 
-    nsIContent *content = aFrame->GetContent();
-    nsIAtom *pseudo =
-      nsGenConNode::ToGeneratedContentType(styleContext->GetPseudoType());
-    if (pseudo) {
-        // The frame with counter increments/resets on it should be either
-        // non-generated content or else a frame for the anonymous
-        // generated content container
-        NS_ASSERTION(content->Tag() ==
-                     (pseudo == nsCSSPseudoElements::before
-                         ? nsGkAtoms::mozgeneratedcontentbefore 
-                         : nsGkAtoms::mozgeneratedcontentafter),
-                     "Not a generated content node");
-        // The parent of the container is the real content node
-        content = content->GetParent();
-    }
-
     // Add in order, resets first, so all the comparisons will be optimized
     // for addition at the end of the list.
     PRInt32 i, i_end;
     PRBool dirty = PR_FALSE;
     for (i = 0, i_end = styleContent->CounterResetCount(); i != i_end; ++i)
-        dirty |= AddResetOrIncrement(content, styleContext, i,
+        dirty |= AddResetOrIncrement(aFrame, i,
                                      styleContent->GetCounterResetAt(i),
                                      nsCounterChangeNode::RESET);
     for (i = 0, i_end = styleContent->CounterIncrementCount(); i != i_end; ++i)
-        dirty |= AddResetOrIncrement(content, styleContext, i,
+        dirty |= AddResetOrIncrement(aFrame, i,
                                      styleContent->GetCounterIncrementAt(i),
                                      nsCounterChangeNode::INCREMENT);
     return dirty;
 }
 
 PRBool
-nsCounterManager::AddResetOrIncrement(nsIContent *aContent,
-                                      nsStyleContext *aStyleContext,
-                                      PRInt32 aIndex,
+nsCounterManager::AddResetOrIncrement(nsIFrame *aFrame, PRInt32 aIndex,
                                       const nsStyleCounterData *aCounterData,
                                       nsCounterNode::Type aType)
 {
     nsCounterChangeNode *node =
-        new nsCounterChangeNode(aContent, aStyleContext, aType,
-                                aCounterData->mValue, aIndex);
+        new nsCounterChangeNode(aFrame, aType, aCounterData->mValue, aIndex);
     if (!node)
         return PR_FALSE;
 
     nsCounterList *counterList = CounterListFor(aCounterData->mCounter);
     if (!counterList) {
         NS_NOTREACHED("CounterListFor failed (should only happen on OOM)");
         return PR_FALSE;
     }
@@ -301,65 +277,59 @@ RecalcDirtyLists(const nsAString& aKey, 
 
 void
 nsCounterManager::RecalcAll()
 {
     mNames.EnumerateRead(RecalcDirtyLists, nsnull);
 }
 
 struct DestroyNodesData {
-    DestroyNodesData(nsIContent* aParentContent, nsIAtom *aPseudo)
-        : mParentContent(aParentContent)
-        , mPseudo(aPseudo)
+    DestroyNodesData(nsIFrame *aFrame)
+        : mFrame(aFrame)
         , mDestroyedAny(PR_FALSE)
     {
     }
 
-    nsIContent *mParentContent;
-    nsIAtom *mPseudo;
+    nsIFrame *mFrame;
     PRBool mDestroyedAny;
 };
 
 PR_STATIC_CALLBACK(PLDHashOperator)
 DestroyNodesInList(const nsAString& aKey, nsCounterList* aList, void* aClosure)
 {
     DestroyNodesData *data = static_cast<DestroyNodesData*>(aClosure);
-    if (aList->DestroyNodesFor(data->mParentContent, data->mPseudo)) {
+    if (aList->DestroyNodesFor(data->mFrame)) {
         data->mDestroyedAny = PR_TRUE;
         aList->SetDirty();
     }
     return PL_DHASH_NEXT;
 }
 
 PRBool
-nsCounterManager::DestroyNodesFor(nsIContent* aParentContent, nsIAtom *aPseudo)
+nsCounterManager::DestroyNodesFor(nsIFrame *aFrame)
 {
-    DestroyNodesData data(aParentContent, aPseudo);
+    DestroyNodesData data(aFrame);
     mNames.EnumerateRead(DestroyNodesInList, &data);
     return data.mDestroyedAny;
 }
 
 #ifdef DEBUG
 PR_STATIC_CALLBACK(PLDHashOperator)
 DumpList(const nsAString& aKey, nsCounterList* aList, void* aClosure)
 {
     printf("Counter named \"%s\":\n", NS_ConvertUTF16toUTF8(aKey).get());
     nsCounterNode *node = aList->First();
 
     if (node) {
         PRInt32 i = 0;
         do {
-            nsCAutoString pseudo;
-            if (node->mPseudoType) {
-              node->mPseudoType->ToUTF8String(pseudo);
-            }
             const char *types[] = { "RESET", "INCREMENT", "USE" };
-            printf("  Node #%d @%p content=%p pseudo=%s index=%d type=%s valAfter=%d\n"
+            printf("  Node #%d @%p frame=%p index=%d type=%s valAfter=%d\n"
                    "       scope-start=%p scope-prev=%p",
-                   i++, (void*)node, (void*)node->mParentContent, pseudo.get(),
+                   i++, (void*)node, (void*)node->mPseudoFrame,
                    node->mContentIndex, types[node->mType], node->mValueAfter,
                    (void*)node->mScopeStart, (void*)node->mScopePrev);
             if (node->mType == nsCounterNode::USE) {
                 nsAutoString text;
                 node->UseNode()->GetText(text);
                 printf(" text=%s", NS_ConvertUTF16toUTF8(text).get());
             }
             printf("\n");
--- a/layout/base/nsCounterManager.h
+++ b/layout/base/nsCounterManager.h
@@ -40,17 +40,16 @@
 
 #ifndef nsCounterManager_h_
 #define nsCounterManager_h_
 
 #include "nsGenConList.h"
 #include "nsAutoPtr.h"
 #include "nsClassHashtable.h"
 
-class nsIFrame;
 class nsCounterList;
 struct nsCounterUseNode;
 struct nsCounterChangeNode;
 
 struct nsCounterNode : public nsGenConNode {
     enum Type {
         RESET,     // a "counter number" pair in 'counter-reset'
         INCREMENT, // a "counter number" pair in 'counter-increment'
@@ -82,25 +81,24 @@ struct nsCounterNode : public nsGenConNo
     // mScopeStart.  Being null for a non-RESET means that it is an
     // implied 'counter-reset'.  Being null for a RESET means it has no
     // outer scope.
     nsCounterNode *mScopePrev;
 
     inline nsCounterUseNode* UseNode();
     inline nsCounterChangeNode* ChangeNode();
 
-    // For RESET and INCREMENT nodes, aStyleContext may have a null
-    // psuedo type, and aContentIndex represents the index within the
+    // For RESET and INCREMENT nodes, aPseudoFrame need not be a
+    // pseudo-element, and aContentIndex represents the index within the
     // 'counter-reset' or 'counter-increment' property instead of within
     // the 'content' property but offset to ensure that (reset,
     // increment, use) sort in that order.  (This slight weirdness
     // allows sharing a lot of code with 'quotes'.)
-    nsCounterNode(nsIContent* aParentContent, nsStyleContext* aStyleContext,
-                  PRInt32 aContentIndex, Type aType)
-        : nsGenConNode(aParentContent, aStyleContext, aContentIndex)
+    nsCounterNode(nsIFrame* aPseudoFrame, PRInt32 aContentIndex, Type aType)
+        : nsGenConNode(aPseudoFrame, aContentIndex)
         , mType(aType)
         , mValueAfter(0)
         , mScopeStart(nsnull)
         , mScopePrev(nsnull)
     {
     }
 
     // to avoid virtual function calls in the common case
@@ -112,20 +110,19 @@ struct nsCounterUseNode : public nsCount
     // containing the values in the counter() or counters() in the order
     // given in the CSS spec.
     nsRefPtr<nsCSSValue::Array> mCounterStyle;
 
     // false for counter(), true for counters()
     PRBool mAllCounters;
 
     // args go directly to member variables here and of nsGenConNode
-    nsCounterUseNode(nsCSSValue::Array* aCounterStyle,
-                     nsIContent* aParentContent, nsStyleContext* aStyleContext,
+    nsCounterUseNode(nsCSSValue::Array* aCounterStyle, nsIFrame* aPseudoFrame,
                      PRUint32 aContentIndex, PRBool aAllCounters)
-        : nsCounterNode(aParentContent, aStyleContext, aContentIndex, USE)
+        : nsCounterNode(aPseudoFrame, aContentIndex, USE)
         , mCounterStyle(aCounterStyle)
         , mAllCounters(aAllCounters)
     {
         NS_ASSERTION(aContentIndex >= 0, "out of range");
     }
 
     // assign the correct |mValueAfter| value to a node that has been inserted
     // Should be called immediately after calling |Insert|.
@@ -133,23 +130,26 @@ struct nsCounterUseNode : public nsCount
 
     // The text that should be displayed for this counter.
     void GetText(nsString& aResult);
 };
 
 struct nsCounterChangeNode : public nsCounterNode {
     PRInt32 mChangeValue; // the numeric value of the increment or reset
 
+    // |aPseudoFrame| is not necessarily a pseudo-element's frame, but
+    // since it is for every other subclass of nsGenConNode, we follow
+    // the naming convention here.
     // |aPropIndex| is the index of the value within the list in the
     // 'counter-increment' or 'counter-reset' property.
-    nsCounterChangeNode(nsIContent* aContent, nsStyleContext* aStyleContext,
+    nsCounterChangeNode(nsIFrame* aPseudoFrame,
                         nsCounterNode::Type aChangeType,
                         PRInt32 aChangeValue,
                         PRInt32 aPropIndex)
-        : nsCounterNode(aContent, aStyleContext,
+        : nsCounterNode(aPseudoFrame,
                         // Fake a content index for resets and increments
                         // that comes before all the real content, with
                         // the resets first, in order, and then the increments.
                         aPropIndex + (aChangeType == RESET
                                         ? (PR_INT32_MIN) 
                                         : (PR_INT32_MIN / 2)),
                         aChangeType)
         , mChangeValue(aChangeValue)
@@ -240,35 +240,29 @@ public:
 
     // Gets the appropriate counter list, creating it if necessary.
     // Returns null only on out-of-memory.
     nsCounterList* CounterListFor(const nsSubstring& aCounterName);
 
     // Clean up data in any dirty counter lists.
     void RecalcAll();
 
-    // Destroy nodes for the given content and pseudo in any lists, and
-    // return whether any nodes were destroyed. aPseudo may be null to
-    // indicate that the counter is an increment or reset directly
-    // associated with the content.
-    PRBool DestroyNodesFor(nsIContent* aParentContent, nsIAtom* aPseudo);
+    // Destroy nodes for the frame in any lists, and return whether any
+    // nodes were destroyed.
+    PRBool DestroyNodesFor(nsIFrame *aFrame);
 
     // Clear all data.
     void Clear() { mNames.Clear(); }
 
 #ifdef DEBUG
     void Dump();
 #endif
-    
-    PRBool IsEmpty() { return mNames.Count() == 0; }
 
 private:
     // for |AddCounterResetsAndIncrements| only
-    PRBool AddResetOrIncrement(nsIContent *aContent,
-                               nsStyleContext *aStyleContext,
-                               PRInt32 aIndex,
+    PRBool AddResetOrIncrement(nsIFrame *aFrame, PRInt32 aIndex,
                                const nsStyleCounterData *aCounterData,
                                nsCounterNode::Type aType);
 
     nsClassHashtable<nsStringHashKey, nsCounterList> mNames;
 };
 
 #endif /* nsCounterManager_h_ */
--- a/layout/base/nsGenConList.cpp
+++ b/layout/base/nsGenConList.cpp
@@ -55,90 +55,91 @@ nsGenConList::Clear()
   }
   delete mFirstNode;
 
   mFirstNode = nsnull;
   mSize = 0;
 }
 
 PRBool
-nsGenConList::DestroyNodesFor(nsIContent* aParentContent, nsIAtom* aPseudo)
+nsGenConList::DestroyNodesFor(nsIFrame* aFrame)
 {
   if (!mFirstNode)
     return PR_FALSE; // list empty
   nsGenConNode* node;
   PRBool destroyed = PR_FALSE;
-  while (mFirstNode->mParentContent == aParentContent &&
-         mFirstNode->mPseudoType == aPseudo) {
+  while (mFirstNode->mPseudoFrame == aFrame) {
     destroyed = PR_TRUE;
     node = Next(mFirstNode);
     PRBool isLastNode = node == mFirstNode; // before they're dangling
     Remove(mFirstNode);
     delete mFirstNode;
     if (isLastNode) {
       mFirstNode = nsnull;
       return PR_TRUE;
     }
     else {
       mFirstNode = node;
     }
   }
   node = Next(mFirstNode);
   while (node != mFirstNode) {
-    if (node->mParentContent == aParentContent &&
-        node->mPseudoType == aPseudo) {
+    if (node->mPseudoFrame == aFrame) {
       destroyed = PR_TRUE;
       nsGenConNode *nextNode = Next(node);
       Remove(node);
       delete node;
       node = nextNode;
     } else {
       node = Next(node);
     }
   }
   return destroyed;
 }
 
 // return -1 for ::before, +1 for ::after, and 0 otherwise.
-inline PRInt32 PseudoCompareType(nsIAtom *aPseudo)
+inline PRInt32 PseudoCompareType(nsIFrame *aFrame)
 {
-  if (aPseudo == nsCSSPseudoElements::before)
+  nsIAtom *pseudo = aFrame->GetStyleContext()->GetPseudoType();
+  if (pseudo == nsCSSPseudoElements::before)
     return -1;
-  if (aPseudo == nsCSSPseudoElements::after)
+  if (pseudo == nsCSSPseudoElements::after)
     return 1;
   return 0;
 }
 
 /* static */ PRBool
 nsGenConList::NodeAfter(const nsGenConNode* aNode1, const nsGenConNode* aNode2)
 {
-  nsIContent *content1 = aNode1->mParentContent;
-  nsIContent *content2 = aNode2->mParentContent;
-  PRInt32 pseudoType1 = PseudoCompareType(aNode1->mPseudoType);
-  PRInt32 pseudoType2 = PseudoCompareType(aNode2->mPseudoType);
-  if (content1 == content2 && pseudoType1 == pseudoType2) {
+  nsIFrame *frame1 = aNode1->mPseudoFrame;
+  nsIFrame *frame2 = aNode2->mPseudoFrame;
+  if (frame1 == frame2) {
     NS_ASSERTION(aNode2->mContentIndex != aNode1->mContentIndex, "identical");
     return aNode1->mContentIndex > aNode2->mContentIndex;
   }
+  PRInt32 pseudoType1 = PseudoCompareType(frame1);
+  PRInt32 pseudoType2 = PseudoCompareType(frame2);
+  nsIContent *content1 = frame1->GetContent();
+  nsIContent *content2 = frame2->GetContent();
   if (pseudoType1 == 0 || pseudoType2 == 0) {
     if (content1 == content2) {
       NS_ASSERTION(pseudoType1 != pseudoType2, "identical");
       return pseudoType2 == 0;
     }
     // We want to treat an element as coming before its :before (preorder
     // traversal), so treating both as :before now works.
     if (pseudoType1 == 0) pseudoType1 = -1;
     if (pseudoType2 == 0) pseudoType2 = -1;
   } else {
     if (content1 == content2) {
       NS_ASSERTION(pseudoType1 != pseudoType2, "identical");
       return pseudoType1 == 1;
     }
   }
-  // XXX This doesn't handle anonymous (XBL) content properly.
+  // XXX Switch to the frame version of DoCompareTreePosition?
   PRInt32 cmp = nsLayoutUtils::DoCompareTreePosition(content1, content2,
                                                      pseudoType1, -pseudoType2);
   NS_ASSERTION(cmp != 0, "same content, different frames");
   return cmp > 0;
 }
 
 void
 nsGenConList::Insert(nsGenConNode* aNode)
--- a/layout/base/nsGenConList.h
+++ b/layout/base/nsGenConList.h
@@ -35,62 +35,57 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 /* base class for nsCounterList and nsQuoteList */
 
 #ifndef nsGenConList_h___
 #define nsGenConList_h___
 
-#include "nsIContent.h"
+#include "nsIFrame.h"
 #include "nsStyleStruct.h"
-#include "nsStyleContext.h"
 #include "prclist.h"
 #include "nsIDOMCharacterData.h"
 #include "nsCSSPseudoElements.h"
 
 struct nsGenConNode : public PRCList {
-  // The content associated with this node. This is never a generated
-  // content element. When mPseudoType is non-null, this is the element
-  // for which we generated the anonymous content. If mPseudoType is null,
-  // this is the element associated with a counter reset or increment.  
-  nsIContent* mParentContent;
-  // nsGkAtoms::before, nsGkAtoms::after, or null
-  nsIAtom*    mPseudoType;
+  // The wrapper frame for all of the pseudo-element's content.  This
+  // frame generally has useful style data and has the
+  // NS_FRAME_GENERATED_CONTENT bit set (so we use it to track removal),
+  // but does not necessarily for |nsCounterChangeNode|s.
+  nsIFrame* const mPseudoFrame;
 
   // Index within the list of things specified by the 'content' property,
   // which is needed to do 'content: open-quote open-quote' correctly,
   // and needed for similar cases for counters.
   const PRInt32 mContentIndex;
 
   // null for 'content:no-open-quote', 'content:no-close-quote' and for
   // counter nodes for increments and resets (rather than uses)
   nsCOMPtr<nsIDOMCharacterData> mText;
 
-  static nsIAtom* ToGeneratedContentType(nsIAtom* aPseudoType)
-  {
-    if (aPseudoType == nsCSSPseudoElements::before ||
-        aPseudoType == nsCSSPseudoElements::after)
-      return aPseudoType;
-    return nsnull;
-  }
-  
-  nsGenConNode(nsIContent* aParentContent, nsStyleContext* aStyleContext,
-               PRInt32 aContentIndex)
-    : mParentContent(aParentContent)
-    , mPseudoType(ToGeneratedContentType(aStyleContext->GetPseudoType()))
+  nsGenConNode(nsIFrame* aPseudoFrame, PRInt32 aContentIndex)
+    : mPseudoFrame(aPseudoFrame)
     , mContentIndex(aContentIndex)
   {
     NS_ASSERTION(aContentIndex <
-                 PRInt32(aStyleContext->GetStyleContent()->ContentCount()),
+                   PRInt32(aPseudoFrame->GetStyleContent()->ContentCount()),
                  "index out of range");
     // We allow negative values of mContentIndex for 'counter-reset' and
     // 'counter-increment'.
-    NS_ASSERTION(aContentIndex < 0 || mPseudoType,
+
+    NS_ASSERTION(aContentIndex < 0 ||
+                 aPseudoFrame->GetStyleContext()->GetPseudoType() ==
+                   nsCSSPseudoElements::before ||
+                 aPseudoFrame->GetStyleContext()->GetPseudoType() ==
+                   nsCSSPseudoElements::after,
                  "not :before/:after generated content and not counter change");
+    NS_ASSERTION(aContentIndex < 0 ||
+                 aPseudoFrame->GetStateBits() & NS_FRAME_GENERATED_CONTENT,
+                 "not generated content and not counter change");
   }
 
   virtual ~nsGenConNode() {} // XXX Avoid, perhaps?
 };
 
 class nsGenConList {
 protected:
   nsGenConNode* mFirstNode;
@@ -101,21 +96,18 @@ public:
   void Clear();
   static nsGenConNode* Next(nsGenConNode* aNode) {
     return static_cast<nsGenConNode*>(PR_NEXT_LINK(aNode));
   }
   static nsGenConNode* Prev(nsGenConNode* aNode) {
     return static_cast<nsGenConNode*>(PR_PREV_LINK(aNode));
   }
   void Insert(nsGenConNode* aNode);
-  /**
-   * Destroy all nodes whose aContent/aPseudo match.
-   * @return true if some nodes were destroyed
-   */
-  PRBool DestroyNodesFor(nsIContent* aParentContent, nsIAtom* aPseudo);
+  // returns whether any nodes have been destroyed
+  PRBool DestroyNodesFor(nsIFrame* aFrame); //destroy all nodes with aFrame as parent
 
   // Return true if |aNode1| is after |aNode2|.
   static PRBool NodeAfter(const nsGenConNode* aNode1,
                           const nsGenConNode* aNode2);
 
   void Remove(nsGenConNode* aNode) { PR_REMOVE_LINK(aNode); mSize--; }
   PRBool IsLast(nsGenConNode* aNode) { return (Next(aNode) == mFirstNode); }
 };
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -243,29 +243,22 @@ nsLayoutUtils::IsGeneratedContentFor(nsI
                                      nsIAtom* aPseudoElement)
 {
   NS_PRECONDITION(aFrame, "Must have a frame");
   NS_PRECONDITION(aPseudoElement, "Must have a pseudo name");
 
   if (!aFrame->IsGeneratedContentFrame()) {
     return PR_FALSE;
   }
-  nsIFrame* parent = aFrame->GetParent();
-  NS_ASSERTION(parent, "Generated content can't be root frame");
-  if (parent->IsGeneratedContentFrame()) {
-    // Not the root of the generated content
-    return PR_FALSE;
-  }
   
-  if (aContent && parent->GetContent() != aContent) {
+  if (aContent && aFrame->GetContent() != aContent) {
     return PR_FALSE;
   }
 
-  return (aFrame->GetContent()->Tag() == nsGkAtoms::mozgeneratedcontentbefore) ==
-    (aPseudoElement == nsCSSPseudoElements::before);
+  return aFrame->GetStyleContext()->GetPseudoType() == aPseudoElement;
 }
 
 // static
 nsIFrame*
 nsLayoutUtils::GetCrossDocParentFrame(nsIFrame* aFrame)
 {
   nsIFrame* p = aFrame->GetParent();
   if (p)
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -66,27 +66,27 @@ class nsBlockFrame;
  * nsLayoutUtils is a namespace class used for various helper
  * functions that are useful in multiple places in layout.  The goal
  * is not to define multiple copies of the same static helper.
  */
 class nsLayoutUtils
 {
 public:
   /**
-   * GetBeforeFrame returns the outermost :before frame of the given frame, if
+   * GetBeforeFrame returns the :before frame of the given frame, if
    * one exists.  This is typically O(1).  The frame passed in must be
    * the first-in-flow.   
    *
    * @param aFrame the frame whose :before is wanted
    * @return the :before frame or nsnull if there isn't one
    */
   static nsIFrame* GetBeforeFrame(nsIFrame* aFrame);
 
   /**
-   * GetAfterFrame returns the outermost :after frame of the given frame, if one
+   * GetAfterFrame returns the :after frame of the given frame, if one
    * exists.  This will walk the in-flow chain to the last-in-flow if
    * needed.  This function is typically O(N) in the number of child
    * frames, following in-flows, etc.
    *
    * @param aFrame the frame whose :after is wanted
    * @return the :after frame or nsnull if there isn't one
    */
   static nsIFrame* GetAfterFrame(nsIFrame* aFrame);
@@ -111,21 +111,18 @@ public:
    *         such ancestor exists
    */
   static nsIFrame* GetPageFrame(nsIFrame* aFrame)
   {
     return GetClosestFrameOfType(aFrame, nsGkAtoms::pageFrame);
   }
 
   /**
-   * IsGeneratedContentFor returns PR_TRUE if aFrame is the outermost
-   * frame for generated content of type aPseudoElement for aContent.
-   * aFrame *might not* have the aPseudoElement pseudo-style! For example
-   * it might be a table outer frame and the inner table frame might
-   * have the pseudo-style.
+   * IsGeneratedContentFor returns PR_TRUE if aFrame is generated
+   * content of type aPseudoElement for aContent
    *
    * @param aContent the content node we're looking at.  If this is
    *        null, then we just assume that aFrame has the right content
    *        pointer.
    * @param aFrame the frame we're looking at
    * @param aPseudoElement the pseudo type we're interested in
    * @return whether aFrame is the generated aPseudoElement frame for aContent
    */
--- a/layout/base/nsQuoteList.cpp
+++ b/layout/base/nsQuoteList.cpp
@@ -42,17 +42,17 @@
 #include "nsReadableUtils.h"
 
 const nsString*
 nsQuoteNode::Text()
 {
   NS_ASSERTION(mType == eStyleContentType_OpenQuote ||
                mType == eStyleContentType_CloseQuote,
                "should only be called when mText should be non-null");
-  const nsStyleQuotes* styleQuotes = mStyleContext->GetStyleQuotes();
+  const nsStyleQuotes* styleQuotes = mPseudoFrame->GetStyleQuotes();
   PRInt32 quotesCount = styleQuotes->QuotesCount(); // 0 if 'quotes:none'
   PRInt32 quoteDepth = Depth();
 
   // Reuse the last pair when the depth is greater than the number of
   // pairs of quotes.  (Also make 'quotes: none' and close-quote from
   // a depth of 0 equivalent for the next test.)
   if (quoteDepth >= quotesCount)
     quoteDepth = quotesCount - 1;
--- a/layout/base/nsQuoteList.h
+++ b/layout/base/nsQuoteList.h
@@ -36,33 +36,30 @@
  * ***** END LICENSE BLOCK ***** */
 
 /* implementation of quotes for the CSS 'content' property */
 
 #ifndef nsQuoteList_h___
 #define nsQuoteList_h___
 
 #include "nsGenConList.h"
-#include "nsStyleContext.h"
 
 struct nsQuoteNode : public nsGenConNode {
   // open-quote, close-quote, no-open-quote, or no-close-quote
   const nsStyleContentType mType;
 
   // Quote depth before this quote, which is always non-negative.
   PRInt32 mDepthBefore;
 
-  nsRefPtr<nsStyleContext> mStyleContext;
 
-  nsQuoteNode(nsStyleContentType& aType, nsIContent* aContentParent,
-              PRUint32 aContentIndex, nsStyleContext* aStyleContext)
-    : nsGenConNode(aContentParent, aStyleContext, aContentIndex)
+  nsQuoteNode(nsStyleContentType& aType, nsIFrame* aPseudoFrame,
+              PRUint32 aContentIndex)
+    : nsGenConNode(aPseudoFrame, aContentIndex)
     , mType(aType)
     , mDepthBefore(0)
-    , mStyleContext(aStyleContext)
   {
     NS_ASSERTION(aType == eStyleContentType_OpenQuote ||
                  aType == eStyleContentType_CloseQuote ||
                  aType == eStyleContentType_NoOpenQuote ||
                  aType == eStyleContentType_NoCloseQuote,
                  "incorrect type");
     NS_ASSERTION(aContentIndex >= 0, "out of range");
   }
--- a/layout/generic/nsHTMLParts.h
+++ b/layout/generic/nsHTMLParts.h
@@ -84,18 +84,17 @@ class nsTableColFrame;
 
 // These are variations on AreaFrame with slightly different layout
 // policies.
 
 // Create a frame that supports "display: block" layout behavior
 nsIFrame*
 NS_NewBlockFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, PRUint32 aFlags = 0);
 
-// Special Generated Content Node. It contains text taken from an
-// attribute of its *grandparent* content node. 
+// Special Generated Content Frame
 nsresult
 NS_NewAttributeContent(nsNodeInfoManager *aNodeInfoManager,
                        PRInt32 aNameSpaceID, nsIAtom* aAttrName,
                        nsIContent** aResult);
 
 // Create a basic area frame but the GetFrameForPoint is overridden to always
 // return the option frame 
 // By default, area frames will extend
--- a/layout/generic/nsTextFrameThebes.cpp
+++ b/layout/generic/nsTextFrameThebes.cpp
@@ -2920,17 +2920,17 @@ nsTextPaintStyle::InitCommonColors()
                   NS_LUMINOSITY_DIFFERENCE(defaultWindowBackgroundColor,
                                            selectionBGColor));
 
   mInitCommonColors = PR_TRUE;
 }
 
 static nsIFrame* GetNonGeneratedAncestor(nsIFrame* f) {
   while (f->GetStateBits() & NS_FRAME_GENERATED_CONTENT) {
-    f = nsLayoutUtils::GetParentOrPlaceholderFor(f->PresContext()->FrameManager(), f);
+    f = f->GetParent();
   }
   return f;
 }
 
 static nsIContent*
 FindElementAncestor(nsINode* aNode)
 {
   while (aNode && !aNode->IsNodeOfType(nsINode::eELEMENT)) {
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/374193-1xbl.xml
@@ -0,0 +1,10 @@
+<bindings xmlns="http://www.mozilla.org/xbl">
+<binding id="a">
+<implementation>
+<constructor>
+  this.style.position='fixed';
+</constructor>
+</implementation>
+<content><children/></content>
+</binding>
+</bindings>
--- a/layout/reftests/bugs/380842-1-ref.html
+++ b/layout/reftests/bugs/380842-1-ref.html
@@ -26,19 +26,19 @@
     Text
     <div>After</div>
   </div>
   After inline-block
 </div>
 <div>
   Before span
   <span>
-    <div>Before</div>
+    <span>Before</span>
     Text
-    <div>After</div>
+    <span>After</span>
   </span>
   After span
 </div>
 <div>
   <div>Before</div>
   Text
   <div>After</div>
 </div>
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -495,16 +495,17 @@ random-if(MOZ_WIDGET_TOOLKIT=="gtk2") ==
 == 373381-1.html 373381-1-ref.html
 == 375508-1.html 375508-1-ref.html
 == 373433-1.html 373433-1-ref.html
 == 372062-1.html 372062-1-ref.html
 == 372768-1.html 372768-1-ref.html
 == 373383-1.html 373383-1-ref.html
 == 374038-1.xul 374038-1-ref.xul
 == 374038-2.xul 374038-2-ref.xul
+== 374193-1.xhtml about:blank
 fails == 374927-1.html 374927-1-ref.html # Was broken by patch for bug 368600; fails until bug 400776 is fixed
 == 375716-1.html 375716-1-ref.html
 == 375827-1.html 375827-1-ref.html
 == 376375-1.html 376375-1-ref.html
 == 376484-1.html 376484-1-ref.html
 == 376532-1.html 376532-1-ref.html
 != 376532-2.html 376532-2-ref.html
 != 376532-3.html 376532-3-ref.html
--- a/layout/reftests/reftest.list
+++ b/layout/reftests/reftest.list
@@ -30,19 +30,16 @@ include bugs/reftest.list
 include canvas/reftest.list
 
 # columns/
 include columns/reftest.list
 
 # counters/
 include counters/reftest.list
 
-# generated-content/
-include generated-content/reftest.list
-
 # first-letter/
 include first-letter/reftest.list
 
 # first-line/
 include first-line/reftest.list
 
 # forms
 include forms/reftest.list
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -3302,17 +3302,60 @@ nsRuleNode::ComputeDisplayData(void* aSt
     if (fullAuto) {
       display->mClipFlags |= NS_STYLE_CLIP_AUTO;
     }
     else {
       display->mClipFlags |= NS_STYLE_CLIP_RECT;
     }
   }
 
-  if (display->mDisplay != NS_STYLE_DISPLAY_NONE) {
+  // CSS2 specified fixups:
+  if (generatedContent) {
+    // According to CSS2 section 12.1, :before and :after
+    // pseudo-elements must not be positioned or floated (CSS2 12.1) and
+    // must be limited to certain display types (depending on the
+    // display type of the element to which they are attached).
+    // XXX These restrictions are no longer present in CSS2.1.  We
+    // should ensure that we support removing them before doing so,
+    // though.
+    // XXXbz For example, the calls to WipeContainingBlock in the
+    // frame constructor will need to be changedif we allow
+    // block-level generated content inside inlines.
+
+    if (display->mPosition != NS_STYLE_POSITION_STATIC)
+      display->mPosition = NS_STYLE_POSITION_STATIC;
+    if (display->mFloats != NS_STYLE_FLOAT_NONE)
+      display->mFloats = NS_STYLE_FLOAT_NONE;
+
+    PRUint8 displayValue = display->mDisplay;
+    if (displayValue != NS_STYLE_DISPLAY_NONE &&
+        displayValue != NS_STYLE_DISPLAY_INLINE &&
+        displayValue != NS_STYLE_DISPLAY_INLINE_BLOCK) {
+      inherited = PR_TRUE;
+      if (parentDisplay->IsBlockOutside() ||
+          parentDisplay->mDisplay == NS_STYLE_DISPLAY_INLINE_BLOCK ||
+          parentDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_CELL ||
+          parentDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_CAPTION) {
+        // If the subject of the selector is a block-level element,
+        // allowed values are 'none', 'inline', 'block', and 'marker'.
+        // If the value of the 'display' has any other value, the
+        // pseudo-element will behave as if the value were 'block'.
+        if (displayValue != NS_STYLE_DISPLAY_BLOCK &&
+            displayValue != NS_STYLE_DISPLAY_MARKER)
+          display->mDisplay = NS_STYLE_DISPLAY_BLOCK;
+      } else {
+        // If the subject of the selector is an inline-level element,
+        // allowed values are 'none' and 'inline'. If the value of the
+        // 'display' has any other value, the pseudo-element will behave
+        // as if the value were 'inline'.
+        display->mDisplay = NS_STYLE_DISPLAY_INLINE;
+      }
+    }
+  }
+  else if (display->mDisplay != NS_STYLE_DISPLAY_NONE) {
     // CSS2 9.7 specifies display type corrections dealing with 'float'
     // and 'position'.  Since generated content can't be floated or
     // positioned, we can deal with it here.
 
     if (nsCSSPseudoElements::firstLetter == pseudoTag) {
       // a non-floating first-letter must be inline
       // XXX this fix can go away once bug 103189 is fixed correctly
       display->mDisplay = NS_STYLE_DISPLAY_INLINE;