Bug 238072. Make generated content take the normal frame construction path. Relanding with changes so that counter and quote nodes are initialized after their frames are available, more like the old code. r+sr=dbaron,bzbarsky
authorRobert O'Callahan <robert@ocallahan.org>
Mon, 18 Aug 2008 13:16:42 +1200
changeset 16976 a159ad4bdb90f0214317ff803c8dd66e32a25515
parent 16975 984186e35427ef30455dbb1639154adf113f0bd0
child 16977 c771615414feb1273ff4db0f197e7437b61df8df
push idunknown
push userunknown
push dateunknown
bugs238072
milestone1.9.1a2pre
Bug 238072. Make generated content take the normal frame construction path. Relanding with changes so that counter and quote nodes are initialized after their frames are available, more like the old code. r+sr=dbaron,bzbarsky
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-1.xhtml
layout/reftests/bugs/374193-1xbl.xml
layout/reftests/bugs/380842-1-ref.html
layout/reftests/bugs/reftest.list
layout/reftests/generated-content/reftest.list
layout/reftests/generated-content/table-parts-01-ref.html
layout/reftests/generated-content/table-parts-01.html
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) {
+  if (imageState & (NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_USERDISABLED)) {
     // 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,16 +58,18 @@
 #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")
@@ -1610,16 +1612,19 @@ GK_ATOM(overflowPlaceholdersProperty, "O
 GK_ATOM(rowUnpaginatedHeightProperty, "RowUnpaginatedHeightProperty")  // nscoord*
 GK_ATOM(spaceManagerProperty, "SpaceManagerProperty")      // the space manager for a block
 GK_ATOM(tabWidthProperty, "TabWidthProperty")              // nsTArray<TabSetting>* array of tab widths
 GK_ATOM(tableBCProperty, "TableBCProperty")                // table border collapsing info (e.g. damage area, table border widths)
 GK_ATOM(usedMarginProperty, "UsedMarginProperty") // nsMargin*
 GK_ATOM(usedPaddingProperty, "UsedPaddingProperty") // nsMargin*
 GK_ATOM(viewProperty, "ViewProperty")                      
 
+// Content property names
+GK_ATOM(genConInitializerProperty, "QuoteNodeProperty")
+
 // Languages for lang-specific transforms
 GK_ATOM(Japanese, "ja")
 GK_ATOM(Chinese, "zh-CN")
 GK_ATOM(Taiwanese, "zh-TW")
 GK_ATOM(HongKongChinese, "zh-HK")
 GK_ATOM(Unicode, "x-unicode")
 
 // Names for editor transactions
--- a/content/base/src/nsTextNode.cpp
+++ b/content/base/src/nsTextNode.cpp
@@ -88,25 +88,26 @@ 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() {
-    DetachListener();
+    NS_ASSERTION(!mGrandparent, "We were not unbound!");
   }
 
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                               nsIContent* aBindingParent,
                               PRBool aCompileEventHandlers);
   virtual void UnbindFromTree(PRBool aDeep = PR_TRUE,
                               PRBool aNullParent = PR_TRUE);
 
@@ -129,19 +130,21 @@ public:
   void UpdateText() {
     UpdateText(PR_TRUE);
   }
 
 private:
   // Update our text to our parent's current attr value
   void UpdateText(PRBool aNotify);
 
-  // Detach ourselves as an attribute listener.
-  void DetachListener();
-
+  // 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;
   // What attribute we're showing
   PRInt32 mNameSpaceID;
   nsCOMPtr<nsIAtom> mAttrName;
 };
 
 nsresult
 NS_NewTextNode(nsIContent** aInstancePtrResult,
                nsNodeInfoManager *aNodeInfoManager)
@@ -296,77 +299,71 @@ NS_NewAttributeContent(nsNodeInfoManager
 NS_IMPL_ISUPPORTS_INHERITED1(nsAttributeTextNode, nsTextNode,
                              nsIMutationObserver)
 
 nsresult
 nsAttributeTextNode::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                                 nsIContent* aBindingParent,
                                 PRBool aCompileEventHandlers)
 {
-  NS_PRECONDITION(aParent, "This node can't be a child of the document");
+  NS_PRECONDITION(aParent && aParent->GetParent(),
+                  "This node can't be a child of the document or of the document root");
 
   nsresult rv = nsTextNode::BindToTree(aDocument, aParent,
                                        aBindingParent, aCompileEventHandlers);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsINode* parent = GetNodeParent();
-  NS_ENSURE_TRUE(parent, NS_ERROR_UNEXPECTED);
-
-  parent->AddMutationObserver(this);
+  NS_ASSERTION(!mGrandparent, "We were already bound!");
+  mGrandparent = aParent->GetParent();
+  mGrandparent->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)
 {
-  // Detach as listener while we know who our parent is!
-  if (aNullParent) {
-    DetachListener();
+  // 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;
   }
   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 == GetNodeParent()) {
+      aContent == mGrandparent) {
     // 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 parent
-    // when it fires, and will do nothing.    
+    // unbound while the event is up that's ok -- we'll just have no
+    // grandparent 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)
 {
-  nsIContent* parent = GetParent();
-  if (parent) {
+  if (mGrandparent) {
     nsAutoString attrValue;
-    parent->GetAttr(mNameSpaceID, mAttrName, attrValue);
+    mGrandparent->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,16 +51,17 @@ 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
@@ -1109,16 +1109,19 @@ 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);
@@ -1213,17 +1216,18 @@ nsFrameConstructorState::nsFrameConstruc
     mPopupItems(mRootBox ? mRootBox->GetPopupSetFrame() : nsnull),
 #endif
     mFixedItems(aFixedContainingBlock),
     mAbsoluteItems(aAbsoluteContainingBlock),
     mFloatedItems(aFloatContainingBlock),
     mFirstLetterStyle(PR_FALSE),
     mFirstLineStyle(PR_FALSE),
     mFrameState(aHistoryState),
-    mPseudoFrames()
+    mPseudoFrames(),
+    mAdditionalStateBits(0)
 {
   MOZ_COUNT_CTOR(nsFrameConstructorState);
 }
 
 nsFrameConstructorState::nsFrameConstructorState(nsIPresShell* aPresShell,
                                                  nsIFrame*     aFixedContainingBlock,
                                                  nsIFrame*     aAbsoluteContainingBlock,
                                                  nsIFrame*     aFloatContainingBlock)
@@ -1234,17 +1238,18 @@ 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()
+    mPseudoFrames(),
+    mAdditionalStateBits(0)
 {
   MOZ_COUNT_CTOR(nsFrameConstructorState);
   mFrameState = aPresShell->GetDocument()->GetLayoutHistoryState();
 }
 
 nsFrameConstructorState::~nsFrameConstructorState()
 {
   // Frame order comparison functions only work properly when the placeholders
@@ -1425,16 +1430,17 @@ 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");
   }
@@ -1892,430 +1898,287 @@ nsCSSFrameConstructor::NotifyDestroyingF
   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;
+struct nsGenConInitializer {
+  nsAutoPtr<nsGenConNode> mNode;
+  nsGenConList*           mList;
+  void (nsCSSFrameConstructor::*mDirtyAll)();
+  
+  nsGenConInitializer(nsGenConNode* aNode, nsGenConList* aList,
+                      void (nsCSSFrameConstructor::*aDirtyAll)())
+    : mNode(aNode), mList(aList), mDirtyAll(aDirtyAll) {}
+};
+
+static void
+DestroyGenConInitializer(void*    aFrame,
+                         nsIAtom* aPropertyName,
+                         void*    aPropertyValue,
+                         void*    aDtorData)
+{
+  delete static_cast<nsGenConInitializer*>(aPropertyValue);
+}
+
+already_AddRefed<nsIContent>
+nsCSSFrameConstructor::CreateGenConTextNode(const nsString& aString,
+                                            nsCOMPtr<nsIDOMCharacterData>* aText,
+                                            nsGenConInitializer* aInitializer)
+{
   nsCOMPtr<nsIContent> content;
-  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;
-
+  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);
+  }
+  if (aInitializer) {
+    content->SetProperty(nsGkAtoms::genConInitializerProperty, aInitializer,
+                         DestroyGenConInitializer);
+  }
+  return content.forget();
+}
+
+already_AddRefed<nsIContent>
+nsCSSFrameConstructor::CreateGeneratedContent(nsIContent*     aParentContent,
+                                              nsStyleContext* aStyleContext,
+                                              PRUint32        aContentIndex)
+{
   // Get the content value
-  const nsStyleContentData &data = aStyleContent->ContentAt(aContentIndex);
-  nsStyleContentType  type = data.mType;
-
-  nsCOMPtr<nsIContent> content;
+  const nsStyleContentData &data =
+    aStyleContext->GetStyleContent()->ContentAt(aContentIndex);
+  nsStyleContentType type = data.mType;
 
   if (eStyleContentType_Image == type) {
     if (!data.mContent.mImage) {
       // CSS had something specified that couldn't be converted to an
       // image object
-      return NS_ERROR_FAILURE;
+      return nsnull;
     }
     
     // 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_None,
+                                              kNameSpaceID_XHTML,
                                               getter_AddRefs(nodeInfo));
 
-    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 {
+    nsCOMPtr<nsIContent> content;
+    NS_NewGenConImageContent(getter_AddRefs(content), nodeInfo,
+                             data.mContent.mImage);
+    return content.forget();
+  }
+
+  switch (type) {
+  case eStyleContentType_String:
+    return CreateGenConTextNode(nsDependentString(data.mContent.mString), nsnull,
+                                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()) {
           attrName = do_GetAtom(contentString);
         }
-
-        if (!attrName) {
-          return NS_ERROR_OUT_OF_MEMORY;
+      }
+      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, aContentIndex,
+                             type == eStyleContentType_Counters);
+      if (!node)
+        return nsnull;
+
+      nsGenConInitializer* initializer =
+        new nsGenConInitializer(node, counterList,
+                                &nsCSSFrameConstructor::CountersDirty);
+      return CreateGenConTextNode(EmptyString(), &node->mText, initializer);
+    }
+
+  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, aContentIndex);
+      if (!node)
+        return nsnull;
+
+      nsGenConInitializer* initializer =
+        new nsGenConInitializer(node, &mQuoteList,
+                                &nsCSSFrameConstructor::QuotesDirty);
+      return CreateGenConTextNode(EmptyString(), &node->mText, initializer);
+    }
+  
+  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();
         }
 
