Back to index

lightning-sunbird  0.9+nobinonly
nsPlaintextDataTransfer.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  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 
00039 #include "nsPlaintextEditor.h"
00040 
00041 #include "nsIDOMDocument.h"
00042 #include "nsIDocument.h"
00043 #include "nsIContent.h"
00044 #include "nsIFormControl.h"
00045 #include "nsIDOMEventReceiver.h" 
00046 #include "nsIDOMNSEvent.h"
00047 #include "nsIDOMMouseEvent.h"
00048 #include "nsISelection.h"
00049 #include "nsCRT.h"
00050 #include "nsServiceManagerUtils.h"
00051 
00052 #include "nsIDOMRange.h"
00053 #include "nsIDOMNSRange.h"
00054 #include "nsISupportsArray.h"
00055 #include "nsIDocumentEncoder.h"
00056 #include "nsISupportsPrimitives.h"
00057 
00058 // Drag & Drop, Clipboard
00059 #include "nsIClipboard.h"
00060 #include "nsITransferable.h"
00061 #include "nsIDragService.h"
00062 #include "nsIDOMNSUIEvent.h"
00063 
00064 // Misc
00065 #include "nsEditorUtils.h"
00066 
00067 
00068 NS_IMETHODIMP nsPlaintextEditor::PrepareTransferable(nsITransferable **transferable)
00069 {
00070   // Create generic Transferable for getting the data
00071   nsresult rv = CallCreateInstance("@mozilla.org/widget/transferable;1", transferable);
00072   if (NS_FAILED(rv))
00073     return rv;
00074 
00075   // Get the nsITransferable interface for getting the data from the clipboard
00076   if (transferable) (*transferable)->AddDataFlavor(kUnicodeMime);
00077   return NS_OK;
00078 }
00079 
00080 nsresult nsPlaintextEditor::InsertTextAt(const nsAString &aStringToInsert,
00081                                          nsIDOMNode *aDestinationNode,
00082                                          PRInt32 aDestOffset,
00083                                          PRBool aDoDeleteSelection)
00084 {
00085   if (aDestinationNode)
00086   {
00087     nsresult res;
00088     nsCOMPtr<nsISelection>selection;
00089     res = GetSelection(getter_AddRefs(selection));
00090     NS_ENSURE_SUCCESS(res, res);
00091 
00092     nsCOMPtr<nsIDOMNode> targetNode = aDestinationNode;
00093     PRInt32 targetOffset = aDestOffset;
00094 
00095     if (aDoDeleteSelection)
00096     {
00097       // Use an auto tracker so that our drop point is correctly
00098       // positioned after the delete.
00099       nsAutoTrackDOMPoint tracker(mRangeUpdater, &targetNode, &targetOffset);
00100       res = DeleteSelection(eNone);
00101       NS_ENSURE_SUCCESS(res, res);
00102     }
00103 
00104     res = selection->Collapse(targetNode, targetOffset);
00105     NS_ENSURE_SUCCESS(res, res);
00106   }
00107 
00108   return InsertText(aStringToInsert);
00109 }
00110 
00111 NS_IMETHODIMP nsPlaintextEditor::InsertTextFromTransferable(nsITransferable *aTransferable,
00112                                                             nsIDOMNode *aDestinationNode,
00113                                                             PRInt32 aDestOffset,
00114                                                             PRBool aDoDeleteSelection)
00115 {
00116   nsresult rv = NS_OK;
00117   char* bestFlavor = nsnull;
00118   nsCOMPtr<nsISupports> genericDataObj;
00119   PRUint32 len = 0;
00120   if (NS_SUCCEEDED(aTransferable->GetAnyTransferData(&bestFlavor, getter_AddRefs(genericDataObj), &len))
00121       && bestFlavor && 0 == nsCRT::strcmp(bestFlavor, kUnicodeMime))
00122   {
00123     nsAutoTxnsConserveSelection dontSpazMySelection(this);
00124     nsCOMPtr<nsISupportsString> textDataObj ( do_QueryInterface(genericDataObj) );
00125     if (textDataObj && len > 0)
00126     {
00127       nsAutoString stuffToPaste;
00128       textDataObj->GetData(stuffToPaste);
00129       NS_ASSERTION(stuffToPaste.Length() <= (len/2), "Invalid length!");
00130       nsAutoEditBatch beginBatching(this);
00131       rv = InsertTextAt(stuffToPaste, aDestinationNode, aDestOffset, aDoDeleteSelection);
00132     }
00133   }
00134   NS_Free(bestFlavor);
00135       
00136   // Try to scroll the selection into view if the paste/drop succeeded
00137   if (NS_SUCCEEDED(rv))
00138     ScrollSelectionIntoView(PR_FALSE);
00139 
00140   return rv;
00141 }
00142 
00143 NS_IMETHODIMP nsPlaintextEditor::InsertFromDrop(nsIDOMEvent* aDropEvent)
00144 {
00145   ForceCompositionEnd();
00146   
00147   nsresult rv;
00148   nsCOMPtr<nsIDragService> dragService = 
00149            do_GetService("@mozilla.org/widget/dragservice;1", &rv);
00150   if (NS_FAILED(rv)) return rv;
00151 
00152   nsCOMPtr<nsIDragSession> dragSession;
00153   dragService->GetCurrentSession(getter_AddRefs(dragSession));
00154   if (!dragSession) return NS_OK;
00155 
00156   // Current doc is destination
00157   nsCOMPtr<nsIDOMDocument> destdomdoc; 
00158   rv = GetDocument(getter_AddRefs(destdomdoc)); 
00159   if (NS_FAILED(rv)) return rv;
00160 
00161   // transferable hooks
00162   if (!nsEditorHookUtils::DoAllowDropHook(destdomdoc, aDropEvent, dragSession))
00163     return NS_OK;
00164 
00165   // Get the nsITransferable interface for getting the data from the drop
00166   nsCOMPtr<nsITransferable> trans;
00167   rv = PrepareTransferable(getter_AddRefs(trans));
00168   if (NS_FAILED(rv)) return rv;
00169   if (!trans) return NS_OK;  // NS_ERROR_FAILURE; SHOULD WE FAIL?
00170 
00171   PRUint32 numItems = 0; 
00172   rv = dragSession->GetNumDropItems(&numItems);
00173   if (NS_FAILED(rv)) return rv;
00174   if (numItems < 1) return NS_ERROR_FAILURE;  // nothing to drop?
00175 
00176   // Combine any deletion and drop insertion into one transaction
00177   nsAutoEditBatch beginBatching(this);
00178 
00179   PRBool deleteSelection = PR_FALSE;
00180 
00181   // We have to figure out whether to delete and relocate caret only once
00182   // Parent and offset are under the mouse cursor
00183   nsCOMPtr<nsIDOMNSUIEvent> nsuiEvent (do_QueryInterface(aDropEvent));
00184   if (!nsuiEvent) return NS_ERROR_FAILURE;
00185 
00186   nsCOMPtr<nsIDOMNode> newSelectionParent;
00187   rv = nsuiEvent->GetRangeParent(getter_AddRefs(newSelectionParent));
00188   if (NS_FAILED(rv)) return rv;
00189   if (!newSelectionParent) return NS_ERROR_FAILURE;
00190 
00191   PRInt32 newSelectionOffset;
00192   rv = nsuiEvent->GetRangeOffset(&newSelectionOffset);
00193   if (NS_FAILED(rv)) return rv;
00194 
00195   nsCOMPtr<nsISelection> selection;
00196   rv = GetSelection(getter_AddRefs(selection));
00197   if (NS_FAILED(rv)) return rv;
00198   if (!selection) return NS_ERROR_FAILURE;
00199 
00200   PRBool isCollapsed;
00201   rv = selection->GetIsCollapsed(&isCollapsed);
00202   if (NS_FAILED(rv)) return rv;
00203 
00204   // Check if mouse is in the selection
00205   // if so, jump through some hoops to determine if mouse is over selection (bail)
00206   // and whether user wants to copy selection or delete it
00207   if (!isCollapsed)
00208   {
00209     // We never have to delete if selection is already collapsed
00210     PRBool cursorIsInSelection = PR_FALSE;
00211 
00212     PRInt32 rangeCount;
00213     rv = selection->GetRangeCount(&rangeCount);
00214     if (NS_FAILED(rv)) return rv;
00215 
00216     for (PRInt32 j = 0; j < rangeCount; j++)
00217     {
00218       nsCOMPtr<nsIDOMRange> range;
00219       rv = selection->GetRangeAt(j, getter_AddRefs(range));
00220       nsCOMPtr<nsIDOMNSRange> nsrange(do_QueryInterface(range));
00221       if (NS_FAILED(rv) || !nsrange) 
00222         continue;  // don't bail yet, iterate through them all
00223 
00224       rv = nsrange->IsPointInRange(newSelectionParent, newSelectionOffset, &cursorIsInSelection);
00225       if (cursorIsInSelection)
00226         break;
00227     }
00228 
00229     // Source doc is null if source is *not* the current editor document
00230     // Current doc is destination (set earlier)
00231     nsCOMPtr<nsIDOMDocument> srcdomdoc;
00232     rv = dragSession->GetSourceDocument(getter_AddRefs(srcdomdoc));
00233     if (NS_FAILED(rv)) return rv;
00234 
00235     if (cursorIsInSelection)
00236     {
00237       // Dragging within same doc can't drop on itself -- leave!
00238       if (srcdomdoc == destdomdoc)
00239         return NS_OK;
00240 
00241       // Dragging from another window onto a selection
00242       // XXX Decision made to NOT do this,
00243       //     note that 4.x does replace if dropped on
00244       //deleteSelection = PR_TRUE;
00245     }
00246     else 
00247     {
00248       // We are NOT over the selection
00249       if (srcdomdoc == destdomdoc)
00250       {
00251         // Within the same doc: delete if user doesn't want to copy
00252  
00253         // check if the user pressed the key to force a copy rather than a move
00254         // if we run into problems here, we'll just assume the user doesn't want a copy
00255         PRBool userWantsCopy = PR_FALSE;
00256 
00257         nsCOMPtr<nsIDOMMouseEvent> mouseEvent ( do_QueryInterface(aDropEvent) );
00258         if (mouseEvent)
00259 #if defined(XP_MAC) || defined(XP_MACOSX)
00260           mouseEvent->GetAltKey(&userWantsCopy);
00261 #else
00262           mouseEvent->GetCtrlKey(&userWantsCopy);
00263 #endif
00264 
00265         deleteSelection = !userWantsCopy;
00266       }
00267       else
00268       {
00269         // Different source doc: Don't delete
00270         deleteSelection = PR_FALSE;
00271       }
00272     }
00273   }
00274 
00275   nsCOMPtr<nsIContent> newSelectionContent =
00276     do_QueryInterface(newSelectionParent);
00277   nsIContent *content = newSelectionContent;
00278 
00279   while (content) {
00280     nsCOMPtr<nsIFormControl> formControl(do_QueryInterface(content));
00281 
00282     if (formControl && !formControl->AllowDrop()) {
00283       // Don't allow dropping into a form control that doesn't allow being
00284       // dropped into.
00285 
00286       return NS_OK;
00287     }
00288 
00289     content = content->GetParent();
00290   }
00291 
00292   PRUint32 i; 
00293   for (i = 0; i < numItems; ++i)
00294   {
00295     rv = dragSession->GetData(trans, i);
00296     if (NS_FAILED(rv)) return rv;
00297     if (!trans) return NS_OK; // NS_ERROR_FAILURE; Should we fail?
00298 
00299     if (!nsEditorHookUtils::DoInsertionHook(destdomdoc, aDropEvent, trans))
00300       return NS_OK;
00301 
00302     rv = InsertTextFromTransferable(trans, newSelectionParent, newSelectionOffset, deleteSelection);
00303   }
00304 
00305   return rv;
00306 }
00307 
00308 NS_IMETHODIMP nsPlaintextEditor::CanDrag(nsIDOMEvent *aDragEvent, PRBool *aCanDrag)
00309 {
00310   if (!aCanDrag)
00311     return NS_ERROR_NULL_POINTER;
00312   /* we really should be checking the XY coordinates of the mouseevent and ensure that
00313    * that particular point is actually within the selection (not just that there is a selection)
00314    */
00315   *aCanDrag = PR_FALSE;
00316  
00317   // KLUDGE to work around bug 50703
00318   // After double click and object property editing, 
00319   //  we get a spurious drag event
00320   if (mIgnoreSpuriousDragEvent)
00321   {
00322     mIgnoreSpuriousDragEvent = PR_FALSE;
00323     return NS_OK;
00324   }
00325    
00326   nsCOMPtr<nsISelection> selection;
00327   nsresult res = GetSelection(getter_AddRefs(selection));
00328   if (NS_FAILED(res)) return res;
00329     
00330   PRBool isCollapsed;
00331   res = selection->GetIsCollapsed(&isCollapsed);
00332   if (NS_FAILED(res)) return res;
00333   
00334   // if we are collapsed, we have no selection so nothing to drag
00335   if ( isCollapsed )
00336     return NS_OK;
00337 
00338   nsCOMPtr<nsIDOMEventTarget> eventTarget;
00339 
00340   nsCOMPtr<nsIDOMNSEvent> nsevent(do_QueryInterface(aDragEvent));
00341   if (nsevent) {
00342     res = nsevent->GetTmpRealOriginalTarget(getter_AddRefs(eventTarget));
00343     if (NS_FAILED(res)) return res;
00344   }
00345 
00346   if (eventTarget)
00347   {
00348     nsCOMPtr<nsIDOMNode> eventTargetDomNode = do_QueryInterface(eventTarget);
00349     if ( eventTargetDomNode )
00350     {
00351       PRBool isTargetedCorrectly = PR_FALSE;
00352       res = selection->ContainsNode(eventTargetDomNode, PR_FALSE, &isTargetedCorrectly);
00353       if (NS_FAILED(res)) return res;
00354 
00355       *aCanDrag = isTargetedCorrectly;
00356     }
00357   }
00358 
00359   if (NS_FAILED(res)) return res;
00360   if (!*aCanDrag) return NS_OK;
00361 
00362   nsCOMPtr<nsIDOMDocument> domdoc;
00363   GetDocument(getter_AddRefs(domdoc));
00364   *aCanDrag = nsEditorHookUtils::DoAllowDragHook(domdoc, aDragEvent);
00365   return NS_OK;
00366 }
00367 
00368 NS_IMETHODIMP nsPlaintextEditor::DoDrag(nsIDOMEvent *aDragEvent)
00369 {
00370   nsresult rv;
00371 
00372   nsCOMPtr<nsITransferable> trans;
00373   rv = PutDragDataInTransferable(getter_AddRefs(trans));
00374   if (NS_FAILED(rv)) return rv;
00375   if (!trans) return NS_OK; // maybe there was nothing to copy?
00376 
00377  /* get the drag service */
00378   nsCOMPtr<nsIDragService> dragService = 
00379            do_GetService("@mozilla.org/widget/dragservice;1", &rv);
00380   if (NS_FAILED(rv)) return rv;
00381 
00382   /* create an array of transferables */
00383   nsCOMPtr<nsISupportsArray> transferableArray;
00384   NS_NewISupportsArray(getter_AddRefs(transferableArray));
00385   if (!transferableArray)
00386     return NS_ERROR_OUT_OF_MEMORY;
00387 
00388   /* add the transferable to the array */
00389   rv = transferableArray->AppendElement(trans);
00390   if (NS_FAILED(rv)) return rv;
00391 
00392   // check our transferable hooks (if any)
00393   nsCOMPtr<nsIDOMDocument> domdoc;
00394   GetDocument(getter_AddRefs(domdoc));
00395   if (!nsEditorHookUtils::DoDragHook(domdoc, aDragEvent, trans))
00396     return NS_OK;
00397 
00398   /* invoke drag */
00399   nsCOMPtr<nsIDOMEventTarget> eventTarget;
00400   rv = aDragEvent->GetTarget(getter_AddRefs(eventTarget));
00401   if (NS_FAILED(rv)) return rv;
00402   nsCOMPtr<nsIDOMNode> domnode = do_QueryInterface(eventTarget);
00403 
00404   unsigned int flags;
00405   // in some cases we'll want to cut rather than copy... hmmmmm...
00406   flags = nsIDragService::DRAGDROP_ACTION_COPY + nsIDragService::DRAGDROP_ACTION_MOVE;
00407 
00408   rv = dragService->InvokeDragSession(domnode, transferableArray, nsnull, flags);
00409   if (NS_FAILED(rv)) return rv;
00410 
00411   aDragEvent->StopPropagation();
00412 
00413   return rv;
00414 }
00415 
00416 NS_IMETHODIMP nsPlaintextEditor::Paste(PRInt32 aSelectionType)
00417 {
00418   ForceCompositionEnd();
00419 
00420   // Get Clipboard Service
00421   nsresult rv;
00422   nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1", &rv));
00423   if ( NS_FAILED(rv) )
00424     return rv;
00425 
00426   // Get the nsITransferable interface for getting the data from the clipboard
00427   nsCOMPtr<nsITransferable> trans;
00428   rv = PrepareTransferable(getter_AddRefs(trans));
00429   if (NS_SUCCEEDED(rv) && trans)
00430   {
00431     // Get the Data from the clipboard  
00432     if (NS_SUCCEEDED(clipboard->GetData(trans, aSelectionType)) && IsModifiable())
00433     {
00434       // handle transferable hooks
00435       nsCOMPtr<nsIDOMDocument> domdoc;
00436       GetDocument(getter_AddRefs(domdoc));
00437       if (!nsEditorHookUtils::DoInsertionHook(domdoc, nsnull, trans))
00438         return NS_OK;
00439 
00440       rv = InsertTextFromTransferable(trans, nsnull, nsnull, PR_TRUE);
00441     }
00442   }
00443 
00444   return rv;
00445 }
00446 
00447 
00448 NS_IMETHODIMP nsPlaintextEditor::CanPaste(PRInt32 aSelectionType, PRBool *aCanPaste)
00449 {
00450   if (!aCanPaste)
00451     return NS_ERROR_NULL_POINTER;
00452   *aCanPaste = PR_FALSE;
00453   
00454   // can't paste if readonly
00455   if (!IsModifiable())
00456     return NS_OK;
00457 
00458   nsresult rv;
00459   nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1", &rv));
00460   if (NS_FAILED(rv)) return rv;
00461   
00462   // the flavors that we can deal with
00463   const char* const textEditorFlavors[] = { kUnicodeMime, nsnull };
00464 
00465   nsCOMPtr<nsISupportsArray> flavorsList = do_CreateInstance(NS_SUPPORTSARRAY_CONTRACTID);
00466 
00467   PRUint32 editorFlags;
00468   GetFlags(&editorFlags);
00469   
00470   // add the flavors for text editors
00471   for (const char* const* flavor = textEditorFlavors; *flavor; flavor++)
00472   {
00473     nsCOMPtr<nsISupportsCString> flavorString =
00474         do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID);
00475     if (flavorString)
00476     {
00477       flavorString->SetData(nsDependentCString(*flavor));
00478       flavorsList->AppendElement(flavorString);
00479     }
00480   }
00481   
00482   PRBool haveFlavors;
00483   rv = clipboard->HasDataMatchingFlavors(flavorsList, aSelectionType, &haveFlavors);
00484   if (NS_FAILED(rv)) return rv;
00485   
00486   *aCanPaste = haveFlavors;
00487   return NS_OK;
00488 }
00489 
00490 nsresult
00491 nsPlaintextEditor::SetupDocEncoder(nsIDocumentEncoder **aDocEncoder)
00492 {
00493   nsCOMPtr<nsIDOMDocument> domdoc;
00494   nsresult rv = GetDocument(getter_AddRefs(domdoc));
00495   if (NS_FAILED(rv)) return rv;
00496        
00497   nsCOMPtr<nsIDocument> doc = do_QueryInterface(domdoc);
00498   if (!doc) return NS_ERROR_FAILURE;
00499 
00500   // find out if we're a plaintext control or not
00501   PRUint32 editorFlags = 0;
00502   rv = GetFlags(&editorFlags);
00503   if (NS_FAILED(rv)) return rv;
00504 
00505   PRBool bIsPlainTextControl = ((editorFlags & eEditorPlaintextMask) != 0);
00506 
00507   // get correct mimeType and document encoder flags set
00508   nsAutoString mimeType;
00509   PRUint32 docEncoderFlags = 0;
00510   if (bIsPlainTextControl)
00511   {
00512     docEncoderFlags |= nsIDocumentEncoder::OutputBodyOnly | nsIDocumentEncoder::OutputPreformatted;
00513     mimeType.AssignLiteral(kUnicodeMime);
00514   }
00515   else
00516     mimeType.AssignLiteral(kHTMLMime);
00517 
00518   // set up docEncoder
00519   nsCOMPtr<nsIDocumentEncoder> encoder = do_CreateInstance(NS_HTMLCOPY_ENCODER_CONTRACTID);
00520   if (!encoder)
00521     return NS_ERROR_OUT_OF_MEMORY;
00522 
00523   rv = encoder->Init(doc, mimeType, docEncoderFlags);
00524   if (NS_FAILED(rv)) return rv;
00525     
00526   /* get the selection to be dragged */
00527   nsCOMPtr<nsISelection> selection;
00528   rv = GetSelection(getter_AddRefs(selection));
00529   if (NS_FAILED(rv)) return rv;
00530 
00531   rv = encoder->SetSelection(selection);
00532   if (NS_FAILED(rv)) return rv;
00533 
00534   *aDocEncoder = encoder;
00535   NS_ADDREF(*aDocEncoder);
00536   return NS_OK;
00537 }
00538 
00539 nsresult
00540 nsPlaintextEditor::PutDragDataInTransferable(nsITransferable **aTransferable)
00541 {
00542   *aTransferable = nsnull;
00543   nsCOMPtr<nsIDocumentEncoder> docEncoder;
00544   nsresult rv = SetupDocEncoder(getter_AddRefs(docEncoder));
00545   if (NS_FAILED(rv)) return rv;
00546 
00547   // grab a string
00548   nsAutoString buffer;
00549   rv = docEncoder->EncodeToString(buffer);
00550   if (NS_FAILED(rv)) return rv;
00551 
00552   // if we have an empty string, we're done; otherwise continue
00553   if (buffer.IsEmpty())
00554     return NS_OK;
00555 
00556   nsCOMPtr<nsISupportsString> dataWrapper =
00557                         do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
00558   NS_ENSURE_SUCCESS(rv, rv);
00559 
00560   rv = dataWrapper->SetData(buffer);
00561   if (NS_FAILED(rv)) return rv;
00562 
00563   /* create html flavor transferable */
00564   nsCOMPtr<nsITransferable> trans = do_CreateInstance("@mozilla.org/widget/transferable;1", &rv);
00565   NS_ENSURE_SUCCESS(rv, rv);
00566 
00567   // find out if we're a plaintext control or not
00568   PRUint32 editorFlags = 0;
00569   rv = GetFlags(&editorFlags);
00570   if (NS_FAILED(rv)) return rv;
00571 
00572   PRBool bIsPlainTextControl = ((editorFlags & eEditorPlaintextMask) != 0);
00573   if (bIsPlainTextControl)
00574   {
00575     // Add the unicode flavor to the transferable
00576     rv = trans->AddDataFlavor(kUnicodeMime);
00577     if (NS_FAILED(rv)) return rv;
00578   }
00579   else
00580   {
00581     rv = trans->AddDataFlavor(kHTMLMime);
00582     if (NS_FAILED(rv)) return rv;
00583 
00584     nsCOMPtr<nsIFormatConverter> htmlConverter = do_CreateInstance("@mozilla.org/widget/htmlformatconverter;1");
00585     NS_ENSURE_TRUE(htmlConverter, NS_ERROR_FAILURE);
00586 
00587     rv = trans->SetConverter(htmlConverter);
00588     if (NS_FAILED(rv)) return rv;
00589   }
00590 
00591   // QI the data object an |nsISupports| so that when the transferable holds
00592   // onto it, it will addref the correct interface.
00593   nsCOMPtr<nsISupports> nsisupportsDataWrapper = do_QueryInterface(dataWrapper);
00594   rv = trans->SetTransferData(bIsPlainTextControl ? kUnicodeMime : kHTMLMime,
00595                    nsisupportsDataWrapper, buffer.Length() * sizeof(PRUnichar));
00596   if (NS_FAILED(rv)) return rv;
00597 
00598   *aTransferable = trans;
00599   NS_ADDREF(*aTransferable);
00600   return NS_OK;
00601 }