diff --git a/content/html/document/src/nsHTMLContentSink.cpp b/content/html/document/src/nsHTMLContentSink.cpp
index 5cc161541399..e17c954826e0 100644
--- a/content/html/document/src/nsHTMLContentSink.cpp
+++ b/content/html/document/src/nsHTMLContentSink.cpp
@@ -114,11 +114,11 @@ static PRLogModuleInfo* gSinkLogModuleInfo;
} \
PR_END_MACRO
-#define SINK_TRACE_NODE(_bit,_msg,_node,_obj) _obj->SinkTraceNode(_bit,_msg,_node,this)
+#define SINK_TRACE_NODE(_bit,_msg,_node,_sp,_obj) _obj->SinkTraceNode(_bit,_msg,_node,_sp,this)
#else
#define SINK_TRACE(_bit,_args)
-#define SINK_TRACE_NODE(_bit,_msg,_node,_obj)
+#define SINK_TRACE_NODE(_bit,_msg,_node,_sp,_obj)
#endif
//----------------------------------------------------------------------
@@ -172,6 +172,7 @@ public:
NS_IMETHOD DoFragment(PRBool aFlag);
+ PRBool IsInScript();
void ReduceEntities(nsString& aString);
void GetAttributeValueAt(const nsIParserNode& aNode,
PRInt32 aIndex,
@@ -190,6 +191,7 @@ public:
void SinkTraceNode(PRUint32 aBit,
const char* aMsg,
const nsIParserNode& aNode,
+ PRInt32 aStackPos,
void* aThis);
#endif
@@ -202,20 +204,15 @@ public:
nsIHTMLContent* mRoot;
nsIHTMLContent* mBody;
- PRInt32 mBodyChildCount;
nsIHTMLContent* mFrameset;
nsIHTMLContent* mHead;
nsString* mTitle;
- PRInt32 mInMonolithicContainer;
- PRBool mDirty;
PRBool mLayoutStarted;
+ PRInt32 mInScript;
nsIDOMHTMLFormElement* mCurrentForm;
nsIHTMLContent* mCurrentMap;
- nsIDOMHTMLMapElement* mCurrentDOMMap;
- SinkContext** mContexts;
- PRInt32 mNumContexts;
nsVoidArray mContextStack;
SinkContext* mCurrentContext;
SinkContext* mHeadContext;
@@ -268,24 +265,14 @@ public:
nsresult EvaluateScript(nsString& aScript,
PRInt32 aLineNo);
- void NotifyBody() {
- PRInt32 currentCount;
- mBody->ChildCount(currentCount);
- if (mBodyChildCount < currentCount) {
- RAPTOR_STOPWATCH_DEBUGTRACE(("Save and stop: nsHTMLContentSink::Notify()\n"));
- NS_SAVE_STOPWATCH_STATE(mWatch)
- NS_STOP_STOPWATCH(mWatch)
- mDocument->ContentAppended(mBody, mBodyChildCount);
- RAPTOR_STOPWATCH_DEBUGTRACE(("Restore: nsHTMLContentSink::Notify()\n"));
- NS_RESTORE_STOPWATCH_STATE(mWatch)
- }
- mBodyChildCount = currentCount;
- }
-#ifdef DEBUG
- void ForceReflow() {
- NotifyBody();
- mDirty = PR_FALSE;
- }
+ void UpdateAllContexts();
+ void NotifyAppend(nsIContent* aContent,
+ PRInt32 aStartIndex);
+ void NotifyInsert(nsIContent* aContent,
+ nsIContent* aChildContent,
+ PRInt32 aIndexInContainer);
+#ifdef NS_DEBUG
+ void ForceReflow();
#endif
#ifdef MOZ_PERF_METRICS
@@ -306,35 +293,43 @@ public:
mPreAppend = aPreAppend;
}
- nsresult Begin(nsHTMLTag aNodeType, nsIHTMLContent* aRoot);
+ nsresult Begin(nsHTMLTag aNodeType, nsIHTMLContent* aRoot,
+ PRInt32 aNumFlushed, PRInt32 aInsertionPoint);
nsresult OpenContainer(const nsIParserNode& aNode);
nsresult CloseContainer(const nsIParserNode& aNode);
nsresult AddLeaf(const nsIParserNode& aNode);
nsresult AddLeaf(nsIHTMLContent* aContent);
nsresult AddComment(const nsIParserNode& aNode);
- nsresult DemoteContainer(const nsIParserNode& aNode,
- nsIHTMLContent*& aParent);
+ nsresult DemoteContainer(const nsIParserNode& aNode);
nsresult End();
nsresult GrowStack();
nsresult AddText(const nsString& aText);
- nsresult FlushText(PRBool* aDidFlush = nsnull);
+ nsresult FlushText(PRBool* aDidFlush = nsnull, PRBool aReleaseLast = PR_FALSE);
+ nsresult FlushTextAndRelease(PRBool* aDidFlush = nsnull)
+ {
+ return FlushText(aDidFlush, PR_TRUE);
+ }
nsresult FlushTags();
PRBool IsCurrentContainer(nsHTMLTag mType);
PRBool IsAncestorContainer(nsHTMLTag mType);
nsIHTMLContent* GetCurrentContainer();
- void MaybeMarkSinkDirty();
- void MaybeMarkSinkClean();
+ void DidAddContent(nsIContent* aContent);
+ void UpdateChildCounts();
HTMLContentSink* mSink;
PRBool mPreAppend;
+ PRInt32 mNotifyLevel;
+ nsIContent* mLastTextNode;
struct Node {
nsHTMLTag mType;
nsIHTMLContent* mContent;
PRUint32 mFlags;
+ PRInt32 mNumFlushed;
+ PRInt32 mInsertionPoint;
};
// Node.mFlags
@@ -357,6 +352,7 @@ void
HTMLContentSink::SinkTraceNode(PRUint32 aBit,
const char* aMsg,
const nsIParserNode& aNode,
+ PRInt32 aStackPos,
void* aThis)
{
if (SINK_LOG_TEST(gSinkLogModuleInfo,aBit)) {
@@ -374,7 +370,7 @@ HTMLContentSink::SinkTraceNode(PRUint32 aBit,
aNode.GetText().ToCString(cbuf, sizeof(cbuf));
cp = cbuf;
}
- PR_LogPrint("%s: this=%p node='%s'", aMsg, aThis, cp);
+ PR_LogPrint("%s: this=%p node='%s' stackPos=%d", aMsg, aThis, cp, aStackPos);
}
}
#endif
@@ -964,12 +960,14 @@ SinkContext::SinkContext(HTMLContentSink* aSink)
MOZ_COUNT_CTOR(SinkContext);
mSink = aSink;
mPreAppend = PR_FALSE;
+ mNotifyLevel = 0;
mStack = nsnull;
mStackSize = 0;
mStackPos = 0;
mText = nsnull;
mTextLength = 0;
mTextSize = 0;
+ mLastTextNode = nsnull;
}
SinkContext::~SinkContext()
@@ -984,10 +982,14 @@ SinkContext::~SinkContext()
if (nsnull != mText) {
delete [] mText;
}
+ NS_IF_RELEASE(mLastTextNode);
}
nsresult
-SinkContext::Begin(nsHTMLTag aNodeType, nsIHTMLContent* aRoot)
+SinkContext::Begin(nsHTMLTag aNodeType,
+ nsIHTMLContent* aRoot,
+ PRInt32 aNumFlushed,
+ PRInt32 aInsertionPoint)
{
if (1 > mStackSize) {
nsresult rv = GrowStack();
@@ -999,6 +1001,8 @@ SinkContext::Begin(nsHTMLTag aNodeType, nsIHTMLContent* aRoot)
mStack[0].mType = aNodeType;
mStack[0].mContent = aRoot;
mStack[0].mFlags = APPENDED;
+ mStack[0].mNumFlushed = aNumFlushed;
+ mStack[0].mInsertionPoint = aInsertionPoint;
NS_ADDREF(aRoot);
mStackPos = 1;
mTextLength = 0;
@@ -1041,33 +1045,52 @@ SinkContext::GetCurrentContainer()
}
void
-SinkContext::MaybeMarkSinkDirty()
+SinkContext::DidAddContent(nsIContent* aContent)
{
- if (!mSink->mDirty &&
- (2 == mStackPos) &&
+ if ((2 == mStackPos) &&
(mSink->mBody == mStack[1].mContent)) {
// We just finished adding something to the body
- mSink->mDirty = PR_TRUE;
+ mNotifyLevel = 0;
}
-}
-void
-SinkContext::MaybeMarkSinkClean()
-{
- // XXX For now just clear the dirty bit. In the future, we might
- // selectively clear it based on the current context.
- // Note that it will be marked dirty again when we're in a
- // safe state.
- mSink->mDirty = PR_FALSE;
+ // If we just added content to a node for which
+ // an insertion happen, we need to do an immediate
+ // notification for that insertion.
+ if ((0 < mStackPos) &&
+ (mStack[mStackPos-1].mInsertionPoint != -1)) {
+ nsIContent* parent = mStack[mStackPos-1].mContent;
+ PRInt32 childCount;
+
+#ifdef NS_DEBUG
+ // Tracing code
+ char cbuf[40];
+ const char* cp;
+ nsAutoString str;
+ nsCOMPtr dtd;
+ mSink->mParser->GetDTD(getter_AddRefs(dtd));
+ dtd->IntTagToStringTag(nsHTMLTag(mStack[mStackPos-1].mType), str);
+ cp = str.ToCString(cbuf, sizeof(cbuf));
+
+ SINK_TRACE(SINK_TRACE_REFLOW,
+ ("SinkContext::DidAddContent: Insertion notification for parent=%s at position=%d and stackPos=%d",
+ cp, mStack[mStackPos-1].mInsertionPoint-1, mStackPos-1));
+#endif
+
+ mSink->NotifyInsert(parent,
+ aContent,
+ mStack[mStackPos-1].mInsertionPoint-1);
+ parent->ChildCount(childCount);
+ mStack[mStackPos-1].mNumFlushed = childCount;
+ }
}
nsresult
SinkContext::OpenContainer(const nsIParserNode& aNode)
{
- FlushText();
+ FlushTextAndRelease();
SINK_TRACE_NODE(SINK_TRACE_CALLS,
- "SinkContext::OpenContainer", aNode, mSink);
+ "SinkContext::OpenContainer", aNode, mStackPos, mSink);
nsresult rv;
if (mStackPos + 1 > mStackSize) {
@@ -1092,6 +1115,8 @@ SinkContext::OpenContainer(const nsIParserNode& aNode)
mStack[mStackPos].mType = nodeType;
mStack[mStackPos].mContent = content;
mStack[mStackPos].mFlags = 0;
+ mStack[mStackPos].mNumFlushed = 0;
+ mStack[mStackPos].mInsertionPoint = -1;
content->SetDocument(mSink->mDocument, PR_FALSE);
nsIScriptContextOwner* sco = mSink->mDocument->GetScriptContextOwner();
@@ -1101,7 +1126,14 @@ SinkContext::OpenContainer(const nsIParserNode& aNode)
if (mPreAppend) {
NS_ASSERTION(mStackPos > 0, "container w/o parent");
nsIHTMLContent* parent = mStack[mStackPos-1].mContent;
- parent->AppendChildTo(content, PR_FALSE);
+ if (mStack[mStackPos-1].mInsertionPoint != -1) {
+ parent->InsertChildAt(content,
+ mStack[mStackPos-1].mInsertionPoint++,
+ mSink->IsInScript());
+ }
+ else {
+ parent->AppendChildTo(content, mSink->IsInScript());
+ }
mStack[mStackPos].mFlags |= APPENDED;
}
mStackPos++;
@@ -1115,7 +1147,6 @@ SinkContext::OpenContainer(const nsIParserNode& aNode)
mSink->ProcessATag(aNode, content);
break;
case eHTMLTag_table:
- mSink->mInMonolithicContainer++;
case eHTMLTag_layer:
case eHTMLTag_thead:
case eHTMLTag_tbody:
@@ -1143,10 +1174,13 @@ nsresult
SinkContext::CloseContainer(const nsIParserNode& aNode)
{
nsresult result = NS_OK;
- FlushText();
+ // Flush any collected text content. Release the last text
+ // node to indicate that no more should be added to it.
+ FlushTextAndRelease();
+
SINK_TRACE_NODE(SINK_TRACE_CALLS,
- "SinkContext::CloseContainer", aNode, mSink);
+ "SinkContext::CloseContainer", aNode, mStackPos-1, mSink);
--mStackPos;
nsHTMLTag nodeType = mStack[mStackPos].mType;
@@ -1157,15 +1191,54 @@ SinkContext::CloseContainer(const nsIParserNode& aNode)
if (0 == (mStack[mStackPos].mFlags & APPENDED)) {
NS_ASSERTION(mStackPos > 0, "container w/o parent");
nsIHTMLContent* parent = mStack[mStackPos-1].mContent;
- result = parent->AppendChildTo(content, PR_FALSE);
+ // If the parent has an insertion point, insert rather than
+ // append.
+ if (mStack[mStackPos-1].mInsertionPoint != -1) {
+ result = parent->InsertChildAt(content,
+ mStack[mStackPos-1].mInsertionPoint++,
+ mSink->IsInScript());
+ }
+ else {
+ result = parent->AppendChildTo(content, mSink->IsInScript());
+ }
}
+
+ // If we're in a state where we do append notifications as
+ // we go up the tree, and we're at the level where the next
+ // notification needs to be done, do the notification.
+ if (mNotifyLevel >= mStackPos) {
+ PRInt32 childCount;
+
+ // Check to see if new content has been added after our last
+ // notification
+ content->ChildCount(childCount);
+ if (mStack[mStackPos].mNumFlushed < childCount) {
+#ifdef NS_DEBUG
+ // Tracing code
+ char cbuf[40];
+ const char* cp;
+ nsAutoString str;
+ nsCOMPtr dtd;
+ mSink->mParser->GetDTD(getter_AddRefs(dtd));
+ dtd->IntTagToStringTag(nsHTMLTag(nodeType), str);
+ cp = str.ToCString(cbuf, sizeof(cbuf));
+
+ SINK_TRACE(SINK_TRACE_REFLOW,
+ ("SinkContext::CloseContainer: reflow on notifyImmediate tag=%s newIndex=%d stackPos=%d", cp, mStack[mStackPos].mNumFlushed, mStackPos));
+#endif
+ mSink->NotifyAppend(content, mStack[mStackPos].mNumFlushed);
+ }
+
+ // Indicate that notification has now happened at this level
+ mNotifyLevel = mStackPos-1;
+ }
+
+ DidAddContent(content);
+
NS_IF_RELEASE(content);
// Special handling for certain tags
switch (nodeType) {
- case eHTMLTag_table:
- mSink->mInMonolithicContainer--;
- break;
case eHTMLTag_form:
{
@@ -1174,8 +1247,8 @@ SinkContext::CloseContainer(const nsIParserNode& aNode)
// If there's a FORM on the stack, but this close tag doesn't
// close the form, then close out the form *and* close out the
// next container up. This is since the parser doesn't do fix up
- // of forms. When the end FORM tag comes through, we'll ignore
- // it.
+ // of invalid form nesting. When the end FORM tag comes through,
+ // we'll ignore it.
if (parserNodeType != nodeType) {
result = CloseContainer(aNode);
}
@@ -1190,11 +1263,8 @@ SinkContext::CloseContainer(const nsIParserNode& aNode)
break;
}
- // Mark sink dirty if it can safely reflow something
- MaybeMarkSinkDirty();
-
#ifdef DEBUG
- if (mPreAppend && mSink->mDirty &&
+ if (mPreAppend &&
SINK_LOG_TEST(gSinkLogModuleInfo,SINK_ALWAYS_REFLOW)) {
mSink->ForceReflow();
}
@@ -1219,14 +1289,17 @@ SetDocumentInChildrenOf(nsIContent* aContent,
}
}
+// This method is called when a container is determined to be
+// non well-formed in the source content. Currently this can only
+// happen for forms, since the parser doesn't do fixup of forms.
+// The method makes the container a leaf and moves all the container's
+// children up a level to the container's parent.
nsresult
-SinkContext::DemoteContainer(const nsIParserNode& aNode,
- nsIHTMLContent*& aParent)
+SinkContext::DemoteContainer(const nsIParserNode& aNode)
{
nsresult result = NS_OK;
nsHTMLTag nodeType = nsHTMLTag(aNode.GetNodeType());
-
- aParent = nsnull;
+
// Search for the nearest container on the stack of the
// specified type
PRInt32 stackPos = mStackPos-1;
@@ -1234,7 +1307,7 @@ SinkContext::DemoteContainer(const nsIParserNode& aNode,
stackPos--;
}
- // If we find such a container
+ // If we find such a container...
if (stackPos > 0) {
nsIHTMLContent* container = mStack[stackPos].mContent;
@@ -1242,70 +1315,71 @@ SinkContext::DemoteContainer(const nsIParserNode& aNode,
// cases for which this is called, but put in a check anyway
if (stackPos > 1) {
nsIHTMLContent* parent = mStack[stackPos-1].mContent;
-
- // If the demoted container hasn't yet been appended to its
- // parent, do so now.
- if (0 == (mStack[stackPos].mFlags & APPENDED)) {
- result = parent->AppendChildTo(container, PR_FALSE);
- }
-
- if (NS_SUCCEEDED(result)) {
- // Move all of the demoted containers children to its parent
- PRInt32 i, count;
- container->ChildCount(count);
-
- for (i = 0; i < count && NS_SUCCEEDED(result); i++) {
- nsIContent* child;
-
- // Since we're removing as we go along, always get the
- // first child
- result = container->ChildAt(0, child);
- if (NS_SUCCEEDED(result)) {
- // Remove it from its old parent (the demoted container)
- // If the child is a form control, cache the form that contains it.
- // After the form control is removed from it's container, restore
- // it's form.
+ // Flush all tags and do notifications - it's easier to deal
+ // with later notifications.
+ FlushTags();
+
+ if (NS_SUCCEEDED(result)) {
+ // Move all of the demoted containers children to its parent
+ PRInt32 i, count;
+ container->ChildCount(count);
+
+ for (i = 0; i < count && NS_SUCCEEDED(result); i++) {
+ nsIContent* child;
+
+ // Since we're removing as we go along, always get the
+ // first child
+ result = container->ChildAt(0, child);
+ if (NS_SUCCEEDED(result)) {
+ // Remove it from its old parent (the demoted container)
+
+ // If the child is a form control, cache the form that contains it.
+ // After the form control is removed from it's container, restore
+ // it's form.
nsIFormControl* childFormControl = nsnull;
result = child->QueryInterface(kIFormControlIID, (void**)&childFormControl);
if (NS_SUCCEEDED(result)) {
- // It is a form control, so get it's form and cache it.
+ // It is a form control, so get it's form and cache it.
nsIDOMHTMLFormElement* formElem = nsnull;
childFormControl->GetForm(&formElem);
- // Removing the child will set it's form control to nsnull.
- result = container->RemoveChildAt(0, PR_FALSE);
- // Restore the child's form control using the cache'd pointer.
+ // Removing the child will set it's form control to nsnull.
+ result = container->RemoveChildAt(0, PR_TRUE);
+ // Restore the child's form control using the cache'd pointer.
childFormControl->SetForm(formElem);
-
+
NS_RELEASE(childFormControl);
NS_IF_RELEASE(formElem);
} else {
- result = container->RemoveChildAt(0, PR_FALSE);
+ result = container->RemoveChildAt(0, PR_TRUE);
}
-
-
- if (NS_SUCCEEDED(result)) {
- result = parent->AppendChildTo(child, PR_FALSE);
- SetDocumentInChildrenOf(child, mSink->mDocument);
- }
- NS_RELEASE(child);
- }
- }
-
- // Remove the current element from the context stack,
- // since it's been demoted.
- while (stackPos < mStackPos-1) {
- mStack[stackPos].mType = mStack[stackPos+1].mType;
- mStack[stackPos].mContent = mStack[stackPos+1].mContent;
- mStack[stackPos].mFlags = mStack[stackPos+1].mFlags;
- stackPos++;
- }
- mStackPos--;
- }
- aParent = parent;
- NS_ADDREF(parent);
+
+ if (NS_SUCCEEDED(result)) {
+ SetDocumentInChildrenOf(child, mSink->mDocument);
+ // Note that we're doing synchronous notifications here
+ // since we already did notifications for all content
+ // that's come through with the FlushTags() call so far.
+ result = parent->AppendChildTo(child, PR_TRUE);
+ }
+ NS_RELEASE(child);
+ }
+ }
+
+ // Remove the demoted element from the context stack.
+ while (stackPos < mStackPos-1) {
+ mStack[stackPos].mType = mStack[stackPos+1].mType;
+ mStack[stackPos].mContent = mStack[stackPos+1].mContent;
+ mStack[stackPos].mFlags = mStack[stackPos+1].mFlags;
+ stackPos++;
+ }
+ mStackPos--;
+ }
}
NS_RELEASE(container);
+
+ // Update child counts for everything on the stack, since
+ // we've moved around content in the hierarchy
+ UpdateChildCounts();
}
return result;
@@ -1315,14 +1389,14 @@ nsresult
SinkContext::AddLeaf(const nsIParserNode& aNode)
{
SINK_TRACE_NODE(SINK_TRACE_CALLS,
- "SinkContext::AddLeaf", aNode, mSink);
+ "SinkContext::AddLeaf", aNode, mStackPos, mSink);
nsresult rv = NS_OK;
switch (aNode.GetTokenType()) {
case eToken_start:
{
- FlushText();
+ FlushTextAndRelease();
// Create new leaf content object
nsHTMLTag nodeType = nsHTMLTag(aNode.GetNodeType());
@@ -1396,13 +1470,21 @@ SinkContext::AddLeaf(nsIHTMLContent* aContent)
{
NS_ASSERTION(mStackPos > 0, "leaf w/o container");
nsIHTMLContent* parent = mStack[mStackPos-1].mContent;
- parent->AppendChildTo(aContent, PR_FALSE);
+ // If the parent has an insertion point, insert rather than
+ // append.
+ if (mStack[mStackPos-1].mInsertionPoint != -1) {
+ parent->InsertChildAt(aContent,
+ mStack[mStackPos-1].mInsertionPoint++,
+ mSink->IsInScript());
+ }
+ else {
+ parent->AppendChildTo(aContent, mSink->IsInScript());
+ }
- // Mark sink dirty if it can safely reflow something
- MaybeMarkSinkDirty();
+ DidAddContent(aContent);
#ifdef DEBUG
- if (mPreAppend && mSink->mDirty &&
+ if (mPreAppend &&
SINK_LOG_TEST(gSinkLogModuleInfo,SINK_ALWAYS_REFLOW)) {
mSink->ForceReflow();
}
@@ -1414,12 +1496,15 @@ SinkContext::AddLeaf(nsIHTMLContent* aContent)
nsresult
SinkContext::AddComment(const nsIParserNode& aNode)
{
+ SINK_TRACE_NODE(SINK_TRACE_CALLS,
+ "SinkContext::AddLeaf", aNode, mStackPos, mSink);
+
nsIContent *comment;
nsIDOMComment *domComment;
nsresult result = NS_OK;
- FlushText();
-
+ FlushTextAndRelease();
+
result = NS_NewCommentNode(&comment);
if (NS_OK == result) {
result = comment->QueryInterface(kIDOMCommentIID,
@@ -1437,13 +1522,21 @@ SinkContext::AddComment(const nsIParserNode& aNode)
else {
parent = mStack[mStackPos - 1].mContent;
}
- parent->AppendChildTo(comment, PR_FALSE);
+ // If the parent has an insertion point, insert rather than
+ // append.
+ if (mStack[mStackPos-1].mInsertionPoint != -1) {
+ parent->InsertChildAt(comment,
+ mStack[mStackPos-1].mInsertionPoint++,
+ mSink->IsInScript());
+ }
+ else {
+ parent->AppendChildTo(comment, mSink->IsInScript());
+ }
- // Mark sink dirty if it can safely reflow something
- MaybeMarkSinkDirty();
+ DidAddContent(comment);
#ifdef DEBUG
- if (mPreAppend && mSink->mDirty &&
+ if (mPreAppend &&
SINK_LOG_TEST(gSinkLogModuleInfo,SINK_ALWAYS_REFLOW)) {
mSink->ForceReflow();
}
@@ -1522,6 +1615,7 @@ SinkContext::AddText(const nsString& aText)
amount = addLen;
}
if (0 == amount) {
+ // Don't release last text node so we can add to it again
nsresult rv = FlushText();
if (NS_OK != rv) {
return rv;
@@ -1541,29 +1635,109 @@ SinkContext::AddText(const nsString& aText)
* Flush all elements that have been seen so far such that
* they are visible in the tree. Specifically, make sure
* that they are all added to their respective parents.
+ * Also, do notification at the top for all content that
+ * has been newly added so that the frame tree is complete.
*/
nsresult
SinkContext::FlushTags()
{
- FlushText();
- // Prevent reflows if we're in a situation where we have
- // incomplete content.
- MaybeMarkSinkClean();
+ nsresult result = NS_OK;
+ // Don't release last text node in case we need to add to it again
+ FlushText();
+
+ PRInt32 childCount;
+ nsIHTMLContent* content;
+
+ // Start from the top of the stack (growing upwards) and append
+ // all content that hasn't been previously appended to the tree
PRInt32 stackPos = mStackPos-1;
while ((stackPos > 0) && (0 == (mStack[stackPos].mFlags & APPENDED))) {
- nsIHTMLContent* content = mStack[stackPos].mContent;
+ content = mStack[stackPos].mContent;
nsIHTMLContent* parent = mStack[stackPos-1].mContent;
- parent->AppendChildTo(content, PR_FALSE);
+ // If the parent has an insertion point, insert rather than
+ // append.
+ if (mStack[mStackPos-1].mInsertionPoint != -1) {
+ parent->InsertChildAt(content,
+ mStack[mStackPos-1].mInsertionPoint++,
+ PR_FALSE);
+ }
+ else {
+ parent->AppendChildTo(content, PR_FALSE);
+ }
mStack[stackPos].mFlags |= APPENDED;
+
if (eHTMLTag_iframe == mStack[mStackPos].mType) {
mSink->mNumOpenIFRAMES--;
}
stackPos--;
}
- return NS_OK;
+ // Start from the base of the stack (growing upward) and do
+ // a notification from the node that is closest to the root of
+ // tree for any content that has been added.
+ stackPos = 1;
+ PRBool flushed = PR_FALSE;
+ while (stackPos < mStackPos) {
+ content = mStack[stackPos].mContent;
+ content->ChildCount(childCount);
+
+ if (!flushed && (mStack[stackPos].mNumFlushed < childCount)) {
+#ifdef NS_DEBUG
+ // Tracing code
+ char cbuf[40];
+ const char* cp;
+ nsAutoString str;
+ nsCOMPtr dtd;
+ mSink->mParser->GetDTD(getter_AddRefs(dtd));
+ dtd->IntTagToStringTag(nsHTMLTag(mStack[stackPos].mType), str);
+ cp = str.ToCString(cbuf, sizeof(cbuf));
+
+ SINK_TRACE(SINK_TRACE_REFLOW,
+ ("SinkContext::FlushTags: tag=%s from newindex=%d at stackPos=%d",
+ cp, mStack[stackPos].mNumFlushed, stackPos));
+#endif
+ if ((mStack[stackPos].mInsertionPoint != -1) &&
+ (mStackPos > (stackPos+1))) {
+ nsIContent* child = mStack[stackPos+1].mContent;
+ mSink->NotifyInsert(content,
+ child,
+ mStack[stackPos].mInsertionPoint);
+ }
+ else {
+ mSink->NotifyAppend(content, mStack[stackPos].mNumFlushed);
+ }
+ flushed = PR_TRUE;
+ }
+
+ mStack[stackPos].mNumFlushed = childCount;
+ stackPos++;
+ }
+ mNotifyLevel = mStackPos-1;
+
+ return result;
+}
+
+void
+SinkContext::UpdateChildCounts()
+{
+ PRInt32 childCount;
+ nsIHTMLContent* content;
+
+ // Start from the top of the stack (growing upwards) and see if
+ // any new content has been appended. If so, we recognize that
+ // reflows have been generated for it and we should make sure that
+ // no further reflows occur.
+ PRInt32 stackPos = mStackPos-1;
+ while (stackPos > 0) {
+ if (mStack[stackPos].mFlags & APPENDED) {
+ content = mStack[stackPos].mContent;
+ content->ChildCount(childCount);
+ mStack[stackPos].mNumFlushed = childCount;
+ }
+ stackPos--;
+ }
}
/**
@@ -1571,31 +1745,50 @@ SinkContext::FlushTags()
* adding it to the content.
*/
nsresult
-SinkContext::FlushText(PRBool* aDidFlush)
+SinkContext::FlushText(PRBool* aDidFlush, PRBool aReleaseLast)
{
nsresult rv = NS_OK;
PRBool didFlush = PR_FALSE;
if (0 != mTextLength) {
- nsIContent* content;
- rv = NS_NewTextNode(&content);
- if (NS_OK == rv) {
- // Set the content's document
- content->SetDocument(mSink->mDocument, PR_FALSE);
+ if (mLastTextNode) {
+ nsCOMPtr cdata = do_QueryInterface(mLastTextNode, &rv);
+ if (NS_SUCCEEDED(rv)) {
+ CBufDescriptor bd(mText, PR_TRUE, mTextSize+1, mTextLength);
+ bd.mIsConst = PR_TRUE;
+ nsAutoString str(bd);
- // Set the text in the text node
- nsITextContent* text = nsnull;
- content->QueryInterface(kITextContentIID, (void**) &text);
- text->SetText(mText, mTextLength, PR_FALSE);
- NS_RELEASE(text);
+ rv = cdata->AppendData(str);
+ }
+ }
+ else {
+ nsIContent* content;
+ rv = NS_NewTextNode(&content);
+ if (NS_OK == rv) {
+ // Set the content's document
+ content->SetDocument(mSink->mDocument, PR_FALSE);
+
+ // Set the text in the text node
+ nsITextContent* text = nsnull;
+ content->QueryInterface(kITextContentIID, (void**) &text);
+ text->SetText(mText, mTextLength, PR_FALSE);
+ NS_RELEASE(text);
- // Add text to its parent
- NS_ASSERTION(mStackPos > 0, "leaf w/o container");
- nsIHTMLContent* parent = mStack[mStackPos - 1].mContent;
- parent->AppendChildTo(content, PR_FALSE);
- NS_RELEASE(content);
+ // Add text to its parent
+ NS_ASSERTION(mStackPos > 0, "leaf w/o container");
+ nsIHTMLContent* parent = mStack[mStackPos - 1].mContent;
+ if (mStack[mStackPos-1].mInsertionPoint != -1) {
+ parent->InsertChildAt(content,
+ mStack[mStackPos-1].mInsertionPoint++,
+ mSink->IsInScript());
+ }
+ else {
+ parent->AppendChildTo(content, mSink->IsInScript());
+ }
- // Mark sink dirty if it can safely reflow something
- MaybeMarkSinkDirty();
+ mLastTextNode = content;
+
+ DidAddContent(content);
+ }
}
mTextLength = 0;
didFlush = PR_TRUE;
@@ -1604,8 +1797,11 @@ SinkContext::FlushText(PRBool* aDidFlush)
*aDidFlush = didFlush;
}
+ if (aReleaseLast && mLastTextNode) {
+ NS_RELEASE(mLastTextNode);
+ }
#ifdef DEBUG
- if (mPreAppend && mSink->mDirty && didFlush &&
+ if (mPreAppend && didFlush &&
SINK_LOG_TEST(gSinkLogModuleInfo,SINK_ALWAYS_REFLOW)) {
mSink->ForceReflow();
}
@@ -1647,6 +1843,7 @@ HTMLContentSink::HTMLContentSink()
#endif
mNotAtRef = PR_TRUE;
mContentIDCounter = NS_CONTENT_ID_COUNTER_BASE;
+ mInScript = 0;
}
HTMLContentSink::~HTMLContentSink()
@@ -1666,20 +1863,18 @@ HTMLContentSink::~HTMLContentSink()
NS_IF_RELEASE(mCurrentForm);
NS_IF_RELEASE(mCurrentMap);
- NS_IF_RELEASE(mCurrentDOMMap);
NS_IF_RELEASE(mRefContent);
- for (PRInt32 i = 0; i < mNumContexts; i++) {
- SinkContext* sc = mContexts[i];
+ PRInt32 numContexts = mContextStack.Count();
+ for (PRInt32 i = 0; i < numContexts; i++) {
+ SinkContext* sc = (SinkContext*)mContextStack.ElementAt(i);
sc->End();
- delete sc;
if (sc == mCurrentContext) {
mCurrentContext = nsnull;
}
+ delete sc;
}
- if (nsnull != mContexts) {
- delete [] mContexts;
- }
+
if (mCurrentContext == mHeadContext) {
mCurrentContext = nsnull;
}
@@ -1763,7 +1958,7 @@ HTMLContentSink::Init(nsIDocument* aDoc,
mRoot->AppendChildTo(mHead, PR_FALSE);
mCurrentContext = new SinkContext(this);
- mCurrentContext->Begin(eHTMLTag_html, mRoot);
+ mCurrentContext->Begin(eHTMLTag_html, mRoot, 0, -1);
mContextStack.AppendElement(mCurrentContext);
char* spec;
@@ -1820,7 +2015,7 @@ HTMLContentSink::DidBuildModel(PRInt32 aQualityLevel)
if (nsnull != mBody) {
SINK_TRACE(SINK_TRACE_REFLOW,
("HTMLContentSink::DidBuildModel: layout final content"));
- NotifyBody();
+ mCurrentContext->FlushTags();
}
ScrollToRef();
@@ -1836,15 +2031,7 @@ HTMLContentSink::WillInterrupt()
{
SINK_TRACE(SINK_TRACE_CALLS,
("HTMLContentSink::WillInterrupt: this=%p", this));
- if (mDirty) {
- if (nsnull != mBody) {
- SINK_TRACE(SINK_TRACE_REFLOW,
- ("HTMLContentSink::WillInterrupt: reflow content"));
- NotifyBody();
- }
- mDirty = PR_FALSE;
- }
- return NS_OK;
+ return mCurrentContext->FlushTags();
}
NS_IMETHODIMP
@@ -1882,9 +2069,24 @@ HTMLContentSink::BeginContext(PRInt32 aPosition)
NS_ASSERTION(mCurrentContext != nsnull," Non-existing context");
+ // Flush everything in the current context so that we don't have
+ // to worry about insertions resulting in inconsistent frame creation.
+ mCurrentContext->FlushTags();
+
+ PRInt32 insertionPoint = -1;
nsHTMLTag nodeType = mCurrentContext->mStack[aPosition].mType;
nsIHTMLContent* content = mCurrentContext->mStack[aPosition].mContent;
- sc->Begin(nodeType,content);
+ // If the content under which the new context is created
+ // has a child on the stack, the insertion point is
+ // before the last child.
+ if (aPosition < (mCurrentContext->mStackPos-1)) {
+ content->ChildCount(insertionPoint);
+ insertionPoint--;
+ }
+ sc->Begin(nodeType,
+ content,
+ mCurrentContext->mStack[aPosition].mNumFlushed,
+ insertionPoint);
NS_ADDREF(sc->mSink);
mContextStack.AppendElement(mCurrentContext);
@@ -1906,7 +2108,10 @@ HTMLContentSink::EndContext(PRInt32 aPosition)
NS_ASSERTION(sc->mStack[aPosition].mType == mCurrentContext->mStack[0].mType,"ending a wrong context");
- mCurrentContext->FlushText();
+ mCurrentContext->FlushTextAndRelease();
+
+ sc->mStack[aPosition].mNumFlushed = mCurrentContext->mStack[0].mNumFlushed;
+
for(PRInt32 i=0; imStackPos; i++)
NS_IF_RELEASE(mCurrentContext->mStack[i].mContent);
delete [] mCurrentContext->mStack;
@@ -1983,7 +2188,7 @@ HTMLContentSink::OpenHTML(const nsIParserNode& aNode)
NS_START_STOPWATCH(mWatch)
SINK_TRACE_NODE(SINK_TRACE_CALLS,
- "HTMLContentSink::OpenHTML", aNode, this);
+ "HTMLContentSink::OpenHTML", aNode, 0, this);
NS_STOP_STOPWATCH(mWatch)
return NS_OK;
@@ -1995,7 +2200,7 @@ HTMLContentSink::CloseHTML(const nsIParserNode& aNode)
RAPTOR_STOPWATCH_DEBUGTRACE(("Start: nsHTMLContentSink::CloseHTML()\n"));
NS_START_STOPWATCH(mWatch)
SINK_TRACE_NODE(SINK_TRACE_CALLS,
- "HTMLContentSink::CloseHTML", aNode, this);
+ "HTMLContentSink::CloseHTML", aNode, 0, this);
if (nsnull != mHeadContext) {
mHeadContext->End();
delete mHeadContext;
@@ -2012,7 +2217,7 @@ HTMLContentSink::OpenHead(const nsIParserNode& aNode)
RAPTOR_STOPWATCH_DEBUGTRACE(("Start: nsHTMLContentSink::OpenHead()\n"));
NS_START_STOPWATCH(mWatch)
SINK_TRACE_NODE(SINK_TRACE_CALLS,
- "HTMLContentSink::OpenHead", aNode, this);
+ "HTMLContentSink::OpenHead", aNode, 0, this);
nsresult rv = NS_OK;
if (nsnull == mHeadContext) {
mHeadContext = new SinkContext(this);
@@ -2022,7 +2227,7 @@ HTMLContentSink::OpenHead(const nsIParserNode& aNode)
return NS_ERROR_OUT_OF_MEMORY;
}
mHeadContext->SetPreAppend(PR_TRUE);
- rv = mHeadContext->Begin(eHTMLTag_head, mHead);
+ rv = mHeadContext->Begin(eHTMLTag_head, mHead, 0, -1);
if (NS_OK != rv) {
RAPTOR_STOPWATCH_DEBUGTRACE(("Stop: nsHTMLContentSink::OpenHead()\n"));
NS_STOP_STOPWATCH(mWatch)
@@ -2049,7 +2254,8 @@ HTMLContentSink::CloseHead(const nsIParserNode& aNode)
RAPTOR_STOPWATCH_DEBUGTRACE(("Start: nsHTMLContentSink::CloseHead()\n"));
NS_START_STOPWATCH(mWatch)
SINK_TRACE_NODE(SINK_TRACE_CALLS,
- "HTMLContentSink::CloseHead", aNode, this);
+ "HTMLContentSink::CloseHead", aNode,
+ 0, this);
PRInt32 n = mContextStack.Count() - 1;
mCurrentContext = (SinkContext*) mContextStack.ElementAt(n);
mContextStack.RemoveElementAt(n);
@@ -2066,7 +2272,8 @@ HTMLContentSink::OpenBody(const nsIParserNode& aNode)
//NS_PRECONDITION(nsnull == mBody, "parser called OpenBody twice");
SINK_TRACE_NODE(SINK_TRACE_CALLS,
- "HTMLContentSink::OpenBody", aNode, this);
+ "HTMLContentSink::OpenBody", aNode,
+ mCurrentContext->mStackPos, this);
// Add attributes, if any, to the current BODY node
if(mBody != nsnull){
nsIScriptContextOwner* sco = mDocument->GetScriptContextOwner();
@@ -2088,7 +2295,6 @@ HTMLContentSink::OpenBody(const nsIParserNode& aNode)
return rv;
}
mBody = mCurrentContext->mStack[mCurrentContext->mStackPos - 1].mContent;
- mBodyChildCount = 0;
NS_ADDREF(mBody);
RAPTOR_STOPWATCH_DEBUGTRACE(("Stop: nsHTMLContentSink::OpenBody()\n"));
@@ -2103,10 +2309,11 @@ HTMLContentSink::CloseBody(const nsIParserNode& aNode)
RAPTOR_STOPWATCH_DEBUGTRACE(("Start: nsHTMLContentSink::CloseBody()\n"));
NS_START_STOPWATCH(mWatch)
SINK_TRACE_NODE(SINK_TRACE_CALLS,
- "HTMLContentSink::CloseBody", aNode, this);
+ "HTMLContentSink::CloseBody", aNode,
+ mCurrentContext->mStackPos-1, this);
PRBool didFlush;
- nsresult rv = mCurrentContext->FlushText(&didFlush);
+ nsresult rv = mCurrentContext->FlushTextAndRelease(&didFlush);
if (NS_OK != rv) {
RAPTOR_STOPWATCH_DEBUGTRACE(("Stop: nsHTMLContentSink::CloseBody()\n"));
NS_STOP_STOPWATCH(mWatch)
@@ -2115,8 +2322,10 @@ HTMLContentSink::CloseBody(const nsIParserNode& aNode)
mCurrentContext->CloseContainer(aNode);
if (didFlush) {
+ SINK_TRACE(SINK_TRACE_REFLOW,
+ ("HTMLContentSink::CloseBody: layout final body text"));
// Trigger a reflow for the flushed text
- NotifyBody();
+ mCurrentContext->FlushTags();
}
RAPTOR_STOPWATCH_DEBUGTRACE(("Stop: nsHTMLContentSink::CloseBody()\n"));
@@ -2132,18 +2341,18 @@ HTMLContentSink::OpenForm(const nsIParserNode& aNode)
nsresult result = NS_OK;
nsIHTMLContent* content = nsnull;
- mCurrentContext->FlushText();
+ mCurrentContext->FlushTextAndRelease();
SINK_TRACE_NODE(SINK_TRACE_CALLS,
- "HTMLContentSink::OpenForm", aNode, this);
+ "HTMLContentSink::OpenForm", aNode,
+ mCurrentContext->mStackPos, this);
// Close out previous form if it's there. If there is one
// around, it's probably because the last one wasn't well-formed.
NS_IF_RELEASE(mCurrentForm);
// Check if the parent is a table, tbody, thead, tfoot, tr, col or
- // colgroup. If so, this is just going to be a leaf element.
- // If so, we fix up by making the form leaf content.
+ // colgroup. If so, we fix up by making the form leaf content.
if (mCurrentContext->IsCurrentContainer(eHTMLTag_table) ||
mCurrentContext->IsCurrentContainer(eHTMLTag_tbody) ||
mCurrentContext->IsCurrentContainer(eHTMLTag_thead) ||
@@ -2195,9 +2404,11 @@ HTMLContentSink::CloseForm(const nsIParserNode& aNode)
NS_START_STOPWATCH(mWatch)
nsresult result = NS_OK;
- mCurrentContext->FlushText();
+ mCurrentContext->FlushTextAndRelease();
+
SINK_TRACE_NODE(SINK_TRACE_CALLS,
- "HTMLContentSink::CloseForm", aNode, this);
+ "HTMLContentSink::CloseForm", aNode,
+ mCurrentContext->mStackPos-1, this);
if (nsnull != mCurrentForm) {
// Check if this is a well-formed form
@@ -2205,14 +2416,7 @@ HTMLContentSink::CloseForm(const nsIParserNode& aNode)
result = mCurrentContext->CloseContainer(aNode);
}
else if (mCurrentContext->IsAncestorContainer(eHTMLTag_form)) {
- nsIHTMLContent* parent;
- result = mCurrentContext->DemoteContainer(aNode, parent);
- if (NS_SUCCEEDED(result)) {
- if (parent == mBody) {
- NotifyBody();
- }
- NS_IF_RELEASE(parent);
- }
+ result = mCurrentContext->DemoteContainer(aNode);
}
NS_RELEASE(mCurrentForm);
}
@@ -2228,14 +2432,14 @@ HTMLContentSink::OpenFrameset(const nsIParserNode& aNode)
RAPTOR_STOPWATCH_DEBUGTRACE(("Start: nsHTMLContentSink::OpenFrameset()\n"));
NS_START_STOPWATCH(mWatch)
SINK_TRACE_NODE(SINK_TRACE_CALLS,
- "HTMLContentSink::OpenFrameset", aNode, this);
+ "HTMLContentSink::OpenFrameset", aNode,
+ mCurrentContext->mStackPos, this);
nsresult rv = mCurrentContext->OpenContainer(aNode);
if ((NS_OK == rv) && (nsnull == mFrameset)) {
mFrameset = mCurrentContext->mStack[mCurrentContext->mStackPos-1].mContent;
NS_ADDREF(mFrameset);
}
- mInMonolithicContainer++;
RAPTOR_STOPWATCH_DEBUGTRACE(("Stop: nsHTMLContentSink::OpenFrameset()\n"));
NS_STOP_STOPWATCH(mWatch)
return rv;
@@ -2247,7 +2451,8 @@ HTMLContentSink::CloseFrameset(const nsIParserNode& aNode)
RAPTOR_STOPWATCH_DEBUGTRACE(("Start: nsHTMLContentSink::CloseFrameset()\n"));
NS_START_STOPWATCH(mWatch)
SINK_TRACE_NODE(SINK_TRACE_CALLS,
- "HTMLContentSink::CloseFrameset", aNode, this);
+ "HTMLContentSink::CloseFrameset", aNode,
+ mCurrentContext->mStackPos-1, this);
SinkContext* sc = mCurrentContext;
nsIHTMLContent* fs = sc->mStack[sc->mStackPos-1].mContent;
@@ -2268,7 +2473,8 @@ HTMLContentSink::OpenMap(const nsIParserNode& aNode)
NS_START_STOPWATCH(mWatch)
nsresult rv = NS_OK;
SINK_TRACE_NODE(SINK_TRACE_CALLS,
- "HTMLContentSink::OpenMap", aNode, this);
+ "HTMLContentSink::OpenMap", aNode,
+ mCurrentContext->mStackPos, this);
// We used to treat MAP elements specially (i.e. they were
// only parent elements for AREAs), but we don't anymore.
// HTML 4.0 says that MAP elements can have block content
@@ -2286,9 +2492,9 @@ HTMLContentSink::CloseMap(const nsIParserNode& aNode)
NS_START_STOPWATCH(mWatch)
nsresult rv = NS_OK;
SINK_TRACE_NODE(SINK_TRACE_CALLS,
- "HTMLContentSink::CloseMap", aNode, this);
+ "HTMLContentSink::CloseMap", aNode,
+ mCurrentContext->mStackPos-1, this);
NS_IF_RELEASE(mCurrentMap);
- NS_IF_RELEASE(mCurrentDOMMap);
rv = mCurrentContext->CloseContainer(aNode);
RAPTOR_STOPWATCH_DEBUGTRACE(("Stop: nsHTMLContentSink::CloseMap()\n"));
@@ -2346,27 +2552,27 @@ HTMLContentSink::AddLeaf(const nsIParserNode& aNode)
break;
case eHTMLTag_base:
- mCurrentContext->FlushText();
+ mCurrentContext->FlushTextAndRelease();
rv = ProcessBASETag(aNode);
break;
case eHTMLTag_link:
- mCurrentContext->FlushText();
+ mCurrentContext->FlushTextAndRelease();
rv = ProcessLINKTag(aNode);
break;
case eHTMLTag_meta:
- mCurrentContext->FlushText();
+ mCurrentContext->FlushTextAndRelease();
rv = ProcessMETATag(aNode);
break;
case eHTMLTag_style:
- mCurrentContext->FlushText();
+ mCurrentContext->FlushTextAndRelease();
rv = ProcessSTYLETag(aNode);
break;
case eHTMLTag_script:
- mCurrentContext->FlushText();
+ mCurrentContext->FlushTextAndRelease();
rv = ProcessSCRIPTTag(aNode);
break;
@@ -3042,7 +3248,6 @@ HTMLContentSink::ProcessMAPTag(const nsIParserNode& aNode,
nsresult rv;
NS_IF_RELEASE(mCurrentMap);
- NS_IF_RELEASE(mCurrentDOMMap);
nsIDOMHTMLMapElement* domMap;
rv = aContent->QueryInterface(kIDOMHTMLMapElementIID, (void**)&domMap);
@@ -3066,7 +3271,7 @@ HTMLContentSink::ProcessMAPTag(const nsIParserNode& aNode,
// The map adds itself
mCurrentMap = aContent;
NS_ADDREF(aContent);
- mCurrentDOMMap = domMap; // holds a reference
+ NS_IF_RELEASE(domMap);
return rv;
}
@@ -3228,6 +3433,51 @@ IsJavaScriptLanguage(const nsString& aName)
}
}
+#ifdef DEBUG
+void
+HTMLContentSink::ForceReflow()
+{
+ mCurrentContext->FlushTags();
+}
+#endif
+
+
+void
+HTMLContentSink::NotifyAppend(nsIContent* aContainer, PRInt32 aStartIndex)
+{
+ RAPTOR_STOPWATCH_DEBUGTRACE(("Save and stop: nsHTMLContentSink::NotifyAppend()\n"));
+ NS_SAVE_STOPWATCH_STATE(mWatch)
+ NS_STOP_STOPWATCH(mWatch)
+ mDocument->ContentAppended(aContainer, aStartIndex);
+ RAPTOR_STOPWATCH_DEBUGTRACE(("Restore: nsHTMLContentSink::NotifyAppend()\n"));
+ NS_RESTORE_STOPWATCH_STATE(mWatch)
+}
+
+void
+HTMLContentSink::NotifyInsert(nsIContent* aContent,
+ nsIContent* aChildContent,
+ PRInt32 aIndexInContainer)
+{
+ RAPTOR_STOPWATCH_DEBUGTRACE(("Save and stop: nsHTMLContentSink::NotifyInsert()\n"));
+ NS_SAVE_STOPWATCH_STATE(mWatch)
+ NS_STOP_STOPWATCH(mWatch)
+ mDocument->ContentInserted(aContent, aChildContent, aIndexInContainer);
+ RAPTOR_STOPWATCH_DEBUGTRACE(("Restore: nsHTMLContentSink::NotifyInsert()\n"));
+ NS_RESTORE_STOPWATCH_STATE(mWatch)
+}
+
+void
+HTMLContentSink::UpdateAllContexts()
+{
+ PRInt32 numContexts = mContextStack.Count();
+ for (PRInt32 i = 0; i < numContexts; i++) {
+ SinkContext* sc = (SinkContext*)mContextStack.ElementAt(i);
+
+ sc->UpdateChildCounts();
+ }
+ mCurrentContext->UpdateChildCounts();
+}
+
nsresult
HTMLContentSink::ResumeParsing()
{
@@ -3242,54 +3492,29 @@ HTMLContentSink::ResumeParsing()
PRBool
HTMLContentSink::PreEvaluateScript()
{
- // Cause frame creation and reflow of all body children that
- // have thus far been appended. Note that if mDirty is true
- // then we know that the current body child has not yet been
- // added to the content model (and, hence, it's safe to create
- // frames for all body children).
- // We don't want the current body child to be appended (and
- // have frames be constructed for it) since the current script
- // may add new content to the tree (and cause an immediate
- // reflow). As long as frames don't exist for the subtree rooted
- // by the current body child, new content added to the subtree
- // will not generate new frames and, hence, we won't have to
- // worry about reflowing of incomplete content or double frame
- // creation.
- if (mDirty) {
- if (nsnull != mBody) {
- SINK_TRACE(SINK_TRACE_REFLOW,
- ("HTMLContentSink::PreEvaluateScript: reflow content"));
- NotifyBody();
- }
- mDirty = PR_FALSE;
- }
-
- // Now eagerly all pending elements (including the current body child)
- // to the body (so that they can be seen by scripts). Note that frames
- // have not yet been created for them (and shouldn't be).
+ // Eagerly append all pending elements (including the current body child)
+ // to the body (so that they can be seen by scripts) and force reflow.
+ SINK_TRACE(SINK_TRACE_CALLS,
+ ("HTMLContentSink::PreEvaluateScript: flushing tags before evaluating script"));
mCurrentContext->FlushTags();
+
+ mInScript++;
- return (nsnull != mBody);
+ return PR_TRUE;
}
void
HTMLContentSink::PostEvaluateScript(PRBool aBodyPresent)
{
- // If the script added new content directly to the body, we update
- // our body child count so that frames aren't created twice.
- if (nsnull != mBody) {
- mBody->ChildCount(mBodyChildCount);
- // If the script is not a body child, we shouldn't include
- // the element that we eagerly appended (the ancestor of the
- // script), since it is not yet complete. Of course, this
- // should only happen if the body element was present *before*
- // script evaluation (a body could have been created by
- // the script).
- if (!mCurrentContext->IsCurrentContainer(eHTMLTag_body) &&
- aBodyPresent) {
- mBodyChildCount--;
- }
- }
+ mInScript--;
+
+ mCurrentContext->UpdateChildCounts();
+}
+
+PRBool
+HTMLContentSink::IsInScript()
+{
+ return (mInScript > 0);
}
nsresult
@@ -3419,7 +3644,14 @@ HTMLContentSink::ProcessSCRIPTTag(const nsIParserNode& aNode)
NS_RELEASE(element);
return rv;
}
- parent->AppendChildTo(element, PR_FALSE);
+ if (mCurrentContext->mStack[mCurrentContext->mStackPos-1].mInsertionPoint != -1) {
+ parent->InsertChildAt(element,
+ mCurrentContext->mStack[mCurrentContext->mStackPos-1].mInsertionPoint++,
+ IsInScript());
+ }
+ else {
+ parent->AppendChildTo(element, IsInScript());
+ }
}
else {
NS_IF_RELEASE(sco);
@@ -3442,7 +3674,7 @@ HTMLContentSink::ProcessSCRIPTTag(const nsIParserNode& aNode)
tc->SetData(script);
NS_RELEASE(tc);
}
- element->AppendChildTo(text, PR_FALSE);
+ element->AppendChildTo(text, IsInScript());
text->SetDocument(mDocument, PR_FALSE);
NS_RELEASE(text);
}
@@ -3492,7 +3724,7 @@ HTMLContentSink::ProcessSCRIPTTag(const nsIParserNode& aNode)
PRUint32 lineNo = (PRUint32)aNode.GetSourceLineNumber();
EvaluateScript(script, lineNo);
-
+
PostEvaluateScript(bodyPresent);
// If the parse was disabled as a result of this evaluate script
@@ -3599,7 +3831,7 @@ HTMLContentSink::ProcessSTYLETag(const nsIParserNode& aNode)
tc->SetData(content);
NS_RELEASE(tc);
}
- element->AppendChildTo(text, PR_FALSE);
+ element->AppendChildTo(text, IsInScript());
text->SetDocument(mDocument, PR_FALSE);
NS_RELEASE(text);
}
@@ -3618,6 +3850,15 @@ HTMLContentSink::ProcessSTYLETag(const nsIParserNode& aNode)
((blockParser) ? mParser : nsnull),
doneLoading);
NS_RELEASE(uin);
+
+ // If we're done loading the inline style, we know that frames
+ // have been created for all content seen so far (processing
+ // of a new style sheet causes recreation of the frame model).
+ // As a result, all contexts should update their notion of
+ // how much frame creation has happened.
+ if (doneLoading) {
+ UpdateAllContexts();
+ }
}
else {
// src with immediate style data doesn't add up
diff --git a/layout/html/document/src/nsHTMLContentSink.cpp b/layout/html/document/src/nsHTMLContentSink.cpp
index 5cc161541399..e17c954826e0 100644
--- a/layout/html/document/src/nsHTMLContentSink.cpp
+++ b/layout/html/document/src/nsHTMLContentSink.cpp
@@ -114,11 +114,11 @@ static PRLogModuleInfo* gSinkLogModuleInfo;
} \
PR_END_MACRO
-#define SINK_TRACE_NODE(_bit,_msg,_node,_obj) _obj->SinkTraceNode(_bit,_msg,_node,this)
+#define SINK_TRACE_NODE(_bit,_msg,_node,_sp,_obj) _obj->SinkTraceNode(_bit,_msg,_node,_sp,this)
#else
#define SINK_TRACE(_bit,_args)
-#define SINK_TRACE_NODE(_bit,_msg,_node,_obj)
+#define SINK_TRACE_NODE(_bit,_msg,_node,_sp,_obj)
#endif
//----------------------------------------------------------------------
@@ -172,6 +172,7 @@ public:
NS_IMETHOD DoFragment(PRBool aFlag);
+ PRBool IsInScript();
void ReduceEntities(nsString& aString);
void GetAttributeValueAt(const nsIParserNode& aNode,
PRInt32 aIndex,
@@ -190,6 +191,7 @@ public:
void SinkTraceNode(PRUint32 aBit,
const char* aMsg,
const nsIParserNode& aNode,
+ PRInt32 aStackPos,
void* aThis);
#endif
@@ -202,20 +204,15 @@ public:
nsIHTMLContent* mRoot;
nsIHTMLContent* mBody;
- PRInt32 mBodyChildCount;
nsIHTMLContent* mFrameset;
nsIHTMLContent* mHead;
nsString* mTitle;
- PRInt32 mInMonolithicContainer;
- PRBool mDirty;
PRBool mLayoutStarted;
+ PRInt32 mInScript;
nsIDOMHTMLFormElement* mCurrentForm;
nsIHTMLContent* mCurrentMap;
- nsIDOMHTMLMapElement* mCurrentDOMMap;
- SinkContext** mContexts;
- PRInt32 mNumContexts;
nsVoidArray mContextStack;
SinkContext* mCurrentContext;
SinkContext* mHeadContext;
@@ -268,24 +265,14 @@ public:
nsresult EvaluateScript(nsString& aScript,
PRInt32 aLineNo);
- void NotifyBody() {
- PRInt32 currentCount;
- mBody->ChildCount(currentCount);
- if (mBodyChildCount < currentCount) {
- RAPTOR_STOPWATCH_DEBUGTRACE(("Save and stop: nsHTMLContentSink::Notify()\n"));
- NS_SAVE_STOPWATCH_STATE(mWatch)
- NS_STOP_STOPWATCH(mWatch)
- mDocument->ContentAppended(mBody, mBodyChildCount);
- RAPTOR_STOPWATCH_DEBUGTRACE(("Restore: nsHTMLContentSink::Notify()\n"));
- NS_RESTORE_STOPWATCH_STATE(mWatch)
- }
- mBodyChildCount = currentCount;
- }
-#ifdef DEBUG
- void ForceReflow() {
- NotifyBody();
- mDirty = PR_FALSE;
- }
+ void UpdateAllContexts();
+ void NotifyAppend(nsIContent* aContent,
+ PRInt32 aStartIndex);
+ void NotifyInsert(nsIContent* aContent,
+ nsIContent* aChildContent,
+ PRInt32 aIndexInContainer);
+#ifdef NS_DEBUG
+ void ForceReflow();
#endif
#ifdef MOZ_PERF_METRICS
@@ -306,35 +293,43 @@ public:
mPreAppend = aPreAppend;
}
- nsresult Begin(nsHTMLTag aNodeType, nsIHTMLContent* aRoot);
+ nsresult Begin(nsHTMLTag aNodeType, nsIHTMLContent* aRoot,
+ PRInt32 aNumFlushed, PRInt32 aInsertionPoint);
nsresult OpenContainer(const nsIParserNode& aNode);
nsresult CloseContainer(const nsIParserNode& aNode);
nsresult AddLeaf(const nsIParserNode& aNode);
nsresult AddLeaf(nsIHTMLContent* aContent);
nsresult AddComment(const nsIParserNode& aNode);
- nsresult DemoteContainer(const nsIParserNode& aNode,
- nsIHTMLContent*& aParent);
+ nsresult DemoteContainer(const nsIParserNode& aNode);
nsresult End();
nsresult GrowStack();
nsresult AddText(const nsString& aText);
- nsresult FlushText(PRBool* aDidFlush = nsnull);
+ nsresult FlushText(PRBool* aDidFlush = nsnull, PRBool aReleaseLast = PR_FALSE);
+ nsresult FlushTextAndRelease(PRBool* aDidFlush = nsnull)
+ {
+ return FlushText(aDidFlush, PR_TRUE);
+ }
nsresult FlushTags();
PRBool IsCurrentContainer(nsHTMLTag mType);
PRBool IsAncestorContainer(nsHTMLTag mType);
nsIHTMLContent* GetCurrentContainer();
- void MaybeMarkSinkDirty();
- void MaybeMarkSinkClean();
+ void DidAddContent(nsIContent* aContent);
+ void UpdateChildCounts();
HTMLContentSink* mSink;
PRBool mPreAppend;
+ PRInt32 mNotifyLevel;
+ nsIContent* mLastTextNode;
struct Node {
nsHTMLTag mType;
nsIHTMLContent* mContent;
PRUint32 mFlags;
+ PRInt32 mNumFlushed;
+ PRInt32 mInsertionPoint;
};
// Node.mFlags
@@ -357,6 +352,7 @@ void
HTMLContentSink::SinkTraceNode(PRUint32 aBit,
const char* aMsg,
const nsIParserNode& aNode,
+ PRInt32 aStackPos,
void* aThis)
{
if (SINK_LOG_TEST(gSinkLogModuleInfo,aBit)) {
@@ -374,7 +370,7 @@ HTMLContentSink::SinkTraceNode(PRUint32 aBit,
aNode.GetText().ToCString(cbuf, sizeof(cbuf));
cp = cbuf;
}
- PR_LogPrint("%s: this=%p node='%s'", aMsg, aThis, cp);
+ PR_LogPrint("%s: this=%p node='%s' stackPos=%d", aMsg, aThis, cp, aStackPos);
}
}
#endif
@@ -964,12 +960,14 @@ SinkContext::SinkContext(HTMLContentSink* aSink)
MOZ_COUNT_CTOR(SinkContext);
mSink = aSink;
mPreAppend = PR_FALSE;
+ mNotifyLevel = 0;
mStack = nsnull;
mStackSize = 0;
mStackPos = 0;
mText = nsnull;
mTextLength = 0;
mTextSize = 0;
+ mLastTextNode = nsnull;
}
SinkContext::~SinkContext()
@@ -984,10 +982,14 @@ SinkContext::~SinkContext()
if (nsnull != mText) {
delete [] mText;
}
+ NS_IF_RELEASE(mLastTextNode);
}
nsresult
-SinkContext::Begin(nsHTMLTag aNodeType, nsIHTMLContent* aRoot)
+SinkContext::Begin(nsHTMLTag aNodeType,
+ nsIHTMLContent* aRoot,
+ PRInt32 aNumFlushed,
+ PRInt32 aInsertionPoint)
{
if (1 > mStackSize) {
nsresult rv = GrowStack();
@@ -999,6 +1001,8 @@ SinkContext::Begin(nsHTMLTag aNodeType, nsIHTMLContent* aRoot)
mStack[0].mType = aNodeType;
mStack[0].mContent = aRoot;
mStack[0].mFlags = APPENDED;
+ mStack[0].mNumFlushed = aNumFlushed;
+ mStack[0].mInsertionPoint = aInsertionPoint;
NS_ADDREF(aRoot);
mStackPos = 1;
mTextLength = 0;
@@ -1041,33 +1045,52 @@ SinkContext::GetCurrentContainer()
}
void
-SinkContext::MaybeMarkSinkDirty()
+SinkContext::DidAddContent(nsIContent* aContent)
{
- if (!mSink->mDirty &&
- (2 == mStackPos) &&
+ if ((2 == mStackPos) &&
(mSink->mBody == mStack[1].mContent)) {
// We just finished adding something to the body
- mSink->mDirty = PR_TRUE;
+ mNotifyLevel = 0;
}
-}
-void
-SinkContext::MaybeMarkSinkClean()
-{
- // XXX For now just clear the dirty bit. In the future, we might
- // selectively clear it based on the current context.
- // Note that it will be marked dirty again when we're in a
- // safe state.
- mSink->mDirty = PR_FALSE;
+ // If we just added content to a node for which
+ // an insertion happen, we need to do an immediate
+ // notification for that insertion.
+ if ((0 < mStackPos) &&
+ (mStack[mStackPos-1].mInsertionPoint != -1)) {
+ nsIContent* parent = mStack[mStackPos-1].mContent;
+ PRInt32 childCount;
+
+#ifdef NS_DEBUG
+ // Tracing code
+ char cbuf[40];
+ const char* cp;
+ nsAutoString str;
+ nsCOMPtr dtd;
+ mSink->mParser->GetDTD(getter_AddRefs(dtd));
+ dtd->IntTagToStringTag(nsHTMLTag(mStack[mStackPos-1].mType), str);
+ cp = str.ToCString(cbuf, sizeof(cbuf));
+
+ SINK_TRACE(SINK_TRACE_REFLOW,
+ ("SinkContext::DidAddContent: Insertion notification for parent=%s at position=%d and stackPos=%d",
+ cp, mStack[mStackPos-1].mInsertionPoint-1, mStackPos-1));
+#endif
+
+ mSink->NotifyInsert(parent,
+ aContent,
+ mStack[mStackPos-1].mInsertionPoint-1);
+ parent->ChildCount(childCount);
+ mStack[mStackPos-1].mNumFlushed = childCount;
+ }
}
nsresult
SinkContext::OpenContainer(const nsIParserNode& aNode)
{
- FlushText();
+ FlushTextAndRelease();
SINK_TRACE_NODE(SINK_TRACE_CALLS,
- "SinkContext::OpenContainer", aNode, mSink);
+ "SinkContext::OpenContainer", aNode, mStackPos, mSink);
nsresult rv;
if (mStackPos + 1 > mStackSize) {
@@ -1092,6 +1115,8 @@ SinkContext::OpenContainer(const nsIParserNode& aNode)
mStack[mStackPos].mType = nodeType;
mStack[mStackPos].mContent = content;
mStack[mStackPos].mFlags = 0;
+ mStack[mStackPos].mNumFlushed = 0;
+ mStack[mStackPos].mInsertionPoint = -1;
content->SetDocument(mSink->mDocument, PR_FALSE);
nsIScriptContextOwner* sco = mSink->mDocument->GetScriptContextOwner();
@@ -1101,7 +1126,14 @@ SinkContext::OpenContainer(const nsIParserNode& aNode)
if (mPreAppend) {
NS_ASSERTION(mStackPos > 0, "container w/o parent");
nsIHTMLContent* parent = mStack[mStackPos-1].mContent;
- parent->AppendChildTo(content, PR_FALSE);
+ if (mStack[mStackPos-1].mInsertionPoint != -1) {
+ parent->InsertChildAt(content,
+ mStack[mStackPos-1].mInsertionPoint++,
+ mSink->IsInScript());
+ }
+ else {
+ parent->AppendChildTo(content, mSink->IsInScript());
+ }
mStack[mStackPos].mFlags |= APPENDED;
}
mStackPos++;
@@ -1115,7 +1147,6 @@ SinkContext::OpenContainer(const nsIParserNode& aNode)
mSink->ProcessATag(aNode, content);
break;
case eHTMLTag_table:
- mSink->mInMonolithicContainer++;
case eHTMLTag_layer:
case eHTMLTag_thead:
case eHTMLTag_tbody:
@@ -1143,10 +1174,13 @@ nsresult
SinkContext::CloseContainer(const nsIParserNode& aNode)
{
nsresult result = NS_OK;
- FlushText();
+ // Flush any collected text content. Release the last text
+ // node to indicate that no more should be added to it.
+ FlushTextAndRelease();
+
SINK_TRACE_NODE(SINK_TRACE_CALLS,
- "SinkContext::CloseContainer", aNode, mSink);
+ "SinkContext::CloseContainer", aNode, mStackPos-1, mSink);
--mStackPos;
nsHTMLTag nodeType = mStack[mStackPos].mType;
@@ -1157,15 +1191,54 @@ SinkContext::CloseContainer(const nsIParserNode& aNode)
if (0 == (mStack[mStackPos].mFlags & APPENDED)) {
NS_ASSERTION(mStackPos > 0, "container w/o parent");
nsIHTMLContent* parent = mStack[mStackPos-1].mContent;
- result = parent->AppendChildTo(content, PR_FALSE);
+ // If the parent has an insertion point, insert rather than
+ // append.
+ if (mStack[mStackPos-1].mInsertionPoint != -1) {
+ result = parent->InsertChildAt(content,
+ mStack[mStackPos-1].mInsertionPoint++,
+ mSink->IsInScript());
+ }
+ else {
+ result = parent->AppendChildTo(content, mSink->IsInScript());
+ }
}
+
+ // If we're in a state where we do append notifications as
+ // we go up the tree, and we're at the level where the next
+ // notification needs to be done, do the notification.
+ if (mNotifyLevel >= mStackPos) {
+ PRInt32 childCount;
+
+ // Check to see if new content has been added after our last
+ // notification
+ content->ChildCount(childCount);
+ if (mStack[mStackPos].mNumFlushed < childCount) {
+#ifdef NS_DEBUG
+ // Tracing code
+ char cbuf[40];
+ const char* cp;
+ nsAutoString str;
+ nsCOMPtr dtd;
+ mSink->mParser->GetDTD(getter_AddRefs(dtd));
+ dtd->IntTagToStringTag(nsHTMLTag(nodeType), str);
+ cp = str.ToCString(cbuf, sizeof(cbuf));
+
+ SINK_TRACE(SINK_TRACE_REFLOW,
+ ("SinkContext::CloseContainer: reflow on notifyImmediate tag=%s newIndex=%d stackPos=%d", cp, mStack[mStackPos].mNumFlushed, mStackPos));
+#endif
+ mSink->NotifyAppend(content, mStack[mStackPos].mNumFlushed);
+ }
+
+ // Indicate that notification has now happened at this level
+ mNotifyLevel = mStackPos-1;
+ }
+
+ DidAddContent(content);
+
NS_IF_RELEASE(content);
// Special handling for certain tags
switch (nodeType) {
- case eHTMLTag_table:
- mSink->mInMonolithicContainer--;
- break;
case eHTMLTag_form:
{
@@ -1174,8 +1247,8 @@ SinkContext::CloseContainer(const nsIParserNode& aNode)
// If there's a FORM on the stack, but this close tag doesn't
// close the form, then close out the form *and* close out the
// next container up. This is since the parser doesn't do fix up
- // of forms. When the end FORM tag comes through, we'll ignore
- // it.
+ // of invalid form nesting. When the end FORM tag comes through,
+ // we'll ignore it.
if (parserNodeType != nodeType) {
result = CloseContainer(aNode);
}
@@ -1190,11 +1263,8 @@ SinkContext::CloseContainer(const nsIParserNode& aNode)
break;
}
- // Mark sink dirty if it can safely reflow something
- MaybeMarkSinkDirty();
-
#ifdef DEBUG
- if (mPreAppend && mSink->mDirty &&
+ if (mPreAppend &&
SINK_LOG_TEST(gSinkLogModuleInfo,SINK_ALWAYS_REFLOW)) {
mSink->ForceReflow();
}
@@ -1219,14 +1289,17 @@ SetDocumentInChildrenOf(nsIContent* aContent,
}
}
+// This method is called when a container is determined to be
+// non well-formed in the source content. Currently this can only
+// happen for forms, since the parser doesn't do fixup of forms.
+// The method makes the container a leaf and moves all the container's
+// children up a level to the container's parent.
nsresult
-SinkContext::DemoteContainer(const nsIParserNode& aNode,
- nsIHTMLContent*& aParent)
+SinkContext::DemoteContainer(const nsIParserNode& aNode)
{
nsresult result = NS_OK;
nsHTMLTag nodeType = nsHTMLTag(aNode.GetNodeType());
-
- aParent = nsnull;
+
// Search for the nearest container on the stack of the
// specified type
PRInt32 stackPos = mStackPos-1;
@@ -1234,7 +1307,7 @@ SinkContext::DemoteContainer(const nsIParserNode& aNode,
stackPos--;
}
- // If we find such a container
+ // If we find such a container...
if (stackPos > 0) {
nsIHTMLContent* container = mStack[stackPos].mContent;
@@ -1242,70 +1315,71 @@ SinkContext::DemoteContainer(const nsIParserNode& aNode,
// cases for which this is called, but put in a check anyway
if (stackPos > 1) {
nsIHTMLContent* parent = mStack[stackPos-1].mContent;
-
- // If the demoted container hasn't yet been appended to its
- // parent, do so now.
- if (0 == (mStack[stackPos].mFlags & APPENDED)) {
- result = parent->AppendChildTo(container, PR_FALSE);
- }
-
- if (NS_SUCCEEDED(result)) {
- // Move all of the demoted containers children to its parent
- PRInt32 i, count;
- container->ChildCount(count);
-
- for (i = 0; i < count && NS_SUCCEEDED(result); i++) {
- nsIContent* child;
-
- // Since we're removing as we go along, always get the
- // first child
- result = container->ChildAt(0, child);
- if (NS_SUCCEEDED(result)) {
- // Remove it from its old parent (the demoted container)
- // If the child is a form control, cache the form that contains it.
- // After the form control is removed from it's container, restore
- // it's form.
+ // Flush all tags and do notifications - it's easier to deal
+ // with later notifications.
+ FlushTags();
+
+ if (NS_SUCCEEDED(result)) {
+ // Move all of the demoted containers children to its parent
+ PRInt32 i, count;
+ container->ChildCount(count);
+
+ for (i = 0; i < count && NS_SUCCEEDED(result); i++) {
+ nsIContent* child;
+
+ // Since we're removing as we go along, always get the
+ // first child
+ result = container->ChildAt(0, child);
+ if (NS_SUCCEEDED(result)) {
+ // Remove it from its old parent (the demoted container)
+
+ // If the child is a form control, cache the form that contains it.
+ // After the form control is removed from it's container, restore
+ // it's form.
nsIFormControl* childFormControl = nsnull;
result = child->QueryInterface(kIFormControlIID, (void**)&childFormControl);
if (NS_SUCCEEDED(result)) {
- // It is a form control, so get it's form and cache it.
+ // It is a form control, so get it's form and cache it.
nsIDOMHTMLFormElement* formElem = nsnull;
childFormControl->GetForm(&formElem);
- // Removing the child will set it's form control to nsnull.
- result = container->RemoveChildAt(0, PR_FALSE);
- // Restore the child's form control using the cache'd pointer.
+ // Removing the child will set it's form control to nsnull.
+ result = container->RemoveChildAt(0, PR_TRUE);
+ // Restore the child's form control using the cache'd pointer.
childFormControl->SetForm(formElem);
-
+
NS_RELEASE(childFormControl);
NS_IF_RELEASE(formElem);
} else {
- result = container->RemoveChildAt(0, PR_FALSE);
+ result = container->RemoveChildAt(0, PR_TRUE);
}
-
-
- if (NS_SUCCEEDED(result)) {
- result = parent->AppendChildTo(child, PR_FALSE);
- SetDocumentInChildrenOf(child, mSink->mDocument);
- }
- NS_RELEASE(child);
- }
- }
-
- // Remove the current element from the context stack,
- // since it's been demoted.
- while (stackPos < mStackPos-1) {
- mStack[stackPos].mType = mStack[stackPos+1].mType;
- mStack[stackPos].mContent = mStack[stackPos+1].mContent;
- mStack[stackPos].mFlags = mStack[stackPos+1].mFlags;
- stackPos++;
- }
- mStackPos--;
- }
- aParent = parent;
- NS_ADDREF(parent);
+
+ if (NS_SUCCEEDED(result)) {
+ SetDocumentInChildrenOf(child, mSink->mDocument);
+ // Note that we're doing synchronous notifications here
+ // since we already did notifications for all content
+ // that's come through with the FlushTags() call so far.
+ result = parent->AppendChildTo(child, PR_TRUE);
+ }
+ NS_RELEASE(child);
+ }
+ }
+
+ // Remove the demoted element from the context stack.
+ while (stackPos < mStackPos-1) {
+ mStack[stackPos].mType = mStack[stackPos+1].mType;
+ mStack[stackPos].mContent = mStack[stackPos+1].mContent;
+ mStack[stackPos].mFlags = mStack[stackPos+1].mFlags;
+ stackPos++;
+ }
+ mStackPos--;
+ }
}
NS_RELEASE(container);
+
+ // Update child counts for everything on the stack, since
+ // we've moved around content in the hierarchy
+ UpdateChildCounts();
}
return result;
@@ -1315,14 +1389,14 @@ nsresult
SinkContext::AddLeaf(const nsIParserNode& aNode)
{
SINK_TRACE_NODE(SINK_TRACE_CALLS,
- "SinkContext::AddLeaf", aNode, mSink);
+ "SinkContext::AddLeaf", aNode, mStackPos, mSink);
nsresult rv = NS_OK;
switch (aNode.GetTokenType()) {
case eToken_start:
{
- FlushText();
+ FlushTextAndRelease();
// Create new leaf content object
nsHTMLTag nodeType = nsHTMLTag(aNode.GetNodeType());
@@ -1396,13 +1470,21 @@ SinkContext::AddLeaf(nsIHTMLContent* aContent)
{
NS_ASSERTION(mStackPos > 0, "leaf w/o container");
nsIHTMLContent* parent = mStack[mStackPos-1].mContent;
- parent->AppendChildTo(aContent, PR_FALSE);
+ // If the parent has an insertion point, insert rather than
+ // append.
+ if (mStack[mStackPos-1].mInsertionPoint != -1) {
+ parent->InsertChildAt(aContent,
+ mStack[mStackPos-1].mInsertionPoint++,
+ mSink->IsInScript());
+ }
+ else {
+ parent->AppendChildTo(aContent, mSink->IsInScript());
+ }
- // Mark sink dirty if it can safely reflow something
- MaybeMarkSinkDirty();
+ DidAddContent(aContent);
#ifdef DEBUG
- if (mPreAppend && mSink->mDirty &&
+ if (mPreAppend &&
SINK_LOG_TEST(gSinkLogModuleInfo,SINK_ALWAYS_REFLOW)) {
mSink->ForceReflow();
}
@@ -1414,12 +1496,15 @@ SinkContext::AddLeaf(nsIHTMLContent* aContent)
nsresult
SinkContext::AddComment(const nsIParserNode& aNode)
{
+ SINK_TRACE_NODE(SINK_TRACE_CALLS,
+ "SinkContext::AddLeaf", aNode, mStackPos, mSink);
+
nsIContent *comment;
nsIDOMComment *domComment;
nsresult result = NS_OK;
- FlushText();
-
+ FlushTextAndRelease();
+
result = NS_NewCommentNode(&comment);
if (NS_OK == result) {
result = comment->QueryInterface(kIDOMCommentIID,
@@ -1437,13 +1522,21 @@ SinkContext::AddComment(const nsIParserNode& aNode)
else {
parent = mStack[mStackPos - 1].mContent;
}
- parent->AppendChildTo(comment, PR_FALSE);
+ // If the parent has an insertion point, insert rather than
+ // append.
+ if (mStack[mStackPos-1].mInsertionPoint != -1) {
+ parent->InsertChildAt(comment,
+ mStack[mStackPos-1].mInsertionPoint++,
+ mSink->IsInScript());
+ }
+ else {
+ parent->AppendChildTo(comment, mSink->IsInScript());
+ }
- // Mark sink dirty if it can safely reflow something
- MaybeMarkSinkDirty();
+ DidAddContent(comment);
#ifdef DEBUG
- if (mPreAppend && mSink->mDirty &&
+ if (mPreAppend &&
SINK_LOG_TEST(gSinkLogModuleInfo,SINK_ALWAYS_REFLOW)) {
mSink->ForceReflow();
}
@@ -1522,6 +1615,7 @@ SinkContext::AddText(const nsString& aText)
amount = addLen;
}
if (0 == amount) {
+ // Don't release last text node so we can add to it again
nsresult rv = FlushText();
if (NS_OK != rv) {
return rv;
@@ -1541,29 +1635,109 @@ SinkContext::AddText(const nsString& aText)
* Flush all elements that have been seen so far such that
* they are visible in the tree. Specifically, make sure
* that they are all added to their respective parents.
+ * Also, do notification at the top for all content that
+ * has been newly added so that the frame tree is complete.
*/
nsresult
SinkContext::FlushTags()
{
- FlushText();
- // Prevent reflows if we're in a situation where we have
- // incomplete content.
- MaybeMarkSinkClean();
+ nsresult result = NS_OK;
+ // Don't release last text node in case we need to add to it again
+ FlushText();
+
+ PRInt32 childCount;
+ nsIHTMLContent* content;
+
+ // Start from the top of the stack (growing upwards) and append
+ // all content that hasn't been previously appended to the tree
PRInt32 stackPos = mStackPos-1;
while ((stackPos > 0) && (0 == (mStack[stackPos].mFlags & APPENDED))) {
- nsIHTMLContent* content = mStack[stackPos].mContent;
+ content = mStack[stackPos].mContent;
nsIHTMLContent* parent = mStack[stackPos-1].mContent;
- parent->AppendChildTo(content, PR_FALSE);
+ // If the parent has an insertion point, insert rather than
+ // append.
+ if (mStack[mStackPos-1].mInsertionPoint != -1) {
+ parent->InsertChildAt(content,
+ mStack[mStackPos-1].mInsertionPoint++,
+ PR_FALSE);
+ }
+ else {
+ parent->AppendChildTo(content, PR_FALSE);
+ }
mStack[stackPos].mFlags |= APPENDED;
+
if (eHTMLTag_iframe == mStack[mStackPos].mType) {
mSink->mNumOpenIFRAMES--;
}
stackPos--;
}
- return NS_OK;
+ // Start from the base of the stack (growing upward) and do
+ // a notification from the node that is closest to the root of
+ // tree for any content that has been added.
+ stackPos = 1;
+ PRBool flushed = PR_FALSE;
+ while (stackPos < mStackPos) {
+ content = mStack[stackPos].mContent;
+ content->ChildCount(childCount);
+
+ if (!flushed && (mStack[stackPos].mNumFlushed < childCount)) {
+#ifdef NS_DEBUG
+ // Tracing code
+ char cbuf[40];
+ const char* cp;
+ nsAutoString str;
+ nsCOMPtr dtd;
+ mSink->mParser->GetDTD(getter_AddRefs(dtd));
+ dtd->IntTagToStringTag(nsHTMLTag(mStack[stackPos].mType), str);
+ cp = str.ToCString(cbuf, sizeof(cbuf));
+
+ SINK_TRACE(SINK_TRACE_REFLOW,
+ ("SinkContext::FlushTags: tag=%s from newindex=%d at stackPos=%d",
+ cp, mStack[stackPos].mNumFlushed, stackPos));
+#endif
+ if ((mStack[stackPos].mInsertionPoint != -1) &&
+ (mStackPos > (stackPos+1))) {
+ nsIContent* child = mStack[stackPos+1].mContent;
+ mSink->NotifyInsert(content,
+ child,
+ mStack[stackPos].mInsertionPoint);
+ }
+ else {
+ mSink->NotifyAppend(content, mStack[stackPos].mNumFlushed);
+ }
+ flushed = PR_TRUE;
+ }
+
+ mStack[stackPos].mNumFlushed = childCount;
+ stackPos++;
+ }
+ mNotifyLevel = mStackPos-1;
+
+ return result;
+}
+
+void
+SinkContext::UpdateChildCounts()
+{
+ PRInt32 childCount;
+ nsIHTMLContent* content;
+
+ // Start from the top of the stack (growing upwards) and see if
+ // any new content has been appended. If so, we recognize that
+ // reflows have been generated for it and we should make sure that
+ // no further reflows occur.
+ PRInt32 stackPos = mStackPos-1;
+ while (stackPos > 0) {
+ if (mStack[stackPos].mFlags & APPENDED) {
+ content = mStack[stackPos].mContent;
+ content->ChildCount(childCount);
+ mStack[stackPos].mNumFlushed = childCount;
+ }
+ stackPos--;
+ }
}
/**
@@ -1571,31 +1745,50 @@ SinkContext::FlushTags()
* adding it to the content.
*/
nsresult
-SinkContext::FlushText(PRBool* aDidFlush)
+SinkContext::FlushText(PRBool* aDidFlush, PRBool aReleaseLast)
{
nsresult rv = NS_OK;
PRBool didFlush = PR_FALSE;
if (0 != mTextLength) {
- nsIContent* content;
- rv = NS_NewTextNode(&content);
- if (NS_OK == rv) {
- // Set the content's document
- content->SetDocument(mSink->mDocument, PR_FALSE);
+ if (mLastTextNode) {
+ nsCOMPtr cdata = do_QueryInterface(mLastTextNode, &rv);
+ if (NS_SUCCEEDED(rv)) {
+ CBufDescriptor bd(mText, PR_TRUE, mTextSize+1, mTextLength);
+ bd.mIsConst = PR_TRUE;
+ nsAutoString str(bd);
- // Set the text in the text node
- nsITextContent* text = nsnull;
- content->QueryInterface(kITextContentIID, (void**) &text);
- text->SetText(mText, mTextLength, PR_FALSE);
- NS_RELEASE(text);
+ rv = cdata->AppendData(str);
+ }
+ }
+ else {
+ nsIContent* content;
+ rv = NS_NewTextNode(&content);
+ if (NS_OK == rv) {
+ // Set the content's document
+ content->SetDocument(mSink->mDocument, PR_FALSE);
+
+ // Set the text in the text node
+ nsITextContent* text = nsnull;
+ content->QueryInterface(kITextContentIID, (void**) &text);
+ text->SetText(mText, mTextLength, PR_FALSE);
+ NS_RELEASE(text);
- // Add text to its parent
- NS_ASSERTION(mStackPos > 0, "leaf w/o container");
- nsIHTMLContent* parent = mStack[mStackPos - 1].mContent;
- parent->AppendChildTo(content, PR_FALSE);
- NS_RELEASE(content);
+ // Add text to its parent
+ NS_ASSERTION(mStackPos > 0, "leaf w/o container");
+ nsIHTMLContent* parent = mStack[mStackPos - 1].mContent;
+ if (mStack[mStackPos-1].mInsertionPoint != -1) {
+ parent->InsertChildAt(content,
+ mStack[mStackPos-1].mInsertionPoint++,
+ mSink->IsInScript());
+ }
+ else {
+ parent->AppendChildTo(content, mSink->IsInScript());
+ }
- // Mark sink dirty if it can safely reflow something
- MaybeMarkSinkDirty();
+ mLastTextNode = content;
+
+ DidAddContent(content);
+ }
}
mTextLength = 0;
didFlush = PR_TRUE;
@@ -1604,8 +1797,11 @@ SinkContext::FlushText(PRBool* aDidFlush)
*aDidFlush = didFlush;
}
+ if (aReleaseLast && mLastTextNode) {
+ NS_RELEASE(mLastTextNode);
+ }
#ifdef DEBUG
- if (mPreAppend && mSink->mDirty && didFlush &&
+ if (mPreAppend && didFlush &&
SINK_LOG_TEST(gSinkLogModuleInfo,SINK_ALWAYS_REFLOW)) {
mSink->ForceReflow();
}
@@ -1647,6 +1843,7 @@ HTMLContentSink::HTMLContentSink()
#endif
mNotAtRef = PR_TRUE;
mContentIDCounter = NS_CONTENT_ID_COUNTER_BASE;
+ mInScript = 0;
}
HTMLContentSink::~HTMLContentSink()
@@ -1666,20 +1863,18 @@ HTMLContentSink::~HTMLContentSink()
NS_IF_RELEASE(mCurrentForm);
NS_IF_RELEASE(mCurrentMap);
- NS_IF_RELEASE(mCurrentDOMMap);
NS_IF_RELEASE(mRefContent);
- for (PRInt32 i = 0; i < mNumContexts; i++) {
- SinkContext* sc = mContexts[i];
+ PRInt32 numContexts = mContextStack.Count();
+ for (PRInt32 i = 0; i < numContexts; i++) {
+ SinkContext* sc = (SinkContext*)mContextStack.ElementAt(i);
sc->End();
- delete sc;
if (sc == mCurrentContext) {
mCurrentContext = nsnull;
}
+ delete sc;
}
- if (nsnull != mContexts) {
- delete [] mContexts;
- }
+
if (mCurrentContext == mHeadContext) {
mCurrentContext = nsnull;
}
@@ -1763,7 +1958,7 @@ HTMLContentSink::Init(nsIDocument* aDoc,
mRoot->AppendChildTo(mHead, PR_FALSE);
mCurrentContext = new SinkContext(this);
- mCurrentContext->Begin(eHTMLTag_html, mRoot);
+ mCurrentContext->Begin(eHTMLTag_html, mRoot, 0, -1);
mContextStack.AppendElement(mCurrentContext);
char* spec;
@@ -1820,7 +2015,7 @@ HTMLContentSink::DidBuildModel(PRInt32 aQualityLevel)
if (nsnull != mBody) {
SINK_TRACE(SINK_TRACE_REFLOW,
("HTMLContentSink::DidBuildModel: layout final content"));
- NotifyBody();
+ mCurrentContext->FlushTags();
}
ScrollToRef();
@@ -1836,15 +2031,7 @@ HTMLContentSink::WillInterrupt()
{
SINK_TRACE(SINK_TRACE_CALLS,
("HTMLContentSink::WillInterrupt: this=%p", this));
- if (mDirty) {
- if (nsnull != mBody) {
- SINK_TRACE(SINK_TRACE_REFLOW,
- ("HTMLContentSink::WillInterrupt: reflow content"));
- NotifyBody();
- }
- mDirty = PR_FALSE;
- }
- return NS_OK;
+ return mCurrentContext->FlushTags();
}
NS_IMETHODIMP
@@ -1882,9 +2069,24 @@ HTMLContentSink::BeginContext(PRInt32 aPosition)
NS_ASSERTION(mCurrentContext != nsnull," Non-existing context");
+ // Flush everything in the current context so that we don't have
+ // to worry about insertions resulting in inconsistent frame creation.
+ mCurrentContext->FlushTags();
+
+ PRInt32 insertionPoint = -1;
nsHTMLTag nodeType = mCurrentContext->mStack[aPosition].mType;
nsIHTMLContent* content = mCurrentContext->mStack[aPosition].mContent;
- sc->Begin(nodeType,content);
+ // If the content under which the new context is created
+ // has a child on the stack, the insertion point is
+ // before the last child.
+ if (aPosition < (mCurrentContext->mStackPos-1)) {
+ content->ChildCount(insertionPoint);
+ insertionPoint--;
+ }
+ sc->Begin(nodeType,
+ content,
+ mCurrentContext->mStack[aPosition].mNumFlushed,
+ insertionPoint);
NS_ADDREF(sc->mSink);
mContextStack.AppendElement(mCurrentContext);
@@ -1906,7 +2108,10 @@ HTMLContentSink::EndContext(PRInt32 aPosition)
NS_ASSERTION(sc->mStack[aPosition].mType == mCurrentContext->mStack[0].mType,"ending a wrong context");
- mCurrentContext->FlushText();
+ mCurrentContext->FlushTextAndRelease();
+
+ sc->mStack[aPosition].mNumFlushed = mCurrentContext->mStack[0].mNumFlushed;
+
for(PRInt32 i=0; imStackPos; i++)
NS_IF_RELEASE(mCurrentContext->mStack[i].mContent);
delete [] mCurrentContext->mStack;
@@ -1983,7 +2188,7 @@ HTMLContentSink::OpenHTML(const nsIParserNode& aNode)
NS_START_STOPWATCH(mWatch)
SINK_TRACE_NODE(SINK_TRACE_CALLS,
- "HTMLContentSink::OpenHTML", aNode, this);
+ "HTMLContentSink::OpenHTML", aNode, 0, this);
NS_STOP_STOPWATCH(mWatch)
return NS_OK;
@@ -1995,7 +2200,7 @@ HTMLContentSink::CloseHTML(const nsIParserNode& aNode)
RAPTOR_STOPWATCH_DEBUGTRACE(("Start: nsHTMLContentSink::CloseHTML()\n"));
NS_START_STOPWATCH(mWatch)
SINK_TRACE_NODE(SINK_TRACE_CALLS,
- "HTMLContentSink::CloseHTML", aNode, this);
+ "HTMLContentSink::CloseHTML", aNode, 0, this);
if (nsnull != mHeadContext) {
mHeadContext->End();
delete mHeadContext;
@@ -2012,7 +2217,7 @@ HTMLContentSink::OpenHead(const nsIParserNode& aNode)
RAPTOR_STOPWATCH_DEBUGTRACE(("Start: nsHTMLContentSink::OpenHead()\n"));
NS_START_STOPWATCH(mWatch)
SINK_TRACE_NODE(SINK_TRACE_CALLS,
- "HTMLContentSink::OpenHead", aNode, this);
+ "HTMLContentSink::OpenHead", aNode, 0, this);
nsresult rv = NS_OK;
if (nsnull == mHeadContext) {
mHeadContext = new SinkContext(this);
@@ -2022,7 +2227,7 @@ HTMLContentSink::OpenHead(const nsIParserNode& aNode)
return NS_ERROR_OUT_OF_MEMORY;
}
mHeadContext->SetPreAppend(PR_TRUE);
- rv = mHeadContext->Begin(eHTMLTag_head, mHead);
+ rv = mHeadContext->Begin(eHTMLTag_head, mHead, 0, -1);
if (NS_OK != rv) {
RAPTOR_STOPWATCH_DEBUGTRACE(("Stop: nsHTMLContentSink::OpenHead()\n"));
NS_STOP_STOPWATCH(mWatch)
@@ -2049,7 +2254,8 @@ HTMLContentSink::CloseHead(const nsIParserNode& aNode)
RAPTOR_STOPWATCH_DEBUGTRACE(("Start: nsHTMLContentSink::CloseHead()\n"));
NS_START_STOPWATCH(mWatch)
SINK_TRACE_NODE(SINK_TRACE_CALLS,
- "HTMLContentSink::CloseHead", aNode, this);
+ "HTMLContentSink::CloseHead", aNode,
+ 0, this);
PRInt32 n = mContextStack.Count() - 1;
mCurrentContext = (SinkContext*) mContextStack.ElementAt(n);
mContextStack.RemoveElementAt(n);
@@ -2066,7 +2272,8 @@ HTMLContentSink::OpenBody(const nsIParserNode& aNode)
//NS_PRECONDITION(nsnull == mBody, "parser called OpenBody twice");
SINK_TRACE_NODE(SINK_TRACE_CALLS,
- "HTMLContentSink::OpenBody", aNode, this);
+ "HTMLContentSink::OpenBody", aNode,
+ mCurrentContext->mStackPos, this);
// Add attributes, if any, to the current BODY node
if(mBody != nsnull){
nsIScriptContextOwner* sco = mDocument->GetScriptContextOwner();
@@ -2088,7 +2295,6 @@ HTMLContentSink::OpenBody(const nsIParserNode& aNode)
return rv;
}
mBody = mCurrentContext->mStack[mCurrentContext->mStackPos - 1].mContent;
- mBodyChildCount = 0;
NS_ADDREF(mBody);
RAPTOR_STOPWATCH_DEBUGTRACE(("Stop: nsHTMLContentSink::OpenBody()\n"));
@@ -2103,10 +2309,11 @@ HTMLContentSink::CloseBody(const nsIParserNode& aNode)
RAPTOR_STOPWATCH_DEBUGTRACE(("Start: nsHTMLContentSink::CloseBody()\n"));
NS_START_STOPWATCH(mWatch)
SINK_TRACE_NODE(SINK_TRACE_CALLS,
- "HTMLContentSink::CloseBody", aNode, this);
+ "HTMLContentSink::CloseBody", aNode,
+ mCurrentContext->mStackPos-1, this);
PRBool didFlush;
- nsresult rv = mCurrentContext->FlushText(&didFlush);
+ nsresult rv = mCurrentContext->FlushTextAndRelease(&didFlush);
if (NS_OK != rv) {
RAPTOR_STOPWATCH_DEBUGTRACE(("Stop: nsHTMLContentSink::CloseBody()\n"));
NS_STOP_STOPWATCH(mWatch)
@@ -2115,8 +2322,10 @@ HTMLContentSink::CloseBody(const nsIParserNode& aNode)
mCurrentContext->CloseContainer(aNode);
if (didFlush) {
+ SINK_TRACE(SINK_TRACE_REFLOW,
+ ("HTMLContentSink::CloseBody: layout final body text"));
// Trigger a reflow for the flushed text
- NotifyBody();
+ mCurrentContext->FlushTags();
}
RAPTOR_STOPWATCH_DEBUGTRACE(("Stop: nsHTMLContentSink::CloseBody()\n"));
@@ -2132,18 +2341,18 @@ HTMLContentSink::OpenForm(const nsIParserNode& aNode)
nsresult result = NS_OK;
nsIHTMLContent* content = nsnull;
- mCurrentContext->FlushText();
+ mCurrentContext->FlushTextAndRelease();
SINK_TRACE_NODE(SINK_TRACE_CALLS,
- "HTMLContentSink::OpenForm", aNode, this);
+ "HTMLContentSink::OpenForm", aNode,
+ mCurrentContext->mStackPos, this);
// Close out previous form if it's there. If there is one
// around, it's probably because the last one wasn't well-formed.
NS_IF_RELEASE(mCurrentForm);
// Check if the parent is a table, tbody, thead, tfoot, tr, col or
- // colgroup. If so, this is just going to be a leaf element.
- // If so, we fix up by making the form leaf content.
+ // colgroup. If so, we fix up by making the form leaf content.
if (mCurrentContext->IsCurrentContainer(eHTMLTag_table) ||
mCurrentContext->IsCurrentContainer(eHTMLTag_tbody) ||
mCurrentContext->IsCurrentContainer(eHTMLTag_thead) ||
@@ -2195,9 +2404,11 @@ HTMLContentSink::CloseForm(const nsIParserNode& aNode)
NS_START_STOPWATCH(mWatch)
nsresult result = NS_OK;
- mCurrentContext->FlushText();
+ mCurrentContext->FlushTextAndRelease();
+
SINK_TRACE_NODE(SINK_TRACE_CALLS,
- "HTMLContentSink::CloseForm", aNode, this);
+ "HTMLContentSink::CloseForm", aNode,
+ mCurrentContext->mStackPos-1, this);
if (nsnull != mCurrentForm) {
// Check if this is a well-formed form
@@ -2205,14 +2416,7 @@ HTMLContentSink::CloseForm(const nsIParserNode& aNode)
result = mCurrentContext->CloseContainer(aNode);
}
else if (mCurrentContext->IsAncestorContainer(eHTMLTag_form)) {
- nsIHTMLContent* parent;
- result = mCurrentContext->DemoteContainer(aNode, parent);
- if (NS_SUCCEEDED(result)) {
- if (parent == mBody) {
- NotifyBody();
- }
- NS_IF_RELEASE(parent);
- }
+ result = mCurrentContext->DemoteContainer(aNode);
}
NS_RELEASE(mCurrentForm);
}
@@ -2228,14 +2432,14 @@ HTMLContentSink::OpenFrameset(const nsIParserNode& aNode)
RAPTOR_STOPWATCH_DEBUGTRACE(("Start: nsHTMLContentSink::OpenFrameset()\n"));
NS_START_STOPWATCH(mWatch)
SINK_TRACE_NODE(SINK_TRACE_CALLS,
- "HTMLContentSink::OpenFrameset", aNode, this);
+ "HTMLContentSink::OpenFrameset", aNode,
+ mCurrentContext->mStackPos, this);
nsresult rv = mCurrentContext->OpenContainer(aNode);
if ((NS_OK == rv) && (nsnull == mFrameset)) {
mFrameset = mCurrentContext->mStack[mCurrentContext->mStackPos-1].mContent;
NS_ADDREF(mFrameset);
}
- mInMonolithicContainer++;
RAPTOR_STOPWATCH_DEBUGTRACE(("Stop: nsHTMLContentSink::OpenFrameset()\n"));
NS_STOP_STOPWATCH(mWatch)
return rv;
@@ -2247,7 +2451,8 @@ HTMLContentSink::CloseFrameset(const nsIParserNode& aNode)
RAPTOR_STOPWATCH_DEBUGTRACE(("Start: nsHTMLContentSink::CloseFrameset()\n"));
NS_START_STOPWATCH(mWatch)
SINK_TRACE_NODE(SINK_TRACE_CALLS,
- "HTMLContentSink::CloseFrameset", aNode, this);
+ "HTMLContentSink::CloseFrameset", aNode,
+ mCurrentContext->mStackPos-1, this);
SinkContext* sc = mCurrentContext;
nsIHTMLContent* fs = sc->mStack[sc->mStackPos-1].mContent;
@@ -2268,7 +2473,8 @@ HTMLContentSink::OpenMap(const nsIParserNode& aNode)
NS_START_STOPWATCH(mWatch)
nsresult rv = NS_OK;
SINK_TRACE_NODE(SINK_TRACE_CALLS,
- "HTMLContentSink::OpenMap", aNode, this);
+ "HTMLContentSink::OpenMap", aNode,
+ mCurrentContext->mStackPos, this);
// We used to treat MAP elements specially (i.e. they were
// only parent elements for AREAs), but we don't anymore.
// HTML 4.0 says that MAP elements can have block content
@@ -2286,9 +2492,9 @@ HTMLContentSink::CloseMap(const nsIParserNode& aNode)
NS_START_STOPWATCH(mWatch)
nsresult rv = NS_OK;
SINK_TRACE_NODE(SINK_TRACE_CALLS,
- "HTMLContentSink::CloseMap", aNode, this);
+ "HTMLContentSink::CloseMap", aNode,
+ mCurrentContext->mStackPos-1, this);
NS_IF_RELEASE(mCurrentMap);
- NS_IF_RELEASE(mCurrentDOMMap);
rv = mCurrentContext->CloseContainer(aNode);
RAPTOR_STOPWATCH_DEBUGTRACE(("Stop: nsHTMLContentSink::CloseMap()\n"));
@@ -2346,27 +2552,27 @@ HTMLContentSink::AddLeaf(const nsIParserNode& aNode)
break;
case eHTMLTag_base:
- mCurrentContext->FlushText();
+ mCurrentContext->FlushTextAndRelease();
rv = ProcessBASETag(aNode);
break;
case eHTMLTag_link:
- mCurrentContext->FlushText();
+ mCurrentContext->FlushTextAndRelease();
rv = ProcessLINKTag(aNode);
break;
case eHTMLTag_meta:
- mCurrentContext->FlushText();
+ mCurrentContext->FlushTextAndRelease();
rv = ProcessMETATag(aNode);
break;
case eHTMLTag_style:
- mCurrentContext->FlushText();
+ mCurrentContext->FlushTextAndRelease();
rv = ProcessSTYLETag(aNode);
break;
case eHTMLTag_script:
- mCurrentContext->FlushText();
+ mCurrentContext->FlushTextAndRelease();
rv = ProcessSCRIPTTag(aNode);
break;
@@ -3042,7 +3248,6 @@ HTMLContentSink::ProcessMAPTag(const nsIParserNode& aNode,
nsresult rv;
NS_IF_RELEASE(mCurrentMap);
- NS_IF_RELEASE(mCurrentDOMMap);
nsIDOMHTMLMapElement* domMap;
rv = aContent->QueryInterface(kIDOMHTMLMapElementIID, (void**)&domMap);
@@ -3066,7 +3271,7 @@ HTMLContentSink::ProcessMAPTag(const nsIParserNode& aNode,
// The map adds itself
mCurrentMap = aContent;
NS_ADDREF(aContent);
- mCurrentDOMMap = domMap; // holds a reference
+ NS_IF_RELEASE(domMap);
return rv;
}
@@ -3228,6 +3433,51 @@ IsJavaScriptLanguage(const nsString& aName)
}
}
+#ifdef DEBUG
+void
+HTMLContentSink::ForceReflow()
+{
+ mCurrentContext->FlushTags();
+}
+#endif
+
+
+void
+HTMLContentSink::NotifyAppend(nsIContent* aContainer, PRInt32 aStartIndex)
+{
+ RAPTOR_STOPWATCH_DEBUGTRACE(("Save and stop: nsHTMLContentSink::NotifyAppend()\n"));
+ NS_SAVE_STOPWATCH_STATE(mWatch)
+ NS_STOP_STOPWATCH(mWatch)
+ mDocument->ContentAppended(aContainer, aStartIndex);
+ RAPTOR_STOPWATCH_DEBUGTRACE(("Restore: nsHTMLContentSink::NotifyAppend()\n"));
+ NS_RESTORE_STOPWATCH_STATE(mWatch)
+}
+
+void
+HTMLContentSink::NotifyInsert(nsIContent* aContent,
+ nsIContent* aChildContent,
+ PRInt32 aIndexInContainer)
+{
+ RAPTOR_STOPWATCH_DEBUGTRACE(("Save and stop: nsHTMLContentSink::NotifyInsert()\n"));
+ NS_SAVE_STOPWATCH_STATE(mWatch)
+ NS_STOP_STOPWATCH(mWatch)
+ mDocument->ContentInserted(aContent, aChildContent, aIndexInContainer);
+ RAPTOR_STOPWATCH_DEBUGTRACE(("Restore: nsHTMLContentSink::NotifyInsert()\n"));
+ NS_RESTORE_STOPWATCH_STATE(mWatch)
+}
+
+void
+HTMLContentSink::UpdateAllContexts()
+{
+ PRInt32 numContexts = mContextStack.Count();
+ for (PRInt32 i = 0; i < numContexts; i++) {
+ SinkContext* sc = (SinkContext*)mContextStack.ElementAt(i);
+
+ sc->UpdateChildCounts();
+ }
+ mCurrentContext->UpdateChildCounts();
+}
+
nsresult
HTMLContentSink::ResumeParsing()
{
@@ -3242,54 +3492,29 @@ HTMLContentSink::ResumeParsing()
PRBool
HTMLContentSink::PreEvaluateScript()
{
- // Cause frame creation and reflow of all body children that
- // have thus far been appended. Note that if mDirty is true
- // then we know that the current body child has not yet been
- // added to the content model (and, hence, it's safe to create
- // frames for all body children).
- // We don't want the current body child to be appended (and
- // have frames be constructed for it) since the current script
- // may add new content to the tree (and cause an immediate
- // reflow). As long as frames don't exist for the subtree rooted
- // by the current body child, new content added to the subtree
- // will not generate new frames and, hence, we won't have to
- // worry about reflowing of incomplete content or double frame
- // creation.
- if (mDirty) {
- if (nsnull != mBody) {
- SINK_TRACE(SINK_TRACE_REFLOW,
- ("HTMLContentSink::PreEvaluateScript: reflow content"));
- NotifyBody();
- }
- mDirty = PR_FALSE;
- }
-
- // Now eagerly all pending elements (including the current body child)
- // to the body (so that they can be seen by scripts). Note that frames
- // have not yet been created for them (and shouldn't be).
+ // Eagerly append all pending elements (including the current body child)
+ // to the body (so that they can be seen by scripts) and force reflow.
+ SINK_TRACE(SINK_TRACE_CALLS,
+ ("HTMLContentSink::PreEvaluateScript: flushing tags before evaluating script"));
mCurrentContext->FlushTags();
+
+ mInScript++;
- return (nsnull != mBody);
+ return PR_TRUE;
}
void
HTMLContentSink::PostEvaluateScript(PRBool aBodyPresent)
{
- // If the script added new content directly to the body, we update
- // our body child count so that frames aren't created twice.
- if (nsnull != mBody) {
- mBody->ChildCount(mBodyChildCount);
- // If the script is not a body child, we shouldn't include
- // the element that we eagerly appended (the ancestor of the
- // script), since it is not yet complete. Of course, this
- // should only happen if the body element was present *before*
- // script evaluation (a body could have been created by
- // the script).
- if (!mCurrentContext->IsCurrentContainer(eHTMLTag_body) &&
- aBodyPresent) {
- mBodyChildCount--;
- }
- }
+ mInScript--;
+
+ mCurrentContext->UpdateChildCounts();
+}
+
+PRBool
+HTMLContentSink::IsInScript()
+{
+ return (mInScript > 0);
}
nsresult
@@ -3419,7 +3644,14 @@ HTMLContentSink::ProcessSCRIPTTag(const nsIParserNode& aNode)
NS_RELEASE(element);
return rv;
}
- parent->AppendChildTo(element, PR_FALSE);
+ if (mCurrentContext->mStack[mCurrentContext->mStackPos-1].mInsertionPoint != -1) {
+ parent->InsertChildAt(element,
+ mCurrentContext->mStack[mCurrentContext->mStackPos-1].mInsertionPoint++,
+ IsInScript());
+ }
+ else {
+ parent->AppendChildTo(element, IsInScript());
+ }
}
else {
NS_IF_RELEASE(sco);
@@ -3442,7 +3674,7 @@ HTMLContentSink::ProcessSCRIPTTag(const nsIParserNode& aNode)
tc->SetData(script);
NS_RELEASE(tc);
}
- element->AppendChildTo(text, PR_FALSE);
+ element->AppendChildTo(text, IsInScript());
text->SetDocument(mDocument, PR_FALSE);
NS_RELEASE(text);
}
@@ -3492,7 +3724,7 @@ HTMLContentSink::ProcessSCRIPTTag(const nsIParserNode& aNode)
PRUint32 lineNo = (PRUint32)aNode.GetSourceLineNumber();
EvaluateScript(script, lineNo);
-
+
PostEvaluateScript(bodyPresent);
// If the parse was disabled as a result of this evaluate script
@@ -3599,7 +3831,7 @@ HTMLContentSink::ProcessSTYLETag(const nsIParserNode& aNode)
tc->SetData(content);
NS_RELEASE(tc);
}
- element->AppendChildTo(text, PR_FALSE);
+ element->AppendChildTo(text, IsInScript());
text->SetDocument(mDocument, PR_FALSE);
NS_RELEASE(text);
}
@@ -3618,6 +3850,15 @@ HTMLContentSink::ProcessSTYLETag(const nsIParserNode& aNode)
((blockParser) ? mParser : nsnull),
doneLoading);
NS_RELEASE(uin);
+
+ // If we're done loading the inline style, we know that frames
+ // have been created for all content seen so far (processing
+ // of a new style sheet causes recreation of the frame model).
+ // As a result, all contexts should update their notion of
+ // how much frame creation has happened.
+ if (doneLoading) {
+ UpdateAllContexts();
+ }
}
else {
// src with immediate style data doesn't add up