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
This commit is contained in:
jfrancis%netscape.com
2002-11-22 03:30:59 +00:00
parent a676f63720
commit 8247e85525
10 changed files with 457 additions and 114 deletions

View File

@@ -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("<!--StartFragment");
if (startCommentIndx >= 0)
{
PRInt32 startCommentEnd = aStr.Find("-->", PR_FALSE, startCommentIndx);
if (startCommentEnd > startCommentIndx)
aStr.Cut(startCommentIndx, (startCommentEnd+3)-startCommentIndx);
}
PRInt32 endCommentIndx = aStr.Find("<!--EndFragment");
if (endCommentIndx >= 0)
{
PRInt32 endCommentEnd = 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<nsISupportsString> textDataObj ( do_QueryInterface(genericDataObj) );
// note cf_html uses utf8, hence use length = len, not len/2 as in flavors below
nsCOMPtr<nsISupportsCString> 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<nsISupportsString> 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<nsISupportsString> textDataObj ( do_QueryInterface(genericDataObj) );
nsCOMPtr<nsISupportsString> 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<nsIFile> fileObj ( do_QueryInterface(genericDataObj) );
nsCOMPtr<nsIFile> fileObj(do_QueryInterface(genericDataObj));
if (fileObj && len > 0)
{
@@ -839,27 +969,27 @@ NS_IMETHODIMP nsHTMLEditor::InsertFromTransferable(nsITransferable *transferable
return rv;
nsCOMPtr<nsIURL> 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("<IMG src=\""));
stuffToPaste.Append(NS_ConvertUTF8toUCS2(urltext));
@@ -910,9 +1040,15 @@ NS_IMETHODIMP nsHTMLEditor::InsertFromDrop(nsIDOMEvent* aDropEvent)
dragService->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<nsITransferable> 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<nsIDOMNSUIEvent> nsuiEvent (do_QueryInterface(aDropEvent));
nsCOMPtr<nsIDOMNSUIEvent> nsuiEvent(do_QueryInterface(aDropEvent));
if (!nsuiEvent) return NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMMouseEvent> mouseEvent ( do_QueryInterface(aDropEvent) );
nsCOMPtr<nsIDOMMouseEvent> 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<nsIDOMEventTarget> eventTarget;
nsCOMPtr<nsIDOMNSEvent> 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<nsIDOMNode> 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<nsISupportsString> 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<nsISupports> genericDataObj ( do_QueryInterface(dataWrapper) );
nsCOMPtr<nsISupports> 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<nsISupports> genericDataObj ( do_QueryInterface(dataWrapper) );
nsCOMPtr<nsISupports> 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<nsIDOMNSEvent> 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<nsISupportsArray> flavArray;
nsresult res = NS_NewISupportsArray(getter_AddRefs(flavArray));
if (NS_FAILED(res)) return PR_FALSE;
nsCOMPtr<nsISupportsCString> 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<nsIClipboard> clipboard( do_GetService( kCClipboardCID, &rv ) );
if ( NS_FAILED(rv) )
nsCOMPtr<nsIClipboard> 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<nsITransferable> 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<nsISupports> contextDataObj, infoDataObj;
PRUint32 contextLen, infoLen;
nsCOMPtr<nsISupportsString> textDataObj;
nsCOMPtr<nsITransferable> 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<nsITransferable> 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<nsISupports> contextDataObj, infoDataObj;
PRUint32 contextLen, infoLen;
nsCOMPtr<nsISupportsString> textDataObj;
nsCOMPtr<nsITransferable> 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<nsITransferable> 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<nsIClipboard> clipboard( do_GetService( kCClipboardCID, &rv ) );
if ( NS_FAILED(rv) )
nsCOMPtr<nsIClipboard> 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<nsISupportsCString> flavorString;
nsComponentManager::CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID, nsnull,
NS_GET_IID(nsISupportsCString), getter_AddRefs(flavorString));
nsCOMPtr<nsISupportsCString> 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<nsISupportsCString> flavorString;
nsComponentManager::CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID, nsnull,
NS_GET_IID(nsISupportsCString), getter_AddRefs(flavorString));
nsCOMPtr<nsISupportsCString> 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<nsISupportsString> textDataObj ( do_QueryInterface(genericDataObj) );
nsCOMPtr<nsISupportsString> 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<nsIDOMNode> 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<nsIDOMDocumentFragment> docfrag;
nsCOMPtr<nsIDOMNode> contextAsNode;
nsCOMPtr<nsIDOMNode> contextAsNode, tmp;
nsresult res = NS_OK;
// if we have context info, create a fragment for that
nsCOMPtr<nsIDOMDocumentFragment> contextfrag;
nsCOMPtr<nsIDOMNode> contextLeaf;
nsCOMPtr<nsIDOMNode> 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<nsIDOMNode> junk, child, tmp = contextAsNode;
tmp = contextAsNode;
while (tmp)
{
contextDepth++;
contextLeaf = tmp;
contextLeaf->GetFirstChild(getter_AddRefs(tmp));
}
// tweak aNSRange to point inside contextAsNode
nsCOMPtr<nsIDOMRange> 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<nsIDOMNode> 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<nsIDOMNode> *outNode)
{
// create the parser to do the conversion.
nsCOMPtr<nsIParser> 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<nsIContentSink> sink;
sink = do_CreateInstance(NS_HTMLFRAGMENTSINK2_CONTRACTID);
NS_ENSURE_TRUE(sink, NS_ERROR_FAILURE);
nsCOMPtr<nsIHTMLFragmentContentSink> 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<nsIDOMDocumentFragment> contextfrag;
res = fragSink->GetFragment(getter_AddRefs(contextfrag));
NS_ENSURE_SUCCESS(res, res);
*outNode = do_QueryInterface(contextfrag);
return res;
}
nsresult nsHTMLEditor::CreateListOfNodesToPaste(nsIDOMNode *aFragmentAsNode,
nsCOMArray<nsIDOMNode>& outNodeList,
PRInt32 aRangeStartHint,