-        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();
-          }
-        }
-
-        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();
-      }
+        nsXPIDLString temp;
+        nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
+                                           "Submit", temp);
+        return CreateGenConTextNode(temp, nsnull, nsnull);
+      }
+
       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.
-        }
-        NS_ENSURE_SUCCESS(rv, rv);
-      }
-      break;
-    } // 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;
+    }
+  } // 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);
 }
 
 /*
- *
- * aFrame - the frame that should be the parent of the generated
+ * aParentFrame - 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.
  */
-PRBool
+void
 nsCSSFrameConstructor::CreateGeneratedContentFrame(nsFrameConstructorState& aState,
-                                                   nsIFrame*        aFrame,
-                                                   nsIContent*      aContent,
+                                                   nsIFrame*        aParentFrame,
+                                                   nsIContent*      aParentContent,
                                                    nsStyleContext*  aStyleContext,
                                                    nsIAtom*         aPseudoElement,
-                                                   nsIFrame**       aResult)
-{
-  *aResult = nsnull; // initialize OUT parameter
-
-  if (!aContent->IsNodeOfType(nsINode::eELEMENT))
-    return PR_FALSE;
+                                                   nsFrameItems&    aFrameItems)
+{
+  if (!aParentContent->IsNodeOfType(nsINode::eELEMENT))
+    return;
 
   nsStyleSet *styleSet = mPresShell->StyleSet();
 
   // Probe for the existence of the pseudo-element
   nsRefPtr<nsStyleContext> pseudoStyleContext;
-  pseudoStyleContext = styleSet->ProbePseudoStyleFor(aContent,
+  pseudoStyleContext = styleSet->ProbePseudoStyleFor(aParentContent,
                                                      aPseudoElement,
                                                      aStyleContext);
-
-  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;
+  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;
 }
 
 nsresult
 nsCSSFrameConstructor::CreateInputFrame(nsFrameConstructorState& aState,
                                         nsIContent*              aContent,
                                         nsIFrame*                aParentFrame,
                                         nsIAtom*                 aTag,
                                         nsStyleContext*          aStyleContext,
@@ -3629,17 +3492,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_FALSE, childItems,
+    rv = ProcessChildren(aState, aContent, aNewInnerFrame, PR_TRUE, 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);
 
@@ -3758,17 +3621,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_FALSE, childItems,
+    rv = ProcessChildren(aState, aContent, aNewFrame, PR_TRUE, 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);
 
@@ -3819,17 +3682,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_FALSE, childItems,
+    rv = ProcessChildren(aState, aContent, aNewFrame, PR_TRUE, childItems,
                          PR_FALSE);
     if (NS_FAILED(rv)) return rv;
     aNewFrame->SetInitialChildList(nsnull, childItems.childList);
     if (aIsPseudoParent) {
       aState.mPseudoFrames.mTableInner.mChildList.AddChild(aNewFrame);
     }
   }
 
@@ -3875,17 +3738,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_FALSE, childItems,
+    rv = ProcessChildren(aState, aContent, aNewFrame, PR_TRUE, 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) {
@@ -4079,19 +3942,25 @@ nsCSSFrameConstructor::ConstructTableCel
 
   return rv;
 }
 
 static PRBool 
 NeedFrameFor(nsIFrame*   aParentFrame,
              nsIContent* aChildContent) 
 {
-  // don't create a whitespace frame if aParentFrame doesn't want it
-  return !aParentFrame->IsFrameOfType(nsIFrame::eExcludesIgnorableWhitespace) ||
-         !TextIsOnlyWhitespace(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();
 }
 
 const nsStyleDisplay* 
 nsCSSFrameConstructor::GetDisplay(nsIFrame* aFrame)
 {
   if (nsnull == aFrame) {
     return nsnull;
   }
@@ -5250,16 +5119,30 @@ nsCSSFrameConstructor::ConstructFieldSet
   aNewFrame = newFrame; 
 
   // yes we have already initialized our frame 
   aFrameHasBeenInitialized = PR_TRUE; 
 
   return NS_OK;
 }
 
+static nsIFrame*
+FindAncestorWithGeneratedContentPseudo(nsIFrame* aFrame)
+{
+  for (nsIFrame* f = aFrame->GetParent(); f; f = f->GetParent()) {
+    NS_ASSERTION(f->IsGeneratedContentFrame(),
+                 "should not have exited generated content");
+    nsIAtom* pseudo = f->GetStyleContext()->GetPseudoType();
+    if (pseudo == nsCSSPseudoElements::before ||
+        pseudo == nsCSSPseudoElements::after)
+      return f;
+  }
+  return nsnull;
+}
+
 nsresult
 nsCSSFrameConstructor::ConstructTextFrame(nsFrameConstructorState& aState,
                                           nsIContent*              aContent,
                                           nsIFrame*                aParentFrame,
                                           nsStyleContext*          aStyleContext,
                                           nsFrameItems&            aFrameItems,
                                           PRBool                   aPseudoParent)
 {
@@ -5278,39 +5161,49 @@ nsCSSFrameConstructor::ConstructTextFram
       CallQueryInterface(ancestorFrame, &metrics);
       if (!metrics) {
         return NS_OK;
       }
       newFrame = NS_NewSVGGlyphFrame(mPresShell, aContent,
                                      ancestorFrame, aStyleContext);
     }
   }
-  else {
+  else
+#endif
+  {
     newFrame = NS_NewTextFrame(mPresShell, aStyleContext);
   }
-#else
-  newFrame = NS_NewTextFrame(mPresShell, aStyleContext);
-#endif
 
   if (NS_UNLIKELY(!newFrame))
     return NS_ERROR_OUT_OF_MEMORY;
 
   nsresult rv = InitAndRestoreFrame(aState, aContent, aParentFrame,
                                     nsnull, newFrame);
 
   if (NS_FAILED(rv)) {
     newFrame->Destroy();
     return rv;
   }
 
   // We never need to create a view for a text frame.
 
-  // Set the frame's initial child list to null.
-  newFrame->SetInitialChildList(nsnull, nsnull);
-
+  if (newFrame->IsGeneratedContentFrame()) {
+    nsAutoPtr<nsGenConInitializer> initializer;
+    initializer =
+      static_cast<nsGenConInitializer*>(
+        aContent->UnsetProperty(nsGkAtoms::genConInitializerProperty));
+    if (initializer) {
+      if (initializer->mNode->InitTextFrame(initializer->mList,
+              FindAncestorWithGeneratedContentPseudo(newFrame), newFrame)) {
+        (this->*(initializer->mDirtyAll))();
+      }
+      initializer->mNode.forget();
+    }
+  }
+  
   // Add the newly constructed frame to the flow
   aFrameItems.AddChild(newFrame);
 
   // Text frames don't go in the content->frame hash table, because
   // they're anonymous. This keeps the hash table smaller
 
   return rv;
 }
@@ -6763,16 +6656,17 @@ 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)) {
@@ -8584,23 +8478,20 @@ 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.
 
-    // 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 we're appending before :after content, then we're not really
+    // appending, so let WipeContainingBlock know that.
     if (WipeContainingBlock(state, containingBlock, parentFrame, frameItems,
-                            PR_TRUE, nsnull)) {
+                            !parentAfterFrame, 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) { 
@@ -8990,23 +8881,21 @@ 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.
-  // 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 we're appending before :after content, then we're not really
+  // appending, so let WipeContainingBlock know that.
   if (WipeContainingBlock(state, containingBlock, parentFrame, frameItems,
-                          isAppend, prevSibling))
+                          isAppend && !appendAfterFrame, 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(),
@@ -10229,17 +10118,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_FALSE, childItems, PR_FALSE);
+                          PR_TRUE, 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...
@@ -11221,61 +11110,52 @@ 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);
     }
   }
