Bug 338954, Make .innerHTML to work without DOM Ranges, r=sicking, sr=jst

This commit is contained in:
Olli.Pettay%helsinki.fi 2006-06-05 20:32:48 +00:00
parent 7c4effa11c
commit 5e667ae978
6 changed files with 240 additions and 232 deletions

View File

@ -865,6 +865,18 @@ public:
*/
static void CopyUserData(nsIDocument *aOldDocument, const nsINode *aNode);
/**
* Creates a DocumentFragment from text using a context node to resolve
* namespaces.
*
* @param aContextNode the node which is used to resolve namespaces
* @param aFragment the string which is parsed to a DocumentFragment
* @param aReturn [out] the created DocumentFragment
*/
static nsresult CreateContextualFragment(nsIDOMNode* aContextNode,
const nsAString& aFragment,
nsIDOMDocumentFragment** aReturn);
private:
static nsresult doReparentContentWrapper(nsIContent *aChild,
JSContext *cx,

View File

@ -58,7 +58,7 @@ interface nsIDocumentEncoderNodeFixup : nsISupports
nsIDOMNode fixupNode(in nsIDOMNode aNode);
};
[scriptable, uuid(ea9f3938-6ba0-11da-9531-00e08161165f)]
[scriptable, uuid(9e732561-e720-409a-a93c-6754b44b311a)]
interface nsIDocumentEncoder : nsISupports
{
// Output methods flag bits. There are a frightening number of these,
@ -220,6 +220,14 @@ interface nsIDocumentEncoder : nsISupports
*/
void setNode(in nsIDOMNode aNode);
/**
* If the container is set to a non-null value, then its
* child nodes are used for encoding, otherwise the entire
* document or range or selection or node is encoded.
* @param aContainer The node which child nodes will be encoded.
*/
void setContainerNode(in nsIDOMNode aContainer);
/**
* Documents typically have an intrinsic character set,
* but if no intrinsic value is found, the platform character set

View File

@ -76,6 +76,10 @@
#include "nsIDocShell.h"
#include "nsIDocShellTreeItem.h"
#include "nsParserCIID.h"
#include "nsIParser.h"
#include "nsIFragmentContentSink.h"
#include "nsIContentSink.h"
#include "nsHTMLParts.h"
#include "nsIParserService.h"
#include "nsIServiceManager.h"
#include "nsIAttribute.h"
@ -139,6 +143,7 @@ static NS_DEFINE_CID(kXTFServiceCID, NS_XTFSERVICE_CID);
static const char kJSStackContractID[] = "@mozilla.org/js/xpc/ContextStack;1";
static NS_DEFINE_CID(kParserServiceCID, NS_PARSERSERVICE_CID);
static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
nsIDOMScriptObjectFactory *nsContentUtils::sDOMScriptObjectFactory = nsnull;
nsIXPConnect *nsContentUtils::sXPConnect;
@ -3175,3 +3180,166 @@ nsContentUtils::CopyUserData(nsIDocument *aOldDocument, const nsINode *aNode)
nsPropertyTable *table = aOldDocument->PropertyTable();
table->Enumerate(aNode, DOM_USER_DATA, CopyData, table);
}
/* static */
nsresult
nsContentUtils::CreateContextualFragment(nsIDOMNode* aContextNode,
const nsAString& aFragment,
nsIDOMDocumentFragment** aReturn)
{
NS_ENSURE_ARG(aContextNode);
*aReturn = nsnull;
// Create a new parser for this entire operation
nsresult rv;
nsCOMPtr<nsIParser> parser = do_CreateInstance(kCParserCID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDocument> document;
nsCOMPtr<nsIDOMDocument> domDocument;
aContextNode->GetOwnerDocument(getter_AddRefs(domDocument));
document = do_QueryInterface(domDocument);
// If we don't have a document here, we can't get the right security context
// for compiling event handlers... so just bail out.
NS_ENSURE_TRUE(document, NS_ERROR_NOT_AVAILABLE);
nsVoidArray tagStack;
nsCOMPtr<nsIDOMNode> parent = aContextNode;
while (parent && (parent != domDocument)) {
PRUint16 nodeType;
parent->GetNodeType(&nodeType);
if (nsIDOMNode::ELEMENT_NODE == nodeType) {
nsAutoString tagName, uriStr;
parent->GetNodeName(tagName);
// see if we need to add xmlns declarations
nsCOMPtr<nsIContent> content = do_QueryInterface(parent);
if (!content) {
rv = NS_ERROR_FAILURE;
break;
}
PRUint32 count = content->GetAttrCount();
PRBool setDefaultNamespace = PR_FALSE;
if (count > 0) {
PRUint32 index;
nsAutoString nameStr, prefixStr, valueStr;
for (index = 0; index < count; index++) {
const nsAttrName* name = content->GetAttrNameAt(index);
if (name->NamespaceEquals(kNameSpaceID_XMLNS)) {
content->GetAttr(kNameSpaceID_XMLNS, name->LocalName(), uriStr);
// really want something like nsXMLContentSerializer::SerializeAttr
tagName.Append(NS_LITERAL_STRING(" xmlns")); // space important
if (name->GetPrefix()) {
tagName.Append(PRUnichar(':'));
name->LocalName()->ToString(nameStr);
tagName.Append(nameStr);
} else {
setDefaultNamespace = PR_TRUE;
}
tagName.Append(NS_LITERAL_STRING("=\"") + uriStr +
NS_LITERAL_STRING("\""));
}
}
}
if (!setDefaultNamespace) {
nsINodeInfo* info = content->NodeInfo();
if (!info->GetPrefixAtom() &&
info->NamespaceID() != kNameSpaceID_None) {
// We have no namespace prefix, but have a namespace ID. Push
// default namespace attr in, so that our kids will be in our
// namespace.
nsAutoString uri;
info->GetNamespaceURI(uri);
tagName.Append(NS_LITERAL_STRING(" xmlns=\"") + uri +
NS_LITERAL_STRING("\""));
}
}
// XXX Wish we didn't have to allocate here
PRUnichar* name = ToNewUnicode(tagName);
if (name) {
tagStack.AppendElement(name);
nsCOMPtr<nsIDOMNode> temp = parent;
rv = temp->GetParentNode(getter_AddRefs(parent));
if (NS_FAILED(rv)) {
break;
}
} else {
rv = NS_ERROR_OUT_OF_MEMORY;
break;
}
} else {
nsCOMPtr<nsIDOMNode> temp = parent;
rv = temp->GetParentNode(getter_AddRefs(parent));
if (NS_FAILED(rv)) {
break;
}
}
}
if (NS_SUCCEEDED(rv)) {
nsCAutoString contentType;
PRBool bCaseSensitive = PR_TRUE;
nsAutoString buf;
document->GetContentType(buf);
LossyCopyUTF16toASCII(buf, contentType);
bCaseSensitive = document->IsCaseSensitive();
nsCOMPtr<nsIHTMLDocument> htmlDoc(do_QueryInterface(domDocument));
PRBool bHTML = htmlDoc && !bCaseSensitive;
nsCOMPtr<nsIFragmentContentSink> sink;
if (bHTML) {
rv = NS_NewHTMLFragmentContentSink(getter_AddRefs(sink));
} else {
rv = NS_NewXMLFragmentContentSink(getter_AddRefs(sink));
}
if (NS_SUCCEEDED(rv)) {
sink->SetTargetDocument(document);
nsCOMPtr<nsIContentSink> contentsink(do_QueryInterface(sink));
parser->SetContentSink(contentsink);
nsDTDMode mode = eDTDMode_autodetect;
if (bHTML) {
switch (htmlDoc->GetCompatibilityMode()) {
case eCompatibility_NavQuirks:
mode = eDTDMode_quirks;
break;
case eCompatibility_AlmostStandards:
mode = eDTDMode_almost_standards;
break;
case eCompatibility_FullStandards:
mode = eDTDMode_full_standards;
break;
default:
NS_NOTREACHED("unknown mode");
break;
}
} else {
mode = eDTDMode_full_standards;
}
rv = parser->ParseFragment(aFragment, nsnull, tagStack,
!bHTML, contentType, mode);
if (NS_SUCCEEDED(rv)) {
rv = sink->GetFragment(aReturn);
}
}
}
// XXX Ick! Delete strings we allocated above.
PRInt32 count = tagStack.Count();
for (PRInt32 i = 0; i < count; i++) {
PRUnichar* str = (PRUnichar*)tagStack.ElementAt(i);
if (str) {
nsCRT::free(str);
}
}
return NS_OK;
}

View File

@ -91,33 +91,17 @@ public:
nsDocumentEncoder();
virtual ~nsDocumentEncoder();
NS_IMETHOD Init(nsIDOMDocument* aDocument,
const nsAString& aMimeType,
PRUint32 aFlags);
/* Interfaces for addref and release and queryinterface */
NS_DECL_ISUPPORTS
// Inherited methods from nsIDocumentEncoder
NS_IMETHOD SetSelection(nsISelection* aSelection);
NS_IMETHOD SetRange(nsIDOMRange* aRange);
NS_IMETHOD SetNode(nsIDOMNode* aNode);
NS_IMETHOD SetWrapColumn(PRUint32 aWC);
NS_IMETHOD SetCharset(const nsACString& aCharset);
NS_IMETHOD GetMimeType(nsAString& aMimeType);
NS_IMETHOD EncodeToStream(nsIOutputStream* aStream);
NS_IMETHOD EncodeToString(nsAString& aOutputString);
NS_IMETHOD EncodeToStringWithContext(nsAString& aContextString,
nsAString& aInfoString,
nsAString& aEncodedString);
NS_IMETHOD SetNodeFixup(nsIDocumentEncoderNodeFixup *aFixup);
NS_DECL_NSIDOCUMENTENCODER
protected:
void Initialize();
nsresult SerializeNodeStart(nsIDOMNode* aNode, PRInt32 aStartOffset,
PRInt32 aEndOffset, nsAString& aStr);
nsresult SerializeToStringRecursive(nsIDOMNode* aNode,
nsAString& aStr);
nsAString& aStr,
PRBool aDontSerializeRoot);
nsresult SerializeNodeEnd(nsIDOMNode* aNode, nsAString& aStr);
nsresult SerializeRangeToString(nsIDOMRange *aRange,
nsAString& aOutputString);
@ -162,6 +146,7 @@ protected:
nsAutoVoidArray mEndOffsets;
PRPackedBool mHaltRangeHint;
PRPackedBool mIsCopying; // Set to PR_TRUE only while copying
PRPackedBool mNodeIsContainer;
};
NS_IMPL_ADDREF(nsDocumentEncoder)
@ -188,6 +173,7 @@ void nsDocumentEncoder::Initialize()
mStartRootIndex = 0;
mEndRootIndex = 0;
mHaltRangeHint = PR_FALSE;
mNodeIsContainer = PR_FALSE;
}
nsDocumentEncoder::~nsDocumentEncoder()
@ -239,10 +225,19 @@ nsDocumentEncoder::SetRange(nsIDOMRange* aRange)
NS_IMETHODIMP
nsDocumentEncoder::SetNode(nsIDOMNode* aNode)
{
mNodeIsContainer = PR_FALSE;
mNode = aNode;
return NS_OK;
}
NS_IMETHODIMP
nsDocumentEncoder::SetContainerNode(nsIDOMNode *aContainer)
{
mNodeIsContainer = PR_TRUE;
mNode = aContainer;
return NS_OK;
}
NS_IMETHODIMP
nsDocumentEncoder::SetCharset(const nsACString& aCharset)
{
@ -347,10 +342,14 @@ nsDocumentEncoder::SerializeNodeEnd(nsIDOMNode* aNode,
nsresult
nsDocumentEncoder::SerializeToStringRecursive(nsIDOMNode* aNode,
nsAString& aStr)
nsAString& aStr,
PRBool aDontSerializeRoot)
{
nsresult rv = SerializeNodeStart(aNode, 0, -1, aStr);
NS_ENSURE_SUCCESS(rv, rv);
nsresult rv = NS_OK;
if (!aDontSerializeRoot) {
rv = SerializeNodeStart(aNode, 0, -1, aStr);
NS_ENSURE_SUCCESS(rv, rv);
}
PRBool hasChildren = PR_FALSE;
@ -370,13 +369,15 @@ nsDocumentEncoder::SerializeToStringRecursive(nsIDOMNode* aNode,
rv = childNodes->Item(index, getter_AddRefs(child));
NS_ENSURE_SUCCESS(rv, rv);
rv = SerializeToStringRecursive(child, aStr);
rv = SerializeToStringRecursive(child, aStr, PR_FALSE);
NS_ENSURE_SUCCESS(rv, rv);
}
}
rv = SerializeNodeEnd(aNode, aStr);
NS_ENSURE_SUCCESS(rv, rv);
if (!aDontSerializeRoot) {
rv = SerializeNodeEnd(aNode, aStr);
NS_ENSURE_SUCCESS(rv, rv);
}
return FlushText(aStr, PR_FALSE);
}
@ -656,7 +657,7 @@ nsDocumentEncoder::SerializeRangeNodes(nsIDOMRange* aRange,
{
// node is completely contained in range. Serialize the whole subtree
// rooted by this node.
rv = SerializeToStringRecursive(aNode, aString);
rv = SerializeToStringRecursive(aNode, aString, PR_FALSE);
NS_ENSURE_SUCCESS(rv, rv);
}
else
@ -735,7 +736,7 @@ nsDocumentEncoder::SerializeRangeNodes(nsIDOMRange* aRange,
if ((j==startOffset) || (j==endOffset-1))
rv = SerializeRangeNodes(aRange, childAsNode, aString, aDepth+1);
else
rv = SerializeToStringRecursive(childAsNode, aString);
rv = SerializeToStringRecursive(childAsNode, aString, PR_FALSE);
NS_ENSURE_SUCCESS(rv, rv);
}
@ -912,7 +913,7 @@ nsDocumentEncoder::EncodeToString(nsAString& aOutputString)
mRange = nsnull;
} else if (mNode) {
rv = SerializeToStringRecursive(mNode, aOutputString);
rv = SerializeToStringRecursive(mNode, aOutputString, mNodeIsContainer);
mNode = nsnull;
} else {
nsCOMPtr<nsIDOMDocument> domdoc(do_QueryInterface(mDocument));
@ -921,7 +922,7 @@ nsDocumentEncoder::EncodeToString(nsAString& aOutputString)
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIDOMNode> doc(do_QueryInterface(mDocument));
rv = SerializeToStringRecursive(doc, aOutputString);
rv = SerializeToStringRecursive(doc, aOutputString, PR_FALSE);
}
}

