gecko/widget/os2/nsDragService.cpp
Ehsan Akhgari 45fe6d3ae2 Bug 722872 - Part 1: Add nsITransferable::Init(nsILoadContext*), enforce that it's called in debug builds, and add nsIDOMDocument* arguments to nsIClipboardHelper methods; r=roc
This patch does the following:

* It adds nsITransferable::Init(nsILoadContext*).  The load context
  might be null, which means that the transferable is non-private, but
  if it's non-null, we extract the boolean value for the privacy mode
  and store it in the transferable.
* It adds checks in debug builds to make sure that Init is always
  called, in form of fatal assertions.
* It adds nsIDOMDocument* agruments to nsIClipboardHelper methods which
  represent the document that the string is coming from.
  nsIClipboardHelper implementation internally gets the nsILoadContext
  from that and passes it on to the transferable upon creation.  The
  reason that I did this was that nsIClipboardHelper is supposed to be a
  high-level helper, and in most of its call sites, we have easy access
  to a document object.
* It modifies all of the call sites of the above interfaces according to
  this change.
* It adds a GetLoadContext helper to nsIDocument to help with changing
  the call sites.
2012-04-16 22:14:01 -04:00

1790 lines
55 KiB
C++

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#define INCL_DOSMISC
#define INCL_DOSERRORS
#include "nsDragService.h"
#include "nsXPCOM.h"
#include "nsISupportsPrimitives.h"
#include "nsString.h"
#include "nsXPIDLString.h"
#include "nsReadableUtils.h"
#include "nsIWebBrowserPersist.h"
#include "nsIFile.h"
#include "nsIURI.h"
#include "nsIURL.h"
#include "nsNetUtil.h"
#include "nsOS2Uni.h"
#include "wdgtos2rc.h"
#include "nsILocalFileOS2.h"
#include "nsIDocument.h"
#include "nsGUIEvent.h"
#include "nsISelection.h"
// --------------------------------------------------------------------------
// Local defines
// undocumented(?)
#ifndef DC_PREPAREITEM
#define DC_PREPAREITEM 0x0040
#endif
// limit URL object titles to a reasonable length
#define MAXTITLELTH 31
#define TITLESEPARATOR (L' ')
#define DTSHARE_NAME "\\SHAREMEM\\MOZ_DND"
#define DTSHARE_RMF "<DRM_DTSHARE, DRF_TEXT>"
#define OS2FILE_NAME "MOZ_TGT.TMP"
#define OS2FILE_TXTRMF "<DRM_OS2FILE, DRF_TEXT>"
#define OS2FILE_UNKRMF "<DRM_OS2FILE, DRF_UNKNOWN>"
// not defined in the OS/2 toolkit headers
extern "C" {
APIRET APIENTRY DosQueryModFromEIP(HMODULE *phMod, ULONG *pObjNum,
ULONG BuffLen, PCHAR pBuff,
ULONG *pOffset, ULONG Address);
}
// --------------------------------------------------------------------------
// Helper functions
nsresult RenderToOS2File( PDRAGITEM pditem, HWND hwnd);
nsresult RenderToOS2FileComplete(PDRAGTRANSFER pdxfer, USHORT usResult,
bool content, char** outText);
nsresult RenderToDTShare( PDRAGITEM pditem, HWND hwnd);
nsresult RenderToDTShareComplete(PDRAGTRANSFER pdxfer, USHORT usResult,
char** outText);
nsresult RequestRendering( PDRAGITEM pditem, HWND hwnd, PCSZ pRMF, PCSZ pName);
nsresult GetAtom( ATOM aAtom, char** outText);
nsresult GetFileName(PDRAGITEM pditem, char** outText);
nsresult GetFileContents(PCSZ pszPath, char** outText);
nsresult GetTempFileName(char** outText);
void SaveTypeAndSource(nsIFile *file, nsIDOMDocument *domDoc,
PCSZ pszType);
int UnicodeToCodepage( const nsAString& inString, char **outText);
int CodepageToUnicode( const nsACString& inString, PRUnichar **outText);
void RemoveCarriageReturns(char * pszText);
MRESULT EXPENTRY nsDragWindowProc(HWND hWnd, ULONG msg, MPARAM mp1, MPARAM mp2);
// --------------------------------------------------------------------------
// Global data
static HPOINTER gPtrArray[IDC_DNDCOUNT];
static char * gTempFile = 0;
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
nsDragService::nsDragService()
{
// member initializers and constructor code
mDragWnd = WinCreateWindow( HWND_DESKTOP, WC_STATIC, 0, 0, 0, 0, 0, 0,
HWND_DESKTOP, HWND_BOTTOM, 0, 0, 0);
WinSubclassWindow( mDragWnd, nsDragWindowProc);
HMODULE hModResources = NULLHANDLE;
DosQueryModFromEIP(&hModResources, NULL, 0, NULL, NULL, (ULONG) &gPtrArray);
for (int i = 0; i < IDC_DNDCOUNT; i++)
gPtrArray[i] = ::WinLoadPointer(HWND_DESKTOP, hModResources, i+IDC_DNDBASE);
}
// --------------------------------------------------------------------------
nsDragService::~nsDragService()
{
// destructor code
WinDestroyWindow(mDragWnd);
for (int i = 0; i < IDC_DNDCOUNT; i++) {
WinDestroyPointer(gPtrArray[i]);
gPtrArray[i] = 0;
}
}
NS_IMPL_ISUPPORTS_INHERITED1(nsDragService, nsBaseDragService, nsIDragSessionOS2)
// --------------------------------------------------------------------------
NS_IMETHODIMP nsDragService::InvokeDragSession(nsIDOMNode *aDOMNode,
nsISupportsArray *aTransferables,
nsIScriptableRegion *aRegion,
PRUint32 aActionType)
{
if (mDoingDrag)
return NS_ERROR_UNEXPECTED;
nsresult rv = nsBaseDragService::InvokeDragSession(aDOMNode, aTransferables,
aRegion, aActionType);
NS_ENSURE_SUCCESS(rv, rv);
mSourceDataItems = aTransferables;
WinSetCapture(HWND_DESKTOP, NULLHANDLE);
// Assume we are only dragging one thing for now
PDRAGINFO pDragInfo = DrgAllocDraginfo(1);
if (!pDragInfo)
return NS_ERROR_UNEXPECTED;
pDragInfo->usOperation = DO_DEFAULT;
DRAGITEM dragitem;
dragitem.hwndItem = mDragWnd;
dragitem.ulItemID = (ULONG)this;
dragitem.fsControl = DC_OPEN;
dragitem.cxOffset = 2;
dragitem.cyOffset = 2;
dragitem.fsSupportedOps = DO_COPYABLE|DO_MOVEABLE|DO_LINKABLE;
// since there is no source file, leave these "blank"
dragitem.hstrContainerName = NULLHANDLE;
dragitem.hstrSourceName = NULLHANDLE;
rv = NS_ERROR_FAILURE;
ULONG idIcon = 0;
// bracket this to reduce our footprint before the drag begins
{
nsCOMPtr<nsISupports> genericItem;
mSourceDataItems->GetElementAt(0, getter_AddRefs(genericItem));
nsCOMPtr<nsITransferable> transItem (do_QueryInterface(genericItem));
nsCOMPtr<nsISupports> genericData;
PRUint32 len = 0;
// see if we have a URL or text; if so, the title method
// will save the data and mimetype for use with a native drop
if (NS_SUCCEEDED(transItem->GetTransferData(kURLMime,
getter_AddRefs(genericData), &len))) {
nsXPIDLCString targetName;
rv = GetUrlAndTitle( genericData, getter_Copies(targetName));
if (NS_SUCCEEDED(rv)) {
// advise PM that we need a DM_RENDERPREPARE msg
// *before* it composes a render-to filename
dragitem.fsControl |= DC_PREPAREITEM;
dragitem.hstrType = DrgAddStrHandle("UniformResourceLocator");
dragitem.hstrRMF = DrgAddStrHandle("<DRM_OS2FILE,DRF_TEXT>");
dragitem.hstrTargetName = DrgAddStrHandle(targetName.get());
idIcon = IDC_DNDURL;
}
}
else
if (NS_SUCCEEDED(transItem->GetTransferData(kUnicodeMime,
getter_AddRefs(genericData), &len))) {
nsXPIDLCString targetName;
rv = GetUniTextTitle( genericData, getter_Copies(targetName));
if (NS_SUCCEEDED(rv)) {
dragitem.hstrType = DrgAddStrHandle("Plain Text");
dragitem.hstrRMF = DrgAddStrHandle("<DRM_OS2FILE,DRF_TEXT>");
dragitem.hstrTargetName = DrgAddStrHandle(targetName.get());
idIcon = IDC_DNDTEXT;
}
}
}
// if neither URL nor text are available, make this a Moz-only drag
// by making it unidentifiable to native apps
if (NS_FAILED(rv)) {
mMimeType = 0;
dragitem.hstrType = DrgAddStrHandle("Unknown");
dragitem.hstrRMF = DrgAddStrHandle("<DRM_UNKNOWN,DRF_UNKNOWN>");
dragitem.hstrTargetName = NULLHANDLE;
}
DrgSetDragitem(pDragInfo, &dragitem, sizeof(DRAGITEM), 0);
DRAGIMAGE dragimage;
memset(&dragimage, 0, sizeof(DRAGIMAGE));
dragimage.cb = sizeof(DRAGIMAGE);
dragimage.fl = DRG_ICON;
if (idIcon)
dragimage.hImage = gPtrArray[idIcon-IDC_DNDBASE];
if (dragimage.hImage) {
dragimage.cyOffset = 8;
dragimage.cxOffset = 2;
}
else
dragimage.hImage = WinQuerySysPointer(HWND_DESKTOP, SPTR_FILE, FALSE);
mDoingDrag = true;
LONG escState = WinGetKeyState(HWND_DESKTOP, VK_ESC) & 0x01;
HWND hwndDest = DrgDrag(mDragWnd, pDragInfo, &dragimage, 1, VK_BUTTON2,
(void*)0x80000000L); // Don't lock the desktop PS
// determine whether the drag ended because Escape was pressed
if (hwndDest == 0 && (WinGetKeyState(HWND_DESKTOP, VK_ESC) & 0x01) != escState)
mUserCancelled = true;
FireDragEventAtSource(NS_DRAGDROP_END);
mDoingDrag = false;
// do clean up; if the drop completed,
// the target will delete the string handles
if (hwndDest == 0)
DrgDeleteDraginfoStrHandles(pDragInfo);
DrgFreeDraginfo(pDragInfo);
// reset nsDragService's members
mSourceDataItems = 0;
mSourceData = 0;
mMimeType = 0;
// reset nsBaseDragService's members
mSourceDocument = nsnull;
mSourceNode = nsnull;
mSelection = nsnull;
mDataTransfer = nsnull;
mUserCancelled = false;
mHasImage = false;
mImage = nsnull;
mImageX = 0;
mImageY = 0;
mScreenX = -1;
mScreenY = -1;
return NS_OK;
}
// --------------------------------------------------------------------------
MRESULT EXPENTRY nsDragWindowProc(HWND hWnd, ULONG msg, MPARAM mp1, MPARAM mp2)
{
switch (msg) {
// if the user requests the contents of a URL be rendered (vs the URL
// itself), change the suggested target name from the URL's title to
// the name of the file that will be retrieved
case DM_RENDERPREPARE: {
PDRAGTRANSFER pdxfer = (PDRAGTRANSFER)mp1;
nsDragService* dragservice = (nsDragService*)pdxfer->pditem->ulItemID;
if (pdxfer->usOperation == DO_COPY &&
(WinGetKeyState(HWND_DESKTOP, VK_CTRL) & 0x8000) &&
!strcmp(dragservice->mMimeType, kURLMime)) {
// QI'ing nsIURL will fail for mailto: and the like
nsCOMPtr<nsIURL> urlObject(do_QueryInterface(dragservice->mSourceData));
if (urlObject) {
nsCAutoString filename;
urlObject->GetFileName(filename);
if (filename.IsEmpty()) {
urlObject->GetHost(filename);
filename.Append("/file");
}
DrgDeleteStrHandle(pdxfer->pditem->hstrTargetName);
pdxfer->pditem->hstrTargetName = DrgAddStrHandle(filename.get());
}
}
return (MRESULT)TRUE;
}
case DM_RENDER: {
nsresult rv = NS_ERROR_FAILURE;
PDRAGTRANSFER pdxfer = (PDRAGTRANSFER)mp1;
nsDragService* dragservice = (nsDragService*)pdxfer->pditem->ulItemID;
char chPath[CCHMAXPATH];
DrgQueryStrName(pdxfer->hstrRenderToName, CCHMAXPATH, chPath);
// if the user Ctrl-dropped a URL, use the nsIURL interface
// to determine if it points to content (i.e. a file); if so,
// fetch its contents; if not (e.g. a 'mailto:' url), drop into
// the code that uses nsIURI to render a URL object
if (!strcmp(dragservice->mMimeType, kURLMime)) {
if (pdxfer->usOperation == DO_COPY &&
(WinGetKeyState(HWND_DESKTOP, VK_CTRL) & 0x8000)) {
nsCOMPtr<nsIURL> urlObject(do_QueryInterface(dragservice->mSourceData));
if (urlObject)
rv = dragservice->SaveAsContents(chPath, urlObject);
}
if (!NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIURI> uriObject(do_QueryInterface(dragservice->mSourceData));
if (uriObject)
rv = dragservice->SaveAsURL(chPath, uriObject);
}
}
else
// if we're dragging text, do NLS conversion then write it to file
if (!strcmp(dragservice->mMimeType, kUnicodeMime)) {
nsCOMPtr<nsISupportsString> strObject(
do_QueryInterface(dragservice->mSourceData));
if (strObject)
rv = dragservice->SaveAsText(chPath, strObject);
}
DrgPostTransferMsg(pdxfer->hwndClient, DM_RENDERCOMPLETE, pdxfer,
(NS_SUCCEEDED(rv) ? DMFL_RENDEROK : DMFL_RENDERFAIL),
0, TRUE);
DrgFreeDragtransfer(pdxfer);
return (MRESULT)TRUE;
}
// we don't use these msgs but neither does WinDefWindowProc()
case DM_DRAGOVERNOTIFY:
case DM_ENDCONVERSATION:
return 0;
default:
break;
}
return ::WinDefWindowProc(hWnd, msg, mp1, mp2);
}
//-------------------------------------------------------------------------
// if the versions of Start & EndDragSession in nsBaseDragService
// were called (and they shouldn't be), they'd break nsIDragSessionOS2;
// they're overridden here and turned into no-ops to prevent this
NS_IMETHODIMP nsDragService::StartDragSession()
{
NS_ERROR("OS/2 version of StartDragSession() should never be called!");
return NS_OK;
}
NS_IMETHODIMP nsDragService::EndDragSession(bool aDragDone)
{
NS_ERROR("OS/2 version of EndDragSession() should never be called!");
return NS_OK;
}
// --------------------------------------------------------------------------
NS_IMETHODIMP nsDragService::GetNumDropItems(PRUint32 *aNumDropItems)
{
if (mSourceDataItems)
mSourceDataItems->Count(aNumDropItems);
else
*aNumDropItems = 0;
return NS_OK;
}
// --------------------------------------------------------------------------
NS_IMETHODIMP nsDragService::GetData(nsITransferable *aTransferable,
PRUint32 aItemIndex)
{
// make sure that we have a transferable
if (!aTransferable)
return NS_ERROR_INVALID_ARG;
// get flavor list that includes all acceptable flavors (including
// ones obtained through conversion). Flavors are nsISupportsCStrings
// so that they can be seen from JS.
nsresult rv = NS_ERROR_FAILURE;
nsCOMPtr<nsISupportsArray> flavorList;
rv = aTransferable->FlavorsTransferableCanImport(getter_AddRefs(flavorList));
if (NS_FAILED(rv))
return rv;
// count the number of flavors
PRUint32 cnt;
flavorList->Count (&cnt);
for (unsigned int i= 0; i < cnt; ++i ) {
nsCOMPtr<nsISupports> genericWrapper;
flavorList->GetElementAt(i, getter_AddRefs(genericWrapper));
nsCOMPtr<nsISupportsCString> currentFlavor;
currentFlavor = do_QueryInterface(genericWrapper);
if (currentFlavor) {
nsXPIDLCString flavorStr;
currentFlavor->ToString(getter_Copies(flavorStr));
nsCOMPtr<nsISupports> genericItem;
mSourceDataItems->GetElementAt(aItemIndex, getter_AddRefs(genericItem));
nsCOMPtr<nsITransferable> item (do_QueryInterface(genericItem));
if (item) {
nsCOMPtr<nsISupports> data;
PRUint32 tmpDataLen = 0;
rv = item->GetTransferData(flavorStr, getter_AddRefs(data),
&tmpDataLen);
if (NS_SUCCEEDED(rv)) {
rv = aTransferable->SetTransferData(flavorStr, data, tmpDataLen);
break;
}
}
}
}
return rv;
}
// --------------------------------------------------------------------------
// This returns true if any of the dragged items support a specified data
// flavor. This doesn't make a lot of sense when dragging multiple items:
// all of them ought to match. OTOH, Moz doesn't support multiple drag
// items so no problems arise. If they do, use the commented-out code to
// switch from "any" to "all".
NS_IMETHODIMP nsDragService::IsDataFlavorSupported(const char *aDataFlavor,
bool *_retval)
{
if (!_retval)
return NS_ERROR_INVALID_ARG;
*_retval = false;
PRUint32 numDragItems = 0;
if (mSourceDataItems)
mSourceDataItems->Count(&numDragItems);
if (!numDragItems)
return NS_OK;
// return true if all items support this flavor
// for (PRUint32 itemIndex = 0, *_retval = true;
// itemIndex < numDragItems && *_retval; ++itemIndex) {
// *_retval = false;
// return true if any item supports this flavor
for (PRUint32 itemIndex = 0;
itemIndex < numDragItems && !(*_retval); ++itemIndex) {
nsCOMPtr<nsISupports> genericItem;
mSourceDataItems->GetElementAt(itemIndex, getter_AddRefs(genericItem));
nsCOMPtr<nsITransferable> currItem (do_QueryInterface(genericItem));
if (currItem) {
nsCOMPtr <nsISupportsArray> flavorList;
currItem->FlavorsTransferableCanExport(getter_AddRefs(flavorList));
if (flavorList) {
PRUint32 numFlavors;
flavorList->Count( &numFlavors );
for (PRUint32 flavorIndex=0; flavorIndex < numFlavors; ++flavorIndex) {
nsCOMPtr<nsISupports> genericWrapper;
flavorList->GetElementAt(flavorIndex, getter_AddRefs(genericWrapper));
nsCOMPtr<nsISupportsCString> currentFlavor;
currentFlavor = do_QueryInterface(genericWrapper);
if (currentFlavor) {
nsXPIDLCString flavorStr;
currentFlavor->ToString ( getter_Copies(flavorStr) );
if (strcmp(flavorStr, aDataFlavor) == 0) {
*_retval = true;
break;
}
}
} // for each flavor
}
}
}
return NS_OK;
}
// --------------------------------------------------------------------------
// use nsIWebBrowserPersist to fetch the contents of a URL
nsresult nsDragService::SaveAsContents(PCSZ pszDest, nsIURL* aURL)
{
nsCOMPtr<nsIURI> linkURI(do_QueryInterface(aURL));
if (!linkURI)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIWebBrowserPersist> webPersist(
do_CreateInstance("@mozilla.org/embedding/browser/nsWebBrowserPersist;1"));
if (!webPersist)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIFile> file;
NS_NewNativeLocalFile(nsDependentCString(pszDest), true,
getter_AddRefs(file));
if (!file)
return NS_ERROR_FAILURE;
FILE* fp;
if (NS_FAILED(file->OpenANSIFileDesc("wb+", &fp)))
return NS_ERROR_FAILURE;
fwrite("", 0, 1, fp);
fclose(fp);
webPersist->SaveURI(linkURI, nsnull, nsnull, nsnull, nsnull, file);
return NS_OK;
}
// --------------------------------------------------------------------------
// save this URL in a file that the WPS will identify as a WPUrl object
nsresult nsDragService::SaveAsURL(PCSZ pszDest, nsIURI* aURI)
{
nsCAutoString strUri;
aURI->GetSpec(strUri);
if (strUri.IsEmpty())
return NS_ERROR_FAILURE;
nsCOMPtr<nsIFile> file;
NS_NewNativeLocalFile(nsDependentCString(pszDest), true,
getter_AddRefs(file));
if (!file)
return NS_ERROR_FAILURE;
FILE* fp;
if (NS_FAILED(file->OpenANSIFileDesc("wb+", &fp)))
return NS_ERROR_FAILURE;
fwrite(strUri.get(), strUri.Length(), 1, fp);
fclose(fp);
nsCOMPtr<nsIDOMDocument> domDoc;
GetSourceDocument(getter_AddRefs(domDoc));
SaveTypeAndSource(file, domDoc, "UniformResourceLocator");
return NS_OK;
}
// --------------------------------------------------------------------------
// save this text to file after conversion to the current codepage
nsresult nsDragService::SaveAsText(PCSZ pszDest, nsISupportsString* aString)
{
nsAutoString strData;
aString->GetData(strData);
if (strData.IsEmpty())
return NS_ERROR_FAILURE;
nsCOMPtr<nsIFile> file;
NS_NewNativeLocalFile(nsDependentCString(pszDest), true,
getter_AddRefs(file));
if (!file)
return NS_ERROR_FAILURE;
nsXPIDLCString textStr;
int cnt = UnicodeToCodepage(strData, getter_Copies(textStr));
if (!cnt)
return NS_ERROR_FAILURE;
FILE* fp;
if (NS_FAILED(file->OpenANSIFileDesc("wb+", &fp)))
return NS_ERROR_FAILURE;
fwrite(textStr.get(), cnt, 1, fp);
fclose(fp);
nsCOMPtr<nsIDOMDocument> domDoc;
GetSourceDocument(getter_AddRefs(domDoc));
SaveTypeAndSource(file, domDoc, "Plain Text");
return NS_OK;
}
// --------------------------------------------------------------------------
// Split a Moz Url/Title into its components, save the Url for use by
// a native drop, then compose a title.
nsresult nsDragService::GetUrlAndTitle(nsISupports *aGenericData,
char **aTargetName)
{
// get the URL/title string
nsCOMPtr<nsISupportsString> strObject ( do_QueryInterface(aGenericData));
if (!strObject)
return NS_ERROR_FAILURE;
nsAutoString strData;
strObject->GetData(strData);
// split string into URL and Title -
// if there's a title but no URL, there's no reason to continue
PRInt32 lineIndex = strData.FindChar ('\n');
if (lineIndex == 0)
return NS_ERROR_FAILURE;
// get the URL portion of the text
nsAutoString strUrl;
if (lineIndex == -1)
strUrl = strData;
else
strData.Left(strUrl, lineIndex);
// save the URL for later use
nsCOMPtr<nsIURI> saveURI;
NS_NewURI(getter_AddRefs(saveURI), strUrl);
if (!saveURI)
return NS_ERROR_FAILURE;
// if there's a bona-fide title & it isn't just a copy of the URL,
// limit it to a reasonable length, perform NLS conversion, then return
if (++lineIndex && lineIndex != (int)strData.Length() &&
!strUrl.Equals(Substring(strData, lineIndex, strData.Length()))) {
PRUint32 strLth = NS_MIN((int)strData.Length()-lineIndex, MAXTITLELTH);
nsAutoString strTitle;
strData.Mid(strTitle, lineIndex, strLth);
if (!UnicodeToCodepage(strTitle, aTargetName))
return NS_ERROR_FAILURE;
mSourceData = do_QueryInterface(saveURI);
mMimeType = kURLMime;
return NS_OK;
}
// if the URI can be handled as a URL, construct a title from
// the hostname & filename; if not, use the first MAXTITLELTH
// characters that appear after the scheme name
nsCAutoString strTitle;
nsCOMPtr<nsIURL> urlObj( do_QueryInterface(saveURI));
if (urlObj) {
nsCAutoString strFile;
urlObj->GetHost(strTitle);
urlObj->GetFileName(strFile);
if (!strFile.IsEmpty()) {
strTitle.AppendLiteral("/");
strTitle.Append(strFile);
}
else {
urlObj->GetDirectory(strFile);
if (strFile.Length() > 1) {
nsCAutoString::const_iterator start, end, curr;
strFile.BeginReading(start);
strFile.EndReading(end);
strFile.EndReading(curr);
for (curr.advance(-2); curr != start; --curr)
if (*curr == '/')
break;
strTitle.Append(Substring(curr, end));
}
}
}
else {
saveURI->GetSpec(strTitle);
PRInt32 index = strTitle.FindChar (':');
if (index != -1) {
if ((strTitle.get())[++index] == '/')
if ((strTitle.get())[++index] == '/')
++index;
strTitle.Cut(0, index);
}
if (strTitle.Length() > MAXTITLELTH)
strTitle.Truncate(MAXTITLELTH);
}
*aTargetName = ToNewCString(strTitle);
mSourceData = do_QueryInterface(saveURI);
mMimeType = kURLMime;
return NS_OK;
}
// --------------------------------------------------------------------------
// Construct a title for text drops from the leading words of the text.
// Alphanumeric characters are copied to the title; sequences of
// non-alphanums are replaced by a single space
nsresult nsDragService::GetUniTextTitle(nsISupports *aGenericData,
char **aTargetName)
{
// get the string
nsCOMPtr<nsISupportsString> strObject ( do_QueryInterface(aGenericData));
if (!strObject)
return NS_ERROR_FAILURE;
// alloc a buffer to hold the unicode title text
int bufsize = (MAXTITLELTH+1)*2;
PRUnichar * buffer = (PRUnichar*)nsMemory::Alloc(bufsize);
if (!buffer)
return NS_ERROR_FAILURE;
nsAutoString strData;
strObject->GetData(strData);
nsAutoString::const_iterator start, end;
strData.BeginReading(start);
strData.EndReading(end);
// skip over leading non-alphanumerics
for( ; start != end; ++start)
if (UniQueryChar( *start, CT_ALNUM))
break;
// move alphanumerics into the buffer & replace contiguous
// non-alnums with a single separator character
int ctr, sep;
for (ctr=0, sep=0; start != end && ctr < MAXTITLELTH; ++start) {
if (UniQueryChar( *start, CT_ALNUM)) {
buffer[ctr] = *start;
ctr++;
sep = 0;
}
else
if (!sep) {
buffer[ctr] = TITLESEPARATOR;
ctr++;
sep = 1;
}
}
// eliminate trailing separators & lone characters
// orphaned when the title is truncated
if (sep)
ctr--;
if (ctr >= MAXTITLELTH - sep && buffer[ctr-2] == TITLESEPARATOR)
ctr -= 2;
buffer[ctr] = 0;
// if we ended up with no alnums, call the result "text";
// otherwise, do NLS conversion
if (!ctr) {
*aTargetName = ToNewCString(NS_LITERAL_CSTRING("text"));
ctr = 1;
}
else
ctr = UnicodeToCodepage( nsDependentString(buffer), aTargetName);
// free our buffer, then exit
nsMemory::Free(buffer);
if (!ctr)
return NS_ERROR_FAILURE;
mSourceData = aGenericData;
mMimeType = kUnicodeMime;
return NS_OK;
}
// --------------------------------------------------------------------------
// nsIDragSessionOS2
// --------------------------------------------------------------------------
// DragOverMsg() provides minimal handling if a drag session is already
// in progress. If not, it assumes this is a native drag that has just
// entered the window and calls NativeDragEnter() to start a session.
NS_IMETHODIMP nsDragService::DragOverMsg(PDRAGINFO pdinfo, MRESULT &mr,
PRUint32* dragFlags)
{
nsresult rv = NS_ERROR_FAILURE;
if (!&mr || !dragFlags || !pdinfo || !DrgAccessDraginfo(pdinfo))
return rv;
*dragFlags = 0;
mr = MRFROM2SHORT(DOR_NEVERDROP, 0);
// examine the dragged item & "start" a drag session if OK;
// also, signal the need for a dragenter event
if (!mDoingDrag)
if (NS_SUCCEEDED(NativeDragEnter(pdinfo)))
*dragFlags |= DND_DISPATCHENTEREVENT;
// if we're in a drag, set it up to be dispatched
if (mDoingDrag) {
SetCanDrop(false);
switch (pdinfo->usOperation) {
case DO_COPY:
SetDragAction(DRAGDROP_ACTION_COPY);
break;
case DO_LINK:
SetDragAction(DRAGDROP_ACTION_LINK);
break;
default:
SetDragAction(DRAGDROP_ACTION_MOVE);
break;
}
if (mSourceNode)
*dragFlags |= DND_DISPATCHEVENT | DND_GETDRAGOVERRESULT | DND_MOZDRAG;
else
*dragFlags |= DND_DISPATCHEVENT | DND_GETDRAGOVERRESULT | DND_NATIVEDRAG;
rv = NS_OK;
}
DrgFreeDraginfo(pdinfo);
return rv;
}
// --------------------------------------------------------------------------
// Evaluates native drag data, and if acceptable, creates & stores
// a transferable with the available flavors (but not the data);
// if successful, it "starts" the session.
NS_IMETHODIMP nsDragService::NativeDragEnter(PDRAGINFO pdinfo)
{
nsresult rv = NS_ERROR_FAILURE;
bool isFQFile = FALSE;
bool isAtom = FALSE;
PDRAGITEM pditem = 0;
if (pdinfo->cditem != 1)
return rv;
pditem = DrgQueryDragitemPtr(pdinfo, 0);
if (pditem) {
if (DrgVerifyRMF(pditem, "DRM_ATOM", 0)) {
isAtom = TRUE;
rv = NS_OK;
}
else
if (DrgVerifyRMF(pditem, "DRM_DTSHARE", 0))
rv = NS_OK;
else
if (DrgVerifyRMF(pditem, "DRM_OS2FILE", 0)) {
rv = NS_OK;
if (pditem->hstrContainerName && pditem->hstrSourceName)
isFQFile = TRUE;
}
}
if (NS_SUCCEEDED(rv)) {
rv = NS_ERROR_FAILURE;
nsCOMPtr<nsITransferable> trans(
do_CreateInstance("@mozilla.org/widget/transferable;1", &rv));
if (trans) {
trans->Init(nsnull);
bool isUrl = DrgVerifyType(pditem, "UniformResourceLocator");
bool isAlt = (WinGetKeyState(HWND_DESKTOP, VK_ALT) & 0x8000);
// if this is a fully-qualified file or the item claims to be
// a Url, identify it as a Url & also offer it as html
if ((isFQFile && !isAlt) || isUrl) {
trans->AddDataFlavor(kURLMime);
trans->AddDataFlavor(kHTMLMime);
}
// everything is always "text"
trans->AddDataFlavor(kUnicodeMime);
// if we can create the array, initialize the session
nsCOMPtr<nsISupportsArray> transArray(
do_CreateInstance("@mozilla.org/supports-array;1", &rv));
if (transArray) {
transArray->InsertElementAt(trans, 0);
mSourceDataItems = transArray;
// add the dragged data to the transferable if we have easy access
// to it (i.e. no need to read a file or request rendering); for
// URLs, we'll skip creating a title until the drop occurs
nsXPIDLCString someText;
if (isAtom) {
if (NS_SUCCEEDED(GetAtom(pditem->ulItemID, getter_Copies(someText))))
NativeDataToTransferable( someText.get(), 0, isUrl);
}
else
if (isFQFile && !isAlt &&
NS_SUCCEEDED(GetFileName(pditem, getter_Copies(someText)))) {
nsCOMPtr<nsIFile> file;
if (NS_SUCCEEDED(NS_NewNativeLocalFile(someText, true,
getter_AddRefs(file)))) {
nsCAutoString textStr;
NS_GetURLSpecFromFile(file, textStr);
if (!textStr.IsEmpty()) {
someText.Assign(ToNewCString(textStr));
NativeDataToTransferable( someText.get(), 0, TRUE);
}
}
}
mSourceNode = 0;
mSourceDocument = 0;
mDoingDrag = TRUE;
rv = NS_OK;
}
}
}
return rv;
}
// --------------------------------------------------------------------------
// Invoked after a dragover event has been dispatched, this constructs
// a reply to DM_DRAGOVER based on the canDrop & dragAction attributes.
NS_IMETHODIMP nsDragService::GetDragoverResult(MRESULT& mr)
{
nsresult rv = NS_ERROR_FAILURE;
if (!&mr)
return rv;
if (mDoingDrag) {
bool canDrop = false;
USHORT usDrop;
GetCanDrop(&canDrop);
if (canDrop)
usDrop = DOR_DROP;
else
usDrop = DOR_NODROP;
PRUint32 action;
USHORT usOp;
GetDragAction(&action);
if (action & DRAGDROP_ACTION_COPY)
usOp = DO_COPY;
else
if (action & DRAGDROP_ACTION_LINK)
usOp = DO_LINK;
else {
if (mSourceNode)
usOp = DO_MOVE;
else
usOp = DO_UNKNOWN+1;
if (action == DRAGDROP_ACTION_NONE)
usDrop = DOR_NODROP;
}
mr = MRFROM2SHORT(usDrop, usOp);
rv = NS_OK;
}
else
mr = MRFROM2SHORT(DOR_NEVERDROP, 0);
return rv;
}
// --------------------------------------------------------------------------
// have the client dispatch the event, then call ExitSession()
NS_IMETHODIMP nsDragService::DragLeaveMsg(PDRAGINFO pdinfo, PRUint32* dragFlags)
{
if (!mDoingDrag || !dragFlags)
return NS_ERROR_FAILURE;
if (mSourceNode)
*dragFlags = DND_DISPATCHEVENT | DND_EXITSESSION | DND_MOZDRAG;
else
*dragFlags = DND_DISPATCHEVENT | DND_EXITSESSION | DND_NATIVEDRAG;
return NS_OK;
}
// --------------------------------------------------------------------------
// DropHelp occurs when you press F1 during a drag; apparently,
// it's like a regular drop in that the target has to do clean up
NS_IMETHODIMP nsDragService::DropHelpMsg(PDRAGINFO pdinfo, PRUint32* dragFlags)
{
if (!mDoingDrag)
return NS_ERROR_FAILURE;
if (pdinfo && DrgAccessDraginfo(pdinfo)) {
DrgDeleteDraginfoStrHandles(pdinfo);
DrgFreeDraginfo(pdinfo);
}
if (!dragFlags)
return NS_ERROR_FAILURE;
if (mSourceNode)
*dragFlags = DND_DISPATCHEVENT | DND_EXITSESSION | DND_MOZDRAG;
else
*dragFlags = DND_DISPATCHEVENT | DND_EXITSESSION | DND_NATIVEDRAG;
return NS_OK;
}
// --------------------------------------------------------------------------
// for native drags, clean up;
// for all drags, signal that Moz is no longer in d&d mode
NS_IMETHODIMP nsDragService::ExitSession(PRUint32* dragFlags)
{
if (!mDoingDrag)
return NS_ERROR_FAILURE;
if (!mSourceNode) {
mSourceDataItems = 0;
mDataTransfer = 0;
mDoingDrag = FALSE;
// if we created a temp file, delete it
if (gTempFile) {
DosDelete(gTempFile);
nsMemory::Free(gTempFile);
gTempFile = 0;
}
}
if (!dragFlags)
return NS_ERROR_FAILURE;
*dragFlags = 0;
return NS_OK;
}
// --------------------------------------------------------------------------
// If DropMsg() is presented with native data that has to be rendered,
// the drop event & cleanup will be defered until the client's window
// has received a render-complete msg.
NS_IMETHODIMP nsDragService::DropMsg(PDRAGINFO pdinfo, HWND hwnd,
PRUint32* dragFlags)
{
if (!mDoingDrag || !dragFlags || !pdinfo || !DrgAccessDraginfo(pdinfo))
return NS_ERROR_FAILURE;
switch (pdinfo->usOperation) {
case DO_MOVE:
SetDragAction(DRAGDROP_ACTION_MOVE);
break;
case DO_COPY:
SetDragAction(DRAGDROP_ACTION_COPY);
break;
case DO_LINK:
SetDragAction(DRAGDROP_ACTION_LINK);
break;
default: // avoid "moving" (i.e. deleting) native text/objects
if (mSourceNode)
SetDragAction(DRAGDROP_ACTION_MOVE);
else
SetDragAction(DRAGDROP_ACTION_COPY);
break;
}
// if this is a native drag, move the source data to a transferable;
// request rendering if needed
nsresult rv = NS_OK;
bool rendering = false;
if (!mSourceNode)
rv = NativeDrop( pdinfo, hwnd, &rendering);
// note: NativeDrop() sends an end-conversation msg to native
// sources but nothing sends them to Mozilla - however, Mozilla
// (i.e. nsDragService) doesn't need them, so...
// if rendering, the action flags are off because we don't want
// the client to do anything yet; the status flags are off because
// we'll be exiting d&d mode before the next screen update occurs
if (rendering)
*dragFlags = 0;
else {
// otherwise, set the flags & free the native drag structures
*dragFlags = DND_EXITSESSION;
if (NS_SUCCEEDED(rv)) {
if (mSourceNode)
*dragFlags |= DND_DISPATCHEVENT | DND_INDROP | DND_MOZDRAG;
else
*dragFlags |= DND_DISPATCHEVENT | DND_INDROP | DND_NATIVEDRAG;
}
DrgDeleteDraginfoStrHandles(pdinfo);
DrgFreeDraginfo(pdinfo);
rv = NS_OK;
}
return rv;
}
// --------------------------------------------------------------------------
// Invoked by DropMsg to fill a transferable with native data;
// if necessary, requests the source to render it.
NS_IMETHODIMP nsDragService::NativeDrop(PDRAGINFO pdinfo, HWND hwnd,
bool* rendering)
{
*rendering = false;
nsresult rv = NS_ERROR_FAILURE;
PDRAGITEM pditem = DrgQueryDragitemPtr(pdinfo, 0);
if (!pditem)
return rv;
nsXPIDLCString dropText;
bool isUrl = DrgVerifyType(pditem, "UniformResourceLocator");
// identify format; the order of evaluation here is important
// DRM_ATOM - DragText stores up to 255 chars in a Drg API atom
// DRM_DTSHARE - DragText renders up to 1mb to named shared mem
if (DrgVerifyRMF(pditem, "DRM_ATOM", 0))
rv = GetAtom(pditem->ulItemID, getter_Copies(dropText));
else
if (DrgVerifyRMF(pditem, "DRM_DTSHARE", 0)) {
rv = RenderToDTShare( pditem, hwnd);
if (NS_SUCCEEDED(rv))
*rendering = true;
}
// DRM_OS2FILE - get the file's path or contents if it exists;
// request rendering if it doesn't
else
if (DrgVerifyRMF(pditem, "DRM_OS2FILE", 0)) {
bool isAlt = (WinGetKeyState(HWND_DESKTOP, VK_ALT) & 0x8000);
// the file has to be rendered - currently, we only present
// its content, not its name, to Moz to avoid conflicts
if (!pditem->hstrContainerName || !pditem->hstrSourceName) {
rv = RenderToOS2File( pditem, hwnd);
if (NS_SUCCEEDED(rv))
*rendering = true;
}
// for Url objects and 'Alt+Drop', get the file's contents;
// otherwise, convert it's path to a Url
else {
nsXPIDLCString fileName;
if (NS_SUCCEEDED(GetFileName(pditem, getter_Copies(fileName)))) {
if (isUrl || isAlt)
rv = GetFileContents(fileName.get(), getter_Copies(dropText));
else {
isUrl = true;
nsCOMPtr<nsIFile> file;
if (NS_SUCCEEDED(NS_NewNativeLocalFile(fileName,
true, getter_AddRefs(file)))) {
nsCAutoString textStr;
NS_GetURLSpecFromFile(file, textStr);
if (!textStr.IsEmpty()) {
dropText.Assign(ToNewCString(textStr));
rv = NS_OK;
}
}
} // filename as Url
} // got filename
} // existing files
} // DRM_OS2FILE
// if OK, put what data there is in the transferable; this could be
// everything needed or just the title of a Url that needs rendering
if (NS_SUCCEEDED(rv)) {
// for Urls, get the title & remove any linefeeds
nsXPIDLCString titleText;
if (isUrl &&
pditem->hstrTargetName &&
NS_SUCCEEDED(GetAtom(pditem->hstrTargetName, getter_Copies(titleText))))
for (char* ptr=strchr(titleText.BeginWriting(),'\n'); ptr; ptr=strchr(ptr, '\n'))
*ptr = ' ';
rv = NativeDataToTransferable( dropText.get(), titleText.get(), isUrl);
}
// except for renderings, tell the source we're done with the data
if (!*rendering)
DrgSendTransferMsg( pditem->hwndItem, DM_ENDCONVERSATION,
(MPARAM)pditem->ulItemID,
(MPARAM)DMFL_TARGETSUCCESSFUL);
return (rv);
}
// --------------------------------------------------------------------------
// Because RenderCompleteMsg() is called after the native (PM) drag
// session has ended, all of the drag status flags should be off.
//
// FYI... PM's asynchronous rendering mechanism is not compatible with
// nsIDataFlavorProvider which expects data to be rendered synchronously
NS_IMETHODIMP nsDragService::RenderCompleteMsg(PDRAGTRANSFER pdxfer,
USHORT usResult, PRUint32* dragFlags)
{
nsresult rv = NS_ERROR_FAILURE;
if (!mDoingDrag || !pdxfer)
return rv;
// this msg should never come from Moz - if it does, fail
if (!mSourceNode)
rv = NativeRenderComplete(pdxfer, usResult);
// DrgQueryDraginfoPtrFromDragitem() doesn't work - this does
PDRAGINFO pdinfo = (PDRAGINFO)MAKEULONG(0x2c, HIUSHORT(pdxfer->pditem));
DrgDeleteStrHandle(pdxfer->hstrSelectedRMF);
DrgDeleteStrHandle(pdxfer->hstrRenderToName);
DrgFreeDragtransfer(pdxfer);
// if the source is Moz, don't touch pdinfo - it's been freed already
if (pdinfo && !mSourceNode) {
DrgDeleteDraginfoStrHandles(pdinfo);
DrgFreeDraginfo(pdinfo);
}
// this shouldn't happen
if (!dragFlags)
return (ExitSession(dragFlags));
// d&d is over, so the DND_DragStatus flags should all be off
*dragFlags = DND_EXITSESSION;
if (NS_SUCCEEDED(rv))
*dragFlags |= DND_DISPATCHEVENT;
// lie so the client will honor the exit-session flag
return NS_OK;
}
// --------------------------------------------------------------------------
// this is here to provide a false sense of symmetry with the other
// method-pairs - rendered data always comes from a native source
NS_IMETHODIMP nsDragService::NativeRenderComplete(PDRAGTRANSFER pdxfer,
USHORT usResult)
{
nsresult rv = NS_ERROR_FAILURE;
nsXPIDLCString rmf;
// identify the rendering mechanism, then get the data
if (NS_SUCCEEDED(GetAtom(pdxfer->hstrSelectedRMF, getter_Copies(rmf)))) {
nsXPIDLCString dropText;
if (!strcmp(rmf.get(), DTSHARE_RMF))
rv = RenderToDTShareComplete(pdxfer, usResult, getter_Copies(dropText));
else
if (!strcmp(rmf.get(), OS2FILE_TXTRMF) ||
!strcmp(rmf.get(), OS2FILE_UNKRMF))
rv = RenderToOS2FileComplete(pdxfer, usResult, true,
getter_Copies(dropText));
if (NS_SUCCEEDED(rv)) {
bool isUrl = false;
IsDataFlavorSupported(kURLMime, &isUrl);
rv = NativeDataToTransferable( dropText.get(), 0, isUrl);
}
}
DrgSendTransferMsg(pdxfer->hwndClient, DM_ENDCONVERSATION,
(MPARAM)pdxfer->ulTargetInfo,
(MPARAM)DMFL_TARGETSUCCESSFUL);
return rv;
}
// --------------------------------------------------------------------------
// fills the transferable created by NativeDragEnter with
// the set of flavors and data the target will see onDrop
NS_IMETHODIMP nsDragService::NativeDataToTransferable( PCSZ pszText,
PCSZ pszTitle, bool isUrl)
{
nsresult rv = NS_ERROR_FAILURE;
// the transferable should have been created on DragEnter
if (!mSourceDataItems)
return rv;
nsCOMPtr<nsISupports> genericItem;
mSourceDataItems->GetElementAt(0, getter_AddRefs(genericItem));
nsCOMPtr<nsITransferable> trans (do_QueryInterface(genericItem));
if (!trans)
return rv;
// remove invalid flavors
if (!isUrl) {
trans->RemoveDataFlavor(kURLMime);
trans->RemoveDataFlavor(kHTMLMime);
}
// if there's no text, exit - but first see if we have the title of
// a Url that needs to be rendered; if so, stash it for later use
if (!pszText || !*pszText) {
if (isUrl && pszTitle && *pszTitle) {
nsXPIDLString outTitle;
if (CodepageToUnicode(nsDependentCString(pszTitle),
getter_Copies(outTitle))) {
nsCOMPtr<nsISupportsString> urlPrimitive(
do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
if (urlPrimitive ) {
urlPrimitive->SetData(outTitle);
trans->SetTransferData(kURLDescriptionMime, urlPrimitive,
2*outTitle.Length());
}
}
}
return NS_OK;
}
nsXPIDLString outText;
if (!CodepageToUnicode(nsDependentCString(pszText), getter_Copies(outText)))
return rv;
if (isUrl) {
// if no title was supplied, see if it was stored in the transferable
nsXPIDLString outTitle;
if (pszTitle && *pszTitle) {
if (!CodepageToUnicode(nsDependentCString(pszTitle),
getter_Copies(outTitle)))
return rv;
}
else {
PRUint32 len;
nsCOMPtr<nsISupports> genericData;
if (NS_SUCCEEDED(trans->GetTransferData(kURLDescriptionMime,
getter_AddRefs(genericData), &len))) {
nsCOMPtr<nsISupportsString> strObject(do_QueryInterface(genericData));
if (strObject)
strObject->GetData(outTitle);
}
}
// construct the Url flavor
nsCOMPtr<nsISupportsString> urlPrimitive(
do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
if (urlPrimitive ) {
if (outTitle.IsEmpty()) {
urlPrimitive->SetData(outText);
trans->SetTransferData(kURLMime, urlPrimitive, 2*outText.Length());
}
else {
nsString urlStr( outText + NS_LITERAL_STRING("\n") + outTitle);
urlPrimitive->SetData(urlStr);
trans->SetTransferData(kURLMime, urlPrimitive, 2*urlStr.Length());
}
rv = NS_OK;
}
// construct the HTML flavor - for supported graphics,
// use an IMG tag, for all others create a link
nsCOMPtr<nsISupportsString> htmlPrimitive(
do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
if (htmlPrimitive ) {
nsString htmlStr;
nsCOMPtr<nsIURI> uri;
rv = NS_ERROR_FAILURE;
if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(uri), pszText))) {
nsCOMPtr<nsIURL> url (do_QueryInterface(uri));
if (url) {
nsCAutoString extension;
url->GetFileExtension(extension);
if (!extension.IsEmpty()) {
if (extension.LowerCaseEqualsLiteral("gif") ||
extension.LowerCaseEqualsLiteral("jpg") ||
extension.LowerCaseEqualsLiteral("png") ||
extension.LowerCaseEqualsLiteral("jpeg"))
rv = NS_OK;
}
}
}
if (NS_SUCCEEDED(rv))
htmlStr.Assign(NS_LITERAL_STRING("<img src=\"") +
outText +
NS_LITERAL_STRING("\" alt=\"") +
outTitle +
NS_LITERAL_STRING("\"/>") );
else
htmlStr.Assign(NS_LITERAL_STRING("<a href=\"") +
outText +
NS_LITERAL_STRING("\">") +
(outTitle.IsEmpty() ? outText : outTitle) +
NS_LITERAL_STRING("</a>") );
htmlPrimitive->SetData(htmlStr);
trans->SetTransferData(kHTMLMime, htmlPrimitive, 2*htmlStr.Length());
rv = NS_OK;
}
}
// add the Text flavor
nsCOMPtr<nsISupportsString> textPrimitive(
do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
if (textPrimitive ) {
textPrimitive->SetData(nsDependentString(outText));
trans->SetTransferData(kUnicodeMime, textPrimitive, 2*outText.Length());
rv = NS_OK;
}
// return OK if we put anything in the transferable
return rv;
}
// --------------------------------------------------------------------------
// Helper functions
// --------------------------------------------------------------------------
// currently, the same filename is used for every render request;
// it is deleted when the drag session ends
nsresult RenderToOS2File( PDRAGITEM pditem, HWND hwnd)
{
nsresult rv = NS_ERROR_FAILURE;
nsXPIDLCString fileName;
if (NS_SUCCEEDED(GetTempFileName(getter_Copies(fileName)))) {
const char * pszRMF;
if (DrgVerifyRMF(pditem, "DRM_OS2FILE", "DRF_TEXT"))
pszRMF = OS2FILE_TXTRMF;
else
pszRMF = OS2FILE_UNKRMF;
rv = RequestRendering( pditem, hwnd, pszRMF, fileName.get());
}
return rv;
}
// --------------------------------------------------------------------------
// return a buffer with the rendered file's Url or contents
nsresult RenderToOS2FileComplete(PDRAGTRANSFER pdxfer, USHORT usResult,
bool content, char** outText)
{
nsresult rv = NS_ERROR_FAILURE;
// for now, override content flag & always return content
content = true;
if (usResult & DMFL_RENDEROK) {
if (NS_SUCCEEDED(GetAtom( pdxfer->hstrRenderToName, &gTempFile))) {
if (content)
rv = GetFileContents(gTempFile, outText);
else {
nsCOMPtr<nsIFile> file;
if (NS_SUCCEEDED(NS_NewNativeLocalFile(nsDependentCString(gTempFile),
true, getter_AddRefs(file)))) {
nsCAutoString textStr;
NS_GetURLSpecFromFile(file, textStr);
if (!textStr.IsEmpty()) {
*outText = ToNewCString(textStr);
rv = NS_OK;
}
}
}
}
}
// gTempFile will be deleted when ExitSession() is called
return rv;
}
// --------------------------------------------------------------------------
// DTShare uses 1mb of uncommitted named-shared memory
// (next time I'll do it differently - rw)
nsresult RenderToDTShare( PDRAGITEM pditem, HWND hwnd)
{
nsresult rv;
void * pMem;
#ifdef MOZ_OS2_HIGH_MEMORY
APIRET rc = DosAllocSharedMem( &pMem, DTSHARE_NAME, 0x100000,
PAG_WRITE | PAG_READ | OBJ_ANY);
if (rc != NO_ERROR &&
rc != ERROR_ALREADY_EXISTS) { // Did the kernel handle OBJ_ANY?
// Try again without OBJ_ANY and if the first failure was not caused
// by OBJ_ANY then we will get the same failure, else we have taken
// care of pre-FP13 systems where the kernel couldn't handle it.
rc = DosAllocSharedMem( &pMem, DTSHARE_NAME, 0x100000,
PAG_WRITE | PAG_READ);
}
#else
APIRET rc = DosAllocSharedMem( &pMem, DTSHARE_NAME, 0x100000,
PAG_WRITE | PAG_READ);
#endif
if (rc == ERROR_ALREADY_EXISTS)
rc = DosGetNamedSharedMem( &pMem, DTSHARE_NAME,
PAG_WRITE | PAG_READ);
if (rc)
rv = NS_ERROR_FAILURE;
else
rv = RequestRendering( pditem, hwnd, DTSHARE_RMF, DTSHARE_NAME);
return rv;
}
// --------------------------------------------------------------------------
// return a buffer with the rendered text
nsresult RenderToDTShareComplete(PDRAGTRANSFER pdxfer, USHORT usResult,
char** outText)
{
nsresult rv = NS_ERROR_FAILURE;
void * pMem;
char * pszText = 0;
APIRET rc = DosGetNamedSharedMem( &pMem, DTSHARE_NAME, PAG_WRITE | PAG_READ);
if (!rc) {
if (usResult & DMFL_RENDEROK) {
pszText = (char*)nsMemory::Alloc( ((ULONG*)pMem)[0] + 1);
if (pszText) {
strcpy(pszText, &((char*)pMem)[sizeof(ULONG)] );
RemoveCarriageReturns(pszText);
*outText = pszText;
rv = NS_OK;
}
}
// using DosGetNamedSharedMem() on memory we allocated appears
// to increment its usage ctr, so we have to free it 2x
DosFreeMem(pMem);
DosFreeMem(pMem);
}
return rv;
}
// --------------------------------------------------------------------------
// a generic request dispatcher
nsresult RequestRendering( PDRAGITEM pditem, HWND hwnd, PCSZ pRMF, PCSZ pName)
{
PDRAGTRANSFER pdxfer = DrgAllocDragtransfer( 1);
if (!pdxfer)
return NS_ERROR_FAILURE;
pdxfer->cb = sizeof(DRAGTRANSFER);
pdxfer->hwndClient = hwnd;
pdxfer->pditem = pditem;
pdxfer->hstrSelectedRMF = DrgAddStrHandle( pRMF);
pdxfer->hstrRenderToName = 0;
pdxfer->ulTargetInfo = pditem->ulItemID;
pdxfer->usOperation = (USHORT)DO_COPY;
pdxfer->fsReply = 0;
// send the msg before setting a render-to name
if (pditem->fsControl & DC_PREPAREITEM)
DrgSendTransferMsg( pditem->hwndItem, DM_RENDERPREPARE, (MPARAM)pdxfer, 0);
pdxfer->hstrRenderToName = DrgAddStrHandle( pName);
// send the msg after setting a render-to name
if ((pditem->fsControl & (DC_PREPARE | DC_PREPAREITEM)) == DC_PREPARE)
DrgSendTransferMsg( pditem->hwndItem, DM_RENDERPREPARE, (MPARAM)pdxfer, 0);
// ask the source to render the selected item
if (!DrgSendTransferMsg( pditem->hwndItem, DM_RENDER, (MPARAM)pdxfer, 0))
return NS_ERROR_FAILURE;
return NS_OK;
}
// --------------------------------------------------------------------------
// return a ptr to a buffer containing the text associated
// with a drag atom; the caller frees the buffer
nsresult GetAtom( ATOM aAtom, char** outText)
{
nsresult rv = NS_ERROR_FAILURE;
ULONG ulInLength = DrgQueryStrNameLen(aAtom);
if (ulInLength) {
char* pszText = (char*)nsMemory::Alloc(++ulInLength);
if (pszText) {
DrgQueryStrName(aAtom, ulInLength, pszText);
RemoveCarriageReturns(pszText);
*outText = pszText;
rv = NS_OK;
}
}
return rv;
}
// --------------------------------------------------------------------------
// return a ptr to a buffer containing the file path specified
// in the dragitem; the caller frees the buffer
nsresult GetFileName(PDRAGITEM pditem, char** outText)
{
nsresult rv = NS_ERROR_FAILURE;
ULONG cntCnr = DrgQueryStrNameLen(pditem->hstrContainerName);
ULONG cntSrc = DrgQueryStrNameLen(pditem->hstrSourceName);
char* pszText = (char*)nsMemory::Alloc(cntCnr+cntSrc+1);
if (pszText) {
DrgQueryStrName(pditem->hstrContainerName, cntCnr+1, pszText);
DrgQueryStrName(pditem->hstrSourceName, cntSrc+1, &pszText[cntCnr]);
pszText[cntCnr+cntSrc] = 0;
*outText = pszText;
rv = NS_OK;
}
return rv;
}
// --------------------------------------------------------------------------
// read the file; if successful, return a ptr to its contents;
// the caller frees the buffer
nsresult GetFileContents(PCSZ pszPath, char** outText)
{
nsresult rv = NS_ERROR_FAILURE;
char* pszText = 0;
if (pszPath) {
FILE *fp = fopen(pszPath, "r");
if (fp) {
fseek(fp, 0, SEEK_END);
ULONG filesize = ftell(fp);
fseek(fp, 0, SEEK_SET);
if (filesize > 0) {
size_t readsize = (size_t)filesize;
pszText = (char*)nsMemory::Alloc(readsize+1);
if (pszText) {
readsize = fread((void *)pszText, 1, readsize, fp);
if (readsize) {
pszText[readsize] = '\0';
RemoveCarriageReturns(pszText);
*outText = pszText;
rv = NS_OK;
}
else {
nsMemory::Free(pszText);
pszText = 0;
}
}
}
fclose(fp);
}
}
return rv;
}
// --------------------------------------------------------------------------
// currently, this returns the same path & filename every time
nsresult GetTempFileName(char** outText)
{
char * pszText = (char*)nsMemory::Alloc(CCHMAXPATH);
if (!pszText)
return NS_ERROR_FAILURE;
const char * pszPath;
if (!DosScanEnv("TEMP", &pszPath) || !DosScanEnv("TMP", &pszPath))
strcpy(pszText, pszPath);
else
if (DosQueryPathInfo(".\\.", FIL_QUERYFULLNAME, pszText, CCHMAXPATH))
pszText[0] = 0;
strcat(pszText, "\\");
strcat(pszText, OS2FILE_NAME);
*outText = pszText;
return NS_OK;
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
// set the file's .TYPE and .SUBJECT EAs; since this is non-critical
// (though highly desirable), errors aren't reported
void SaveTypeAndSource(nsIFile *file, nsIDOMDocument *domDoc,
PCSZ pszType)
{
if (!file)
return;
nsCOMPtr<nsILocalFileOS2> os2file(do_QueryInterface(file));
if (!os2file ||
NS_FAILED(os2file->SetFileTypes(nsDependentCString(pszType))))
return;
// since the filetype has to be saved, this function
// may be called even if there isn't any document
if (!domDoc)
return;
nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc));
if (!doc)
return;
// this gets the top-level content URL when frames are used;
// when nextDoc is zero, currDoc is the browser window, and
// prevDoc points to the content;
// note: neither GetParentDocument() nor GetDocumentURI()
// refcount the pointers they return, so nsCOMPtr isn't needed
nsIDocument *prevDoc;
nsIDocument *currDoc = doc;
nsIDocument *nextDoc = doc;
do {
prevDoc = currDoc;
currDoc = nextDoc;
nextDoc = currDoc->GetParentDocument();
} while (nextDoc);
nsIURI* srcUri = prevDoc->GetDocumentURI();
if (!srcUri)
return;
// identifying content as coming from chrome is none too useful...
bool ignore = false;
srcUri->SchemeIs("chrome", &ignore);
if (ignore)
return;
nsCAutoString url;
srcUri->GetSpec(url);
os2file->SetFileSource(url);
return;
}
// --------------------------------------------------------------------------
// to do: this needs to take into account the current page's encoding
// if it is different than the PM codepage
int UnicodeToCodepage(const nsAString& aString, char **aResult)
{
nsAutoCharBuffer buffer;
PRInt32 bufLength;
WideCharToMultiByte(0, PromiseFlatString(aString).get(), aString.Length(),
buffer, bufLength);
*aResult = ToNewCString(nsDependentCString(buffer.Elements()));
return bufLength;
}
// --------------------------------------------------------------------------
int CodepageToUnicode(const nsACString& aString, PRUnichar **aResult)
{
nsAutoChar16Buffer buffer;
PRInt32 bufLength;
MultiByteToWideChar(0, PromiseFlatCString(aString).get(),
aString.Length(), buffer, bufLength);
*aResult = ToNewUnicode(nsDependentString(buffer.Elements()));
return bufLength;
}
// --------------------------------------------------------------------------
// removes carriage returns in-place; it should only be used on
// raw text buffers that haven't been assigned to a string object
void RemoveCarriageReturns(char * pszText)
{
ULONG cnt;
char * next;
char * source;
char * target;
target = strchr(pszText, 0x0d);
if (!target)
return;
source = target + 1;
while ((next = strchr(source, 0x0d)) != 0) {
cnt = next - source;
memcpy(target, source, cnt);
target += cnt;
source = next + 1;
}
strcpy(target, source);
return;
}
// --------------------------------------------------------------------------