@@ -12603,24 +12483,20 @@ 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();
-    if (CreateGeneratedContentFrame(aState, aFrame, aContent,
-                                    styleContext, nsCSSPseudoElements::before,
-                                    &generatedFrame)) {
-      // Add the generated frame to the child list
-      aFrameItems.AddChild(generatedFrame);
-    }
+    CreateGeneratedContentFrame(aState, aFrame, aContent,
+                                styleContext, nsCSSPseudoElements::before,
+                                aFrameItems);
   }
 
   // Iterate the child content objects and construct frames
   PRBool allKidsInline = PR_TRUE;
   ChildIterator iter, last;
   for (ChildIterator::Init(aContent, &iter, &last);
        iter != last;
        ++iter) {
@@ -12651,23 +12527,19 @@ nsCSSFrameConstructor::ProcessInlineChil
         }
         kid = kid->GetNextSibling();
       }
     }
   }
 
   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);
-    }
+    CreateGeneratedContentFrame(aState, aFrame, aContent,
+                                styleContext, nsCSSPseudoElements::after,
+                                aFrameItems);
   }
 
   // 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
@@ -62,16 +62,17 @@ struct nsStyleContent;
 struct nsStyleDisplay;
 class nsIPresShell;
 class nsVoidArray;
 class nsFrameManager;
 class nsIDOMHTMLSelectElement;
 class nsPresContext;
 class nsStyleChangeList;
 class nsIFrame;
