Back to index

lightning-sunbird  0.9+nobinonly
nsClipboard.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 the GNU General Public License Version 2 or later (the "GPL"), or
00026  * 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 // Mike Pinkerton
00040 // Netscape Communications
00041 //
00042 // See header file for details.
00043 //
00044 // Remaining Work:
00045 // * only convert data to clipboard on an app switch.
00046 //
00047 
00048 #include "nsCOMPtr.h"
00049 #include "nsClipboard.h"
00050 
00051 #include "nsIClipboardOwner.h"
00052 #include "nsString.h"
00053 #include "nsIFormatConverter.h"
00054 #include "nsMimeMapper.h"
00055 
00056 #include "nsIComponentManager.h"
00057 #include "nsXPCOM.h"
00058 #include "nsISupportsPrimitives.h"
00059 #include "nsXPIDLString.h"
00060 #include "nsPrimitiveHelpers.h"
00061 #include "nsIImageMac.h"
00062 #include "nsMemory.h"
00063 #include "nsMacNativeUnicodeConverter.h"
00064 #include "nsICharsetConverterManager.h"
00065 #include "nsCRT.h"
00066 #include "nsStylClipboardUtils.h"
00067 #include "nsLinebreakConverter.h"
00068 #include "nsAutoPtr.h"
00069 #include "nsIServiceManager.h"
00070 #include "nsIMacUtils.h"
00071 
00072 #include <Scrap.h>
00073 #include <Script.h>
00074 #include <TextEdit.h>
00075 
00076 static const PRUint32 kPrivateFlavorMask = 0xffff0000;
00077 static const PRUint32 kPrivateFlavorTag = 'MZ..' & kPrivateFlavorMask;
00078 
00079 
00080 
00081 //
00082 // nsClipboard constructor
00083 //
00084 nsClipboard::nsClipboard() : nsBaseClipboard()
00085 {
00086 }
00087 
00088 //
00089 // nsClipboard destructor
00090 //
00091 nsClipboard::~nsClipboard()
00092 {
00093 }
00094 
00095 
00096 //
00097 // SetNativeClipboardData
00098 //
00099 // Take data off the transferrable and put it on the clipboard in as many formats
00100 // as are registered.
00101 //
00102 // NOTE: This code could all live in a shutdown observer and this could be a NOOP. 
00103 // If speed and large data sizes are an issue, we should move that code there and only
00104 // do it on an app switch.
00105 //
00106 NS_IMETHODIMP
00107 nsClipboard :: SetNativeClipboardData ( PRInt32 aWhichClipboard )
00108 {
00109   if ( aWhichClipboard != kGlobalClipboard )
00110     return NS_ERROR_FAILURE;
00111 
00112   nsresult errCode = NS_OK;
00113   
00114   mIgnoreEmptyNotification = PR_TRUE;
00115 
00116   // make sure we have a good transferable
00117   if ( !mTransferable )
00118     return NS_ERROR_INVALID_ARG;
00119   
00120   nsMimeMapperMac theMapper;
00121 
00122   ::ClearCurrentScrap();
00123 
00124   // get flavor list that includes all flavors that can be written (including ones 
00125   // obtained through conversion)
00126   nsCOMPtr<nsISupportsArray> flavorList;
00127   errCode = mTransferable->FlavorsTransferableCanExport ( getter_AddRefs(flavorList) );
00128   if ( NS_FAILED(errCode) )
00129     return NS_ERROR_FAILURE;
00130 
00131   // For each flavor present (either directly in the transferable or that its
00132   // converter knows about) put it on the clipboard. Luckily, GetTransferData() 
00133   // handles conversions for us, so we really don't need to know if a conversion
00134   // is required or not.
00135   PRUint32 cnt;
00136   flavorList->Count(&cnt);
00137   for ( PRUint32 i = 0; i < cnt; ++i ) {
00138     nsCOMPtr<nsISupports> genericFlavor;
00139     flavorList->GetElementAt ( i, getter_AddRefs(genericFlavor) );
00140     nsCOMPtr<nsISupportsCString> currentFlavor ( do_QueryInterface(genericFlavor) );
00141     if ( currentFlavor ) {
00142       nsXPIDLCString flavorStr;
00143       currentFlavor->ToString( getter_Copies(flavorStr) );
00144       
00145       // find MacOS flavor
00146       ResType macOSFlavor = theMapper.MapMimeTypeToMacOSType(flavorStr);
00147     
00148       // get data. This takes converters into account. Different things happen for
00149       // different flavors, so do some special processing.
00150       void* data = nsnull;
00151       PRUint32 dataSize = 0;
00152       if ( strcmp(flavorStr,kUnicodeMime) == 0 ) {
00153         // we have unicode, put it on first as unicode
00154         nsCOMPtr<nsISupports> genericDataWrapper;
00155         errCode = mTransferable->GetTransferData ( flavorStr, getter_AddRefs(genericDataWrapper), &dataSize );
00156         nsPrimitiveHelpers::CreateDataFromPrimitive ( flavorStr, genericDataWrapper, &data, dataSize );
00157 
00158         // Convert unix to mac linebreaks, since mac linebreaks are required for clipboard compatibility.
00159 
00160         PRUnichar* castedData = NS_REINTERPRET_CAST(PRUnichar*, data);
00161         PRUnichar* linebreakConvertedUnicode = castedData;
00162         nsLinebreakConverter::ConvertUnicharLineBreaksInSitu(&linebreakConvertedUnicode,
00163                                                              nsLinebreakConverter::eLinebreakUnix,
00164                                                              nsLinebreakConverter::eLinebreakMac,
00165                                                              dataSize / sizeof(PRUnichar), nsnull);
00166           
00167         errCode = PutOnClipboard ( macOSFlavor, data, dataSize );
00168         if ( NS_SUCCEEDED(errCode) ) {
00169           // we also need to put it on as 'TEXT' after doing the conversion to the platform charset.
00170           char* plainTextData = nsnull;
00171           PRInt32 plainTextLen = 0;
00172           errCode = nsPrimitiveHelpers::ConvertUnicodeToPlatformPlainText ( linebreakConvertedUnicode, dataSize / 2, &plainTextData, &plainTextLen );
00173           
00174           ScriptCodeRun *scriptCodeRuns = nsnull;
00175           PRInt32 scriptRunOutLen;
00176           
00177           // if characters are not mapped from Unicode then try native API to convert to 
00178           // available script
00179           if (errCode == NS_ERROR_UENC_NOMAPPING) {
00180             if (plainTextData) {
00181               nsMemory::Free(plainTextData);
00182               plainTextData = nsnull;
00183             }
00184             errCode = nsMacNativeUnicodeConverter::ConvertUnicodetoScript(linebreakConvertedUnicode, 
00185                                                                           dataSize / 2,
00186                                                                           &plainTextData, 
00187                                                                           &plainTextLen,
00188                                                                           &scriptCodeRuns,
00189                                                                           &scriptRunOutLen);
00190           }
00191           else if (NS_SUCCEEDED(errCode)) {
00192             // create a single run with the default system script
00193             scriptCodeRuns = NS_REINTERPRET_CAST(ScriptCodeRun*,
00194                                                  nsMemory::Alloc(sizeof(ScriptCodeRun)));
00195             if (scriptCodeRuns) {
00196               scriptCodeRuns[0].offset = 0;
00197               scriptCodeRuns[0].script = (ScriptCode) ::GetScriptManagerVariable(smSysScript);
00198               scriptRunOutLen = 1;
00199             }
00200           }
00201           
00202           if ( NS_SUCCEEDED(errCode) && plainTextData ) {
00203             errCode = PutOnClipboard ( 'TEXT', plainTextData, plainTextLen );
00204             nsMemory::Free ( plainTextData ); 
00205             
00206             // create 'styl' from the script runs
00207             if (NS_SUCCEEDED(errCode) && scriptCodeRuns) {
00208               char *stylData;
00209               PRInt32 stylLen;
00210               errCode = CreateStylFromScriptRuns(scriptCodeRuns,
00211                                                  scriptRunOutLen,
00212                                                  &stylData,
00213                                                  &stylLen);
00214               if (NS_SUCCEEDED(errCode)) {
00215                 errCode = PutOnClipboard ('styl', stylData, stylLen);
00216                 nsMemory::Free(stylData);
00217               }
00218             }
00219           }
00220           if (scriptCodeRuns)
00221             nsMemory::Free(scriptCodeRuns);
00222         }
00223 
00224         // ConvertUnicharLineBreaksInSitu may have allocated a new buffer
00225         // (although unlikely when converting from '\n' to '\r').
00226         if (linebreakConvertedUnicode != castedData)
00227           nsMemory::Free(linebreakConvertedUnicode);
00228       } // if unicode
00229       else if ( strcmp(flavorStr, kPNGImageMime) == 0 || strcmp(flavorStr, kJPEGImageMime) == 0 ||
00230                 strcmp(flavorStr, kGIFImageMime) == 0 || strcmp(flavorStr, kNativeImageMime) == 0 ) {
00231         // we have an image, which is in the transferable as an nsIImage. Convert it
00232         // to PICT (PicHandle) and put those bits on the clipboard. The actual size
00233         // of the picture is the size of the handle, not sizeof(Picture).
00234         nsCOMPtr<nsISupports> transferSupports;
00235         errCode = mTransferable->GetTransferData ( flavorStr, getter_AddRefs(transferSupports), &dataSize );
00236         nsCOMPtr<nsISupportsInterfacePointer> ptrPrimitive(do_QueryInterface(transferSupports));
00237         nsCOMPtr<nsIImageMac> image;
00238         if (ptrPrimitive) {
00239           nsCOMPtr<nsISupports> primitiveData;
00240           ptrPrimitive->GetData(getter_AddRefs(primitiveData));
00241           image = do_QueryInterface(primitiveData);
00242         }
00243         if ( image ) {
00244           PicHandle picture = nsnull;
00245           image->ConvertToPICT ( &picture );
00246           if ( picture ) {
00247             errCode = PutOnClipboard ( 'PICT', *picture, ::GetHandleSize((Handle)picture) );
00248             ::KillPicture ( picture );
00249           }
00250         }
00251         else
00252           NS_WARNING ( "Image isn't an nsIImageMac in transferable" );
00253       }
00254       else if (strcmp(flavorStr.get(), kURLDataMime) == 0 ||
00255                strcmp(flavorStr.get(), kURLDescriptionMime) == 0) {
00256         nsCOMPtr<nsISupports> genericDataWrapper;
00257         errCode = mTransferable->GetTransferData(
00258                                             flavorStr,
00259                                             getter_AddRefs(genericDataWrapper),
00260                                             &dataSize);
00261         if (NS_SUCCEEDED(errCode)) {
00262           nsPrimitiveHelpers::CreateDataFromPrimitive(flavorStr,
00263                                                       genericDataWrapper,
00264                                                       &data,
00265                                                       dataSize);
00266 
00267           // Transform the line break format from Unix-style '\n' to
00268           // classic Mac-style '\r', as expected on the clipboard.
00269           PRUnichar* castedData = NS_REINTERPRET_CAST(PRUnichar*, data);
00270           PRUnichar* linebreakConvertedUnicode = castedData;
00271           nsLinebreakConverter::ConvertUnicharLineBreaksInSitu(
00272                                           &linebreakConvertedUnicode,
00273                                           nsLinebreakConverter::eLinebreakUnix,
00274                                           nsLinebreakConverter::eLinebreakMac,
00275                                           dataSize / sizeof(PRUnichar),
00276                                           nsnull);
00277 
00278           // kURLDataMime ('url ') goes onto the clipboard in an 8-bit
00279           // encoding using the %-escaped ASCII subset.  The URL has already
00280           // been %-escaped, so convert it from UTF-16 on the transferable to
00281           // UTF-8 because it's easy.  UTF-8 will also be used for
00282           // kURLDescriptionMime ('urld'), URLs that carry a description, for
00283           // congruity and to avoid using endian-dependent UTF-16 where the
00284           // flavor is not mapped as a private 'MZ..' (see 340071), even
00285           // though 'urld' is not used by others.
00286           nsDependentString utf16(linebreakConvertedUnicode);
00287           NS_ConvertUTF16toUTF8 utf8(utf16);
00288           PutOnClipboard(macOSFlavor,
00289                          PromiseFlatCString(utf8).get(), utf8.Length());
00290 
00291           // ConvertUnicharLineBreaksInSitu may have allocated a new buffer
00292           // (although unlikely when converting from '\n' to '\r').
00293           if (linebreakConvertedUnicode != castedData)
00294             nsMemory::Free(linebreakConvertedUnicode);
00295         }
00296       }
00297       else {
00298         // we don't know what we have. let's just assume it's unicode but doesn't need to be
00299         // translated to TEXT.
00300         nsCOMPtr<nsISupports> genericDataWrapper;
00301         errCode = mTransferable->GetTransferData ( flavorStr, getter_AddRefs(genericDataWrapper), &dataSize );
00302         nsPrimitiveHelpers::CreateDataFromPrimitive ( flavorStr, genericDataWrapper, &data, dataSize );
00303         errCode = PutOnClipboard ( macOSFlavor, data, dataSize );
00304       }
00305               
00306       nsMemory::Free ( data );
00307     }
00308   } // foreach flavor in transferable
00309 
00310   // write out the mapping data in a special flavor on the clipboard. |mappingLen|
00311   // includes the NULL terminator.
00312   short mappingLen = 0;
00313   const char* mapping = theMapper.ExportMapping(&mappingLen);
00314   if ( mapping && mappingLen ) {
00315     errCode = PutOnClipboard ( nsMimeMapperMac::MappingFlavor(), mapping, mappingLen );
00316     nsMemory::Free ( NS_CONST_CAST(char*, mapping) );
00317   }
00318   
00319   return errCode;
00320   
00321 } // SetNativeClipboardData
00322 
00323 
00324 //
00325 // PutOnClipboard
00326 //
00327 // Actually does the work of placing the data on the native clipboard 
00328 // in the given flavor
00329 //
00330 nsresult
00331 nsClipboard :: PutOnClipboard ( ResType inFlavor, const void* inData, PRInt32 inLen )
00332 {
00333   nsresult errCode = NS_OK;
00334 
00335   void* data = (void*) inData;
00336   if ((inFlavor & kPrivateFlavorMask) == kPrivateFlavorTag) {
00337     // Byte-swap private flavors if running translated
00338     nsCOMPtr<nsIMacUtils> macUtils =
00339      do_GetService("@mozilla.org/xpcom/mac-utils;1");
00340     PRBool isTranslated;
00341     if (macUtils &&
00342         NS_SUCCEEDED(macUtils->GetIsTranslated(&isTranslated)) &&
00343         isTranslated) {
00344       data = nsMemory::Alloc(inLen);
00345       if (!data)
00346         return NS_ERROR_OUT_OF_MEMORY;
00347 
00348       swab(inData, data, inLen);
00349     }
00350   }
00351   
00352   ScrapRef scrap;
00353   ::GetCurrentScrap(&scrap);
00354   ::PutScrapFlavor( scrap, inFlavor, kScrapFlavorMaskNone, inLen, data );
00355 
00356   if (data != inData)
00357     nsMemory::Free(data);
00358 
00359   return errCode;
00360   
00361 } // PutOnClipboard
00362 
00363 
00364 //
00365 // GetNativeClipboardData
00366 //
00367 // Take data off the native clip and put it on the transferable.
00368 //
00369 NS_IMETHODIMP
00370 nsClipboard :: GetNativeClipboardData ( nsITransferable * aTransferable, PRInt32 aWhichClipboard )
00371 {
00372   if ( aWhichClipboard != kGlobalClipboard )
00373     return NS_ERROR_FAILURE;
00374 
00375   nsresult errCode = NS_OK;
00376 
00377   // make sure we have a good transferable
00378   if ( !aTransferable )
00379     return NS_ERROR_INVALID_ARG;
00380 
00381   // get flavor list that includes all acceptable flavors (including ones obtained through
00382   // conversion)
00383   nsCOMPtr<nsISupportsArray> flavorList;
00384   errCode = aTransferable->FlavorsTransferableCanImport ( getter_AddRefs(flavorList) );
00385   if ( NS_FAILED(errCode) )
00386     return NS_ERROR_FAILURE;
00387 
00388   // create a mime mapper. It's ok for this to fail because the data may come from
00389   // another app which obviously wouldn't put our mime mapping data on the clipboard.
00390   char* mimeMapperData = nsnull;
00391   errCode = GetDataOffClipboard ( nsMimeMapperMac::MappingFlavor(), (void**)&mimeMapperData, 0 );
00392   nsMimeMapperMac theMapper ( mimeMapperData );
00393   if (mimeMapperData)
00394     nsMemory::Free ( mimeMapperData );
00395  
00396   // Now walk down the list of flavors. When we find one that is actually on the
00397   // clipboard, copy out the data into the transferable in that format. SetTransferData()
00398   // implicitly handles conversions.
00399   PRBool dataFound = PR_FALSE;
00400   PRUint32 cnt;
00401   flavorList->Count(&cnt);
00402   for ( PRUint32 i = 0; i < cnt; ++i ) {
00403     nsCOMPtr<nsISupports> genericFlavor;
00404     flavorList->GetElementAt ( i, getter_AddRefs(genericFlavor) );
00405     nsCOMPtr<nsISupportsCString> currentFlavor ( do_QueryInterface(genericFlavor) );
00406     if ( currentFlavor ) {
00407       nsXPIDLCString flavorStr;
00408       currentFlavor->ToString ( getter_Copies(flavorStr) );
00409       
00410       // find MacOS flavor (don't add if not present)
00411       ResType macOSFlavor = theMapper.MapMimeTypeToMacOSType(flavorStr, PR_FALSE);
00412     
00413       void* clipboardData = nsnull;
00414       PRInt32 dataSize = 0L;
00415       nsresult loadResult = GetDataOffClipboard ( macOSFlavor, &clipboardData, &dataSize );
00416       if ( NS_SUCCEEDED(loadResult) && clipboardData )
00417         dataFound = PR_TRUE;
00418       else {
00419         // if we are looking for text/unicode and we fail to find it on the clipboard first,
00420         // try again with text/plain. If that is present, convert it to unicode.
00421         if ( strcmp(flavorStr, kUnicodeMime) == 0 ) {
00422         
00423           // if 'styl' is available, we can get a script of the first run
00424           // and use it for converting 'TEXT'
00425           loadResult = GetDataOffClipboard ( 'styl', &clipboardData, &dataSize );
00426           if (NS_SUCCEEDED(loadResult) && 
00427               clipboardData &&
00428               ((PRUint32)dataSize >= (sizeof(ScrpSTElement) + 2))) {
00429             StScrpRec *scrpRecP = (StScrpRec *) clipboardData;
00430             ScrpSTElement *styl = scrpRecP->scrpStyleTab;
00431             ScriptCode script = styl ? ::FontToScript(styl->scrpFont) : smCurrentScript;
00432             
00433             // free 'styl' and get 'TEXT'
00434             nsMemory::Free(clipboardData);
00435             loadResult = GetDataOffClipboard ( 'TEXT', &clipboardData, &dataSize );
00436             if ( NS_SUCCEEDED(loadResult) && clipboardData ) {
00437               PRUnichar* convertedText = nsnull;
00438               PRInt32 convertedTextLen = 0;
00439               errCode = nsMacNativeUnicodeConverter::ConvertScripttoUnicode(
00440                                                                     script, 
00441                                                                     (const char *) clipboardData,
00442                                                                     dataSize,
00443                                                                     &convertedText,
00444                                                                     &convertedTextLen);
00445               if (NS_SUCCEEDED(errCode) && convertedText) {
00446                 nsMemory::Free(clipboardData);
00447                 clipboardData = convertedText;
00448                 dataSize = convertedTextLen * sizeof(PRUnichar);
00449                 dataFound = PR_TRUE;
00450               }
00451             }
00452           }          
00453           
00454           if (!dataFound) {
00455             loadResult = GetDataOffClipboard ( 'TEXT', &clipboardData, &dataSize );
00456             if ( NS_SUCCEEDED(loadResult) && clipboardData ) {
00457               const char* castedText = NS_REINTERPRET_CAST(char*, clipboardData);          
00458               PRUnichar* convertedText = nsnull;
00459               PRInt32 convertedTextLen = 0;
00460               nsPrimitiveHelpers::ConvertPlatformPlainTextToUnicode ( castedText, dataSize, 
00461                                                                         &convertedText, &convertedTextLen );
00462               if ( convertedText ) {
00463                 // out with the old, in with the new 
00464                 nsMemory::Free(clipboardData);
00465                 clipboardData = convertedText;
00466                 dataSize = convertedTextLen * 2;
00467                 dataFound = PR_TRUE;
00468               }
00469             } // if plain text data on clipboard
00470           }
00471         } // if looking for text/unicode   
00472       } // else we try one last ditch effort to find our data
00473       
00474       if ( dataFound ) {
00475         if ( strcmp(flavorStr, kPNGImageMime) == 0 || strcmp(flavorStr, kJPEGImageMime) == 0 ||
00476                strcmp(flavorStr, kGIFImageMime) == 0 ) {
00477           // someone asked for an image, so we have to convert from PICT to the desired
00478           // image format
00479           
00480           #ifdef DEBUG
00481           printf ( "----------- IMAGE REQUESTED ----------" );
00482           #endif
00483                
00484         } // if image requested
00485         else {
00486           if (strcmp(flavorStr.get(), kURLDataMime) == 0 ||
00487               strcmp(flavorStr.get(), kURLDescriptionMime) == 0) {
00488             // kURLDataMime ('url ') exists on the clipboard in a %-encoded
00489             // subset of ASCII.  It belongs on the transferable in UTF-16.
00490             // The %-encoding may remain intact, so this is handled by
00491             // converting UTF-8 to UTF-16.  kURLDescriptionMime ('urld'),
00492             // URLs carrying a description, are also treated to the same
00493             // conversion because it's used when the data is copied to the
00494             // clipboard.
00495             nsDependentCString utf8(NS_REINTERPRET_CAST(char*, clipboardData));
00496             NS_ConvertUTF8toUTF16 utf16(utf8);
00497 
00498             // Replace clipboardData with the new wide-char version.
00499             nsMemory::Free(clipboardData);
00500             clipboardData = ToNewUnicode(utf16);
00501             dataSize = utf16.Length() * 2;
00502           }
00503 
00504           // Assume that we have some form of textual data. The DOM only wants LF, so convert
00505           // from MacOS line endings to DOM line endings.
00506           nsLinebreakHelpers::ConvertPlatformToDOMLinebreaks ( flavorStr, &clipboardData, &dataSize );
00507           
00508           unsigned char *clipboardDataPtr = (unsigned char *) clipboardData;
00509           // skip BOM (Byte Order Mark to distinguish little or big endian) in 'utxt'
00510           // 10.2 puts BOM for 'utxt', we need to remove it here
00511           // for little endian case, we also need to convert the data to big endian
00512           // but we do not do that currently (need this in case 'utxt' is really in little endian)
00513           if ( (macOSFlavor == 'utxt') &&
00514                (dataSize > 2) &&
00515                ((clipboardDataPtr[0] == 0xFE && clipboardDataPtr[1] == 0xFF) ||
00516                (clipboardDataPtr[0] == 0xFF && clipboardDataPtr[1] == 0xFE)) ) {
00517             dataSize -= sizeof(PRUnichar);
00518             clipboardDataPtr += sizeof(PRUnichar);
00519           }
00520 
00521           // put it into the transferable
00522           nsCOMPtr<nsISupports> genericDataWrapper;
00523           nsPrimitiveHelpers::CreatePrimitiveForData ( flavorStr, clipboardDataPtr, dataSize, getter_AddRefs(genericDataWrapper) );        
00524           errCode = aTransferable->SetTransferData ( flavorStr, genericDataWrapper, dataSize );
00525         }
00526         
00527         nsMemory::Free ( clipboardData );
00528         
00529         // we found one, get out of this loop!
00530         break;        
00531       } // if flavor found on clipboard
00532     }
00533   } // foreach flavor
00534   
00535   return errCode;
00536 }
00537 
00538 
00539 //
00540 // GetDataOffClipboard
00541 //
00542 // Actually does the work of retrieving the data from the native clipboard 
00543 // with the given MacOS flavor
00544 //
00545 nsresult
00546 nsClipboard :: GetDataOffClipboard ( ResType inMacFlavor, void** outData, PRInt32* outDataSize )
00547 {
00548   if ( !outData || !inMacFlavor )
00549     return NS_ERROR_FAILURE;
00550 
00551   // set up default results.
00552   *outData = nsnull;
00553   if ( outDataSize )
00554       *outDataSize = 0;
00555 
00556 
00557   ScrapRef scrap;
00558   long dataSize;
00559   OSStatus err;
00560 
00561   err = ::GetCurrentScrap(&scrap);
00562   if (err != noErr) return NS_ERROR_FAILURE;
00563   err = ::GetScrapFlavorSize(scrap, inMacFlavor, &dataSize);
00564   if (err != noErr) return NS_ERROR_FAILURE;
00565   if (dataSize > 0) {
00566     char* dataBuff = (char*) nsMemory::Alloc(dataSize);
00567     if ( !dataBuff )
00568       return NS_ERROR_OUT_OF_MEMORY;
00569       
00570     // ::GetScrapFlavorData can be very expensive when a conversion
00571     // is required (say the OS does the conversion from TEXT to utxt). Be
00572     // sure to only call this when pasting. We no longer use it in 
00573     // CheckIfFlavorPresent() for this very reason.
00574     err = ::GetScrapFlavorData(scrap, inMacFlavor, &dataSize, dataBuff);
00575     NS_ASSERTION(err == noErr, "nsClipboard:: Error getting data off clipboard");
00576     if ( err ) {
00577       nsMemory::Free(dataBuff);
00578       return NS_ERROR_FAILURE;
00579     }
00580 
00581     if ((inMacFlavor & kPrivateFlavorMask) == kPrivateFlavorTag) {
00582       // Byte-swap private flavors if running translated
00583       nsCOMPtr<nsIMacUtils> macUtils =
00584        do_GetService("@mozilla.org/xpcom/mac-utils;1");
00585       PRBool isTranslated;
00586       if (macUtils &&
00587           NS_SUCCEEDED(macUtils->GetIsTranslated(&isTranslated)) &&
00588           isTranslated) {
00589         char* swappedData = (char*) nsMemory::Alloc(dataSize);
00590         if (!swappedData) {
00591           nsMemory::Free(dataBuff);
00592           return NS_ERROR_OUT_OF_MEMORY;
00593         }
00594 
00595         swab(dataBuff, swappedData, dataSize);
00596         nsMemory::Free(dataBuff);
00597         dataBuff = swappedData;
00598       }
00599     }
00600 
00601     // put it into the transferable
00602     if ( outDataSize )
00603       *outDataSize = dataSize;
00604     *outData = dataBuff;
00605   }
00606   return NS_OK;
00607   
00608 } // GetDataOffClipboard
00609 
00610 
00611 //
00612 // HasDataMatchingFlavors
00613 //
00614 // Check the clipboard to see if we have any data that matches the given flavors. This
00615 // does NOT actually fetch the data. The items in the flavor list are nsISupportsCString's.
00616 // 
00617 // Handle the case where we ask for unicode and it's not there, but plain text is. We 
00618 // say "yes" in that case, knowing that we will have to perform a conversion when we actually
00619 // pull the data off the clipboard.
00620 //
00621 NS_IMETHODIMP
00622 nsClipboard :: HasDataMatchingFlavors ( nsISupportsArray* aFlavorList, PRInt32 aWhichClipboard, PRBool * outResult ) 
00623 {
00624   nsresult rv = NS_OK;
00625   *outResult = PR_FALSE;  // assume there is nothing there we want.
00626   if ( aWhichClipboard != kGlobalClipboard )
00627     return NS_OK;
00628   
00629   // create a mime mapper. It's ok for this to fail because the data may come from
00630   // another app which obviously wouldn't put our mime mapping data on the clipboard.
00631   char* mimeMapperData = nsnull;
00632   rv = GetDataOffClipboard ( nsMimeMapperMac::MappingFlavor(), (void**)&mimeMapperData, 0 );
00633   nsMimeMapperMac theMapper ( mimeMapperData );
00634   nsMemory::Free ( mimeMapperData );
00635   
00636   PRUint32 length;
00637   aFlavorList->Count(&length);
00638   for ( PRUint32 i = 0; i < length; ++i ) {
00639     nsCOMPtr<nsISupports> genericFlavor;
00640     aFlavorList->GetElementAt ( i, getter_AddRefs(genericFlavor) );
00641     nsCOMPtr<nsISupportsCString> flavorWrapper ( do_QueryInterface(genericFlavor) );
00642     if ( flavorWrapper ) {
00643       nsXPIDLCString flavor;
00644       flavorWrapper->ToString ( getter_Copies(flavor) );
00645       
00646 #ifdef NS_DEBUG
00647       if ( strcmp(flavor, kTextMime) == 0 )
00648         NS_WARNING ( "DO NOT USE THE text/plain DATA FLAVOR ANY MORE. USE text/unicode INSTEAD" );
00649 #endif
00650 
00651       // now that we have the flavor (whew!), run it through the mime mapper. If
00652       // the mapper returns a null flavor, then it ain't there.
00653       ResType macFlavor = theMapper.MapMimeTypeToMacOSType ( flavor, PR_FALSE );
00654       if ( macFlavor ) {
00655         if ( CheckIfFlavorPresent(macFlavor) ) {
00656           *outResult = PR_TRUE;   // we found one!
00657           break;
00658         }
00659         else {
00660           // if the client asked for unicode and it wasn't present, check if we have TEXT.
00661           // We'll handle the actual data substitution in GetNativeClipboardData().
00662           if ( strcmp(flavor, kUnicodeMime) == 0 ) {
00663             if ( CheckIfFlavorPresent('TEXT') ) {
00664               *outResult = PR_TRUE;
00665               break;
00666             }
00667           }
00668         }
00669       }
00670     }  
00671   } // foreach flavor
00672   
00673   return NS_OK;
00674 }
00675 
00676 
00677 //
00678 // CheckIfFlavorPresent
00679 //
00680 // A little utility routine for derminining if a given flavor is really there
00681 //
00682 PRBool
00683 nsClipboard :: CheckIfFlavorPresent ( ResType inMacFlavor )
00684 {
00685   PRBool retval = PR_FALSE;
00686 
00687   ScrapRef scrap = nsnull;
00688   OSStatus err = ::GetCurrentScrap(&scrap);
00689   if ( scrap ) {
00690   
00691     // the OS clipboard may contain promises and require conversions. Instead
00692     // of calling in those promises, we can use ::GetScrapFlavorInfoList() to
00693     // see the list of what could be there if we asked for it. This is really
00694     // fast. Iterate over the list, and if we find it, we're good to go.
00695     UInt32 flavorCount = 0;
00696     ::GetScrapFlavorCount ( scrap, &flavorCount );
00697     nsAutoArrayPtr<ScrapFlavorInfo> flavorList(new ScrapFlavorInfo[flavorCount]);
00698     if ( flavorList ) {
00699       err = ::GetScrapFlavorInfoList ( scrap, &flavorCount, flavorList );
00700       if ( !err && flavorList ) {
00701         for ( unsigned int i = 0; i < flavorCount; ++i ) {
00702           if ( flavorList[i].flavorType == inMacFlavor )
00703             retval = PR_TRUE;
00704         }
00705       }
00706     }
00707 
00708   }
00709   return retval;
00710 } // CheckIfFlavorPresent