Back to index

lightning-sunbird  0.9+nobinonly
nsCopySupport.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Kathleen Brade <brade@netscape.com>
00024  *   David Gardiner <david.gardiner@unisa.edu.au>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "nsCopySupport.h"
00041 #include "nsIDocumentEncoder.h"
00042 #include "nsISupports.h"
00043 #include "nsIContent.h"
00044 #include "nsIComponentManager.h" 
00045 #include "nsIServiceManager.h"
00046 #include "nsIClipboard.h"
00047 #include "nsISelection.h"
00048 #include "nsWidgetsCID.h"
00049 #include "nsXPCOM.h"
00050 #include "nsISupportsPrimitives.h"
00051 #include "nsIDOMRange.h"
00052 
00053 #include "nsIDocShell.h"
00054 #include "nsIContentViewerEdit.h"
00055 #include "nsIClipboardDragDropHooks.h"
00056 #include "nsIClipboardDragDropHookList.h"
00057 
00058 #include "nsIDocument.h"
00059 #include "nsIDOMNode.h"
00060 #include "nsIDOMElement.h"
00061 #include "nsIDOMDocument.h"
00062 #include "nsIHTMLDocument.h"
00063 #include "nsHTMLAtoms.h"
00064 
00065 // image copy stuff
00066 #include "nsIImageLoadingContent.h"
00067 #include "nsIInterfaceRequestorUtils.h"
00068 #include "nsIImage.h"
00069 #include "nsContentUtils.h"
00070 
00071 static NS_DEFINE_CID(kCClipboardCID,           NS_CLIPBOARD_CID);
00072 static NS_DEFINE_CID(kCTransferableCID,        NS_TRANSFERABLE_CID);
00073 static NS_DEFINE_CID(kHTMLConverterCID,        NS_HTMLFORMATCONVERTER_CID);
00074 
00075 // private clipboard data flavors for html copy, used by editor when pasting
00076 #define kHTMLContext   "text/_moz_htmlcontext"
00077 #define kHTMLInfo      "text/_moz_htmlinfo"
00078 
00079 // copy string data onto the transferable
00080 static nsresult AppendString(nsITransferable *aTransferable,
00081                              const nsAString& aString,
00082                              const char* aFlavor);
00083 
00084 // copy HTML node data
00085 static nsresult AppendDOMNode(nsITransferable *aTransferable,
00086                               nsIDOMNode *aDOMNode);
00087 
00088 nsresult nsCopySupport::HTMLCopy(nsISelection *aSel, nsIDocument *aDoc, PRInt16 aClipboardID)
00089 {
00090   nsresult rv = NS_OK;
00091   
00092   PRBool bIsPlainTextContext = PR_FALSE;
00093 
00094   rv = IsPlainTextContext(aSel, aDoc, &bIsPlainTextContext);
00095   if (NS_FAILED(rv)) 
00096     return rv;
00097 
00098   PRBool bIsHTMLCopy = !bIsPlainTextContext;
00099   nsAutoString mimeType;
00100 
00101   nsCOMPtr<nsIDocumentEncoder> docEncoder;
00102 
00103   docEncoder = do_CreateInstance(NS_HTMLCOPY_ENCODER_CONTRACTID);
00104   NS_ENSURE_TRUE(docEncoder, NS_ERROR_FAILURE);
00105 
00106   // We always require a plaintext version
00107   mimeType.AssignLiteral(kUnicodeMime);
00108   PRUint32 flags = nsIDocumentEncoder::OutputPreformatted;
00109 
00110   rv = docEncoder->Init(aDoc, mimeType, flags);
00111   if (NS_FAILED(rv)) 
00112     return rv;
00113   rv = docEncoder->SetSelection(aSel);
00114   if (NS_FAILED(rv)) 
00115     return rv;
00116 
00117   nsAutoString buffer, parents, info, textBuffer, plaintextBuffer;
00118 
00119   rv = docEncoder->EncodeToString(textBuffer);
00120   if (NS_FAILED(rv)) 
00121     return rv;
00122 
00123   nsCOMPtr<nsIFormatConverter> htmlConverter;
00124 
00125   // sometimes we also need the HTML version
00126   if (bIsHTMLCopy) {
00127 
00128     // this string may still contain HTML formatting, so we need to remove that too.
00129     htmlConverter = do_CreateInstance(kHTMLConverterCID);
00130     NS_ENSURE_TRUE(htmlConverter, NS_ERROR_FAILURE);
00131 
00132     nsCOMPtr<nsISupportsString> plainHTML = do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID);
00133     NS_ENSURE_TRUE(plainHTML, NS_ERROR_FAILURE);
00134     plainHTML->SetData(textBuffer);
00135 
00136     nsCOMPtr<nsISupportsString> ConvertedData;
00137     PRUint32 ConvertedLen;
00138     rv = htmlConverter->Convert(kHTMLMime, plainHTML, textBuffer.Length() * 2, kUnicodeMime, getter_AddRefs(ConvertedData), &ConvertedLen);
00139     NS_ENSURE_SUCCESS(rv, rv);
00140 
00141     ConvertedData->GetData(plaintextBuffer);
00142 
00143     mimeType.AssignLiteral(kHTMLMime);
00144 
00145     flags = 0;
00146 
00147     rv = docEncoder->Init(aDoc, mimeType, flags);
00148     NS_ENSURE_SUCCESS(rv, rv);
00149 
00150     rv = docEncoder->SetSelection(aSel);
00151     NS_ENSURE_SUCCESS(rv, rv);
00152 
00153     // encode the selection as html with contextual info
00154     rv = docEncoder->EncodeToStringWithContext(buffer, parents, info);
00155     NS_ENSURE_SUCCESS(rv, rv);
00156   }
00157   
00158   // Get the Clipboard
00159   nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
00160   if (NS_FAILED(rv)) 
00161     return rv;
00162 
00163   if ( clipboard ) 
00164   {
00165     // Create a transferable for putting data on the Clipboard
00166     nsCOMPtr<nsITransferable> trans = do_CreateInstance(kCTransferableCID);
00167     if ( trans ) 
00168     {
00169       if (bIsHTMLCopy)
00170       {
00171         // set up the data converter
00172         trans->SetConverter(htmlConverter);
00173 
00174         if (!buffer.IsEmpty())
00175         {
00176           // Add the html DataFlavor to the transferable
00177           rv = AppendString(trans, buffer, kHTMLMime);
00178           NS_ENSURE_SUCCESS(rv, rv);
00179         }
00180         {
00181           // Add the htmlcontext DataFlavor to the transferable
00182           // Even if parents is empty string, this flavor should
00183           // be attached to the transferable
00184           rv = AppendString(trans, parents, kHTMLContext);
00185           NS_ENSURE_SUCCESS(rv, rv);
00186         }
00187         if (!info.IsEmpty())
00188         {
00189           // Add the htmlinfo DataFlavor to the transferable
00190           rv = AppendString(trans, info, kHTMLInfo);
00191           NS_ENSURE_SUCCESS(rv, rv);
00192         }
00193         if (!plaintextBuffer.IsEmpty())
00194         {
00195           // unicode text
00196           // Add the unicode DataFlavor to the transferable
00197           // If we didn't have this, then nsDataObj::GetData matches text/unicode against
00198           // the kURLMime flavour which is not desirable (eg. when pasting into Notepad)
00199           rv = AppendString(trans, plaintextBuffer, kUnicodeMime);
00200           NS_ENSURE_SUCCESS(rv, rv);
00201         }
00202 
00203         // Try and get source URI of the items that are being dragged
00204         nsIURI *uri = aDoc->GetDocumentURI();
00205         if (uri) {
00206           nsCAutoString spec;
00207           uri->GetSpec(spec);
00208           if (!spec.IsEmpty()) {
00209             nsAutoString shortcut;
00210             AppendUTF8toUTF16(spec, shortcut);
00211 
00212             // Add the URL DataFlavor to the transferable. Don't use kURLMime,
00213             // as it will cause an unnecessary UniformResourceLocator to be
00214             // added which confuses some apps eg. Outlook 2000 - (See Bug
00215             // 315370). Don't use kURLDataMime, as it will cause a bogus
00216             // 'url ' flavor to show up on the Mac clipboard, confusing other
00217             // apps, like Terminal (see Bug 336012)
00218             rv = AppendString(trans, shortcut, kURLPrivateMime);
00219             NS_ENSURE_SUCCESS(rv, rv);
00220           }
00221         }
00222       }
00223       else
00224       {
00225         if (!textBuffer.IsEmpty())
00226         {
00227          // Add the unicode DataFlavor to the transferable
00228           rv = AppendString(trans, textBuffer, kUnicodeMime);
00229           NS_ENSURE_SUCCESS(rv, rv);
00230         }
00231       }
00232 
00233       PRBool doPutOnClipboard = PR_TRUE;
00234       DoHooks(aDoc, trans, &doPutOnClipboard);
00235 
00236       // put the transferable on the clipboard
00237       if (doPutOnClipboard)
00238         clipboard->SetData(trans, nsnull, aClipboardID);
00239     }
00240   }
00241   return rv;
00242 }
00243 
00244 nsresult nsCopySupport::DoHooks(nsIDocument *aDoc, nsITransferable *aTrans,
00245                                 PRBool *aDoPutOnClipboard)
00246 {
00247   NS_ENSURE_ARG(aDoc);
00248 
00249   *aDoPutOnClipboard = PR_TRUE;
00250 
00251   nsCOMPtr<nsISupports> container = aDoc->GetContainer();
00252   nsCOMPtr<nsIClipboardDragDropHookList> hookObj = do_GetInterface(container);
00253   if (!hookObj) return NS_ERROR_FAILURE;
00254 
00255   nsCOMPtr<nsISimpleEnumerator> enumerator;
00256   hookObj->GetHookEnumerator(getter_AddRefs(enumerator));
00257   if (!enumerator) return NS_ERROR_FAILURE;
00258 
00259   // the logic here should follow the behavior specified in
00260   // nsIClipboardDragDropHooks.h
00261 
00262   nsCOMPtr<nsIClipboardDragDropHooks> override;
00263   nsCOMPtr<nsISupports> isupp;
00264   PRBool hasMoreHooks = PR_FALSE;
00265   nsresult rv = NS_OK;
00266   while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMoreHooks))
00267          && hasMoreHooks)
00268   {
00269     rv = enumerator->GetNext(getter_AddRefs(isupp));
00270     if (NS_FAILED(rv)) break;
00271     override = do_QueryInterface(isupp);
00272     if (override)
00273     {
00274 #ifdef DEBUG
00275       nsresult hookResult =
00276 #endif
00277       override->OnCopyOrDrag(nsnull, aTrans, aDoPutOnClipboard);
00278       NS_ASSERTION(NS_SUCCEEDED(hookResult), "OnCopyOrDrag hook failed");
00279       if (!*aDoPutOnClipboard)
00280         break;
00281     }
00282   }
00283 
00284   return rv;
00285 }
00286 
00287 nsresult nsCopySupport::IsPlainTextContext(nsISelection *aSel, nsIDocument *aDoc, PRBool *aIsPlainTextContext)
00288 {
00289   nsresult rv;
00290 
00291   if (!aSel || !aIsPlainTextContext)
00292     return NS_ERROR_NULL_POINTER;
00293 
00294   *aIsPlainTextContext = PR_FALSE;
00295   
00296   nsCOMPtr<nsIDOMRange> range;
00297   nsCOMPtr<nsIDOMNode> commonParent;
00298   PRInt32 count = 0;
00299 
00300   rv = aSel->GetRangeCount(&count);
00301   NS_ENSURE_SUCCESS(rv, rv);
00302 
00303   // if selection is uninitialized return
00304   if (!count)
00305     return NS_ERROR_FAILURE;
00306   
00307   // we'll just use the common parent of the first range.  Implicit assumption
00308   // here that multi-range selections are table cell selections, in which case
00309   // the common parent is somewhere in the table and we don't really care where.
00310   rv = aSel->GetRangeAt(0, getter_AddRefs(range));
00311   NS_ENSURE_SUCCESS(rv, rv);
00312   if (!range)
00313     return NS_ERROR_NULL_POINTER;
00314   range->GetCommonAncestorContainer(getter_AddRefs(commonParent));
00315 
00316   for (nsCOMPtr<nsIContent> selContent(do_QueryInterface(commonParent));
00317        selContent;
00318        selContent = selContent->GetParent())
00319   {
00320     // checking for selection inside a plaintext form widget
00321 
00322     if (!selContent->IsContentOfType(nsIContent::eHTML)) {
00323       continue;
00324     }
00325 
00326     nsIAtom *atom = selContent->Tag();
00327 
00328     if (atom == nsHTMLAtoms::input ||
00329         atom == nsHTMLAtoms::textarea)
00330     {
00331       *aIsPlainTextContext = PR_TRUE;
00332       break;
00333     }
00334 
00335     if (atom == nsHTMLAtoms::body)
00336     {
00337       // check for moz prewrap style on body.  If it's there we are 
00338       // in a plaintext editor.  This is pretty cheezy but I haven't 
00339       // found a good way to tell if we are in a plaintext editor.
00340       nsCOMPtr<nsIDOMElement> bodyElem = do_QueryInterface(selContent);
00341       nsAutoString wsVal;
00342       rv = bodyElem->GetAttribute(NS_LITERAL_STRING("style"), wsVal);
00343       if (NS_SUCCEEDED(rv) && (kNotFound != wsVal.Find(NS_LITERAL_STRING("-moz-pre-wrap"))))
00344       {
00345         *aIsPlainTextContext = PR_TRUE;
00346         break;
00347       }
00348     }
00349   }
00350   
00351   // also consider ourselves in a text widget if we can't find an html
00352   // document. Note that XHTML is not counted as HTML here, because we can't
00353   // copy it properly (all the copy code for non-plaintext assumes using HTML
00354   // serializers and parsers is OK, and those mess up XHTML).
00355   nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(aDoc);
00356   if (!htmlDoc || aDoc->IsCaseSensitive())
00357     *aIsPlainTextContext = PR_TRUE;
00358 
00359   return NS_OK;
00360 }
00361 
00362 nsresult
00363 nsCopySupport::GetContents(const nsACString& aMimeType, PRUint32 aFlags, nsISelection *aSel, nsIDocument *aDoc, nsAString& outdata)
00364 {
00365   nsresult rv = NS_OK;
00366   
00367   nsCOMPtr<nsIDocumentEncoder> docEncoder;
00368 
00369   nsCAutoString encoderContractID(NS_DOC_ENCODER_CONTRACTID_BASE);
00370   encoderContractID.Append(aMimeType);
00371     
00372   docEncoder = do_CreateInstance(encoderContractID.get());
00373   NS_ENSURE_TRUE(docEncoder, NS_ERROR_FAILURE);
00374 
00375   PRUint32 flags = aFlags;
00376   
00377   if (aMimeType.Equals("text/plain"))
00378     flags |= nsIDocumentEncoder::OutputPreformatted;
00379 
00380   NS_ConvertASCIItoUCS2 unicodeMimeType(aMimeType);
00381   rv = docEncoder->Init(aDoc, unicodeMimeType, flags);
00382   if (NS_FAILED(rv)) return rv;
00383   
00384   if (aSel)
00385   {
00386     rv = docEncoder->SetSelection(aSel);
00387     if (NS_FAILED(rv)) return rv;
00388   } 
00389   
00390   // encode the selection
00391   return docEncoder->EncodeToString(outdata);
00392 }
00393 
00394 
00395 nsresult
00396 nsCopySupport::ImageCopy(nsIImageLoadingContent* aImageElement,
00397                          PRInt32 aCopyFlags)
00398 {
00399   nsresult rv;
00400 
00401   // create a transferable for putting data on the Clipboard
00402   nsCOMPtr<nsITransferable> trans(do_CreateInstance(kCTransferableCID, &rv));
00403   NS_ENSURE_SUCCESS(rv, rv);
00404 
00405   if (aCopyFlags & nsIContentViewerEdit::COPY_IMAGE_TEXT) {
00406     // get the location from the element
00407     nsCOMPtr<nsIURI> uri;
00408     rv = aImageElement->GetCurrentURI(getter_AddRefs(uri));
00409     NS_ENSURE_SUCCESS(rv, rv);
00410     NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
00411 
00412     nsCAutoString location;
00413     rv = uri->GetSpec(location);
00414     NS_ENSURE_SUCCESS(rv, rv);
00415 
00416     // append the string to the transferable
00417     rv = AppendString(trans, NS_ConvertUTF8toUTF16(location), kUnicodeMime);
00418     NS_ENSURE_SUCCESS(rv, rv);
00419   }
00420 
00421   if (aCopyFlags & nsIContentViewerEdit::COPY_IMAGE_HTML) {
00422     // append HTML data to the transferable
00423     nsCOMPtr<nsIDOMNode> node(do_QueryInterface(aImageElement, &rv));
00424     NS_ENSURE_SUCCESS(rv, rv);
00425 
00426     rv = AppendDOMNode(trans, node);
00427     NS_ENSURE_SUCCESS(rv, rv);
00428   }
00429 
00430   if (aCopyFlags & nsIContentViewerEdit::COPY_IMAGE_DATA) {
00431     // get the image data from the element
00432     nsCOMPtr<nsIImage> image =
00433       nsContentUtils::GetImageFromContent(aImageElement);
00434     NS_ENSURE_TRUE(image, NS_ERROR_FAILURE);
00435 
00436     nsCOMPtr<nsISupportsInterfacePointer>
00437       imgPtr(do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID, &rv));
00438     NS_ENSURE_SUCCESS(rv, rv);
00439 
00440     rv = imgPtr->SetData(image);
00441     NS_ENSURE_SUCCESS(rv, rv);
00442 
00443     // copy the image data onto the transferable
00444     rv = trans->SetTransferData(kNativeImageMime, imgPtr,
00445                                 sizeof(nsISupports*));
00446     NS_ENSURE_SUCCESS(rv, rv);
00447   }
00448 
00449   // get clipboard
00450   nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
00451   NS_ENSURE_SUCCESS(rv, rv);
00452 
00453   // check whether the system supports the selection clipboard or not.
00454   PRBool selectionSupported;
00455   rv = clipboard->SupportsSelectionClipboard(&selectionSupported);
00456   NS_ENSURE_SUCCESS(rv, rv);
00457 
00458   // put the transferable on the clipboard
00459   if (selectionSupported) {
00460     rv = clipboard->SetData(trans, nsnull, nsIClipboard::kSelectionClipboard);
00461     NS_ENSURE_SUCCESS(rv, rv);
00462   }
00463 
00464   return clipboard->SetData(trans, nsnull, nsIClipboard::kGlobalClipboard);
00465 }
00466 
00467 static nsresult AppendString(nsITransferable *aTransferable,
00468                              const nsAString& aString,
00469                              const char* aFlavor)
00470 {
00471   nsresult rv;
00472 
00473   nsCOMPtr<nsISupportsString>
00474     data(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv));
00475   NS_ENSURE_SUCCESS(rv, rv);
00476 
00477   rv = data->SetData(aString);
00478   NS_ENSURE_SUCCESS(rv, rv);
00479 
00480   rv = aTransferable->AddDataFlavor(aFlavor);
00481   NS_ENSURE_SUCCESS(rv, rv);
00482 
00483   return aTransferable->SetTransferData(aFlavor, data,
00484                                         aString.Length() * sizeof(PRUnichar));
00485 }
00486 
00487 static nsresult AppendDOMNode(nsITransferable *aTransferable,
00488                               nsIDOMNode *aDOMNode)
00489 {
00490   nsresult rv;
00491   
00492   // selializer
00493   nsCOMPtr<nsIDocumentEncoder>
00494     docEncoder(do_CreateInstance(NS_HTMLCOPY_ENCODER_CONTRACTID, &rv));
00495   NS_ENSURE_SUCCESS(rv, rv);
00496 
00497   // get document for the encoder
00498   nsCOMPtr<nsIDOMDocument> domDocument;
00499   rv = aDOMNode->GetOwnerDocument(getter_AddRefs(domDocument));
00500   NS_ENSURE_SUCCESS(rv, rv);
00501   nsCOMPtr<nsIDocument> document(do_QueryInterface(domDocument, &rv));
00502   NS_ENSURE_SUCCESS(rv, rv);
00503 
00504   // Note that XHTML is not counted as HTML here, because we can't copy it
00505   // properly (all the copy code for non-plaintext assumes using HTML
00506   // serializers and parsers is OK, and those mess up XHTML).
00507   nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(document, &rv);
00508   NS_ENSURE_SUCCESS(rv, NS_OK);
00509   NS_ENSURE_TRUE(!(document->IsCaseSensitive()), NS_OK);
00510 
00511   // init encoder with document and node
00512   rv = docEncoder->Init(document, NS_LITERAL_STRING(kHTMLMime),
00513                         nsIDocumentEncoder::OutputAbsoluteLinks |
00514                         nsIDocumentEncoder::OutputEncodeW3CEntities);
00515   NS_ENSURE_SUCCESS(rv, rv);
00516 
00517   rv = docEncoder->SetNode(aDOMNode);
00518   NS_ENSURE_SUCCESS(rv, rv);
00519 
00520   // serialize to string
00521   nsAutoString html, context, info;
00522   rv = docEncoder->EncodeToStringWithContext(html, context, info);
00523   NS_ENSURE_SUCCESS(rv, rv);
00524 
00525   // copy them to the transferable
00526   if (!html.IsEmpty()) {
00527     rv = AppendString(aTransferable, html, kHTMLMime);
00528     NS_ENSURE_SUCCESS(rv, rv);
00529   }
00530 
00531   if (!info.IsEmpty()) {
00532     rv = AppendString(aTransferable, info, kHTMLInfo);
00533     NS_ENSURE_SUCCESS(rv, rv);
00534   }
00535 
00536   // add a special flavor, even if we don't have html context data
00537   return AppendString(aTransferable, context, kHTMLContext);
00538 }