+struct nsGenConInitializer;
 
 struct nsFindFrameHint
 {
   nsIFrame *mPrimaryFrameForPrevSibling;  // weak ref to the primary frame for the content for which we need a frame
   nsFindFrameHint() : mPrimaryFrameForPrevSibling(nsnull) { }
 };
 
 typedef void (PR_CALLBACK nsLazyFrameConstructionCallback)
@@ -324,31 +325,44 @@ private:
   nsresult CreateAttributeContent(nsIContent* aParentContent,
                                   nsIFrame* aParentFrame,
                                   PRInt32 aAttrNamespace,
                                   nsIAtom* aAttrName,
                                   nsStyleContext* aStyleContext,
                                   nsCOMArray<nsIContent>& aGeneratedContent,
                                   nsIContent** aNewContent,
                                   nsIFrame** aNewFrame);
-  
-  nsresult CreateGeneratedFrameFor(nsIFrame*             aParentFrame,
-                                   nsIContent*           aContent,
-                                   nsStyleContext*       aStyleContext,
-                                   const nsStyleContent* aStyleContent,
-                                   PRUint32              aContentIndex,
-                                   nsCOMArray<nsIContent>& aGeneratedContent,
-                                   nsIFrame**            aFrame);
+
+  /**
+   * 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> CreateGenConTextNode(const nsString& aString,  
+                                                    nsCOMPtr<nsIDOMCharacterData>* aText,
+                                                    nsGenConInitializer* aInitializer);
 
-  PRBool CreateGeneratedContentFrame(nsFrameConstructorState& aState,
-                                     nsIFrame*                aFrame,
-                                     nsIContent*              aContent,
-                                     nsStyleContext*          aStyleContext,
-                                     nsIAtom*                 aPseudoElement,
-                                     nsIFrame**               aResult);
+  /**
+   * 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);
 
   // 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
@@ -37,16 +37,43 @@
  * ***** END LICENSE BLOCK ***** */
 
 /* implementation of CSS counters (for numbering things) */
 
 #include "nsCounterManager.h"
 #include "nsBulletFrame.h" // legacy location for list style type to text code
 #include "nsContentUtils.h"
 
