Implement 'quotes' property correctly by tracking quote nesting depth using a linked list. Patch by Esben Mose Hansen <esben@oek.dk>, Ulrich Drepper <drepper@redhat.com>, and me. b=24861

This commit is contained in:
dbaron%dbaron.org 2004-04-12 21:53:22 +00:00
parent b23ec21050
commit fcaa82359f
19 changed files with 1173 additions and 128 deletions

View File

@ -910,6 +910,17 @@ struct nsStyleQuotes : public nsStyleStruct {
PRUint32 QuotesCount(void) const { return mQuotesCount; } // [inherited]
const nsString* OpenQuoteAt(PRUint32 aIndex) const
{
NS_ASSERTION(aIndex < mQuotesCount, "out of range");
return mQuotes + (aIndex * 2);
}
const nsString* CloseQuoteAt(PRUint32 aIndex) const
{
NS_ASSERTION(aIndex < mQuotesCount, "out of range");
return mQuotes + (aIndex * 2 + 1);
}
nsresult GetQuotesAt(PRUint32 aIndex, nsString& aOpen, nsString& aClose) const {
if (aIndex < mQuotesCount) {
aIndex *= 2;

View File

@ -1254,11 +1254,12 @@ GetChildListNameFor(nsIPresContext* aPresContext,
//----------------------------------------------------------------------
nsCSSFrameConstructor::nsCSSFrameConstructor(nsIDocument *aDocument)
: mDocument(aDocument),
mInitialContainingBlock(nsnull),
mFixedContainingBlock(nsnull),
mDocElementContainingBlock(nsnull),
mGfxScrollFrame(nsnull)
: mDocument(aDocument)
, mInitialContainingBlock(nsnull)
, mFixedContainingBlock(nsnull)
, mDocElementContainingBlock(nsnull)
, mGfxScrollFrame(nsnull)
, mUpdateCount(0)
{
if (!gGotXBLFormPrefs) {
gGotXBLFormPrefs = PR_TRUE;
@ -1333,6 +1334,13 @@ nsIXBLService * nsCSSFrameConstructor::GetXBLService()
return gXBLService;
}
void
nsCSSFrameConstructor::GeneratedContentFrameRemoved(nsIFrame* aFrame)
{
if (mQuoteList.DestroyNodesFor(aFrame))
QuotesDirty();
}
nsresult
nsCSSFrameConstructor::CreateGeneratedFrameFor(nsIPresContext* aPresContext,
nsIDocument* aDocument,
@ -1345,6 +1353,9 @@ nsCSSFrameConstructor::CreateGeneratedFrameFor(nsIPresContext* aPresContex
{
*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;
@ -1467,36 +1478,27 @@ nsCSSFrameConstructor::CreateGeneratedFrameFor(nsIPresContext* aPresContex
case eStyleContentType_OpenQuote:
case eStyleContentType_CloseQuote:
case eStyleContentType_NoOpenQuote:
case eStyleContentType_NoCloseQuote:
{
const nsStyleQuotes* quotes = aStyleContext->GetStyleQuotes();
PRUint32 quotesCount = quotes->QuotesCount();
if (quotesCount > 0) {
nsAutoString openQuote, closeQuote;
// If the depth is greater than the number of pairs, the last pair
// is repeated
PRUint32 quoteDepth = 0; // XXX really track the nested quotes...
if (quoteDepth > quotesCount) {
quoteDepth = quotesCount - 1;
}
quotes->GetQuotesAt(quoteDepth, openQuote, closeQuote);
if (eStyleContentType_OpenQuote == type) {
contentString = openQuote;
} else {
contentString = closeQuote;
}
} else {
// XXX Don't assume default. Only use what is in 'quotes' property
contentString.Assign(PRUnichar('\"'));
}
nsQuoteListNode* node =
new nsQuoteListNode(type, aParentFrame, aContentIndex);
if (!node)
return NS_ERROR_OUT_OF_MEMORY;
mQuoteList.Insert(node);
if (!mQuoteList.IsLast(node))
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; // Delayed storage of text node.
contentString = *node->Text();
}
break;
case eStyleContentType_NoOpenQuote:
case eStyleContentType_NoCloseQuote:
// XXX Adjust quote depth...
return NS_OK;
} // switch
@ -1506,8 +1508,10 @@ nsCSSFrameConstructor::CreateGeneratedFrameFor(nsIPresContext* aPresContex
if (textContent) {
// Set the text
nsCOMPtr<nsIDOMCharacterData> domData = do_QueryInterface(textContent);
if (domData)
domData->SetData(contentString);
domData->SetData(contentString);
if (textPtr)
*textPtr = domData;
// Set aContent as the parent content and set the document object. This
// way event handling works
@ -10044,6 +10048,24 @@ nsCSSFrameConstructor::AttributeChanged(nsIPresContext* aPresContext,
return result;
}
void
nsCSSFrameConstructor::EndUpdate()
{
if (--mUpdateCount == 0) {
if (mQuotesDirty) {
mQuoteList.RecalcAll();
mQuotesDirty = PR_FALSE;
}
}
}
void
nsCSSFrameConstructor::WillDestroyFrameTree()
{
// Prevent frame tree destruction from being O(N^2)
mQuoteList.Clear();
}
//STATIC
void nsCSSFrameConstructor::GetAlternateTextFor(nsIContent* aContent,
nsIAtom* aTag, // content object's tag

View File

@ -40,6 +40,7 @@
#include "nsCOMPtr.h"
#include "nsILayoutHistoryState.h"
#include "nsIXBLService.h"
#include "nsQuoteList.h"
class nsIDocument;
struct nsFrameItems;
@ -124,12 +125,19 @@ public:
nsIContent* aContent2,
PRInt32 aStateMask);
void GeneratedContentFrameRemoved(nsIFrame* aFrame);
nsresult AttributeChanged(nsIPresContext* aPresContext,
nsIContent* aContent,
PRInt32 aNameSpaceID,
nsIAtom* aAttribute,
PRInt32 aModType);
void BeginUpdate() { ++mUpdateCount; }
void EndUpdate();
void WillDestroyFrameTree();
nsresult ProcessRestyledFrames(nsStyleChangeList& aRestyleArray,
nsIPresContext* aPresContext);
@ -173,7 +181,7 @@ public:
nsIFrame* aRemovedFrame,
nsILayoutHistoryState* aFrameState);
protected:
private:
nsresult ConstructPageFrame(nsIPresShell* aPresShell,
nsIPresContext* aPresContext,
@ -1015,13 +1023,24 @@ protected:
PRUint8 aSiblingDisplay,
nsIContent& aContent,
PRUint8& aDisplay);
protected:
void QuotesDirty() {
if (mUpdateCount != 0)
mQuotesDirty = PR_TRUE;
else
mQuoteList.RecalcAll();
}
private:
nsIDocument* mDocument;
nsIFrame* mInitialContainingBlock;
nsIFrame* mFixedContainingBlock;
nsIFrame* mDocElementContainingBlock;
nsIFrame* mGfxScrollFrame;
nsQuoteList mQuoteList;
PRUint16 mUpdateCount;
PRPackedBool mQuotesDirty;
nsCOMPtr<nsILayoutHistoryState> mTempFrameTreeState;

View File

@ -217,8 +217,12 @@ nsLayoutUtils::IsProperAncestorFrame(nsIFrame* aAncestorFrame, nsIFrame* aFrame,
// static
PRInt32
nsLayoutUtils::CompareTreePosition(nsIContent* aContent1, nsIContent* aContent2,
nsIContent* aCommonAncestor) {
nsLayoutUtils::DoCompareTreePosition(nsIContent* aContent1,
nsIContent* aContent2,
PRInt32 aIf1Ancestor,
PRInt32 aIf2Ancestor,
nsIContent* aCommonAncestor)
{
NS_PRECONDITION(aContent1, "aContent1 must not be null");
NS_PRECONDITION(aContent2, "aContent2 must not be null");
@ -241,7 +245,8 @@ nsLayoutUtils::CompareTreePosition(nsIContent* aContent1, nsIContent* aContent2,
if (!c2 && aCommonAncestor) {
// So, it turns out aCommonAncestor was not an ancestor of c2.
// We need to retry with no common ancestor hint.
return CompareTreePosition(aContent1, aContent2, nsnull);
return DoCompareTreePosition(aContent1, aContent2,
aIf1Ancestor, aIf2Ancestor, nsnull);
}
int last1 = content1Ancestors.Count() - 1;
@ -261,12 +266,12 @@ nsLayoutUtils::CompareTreePosition(nsIContent* aContent1, nsIContent* aContent2,
return 0;
} else {
// aContent1 is an ancestor of aContent2
return -1;
return aIf1Ancestor;
}
} else {
if (last2 < 0) {
// aContent2 is an ancestor of aContent1
return 1;
return aIf2Ancestor;
} else {
// content1Ancestor != content2Ancestor, so they must be siblings with the same parent
nsIContent* parent = content1Ancestor->GetParent();

View File

@ -105,17 +105,38 @@ public:
nsIAtom* aPseudoElement);
/**
* CompareTreePosition determines whether aContent1 comes before or after aContent2 in
* a preorder traversal of the content tree.
* CompareTreePosition determines whether aContent1 comes before or
* after aContent2 in a preorder traversal of the content tree.
*
* @param aCommonAncestor either null, or a common ancestor of aContent1 and aContent2
* Actually this is only a hint; if it's not an ancestor of aContent1 or aContent2,
* this function will still work, but it will be slower than normal.
* @return < 0 if aContent1 is before aContent2, > 0 if aContent1 is after aContent2,
* 0 otherwise (meaning they're the same, or they're in different documents)
* @param aCommonAncestor either null, or a common ancestor of
* aContent1 and aContent2. Actually this is
* only a hint; if it's not an ancestor of
* aContent1 or aContent2, this function will
* still work, but it will be slower than
* normal.
* @return < 0 if aContent1 is before aContent2
* > 0 if aContent1 is after aContent2,
* 0 otherwise (meaning they're the same, or they're in
* different documents)
*/
static PRInt32 CompareTreePosition(nsIContent* aContent1, nsIContent* aContent2,
nsIContent* aCommonAncestor = nsnull);
static PRInt32 CompareTreePosition(nsIContent* aContent1,
nsIContent* aContent2,
nsIContent* aCommonAncestor = nsnull)
{
return DoCompareTreePosition(aContent1, aContent2, -1, 1, aCommonAncestor);
}
/*
* More generic version of |CompareTreePosition|. |aIf1Ancestor|
* gives the value to return when 1 is an ancestor of 2, and likewise
* for |aIf2Ancestor|. Passing (-1, 1) gives preorder traversal
* order, and (1, -1) gives postorder traversal order.
*/
static PRInt32 DoCompareTreePosition(nsIContent* aContent1,
nsIContent* aContent2,
PRInt32 aIf1Ancestor,
PRInt32 aIf2Ancestor,
nsIContent* aCommonAncestor = nsnull);
/**
* GetLastSibling simply finds the last sibling of aFrame, or returns nsnull if

View File

@ -1872,6 +1872,7 @@ PresShell::Destroy()
}
// Destroy the frame manager. This will destroy the frame hierarchy
mFrameConstructor->WillDestroyFrameTree();
FrameManager()->Destroy();
// Let the style set do its cleanup.
@ -3547,6 +3548,8 @@ PresShell::BeginUpdate(nsIDocument *aDocument, nsUpdateType aUpdateType)
#ifdef DEBUG
mUpdateCount++;
#endif
mFrameConstructor->BeginUpdate();
if (aUpdateType & UPDATE_STYLE)
mStyleSet->BeginUpdate();
}
@ -3559,11 +3562,13 @@ PresShell::EndUpdate(nsIDocument *aDocument, nsUpdateType aUpdateType)
--mUpdateCount;
#endif
if (aUpdateType & UPDATE_STYLE)
if (aUpdateType & UPDATE_STYLE) {
mStyleSet->EndUpdate();
if (mStylesHaveChanged)
ReconstructStyleData();
}
if (mStylesHaveChanged && (aUpdateType & UPDATE_STYLE))
ReconstructStyleData();
mFrameConstructor->EndUpdate();
}
void

279
layout/base/nsQuoteList.cpp Normal file
View File

@ -0,0 +1,279 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
// vim:cindent:ts=2:et:sw=2:
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Esben Mose Hansen.
*
* Contributor(s):
* Esben Mose Hansen <esben@oek.dk> (original author)
* L. David Baron <dbaron@dbaron.org>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsQuoteList.h"
#include "nsIDOMCharacterData.h"
#include "nsCSSPseudoElements.h"
#include "nsLayoutUtils.h"
#include "nsReadableUtils.h"
const nsString*
nsQuoteListNode::Text()
{
NS_ASSERTION(mType == eStyleContentType_OpenQuote ||
mType == eStyleContentType_CloseQuote,
"should only be called when mText should be non-null");
const nsStyleQuotes* styleQuotes = mPseudoFrame->GetStyleQuotes();
PRInt32 quotesCount = styleQuotes->QuotesCount(); // 0 if 'quotes:none'
PRInt32 quoteDepth = Depth();
// Reuse the last pair when the depth is greater than the number of
// pairs of quotes. (Also make 'quotes: none' and close-quote from
// a depth of 0 equivalent for the next test.)
if (quoteDepth >= quotesCount)
quoteDepth = quotesCount - 1;
const nsString *result;
if (quoteDepth == -1) {
// close-quote from a depth of 0 or 'quotes: none' (we want a node
// with the empty string so dynamic changes are easier to handle)
result = & EmptyString();
} else {
result = eStyleContentType_OpenQuote == mType
? styleQuotes->OpenQuoteAt(quoteDepth)
: styleQuotes->CloseQuoteAt(quoteDepth);
}
return result;
}
void
nsQuoteList::Calc(nsQuoteListNode* aNode)
{
if (aNode == mFirstNode) {
aNode->mDepthBefore = 0;
} else {
aNode->mDepthBefore = Prev(aNode)->DepthAfter();
}
}
void
nsQuoteList::RecalcAll()
{
nsQuoteListNode *node = mFirstNode;
if (!node)
return;
do {
PRInt32 oldDepth = node->mDepthBefore;
Calc(node);
if (node->mDepthBefore != oldDepth && node->mText)
node->mText->SetData(*node->Text());
// Next node
node = Next(node);
} while (node != mFirstNode);
}
#ifdef DEBUG
void
nsQuoteList::PrintChain()
{
printf("Chain: \n");
if (!mFirstNode) {
return;
}
nsQuoteListNode* node = mFirstNode;
do {
printf(" %p %d - ", node, node->mDepthBefore);
switch(node->mType) {
case (eStyleContentType_OpenQuote):
printf("open");
break;
case (eStyleContentType_NoOpenQuote):
printf("noOpen");
break;
case (eStyleContentType_CloseQuote):
printf("close");
break;
case (eStyleContentType_NoCloseQuote):
printf("noClose");
break;
default:
printf("unknown!!!");
}
printf(" %d - %d,", node->Depth(), node->DepthAfter());
if (node->mText) {
nsAutoString data;
node->mText->GetData(data);
printf(" \"%s\",", NS_ConvertUCS2toUTF8(data).get());
}
printf("\n");
node = Next(node);
} while (node != mFirstNode);
}
#endif
void
nsQuoteList::Clear()
{
//Delete entire list
if (!mFirstNode)
return;
for (nsQuoteListNode *node = Next(mFirstNode); node != mFirstNode;
node = Next(mFirstNode))
{
Remove(node);
delete node;
}
delete mFirstNode;
mFirstNode = nsnull;
mSize = 0;
}
PRBool
nsQuoteList::DestroyNodesFor(nsIFrame* aFrame)
{
if (!mFirstNode)
return PR_FALSE; // list empty
nsQuoteListNode* node;
PRBool destroyed = PR_FALSE;
while (mFirstNode->mPseudoFrame == aFrame) {
destroyed = PR_TRUE;
node = Next(mFirstNode);
if (node == mFirstNode) { // Last link
mFirstNode = nsnull;
delete node;
return PR_TRUE;
}
else {
Remove(mFirstNode);
delete mFirstNode;
mFirstNode = node;
}
}
node = Next(mFirstNode);
while (node != mFirstNode) {
if (node->mPseudoFrame == aFrame) {
destroyed = PR_TRUE;
nsQuoteListNode *nextNode = Next(node);
Remove(node);
delete node;
node = nextNode;
} else {
node = Next(node);
}
}
return destroyed;
}
// return -1 for ::before and +1 for ::after
inline PRBool PseudoCompareType(nsIFrame *aFrame)
{
nsIAtom *pseudo = aFrame->GetStyleContext()->GetPseudoType();
NS_ASSERTION(pseudo == nsCSSPseudoElements::before ||
pseudo == nsCSSPseudoElements::after,
"not a pseudo-element frame");
return pseudo == nsCSSPseudoElements::before ? -1 : 1;
}
static PRBool NodeAfter(nsQuoteListNode* aNode1, nsQuoteListNode* aNode2)
{
nsIFrame *frame1 = aNode1->mPseudoFrame;
nsIFrame *frame2 = aNode2->mPseudoFrame;
if (frame1 == frame2) {
NS_ASSERTION(aNode2->mContentIndex != aNode1->mContentIndex, "identical");
return aNode1->mContentIndex > aNode2->mContentIndex;
}
PRInt32 pseudoType1 = PseudoCompareType(frame1);
PRInt32 pseudoType2 = PseudoCompareType(frame2);
nsIContent *content1 = frame1->GetContent();
nsIContent *content2 = frame2->GetContent();
if (content1 == content2) {
NS_ASSERTION(pseudoType1 != pseudoType2, "identical");
return pseudoType1 == 1;
}
PRInt32 cmp = nsLayoutUtils::DoCompareTreePosition(content1, content2,
pseudoType1, -pseudoType2);
NS_ASSERTION(cmp != 0, "same content, different frames");
return cmp > 0;
}
void
nsQuoteList::Insert(nsQuoteListNode* aNode)
{
if (mFirstNode) {
// Check for append.
if (NodeAfter(aNode, Prev(mFirstNode))) {
PR_INSERT_BEFORE(aNode, mFirstNode);
}
else {
// Binary search.
// the range of indices at which |aNode| could end up.
PRUint32 first = 0, last = mSize - 1;
// A cursor to avoid walking more than the length of the list.
nsQuoteListNode *curNode = Prev(mFirstNode);
PRUint32 curIndex = mSize - 1;
while (first != last) {
PRUint32 test = (first + last) / 2;
if (last == curIndex) {
for ( ; curIndex != test; --curIndex)
curNode = Prev(curNode);
} else {
for ( ; curIndex != test; ++curIndex)
curNode = Next(curNode);
}
if (NodeAfter(aNode, curNode)) {
first = test + 1;
// if we exit the loop, we need curNode to be right
++curIndex;
curNode = Next(curNode);
} else {
last = test;
}
}
PR_INSERT_BEFORE(aNode, curNode);
if (curNode == mFirstNode) {
mFirstNode = aNode;
}
}
}
else {
// initialize list with first node
PR_INIT_CLIST(aNode);
mFirstNode = aNode;
}
++mSize;
Calc(aNode);
}

156
layout/base/nsQuoteList.h Normal file
View File

@ -0,0 +1,156 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Esben Mose Hansen.
*
* Contributor(s):
* Esben Mose Hansen <esben@oek.dk> (original author)
* L. David Baron <dbaron@dbaron.org>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef nsQuoteList_h___
#define nsQuoteList_h___
#include "nsIFrame.h"
#include "nsStyleStruct.h"
#include "prclist.h"
#include "nsIDOMCharacterData.h"
#include "nsCSSPseudoElements.h"
struct nsQuoteListNode : public PRCList {
// open-quote, close-quote, no-open-quote, or no-close-quote
const nsStyleContentType mType;
// Quote depth before this quote, which is always non-negative.
PRInt32 mDepthBefore;
// Index within the list of things specified by the 'content' property,
// which is needed to do 'content: open-quote open-quote' correctly.
const PRUint32 mContentIndex;
// null for 'content:no-open-quote', 'content:no-close-quote'
nsCOMPtr<nsIDOMCharacterData> mText;
// The wrapper frame for all of the pseudo-element's content. This
// frame has useful style data and has the NS_FRAME_GENERATED_CONTENT
// bit set (so we use it to track removal).
nsIFrame* const mPseudoFrame;
nsQuoteListNode(nsStyleContentType& aType, nsIFrame* aPseudoFrame,
PRUint32 aContentIndex)
: mType(aType)
, mDepthBefore(0)
, mContentIndex(aContentIndex)
, mPseudoFrame(aPseudoFrame)
{
NS_ASSERTION(aType == eStyleContentType_OpenQuote ||
aType == eStyleContentType_CloseQuote ||
aType == eStyleContentType_NoOpenQuote ||
aType == eStyleContentType_NoCloseQuote,
"incorrect type");
NS_ASSERTION(aPseudoFrame->GetStateBits() & NS_FRAME_GENERATED_CONTENT,
"not generated content");
NS_ASSERTION(aPseudoFrame->GetStyleContext()->GetPseudoType() ==
nsCSSPseudoElements::before ||
aPseudoFrame->GetStyleContext()->GetPseudoType() ==
nsCSSPseudoElements::after,
"not :before/:after generated content");
NS_ASSERTION(aContentIndex <
aPseudoFrame->GetStyleContent()->ContentCount(),
"index out of range");
}
// is this 'open-quote' or 'no-open-quote'?
PRBool IsOpenQuote() {
return mType == eStyleContentType_OpenQuote ||
mType == eStyleContentType_NoOpenQuote;
}
// is this 'close-quote' or 'no-close-quote'?
PRBool IsCloseQuote() {
return !IsOpenQuote();
}
// is this 'open-quote' or 'close-quote'?
PRBool IsRealQuote() {
return mType == eStyleContentType_OpenQuote ||
mType == eStyleContentType_CloseQuote;
}
// is this 'no-open-quote' or 'no-close-quote'?
PRBool IsHiddenQuote() {
return !IsRealQuote();
}
// Depth of the quote for *this* node. Either non-negative or -1.
// -1 means this is a closing quote that tried to decrement the
// counter below zero (which means no quote should be rendered).
PRInt32 Depth() {
return IsOpenQuote() ? mDepthBefore : mDepthBefore - 1;
}
// always non-negative
PRInt32 DepthAfter() {
return IsOpenQuote() ? mDepthBefore + 1
: (mDepthBefore == 0 ? 0 : mDepthBefore - 1);
}
// The text that should be displayed for this quote.
const nsString* Text();
};
class nsQuoteList {
private:
nsQuoteListNode* mFirstNode;
PRUint32 mSize;
// assign the correct |mDepthBefore| value to a node that has been inserted
void Calc(nsQuoteListNode* aNode);
public:
nsQuoteList() : mFirstNode(nsnull), mSize(0) {}
~nsQuoteList() { Clear(); }
void Clear();
nsQuoteListNode* Next(nsQuoteListNode* aNode) {
return NS_STATIC_CAST(nsQuoteListNode*, PR_NEXT_LINK(aNode));
}
nsQuoteListNode* Prev(nsQuoteListNode* aNode) {
return NS_STATIC_CAST(nsQuoteListNode*, PR_PREV_LINK(aNode));
}
void Insert(nsQuoteListNode* aNode);
// returns whether any nodes have been destroyed
PRBool DestroyNodesFor(nsIFrame* aFrame); //destroy all nodes with aFrame as parent
void Remove(nsQuoteListNode* aNode) { PR_REMOVE_LINK(aNode); mSize--; }
void RecalcAll();
PRBool IsLast(nsQuoteListNode* aNode) { return (Next(aNode) == mFirstNode); }
#ifdef DEBUG
void PrintChain();
#endif
};
#endif /* nsQuoteList_h___ */

View File

@ -105,17 +105,38 @@ public:
nsIAtom* aPseudoElement);
/**
* CompareTreePosition determines whether aContent1 comes before or after aContent2 in
* a preorder traversal of the content tree.
* CompareTreePosition determines whether aContent1 comes before or
* after aContent2 in a preorder traversal of the content tree.
*
* @param aCommonAncestor either null, or a common ancestor of aContent1 and aContent2
* Actually this is only a hint; if it's not an ancestor of aContent1 or aContent2,
* this function will still work, but it will be slower than normal.
* @return < 0 if aContent1 is before aContent2, > 0 if aContent1 is after aContent2,
* 0 otherwise (meaning they're the same, or they're in different documents)
* @param aCommonAncestor either null, or a common ancestor of
* aContent1 and aContent2. Actually this is
* only a hint; if it's not an ancestor of
* aContent1 or aContent2, this function will
* still work, but it will be slower than
* normal.
* @return < 0 if aContent1 is before aContent2
* > 0 if aContent1 is after aContent2,
* 0 otherwise (meaning they're the same, or they're in
* different documents)
*/
static PRInt32 CompareTreePosition(nsIContent* aContent1, nsIContent* aContent2,
nsIContent* aCommonAncestor = nsnull);
static PRInt32 CompareTreePosition(nsIContent* aContent1,
nsIContent* aContent2,
nsIContent* aCommonAncestor = nsnull)
{
return DoCompareTreePosition(aContent1, aContent2, -1, 1, aCommonAncestor);
}
/*
* More generic version of |CompareTreePosition|. |aIf1Ancestor|
* gives the value to return when 1 is an ancestor of 2, and likewise
* for |aIf2Ancestor|. Passing (-1, 1) gives preorder traversal
* order, and (1, -1) gives postorder traversal order.
*/
static PRInt32 DoCompareTreePosition(nsIContent* aContent1,
nsIContent* aContent2,
PRInt32 aIf1Ancestor,
PRInt32 aIf2Ancestor,
nsIContent* aCommonAncestor = nsnull);
/**
* GetLastSibling simply finds the last sibling of aFrame, or returns nsnull if

View File

@ -217,8 +217,12 @@ nsLayoutUtils::IsProperAncestorFrame(nsIFrame* aAncestorFrame, nsIFrame* aFrame,
// static
PRInt32
nsLayoutUtils::CompareTreePosition(nsIContent* aContent1, nsIContent* aContent2,
nsIContent* aCommonAncestor) {
nsLayoutUtils::DoCompareTreePosition(nsIContent* aContent1,
nsIContent* aContent2,
PRInt32 aIf1Ancestor,
PRInt32 aIf2Ancestor,
nsIContent* aCommonAncestor)
{
NS_PRECONDITION(aContent1, "aContent1 must not be null");
NS_PRECONDITION(aContent2, "aContent2 must not be null");
@ -241,7 +245,8 @@ nsLayoutUtils::CompareTreePosition(nsIContent* aContent1, nsIContent* aContent2,
if (!c2 && aCommonAncestor) {
// So, it turns out aCommonAncestor was not an ancestor of c2.
// We need to retry with no common ancestor hint.
return CompareTreePosition(aContent1, aContent2, nsnull);
return DoCompareTreePosition(aContent1, aContent2,
aIf1Ancestor, aIf2Ancestor, nsnull);
}
int last1 = content1Ancestors.Count() - 1;
@ -261,12 +266,12 @@ nsLayoutUtils::CompareTreePosition(nsIContent* aContent1, nsIContent* aContent2,
return 0;
} else {
// aContent1 is an ancestor of aContent2
return -1;
return aIf1Ancestor;
}
} else {
if (last2 < 0) {
// aContent2 is an ancestor of aContent1
return 1;
return aIf2Ancestor;
} else {
// content1Ancestor != content2Ancestor, so they must be siblings with the same parent
nsIContent* parent = content1Ancestor->GetParent();

View File

@ -79,6 +79,7 @@
#include "nsCSSPseudoElements.h"
#include "nsHTMLAtoms.h"
#include "nsIHTMLContentSink.h"
#include "nsCSSFrameConstructor.h"
#include "nsFrameTraversal.h"
#include "nsCOMPtr.h"
@ -609,22 +610,25 @@ nsFrame::ReplaceFrame(nsIPresContext* aPresContext,
NS_IMETHODIMP
nsFrame::Destroy(nsIPresContext* aPresContext)
{
nsIPresShell *shell = aPresContext->GetPresShell();
// Get the view pointer now before the frame properties disappear
// when we call NotifyDestroyingFrame()
nsIView* view = GetView();
// XXX Rather than always doing this it would be better if it was part of
// a frame observer mechanism and the pres shell could register as an
// observer of the frame while the reflow command is pending...
if (shell) {
shell->NotifyDestroyingFrame(this);
}
if ((mState & NS_FRAME_EXTERNAL_REFERENCE) ||
(mState & NS_FRAME_SELECTED_CONTENT)) {
if (shell) {
nsIPresShell *shell = aPresContext->GetPresShell();
if (shell) {
// If the frame contains generated context, remove it from
// the quoteList.
if (mState & NS_FRAME_GENERATED_CONTENT) {
shell->FrameConstructor()->GeneratedContentFrameRemoved(this);
}
// XXX Rather than always doing this it would be better if it was part of
// a frame observer mechanism and the pres shell could register as an
// observer of the frame while the reflow command is pending...
shell->NotifyDestroyingFrame(this);
if ((mState & NS_FRAME_EXTERNAL_REFERENCE) ||
(mState & NS_FRAME_SELECTED_CONTENT)) {
shell->ClearFrameRefs(this);
}
}

View File

@ -79,6 +79,7 @@
#include "nsCSSPseudoElements.h"
#include "nsHTMLAtoms.h"
#include "nsIHTMLContentSink.h"
#include "nsCSSFrameConstructor.h"
#include "nsFrameTraversal.h"
#include "nsCOMPtr.h"
@ -609,22 +610,25 @@ nsFrame::ReplaceFrame(nsIPresContext* aPresContext,
NS_IMETHODIMP
nsFrame::Destroy(nsIPresContext* aPresContext)
{
nsIPresShell *shell = aPresContext->GetPresShell();
// Get the view pointer now before the frame properties disappear
// when we call NotifyDestroyingFrame()
nsIView* view = GetView();
// XXX Rather than always doing this it would be better if it was part of
// a frame observer mechanism and the pres shell could register as an
// observer of the frame while the reflow command is pending...
if (shell) {
shell->NotifyDestroyingFrame(this);
}
if ((mState & NS_FRAME_EXTERNAL_REFERENCE) ||
(mState & NS_FRAME_SELECTED_CONTENT)) {
if (shell) {
nsIPresShell *shell = aPresContext->GetPresShell();
if (shell) {
// If the frame contains generated context, remove it from
// the quoteList.
if (mState & NS_FRAME_GENERATED_CONTENT) {
shell->FrameConstructor()->GeneratedContentFrameRemoved(this);
}
// XXX Rather than always doing this it would be better if it was part of
// a frame observer mechanism and the pres shell could register as an
// observer of the frame while the reflow command is pending...
shell->NotifyDestroyingFrame(this);
if ((mState & NS_FRAME_EXTERNAL_REFERENCE) ||
(mState & NS_FRAME_SELECTED_CONTENT)) {
shell->ClearFrameRefs(this);
}
}

View File

@ -1872,6 +1872,7 @@ PresShell::Destroy()
}
// Destroy the frame manager. This will destroy the frame hierarchy
mFrameConstructor->WillDestroyFrameTree();
FrameManager()->Destroy();
// Let the style set do its cleanup.
@ -3547,6 +3548,8 @@ PresShell::BeginUpdate(nsIDocument *aDocument, nsUpdateType aUpdateType)
#ifdef DEBUG
mUpdateCount++;
#endif
mFrameConstructor->BeginUpdate();
if (aUpdateType & UPDATE_STYLE)
mStyleSet->BeginUpdate();
}
@ -3559,11 +3562,13 @@ PresShell::EndUpdate(nsIDocument *aDocument, nsUpdateType aUpdateType)
--mUpdateCount;
#endif
if (aUpdateType & UPDATE_STYLE)
if (aUpdateType & UPDATE_STYLE) {
mStyleSet->EndUpdate();
if (mStylesHaveChanged)
ReconstructStyleData();
}
if (mStylesHaveChanged && (aUpdateType & UPDATE_STYLE))
ReconstructStyleData();
mFrameConstructor->EndUpdate();
}
void

View File

@ -51,6 +51,7 @@ CPPSRCS = \
nsCSSColorUtils.cpp \
nsFrameContentIterator.cpp \
nsChildIterator.cpp \
nsQuoteList.cpp \
$(NULL)
EXPORTS = \

View File

@ -1254,11 +1254,12 @@ GetChildListNameFor(nsIPresContext* aPresContext,
//----------------------------------------------------------------------
nsCSSFrameConstructor::nsCSSFrameConstructor(nsIDocument *aDocument)
: mDocument(aDocument),
mInitialContainingBlock(nsnull),
mFixedContainingBlock(nsnull),
mDocElementContainingBlock(nsnull),
mGfxScrollFrame(nsnull)
: mDocument(aDocument)
, mInitialContainingBlock(nsnull)
, mFixedContainingBlock(nsnull)
, mDocElementContainingBlock(nsnull)
, mGfxScrollFrame(nsnull)
, mUpdateCount(0)
{
if (!gGotXBLFormPrefs) {
gGotXBLFormPrefs = PR_TRUE;
@ -1333,6 +1334,13 @@ nsIXBLService * nsCSSFrameConstructor::GetXBLService()
return gXBLService;
}
void
nsCSSFrameConstructor::GeneratedContentFrameRemoved(nsIFrame* aFrame)
{
if (mQuoteList.DestroyNodesFor(aFrame))
QuotesDirty();
}
nsresult
nsCSSFrameConstructor::CreateGeneratedFrameFor(nsIPresContext* aPresContext,
nsIDocument* aDocument,
@ -1345,6 +1353,9 @@ nsCSSFrameConstructor::CreateGeneratedFrameFor(nsIPresContext* aPresContex
{
*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;
@ -1467,36 +1478,27 @@ nsCSSFrameConstructor::CreateGeneratedFrameFor(nsIPresContext* aPresContex
case eStyleContentType_OpenQuote:
case eStyleContentType_CloseQuote:
case eStyleContentType_NoOpenQuote:
case eStyleContentType_NoCloseQuote:
{
const nsStyleQuotes* quotes = aStyleContext->GetStyleQuotes();
PRUint32 quotesCount = quotes->QuotesCount();
if (quotesCount > 0) {
nsAutoString openQuote, closeQuote;
// If the depth is greater than the number of pairs, the last pair
// is repeated
PRUint32 quoteDepth = 0; // XXX really track the nested quotes...
if (quoteDepth > quotesCount) {
quoteDepth = quotesCount - 1;
}
quotes->GetQuotesAt(quoteDepth, openQuote, closeQuote);
if (eStyleContentType_OpenQuote == type) {
contentString = openQuote;
} else {
contentString = closeQuote;
}
} else {
// XXX Don't assume default. Only use what is in 'quotes' property
contentString.Assign(PRUnichar('\"'));
}
nsQuoteListNode* node =
new nsQuoteListNode(type, aParentFrame, aContentIndex);
if (!node)
return NS_ERROR_OUT_OF_MEMORY;
mQuoteList.Insert(node);
if (!mQuoteList.IsLast(node))
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; // Delayed storage of text node.
contentString = *node->Text();
}
break;
case eStyleContentType_NoOpenQuote:
case eStyleContentType_NoCloseQuote:
// XXX Adjust quote depth...
return NS_OK;
} // switch
@ -1506,8 +1508,10 @@ nsCSSFrameConstructor::CreateGeneratedFrameFor(nsIPresContext* aPresContex
if (textContent) {
// Set the text
nsCOMPtr<nsIDOMCharacterData> domData = do_QueryInterface(textContent);
if (domData)
domData->SetData(contentString);
domData->SetData(contentString);
if (textPtr)
*textPtr = domData;
// Set aContent as the parent content and set the document object. This
// way event handling works
@ -10044,6 +10048,24 @@ nsCSSFrameConstructor::AttributeChanged(nsIPresContext* aPresContext,
return result;
}
void
nsCSSFrameConstructor::EndUpdate()
{
if (--mUpdateCount == 0) {
if (mQuotesDirty) {
mQuoteList.RecalcAll();
mQuotesDirty = PR_FALSE;
}
}
}
void
nsCSSFrameConstructor::WillDestroyFrameTree()
{
// Prevent frame tree destruction from being O(N^2)
mQuoteList.Clear();
}
//STATIC
void nsCSSFrameConstructor::GetAlternateTextFor(nsIContent* aContent,
nsIAtom* aTag, // content object's tag

View File

@ -40,6 +40,7 @@
#include "nsCOMPtr.h"
#include "nsILayoutHistoryState.h"
#include "nsIXBLService.h"
#include "nsQuoteList.h"
class nsIDocument;
struct nsFrameItems;
@ -124,12 +125,19 @@ public:
nsIContent* aContent2,
PRInt32 aStateMask);
void GeneratedContentFrameRemoved(nsIFrame* aFrame);
nsresult AttributeChanged(nsIPresContext* aPresContext,
nsIContent* aContent,
PRInt32 aNameSpaceID,
nsIAtom* aAttribute,
PRInt32 aModType);
void BeginUpdate() { ++mUpdateCount; }
void EndUpdate();
void WillDestroyFrameTree();
nsresult ProcessRestyledFrames(nsStyleChangeList& aRestyleArray,
nsIPresContext* aPresContext);
@ -173,7 +181,7 @@ public:
nsIFrame* aRemovedFrame,
nsILayoutHistoryState* aFrameState);
protected:
private:
nsresult ConstructPageFrame(nsIPresShell* aPresShell,
nsIPresContext* aPresContext,
@ -1015,13 +1023,24 @@ protected:
PRUint8 aSiblingDisplay,
nsIContent& aContent,
PRUint8& aDisplay);
protected:
void QuotesDirty() {
if (mUpdateCount != 0)
mQuotesDirty = PR_TRUE;
else
mQuoteList.RecalcAll();
}
private:
nsIDocument* mDocument;
nsIFrame* mInitialContainingBlock;
nsIFrame* mFixedContainingBlock;
nsIFrame* mDocElementContainingBlock;
nsIFrame* mGfxScrollFrame;
nsQuoteList mQuoteList;
PRUint16 mUpdateCount;
PRPackedBool mQuotesDirty;
nsCOMPtr<nsILayoutHistoryState> mTempFrameTreeState;

View File

@ -0,0 +1,279 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
// vim:cindent:ts=2:et:sw=2:
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Esben Mose Hansen.
*
* Contributor(s):
* Esben Mose Hansen <esben@oek.dk> (original author)
* L. David Baron <dbaron@dbaron.org>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsQuoteList.h"
#include "nsIDOMCharacterData.h"
#include "nsCSSPseudoElements.h"
#include "nsLayoutUtils.h"
#include "nsReadableUtils.h"
const nsString*
nsQuoteListNode::Text()
{
NS_ASSERTION(mType == eStyleContentType_OpenQuote ||
mType == eStyleContentType_CloseQuote,
"should only be called when mText should be non-null");
const nsStyleQuotes* styleQuotes = mPseudoFrame->GetStyleQuotes();
PRInt32 quotesCount = styleQuotes->QuotesCount(); // 0 if 'quotes:none'
PRInt32 quoteDepth = Depth();
// Reuse the last pair when the depth is greater than the number of
// pairs of quotes. (Also make 'quotes: none' and close-quote from
// a depth of 0 equivalent for the next test.)
if (quoteDepth >= quotesCount)
quoteDepth = quotesCount - 1;
const nsString *result;
if (quoteDepth == -1) {
// close-quote from a depth of 0 or 'quotes: none' (we want a node
// with the empty string so dynamic changes are easier to handle)
result = & EmptyString();
} else {
result = eStyleContentType_OpenQuote == mType
? styleQuotes->OpenQuoteAt(quoteDepth)
: styleQuotes->CloseQuoteAt(quoteDepth);
}
return result;
}
void
nsQuoteList::Calc(nsQuoteListNode* aNode)
{
if (aNode == mFirstNode) {
aNode->mDepthBefore = 0;
} else {
aNode->mDepthBefore = Prev(aNode)->DepthAfter();
}
}
void
nsQuoteList::RecalcAll()
{
nsQuoteListNode *node = mFirstNode;
if (!node)
return;
do {
PRInt32 oldDepth = node->mDepthBefore;
Calc(node);
if (node->mDepthBefore != oldDepth && node->mText)
node->mText->SetData(*node->Text());
// Next node
node = Next(node);
} while (node != mFirstNode);
}
#ifdef DEBUG
void
nsQuoteList::PrintChain()
{
printf("Chain: \n");
if (!mFirstNode) {
return;
}
nsQuoteListNode* node = mFirstNode;
do {
printf(" %p %d - ", node, node->mDepthBefore);
switch(node->mType) {
case (eStyleContentType_OpenQuote):
printf("open");
break;
case (eStyleContentType_NoOpenQuote):
printf("noOpen");
break;
case (eStyleContentType_CloseQuote):
printf("close");
break;
case (eStyleContentType_NoCloseQuote):
printf("noClose");
break;
default:
printf("unknown!!!");
}
printf(" %d - %d,", node->Depth(), node->DepthAfter());
if (node->mText) {
nsAutoString data;
node->mText->GetData(data);
printf(" \"%s\",", NS_ConvertUCS2toUTF8(data).get());
}
printf("\n");
node = Next(node);
} while (node != mFirstNode);
}
#endif
void
nsQuoteList::Clear()
{
//Delete entire list
if (!mFirstNode)
return;
for (nsQuoteListNode *node = Next(mFirstNode); node != mFirstNode;
node = Next(mFirstNode))
{
Remove(node);
delete node;
}
delete mFirstNode;
mFirstNode = nsnull;
mSize = 0;
}
PRBool
nsQuoteList::DestroyNodesFor(nsIFrame* aFrame)
{
if (!mFirstNode)
return PR_FALSE; // list empty
nsQuoteListNode* node;
PRBool destroyed = PR_FALSE;
while (mFirstNode->mPseudoFrame == aFrame) {
destroyed = PR_TRUE;
node = Next(mFirstNode);
if (node == mFirstNode) { // Last link
mFirstNode = nsnull;
delete node;
return PR_TRUE;
}
else {
Remove(mFirstNode);
delete mFirstNode;
mFirstNode = node;
}
}
node = Next(mFirstNode);
while (node != mFirstNode) {
if (node->mPseudoFrame == aFrame) {
destroyed = PR_TRUE;
nsQuoteListNode *nextNode = Next(node);
Remove(node);
delete node;
node = nextNode;
} else {
node = Next(node);
}
}
return destroyed;
}
// return -1 for ::before and +1 for ::after
inline PRBool PseudoCompareType(nsIFrame *aFrame)
{
nsIAtom *pseudo = aFrame->GetStyleContext()->GetPseudoType();
NS_ASSERTION(pseudo == nsCSSPseudoElements::before ||
pseudo == nsCSSPseudoElements::after,
"not a pseudo-element frame");
return pseudo == nsCSSPseudoElements::before ? -1 : 1;
}
static PRBool NodeAfter(nsQuoteListNode* aNode1, nsQuoteListNode* aNode2)
{
nsIFrame *frame1 = aNode1->mPseudoFrame;
nsIFrame *frame2 = aNode2->mPseudoFrame;
if (frame1 == frame2) {
NS_ASSERTION(aNode2->mContentIndex != aNode1->mContentIndex, "identical");
return aNode1->mContentIndex > aNode2->mContentIndex;
}
PRInt32 pseudoType1 = PseudoCompareType(frame1);
PRInt32 pseudoType2 = PseudoCompareType(frame2);
nsIContent *content1 = frame1->GetContent();
nsIContent *content2 = frame2->GetContent();
if (content1 == content2) {
NS_ASSERTION(pseudoType1 != pseudoType2, "identical");
return pseudoType1 == 1;
}
PRInt32 cmp = nsLayoutUtils::DoCompareTreePosition(content1, content2,
pseudoType1, -pseudoType2);
NS_ASSERTION(cmp != 0, "same content, different frames");
return cmp > 0;
}
void
nsQuoteList::Insert(nsQuoteListNode* aNode)
{
if (mFirstNode) {
// Check for append.
if (NodeAfter(aNode, Prev(mFirstNode))) {
PR_INSERT_BEFORE(aNode, mFirstNode);
}
else {
// Binary search.
// the range of indices at which |aNode| could end up.
PRUint32 first = 0, last = mSize - 1;
// A cursor to avoid walking more than the length of the list.
nsQuoteListNode *curNode = Prev(mFirstNode);
PRUint32 curIndex = mSize - 1;
while (first != last) {
PRUint32 test = (first + last) / 2;
if (last == curIndex) {
for ( ; curIndex != test; --curIndex)
curNode = Prev(curNode);
} else {
for ( ; curIndex != test; ++curIndex)
curNode = Next(curNode);
}
if (NodeAfter(aNode, curNode)) {
first = test + 1;
// if we exit the loop, we need curNode to be right
++curIndex;
curNode = Next(curNode);
} else {
last = test;
}
}
PR_INSERT_BEFORE(aNode, curNode);
if (curNode == mFirstNode) {
mFirstNode = aNode;
}
}
}
else {
// initialize list with first node
PR_INIT_CLIST(aNode);
mFirstNode = aNode;
}
++mSize;
Calc(aNode);
}

View File

@ -0,0 +1,156 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Esben Mose Hansen.
*
* Contributor(s):
* Esben Mose Hansen <esben@oek.dk> (original author)
* L. David Baron <dbaron@dbaron.org>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef nsQuoteList_h___
#define nsQuoteList_h___
#include "nsIFrame.h"
#include "nsStyleStruct.h"
#include "prclist.h"
#include "nsIDOMCharacterData.h"
#include "nsCSSPseudoElements.h"
struct nsQuoteListNode : public PRCList {
// open-quote, close-quote, no-open-quote, or no-close-quote
const nsStyleContentType mType;
// Quote depth before this quote, which is always non-negative.
PRInt32 mDepthBefore;
// Index within the list of things specified by the 'content' property,
// which is needed to do 'content: open-quote open-quote' correctly.
const PRUint32 mContentIndex;
// null for 'content:no-open-quote', 'content:no-close-quote'
nsCOMPtr<nsIDOMCharacterData> mText;
// The wrapper frame for all of the pseudo-element's content. This
// frame has useful style data and has the NS_FRAME_GENERATED_CONTENT
// bit set (so we use it to track removal).
nsIFrame* const mPseudoFrame;
nsQuoteListNode(nsStyleContentType& aType, nsIFrame* aPseudoFrame,
PRUint32 aContentIndex)
: mType(aType)
, mDepthBefore(0)
, mContentIndex(aContentIndex)
, mPseudoFrame(aPseudoFrame)
{
NS_ASSERTION(aType == eStyleContentType_OpenQuote ||
aType == eStyleContentType_CloseQuote ||
aType == eStyleContentType_NoOpenQuote ||
aType == eStyleContentType_NoCloseQuote,
"incorrect type");
NS_ASSERTION(aPseudoFrame->GetStateBits() & NS_FRAME_GENERATED_CONTENT,
"not generated content");
NS_ASSERTION(aPseudoFrame->GetStyleContext()->GetPseudoType() ==
nsCSSPseudoElements::before ||
aPseudoFrame->GetStyleContext()->GetPseudoType() ==
nsCSSPseudoElements::after,
"not :before/:after generated content");
NS_ASSERTION(aContentIndex <
aPseudoFrame->GetStyleContent()->ContentCount(),
"index out of range");
}
// is this 'open-quote' or 'no-open-quote'?
PRBool IsOpenQuote() {
return mType == eStyleContentType_OpenQuote ||
mType == eStyleContentType_NoOpenQuote;
}
// is this 'close-quote' or 'no-close-quote'?
PRBool IsCloseQuote() {
return !IsOpenQuote();
}
// is this 'open-quote' or 'close-quote'?
PRBool IsRealQuote() {
return mType == eStyleContentType_OpenQuote ||
mType == eStyleContentType_CloseQuote;
}
// is this 'no-open-quote' or 'no-close-quote'?
PRBool IsHiddenQuote() {
return !IsRealQuote();
}
// Depth of the quote for *this* node. Either non-negative or -1.
// -1 means this is a closing quote that tried to decrement the
// counter below zero (which means no quote should be rendered).
PRInt32 Depth() {
return IsOpenQuote() ? mDepthBefore : mDepthBefore - 1;
}
// always non-negative
PRInt32 DepthAfter() {
return IsOpenQuote() ? mDepthBefore + 1
: (mDepthBefore == 0 ? 0 : mDepthBefore - 1);
}
// The text that should be displayed for this quote.
const nsString* Text();
};
class nsQuoteList {
private:
nsQuoteListNode* mFirstNode;
PRUint32 mSize;
// assign the correct |mDepthBefore| value to a node that has been inserted
void Calc(nsQuoteListNode* aNode);
public:
nsQuoteList() : mFirstNode(nsnull), mSize(0) {}
~nsQuoteList() { Clear(); }
void Clear();
nsQuoteListNode* Next(nsQuoteListNode* aNode) {
return NS_STATIC_CAST(nsQuoteListNode*, PR_NEXT_LINK(aNode));
}
nsQuoteListNode* Prev(nsQuoteListNode* aNode) {
return NS_STATIC_CAST(nsQuoteListNode*, PR_PREV_LINK(aNode));
}
void Insert(nsQuoteListNode* aNode);
// returns whether any nodes have been destroyed
PRBool DestroyNodesFor(nsIFrame* aFrame); //destroy all nodes with aFrame as parent
void Remove(nsQuoteListNode* aNode) { PR_REMOVE_LINK(aNode); mSize--; }
void RecalcAll();
PRBool IsLast(nsQuoteListNode* aNode) { return (Next(aNode) == mFirstNode); }
#ifdef DEBUG
void PrintChain();
#endif
};
#endif /* nsQuoteList_h___ */

View File

@ -910,6 +910,17 @@ struct nsStyleQuotes : public nsStyleStruct {
PRUint32 QuotesCount(void) const { return mQuotesCount; } // [inherited]
const nsString* OpenQuoteAt(PRUint32 aIndex) const
{
NS_ASSERTION(aIndex < mQuotesCount, "out of range");
return mQuotes + (aIndex * 2);
}
const nsString* CloseQuoteAt(PRUint32 aIndex) const
{
NS_ASSERTION(aIndex < mQuotesCount, "out of range");
return mQuotes + (aIndex * 2 + 1);
}
nsresult GetQuotesAt(PRUint32 aIndex, nsString& aOpen, nsString& aClose) const {
if (aIndex < mQuotesCount) {
aIndex *= 2;