Bug 867104 - Add a crashtest. r=ehsan
[gecko.git] / widget / os2 / nsDragService.cpp
blob28299efa19128bdfe09d5cfde4ae526e08785f95
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #define INCL_DOSMISC
6 #define INCL_DOSERRORS
8 #include "nsDragService.h"
9 #include "nsXPCOM.h"
10 #include "nsISupportsPrimitives.h"
11 #include "nsString.h"
12 #include "nsXPIDLString.h"
13 #include "nsReadableUtils.h"
14 #include "nsIWebBrowserPersist.h"
15 #include "nsIFile.h"
16 #include "nsIURI.h"
17 #include "nsIURL.h"
18 #include "nsNetUtil.h"
19 #include "nsOS2Uni.h"
20 #include "wdgtos2rc.h"
21 #include "nsILocalFileOS2.h"
22 #include "nsIDocument.h"
23 #include "nsGUIEvent.h"
24 #include "nsISelection.h"
25 #include <algorithm>
27 // --------------------------------------------------------------------------
28 // Local defines
30 // undocumented(?)
31 #ifndef DC_PREPAREITEM
32 #define DC_PREPAREITEM 0x0040
33 #endif
35 // limit URL object titles to a reasonable length
36 #define MAXTITLELTH 31
37 #define TITLESEPARATOR (L' ')
39 #define DTSHARE_NAME "\\SHAREMEM\\MOZ_DND"
40 #define DTSHARE_RMF "<DRM_DTSHARE, DRF_TEXT>"
42 #define OS2FILE_NAME "MOZ_TGT.TMP"
43 #define OS2FILE_TXTRMF "<DRM_OS2FILE, DRF_TEXT>"
44 #define OS2FILE_UNKRMF "<DRM_OS2FILE, DRF_UNKNOWN>"
46 // not defined in the OS/2 toolkit headers
47 extern "C" {
48 APIRET APIENTRY DosQueryModFromEIP(HMODULE *phMod, ULONG *pObjNum,
49 ULONG BuffLen, PCHAR pBuff,
50 ULONG *pOffset, ULONG Address);
53 // --------------------------------------------------------------------------
54 // Helper functions
56 nsresult RenderToOS2File( PDRAGITEM pditem, HWND hwnd);
57 nsresult RenderToOS2FileComplete(PDRAGTRANSFER pdxfer, USHORT usResult,
58 bool content, char** outText);
59 nsresult RenderToDTShare( PDRAGITEM pditem, HWND hwnd);
60 nsresult RenderToDTShareComplete(PDRAGTRANSFER pdxfer, USHORT usResult,
61 char** outText);
62 nsresult RequestRendering( PDRAGITEM pditem, HWND hwnd, PCSZ pRMF, PCSZ pName);
63 nsresult GetAtom( ATOM aAtom, char** outText);
64 nsresult GetFileName(PDRAGITEM pditem, char** outText);
65 nsresult GetFileContents(PCSZ pszPath, char** outText);
66 nsresult GetTempFileName(char** outText);
67 void SaveTypeAndSource(nsIFile *file, nsIDOMDocument *domDoc,
68 PCSZ pszType);
69 int UnicodeToCodepage( const nsAString& inString, char **outText);
70 int CodepageToUnicode( const nsACString& inString, PRUnichar **outText);
71 void RemoveCarriageReturns(char * pszText);
72 MRESULT EXPENTRY nsDragWindowProc(HWND hWnd, ULONG msg, MPARAM mp1, MPARAM mp2);
74 // --------------------------------------------------------------------------
75 // Global data
77 static HPOINTER gPtrArray[IDC_DNDCOUNT];
78 static char * gTempFile = 0;
80 // --------------------------------------------------------------------------
81 // --------------------------------------------------------------------------
83 nsDragService::nsDragService()
85 // member initializers and constructor code
86 mDragWnd = WinCreateWindow( HWND_DESKTOP, WC_STATIC, 0, 0, 0, 0, 0, 0,
87 HWND_DESKTOP, HWND_BOTTOM, 0, 0, 0);
88 WinSubclassWindow( mDragWnd, nsDragWindowProc);
90 HMODULE hModResources = NULLHANDLE;
91 DosQueryModFromEIP(&hModResources, NULL, 0, NULL, NULL, (ULONG) &gPtrArray);
92 for (int i = 0; i < IDC_DNDCOUNT; i++)
93 gPtrArray[i] = ::WinLoadPointer(HWND_DESKTOP, hModResources, i+IDC_DNDBASE);
96 // --------------------------------------------------------------------------
98 nsDragService::~nsDragService()
100 // destructor code
101 WinDestroyWindow(mDragWnd);
103 for (int i = 0; i < IDC_DNDCOUNT; i++) {
104 WinDestroyPointer(gPtrArray[i]);
105 gPtrArray[i] = 0;
109 NS_IMPL_ISUPPORTS_INHERITED1(nsDragService, nsBaseDragService, nsIDragSessionOS2)
111 // --------------------------------------------------------------------------
113 NS_IMETHODIMP nsDragService::InvokeDragSession(nsIDOMNode *aDOMNode,
114 nsISupportsArray *aTransferables,
115 nsIScriptableRegion *aRegion,
116 uint32_t aActionType)
118 if (mDoingDrag)
119 return NS_ERROR_UNEXPECTED;
121 nsresult rv = nsBaseDragService::InvokeDragSession(aDOMNode, aTransferables,
122 aRegion, aActionType);
123 NS_ENSURE_SUCCESS(rv, rv);
125 mSourceDataItems = aTransferables;
126 WinSetCapture(HWND_DESKTOP, NULLHANDLE);
128 // Assume we are only dragging one thing for now
129 PDRAGINFO pDragInfo = DrgAllocDraginfo(1);
130 if (!pDragInfo)
131 return NS_ERROR_UNEXPECTED;
133 pDragInfo->usOperation = DO_DEFAULT;
135 DRAGITEM dragitem;
136 dragitem.hwndItem = mDragWnd;
137 dragitem.ulItemID = (ULONG)this;
138 dragitem.fsControl = DC_OPEN;
139 dragitem.cxOffset = 2;
140 dragitem.cyOffset = 2;
141 dragitem.fsSupportedOps = DO_COPYABLE|DO_MOVEABLE|DO_LINKABLE;
143 // since there is no source file, leave these "blank"
144 dragitem.hstrContainerName = NULLHANDLE;
145 dragitem.hstrSourceName = NULLHANDLE;
147 rv = NS_ERROR_FAILURE;
148 ULONG idIcon = 0;
150 // bracket this to reduce our footprint before the drag begins
152 nsCOMPtr<nsISupports> genericItem;
153 mSourceDataItems->GetElementAt(0, getter_AddRefs(genericItem));
154 nsCOMPtr<nsITransferable> transItem (do_QueryInterface(genericItem));
156 nsCOMPtr<nsISupports> genericData;
157 uint32_t len = 0;
159 // see if we have a URL or text; if so, the title method
160 // will save the data and mimetype for use with a native drop
162 if (NS_SUCCEEDED(transItem->GetTransferData(kURLMime,
163 getter_AddRefs(genericData), &len))) {
164 nsXPIDLCString targetName;
165 rv = GetUrlAndTitle( genericData, getter_Copies(targetName));
166 if (NS_SUCCEEDED(rv)) {
167 // advise PM that we need a DM_RENDERPREPARE msg
168 // *before* it composes a render-to filename
169 dragitem.fsControl |= DC_PREPAREITEM;
170 dragitem.hstrType = DrgAddStrHandle("UniformResourceLocator");
171 dragitem.hstrRMF = DrgAddStrHandle("<DRM_OS2FILE,DRF_TEXT>");
172 dragitem.hstrTargetName = DrgAddStrHandle(targetName.get());
173 idIcon = IDC_DNDURL;
176 else
177 if (NS_SUCCEEDED(transItem->GetTransferData(kUnicodeMime,
178 getter_AddRefs(genericData), &len))) {
179 nsXPIDLCString targetName;
180 rv = GetUniTextTitle( genericData, getter_Copies(targetName));
181 if (NS_SUCCEEDED(rv)) {
182 dragitem.hstrType = DrgAddStrHandle("Plain Text");
183 dragitem.hstrRMF = DrgAddStrHandle("<DRM_OS2FILE,DRF_TEXT>");
184 dragitem.hstrTargetName = DrgAddStrHandle(targetName.get());
185 idIcon = IDC_DNDTEXT;
190 // if neither URL nor text are available, make this a Moz-only drag
191 // by making it unidentifiable to native apps
192 if (NS_FAILED(rv)) {
193 mMimeType = 0;
194 dragitem.hstrType = DrgAddStrHandle("Unknown");
195 dragitem.hstrRMF = DrgAddStrHandle("<DRM_UNKNOWN,DRF_UNKNOWN>");
196 dragitem.hstrTargetName = NULLHANDLE;
198 DrgSetDragitem(pDragInfo, &dragitem, sizeof(DRAGITEM), 0);
200 DRAGIMAGE dragimage;
201 memset(&dragimage, 0, sizeof(DRAGIMAGE));
202 dragimage.cb = sizeof(DRAGIMAGE);
203 dragimage.fl = DRG_ICON;
204 if (idIcon)
205 dragimage.hImage = gPtrArray[idIcon-IDC_DNDBASE];
206 if (dragimage.hImage) {
207 dragimage.cyOffset = 8;
208 dragimage.cxOffset = 2;
210 else
211 dragimage.hImage = WinQuerySysPointer(HWND_DESKTOP, SPTR_FILE, FALSE);
213 mDoingDrag = true;
214 LONG escState = WinGetKeyState(HWND_DESKTOP, VK_ESC) & 0x01;
215 HWND hwndDest = DrgDrag(mDragWnd, pDragInfo, &dragimage, 1, VK_BUTTON2,
216 (void*)0x80000000L); // Don't lock the desktop PS
218 // determine whether the drag ended because Escape was pressed
219 if (hwndDest == 0 && (WinGetKeyState(HWND_DESKTOP, VK_ESC) & 0x01) != escState)
220 mUserCancelled = true;
221 FireDragEventAtSource(NS_DRAGDROP_END);
222 mDoingDrag = false;
224 // do clean up; if the drop completed,
225 // the target will delete the string handles
226 if (hwndDest == 0)
227 DrgDeleteDraginfoStrHandles(pDragInfo);
228 DrgFreeDraginfo(pDragInfo);
230 // reset nsDragService's members
231 mSourceDataItems = 0;
232 mSourceData = 0;
233 mMimeType = 0;
235 // reset nsBaseDragService's members
236 mSourceDocument = nullptr;
237 mSourceNode = nullptr;
238 mSelection = nullptr;
239 mDataTransfer = nullptr;
240 mUserCancelled = false;
241 mHasImage = false;
242 mImage = nullptr;
243 mImageX = 0;
244 mImageY = 0;
245 mScreenX = -1;
246 mScreenY = -1;
248 return NS_OK;
251 // --------------------------------------------------------------------------
253 MRESULT EXPENTRY nsDragWindowProc(HWND hWnd, ULONG msg, MPARAM mp1, MPARAM mp2)
255 switch (msg) {
257 // if the user requests the contents of a URL be rendered (vs the URL
258 // itself), change the suggested target name from the URL's title to
259 // the name of the file that will be retrieved
260 case DM_RENDERPREPARE: {
261 PDRAGTRANSFER pdxfer = (PDRAGTRANSFER)mp1;
262 nsDragService* dragservice = (nsDragService*)pdxfer->pditem->ulItemID;
264 if (pdxfer->usOperation == DO_COPY &&
265 (WinGetKeyState(HWND_DESKTOP, VK_CTRL) & 0x8000) &&
266 !strcmp(dragservice->mMimeType, kURLMime)) {
267 // QI'ing nsIURL will fail for mailto: and the like
268 nsCOMPtr<nsIURL> urlObject(do_QueryInterface(dragservice->mSourceData));
269 if (urlObject) {
270 nsAutoCString filename;
271 urlObject->GetFileName(filename);
272 if (filename.IsEmpty()) {
273 urlObject->GetHost(filename);
274 filename.Append("/file");
276 DrgDeleteStrHandle(pdxfer->pditem->hstrTargetName);
277 pdxfer->pditem->hstrTargetName = DrgAddStrHandle(filename.get());
280 return (MRESULT)TRUE;
283 case DM_RENDER: {
284 nsresult rv = NS_ERROR_FAILURE;
285 PDRAGTRANSFER pdxfer = (PDRAGTRANSFER)mp1;
286 nsDragService* dragservice = (nsDragService*)pdxfer->pditem->ulItemID;
287 char chPath[CCHMAXPATH];
289 DrgQueryStrName(pdxfer->hstrRenderToName, CCHMAXPATH, chPath);
291 // if the user Ctrl-dropped a URL, use the nsIURL interface
292 // to determine if it points to content (i.e. a file); if so,
293 // fetch its contents; if not (e.g. a 'mailto:' url), drop into
294 // the code that uses nsIURI to render a URL object
296 if (!strcmp(dragservice->mMimeType, kURLMime)) {
297 if (pdxfer->usOperation == DO_COPY &&
298 (WinGetKeyState(HWND_DESKTOP, VK_CTRL) & 0x8000)) {
299 nsCOMPtr<nsIURL> urlObject(do_QueryInterface(dragservice->mSourceData));
300 if (urlObject)
301 rv = dragservice->SaveAsContents(chPath, urlObject);
303 if (!NS_SUCCEEDED(rv)) {
304 nsCOMPtr<nsIURI> uriObject(do_QueryInterface(dragservice->mSourceData));
305 if (uriObject)
306 rv = dragservice->SaveAsURL(chPath, uriObject);
309 else
310 // if we're dragging text, do NLS conversion then write it to file
311 if (!strcmp(dragservice->mMimeType, kUnicodeMime)) {
312 nsCOMPtr<nsISupportsString> strObject(
313 do_QueryInterface(dragservice->mSourceData));
314 if (strObject)
315 rv = dragservice->SaveAsText(chPath, strObject);
318 DrgPostTransferMsg(pdxfer->hwndClient, DM_RENDERCOMPLETE, pdxfer,
319 (NS_SUCCEEDED(rv) ? DMFL_RENDEROK : DMFL_RENDERFAIL),
320 0, TRUE);
321 DrgFreeDragtransfer(pdxfer);
322 return (MRESULT)TRUE;
325 // we don't use these msgs but neither does WinDefWindowProc()
326 case DM_DRAGOVERNOTIFY:
327 case DM_ENDCONVERSATION:
328 return 0;
330 default:
331 break;
334 return ::WinDefWindowProc(hWnd, msg, mp1, mp2);
337 //-------------------------------------------------------------------------
339 // if the versions of Start & EndDragSession in nsBaseDragService
340 // were called (and they shouldn't be), they'd break nsIDragSessionOS2;
341 // they're overridden here and turned into no-ops to prevent this
343 NS_IMETHODIMP nsDragService::StartDragSession()
345 NS_ERROR("OS/2 version of StartDragSession() should never be called!");
346 return NS_OK;
349 NS_IMETHODIMP nsDragService::EndDragSession(bool aDragDone)
351 NS_ERROR("OS/2 version of EndDragSession() should never be called!");
352 return NS_OK;
355 // --------------------------------------------------------------------------
357 NS_IMETHODIMP nsDragService::GetNumDropItems(uint32_t *aNumDropItems)
359 if (mSourceDataItems)
360 mSourceDataItems->Count(aNumDropItems);
361 else
362 *aNumDropItems = 0;
364 return NS_OK;
367 // --------------------------------------------------------------------------
369 NS_IMETHODIMP nsDragService::GetData(nsITransferable *aTransferable,
370 uint32_t aItemIndex)
372 // make sure that we have a transferable
373 if (!aTransferable)
374 return NS_ERROR_INVALID_ARG;
376 // get flavor list that includes all acceptable flavors (including
377 // ones obtained through conversion). Flavors are nsISupportsCStrings
378 // so that they can be seen from JS.
379 nsresult rv = NS_ERROR_FAILURE;
380 nsCOMPtr<nsISupportsArray> flavorList;
381 rv = aTransferable->FlavorsTransferableCanImport(getter_AddRefs(flavorList));
382 if (NS_FAILED(rv))
383 return rv;
385 // count the number of flavors
386 uint32_t cnt;
387 flavorList->Count (&cnt);
389 for (unsigned int i= 0; i < cnt; ++i ) {
390 nsCOMPtr<nsISupports> genericWrapper;
391 flavorList->GetElementAt(i, getter_AddRefs(genericWrapper));
392 nsCOMPtr<nsISupportsCString> currentFlavor;
393 currentFlavor = do_QueryInterface(genericWrapper);
394 if (currentFlavor) {
395 nsXPIDLCString flavorStr;
396 currentFlavor->ToString(getter_Copies(flavorStr));
398 nsCOMPtr<nsISupports> genericItem;
400 mSourceDataItems->GetElementAt(aItemIndex, getter_AddRefs(genericItem));
401 nsCOMPtr<nsITransferable> item (do_QueryInterface(genericItem));
402 if (item) {
403 nsCOMPtr<nsISupports> data;
404 uint32_t tmpDataLen = 0;
405 rv = item->GetTransferData(flavorStr, getter_AddRefs(data),
406 &tmpDataLen);
407 if (NS_SUCCEEDED(rv)) {
408 rv = aTransferable->SetTransferData(flavorStr, data, tmpDataLen);
409 break;
415 return rv;
418 // --------------------------------------------------------------------------
420 // This returns true if any of the dragged items support a specified data
421 // flavor. This doesn't make a lot of sense when dragging multiple items:
422 // all of them ought to match. OTOH, Moz doesn't support multiple drag
423 // items so no problems arise. If they do, use the commented-out code to
424 // switch from "any" to "all".
426 NS_IMETHODIMP nsDragService::IsDataFlavorSupported(const char *aDataFlavor,
427 bool *_retval)
429 if (!_retval)
430 return NS_ERROR_INVALID_ARG;
432 *_retval = false;
434 uint32_t numDragItems = 0;
435 if (mSourceDataItems)
436 mSourceDataItems->Count(&numDragItems);
437 if (!numDragItems)
438 return NS_OK;
440 // return true if all items support this flavor
441 // for (uint32_t itemIndex = 0, *_retval = true;
442 // itemIndex < numDragItems && *_retval; ++itemIndex) {
443 // *_retval = false;
445 // return true if any item supports this flavor
446 for (uint32_t itemIndex = 0;
447 itemIndex < numDragItems && !(*_retval); ++itemIndex) {
449 nsCOMPtr<nsISupports> genericItem;
450 mSourceDataItems->GetElementAt(itemIndex, getter_AddRefs(genericItem));
451 nsCOMPtr<nsITransferable> currItem (do_QueryInterface(genericItem));
453 if (currItem) {
454 nsCOMPtr <nsISupportsArray> flavorList;
455 currItem->FlavorsTransferableCanExport(getter_AddRefs(flavorList));
457 if (flavorList) {
458 uint32_t numFlavors;
459 flavorList->Count( &numFlavors );
461 for (uint32_t flavorIndex=0; flavorIndex < numFlavors; ++flavorIndex) {
462 nsCOMPtr<nsISupports> genericWrapper;
463 flavorList->GetElementAt(flavorIndex, getter_AddRefs(genericWrapper));
464 nsCOMPtr<nsISupportsCString> currentFlavor;
465 currentFlavor = do_QueryInterface(genericWrapper);
467 if (currentFlavor) {
468 nsXPIDLCString flavorStr;
469 currentFlavor->ToString ( getter_Copies(flavorStr) );
470 if (strcmp(flavorStr, aDataFlavor) == 0) {
471 *_retval = true;
472 break;
475 } // for each flavor
480 return NS_OK;
483 // --------------------------------------------------------------------------
485 // use nsIWebBrowserPersist to fetch the contents of a URL
487 nsresult nsDragService::SaveAsContents(PCSZ pszDest, nsIURL* aURL)
489 nsCOMPtr<nsIURI> linkURI(do_QueryInterface(aURL));
490 if (!linkURI)
491 return NS_ERROR_FAILURE;
493 nsCOMPtr<nsIWebBrowserPersist> webPersist(
494 do_CreateInstance("@mozilla.org/embedding/browser/nsWebBrowserPersist;1"));
495 if (!webPersist)
496 return NS_ERROR_FAILURE;
498 nsCOMPtr<nsIFile> file;
499 NS_NewNativeLocalFile(nsDependentCString(pszDest), true,
500 getter_AddRefs(file));
501 if (!file)
502 return NS_ERROR_FAILURE;
504 FILE* fp;
505 if (NS_FAILED(file->OpenANSIFileDesc("wb+", &fp)))
506 return NS_ERROR_FAILURE;
508 nsCOMPtr<nsIDOMDocument> domDoc;
509 GetSourceDocument(getter_AddRefs(domDoc));
510 nsCOMPtr<nsIDocument> document = do_QueryInterface(domDoc);
512 fwrite("", 0, 1, fp);
513 fclose(fp);
514 webPersist->SaveURI(linkURI, nullptr, nullptr, nullptr, nullptr, file,
515 document->GetLoadContext());
517 return NS_OK;
520 // --------------------------------------------------------------------------
522 // save this URL in a file that the WPS will identify as a WPUrl object
524 nsresult nsDragService::SaveAsURL(PCSZ pszDest, nsIURI* aURI)
526 nsAutoCString strUri;
527 aURI->GetSpec(strUri);
529 if (strUri.IsEmpty())
530 return NS_ERROR_FAILURE;
532 nsCOMPtr<nsIFile> file;
533 NS_NewNativeLocalFile(nsDependentCString(pszDest), true,
534 getter_AddRefs(file));
535 if (!file)
536 return NS_ERROR_FAILURE;
538 FILE* fp;
539 if (NS_FAILED(file->OpenANSIFileDesc("wb+", &fp)))
540 return NS_ERROR_FAILURE;
542 fwrite(strUri.get(), strUri.Length(), 1, fp);
543 fclose(fp);
545 nsCOMPtr<nsIDOMDocument> domDoc;
546 GetSourceDocument(getter_AddRefs(domDoc));
547 SaveTypeAndSource(file, domDoc, "UniformResourceLocator");
549 return NS_OK;
552 // --------------------------------------------------------------------------
554 // save this text to file after conversion to the current codepage
556 nsresult nsDragService::SaveAsText(PCSZ pszDest, nsISupportsString* aString)
558 nsAutoString strData;
559 aString->GetData(strData);
561 if (strData.IsEmpty())
562 return NS_ERROR_FAILURE;
564 nsCOMPtr<nsIFile> file;
565 NS_NewNativeLocalFile(nsDependentCString(pszDest), true,
566 getter_AddRefs(file));
567 if (!file)
568 return NS_ERROR_FAILURE;
570 nsXPIDLCString textStr;
571 int cnt = UnicodeToCodepage(strData, getter_Copies(textStr));
572 if (!cnt)
573 return NS_ERROR_FAILURE;
575 FILE* fp;
576 if (NS_FAILED(file->OpenANSIFileDesc("wb+", &fp)))
577 return NS_ERROR_FAILURE;
579 fwrite(textStr.get(), cnt, 1, fp);
580 fclose(fp);
582 nsCOMPtr<nsIDOMDocument> domDoc;
583 GetSourceDocument(getter_AddRefs(domDoc));
584 SaveTypeAndSource(file, domDoc, "Plain Text");
586 return NS_OK;
589 // --------------------------------------------------------------------------
591 // Split a Moz Url/Title into its components, save the Url for use by
592 // a native drop, then compose a title.
594 nsresult nsDragService::GetUrlAndTitle(nsISupports *aGenericData,
595 char **aTargetName)
597 // get the URL/title string
598 nsCOMPtr<nsISupportsString> strObject ( do_QueryInterface(aGenericData));
599 if (!strObject)
600 return NS_ERROR_FAILURE;
601 nsAutoString strData;
602 strObject->GetData(strData);
604 // split string into URL and Title -
605 // if there's a title but no URL, there's no reason to continue
606 int32_t lineIndex = strData.FindChar ('\n');
607 if (lineIndex == 0)
608 return NS_ERROR_FAILURE;
610 // get the URL portion of the text
611 nsAutoString strUrl;
612 if (lineIndex == -1)
613 strUrl = strData;
614 else
615 strData.Left(strUrl, lineIndex);
617 // save the URL for later use
618 nsCOMPtr<nsIURI> saveURI;
619 NS_NewURI(getter_AddRefs(saveURI), strUrl);
620 if (!saveURI)
621 return NS_ERROR_FAILURE;
623 // if there's a bona-fide title & it isn't just a copy of the URL,
624 // limit it to a reasonable length, perform NLS conversion, then return
626 if (++lineIndex && lineIndex != (int)strData.Length() &&
627 !strUrl.Equals(Substring(strData, lineIndex, strData.Length()))) {
628 uint32_t strLth = std::min((int)strData.Length()-lineIndex, MAXTITLELTH);
629 nsAutoString strTitle;
630 strData.Mid(strTitle, lineIndex, strLth);
631 if (!UnicodeToCodepage(strTitle, aTargetName))
632 return NS_ERROR_FAILURE;
634 mSourceData = do_QueryInterface(saveURI);
635 mMimeType = kURLMime;
636 return NS_OK;
639 // if the URI can be handled as a URL, construct a title from
640 // the hostname & filename; if not, use the first MAXTITLELTH
641 // characters that appear after the scheme name
643 nsAutoCString strTitle;
645 nsCOMPtr<nsIURL> urlObj( do_QueryInterface(saveURI));
646 if (urlObj) {
647 nsAutoCString strFile;
649 urlObj->GetHost(strTitle);
650 urlObj->GetFileName(strFile);
651 if (!strFile.IsEmpty()) {
652 strTitle.AppendLiteral("/");
653 strTitle.Append(strFile);
655 else {
656 urlObj->GetDirectory(strFile);
657 if (strFile.Length() > 1) {
658 nsAutoCString::const_iterator start, end, curr;
659 strFile.BeginReading(start);
660 strFile.EndReading(end);
661 strFile.EndReading(curr);
662 for (curr.advance(-2); curr != start; --curr)
663 if (*curr == '/')
664 break;
665 strTitle.Append(Substring(curr, end));
669 else {
670 saveURI->GetSpec(strTitle);
671 int32_t index = strTitle.FindChar (':');
672 if (index != -1) {
673 if ((strTitle.get())[++index] == '/')
674 if ((strTitle.get())[++index] == '/')
675 ++index;
676 strTitle.Cut(0, index);
678 if (strTitle.Length() > MAXTITLELTH)
679 strTitle.Truncate(MAXTITLELTH);
682 *aTargetName = ToNewCString(strTitle);
684 mSourceData = do_QueryInterface(saveURI);
685 mMimeType = kURLMime;
686 return NS_OK;
689 // --------------------------------------------------------------------------
691 // Construct a title for text drops from the leading words of the text.
692 // Alphanumeric characters are copied to the title; sequences of
693 // non-alphanums are replaced by a single space
695 nsresult nsDragService::GetUniTextTitle(nsISupports *aGenericData,
696 char **aTargetName)
698 // get the string
699 nsCOMPtr<nsISupportsString> strObject ( do_QueryInterface(aGenericData));
700 if (!strObject)
701 return NS_ERROR_FAILURE;
703 // alloc a buffer to hold the unicode title text
704 int bufsize = (MAXTITLELTH+1)*2;
705 PRUnichar * buffer = (PRUnichar*)nsMemory::Alloc(bufsize);
706 if (!buffer)
707 return NS_ERROR_FAILURE;
709 nsAutoString strData;
710 strObject->GetData(strData);
711 nsAutoString::const_iterator start, end;
712 strData.BeginReading(start);
713 strData.EndReading(end);
715 // skip over leading non-alphanumerics
716 for( ; start != end; ++start)
717 if (UniQueryChar( *start, CT_ALNUM))
718 break;
720 // move alphanumerics into the buffer & replace contiguous
721 // non-alnums with a single separator character
722 int ctr, sep;
723 for (ctr=0, sep=0; start != end && ctr < MAXTITLELTH; ++start) {
724 if (UniQueryChar( *start, CT_ALNUM)) {
725 buffer[ctr] = *start;
726 ctr++;
727 sep = 0;
729 else
730 if (!sep) {
731 buffer[ctr] = TITLESEPARATOR;
732 ctr++;
733 sep = 1;
736 // eliminate trailing separators & lone characters
737 // orphaned when the title is truncated
738 if (sep)
739 ctr--;
740 if (ctr >= MAXTITLELTH - sep && buffer[ctr-2] == TITLESEPARATOR)
741 ctr -= 2;
742 buffer[ctr] = 0;
744 // if we ended up with no alnums, call the result "text";
745 // otherwise, do NLS conversion
746 if (!ctr) {
747 *aTargetName = ToNewCString(NS_LITERAL_CSTRING("text"));
748 ctr = 1;
750 else
751 ctr = UnicodeToCodepage( nsDependentString(buffer), aTargetName);
753 // free our buffer, then exit
754 nsMemory::Free(buffer);
756 if (!ctr)
757 return NS_ERROR_FAILURE;
759 mSourceData = aGenericData;
760 mMimeType = kUnicodeMime;
761 return NS_OK;
764 // --------------------------------------------------------------------------
765 // nsIDragSessionOS2
766 // --------------------------------------------------------------------------
768 // DragOverMsg() provides minimal handling if a drag session is already
769 // in progress. If not, it assumes this is a native drag that has just
770 // entered the window and calls NativeDragEnter() to start a session.
772 NS_IMETHODIMP nsDragService::DragOverMsg(PDRAGINFO pdinfo, MRESULT &mr,
773 uint32_t* dragFlags)
775 nsresult rv = NS_ERROR_FAILURE;
777 if (!&mr || !dragFlags || !pdinfo || !DrgAccessDraginfo(pdinfo))
778 return rv;
780 *dragFlags = 0;
781 mr = MRFROM2SHORT(DOR_NEVERDROP, 0);
783 // examine the dragged item & "start" a drag session if OK;
784 // also, signal the need for a dragenter event
785 if (!mDoingDrag)
786 if (NS_SUCCEEDED(NativeDragEnter(pdinfo)))
787 *dragFlags |= DND_DISPATCHENTEREVENT;
789 // if we're in a drag, set it up to be dispatched
790 if (mDoingDrag) {
791 SetCanDrop(false);
792 switch (pdinfo->usOperation) {
793 case DO_COPY:
794 SetDragAction(DRAGDROP_ACTION_COPY);
795 break;
796 case DO_LINK:
797 SetDragAction(DRAGDROP_ACTION_LINK);
798 break;
799 default:
800 SetDragAction(DRAGDROP_ACTION_MOVE);
801 break;
803 if (mSourceNode)
804 *dragFlags |= DND_DISPATCHEVENT | DND_GETDRAGOVERRESULT | DND_MOZDRAG;
805 else
806 *dragFlags |= DND_DISPATCHEVENT | DND_GETDRAGOVERRESULT | DND_NATIVEDRAG;
807 rv = NS_OK;
810 DrgFreeDraginfo(pdinfo);
811 return rv;
814 // --------------------------------------------------------------------------
816 // Evaluates native drag data, and if acceptable, creates & stores
817 // a transferable with the available flavors (but not the data);
818 // if successful, it "starts" the session.
820 NS_IMETHODIMP nsDragService::NativeDragEnter(PDRAGINFO pdinfo)
822 nsresult rv = NS_ERROR_FAILURE;
823 bool isFQFile = FALSE;
824 bool isAtom = FALSE;
825 PDRAGITEM pditem = 0;
827 if (pdinfo->cditem != 1)
828 return rv;
830 pditem = DrgQueryDragitemPtr(pdinfo, 0);
832 if (pditem) {
833 if (DrgVerifyRMF(pditem, "DRM_ATOM", 0)) {
834 isAtom = TRUE;
835 rv = NS_OK;
837 else
838 if (DrgVerifyRMF(pditem, "DRM_DTSHARE", 0))
839 rv = NS_OK;
840 else
841 if (DrgVerifyRMF(pditem, "DRM_OS2FILE", 0)) {
842 rv = NS_OK;
843 if (pditem->hstrContainerName && pditem->hstrSourceName)
844 isFQFile = TRUE;
848 if (NS_SUCCEEDED(rv)) {
849 rv = NS_ERROR_FAILURE;
850 nsCOMPtr<nsITransferable> trans(
851 do_CreateInstance("@mozilla.org/widget/transferable;1", &rv));
852 if (trans) {
853 trans->Init(nullptr);
855 bool isUrl = DrgVerifyType(pditem, "UniformResourceLocator");
856 bool isAlt = (WinGetKeyState(HWND_DESKTOP, VK_ALT) & 0x8000);
858 // if this is a fully-qualified file or the item claims to be
859 // a Url, identify it as a Url & also offer it as html
860 if ((isFQFile && !isAlt) || isUrl) {
861 trans->AddDataFlavor(kURLMime);
862 trans->AddDataFlavor(kHTMLMime);
865 // everything is always "text"
866 trans->AddDataFlavor(kUnicodeMime);
868 // if we can create the array, initialize the session
869 nsCOMPtr<nsISupportsArray> transArray(
870 do_CreateInstance("@mozilla.org/supports-array;1", &rv));
871 if (transArray) {
872 transArray->InsertElementAt(trans, 0);
873 mSourceDataItems = transArray;
875 // add the dragged data to the transferable if we have easy access
876 // to it (i.e. no need to read a file or request rendering); for
877 // URLs, we'll skip creating a title until the drop occurs
878 nsXPIDLCString someText;
879 if (isAtom) {
880 if (NS_SUCCEEDED(GetAtom(pditem->ulItemID, getter_Copies(someText))))
881 NativeDataToTransferable( someText.get(), 0, isUrl);
883 else
884 if (isFQFile && !isAlt &&
885 NS_SUCCEEDED(GetFileName(pditem, getter_Copies(someText)))) {
886 nsCOMPtr<nsIFile> file;
887 if (NS_SUCCEEDED(NS_NewNativeLocalFile(someText, true,
888 getter_AddRefs(file)))) {
889 nsAutoCString textStr;
890 NS_GetURLSpecFromFile(file, textStr);
891 if (!textStr.IsEmpty()) {
892 someText.Assign(ToNewCString(textStr));
893 NativeDataToTransferable( someText.get(), 0, TRUE);
898 mSourceNode = 0;
899 mSourceDocument = 0;
900 mDoingDrag = TRUE;
901 rv = NS_OK;
906 return rv;
909 // --------------------------------------------------------------------------
911 // Invoked after a dragover event has been dispatched, this constructs
912 // a reply to DM_DRAGOVER based on the canDrop & dragAction attributes.
914 NS_IMETHODIMP nsDragService::GetDragoverResult(MRESULT& mr)
916 nsresult rv = NS_ERROR_FAILURE;
917 if (!&mr)
918 return rv;
920 if (mDoingDrag) {
922 bool canDrop = false;
923 USHORT usDrop;
924 GetCanDrop(&canDrop);
925 if (canDrop)
926 usDrop = DOR_DROP;
927 else
928 usDrop = DOR_NODROP;
930 uint32_t action;
931 USHORT usOp;
932 GetDragAction(&action);
933 if (action & DRAGDROP_ACTION_COPY)
934 usOp = DO_COPY;
935 else
936 if (action & DRAGDROP_ACTION_LINK)
937 usOp = DO_LINK;
938 else {
939 if (mSourceNode)
940 usOp = DO_MOVE;
941 else
942 usOp = DO_UNKNOWN+1;
943 if (action == DRAGDROP_ACTION_NONE)
944 usDrop = DOR_NODROP;
947 mr = MRFROM2SHORT(usDrop, usOp);
948 rv = NS_OK;
950 else
951 mr = MRFROM2SHORT(DOR_NEVERDROP, 0);
953 return rv;
956 // --------------------------------------------------------------------------
958 // have the client dispatch the event, then call ExitSession()
960 NS_IMETHODIMP nsDragService::DragLeaveMsg(PDRAGINFO pdinfo, uint32_t* dragFlags)
962 if (!mDoingDrag || !dragFlags)
963 return NS_ERROR_FAILURE;
965 if (mSourceNode)
966 *dragFlags = DND_DISPATCHEVENT | DND_EXITSESSION | DND_MOZDRAG;
967 else
968 *dragFlags = DND_DISPATCHEVENT | DND_EXITSESSION | DND_NATIVEDRAG;
970 return NS_OK;
973 // --------------------------------------------------------------------------
975 // DropHelp occurs when you press F1 during a drag; apparently,
976 // it's like a regular drop in that the target has to do clean up
978 NS_IMETHODIMP nsDragService::DropHelpMsg(PDRAGINFO pdinfo, uint32_t* dragFlags)
980 if (!mDoingDrag)
981 return NS_ERROR_FAILURE;
983 if (pdinfo && DrgAccessDraginfo(pdinfo)) {
984 DrgDeleteDraginfoStrHandles(pdinfo);
985 DrgFreeDraginfo(pdinfo);
988 if (!dragFlags)
989 return NS_ERROR_FAILURE;
991 if (mSourceNode)
992 *dragFlags = DND_DISPATCHEVENT | DND_EXITSESSION | DND_MOZDRAG;
993 else
994 *dragFlags = DND_DISPATCHEVENT | DND_EXITSESSION | DND_NATIVEDRAG;
996 return NS_OK;
999 // --------------------------------------------------------------------------
1001 // for native drags, clean up;
1002 // for all drags, signal that Moz is no longer in d&d mode
1004 NS_IMETHODIMP nsDragService::ExitSession(uint32_t* dragFlags)
1006 if (!mDoingDrag)
1007 return NS_ERROR_FAILURE;
1009 if (!mSourceNode) {
1010 mSourceDataItems = 0;
1011 mDataTransfer = 0;
1012 mDoingDrag = FALSE;
1014 // if we created a temp file, delete it
1015 if (gTempFile) {
1016 DosDelete(gTempFile);
1017 nsMemory::Free(gTempFile);
1018 gTempFile = 0;
1022 if (!dragFlags)
1023 return NS_ERROR_FAILURE;
1024 *dragFlags = 0;
1026 return NS_OK;
1029 // --------------------------------------------------------------------------
1031 // If DropMsg() is presented with native data that has to be rendered,
1032 // the drop event & cleanup will be defered until the client's window
1033 // has received a render-complete msg.
1035 NS_IMETHODIMP nsDragService::DropMsg(PDRAGINFO pdinfo, HWND hwnd,
1036 uint32_t* dragFlags)
1038 if (!mDoingDrag || !dragFlags || !pdinfo || !DrgAccessDraginfo(pdinfo))
1039 return NS_ERROR_FAILURE;
1041 switch (pdinfo->usOperation) {
1042 case DO_MOVE:
1043 SetDragAction(DRAGDROP_ACTION_MOVE);
1044 break;
1045 case DO_COPY:
1046 SetDragAction(DRAGDROP_ACTION_COPY);
1047 break;
1048 case DO_LINK:
1049 SetDragAction(DRAGDROP_ACTION_LINK);
1050 break;
1051 default: // avoid "moving" (i.e. deleting) native text/objects
1052 if (mSourceNode)
1053 SetDragAction(DRAGDROP_ACTION_MOVE);
1054 else
1055 SetDragAction(DRAGDROP_ACTION_COPY);
1056 break;
1059 // if this is a native drag, move the source data to a transferable;
1060 // request rendering if needed
1061 nsresult rv = NS_OK;
1062 bool rendering = false;
1063 if (!mSourceNode)
1064 rv = NativeDrop( pdinfo, hwnd, &rendering);
1066 // note: NativeDrop() sends an end-conversation msg to native
1067 // sources but nothing sends them to Mozilla - however, Mozilla
1068 // (i.e. nsDragService) doesn't need them, so...
1070 // if rendering, the action flags are off because we don't want
1071 // the client to do anything yet; the status flags are off because
1072 // we'll be exiting d&d mode before the next screen update occurs
1073 if (rendering)
1074 *dragFlags = 0;
1075 else {
1076 // otherwise, set the flags & free the native drag structures
1078 *dragFlags = DND_EXITSESSION;
1079 if (NS_SUCCEEDED(rv)) {
1080 if (mSourceNode)
1081 *dragFlags |= DND_DISPATCHEVENT | DND_INDROP | DND_MOZDRAG;
1082 else
1083 *dragFlags |= DND_DISPATCHEVENT | DND_INDROP | DND_NATIVEDRAG;
1086 DrgDeleteDraginfoStrHandles(pdinfo);
1087 DrgFreeDraginfo(pdinfo);
1088 rv = NS_OK;
1091 return rv;
1094 // --------------------------------------------------------------------------
1096 // Invoked by DropMsg to fill a transferable with native data;
1097 // if necessary, requests the source to render it.
1099 NS_IMETHODIMP nsDragService::NativeDrop(PDRAGINFO pdinfo, HWND hwnd,
1100 bool* rendering)
1102 *rendering = false;
1104 nsresult rv = NS_ERROR_FAILURE;
1105 PDRAGITEM pditem = DrgQueryDragitemPtr(pdinfo, 0);
1106 if (!pditem)
1107 return rv;
1109 nsXPIDLCString dropText;
1110 bool isUrl = DrgVerifyType(pditem, "UniformResourceLocator");
1112 // identify format; the order of evaluation here is important
1114 // DRM_ATOM - DragText stores up to 255 chars in a Drg API atom
1115 // DRM_DTSHARE - DragText renders up to 1mb to named shared mem
1116 if (DrgVerifyRMF(pditem, "DRM_ATOM", 0))
1117 rv = GetAtom(pditem->ulItemID, getter_Copies(dropText));
1118 else
1119 if (DrgVerifyRMF(pditem, "DRM_DTSHARE", 0)) {
1120 rv = RenderToDTShare( pditem, hwnd);
1121 if (NS_SUCCEEDED(rv))
1122 *rendering = true;
1125 // DRM_OS2FILE - get the file's path or contents if it exists;
1126 // request rendering if it doesn't
1127 else
1128 if (DrgVerifyRMF(pditem, "DRM_OS2FILE", 0)) {
1129 bool isAlt = (WinGetKeyState(HWND_DESKTOP, VK_ALT) & 0x8000);
1131 // the file has to be rendered - currently, we only present
1132 // its content, not its name, to Moz to avoid conflicts
1133 if (!pditem->hstrContainerName || !pditem->hstrSourceName) {
1134 rv = RenderToOS2File( pditem, hwnd);
1135 if (NS_SUCCEEDED(rv))
1136 *rendering = true;
1138 // for Url objects and 'Alt+Drop', get the file's contents;
1139 // otherwise, convert it's path to a Url
1140 else {
1141 nsXPIDLCString fileName;
1142 if (NS_SUCCEEDED(GetFileName(pditem, getter_Copies(fileName)))) {
1143 if (isUrl || isAlt)
1144 rv = GetFileContents(fileName.get(), getter_Copies(dropText));
1145 else {
1146 isUrl = true;
1147 nsCOMPtr<nsIFile> file;
1148 if (NS_SUCCEEDED(NS_NewNativeLocalFile(fileName,
1149 true, getter_AddRefs(file)))) {
1150 nsAutoCString textStr;
1151 NS_GetURLSpecFromFile(file, textStr);
1152 if (!textStr.IsEmpty()) {
1153 dropText.Assign(ToNewCString(textStr));
1154 rv = NS_OK;
1157 } // filename as Url
1158 } // got filename
1159 } // existing files
1160 } // DRM_OS2FILE
1162 // if OK, put what data there is in the transferable; this could be
1163 // everything needed or just the title of a Url that needs rendering
1164 if (NS_SUCCEEDED(rv)) {
1166 // for Urls, get the title & remove any linefeeds
1167 nsXPIDLCString titleText;
1168 if (isUrl &&
1169 pditem->hstrTargetName &&
1170 NS_SUCCEEDED(GetAtom(pditem->hstrTargetName, getter_Copies(titleText))))
1171 for (char* ptr=strchr(titleText.BeginWriting(),'\n'); ptr; ptr=strchr(ptr, '\n'))
1172 *ptr = ' ';
1174 rv = NativeDataToTransferable( dropText.get(), titleText.get(), isUrl);
1177 // except for renderings, tell the source we're done with the data
1178 if (!*rendering)
1179 DrgSendTransferMsg( pditem->hwndItem, DM_ENDCONVERSATION,
1180 (MPARAM)pditem->ulItemID,
1181 (MPARAM)DMFL_TARGETSUCCESSFUL);
1183 return (rv);
1186 // --------------------------------------------------------------------------
1188 // Because RenderCompleteMsg() is called after the native (PM) drag
1189 // session has ended, all of the drag status flags should be off.
1191 // FYI... PM's asynchronous rendering mechanism is not compatible with
1192 // nsIDataFlavorProvider which expects data to be rendered synchronously
1194 NS_IMETHODIMP nsDragService::RenderCompleteMsg(PDRAGTRANSFER pdxfer,
1195 USHORT usResult, uint32_t* dragFlags)
1197 nsresult rv = NS_ERROR_FAILURE;
1198 if (!mDoingDrag || !pdxfer)
1199 return rv;
1201 // this msg should never come from Moz - if it does, fail
1202 if (!mSourceNode)
1203 rv = NativeRenderComplete(pdxfer, usResult);
1205 // DrgQueryDraginfoPtrFromDragitem() doesn't work - this does
1206 PDRAGINFO pdinfo = (PDRAGINFO)MAKEULONG(0x2c, HIUSHORT(pdxfer->pditem));
1208 DrgDeleteStrHandle(pdxfer->hstrSelectedRMF);
1209 DrgDeleteStrHandle(pdxfer->hstrRenderToName);
1210 DrgFreeDragtransfer(pdxfer);
1212 // if the source is Moz, don't touch pdinfo - it's been freed already
1213 if (pdinfo && !mSourceNode) {
1214 DrgDeleteDraginfoStrHandles(pdinfo);
1215 DrgFreeDraginfo(pdinfo);
1218 // this shouldn't happen
1219 if (!dragFlags)
1220 return (ExitSession(dragFlags));
1222 // d&d is over, so the DND_DragStatus flags should all be off
1223 *dragFlags = DND_EXITSESSION;
1224 if (NS_SUCCEEDED(rv))
1225 *dragFlags |= DND_DISPATCHEVENT;
1227 // lie so the client will honor the exit-session flag
1228 return NS_OK;
1231 // --------------------------------------------------------------------------
1233 // this is here to provide a false sense of symmetry with the other
1234 // method-pairs - rendered data always comes from a native source
1236 NS_IMETHODIMP nsDragService::NativeRenderComplete(PDRAGTRANSFER pdxfer,
1237 USHORT usResult)
1239 nsresult rv = NS_ERROR_FAILURE;
1240 nsXPIDLCString rmf;
1242 // identify the rendering mechanism, then get the data
1243 if (NS_SUCCEEDED(GetAtom(pdxfer->hstrSelectedRMF, getter_Copies(rmf)))) {
1244 nsXPIDLCString dropText;
1245 if (!strcmp(rmf.get(), DTSHARE_RMF))
1246 rv = RenderToDTShareComplete(pdxfer, usResult, getter_Copies(dropText));
1247 else
1248 if (!strcmp(rmf.get(), OS2FILE_TXTRMF) ||
1249 !strcmp(rmf.get(), OS2FILE_UNKRMF))
1250 rv = RenderToOS2FileComplete(pdxfer, usResult, true,
1251 getter_Copies(dropText));
1253 if (NS_SUCCEEDED(rv)) {
1254 bool isUrl = false;
1255 IsDataFlavorSupported(kURLMime, &isUrl);
1256 rv = NativeDataToTransferable( dropText.get(), 0, isUrl);
1260 DrgSendTransferMsg(pdxfer->hwndClient, DM_ENDCONVERSATION,
1261 (MPARAM)pdxfer->ulTargetInfo,
1262 (MPARAM)DMFL_TARGETSUCCESSFUL);
1264 return rv;
1267 // --------------------------------------------------------------------------
1269 // fills the transferable created by NativeDragEnter with
1270 // the set of flavors and data the target will see onDrop
1272 NS_IMETHODIMP nsDragService::NativeDataToTransferable( PCSZ pszText,
1273 PCSZ pszTitle, bool isUrl)
1275 nsresult rv = NS_ERROR_FAILURE;
1276 // the transferable should have been created on DragEnter
1277 if (!mSourceDataItems)
1278 return rv;
1280 nsCOMPtr<nsISupports> genericItem;
1281 mSourceDataItems->GetElementAt(0, getter_AddRefs(genericItem));
1282 nsCOMPtr<nsITransferable> trans (do_QueryInterface(genericItem));
1283 if (!trans)
1284 return rv;
1286 // remove invalid flavors
1287 if (!isUrl) {
1288 trans->RemoveDataFlavor(kURLMime);
1289 trans->RemoveDataFlavor(kHTMLMime);
1292 // if there's no text, exit - but first see if we have the title of
1293 // a Url that needs to be rendered; if so, stash it for later use
1294 if (!pszText || !*pszText) {
1295 if (isUrl && pszTitle && *pszTitle) {
1296 nsXPIDLString outTitle;
1297 if (CodepageToUnicode(nsDependentCString(pszTitle),
1298 getter_Copies(outTitle))) {
1299 nsCOMPtr<nsISupportsString> urlPrimitive(
1300 do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
1301 if (urlPrimitive ) {
1302 urlPrimitive->SetData(outTitle);
1303 trans->SetTransferData(kURLDescriptionMime, urlPrimitive,
1304 2*outTitle.Length());
1308 return NS_OK;
1311 nsXPIDLString outText;
1312 if (!CodepageToUnicode(nsDependentCString(pszText), getter_Copies(outText)))
1313 return rv;
1315 if (isUrl) {
1317 // if no title was supplied, see if it was stored in the transferable
1318 nsXPIDLString outTitle;
1319 if (pszTitle && *pszTitle) {
1320 if (!CodepageToUnicode(nsDependentCString(pszTitle),
1321 getter_Copies(outTitle)))
1322 return rv;
1324 else {
1325 uint32_t len;
1326 nsCOMPtr<nsISupports> genericData;
1327 if (NS_SUCCEEDED(trans->GetTransferData(kURLDescriptionMime,
1328 getter_AddRefs(genericData), &len))) {
1329 nsCOMPtr<nsISupportsString> strObject(do_QueryInterface(genericData));
1330 if (strObject)
1331 strObject->GetData(outTitle);
1335 // construct the Url flavor
1336 nsCOMPtr<nsISupportsString> urlPrimitive(
1337 do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
1338 if (urlPrimitive ) {
1339 if (outTitle.IsEmpty()) {
1340 urlPrimitive->SetData(outText);
1341 trans->SetTransferData(kURLMime, urlPrimitive, 2*outText.Length());
1343 else {
1344 nsString urlStr( outText + NS_LITERAL_STRING("\n") + outTitle);
1345 urlPrimitive->SetData(urlStr);
1346 trans->SetTransferData(kURLMime, urlPrimitive, 2*urlStr.Length());
1348 rv = NS_OK;
1351 // construct the HTML flavor - for supported graphics,
1352 // use an IMG tag, for all others create a link
1353 nsCOMPtr<nsISupportsString> htmlPrimitive(
1354 do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
1355 if (htmlPrimitive ) {
1356 nsString htmlStr;
1357 nsCOMPtr<nsIURI> uri;
1359 rv = NS_ERROR_FAILURE;
1360 if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(uri), pszText))) {
1361 nsCOMPtr<nsIURL> url (do_QueryInterface(uri));
1362 if (url) {
1363 nsAutoCString extension;
1364 url->GetFileExtension(extension);
1365 if (!extension.IsEmpty()) {
1366 if (extension.LowerCaseEqualsLiteral("gif") ||
1367 extension.LowerCaseEqualsLiteral("jpg") ||
1368 extension.LowerCaseEqualsLiteral("png") ||
1369 extension.LowerCaseEqualsLiteral("jpeg"))
1370 rv = NS_OK;
1375 if (NS_SUCCEEDED(rv))
1376 htmlStr.Assign(NS_LITERAL_STRING("<img src=\"") +
1377 outText +
1378 NS_LITERAL_STRING("\" alt=\"") +
1379 outTitle +
1380 NS_LITERAL_STRING("\"/>") );
1381 else
1382 htmlStr.Assign(NS_LITERAL_STRING("<a href=\"") +
1383 outText +
1384 NS_LITERAL_STRING("\">") +
1385 (outTitle.IsEmpty() ? outText : outTitle) +
1386 NS_LITERAL_STRING("</a>") );
1388 htmlPrimitive->SetData(htmlStr);
1389 trans->SetTransferData(kHTMLMime, htmlPrimitive, 2*htmlStr.Length());
1390 rv = NS_OK;
1394 // add the Text flavor
1395 nsCOMPtr<nsISupportsString> textPrimitive(
1396 do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
1397 if (textPrimitive ) {
1398 textPrimitive->SetData(nsDependentString(outText));
1399 trans->SetTransferData(kUnicodeMime, textPrimitive, 2*outText.Length());
1400 rv = NS_OK;
1403 // return OK if we put anything in the transferable
1404 return rv;
1407 // --------------------------------------------------------------------------
1408 // Helper functions
1409 // --------------------------------------------------------------------------
1411 // currently, the same filename is used for every render request;
1412 // it is deleted when the drag session ends
1414 nsresult RenderToOS2File( PDRAGITEM pditem, HWND hwnd)
1416 nsresult rv = NS_ERROR_FAILURE;
1417 nsXPIDLCString fileName;
1419 if (NS_SUCCEEDED(GetTempFileName(getter_Copies(fileName)))) {
1420 const char * pszRMF;
1421 if (DrgVerifyRMF(pditem, "DRM_OS2FILE", "DRF_TEXT"))
1422 pszRMF = OS2FILE_TXTRMF;
1423 else
1424 pszRMF = OS2FILE_UNKRMF;
1426 rv = RequestRendering( pditem, hwnd, pszRMF, fileName.get());
1429 return rv;
1432 // --------------------------------------------------------------------------
1434 // return a buffer with the rendered file's Url or contents
1436 nsresult RenderToOS2FileComplete(PDRAGTRANSFER pdxfer, USHORT usResult,
1437 bool content, char** outText)
1439 nsresult rv = NS_ERROR_FAILURE;
1441 // for now, override content flag & always return content
1442 content = true;
1444 if (usResult & DMFL_RENDEROK) {
1445 if (NS_SUCCEEDED(GetAtom( pdxfer->hstrRenderToName, &gTempFile))) {
1446 if (content)
1447 rv = GetFileContents(gTempFile, outText);
1448 else {
1449 nsCOMPtr<nsIFile> file;
1450 if (NS_SUCCEEDED(NS_NewNativeLocalFile(nsDependentCString(gTempFile),
1451 true, getter_AddRefs(file)))) {
1452 nsAutoCString textStr;
1453 NS_GetURLSpecFromFile(file, textStr);
1454 if (!textStr.IsEmpty()) {
1455 *outText = ToNewCString(textStr);
1456 rv = NS_OK;
1462 // gTempFile will be deleted when ExitSession() is called
1464 return rv;
1467 // --------------------------------------------------------------------------
1469 // DTShare uses 1mb of uncommitted named-shared memory
1470 // (next time I'll do it differently - rw)
1472 nsresult RenderToDTShare( PDRAGITEM pditem, HWND hwnd)
1474 nsresult rv;
1475 void * pMem;
1477 #ifdef MOZ_OS2_HIGH_MEMORY
1478 APIRET rc = DosAllocSharedMem( &pMem, DTSHARE_NAME, 0x100000,
1479 PAG_WRITE | PAG_READ | OBJ_ANY);
1480 if (rc != NO_ERROR &&
1481 rc != ERROR_ALREADY_EXISTS) { // Did the kernel handle OBJ_ANY?
1482 // Try again without OBJ_ANY and if the first failure was not caused
1483 // by OBJ_ANY then we will get the same failure, else we have taken
1484 // care of pre-FP13 systems where the kernel couldn't handle it.
1485 rc = DosAllocSharedMem( &pMem, DTSHARE_NAME, 0x100000,
1486 PAG_WRITE | PAG_READ);
1488 #else
1489 APIRET rc = DosAllocSharedMem( &pMem, DTSHARE_NAME, 0x100000,
1490 PAG_WRITE | PAG_READ);
1491 #endif
1493 if (rc == ERROR_ALREADY_EXISTS)
1494 rc = DosGetNamedSharedMem( &pMem, DTSHARE_NAME,
1495 PAG_WRITE | PAG_READ);
1496 if (rc)
1497 rv = NS_ERROR_FAILURE;
1498 else
1499 rv = RequestRendering( pditem, hwnd, DTSHARE_RMF, DTSHARE_NAME);
1501 return rv;
1504 // --------------------------------------------------------------------------
1506 // return a buffer with the rendered text
1508 nsresult RenderToDTShareComplete(PDRAGTRANSFER pdxfer, USHORT usResult,
1509 char** outText)
1511 nsresult rv = NS_ERROR_FAILURE;
1512 void * pMem;
1513 char * pszText = 0;
1515 APIRET rc = DosGetNamedSharedMem( &pMem, DTSHARE_NAME, PAG_WRITE | PAG_READ);
1517 if (!rc) {
1518 if (usResult & DMFL_RENDEROK) {
1519 pszText = (char*)nsMemory::Alloc( ((ULONG*)pMem)[0] + 1);
1520 if (pszText) {
1521 strcpy(pszText, &((char*)pMem)[sizeof(ULONG)] );
1522 RemoveCarriageReturns(pszText);
1523 *outText = pszText;
1524 rv = NS_OK;
1527 // using DosGetNamedSharedMem() on memory we allocated appears
1528 // to increment its usage ctr, so we have to free it 2x
1529 DosFreeMem(pMem);
1530 DosFreeMem(pMem);
1533 return rv;
1536 // --------------------------------------------------------------------------
1538 // a generic request dispatcher
1540 nsresult RequestRendering( PDRAGITEM pditem, HWND hwnd, PCSZ pRMF, PCSZ pName)
1542 PDRAGTRANSFER pdxfer = DrgAllocDragtransfer( 1);
1543 if (!pdxfer)
1544 return NS_ERROR_FAILURE;
1546 pdxfer->cb = sizeof(DRAGTRANSFER);
1547 pdxfer->hwndClient = hwnd;
1548 pdxfer->pditem = pditem;
1549 pdxfer->hstrSelectedRMF = DrgAddStrHandle( pRMF);
1550 pdxfer->hstrRenderToName = 0;
1551 pdxfer->ulTargetInfo = pditem->ulItemID;
1552 pdxfer->usOperation = (USHORT)DO_COPY;
1553 pdxfer->fsReply = 0;
1555 // send the msg before setting a render-to name
1556 if (pditem->fsControl & DC_PREPAREITEM)
1557 DrgSendTransferMsg( pditem->hwndItem, DM_RENDERPREPARE, (MPARAM)pdxfer, 0);
1559 pdxfer->hstrRenderToName = DrgAddStrHandle( pName);
1561 // send the msg after setting a render-to name
1562 if ((pditem->fsControl & (DC_PREPARE | DC_PREPAREITEM)) == DC_PREPARE)
1563 DrgSendTransferMsg( pditem->hwndItem, DM_RENDERPREPARE, (MPARAM)pdxfer, 0);
1565 // ask the source to render the selected item
1566 if (!DrgSendTransferMsg( pditem->hwndItem, DM_RENDER, (MPARAM)pdxfer, 0))
1567 return NS_ERROR_FAILURE;
1569 return NS_OK;
1572 // --------------------------------------------------------------------------
1574 // return a ptr to a buffer containing the text associated
1575 // with a drag atom; the caller frees the buffer
1577 nsresult GetAtom( ATOM aAtom, char** outText)
1579 nsresult rv = NS_ERROR_FAILURE;
1581 ULONG ulInLength = DrgQueryStrNameLen(aAtom);
1582 if (ulInLength) {
1583 char* pszText = (char*)nsMemory::Alloc(++ulInLength);
1584 if (pszText) {
1585 DrgQueryStrName(aAtom, ulInLength, pszText);
1586 RemoveCarriageReturns(pszText);
1587 *outText = pszText;
1588 rv = NS_OK;
1591 return rv;
1594 // --------------------------------------------------------------------------
1596 // return a ptr to a buffer containing the file path specified
1597 // in the dragitem; the caller frees the buffer
1599 nsresult GetFileName(PDRAGITEM pditem, char** outText)
1601 nsresult rv = NS_ERROR_FAILURE;
1602 ULONG cntCnr = DrgQueryStrNameLen(pditem->hstrContainerName);
1603 ULONG cntSrc = DrgQueryStrNameLen(pditem->hstrSourceName);
1605 char* pszText = (char*)nsMemory::Alloc(cntCnr+cntSrc+1);
1606 if (pszText) {
1607 DrgQueryStrName(pditem->hstrContainerName, cntCnr+1, pszText);
1608 DrgQueryStrName(pditem->hstrSourceName, cntSrc+1, &pszText[cntCnr]);
1609 pszText[cntCnr+cntSrc] = 0;
1610 *outText = pszText;
1611 rv = NS_OK;
1613 return rv;
1616 // --------------------------------------------------------------------------
1618 // read the file; if successful, return a ptr to its contents;
1619 // the caller frees the buffer
1621 nsresult GetFileContents(PCSZ pszPath, char** outText)
1623 nsresult rv = NS_ERROR_FAILURE;
1624 char* pszText = 0;
1626 if (pszPath) {
1627 FILE *fp = fopen(pszPath, "r");
1628 if (fp) {
1629 fseek(fp, 0, SEEK_END);
1630 ULONG filesize = ftell(fp);
1631 fseek(fp, 0, SEEK_SET);
1632 if (filesize > 0) {
1633 size_t readsize = (size_t)filesize;
1634 pszText = (char*)nsMemory::Alloc(readsize+1);
1635 if (pszText) {
1636 readsize = fread((void *)pszText, 1, readsize, fp);
1637 if (readsize) {
1638 pszText[readsize] = '\0';
1639 RemoveCarriageReturns(pszText);
1640 *outText = pszText;
1641 rv = NS_OK;
1643 else {
1644 nsMemory::Free(pszText);
1645 pszText = 0;
1649 fclose(fp);
1653 return rv;
1656 // --------------------------------------------------------------------------
1658 // currently, this returns the same path & filename every time
1660 nsresult GetTempFileName(char** outText)
1662 char * pszText = (char*)nsMemory::Alloc(CCHMAXPATH);
1663 if (!pszText)
1664 return NS_ERROR_FAILURE;
1666 const char * pszPath;
1667 if (!DosScanEnv("TEMP", &pszPath) || !DosScanEnv("TMP", &pszPath))
1668 strcpy(pszText, pszPath);
1669 else
1670 if (DosQueryPathInfo(".\\.", FIL_QUERYFULLNAME, pszText, CCHMAXPATH))
1671 pszText[0] = 0;
1673 strcat(pszText, "\\");
1674 strcat(pszText, OS2FILE_NAME);
1675 *outText = pszText;
1677 return NS_OK;
1680 // --------------------------------------------------------------------------
1681 // --------------------------------------------------------------------------
1683 // set the file's .TYPE and .SUBJECT EAs; since this is non-critical
1684 // (though highly desirable), errors aren't reported
1686 void SaveTypeAndSource(nsIFile *file, nsIDOMDocument *domDoc,
1687 PCSZ pszType)
1689 if (!file)
1690 return;
1692 nsCOMPtr<nsILocalFileOS2> os2file(do_QueryInterface(file));
1693 if (!os2file ||
1694 NS_FAILED(os2file->SetFileTypes(nsDependentCString(pszType))))
1695 return;
1697 // since the filetype has to be saved, this function
1698 // may be called even if there isn't any document
1699 if (!domDoc)
1700 return;
1702 nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc));
1703 if (!doc)
1704 return;
1706 // this gets the top-level content URL when frames are used;
1707 // when nextDoc is zero, currDoc is the browser window, and
1708 // prevDoc points to the content;
1709 // note: neither GetParentDocument() nor GetDocumentURI()
1710 // refcount the pointers they return, so nsCOMPtr isn't needed
1711 nsIDocument *prevDoc;
1712 nsIDocument *currDoc = doc;
1713 nsIDocument *nextDoc = doc;
1714 do {
1715 prevDoc = currDoc;
1716 currDoc = nextDoc;
1717 nextDoc = currDoc->GetParentDocument();
1718 } while (nextDoc);
1720 nsIURI* srcUri = prevDoc->GetDocumentURI();
1721 if (!srcUri)
1722 return;
1724 // identifying content as coming from chrome is none too useful...
1725 bool ignore = false;
1726 srcUri->SchemeIs("chrome", &ignore);
1727 if (ignore)
1728 return;
1730 nsAutoCString url;
1731 srcUri->GetSpec(url);
1732 os2file->SetFileSource(url);
1734 return;
1737 // --------------------------------------------------------------------------
1739 // to do: this needs to take into account the current page's encoding
1740 // if it is different than the PM codepage
1742 int UnicodeToCodepage(const nsAString& aString, char **aResult)
1744 nsAutoCharBuffer buffer;
1745 int32_t bufLength;
1746 WideCharToMultiByte(0, PromiseFlatString(aString).get(), aString.Length(),
1747 buffer, bufLength);
1748 *aResult = ToNewCString(nsDependentCString(buffer.Elements()));
1749 return bufLength;
1752 // --------------------------------------------------------------------------
1754 int CodepageToUnicode(const nsACString& aString, PRUnichar **aResult)
1756 nsAutoChar16Buffer buffer;
1757 int32_t bufLength;
1758 MultiByteToWideChar(0, PromiseFlatCString(aString).get(),
1759 aString.Length(), buffer, bufLength);
1760 *aResult = ToNewUnicode(nsDependentString(buffer.Elements()));
1761 return bufLength;
1764 // --------------------------------------------------------------------------
1766 // removes carriage returns in-place; it should only be used on
1767 // raw text buffers that haven't been assigned to a string object
1769 void RemoveCarriageReturns(char * pszText)
1771 ULONG cnt;
1772 char * next;
1773 char * source;
1774 char * target;
1776 target = strchr(pszText, 0x0d);
1777 if (!target)
1778 return;
1780 source = target + 1;
1782 while ((next = strchr(source, 0x0d)) != 0) {
1784 cnt = next - source;
1785 memcpy(target, source, cnt);
1786 target += cnt;
1787 source = next + 1;
1791 strcpy(target, source);
1792 return;
1795 // --------------------------------------------------------------------------