+PRBool
+nsCounterUseNode::InitTextFrame(nsGenConList* aList,
+        nsIFrame* aPseudoFrame, nsIFrame* aTextFrame)
+{
+  nsCounterNode::InitTextFrame(aList, aPseudoFrame, aTextFrame);
+
+  nsCounterList *counterList = static_cast<nsCounterList*>(aList);
+  counterList->Insert(this);
+  PRBool dirty = counterList->IsDirty();
+  if (!dirty) {
+    if (counterList->IsLast(this)) {
+      Calc(counterList);
+      nsAutoString contentString;
+      GetText(contentString);
+      aTextFrame->GetContent()->SetText(contentString, PR_FALSE);
+    } else {
+      // 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.
+      counterList->SetDirty();
+      return PR_TRUE;
+    }
+  }
+  
+  return PR_FALSE;
+}
+
 // 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);
 }
@@ -109,40 +136,31 @@ nsCounterList::SetScope(nsCounterNode *a
     if (aNode == First()) {
         aNode->mScopeStart = nsnull;
         aNode->mScopePrev = nsnull;
         return;
     }
 
     // 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();
-    }
+    // parents.
+    nsIContent *nodeContent = aNode->mPseudoFrame->GetContent()->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 = start->mPseudoFrame->GetContent();
-        if (!start->mPseudoFrame->GetStyleContext()->GetPseudoType()) {
-            startContent = startContent->GetParent();
-        }
+        nsIContent *startContent = start->mPseudoFrame->GetContent()->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 &&
--- a/layout/base/nsCounterManager.h
+++ b/layout/base/nsCounterManager.h
@@ -87,18 +87,18 @@ struct nsCounterNode : public nsGenConNo
     inline nsCounterChangeNode* ChangeNode();
 
     // 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(nsIFrame* aPseudoFrame, PRInt32 aContentIndex, Type aType)
-        : nsGenConNode(aPseudoFrame, aContentIndex)
+    nsCounterNode(PRInt32 aContentIndex, Type aType)
+        : nsGenConNode(aContentIndex)
         , mType(aType)
         , mValueAfter(0)
         , mScopeStart(nsnull)
         , mScopePrev(nsnull)
     {
     }
 
     // to avoid virtual function calls in the common case
@@ -110,24 +110,27 @@ 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, nsIFrame* aPseudoFrame,
+    nsCounterUseNode(nsCSSValue::Array* aCounterStyle,
                      PRUint32 aContentIndex, PRBool aAllCounters)
-        : nsCounterNode(aPseudoFrame, aContentIndex, USE)
+        : nsCounterNode(aContentIndex, USE)
         , mCounterStyle(aCounterStyle)
         , mAllCounters(aAllCounters)
     {
         NS_ASSERTION(aContentIndex >= 0, "out of range");
     }
+    
+    virtual PRBool InitTextFrame(nsGenConList* aList,
+            nsIFrame* aPseudoFrame, nsIFrame* aTextFrame);
 
     // assign the correct |mValueAfter| value to a node that has been inserted
     // Should be called immediately after calling |Insert|.
     void Calc(nsCounterList* aList);
 
     // The text that should be displayed for this counter.
     void GetText(nsString& aResult);
 };
@@ -139,29 +142,30 @@ struct nsCounterChangeNode : public nsCo
     // 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(nsIFrame* aPseudoFrame,
                         nsCounterNode::Type aChangeType,
                         PRInt32 aChangeValue,
                         PRInt32 aPropIndex)
