Bug 238072. Rework generated content support to match CSS 2.1, making generated content take the normal frame construction path so supporting any style applied to it (including 'position', 'float', etc). r+sr=bz,r=dbaron

This commit is contained in:
Robert O'Callahan 2008-08-07 13:18:24 +12:00
Родитель 8302647ae7
Коммит dfd9fa29d4
35 изменённых файлов: 1039 добавлений и 593 удалений

Просмотреть файл

@ -0,0 +1,7 @@
<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>

Просмотреть файл

@ -56,6 +56,7 @@ load 371681-1.xhtml
load 372237-1.html
load 372475-1.xhtml
load 372550-1.html
load 374193-1.html
load 374297-1.html
load 374297-2.html
load 376223-1.xhtml

Просмотреть файл

@ -1116,6 +1116,9 @@ public:
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.
@ -1220,7 +1223,8 @@ nsFrameConstructorState::nsFrameConstructorState(nsIPresShell* aPresShe
mFirstLetterStyle(PR_FALSE),
mFirstLineStyle(PR_FALSE),
mFrameState(aHistoryState),
mPseudoFrames()
mPseudoFrames(),
mAdditionalStateBits(0)
{
MOZ_COUNT_CTOR(nsFrameConstructorState);
}
@ -1241,7 +1245,8 @@ nsFrameConstructorState::nsFrameConstructorState(nsIPresShell* aPresShell,
mFloatedItems(aFloatContainingBlock),
mFirstLetterStyle(PR_FALSE),
mFirstLineStyle(PR_FALSE),
mPseudoFrames()
mPseudoFrames(),
mAdditionalStateBits(0)
{
MOZ_COUNT_CTOR(nsFrameConstructorState);
mFrameState = aPresShell->GetDocument()->GetLayoutHistoryState();
@ -1432,6 +1437,7 @@ nsFrameConstructorState::AddChild(nsIFrame* aNewFrame,
return rv;
}
placeholderFrame->AddStateBits(mAdditionalStateBits);
// Add the placeholder frame to the flow
aFrameItems.AddChild(placeholderFrame);
}
@ -1879,99 +1885,99 @@ nsIXBLService * nsCSSFrameConstructor::GetXBLService()
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");
if (aFrame->GetStateBits() & NS_FRAME_GENERATED_CONTENT) {
if (mQuoteList.DestroyNodesFor(aFrame))
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)) {
QuotesDirty();
}
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;
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;
}
// 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();
}
}
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)
already_AddRefed<nsIContent>
nsCSSFrameConstructor::CreateTextNode(const nsString& aString,
nsCOMPtr<nsIDOMCharacterData>* aText)
{
*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 = aStyleContent->ContentAt(aContentIndex);
nsStyleContentType type = data.mType;
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)
{
// Get the content value
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.
@ -1979,339 +1985,240 @@ nsCSSFrameConstructor::CreateGeneratedFrameFor(nsIFrame* aParentFram
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 {
attrName = do_GetAtom(contentString);
}
if (!attrName) {
return NS_ERROR_OUT_OF_MEMORY;
}
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();
}
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;
}
nsCOMPtr<nsIContent> content;
NS_NewGenConImageContent(getter_AddRefs(content), nodeInfo,
data.mContent.mImage);
return content.forget();
}
return NS_OK;
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()) {
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);
}
// 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();
}
nsXPIDLString temp;
nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
"Submit", temp);
return CreateTextNode(temp, nsnull);
}
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);
}
/*
*
* 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)
nsFrameItems& aFrameItems)
{
*aResult = nsnull; // initialize OUT parameter
if (!aContent->IsNodeOfType(nsINode::eELEMENT))
return PR_FALSE;
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)
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);
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;
rv = container->BindToTree(mDocument, aParentContent, aParentContent, PR_TRUE);
if (NS_FAILED(rv)) {
container->UnbindFromTree();
return;
}
return PR_FALSE;
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
@ -3635,7 +3542,7 @@ nsCSSFrameConstructor::ConstructTableFrame(nsFrameConstructorState& aState,
}
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;
@ -3764,7 +3671,7 @@ nsCSSFrameConstructor::ConstructTableRowGroupFrame(nsFrameConstructorState& aSta
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;
@ -3825,7 +3732,7 @@ nsCSSFrameConstructor::ConstructTableColGroupFrame(nsFrameConstructorState& aSta
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);
@ -3881,7 +3788,7 @@ nsCSSFrameConstructor::ConstructTableRowFrame(nsFrameConstructorState& aState,
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
@ -4085,9 +3992,15 @@ 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*
@ -6759,6 +6672,7 @@ nsCSSFrameConstructor::InitAndRestoreFrame(const nsFrameConstructorState& aState
// 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.
@ -8587,13 +8501,10 @@ nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer,
// 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;
}
@ -8993,13 +8904,11 @@ nsCSSFrameConstructor::ContentInserted(nsIContent* aContainer,
// 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) {
@ -10232,7 +10141,7 @@ nsCSSFrameConstructor::CreateContinuingTableFrame(nsIPresShell* aPresShell,
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);
@ -11224,22 +11133,17 @@ nsCSSFrameConstructor::ProcessChildren(nsFrameConstructorState& aState,
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;
@ -11250,6 +11154,13 @@ nsCSSFrameConstructor::ProcessChildren(nsFrameConstructorState& aState,
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);
@ -11258,17 +11169,6 @@ nsCSSFrameConstructor::ProcessChildren(nsFrameConstructorState& aState,
// 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);
@ -12606,14 +12506,10 @@ nsCSSFrameConstructor::ProcessInlineChildren(nsFrameConstructorState& aState,
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
@ -12654,13 +12550,9 @@ nsCSSFrameConstructor::ProcessInlineChildren(nsFrameConstructorState& aState,
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

Просмотреть файл

@ -323,21 +323,33 @@ private:
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);
PRBool CreateGeneratedContentFrame(nsFrameConstructorState& aState,
nsIFrame* aFrame,
nsIContent* aContent,
nsStyleContext* aStyleContext,
nsIAtom* aPseudoElement,
nsIFrame** aResult);
/**
* 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);
/**
* 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

Просмотреть файл

@ -39,8 +39,11 @@
/* 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|.
@ -93,6 +96,19 @@ nsCounterUseNode::GetText(nsString& aResult)
}
}
// 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)
{
@ -112,16 +128,7 @@ nsCounterList::SetScope(nsCounterNode *aNode)
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();
}
nsIContent *nodeContent = GetScopeContent(aNode);
for (nsCounterNode *prev = Prev(aNode), *start;
prev; prev = start->mScopePrev) {
@ -134,10 +141,7 @@ nsCounterList::SetScope(nsCounterNode *aNode)
? 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 = GetScopeContent(start);
NS_ASSERTION(nodeContent || !startContent,
"null check on startContent should be sufficient to "
"null check nodeContent as well, since if nodeContent "
@ -197,33 +201,53 @@ nsCounterManager::nsCounterManager()
PRBool
nsCounterManager::AddCounterResetsAndIncrements(nsIFrame *aFrame)
{
const nsStyleContent *styleContent = aFrame->GetStyleContent();
nsStyleContext *styleContext = aFrame->GetStyleContext();
const nsStyleContent *styleContent = styleContext->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(aFrame, i,
dirty |= AddResetOrIncrement(content, styleContext, i,
styleContent->GetCounterResetAt(i),
nsCounterChangeNode::RESET);
for (i = 0, i_end = styleContent->CounterIncrementCount(); i != i_end; ++i)
dirty |= AddResetOrIncrement(aFrame, i,
dirty |= AddResetOrIncrement(content, styleContext, i,
styleContent->GetCounterIncrementAt(i),
nsCounterChangeNode::INCREMENT);
return dirty;
}
PRBool
nsCounterManager::AddResetOrIncrement(nsIFrame *aFrame, PRInt32 aIndex,
nsCounterManager::AddResetOrIncrement(nsIContent *aContent,
nsStyleContext *aStyleContext,
PRInt32 aIndex,
const nsStyleCounterData *aCounterData,
nsCounterNode::Type aType)
{
nsCounterChangeNode *node =
new nsCounterChangeNode(aFrame, aType, aCounterData->mValue, aIndex);
new nsCounterChangeNode(aContent, aStyleContext, aType,
aCounterData->mValue, aIndex);
if (!node)
return PR_FALSE;
@ -282,13 +306,15 @@ nsCounterManager::RecalcAll()
}
struct DestroyNodesData {
DestroyNodesData(nsIFrame *aFrame)
: mFrame(aFrame)
DestroyNodesData(nsIContent* aParentContent, nsIAtom *aPseudo)
: mParentContent(aParentContent)
, mPseudo(aPseudo)
, mDestroyedAny(PR_FALSE)
{
}
nsIFrame *mFrame;
nsIContent *mParentContent;
nsIAtom *mPseudo;
PRBool mDestroyedAny;
};
@ -296,7 +322,7 @@ PR_STATIC_CALLBACK(PLDHashOperator)
DestroyNodesInList(const nsAString& aKey, nsCounterList* aList, void* aClosure)
{
DestroyNodesData *data = static_cast<DestroyNodesData*>(aClosure);
if (aList->DestroyNodesFor(data->mFrame)) {
if (aList->DestroyNodesFor(data->mParentContent, data->mPseudo)) {
data->mDestroyedAny = PR_TRUE;
aList->SetDirty();
}
@ -304,9 +330,9 @@ DestroyNodesInList(const nsAString& aKey, nsCounterList* aList, void* aClosure)
}
PRBool
nsCounterManager::DestroyNodesFor(nsIFrame *aFrame)
nsCounterManager::DestroyNodesFor(nsIContent* aParentContent, nsIAtom *aPseudo)
{
DestroyNodesData data(aFrame);
DestroyNodesData data(aParentContent, aPseudo);
mNames.EnumerateRead(DestroyNodesInList, &data);
return data.mDestroyedAny;
}
@ -321,10 +347,14 @@ DumpList(const nsAString& aKey, nsCounterList* aList, void* aClosure)
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 frame=%p index=%d type=%s valAfter=%d\n"
printf(" Node #%d @%p content=%p pseudo=%s index=%d type=%s valAfter=%d\n"
" scope-start=%p scope-prev=%p",
i++, (void*)node, (void*)node->mPseudoFrame,
i++, (void*)node, (void*)node->mParentContent, pseudo.get(),
node->mContentIndex, types[node->mType], node->mValueAfter,
(void*)node->mScopeStart, (void*)node->mScopePrev);
if (node->mType == nsCounterNode::USE) {

Просмотреть файл

@ -45,6 +45,7 @@
#include "nsAutoPtr.h"
#include "nsClassHashtable.h"
class nsIFrame;
class nsCounterList;
struct nsCounterUseNode;
struct nsCounterChangeNode;
@ -86,14 +87,15 @@ struct nsCounterNode : public nsGenConNode {
inline nsCounterUseNode* UseNode();
inline nsCounterChangeNode* ChangeNode();
// For RESET and INCREMENT nodes, aPseudoFrame need not be a
// pseudo-element, and aContentIndex represents the index within the
// For RESET and INCREMENT nodes, aStyleContext may have a null
// psuedo type, 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(nsIContent* aParentContent, nsStyleContext* aStyleContext,
PRInt32 aContentIndex, Type aType)
: nsGenConNode(aParentContent, aStyleContext, aContentIndex)
, mType(aType)
, mValueAfter(0)
, mScopeStart(nsnull)
@ -115,9 +117,10 @@ struct nsCounterUseNode : public nsCounterNode {
PRBool mAllCounters;
// args go directly to member variables here and of nsGenConNode
nsCounterUseNode(nsCSSValue::Array* aCounterStyle, nsIFrame* aPseudoFrame,
nsCounterUseNode(nsCSSValue::Array* aCounterStyle,
nsIContent* aParentContent, nsStyleContext* aStyleContext,
PRUint32 aContentIndex, PRBool aAllCounters)
: nsCounterNode(aPseudoFrame, aContentIndex, USE)
: nsCounterNode(aParentContent, aStyleContext, aContentIndex, USE)
, mCounterStyle(aCounterStyle)
, mAllCounters(aAllCounters)
{
@ -135,16 +138,13 @@ struct nsCounterUseNode : public nsCounterNode {
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(nsIFrame* aPseudoFrame,
nsCounterChangeNode(nsIContent* aContent, nsStyleContext* aStyleContext,
nsCounterNode::Type aChangeType,
PRInt32 aChangeValue,
PRInt32 aPropIndex)
: nsCounterNode(aPseudoFrame,
: nsCounterNode(aContent, aStyleContext,
// 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.
@ -245,9 +245,11 @@ public:
// Clean up data in any dirty counter lists.
void RecalcAll();
// Destroy nodes for the frame in any lists, and return whether any
// nodes were destroyed.
PRBool DestroyNodesFor(nsIFrame *aFrame);
// 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);
// Clear all data.
void Clear() { mNames.Clear(); }
@ -255,10 +257,14 @@ public:
#ifdef DEBUG
void Dump();
#endif
PRBool IsEmpty() { return mNames.Count() == 0; }
private:
// for |AddCounterResetsAndIncrements| only
PRBool AddResetOrIncrement(nsIFrame *aFrame, PRInt32 aIndex,
PRBool AddResetOrIncrement(nsIContent *aContent,
nsStyleContext *aStyleContext,
PRInt32 aIndex,
const nsStyleCounterData *aCounterData,
nsCounterNode::Type aType);

Просмотреть файл

@ -60,13 +60,14 @@ nsGenConList::Clear()
}
PRBool
nsGenConList::DestroyNodesFor(nsIFrame* aFrame)
nsGenConList::DestroyNodesFor(nsIContent* aParentContent, nsIAtom* aPseudo)
{
if (!mFirstNode)
return PR_FALSE; // list empty
nsGenConNode* node;
PRBool destroyed = PR_FALSE;
while (mFirstNode->mPseudoFrame == aFrame) {
while (mFirstNode->mParentContent == aParentContent &&
mFirstNode->mPseudoType == aPseudo) {
destroyed = PR_TRUE;
node = Next(mFirstNode);
PRBool isLastNode = node == mFirstNode; // before they're dangling
@ -82,7 +83,8 @@ nsGenConList::DestroyNodesFor(nsIFrame* aFrame)
}
node = Next(mFirstNode);
while (node != mFirstNode) {
if (node->mPseudoFrame == aFrame) {
if (node->mParentContent == aParentContent &&
node->mPseudoType == aPseudo) {
destroyed = PR_TRUE;
nsGenConNode *nextNode = Next(node);
Remove(node);
@ -96,12 +98,11 @@ nsGenConList::DestroyNodesFor(nsIFrame* aFrame)
}
// return -1 for ::before, +1 for ::after, and 0 otherwise.
inline PRInt32 PseudoCompareType(nsIFrame *aFrame)
inline PRInt32 PseudoCompareType(nsIAtom *aPseudo)
{
nsIAtom *pseudo = aFrame->GetStyleContext()->GetPseudoType();
if (pseudo == nsCSSPseudoElements::before)
if (aPseudo == nsCSSPseudoElements::before)
return -1;
if (pseudo == nsCSSPseudoElements::after)
if (aPseudo == nsCSSPseudoElements::after)
return 1;
return 0;
}
@ -109,16 +110,14 @@ inline PRInt32 PseudoCompareType(nsIFrame *aFrame)
/* static */ PRBool
nsGenConList::NodeAfter(const nsGenConNode* aNode1, const nsGenConNode* aNode2)
{
nsIFrame *frame1 = aNode1->mPseudoFrame;
nsIFrame *frame2 = aNode2->mPseudoFrame;
if (frame1 == frame2) {
nsIContent *content1 = aNode1->mParentContent;
nsIContent *content2 = aNode2->mParentContent;
PRInt32 pseudoType1 = PseudoCompareType(aNode1->mPseudoType);
PRInt32 pseudoType2 = PseudoCompareType(aNode2->mPseudoType);
if (content1 == content2 && pseudoType1 == pseudoType2) {
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");
@ -134,7 +133,7 @@ nsGenConList::NodeAfter(const nsGenConNode* aNode1, const nsGenConNode* aNode2)
return pseudoType1 == 1;
}
}
// XXX Switch to the frame version of DoCompareTreePosition?
// XXX This doesn't handle anonymous (XBL) content properly.
PRInt32 cmp = nsLayoutUtils::DoCompareTreePosition(content1, content2,
pseudoType1, -pseudoType2);
NS_ASSERTION(cmp != 0, "same content, different frames");

Просмотреть файл

@ -40,18 +40,21 @@
#ifndef nsGenConList_h___
#define nsGenConList_h___
#include "nsIFrame.h"
#include "nsIContent.h"
#include "nsStyleStruct.h"
#include "nsStyleContext.h"
#include "prclist.h"
#include "nsIDOMCharacterData.h"
#include "nsCSSPseudoElements.h"
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;
// 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;
// Index within the list of things specified by the 'content' property,
// which is needed to do 'content: open-quote open-quote' correctly,
@ -62,25 +65,27 @@ struct nsGenConNode : public PRCList {
// counter nodes for increments and resets (rather than uses)
nsCOMPtr<nsIDOMCharacterData> mText;
nsGenConNode(nsIFrame* aPseudoFrame, PRInt32 aContentIndex)
: mPseudoFrame(aPseudoFrame)
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()))
, mContentIndex(aContentIndex)
{
NS_ASSERTION(aContentIndex <
PRInt32(aPseudoFrame->GetStyleContent()->ContentCount()),
PRInt32(aStyleContext->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,
NS_ASSERTION(aContentIndex < 0 || mPseudoType,
"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?
@ -101,8 +106,11 @@ public:
return static_cast<nsGenConNode*>(PR_PREV_LINK(aNode));
}
void Insert(nsGenConNode* aNode);
// returns whether any nodes have been destroyed
PRBool DestroyNodesFor(nsIFrame* aFrame); //destroy all nodes with aFrame as parent
/**
* Destroy all nodes whose aContent/aPseudo match.
* @return true if some nodes were destroyed
*/
PRBool DestroyNodesFor(nsIContent* aParentContent, nsIAtom* aPseudo);
// Return true if |aNode1| is after |aNode2|.
static PRBool NodeAfter(const nsGenConNode* aNode1,

Просмотреть файл

@ -248,12 +248,19 @@ nsLayoutUtils::IsGeneratedContentFor(nsIContent* aContent,
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

Просмотреть файл

@ -71,7 +71,7 @@ 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.
*
@ -81,7 +81,7 @@ public:
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.
@ -116,8 +116,11 @@ public:
}
/**
* 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

Просмотреть файл

@ -47,7 +47,7 @@ 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();
const nsStyleQuotes* styleQuotes = mStyleContext->GetStyleQuotes();
PRInt32 quotesCount = styleQuotes->QuotesCount(); // 0 if 'quotes:none'
PRInt32 quoteDepth = Depth();

Просмотреть файл

@ -41,6 +41,7 @@
#define nsQuoteList_h___
#include "nsGenConList.h"
#include "nsStyleContext.h"
struct nsQuoteNode : public nsGenConNode {
// open-quote, close-quote, no-open-quote, or no-close-quote
@ -49,12 +50,14 @@ struct nsQuoteNode : public nsGenConNode {
// Quote depth before this quote, which is always non-negative.
PRInt32 mDepthBefore;
nsRefPtr<nsStyleContext> mStyleContext;
nsQuoteNode(nsStyleContentType& aType, nsIFrame* aPseudoFrame,
PRUint32 aContentIndex)
: nsGenConNode(aPseudoFrame, aContentIndex)
nsQuoteNode(nsStyleContentType& aType, nsIContent* aContentParent,
PRUint32 aContentIndex, nsStyleContext* aStyleContext)
: nsGenConNode(aContentParent, aStyleContext, aContentIndex)
, mType(aType)
, mDepthBefore(0)
, mStyleContext(aStyleContext)
{
NS_ASSERTION(aType == eStyleContentType_OpenQuote ||
aType == eStyleContentType_CloseQuote ||

Просмотреть файл

@ -89,7 +89,8 @@ class nsTableColFrame;
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,

Просмотреть файл

@ -2925,7 +2925,7 @@ nsTextPaintStyle::InitCommonColors()
static nsIFrame* GetNonGeneratedAncestor(nsIFrame* f) {
while (f->GetStateBits() & NS_FRAME_GENERATED_CONTENT) {
f = f->GetParent();
f = nsLayoutUtils::GetParentOrPlaceholderFor(f->PresContext()->FrameManager(), f);
}
return f;
}

Просмотреть файл

@ -31,9 +31,9 @@
<div>
Before span
<span>
<span>Before</span>
<div>Before</div>
Text
<span>After</span>
<div>After</div>
</span>
After span
</div>

Просмотреть файл

@ -500,7 +500,6 @@ random-if(MOZ_WIDGET_TOOLKIT=="gtk2") == 368020-5.html 368020-5-ref.html # bug 3
== 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

Просмотреть файл

@ -0,0 +1,45 @@
<!DOCTYPE HTML>
<html>
<head>
<style>
div { border:1px solid green; margin:5px; }
</style>
</head>
<body>
<table width="100%"><tr><td valign="top">
<div><span style="display:block">1<img src="square-outline-32x32.png">"Before block</span
>Inner<span style="display:block">2<img src="square-outline-32x32.png">After block"</span
></div>
<div><span style="display:inline">1<img src="square-outline-32x32.png">"Before inline</span
>Inner<span style="display:inline">2<img src="square-outline-32x32.png">After inline"</span
></div>
<div><span style="display:inline-block">1<img src="square-outline-32x32.png">"Before inline-block</span
>Inner<span style="display:inline-block">2<img src="square-outline-32x32.png">After inline-block"</span
></div>
<div><span style="display:table">1<img src="square-outline-32x32.png">"Before table</span
>Inner<span style="display:table">2<img src="square-outline-32x32.png">After table"</span
></div>
<div><span style="display:inline-table">1<img src="square-outline-32x32.png">"Before inline-table</span
>Inner<span style="display:inline-table">2<img src="square-outline-32x32.png">After inline-table"</span
></div>
<div><span style="display:table-row-group">1<img src="square-outline-32x32.png">"Before table-row-group</span
>Inner<span style="display:table-row-group">2<img src="square-outline-32x32.png">After table-row-group"</span
></div>
</td><td valign="top">
<div><span style="display:table-row">1<img src="square-outline-32x32.png">"Before table-row</span
>Inner<span style="display:table-row">2<img src="square-outline-32x32.png">After table-row"</span
></div>
<div><span style="display:table-cell">1<img src="square-outline-32x32.png">"Before table-cell</span
>Inner<span style="display:table-cell">2<img src="square-outline-32x32.png">After table-cell"</span
></div>
<div><span style="display:table-caption">1<img src="square-outline-32x32.png">"Before table-caption</span
>Inner<span style="display:table-caption">2<img src="square-outline-32x32.png">After table-caption"</span
></div>
<div><span style="display:-moz-box">1<img src="square-outline-32x32.png">"Before flexbox</span
>Inner<span style="display:-moz-box">2<img src="square-outline-32x32.png">After flexbox"</span
></div>
<div><span style="display:-moz-inline-box">1<img src="square-outline-32x32.png">"Before inline-flexbox</span
>Inner<span style="display:-moz-inline-box">2<img src="square-outline-32x32.png">After inline-flexbox"</span
></div>
</body>
</html>

Просмотреть файл

@ -0,0 +1,48 @@
<!DOCTYPE HTML>
<html>
<head>
<style>
div { counter-reset:ctr; quotes:"\0022" "\0022" "\0022" "\0022"}
div::before {
content:counter(ctr) url(square-outline-32x32.png) open-quote "Before " attr(class);
counter-increment:ctr;
}
div::after {
content:counter(ctr) url(square-outline-32x32.png) "After " attr(class) close-quote;
counter-increment:ctr;
}
.block::before, .block::after { display:block; }
.inline::before, .inline::after { display:inline; }
.inline-block::before, .inline-block::after { display:inline-block; }
.table::before, .table::after { display:table; }
.inline-table::before, .inline-table::after { display:inline-table; }
.table-row-group::before, .table-row-group::after { display:table-row-group; }
.table-row::before, .table-row::after { display:table-row; }
.table-cell::before, .table-cell::after { display:table-cell; }
.table-caption::before, .table-caption::after { display:table-caption; }
.flexbox::before, .flexbox::after { display:-moz-box; }
.inline-flexbox::before, .inline-flexbox::after { display:-moz-inline-box; }
div { border:1px solid green; margin:5px; }
</style>
</head>
<body>
<table width="100%"><tr><td valign="top">
<div class="block">Inner</div>
<div class="inline">Inner</div>
<div class="inline-block">Inner</div>
<div class="table">Inner</div>
<div class="inline-table">Inner</div>
<div class="table-row-group">Inner</div>
</td><td valign="top">
<div class="table-row">Inner</div>
<div class="table-cell">Inner</div>
<div class="table-caption">Inner</div>
<div class="flexbox">Inner</div>
<div class="inline-flexbox">Inner</div>
</td></tr></table>
</body>
</html>

Просмотреть файл

@ -0,0 +1,6 @@
<!DOCTYPE HTML>
<html>
<body>
before after
</body>
</html>

Просмотреть файл

@ -0,0 +1,22 @@
<!DOCTYPE HTML>
<html class="reftest-wait">
<head>
<style>
body::before {
content:attr(my-attr);
}
body::after {
content:attr(my-attr-2);
}
</style>
<script>
function fixupDOM() {
document.body.setAttribute("my-attr", "before");
document.body.setAttribute("my-attr-2", "after");
document.documentElement.className = "";
}
</script>
</head>
<body my-attr-2="xyz" onload="fixupDOM()">
</body>
</html>

Просмотреть файл

@ -0,0 +1,7 @@
<!DOCTYPE HTML>
<html>
<body style="border:2px solid red;">
<span style="border:2px solid red;">Before</span>
<div>After</div>
</body>
</html>

Просмотреть файл

@ -0,0 +1,24 @@
<!DOCTYPE HTML>
<html class="reftest-wait">
<head>
<style>
body::before {
content:"Before";
border:inherit;
}
.cl::after {
display:block;
content:"After";
}
</style>
<script>
function fixupDOM() {
document.body.setAttribute("style", "border:2px solid red;");
document.body.className = "cl";
document.documentElement.className = "";
}
</script>
</head>
<body onload="fixupDOM()">
</body>
</html>

Просмотреть файл

@ -0,0 +1,23 @@
<!DOCTYPE HTML>
<html>
<head>
<style>
div { border:1px solid green; margin:5px; }
div { overflow:auto; }
</style>
</head>
<body>
<div><span style="float:left">1<img src="square-outline-32x32.png">"Before beforeleft afterleft</span
>Inner<span style="float:left">2<img src="square-outline-32x32.png">After beforeleft afterleft"</span
></div>
<div><span style="float:left">1<img src="square-outline-32x32.png">"Before beforeleft afterright</span
>Inner<span style="float:right">2<img src="square-outline-32x32.png">After beforeleft afterright"</span
></div>
<div><span style="float:right">1<img src="square-outline-32x32.png">"Before beforeright afterleft</span
>Inner<span style="float:left">2<img src="square-outline-32x32.png">After beforeright afterleft"</span
></div>
<div><span style="float:right">1<img src="square-outline-32x32.png">"Before beforeright afterright</span
>Inner<span style="float:right">2<img src="square-outline-32x32.png">After beforeright afterright"</span
></div>
</body>
</html>

Просмотреть файл

@ -0,0 +1,39 @@
<!DOCTYPE HTML>
<html>
<head>
<style>
div { counter-reset:ctr; quotes:"\0022" "\0022" "\0022" "\0022"; }
div::before {
content:counter(ctr) url(square-outline-32x32.png) open-quote "Before " attr(class);
counter-increment:ctr;
}
div::after {
content:counter(ctr) url(square-outline-32x32.png) "After " attr(class) close-quote;
counter-increment:ctr;
}
.beforeleft::before {
float:left;
}
.beforeright::before {
float:right;
}
.afterleft::after {
float:left;
}
.afterright::after {
float:right;
}
div { border:1px solid green; margin:5px; }
div { overflow:auto; }
</style>
</head>
<body>
<div class="beforeleft afterleft">Inner</div>
<div class="beforeleft afterright">Inner</div>
<div class="beforeright afterleft">Inner</div>
<div class="beforeright afterright">Inner</div>
</body>
</html>

Просмотреть файл

@ -0,0 +1,11 @@
<!DOCTYPE HTML>
<html>
<head>
<style>
div { border:1px solid green; margin:5px; }
</style>
</head>
<body>
<div>Inner</div>
</body>
</html>

Просмотреть файл

@ -0,0 +1,13 @@
<!DOCTYPE HTML>
<html>
<head>
<style>
div::before {
content:url(missing-image.png);
}
div { border:1px solid green; margin:5px; }
</style>
</head>
<div>Inner</div>
</body>
</html>

Просмотреть файл

@ -0,0 +1,21 @@
<!DOCTYPE HTML>
<html>
<head>
<style>
div { border:1px solid green; margin:5px; height:100px; }
</style>
</head>
<div><span style="position:absolute; left:0">1<img src="square-outline-32x32.png">"Before gen abs</span
>Inner<span style="position:absolute; right:0">2<img src="square-outline-32x32.png">After gen abs"</span
></div>
<div style="position:relative"><span style="position:absolute; left:0">1<img src="square-outline-32x32.png">"Before gen abs</span
>Inner<span style="position:absolute; right:0">2<img src="square-outline-32x32.png">After gen abs"</span
></div>
<div><span style="position:relative; top:-10px;">1<img src="square-outline-32x32.png">"Before gen rel</span
>Inner<span style="position:relative; top:10px;">2<img src="square-outline-32x32.png">After gen rel"</span
></div>
<div>Begin <span style="position:relative; top:-10px;">1<img src="square-outline-32x32.png">"Before gen rel</span
>Inner<span style="position:relative; top:10px;">2<img src="square-outline-32x32.png">After gen rel"</span
> End</div>
</body>
</html>

Просмотреть файл

@ -0,0 +1,43 @@
<!DOCTYPE HTML>
<html>
<head>
<style>
div { counter-reset:ctr; quotes:"\0022" "\0022" "\0022" "\0022"; }
.gen::before {
content:counter(ctr) url(square-outline-32x32.png) open-quote "Before " attr(class);
counter-increment:ctr;
}
.gen::after {
content:counter(ctr) url(square-outline-32x32.png) "After " attr(class) close-quote;
counter-increment:ctr;
}
.abs::before {
position:absolute;
left:0;
}
.abs::after {
position:absolute;
right:0;
}
.rel::before {
position:relative;
top:-10px;
}
.rel::after {
position:relative;
top:10px;
}
div { border:1px solid green; margin:5px; height:100px; }
</style>
</head>
<div class="gen abs">Inner</div>
<!-- an element should be the containing block for its positioned content -->
<div style="position:relative;" class="gen abs">Inner</div>
<div class="gen rel">Inner</div>
<div>Begin <span class="gen rel">Inner</span> End</div>
</body>
</html>

Просмотреть файл

@ -0,0 +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

Просмотреть файл

@ -0,0 +1,27 @@
<!DOCTYPE HTML>
<html>
<head>
<style>
div { border:1px solid green; margin:5px; }
</style>
</head>
<body>
<div>
<table><tbody><tr><td>Cell0</td><td></td></tr>
<tr><td>Cell1</td><td>Cell2</td></tr></tbody></table>
</div>
<div>
<table><tbody><tr><td></td><td>Cell0</td></tr>
<tr><td>Cell1</td><td>Cell2</td></tr></tbody></table>
</div>
<div>
<table><tbody><tr><td></td><td>Cell0</td></tr>
<tr><td>Cell1</td><td>Cell2</td></tr></tbody></table>
</div>
<div>
<table><tbody><tr><td></td><td>Cell0</td></tr>
<tr><td>Cell1</td><td>Cell2</td></tr></tbody></table>
</div>
</body>
</html>

Просмотреть файл

@ -0,0 +1,44 @@
<!DOCTYPE HTML>
<html>
<head>
<style>
.gen0::before {
padding:1px;
}
.gen1::before {
content: " ";
}
.gen2::before {
content: attr(missing);
}
.gen3::before {
content: url(missing-image.png);
}
div { border:1px solid green; margin:5px; }
</style>
</head>
<!-- This tests that generated content items that evaluate to empty strings or
broken images are *not* treated as whitespace text and ignored by the table.
Altogether missing content should be ignored, though. (In fact it won't even be generated.) -->
<body>
<div>
<table><tbody><tr class="gen0"><td>Cell0</td></tr>
<tr><td>Cell1</td><td>Cell2</td></tr></tbody></table>
</div>
<div>
<table><tbody><tr class="gen1"><td>Cell0</td></tr>
<tr><td>Cell1</td><td>Cell2</td></tr></tbody></table>
</div>
<div>
<table><tbody><tr class="gen2"><td>Cell0</td></tr>
<tr><td>Cell1</td><td>Cell2</td></tr></tbody></table>
</div>
<div>
<table><tbody><tr class="gen3"><td>Cell0</td></tr>
<tr><td>Cell1</td><td>Cell2</td></tr></tbody></table>
</div>
</body>
</html>

Просмотреть файл

@ -0,0 +1,80 @@
<!DOCTYPE HTML>
<html>
<head>
<style>
table, div.gen { counter-reset:ctr; quotes:"\0022" "\0022" "\0022" "\0022"; }
.gen::before {
content:counter(ctr) url(square-outline-32x32.png) open-quote "Before " attr(class);
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; }
td.real { 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! */
table.headfoot::after { display:table-header-group; }
table.headfoot::before { display:table-footer-group; }
.row { display:table-row; }
.rowgroup { display:table-row-group; }
div.gencell::before, div.gencell::after { display:table-cell; }
div.genrow::before, div.genrow::after { display:table-row; }
div.genblock::before, div.genblock::after { display:block; }
div.geninline::before, div.geninline::after { display:inline; }
div { border:1px solid green; margin:5px; }
div.cell { border:none; display:table-cell; }
div.real { display:table-cell; }
</style>
</head>
<body>
<table style="border:none" width="100%"><tr><td style="border:none" valign="top">
<div><table><tbody><tr><td>1<img src="square-outline-32x32.png">"Before gen</td
><td class="real">Inner</td><td>2<img src="square-outline-32x32.png">After gen"</td
></tr></tbody></table></div>
<div><table><tbody><tr><td>1<img src="square-outline-32x32.png">"Before gen</td
></tr><tr><td class="real">Inner</td></tr><tr><td>2<img src="square-outline-32x32.png">After gen"</td
></tr></tbody></table></div>
<div><table><tbody><tr><td>1<img src="square-outline-32x32.png">"Before gen</td
></tr><tr><td>2<img src="square-outline-32x32.png">After gen"</td
></tr><tr><td class="real">Inner</td></tr></tbody></table></div>
<div><table><tbody><tr><td class="real">Inner</td></tr></tbody></table></div>
<div><table><tbody><tr><td>2<img src="square-outline-32x32.png">After gen headfoot"</td
></tr><tr><td class="real">Inner</td></tr><tr><td>1<img src="square-outline-32x32.png">"Before gen headfoot</td
></tr></tbody></table></div>
</td><td style="border:none" valign="top">
<div><div class="row"><div class="cell">1<img src="square-outline-32x32.png">"Before gen row gencell</div
><div class="real">Inner</div><div class="cell">2<img src="square-outline-32x32.png">After gen row gencell"</div
></div></div>
<div><div class="row"><div class="cell">1<img src="square-outline-32x32.png">"Before gen row genblock</div
><div class="real">Inner</div><div class="cell">2<img src="square-outline-32x32.png">After gen row genblock"</div
></div></div>
<div><div class="row"><div class="cell">1<img src="square-outline-32x32.png">"Before gen row geninline</div
><div class="real">Inner</div><div class="cell">2<img src="square-outline-32x32.png">After gen row geninline"</div
></div></div>
<div><div class="rowgroup"><div class="row">1<img src="square-outline-32x32.png">"Before gen rowgroup genrow</div
><div class="row"><div class="real">Inner</div></div><div class="row">2<img src="square-outline-32x32.png">After gen rowgroup genrow"</div
></div></div>
<div><div class="rowgroup"><div class="row">1<img src="square-outline-32x32.png">"Before gen rowgroup gencell</div
><div class="row"><div class="real">Inner</div></div><div class="row">2<img src="square-outline-32x32.png">After gen rowgroup gencell"</div
></div></div>
<div><div class="rowgroup"><div class="row">1<img src="square-outline-32x32.png">"Before gen rowgroup genblock</div
><div class="row"><div class="real">Inner</div></div><div class="row">2<img src="square-outline-32x32.png">After gen rowgroup genblock"</div
></div></div>
<div><div class="rowgroup"><div class="row">1<img src="square-outline-32x32.png">"Before gen rowgroup geninline</div
><div class="row"><div class="real">Inner</div></div><div class="row">2<img src="square-outline-32x32.png">After gen rowgroup geninline"</div
></div></div>
</tr></td></table>
</body>
</html>

Просмотреть файл

@ -0,0 +1,57 @@
<!DOCTYPE HTML>
<html>
<head>
<style>
table, div.gen { counter-reset:ctr; quotes:"\0022" "\0022" "\0022" "\0022"; }
.gen::before {
content:counter(ctr) url(square-outline-32x32.png) open-quote "Before " attr(class);
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; }
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! */
table.headfoot::after { display:table-header-group; }
table.headfoot::before { display:table-footer-group; }
.cell { display:table-cell; }
.row { display:table-row; }
.rowgroup { display:table-row-group; }
div.gencell::before, div.gencell::after { display:table-cell; }
div.genrow::before, div.genrow::after { display:table-row; }
div.genblock::before, div.genblock::after { display:block; }
div.geninline::before, div.geninline::after { display:inline; }
div { border:1px solid green; margin:5px; }
</style>
</head>
<body>
<table style="border:none" width="100%"><tr><td style="border:none" valign="top">
<div><table><tbody><tr class="gen"><td>Inner</td></tr></tbody></table></div>
<div><table><tbody class="gen"><tr><td>Inner</td></tr></tbody></table></div>
<div><table class="gen"><tfoot><tr><td>Inner</td></tr></tfoot></table></div>
<div><table class="gen col"><tbody><tr><td>Inner</td></tr></tbody></table></div>
<div><table class="gen headfoot"><tbody><tr><td>Inner</td></tr></tbody></table></div>
</td><td style="border:none" valign="top">
<div><div class="gen row gencell"><div class="cell">Inner</div></div></div>
<div><div class="gen row genblock"><div class="cell">Inner</div></div></div>
<div><div class="gen row geninline"><div class="cell">Inner</div></div></div>
<div><div class="gen rowgroup genrow"><div class="row"><div class="cell">Inner</div></div></div></div>
<div><div class="gen rowgroup gencell"><div class="row"><div class="cell">Inner</div></div></div></div>
<div><div class="gen rowgroup genblock"><div class="row"><div class="cell">Inner</div></div></div></div>
<div><div class="gen rowgroup geninline"><div class="row"><div class="cell">Inner</div></div></div></div>
</tr></td></table>
</body>
</html>

Просмотреть файл

@ -35,6 +35,9 @@ include columns/reftest.list
# counters/
include counters/reftest.list
# generated-content/
include generated-content/reftest.list
# first-letter/
include first-letter/reftest.list

Просмотреть файл

@ -3307,50 +3307,7 @@ nsRuleNode::ComputeDisplayData(void* aStartStruct,
}
}
// 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.