Back to index

lightning-sunbird  0.9+nobinonly
nsDataObj.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  *   Sean Echevarria <Sean@Beatnik.com>
00024  *   Blake Ross <blaker@netscape.com>
00025  *   Brodie Thiesfield <brofield@jellycan.com>
00026  *   Masayuki Nakano <masayuki@d-toybox.com>
00027  *   David Gardiner <david.gardiner@unisa.edu.au>
00028  *
00029  * Alternatively, the contents of this file may be used under the terms of
00030  * either the GNU General Public License Version 2 or later (the "GPL"), or
00031  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00032  * in which case the provisions of the GPL or the LGPL are applicable instead
00033  * of those above. If you wish to allow use of your version of this file only
00034  * under the terms of either the GPL or the LGPL, and not to allow others to
00035  * use your version of this file under the terms of the MPL, indicate your
00036  * decision by deleting the provisions above and replace them with the notice
00037  * and other provisions required by the GPL or the LGPL. If you do not delete
00038  * the provisions above, a recipient may use your version of this file under
00039  * the terms of any one of the MPL, the GPL or the LGPL.
00040  *
00041  * ***** END LICENSE BLOCK ***** */
00042 
00043 #include <ole2.h>
00044 #ifndef __MINGW32__
00045 #include <urlmon.h>
00046 #endif
00047 #include <shlobj.h>
00048 
00049 #include "nsDataObj.h"
00050 #include "nsClipboard.h"
00051 #include "nsString.h"
00052 #include "nsReadableUtils.h"
00053 #include "nsVoidArray.h"
00054 #include "nsITransferable.h"
00055 #include "nsXPCOM.h"
00056 #include "nsISupportsPrimitives.h"
00057 #include "IENUMFE.H"
00058 #include "nsCOMPtr.h"
00059 #include "nsIComponentManager.h"
00060 #include "nsPrimitiveHelpers.h"
00061 #include "nsXPIDLString.h"
00062 #include "nsIImage.h"
00063 #include "nsImageClipboard.h"
00064 #include "nsIDirectoryService.h"
00065 #include "nsILocalFile.h"
00066 #include "nsDirectoryServiceDefs.h"
00067 #include "prprf.h"
00068 #include "nsCRT.h"
00069 #include "nsPrintfCString.h"
00070 #include "nsIStringBundle.h"
00071 
00072 #if 0
00073 #define PRNTDEBUG(_x) printf(_x);
00074 #define PRNTDEBUG2(_x1, _x2) printf(_x1, _x2);
00075 #define PRNTDEBUG3(_x1, _x2, _x3) printf(_x1, _x2, _x3);
00076 #else
00077 #define PRNTDEBUG(_x) // printf(_x);
00078 #define PRNTDEBUG2(_x1, _x2) // printf(_x1, _x2);
00079 #define PRNTDEBUG3(_x1, _x2, _x3) // printf(_x1, _x2, _x3);
00080 #endif
00081 
00082 ULONG nsDataObj::g_cRef = 0;
00083 
00084 EXTERN_C GUID CDECL CLSID_nsDataObj =
00085        { 0x1bba7640, 0xdf52, 0x11cf, { 0x82, 0x7b, 0, 0xa0, 0x24, 0x3a, 0xe5, 0x05 } };
00086 
00087 /* 
00088  * deliberately not using MAX_PATH. This is because on platforms < XP
00089  * a file created with a long filename may be mishandled by the shell
00090  * resulting in it not being able to be deleted or moved. 
00091  * See bug 250392 for more details.
00092  */
00093 #define NS_MAX_FILEDESCRIPTOR 128 + 1
00094 
00095 /*
00096  * Class nsDataObj
00097  */
00098 
00099 //-----------------------------------------------------
00100 // construction 
00101 //-----------------------------------------------------
00102 nsDataObj::nsDataObj(nsIURI * uri)
00103 : m_cRef(0), mTransferable(nsnull)
00104 {
00105   mDataFlavors    = new nsVoidArray();
00106   m_enumFE        = new CEnumFormatEtc(32);
00107  
00108   m_enumFE->AddRef();
00109 
00110   if (uri) {
00111 
00112     // A URI was obtained, so pass this through to the DataObject
00113     // so it can create a SourceURL for CF_HTML flavour
00114     uri->GetSpec(mSourceURL);
00115   }
00116 }
00117 //-----------------------------------------------------
00118 // destruction
00119 //-----------------------------------------------------
00120 nsDataObj::~nsDataObj()
00121 {
00122   NS_IF_RELEASE(mTransferable);
00123   PRInt32 i;
00124   for (i=0;i<mDataFlavors->Count();i++) {
00125     nsCAutoString* df = NS_REINTERPRET_CAST(nsCAutoString *, mDataFlavors->ElementAt(i));
00126     delete df;
00127   }
00128 
00129   delete mDataFlavors;
00130 
00131   m_cRef = 0;
00132   m_enumFE->Release();
00133 
00134 }
00135 
00136 
00137 //-----------------------------------------------------
00138 // IUnknown interface methods - see inknown.h for documentation
00139 //-----------------------------------------------------
00140 STDMETHODIMP nsDataObj::QueryInterface(REFIID riid, void** ppv)
00141 {
00142        *ppv=NULL;
00143 
00144        if ( (IID_IUnknown == riid) || (IID_IDataObject  == riid) ) {
00145               *ppv = this;
00146               AddRef();
00147               return NOERROR;
00148        }
00149 
00150        return ResultFromScode(E_NOINTERFACE);
00151 }
00152 
00153 //-----------------------------------------------------
00154 STDMETHODIMP_(ULONG) nsDataObj::AddRef()
00155 {
00156        ++g_cRef;
00157        ++m_cRef;
00158        NS_LOG_ADDREF(this, m_cRef, "nsDataObj", sizeof(*this));
00159   //PRNTDEBUG3("nsDataObj::AddRef  >>>>>>>>>>>>>>>>>> %d on %p\n", (m_cRef+1), this);
00160        return m_cRef;
00161 }
00162 
00163 
00164 //-----------------------------------------------------
00165 STDMETHODIMP_(ULONG) nsDataObj::Release()
00166 {
00167   //PRNTDEBUG3("nsDataObj::Release >>>>>>>>>>>>>>>>>> %d on %p\n", (m_cRef-1), this);
00168        if (0 < g_cRef)
00169               --g_cRef;
00170 
00171        NS_LOG_RELEASE(this, m_cRef, "nsDataObj");
00172        if (0 != --m_cRef)
00173               return m_cRef;
00174 
00175        delete this;
00176 
00177        return 0;
00178 }
00179 
00180 //-----------------------------------------------------
00181 BOOL nsDataObj::FormatsMatch(const FORMATETC& source, const FORMATETC& target) const
00182 {
00183        if ((source.cfFormat == target.cfFormat) &&
00184                (source.dwAspect  & target.dwAspect)  &&
00185                (source.tymed     & target.tymed))       {
00186               return TRUE;
00187        } else {
00188               return FALSE;
00189        }
00190 }
00191 
00192 //-----------------------------------------------------
00193 // IDataObject methods
00194 //-----------------------------------------------------
00195 STDMETHODIMP nsDataObj::GetData(LPFORMATETC pFE, LPSTGMEDIUM pSTM)
00196 {
00197   PRNTDEBUG("nsDataObj::GetData\n");
00198   PRNTDEBUG3("  format: %d  Text: %d\n", pFE->cfFormat, CF_TEXT);
00199   if ( !mTransferable )
00200          return ResultFromScode(DATA_E_FORMATETC);
00201 
00202   PRUint32 dfInx = 0;
00203 
00204   static CLIPFORMAT fileDescriptorFlavorA = ::RegisterClipboardFormat( CFSTR_FILEDESCRIPTORA ); 
00205   static CLIPFORMAT fileDescriptorFlavorW = ::RegisterClipboardFormat( CFSTR_FILEDESCRIPTORW ); 
00206   static CLIPFORMAT uniformResourceLocatorA = ::RegisterClipboardFormat( CFSTR_INETURLA );
00207   static CLIPFORMAT uniformResourceLocatorW = ::RegisterClipboardFormat( CFSTR_INETURLW );
00208 #ifndef WINCE
00209   static CLIPFORMAT fileFlavor = ::RegisterClipboardFormat( CFSTR_FILECONTENTS ); 
00210   static CLIPFORMAT PreferredDropEffect = ::RegisterClipboardFormat( CFSTR_PREFERREDDROPEFFECT );
00211 #endif
00212 
00213   ULONG count;
00214   FORMATETC fe;
00215   m_enumFE->Reset();
00216   while (NOERROR == m_enumFE->Next(1, &fe, &count)) {
00217     nsCAutoString * df = NS_REINTERPRET_CAST(nsCAutoString*, mDataFlavors->SafeElementAt(dfInx));
00218     if ( df ) {
00219       if (FormatsMatch(fe, *pFE)) {
00220         pSTM->pUnkForRelease = NULL;        // caller is responsible for deleting this data
00221         CLIPFORMAT format = pFE->cfFormat;
00222         switch(format) {
00223 
00224         // Someone is asking for plain or unicode text
00225         case CF_TEXT:
00226         case CF_UNICODETEXT:
00227         return GetText(*df, *pFE, *pSTM);
00228                   
00229 #ifndef WINCE
00230         case CF_HDROP:
00231           return GetFile(*df, *pFE, *pSTM);
00232 #endif
00233         // Someone is asking for an image
00234         case CF_DIB:
00235           return GetDib(*df, *pFE, *pSTM);
00236                                               
00237         // ... not yet implemented ...
00238         //case CF_BITMAP:
00239         //  return GetBitmap(*pFE, *pSTM);
00240         //case CF_METAFILEPICT:
00241         //  return GetMetafilePict(*pFE, *pSTM);
00242             
00243         default:
00244           if ( format == fileDescriptorFlavorA )
00245             return GetFileDescriptor ( *pFE, *pSTM, PR_FALSE );
00246           if ( format == fileDescriptorFlavorW )
00247             return GetFileDescriptor ( *pFE, *pSTM, PR_TRUE);
00248           if ( format == uniformResourceLocatorA )
00249             return GetUniformResourceLocator( *pFE, *pSTM, PR_FALSE);
00250           if ( format == uniformResourceLocatorW )
00251             return GetUniformResourceLocator( *pFE, *pSTM, PR_TRUE);
00252 #ifndef WINCE
00253           if ( format == fileFlavor )
00254             return GetFileContents ( *pFE, *pSTM );
00255           if ( format == PreferredDropEffect )
00256             return GetPreferredDropEffect( *pFE, *pSTM );
00257 #endif
00258           PRNTDEBUG2("***** nsDataObj::GetData - Unknown format %u\n", format);
00259           return GetText(*df, *pFE, *pSTM);
00260         } //switch
00261       } // if
00262     }
00263     dfInx++;
00264   } // while
00265 
00266   return ResultFromScode(DATA_E_FORMATETC);
00267 }
00268 
00269 
00270 //-----------------------------------------------------
00271 STDMETHODIMP nsDataObj::GetDataHere(LPFORMATETC pFE, LPSTGMEDIUM pSTM)
00272 {
00273   PRNTDEBUG("nsDataObj::GetDataHere\n");
00274               return ResultFromScode(E_FAIL);
00275 }
00276 
00277 
00278 //-----------------------------------------------------
00279 // Other objects querying to see if we support a 
00280 // particular format
00281 //-----------------------------------------------------
00282 STDMETHODIMP nsDataObj::QueryGetData(LPFORMATETC pFE)
00283 {
00284   PRNTDEBUG("nsDataObj::QueryGetData  ");
00285   PRNTDEBUG3("format: %d  Text: %d\n", pFE->cfFormat, CF_TEXT);
00286 
00287   ULONG count;
00288   FORMATETC fe;
00289   m_enumFE->Reset();
00290   while (NOERROR == m_enumFE->Next(1, &fe, &count)) {
00291     if (fe.cfFormat == pFE->cfFormat) {
00292       return S_OK;
00293     }
00294   }
00295   
00296   PRNTDEBUG2("***** nsDataObj::QueryGetData - Unknown format %d\n", pFE->cfFormat);
00297        return ResultFromScode(E_FAIL);
00298 }
00299 
00300 //-----------------------------------------------------
00301 STDMETHODIMP nsDataObj::GetCanonicalFormatEtc
00302         (LPFORMATETC pFEIn, LPFORMATETC pFEOut)
00303 {
00304   PRNTDEBUG("nsDataObj::GetCanonicalFormatEtc\n");
00305               return ResultFromScode(E_FAIL);
00306 }
00307 
00308 
00309 //-----------------------------------------------------
00310 STDMETHODIMP nsDataObj::SetData(LPFORMATETC pFE, LPSTGMEDIUM pSTM, BOOL fRelease)
00311 {
00312   PRNTDEBUG("nsDataObj::SetData\n");
00313 #ifndef WINCE
00314   static CLIPFORMAT PerformedDropEffect = ::RegisterClipboardFormat( CFSTR_PERFORMEDDROPEFFECT );  
00315 
00316   if (pFE && pFE->cfFormat == PerformedDropEffect) {
00317     // The drop operation has completed.  Delete the temp file if it exists.
00318     if (mCachedTempFile) {
00319       mCachedTempFile->Remove(PR_FALSE);
00320       mCachedTempFile = NULL;
00321     }
00322   }
00323 #endif
00324 
00325   if (fRelease) {
00326     ReleaseStgMedium(pSTM);
00327   }
00328 
00329   return ResultFromScode(S_OK);
00330 }
00331 
00332 
00333 //-----------------------------------------------------
00334 STDMETHODIMP nsDataObj::EnumFormatEtc(DWORD dwDir, LPENUMFORMATETC *ppEnum)
00335 {
00336   PRNTDEBUG("nsDataObj::EnumFormatEtc\n");
00337 
00338   switch (dwDir) {
00339     case DATADIR_GET: {
00340        m_enumFE->Clone(ppEnum);
00341     } break;
00342     case DATADIR_SET:
00343         *ppEnum=NULL;
00344         break;
00345     default:
00346         *ppEnum=NULL;
00347         break;
00348   } // switch
00349 
00350   // Since a new one has been created, 
00351   // we will ref count the new clone here 
00352   // before giving it back
00353   if (NULL == *ppEnum)
00354     return ResultFromScode(E_FAIL);
00355   else
00356     (*ppEnum)->AddRef();
00357 
00358   return NOERROR;
00359 
00360 }
00361 
00362 //-----------------------------------------------------
00363 STDMETHODIMP nsDataObj::DAdvise(LPFORMATETC pFE, DWORD dwFlags,
00364                                                                                   LPADVISESINK pIAdviseSink, DWORD* pdwConn)
00365 {
00366   PRNTDEBUG("nsDataObj::DAdvise\n");
00367        return ResultFromScode(E_FAIL);
00368 }
00369 
00370 
00371 //-----------------------------------------------------
00372 STDMETHODIMP nsDataObj::DUnadvise(DWORD dwConn)
00373 {
00374   PRNTDEBUG("nsDataObj::DUnadvise\n");
00375        return ResultFromScode(E_FAIL);
00376 }
00377 
00378 //-----------------------------------------------------
00379 STDMETHODIMP nsDataObj::EnumDAdvise(LPENUMSTATDATA *ppEnum)
00380 {
00381   PRNTDEBUG("nsDataObj::EnumDAdvise\n");
00382        return ResultFromScode(E_FAIL);
00383 }
00384 
00385 //-----------------------------------------------------
00386 // other methods
00387 //-----------------------------------------------------
00388 ULONG nsDataObj::GetCumRefCount()
00389 {
00390        return g_cRef;
00391 }
00392 
00393 //-----------------------------------------------------
00394 ULONG nsDataObj::GetRefCount() const
00395 {
00396        return m_cRef;
00397 }
00398 
00399 //-----------------------------------------------------
00400 // GetData and SetData helper functions
00401 //-----------------------------------------------------
00402 HRESULT nsDataObj::AddSetFormat(FORMATETC& aFE)
00403 {
00404   PRNTDEBUG("nsDataObj::AddSetFormat\n");
00405        return ResultFromScode(S_OK);
00406 }
00407 
00408 //-----------------------------------------------------
00409 HRESULT nsDataObj::AddGetFormat(FORMATETC& aFE)
00410 {
00411   PRNTDEBUG("nsDataObj::AddGetFormat\n");
00412        return ResultFromScode(S_OK);
00413 }
00414 
00415 //-----------------------------------------------------
00416 HRESULT 
00417 nsDataObj::GetBitmap ( const nsACString& , FORMATETC&, STGMEDIUM& )
00418 {
00419   PRNTDEBUG("nsDataObj::GetBitmap\n");
00420        return ResultFromScode(E_NOTIMPL);
00421 }
00422 
00423 
00424 //
00425 // GetDIB
00426 //
00427 // Someone is asking for a bitmap. The data in the transferable will be a straight
00428 // nsIImage, so just QI it.
00429 //
00430 HRESULT 
00431 nsDataObj :: GetDib ( const nsACString& inFlavor, FORMATETC &, STGMEDIUM & aSTG )
00432 {
00433   PRNTDEBUG("nsDataObj::GetDib\n");
00434   ULONG result = E_FAIL;
00435 #ifndef WINCE  
00436   
00437   PRUint32 len = 0;
00438   nsCOMPtr<nsISupports> genericDataWrapper;
00439   mTransferable->GetTransferData(PromiseFlatCString(inFlavor).get(), getter_AddRefs(genericDataWrapper), &len);
00440   nsCOMPtr<nsIImage> image ( do_QueryInterface(genericDataWrapper) );
00441   if ( !image ) {
00442     // In the 0.9.4 timeframe, I had some embedding clients put the nsIImage directly into the
00443     // transferable. Newer code, however, wraps the nsIImage in a nsISupportsInterfacePointer.
00444     // We should be backwards compatibile with code already out in the field. If we can't find
00445     // the image directly out of the transferable,  unwrap the image from its wrapper.
00446     nsCOMPtr<nsISupportsInterfacePointer> ptr(do_QueryInterface(genericDataWrapper));
00447     if ( ptr )
00448       ptr->GetData(getter_AddRefs(image));
00449   }
00450   
00451   if ( image ) {
00452     // use a the helper class to build up a bitmap. We now own the bits,
00453     // and pass them back to the OS in |aSTG|.
00454     nsImageToClipboard converter ( image );
00455     HANDLE bits = nsnull;
00456     nsresult rv = converter.GetPicture ( &bits );
00457     if ( NS_SUCCEEDED(rv) && bits ) {
00458       aSTG.hGlobal = bits;
00459       aSTG.tymed = TYMED_HGLOBAL;
00460       result = S_OK;
00461     }
00462   } // if we have an image
00463   else  
00464     NS_WARNING ( "Definately not an image on clipboard" );
00465 
00466 #endif
00467        return ResultFromScode(result);
00468 }
00469 
00470 
00471 
00472 //
00473 // GetFileDescriptor
00474 //
00475 
00476 HRESULT 
00477 nsDataObj :: GetFileDescriptor ( FORMATETC& aFE, STGMEDIUM& aSTG, PRBool aIsUnicode )
00478 {
00479   HRESULT res = S_OK;
00480   
00481   // How we handle this depends on if we're dealing with an internet
00482   // shortcut, since those are done under the covers.
00483   if ( IsInternetShortcut() ) {
00484     if ( aIsUnicode )
00485       res = GetFileDescriptorInternetShortcutW ( aFE, aSTG );
00486     else
00487       res = GetFileDescriptorInternetShortcutA ( aFE, aSTG );
00488   } 
00489   else
00490     NS_WARNING ( "Not yet implemented\n" );
00491   
00492        return res;
00493        
00494 } // GetFileDescriptor
00495 
00496 
00497 //
00498 HRESULT 
00499 nsDataObj :: GetFileContents ( FORMATETC& aFE, STGMEDIUM& aSTG )
00500 {
00501   HRESULT res = S_OK;
00502   
00503   // How we handle this depends on if we're dealing with an internet
00504   // shortcut, since those are done under the covers.
00505   if ( IsInternetShortcut() )  
00506     res = GetFileContentsInternetShortcut ( aFE, aSTG );
00507   else
00508     NS_WARNING ( "Not yet implemented\n" );
00509 
00510        return res;
00511        
00512 } // GetFileContents
00513 
00514 // 
00515 // Given a unicode string, we ensure that it contains only characters which are valid within
00516 // the file system. Remove all forbidden characters from the name, and completely disallow 
00517 // any title that starts with a forbidden name and extension (e.g. "nul" is invalid, but 
00518 // "nul." and "nul.txt" are also invalid and will cause problems).
00519 //
00520 // It would seem that this is more functionality suited to being in nsILocalFile.
00521 //
00522 static void
00523 MangleTextToValidFilename(nsString & aText)
00524 {
00525   static const char* forbiddenNames[] = {
00526     "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", 
00527     "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
00528     "CON", "PRN", "AUX", "NUL", "CLOCK$"
00529   };
00530 
00531   aText.StripChars(FILE_PATH_SEPARATOR  FILE_ILLEGAL_CHARACTERS);
00532   aText.CompressWhitespace(PR_TRUE, PR_TRUE);
00533   PRUint32 nameLen;
00534   for (size_t n = 0; n < NS_ARRAY_LENGTH(forbiddenNames); ++n) {
00535     nameLen = (PRUint32) strlen(forbiddenNames[n]);
00536     if (aText.EqualsIgnoreCase(forbiddenNames[n], nameLen)) {
00537       // invalid name is either the entire string, or a prefix with a period
00538       if (aText.Length() == nameLen || aText.CharAt(nameLen) == PRUnichar('.')) {
00539         aText.Truncate();
00540         break;
00541       }
00542     }
00543   }
00544 }
00545 
00546 // 
00547 // Given a unicode string, convert it down to a valid local charset filename
00548 // with the supplied extension. This ensures that we do not cut MBCS characters
00549 // in the middle.
00550 //
00551 // It would seem that this is more functionality suited to being in nsILocalFile.
00552 //
00553 static PRBool
00554 CreateFilenameFromTextA(nsString & aText, const char * aExtension, 
00555                          char * aFilename, PRUint32 aFilenameLen)
00556 {
00557   // ensure that the supplied name doesn't have invalid characters. If 
00558   // a valid mangled filename couldn't be created then it will leave the
00559   // text empty.
00560   MangleTextToValidFilename(aText);
00561   if (aText.IsEmpty())
00562     return PR_FALSE;
00563 
00564   // repeatably call WideCharToMultiByte as long as the title doesn't fit in the buffer 
00565   // available to us. Continually reduce the length of the source title until the MBCS
00566   // version will fit in the buffer with room for the supplied extension. Doing it this
00567   // way ensures that even in MBCS environments there will be a valid MBCS filename of
00568   // the correct length.
00569   int maxUsableFilenameLen = aFilenameLen - strlen(aExtension) - 1; // space for ext + null byte
00570   int currLen, textLen = (int) NS_MIN(aText.Length(), aFilenameLen);
00571   char defaultChar = '_';
00572   do {
00573     currLen = WideCharToMultiByte(CP_ACP, 
00574       WC_COMPOSITECHECK|WC_DEFAULTCHAR,
00575       aText.get(), textLen--, aFilename, maxUsableFilenameLen, &defaultChar, NULL);
00576   }
00577   while (currLen == 0 && textLen > 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER);
00578   if (currLen > 0 && textLen > 0) {
00579     strcpy(&aFilename[currLen], aExtension);
00580     return PR_TRUE;
00581   }
00582   else {
00583     // empty names aren't permitted
00584     return PR_FALSE;
00585   }
00586 }
00587 
00588 static PRBool
00589 CreateFilenameFromTextW(nsString & aText, const wchar_t * aExtension, 
00590                          wchar_t * aFilename, PRUint32 aFilenameLen)
00591 {
00592   // ensure that the supplied name doesn't have invalid characters. If 
00593   // a valid mangled filename couldn't be created then it will leave the
00594   // text empty.
00595   MangleTextToValidFilename(aText);
00596   if (aText.IsEmpty())
00597     return PR_FALSE;
00598 
00599   const int extensionLen = wcslen(aExtension);
00600   if (aText.Length() + extensionLen + 1 > aFilenameLen)
00601     aText.Truncate(aFilenameLen - extensionLen - 1);
00602   wcscpy(&aFilename[0], aText.get());
00603   wcscpy(&aFilename[aText.Length()], aExtension);
00604   return PR_TRUE;
00605 }
00606 
00607 static NS_DEFINE_CID(kStringBundleServiceCID, NS_STRINGBUNDLESERVICE_CID);
00608 #define PAGEINFO_PROPERTIES "chrome://navigator/locale/pageInfo.properties"
00609 
00610 static PRBool
00611 GetLocalizedString(const PRUnichar * aName, nsXPIDLString & aString)
00612 {
00613   nsresult rv;
00614   nsCOMPtr<nsIStringBundleService> stringService = do_GetService(kStringBundleServiceCID, &rv);
00615   if (NS_FAILED(rv)) 
00616     return PR_FALSE;
00617 
00618   nsCOMPtr<nsIStringBundle> stringBundle;
00619   rv = stringService->CreateBundle(PAGEINFO_PROPERTIES, getter_AddRefs(stringBundle));
00620   if (NS_FAILED(rv))
00621     return PR_FALSE;
00622 
00623   rv = stringBundle->GetStringFromName(aName, getter_Copies(aString));
00624   return NS_SUCCEEDED(rv);
00625 }
00626 
00627 //
00628 // GetFileDescriptorInternetShortcut
00629 //
00630 // Create the special format for an internet shortcut and build up the data
00631 // structures the shell is expecting.
00632 //
00633 HRESULT
00634 nsDataObj :: GetFileDescriptorInternetShortcutA ( FORMATETC& aFE, STGMEDIUM& aSTG )
00635 {
00636 #ifdef WINCE
00637   return E_FAIL;
00638 #else
00639   // get the title of the shortcut
00640   nsAutoString title;
00641   if ( NS_FAILED(ExtractShortcutTitle(title)) )
00642     return E_OUTOFMEMORY;
00643 
00644   HGLOBAL fileGroupDescHandle = ::GlobalAlloc(GMEM_ZEROINIT|GMEM_SHARE,sizeof(FILEGROUPDESCRIPTORA));
00645   if (!fileGroupDescHandle)
00646     return E_OUTOFMEMORY;
00647 
00648   LPFILEGROUPDESCRIPTORA fileGroupDescA = NS_REINTERPRET_CAST(LPFILEGROUPDESCRIPTORA, 
00649       ::GlobalLock(fileGroupDescHandle));
00650   if (!fileGroupDescA) {
00651     ::GlobalFree(fileGroupDescHandle);
00652     return E_OUTOFMEMORY;
00653   }
00654 
00655   // get a valid filename in the following order: 1) from the page title, 
00656   // 2) localized string for an untitled page, 3) just use "Untitled.URL"
00657   if (!CreateFilenameFromTextA(title, ".URL", 
00658           fileGroupDescA->fgd[0].cFileName, NS_MAX_FILEDESCRIPTOR)) {
00659     nsXPIDLString untitled;
00660     if (!GetLocalizedString(NS_LITERAL_STRING("noPageTitle").get(), untitled) ||
00661         !CreateFilenameFromTextA(untitled, ".URL", 
00662             fileGroupDescA->fgd[0].cFileName, NS_MAX_FILEDESCRIPTOR)) {
00663       strcpy(fileGroupDescA->fgd[0].cFileName, "Untitled.URL");
00664     }
00665   }
00666 
00667   // one file in the file block
00668   fileGroupDescA->cItems = 1;
00669   fileGroupDescA->fgd[0].dwFlags = FD_LINKUI;
00670 
00671   ::GlobalUnlock( fileGroupDescHandle );
00672   aSTG.hGlobal = fileGroupDescHandle;
00673   aSTG.tymed = TYMED_HGLOBAL;
00674 
00675   return S_OK;
00676 #endif
00677 } // GetFileDescriptorInternetShortcutA
00678 
00679 HRESULT
00680 nsDataObj :: GetFileDescriptorInternetShortcutW ( FORMATETC& aFE, STGMEDIUM& aSTG )
00681 {
00682 #ifdef WINCE
00683   return E_FAIL;
00684 #else
00685   // get the title of the shortcut
00686   nsAutoString title;
00687   if ( NS_FAILED(ExtractShortcutTitle(title)) )
00688     return E_OUTOFMEMORY;
00689 
00690   HGLOBAL fileGroupDescHandle = ::GlobalAlloc(GMEM_ZEROINIT|GMEM_SHARE,sizeof(FILEGROUPDESCRIPTORW));
00691   if (!fileGroupDescHandle)
00692     return E_OUTOFMEMORY;
00693 
00694   LPFILEGROUPDESCRIPTORW fileGroupDescW = NS_REINTERPRET_CAST(LPFILEGROUPDESCRIPTORW, 
00695       ::GlobalLock(fileGroupDescHandle));
00696   if (!fileGroupDescW) {
00697     ::GlobalFree(fileGroupDescHandle);
00698     return E_OUTOFMEMORY;
00699   }
00700 
00701   // get a valid filename in the following order: 1) from the page title, 
00702   // 2) localized string for an untitled page, 3) just use "Untitled.URL"
00703   if (!CreateFilenameFromTextW(title, NS_LITERAL_STRING(".URL").get(), 
00704           fileGroupDescW->fgd[0].cFileName, NS_MAX_FILEDESCRIPTOR)) {
00705     nsXPIDLString untitled;
00706     if (!GetLocalizedString(NS_LITERAL_STRING("noPageTitle").get(), untitled) ||
00707         !CreateFilenameFromTextW(untitled, NS_LITERAL_STRING(".URL").get(), 
00708             fileGroupDescW->fgd[0].cFileName, NS_MAX_FILEDESCRIPTOR)) {
00709       wcscpy(fileGroupDescW->fgd[0].cFileName, NS_LITERAL_STRING("Untitled.URL").get());
00710     }
00711   }
00712 
00713   // one file in the file block
00714   fileGroupDescW->cItems = 1;
00715   fileGroupDescW->fgd[0].dwFlags = FD_LINKUI;
00716 
00717   ::GlobalUnlock( fileGroupDescHandle );
00718   aSTG.hGlobal = fileGroupDescHandle;
00719   aSTG.tymed = TYMED_HGLOBAL;
00720 
00721   return S_OK;
00722 #endif
00723 } // GetFileDescriptorInternetShortcutW
00724 
00725 
00726 //
00727 // GetFileContentsInternetShortcut
00728 //
00729 // Create the special format for an internet shortcut and build up the data
00730 // structures the shell is expecting.
00731 //
00732 HRESULT
00733 nsDataObj :: GetFileContentsInternetShortcut ( FORMATETC& aFE, STGMEDIUM& aSTG )
00734 {
00735 #ifdef WINCE
00736   return E_FAIL;
00737 #else
00738   nsAutoString url;
00739   if ( NS_FAILED(ExtractShortcutURL(url)) )
00740     return E_OUTOFMEMORY;
00741 
00742   // will need to change if we ever support iDNS
00743   nsCAutoString asciiUrl;
00744   LossyCopyUTF16toASCII(url, asciiUrl);
00745     
00746   static const char* shortcutFormatStr = "[InternetShortcut]\r\nURL=%s\r\n";
00747   static const int formatLen = strlen(shortcutFormatStr) - 2; // don't include %s in the len
00748   const int totalLen = formatLen + asciiUrl.Length(); // we don't want a null character on the end
00749 
00750   // create a global memory area and build up the file contents w/in it
00751   HGLOBAL hGlobalMemory = ::GlobalAlloc(GMEM_SHARE, totalLen);
00752   if ( !hGlobalMemory )
00753     return E_OUTOFMEMORY;
00754 
00755   char* contents = NS_REINTERPRET_CAST(char*, ::GlobalLock(hGlobalMemory));
00756   if ( !contents ) {
00757     ::GlobalFree( hGlobalMemory );
00758     return E_OUTOFMEMORY;
00759   }
00760     
00761   //NOTE: we intentionally use the Microsoft version of snprintf here because it does NOT null 
00762   // terminate strings which reach the maximum size of the buffer. Since we know that the 
00763   // formatted length here is totalLen, this call to _snprintf will format the string into 
00764   // the buffer without appending the null character.
00765   _snprintf( contents, totalLen, shortcutFormatStr, asciiUrl.get() );
00766     
00767   ::GlobalUnlock(hGlobalMemory);
00768   aSTG.hGlobal = hGlobalMemory;
00769   aSTG.tymed = TYMED_HGLOBAL;
00770 
00771   return S_OK;
00772 #endif  
00773 } // GetFileContentsInternetShortcut
00774 
00775 
00776 //
00777 // IsInternetShortcut
00778 //
00779 // Is the data that we're handling destined for an internet shortcut if
00780 // dragged to the desktop? We can tell because there will be a mime type
00781 // of |kURLMime| present in the transferable
00782 //
00783 PRBool
00784 nsDataObj :: IsInternetShortcut ( )
00785 {
00786   PRBool retval = PR_FALSE;
00787   
00788   if ( !mTransferable )
00789     return PR_FALSE;
00790   
00791   // get the list of flavors available in the transferable
00792   nsCOMPtr<nsISupportsArray> flavorList;
00793   mTransferable->FlavorsTransferableCanExport ( getter_AddRefs(flavorList) );
00794   if ( !flavorList )
00795     return PR_FALSE;
00796   
00797   // go spelunking for the url flavor
00798   PRUint32 cnt;
00799   flavorList->Count(&cnt);
00800   for ( PRUint32 i = 0;i < cnt; ++i ) {
00801     nsCOMPtr<nsISupports> genericFlavor;
00802     flavorList->GetElementAt (i, getter_AddRefs(genericFlavor));
00803     nsCOMPtr<nsISupportsCString> currentFlavor (do_QueryInterface(genericFlavor));
00804     if (currentFlavor) {
00805       nsCAutoString flavorStr;
00806       currentFlavor->GetData(flavorStr);
00807       if ( flavorStr.Equals(kURLMime) ) {
00808         retval = PR_TRUE;         // found it!
00809         break;
00810       }
00811     }
00812   } // for each flavor
00813 
00814   return retval;
00815   
00816 } // IsInternetShortcut
00817 
00818 HRESULT nsDataObj::GetPreferredDropEffect ( FORMATETC& aFE, STGMEDIUM& aSTG )
00819 {
00820   HRESULT res = S_OK;
00821   aSTG.tymed = TYMED_HGLOBAL;
00822   aSTG.pUnkForRelease = NULL;    
00823   HGLOBAL hGlobalMemory = NULL;
00824   hGlobalMemory = ::GlobalAlloc(GMEM_MOVEABLE, sizeof(DWORD));
00825   if (hGlobalMemory) {
00826     DWORD* pdw = (DWORD*) GlobalLock(hGlobalMemory);
00827     // The PreferredDropEffect clipboard format is only registered if a drag/drop
00828     // of an image happens from Mozilla to the desktop.  We want its value
00829     // to be DROPEFFECT_MOVE in that case so that the file is moved from the
00830     // temporary location, not copied.
00831     // This value should, ideally, be set on the data object via SetData() but 
00832     // our IDataObject implementation doesn't implement SetData.  It adds data
00833     // to the data object lazily only when the drop target asks for it.
00834     *pdw = (DWORD) DROPEFFECT_MOVE;
00835     GlobalUnlock(hGlobalMemory);
00836   }
00837   else {
00838     res = E_OUTOFMEMORY;
00839   }
00840   aSTG.hGlobal = hGlobalMemory;
00841   return res;
00842 }
00843 
00844 //-----------------------------------------------------
00845 HRESULT nsDataObj::GetText(const nsACString & aDataFlavor, FORMATETC& aFE, STGMEDIUM& aSTG)
00846 {
00847   void* data;
00848   PRUint32   len;
00849   
00850   // if someone asks for text/plain, look up text/unicode instead in the transferable.
00851   const char* flavorStr;
00852   const nsPromiseFlatCString& flat = PromiseFlatCString(aDataFlavor);
00853   if ( aDataFlavor.Equals("text/plain") )
00854     flavorStr = kUnicodeMime;
00855   else
00856     flavorStr = flat.get();
00857 
00858   // NOTE: CreateDataFromPrimitive creates new memory, that needs to be deleted
00859   nsCOMPtr<nsISupports> genericDataWrapper;
00860   mTransferable->GetTransferData(flavorStr, getter_AddRefs(genericDataWrapper), &len);
00861   if ( !len )
00862     return ResultFromScode(E_FAIL);
00863   nsPrimitiveHelpers::CreateDataFromPrimitive ( flavorStr, genericDataWrapper, &data, len );
00864   HGLOBAL     hGlobalMemory = NULL;
00865 
00866   aSTG.tymed          = TYMED_HGLOBAL;
00867   aSTG.pUnkForRelease = NULL;
00868 
00869   // We play games under the hood and advertise flavors that we know we
00870   // can support, only they require a bit of conversion or munging of the data.
00871   // Do that here.
00872   //
00873   // The transferable gives us data that is null-terminated, but this isn't reflected in
00874   // the |len| parameter. Windoze apps expect this null to be there so bump our data buffer
00875   // by the appropriate size to account for the null (one char for CF_TEXT, one PRUnichar for
00876   // CF_UNICODETEXT).
00877   DWORD allocLen = (DWORD)len;
00878   if ( aFE.cfFormat == CF_TEXT ) {
00879     // Someone is asking for text/plain; convert the unicode (assuming it's present)
00880     // to text with the correct platform encoding.
00881     char* plainTextData = nsnull;
00882     PRUnichar* castedUnicode = NS_REINTERPRET_CAST(PRUnichar*, data);
00883     PRInt32 plainTextLen = 0;
00884     nsPrimitiveHelpers::ConvertUnicodeToPlatformPlainText ( castedUnicode, len / 2, &plainTextData, &plainTextLen );
00885    
00886     // replace the unicode data with our plaintext data. Recall that |plainTextLen| doesn't include
00887     // the null in the length.
00888     nsMemory::Free(data);
00889     if ( plainTextData ) {
00890       data = plainTextData;
00891       allocLen = plainTextLen + sizeof(char);
00892     }
00893     else {
00894       NS_WARNING ( "Oh no, couldn't convert unicode to plain text" );
00895       return ResultFromScode(S_OK);
00896     }
00897   }
00898   else if ( aFE.cfFormat == nsClipboard::CF_HTML ) {
00899     // Someone is asking for win32's HTML flavor. Convert our html fragment
00900     // from unicode to UTF-8 then put it into a format specified by msft.
00901     NS_ConvertUCS2toUTF8 converter ( NS_REINTERPRET_CAST(PRUnichar*, data) );
00902     char* utf8HTML = nsnull;
00903     nsresult rv = BuildPlatformHTML ( converter.get(), &utf8HTML );      // null terminates
00904     
00905     nsMemory::Free(data);
00906     if ( NS_SUCCEEDED(rv) && utf8HTML ) {
00907       // replace the unicode data with our HTML data. Don't forget the null.
00908       data = utf8HTML;
00909       allocLen = strlen(utf8HTML) + sizeof(char);
00910     }
00911     else {
00912       NS_WARNING ( "Oh no, couldn't convert to HTML" );
00913       return ResultFromScode(S_OK);
00914     }
00915   }
00916   else {
00917     // we assume that any data that isn't caught above is unicode. This may
00918     // be an erroneous assumption, but is true so far.
00919     allocLen += sizeof(PRUnichar);
00920   }
00921 
00922   hGlobalMemory = (HGLOBAL)GlobalAlloc(GMEM_MOVEABLE, allocLen);
00923 
00924   // Copy text to Global Memory Area
00925   if ( hGlobalMemory ) {
00926     char* dest = NS_REINTERPRET_CAST(char*, GlobalLock(hGlobalMemory));
00927     char* source = NS_REINTERPRET_CAST(char*, data);
00928     memcpy ( dest, source, allocLen );                         // copies the null as well
00929     GlobalUnlock(hGlobalMemory);
00930   }
00931   aSTG.hGlobal = hGlobalMemory;
00932 
00933   // Now, delete the memory that was created by CreateDataFromPrimitive (or our text/plain data)
00934   nsMemory::Free(data);
00935 
00936   return ResultFromScode(S_OK);
00937 }
00938 
00939 //-----------------------------------------------------
00940 HRESULT nsDataObj::GetFile(const nsACString & aDataFlavor, FORMATETC& aFE, STGMEDIUM& aSTG)
00941 {
00942   HRESULT res = S_OK;
00943   if (strcmp(PromiseFlatCString(aDataFlavor).get(), kFilePromiseMime) == 0) {
00944     // XXX We are copying the file twice below.  Once from the original location to the temporary
00945     // one (here) and then from the temporary location to the drop location (done by the drop target
00946     // when this function returns).  If the file being drag-dropped is in the cache, we should be able to
00947     // just pass back its file name to the drop target and save a copy.  Need help here!
00948     nsresult rv;
00949     if (!mCachedTempFile) {
00950       // Save the file to a temporary location.      
00951       nsCOMPtr<nsILocalFile> dropDirectory;
00952       nsCOMPtr<nsIProperties> directoryService = 
00953         do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
00954       if (!directoryService || NS_FAILED(rv)) return ResultFromScode(E_FAIL);
00955       directoryService->Get(NS_OS_TEMP_DIR, 
00956                             NS_GET_IID(nsIFile), 
00957                             getter_AddRefs(dropDirectory));
00958       nsCOMPtr<nsISupports> localFileISupports = do_QueryInterface(dropDirectory);
00959       rv = mTransferable->SetTransferData(kFilePromiseDirectoryMime, localFileISupports, sizeof(nsILocalFile*));
00960       if (NS_FAILED(rv)) return ResultFromScode(E_FAIL);
00961       nsCOMPtr<nsISupports> fileDataPrimitive;
00962       PRUint32 dataSize = 0;
00963       rv = mTransferable->GetTransferData(kFilePromiseMime, getter_AddRefs(fileDataPrimitive), &dataSize);
00964       if (NS_FAILED(rv)) return ResultFromScode(E_FAIL);      
00965       
00966       mCachedTempFile = do_QueryInterface(fileDataPrimitive);
00967       if (!mCachedTempFile) return ResultFromScode(E_FAIL);
00968     }
00969 
00970     // Pass the file name back to the drop target so that it can copy to the drop location
00971     nsAutoString path;
00972     rv = mCachedTempFile->GetPath(path);
00973     if (NS_FAILED(rv)) return ResultFromScode(E_FAIL);
00974     // Two null characters are needed to terminate the file name list
00975     PRUint32 allocLen = path.Length() + 2;
00976     HGLOBAL hGlobalMemory = NULL;
00977     aSTG.tymed = TYMED_HGLOBAL;
00978     aSTG.pUnkForRelease = NULL;
00979     hGlobalMemory = GlobalAlloc(GMEM_MOVEABLE, sizeof(DROPFILES) + allocLen * sizeof(PRUnichar));
00980     if (hGlobalMemory) {
00981       DROPFILES* pDropFile = NS_REINTERPRET_CAST(DROPFILES*, GlobalLock(hGlobalMemory));
00982 
00983       // First, populate drop file structure
00984       pDropFile->pFiles = sizeof(DROPFILES);   // offset to start of file name char array
00985       pDropFile->fNC = 0;
00986       pDropFile->pt.x = 0;
00987       pDropFile->pt.y = 0;
00988       pDropFile->fWide = TRUE;
00989 
00990       // Copy the filename right after the DROPFILES structure
00991       PRUnichar* dest = NS_REINTERPRET_CAST(PRUnichar*, NS_REINTERPRET_CAST(char*, pDropFile) + pDropFile->pFiles);
00992       memcpy(dest, path.get(), (allocLen - 1) * sizeof(PRUnichar));    // copies the null character in path as well
00993 
00994       // Two null characters are needed at the end of the file name.  
00995       // Lookup the CF_HDROP shell clipboard format for more info.
00996       // Add the second null character right after the first one.
00997       dest[allocLen - 1] = L'\0';
00998 
00999       GlobalUnlock(hGlobalMemory);
01000     }
01001     else {
01002       res = E_OUTOFMEMORY;
01003     }
01004     aSTG.hGlobal = hGlobalMemory;
01005   }
01006   return res;
01007 }
01008 
01009 //-----------------------------------------------------
01010 HRESULT nsDataObj::GetMetafilePict(FORMATETC&, STGMEDIUM&)
01011 {
01012        return ResultFromScode(E_NOTIMPL);
01013 }
01014 
01015 //-----------------------------------------------------
01016 HRESULT nsDataObj::SetBitmap(FORMATETC&, STGMEDIUM&)
01017 {
01018        return ResultFromScode(E_NOTIMPL);
01019 }
01020 
01021 //-----------------------------------------------------
01022 HRESULT nsDataObj::SetDib   (FORMATETC&, STGMEDIUM&)
01023 {
01024        return ResultFromScode(E_FAIL);
01025 }
01026 
01027 //-----------------------------------------------------
01028 HRESULT nsDataObj::SetText  (FORMATETC& aFE, STGMEDIUM& aSTG)
01029 {
01030   if (aFE.cfFormat == CF_TEXT && aFE.tymed ==  TYMED_HGLOBAL) {
01031               HGLOBAL hString = (HGLOBAL)aSTG.hGlobal;
01032               if(hString == NULL)
01033                      return(FALSE);
01034 
01035               // get a pointer to the actual bytes
01036               char *  pString = (char *) GlobalLock(hString);    
01037               if(!pString)
01038                      return(FALSE);
01039 
01040               GlobalUnlock(hString);
01041     nsAutoString str; str.AssignWithConversion(pString);
01042 
01043   }
01044        return ResultFromScode(E_FAIL);
01045 }
01046 
01047 //-----------------------------------------------------
01048 HRESULT nsDataObj::SetMetafilePict (FORMATETC&, STGMEDIUM&)
01049 {
01050        return ResultFromScode(E_FAIL);
01051 }
01052 
01053 
01054 
01055 //-----------------------------------------------------
01056 //-----------------------------------------------------
01057 CLSID nsDataObj::GetClassID() const
01058 {
01059        return CLSID_nsDataObj;
01060 }
01061 
01062 //-----------------------------------------------------
01063 // Registers a the DataFlavor/FE pair
01064 //-----------------------------------------------------
01065 void nsDataObj::AddDataFlavor(const char* aDataFlavor, LPFORMATETC aFE)
01066 {
01067   // These two lists are the mapping to and from data flavors and FEs
01068   // Later, OLE will tell us it's needs a certain type of FORMATETC (text, unicode, etc)
01069   // so we will look up data flavor that corresponds to the FE
01070   // and then ask the transferable for that type of data
01071 
01072   // Add CF_HDROP to the head of the list so that it is returned first
01073   // when the drop target calls EnumFormatEtc to enumerate through the FE list
01074   // We use the CF_HDROP format to copy file contents when an image is dragged
01075   // from Mozilla to a drop target.
01076 #ifndef WINCE
01077   if (aFE->cfFormat == CF_HDROP) {
01078     mDataFlavors->InsertElementAt(new nsCAutoString(aDataFlavor), 0);
01079     m_enumFE->InsertFEAt(aFE, 0);
01080   }  
01081   else 
01082 #endif
01083   {
01084     mDataFlavors->AppendElement(new nsCAutoString(aDataFlavor));
01085     m_enumFE->AddFE(aFE);
01086   }
01087 }
01088 
01089 //-----------------------------------------------------
01090 // Sets the transferable object
01091 //-----------------------------------------------------
01092 void nsDataObj::SetTransferable(nsITransferable * aTransferable)
01093 {
01094     NS_IF_RELEASE(mTransferable);
01095 
01096   mTransferable = aTransferable;
01097   if (nsnull == mTransferable) {
01098     return;
01099   }
01100 
01101   NS_ADDREF(mTransferable);
01102 
01103   return;
01104 }
01105 
01106 
01107 //
01108 // ExtractURL
01109 //
01110 // Roots around in the transferable for the appropriate flavor that indicates
01111 // a url and pulls out the url portion of the data. Used mostly for creating
01112 // internet shortcuts on the desktop. The url flavor is of the format:
01113 //
01114 //   <url> <linefeed> <page title>
01115 //
01116 nsresult
01117 nsDataObj :: ExtractShortcutURL ( nsString & outURL )
01118 {
01119   NS_ASSERTION ( mTransferable, "We don't have a good transferable" );
01120   nsresult rv = NS_ERROR_FAILURE;
01121   
01122   PRUint32 len = 0;
01123   nsCOMPtr<nsISupports> genericURL;
01124   if ( NS_SUCCEEDED(mTransferable->GetTransferData(kURLMime, getter_AddRefs(genericURL), &len)) ) {
01125     nsCOMPtr<nsISupportsString> urlObject ( do_QueryInterface(genericURL) );
01126     if ( urlObject ) {
01127       nsAutoString url;
01128       urlObject->GetData ( url );
01129       outURL = url;
01130 
01131       // find the first linefeed in the data, that's where the url ends. trunc the 
01132       // result string at that point.
01133       PRInt32 lineIndex = outURL.FindChar ( '\n' );
01134       NS_ASSERTION ( lineIndex > 0, "Format for url flavor is <url> <linefeed> <page title>" );
01135       if ( lineIndex > 0 ) {
01136         outURL.Truncate ( lineIndex );
01137         rv = NS_OK;    
01138       }
01139     }
01140   } else if ( NS_SUCCEEDED(mTransferable->GetTransferData(kURLDataMime, getter_AddRefs(genericURL), &len)) ||
01141               NS_SUCCEEDED(mTransferable->GetTransferData(kURLPrivateMime, getter_AddRefs(genericURL), &len)) ) {
01142     nsCOMPtr<nsISupportsString> urlObject ( do_QueryInterface(genericURL) );
01143     if ( urlObject ) {
01144       nsAutoString url;
01145       urlObject->GetData ( url );
01146       outURL = url;
01147 
01148       rv = NS_OK;    
01149     }
01150 
01151   }  // if found flavor
01152   
01153   return rv;
01154 
01155 } // ExtractShortcutURL
01156 
01157 
01158 //
01159 // ExtractShortcutTitle
01160 //
01161 // Roots around in the transferable for the appropriate flavor that indicates
01162 // a url and pulls out the title portion of the data. Used mostly for creating
01163 // internet shortcuts on the desktop. The url flavor is of the format:
01164 //
01165 //   <url> <linefeed> <page title>
01166 //
01167 nsresult
01168 nsDataObj :: ExtractShortcutTitle ( nsString & outTitle )
01169 {
01170   NS_ASSERTION ( mTransferable, "We'd don't have a good transferable" );
01171   nsresult rv = NS_ERROR_FAILURE;
01172   
01173   PRUint32 len = 0;
01174   nsCOMPtr<nsISupports> genericURL;
01175   if ( NS_SUCCEEDED(mTransferable->GetTransferData(kURLMime, getter_AddRefs(genericURL), &len)) ) {
01176     nsCOMPtr<nsISupportsString> urlObject ( do_QueryInterface(genericURL) );
01177     if ( urlObject ) {
01178       nsAutoString url;
01179       urlObject->GetData ( url );
01180 
01181       // find the first linefeed in the data, that's where the url ends. we want
01182       // everything after that linefeed. FindChar() returns -1 if we can't find
01183       PRInt32 lineIndex = url.FindChar ( '\n' );
01184       NS_ASSERTION ( lineIndex != -1, "Format for url flavor is <url> <linefeed> <page title>" );
01185       if ( lineIndex != -1 ) {
01186         url.Mid ( outTitle, lineIndex + 1, (len/2) - (lineIndex + 1) );
01187         rv = NS_OK;    
01188       }
01189     }
01190   } // if found flavor
01191   
01192   return rv;
01193 
01194 } // ExtractShortcutTitle
01195 
01196 
01197 //
01198 // BuildPlatformHTML
01199 //
01200 // Munge our HTML data to win32's CF_HTML spec. Basically, put the requisite
01201 // header information on it. This will null terminate |outPlatformHTML|. See
01202 //  http://msdn.microsoft.com/workshop/networking/clipboard/htmlclipboard.asp
01203 // for details.
01204 //
01205 // We assume that |inOurHTML| is already a fragment (ie, doesn't have <HTML>
01206 // or <BODY> tags). We'll wrap the fragment with them to make other apps
01207 // happy.
01208 //
01209 nsresult 
01210 nsDataObj :: BuildPlatformHTML ( const char* inOurHTML, char** outPlatformHTML ) 
01211 {
01212   *outPlatformHTML = nsnull;
01213 
01214   nsDependentCString inHTMLString(inOurHTML);
01215   const char* const numPlaceholder  = "00000000";
01216   const char* const startHTMLPrefix = "Version:0.9\r\nStartHTML:";
01217   const char* const endHTMLPrefix   = "\r\nEndHTML:";
01218   const char* const startFragPrefix = "\r\nStartFragment:";
01219   const char* const endFragPrefix   = "\r\nEndFragment:";
01220   const char* const startSourceURLPrefix = "\r\nSourceURL:";
01221   const char* const endFragTrailer  = "\r\n";
01222 
01223   // Do we already have mSourceURL from a drag?
01224   if (mSourceURL.IsEmpty()) {
01225     nsAutoString url;
01226     ExtractShortcutURL(url);
01227 
01228     AppendUTF16toUTF8(url, mSourceURL);
01229   }
01230 
01231   const PRInt32 kSourceURLLength    = mSourceURL.Length();
01232   const PRInt32 kNumberLength       = strlen(numPlaceholder);
01233 
01234   const PRInt32 kTotalHeaderLen     = strlen(startHTMLPrefix) +
01235                                       strlen(endHTMLPrefix) +
01236                                       strlen(startFragPrefix) + 
01237                                       strlen(endFragPrefix) + 
01238                                       strlen(endFragTrailer) +
01239                                       (kSourceURLLength > 0 ? strlen(startSourceURLPrefix) : 0) +
01240                                       kSourceURLLength +
01241                                       (4 * kNumberLength);
01242 
01243   NS_NAMED_LITERAL_CSTRING(htmlHeaderString, "<html><body>\r\n");
01244 
01245   NS_NAMED_LITERAL_CSTRING(fragmentHeaderString, "<!--StartFragment-->");
01246 
01247   nsDependentCString trailingString(
01248       "<!--EndFragment-->\r\n"
01249       "</body>\r\n"
01250       "</html>");
01251 
01252   // calculate the offsets
01253   PRInt32 startHTMLOffset = kTotalHeaderLen;
01254   PRInt32 startFragOffset = startHTMLOffset
01255                               + htmlHeaderString.Length()
01256                            + fragmentHeaderString.Length();
01257 
01258   PRInt32 endFragOffset   = startFragOffset
01259                               + inHTMLString.Length();
01260 
01261   PRInt32 endHTMLOffset   = endFragOffset
01262                               + trailingString.Length();
01263 
01264   // now build the final version
01265   nsCString clipboardString;
01266   clipboardString.SetCapacity(endHTMLOffset);
01267 
01268   clipboardString.Append(startHTMLPrefix);
01269   clipboardString.Append(nsPrintfCString("%08u", startHTMLOffset));
01270 
01271   clipboardString.Append(endHTMLPrefix);  
01272   clipboardString.Append(nsPrintfCString("%08u", endHTMLOffset));
01273 
01274   clipboardString.Append(startFragPrefix);
01275   clipboardString.Append(nsPrintfCString("%08u", startFragOffset));
01276 
01277   clipboardString.Append(endFragPrefix);
01278   clipboardString.Append(nsPrintfCString("%08u", endFragOffset));
01279 
01280   if (kSourceURLLength > 0) {
01281     clipboardString.Append(startSourceURLPrefix);
01282     clipboardString.Append(mSourceURL);
01283   }
01284 
01285   clipboardString.Append(endFragTrailer);
01286 
01287   clipboardString.Append(htmlHeaderString);
01288   clipboardString.Append(fragmentHeaderString);
01289   clipboardString.Append(inHTMLString);
01290   clipboardString.Append(trailingString);
01291 
01292   *outPlatformHTML = ToNewCString(clipboardString);
01293   if (!*outPlatformHTML)
01294     return NS_ERROR_OUT_OF_MEMORY;
01295 
01296   return NS_OK;
01297 }
01298 
01299 HRESULT 
01300 nsDataObj :: GetUniformResourceLocator( FORMATETC& aFE, STGMEDIUM& aSTG, PRBool aIsUnicode )
01301 {
01302   HRESULT res = S_OK;
01303   if ( IsInternetShortcut() ) {
01304     if ( aIsUnicode )
01305       res = ExtractUniformResourceLocatorW( aFE, aSTG );
01306     else
01307       res = ExtractUniformResourceLocatorA( aFE, aSTG );
01308   }
01309   else
01310     NS_WARNING ("Not yet implemented\n");
01311   return res;
01312 }
01313 
01314 HRESULT
01315 nsDataObj::ExtractUniformResourceLocatorA(FORMATETC& aFE, STGMEDIUM& aSTG )
01316 {
01317   HRESULT result = S_OK;
01318 
01319   nsAutoString url;
01320   if (NS_FAILED(ExtractShortcutURL(url)))
01321     return E_OUTOFMEMORY;
01322 
01323   NS_LossyConvertUTF16toASCII asciiUrl(url);
01324   const int totalLen = asciiUrl.Length() + 1;
01325   HGLOBAL hGlobalMemory = GlobalAlloc(GMEM_ZEROINIT|GMEM_SHARE, totalLen);
01326   if (!hGlobalMemory)
01327     return E_OUTOFMEMORY;
01328 
01329   char* contents = NS_REINTERPRET_CAST(char*, GlobalLock(hGlobalMemory));
01330   if (!contents) {
01331     GlobalFree(hGlobalMemory);
01332     return E_OUTOFMEMORY;
01333   }
01334 
01335   strcpy(contents, asciiUrl.get());
01336   GlobalUnlock(hGlobalMemory);
01337   aSTG.hGlobal = hGlobalMemory;
01338   aSTG.tymed = TYMED_HGLOBAL;
01339 
01340   return result;
01341 }
01342 
01343 HRESULT
01344 nsDataObj::ExtractUniformResourceLocatorW(FORMATETC& aFE, STGMEDIUM& aSTG )
01345 {
01346   HRESULT result = S_OK;
01347 
01348   nsAutoString url;
01349   if (NS_FAILED(ExtractShortcutURL(url)))
01350     return E_OUTOFMEMORY;
01351 
01352   const int totalLen = (url.Length() + 1) * sizeof(PRUnichar);
01353   HGLOBAL hGlobalMemory = GlobalAlloc(GMEM_ZEROINIT|GMEM_SHARE, totalLen);
01354   if (!hGlobalMemory)
01355     return E_OUTOFMEMORY;
01356 
01357   wchar_t* contents = NS_REINTERPRET_CAST(wchar_t*, GlobalLock(hGlobalMemory));
01358   if (!contents) {
01359     GlobalFree(hGlobalMemory);
01360     return E_OUTOFMEMORY;
01361   }
01362 
01363   wcscpy(contents, url.get());
01364   GlobalUnlock(hGlobalMemory);
01365   aSTG.hGlobal = hGlobalMemory;
01366   aSTG.tymed = TYMED_HGLOBAL;
01367 
01368   return result;
01369 }