Back to index

lightning-sunbird  0.9+nobinonly
nsTransferable.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  *   Mike Pinkerton (pinkerton@netscape.com)
00024  *   Dainis Jonitis (Dainis_Jonitis@swh-t.lv)
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * 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 /*
00041 Notes to self:
00042 
00043 - at some point, strings will be accessible from JS, so we won't have to wrap
00044    flavors in an nsISupportsCString. Until then, we're kinda stuck with
00045    this crappy API of nsISupportsArrays.
00046 
00047 */
00048 
00049  
00050 #include "nsTransferable.h"
00051 #include "nsString.h"
00052 #include "nsReadableUtils.h"
00053 #include "nsVoidArray.h"
00054 #include "nsIFormatConverter.h"
00055 #include "nsIComponentManager.h"
00056 #include "nsCOMPtr.h"
00057 #include "nsXPCOM.h"
00058 #include "nsISupportsPrimitives.h"
00059 #include "nsMemory.h"
00060 #include "nsPrimitiveHelpers.h"
00061 #include "nsXPIDLString.h"
00062 #include "nsDirectoryServiceDefs.h"
00063 #include "nsDirectoryService.h"
00064 #include "nsCRT.h" 
00065 #include "nsNetUtil.h"
00066 #include "nsIOutputStream.h"
00067 #include "nsIInputStream.h"
00068 #include "nsIFile.h"
00069 #include "nsAutoPtr.h"
00070 
00071 NS_IMPL_ISUPPORTS1(nsTransferable, nsITransferable)
00072 
00073 
00074 //
00075 // DataStruct
00076 //
00077 // Holds a flavor (a mime type) that describes the data and the associated data.
00078 //
00079 struct DataStruct
00080 {
00081   DataStruct ( const char* aFlavor )
00082     : mDataLen(0), mFlavor(aFlavor), mCacheFileName(nsnull) { }
00083   ~DataStruct();
00084   
00085   const nsCString& GetFlavor() const { return mFlavor; }
00086   void SetData( nsISupports* inData, PRUint32 inDataLen );
00087   void GetData( nsISupports** outData, PRUint32 *outDataLen );
00088   nsIFile * GetFileSpec(const char * aFileName);
00089   PRBool IsDataAvailable() const { return (mData && mDataLen > 0) || (!mData && mCacheFileName); }
00090   
00091 protected:
00092 
00093   enum {
00094     // The size of data over which we write the data to disk rather than
00095     // keep it around in memory.
00096     kLargeDatasetSize = 1000000        // 1 million bytes
00097   };
00098   
00099   nsresult WriteCache(nsISupports* aData, PRUint32 aDataLen );
00100   nsresult ReadCache(nsISupports** aData, PRUint32* aDataLen );
00101   
00102   nsCOMPtr<nsISupports> mData;   // OWNER - some varient of primitive wrapper
00103   PRUint32 mDataLen;
00104   const nsCAutoString mFlavor;
00105   char *   mCacheFileName;
00106 
00107 };
00108 
00109 DataStruct* GetDataForFlavor (const nsVoidArray* pArray, const char* aDataFlavor);
00110 
00111 DataStruct* GetDataForFlavor (const nsVoidArray* pArray, const char* aDataFlavor)
00112 {
00113   for (PRInt32 i = 0 ; i < pArray->Count () ; ++i) {
00114     DataStruct* data = (DataStruct*)pArray->ElementAt (i);
00115     if (data->GetFlavor().Equals (aDataFlavor))
00116       return data;
00117   }
00118 
00119   return nsnull;
00120 }
00121 
00122 //-------------------------------------------------------------------------
00123 DataStruct::~DataStruct() 
00124 { 
00125   if (mCacheFileName) nsCRT::free(mCacheFileName); 
00126 
00127     //nsIFileSpec * cacheFile = GetFileSpec(mCacheFileName);
00128     //cacheFile->Remove();
00129 }
00130 
00131 //-------------------------------------------------------------------------
00132 void
00133 DataStruct::SetData ( nsISupports* aData, PRUint32 aDataLen )
00134 {
00135   // Now, check to see if we consider the data to be "too large"
00136   if (aDataLen > kLargeDatasetSize) {
00137     // if so, cache it to disk instead of memory
00138     if ( NS_SUCCEEDED(WriteCache(aData, aDataLen)) )
00139       return;
00140     else
00141                      NS_WARNING("Oh no, couldn't write data to the cache file");   
00142   } 
00143 
00144   mData    = aData;
00145   mDataLen = aDataLen;  
00146 }
00147 
00148 
00149 //-------------------------------------------------------------------------
00150 void
00151 DataStruct::GetData ( nsISupports** aData, PRUint32 *aDataLen )
00152 {
00153   // check here to see if the data is cached on disk
00154   if ( !mData && mCacheFileName ) {
00155     // if so, read it in and pass it back
00156     // ReadCache creates memory and copies the data into it.
00157     if ( NS_SUCCEEDED(ReadCache(aData, aDataLen)) )
00158       return;
00159     else {
00160       // oh shit, something went horribly wrong here.
00161       NS_WARNING("Oh no, couldn't read data in from the cache file");
00162       *aData = nsnull;
00163       *aDataLen = 0;
00164       return;
00165     }
00166   }
00167   
00168   *aData = mData;
00169   if ( mData )
00170     NS_ADDREF(*aData); 
00171   *aDataLen = mDataLen;
00172 }
00173 
00174 
00175 //-------------------------------------------------------------------------
00176 nsIFile*
00177 DataStruct::GetFileSpec(const char * aFileName)
00178 {
00179   nsIFile* cacheFile;
00180   NS_GetSpecialDirectory(NS_OS_TEMP_DIR, &cacheFile);
00181   
00182   if (cacheFile == nsnull)
00183     return nsnull;
00184 
00185   // if the param aFileName contains a name we should use that
00186   // because the file probably already exists
00187   // otherwise create a unique name
00188   if (!aFileName) {
00189     cacheFile->AppendNative(NS_LITERAL_CSTRING("clipboardcache"));
00190     cacheFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
00191   } else {
00192     cacheFile->AppendNative(nsDependentCString(aFileName));
00193   }
00194   
00195   return cacheFile;
00196 }
00197 
00198 
00199 //-------------------------------------------------------------------------
00200 nsresult
00201 DataStruct::WriteCache(nsISupports* aData, PRUint32 aDataLen)
00202 {
00203   // Get a new path and file to the temp directory
00204   nsCOMPtr<nsIFile> cacheFile ( getter_AddRefs(GetFileSpec(mCacheFileName)) );
00205   if (cacheFile) {
00206     // remember the file name
00207     if (!mCacheFileName) {
00208       nsXPIDLCString fName;
00209       cacheFile->GetNativeLeafName(fName);
00210       mCacheFileName = nsCRT::strdup(fName);
00211     }
00212 
00213     // write out the contents of the clipboard
00214     // to the file
00215     //PRUint32 bytes;
00216     nsCOMPtr<nsIOutputStream> outStr;
00217 
00218     NS_NewLocalFileOutputStream(getter_AddRefs(outStr),
00219                                 cacheFile);
00220 
00221     if (!outStr) return NS_ERROR_FAILURE;
00222 
00223     void* buff = nsnull;
00224     nsPrimitiveHelpers::CreateDataFromPrimitive ( mFlavor.get(), aData, &buff, aDataLen );
00225     if ( buff ) {
00226       PRUint32 ignored;
00227       outStr->Write(NS_REINTERPRET_CAST(char*, buff), aDataLen, &ignored);
00228       nsMemory::Free(buff);
00229       return NS_OK;
00230     }
00231   }
00232   return NS_ERROR_FAILURE;
00233 }
00234 
00235 
00236 //-------------------------------------------------------------------------
00237 nsresult
00238 DataStruct::ReadCache(nsISupports** aData, PRUint32* aDataLen)
00239 {
00240   // if we don't have a cache filename we are out of luck
00241   if (!mCacheFileName)
00242     return NS_ERROR_FAILURE;
00243 
00244   // get the path and file name
00245   nsCOMPtr<nsIFile> cacheFile ( getter_AddRefs(GetFileSpec(mCacheFileName)) );
00246   PRBool exists;
00247   if ( cacheFile && NS_SUCCEEDED(cacheFile->Exists(&exists)) && exists ) {
00248     // get the size of the file
00249     PRInt64 fileSize;
00250     PRInt64 max32(LL_INIT(0, 0xFFFFFFFF));
00251     cacheFile->GetFileSize(&fileSize);
00252     if (LL_CMP(fileSize, >, max32))
00253       return NS_ERROR_OUT_OF_MEMORY;
00254     PRUint32 size;
00255     LL_L2UI(size, fileSize);
00256 
00257     // create new memory for the large clipboard data
00258     nsAutoArrayPtr<char> data(new char[size]);
00259     if ( !data )
00260       return NS_ERROR_OUT_OF_MEMORY;
00261       
00262     // now read it all in
00263     nsCOMPtr<nsIInputStream> inStr;
00264     NS_NewLocalFileInputStream( getter_AddRefs(inStr),
00265                                 cacheFile);
00266     
00267     if (!cacheFile) return NS_ERROR_FAILURE;
00268 
00269     nsresult rv = inStr->Read(data, fileSize, aDataLen);
00270 
00271     // make sure we got all the data ok
00272     if (NS_SUCCEEDED(rv) && *aDataLen == size) {
00273       nsPrimitiveHelpers::CreatePrimitiveForData ( mFlavor.get(), data, fileSize, aData );
00274       return *aData ? NS_OK : NS_ERROR_FAILURE;
00275     }
00276 
00277     // zero the return params
00278     *aData    = nsnull;
00279     *aDataLen = 0;
00280   }
00281 
00282   return NS_ERROR_FAILURE;
00283 }
00284 
00285 
00286 #ifdef XP_MAC
00287 #pragma mark -
00288 #endif
00289 
00290 
00291 //-------------------------------------------------------------------------
00292 //
00293 // Transferable constructor
00294 //
00295 //-------------------------------------------------------------------------
00296 nsTransferable::nsTransferable()
00297 {
00298   mDataArray = new nsVoidArray();
00299 }
00300 
00301 //-------------------------------------------------------------------------
00302 //
00303 // Transferable destructor
00304 //
00305 //-------------------------------------------------------------------------
00306 nsTransferable::~nsTransferable()
00307 {
00308   for (PRInt32 i=0;i<mDataArray->Count();i++) {
00309     DataStruct * data = (DataStruct *)mDataArray->ElementAt(i);
00310     delete data;
00311   }
00312   delete mDataArray;
00313 }
00314 
00315 
00316 //
00317 // GetTransferDataFlavors
00318 //
00319 // Returns a copy of the internal list of flavors. This does NOT take into 
00320 // account any converter that may be registered. This list consists of
00321 // nsISupportsCString objects so that the flavor list can be accessed from JS.
00322 //
00323 nsresult
00324 nsTransferable::GetTransferDataFlavors(nsISupportsArray ** aDataFlavorList)
00325 {
00326   nsresult rv = NS_NewISupportsArray ( aDataFlavorList );
00327   if (NS_FAILED(rv)) return rv;
00328 
00329   for ( PRInt32 i=0; i<mDataArray->Count(); ++i ) {
00330     DataStruct * data = (DataStruct *)mDataArray->ElementAt(i);
00331     nsCOMPtr<nsISupportsCString> flavorWrapper = do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID);
00332     if ( flavorWrapper ) {
00333       flavorWrapper->SetData ( data->GetFlavor() );
00334       nsCOMPtr<nsISupports> genericWrapper ( do_QueryInterface(flavorWrapper) );
00335       (*aDataFlavorList)->AppendElement( genericWrapper );
00336     }
00337   }
00338 
00339   return NS_OK;
00340 }
00341 
00342 
00343 //
00344 // GetTransferData
00345 //
00346 // Returns the data of the requested flavor, obtained from either having the data on hand or
00347 // using a converter to get it. The data is wrapped in a nsISupports primitive so that it is
00348 // accessable from JS.
00349 //
00350 NS_IMETHODIMP
00351 nsTransferable::GetTransferData(const char *aFlavor, nsISupports **aData, PRUint32 *aDataLen)
00352 {
00353   NS_ENSURE_ARG_POINTER(aFlavor && aData && aDataLen);
00354 
00355   nsresult rv = NS_OK;
00356   
00357   // first look and see if the data is present in one of the intrinsic flavors
00358   PRInt32 i;
00359   for (i = 0; i < mDataArray->Count(); ++i ) {
00360     DataStruct * data = (DataStruct *)mDataArray->ElementAt(i);
00361     if ( data->GetFlavor().Equals(aFlavor) ) {
00362       data->GetData(aData, aDataLen);
00363       if (*aDataLen == kFlavorHasDataProvider) {
00364         // do we have a data provider?
00365         nsCOMPtr<nsIFlavorDataProvider> dataProvider = do_QueryInterface(*aData);
00366         if (dataProvider) {
00367           rv = dataProvider->GetFlavorData(this, aFlavor, aData, aDataLen);
00368           if (NS_FAILED(rv))
00369             break;    // the provider failed. fall into the conveter code below.
00370         }
00371       }
00372       if (*aData && *aDataLen > 0)
00373         return NS_OK;
00374     
00375       break;
00376     }
00377   }
00378 
00379   PRBool found = PR_FALSE;
00380 
00381   // if not, try using a format converter to get the requested flavor
00382   if ( mFormatConv ) {
00383     for (i = 0; i < mDataArray->Count(); ++i) {
00384       DataStruct * data = (DataStruct *)mDataArray->ElementAt(i);
00385       PRBool canConvert = PR_FALSE;
00386       mFormatConv->CanConvert(data->GetFlavor().get(), aFlavor, &canConvert);
00387       if ( canConvert ) {
00388         nsCOMPtr<nsISupports> dataBytes;
00389         PRUint32 len;
00390         data->GetData(getter_AddRefs(dataBytes), &len);
00391         if (len == kFlavorHasDataProvider) {
00392           // do we have a data provider?
00393           nsCOMPtr<nsIFlavorDataProvider> dataProvider = do_QueryInterface(dataBytes);
00394           if (dataProvider) {
00395             rv = dataProvider->GetFlavorData(this, aFlavor, getter_AddRefs(dataBytes), &len);
00396             if (NS_FAILED(rv))
00397               break;  // give up
00398           }
00399         }
00400         mFormatConv->Convert(data->GetFlavor().get(), dataBytes, len, aFlavor, aData, aDataLen);
00401         found = PR_TRUE;
00402         break;
00403       }
00404     }
00405   }
00406   return found ? NS_OK : NS_ERROR_FAILURE;
00407 }
00408 
00409 
00410 //
00411 // GetAnyTransferData
00412 //
00413 // Returns the data of the first flavor found. Caller is responsible for deleting the
00414 // flavor string.
00415 // 
00416 NS_IMETHODIMP
00417 nsTransferable::GetAnyTransferData(char **aFlavor, nsISupports **aData, PRUint32 *aDataLen)
00418 {
00419   NS_ENSURE_ARG_POINTER(aFlavor && aData && aDataLen);
00420 
00421   for ( PRInt32 i=0; i < mDataArray->Count(); ++i ) {
00422     DataStruct * data = (DataStruct *)mDataArray->ElementAt(i);
00423     if (data->IsDataAvailable()) {
00424       *aFlavor = ToNewCString(data->GetFlavor());
00425       data->GetData(aData, aDataLen);
00426       return NS_OK;
00427     }
00428   }
00429 
00430   return NS_ERROR_FAILURE;
00431 }
00432 
00433 
00434 //
00435 // SetTransferData
00436 //
00437 //
00438 // 
00439 NS_IMETHODIMP
00440 nsTransferable::SetTransferData(const char *aFlavor, nsISupports *aData, PRUint32 aDataLen)
00441 {
00442   NS_ENSURE_ARG(aFlavor);
00443 
00444   // first check our intrinsic flavors to see if one has been registered.
00445   PRInt32 i = 0;
00446   for (i = 0; i < mDataArray->Count(); ++i) {
00447     DataStruct * data = (DataStruct *)mDataArray->ElementAt(i);
00448     if ( data->GetFlavor().Equals(aFlavor) ) {
00449       data->SetData ( aData, aDataLen );
00450       return NS_OK;
00451     }
00452   }
00453 
00454   // if not, try using a format converter to find a flavor to put the data in
00455   if ( mFormatConv ) {
00456     for (i = 0; i < mDataArray->Count(); ++i) {
00457       DataStruct * data = (DataStruct *)mDataArray->ElementAt(i);
00458       PRBool canConvert = PR_FALSE;
00459       mFormatConv->CanConvert(aFlavor, data->GetFlavor().get(), &canConvert);
00460 
00461       if ( canConvert ) {
00462         nsCOMPtr<nsISupports> ConvertedData;
00463         PRUint32 ConvertedLen;
00464         mFormatConv->Convert(aFlavor, aData, aDataLen, data->GetFlavor().get(), getter_AddRefs(ConvertedData), &ConvertedLen);
00465         data->SetData(ConvertedData, ConvertedLen);
00466         return NS_OK;
00467       }
00468     }
00469   }
00470 
00471   // Can't set data neither directly nor through converter. Just add this flavor and try again
00472   nsresult result = NS_ERROR_FAILURE;
00473   if ( NS_SUCCEEDED(AddDataFlavor(aFlavor)) )
00474     result = SetTransferData (aFlavor, aData, aDataLen);
00475     
00476   return result;
00477 }
00478 
00479 
00480 //
00481 // AddDataFlavor
00482 //
00483 // Adds a data flavor to our list with no data. Error if it already exists.
00484 // 
00485 NS_IMETHODIMP
00486 nsTransferable::AddDataFlavor(const char *aDataFlavor)
00487 {
00488   if (GetDataForFlavor (mDataArray, aDataFlavor))
00489     return NS_ERROR_FAILURE;
00490 
00491   // Create a new "slot" for the data
00492   DataStruct * data = new DataStruct ( aDataFlavor ) ;
00493   mDataArray->AppendElement((void *)data);
00494 
00495   return NS_OK;
00496 }
00497 
00498 
00499 //
00500 // RemoveDataFlavor
00501 //
00502 // Removes a data flavor (and causes the data to be destroyed). Error if
00503 // the requested flavor is not present.
00504 //
00505 NS_IMETHODIMP
00506 nsTransferable::RemoveDataFlavor(const char *aDataFlavor)
00507 {
00508   DataStruct* data = GetDataForFlavor (mDataArray, aDataFlavor);
00509 
00510   if ( data ) {
00511     mDataArray->RemoveElement (data);
00512     delete data;
00513 
00514     return NS_OK;
00515   }
00516   else
00517     return NS_ERROR_FAILURE;
00518 }
00519 
00520 
00525 NS_IMETHODIMP
00526 nsTransferable::IsLargeDataSet(PRBool *_retval)
00527 {
00528   NS_ENSURE_ARG_POINTER(_retval);
00529   *_retval = PR_FALSE;
00530   return NS_OK;
00531 }
00532 
00533 
00538 NS_IMETHODIMP nsTransferable::SetConverter(nsIFormatConverter * aConverter)
00539 {
00540   mFormatConv = aConverter;
00541   return NS_OK;
00542 }
00543 
00544 
00549 NS_IMETHODIMP nsTransferable::GetConverter(nsIFormatConverter * *aConverter)
00550 {
00551   NS_ENSURE_ARG_POINTER(aConverter);
00552   *aConverter = mFormatConv;
00553   NS_IF_ADDREF(*aConverter);
00554   return NS_OK;
00555 }
00556 
00557 
00558 //
00559 // FlavorsTransferableCanImport
00560 //
00561 // Computes a list of flavors that the transferable can accept into it, either through
00562 // intrinsic knowledge or input data converters.
00563 //
00564 NS_IMETHODIMP
00565 nsTransferable::FlavorsTransferableCanImport(nsISupportsArray **_retval)
00566 {
00567   NS_ENSURE_ARG_POINTER(_retval);
00568   
00569   // Get the flavor list, and on to the end of it, append the list of flavors we
00570   // can also get to through a converter. This is so that we can just walk the list
00571   // in one go, looking for the desired flavor.
00572   GetTransferDataFlavors(_retval);                        // addrefs
00573   nsCOMPtr<nsIFormatConverter> converter;
00574   GetConverter(getter_AddRefs(converter));
00575   if ( converter ) {
00576     nsCOMPtr<nsISupportsArray> convertedList;
00577     converter->GetInputDataFlavors(getter_AddRefs(convertedList));
00578 
00579     if ( convertedList ) {
00580       PRUint32 importListLen;
00581       convertedList->Count(&importListLen);
00582 
00583       for ( PRUint32 i=0; i < importListLen; ++i ) {
00584         nsCOMPtr<nsISupports> genericFlavor;
00585         convertedList->GetElementAt ( i, getter_AddRefs(genericFlavor) );
00586 
00587         nsCOMPtr<nsISupportsCString> flavorWrapper ( do_QueryInterface (genericFlavor) );
00588         nsCAutoString flavorStr;
00589         flavorWrapper->GetData( flavorStr );
00590 
00591         if (!GetDataForFlavor (mDataArray, flavorStr.get()))    // Don't append if already in intrinsic list
00592           (*_retval)->AppendElement (genericFlavor);
00593       } // foreach flavor that can be converted to
00594     }
00595   } // if a converter exists
00596 
00597   return NS_OK;  
00598 } // FlavorsTransferableCanImport
00599 
00600 
00601 //
00602 // FlavorsTransferableCanExport
00603 //
00604 // Computes a list of flavors that the transferable can export, either through
00605 // intrinsic knowledge or output data converters.
00606 //
00607 NS_IMETHODIMP
00608 nsTransferable::FlavorsTransferableCanExport(nsISupportsArray **_retval)
00609 {
00610   NS_ENSURE_ARG_POINTER(_retval);
00611   
00612   // Get the flavor list, and on to the end of it, append the list of flavors we
00613   // can also get to through a converter. This is so that we can just walk the list
00614   // in one go, looking for the desired flavor.
00615   GetTransferDataFlavors(_retval);  // addrefs
00616   nsCOMPtr<nsIFormatConverter> converter;
00617   GetConverter(getter_AddRefs(converter));
00618   if ( converter ) {
00619     nsCOMPtr<nsISupportsArray> convertedList;
00620     converter->GetOutputDataFlavors(getter_AddRefs(convertedList));
00621 
00622     if ( convertedList ) {
00623       PRUint32 importListLen;
00624       convertedList->Count(&importListLen);
00625 
00626       for ( PRUint32 i=0; i < importListLen; ++i ) {
00627         nsCOMPtr<nsISupports> genericFlavor;
00628         convertedList->GetElementAt ( i, getter_AddRefs(genericFlavor) );
00629 
00630         nsCOMPtr<nsISupportsCString> flavorWrapper ( do_QueryInterface (genericFlavor) );
00631         nsCAutoString flavorStr;
00632         flavorWrapper->GetData( flavorStr );
00633 
00634         if (!GetDataForFlavor (mDataArray, flavorStr.get()))    // Don't append if already in intrinsic list
00635           (*_retval)->AppendElement (genericFlavor);
00636       } // foreach flavor that can be converted to
00637     }
00638   } // if a converter exists
00639 
00640   return NS_OK;
00641 } // FlavorsTransferableCanExport