-        : nsCounterNode(aPseudoFrame,
-                        // Fake a content index for resets and increments
+        : nsCounterNode(// 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)
     {
         NS_ASSERTION(aPropIndex >= 0, "out of range");
         NS_ASSERTION(aChangeType == INCREMENT || aChangeType == RESET,
                      "bad type");
+        mPseudoFrame = aPseudoFrame;
+        CheckFrameAssertions();
     }
 
     // assign the correct |mValueAfter| value to a node that has been inserted
     // Should be called immediately after calling |Insert|.
     void Calc(nsCounterList* aList);
 };
 
 inline nsCounterUseNode* nsCounterNode::UseNode()
--- a/layout/base/nsGenConList.cpp
+++ b/layout/base/nsGenConList.cpp
@@ -90,40 +90,52 @@ nsGenConList::DestroyNodesFor(nsIFrame* 
       node = nextNode;
     } else {
       node = Next(node);
     }
   }
   return destroyed;
 }
 
-// return -1 for ::before, +1 for ::after, and 0 otherwise.
-inline PRInt32 PseudoCompareType(nsIFrame *aFrame)
+/**
+ * Compute the type of the pseudo and the content for the pseudo that
+ * we'll use for comparison purposes.
+ * @param aContent the content to use is stored here; it's the element
+ * that generated the ::before or ::after content, or (if not for generated
+ * content), the frame's own element
+ * @return -1 for ::before, +1 for ::after, and 0 otherwise.
+ */
+inline PRInt32 PseudoCompareType(nsIFrame* aFrame, nsIContent** aContent)
 {
   nsIAtom *pseudo = aFrame->GetStyleContext()->GetPseudoType();
-  if (pseudo == nsCSSPseudoElements::before)
+  if (pseudo == nsCSSPseudoElements::before) {
+    *aContent = aFrame->GetContent()->GetParent();
     return -1;
-  if (pseudo == nsCSSPseudoElements::after)
+  }
+  if (pseudo == nsCSSPseudoElements::after) {
+    *aContent = aFrame->GetContent()->GetParent();
     return 1;
+  }
+  *aContent = aFrame->GetContent();
   return 0;
 }
 
 /* static */ PRBool
 nsGenConList::NodeAfter(const nsGenConNode* aNode1, const nsGenConNode* aNode2)
 {
   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();
+  nsIContent *content1;
+  nsIContent *content2;
+  PRInt32 pseudoType1 = PseudoCompareType(frame1, &content1);
+  PRInt32 pseudoType2 = PseudoCompareType(frame2, &content2);
   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;
--- a/layout/base/nsGenConList.h
+++ b/layout/base/nsGenConList.h
@@ -41,54 +41,80 @@
 #define nsGenConList_h___
 
 #include "nsIFrame.h"
 #include "nsStyleStruct.h"
 #include "prclist.h"
 #include "nsIDOMCharacterData.h"
 #include "nsCSSPseudoElements.h"
 
+class nsGenConList;
+
 struct nsGenConNode : public PRCList {
   // 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;
+  nsIFrame* 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;
 
-  nsGenConNode(nsIFrame* aPseudoFrame, PRInt32 aContentIndex)
-    : mPseudoFrame(aPseudoFrame)
+  nsGenConNode(PRInt32 aContentIndex)
+    : mPseudoFrame(nsnull)
     , mContentIndex(aContentIndex)
   {
-    NS_ASSERTION(aContentIndex <
-                   PRInt32(aPseudoFrame->GetStyleContent()->ContentCount()),
-                 "index out of range");
-    // We allow negative values of mContentIndex for 'counter-reset' and
-    // 'counter-increment'.
+  }
 
-    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");
+  /**
+   * Finish initializing the generated content node once we know the
+   * relevant text frame. This must be called just after
+   * the textframe has been initialized. This need not be called at all
+   * for nodes that don't generate text. This will generally set the
+   * mPseudoFrame, insert the node into aList, and set aTextFrame up
+   * with the correct text.
+   * @param aList the list the node belongs to
+   * @param aPseudoFrame the :before or :after frame
+   * @param aTextFrame the textframe where the node contents will render
+   * @return true iff this marked the list dirty
+   */
+  virtual PRBool InitTextFrame(nsGenConList* aList, nsIFrame* aPseudoFrame,
+                               nsIFrame* aTextFrame)
+  {
+    mPseudoFrame = aPseudoFrame;
+    CheckFrameAssertions();
+    return PR_FALSE;
   }
 
   virtual ~nsGenConNode() {} // XXX Avoid, perhaps?
+
+protected:
+  void CheckFrameAssertions() {
+    NS_ASSERTION(mContentIndex <
+                   PRInt32(mPseudoFrame->GetStyleContent()->ContentCount()),
+                 "index out of range");
+      // We allow negative values of mContentIndex for 'counter-reset' and
+      // 'counter-increment'.
+
+    NS_ASSERTION(mContentIndex < 0 ||
+                 mPseudoFrame->GetStyleContext()->GetPseudoType() ==
+                   nsCSSPseudoElements::before ||
+                 mPseudoFrame->GetStyleContext()->GetPseudoType() ==
+                   nsCSSPseudoElements::after,
+                 "not :before/:after generated content and not counter change");
+    NS_ASSERTION(mContentIndex < 0 ||
+                 mPseudoFrame->GetStateBits() & NS_FRAME_GENERATED_CONTENT,
+                 "not generated content and not counter change");
+  }
 };
 
 class nsGenConList {
 protected:
   nsGenConNode* mFirstNode;
   PRUint32 mSize;
 public:
   nsGenConList() : mFirstNode(nsnull), mSize(0) {}
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -242,22 +242,29 @@ 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 && aFrame->GetContent() != aContent) {
+  if (aContent && parent->GetContent() != aContent) {
     return PR_FALSE;
   }
 
-  return aFrame->GetStyleContext()->GetPseudoType() == aPseudoElement;
+  return (aFrame->GetContent()->Tag() == nsGkAtoms::mozgeneratedcontentbefore) ==
+    (aPseudoElement == nsCSSPseudoElements::before);
 }
 
 // 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 :before frame of the given frame, if
+   * GetBeforeFrame returns the outermost :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 :after frame of the given frame, if one
+   * GetAfterFrame returns the outermost :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,18 +111,21 @@ public:
    *         such ancestor exists
    */
   static nsIFrame* GetPageFrame(nsIFrame* aFrame)
   {
     return GetClosestFrameOfType(aFrame, nsGkAtoms::pageFrame);
   }
 
   /**
-   * IsGeneratedContentFor returns PR_TRUE if aFrame is generated
-   * content of type aPseudoElement for aContent
+   * 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.
    *
    * @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
@@ -36,16 +36,37 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 /* implementation of quotes for the CSS 'content' property */
 
 #include "nsQuoteList.h"
 #include "nsReadableUtils.h"
 
+PRBool
+nsQuoteNode::InitTextFrame(nsGenConList* aList, nsIFrame* aPseudoFrame,
+                           nsIFrame* aTextFrame)
+{
+  nsGenConNode::InitTextFrame(aList, aPseudoFrame, aTextFrame);
+
+  nsQuoteList* quoteList = static_cast<nsQuoteList*>(aList);
+  PRBool dirty = PR_FALSE;
+  quoteList->Insert(this);
+  if (quoteList->IsLast(this))
+    quoteList->Calc(this);
+  else
+    dirty = PR_TRUE;
+
+  // Don't set up text for 'no-open-quote' and 'no-close-quote'.
+  if (IsRealQuote()) {
+    aTextFrame->GetContent()->SetText(*Text(), PR_FALSE);
+  }
+  return dirty;
+}
+
 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 = mPseudoFrame->GetStyleQuotes();
   PRInt32 quotesCount = styleQuotes->QuotesCount(); // 0 if 'quotes:none'
@@ -86,17 +107,17 @@ nsQuoteList::RecalcAll()
   nsQuoteNode *node = FirstNode();
   if (!node)
     return;
 
   do {
     PRInt32 oldDepth = node->mDepthBefore;
     Calc(node);
 
-    if (node->mDepthBefore != oldDepth && node->mText)
+    if (node->mDepthBefore != oldDepth && node->mText && node->IsRealQuote())
       node->mText->SetData(*node->Text());
 
     // Next node
     node = Next(node);
   } while (node != FirstNode());
 }
 
 #ifdef DEBUG