View File

@ -50,28 +50,13 @@
#include "nsIDOMDocumentFragment.h"
#include "nsIContent.h"
#include "nsIDocument.h"
#include "nsVoidArray.h"
#include "nsIDOMText.h"
#include "nsDOMError.h"
#include "nsIContentIterator.h"
#include "nsIDOMNodeList.h"
#include "nsIParser.h"
#include "nsIComponentManager.h"
#include "nsParserCIID.h"
#include "nsIFragmentContentSink.h"
#include "nsIContentSink.h"
#include "nsIEnumerator.h"
#include "nsIHTMLDocument.h"
#include "nsCRT.h"
#include "nsAttrName.h"
// XXX Temporary inclusion to deal with fragment parsing
#include "nsHTMLParts.h"
#include "nsContentUtils.h"
static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
PRMonitor* nsRange::mMonitor = nsnull;
nsVoidArray* nsRange::mStartAncestors = nsnull;
nsVoidArray* nsRange::mEndAncestors = nsnull;
@ -2385,161 +2370,13 @@ nsRange::TextOwnerChanged(nsIContent* aTextNode, const nsVoidArray *aRangeList,
// nsIDOMNSRange interface
NS_IMETHODIMP
nsRange::CreateContextualFragment(const nsAString& aFragment,
nsRange::CreateContextualFragment(const nsAString& aFragment,
nsIDOMDocumentFragment** aReturn)
{
if (!mIsPositioned) {
return NS_ERROR_FAILURE;
}
// Create a new parser for this entire operation
nsresult result;
nsCOMPtr<nsIParser> parser = do_CreateInstance(kCParserCID, &result);
NS_ENSURE_SUCCESS(result, result);
nsCOMPtr<nsIDocument> document;
nsCOMPtr<nsIDOMDocument> domDocument;
result = mStartParent->GetOwnerDocument(getter_AddRefs(domDocument));
if (domDocument && NS_SUCCEEDED(result)) {
document = do_QueryInterface(domDocument, &result);
}
// If we don't have a document here, we can't get the right security context
// for compiling event handlers... so just bail out.
NS_ENSURE_TRUE(document, NS_ERROR_NOT_AVAILABLE);
nsVoidArray tagStack;
nsCOMPtr<nsIDOMNode> parent = mStartParent;
while (parent &&
(parent != domDocument) &&
NS_SUCCEEDED(result)) {
PRUint16 nodeType;
parent->GetNodeType(&nodeType);
if (nsIDOMNode::ELEMENT_NODE == nodeType) {
nsAutoString tagName, uriStr;
parent->GetNodeName(tagName);
// see if we need to add xmlns declarations
nsCOMPtr<nsIContent> content( do_QueryInterface(parent) );
PRUint32 count = content->GetAttrCount();
PRBool setDefaultNamespace = PR_FALSE;
if (count > 0) {
PRUint32 index;
nsAutoString nameStr, prefixStr, valueStr;
for (index = 0; index < count; index++) {
const nsAttrName* name = content->GetAttrNameAt(index);
if (name->NamespaceEquals(kNameSpaceID_XMLNS)) {
content->GetAttr(kNameSpaceID_XMLNS, name->LocalName(), uriStr);
// really want something like nsXMLContentSerializer::SerializeAttr()
tagName.Append(NS_LITERAL_STRING(" xmlns")); // space important
if (name->GetPrefix()) {
tagName.Append(PRUnichar(':'));
name->LocalName()->ToString(nameStr);
tagName.Append(nameStr);
}
else {
setDefaultNamespace = PR_TRUE;
}
tagName.Append(NS_LITERAL_STRING("=\"") + uriStr + NS_LITERAL_STRING("\""));
}
}
}
if (!setDefaultNamespace) {
nsINodeInfo* info = content->NodeInfo();
if (!info->GetPrefixAtom() &&
info->NamespaceID() != kNameSpaceID_None) {
// We have no namespace prefix, but have a namespace ID. Push
// default namespace attr in, so that our kids will be in our
// namespace.
nsAutoString uri;
info->GetNamespaceURI(uri);
tagName.Append(NS_LITERAL_STRING(" xmlns=\"") + uri +
NS_LITERAL_STRING("\""));
}
}
// XXX Wish we didn't have to allocate here
PRUnichar* name = ToNewUnicode(tagName);
if (name) {
tagStack.AppendElement(name);
nsCOMPtr<nsIDOMNode> temp = parent;
result = temp->GetParentNode(getter_AddRefs(parent));
}
else {
result = NS_ERROR_OUT_OF_MEMORY;
}
}
else {
nsCOMPtr<nsIDOMNode> temp = parent;
result = temp->GetParentNode(getter_AddRefs(parent));
}
}
if (NS_SUCCEEDED(result)) {
nsCAutoString contentType;
PRBool bCaseSensitive = PR_TRUE;
nsAutoString buf;
document->GetContentType(buf);
LossyCopyUTF16toASCII(buf, contentType);
bCaseSensitive = document->IsCaseSensitive();
nsCOMPtr<nsIHTMLDocument> htmlDoc(do_QueryInterface(domDocument));
PRBool bHTML = htmlDoc && !bCaseSensitive;
nsCOMPtr<nsIFragmentContentSink> sink;
if (bHTML) {
result = NS_NewHTMLFragmentContentSink(getter_AddRefs(sink));
} else {
result = NS_NewXMLFragmentContentSink(getter_AddRefs(sink));
}
if (NS_SUCCEEDED(result)) {
sink->SetTargetDocument(document);
nsCOMPtr<nsIContentSink> contentsink( do_QueryInterface(sink) );
parser->SetContentSink(contentsink);
nsDTDMode mode = eDTDMode_autodetect;
if (bHTML) {
switch (htmlDoc->GetCompatibilityMode()) {
case eCompatibility_NavQuirks:
mode = eDTDMode_quirks;
break;
case eCompatibility_AlmostStandards:
mode = eDTDMode_almost_standards;
break;
case eCompatibility_FullStandards:
mode = eDTDMode_full_standards;
break;
default:
NS_NOTREACHED("unknown mode");
break;
}
} else {
mode = eDTDMode_full_standards;
}
result = parser->ParseFragment(aFragment, (void*)0,
tagStack,
!bHTML, contentType, mode);
if (NS_SUCCEEDED(result)) {
result = sink->GetFragment(aReturn);
}
}
}
// XXX Ick! Delete strings we allocated above.
PRInt32 count = tagStack.Count();
for (PRInt32 i = 0; i < count; i++) {
PRUnichar* str = (PRUnichar*)tagStack.ElementAt(i);
if (str) {
nsCRT::free(str);
}
}
return result;
return
mIsPositioned
? nsContentUtils::CreateContextualFragment(mStartParent, aFragment, aReturn)
: NS_ERROR_FAILURE;
}
NS_IMETHODIMP

View File

@ -888,45 +888,25 @@ nsGenericHTMLElement::GetInnerHTML(nsAString& aInnerHTML)
}
NS_ENSURE_TRUE(docEncoder, NS_ERROR_FAILURE);
nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(doc);
docEncoder->Init(domDoc, contentType,
nsIDocumentEncoder::OutputEncodeBasicEntities |
// Output DOM-standard newlines
nsIDocumentEncoder::OutputLFLineBreak |
// Don't do linebreaking that's not present in the source
nsIDocumentEncoder::OutputRaw);
nsCOMPtr<nsIDOMRange> range(new nsRange);
NS_ENSURE_TRUE(range, NS_ERROR_OUT_OF_MEMORY);
rv = range->SelectNodeContents(thisNode);
rv = docEncoder->Init(domDoc, contentType,
nsIDocumentEncoder::OutputEncodeBasicEntities |
// Output DOM-standard newlines
nsIDocumentEncoder::OutputLFLineBreak |
// Don't do linebreaking that's not present in the source
nsIDocumentEncoder::OutputRaw);
NS_ENSURE_SUCCESS(rv, rv);
docEncoder->SetRange(range);
docEncoder->EncodeToString(aInnerHTML);
return rv;
docEncoder->SetContainerNode(thisNode);
return docEncoder->EncodeToString(aInnerHTML);
}
nsresult
nsGenericHTMLElement::SetInnerHTML(const nsAString& aInnerHTML)
{
nsresult rv = NS_OK;
nsCOMPtr<nsIDOMRange> range = new nsRange;
NS_ENSURE_TRUE(range, NS_ERROR_OUT_OF_MEMORY);
nsCOMPtr<nsIDOMNSRange> nsrange(do_QueryInterface(range, &rv));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDOMNode> thisNode(do_QueryInterface(NS_STATIC_CAST(nsIContent *,
this)));
rv = range->SelectNodeContents(thisNode);
NS_ENSURE_SUCCESS(rv, rv);
rv = range->DeleteContents();
NS_ENSURE_SUCCESS(rv, rv);
// Remove childnodes
nsNode3Tearoff::SetTextContent(this, EmptyString());
nsCOMPtr<nsIDOMDocumentFragment> df;
@ -949,8 +929,10 @@ nsGenericHTMLElement::SetInnerHTML(const nsAString& aInnerHTML)
loader->SetEnabled(PR_FALSE);
}
rv = nsrange->CreateContextualFragment(aInnerHTML, getter_AddRefs(df));
nsCOMPtr<nsIDOMNode> thisNode(do_QueryInterface(NS_STATIC_CAST(nsIContent *,
this)));
nsresult rv = nsContentUtils::CreateContextualFragment(thisNode, aInnerHTML,
getter_AddRefs(df));
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIDOMNode> tmpNode;
rv = thisNode->AppendChild(df, getter_AddRefs(tmpNode));