Back to index

lightning-sunbird  0.9+nobinonly
nsDragService.cpp
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is mozilla.org code.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * IBM Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 1998
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *   Rich Walsh <dragtext@e-vertise.com>
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #define INCL_DOSMISC
00039 #define INCL_DOSERRORS
00040 
00041 #include "nsDragService.h"
00042 #include "nsXPCOM.h"
00043 #include "nsISupportsPrimitives.h"
00044 #include "nsString.h"
00045 #include "nsXPIDLString.h"
00046 #include "nsReadableUtils.h"
00047 #include "nsIWebBrowserPersist.h"
00048 #include "nsILocalFile.h"
00049 #include "nsIURI.h"
00050 #include "nsIURL.h"
00051 #include "nsNetUtil.h"
00052 #include "nsOS2Uni.h"
00053 #include "nsdefs.h"
00054 #include "wdgtos2rc.h"
00055 #include "nsILocalFileOS2.h"
00056 #include "nsIDocument.h"
00057 
00058 NS_IMPL_ADDREF_INHERITED(nsDragService, nsBaseDragService)
00059 NS_IMPL_RELEASE_INHERITED(nsDragService, nsBaseDragService)
00060 NS_IMPL_QUERY_INTERFACE4(nsDragService,
00061                          nsIDragService,
00062                          nsIDragService_1_8_BRANCH,
00063                          nsIDragSession,
00064                          nsIDragSessionOS2)
00065 
00066 // --------------------------------------------------------------------------
00067 // Local defines
00068 
00069 // undocumented(?)
00070 #ifndef DC_PREPAREITEM
00071   #define DC_PREPAREITEM  0x0040
00072 #endif
00073 
00074 // limit URL object titles to a reasonable length
00075 #define MAXTITLELTH 31
00076 #define TITLESEPARATOR (L' ')
00077 
00078 #define DTSHARE_NAME    "\\SHAREMEM\\MOZ_DND"
00079 #define DTSHARE_RMF     "<DRM_DTSHARE, DRF_TEXT>"
00080 
00081 #define OS2FILE_NAME    "MOZ_TGT.TMP"
00082 #define OS2FILE_TXTRMF  "<DRM_OS2FILE, DRF_TEXT>"
00083 #define OS2FILE_UNKRMF  "<DRM_OS2FILE, DRF_UNKNOWN>"
00084 
00085 // --------------------------------------------------------------------------
00086 // Helper functions
00087 
00088 nsresult RenderToOS2File( PDRAGITEM pditem, HWND hwnd);
00089 nsresult RenderToOS2FileComplete(PDRAGTRANSFER pdxfer, USHORT usResult,
00090                                  PRBool content, char** outText);
00091 nsresult RenderToDTShare( PDRAGITEM pditem, HWND hwnd);
00092 nsresult RenderToDTShareComplete(PDRAGTRANSFER pdxfer, USHORT usResult,
00093                                  char** outText);
00094 nsresult RequestRendering( PDRAGITEM pditem, HWND hwnd, PCSZ pRMF, PCSZ pName);
00095 nsresult GetAtom( ATOM aAtom, char** outText);
00096 nsresult GetFileName(PDRAGITEM pditem, char** outText);
00097 nsresult GetFileContents(PCSZ pszPath, char** outText);
00098 nsresult GetTempFileName(char** outText);
00099 void     SaveTypeAndSource(nsILocalFile * file, nsIDOMDocument * domDoc,
00100                            PCSZ pszType);
00101 int      UnicodeToCodepage( const nsAString& inString, char **outText);
00102 int      CodepageToUnicode( const nsACString& inString, PRUnichar **outText);
00103 void     RemoveCarriageReturns(char * pszText);
00104 
00105 // --------------------------------------------------------------------------
00106 // Global data
00107 
00108 static HPOINTER gPtrArray[IDC_DNDCOUNT];
00109 static char *   gTempFile = 0;
00110 
00111 // --------------------------------------------------------------------------
00112 // --------------------------------------------------------------------------
00113 
00114 nsDragService::nsDragService()
00115 {
00116     // member initializers and constructor code
00117   mDragWnd = WinCreateWindow( HWND_DESKTOP, WC_STATIC, 0, 0, 0, 0, 0, 0,
00118                               HWND_DESKTOP, HWND_BOTTOM, 0, 0, 0);
00119   WinSubclassWindow( mDragWnd, nsDragWindowProc);
00120 
00121   HMODULE hModResources = NULLHANDLE;
00122   DosQueryModFromEIP(&hModResources, NULL, 0, NULL, NULL, (ULONG) &gPtrArray);
00123   for (int i = 0; i < IDC_DNDCOUNT; i++)
00124     gPtrArray[i] = ::WinLoadPointer(HWND_DESKTOP, hModResources, i+IDC_DNDBASE);
00125 }
00126 
00127 // --------------------------------------------------------------------------
00128 
00129 nsDragService::~nsDragService()
00130 {
00131     // destructor code
00132   WinDestroyWindow(mDragWnd);
00133 
00134   for (int i = 0; i < IDC_DNDCOUNT; i++) {
00135     WinDestroyPointer(gPtrArray[i]);
00136     gPtrArray[i] = 0;
00137   }
00138 }
00139 
00140 // --------------------------------------------------------------------------
00141 
00142 NS_IMETHODIMP nsDragService::InvokeDragSession(nsIDOMNode *aDOMNode,
00143                                             nsISupportsArray *aTransferables, 
00144                                             nsIScriptableRegion *aRegion,
00145                                             PRUint32 aActionType)
00146 {
00147   if (mDoingDrag)
00148     return NS_ERROR_UNEXPECTED;
00149 
00150   nsresult rv = nsBaseDragService::InvokeDragSession(aDOMNode, aTransferables,
00151                                                      aRegion, aActionType );
00152   NS_ENSURE_SUCCESS(rv, rv);
00153 
00154   mSourceDataItems = aTransferables;
00155   WinSetCapture(HWND_DESKTOP, NULLHANDLE);
00156 
00157     // Assume we are only dragging one thing for now
00158   PDRAGINFO pDragInfo = DrgAllocDraginfo(1);
00159   if (!pDragInfo)
00160     return NS_ERROR_UNEXPECTED;
00161 
00162   pDragInfo->usOperation = DO_DEFAULT;
00163 
00164   DRAGITEM dragitem;
00165   dragitem.hwndItem            = mDragWnd;
00166   dragitem.ulItemID            = (ULONG)this;
00167   dragitem.fsControl           = DC_OPEN;
00168   dragitem.cxOffset            = 2;
00169   dragitem.cyOffset            = 2;
00170   dragitem.fsSupportedOps      = DO_COPYABLE|DO_MOVEABLE|DO_LINKABLE;
00171 
00172     // since there is no source file, leave these "blank"
00173   dragitem.hstrContainerName   = NULLHANDLE;
00174   dragitem.hstrSourceName      = NULLHANDLE;
00175 
00176   rv = NS_ERROR_FAILURE;
00177   ULONG idIcon = 0;
00178 
00179     // bracket this to reduce our footprint before the drag begins
00180   {
00181     nsCOMPtr<nsISupports> genericItem;
00182     mSourceDataItems->GetElementAt(0, getter_AddRefs(genericItem));
00183     nsCOMPtr<nsITransferable> transItem (do_QueryInterface(genericItem));
00184 
00185     nsCOMPtr<nsISupports> genericData;
00186     PRUint32 len = 0;
00187 
00188       // see if we have a URL or text;  if so, the title method
00189       // will save the data and mimetype for use with a native drop
00190 
00191     if (NS_SUCCEEDED(transItem->GetTransferData(kURLMime,
00192                               getter_AddRefs(genericData), &len))) {
00193       nsXPIDLCString targetName;
00194       rv = GetUrlAndTitle( genericData, getter_Copies(targetName));
00195       if (NS_SUCCEEDED(rv)) {
00196         // advise PM that we need a DM_RENDERPREPARE msg
00197         // *before* it composes a render-to filename
00198         dragitem.fsControl     |= DC_PREPAREITEM;
00199         dragitem.hstrType       = DrgAddStrHandle("UniformResourceLocator");
00200         dragitem.hstrRMF        = DrgAddStrHandle("<DRM_OS2FILE,DRF_TEXT>");
00201         dragitem.hstrTargetName = DrgAddStrHandle(targetName.get());
00202         idIcon = IDC_DNDURL;
00203       }
00204     }
00205     else
00206     if (NS_SUCCEEDED(transItem->GetTransferData(kUnicodeMime,
00207                                 getter_AddRefs(genericData), &len))) {
00208       nsXPIDLCString targetName;
00209       rv = GetUniTextTitle( genericData, getter_Copies(targetName));
00210       if (NS_SUCCEEDED(rv)) {
00211         dragitem.hstrType       = DrgAddStrHandle("Plain Text");
00212         dragitem.hstrRMF        = DrgAddStrHandle("<DRM_OS2FILE,DRF_TEXT>");
00213         dragitem.hstrTargetName = DrgAddStrHandle(targetName.get());
00214         idIcon = IDC_DNDTEXT;
00215       }
00216     }
00217   }
00218 
00219     // if neither URL nor text are available, make this a Moz-only drag
00220     // by making it unidentifiable to native apps
00221   if (NS_FAILED(rv)) {
00222     mMimeType = 0;
00223     dragitem.hstrType       = DrgAddStrHandle("Unknown");
00224     dragitem.hstrRMF        = DrgAddStrHandle("<DRM_UNKNOWN,DRF_UNKNOWN>");
00225     dragitem.hstrTargetName = NULLHANDLE;
00226   }
00227   DrgSetDragitem(pDragInfo, &dragitem, sizeof(DRAGITEM), 0);
00228 
00229   DRAGIMAGE dragimage;
00230   memset(&dragimage, 0, sizeof(DRAGIMAGE));
00231   dragimage.cb = sizeof(DRAGIMAGE);
00232   dragimage.fl = DRG_ICON;
00233   if (idIcon)
00234     dragimage.hImage = gPtrArray[idIcon-IDC_DNDBASE];
00235   if (dragimage.hImage) {
00236     dragimage.cyOffset = 8;
00237     dragimage.cxOffset = 2;
00238   }
00239   else
00240     dragimage.hImage  = WinQuerySysPointer(HWND_DESKTOP, SPTR_FILE, FALSE);
00241     
00242   mDoingDrag = PR_TRUE;
00243   HWND hwndDest = DrgDrag(mDragWnd, pDragInfo, &dragimage, 1, VK_BUTTON2,
00244                   (void*)0x80000000L); // Don't lock the desktop PS
00245   mDoingDrag = PR_FALSE;
00246 
00247     // do clean up;  if the drop completed,
00248     // the target will delete the string handles
00249   if (hwndDest == 0)
00250       DrgDeleteDraginfoStrHandles(pDragInfo);
00251   DrgFreeDraginfo(pDragInfo);
00252 
00253   mSourceNode = 0;
00254   mSourceDocument = 0;
00255   mSourceDataItems = 0;
00256   mSourceData = 0;
00257   mMimeType = 0;
00258 
00259   return NS_OK;
00260 }
00261 
00262 // --------------------------------------------------------------------------
00263 
00264 MRESULT EXPENTRY nsDragWindowProc(HWND hWnd, ULONG msg, MPARAM mp1, MPARAM mp2)
00265 {
00266   switch (msg) {
00267 
00268       // if the user requests the contents of a URL be rendered (vs the URL
00269       // itself), change the suggested target name from the URL's title to
00270       // the name of the file that will be retrieved
00271     case DM_RENDERPREPARE: {
00272       PDRAGTRANSFER  pdxfer = (PDRAGTRANSFER)mp1;
00273       nsDragService* dragservice = (nsDragService*)pdxfer->pditem->ulItemID;
00274   
00275       if (pdxfer->usOperation == DO_COPY &&
00276           (WinGetKeyState(HWND_DESKTOP, VK_CTRL) & 0x8000) &&
00277           !strcmp(dragservice->mMimeType, kURLMime)) {
00278           // QI'ing nsIURL will fail for mailto: and the like
00279         nsCOMPtr<nsIURL> urlObject(do_QueryInterface(dragservice->mSourceData));
00280         if (urlObject) {
00281           nsCAutoString filename;
00282           urlObject->GetFileName(filename);
00283           if (filename.IsEmpty()) {
00284             urlObject->GetHost(filename);
00285             filename.Append("/file");
00286           }
00287           DrgDeleteStrHandle(pdxfer->pditem->hstrTargetName);
00288           pdxfer->pditem->hstrTargetName = DrgAddStrHandle(filename.get());
00289         }
00290       }
00291       return (MRESULT)TRUE;
00292     }
00293   
00294     case DM_RENDER: {
00295       nsresult       rv = NS_ERROR_FAILURE;
00296       PDRAGTRANSFER  pdxfer = (PDRAGTRANSFER)mp1;
00297       nsDragService* dragservice = (nsDragService*)pdxfer->pditem->ulItemID;
00298       char           chPath[CCHMAXPATH];
00299 
00300       DrgQueryStrName(pdxfer->hstrRenderToName, CCHMAXPATH, chPath);
00301 
00302         // if the user Ctrl-dropped a URL, use the nsIURL interface
00303         // to determine if it points to content (i.e. a file);  if so,
00304         // fetch its contents; if not (e.g. a 'mailto:' url), drop into
00305         // the code that uses nsIURI to render a URL object
00306 
00307       if (!strcmp(dragservice->mMimeType, kURLMime)) {
00308         if (pdxfer->usOperation == DO_COPY &&
00309             (WinGetKeyState(HWND_DESKTOP, VK_CTRL) & 0x8000)) {
00310           nsCOMPtr<nsIURL> urlObject(do_QueryInterface(dragservice->mSourceData));
00311           if (urlObject)
00312             rv = dragservice->SaveAsContents(chPath, urlObject);
00313         }
00314         if (!NS_SUCCEEDED(rv)) {
00315           nsCOMPtr<nsIURI> uriObject(do_QueryInterface(dragservice->mSourceData));
00316           if (uriObject)
00317             rv = dragservice->SaveAsURL(chPath, uriObject);
00318         }
00319       }
00320       else
00321           // if we're dragging text, do NLS conversion then write it to file
00322         if (!strcmp(dragservice->mMimeType, kUnicodeMime)) {
00323           nsCOMPtr<nsISupportsString> strObject(
00324                                  do_QueryInterface(dragservice->mSourceData));
00325           if (strObject)
00326             rv = dragservice->SaveAsText(chPath, strObject);
00327         }
00328   
00329       DrgPostTransferMsg(pdxfer->hwndClient, DM_RENDERCOMPLETE, pdxfer,
00330                          (NS_SUCCEEDED(rv) ? DMFL_RENDEROK : DMFL_RENDERFAIL),
00331                          0, TRUE);
00332       DrgFreeDragtransfer(pdxfer);
00333       return (MRESULT)TRUE;
00334     }
00335 
00336       // we don't use these msgs but neither does WinDefWindowProc()
00337     case DM_DRAGOVERNOTIFY:
00338     case DM_ENDCONVERSATION:
00339       return 0;
00340   
00341     default:
00342       break;
00343   }
00344 
00345   return ::WinDefWindowProc(hWnd, msg, mp1, mp2);
00346 }
00347 
00348 //-------------------------------------------------------------------------
00349 
00350 // if the versions of Start & EndDragSession in nsBaseDragService
00351 // were called (and they shouldn't be), they'd break nsIDragSessionOS2;
00352 // they're overridden here and turned into no-ops to prevent this
00353 
00354 NS_IMETHODIMP nsDragService::StartDragSession()
00355 {
00356   NS_ASSERTION(0, "OS/2 version of StartDragSession() should never be called!");
00357   return NS_OK;
00358 }
00359 
00360 NS_IMETHODIMP nsDragService::EndDragSession()
00361 {
00362   NS_ASSERTION(0, "OS/2 version of EndDragSession() should never be called!");
00363   return NS_OK;
00364 }
00365 
00366 // --------------------------------------------------------------------------
00367 
00368 NS_IMETHODIMP nsDragService::GetNumDropItems(PRUint32 *aNumDropItems)
00369 {
00370   if (mSourceDataItems)
00371     mSourceDataItems->Count(aNumDropItems);
00372   else
00373     *aNumDropItems = 0;
00374 
00375   return NS_OK;
00376 }
00377 
00378 // --------------------------------------------------------------------------
00379 
00380 NS_IMETHODIMP nsDragService::GetData(nsITransferable *aTransferable,
00381                                      PRUint32 aItemIndex)
00382 {
00383     // make sure that we have a transferable
00384   if (!aTransferable)
00385     return NS_ERROR_INVALID_ARG;
00386 
00387     // get flavor list that includes all acceptable flavors (including
00388     // ones obtained through conversion). Flavors are nsISupportsCStrings
00389     // so that they can be seen from JS.
00390   nsresult rv = NS_ERROR_FAILURE;
00391   nsCOMPtr<nsISupportsArray> flavorList;
00392   rv = aTransferable->FlavorsTransferableCanImport(getter_AddRefs(flavorList));
00393   if (NS_FAILED(rv))
00394     return rv;
00395 
00396     // count the number of flavors
00397   PRUint32 cnt;
00398   flavorList->Count (&cnt);
00399 
00400   for (unsigned int i= 0; i < cnt; ++i ) {
00401     nsCOMPtr<nsISupports> genericWrapper;
00402     flavorList->GetElementAt(i, getter_AddRefs(genericWrapper));
00403     nsCOMPtr<nsISupportsCString> currentFlavor;
00404     currentFlavor = do_QueryInterface(genericWrapper);
00405     if (currentFlavor) {
00406       nsXPIDLCString flavorStr;
00407       currentFlavor->ToString(getter_Copies(flavorStr));
00408   
00409       nsCOMPtr<nsISupports> genericItem;
00410   
00411       mSourceDataItems->GetElementAt(aItemIndex, getter_AddRefs(genericItem));
00412       nsCOMPtr<nsITransferable> item (do_QueryInterface(genericItem));
00413       if (item) {
00414         nsCOMPtr<nsISupports> data;
00415         PRUint32 tmpDataLen = 0;
00416         rv = item->GetTransferData(flavorStr, getter_AddRefs(data),
00417                                    &tmpDataLen);
00418         if (NS_SUCCEEDED(rv)) {
00419           rv = aTransferable->SetTransferData(flavorStr, data, tmpDataLen);
00420           break;
00421         }
00422       }
00423     }
00424   }
00425 
00426   return rv;
00427 }
00428 
00429 // --------------------------------------------------------------------------
00430 
00431 // This returns true if any of the dragged items support a specified data
00432 // flavor.  This doesn't make a lot of sense when dragging multiple items:
00433 // all of them ought to match.  OTOH, Moz doesn't support multiple drag
00434 // items so no problems arise.  If they do, use the commented-out code to
00435 // switch from "any" to "all".
00436 
00437 NS_IMETHODIMP nsDragService::IsDataFlavorSupported(const char *aDataFlavor,
00438                                                    PRBool *_retval)
00439 {
00440   if (!_retval)
00441     return NS_ERROR_INVALID_ARG;
00442 
00443   *_retval = PR_FALSE;
00444 
00445   PRUint32 numDragItems = 0;
00446   if (mSourceDataItems)
00447     mSourceDataItems->Count(&numDragItems);
00448   if (!numDragItems)
00449     return NS_OK;
00450 
00451 // return true if all items support this flavor
00452 //  for (PRUint32 itemIndex = 0, *_retval = PR_TRUE;
00453 //       itemIndex < numDragItems && *_retval; ++itemIndex) {
00454 //    *_retval = PR_FALSE;
00455 
00456 // return true if any item supports this flavor
00457   for (PRUint32 itemIndex = 0;
00458        itemIndex < numDragItems && !(*_retval); ++itemIndex) {
00459 
00460     nsCOMPtr<nsISupports> genericItem;
00461     mSourceDataItems->GetElementAt(itemIndex, getter_AddRefs(genericItem));
00462     nsCOMPtr<nsITransferable> currItem (do_QueryInterface(genericItem));
00463 
00464     if (currItem) {
00465       nsCOMPtr <nsISupportsArray> flavorList;
00466       currItem->FlavorsTransferableCanExport(getter_AddRefs(flavorList));
00467 
00468       if (flavorList) {
00469         PRUint32 numFlavors;
00470         flavorList->Count( &numFlavors );
00471 
00472         for (PRUint32 flavorIndex=0; flavorIndex < numFlavors; ++flavorIndex) {
00473           nsCOMPtr<nsISupports> genericWrapper;
00474           flavorList->GetElementAt(flavorIndex, getter_AddRefs(genericWrapper));
00475           nsCOMPtr<nsISupportsCString> currentFlavor;
00476           currentFlavor = do_QueryInterface(genericWrapper);
00477 
00478           if (currentFlavor) {
00479             nsXPIDLCString flavorStr;
00480             currentFlavor->ToString ( getter_Copies(flavorStr) );
00481             if (strcmp(flavorStr, aDataFlavor) == 0) {
00482               *_retval = PR_TRUE;
00483               break;
00484             }
00485           }
00486         } // for each flavor
00487       }
00488     }
00489   }
00490 
00491   return NS_OK;
00492 }
00493 
00494 // --------------------------------------------------------------------------
00495 
00496 // use nsIWebBrowserPersist to fetch the contents of a URL
00497 
00498 nsresult nsDragService::SaveAsContents(PCSZ pszDest, nsIURL* aURL)
00499 {
00500   nsCOMPtr<nsIURI> linkURI (do_QueryInterface(aURL));
00501   if (!linkURI)
00502     return NS_ERROR_FAILURE;
00503 
00504   nsCOMPtr<nsIWebBrowserPersist> webPersist(
00505     do_CreateInstance("@mozilla.org/embedding/browser/nsWebBrowserPersist;1"));
00506   if (!webPersist)
00507     return NS_ERROR_FAILURE;
00508 
00509   nsCOMPtr<nsILocalFile> file;
00510   NS_NewNativeLocalFile(nsDependentCString(pszDest), PR_TRUE,
00511                         getter_AddRefs(file));
00512   if (!file)
00513     return NS_ERROR_FAILURE;
00514 
00515   FILE* fp;
00516   if (NS_FAILED(file->OpenANSIFileDesc("wb+", &fp)))
00517     return NS_ERROR_FAILURE;
00518 
00519   fwrite("", 0, 1, fp);
00520   fclose(fp);
00521   webPersist->SaveURI(linkURI, nsnull, nsnull, nsnull, nsnull, file);
00522 
00523   return NS_OK;
00524 }
00525 
00526 // --------------------------------------------------------------------------
00527 
00528 // save this URL in a file that the WPS will identify as a WPUrl object
00529 
00530 nsresult nsDragService::SaveAsURL(PCSZ pszDest, nsIURI* aURI)
00531 {
00532   nsCAutoString strUri;
00533   aURI->GetSpec(strUri);
00534 
00535   if (strUri.IsEmpty())
00536     return NS_ERROR_FAILURE;
00537 
00538   nsCOMPtr<nsILocalFile> file;
00539   NS_NewNativeLocalFile(nsDependentCString(pszDest), PR_TRUE,
00540                         getter_AddRefs(file));
00541   if (!file)
00542     return NS_ERROR_FAILURE;
00543 
00544   FILE* fp;
00545   if (NS_FAILED(file->OpenANSIFileDesc("wb+", &fp)))
00546     return NS_ERROR_FAILURE;
00547 
00548   fwrite(strUri.get(), strUri.Length(), 1, fp);
00549   fclose(fp);
00550 
00551   nsCOMPtr<nsIDOMDocument> domDoc;
00552   GetSourceDocument(getter_AddRefs(domDoc));
00553   SaveTypeAndSource(file, domDoc, "UniformResourceLocator");
00554 
00555   return NS_OK;
00556 }
00557 
00558 // --------------------------------------------------------------------------
00559 
00560 // save this text to file after conversion to the current codepage
00561 
00562 nsresult nsDragService::SaveAsText(PCSZ pszDest, nsISupportsString* aString)
00563 {
00564   nsAutoString strData;
00565   aString->GetData(strData);
00566 
00567   if (strData.IsEmpty())
00568     return NS_ERROR_FAILURE;
00569 
00570   nsCOMPtr<nsILocalFile> file;
00571   NS_NewNativeLocalFile(nsDependentCString(pszDest), PR_TRUE,
00572                         getter_AddRefs(file));
00573   if (!file)
00574     return NS_ERROR_FAILURE;
00575 
00576   nsXPIDLCString textStr;
00577   int cnt = UnicodeToCodepage( strData, getter_Copies(textStr));
00578   if (!cnt)
00579     return NS_ERROR_FAILURE;
00580 
00581   FILE* fp;
00582   if (NS_FAILED(file->OpenANSIFileDesc("wb+", &fp)))
00583     return NS_ERROR_FAILURE;
00584 
00585   fwrite(textStr.get(), cnt, 1, fp);
00586   fclose(fp);
00587 
00588   nsCOMPtr<nsIDOMDocument> domDoc;
00589   GetSourceDocument(getter_AddRefs(domDoc));
00590   SaveTypeAndSource(file, domDoc, "Plain Text");
00591 
00592   return NS_OK;
00593 }
00594 
00595 // --------------------------------------------------------------------------
00596 
00597 // Split a Moz Url/Title into its components, save the Url for use by
00598 // a native drop, then compose a title.
00599 
00600 nsresult  nsDragService::GetUrlAndTitle(nsISupports *aGenericData,
00601                                         char **aTargetName)
00602 {
00603     // get the URL/title string
00604   nsCOMPtr<nsISupportsString> strObject ( do_QueryInterface(aGenericData));
00605   if (!strObject)
00606     return NS_ERROR_FAILURE;
00607   nsAutoString strData;
00608   strObject->GetData(strData);
00609 
00610     // split string into URL and Title - 
00611     // if there's a title but no URL, there's no reason to continue
00612   PRInt32 lineIndex = strData.FindChar ('\n');
00613   if (lineIndex == 0)
00614     return NS_ERROR_FAILURE;
00615 
00616     // get the URL portion of the text
00617   nsAutoString strUrl;
00618   if (lineIndex == -1)
00619     strUrl = strData;
00620   else
00621     strData.Left(strUrl, lineIndex);
00622 
00623     // save the URL for later use
00624   nsCOMPtr<nsIURI> saveURI;
00625   NS_NewURI(getter_AddRefs(saveURI), strUrl);
00626   if (!saveURI)
00627     return NS_ERROR_FAILURE;
00628 
00629     // if there's a bona-fide title & it isn't just a copy of the URL,
00630     // limit it to a reasonable length, perform NLS conversion, then return
00631 
00632   if (++lineIndex && lineIndex != (int)strData.Length() &&
00633       !strUrl.Equals(Substring(strData, lineIndex, strData.Length()))) {
00634     PRUint32 strLth = NS_MIN((int)strData.Length()-lineIndex, MAXTITLELTH);
00635     nsAutoString strTitle;
00636     strData.Mid(strTitle, lineIndex, strLth);
00637     if (!UnicodeToCodepage(strTitle, aTargetName))
00638       return NS_ERROR_FAILURE;
00639 
00640     mSourceData = do_QueryInterface(saveURI);
00641     mMimeType = kURLMime;
00642     return NS_OK;
00643   }
00644 
00645     // if the URI can be handled as a URL, construct a title from
00646     // the hostname & filename;  if not, use the first MAXTITLELTH
00647     // characters that appear after the scheme name
00648 
00649   nsCAutoString strTitle;
00650 
00651   nsCOMPtr<nsIURL> urlObj( do_QueryInterface(saveURI));
00652   if (urlObj) {
00653     nsCAutoString strFile;
00654 
00655     urlObj->GetHost(strTitle);
00656     urlObj->GetFileName(strFile);
00657     if (!strFile.IsEmpty()) {
00658       strTitle.AppendLiteral("/");
00659       strTitle.Append(strFile);
00660     }
00661     else {
00662       urlObj->GetDirectory(strFile);
00663       if (strFile.Length() > 1) {
00664         nsCAutoString::const_iterator start, end, curr;
00665         strFile.BeginReading(start);
00666         strFile.EndReading(end);
00667         strFile.EndReading(curr);
00668         for (curr.advance(-2); curr != start; --curr)
00669           if (*curr == '/')
00670             break;
00671         strTitle.Append(Substring(curr, end));
00672       }
00673     }
00674   }
00675   else {
00676     saveURI->GetSpec(strTitle);
00677     PRInt32 index = strTitle.FindChar (':');
00678     if (index != -1) {
00679       if ((strTitle.get())[++index] == '/')
00680         if ((strTitle.get())[++index] == '/')
00681           ++index;
00682       strTitle.Cut(0, index);
00683     }
00684     if (strTitle.Length() > MAXTITLELTH)
00685       strTitle.Truncate(MAXTITLELTH);
00686   }
00687 
00688   *aTargetName = ToNewCString(strTitle);
00689 
00690   mSourceData = do_QueryInterface(saveURI);
00691   mMimeType = kURLMime;
00692   return NS_OK;
00693 }
00694 
00695 // --------------------------------------------------------------------------
00696 
00697 // Construct a title for text drops from the leading words of the text.
00698 // Alphanumeric characters are copied to the title;  sequences of
00699 // non-alphanums are replaced by a single space
00700 
00701 nsresult  nsDragService::GetUniTextTitle(nsISupports *aGenericData,
00702                                          char **aTargetName)
00703 {
00704     // get the string
00705   nsCOMPtr<nsISupportsString> strObject ( do_QueryInterface(aGenericData));
00706   if (!strObject)
00707     return NS_ERROR_FAILURE;
00708 
00709     // alloc a buffer to hold the unicode title text
00710   int bufsize = (MAXTITLELTH+1)*2;
00711   PRUnichar * buffer = (PRUnichar*)nsMemory::Alloc(bufsize);
00712   if (!buffer)
00713     return NS_ERROR_FAILURE;
00714 
00715   nsAutoString strData;
00716   strObject->GetData(strData);
00717   nsAutoString::const_iterator start, end;
00718   strData.BeginReading(start);
00719   strData.EndReading(end);
00720 
00721     // skip over leading non-alphanumerics
00722   for( ; start != end; ++start)
00723     if (UniQueryChar( *start, CT_ALNUM))
00724       break;
00725 
00726     // move alphanumerics into the buffer & replace contiguous
00727     // non-alnums with a single separator character
00728   int ctr, sep;
00729   for (ctr=0, sep=0; start != end && ctr < MAXTITLELTH; ++start) {
00730     if (UniQueryChar( *start, CT_ALNUM)) {
00731       buffer[ctr] = *start;
00732       ctr++;
00733       sep = 0;
00734     }
00735     else
00736       if (!sep) {
00737         buffer[ctr] = TITLESEPARATOR;
00738         ctr++;
00739         sep = 1;
00740       }
00741   }
00742     // eliminate trailing separators & lone characters
00743     // orphaned when the title is truncated
00744   if (sep)
00745     ctr--;
00746   if (ctr >= MAXTITLELTH - sep && buffer[ctr-2] == TITLESEPARATOR)
00747     ctr -= 2;
00748   buffer[ctr] = 0;
00749 
00750     // if we ended up with no alnums, call the result "text";
00751     // otherwise, do NLS conversion
00752   if (!ctr) {
00753     *aTargetName = ToNewCString(NS_LITERAL_CSTRING("text"));
00754     ctr = 1;
00755   }
00756   else
00757     ctr = UnicodeToCodepage( nsDependentString(buffer), aTargetName);
00758 
00759     // free our buffer, then exit
00760   nsMemory::Free(buffer);
00761 
00762   if (!ctr)
00763   return NS_ERROR_FAILURE;
00764 
00765   mSourceData = aGenericData;
00766   mMimeType = kUnicodeMime;
00767   return NS_OK;
00768 }
00769 
00770 // --------------------------------------------------------------------------
00771 // nsIDragSessionOS2
00772 // --------------------------------------------------------------------------
00773 
00774 // DragOverMsg() provides minimal handling if a drag session is already
00775 // in progress.  If not, it assumes this is a native drag that has just
00776 // entered the window and calls NativeDragEnter() to start a session.
00777 
00778 NS_IMETHODIMP nsDragService::DragOverMsg(PDRAGINFO pdinfo, MRESULT &mr,
00779                                          PRUint32* dragFlags)
00780 {
00781   nsresult  rv = NS_ERROR_FAILURE;
00782 
00783   if (!&mr || !dragFlags || !pdinfo || !DrgAccessDraginfo(pdinfo))
00784     return rv;
00785 
00786   *dragFlags = 0;
00787   mr = MRFROM2SHORT(DOR_NEVERDROP, 0);
00788 
00789     // examine the dragged item & "start" a drag session if OK;
00790     // also, signal the need for a dragenter event
00791   if (!mDoingDrag)
00792     if (NS_SUCCEEDED(NativeDragEnter(pdinfo)))
00793       *dragFlags |= DND_DISPATCHENTEREVENT;
00794 
00795     // if we're in a drag, set it up to be dispatched
00796   if (mDoingDrag) {
00797     SetCanDrop(PR_FALSE);
00798     switch (pdinfo->usOperation) {
00799       case DO_COPY:
00800         SetDragAction(DRAGDROP_ACTION_COPY);
00801         break;
00802       case DO_LINK:
00803         SetDragAction(DRAGDROP_ACTION_LINK);
00804         break;
00805       default:
00806         SetDragAction(DRAGDROP_ACTION_MOVE);
00807         break;
00808     }
00809     if (mSourceNode)
00810       *dragFlags |= DND_DISPATCHEVENT | DND_GETDRAGOVERRESULT | DND_MOZDRAG;
00811     else
00812       *dragFlags |= DND_DISPATCHEVENT | DND_GETDRAGOVERRESULT | DND_NATIVEDRAG;
00813     rv = NS_OK;
00814   }
00815 
00816   DrgFreeDraginfo(pdinfo);
00817   return rv;
00818 }
00819 
00820 // --------------------------------------------------------------------------
00821 
00822 // Evaluates native drag data, and if acceptable, creates & stores
00823 // a transferable with the available flavors (but not the data);
00824 // if successful, it "starts" the session.
00825 
00826 NS_IMETHODIMP nsDragService::NativeDragEnter(PDRAGINFO pdinfo)
00827 {
00828   nsresult  rv = NS_ERROR_FAILURE;
00829   PRBool    isFQFile = FALSE;
00830   PRBool    isAtom = FALSE;
00831   PDRAGITEM pditem = 0;
00832 
00833   if (pdinfo->cditem != 1)
00834     return rv;
00835 
00836   pditem = DrgQueryDragitemPtr(pdinfo, 0);
00837 
00838   if (pditem) {
00839     if (DrgVerifyRMF(pditem, "DRM_ATOM", 0)) {
00840       isAtom = TRUE;
00841       rv = NS_OK;
00842     }
00843     else
00844     if (DrgVerifyRMF(pditem, "DRM_DTSHARE", 0))
00845       rv = NS_OK;
00846     else
00847     if (DrgVerifyRMF(pditem, "DRM_OS2FILE", 0)) {
00848       rv = NS_OK;
00849       if (pditem->hstrContainerName && pditem->hstrSourceName)
00850         isFQFile = TRUE;
00851     }
00852   }
00853 
00854   if (NS_SUCCEEDED(rv)) {
00855     rv = NS_ERROR_FAILURE;
00856     nsCOMPtr<nsITransferable> trans(
00857             do_CreateInstance("@mozilla.org/widget/transferable;1", &rv));
00858     if (trans) {
00859 
00860       PRBool isUrl = DrgVerifyType(pditem, "UniformResourceLocator");
00861       PRBool isAlt = (WinGetKeyState(HWND_DESKTOP, VK_ALT) & 0x8000);
00862 
00863         // if this is a fully-qualified file or the item claims to be
00864         // a Url, identify it as a Url & also offer it as html
00865       if ((isFQFile && !isAlt) || isUrl) {
00866         trans->AddDataFlavor(kURLMime);
00867         trans->AddDataFlavor(kHTMLMime);
00868       }
00869 
00870         // everything is always "text"
00871       trans->AddDataFlavor(kUnicodeMime);
00872 
00873         // if we can create the array, initialize the session
00874       nsCOMPtr<nsISupportsArray> transArray(
00875                     do_CreateInstance("@mozilla.org/supports-array;1", &rv));
00876       if (transArray) {
00877         transArray->InsertElementAt(trans, 0);
00878         mSourceDataItems = transArray;
00879 
00880         // add the dragged data to the transferable if we have easy access
00881         // to it (i.e. no need to read a file or request rendering);  for
00882         // URLs, we'll skip creating a title until the drop occurs
00883         nsXPIDLCString someText;
00884         if (isAtom) {
00885           if (NS_SUCCEEDED(GetAtom(pditem->ulItemID, getter_Copies(someText))))
00886             NativeDataToTransferable( someText.get(), 0, isUrl);
00887         }
00888         else
00889         if (isFQFile && !isAlt &&
00890             NS_SUCCEEDED(GetFileName(pditem, getter_Copies(someText)))) {
00891           nsCOMPtr<nsILocalFile> file;
00892           if (NS_SUCCEEDED(NS_NewNativeLocalFile(someText, PR_TRUE,
00893                                                  getter_AddRefs(file)))) {
00894             nsCAutoString textStr;
00895             NS_GetURLSpecFromFile(file, textStr);
00896             if (!textStr.IsEmpty()) {
00897               someText.Assign(ToNewCString(textStr));
00898               NativeDataToTransferable( someText.get(), 0, TRUE);
00899             }
00900           }
00901         }
00902 
00903         mSourceNode = 0;
00904         mSourceDocument = 0;
00905         mDoingDrag = TRUE;
00906         rv = NS_OK;
00907       }
00908     }
00909   }
00910 
00911   return rv;
00912 }
00913 
00914 // --------------------------------------------------------------------------
00915 
00916 // Invoked after a dragover event has been dispatched, this constructs
00917 // a reply to DM_DRAGOVER based on the canDrop & dragAction attributes.
00918 
00919 NS_IMETHODIMP nsDragService::GetDragoverResult(MRESULT& mr)
00920 {
00921   nsresult  rv = NS_ERROR_FAILURE;
00922   if (!&mr)
00923     return rv;
00924 
00925   if (mDoingDrag) {
00926 
00927     PRBool canDrop = PR_FALSE;
00928     USHORT usDrop;
00929     GetCanDrop(&canDrop);
00930     if (canDrop)
00931       usDrop = DOR_DROP;
00932     else
00933       usDrop = DOR_NODROP;
00934 
00935     PRUint32 action;
00936     USHORT   usOp;
00937     GetDragAction(&action);
00938     if (action & DRAGDROP_ACTION_COPY)
00939       usOp = DO_COPY;
00940     else
00941     if (action & DRAGDROP_ACTION_LINK)
00942       usOp = DO_LINK;
00943     else {
00944       if (mSourceNode)
00945         usOp = DO_MOVE;
00946       else
00947         usOp = DO_UNKNOWN+1;
00948       if (action == DRAGDROP_ACTION_NONE)
00949         usDrop = DOR_NODROP;
00950     }
00951 
00952     mr = MRFROM2SHORT(usDrop, usOp);
00953     rv = NS_OK;
00954   }
00955   else
00956     mr = MRFROM2SHORT(DOR_NEVERDROP, 0);
00957 
00958   return rv;
00959 }
00960 
00961 // --------------------------------------------------------------------------
00962 
00963 // have the client dispatch the event, then call ExitSession()
00964 
00965 NS_IMETHODIMP nsDragService::DragLeaveMsg(PDRAGINFO pdinfo, PRUint32* dragFlags)
00966 {
00967   if (!mDoingDrag || !dragFlags)
00968     return NS_ERROR_FAILURE;
00969 
00970   if (mSourceNode)
00971     *dragFlags = DND_DISPATCHEVENT | DND_EXITSESSION | DND_MOZDRAG;
00972   else
00973     *dragFlags = DND_DISPATCHEVENT | DND_EXITSESSION | DND_NATIVEDRAG;
00974 
00975   return NS_OK;
00976 }
00977 
00978 // --------------------------------------------------------------------------
00979 
00980 // DropHelp occurs when you press F1 during a drag;  apparently,
00981 // it's like a regular drop in that the target has to do clean up
00982 
00983 NS_IMETHODIMP nsDragService::DropHelpMsg(PDRAGINFO pdinfo, PRUint32* dragFlags)
00984 {
00985   if (!mDoingDrag)
00986     return NS_ERROR_FAILURE;
00987 
00988   if (pdinfo && DrgAccessDraginfo(pdinfo)) {
00989     DrgDeleteDraginfoStrHandles(pdinfo);
00990     DrgFreeDraginfo(pdinfo);
00991   }
00992 
00993   if (!dragFlags)
00994     return NS_ERROR_FAILURE;
00995 
00996   if (mSourceNode)
00997     *dragFlags = DND_DISPATCHEVENT | DND_EXITSESSION | DND_MOZDRAG;
00998   else
00999     *dragFlags = DND_DISPATCHEVENT | DND_EXITSESSION | DND_NATIVEDRAG;
01000 
01001   return NS_OK;
01002 }
01003 
01004 // --------------------------------------------------------------------------
01005 
01006 // for native drags, clean up;
01007 // for all drags, signal that Moz is no longer in d&d mode
01008 
01009 NS_IMETHODIMP nsDragService::ExitSession(PRUint32* dragFlags)
01010 {
01011   if (!mDoingDrag)
01012     return NS_ERROR_FAILURE;
01013 
01014   if (!mSourceNode) {
01015     mSourceDataItems = 0;
01016     mDoingDrag = FALSE;
01017 
01018       // if we created a temp file, delete it
01019     if (gTempFile) {
01020       DosDelete(gTempFile);
01021       nsMemory::Free(gTempFile);
01022       gTempFile = 0;
01023     }
01024   }
01025 
01026   if (!dragFlags)
01027     return NS_ERROR_FAILURE;
01028   *dragFlags = 0;
01029 
01030   return NS_OK;
01031 }
01032 
01033 // --------------------------------------------------------------------------
01034 
01035 // If DropMsg() is presented with native data that has to be rendered,
01036 // the drop event & cleanup will be defered until the client's window
01037 // has received a render-complete msg.
01038 
01039 NS_IMETHODIMP nsDragService::DropMsg(PDRAGINFO pdinfo, HWND hwnd,
01040                                      PRUint32* dragFlags)
01041 {
01042   if (!mDoingDrag || !dragFlags || !pdinfo || !DrgAccessDraginfo(pdinfo))
01043     return NS_ERROR_FAILURE;
01044 
01045   switch (pdinfo->usOperation) {
01046     case DO_MOVE:
01047       SetDragAction(DRAGDROP_ACTION_MOVE);
01048       break;
01049     case DO_COPY:
01050       SetDragAction(DRAGDROP_ACTION_COPY);
01051       break;
01052     case DO_LINK:
01053       SetDragAction(DRAGDROP_ACTION_LINK);
01054       break;
01055     default:  // avoid "moving" (i.e. deleting) native text/objects
01056       if (mSourceNode)
01057         SetDragAction(DRAGDROP_ACTION_MOVE);
01058       else
01059         SetDragAction(DRAGDROP_ACTION_COPY);
01060       break;
01061   }
01062 
01063     // if this is a native drag, move the source data to a transferable;
01064     // request rendering if needed
01065   nsresult rv = NS_OK;
01066   PRBool rendering = PR_FALSE;
01067   if (!mSourceNode)
01068     rv = NativeDrop( pdinfo, hwnd, &rendering);
01069 
01070     // note: NativeDrop() sends an end-conversation msg to native
01071     // sources but nothing sends them to Mozilla - however, Mozilla
01072     // (i.e. nsDragService) doesn't need them, so...
01073 
01074     // if rendering, the action flags are off because we don't want
01075     // the client to do anything yet;  the status flags are off because
01076     // we'll be exiting d&d mode before the next screen update occurs
01077   if (rendering)
01078     *dragFlags = 0;
01079   else {
01080     // otherwise, set the flags & free the native drag structures
01081 
01082     *dragFlags = DND_EXITSESSION;
01083     if (NS_SUCCEEDED(rv))
01084       if (mSourceNode)
01085         *dragFlags |= DND_DISPATCHEVENT | DND_INDROP | DND_MOZDRAG;
01086       else
01087         *dragFlags |= DND_DISPATCHEVENT | DND_INDROP | DND_NATIVEDRAG;
01088 
01089     DrgDeleteDraginfoStrHandles(pdinfo);
01090     DrgFreeDraginfo(pdinfo);
01091     rv = NS_OK;
01092   }
01093 
01094   return rv;
01095 }
01096 
01097 // --------------------------------------------------------------------------
01098 
01099 // Invoked by DropMsg to fill a transferable with native data;
01100 // if necessary, requests the source to render it.
01101 
01102 NS_IMETHODIMP nsDragService::NativeDrop(PDRAGINFO pdinfo, HWND hwnd,
01103                                         PRBool* rendering)
01104 {
01105   *rendering = PR_FALSE;
01106 
01107   nsresult rv = NS_ERROR_FAILURE;
01108   PDRAGITEM pditem = DrgQueryDragitemPtr(pdinfo, 0);
01109   if (!pditem)
01110     return rv;
01111 
01112   nsXPIDLCString dropText;
01113   PRBool isUrl = DrgVerifyType(pditem, "UniformResourceLocator");
01114 
01115     // identify format; the order of evaluation here is important
01116 
01117     // DRM_ATOM - DragText stores up to 255 chars in a Drg API atom
01118     // DRM_DTSHARE - DragText renders up to 1mb to named shared mem
01119   if (DrgVerifyRMF(pditem, "DRM_ATOM", 0))
01120     rv = GetAtom(pditem->ulItemID, getter_Copies(dropText));
01121   else
01122   if (DrgVerifyRMF(pditem, "DRM_DTSHARE", 0)) {
01123     rv = RenderToDTShare( pditem, hwnd);
01124     if (NS_SUCCEEDED(rv))
01125       *rendering = PR_TRUE;
01126   }
01127 
01128     // DRM_OS2FILE - get the file's path or contents if it exists;
01129     // request rendering if it doesn't
01130   else
01131   if (DrgVerifyRMF(pditem, "DRM_OS2FILE", 0)) {
01132     PRBool isAlt = (WinGetKeyState(HWND_DESKTOP, VK_ALT) & 0x8000);
01133 
01134       // the file has to be rendered - currently, we only present
01135       // its content, not its name, to Moz to avoid conflicts
01136     if (!pditem->hstrContainerName || !pditem->hstrSourceName) {
01137       rv = RenderToOS2File( pditem, hwnd);
01138       if (NS_SUCCEEDED(rv))
01139         *rendering = PR_TRUE;
01140     }
01141       // for Url objects and 'Alt+Drop', get the file's contents;
01142       // otherwise, convert it's path to a Url
01143     else {
01144       nsXPIDLCString fileName;
01145       if (NS_SUCCEEDED(GetFileName(pditem, getter_Copies(fileName)))) {
01146         if (isUrl || isAlt)
01147           rv = GetFileContents(fileName.get(), getter_Copies(dropText));
01148         else {
01149           isUrl = PR_TRUE;
01150           nsCOMPtr<nsILocalFile> file;
01151           if (NS_SUCCEEDED(NS_NewNativeLocalFile(fileName,
01152                                          PR_TRUE, getter_AddRefs(file)))) {
01153             nsCAutoString textStr;
01154             NS_GetURLSpecFromFile(file, textStr);
01155             if (!textStr.IsEmpty()) {
01156               dropText.Assign(ToNewCString(textStr));
01157               rv = NS_OK;
01158             }
01159           }
01160         } // filename as Url
01161       } // got filename
01162     } // existing files
01163   } // DRM_OS2FILE
01164 
01165     // if OK, put what data there is in the transferable;  this could be
01166     // everything needed or just the title of a Url that needs rendering
01167   if (NS_SUCCEEDED(rv)) {
01168 
01169       // for Urls, get the title & remove any linefeeds
01170     nsXPIDLCString titleText;
01171     if (isUrl &&
01172         pditem->hstrTargetName &&
01173         NS_SUCCEEDED(GetAtom(pditem->hstrTargetName, getter_Copies(titleText))))
01174       for (char* ptr=strchr(titleText.BeginWriting(),'\n'); ptr; ptr=strchr(ptr, '\n'))
01175         *ptr = ' ';
01176 
01177     rv = NativeDataToTransferable( dropText.get(), titleText.get(), isUrl);
01178   }
01179 
01180     // except for renderings, tell the source we're done with the data
01181   if (!*rendering)
01182     DrgSendTransferMsg( pditem->hwndItem, DM_ENDCONVERSATION,
01183                         (MPARAM)pditem->ulItemID,
01184                         (MPARAM)DMFL_TARGETSUCCESSFUL);
01185 
01186   return (rv);
01187 }
01188 
01189 // --------------------------------------------------------------------------
01190 
01191 // Because RenderCompleteMsg() is called after the native (PM) drag
01192 // session has ended, all of the drag status flags should be off.
01193 //
01194 // FYI... PM's asynchronous rendering mechanism is not compatible with
01195 // nsIDataFlavorProvider which expects data to be rendered synchronously
01196 
01197 NS_IMETHODIMP nsDragService::RenderCompleteMsg(PDRAGTRANSFER pdxfer,
01198                                         USHORT usResult, PRUint32* dragFlags)
01199 {
01200   nsresult rv = NS_ERROR_FAILURE;
01201   if (!mDoingDrag || !pdxfer)
01202     return rv;
01203 
01204     // this msg should never come from Moz - if it does, fail
01205   if (!mSourceNode)
01206     rv = NativeRenderComplete(pdxfer, usResult);
01207 
01208     // DrgQueryDraginfoPtrFromDragitem() doesn't work - this does
01209   PDRAGINFO pdinfo = (PDRAGINFO)MAKEULONG(0x2c, HIUSHORT(pdxfer->pditem));
01210 
01211   DrgDeleteStrHandle(pdxfer->hstrSelectedRMF);
01212   DrgDeleteStrHandle(pdxfer->hstrRenderToName);
01213   DrgFreeDragtransfer(pdxfer);
01214 
01215     // if the source is Moz, don't touch pdinfo - it's been freed already
01216   if (pdinfo && !mSourceNode) {
01217     DrgDeleteDraginfoStrHandles(pdinfo);
01218     DrgFreeDraginfo(pdinfo);
01219   }
01220 
01221     // this shouldn't happen
01222   if (!dragFlags)
01223     return (ExitSession(dragFlags));
01224 
01225     // d&d is over, so the DND_DragStatus flags should all be off
01226   *dragFlags = DND_EXITSESSION;
01227   if (NS_SUCCEEDED(rv))
01228     *dragFlags |= DND_DISPATCHEVENT;
01229 
01230     // lie so the client will honor the exit-session flag
01231   return NS_OK;
01232 }
01233 
01234 // --------------------------------------------------------------------------
01235 
01236 // this is here to provide a false sense of symmetry with the other
01237 // method-pairs - rendered data always comes from a native source
01238 
01239 NS_IMETHODIMP nsDragService::NativeRenderComplete(PDRAGTRANSFER pdxfer,
01240                                                   USHORT usResult)
01241 {
01242   nsresult rv = NS_ERROR_FAILURE;
01243   nsXPIDLCString rmf;
01244 
01245     // identify the rendering mechanism, then get the data
01246   if (NS_SUCCEEDED(GetAtom(pdxfer->hstrSelectedRMF, getter_Copies(rmf)))) {
01247     nsXPIDLCString dropText;
01248     if (!strcmp(rmf.get(), DTSHARE_RMF))
01249       rv = RenderToDTShareComplete(pdxfer, usResult, getter_Copies(dropText));
01250     else
01251     if (!strcmp(rmf.get(), OS2FILE_TXTRMF) ||
01252         !strcmp(rmf.get(), OS2FILE_UNKRMF))
01253       rv = RenderToOS2FileComplete(pdxfer, usResult, PR_TRUE,
01254                                    getter_Copies(dropText));
01255 
01256     if (NS_SUCCEEDED(rv)) {
01257       PRBool isUrl = PR_FALSE;
01258       IsDataFlavorSupported(kURLMime, &isUrl);
01259       rv = NativeDataToTransferable( dropText.get(), 0, isUrl);
01260     }
01261   }
01262 
01263   DrgSendTransferMsg(pdxfer->hwndClient, DM_ENDCONVERSATION,
01264                      (MPARAM)pdxfer->ulTargetInfo,
01265                      (MPARAM)DMFL_TARGETSUCCESSFUL);
01266 
01267   return rv;
01268 }
01269 
01270 // --------------------------------------------------------------------------
01271 
01272 // fills the transferable created by NativeDragEnter with
01273 // the set of flavors and data the target will see onDrop
01274 
01275 NS_IMETHODIMP nsDragService::NativeDataToTransferable( PCSZ pszText,
01276                                                 PCSZ pszTitle, PRBool isUrl)
01277 {
01278   nsresult rv = NS_ERROR_FAILURE;
01279     // the transferable should have been created on DragEnter
01280  if (!mSourceDataItems)
01281     return rv;
01282 
01283   nsCOMPtr<nsISupports> genericItem;
01284   mSourceDataItems->GetElementAt(0, getter_AddRefs(genericItem));
01285   nsCOMPtr<nsITransferable> trans (do_QueryInterface(genericItem));
01286   if (!trans)
01287     return rv;
01288 
01289     // remove invalid flavors
01290   if (!isUrl) {
01291     trans->RemoveDataFlavor(kURLMime);
01292     trans->RemoveDataFlavor(kHTMLMime);
01293   }
01294 
01295     // if there's no text, exit - but first see if we have the title of
01296     // a Url that needs to be rendered;  if so, stash it for later use
01297   if (!pszText || !*pszText) {
01298     if (isUrl && pszTitle && *pszTitle) {
01299       nsXPIDLString outTitle;
01300       if (CodepageToUnicode(nsDependentCString(pszTitle),
01301                                                getter_Copies(outTitle))) {
01302         nsCOMPtr<nsISupportsString> urlPrimitive(
01303                         do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
01304         if (urlPrimitive ) {
01305           urlPrimitive->SetData(outTitle);
01306           trans->SetTransferData(kURLDescriptionMime, urlPrimitive,
01307                                  2*outTitle.Length());
01308         }
01309       }
01310     }
01311     return NS_OK;
01312   }
01313 
01314   nsXPIDLString outText;
01315   if (!CodepageToUnicode(nsDependentCString(pszText), getter_Copies(outText)))
01316     return rv;
01317 
01318   if (isUrl) {
01319 
01320       // if no title was supplied, see if it was stored in the transferable
01321     nsXPIDLString outTitle;
01322     if (pszTitle && *pszTitle) {
01323       if (!CodepageToUnicode(nsDependentCString(pszTitle),
01324                              getter_Copies(outTitle)))
01325         return rv;
01326     }
01327     else {
01328       PRUint32 len;
01329       nsCOMPtr<nsISupports> genericData;
01330       if (NS_SUCCEEDED(trans->GetTransferData(kURLDescriptionMime,
01331                                    getter_AddRefs(genericData), &len))) {
01332         nsCOMPtr<nsISupportsString> strObject(do_QueryInterface(genericData));
01333         if (strObject)
01334           strObject->GetData(outTitle);
01335       }
01336     }
01337 
01338       // construct the Url flavor
01339     nsCOMPtr<nsISupportsString> urlPrimitive(
01340                             do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
01341     if (urlPrimitive ) {
01342       if (outTitle.IsEmpty()) {
01343         urlPrimitive->SetData(outText);
01344         trans->SetTransferData(kURLMime, urlPrimitive, 2*outText.Length());
01345       }
01346       else {
01347         nsString urlStr( outText + NS_LITERAL_STRING("\n") + outTitle);
01348         urlPrimitive->SetData(urlStr);
01349         trans->SetTransferData(kURLMime, urlPrimitive, 2*urlStr.Length());
01350       }
01351       rv = NS_OK;
01352     }
01353 
01354       // construct the HTML flavor - for supported graphics,
01355       // use an IMG tag, for all others create a link
01356     nsCOMPtr<nsISupportsString> htmlPrimitive(
01357                             do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
01358     if (htmlPrimitive ) {
01359       nsString htmlStr;
01360       nsCOMPtr<nsIURI> uri;
01361 
01362       rv = NS_ERROR_FAILURE;
01363       if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(uri), pszText))) {
01364         nsCOMPtr<nsIURL> url (do_QueryInterface(uri));
01365         if (url) {
01366           nsCAutoString extension;
01367           url->GetFileExtension(extension);
01368           if (!extension.IsEmpty()) {
01369             if (extension.LowerCaseEqualsLiteral("gif") ||
01370                 extension.LowerCaseEqualsLiteral("jpg") ||
01371                 extension.LowerCaseEqualsLiteral("png") ||
01372                 extension.LowerCaseEqualsLiteral("jpeg"))
01373               rv = NS_OK;
01374           }
01375         }
01376       }
01377 
01378       if (NS_SUCCEEDED(rv))
01379         htmlStr.Assign(NS_LITERAL_STRING("<img src=\"") +
01380                        outText +
01381                        NS_LITERAL_STRING("\" alt=\"") +
01382                        outTitle +
01383                        NS_LITERAL_STRING("\"/>") );
01384       else
01385         htmlStr.Assign(NS_LITERAL_STRING("<a href=\"") +
01386                        outText +
01387                        NS_LITERAL_STRING("\">") +
01388                        (outTitle.IsEmpty() ? outText : outTitle) +
01389                        NS_LITERAL_STRING("</a>") );
01390 
01391       htmlPrimitive->SetData(htmlStr);
01392       trans->SetTransferData(kHTMLMime, htmlPrimitive, 2*htmlStr.Length());
01393       rv = NS_OK;
01394     }
01395   }
01396 
01397     // add the Text flavor
01398   nsCOMPtr<nsISupportsString> textPrimitive(
01399                             do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
01400   if (textPrimitive ) {
01401     textPrimitive->SetData(nsDependentString(outText));
01402     trans->SetTransferData(kUnicodeMime, textPrimitive, 2*outText.Length());
01403     rv = NS_OK;
01404   }
01405 
01406     // return OK if we put anything in the transferable
01407   return rv;
01408 }
01409 
01410 // --------------------------------------------------------------------------
01411 // Helper functions
01412 // --------------------------------------------------------------------------
01413 
01414 // currently, the same filename is used for every render request;
01415 // it is deleted when the drag session ends
01416 
01417 nsresult RenderToOS2File( PDRAGITEM pditem, HWND hwnd)
01418 {
01419   nsresult rv = NS_ERROR_FAILURE;
01420   nsXPIDLCString fileName;
01421 
01422   if (NS_SUCCEEDED(GetTempFileName(getter_Copies(fileName)))) {
01423     char * pszRMF;
01424     if (DrgVerifyRMF(pditem, "DRM_OS2FILE", "DRF_TEXT"))
01425       pszRMF = OS2FILE_TXTRMF;
01426     else
01427       pszRMF = OS2FILE_UNKRMF;
01428 
01429     rv = RequestRendering( pditem, hwnd, pszRMF, fileName.get());
01430   }
01431 
01432   return rv;
01433 }
01434 
01435 // --------------------------------------------------------------------------
01436 
01437 // return a buffer with the rendered file's Url or contents
01438 
01439 nsresult RenderToOS2FileComplete(PDRAGTRANSFER pdxfer, USHORT usResult,
01440                                  PRBool content, char** outText)
01441 {
01442   nsresult rv = NS_ERROR_FAILURE;
01443 
01444     // for now, override content flag & always return content
01445   content = PR_TRUE;
01446 
01447   if (usResult & DMFL_RENDEROK) {
01448     if (NS_SUCCEEDED(GetAtom( pdxfer->hstrRenderToName, &gTempFile))) {
01449       if (content)
01450         rv = GetFileContents(gTempFile, outText);
01451       else {
01452         nsCOMPtr<nsILocalFile> file;
01453         if (NS_SUCCEEDED(NS_NewNativeLocalFile(nsDependentCString(gTempFile),
01454                                          PR_TRUE, getter_AddRefs(file)))) {
01455           nsCAutoString textStr;
01456           NS_GetURLSpecFromFile(file, textStr);
01457           if (!textStr.IsEmpty()) {
01458             *outText = ToNewCString(textStr);
01459             rv = NS_OK;
01460           }
01461         }
01462       }
01463     }
01464   }
01465     // gTempFile will be deleted when ExitSession() is called
01466 
01467   return rv;
01468 }
01469 
01470 // --------------------------------------------------------------------------
01471 
01472 // DTShare uses 1mb of uncommitted named-shared memory
01473 // (next time I'll do it differently - rw)
01474 
01475 nsresult RenderToDTShare( PDRAGITEM pditem, HWND hwnd)
01476 {
01477   nsresult rv;
01478   void *   pMem;
01479 
01480   APIRET rc = DosAllocSharedMem( &pMem, DTSHARE_NAME, 0x100000,
01481                                  PAG_WRITE | PAG_READ);
01482   if (rc == ERROR_ALREADY_EXISTS)
01483     rc = DosGetNamedSharedMem( &pMem, DTSHARE_NAME,
01484                                PAG_WRITE | PAG_READ);
01485   if (rc)
01486     rv = NS_ERROR_FAILURE;
01487   else
01488     rv = RequestRendering( pditem, hwnd, DTSHARE_RMF, DTSHARE_NAME);
01489 
01490   return rv;
01491 }
01492 
01493 // --------------------------------------------------------------------------
01494 
01495 // return a buffer with the rendered text
01496 
01497 nsresult RenderToDTShareComplete(PDRAGTRANSFER pdxfer, USHORT usResult,
01498                                  char** outText)
01499 {
01500   nsresult rv = NS_ERROR_FAILURE;
01501   void * pMem;
01502   char * pszText = 0;
01503 
01504   APIRET rc = DosGetNamedSharedMem( &pMem, DTSHARE_NAME, PAG_WRITE | PAG_READ);
01505 
01506   if (!rc) {
01507     if (usResult & DMFL_RENDEROK) {
01508       pszText = (char*)nsMemory::Alloc( ((ULONG*)pMem)[0] + 1);
01509       if (pszText) {
01510         strcpy(pszText, &((char*)pMem)[sizeof(ULONG)] );
01511         RemoveCarriageReturns(pszText);
01512         *outText = pszText;
01513         rv = NS_OK;
01514       }
01515     }
01516       // using DosGetNamedSharedMem() on memory we allocated appears
01517       // to increment its usage ctr, so we have to free it 2x
01518     DosFreeMem(pMem);
01519     DosFreeMem(pMem);
01520   }
01521 
01522   return rv;
01523 }
01524 
01525 // --------------------------------------------------------------------------
01526 
01527 // a generic request dispatcher
01528 
01529 nsresult RequestRendering( PDRAGITEM pditem, HWND hwnd, PCSZ pRMF, PCSZ pName)
01530 {
01531   PDRAGTRANSFER pdxfer = DrgAllocDragtransfer( 1);
01532   if (!pdxfer)
01533     return NS_ERROR_FAILURE;
01534  
01535   pdxfer->cb = sizeof(DRAGTRANSFER);
01536   pdxfer->hwndClient = hwnd;
01537   pdxfer->pditem = pditem;
01538   pdxfer->hstrSelectedRMF = DrgAddStrHandle( pRMF);
01539   pdxfer->hstrRenderToName = 0;
01540   pdxfer->ulTargetInfo = pditem->ulItemID;
01541   pdxfer->usOperation = (USHORT)DO_COPY;
01542   pdxfer->fsReply = 0;
01543  
01544     // send the msg before setting a render-to name
01545   if (pditem->fsControl & DC_PREPAREITEM)
01546     DrgSendTransferMsg( pditem->hwndItem, DM_RENDERPREPARE, (MPARAM)pdxfer, 0);
01547  
01548   pdxfer->hstrRenderToName = DrgAddStrHandle( pName);
01549  
01550     // send the msg after setting a render-to name
01551   if ((pditem->fsControl & (DC_PREPARE | DC_PREPAREITEM)) == DC_PREPARE)
01552     DrgSendTransferMsg( pditem->hwndItem, DM_RENDERPREPARE, (MPARAM)pdxfer, 0);
01553  
01554     // ask the source to render the selected item
01555   if (!DrgSendTransferMsg( pditem->hwndItem, DM_RENDER, (MPARAM)pdxfer, 0))
01556     return NS_ERROR_FAILURE;
01557  
01558   return NS_OK;
01559 }
01560 
01561 // --------------------------------------------------------------------------
01562 
01563 // return a ptr to a buffer containing the text associated
01564 // with a drag atom;  the caller frees the buffer
01565 
01566 nsresult GetAtom( ATOM aAtom, char** outText)
01567 {
01568   nsresult rv = NS_ERROR_FAILURE;
01569 
01570   ULONG ulInLength = DrgQueryStrNameLen(aAtom);
01571   if (ulInLength) {
01572     char* pszText = (char*)nsMemory::Alloc(++ulInLength);
01573     if (pszText) {
01574       DrgQueryStrName(aAtom, ulInLength, pszText);
01575       RemoveCarriageReturns(pszText);
01576       *outText = pszText;
01577       rv = NS_OK;
01578     }
01579   }
01580   return rv;
01581 }
01582 
01583 // --------------------------------------------------------------------------
01584 
01585 // return a ptr to a buffer containing the file path specified
01586 // in the dragitem;  the caller frees the buffer
01587 
01588 nsresult GetFileName(PDRAGITEM pditem, char** outText)
01589 {
01590   nsresult rv = NS_ERROR_FAILURE;
01591   ULONG cntCnr = DrgQueryStrNameLen(pditem->hstrContainerName);
01592   ULONG cntSrc = DrgQueryStrNameLen(pditem->hstrSourceName);
01593 
01594   char* pszText = (char*)nsMemory::Alloc(cntCnr+cntSrc+1);
01595   if (pszText) {
01596     DrgQueryStrName(pditem->hstrContainerName, cntCnr+1, pszText);
01597     DrgQueryStrName(pditem->hstrSourceName, cntSrc+1, &pszText[cntCnr]);
01598     pszText[cntCnr+cntSrc] = 0;
01599     *outText = pszText;
01600     rv = NS_OK;
01601   }
01602   return rv;
01603 }
01604 
01605 // --------------------------------------------------------------------------
01606 
01607 // read the file;  if successful, return a ptr to its contents;
01608 // the caller frees the buffer
01609 
01610 nsresult GetFileContents(PCSZ pszPath, char** outText)
01611 {
01612   nsresult rv = NS_ERROR_FAILURE;
01613   char* pszText = 0;
01614 
01615   if (pszPath) {
01616     FILE *fp = fopen(pszPath, "r");
01617     if (fp) {
01618       fseek(fp, 0, SEEK_END);
01619       ULONG filesize = ftell(fp);
01620       fseek(fp, 0, SEEK_SET);
01621       if (filesize > 0) {
01622         size_t readsize = (size_t)filesize;
01623         pszText = (char*)nsMemory::Alloc(readsize+1);
01624         if (pszText) {
01625           readsize = fread((void *)pszText, 1, readsize, fp);
01626           if (readsize) {
01627             pszText[readsize] = '\0';
01628             RemoveCarriageReturns(pszText);
01629             *outText = pszText;
01630             rv = NS_OK;
01631           }
01632           else {
01633             nsMemory::Free(pszText);
01634             pszText = 0;
01635           }
01636         }
01637       }
01638       fclose(fp);
01639     }
01640   }
01641 
01642   return rv;
01643 }
01644 
01645 // --------------------------------------------------------------------------
01646 
01647 // currently, this returns the same path & filename every time
01648 
01649 nsresult GetTempFileName(char** outText)
01650 {
01651   char * pszText = (char*)nsMemory::Alloc(CCHMAXPATH);
01652   if (!pszText)
01653     return NS_ERROR_FAILURE;
01654 
01655   char * pszPath;
01656   if (!DosScanEnv("TEMP", &pszPath) || !DosScanEnv("TMP", &pszPath))
01657     strcpy(pszText, pszPath);
01658   else
01659     if (DosQueryPathInfo(".\\.", FIL_QUERYFULLNAME, pszText, CCHMAXPATH))
01660       pszText[0] = 0;
01661 
01662   strcat(pszText, "\\");
01663   strcat(pszText, OS2FILE_NAME);
01664   *outText = pszText;
01665 
01666   return NS_OK;
01667 }
01668 
01669 // --------------------------------------------------------------------------
01670 // --------------------------------------------------------------------------
01671 
01672 // set the file's .TYPE and .SUBJECT EAs;  since this is non-critical
01673 // (though highly desirable), errors aren't reported
01674 
01675 void    SaveTypeAndSource(nsILocalFile * file, nsIDOMDocument * domDoc,
01676                           PCSZ pszType)
01677 {
01678   if (!file)
01679     return;
01680 
01681   nsCOMPtr<nsILocalFileOS2> os2file (do_QueryInterface(file));
01682   if (!os2file ||
01683       NS_FAILED(os2file->SetFileTypes(nsDependentCString(pszType))))
01684     return;
01685 
01686   // since the filetype has to be saved, this function
01687   // may be called even if there isn't any document
01688   if (!domDoc)
01689     return;
01690 
01691   nsCOMPtr<nsIDocument> doc (do_QueryInterface(domDoc));
01692   if (!doc)
01693     return;
01694 
01695   // this gets the top-level content URL when frames are used;
01696   // when nextDoc is zero, currDoc is the browser window, and
01697   // prevDoc points to the content;
01698   // note:  neither GetParentDocument() nor GetDocumentURI()
01699   // refcount the pointers they return, so nsCOMPtr isn't needed
01700   nsIDocument * prevDoc;
01701   nsIDocument * currDoc = doc;
01702   nsIDocument * nextDoc = doc;
01703   do {
01704     prevDoc = currDoc;
01705     currDoc = nextDoc;
01706     nextDoc = currDoc->GetParentDocument();
01707   } while (nextDoc);
01708 
01709   nsIURI* srcUri = prevDoc->GetDocumentURI();
01710   if (!srcUri)
01711     return;
01712 
01713   // identifying content as coming from chrome is none too useful...
01714   PRBool ignore = PR_FALSE;
01715   srcUri->SchemeIs("chrome", &ignore);
01716   if (ignore)
01717     return;
01718 
01719   nsCAutoString url;
01720   srcUri->GetSpec(url);
01721   os2file->SetFileSource(url);
01722 
01723   return;
01724 }
01725 
01726 // --------------------------------------------------------------------------
01727 
01728 // to do:  this needs to take into account the current page's encoding
01729 // if it is different than the PM codepage
01730 
01731 int UnicodeToCodepage(const nsAString& aString, char **aResult)
01732 {
01733   nsAutoCharBuffer buffer;
01734   PRInt32 bufLength;
01735   WideCharToMultiByte(0, PromiseFlatString(aString).get(), aString.Length(),
01736                       buffer, bufLength);
01737   *aResult = ToNewCString(nsDependentCString(buffer.get()));
01738   return bufLength;
01739 }
01740 
01741 // --------------------------------------------------------------------------
01742 
01743 int CodepageToUnicode(const nsACString& aString, PRUnichar **aResult)
01744 {
01745   nsAutoChar16Buffer buffer;
01746   PRInt32 bufLength;
01747   MultiByteToWideChar(0, PromiseFlatCString(aString).get(),
01748                       aString.Length(), buffer, bufLength);
01749   *aResult = ToNewUnicode(nsDependentString(buffer.get()));
01750   return bufLength;
01751 }
01752 
01753 // --------------------------------------------------------------------------
01754 
01755 // removes carriage returns in-place;  it should only be used on
01756 // raw text buffers that haven't been assigned to a string object
01757 
01758 void RemoveCarriageReturns(char * pszText)
01759 {
01760   ULONG  cnt;
01761   char * next;
01762   char * source;
01763   char * target;
01764 
01765   target = strchr(pszText, 0x0d);
01766   if (!target)
01767     return;
01768 
01769   source = target + 1;
01770 
01771   while ((next = strchr(source, 0x0d)) != 0) {
01772 
01773     cnt = next - source;
01774     memcpy(target, source, cnt);
01775     target += cnt;
01776     source = next + 1;
01777 
01778   }
01779 
01780   strcpy(target, source);
01781   return;
01782 }
01783 
01784 // --------------------------------------------------------------------------