--- a/layout/base/nsQuoteList.h
+++ b/layout/base/nsQuoteList.h
@@ -44,31 +44,32 @@
 
 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;
 
-
-  nsQuoteNode(nsStyleContentType& aType, nsIFrame* aPseudoFrame,
-              PRUint32 aContentIndex)
-    : nsGenConNode(aPseudoFrame, aContentIndex)
+  nsQuoteNode(nsStyleContentType& aType, PRUint32 aContentIndex)
+    : nsGenConNode(aContentIndex)
     , mType(aType)
     , mDepthBefore(0)
   {
     NS_ASSERTION(aType == eStyleContentType_OpenQuote ||
                  aType == eStyleContentType_CloseQuote ||
                  aType == eStyleContentType_NoOpenQuote ||
                  aType == eStyleContentType_NoCloseQuote,
                  "incorrect type");
     NS_ASSERTION(aContentIndex >= 0, "out of range");
   }
 
+  virtual PRBool InitTextFrame(nsGenConList* aList, 
+          nsIFrame* aPseudoFrame, nsIFrame* aTextFrame);
+
   // is this 'open-quote' or 'no-open-quote'?
   PRBool IsOpenQuote() {
     return mType == eStyleContentType_OpenQuote ||
            mType == eStyleContentType_NoOpenQuote;
   }
 
   // is this 'close-quote' or 'no-close-quote'?
   PRBool IsCloseQuote() {
@@ -76,21 +77,16 @@ struct nsQuoteNode : public nsGenConNode
   }
 
   // is this 'open-quote' or 'close-quote'?
   PRBool IsRealQuote() {
     return mType == eStyleContentType_OpenQuote ||
            mType == eStyleContentType_CloseQuote;
   }
 
