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  *   Pierre Phaneuf <pp@ludusdesign.com>
00024  *   Sean Echevarria <sean@beatnik.com>
00025  *   David Gardiner <david.gardiner@unisa.edu.au>
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either the GNU General Public License Version 2 or later (the "GPL"), or
00029  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 #include "nsClipboard.h"
00042 #include <ole2.h>
00043 #include <shlobj.h>
00044 #include <intshcut.h>
00045 
00046 // shellapi.h is needed to build with WIN32_LEAN_AND_MEAN
00047 #include <shellapi.h>
00048 
00049 #include "nsCOMPtr.h"
00050 #include "nsDataObj.h"
00051 #include "nsIClipboardOwner.h"
00052 #include "nsString.h"
00053 #include "nsIFormatConverter.h"
00054 #include "nsITransferable.h"
00055 #include "nsCOMPtr.h"
00056 #include "nsXPCOM.h"
00057 #include "nsISupportsPrimitives.h"
00058 #include "nsXPIDLString.h"
00059 #include "nsReadableUtils.h"
00060 #include "nsUnicharUtils.h"
00061 #include "nsPrimitiveHelpers.h"
00062 #include "nsImageClipboard.h"
00063 #include "nsIWidget.h"
00064 #include "nsIComponentManager.h"
00065 #include "nsWidgetsCID.h"
00066 #include "nsCRT.h"
00067 
00068 #include "nsNetUtil.h"
00069 
00070 #include "nsIImage.h"
00071 #include "nsIObserverService.h"
00072 
00073 #include "nsToolkit.h"
00074 
00075 // oddly, this isn't in the MSVC headers anywhere.
00076 UINT nsClipboard::CF_HTML = ::RegisterClipboardFormat("HTML Format");
00077 
00078 
00079 //-------------------------------------------------------------------------
00080 //
00081 // nsClipboard constructor
00082 //
00083 //-------------------------------------------------------------------------
00084 nsClipboard::nsClipboard() : nsBaseClipboard()
00085 {
00086   mIgnoreEmptyNotification = PR_FALSE;
00087   mWindow         = nsnull;
00088 
00089   // Register for a shutdown notification so that we can flush data
00090   // to the OS clipboard.
00091   nsCOMPtr<nsIObserverService> observerService =
00092     do_GetService("@mozilla.org/observer-service;1");
00093   if (observerService)
00094     observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_FALSE);
00095 }
00096 
00097 //-------------------------------------------------------------------------
00098 // nsClipboard destructor
00099 //-------------------------------------------------------------------------
00100 nsClipboard::~nsClipboard()
00101 {
00102 
00103 }
00104 
00105 NS_IMPL_ISUPPORTS_INHERITED1(nsClipboard, nsBaseClipboard, nsIObserver)
00106 
00107 //-------------------------------------------------------------------------
00108 UINT nsClipboard::GetFormat(const char* aMimeStr)
00109 {
00110   UINT format;
00111 
00112   if (strcmp(aMimeStr, kTextMime) == 0)
00113     format = CF_TEXT;
00114   else if (strcmp(aMimeStr, kUnicodeMime) == 0)
00115     format = CF_UNICODETEXT;
00116 #ifndef WINCE
00117   else if (strcmp(aMimeStr, kJPEGImageMime) == 0)
00118     format = CF_DIB;
00119   else if (strcmp(aMimeStr, kFileMime) == 0 || 
00120            strcmp(aMimeStr, kFilePromiseMime) == 0)
00121     format = CF_HDROP;
00122 #endif
00123   else if (strcmp(aMimeStr, kURLMime) == 0 || 
00124            strcmp(aMimeStr, kURLDataMime) == 0 || 
00125            strcmp(aMimeStr, kURLDescriptionMime) == 0 || 
00126            strcmp(aMimeStr, kURLPrivateMime) == 0 || 
00127            strcmp(aMimeStr, kFilePromiseURLMime) == 0)
00128     format = CF_UNICODETEXT;
00129   else if (strcmp(aMimeStr, kNativeHTMLMime) == 0)
00130     format = CF_HTML;
00131   else
00132     format = ::RegisterClipboardFormat(aMimeStr);
00133 
00134   return format;
00135 }
00136 
00137 //-------------------------------------------------------------------------
00138 nsresult nsClipboard::CreateNativeDataObject(nsITransferable * aTransferable, IDataObject ** aDataObj, nsIURI * uri)
00139 {
00140   if (nsnull == aTransferable) {
00141     return NS_ERROR_FAILURE;
00142   }
00143 
00144   // Create our native DataObject that implements 
00145   // the OLE IDataObject interface
00146   nsDataObj * dataObj = new nsDataObj(uri);
00147 
00148   if (!dataObj) 
00149     return NS_ERROR_OUT_OF_MEMORY;
00150 
00151   dataObj->AddRef();
00152 
00153   // Now set it up with all the right data flavors & enums
00154   nsresult res = SetupNativeDataObject(aTransferable, dataObj);
00155   if (NS_OK == res) {
00156     *aDataObj = dataObj; 
00157   } else {
00158     delete dataObj;
00159   }
00160   return res;
00161 }
00162 
00163 //-------------------------------------------------------------------------
00164 nsresult nsClipboard::SetupNativeDataObject(nsITransferable * aTransferable, IDataObject * aDataObj)
00165 {
00166   if (nsnull == aTransferable || nsnull == aDataObj) {
00167     return NS_ERROR_FAILURE;
00168   }
00169 
00170   nsDataObj * dObj = NS_STATIC_CAST(nsDataObj *, aDataObj);
00171 
00172   // Now give the Transferable to the DataObject 
00173   // for getting the data out of it
00174   dObj->SetTransferable(aTransferable);
00175 
00176   // Get the transferable list of data flavors
00177   nsCOMPtr<nsISupportsArray> dfList;
00178   aTransferable->FlavorsTransferableCanExport(getter_AddRefs(dfList));
00179 
00180   // Walk through flavors that contain data and register them
00181   // into the DataObj as supported flavors
00182   PRUint32 i;
00183   PRUint32 cnt;
00184   dfList->Count(&cnt);
00185   for (i=0;i<cnt;i++) {
00186     nsCOMPtr<nsISupports> genericFlavor;
00187     dfList->GetElementAt ( i, getter_AddRefs(genericFlavor) );
00188     nsCOMPtr<nsISupportsCString> currentFlavor ( do_QueryInterface(genericFlavor) );
00189     if ( currentFlavor ) {
00190       nsXPIDLCString flavorStr;
00191       currentFlavor->ToString(getter_Copies(flavorStr));
00192       UINT format = GetFormat(flavorStr);
00193 
00194       // Now tell the native IDataObject about both our mime type and 
00195       // the native data format
00196       FORMATETC fe;
00197       SET_FORMATETC(fe, format, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL);
00198       dObj->AddDataFlavor(flavorStr, &fe);
00199       
00200       // Do various things internal to the implementation, like map one
00201       // flavor to another or add additional flavors based on what's required
00202       // for the win32 impl.
00203       if ( strcmp(flavorStr, kUnicodeMime) == 0 ) {
00204         // if we find text/unicode, also advertise text/plain (which we will convert
00205         // on our own in nsDataObj::GetText().
00206         FORMATETC textFE;
00207         SET_FORMATETC(textFE, CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL);
00208         dObj->AddDataFlavor(kTextMime, &textFE);
00209       }
00210       else if ( strcmp(flavorStr, kHTMLMime) == 0 ) {      
00211         // if we find text/html, also advertise win32's html flavor (which we will convert
00212         // on our own in nsDataObj::GetText().
00213         FORMATETC htmlFE;
00214         SET_FORMATETC(htmlFE, CF_HTML, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL);
00215         dObj->AddDataFlavor(kHTMLMime, &htmlFE);     
00216       }
00217       else if ( strcmp(flavorStr, kURLMime) == 0 ) {
00218         // if we're a url, in addition to also being text, we need to register
00219         // the "file" flavors so that the win32 shell knows to create an internet
00220         // shortcut when it sees one of these beasts.
00221         FORMATETC shortcutFE;
00222         SET_FORMATETC(shortcutFE, ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORA), 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL)
00223         dObj->AddDataFlavor(kURLMime, &shortcutFE);      
00224         SET_FORMATETC(shortcutFE, ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW), 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL)
00225         dObj->AddDataFlavor(kURLMime, &shortcutFE);      
00226 #ifndef WINCE
00227         SET_FORMATETC(shortcutFE, ::RegisterClipboardFormat(CFSTR_FILECONTENTS), 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL)
00228         dObj->AddDataFlavor(kURLMime, &shortcutFE);  
00229 #endif
00230         SET_FORMATETC(shortcutFE, ::RegisterClipboardFormat(CFSTR_INETURLA), 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL)
00231         dObj->AddDataFlavor(kURLMime, &shortcutFE);      
00232         SET_FORMATETC(shortcutFE, ::RegisterClipboardFormat(CFSTR_INETURLW), 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL)
00233         dObj->AddDataFlavor(kURLMime, &shortcutFE);      
00234       }
00235       else if ( strcmp(flavorStr, kPNGImageMime) == 0 || strcmp(flavorStr, kJPEGImageMime) == 0 ||
00236                   strcmp(flavorStr, kGIFImageMime) == 0 || strcmp(flavorStr, kNativeImageMime) == 0  ) {
00237         // if we're an image, register the native bitmap flavor
00238         FORMATETC imageFE;
00239         SET_FORMATETC(imageFE, CF_DIB, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL)
00240         dObj->AddDataFlavor(flavorStr, &imageFE);      
00241       }
00242 #ifndef WINCE
00243       else if ( strcmp(flavorStr, kFilePromiseMime) == 0 ) {
00244          // if we're a file promise flavor, also register the 
00245          // CFSTR_PREFERREDDROPEFFECT format.  The data object
00246          // returns a value of DROPEFFECTS_MOVE to the drop target
00247          // when it asks for the value of this format.  This causes
00248          // the file to be moved from the temporary location instead
00249          // of being copied.  The right thing to do here is to call
00250          // SetData() on the data object and set the value of this format
00251          // to DROPEFFECTS_MOVE on this particular data object.  But,
00252          // since all the other clipboard formats follow the model of setting
00253          // data on the data object only when the drop object calls GetData(),
00254          // I am leaving this format's value hard coded in the data object.
00255          // We can change this if other consumers of this format get added to this
00256          // codebase and they need different values.
00257         FORMATETC shortcutFE;
00258         SET_FORMATETC(shortcutFE, ::RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT), 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL)
00259         dObj->AddDataFlavor(kFilePromiseMime, &shortcutFE);
00260       }
00261 #endif
00262     }
00263   }
00264 
00265   return NS_OK;
00266 }
00267 
00268 //-------------------------------------------------------------------------
00269 NS_IMETHODIMP nsClipboard::SetNativeClipboardData ( PRInt32 aWhichClipboard )
00270 {
00271   if ( aWhichClipboard != kGlobalClipboard )
00272     return NS_ERROR_FAILURE;
00273 
00274   mIgnoreEmptyNotification = PR_TRUE;
00275 
00276   // make sure we have a good transferable
00277   if (nsnull == mTransferable) {
00278     return NS_ERROR_FAILURE;
00279   }
00280 
00281   IDataObject * dataObj;
00282   if ( NS_SUCCEEDED(CreateNativeDataObject(mTransferable, &dataObj, NULL)) ) { // this add refs dataObj
00283     ::OleSetClipboard(dataObj);
00284     dataObj->Release();
00285   } else {
00286     // Clear the native clipboard
00287     ::OleSetClipboard(NULL);
00288   }
00289 
00290   mIgnoreEmptyNotification = PR_FALSE;
00291 
00292   return NS_OK;
00293 }
00294 
00295 
00296 //-------------------------------------------------------------------------
00297 nsresult nsClipboard::GetGlobalData(HGLOBAL aHGBL, void ** aData, PRUint32 * aLen)
00298 {
00299   // Allocate a new memory buffer and copy the data from global memory.
00300   // Recall that win98 allocates to nearest DWORD boundary. As a safety
00301   // precaution, allocate an extra 2 bytes (but don't report them!) and
00302   // null them out to ensure that all of our strlen calls will succeed.
00303   nsresult  result = NS_ERROR_FAILURE;
00304   if (aHGBL != NULL) {
00305     LPSTR lpStr = (LPSTR) GlobalLock(aHGBL);
00306     DWORD allocSize = GlobalSize(aHGBL);
00307     char* data = NS_STATIC_CAST(char*, nsMemory::Alloc(allocSize + sizeof(PRUnichar)));
00308     if ( data ) {    
00309       memcpy ( data, lpStr, allocSize );
00310       data[allocSize] = data[allocSize + 1] = '\0';     // null terminate for safety
00311 
00312       GlobalUnlock(aHGBL);
00313       *aData = data;
00314       *aLen = allocSize;
00315 
00316       result = NS_OK;
00317     }
00318   } 
00319   else {
00320     // We really shouldn't ever get here
00321     // but just in case
00322     *aData = nsnull;
00323     *aLen  = 0;
00324     LPVOID lpMsgBuf;
00325 
00326     FormatMessage( 
00327         FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
00328         NULL,
00329         GetLastError(),
00330         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
00331         (LPTSTR) &lpMsgBuf,
00332         0,
00333         NULL 
00334     );
00335 
00336     // Display the string.
00337     MessageBox( NULL, (const char *)lpMsgBuf, "GetLastError", MB_OK|MB_ICONINFORMATION );
00338 
00339     // Free the buffer.
00340     LocalFree( lpMsgBuf );    
00341   }
00342 
00343   return result;
00344 }
00345 
00346 //-------------------------------------------------------------------------
00347 nsresult nsClipboard::GetNativeDataOffClipboard(nsIWidget * aWindow, UINT /*aIndex*/, UINT aFormat, void ** aData, PRUint32 * aLen)
00348 {
00349   HGLOBAL   hglb; 
00350   nsresult  result = NS_ERROR_FAILURE;
00351 
00352   HWND nativeWin = nsnull;//(HWND)aWindow->GetNativeData(NS_NATIVE_WINDOW);
00353   if (::OpenClipboard(nativeWin)) { 
00354     hglb = ::GetClipboardData(aFormat); 
00355     result = GetGlobalData(hglb, aData, aLen);
00356     ::CloseClipboard();
00357   }
00358   return result;
00359 }
00360 
00361 static void DisplayErrCode(HRESULT hres) 
00362 {
00363 #if defined(DEBUG_rods) || defined(DEBUG_pinkerton)
00364   if (hres == E_INVALIDARG) {
00365     printf("E_INVALIDARG\n");
00366   } else
00367   if (hres == E_UNEXPECTED) {
00368     printf("E_UNEXPECTED\n");
00369   } else
00370   if (hres == E_OUTOFMEMORY) {
00371     printf("E_OUTOFMEMORY\n");
00372   } else
00373   if (hres == DV_E_LINDEX ) {
00374     printf("DV_E_LINDEX\n");
00375   } else
00376   if (hres == DV_E_FORMATETC) {
00377     printf("DV_E_FORMATETC\n");
00378   }  else
00379   if (hres == DV_E_TYMED) {
00380     printf("DV_E_TYMED\n");
00381   }  else
00382   if (hres == DV_E_DVASPECT) {
00383     printf("DV_E_DVASPECT\n");
00384   }  else
00385   if (hres == OLE_E_NOTRUNNING) {
00386     printf("OLE_E_NOTRUNNING\n");
00387   }  else
00388   if (hres == STG_E_MEDIUMFULL) {
00389     printf("STG_E_MEDIUMFULL\n");
00390   }  else
00391   if (hres == DV_E_CLIPFORMAT) {
00392     printf("DV_E_CLIPFORMAT\n");
00393   }  else
00394   if (hres == S_OK) {
00395     printf("S_OK\n");
00396   } else {
00397     printf("****** DisplayErrCode 0x%X\n", hres);
00398   }
00399 #endif
00400 }
00401 
00402 //-------------------------------------------------------------------------
00403 static HRESULT FillSTGMedium(IDataObject * aDataObject, UINT aFormat, LPFORMATETC pFE, LPSTGMEDIUM pSTM, DWORD aTymed)
00404 {
00405   SET_FORMATETC(*pFE, aFormat, 0, DVASPECT_CONTENT, -1, aTymed);
00406 
00407   // Starting by querying for the data to see if we can get it as from global memory
00408   HRESULT hres = S_FALSE;
00409   hres = aDataObject->QueryGetData(pFE);
00410   DisplayErrCode(hres);
00411   if (S_OK == hres) {
00412     hres = aDataObject->GetData(pFE, pSTM);
00413     DisplayErrCode(hres);
00414   }
00415   return hres;
00416 }
00417 
00418 
00419 //-------------------------------------------------------------------------
00420 nsresult nsClipboard::GetNativeDataOffClipboard(IDataObject * aDataObject, UINT aIndex, UINT aFormat, void ** aData, PRUint32 * aLen)
00421 {
00422   nsresult result = NS_ERROR_FAILURE;
00423   *aData = nsnull;
00424   *aLen = 0;
00425 
00426   if ( !aDataObject )
00427     return result;
00428 
00429   UINT    format = aFormat;
00430   HRESULT hres   = S_FALSE;
00431 
00432   // XXX at the moment we only support global memory transfers
00433   // It is here where we will add support for native images 
00434   // and IStream
00435   FORMATETC fe;
00436   STGMEDIUM stm;
00437   hres = FillSTGMedium(aDataObject, format, &fe, &stm, TYMED_HGLOBAL);
00438 
00439   // Currently this is only handling TYMED_HGLOBAL data
00440   // For Text, Dibs, Files, and generic data (like HTML)
00441   if (S_OK == hres) {
00442     static CLIPFORMAT fileDescriptorFlavorA = ::RegisterClipboardFormat( CFSTR_FILEDESCRIPTORA ); 
00443     static CLIPFORMAT fileDescriptorFlavorW = ::RegisterClipboardFormat( CFSTR_FILEDESCRIPTORW ); 
00444 #ifndef WINCE
00445     static CLIPFORMAT fileFlavor = ::RegisterClipboardFormat( CFSTR_FILECONTENTS ); 
00446 #endif
00447     switch (stm.tymed) {
00448      case TYMED_HGLOBAL: 
00449         {
00450           switch (fe.cfFormat) {
00451             case CF_TEXT:
00452               {
00453                 // Get the data out of the global data handle. The size we return
00454                 // should not include the null because the other platforms don't
00455                 // use nulls, so just return the length we get back from strlen(),
00456                 // since we know CF_TEXT is null terminated. Recall that GetGlobalData() 
00457                 // returns the size of the allocated buffer, not the size of the data 
00458                 // (on 98, these are not the same) so we can't use that.
00459                 PRUint32 allocLen = 0;
00460                 if ( NS_SUCCEEDED(GetGlobalData(stm.hGlobal, aData, &allocLen)) ) {
00461                   *aLen = strlen ( NS_REINTERPRET_CAST(char*, *aData) );
00462                   result = NS_OK;
00463                 }
00464               } break;
00465 
00466             case CF_UNICODETEXT:
00467               {
00468                 // Get the data out of the global data handle. The size we return
00469                 // should not include the null because the other platforms don't
00470                 // use nulls, so just return the length we get back from strlen(),
00471                 // since we know CF_UNICODETEXT is null terminated. Recall that GetGlobalData() 
00472                 // returns the size of the allocated buffer, not the size of the data 
00473                 // (on 98, these are not the same) so we can't use that.
00474                 PRUint32 allocLen = 0;
00475                 if ( NS_SUCCEEDED(GetGlobalData(stm.hGlobal, aData, &allocLen)) ) {
00476                   *aLen = nsCRT::strlen(NS_REINTERPRET_CAST(PRUnichar*, *aData)) * 2;
00477                   result = NS_OK;
00478                 }
00479               } break;
00480 
00481 #ifndef WINCE
00482             case CF_DIB :
00483               {
00484                 PRUint32 allocLen = 0;
00485                 unsigned char * clipboardData;
00486                 nsresult rv = GetGlobalData(stm.hGlobal, (void **) &clipboardData, &allocLen);
00487                 if (NS_SUCCEEDED(rv))
00488                 {
00489                   nsImageFromClipboard converter;
00490                   nsIInputStream * inputStream;
00491                   converter.GetEncodedImageStream (clipboardData,  &inputStream );   // addrefs for us, don't release
00492                   if ( inputStream ) {
00493                     *aData = inputStream;
00494                     *aLen = sizeof(nsIInputStream*);
00495                     result = NS_OK;
00496                   }
00497                 }
00498               } break;
00499 
00500             case CF_HDROP : 
00501               {
00502                 // in the case of a file drop, multiple files are stashed within a
00503                 // single data object. In order to match mozilla's D&D apis, we
00504                 // just pull out the file at the requested index, pretending as
00505                 // if there really are multiple drag items.
00506                 HDROP dropFiles = (HDROP) GlobalLock(stm.hGlobal);
00507 
00508                 UINT numFiles = nsToolkit::mDragQueryFile(dropFiles, 0xFFFFFFFF, NULL, 0);
00509                 NS_ASSERTION ( numFiles > 0, "File drop flavor, but no files...hmmmm" );
00510                 NS_ASSERTION ( aIndex < numFiles, "Asked for a file index out of range of list" );
00511                 if (numFiles > 0) {
00512                   UINT fileNameLen = nsToolkit::mDragQueryFile(dropFiles, aIndex, nsnull, 0);
00513                   PRUnichar* buffer = NS_REINTERPRET_CAST(PRUnichar*, nsMemory::Alloc((fileNameLen + 1) * sizeof(PRUnichar)));
00514                   if ( buffer ) {
00515                     nsToolkit::mDragQueryFile(dropFiles, aIndex, buffer, fileNameLen + 1);
00516                     *aData = buffer;
00517                     *aLen = fileNameLen * sizeof(PRUnichar);
00518                     result = NS_OK;
00519                   }
00520                   else
00521                     result = NS_ERROR_OUT_OF_MEMORY;
00522                 }
00523                 GlobalUnlock (stm.hGlobal) ;
00524 
00525               } break;
00526 
00527 #endif
00528             default: {
00529 #ifndef WINCE
00530               if ( fe.cfFormat == fileDescriptorFlavorA || fe.cfFormat == fileDescriptorFlavorW || fe.cfFormat == fileFlavor ) {
00531                 NS_WARNING ( "Mozilla doesn't yet understand how to read this type of file flavor" );
00532               } 
00533               else
00534 #endif
00535               {
00536                 // Get the data out of the global data handle. The size we return
00537                 // should not include the null because the other platforms don't
00538                 // use nulls, so just return the length we get back from strlen(),
00539                 // since we know CF_UNICODETEXT is null terminated. Recall that GetGlobalData() 
00540                 // returns the size of the allocated buffer, not the size of the data 
00541                 // (on 98, these are not the same) so we can't use that.
00542                 //
00543                 // NOTE: we are assuming that anything that falls into this default case
00544                 //        is unicode. As we start to get more kinds of binary data, this
00545                 //        may become an incorrect assumption. Stay tuned.
00546                 PRUint32 allocLen = 0;
00547                 if ( NS_SUCCEEDED(GetGlobalData(stm.hGlobal, aData, &allocLen)) ) {
00548                   if ( fe.cfFormat == CF_HTML ) {
00549                     // CF_HTML is actually UTF8, not unicode, so disregard the assumption
00550                     // above. We have to check the header for the actual length, and we'll
00551                     // do that in FindPlatformHTML(). For now, return the allocLen. This
00552                     // case is mostly to ensure we don't try to call strlen on the buffer.
00553                     *aLen = allocLen;
00554                   }
00555                   else
00556                     *aLen = nsCRT::strlen(NS_REINTERPRET_CAST(PRUnichar*, *aData)) * sizeof(PRUnichar);
00557                   result = NS_OK;
00558                 }
00559               }
00560             } break;
00561           } // switch
00562         } break;
00563 
00564       case TYMED_GDI: 
00565         {
00566 #ifdef DEBUG
00567           printf("*********************** TYMED_GDI\n");
00568 #endif
00569         } break;
00570 
00571       default:
00572         break;
00573     } //switch
00574     
00575     ReleaseStgMedium(&stm);
00576   }
00577 
00578   return result;
00579 }
00580 
00581 
00582 //-------------------------------------------------------------------------
00583 nsresult nsClipboard::GetDataFromDataObject(IDataObject     * aDataObject,
00584                                             UINT              anIndex,
00585                                             nsIWidget       * aWindow,
00586                                             nsITransferable * aTransferable)
00587 {
00588   // make sure we have a good transferable
00589   if ( !aTransferable )
00590     return NS_ERROR_INVALID_ARG;
00591 
00592   nsresult res = NS_ERROR_FAILURE;
00593 
00594   // get flavor list that includes all flavors that can be written (including ones 
00595   // obtained through conversion)
00596   nsCOMPtr<nsISupportsArray> flavorList;
00597   res = aTransferable->FlavorsTransferableCanImport ( getter_AddRefs(flavorList) );
00598   if ( NS_FAILED(res) )
00599     return NS_ERROR_FAILURE;
00600 
00601   // Walk through flavors and see which flavor is on the clipboard them on the native clipboard,
00602   PRUint32 i;
00603   PRUint32 cnt;
00604   flavorList->Count(&cnt);
00605   for (i=0;i<cnt;i++) {
00606     nsCOMPtr<nsISupports> genericFlavor;
00607     flavorList->GetElementAt ( i, getter_AddRefs(genericFlavor) );
00608     nsCOMPtr<nsISupportsCString> currentFlavor ( do_QueryInterface(genericFlavor) );
00609     if ( currentFlavor ) {
00610       nsXPIDLCString flavorStr;
00611       currentFlavor->ToString(getter_Copies(flavorStr));
00612       UINT format = GetFormat(flavorStr);
00613 
00614       // Try to get the data using the desired flavor. This might fail, but all is
00615       // not lost.
00616       void* data = nsnull;
00617       PRUint32 dataLen = 0;
00618       PRBool dataFound = PR_FALSE;
00619       if (nsnull != aDataObject) {
00620         if ( NS_SUCCEEDED(GetNativeDataOffClipboard(aDataObject, anIndex, format, &data, &dataLen)) )
00621           dataFound = PR_TRUE;
00622       } 
00623       else if (nsnull != aWindow) {
00624         if ( NS_SUCCEEDED(GetNativeDataOffClipboard(aWindow, anIndex, format, &data, &dataLen)) )
00625           dataFound = PR_TRUE;
00626       }
00627 
00628       // This is our second chance to try to find some data, having not found it
00629       // when directly asking for the flavor. Let's try digging around in other
00630       // flavors to help satisfy our craving for data.
00631       if ( !dataFound ) {
00632         if ( strcmp(flavorStr, kUnicodeMime) == 0 )
00633           dataFound = FindUnicodeFromPlainText ( aDataObject, anIndex, &data, &dataLen );
00634         else if ( strcmp(flavorStr, kURLMime) == 0 )
00635           dataFound = FindURLFromLocalFile ( aDataObject, anIndex, &data, &dataLen );
00636       } // if we try one last ditch effort to find our data
00637 
00638       // Hopefully by this point we've found it and can go about our business
00639       if ( dataFound ) {
00640         nsCOMPtr<nsISupports> genericDataWrapper;
00641         if ( strcmp(flavorStr, kFileMime) == 0 ) {
00642           // we have a file path in |data|. Create an nsLocalFile object.
00643           nsDependentString filepath(NS_REINTERPRET_CAST(PRUnichar*, data));
00644           nsCOMPtr<nsILocalFile> file;
00645           if ( NS_SUCCEEDED(NS_NewLocalFile(filepath, PR_FALSE, getter_AddRefs(file))) )
00646             genericDataWrapper = do_QueryInterface(file);
00647           nsMemory::Free(data);
00648         }
00649         else if ( strcmp(flavorStr, kNativeHTMLMime) == 0) {
00650           // the editor folks want CF_HTML exactly as it's on the clipboard, no conversions,
00651           // no fancy stuff. Pull it off the clipboard, stuff it into a wrapper and hand
00652           // it back to them.
00653           if ( FindPlatformHTML(aDataObject, anIndex, &data, &dataLen) )
00654             nsPrimitiveHelpers::CreatePrimitiveForData ( flavorStr, data, dataLen, getter_AddRefs(genericDataWrapper) );
00655           else
00656           {
00657             nsMemory::Free(data);
00658             continue;     // something wrong with this flavor, keep looking for other data
00659           }
00660           nsMemory::Free(data);
00661         }
00662         else if ( strcmp(flavorStr, kJPEGImageMime) == 0) {
00663           nsIInputStream * imageStream = NS_REINTERPRET_CAST(nsIInputStream*, data);
00664           genericDataWrapper = do_QueryInterface(imageStream);
00665           NS_IF_RELEASE(imageStream);
00666         }
00667         else {
00668           // we probably have some form of text. The DOM only wants LF, so convert from Win32 line 
00669           // endings to DOM line endings.
00670           PRInt32 signedLen = NS_STATIC_CAST(PRInt32, dataLen);
00671           nsLinebreakHelpers::ConvertPlatformToDOMLinebreaks ( flavorStr, &data, &signedLen );
00672           dataLen = signedLen;
00673 
00674           nsPrimitiveHelpers::CreatePrimitiveForData ( flavorStr, data, dataLen, getter_AddRefs(genericDataWrapper) );
00675           nsMemory::Free(data);
00676         }
00677         
00678         NS_ASSERTION ( genericDataWrapper, "About to put null data into the transferable" );
00679         aTransferable->SetTransferData(flavorStr, genericDataWrapper, dataLen);
00680         res = NS_OK;
00681 
00682         // we found one, get out of the loop
00683         break;
00684       }
00685 
00686     }
00687   } // foreach flavor
00688 
00689   return res;
00690 
00691 }
00692 
00693 
00694 
00695 //
00696 // FindPlatformHTML
00697 //
00698 // Someone asked for the OS CF_HTML flavor. We give it back to them exactly as-is.
00699 //
00700 PRBool
00701 nsClipboard :: FindPlatformHTML ( IDataObject* inDataObject, UINT inIndex, void** outData, PRUint32* outDataLen )
00702 {
00703   PRBool dataFound = PR_FALSE;
00704 
00705   if ( outData && *outData ) {
00706     // CF_HTML is UTF8, not unicode. We also can't rely on it being null-terminated
00707     // so we have to check the CF_HTML header for the correct length. The length we return
00708     // is the bytecount from the beginning of the data to the end, without
00709     // the null termination. Because it's UTF8, we're guaranteed the header is ascii (yay!).
00710     float vers = 0.0;
00711     PRUint32 startOfData = 0;
00712     sscanf((char*)*outData, "Version:%f\nStartHTML:%d\nEndHTML:%d", &vers, &startOfData, outDataLen);
00713     NS_ASSERTION(startOfData && *outDataLen, "Couldn't parse CF_HTML description header");
00714  
00715     if ( *outDataLen )
00716       dataFound = PR_TRUE;
00717   }
00718 
00719   return dataFound;
00720 }
00721 
00722 
00723 //
00724 // FindURLFromLocalFile
00725 //
00726 // we are looking for text/unicode and we failed to find it on the clipboard first,
00727 // try again with text/plain. If that is present, convert it to unicode.
00728 //
00729 PRBool
00730 nsClipboard :: FindUnicodeFromPlainText ( IDataObject* inDataObject, UINT inIndex, void** outData, PRUint32* outDataLen )
00731 {
00732   PRBool dataFound = PR_FALSE;
00733 
00734   // we are looking for text/unicode and we failed to find it on the clipboard first,
00735   // try again with text/plain. If that is present, convert it to unicode.
00736   nsresult loadResult = GetNativeDataOffClipboard(inDataObject, inIndex, GetFormat(kTextMime), outData, outDataLen);
00737   if ( NS_SUCCEEDED(loadResult) && *outData ) {
00738     const char* castedText = NS_REINTERPRET_CAST(char*, *outData);          
00739     PRUnichar* convertedText = nsnull;
00740     PRInt32 convertedTextLen = 0;
00741     nsPrimitiveHelpers::ConvertPlatformPlainTextToUnicode ( castedText, *outDataLen, 
00742                                                               &convertedText, &convertedTextLen );
00743     if ( convertedText ) {
00744       // out with the old, in with the new 
00745       nsMemory::Free(*outData);
00746       *outData = convertedText;
00747       *outDataLen = convertedTextLen * sizeof(PRUnichar);
00748       dataFound = PR_TRUE;
00749     }
00750   } // if plain text data on clipboard
00751 
00752   return dataFound;
00753 
00754 } // FindUnicodeFromPlainText
00755 
00756 
00757 //
00758 // FindURLFromLocalFile
00759 //
00760 // we are looking for a URL and couldn't find it, try again with looking for 
00761 // a local file. If we have one, it may either be a normal file or an internet shortcut.
00762 // In both cases, however, we can get a URL (it will be a file:// url in the
00763 // local file case).
00764 //
00765 PRBool
00766 nsClipboard :: FindURLFromLocalFile ( IDataObject* inDataObject, UINT inIndex, void** outData, PRUint32* outDataLen )
00767 {
00768   PRBool dataFound = PR_FALSE;
00769 
00770   nsresult loadResult = GetNativeDataOffClipboard(inDataObject, inIndex, GetFormat(kFileMime), outData, outDataLen);
00771   if ( NS_SUCCEEDED(loadResult) && *outData ) {
00772     // we have a file path in |data|. Is it an internet shortcut or a normal file?
00773     const nsDependentString filepath(NS_STATIC_CAST(PRUnichar*, *outData));
00774     nsCOMPtr<nsILocalFile> file;
00775     nsresult rv = NS_NewLocalFile(filepath, PR_TRUE, getter_AddRefs(file));
00776     if (NS_FAILED(rv))
00777       return dataFound;
00778 
00779     if ( IsInternetShortcut(filepath) ) {
00780       nsCAutoString url;
00781       ResolveShortcut( file, url );
00782       if ( !url.IsEmpty() ) {
00783         // convert it to unicode and pass it out
00784         nsMemory::Free(*outData);
00785         *outData = UTF8ToNewUnicode(url);
00786         *outDataLen = nsCRT::strlen(NS_STATIC_CAST(PRUnichar*, *outData)) * sizeof(PRUnichar);
00787 
00788         dataFound = PR_TRUE;
00789       }
00790     }
00791     else {
00792       // we have a normal file, use some Necko objects to get our file path
00793       nsCAutoString urlSpec;
00794       NS_GetURLSpecFromFile(file, urlSpec);
00795 
00796       // convert it to unicode and pass it out
00797       nsMemory::Free(*outData);
00798       *outData = UTF8ToNewUnicode(urlSpec);
00799       *outDataLen = nsCRT::strlen(NS_STATIC_CAST(PRUnichar*, *outData)) * sizeof(PRUnichar);
00800       dataFound = PR_TRUE;
00801     } // else regular file
00802   }
00803 
00804   return dataFound;
00805 } // FindURLFromLocalFile
00806 
00807 
00808 //
00809 // ResolveShortcut
00810 //
00811 void
00812 nsClipboard :: ResolveShortcut ( nsILocalFile* aFile, nsACString& outURL )
00813 {
00814   nsCOMPtr<nsIFileProtocolHandler> fph;
00815   nsresult rv = NS_GetFileProtocolHandler(getter_AddRefs(fph));
00816   if (NS_FAILED(rv))
00817     return;
00818 
00819   nsCOMPtr<nsIURI> uri;
00820   rv = fph->ReadURLFile(aFile, getter_AddRefs(uri));
00821   if (NS_FAILED(rv))
00822     return;
00823 
00824   uri->GetSpec(outURL);
00825 } // ResolveShortcut
00826 
00827 
00828 //
00829 // IsInternetShortcut
00830 //
00831 // A file is an Internet Shortcut if it ends with .URL
00832 //
00833 PRBool
00834 nsClipboard :: IsInternetShortcut ( const nsAString& inFileName ) 
00835 {
00836   return StringEndsWith(inFileName, NS_LITERAL_STRING(".url"), nsCaseInsensitiveStringComparator());
00837 } // IsInternetShortcut
00838 
00839 
00840 //-------------------------------------------------------------------------
00841 NS_IMETHODIMP 
00842 nsClipboard::GetNativeClipboardData ( nsITransferable * aTransferable, PRInt32 aWhichClipboard )
00843 {
00844   // make sure we have a good transferable
00845   if ( !aTransferable || aWhichClipboard != kGlobalClipboard )
00846     return NS_ERROR_FAILURE;
00847 
00848   nsresult res;
00849 
00850   // This makes sure we can use the OLE functionality for the clipboard
00851   IDataObject * dataObj;
00852   if (S_OK == ::OleGetClipboard(&dataObj)) {
00853     // Use OLE IDataObject for clipboard operations
00854     res = GetDataFromDataObject(dataObj, 0, nsnull, aTransferable);
00855     dataObj->Release();
00856   } 
00857   else {
00858     // do it the old manual way
00859     res = GetDataFromDataObject(nsnull, 0, mWindow, aTransferable);
00860   }
00861   return res;
00862 
00863 }
00864 
00865 
00866 //-------------------------------------------------------------------------
00867 NS_IMETHODIMP
00868 nsClipboard::Observe(nsISupports *aSubject, const char *aTopic,
00869                      const PRUnichar *aData)
00870 {
00871   // This will be called on shutdown.
00872   ::OleFlushClipboard();
00873   ::CloseClipboard();
00874 
00875   return NS_OK;
00876 }
00877 
00878 //-------------------------------------------------------------------------
00879 NS_IMETHODIMP nsClipboard::HasDataMatchingFlavors(nsISupportsArray *aFlavorList, PRInt32 aWhichClipboard,
00880                                                   PRBool           *_retval)
00881 {
00882   *_retval = PR_FALSE;
00883   if ( aWhichClipboard != kGlobalClipboard )
00884     return NS_OK;
00885 
00886   PRUint32 cnt;
00887   aFlavorList->Count(&cnt);
00888   for ( PRUint32 i = 0;i < cnt; ++i ) {
00889     nsCOMPtr<nsISupports> genericFlavor;
00890     aFlavorList->GetElementAt (i, getter_AddRefs(genericFlavor));
00891     nsCOMPtr<nsISupportsCString> currentFlavor (do_QueryInterface(genericFlavor));
00892     if (currentFlavor) {
00893       nsXPIDLCString flavorStr;
00894       currentFlavor->ToString(getter_Copies(flavorStr));
00895 
00896 #ifdef NS_DEBUG
00897       if ( strcmp(flavorStr, kTextMime) == 0 )
00898         NS_WARNING ( "DO NOT USE THE text/plain DATA FLAVOR ANY MORE. USE text/unicode INSTEAD" );
00899 #endif
00900 
00901       UINT format = GetFormat(flavorStr);
00902       if (IsClipboardFormatAvailable(format)) {
00903         *_retval = PR_TRUE;
00904         break;
00905       }
00906       else {
00907         // We haven't found the exact flavor the client asked for, but maybe we can
00908         // still find it from something else that's on the clipboard...
00909         if ( strcmp(flavorStr, kUnicodeMime) == 0 ) {
00910           // client asked for unicode and it wasn't present, check if we have CF_TEXT.
00911           // We'll handle the actual data substitution in the data object.
00912           if (IsClipboardFormatAvailable(GetFormat(kTextMime)) )
00913             *_retval = PR_TRUE;
00914         }
00915       }
00916     }
00917   }
00918 
00919   return NS_OK;
00920 }