widget/windows/nsClipboard.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "nsClipboard.h"
     7 #include <ole2.h>
     8 #include <shlobj.h>
     9 #include <intshcut.h>
    11 // shellapi.h is needed to build with WIN32_LEAN_AND_MEAN
    12 #include <shellapi.h>
    14 #include "nsCOMPtr.h"
    15 #include "nsDataObj.h"
    16 #include "nsIClipboardOwner.h"
    17 #include "nsString.h"
    18 #include "nsNativeCharsetUtils.h"
    19 #include "nsIFormatConverter.h"
    20 #include "nsITransferable.h"
    21 #include "nsCOMPtr.h"
    22 #include "nsXPCOM.h"
    23 #include "nsISupportsPrimitives.h"
    24 #include "nsXPIDLString.h"
    25 #include "nsReadableUtils.h"
    26 #include "nsUnicharUtils.h"
    27 #include "nsPrimitiveHelpers.h"
    28 #include "nsImageClipboard.h"
    29 #include "nsIWidget.h"
    30 #include "nsIComponentManager.h"
    31 #include "nsWidgetsCID.h"
    32 #include "nsCRT.h"
    33 #include "nsNetUtil.h"
    34 #include "nsEscape.h"
    35 #include "nsIObserverService.h"
    37 #ifdef PR_LOGGING
    38 PRLogModuleInfo* gWin32ClipboardLog = nullptr;
    39 #endif
    41 // oddly, this isn't in the MSVC headers anywhere.
    42 UINT nsClipboard::CF_HTML = ::RegisterClipboardFormatW(L"HTML Format");
    45 //-------------------------------------------------------------------------
    46 //
    47 // nsClipboard constructor
    48 //
    49 //-------------------------------------------------------------------------
    50 nsClipboard::nsClipboard() : nsBaseClipboard()
    51 {
    52 #ifdef PR_LOGGING
    53   if (!gWin32ClipboardLog) {
    54     gWin32ClipboardLog = PR_NewLogModule("nsClipboard");
    55   }
    56 #endif
    58   mIgnoreEmptyNotification = false;
    59   mWindow         = nullptr;
    61   // Register for a shutdown notification so that we can flush data
    62  // to the OS clipboard.
    63   nsCOMPtr<nsIObserverService> observerService =
    64     do_GetService("@mozilla.org/observer-service;1");
    65   if (observerService)
    66     observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_FALSE);
    67 }
    69 //-------------------------------------------------------------------------
    70 // nsClipboard destructor
    71 //-------------------------------------------------------------------------
    72 nsClipboard::~nsClipboard()
    73 {
    75 }
    77 NS_IMPL_ISUPPORTS_INHERITED(nsClipboard, nsBaseClipboard, nsIObserver)
    79 NS_IMETHODIMP
    80 nsClipboard::Observe(nsISupports *aSubject, const char *aTopic,
    81                      const char16_t *aData)
    82 {
    83   // This will be called on shutdown.
    84   ::OleFlushClipboard();
    85   ::CloseClipboard();
    87   return NS_OK;
    88 }
    90 //-------------------------------------------------------------------------
    91 UINT nsClipboard::GetFormat(const char* aMimeStr)
    92 {
    93   UINT format;
    95   if (strcmp(aMimeStr, kTextMime) == 0)
    96     format = CF_TEXT;
    97   else if (strcmp(aMimeStr, kUnicodeMime) == 0)
    98     format = CF_UNICODETEXT;
    99   else if (strcmp(aMimeStr, kJPEGImageMime) == 0 ||
   100            strcmp(aMimeStr, kJPGImageMime) == 0 ||
   101            strcmp(aMimeStr, kPNGImageMime) == 0)
   102     format = CF_DIBV5;
   103   else if (strcmp(aMimeStr, kFileMime) == 0 ||
   104            strcmp(aMimeStr, kFilePromiseMime) == 0)
   105     format = CF_HDROP;
   106   else if (strcmp(aMimeStr, kNativeHTMLMime) == 0)
   107     format = CF_HTML;
   108   else
   109     format = ::RegisterClipboardFormatW(NS_ConvertASCIItoUTF16(aMimeStr).get());
   111   return format;
   112 }
   114 //-------------------------------------------------------------------------
   115 nsresult nsClipboard::CreateNativeDataObject(nsITransferable * aTransferable, IDataObject ** aDataObj, nsIURI * uri)
   116 {
   117   if (nullptr == aTransferable) {
   118     return NS_ERROR_FAILURE;
   119   }
   121   // Create our native DataObject that implements 
   122   // the OLE IDataObject interface
   123   nsDataObj * dataObj = new nsDataObj(uri);
   125   if (!dataObj) 
   126     return NS_ERROR_OUT_OF_MEMORY;
   128   dataObj->AddRef();
   130   // Now set it up with all the right data flavors & enums
   131   nsresult res = SetupNativeDataObject(aTransferable, dataObj);
   132   if (NS_OK == res) {
   133     *aDataObj = dataObj; 
   134   } else {
   135     delete dataObj;
   136   }
   137   return res;
   138 }
   140 //-------------------------------------------------------------------------
   141 nsresult nsClipboard::SetupNativeDataObject(nsITransferable * aTransferable, IDataObject * aDataObj)
   142 {
   143   if (nullptr == aTransferable || nullptr == aDataObj) {
   144     return NS_ERROR_FAILURE;
   145   }
   147   nsDataObj * dObj = static_cast<nsDataObj *>(aDataObj);
   149   // Now give the Transferable to the DataObject 
   150   // for getting the data out of it
   151   dObj->SetTransferable(aTransferable);
   153   // Get the transferable list of data flavors
   154   nsCOMPtr<nsISupportsArray> dfList;
   155   aTransferable->FlavorsTransferableCanExport(getter_AddRefs(dfList));
   157   // Walk through flavors that contain data and register them
   158   // into the DataObj as supported flavors
   159   uint32_t i;
   160   uint32_t cnt;
   161   dfList->Count(&cnt);
   162   for (i=0;i<cnt;i++) {
   163     nsCOMPtr<nsISupports> genericFlavor;
   164     dfList->GetElementAt ( i, getter_AddRefs(genericFlavor) );
   165     nsCOMPtr<nsISupportsCString> currentFlavor ( do_QueryInterface(genericFlavor) );
   166     if ( currentFlavor ) {
   167       nsXPIDLCString flavorStr;
   168       currentFlavor->ToString(getter_Copies(flavorStr));
   169       UINT format = GetFormat(flavorStr);
   171       // Now tell the native IDataObject about both our mime type and 
   172       // the native data format
   173       FORMATETC fe;
   174       SET_FORMATETC(fe, format, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL);
   175       dObj->AddDataFlavor(flavorStr, &fe);
   177       // Do various things internal to the implementation, like map one
   178       // flavor to another or add additional flavors based on what's required
   179       // for the win32 impl.
   180       if ( strcmp(flavorStr, kUnicodeMime) == 0 ) {
   181         // if we find text/unicode, also advertise text/plain (which we will convert
   182         // on our own in nsDataObj::GetText().
   183         FORMATETC textFE;
   184         SET_FORMATETC(textFE, CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL);
   185         dObj->AddDataFlavor(kTextMime, &textFE);
   186       }
   187       else if ( strcmp(flavorStr, kHTMLMime) == 0 ) {      
   188         // if we find text/html, also advertise win32's html flavor (which we will convert
   189         // on our own in nsDataObj::GetText().
   190         FORMATETC htmlFE;
   191         SET_FORMATETC(htmlFE, CF_HTML, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL);
   192         dObj->AddDataFlavor(kHTMLMime, &htmlFE);     
   193       }
   194       else if ( strcmp(flavorStr, kURLMime) == 0 ) {
   195         // if we're a url, in addition to also being text, we need to register
   196         // the "file" flavors so that the win32 shell knows to create an internet
   197         // shortcut when it sees one of these beasts.
   198         FORMATETC shortcutFE;
   199         SET_FORMATETC(shortcutFE, ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORA), 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL)
   200         dObj->AddDataFlavor(kURLMime, &shortcutFE);      
   201         SET_FORMATETC(shortcutFE, ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW), 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL)
   202         dObj->AddDataFlavor(kURLMime, &shortcutFE);      
   203         SET_FORMATETC(shortcutFE, ::RegisterClipboardFormat(CFSTR_FILECONTENTS), 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL)
   204         dObj->AddDataFlavor(kURLMime, &shortcutFE);  
   205         SET_FORMATETC(shortcutFE, ::RegisterClipboardFormat(CFSTR_INETURLA), 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL)
   206         dObj->AddDataFlavor(kURLMime, &shortcutFE);      
   207         SET_FORMATETC(shortcutFE, ::RegisterClipboardFormat(CFSTR_INETURLW), 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL)
   208         dObj->AddDataFlavor(kURLMime, &shortcutFE);      
   209       }
   210       else if ( strcmp(flavorStr, kPNGImageMime) == 0 || strcmp(flavorStr, kJPEGImageMime) == 0 ||
   211                 strcmp(flavorStr, kJPGImageMime) == 0 || strcmp(flavorStr, kGIFImageMime) == 0 ||
   212                 strcmp(flavorStr, kNativeImageMime) == 0  ) {
   213         // if we're an image, register the native bitmap flavor
   214         FORMATETC imageFE;
   215         // Add DIBv5
   216         SET_FORMATETC(imageFE, CF_DIBV5, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL)
   217         dObj->AddDataFlavor(flavorStr, &imageFE);
   218         // Add DIBv3 
   219         SET_FORMATETC(imageFE, CF_DIB, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL)
   220         dObj->AddDataFlavor(flavorStr, &imageFE);
   221       }
   222       else if ( strcmp(flavorStr, kFilePromiseMime) == 0 ) {
   223          // if we're a file promise flavor, also register the 
   224          // CFSTR_PREFERREDDROPEFFECT format.  The data object
   225          // returns a value of DROPEFFECTS_MOVE to the drop target
   226          // when it asks for the value of this format.  This causes
   227          // the file to be moved from the temporary location instead
   228          // of being copied.  The right thing to do here is to call
   229          // SetData() on the data object and set the value of this format
   230          // to DROPEFFECTS_MOVE on this particular data object.  But,
   231          // since all the other clipboard formats follow the model of setting
   232          // data on the data object only when the drop object calls GetData(),
   233          // I am leaving this format's value hard coded in the data object.
   234          // We can change this if other consumers of this format get added to this
   235          // codebase and they need different values.
   236         FORMATETC shortcutFE;
   237         SET_FORMATETC(shortcutFE, ::RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT), 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL)
   238         dObj->AddDataFlavor(kFilePromiseMime, &shortcutFE);
   239       }
   240     }
   241   }
   243   return NS_OK;
   244 }
   246 //-------------------------------------------------------------------------
   247 NS_IMETHODIMP nsClipboard::SetNativeClipboardData ( int32_t aWhichClipboard )
   248 {
   249   if ( aWhichClipboard != kGlobalClipboard )
   250     return NS_ERROR_FAILURE;
   252   mIgnoreEmptyNotification = true;
   254   // make sure we have a good transferable
   255   if (nullptr == mTransferable) {
   256     return NS_ERROR_FAILURE;
   257   }
   259   IDataObject * dataObj;
   260   if ( NS_SUCCEEDED(CreateNativeDataObject(mTransferable, &dataObj, nullptr)) ) { // this add refs dataObj
   261     ::OleSetClipboard(dataObj);
   262     dataObj->Release();
   263   } else {
   264     // Clear the native clipboard
   265     ::OleSetClipboard(nullptr);
   266   }
   268   mIgnoreEmptyNotification = false;
   270   return NS_OK;
   271 }
   274 //-------------------------------------------------------------------------
   275 nsresult nsClipboard::GetGlobalData(HGLOBAL aHGBL, void ** aData, uint32_t * aLen)
   276 {
   277   // Allocate a new memory buffer and copy the data from global memory.
   278   // Recall that win98 allocates to nearest DWORD boundary. As a safety
   279   // precaution, allocate an extra 2 bytes (but don't report them!) and
   280   // null them out to ensure that all of our strlen calls will succeed.
   281   nsresult  result = NS_ERROR_FAILURE;
   282   if (aHGBL != nullptr) {
   283     LPSTR lpStr = (LPSTR) GlobalLock(aHGBL);
   284     DWORD allocSize = GlobalSize(aHGBL);
   285     char* data = static_cast<char*>(nsMemory::Alloc(allocSize + sizeof(char16_t)));
   286     if ( data ) {    
   287       memcpy ( data, lpStr, allocSize );
   288       data[allocSize] = data[allocSize + 1] = '\0';     // null terminate for safety
   290       GlobalUnlock(aHGBL);
   291       *aData = data;
   292       *aLen = allocSize;
   294       result = NS_OK;
   295     }
   296   } else {
   297 #ifdef MOZ_METRO
   298     return result;
   299 #endif
   300     // We really shouldn't ever get here
   301     // but just in case
   302     *aData = nullptr;
   303     *aLen  = 0;
   304     LPVOID lpMsgBuf;
   306     FormatMessageW( 
   307         FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
   308         nullptr,
   309         GetLastError(),
   310         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
   311         (LPWSTR) &lpMsgBuf,
   312         0,
   313         nullptr 
   314     );
   316     // Display the string.
   317     MessageBoxW( nullptr, (LPCWSTR) lpMsgBuf, L"GetLastError",
   318                  MB_OK | MB_ICONINFORMATION );
   320     // Free the buffer.
   321     LocalFree( lpMsgBuf );    
   322   }
   324   return result;
   325 }
   327 //-------------------------------------------------------------------------
   328 nsresult nsClipboard::GetNativeDataOffClipboard(nsIWidget * aWidget, UINT /*aIndex*/, UINT aFormat, void ** aData, uint32_t * aLen)
   329 {
   330   HGLOBAL   hglb; 
   331   nsresult  result = NS_ERROR_FAILURE;
   333   HWND nativeWin = nullptr;
   334   if (::OpenClipboard(nativeWin)) { 
   335     hglb = ::GetClipboardData(aFormat); 
   336     result = GetGlobalData(hglb, aData, aLen);
   337     ::CloseClipboard();
   338   }
   339   return result;
   340 }
   342 static void DisplayErrCode(HRESULT hres) 
   343 {
   344 #if defined(DEBUG_rods) || defined(DEBUG_pinkerton)
   345   if (hres == E_INVALIDARG) {
   346     PR_LOG(gWin32ClipboardLog, PR_LOG_ALWAYS, ("E_INVALIDARG\n"));
   347   } else
   348   if (hres == E_UNEXPECTED) {
   349     PR_LOG(gWin32ClipboardLog, PR_LOG_ALWAYS, ("E_UNEXPECTED\n"));
   350   } else
   351   if (hres == E_OUTOFMEMORY) {
   352     PR_LOG(gWin32ClipboardLog, PR_LOG_ALWAYS, ("E_OUTOFMEMORY\n"));
   353   } else
   354   if (hres == DV_E_LINDEX ) {
   355     PR_LOG(gWin32ClipboardLog, PR_LOG_ALWAYS, ("DV_E_LINDEX\n"));
   356   } else
   357   if (hres == DV_E_FORMATETC) {
   358     PR_LOG(gWin32ClipboardLog, PR_LOG_ALWAYS, ("DV_E_FORMATETC\n"));
   359   }  else
   360   if (hres == DV_E_TYMED) {
   361     PR_LOG(gWin32ClipboardLog, PR_LOG_ALWAYS, ("DV_E_TYMED\n"));
   362   }  else
   363   if (hres == DV_E_DVASPECT) {
   364     PR_LOG(gWin32ClipboardLog, PR_LOG_ALWAYS, ("DV_E_DVASPECT\n"));
   365   }  else
   366   if (hres == OLE_E_NOTRUNNING) {
   367     PR_LOG(gWin32ClipboardLog, PR_LOG_ALWAYS, ("OLE_E_NOTRUNNING\n"));
   368   }  else
   369   if (hres == STG_E_MEDIUMFULL) {
   370     PR_LOG(gWin32ClipboardLog, PR_LOG_ALWAYS, ("STG_E_MEDIUMFULL\n"));
   371   }  else
   372   if (hres == DV_E_CLIPFORMAT) {
   373     PR_LOG(gWin32ClipboardLog, PR_LOG_ALWAYS, ("DV_E_CLIPFORMAT\n"));
   374   }  else
   375   if (hres == S_OK) {
   376     PR_LOG(gWin32ClipboardLog, PR_LOG_ALWAYS, ("S_OK\n"));
   377   } else {
   378     PR_LOG(gWin32ClipboardLog, PR_LOG_ALWAYS, 
   379            ("****** DisplayErrCode 0x%X\n", hres));
   380   }
   381 #endif
   382 }
   384 //-------------------------------------------------------------------------
   385 static HRESULT FillSTGMedium(IDataObject * aDataObject, UINT aFormat, LPFORMATETC pFE, LPSTGMEDIUM pSTM, DWORD aTymed)
   386 {
   387   SET_FORMATETC(*pFE, aFormat, 0, DVASPECT_CONTENT, -1, aTymed);
   389   // Starting by querying for the data to see if we can get it as from global memory
   390   HRESULT hres = S_FALSE;
   391   hres = aDataObject->QueryGetData(pFE);
   392   DisplayErrCode(hres);
   393   if (S_OK == hres) {
   394     hres = aDataObject->GetData(pFE, pSTM);
   395     DisplayErrCode(hres);
   396   }
   397   return hres;
   398 }
   401 //-------------------------------------------------------------------------
   402 // If aFormat is CF_DIBV5, aMIMEImageFormat must be a type for which we have
   403 // an image encoder (e.g. image/png).
   404 // For other values of aFormat, it is OK to pass null for aMIMEImageFormat.
   405 nsresult nsClipboard::GetNativeDataOffClipboard(IDataObject * aDataObject, UINT aIndex, UINT aFormat, const char * aMIMEImageFormat, void ** aData, uint32_t * aLen)
   406 {
   407   nsresult result = NS_ERROR_FAILURE;
   408   *aData = nullptr;
   409   *aLen = 0;
   411   if ( !aDataObject )
   412     return result;
   414   UINT    format = aFormat;
   415   HRESULT hres   = S_FALSE;
   417   // XXX at the moment we only support global memory transfers
   418   // It is here where we will add support for native images 
   419   // and IStream
   420   FORMATETC fe;
   421   STGMEDIUM stm;
   422   hres = FillSTGMedium(aDataObject, format, &fe, &stm, TYMED_HGLOBAL);
   424   // Currently this is only handling TYMED_HGLOBAL data
   425   // For Text, Dibs, Files, and generic data (like HTML)
   426   if (S_OK == hres) {
   427     static CLIPFORMAT fileDescriptorFlavorA = ::RegisterClipboardFormat( CFSTR_FILEDESCRIPTORA ); 
   428     static CLIPFORMAT fileDescriptorFlavorW = ::RegisterClipboardFormat( CFSTR_FILEDESCRIPTORW ); 
   429     static CLIPFORMAT fileFlavor = ::RegisterClipboardFormat( CFSTR_FILECONTENTS ); 
   430     static CLIPFORMAT preferredDropEffect = ::RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT);
   432     switch (stm.tymed) {
   433      case TYMED_HGLOBAL: 
   434         {
   435           switch (fe.cfFormat) {
   436             case CF_TEXT:
   437               {
   438                 // Get the data out of the global data handle. The size we return
   439                 // should not include the null because the other platforms don't
   440                 // use nulls, so just return the length we get back from strlen(),
   441                 // since we know CF_TEXT is null terminated. Recall that GetGlobalData() 
   442                 // returns the size of the allocated buffer, not the size of the data 
   443                 // (on 98, these are not the same) so we can't use that.
   444                 uint32_t allocLen = 0;
   445                 if ( NS_SUCCEEDED(GetGlobalData(stm.hGlobal, aData, &allocLen)) ) {
   446                   *aLen = strlen ( reinterpret_cast<char*>(*aData) );
   447                   result = NS_OK;
   448                 }
   449               } break;
   451             case CF_UNICODETEXT:
   452               {
   453                 // Get the data out of the global data handle. The size we return
   454                 // should not include the null because the other platforms don't
   455                 // use nulls, so just return the length we get back from strlen(),
   456                 // since we know CF_UNICODETEXT is null terminated. Recall that GetGlobalData() 
   457                 // returns the size of the allocated buffer, not the size of the data 
   458                 // (on 98, these are not the same) so we can't use that.
   459                 uint32_t allocLen = 0;
   460                 if ( NS_SUCCEEDED(GetGlobalData(stm.hGlobal, aData, &allocLen)) ) {
   461                   *aLen = NS_strlen(reinterpret_cast<char16_t*>(*aData)) * 2;
   462                   result = NS_OK;
   463                 }
   464               } break;
   466             case CF_DIBV5:
   467               if (aMIMEImageFormat)
   468               {
   469                 uint32_t allocLen = 0;
   470                 unsigned char * clipboardData;
   471                 if (NS_SUCCEEDED(GetGlobalData(stm.hGlobal, (void **)&clipboardData, &allocLen)))
   472                 {
   473                   nsImageFromClipboard converter;
   474                   nsIInputStream * inputStream;
   475                   converter.GetEncodedImageStream(clipboardData, aMIMEImageFormat, &inputStream);   // addrefs for us, don't release
   476                   if ( inputStream ) {
   477                     *aData = inputStream;
   478                     *aLen = sizeof(nsIInputStream*);
   479                     result = NS_OK;
   480                   }
   481                 }
   482               } break;
   484             case CF_HDROP : 
   485               {
   486                 // in the case of a file drop, multiple files are stashed within a
   487                 // single data object. In order to match mozilla's D&D apis, we
   488                 // just pull out the file at the requested index, pretending as
   489                 // if there really are multiple drag items.
   490                 HDROP dropFiles = (HDROP) GlobalLock(stm.hGlobal);
   492                 UINT numFiles = ::DragQueryFileW(dropFiles, 0xFFFFFFFF, nullptr, 0);
   493                 NS_ASSERTION ( numFiles > 0, "File drop flavor, but no files...hmmmm" );
   494                 NS_ASSERTION ( aIndex < numFiles, "Asked for a file index out of range of list" );
   495                 if (numFiles > 0) {
   496                   UINT fileNameLen = ::DragQueryFileW(dropFiles, aIndex, nullptr, 0);
   497                   wchar_t* buffer = reinterpret_cast<wchar_t*>(nsMemory::Alloc((fileNameLen + 1) * sizeof(wchar_t)));
   498                   if ( buffer ) {
   499                     ::DragQueryFileW(dropFiles, aIndex, buffer, fileNameLen + 1);
   500                     *aData = buffer;
   501                     *aLen = fileNameLen * sizeof(char16_t);
   502                     result = NS_OK;
   503                   }
   504                   else
   505                     result = NS_ERROR_OUT_OF_MEMORY;
   506                 }
   507                 GlobalUnlock (stm.hGlobal) ;
   509               } break;
   511             default: {
   512               if ( fe.cfFormat == fileDescriptorFlavorA || fe.cfFormat == fileDescriptorFlavorW || fe.cfFormat == fileFlavor ) {
   513                 NS_WARNING ( "Mozilla doesn't yet understand how to read this type of file flavor" );
   514               } 
   515               else
   516               {
   517                 // Get the data out of the global data handle. The size we return
   518                 // should not include the null because the other platforms don't
   519                 // use nulls, so just return the length we get back from strlen(),
   520                 // since we know CF_UNICODETEXT is null terminated. Recall that GetGlobalData() 
   521                 // returns the size of the allocated buffer, not the size of the data 
   522                 // (on 98, these are not the same) so we can't use that.
   523                 //
   524                 // NOTE: we are assuming that anything that falls into this default case
   525                 //        is unicode. As we start to get more kinds of binary data, this
   526                 //        may become an incorrect assumption. Stay tuned.
   527                 uint32_t allocLen = 0;
   528                 if ( NS_SUCCEEDED(GetGlobalData(stm.hGlobal, aData, &allocLen)) ) {
   529                   if ( fe.cfFormat == CF_HTML ) {
   530                     // CF_HTML is actually UTF8, not unicode, so disregard the assumption
   531                     // above. We have to check the header for the actual length, and we'll
   532                     // do that in FindPlatformHTML(). For now, return the allocLen. This
   533                     // case is mostly to ensure we don't try to call strlen on the buffer.
   534                     *aLen = allocLen;
   535                   } else if (fe.cfFormat == preferredDropEffect) {
   536                     // As per the MSDN doc entitled: "Shell Clipboard Formats"
   537                     // CFSTR_PREFERREDDROPEFFECT should return a DWORD
   538                     // Reference: http://msdn.microsoft.com/en-us/library/bb776902(v=vs.85).aspx
   539                     NS_ASSERTION(allocLen == sizeof(DWORD),
   540                       "CFSTR_PREFERREDDROPEFFECT should return a DWORD");
   541                     *aLen = allocLen;
   542                   } else {
   543                     *aLen = NS_strlen(reinterpret_cast<char16_t*>(*aData)) * 
   544                             sizeof(char16_t);
   545                   }
   546                   result = NS_OK;
   547                 }
   548               }
   549             } break;
   550           } // switch
   551         } break;
   553       case TYMED_GDI: 
   554         {
   555 #ifdef DEBUG
   556           PR_LOG(gWin32ClipboardLog, PR_LOG_ALWAYS, 
   557                  ("*********************** TYMED_GDI\n"));
   558 #endif
   559         } break;
   561       default:
   562         break;
   563     } //switch
   565     ReleaseStgMedium(&stm);
   566   }
   568   return result;
   569 }
   572 //-------------------------------------------------------------------------
   573 nsresult nsClipboard::GetDataFromDataObject(IDataObject     * aDataObject,
   574                                             UINT              anIndex,
   575                                             nsIWidget       * aWindow,
   576                                             nsITransferable * aTransferable)
   577 {
   578   // make sure we have a good transferable
   579   if ( !aTransferable )
   580     return NS_ERROR_INVALID_ARG;
   582   nsresult res = NS_ERROR_FAILURE;
   584   // get flavor list that includes all flavors that can be written (including ones 
   585   // obtained through conversion)
   586   nsCOMPtr<nsISupportsArray> flavorList;
   587   res = aTransferable->FlavorsTransferableCanImport ( getter_AddRefs(flavorList) );
   588   if ( NS_FAILED(res) )
   589     return NS_ERROR_FAILURE;
   591   // Walk through flavors and see which flavor is on the clipboard them on the native clipboard,
   592   uint32_t i;
   593   uint32_t cnt;
   594   flavorList->Count(&cnt);
   595   for (i=0;i<cnt;i++) {
   596     nsCOMPtr<nsISupports> genericFlavor;
   597     flavorList->GetElementAt ( i, getter_AddRefs(genericFlavor) );
   598     nsCOMPtr<nsISupportsCString> currentFlavor ( do_QueryInterface(genericFlavor) );
   599     if ( currentFlavor ) {
   600       nsXPIDLCString flavorStr;
   601       currentFlavor->ToString(getter_Copies(flavorStr));
   602       UINT format = GetFormat(flavorStr);
   604       // Try to get the data using the desired flavor. This might fail, but all is
   605       // not lost.
   606       void* data = nullptr;
   607       uint32_t dataLen = 0;
   608       bool dataFound = false;
   609       if (nullptr != aDataObject) {
   610         if ( NS_SUCCEEDED(GetNativeDataOffClipboard(aDataObject, anIndex, format, flavorStr, &data, &dataLen)) )
   611           dataFound = true;
   612       } 
   613       else if (nullptr != aWindow) {
   614         if ( NS_SUCCEEDED(GetNativeDataOffClipboard(aWindow, anIndex, format, &data, &dataLen)) )
   615           dataFound = true;
   616       }
   618       // This is our second chance to try to find some data, having not found it
   619       // when directly asking for the flavor. Let's try digging around in other
   620       // flavors to help satisfy our craving for data.
   621       if ( !dataFound ) {
   622         if ( strcmp(flavorStr, kUnicodeMime) == 0 )
   623           dataFound = FindUnicodeFromPlainText ( aDataObject, anIndex, &data, &dataLen );
   624         else if ( strcmp(flavorStr, kURLMime) == 0 ) {
   625           // drags from other windows apps expose the native
   626           // CFSTR_INETURL{A,W} flavor
   627           dataFound = FindURLFromNativeURL ( aDataObject, anIndex, &data, &dataLen );
   628           if ( !dataFound )
   629             dataFound = FindURLFromLocalFile ( aDataObject, anIndex, &data, &dataLen );
   630         }
   631       } // if we try one last ditch effort to find our data
   633       // Hopefully by this point we've found it and can go about our business
   634       if ( dataFound ) {
   635         nsCOMPtr<nsISupports> genericDataWrapper;
   636           if ( strcmp(flavorStr, kFileMime) == 0 ) {
   637             // we have a file path in |data|. Create an nsLocalFile object.
   638             nsDependentString filepath(reinterpret_cast<char16_t*>(data));
   639             nsCOMPtr<nsIFile> file;
   640             if ( NS_SUCCEEDED(NS_NewLocalFile(filepath, false, getter_AddRefs(file))) )
   641               genericDataWrapper = do_QueryInterface(file);
   642             nsMemory::Free(data);
   643           }
   644         else if ( strcmp(flavorStr, kNativeHTMLMime) == 0) {
   645           // the editor folks want CF_HTML exactly as it's on the clipboard, no conversions,
   646           // no fancy stuff. Pull it off the clipboard, stuff it into a wrapper and hand
   647           // it back to them.
   648           if ( FindPlatformHTML(aDataObject, anIndex, &data, &dataLen) )
   649             nsPrimitiveHelpers::CreatePrimitiveForData ( flavorStr, data, dataLen, getter_AddRefs(genericDataWrapper) );
   650           else
   651           {
   652             nsMemory::Free(data);
   653             continue;     // something wrong with this flavor, keep looking for other data
   654           }
   655           nsMemory::Free(data);
   656         }
   657         else if ( strcmp(flavorStr, kJPEGImageMime) == 0 ||
   658                   strcmp(flavorStr, kJPGImageMime) == 0 ||
   659                   strcmp(flavorStr, kPNGImageMime) == 0) {
   660           nsIInputStream * imageStream = reinterpret_cast<nsIInputStream*>(data);
   661           genericDataWrapper = do_QueryInterface(imageStream);
   662           NS_IF_RELEASE(imageStream);
   663         }
   664         else {
   665           // we probably have some form of text. The DOM only wants LF, so convert from Win32 line 
   666           // endings to DOM line endings.
   667           int32_t signedLen = static_cast<int32_t>(dataLen);
   668           nsLinebreakHelpers::ConvertPlatformToDOMLinebreaks ( flavorStr, &data, &signedLen );
   669           dataLen = signedLen;
   671           nsPrimitiveHelpers::CreatePrimitiveForData ( flavorStr, data, dataLen, getter_AddRefs(genericDataWrapper) );
   672           nsMemory::Free(data);
   673         }
   675         NS_ASSERTION ( genericDataWrapper, "About to put null data into the transferable" );
   676         aTransferable->SetTransferData(flavorStr, genericDataWrapper, dataLen);
   677         res = NS_OK;
   679         // we found one, get out of the loop
   680         break;
   681       }
   683     }
   684   } // foreach flavor
   686   return res;
   688 }
   692 //
   693 // FindPlatformHTML
   694 //
   695 // Someone asked for the OS CF_HTML flavor. We give it back to them exactly as-is.
   696 //
   697 bool
   698 nsClipboard :: FindPlatformHTML ( IDataObject* inDataObject, UINT inIndex, void** outData, uint32_t* outDataLen )
   699 {
   700   // Reference: MSDN doc entitled "HTML Clipboard Format"
   701   // http://msdn.microsoft.com/en-us/library/aa767917(VS.85).aspx#unknown_854
   702   // CF_HTML is UTF8, not unicode. We also can't rely on it being null-terminated
   703   // so we have to check the CF_HTML header for the correct length. 
   704   // The length we return is the bytecount from the beginning of the selected data to the end
   705   // of the selected data, without the null termination. Because it's UTF8, we're guaranteed 
   706   // the header is ASCII.
   708   if (!outData || !*outData) {
   709     return false;
   710   }
   712   char version[8] = { 0 };
   713   int32_t startOfData = 0;
   714   int32_t endOfData = 0;
   715   int numFound = sscanf((char*)*outData, "Version:%7s\nStartHTML:%d\nEndHTML:%d", 
   716                         version, &startOfData, &endOfData);
   718   if (numFound != 3 || startOfData < -1 || endOfData < -1) {
   719     return false;
   720   }
   722   // Fixup the start and end markers if they have no context (set to -1)
   723   if (startOfData == -1) {
   724     startOfData = 0;
   725   }
   726   if (endOfData == -1) {
   727     endOfData = *outDataLen;
   728   }
   730   // Make sure we were passed sane values within our buffer size.
   731   // (Note that we've handled all cases of negative endOfData above, so we can
   732   // safely cast it to be unsigned here.)
   733   if (!endOfData || startOfData >= endOfData || 
   734       static_cast<uint32_t>(endOfData) > *outDataLen) {
   735     return false;
   736   }
   738   // We want to return the buffer not offset by startOfData because it will be 
   739   // parsed out later (probably by nsHTMLEditor::ParseCFHTML) when it is still
   740   // in CF_HTML format.
   741   *outDataLen = endOfData;
   742   return true;
   743 }
   746 //
   747 // FindUnicodeFromPlainText
   748 //
   749 // we are looking for text/unicode and we failed to find it on the clipboard first,
   750 // try again with text/plain. If that is present, convert it to unicode.
   751 //
   752 bool
   753 nsClipboard :: FindUnicodeFromPlainText ( IDataObject* inDataObject, UINT inIndex, void** outData, uint32_t* outDataLen )
   754 {
   755   bool dataFound = false;
   757   // we are looking for text/unicode and we failed to find it on the clipboard first,
   758   // try again with text/plain. If that is present, convert it to unicode.
   759   nsresult loadResult = GetNativeDataOffClipboard(inDataObject, inIndex, GetFormat(kTextMime), nullptr, outData, outDataLen);
   760   if ( NS_SUCCEEDED(loadResult) && *outData ) {
   761     const char* castedText = reinterpret_cast<char*>(*outData);          
   762     char16_t* convertedText = nullptr;
   763     int32_t convertedTextLen = 0;
   764     nsPrimitiveHelpers::ConvertPlatformPlainTextToUnicode ( castedText, *outDataLen, 
   765                                                               &convertedText, &convertedTextLen );
   766     if ( convertedText ) {
   767       // out with the old, in with the new 
   768       nsMemory::Free(*outData);
   769       *outData = convertedText;
   770       *outDataLen = convertedTextLen * sizeof(char16_t);
   771       dataFound = true;
   772     }
   773   } // if plain text data on clipboard
   775   return dataFound;
   777 } // FindUnicodeFromPlainText
   780 //
   781 // FindURLFromLocalFile
   782 //
   783 // we are looking for a URL and couldn't find it, try again with looking for 
   784 // a local file. If we have one, it may either be a normal file or an internet shortcut.
   785 // In both cases, however, we can get a URL (it will be a file:// url in the
   786 // local file case).
   787 //
   788 bool
   789 nsClipboard :: FindURLFromLocalFile ( IDataObject* inDataObject, UINT inIndex, void** outData, uint32_t* outDataLen )
   790 {
   791   bool dataFound = false;
   793   nsresult loadResult = GetNativeDataOffClipboard(inDataObject, inIndex, GetFormat(kFileMime), nullptr, outData, outDataLen);
   794   if ( NS_SUCCEEDED(loadResult) && *outData ) {
   795     // we have a file path in |data|. Is it an internet shortcut or a normal file?
   796     const nsDependentString filepath(static_cast<char16_t*>(*outData));
   797     nsCOMPtr<nsIFile> file;
   798     nsresult rv = NS_NewLocalFile(filepath, true, getter_AddRefs(file));
   799     if (NS_FAILED(rv)) {
   800       nsMemory::Free(*outData);
   801       return dataFound;
   802     }
   804     if ( IsInternetShortcut(filepath) ) {
   805       nsMemory::Free(*outData);
   806       nsAutoCString url;
   807       ResolveShortcut( file, url );
   808       if ( !url.IsEmpty() ) {
   809         // convert it to unicode and pass it out
   810         nsDependentString urlString(UTF8ToNewUnicode(url));
   811         // the internal mozilla URL format, text/x-moz-url, contains
   812         // URL\ntitle.  We can guess the title from the file's name.
   813         nsAutoString title;
   814         file->GetLeafName(title);
   815         // We rely on IsInternetShortcut check that file has a .url extension.
   816         title.SetLength(title.Length() - 4);
   817         if (title.IsEmpty())
   818           title = urlString;
   819         *outData = ToNewUnicode(urlString + NS_LITERAL_STRING("\n") + title);
   820         *outDataLen = NS_strlen(static_cast<char16_t*>(*outData)) * sizeof(char16_t);
   822         dataFound = true;
   823       }
   824     }
   825     else {
   826       // we have a normal file, use some Necko objects to get our file path
   827       nsAutoCString urlSpec;
   828       NS_GetURLSpecFromFile(file, urlSpec);
   830       // convert it to unicode and pass it out
   831       nsMemory::Free(*outData);
   832       *outData = UTF8ToNewUnicode(urlSpec);
   833       *outDataLen = NS_strlen(static_cast<char16_t*>(*outData)) * sizeof(char16_t);
   834       dataFound = true;
   835     } // else regular file
   836   }
   838   return dataFound;
   839 } // FindURLFromLocalFile
   841 //
   842 // FindURLFromNativeURL
   843 //
   844 // we are looking for a URL and couldn't find it using our internal
   845 // URL flavor, so look for it using the native URL flavor,
   846 // CF_INETURLSTRW (We don't handle CF_INETURLSTRA currently)
   847 //
   848 bool
   849 nsClipboard :: FindURLFromNativeURL ( IDataObject* inDataObject, UINT inIndex, void** outData, uint32_t* outDataLen )
   850 {
   851   bool dataFound = false;
   853   void* tempOutData = nullptr;
   854   uint32_t tempDataLen = 0;
   856   nsresult loadResult = GetNativeDataOffClipboard(inDataObject, inIndex, ::RegisterClipboardFormat(CFSTR_INETURLW), nullptr, &tempOutData, &tempDataLen);
   857   if ( NS_SUCCEEDED(loadResult) && tempOutData ) {
   858     nsDependentString urlString(static_cast<char16_t*>(tempOutData));
   859     // the internal mozilla URL format, text/x-moz-url, contains
   860     // URL\ntitle.  Since we don't actually have a title here,
   861     // just repeat the URL to fake it.
   862     *outData = ToNewUnicode(urlString + NS_LITERAL_STRING("\n") + urlString);
   863     *outDataLen = NS_strlen(static_cast<char16_t*>(*outData)) * sizeof(char16_t);
   864     nsMemory::Free(tempOutData);
   865     dataFound = true;
   866   }
   867   else {
   868     loadResult = GetNativeDataOffClipboard(inDataObject, inIndex, ::RegisterClipboardFormat(CFSTR_INETURLA), nullptr, &tempOutData, &tempDataLen);
   869     if ( NS_SUCCEEDED(loadResult) && tempOutData ) {
   870       // CFSTR_INETURLA is (currently) equal to CFSTR_SHELLURL which is equal to CF_TEXT
   871       // which is by definition ANSI encoded.
   872       nsCString urlUnescapedA;
   873       bool unescaped = NS_UnescapeURL(static_cast<char*>(tempOutData), tempDataLen, esc_OnlyNonASCII | esc_SkipControl, urlUnescapedA);
   875       nsString urlString;
   876       if (unescaped)
   877         NS_CopyNativeToUnicode(urlUnescapedA, urlString);
   878       else
   879         NS_CopyNativeToUnicode(nsDependentCString(static_cast<char*>(tempOutData), tempDataLen), urlString);
   881       // the internal mozilla URL format, text/x-moz-url, contains
   882       // URL\ntitle.  Since we don't actually have a title here,
   883       // just repeat the URL to fake it.
   884       *outData = ToNewUnicode(urlString + NS_LITERAL_STRING("\n") + urlString);
   885       *outDataLen = NS_strlen(static_cast<char16_t*>(*outData)) * sizeof(char16_t);
   886       nsMemory::Free(tempOutData);
   887       dataFound = true;
   888     }
   889   }
   891   return dataFound;
   892 } // FindURLFromNativeURL
   894 //
   895 // ResolveShortcut
   896 //
   897 void
   898 nsClipboard :: ResolveShortcut ( nsIFile* aFile, nsACString& outURL )
   899 {
   900   nsCOMPtr<nsIFileProtocolHandler> fph;
   901   nsresult rv = NS_GetFileProtocolHandler(getter_AddRefs(fph));
   902   if (NS_FAILED(rv))
   903     return;
   905   nsCOMPtr<nsIURI> uri;
   906   rv = fph->ReadURLFile(aFile, getter_AddRefs(uri));
   907   if (NS_FAILED(rv))
   908     return;
   910   uri->GetSpec(outURL);
   911 } // ResolveShortcut
   914 //
   915 // IsInternetShortcut
   916 //
   917 // A file is an Internet Shortcut if it ends with .URL
   918 //
   919 bool
   920 nsClipboard :: IsInternetShortcut ( const nsAString& inFileName ) 
   921 {
   922   return StringEndsWith(inFileName, NS_LITERAL_STRING(".url"), nsCaseInsensitiveStringComparator());
   923 } // IsInternetShortcut
   926 //-------------------------------------------------------------------------
   927 NS_IMETHODIMP 
   928 nsClipboard::GetNativeClipboardData ( nsITransferable * aTransferable, int32_t aWhichClipboard )
   929 {
   930   // make sure we have a good transferable
   931   if ( !aTransferable || aWhichClipboard != kGlobalClipboard )
   932     return NS_ERROR_FAILURE;
   934   nsresult res;
   936   // This makes sure we can use the OLE functionality for the clipboard
   937   IDataObject * dataObj;
   938   if (S_OK == ::OleGetClipboard(&dataObj)) {
   939     // Use OLE IDataObject for clipboard operations
   940     res = GetDataFromDataObject(dataObj, 0, nullptr, aTransferable);
   941     dataObj->Release();
   942   } 
   943   else {
   944     // do it the old manual way
   945     res = GetDataFromDataObject(nullptr, 0, mWindow, aTransferable);
   946   }
   947   return res;
   949 }
   951 NS_IMETHODIMP
   952 nsClipboard::EmptyClipboard(int32_t aWhichClipboard)
   953 {
   954   // Some programs such as ZoneAlarm monitor clipboard usage and then open the
   955   // clipboard to scan it.  If we i) empty and then ii) set data, then the
   956   // 'set data' can sometimes fail with access denied becacuse another program
   957   // has the clipboard open.  So to avoid this race condition for OpenClipboard
   958   // we do not empty the clipboard when we're setting it.
   959   if (aWhichClipboard == kGlobalClipboard && !mEmptyingForSetData) {
   960     OleSetClipboard(nullptr);
   961   }
   962   return nsBaseClipboard::EmptyClipboard(aWhichClipboard);
   963 }
   965 //-------------------------------------------------------------------------
   966 NS_IMETHODIMP nsClipboard::HasDataMatchingFlavors(const char** aFlavorList,
   967                                                   uint32_t aLength,
   968                                                   int32_t aWhichClipboard,
   969                                                   bool *_retval)
   970 {
   971   *_retval = false;
   972   if (aWhichClipboard != kGlobalClipboard || !aFlavorList)
   973     return NS_OK;
   975   for (uint32_t i = 0;i < aLength; ++i) {
   976 #ifdef DEBUG
   977     if (strcmp(aFlavorList[i], kTextMime) == 0)
   978       NS_WARNING ( "DO NOT USE THE text/plain DATA FLAVOR ANY MORE. USE text/unicode INSTEAD" );
   979 #endif
   981     UINT format = GetFormat(aFlavorList[i]);
   982     if (IsClipboardFormatAvailable(format)) {
   983       *_retval = true;
   984       break;
   985     }
   986     else {
   987       // We haven't found the exact flavor the client asked for, but maybe we can
   988       // still find it from something else that's on the clipboard...
   989       if (strcmp(aFlavorList[i], kUnicodeMime) == 0) {
   990         // client asked for unicode and it wasn't present, check if we have CF_TEXT.
   991         // We'll handle the actual data substitution in the data object.
   992         if (IsClipboardFormatAvailable(GetFormat(kTextMime)))
   993           *_retval = true;
   994       }
   995     }
   996   }
   998   return NS_OK;
   999 }

mercurial