diff --git a/mozilla/content/html/document/src/nsHTMLFragmentContentSink.cpp b/mozilla/content/html/document/src/nsHTMLFragmentContentSink.cpp index fca778c6d8d..2f46648d4b9 100644 --- a/mozilla/content/html/document/src/nsHTMLFragmentContentSink.cpp +++ b/mozilla/content/html/document/src/nsHTMLFragmentContentSink.cpp @@ -16,13 +16,22 @@ * Reserved. */ #include "nsCOMPtr.h" -#include "nsIHTMLContentSink.h" +#include "nsIHTMLFragmentContentSink.h" #include "nsIParser.h" #include "nsIHTMLContent.h" #include "nsIHTMLContentContainer.h" #include "nsHTMLTokens.h" #include "nsHTMLEntities.h" #include "nsHTMLParts.h" +#include "nsIDOMComment.h" +#include "nsIDOMHTMLFormElement.h" +#include "nsIDOMDocumentFragment.h" +#include "nsVoidArray.h" +#include "nsITextContent.h" +#include "nsINameSpace.h" +#include "nsINameSpaceManager.h" +#include "nsIDocument.h" +#include "prmem.h" // // XXX THIS IS TEMPORARY CODE @@ -31,7 +40,13 @@ // at some pointe really soon! // -class nsHTMLFragmentContentSink : public nsIHTMLContentSink { +static NS_DEFINE_IID(kIContentIID, NS_ICONTENT_IID); +static NS_DEFINE_IID(kIHTMLContentSinkIID, NS_IHTML_CONTENT_SINK_IID); +static NS_DEFINE_IID(kIHTMLFragmentContentSinkIID, NS_IHTML_FRAGMENT_CONTENT_SINK_IID); +static NS_DEFINE_IID(kIDOMCommentIID, NS_IDOMCOMMENT_IID); +static NS_DEFINE_IID(kIDOMDocumentFragmentIID, NS_IDOMDOCUMENTFRAGMENT_IID); + +class nsHTMLFragmentContentSink : public nsIHTMLFragmentContentSink { public: nsHTMLFragmentContentSink(); virtual ~nsHTMLFragmentContentSink(); @@ -70,19 +85,18 @@ public: NS_IMETHOD AddProcessingInstruction(const nsIParserNode& aNode); NS_IMETHOD DoFragment(PRBool aFlag); + // nsIHTMLFragmentContentSink + NS_IMETHOD GetFragment(nsIDOMDocumentFragment** aFragment); nsIContent* GetCurrentContent(); PRInt32 PushContent(nsIContent *aContent); nsIContent* PopContent(); nsresult AddAttributes(const nsIParserNode& aNode, - nsIContent* aContent, - PRBool aIsHTML); + nsIContent* aContent); nsresult AddText(const nsString& aString); - nsresult FlushText(PRBool aCreateTextNode=PR_TRUE, - PRBool* aDidFlush=nsnull); - + nsresult FlushText(); PRBool mHitSentinel; @@ -99,8 +113,24 @@ public: }; +nsresult +NS_NewHTMLFragmentContentSink(nsIHTMLFragmentContentSink** aResult) +{ + NS_PRECONDITION(nsnull != aResult, "null ptr"); + if (nsnull == aResult) { + return NS_ERROR_NULL_POINTER; + } + nsHTMLFragmentContentSink* it; + NS_NEWXPCOM(it, nsHTMLFragmentContentSink); + if (nsnull == it) { + return NS_ERROR_OUT_OF_MEMORY; + } + return it->QueryInterface(kIHTMLFragmentContentSinkIID, (void **)aResult); +} + nsHTMLFragmentContentSink::nsHTMLFragmentContentSink() { + NS_INIT_REFCNT(); mHitSentinel = PR_FALSE; mRoot = nsnull; mParser = nsnull; @@ -132,13 +162,55 @@ nsHTMLFragmentContentSink::~nsHTMLFragmentContentSink() } } -NS_IMPL_ISUPPORTS(nsHTMLFragmentContentSink, kIHTMLContentSinkIID) +NS_IMPL_ADDREF(nsHTMLFragmentContentSink) +NS_IMPL_RELEASE(nsHTMLFragmentContentSink) + +NS_IMETHODIMP +nsHTMLFragmentContentSink::QueryInterface(REFNSIID aIID, void** aInstancePtr) +{ + if (NULL == aInstancePtr) { + return NS_ERROR_NULL_POINTER; + } + + *aInstancePtr = NULL; + + static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID); + if (aIID.Equals(kIHTMLFragmentContentSinkIID)) { + nsIHTMLFragmentContentSink* tmp = this; + *aInstancePtr = (void*) tmp; + NS_ADDREF_THIS(); + return NS_OK; + } + if (aIID.Equals(kIHTMLContentSinkIID)) { + nsIHTMLContentSink* tmp = this; + *aInstancePtr = (void*) tmp; + NS_ADDREF_THIS(); + return NS_OK; + } + if (aIID.Equals(kISupportsIID)) { + *aInstancePtr = (void*) ((nsISupports*)this); + NS_ADDREF_THIS(); + return NS_OK; + } + return NS_NOINTERFACE; +} NS_IMETHODIMP nsHTMLFragmentContentSink::WillBuildModel(void) { - return NS_OK; + nsresult result = NS_OK; + + if (nsnull == mRoot) { + nsIDOMDocumentFragment* frag; + + result = NS_NewDocumentFragment(&frag, nsnull); + if (NS_SUCCEEDED(result)) { + result = frag->QueryInterface(kIContentIID, (void**)&mRoot); + NS_RELEASE(frag); + } + } + return result; } NS_IMETHODIMP @@ -202,7 +274,7 @@ nsHTMLFragmentContentSink::CloseHTML(const nsIParserNode& aNode) NS_IMETHODIMP nsHTMLFragmentContentSink::OpenHead(const nsIParserNode& aNode) { - // XXX Not likely to get a head in the paste + // XXX Not likely to get a head in the fragment return OpenContainer(aNode); } @@ -266,6 +338,7 @@ NS_IMETHODIMP nsHTMLFragmentContentSink::OpenContainer(const nsIParserNode& aNode) { nsAutoString tag; + nsresult result = NS_OK; tag = aNode.GetText(); if (tag.EqualsIgnoreCase(kSentinelStr)) { @@ -281,25 +354,25 @@ nsHTMLFragmentContentSink::OpenContainer(const nsIParserNode& aNode) result = AddAttributes(aNode, content); if (NS_OK == result) { nsIContent *parent = GetCurrentContent(); - - if (nsnull == parent) { - parent = mRoot; - } - + + if (nsnull == parent) { + parent = mRoot; + } + parent->AppendChildTo(content, PR_FALSE); PushContent(content); } } } - return NS_OK; + return result; } NS_IMETHODIMP nsHTMLFragmentContentSink::CloseContainer(const nsIParserNode& aNode) { - if (mHitSentinel && (nsnull != GetCurrentContent()) { - nsIContent content; + if (mHitSentinel && (nsnull != GetCurrentContent())) { + nsIContent* content; FlushText(); content = PopContent(); NS_RELEASE(content); @@ -311,31 +384,30 @@ NS_IMETHODIMP nsHTMLFragmentContentSink::AddLeaf(const nsIParserNode& aNode) { nsresult result = NS_OK; - nsHTMLTag nodeType = nsHTMLTag(aNode.GetNodeType()); - switch (nodeType) { + switch (aNode.GetTokenType()) { case eToken_start: { - FlushText(); - - // Create new leaf content object - nsHTMLTag nodeType = nsHTMLTag(aNode.GetNodeType()); - nsIHTMLContent* content; - result = NS_CreateHTMLElement(&content, tag); - - if (NS_OK == result) { - result = AddAttributes(aNode, content); - if (NS_OK == result) { - nsIContent *parent = GetCurrentContent(); - - if (nsnull == parent) { - parent = mRoot; - } - - parent->AppendChildTo(content, PR_FALSE); - } - NS_RELEASE(content); - } + FlushText(); + + // Create new leaf content object + nsIHTMLContent* content; + nsAutoString tag = aNode.GetText(); + result = NS_CreateHTMLElement(&content, tag); + + if (NS_OK == result) { + result = AddAttributes(aNode, content); + if (NS_OK == result) { + nsIContent *parent = GetCurrentContent(); + + if (nsnull == parent) { + parent = mRoot; + } + + parent->AppendChildTo(content, PR_FALSE); + } + NS_RELEASE(content); + } } break; case eToken_text: @@ -343,21 +415,21 @@ nsHTMLFragmentContentSink::AddLeaf(const nsIParserNode& aNode) case eToken_newline: result = AddText(aNode.GetText()); break; - + case eToken_entity: { - nsAutoString tmp; - PRInt32 unicode = aNode.TranslateToUnicodeStr(tmp); - if (unicode < 0) { - result = AddText(aNode.GetText()); - } - else { - result = AddText(tmp); - } + nsAutoString tmp; + PRInt32 unicode = aNode.TranslateToUnicodeStr(tmp); + if (unicode < 0) { + result = AddText(aNode.GetText()); + } + else { + result = AddText(tmp); + } } break; } - + return result; } @@ -387,7 +459,7 @@ nsHTMLFragmentContentSink::AddComment(const nsIParserNode& aNode) nsIContent *parent = GetCurrentContent(); if (nsnull == parent) { - parent = mRoot; + parent = mRoot; } parent->AppendChildTo(comment, PR_FALSE); @@ -410,6 +482,17 @@ nsHTMLFragmentContentSink::DoFragment(PRBool aFlag) return NS_OK; } +NS_IMETHODIMP +nsHTMLFragmentContentSink::GetFragment(nsIDOMDocumentFragment** aFragment) +{ + if (nsnull != mRoot) { + return mRoot->QueryInterface(kIDOMDocumentFragmentIID, (void**)aFragment); + } + else { + *aFragment = nsnull; + return NS_OK; + } +} nsIContent* nsHTMLFragmentContentSink::GetCurrentContent() @@ -443,3 +526,235 @@ nsHTMLFragmentContentSink::PopContent() } return content; } + +#define NS_ACCUMULATION_BUFFER_SIZE 4096 + +nsresult +nsHTMLFragmentContentSink::AddText(const nsString& aString) +{ + PRInt32 addLen = aString.Length(); + if (0 == addLen) { + return NS_OK; + } + + // Create buffer when we first need it + if (0 == mTextSize) { + mText = (PRUnichar *) PR_MALLOC(sizeof(PRUnichar) * NS_ACCUMULATION_BUFFER_SIZE); + if (nsnull == mText) { + return NS_ERROR_OUT_OF_MEMORY; + } + mTextSize = NS_ACCUMULATION_BUFFER_SIZE; + } + + // Copy data from string into our buffer; flush buffer when it fills up + PRInt32 offset = 0; + while (0 != addLen) { + PRInt32 amount = mTextSize - mTextLength; + if (amount > addLen) { + amount = addLen; + } + if (0 == amount) { + nsresult rv = FlushText(); + if (NS_OK != rv) { + return rv; + } + } + memcpy(&mText[mTextLength], aString.GetUnicode() + offset, + sizeof(PRUnichar) * amount); + mTextLength += amount; + offset += amount; + addLen -= amount; + } + + return NS_OK; +} + +nsresult +nsHTMLFragmentContentSink::FlushText() +{ + nsresult rv = NS_OK; + if (0 != mTextLength) { + nsIContent* content; + rv = NS_NewTextNode(&content); + if (NS_OK == rv) { + + // Set the text in the text node + static NS_DEFINE_IID(kITextContentIID, NS_ITEXT_CONTENT_IID); + nsITextContent* text = nsnull; + content->QueryInterface(kITextContentIID, (void**) &text); + text->SetText(mText, mTextLength, PR_FALSE); + NS_RELEASE(text); + + // Add text to its parent + nsIContent *parent = GetCurrentContent(); + + if (nsnull == parent) { + parent = mRoot; + } + + parent->AppendChildTo(content, PR_FALSE); + + NS_RELEASE(content); + } + + mTextLength = 0; + } + + return rv; +} + +// XXX Code copied from nsHTMLContentSink. It should be shared. +static void +GetAttributeValueAt(const nsIParserNode& aNode, + PRInt32 aIndex, + nsString& aResult) +{ + // Copy value + const nsString& value = aNode.GetValueAt(aIndex); + aResult.Truncate(); + aResult.Append(value); + + // Strip quotes if present + PRUnichar first = aResult.First(); + if ((first == '\"') || (first == '\'')) { + if (aResult.Last() == first) { + aResult.Cut(0, 1); + PRInt32 pos = aResult.Length() - 1; + if (pos >= 0) { + aResult.Cut(pos, 1); + } + } else { + // Mismatched quotes - leave them in + } + } + + // Reduce any entities + // XXX Note: as coded today, this will only convert well formed + // entities. This may not be compatible enough. + // XXX there is a table in navigator that translates some numeric entities + // should we be doing that? If so then it needs to live in two places (bad) + // so we should add a translate numeric entity method from the parser... + char cbuf[100]; + PRInt32 index = 0; + while (index < aResult.Length()) { + // If we have the start of an entity (and it's not at the end of + // our string) then translate the entity into it's unicode value. + if ((aResult.CharAt(index++) == '&') && (index < aResult.Length())) { + PRInt32 start = index - 1; + PRUnichar e = aResult.CharAt(index); + if (e == '#') { + // Convert a numeric character reference + index++; + char* cp = cbuf; + char* limit = cp + sizeof(cbuf) - 1; + PRBool ok = PR_FALSE; + PRInt32 slen = aResult.Length(); + while ((index < slen) && (cp < limit)) { + PRUnichar e = aResult.CharAt(index); + if (e == ';') { + index++; + ok = PR_TRUE; + break; + } + if ((e >= '0') && (e <= '9')) { + *cp++ = char(e); + index++; + continue; + } + break; + } + if (!ok || (cp == cbuf)) { + continue; + } + *cp = '\0'; + if (cp - cbuf > 5) { + continue; + } + PRInt32 ch = PRInt32( ::atoi(cbuf) ); + if (ch > 65535) { + continue; + } + + // Remove entity from string and replace it with the integer + // value. + aResult.Cut(start, index - start); + aResult.Insert(PRUnichar(ch), start); + index = start + 1; + } + else if (((e >= 'A') && (e <= 'Z')) || + ((e >= 'a') && (e <= 'z'))) { + // Convert a named entity + index++; + char* cp = cbuf; + char* limit = cp + sizeof(cbuf) - 1; + *cp++ = char(e); + PRBool ok = PR_FALSE; + PRInt32 slen = aResult.Length(); + while ((index < slen) && (cp < limit)) { + PRUnichar e = aResult.CharAt(index); + if (e == ';') { + index++; + ok = PR_TRUE; + break; + } + if (((e >= '0') && (e <= '9')) || + ((e >= 'A') && (e <= 'Z')) || + ((e >= 'a') && (e <= 'z'))) { + *cp++ = char(e); + index++; + continue; + } + break; + } + if (!ok || (cp == cbuf)) { + continue; + } + *cp = '\0'; + PRInt32 ch = NS_EntityToUnicode(cbuf); + if (ch < 0) { + continue; + } + + // Remove entity from string and replace it with the integer + // value. + aResult.Cut(start, index - start); + aResult.Insert(PRUnichar(ch), start); + index = start + 1; + } + else if (e == '{') { + // Convert a script entity + // XXX write me! + } + } + } +} + +// XXX Code copied from nsHTMLContentSink. It should be shared. +nsresult +nsHTMLFragmentContentSink::AddAttributes(const nsIParserNode& aNode, + nsIContent* aContent) +{ + // Add tag attributes to the content attributes + nsAutoString k, v; + PRInt32 ac = aNode.GetAttributeCount(); + for (PRInt32 i = 0; i < ac; i++) { + // Get upper-cased key + const nsString& key = aNode.GetKeyAt(i); + k.Truncate(); + k.Append(key); + k.ToLowerCase(); + + nsIAtom* keyAtom = NS_NewAtom(k); + + if (NS_CONTENT_ATTR_NOT_THERE == + aContent->GetAttribute(kNameSpaceID_HTML, keyAtom, v)) { + // Get value and remove mandatory quotes + GetAttributeValueAt(aNode, i, v); + + // Add attribute to content + aContent->SetAttribute(kNameSpaceID_HTML, keyAtom, v, PR_FALSE); + } + NS_RELEASE(keyAtom); + } + return NS_OK; +} diff --git a/mozilla/layout/generic/nsHTMLParts.h b/mozilla/layout/generic/nsHTMLParts.h index 7b87a01b2dd..451018cf816 100644 --- a/mozilla/layout/generic/nsHTMLParts.h +++ b/mozilla/layout/generic/nsHTMLParts.h @@ -27,6 +27,7 @@ class nsIContent; class nsIDocument; class nsIHTMLContent; class nsIHTMLContentSink; +class nsIHTMLFragmentContentSink; class nsITextContent; class nsIURL; class nsString; @@ -343,6 +344,7 @@ extern nsresult NS_NewHTMLContentSink(nsIHTMLContentSink** aInstancePtrResult, nsIDocument* aDoc, nsIURL* aURL, nsIWebShell* aWebShell); +extern nsresult NS_NewHTMLFragmentContentSink(nsIHTMLFragmentContentSink** aInstancePtrResult); /** Create a new HTML reflow command */ extern nsresult diff --git a/mozilla/layout/html/base/src/nsHTMLParts.h b/mozilla/layout/html/base/src/nsHTMLParts.h index 7b87a01b2dd..451018cf816 100644 --- a/mozilla/layout/html/base/src/nsHTMLParts.h +++ b/mozilla/layout/html/base/src/nsHTMLParts.h @@ -27,6 +27,7 @@ class nsIContent; class nsIDocument; class nsIHTMLContent; class nsIHTMLContentSink; +class nsIHTMLFragmentContentSink; class nsITextContent; class nsIURL; class nsString; @@ -343,6 +344,7 @@ extern nsresult NS_NewHTMLContentSink(nsIHTMLContentSink** aInstancePtrResult, nsIDocument* aDoc, nsIURL* aURL, nsIWebShell* aWebShell); +extern nsresult NS_NewHTMLFragmentContentSink(nsIHTMLFragmentContentSink** aInstancePtrResult); /** Create a new HTML reflow command */ extern nsresult diff --git a/mozilla/layout/html/document/src/nsHTMLFragmentContentSink.cpp b/mozilla/layout/html/document/src/nsHTMLFragmentContentSink.cpp index fca778c6d8d..2f46648d4b9 100644 --- a/mozilla/layout/html/document/src/nsHTMLFragmentContentSink.cpp +++ b/mozilla/layout/html/document/src/nsHTMLFragmentContentSink.cpp @@ -16,13 +16,22 @@ * Reserved. */ #include "nsCOMPtr.h" -#include "nsIHTMLContentSink.h" +#include "nsIHTMLFragmentContentSink.h" #include "nsIParser.h" #include "nsIHTMLContent.h" #include "nsIHTMLContentContainer.h" #include "nsHTMLTokens.h" #include "nsHTMLEntities.h" #include "nsHTMLParts.h" +#include "nsIDOMComment.h" +#include "nsIDOMHTMLFormElement.h" +#include "nsIDOMDocumentFragment.h" +#include "nsVoidArray.h" +#include "nsITextContent.h" +#include "nsINameSpace.h" +#include "nsINameSpaceManager.h" +#include "nsIDocument.h" +#include "prmem.h" // // XXX THIS IS TEMPORARY CODE @@ -31,7 +40,13 @@ // at some pointe really soon! // -class nsHTMLFragmentContentSink : public nsIHTMLContentSink { +static NS_DEFINE_IID(kIContentIID, NS_ICONTENT_IID); +static NS_DEFINE_IID(kIHTMLContentSinkIID, NS_IHTML_CONTENT_SINK_IID); +static NS_DEFINE_IID(kIHTMLFragmentContentSinkIID, NS_IHTML_FRAGMENT_CONTENT_SINK_IID); +static NS_DEFINE_IID(kIDOMCommentIID, NS_IDOMCOMMENT_IID); +static NS_DEFINE_IID(kIDOMDocumentFragmentIID, NS_IDOMDOCUMENTFRAGMENT_IID); + +class nsHTMLFragmentContentSink : public nsIHTMLFragmentContentSink { public: nsHTMLFragmentContentSink(); virtual ~nsHTMLFragmentContentSink(); @@ -70,19 +85,18 @@ public: NS_IMETHOD AddProcessingInstruction(const nsIParserNode& aNode); NS_IMETHOD DoFragment(PRBool aFlag); + // nsIHTMLFragmentContentSink + NS_IMETHOD GetFragment(nsIDOMDocumentFragment** aFragment); nsIContent* GetCurrentContent(); PRInt32 PushContent(nsIContent *aContent); nsIContent* PopContent(); nsresult AddAttributes(const nsIParserNode& aNode, - nsIContent* aContent, - PRBool aIsHTML); + nsIContent* aContent); nsresult AddText(const nsString& aString); - nsresult FlushText(PRBool aCreateTextNode=PR_TRUE, - PRBool* aDidFlush=nsnull); - + nsresult FlushText(); PRBool mHitSentinel; @@ -99,8 +113,24 @@ public: }; +nsresult +NS_NewHTMLFragmentContentSink(nsIHTMLFragmentContentSink** aResult) +{ + NS_PRECONDITION(nsnull != aResult, "null ptr"); + if (nsnull == aResult) { + return NS_ERROR_NULL_POINTER; + } + nsHTMLFragmentContentSink* it; + NS_NEWXPCOM(it, nsHTMLFragmentContentSink); + if (nsnull == it) { + return NS_ERROR_OUT_OF_MEMORY; + } + return it->QueryInterface(kIHTMLFragmentContentSinkIID, (void **)aResult); +} + nsHTMLFragmentContentSink::nsHTMLFragmentContentSink() { + NS_INIT_REFCNT(); mHitSentinel = PR_FALSE; mRoot = nsnull; mParser = nsnull; @@ -132,13 +162,55 @@ nsHTMLFragmentContentSink::~nsHTMLFragmentContentSink() } } -NS_IMPL_ISUPPORTS(nsHTMLFragmentContentSink, kIHTMLContentSinkIID) +NS_IMPL_ADDREF(nsHTMLFragmentContentSink) +NS_IMPL_RELEASE(nsHTMLFragmentContentSink) + +NS_IMETHODIMP +nsHTMLFragmentContentSink::QueryInterface(REFNSIID aIID, void** aInstancePtr) +{ + if (NULL == aInstancePtr) { + return NS_ERROR_NULL_POINTER; + } + + *aInstancePtr = NULL; + + static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID); + if (aIID.Equals(kIHTMLFragmentContentSinkIID)) { + nsIHTMLFragmentContentSink* tmp = this; + *aInstancePtr = (void*) tmp; + NS_ADDREF_THIS(); + return NS_OK; + } + if (aIID.Equals(kIHTMLContentSinkIID)) { + nsIHTMLContentSink* tmp = this; + *aInstancePtr = (void*) tmp; + NS_ADDREF_THIS(); + return NS_OK; + } + if (aIID.Equals(kISupportsIID)) { + *aInstancePtr = (void*) ((nsISupports*)this); + NS_ADDREF_THIS(); + return NS_OK; + } + return NS_NOINTERFACE; +} NS_IMETHODIMP nsHTMLFragmentContentSink::WillBuildModel(void) { - return NS_OK; + nsresult result = NS_OK; + + if (nsnull == mRoot) { + nsIDOMDocumentFragment* frag; + + result = NS_NewDocumentFragment(&frag, nsnull); + if (NS_SUCCEEDED(result)) { + result = frag->QueryInterface(kIContentIID, (void**)&mRoot); + NS_RELEASE(frag); + } + } + return result; } NS_IMETHODIMP @@ -202,7 +274,7 @@ nsHTMLFragmentContentSink::CloseHTML(const nsIParserNode& aNode) NS_IMETHODIMP nsHTMLFragmentContentSink::OpenHead(const nsIParserNode& aNode) { - // XXX Not likely to get a head in the paste + // XXX Not likely to get a head in the fragment return OpenContainer(aNode); } @@ -266,6 +338,7 @@ NS_IMETHODIMP nsHTMLFragmentContentSink::OpenContainer(const nsIParserNode& aNode) { nsAutoString tag; + nsresult result = NS_OK; tag = aNode.GetText(); if (tag.EqualsIgnoreCase(kSentinelStr)) { @@ -281,25 +354,25 @@ nsHTMLFragmentContentSink::OpenContainer(const nsIParserNode& aNode) result = AddAttributes(aNode, content); if (NS_OK == result) { nsIContent *parent = GetCurrentContent(); - - if (nsnull == parent) { - parent = mRoot; - } - + + if (nsnull == parent) { + parent = mRoot; + } + parent->AppendChildTo(content, PR_FALSE); PushContent(content); } } } - return NS_OK; + return result; } NS_IMETHODIMP nsHTMLFragmentContentSink::CloseContainer(const nsIParserNode& aNode) { - if (mHitSentinel && (nsnull != GetCurrentContent()) { - nsIContent content; + if (mHitSentinel && (nsnull != GetCurrentContent())) { + nsIContent* content; FlushText(); content = PopContent(); NS_RELEASE(content); @@ -311,31 +384,30 @@ NS_IMETHODIMP nsHTMLFragmentContentSink::AddLeaf(const nsIParserNode& aNode) { nsresult result = NS_OK; - nsHTMLTag nodeType = nsHTMLTag(aNode.GetNodeType()); - switch (nodeType) { + switch (aNode.GetTokenType()) { case eToken_start: { - FlushText(); - - // Create new leaf content object - nsHTMLTag nodeType = nsHTMLTag(aNode.GetNodeType()); - nsIHTMLContent* content; - result = NS_CreateHTMLElement(&content, tag); - - if (NS_OK == result) { - result = AddAttributes(aNode, content); - if (NS_OK == result) { - nsIContent *parent = GetCurrentContent(); - - if (nsnull == parent) { - parent = mRoot; - } - - parent->AppendChildTo(content, PR_FALSE); - } - NS_RELEASE(content); - } + FlushText(); + + // Create new leaf content object + nsIHTMLContent* content; + nsAutoString tag = aNode.GetText(); + result = NS_CreateHTMLElement(&content, tag); + + if (NS_OK == result) { + result = AddAttributes(aNode, content); + if (NS_OK == result) { + nsIContent *parent = GetCurrentContent(); + + if (nsnull == parent) { + parent = mRoot; + } + + parent->AppendChildTo(content, PR_FALSE); + } + NS_RELEASE(content); + } } break; case eToken_text: @@ -343,21 +415,21 @@ nsHTMLFragmentContentSink::AddLeaf(const nsIParserNode& aNode) case eToken_newline: result = AddText(aNode.GetText()); break; - + case eToken_entity: { - nsAutoString tmp; - PRInt32 unicode = aNode.TranslateToUnicodeStr(tmp); - if (unicode < 0) { - result = AddText(aNode.GetText()); - } - else { - result = AddText(tmp); - } + nsAutoString tmp; + PRInt32 unicode = aNode.TranslateToUnicodeStr(tmp); + if (unicode < 0) { + result = AddText(aNode.GetText()); + } + else { + result = AddText(tmp); + } } break; } - + return result; } @@ -387,7 +459,7 @@ nsHTMLFragmentContentSink::AddComment(const nsIParserNode& aNode) nsIContent *parent = GetCurrentContent(); if (nsnull == parent) { - parent = mRoot; + parent = mRoot; } parent->AppendChildTo(comment, PR_FALSE); @@ -410,6 +482,17 @@ nsHTMLFragmentContentSink::DoFragment(PRBool aFlag) return NS_OK; } +NS_IMETHODIMP +nsHTMLFragmentContentSink::GetFragment(nsIDOMDocumentFragment** aFragment) +{ + if (nsnull != mRoot) { + return mRoot->QueryInterface(kIDOMDocumentFragmentIID, (void**)aFragment); + } + else { + *aFragment = nsnull; + return NS_OK; + } +} nsIContent* nsHTMLFragmentContentSink::GetCurrentContent() @@ -443,3 +526,235 @@ nsHTMLFragmentContentSink::PopContent() } return content; } + +#define NS_ACCUMULATION_BUFFER_SIZE 4096 + +nsresult +nsHTMLFragmentContentSink::AddText(const nsString& aString) +{ + PRInt32 addLen = aString.Length(); + if (0 == addLen) { + return NS_OK; + } + + // Create buffer when we first need it + if (0 == mTextSize) { + mText = (PRUnichar *) PR_MALLOC(sizeof(PRUnichar) * NS_ACCUMULATION_BUFFER_SIZE); + if (nsnull == mText) { + return NS_ERROR_OUT_OF_MEMORY; + } + mTextSize = NS_ACCUMULATION_BUFFER_SIZE; + } + + // Copy data from string into our buffer; flush buffer when it fills up + PRInt32 offset = 0; + while (0 != addLen) { + PRInt32 amount = mTextSize - mTextLength; + if (amount > addLen) { + amount = addLen; + } + if (0 == amount) { + nsresult rv = FlushText(); + if (NS_OK != rv) { + return rv; + } + } + memcpy(&mText[mTextLength], aString.GetUnicode() + offset, + sizeof(PRUnichar) * amount); + mTextLength += amount; + offset += amount; + addLen -= amount; + } + + return NS_OK; +} + +nsresult +nsHTMLFragmentContentSink::FlushText() +{ + nsresult rv = NS_OK; + if (0 != mTextLength) { + nsIContent* content; + rv = NS_NewTextNode(&content); + if (NS_OK == rv) { + + // Set the text in the text node + static NS_DEFINE_IID(kITextContentIID, NS_ITEXT_CONTENT_IID); + nsITextContent* text = nsnull; + content->QueryInterface(kITextContentIID, (void**) &text); + text->SetText(mText, mTextLength, PR_FALSE); + NS_RELEASE(text); + + // Add text to its parent + nsIContent *parent = GetCurrentContent(); + + if (nsnull == parent) { + parent = mRoot; + } + + parent->AppendChildTo(content, PR_FALSE); + + NS_RELEASE(content); + } + + mTextLength = 0; + } + + return rv; +} + +// XXX Code copied from nsHTMLContentSink. It should be shared. +static void +GetAttributeValueAt(const nsIParserNode& aNode, + PRInt32 aIndex, + nsString& aResult) +{ + // Copy value + const nsString& value = aNode.GetValueAt(aIndex); + aResult.Truncate(); + aResult.Append(value); + + // Strip quotes if present + PRUnichar first = aResult.First(); + if ((first == '\"') || (first == '\'')) { + if (aResult.Last() == first) { + aResult.Cut(0, 1); + PRInt32 pos = aResult.Length() - 1; + if (pos >= 0) { + aResult.Cut(pos, 1); + } + } else { + // Mismatched quotes - leave them in + } + } + + // Reduce any entities + // XXX Note: as coded today, this will only convert well formed + // entities. This may not be compatible enough. + // XXX there is a table in navigator that translates some numeric entities + // should we be doing that? If so then it needs to live in two places (bad) + // so we should add a translate numeric entity method from the parser... + char cbuf[100]; + PRInt32 index = 0; + while (index < aResult.Length()) { + // If we have the start of an entity (and it's not at the end of + // our string) then translate the entity into it's unicode value. + if ((aResult.CharAt(index++) == '&') && (index < aResult.Length())) { + PRInt32 start = index - 1; + PRUnichar e = aResult.CharAt(index); + if (e == '#') { + // Convert a numeric character reference + index++; + char* cp = cbuf; + char* limit = cp + sizeof(cbuf) - 1; + PRBool ok = PR_FALSE; + PRInt32 slen = aResult.Length(); + while ((index < slen) && (cp < limit)) { + PRUnichar e = aResult.CharAt(index); + if (e == ';') { + index++; + ok = PR_TRUE; + break; + } + if ((e >= '0') && (e <= '9')) { + *cp++ = char(e); + index++; + continue; + } + break; + } + if (!ok || (cp == cbuf)) { + continue; + } + *cp = '\0'; + if (cp - cbuf > 5) { + continue; + } + PRInt32 ch = PRInt32( ::atoi(cbuf) ); + if (ch > 65535) { + continue; + } + + // Remove entity from string and replace it with the integer + // value. + aResult.Cut(start, index - start); + aResult.Insert(PRUnichar(ch), start); + index = start + 1; + } + else if (((e >= 'A') && (e <= 'Z')) || + ((e >= 'a') && (e <= 'z'))) { + // Convert a named entity + index++; + char* cp = cbuf; + char* limit = cp + sizeof(cbuf) - 1; + *cp++ = char(e); + PRBool ok = PR_FALSE; + PRInt32 slen = aResult.Length(); + while ((index < slen) && (cp < limit)) { + PRUnichar e = aResult.CharAt(index); + if (e == ';') { + index++; + ok = PR_TRUE; + break; + } + if (((e >= '0') && (e <= '9')) || + ((e >= 'A') && (e <= 'Z')) || + ((e >= 'a') && (e <= 'z'))) { + *cp++ = char(e); + index++; + continue; + } + break; + } + if (!ok || (cp == cbuf)) { + continue; + } + *cp = '\0'; + PRInt32 ch = NS_EntityToUnicode(cbuf); + if (ch < 0) { + continue; + } + + // Remove entity from string and replace it with the integer + // value. + aResult.Cut(start, index - start); + aResult.Insert(PRUnichar(ch), start); + index = start + 1; + } + else if (e == '{') { + // Convert a script entity + // XXX write me! + } + } + } +} + +// XXX Code copied from nsHTMLContentSink. It should be shared. +nsresult +nsHTMLFragmentContentSink::AddAttributes(const nsIParserNode& aNode, + nsIContent* aContent) +{ + // Add tag attributes to the content attributes + nsAutoString k, v; + PRInt32 ac = aNode.GetAttributeCount(); + for (PRInt32 i = 0; i < ac; i++) { + // Get upper-cased key + const nsString& key = aNode.GetKeyAt(i); + k.Truncate(); + k.Append(key); + k.ToLowerCase(); + + nsIAtom* keyAtom = NS_NewAtom(k); + + if (NS_CONTENT_ATTR_NOT_THERE == + aContent->GetAttribute(kNameSpaceID_HTML, keyAtom, v)) { + // Get value and remove mandatory quotes + GetAttributeValueAt(aNode, i, v); + + // Add attribute to content + aContent->SetAttribute(kNameSpaceID_HTML, keyAtom, v, PR_FALSE); + } + NS_RELEASE(keyAtom); + } + return NS_OK; +}