From 8247e85525d63f4a7fa1e9e48ee642dec38e6673 Mon Sep 17 00:00:00 2001 From: "jfrancis%netscape.com" Date: Fri, 22 Nov 2002 03:30:59 +0000 Subject: [PATCH] fix for 142855: cf_html paste functionality. r=brade,cmanske; sr=kin git-svn-id: svn://10.0.0.236/trunk@134304 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/content/base/public/nsContentCID.h | 4 + mozilla/content/build/nsContentCID.h | 4 + mozilla/content/build/nsContentModule.cpp | 6 + .../src/nsHTMLFragmentContentSink.cpp | 30 +- mozilla/editor/libeditor/base/nsEditor.cpp | 55 ++- mozilla/editor/libeditor/base/nsEditor.h | 3 + .../libeditor/html/nsHTMLDataTransfer.cpp | 462 +++++++++++++----- mozilla/editor/libeditor/html/nsHTMLEditor.h | 5 + .../public/nsIHTMLFragmentContentSink.h | 1 + .../public/nsIHTMLFragmentContentSink.h | 1 + 10 files changed, 457 insertions(+), 114 deletions(-) diff --git a/mozilla/content/base/public/nsContentCID.h b/mozilla/content/base/public/nsContentCID.h index cf8fc73dffd..c97a0dfd1d1 100644 --- a/mozilla/content/base/public/nsContentCID.h +++ b/mozilla/content/base/public/nsContentCID.h @@ -240,6 +240,10 @@ #define NS_HTMLFRAGMENTSINK_CID \ { 0xd4f2b600, 0xb5c1, 0x11d6, { 0xb4, 0x83, 0xcc, 0x97, 0xc6, 0x3e, 0x56, 0x7c } } +// {13111d00-ce81-11d6-8082-ecf3665af67c} +#define NS_HTMLFRAGMENTSINK2_CID \ +{ 0x13111d00, 0xce81, 0x11d6, { 0x80, 0x82, 0xec, 0xf3, 0x66, 0x5a, 0xf6, 0x7c } } + // {3986B301-097C-11d3-BF87-00105A1B0627} #define NS_XULPOPUPLISTENER_CID \ { 0x3986b301, 0x97c, 0x11d3, { 0xbf, 0x87, 0x0, 0x10, 0x5a, 0x1b, 0x6, 0x27 } } diff --git a/mozilla/content/build/nsContentCID.h b/mozilla/content/build/nsContentCID.h index cf8fc73dffd..c97a0dfd1d1 100644 --- a/mozilla/content/build/nsContentCID.h +++ b/mozilla/content/build/nsContentCID.h @@ -240,6 +240,10 @@ #define NS_HTMLFRAGMENTSINK_CID \ { 0xd4f2b600, 0xb5c1, 0x11d6, { 0xb4, 0x83, 0xcc, 0x97, 0xc6, 0x3e, 0x56, 0x7c } } +// {13111d00-ce81-11d6-8082-ecf3665af67c} +#define NS_HTMLFRAGMENTSINK2_CID \ +{ 0x13111d00, 0xce81, 0x11d6, { 0x80, 0x82, 0xec, 0xf3, 0x66, 0x5a, 0xf6, 0x7c } } + // {3986B301-097C-11d3-BF87-00105A1B0627} #define NS_XULPOPUPLISTENER_CID \ { 0x3986b301, 0x97c, 0x11d3, { 0xbf, 0x87, 0x0, 0x10, 0x5a, 0x1b, 0x6, 0x27 } } diff --git a/mozilla/content/build/nsContentModule.cpp b/mozilla/content/build/nsContentModule.cpp index f345a09e017..40e3d70d858 100644 --- a/mozilla/content/build/nsContentModule.cpp +++ b/mozilla/content/build/nsContentModule.cpp @@ -367,6 +367,7 @@ MAKE_CTOR(CreateXMLContentSerializer, nsIContentSerializer, NS_NewXML MAKE_CTOR(CreateHTMLContentSerializer, nsIContentSerializer, NS_NewHTMLContentSerializer) MAKE_CTOR(CreatePlainTextSerializer, nsIContentSerializer, NS_NewPlainTextSerializer) MAKE_CTOR(CreateHTMLFragmentSink, nsIHTMLFragmentContentSink, NS_NewHTMLFragmentContentSink) +MAKE_CTOR(CreateHTMLFragmentSink2, nsIHTMLFragmentContentSink, NS_NewHTMLFragmentContentSink2) MAKE_CTOR(CreateSanitizingHTMLSerializer, nsIContentSerializer, NS_NewSanitizingHTMLSerializer) MAKE_CTOR(CreateXBLService, nsIXBLService, NS_NewXBLService) MAKE_CTOR(CreateBindingManager, nsIBindingManager, NS_NewBindingManager) @@ -772,6 +773,11 @@ static const nsModuleComponentInfo gComponents[] = { NS_HTMLFRAGMENTSINK_CONTRACTID, CreateHTMLFragmentSink }, + { "html fragment sink 2", + NS_HTMLFRAGMENTSINK2_CID, + NS_HTMLFRAGMENTSINK2_CONTRACTID, + CreateHTMLFragmentSink2 }, + { "HTML sanitizing content serializer", MOZ_SANITIZINGHTMLSERIALIZER_CID, MOZ_SANITIZINGHTMLSERIALIZER_CONTRACTID, diff --git a/mozilla/content/html/document/src/nsHTMLFragmentContentSink.cpp b/mozilla/content/html/document/src/nsHTMLFragmentContentSink.cpp index 13a0e9d60f4..f011c1d72d5 100644 --- a/mozilla/content/html/document/src/nsHTMLFragmentContentSink.cpp +++ b/mozilla/content/html/document/src/nsHTMLFragmentContentSink.cpp @@ -164,6 +164,35 @@ public: nsCOMPtr mNodeInfoManager; }; +class nsHTMLFragmentContentSink2 : public nsHTMLFragmentContentSink +{ +public: + nsHTMLFragmentContentSink2() { mHitSentinel = PR_TRUE; mSeenBody = PR_FALSE;} + virtual ~nsHTMLFragmentContentSink2() {} + NS_IMETHODIMP OpenHead(const nsIParserNode& aNode) { return OpenContainer(aNode); } + NS_IMETHODIMP CloseHead(const nsIParserNode& aNode) { return CloseContainer(aNode); } +}; + +nsresult +NS_NewHTMLFragmentContentSink2(nsIHTMLFragmentContentSink** aResult) +{ + NS_PRECONDITION(nsnull != aResult, "null ptr"); + if (nsnull == aResult) { + return NS_ERROR_NULL_POINTER; + } + nsHTMLFragmentContentSink2* it; + NS_NEWXPCOM(it, nsHTMLFragmentContentSink2); + if (nsnull == it) { + return NS_ERROR_OUT_OF_MEMORY; + } + nsresult rv = it->Init(); + if (NS_FAILED(rv)) { + delete it; + return rv; + } + + return it->QueryInterface(NS_GET_IID(nsIHTMLFragmentContentSink), (void **)aResult); +} nsresult NS_NewHTMLFragmentContentSink(nsIHTMLFragmentContentSink** aResult) @@ -386,7 +415,6 @@ nsHTMLFragmentContentSink::CloseHTML() NS_IMETHODIMP nsHTMLFragmentContentSink::OpenHead(const nsIParserNode& aNode) { - // XXX Not likely to get a head in the fragment return NS_OK; } diff --git a/mozilla/editor/libeditor/base/nsEditor.cpp b/mozilla/editor/libeditor/base/nsEditor.cpp index 1fe7d85525d..e1903c3a7d1 100644 --- a/mozilla/editor/libeditor/base/nsEditor.cpp +++ b/mozilla/editor/libeditor/base/nsEditor.cpp @@ -48,6 +48,7 @@ #include "nsIDOMElement.h" #include "nsIDOMAttr.h" #include "nsIDOMNode.h" +#include "nsIDOMDocumentFragment.h" #include "nsIDOMNamedNodeMap.h" #include "nsIDOMNodeList.h" #include "nsIDOMRange.h" @@ -5118,4 +5119,56 @@ nsEditor::RemoveAttributeOrEquivalent(nsIDOMElement * aElement, { return RemoveAttribute(aElement, aAttribute); } - +#if DEBUG_JOE +void +nsEditor::DumpNode(nsIDOMNode *aNode, PRInt32 indent) +{ + PRInt32 i; + for (i=0; i element = do_QueryInterface(aNode); + nsCOMPtr docfrag = do_QueryInterface(aNode); + + if (element || docfrag) + { + if (element) + { + nsAutoString tag; + char ctag[40]; + element->GetTagName(tag); + tag.ToCString(ctag,40); + ctag[40]=0; + printf("<%s>\n", ctag); + } + else + { + printf("\n"); + } + nsCOMPtr childList; + aNode->GetChildNodes(getter_AddRefs(childList)); + if (!childList) return NS_ERROR_NULL_POINTER; + PRUint32 numChildren; + childList->GetLength(&numChildren); + nsCOMPtr child, tmp; + aNode->GetFirstChild(getter_AddRefs(child)); + for (i=0; iGetNextSibling(getter_AddRefs(tmp)); + child = tmp; + } + } + else if (IsTextNode(aNode)) + { + nsCOMPtr textNode = do_QueryInterface(aNode); + nsAutoString str; + textNode->GetData(str); + char theText[30], *c; + str.ToCString(theText,30); + theText[30]=0; + while (c=strchr(theText,'\n')) (*c) = ' '; + printf(" %s\n", theText); + } +} +#endif diff --git a/mozilla/editor/libeditor/base/nsEditor.h b/mozilla/editor/libeditor/base/nsEditor.h index d11c584fe68..0d8f57905da 100644 --- a/mozilla/editor/libeditor/base/nsEditor.h +++ b/mozilla/editor/libeditor/base/nsEditor.h @@ -496,6 +496,9 @@ public: static nsresult GetStartNodeAndOffset(nsISelection *aSelection, nsCOMPtr *outStartNode, PRInt32 *outStartOffset); static nsresult GetEndNodeAndOffset(nsISelection *aSelection, nsCOMPtr *outEndNode, PRInt32 *outEndOffset); +#if DEBUG_JOE + static void DumpNode(nsIDOMNode *aNode, PRInt32 indent=0); +#endif // Helpers to add a node to the selection. // Used by table cell selection methods diff --git a/mozilla/editor/libeditor/html/nsHTMLDataTransfer.cpp b/mozilla/editor/libeditor/html/nsHTMLDataTransfer.cpp index 9b9843475ea..adc21d6e6a2 100644 --- a/mozilla/editor/libeditor/html/nsHTMLDataTransfer.cpp +++ b/mozilla/editor/libeditor/html/nsHTMLDataTransfer.cpp @@ -103,6 +103,8 @@ #include "nsInternetCiter.h" #include "nsXPCOM.h" #include "nsISupportsPrimitives.h" +#include "nsLinebreakConverter.h" +#include "nsIHTMLFragmentContentSink.h" // netwerk #include "nsIURI.h" @@ -123,6 +125,7 @@ const PRUnichar nbsp = 160; static NS_DEFINE_CID(kCRangeCID, NS_RANGE_CID); static NS_DEFINE_CID(kPrefServiceCID, NS_PREF_CID); +static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID); // Drag & Drop, Clipboard Support static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID); @@ -132,6 +135,10 @@ static NS_DEFINE_CID(kCHTMLFormatConverterCID, NS_HTMLFORMATCONVERTER_CID); #define kHTMLContext "text/_moz_htmlcontext" #define kHTMLInfo "text/_moz_htmlinfo" +// some little helpers +static PRInt32 FindPositiveIntegerAfterString(const char *aLeadingString, nsCString &aCStr); +static nsresult RemoveFragComments(nsCString &theStr); +static void RemoveBodyAndHead(nsIDOMNode *aNode); #if defined(NS_DEBUG) && defined(DEBUG_buster) static PRBool gNoisy = PR_FALSE; @@ -381,7 +388,7 @@ nsHTMLEditor::InsertHTMLWithCharsetAndContext(const nsAString & aInputString, // this is because if there is a br at end of what we paste, it will make // the invisible br visible. nsWSRunObject wsObj(this, parentNode, offsetOfNewNode); - if ( nsTextEditUtils::IsBreak(wsObj.mEndReasonNode) && + if (nsTextEditUtils::IsBreak(wsObj.mEndReasonNode) && !IsVisBreak(wsObj.mEndReasonNode) ) { res = DeleteNode(wsObj.mEndReasonNode); @@ -758,31 +765,136 @@ nsHTMLEditor::StripFormattingNodes(nsIDOMNode *aNode, PRBool aListOnly) } NS_IMETHODIMP nsHTMLEditor::PrepareTransferable(nsITransferable **transferable) +{ + return NS_OK; +} + +NS_IMETHODIMP nsHTMLEditor::PrepareHTMLTransferable(nsITransferable **aTransferable, + PRBool aHavePrivFlavor) { // Create generic Transferable for getting the data nsresult rv = nsComponentManager::CreateInstance(kCTransferableCID, nsnull, NS_GET_IID(nsITransferable), - (void**)transferable); + (void**)aTransferable); if (NS_FAILED(rv)) return rv; // Get the nsITransferable interface for getting the data from the clipboard - if (transferable) + if (aTransferable) { // Create the desired DataFlavor for the type of data // we want to get out of the transferable if ((mFlags & eEditorPlaintextMask) == 0) // This should only happen in html editors, not plaintext { - (*transferable)->AddDataFlavor(kHTMLMime); - (*transferable)->AddDataFlavor(kFileMime); + if (!aHavePrivFlavor) + { + (*aTransferable)->AddDataFlavor(kNativeHTMLMime); + } + (*aTransferable)->AddDataFlavor(kHTMLMime); + (*aTransferable)->AddDataFlavor(kFileMime); //(*transferable)->AddDataFlavor(kJPEGImageMime); } - (*transferable)->AddDataFlavor(kUnicodeMime); + (*aTransferable)->AddDataFlavor(kUnicodeMime); } return NS_OK; } +PRInt32 +FindPositiveIntegerAfterString(const char *aLeadingString, nsCString &aCStr) +{ + // first obtain offsets from cfhtml str + PRInt32 numFront = aCStr.Find(aLeadingString); + if (numFront == -1) + return -1; + numFront += nsCRT::strlen(aLeadingString); + + PRInt32 numBack = aCStr.FindCharInSet(CRLF, numFront); + if (numBack == -1) + return -1; + + nsCAutoString numStr(Substring(aCStr, numFront, numBack-numFront)); + PRInt32 errorCode; + return numStr.ToInteger(&errorCode); +} + +nsresult +RemoveFragComments(nsCString & aStr) +{ + // remove the StartFragment/EndFragment comments from the str, if present + PRInt32 startCommentIndx = aStr.Find("", PR_FALSE, startCommentIndx); + if (startCommentEnd > startCommentIndx) + aStr.Cut(startCommentIndx, (startCommentEnd+3)-startCommentIndx); + } + PRInt32 endCommentIndx = aStr.Find("", PR_FALSE, endCommentIndx); + if (endCommentEnd > endCommentIndx) + aStr.Cut(endCommentIndx, (endCommentEnd+3)-endCommentIndx); + } + return NS_OK; +} + +nsresult +nsHTMLEditor::ParseCFHTML(nsCString & aCfhtml, PRUnichar **aStuffToPaste, PRUnichar **aCfcontext) +{ + // first obtain offsets from cfhtml str + PRInt32 startHTML = FindPositiveIntegerAfterString("StartHTML:", aCfhtml); + PRInt32 endHTML = FindPositiveIntegerAfterString("EndHTML:", aCfhtml); + PRInt32 startFragment = FindPositiveIntegerAfterString("StartFragment:", aCfhtml); + PRInt32 endFragment = FindPositiveIntegerAfterString("EndFragment:", aCfhtml); + + if ((startHTML<0) || (endHTML<0) || (startFragment<0) || (endFragment<0)) + return NS_ERROR_FAILURE; + + // create context string + nsCAutoString contextUTF8(Substring(aCfhtml, startHTML, startFragment - startHTML) + + Substring(aCfhtml, endFragment, endHTML - endFragment)); + + // create fragment string + nsCAutoString fragmentUTF8(Substring(aCfhtml, startFragment, endFragment-startFragment)); + + // remove the StartFragment/EndFragment comments from the fragment, if present + RemoveFragComments(fragmentUTF8); + + // remove the StartFragment/EndFragment comments from the context, if present + RemoveFragComments(contextUTF8); + + // convert both strings to usc2 + const nsAFlatString& fragUcs2Str = NS_ConvertUTF8toUCS2(fragmentUTF8); + const nsAFlatString& cntxtUcs2Str = NS_ConvertUTF8toUCS2(contextUTF8); + + // translate platform linebreaks for fragment + PRUnichar* newStr = 0; + PRInt32 oldLengthInChars = fragUcs2Str.Length() + 1; // +1 to include null terminator + PRInt32 newLengthInChars = 0; + *aStuffToPaste = nsLinebreakConverter::ConvertUnicharLineBreaks(fragUcs2Str.get(), + nsLinebreakConverter::eLinebreakAny, + nsLinebreakConverter::eLinebreakContent, + oldLengthInChars, &newLengthInChars); + if (!aStuffToPaste) + { + return NS_ERROR_FAILURE; + } + + // translate platform linebreaks for context + newStr = 0; + oldLengthInChars = cntxtUcs2Str.Length(); // +1 to include null terminator + newLengthInChars = 0; + *aCfcontext = nsLinebreakConverter::ConvertUnicharLineBreaks(cntxtUcs2Str.get(), + nsLinebreakConverter::eLinebreakAny, + nsLinebreakConverter::eLinebreakContent, + oldLengthInChars, &newLengthInChars); + // it's ok for context to be empty. frag might be whole doc and contain all it's context. + + // we're done! + return NS_OK; +} + NS_IMETHODIMP nsHTMLEditor::InsertFromTransferable(nsITransferable *transferable, const nsAString & aContextStr, const nsAString & aInfoStr) @@ -795,41 +907,59 @@ NS_IMETHODIMP nsHTMLEditor::InsertFromTransferable(nsITransferable *transferable { nsAutoTxnsConserveSelection dontSpazMySelection(this); nsAutoString flavor, stuffToPaste; - flavor.AssignWithConversion( bestFlavor ); // just so we can use flavor.Equals() + flavor.AssignWithConversion(bestFlavor); // just so we can use flavor.Equals() #ifdef DEBUG_clipboard printf("Got flavor [%s]\n", bestFlavor); #endif - if (flavor.Equals(NS_LITERAL_STRING(kHTMLMime))) + + if (flavor.Equals(NS_LITERAL_STRING(kNativeHTMLMime))) { - nsCOMPtr textDataObj ( do_QueryInterface(genericDataObj) ); + // note cf_html uses utf8, hence use length = len, not len/2 as in flavors below + nsCOMPtr textDataObj(do_QueryInterface(genericDataObj)); + if (textDataObj && len > 0) + { + nsCAutoString cfhtml; + textDataObj->GetData(cfhtml); + NS_ASSERTION(cfhtml.Length() <= (len), "Invalid length!"); + nsXPIDLString cfcontext, cffragment, cfselection; // cfselection left emtpy for now + + rv = ParseCFHTML(cfhtml, getter_Copies(cffragment), getter_Copies(cfcontext)); + if (NS_SUCCEEDED(rv) && !cffragment.IsEmpty()) + { + nsAutoEditBatch beginBatching(this); + rv = InsertHTMLWithContext(cffragment, cfcontext, cfselection); + } + } + } + else if (flavor.Equals(NS_LITERAL_STRING(kHTMLMime))) + { + nsCOMPtr textDataObj(do_QueryInterface(genericDataObj)); if (textDataObj && len > 0) { nsAutoString text; - - textDataObj->GetData ( text ); - + textDataObj->GetData(text); NS_ASSERTION(text.Length() <= (len/2), "Invalid length!"); - stuffToPaste.Assign ( text.get(), len / 2 ); + stuffToPaste.Assign(text.get(), len / 2); nsAutoEditBatch beginBatching(this); rv = InsertHTMLWithContext(stuffToPaste, aContextStr, aInfoStr); } } else if (flavor.Equals(NS_LITERAL_STRING(kUnicodeMime))) { - nsCOMPtr textDataObj ( do_QueryInterface(genericDataObj) ); + nsCOMPtr textDataObj(do_QueryInterface(genericDataObj)); if (textDataObj && len > 0) { nsAutoString text; - textDataObj->GetData ( text ); + textDataObj->GetData(text); NS_ASSERTION(text.Length() <= (len/2), "Invalid length!"); - stuffToPaste.Assign ( text.get(), len / 2 ); + stuffToPaste.Assign(text.get(), len / 2); nsAutoEditBatch beginBatching(this); rv = InsertText(stuffToPaste); } } else if (flavor.Equals(NS_LITERAL_STRING(kFileMime))) { - nsCOMPtr fileObj ( do_QueryInterface(genericDataObj) ); + nsCOMPtr fileObj(do_QueryInterface(genericDataObj)); if (fileObj && len > 0) { @@ -839,27 +969,27 @@ NS_IMETHODIMP nsHTMLEditor::InsertFromTransferable(nsITransferable *transferable return rv; nsCOMPtr fileURL(do_QueryInterface(uri)); - if ( fileURL ) + if (fileURL) { PRBool insertAsImage = PR_FALSE; nsCAutoString fileextension; - rv = fileURL->GetFileExtension( fileextension ); - if ( NS_SUCCEEDED(rv) && !fileextension.IsEmpty() ) + rv = fileURL->GetFileExtension(fileextension); + if (NS_SUCCEEDED(rv) && !fileextension.IsEmpty()) { - if ( (nsCRT::strcasecmp( fileextension.get(), "jpg" ) == 0 ) - || (nsCRT::strcasecmp( fileextension.get(), "jpeg" ) == 0 ) - || (nsCRT::strcasecmp( fileextension.get(), "gif" ) == 0 ) - || (nsCRT::strcasecmp( fileextension.get(), "png" ) == 0 ) ) + if ( (nsCRT::strcasecmp(fileextension.get(), "jpg") == 0 ) + || (nsCRT::strcasecmp(fileextension.get(), "jpeg") == 0 ) + || (nsCRT::strcasecmp(fileextension.get(), "gif") == 0 ) + || (nsCRT::strcasecmp(fileextension.get(), "png") == 0 ) ) { insertAsImage = PR_TRUE; } } nsCAutoString urltext; - rv = fileURL->GetSpec( urltext ); - if ( NS_SUCCEEDED(rv) && !urltext.IsEmpty() ) + rv = fileURL->GetSpec(urltext); + if (NS_SUCCEEDED(rv) && !urltext.IsEmpty()) { - if ( insertAsImage ) + if (insertAsImage) { stuffToPaste.Assign(NS_LITERAL_STRING("GetCurrentSession(getter_AddRefs(dragSession)); if (!dragSession) return NS_OK; + // find out if we have our internal html flavor on the clipboard. We don't want to mess + // around with cfhtml if we do. + PRBool bHavePrivateHTMLFlavor = PR_FALSE; + rv = dragSession->IsDataFlavorSupported(kHTMLContext, &bHavePrivateHTMLFlavor); + if (NS_FAILED(rv)) return rv; + // Get the nsITransferable interface for getting the data from the drop nsCOMPtr trans; - rv = PrepareTransferable(getter_AddRefs(trans)); + rv = PrepareHTMLTransferable(getter_AddRefs(trans), bHavePrivateHTMLFlavor); if (NS_FAILED(rv)) return rv; if (!trans) return NS_OK; // NS_ERROR_FAILURE; SHOULD WE FAIL? @@ -953,30 +1089,30 @@ NS_IMETHODIMP nsHTMLEditor::InsertFromDrop(nsIDOMEvent* aDropEvent) { nsAutoString text; textDataObj = do_QueryInterface(contextDataObj); - textDataObj->GetData ( text ); + textDataObj->GetData(text); NS_ASSERTION(text.Length() <= (contextLen/2), "Invalid length!"); - contextStr.Assign ( text.get(), contextLen / 2 ); + contextStr.Assign(text.get(), contextLen / 2); } if (infoDataObj) { nsAutoString text; textDataObj = do_QueryInterface(infoDataObj); - textDataObj->GetData ( text ); + textDataObj->GetData(text); NS_ASSERTION(text.Length() <= (infoLen/2), "Invalid length!"); - infoStr.Assign ( text.get(), infoLen / 2 ); + infoStr.Assign(text.get(), infoLen / 2); } - if ( doPlaceCaret ) + if (doPlaceCaret) { // check if the user pressed the key to force a copy rather than a move // if we run into problems here, we'll just assume the user doesn't want a copy PRBool userWantsCopy = PR_FALSE; - nsCOMPtr nsuiEvent (do_QueryInterface(aDropEvent)); + nsCOMPtr nsuiEvent(do_QueryInterface(aDropEvent)); if (!nsuiEvent) return NS_ERROR_FAILURE; - nsCOMPtr mouseEvent ( do_QueryInterface(aDropEvent) ); + nsCOMPtr mouseEvent(do_QueryInterface(aDropEvent)); if (mouseEvent) #ifdef XP_MAC @@ -1141,24 +1277,26 @@ NS_IMETHODIMP nsHTMLEditor::CanDrag(nsIDOMEvent *aDragEvent, PRBool *aCanDrag) if (NS_FAILED(res)) return res; // if we are collapsed, we have no selection so nothing to drag - if ( isCollapsed ) + if (isCollapsed) return NS_OK; nsCOMPtr eventTarget; nsCOMPtr nsevent(do_QueryInterface(aDragEvent)); - if (nsevent) { + if (nsevent) + { res = nsevent->GetOriginalTarget(getter_AddRefs(eventTarget)); - if (NS_FAILED(res)) { + if (NS_FAILED(res)) + { return res; } } - if ( eventTarget ) + if (eventTarget) { nsCOMPtr eventTargetDomNode = do_QueryInterface(eventTarget); - if ( eventTargetDomNode ) + if (eventTargetDomNode) { PRBool isTargetedCorrectly = PR_FALSE; res = selection->ContainsNode(eventTargetDomNode, PR_FALSE, &isTargetedCorrectly); @@ -1252,7 +1390,7 @@ NS_IMETHODIMP nsHTMLEditor::DoDrag(nsIDOMEvent *aDragEvent) } // if we have an empty string, we're done; otherwise continue - if ( !buffer.IsEmpty() ) + if (!buffer.IsEmpty()) { nsCOMPtr dataWrapper, contextWrapper, infoWrapper; @@ -1269,7 +1407,7 @@ NS_IMETHODIMP nsHTMLEditor::DoDrag(nsIDOMEvent *aDragEvent) // QI the data object an |nsISupports| so that when the transferable holds // onto it, it will addref the correct interface. - nsCOMPtr genericDataObj ( do_QueryInterface(dataWrapper) ); + nsCOMPtr genericDataObj(do_QueryInterface(dataWrapper)); rv = trans->SetTransferData(kUnicodeMime, genericDataObj, buffer.Length() * 2); if (NS_FAILED(rv)) return rv; } @@ -1280,8 +1418,8 @@ NS_IMETHODIMP nsHTMLEditor::DoDrag(nsIDOMEvent *aDragEvent) infoWrapper = do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID); NS_ENSURE_TRUE(infoWrapper, NS_ERROR_FAILURE); - contextWrapper->SetData ( parents ); - infoWrapper->SetData ( info ); + contextWrapper->SetData(parents); + infoWrapper->SetData(info); rv = trans->AddDataFlavor(kHTMLMime); if (NS_FAILED(rv)) return rv; @@ -1292,7 +1430,7 @@ NS_IMETHODIMP nsHTMLEditor::DoDrag(nsIDOMEvent *aDragEvent) rv = trans->SetConverter(htmlConverter); if (NS_FAILED(rv)) return rv; - nsCOMPtr genericDataObj ( do_QueryInterface(dataWrapper) ); + nsCOMPtr genericDataObj(do_QueryInterface(dataWrapper)); rv = trans->SetTransferData(kHTMLMime, genericDataObj, buffer.Length() * 2); if (NS_FAILED(rv)) return rv; @@ -1321,12 +1459,13 @@ NS_IMETHODIMP nsHTMLEditor::DoDrag(nsIDOMEvent *aDragEvent) // in some cases we'll want to cut rather than copy... hmmmmm... flags = nsIDragService::DRAGDROP_ACTION_COPY + nsIDragService::DRAGDROP_ACTION_MOVE; - rv = dragService->InvokeDragSession( domnode, transferableArray, nsnull, flags); + rv = dragService->InvokeDragSession(domnode, transferableArray, nsnull, flags); if (NS_FAILED(rv)) return rv; nsCOMPtr nsevent(do_QueryInterface(aDragEvent)); - if (nsevent) { + if (nsevent) + { nsevent->PreventBubble(); } } @@ -1335,19 +1474,50 @@ NS_IMETHODIMP nsHTMLEditor::DoDrag(nsIDOMEvent *aDragEvent) return rv; } + +PRBool nsHTMLEditor::HavePrivateHTMLFlavor(nsIClipboard *aClipboard) +{ + // check the clipboard for our special kHTMLContext flavor. If that is there, we know + // we have our own internal html format on clipboard. + + if (!aClipboard) return PR_FALSE; + PRBool bHavePrivateHTMLFlavor = PR_FALSE; + nsCOMPtr flavArray; + + nsresult res = NS_NewISupportsArray(getter_AddRefs(flavArray)); + if (NS_FAILED(res)) return PR_FALSE; + + nsCOMPtr contextString = do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID); + if (!contextString) return PR_FALSE; + + contextString->SetData(NS_LITERAL_CSTRING(kHTMLContext)); + + flavArray->AppendElement(contextString); + + if (NS_SUCCEEDED(aClipboard->HasDataMatchingFlavors (flavArray, nsIClipboard::kGlobalClipboard, &bHavePrivateHTMLFlavor ))) + return bHavePrivateHTMLFlavor; + + return PR_FALSE; +} + + NS_IMETHODIMP nsHTMLEditor::Paste(PRInt32 aSelectionType) { ForceCompositionEnd(); // Get Clipboard Service nsresult rv; - nsCOMPtr clipboard( do_GetService( kCClipboardCID, &rv ) ); - if ( NS_FAILED(rv) ) + nsCOMPtr clipboard(do_GetService(kCClipboardCID, &rv)); + if (NS_FAILED(rv)) return rv; - + + // find out if we have our internal html flavor on the clipboard. We don't want to mess + // around with cfhtml if we do. + PRBool bHavePrivateHTMLFlavor = HavePrivateHTMLFlavor(clipboard); + // Get the nsITransferable interface for getting the data from the clipboard nsCOMPtr trans; - rv = PrepareTransferable(getter_AddRefs(trans)); + rv = PrepareHTMLTransferable(getter_AddRefs(trans), bHavePrivateHTMLFlavor); if (NS_SUCCEEDED(rv) && trans) { // Get the Data from the clipboard @@ -1355,38 +1525,43 @@ NS_IMETHODIMP nsHTMLEditor::Paste(PRInt32 aSelectionType) { // also get additional html copy hints, if present nsAutoString contextStr, infoStr; - nsCOMPtr contextDataObj, infoDataObj; - PRUint32 contextLen, infoLen; - nsCOMPtr textDataObj; - - nsCOMPtr contextTrans = do_CreateInstance(kCTransferableCID); - NS_ENSURE_TRUE(contextTrans, NS_ERROR_NULL_POINTER); - contextTrans->AddDataFlavor(kHTMLContext); - clipboard->GetData(contextTrans, aSelectionType); - contextTrans->GetTransferData(kHTMLContext, getter_AddRefs(contextDataObj), &contextLen); - nsCOMPtr infoTrans = do_CreateInstance(kCTransferableCID); - NS_ENSURE_TRUE(infoTrans, NS_ERROR_NULL_POINTER); - infoTrans->AddDataFlavor(kHTMLInfo); - clipboard->GetData(infoTrans, aSelectionType); - infoTrans->GetTransferData(kHTMLInfo, getter_AddRefs(infoDataObj), &infoLen); - - if (contextDataObj) + // also get additional html copy hints, if present + if (bHavePrivateHTMLFlavor) { - nsAutoString text; - textDataObj = do_QueryInterface(contextDataObj); - textDataObj->GetData ( text ); - NS_ASSERTION(text.Length() <= (contextLen/2), "Invalid length!"); - contextStr.Assign ( text.get(), contextLen / 2 ); - } - - if (infoDataObj) - { - nsAutoString text; - textDataObj = do_QueryInterface(infoDataObj); - textDataObj->GetData ( text ); - NS_ASSERTION(text.Length() <= (infoLen/2), "Invalid length!"); - infoStr.Assign ( text.get(), infoLen / 2 ); + nsCOMPtr contextDataObj, infoDataObj; + PRUint32 contextLen, infoLen; + nsCOMPtr textDataObj; + + nsCOMPtr contextTrans = do_CreateInstance(kCTransferableCID); + NS_ENSURE_TRUE(contextTrans, NS_ERROR_NULL_POINTER); + contextTrans->AddDataFlavor(kHTMLContext); + clipboard->GetData(contextTrans, aSelectionType); + contextTrans->GetTransferData(kHTMLContext, getter_AddRefs(contextDataObj), &contextLen); + + nsCOMPtr infoTrans = do_CreateInstance(kCTransferableCID); + NS_ENSURE_TRUE(infoTrans, NS_ERROR_NULL_POINTER); + infoTrans->AddDataFlavor(kHTMLInfo); + clipboard->GetData(infoTrans, aSelectionType); + infoTrans->GetTransferData(kHTMLInfo, getter_AddRefs(infoDataObj), &infoLen); + + if (contextDataObj) + { + nsAutoString text; + textDataObj = do_QueryInterface(contextDataObj); + textDataObj->GetData(text); + NS_ASSERTION(text.Length() <= (contextLen/2), "Invalid length!"); + contextStr.Assign(text.get(), contextLen / 2); + } + + if (infoDataObj) + { + nsAutoString text; + textDataObj = do_QueryInterface(infoDataObj); + textDataObj->GetData(text); + NS_ASSERTION(text.Length() <= (infoLen/2), "Invalid length!"); + infoStr.Assign(text.get(), infoLen / 2); + } } rv = InsertFromTransferable(trans, contextStr, infoStr); } @@ -1404,8 +1579,8 @@ NS_IMETHODIMP nsHTMLEditor::PasteNoFormatting(PRInt32 aSelectionType) // Get Clipboard Service nsresult rv; - nsCOMPtr clipboard( do_GetService( kCClipboardCID, &rv ) ); - if ( NS_FAILED(rv) ) + nsCOMPtr clipboard(do_GetService(kCClipboardCID, &rv)); + if (NS_FAILED(rv)) return rv; // Get the nsITransferable interface for getting the data from the clipboard. @@ -1454,9 +1629,7 @@ NS_IMETHODIMP nsHTMLEditor::CanPaste(PRInt32 aSelectionType, PRBool *aCanPaste) // add the flavors for all editors for (const char* const* flavor = textEditorFlavors; *flavor; flavor++) { - nsCOMPtr flavorString; - nsComponentManager::CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID, nsnull, - NS_GET_IID(nsISupportsCString), getter_AddRefs(flavorString)); + nsCOMPtr flavorString = do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID); if (flavorString) { flavorString->SetData(nsDependentCString(*flavor)); @@ -1471,9 +1644,7 @@ NS_IMETHODIMP nsHTMLEditor::CanPaste(PRInt32 aSelectionType, PRBool *aCanPaste) *htmlFlavor; htmlFlavor++) { - nsCOMPtr flavorString; - nsComponentManager::CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID, nsnull, - NS_GET_IID(nsISupportsCString), getter_AddRefs(flavorString)); + nsCOMPtr flavorString = do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID); if (flavorString) { flavorString->SetData(nsDependentCString(*htmlFlavor)); @@ -1595,13 +1766,13 @@ NS_IMETHODIMP nsHTMLEditor::PasteAsPlaintextQuotation(PRInt32 aSelectionType) nsAutoString stuffToPaste; if (flavor.Equals(NS_LITERAL_STRING(kUnicodeMime))) { - nsCOMPtr textDataObj ( do_QueryInterface(genericDataObj) ); + nsCOMPtr textDataObj(do_QueryInterface(genericDataObj)); if (textDataObj && len > 0) { nsAutoString text; - textDataObj->GetData ( text ); + textDataObj->GetData(text); NS_ASSERTION(text.Length() <= (len/2), "Invalid length!"); - stuffToPaste.Assign ( text.get(), len / 2 ); + stuffToPaste.Assign(text.get(), len / 2); nsAutoEditBatch beginBatching(this); rv = InsertAsPlaintextQuotation(stuffToPaste, PR_TRUE, 0); } @@ -1903,6 +2074,46 @@ nsHTMLEditor::InsertAsCitedQuotation(const nsAString & aQuotedText, return res; } + +void RemoveBodyAndHead(nsIDOMNode *aNode) +{ + if (!aNode) + return; + + nsCOMPtr tmp, child, body, head; + // find the body and head nodes if any. + // look only at immediate children of aNode. + aNode->GetFirstChild(getter_AddRefs(child)); + while (child) + { + if (nsTextEditUtils::IsBody(child)) + { + body = child; + } + else if (nsEditor::NodeIsType(child, NS_LITERAL_STRING("head"))) + { + head = child; + } + child->GetNextSibling(getter_AddRefs(tmp)); + child = tmp; + } + if (head) + { + aNode->RemoveChild(head, getter_AddRefs(tmp)); + } + if (body) + { + body->GetFirstChild(getter_AddRefs(child)); + while (child) + { + aNode->InsertBefore(child, body, getter_AddRefs(tmp)); + body->GetFirstChild(getter_AddRefs(child)); + } + aNode->RemoveChild(body, getter_AddRefs(tmp)); + } +} + + nsresult nsHTMLEditor::CreateDOMFragmentFromPaste(nsIDOMNSRange *aNSRange, const nsAString & aInputString, const nsAString & aContextStr, @@ -1914,46 +2125,43 @@ nsresult nsHTMLEditor::CreateDOMFragmentFromPaste(nsIDOMNSRange *aNSRange, if (!outFragNode || !outRangeStartHint || !outRangeEndHint || !aNSRange) return NS_ERROR_NULL_POINTER; nsCOMPtr docfrag; - nsCOMPtr contextAsNode; + nsCOMPtr contextAsNode, tmp; nsresult res = NS_OK; // if we have context info, create a fragment for that - nsCOMPtr contextfrag; - nsCOMPtr contextLeaf; + nsCOMPtr contextLeaf, junk; PRInt32 contextDepth = 0; if (aContextStr.Length()) { - res = aNSRange->CreateContextualFragment(aContextStr, getter_AddRefs(contextfrag)); + res = ParseFragment(aContextStr, address_of(contextAsNode)); NS_ENSURE_SUCCESS(res, res); - contextAsNode = do_QueryInterface(contextfrag); + NS_ENSURE_TRUE(contextAsNode, NS_ERROR_FAILURE); + res = StripFormattingNodes(contextAsNode); NS_ENSURE_SUCCESS(res, res); + + RemoveBodyAndHead(contextAsNode); + // cache the deepest leaf in the context - nsCOMPtr junk, child, tmp = contextAsNode; + tmp = contextAsNode; while (tmp) { contextDepth++; contextLeaf = tmp; contextLeaf->GetFirstChild(getter_AddRefs(tmp)); } - // tweak aNSRange to point inside contextAsNode - nsCOMPtr range(do_QueryInterface(aNSRange)); - if (range) - { - aNSRange->NSDetach(); - range->SetStart(contextLeaf,0); - range->SetEnd(contextLeaf,0); - } } - + + // create fragment for pasted html - res = aNSRange->CreateContextualFragment(aInputString, getter_AddRefs(docfrag)); + res = ParseFragment(aInputString, outFragNode); NS_ENSURE_SUCCESS(res, res); - *outFragNode = do_QueryInterface(docfrag); - - if (contextfrag) + NS_ENSURE_TRUE(*outFragNode, NS_ERROR_FAILURE); + + RemoveBodyAndHead(*outFragNode); + + if (contextAsNode) { - nsCOMPtr junk; // unite the two trees contextLeaf->AppendChild(*outFragNode, getter_AddRefs(junk)); *outFragNode = contextAsNode; @@ -1983,6 +2191,36 @@ nsresult nsHTMLEditor::CreateDOMFragmentFromPaste(nsIDOMNSRange *aNSRange, return res; } +nsresult nsHTMLEditor::ParseFragment(const nsAString &aFragStr, nsCOMPtr *outNode) +{ + // create the parser to do the conversion. + nsCOMPtr parser; + nsresult res = nsComponentManager::CreateInstance(kCParserCID, nsnull, NS_GET_IID(nsIParser), + getter_AddRefs(parser)); + NS_ENSURE_SUCCESS(res, res); + NS_ENSURE_TRUE(parser, NS_ERROR_FAILURE); + + // create the html fragment sink + nsCOMPtr sink; + sink = do_CreateInstance(NS_HTMLFRAGMENTSINK2_CONTRACTID); + NS_ENSURE_TRUE(sink, NS_ERROR_FAILURE); + nsCOMPtr fragSink(do_QueryInterface(sink)); + NS_ENSURE_TRUE(fragSink, NS_ERROR_FAILURE); + + // parse the fragment + parser->SetContentSink(sink); + parser->Parse(aFragStr, 0, NS_LITERAL_CSTRING("text/html"), PR_FALSE, PR_TRUE, eDTDMode_fragment); + + // get the fragment node + nsCOMPtr contextfrag; + res = fragSink->GetFragment(getter_AddRefs(contextfrag)); + NS_ENSURE_SUCCESS(res, res); + *outNode = do_QueryInterface(contextfrag); + + return res; +} + + nsresult nsHTMLEditor::CreateListOfNodesToPaste(nsIDOMNode *aFragmentAsNode, nsCOMArray& outNodeList, PRInt32 aRangeStartHint, diff --git a/mozilla/editor/libeditor/html/nsHTMLEditor.h b/mozilla/editor/libeditor/html/nsHTMLEditor.h index 22bd223236e..876a81c7051 100644 --- a/mozilla/editor/libeditor/html/nsHTMLEditor.h +++ b/mozilla/editor/libeditor/html/nsHTMLEditor.h @@ -70,6 +70,7 @@ class nsITransferable; class nsIDOMEventReceiver; class nsIDOMNSRange; class nsIDocumentEncoder; +class nsIClipboard; class TypeInState; /** @@ -643,9 +644,12 @@ protected: // factored methods for handling insertion of data from transferables (drag&drop or clipboard) NS_IMETHOD PrepareTransferable(nsITransferable **transferable); + NS_IMETHOD PrepareHTMLTransferable(nsITransferable **transferable, PRBool havePrivFlavor); NS_IMETHOD InsertFromTransferable(nsITransferable *transferable, const nsAString & aContextStr, const nsAString & aInfoStr); + PRBool HavePrivateHTMLFlavor( nsIClipboard *clipboard ); + nsresult ParseCFHTML(nsCString & aCfhtml, PRUnichar **aStuffToPaste, PRUnichar **aCfcontext); nsresult InsertHTMLWithContext(const nsAString & aInputString, const nsAString & aContextStr, const nsAString & aInfoStr); @@ -662,6 +666,7 @@ protected: nsCOMPtr *outFragNode, PRInt32 *outRangeStartHint, PRInt32 *outRangeEndHint); + nsresult ParseFragment(const nsAString & aFragStr, nsCOMPtr *outNode); nsresult CreateListOfNodesToPaste(nsIDOMNode *aFragmentAsNode, nsCOMArray& outNodeList, PRInt32 aRangeStartHint, diff --git a/mozilla/htmlparser/public/nsIHTMLFragmentContentSink.h b/mozilla/htmlparser/public/nsIHTMLFragmentContentSink.h index cc9f7523380..bccf6904bec 100644 --- a/mozilla/htmlparser/public/nsIHTMLFragmentContentSink.h +++ b/mozilla/htmlparser/public/nsIHTMLFragmentContentSink.h @@ -41,6 +41,7 @@ #include "nsIHTMLContentSink.h" #define NS_HTMLFRAGMENTSINK_CONTRACTID "@mozilla.org/layout/htmlfragmentsink;1" +#define NS_HTMLFRAGMENTSINK2_CONTRACTID "@mozilla.org/layout/htmlfragmentsink;2" class nsIDOMDocumentFragment; diff --git a/mozilla/parser/htmlparser/public/nsIHTMLFragmentContentSink.h b/mozilla/parser/htmlparser/public/nsIHTMLFragmentContentSink.h index cc9f7523380..bccf6904bec 100644 --- a/mozilla/parser/htmlparser/public/nsIHTMLFragmentContentSink.h +++ b/mozilla/parser/htmlparser/public/nsIHTMLFragmentContentSink.h @@ -41,6 +41,7 @@ #include "nsIHTMLContentSink.h" #define NS_HTMLFRAGMENTSINK_CONTRACTID "@mozilla.org/layout/htmlfragmentsink;1" +#define NS_HTMLFRAGMENTSINK2_CONTRACTID "@mozilla.org/layout/htmlfragmentsink;2" class nsIDOMDocumentFragment;