зеркало из https://github.com/mozilla/pjs.git
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:
Родитель
8302647ae7
Коммит
dfd9fa29d4
|
@ -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.
|
||||
|
|
Загрузка…
Ссылка в новой задаче