-  // is this 'no-open-quote' or 'no-close-quote'?
-  PRBool IsHiddenQuote() {
-    return !IsRealQuote();
-  }
-
   // Depth of the quote for *this* node.  Either non-negative or -1.
   // -1 means this is a closing quote that tried to decrement the
   // counter below zero (which means no quote should be rendered).
   PRInt32 Depth() {
     return IsOpenQuote() ? mDepthBefore : mDepthBefore - 1;
   }
 
   // always non-negative
--- a/layout/generic/nsHTMLParts.h
+++ b/layout/generic/nsHTMLParts.h
@@ -84,17 +84,18 @@ 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 Frame
+// Special Generated Content Node. It contains text taken from an
+// attribute of its *grandparent* content node. 
 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
@@ -2986,17 +2986,17 @@ nsTextPaintStyle::InitCommonColors()
                   NS_LUMINOSITY_DIFFERENCE(defaultWindowBackgroundColor,
                                            selectionBGColor));
 
   mInitCommonColors = PR_TRUE;
 }
 
 static nsIFrame* GetNonGeneratedAncestor(nsIFrame* f) {
   while (f->GetStateBits() & NS_FRAME_GENERATED_CONTENT) {
-    f = f->GetParent();
+    f = nsLayoutUtils::GetParentOrPlaceholderFor(f->PresContext()->FrameManager(), f);
   }
   return f;
 }
 
 static nsIContent*
 FindElementAncestor(nsINode* aNode)
 {
   while (aNode && !aNode->IsNodeOfType(nsINode::eELEMENT)) {
deleted file mode 100644
--- a/layout/reftests/bugs/374193-1.xhtml
+++ /dev/null
@@ -1,7 +0,0 @@
-<html xmlns="http://www.w3.org/1999/xhtml"
-  ><mtd xmlns="http://www.w3.org/1998/Math/MathML"
-  ><th xmlns="http://www.w3.org/1999/xhtml"
-  /><mtable xmlns="http://www.w3.org/1998/Math/MathML"
-  ><th xmlns="http://www.w3.org/1999/xhtml" style="-moz-binding: url(374193-1xbl.xml);" id="mw_th20"></th></mtable></mtd><style>
-mtable::after { content:"anonymous text"; }
-</style></html>
deleted file mode 100644
--- a/layout/reftests/bugs/374193-1xbl.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<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>
-    <span>Before</span>
+    <div>Before</div>
     Text
-    <span>After</span>
+    <div>After</div>
   </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
@@ -500,17 +500,16 @@ 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/generated-content/reftest.list
+++ b/layout/reftests/generated-content/reftest.list
@@ -1,8 +1,8 @@
 == display-types-01.html display-types-01-ref.html
 == dynamic-attr-01.html dynamic-attr-01-ref.html
 == dynamic-restyle-01.html dynamic-restyle-01-ref.html
 == floated-01.html floated-01-ref.html
 == images-01.html images-01-ref.html
 == positioned-01.html positioned-01-ref.html
 == table-ignoring-whitespace-01.html table-ignoring-whitespace-01-ref.html
-# == table-parts-01.html table-parts-01-ref.html
+== table-parts-01.html table-parts-01-ref.html
--- a/layout/reftests/generated-content/table-parts-01-ref.html
+++ b/layout/reftests/generated-content/table-parts-01-ref.html
@@ -1,16 +1,15 @@
 <!DOCTYPE HTML>
 <html>
 <head>
 <style>
 table { counter-reset:ctr; quotes:"\0022" "\0022" "\0022" "\0022"; }
 
 table { border:1px solid blue; }
-tbody { vertical-align:baseline; }
 td.real { border:1px solid cyan; }
 td { border-spacing:0; padding:0; }
 
 .table { display:table; }
 .row { display:table-row; }
 .rowgroup { display:table-row-group; }
 
 div { border:1px solid green; margin:5px; }
--- a/layout/reftests/generated-content/table-parts-01.html
+++ b/layout/reftests/generated-content/table-parts-01.html
@@ -9,17 +9,16 @@ table, div.gen { counter-reset:ctr; quot
   counter-increment:ctr;
 }
 .gen::after {
   content:counter(ctr) url(square-outline-32x32.png) "After " attr(class) close-quote;
   counter-increment:ctr;
 }
 
 table { border:1px solid blue; }
-tbody { vertical-align:baseline; }
 td { border:1px solid cyan; }
 td { border-spacing:0; padding:0; }
 
 tr.gen::before, tr.gen::after { display:table-cell; }
 tbody.gen::before, tbody.gen::after { display:table-row; }
 table.gen::before, table.gen::after { display:table-row-group; }
 table.col::before, table.gen.col::after { display:table-column-group; }
 /* note reordering here! */
--- a/layout/reftests/reftest.list
+++ b/layout/reftests/reftest.list
@@ -30,16 +30,19 @@ 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,60 +3302,17 @@ nsRuleNode::ComputeDisplayData(void* aSt
     if (fullAuto) {
       display->mClipFlags |= NS_STYLE_CLIP_AUTO;
     }
     else {
       display->mClipFlags |= NS_STYLE_CLIP_RECT;
     }
   }
 
-  // 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) {
+  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;