/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: NPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Netscape Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/NPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Kathleen Brade * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the NPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the NPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nsCopySupport.h" #include "nsIDocumentEncoder.h" #include "nsISupports.h" #include "nsIContent.h" #include "nsIComponentManager.h" #include "nsIServiceManager.h" #include "nsIClipboard.h" #include "nsISelection.h" #include "nsWidgetsCID.h" #include "nsIEventStateManager.h" #include "nsIPresContext.h" #include "nsIDOMNSHTMLInputElement.h" #include "nsIDOMNSHTMLTextAreaElement.h" #include "nsXPCOM.h" #include "nsISupportsPrimitives.h" #include "nsIDOMRange.h" #include "nsIFrame.h" #include "nsIDocShell.h" #include "nsIClipboardDragDropHooks.h" #include "nsIClipboardDragDropHookList.h" #include "nsIInterfaceRequestorUtils.h" // for IBMBIDI #include "nsIDocument.h" #include "nsIPresShell.h" #include "nsIDOMNode.h" #include "nsIDOMElement.h" #include "nsIHTMLDocument.h" #include "nsHTMLAtoms.h" #ifdef IBMBIDI #include "nsBidiUtils.h" //static NS_DEFINE_CID(kUBidiUtilCID, NS_UNICHARBIDIUTIL_CID); #endif // image copy stuff #include "nsIDOMHTMLImageElement.h" #include "nsLayoutAtoms.h" #include "nsIImageLoadingContent.h" #include "imgIContainer.h" #include "imgIRequest.h" #include "nsIInterfaceRequestor.h" #include "nsIInterfaceRequestorUtils.h" #include "gfxIImageFrame.h" #include "nsIImage.h" static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID); static NS_DEFINE_CID(kCTransferableCID, NS_TRANSFERABLE_CID); static NS_DEFINE_CID(kHTMLConverterCID, NS_HTMLFORMATCONVERTER_CID); // private clipboard data flavors for html copy, used by editor when pasting #define kHTMLContext "text/_moz_htmlcontext" #define kHTMLInfo "text/_moz_htmlinfo" nsresult nsCopySupport::HTMLCopy(nsISelection *aSel, nsIDocument *aDoc, PRInt16 aClipboardID) { nsresult rv = NS_OK; nsCOMPtr docEncoder; docEncoder = do_CreateInstance(NS_HTMLCOPY_ENCODER_CONTRACTID); NS_ENSURE_TRUE(docEncoder, NS_ERROR_FAILURE); PRBool bIsPlainTextContext = PR_FALSE; rv = IsPlainTextContext(aSel, aDoc, &bIsPlainTextContext); if (NS_FAILED(rv)) return rv; PRBool bIsHTMLCopy = !bIsPlainTextContext; PRUint32 flags = 0; nsAutoString mimeType; if (bIsHTMLCopy) mimeType = NS_LITERAL_STRING(kHTMLMime); else { flags |= nsIDocumentEncoder::OutputBodyOnly | nsIDocumentEncoder::OutputPreformatted; mimeType = NS_LITERAL_STRING(kUnicodeMime); } rv = docEncoder->Init(aDoc, mimeType, flags); if (NS_FAILED(rv)) return rv; rv = docEncoder->SetSelection(aSel); if (NS_FAILED(rv)) return rv; nsAutoString buffer, parents, info; if (bIsHTMLCopy) { // encode the selection as html with contextual info rv = docEncoder->EncodeToStringWithContext(buffer, parents, info); if (NS_FAILED(rv)) return rv; } else { // encode the selection rv = docEncoder->EncodeToString(buffer); if (NS_FAILED(rv)) return rv; } #ifdef IBMBIDI //ahmed rv = NS_OK; PRBool arabicCharset; nsCOMPtr doc = do_QueryInterface(aDoc); if (doc) { nsCOMPtr shell; doc->GetShellAt(0, getter_AddRefs(shell)); if (shell) { nsCOMPtr context; shell->GetPresContext(getter_AddRefs(context) ); if (context) { context->IsArabicEncoding(arabicCharset); if (arabicCharset) { PRUint32 bidiOptions; PRBool isVisual; PRBool isBidiSystem; context->GetBidi(&bidiOptions); context->IsVisualMode(isVisual); context->GetIsBidiSystem(isBidiSystem); if ( (GET_BIDI_OPTION_CLIPBOARDTEXTMODE(bidiOptions) == IBMBIDI_CLIPBOARDTEXTMODE_LOGICAL)&&(isVisual) ) { nsAutoString newBuffer; if (isBidiSystem) { if (GET_BIDI_OPTION_DIRECTION(bidiOptions) == IBMBIDI_TEXTDIRECTION_RTL) { Conv_FE_06(buffer, newBuffer); } else { Conv_FE_06_WithReverse(buffer, newBuffer); } } else { //nonbidisystem HandleNumbers(buffer, newBuffer);//ahmed } buffer = newBuffer; } //Mohamed else { nsCAutoString bidiCharset; context->GetBidiCharset(bidiCharset); if (bidiCharset.EqualsIgnoreCase("UTF-8") || (!isVisual)) { if ( (GET_BIDI_OPTION_CLIPBOARDTEXTMODE(bidiOptions) == IBMBIDI_CLIPBOARDTEXTMODE_VISUAL) || (!isBidiSystem) ) { nsAutoString newBuffer; Conv_06_FE_WithReverse(buffer, newBuffer, GET_BIDI_OPTION_DIRECTION(bidiOptions)); HandleNumbers(newBuffer, buffer); } } } } } } } #endif // IBMBIDI // Get the Clipboard nsCOMPtr clipboard(do_GetService(kCClipboardCID, &rv)); if (NS_FAILED(rv)) return rv; if ( clipboard ) { // Create a transferable for putting data on the Clipboard nsCOMPtr trans = do_CreateInstance(kCTransferableCID); if ( trans ) { if (bIsHTMLCopy) { // set up the data converter nsCOMPtr htmlConverter = do_CreateInstance(kHTMLConverterCID); NS_ENSURE_TRUE(htmlConverter, NS_ERROR_FAILURE); trans->SetConverter(htmlConverter); } // get wStrings to hold clip data nsCOMPtr dataWrapper, contextWrapper, infoWrapper; dataWrapper = do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID); NS_ENSURE_TRUE(dataWrapper, NS_ERROR_FAILURE); if (bIsHTMLCopy) { contextWrapper = do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID); NS_ENSURE_TRUE(contextWrapper, NS_ERROR_FAILURE); infoWrapper = do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID); NS_ENSURE_TRUE(infoWrapper, NS_ERROR_FAILURE); } // populate the strings nsresult data_rv = NS_OK, context_rv = NS_OK, info_rv = NS_OK; data_rv = dataWrapper->SetData(buffer); if (bIsHTMLCopy) { context_rv = contextWrapper->SetData(parents); info_rv = infoWrapper->SetData(info); } // 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) ); if (bIsHTMLCopy) { if (!buffer.IsEmpty() && NS_SUCCEEDED(data_rv)) { // Add the html DataFlavor to the transferable trans->AddDataFlavor(kHTMLMime); trans->SetTransferData(kHTMLMime, genericDataObj, buffer.Length()*2); } if (!parents.IsEmpty() && NS_SUCCEEDED(context_rv)) { // Add the htmlcontext DataFlavor to the transferable trans->AddDataFlavor(kHTMLContext); genericDataObj = do_QueryInterface(contextWrapper); trans->SetTransferData(kHTMLContext, genericDataObj, parents.Length()*2); } if (!info.IsEmpty() && NS_SUCCEEDED(info_rv)) { // Add the htmlinfo DataFlavor to the transferable trans->AddDataFlavor(kHTMLInfo); genericDataObj = do_QueryInterface(infoWrapper); trans->SetTransferData(kHTMLInfo, genericDataObj, info.Length()*2); } } else { if (!buffer.IsEmpty() && NS_SUCCEEDED(data_rv)) { // Add the unicode DataFlavor to the transferable trans->AddDataFlavor(kUnicodeMime); trans->SetTransferData(kUnicodeMime, genericDataObj, buffer.Length()*2); } } PRBool doPutOnClipboard = PR_TRUE; DoHooks(aDoc, trans, &doPutOnClipboard); // put the transferable on the clipboard if (doPutOnClipboard) clipboard->SetData(trans, nsnull, aClipboardID); } } return rv; } nsresult nsCopySupport::DoHooks(nsIDocument *aDoc, nsITransferable *aTrans, PRBool *aDoPutOnClipboard) { NS_ENSURE_ARG(aDoc); *aDoPutOnClipboard = PR_TRUE; nsCOMPtr isupp; nsresult rv = aDoc->GetContainer(getter_AddRefs(isupp)); nsCOMPtr docShell = do_QueryInterface(isupp); nsCOMPtr hookObj = do_GetInterface(docShell); if (!hookObj) return NS_ERROR_FAILURE; nsCOMPtr enumerator; hookObj->GetHookEnumerator(getter_AddRefs(enumerator)); if (!enumerator) return NS_ERROR_FAILURE; // the logic here should follow the behavior specified in // nsIClipboardDragDropHooks.h nsCOMPtr override; PRBool hasMoreHooks = PR_FALSE; while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMoreHooks)) && hasMoreHooks) { rv = enumerator->GetNext(getter_AddRefs(isupp)); if (NS_FAILED(rv)) break; override = do_QueryInterface(isupp); if (override) { nsresult hookResult = override->OnCopyOrDrag(nsnull, aTrans, aDoPutOnClipboard); NS_ASSERTION(NS_SUCCEEDED(hookResult), "OnCopyOrDrag hook failed"); if (!*aDoPutOnClipboard) break; } } return rv; } nsresult nsCopySupport::IsPlainTextContext(nsISelection *aSel, nsIDocument *aDoc, PRBool *aIsPlainTextContext) { nsresult rv; if (!aSel || !aIsPlainTextContext) return NS_ERROR_NULL_POINTER; *aIsPlainTextContext = PR_FALSE; nsCOMPtr range; nsCOMPtr commonParent; PRInt32 count = 0; rv = aSel->GetRangeCount(&count); NS_ENSURE_SUCCESS(rv, rv); // if selection is uninitialized return if (!count) return NS_ERROR_FAILURE; // we'll just use the common parent of the first range. Implicit assumption // here that multi-range selections are table cell selections, in which case // the common parent is somewhere in the table and we don't really care where. rv = aSel->GetRangeAt(0, getter_AddRefs(range)); NS_ENSURE_SUCCESS(rv, rv); if (!range) return NS_ERROR_NULL_POINTER; range->GetCommonAncestorContainer(getter_AddRefs(commonParent)); nsCOMPtr tmp, selContent( do_QueryInterface(commonParent) ); while (selContent) { // checking for selection inside a plaintext form widget nsCOMPtr atom; selContent->GetTag(getter_AddRefs(atom)); if (atom.get() == nsHTMLAtoms::input || atom.get() == nsHTMLAtoms::textarea) { *aIsPlainTextContext = PR_TRUE; break; } if (atom.get() == nsHTMLAtoms::body) { // check for moz prewrap style on body. If it's there we are // in a plaintext editor. This is pretty cheezy but I haven't // found a good way to tell if we are in a plaintext editor. nsCOMPtr bodyElem = do_QueryInterface(selContent); nsAutoString wsVal; rv = bodyElem->GetAttribute(NS_LITERAL_STRING("style"), wsVal); if (NS_SUCCEEDED(rv) && (kNotFound != wsVal.Find(NS_LITERAL_STRING("-moz-pre-wrap")))) { *aIsPlainTextContext = PR_TRUE; break; } } selContent->GetParent(getter_AddRefs(tmp)); selContent = tmp; } // also consider ourselves in a text widget if we can't find an html document nsCOMPtr htmlDoc = do_QueryInterface(aDoc); if (!htmlDoc) *aIsPlainTextContext = PR_TRUE; return NS_OK; } nsresult nsCopySupport::GetContents(const nsACString& aMimeType, PRUint32 aFlags, nsISelection *aSel, nsIDocument *aDoc, nsAString& outdata) { nsresult rv = NS_OK; nsCOMPtr docEncoder; nsCAutoString encoderContractID(NS_DOC_ENCODER_CONTRACTID_BASE); encoderContractID.Append(aMimeType); docEncoder = do_CreateInstance(encoderContractID.get()); NS_ENSURE_TRUE(docEncoder, NS_ERROR_FAILURE); PRUint32 flags = aFlags; if (aMimeType.Equals("text/plain")) flags |= nsIDocumentEncoder::OutputPreformatted; NS_ConvertASCIItoUCS2 unicodeMimeType(aMimeType); rv = docEncoder->Init(aDoc, unicodeMimeType, flags); if (NS_FAILED(rv)) return rv; if (aSel) { rv = docEncoder->SetSelection(aSel); if (NS_FAILED(rv)) return rv; } // encode the selection return docEncoder->EncodeToString(outdata); } nsresult nsCopySupport::ImageCopy(nsIDOMHTMLImageElement* imageElement, PRInt16 aClipboardID) { nsresult rv; nsCOMPtr imageNode = do_QueryInterface(imageElement, &rv); if (NS_FAILED(rv)) return rv; if (!imageNode) return NS_ERROR_FAILURE; nsCOMPtr image; rv = GetImageFromDOMNode(imageNode, getter_AddRefs(image)); if (NS_FAILED(rv)) return rv; if (!image) return NS_ERROR_FAILURE; // Get the Clipboard nsCOMPtr clipboard(do_GetService(kCClipboardCID, &rv)); if (NS_FAILED(rv)) return rv; if (!clipboard) return NS_ERROR_FAILURE; // Create a transferable for putting data on the Clipboard nsCOMPtr trans = do_CreateInstance(kCTransferableCID, &rv); if (NS_FAILED(rv)) return rv; if (!trans) return NS_ERROR_FAILURE; nsCOMPtr ptrPrimitive(do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID, &rv)); if (NS_FAILED(rv)) return rv; if (!ptrPrimitive) return NS_ERROR_FAILURE; ptrPrimitive->SetData(image); trans->SetTransferData(kNativeImageMime, ptrPrimitive, sizeof(nsISupports*)); // put the transferable on the clipboard return clipboard->SetData(trans, nsnull, aClipboardID); } // // GetImage // // Given a dom node that's an image, finds the nsIImage associated with it. // // XXX see also nsContentAreaDragDrop, and factor! nsresult nsCopySupport::GetImageFromDOMNode(nsIDOMNode* inNode, nsIImage**outImage) { NS_ENSURE_ARG_POINTER(outImage); *outImage = nsnull; nsCOMPtr content(do_QueryInterface(inNode)); if (!content) { return NS_ERROR_NOT_AVAILABLE; } nsCOMPtr imgRequest; content->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, getter_AddRefs(imgRequest)); if (!imgRequest) { return NS_ERROR_NOT_AVAILABLE; } nsCOMPtr imgContainer; imgRequest->GetImage(getter_AddRefs(imgContainer)); if (!imgContainer) { return NS_ERROR_NOT_AVAILABLE; } nsCOMPtr imgFrame; imgContainer->GetFrameAt(0, getter_AddRefs(imgFrame)); if (!imgFrame) { return NS_ERROR_NOT_AVAILABLE; } nsCOMPtr ir = do_QueryInterface(imgFrame); if (!ir) { return NS_ERROR_NOT_AVAILABLE; } return CallGetInterface(ir.get(), outImage); }