Bug 338478. Add CF_HDROP support to Mozilla for file drag and drop. r=roc
authorKyle Huey <me@kylehuey.com>
Thu, 10 Dec 2009 00:15:56 -0500
changeset 35690 e2f9cde1e66d427834e800fcf4a66d951c96ea4e
parent 35689 ec5877cfb2178a0c1ce3ce79e58831c1fe376b53
child 35691 215ca7a9e2ba566194dd8194f41e8b14defd95a9
push id10682
push userphilringnalda@gmail.com
push dateMon, 14 Dec 2009 02:11:07 +0000
treeherdermozilla-central@bc0849430750 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs338478
milestone1.9.3a1pre
Bug 338478. Add CF_HDROP support to Mozilla for file drag and drop. r=roc Bug 338478. Add CF_HDROP support to Mozilla for file drag and drop. r=roc
widget/src/windows/Makefile.in
widget/src/windows/nsDataObj.cpp
widget/src/windows/nsDataObj.h
widget/src/windows/tests/Makefile.in
widget/src/windows/tests/TestWinDND.cpp
--- a/widget/src/windows/Makefile.in
+++ b/widget/src/windows/Makefile.in
@@ -127,11 +127,15 @@ FORCE_STATIC_LIB = 1
 ifdef WINCE
 EXTRA_DSO_LDOPTS += ddraw.lib
 endif
 
 ifndef WINCE
 ENABLE_CXX_EXCEPTIONS = 1
 endif
 
+ifdef ENABLE_TESTS
+TOOL_DIRS  += tests
+endif
+
 include $(topsrcdir)/config/rules.mk
 
 CXXFLAGS += $(MOZ_CAIRO_CFLAGS)
--- a/widget/src/windows/nsDataObj.cpp
+++ b/widget/src/windows/nsDataObj.cpp
@@ -20,16 +20,17 @@
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Sean Echevarria <Sean@Beatnik.com>
  *   Blake Ross <blaker@netscape.com>
  *   Brodie Thiesfield <brofield@jellycan.com>
  *   Masayuki Nakano <masayuki@d-toybox.com>
  *   David Gardiner <david.gardiner@unisa.edu.au>
+ *   Kyle Huey <me@kylehuey.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -1362,26 +1363,96 @@ HRESULT nsDataObj::GetFile(FORMATETC& aF
   // if not fail. 
   PRUint32 dfInx = 0;
   ULONG count;
   FORMATETC fe;
   m_enumFE->Reset();
   PRBool found = PR_FALSE;
   while (NOERROR == m_enumFE->Next(1, &fe, &count)
          && dfInx < mDataFlavors.Length()) {
-    if (mDataFlavors[dfInx].EqualsLiteral(kNativeImageMime)) {
+    if (mDataFlavors[dfInx].EqualsLiteral(kNativeImageMime) ||
+        mDataFlavors[dfInx].EqualsLiteral(kFileMime)) {
       found = PR_TRUE;
       break;
     }
     dfInx++;
   }
 
   if (!found)
     return E_FAIL;
 
+  if (mDataFlavors[dfInx].EqualsLiteral(kNativeImageMime))
+    return DropImage(aFE, aSTG);
+  return DropFile(aFE, aSTG);
+}
+
+HRESULT nsDataObj::DropFile(FORMATETC& aFE, STGMEDIUM& aSTG)
+{
+  nsresult rv;
+  PRUint32 len = 0;
+  nsCOMPtr<nsISupports> genericDataWrapper;
+
+  mTransferable->GetTransferData(kFileMime, getter_AddRefs(genericDataWrapper),
+                                 &len);
+  nsCOMPtr<nsIFile> file ( do_QueryInterface(genericDataWrapper) );
+
+  if (!file)
+  {
+    nsCOMPtr<nsISupportsInterfacePointer> ptr(do_QueryInterface(genericDataWrapper));
+    if (ptr)
+      ptr->GetData(getter_AddRefs(file));
+  }
+
+  if (!file)
+    return E_FAIL;
+
+  aSTG.tymed = TYMED_HGLOBAL;
+  aSTG.pUnkForRelease = NULL;
+
+  nsAutoString path;
+  rv = file->GetPath(path);
+  if (NS_FAILED(rv))
+    return E_FAIL;
+
+  PRUint32 allocLen = path.Length() + 2;
+  HGLOBAL hGlobalMemory = NULL;
+  PRUnichar *dest, *dest2;
+
+  hGlobalMemory = GlobalAlloc(GMEM_MOVEABLE, sizeof(DROPFILES) +
+                                             allocLen * sizeof(PRUnichar));
+  if (!hGlobalMemory)
+    return E_FAIL;
+
+  DROPFILES* pDropFile = (DROPFILES*)GlobalLock(hGlobalMemory);
+
+  // First, populate the drop file structure
+  pDropFile->pFiles = sizeof(DROPFILES); //Offset to start of file name string
+  pDropFile->fNC    = 0;
+  pDropFile->pt.x   = 0;
+  pDropFile->pt.y   = 0;
+  pDropFile->fWide  = TRUE;
+
+  // Copy the filename right after the DROPFILES structure
+  dest = (PRUnichar*)(((char*)pDropFile) + pDropFile->pFiles);
+  memcpy(dest, path.get(), (allocLen - 1) * sizeof(PRUnichar));
+
+  // Two null characters are needed at the end of the file name.
+  // Lookup the CF_HDROP shell clipboard format for more info.
+  // Add the second null character right after the first one.
+  dest[allocLen - 1] = L'\0';
+
+  GlobalUnlock(hGlobalMemory);
+
+  aSTG.hGlobal = hGlobalMemory;
+
+  return S_OK;
+}
+
+HRESULT nsDataObj::DropImage(FORMATETC& aFE, STGMEDIUM& aSTG)
+{
   nsresult rv;
   PRUint32 len = 0;
   nsCOMPtr<nsISupports> genericDataWrapper;
 
   mTransferable->GetTransferData(kNativeImageMime, getter_AddRefs(genericDataWrapper), &len);
   nsCOMPtr<imgIContainer> image ( do_QueryInterface(genericDataWrapper) );
   
   if (!image) {
--- a/widget/src/windows/nsDataObj.h
+++ b/widget/src/windows/nsDataObj.h
@@ -51,17 +51,17 @@
 #include "nsIURI.h"
 #include "nsIInputStream.h"
 #include "nsIChannel.h"
 #include "nsTPtrArray.h"
 
 // XXX for older version of PSDK where IAsyncOperation and related stuff is not available
 // but thisdefine  should be removed when parocles config is updated
 #ifndef __IAsyncOperation_INTERFACE_DEFINED__
-// IAsyncOperation inerface definition
+// IAsyncOperation interface definition
 EXTERN_C const IID IID_IAsyncOperation;
 
 MIDL_INTERFACE("3D8B0590-F691-11d2-8EA9-006097DF5BD4")
 IAsyncOperation : public IUnknown
 {
   virtual HRESULT STDMETHODCALLTYPE SetAsyncMode(BOOL fDoOpAsync) = 0;
   virtual HRESULT STDMETHODCALLTYPE GetAsyncMode(BOOL *pfIsOpAsync) = 0;
   virtual HRESULT STDMETHODCALLTYPE StartOperation(IBindCtx *pbcReserved) = 0;
@@ -223,16 +223,19 @@ class nsDataObj : public IDataObject,
 		virtual HRESULT AddSetFormat(FORMATETC&  FE);
 		virtual HRESULT AddGetFormat(FORMATETC&  FE);
 
 		virtual HRESULT GetFile ( FORMATETC& aFE, STGMEDIUM& aSTG );
 		virtual HRESULT GetText ( const nsACString& aDF, FORMATETC& aFE, STGMEDIUM & aSTG );
 		virtual HRESULT GetBitmap ( const nsACString& inFlavor, FORMATETC&  FE, STGMEDIUM&  STM);
 		virtual HRESULT GetDib ( const nsACString& inFlavor, FORMATETC &, STGMEDIUM & aSTG );
 		virtual HRESULT GetMetafilePict(FORMATETC&  FE, STGMEDIUM&  STM);
+        
+        virtual HRESULT DropImage( FORMATETC& aFE, STGMEDIUM& aSTG );
+        virtual HRESULT DropFile( FORMATETC& aFE, STGMEDIUM& aSTG );
 
     virtual HRESULT GetUniformResourceLocator ( FORMATETC& aFE, STGMEDIUM& aSTG, PRBool aIsUnicode ) ;
     virtual HRESULT ExtractUniformResourceLocatorA ( FORMATETC& aFE, STGMEDIUM& aSTG ) ;
     virtual HRESULT ExtractUniformResourceLocatorW ( FORMATETC& aFE, STGMEDIUM& aSTG ) ;
     virtual HRESULT GetFileDescriptor ( FORMATETC& aFE, STGMEDIUM& aSTG, PRBool aIsUnicode ) ;
     virtual HRESULT GetFileContents ( FORMATETC& aFE, STGMEDIUM& aSTG ) ;
     virtual HRESULT GetPreferredDropEffect ( FORMATETC& aFE, STGMEDIUM& aSTG );
    
new file mode 100644
--- /dev/null
+++ b/widget/src/windows/tests/Makefile.in
@@ -0,0 +1,63 @@
+#
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is Mozilla Windows Drag and Drop Tests.
+#
+# The Initial Developer of the Original Code is
+# Kyle Huey <me@kylehuey.com>
+# Portions created by the Initial Developer are Copyright (C) 2009
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH		= ../../../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+ifndef MOZ_ENABLE_LIBXUL
+LOCAL_INCLUDES =  -I$(srcdir)/../   \
+                  -I$(srcdir)/../../xpwidgets \
+                  $(NULL)
+
+LIBS = ../$(LIB_PREFIX)widget_windows.$(LIB_SUFFIX) \
+       ../../xpwidgets/$(LIB_PREFIX)xpwidgets_s.$(LIB_SUFFIX) \
+       $(DIST)/lib/$(LIB_PREFIX)thebes.$(LIB_SUFFIX) \
+       $(XPCOM_LIBS) \
+       $(MOZ_UNICHARUTIL_LIBS) \
+       $(NULL)
+
+OS_LIBS += $(call EXPAND_LIBNAME,ole32 shell32)
+
+CPP_UNIT_TESTS = TestWinDND.cpp \
+                 $(NULL)
+endif 
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/widget/src/windows/tests/TestWinDND.cpp
@@ -0,0 +1,404 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Kyle Huey <me@kylehuey.com>
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+#define MOZILLA_INTERNAL_API
+
+#include <ole2.h>
+#include <shlobj.h>
+
+#include "TestHarness.h"
+#include "nsIArray.h"
+#include "nsILocalFile.h"
+#include "nsNetUtil.h"
+#include "nsISupportsPrimitives.h"
+#include "nsIFileURL.h"
+#include "nsITransferable.h"
+#include "nsClipboard.h"
+#include "nsDataObjCollection.h"
+
+nsIFile* xferFile;
+
+nsresult CheckValidHDROP(STGMEDIUM* pSTG)
+{
+  if (pSTG->tymed != TYMED_HGLOBAL) {
+    fail("Received data is not in an HGLOBAL");
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  HGLOBAL hGlobal = pSTG->hGlobal;
+  DROPFILES* pDropFiles;
+  pDropFiles = (DROPFILES*)GlobalLock(hGlobal);
+  if (!pDropFiles) {
+    fail("There is no data at the given HGLOBAL");
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  if (pDropFiles->pFiles != sizeof(DROPFILES))
+    fail("DROPFILES struct has wrong size");
+
+  if (pDropFiles->fWide != true) {
+    fail("Received data is not Unicode");
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  nsString s;
+  s = (PRUnichar*)((char*)pDropFiles + pDropFiles->pFiles);
+  nsresult rv;
+  nsCOMPtr<nsILocalFile> localFile(
+             do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
+  rv = localFile->InitWithPath(s);
+  if (NS_FAILED(rv)) {
+    fail("File could not be opened");
+    return NS_ERROR_UNEXPECTED;
+  }
+  return NS_OK;
+}
+
+nsresult CheckValidTEXT(STGMEDIUM* pSTG)
+{
+  if (pSTG->tymed != TYMED_HGLOBAL) {
+    fail("Received data is not in an HGLOBAL");
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  HGLOBAL hGlobal = pSTG->hGlobal;
+  char* pText;
+  pText = (char*)GlobalLock(hGlobal);
+  if (!pText) {
+    fail("There is no data at the given HGLOBAL");
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  nsCString string;
+  string = pText;
+
+  if (!string.Equals(NS_LITERAL_CSTRING("Mozilla can drag and drop"))) {
+    fail("Text passed through drop object wrong");
+    return NS_ERROR_UNEXPECTED;
+  }
+  return NS_OK;
+}
+
+nsresult CheckValidUNICODE(STGMEDIUM* pSTG)
+{
+  if (pSTG->tymed != TYMED_HGLOBAL) {
+    fail("Received data is not in an HGLOBAL");
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  HGLOBAL hGlobal = pSTG->hGlobal;
+  PRUnichar* pText;
+  pText = (PRUnichar*)GlobalLock(hGlobal);
+  if (!pText) {
+    fail("There is no data at the given HGLOBAL");
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  nsString string;
+  string = pText;
+
+  if (!string.Equals(NS_LITERAL_STRING("Mozilla can drag and drop"))) {
+    fail("Text passed through drop object wrong");
+    return NS_ERROR_UNEXPECTED;
+  }
+  return NS_OK;
+}
+
+nsresult GetTransferableFile(nsCOMPtr<nsITransferable>& pTransferable)
+{
+  nsresult rv;
+
+  nsCOMPtr<nsISupports> genericWrapper = do_QueryInterface(xferFile);
+
+  pTransferable = do_CreateInstance("@mozilla.org/widget/transferable;1");
+  rv = pTransferable->SetTransferData("application/x-moz-file", genericWrapper,
+                                      0);
+  return rv;
+}
+
+nsresult GetTransferableText(nsCOMPtr<nsITransferable>& pTransferable)
+{
+  nsresult rv;
+  NS_NAMED_LITERAL_STRING(mozString, "Mozilla can drag and drop");
+  nsCOMPtr<nsISupportsString> xferString =
+                               do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID);
+  rv = xferString->SetData(mozString);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsISupports> genericWrapper = do_QueryInterface(xferString);
+
+  pTransferable = do_CreateInstance("@mozilla.org/widget/transferable;1");
+  rv = pTransferable->SetTransferData("text/unicode", genericWrapper,
+                                      mozString.Length() * sizeof(PRUnichar));
+  return rv;
+}
+
+nsresult GetTransferableURI(nsCOMPtr<nsITransferable>& pTransferable)
+{
+  nsresult rv;
+
+  nsCOMPtr<nsIURI> xferURI;
+
+  rv = NS_NewURI(getter_AddRefs(xferURI), "http://www.mozilla.org");
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsISupports> genericWrapper = do_QueryInterface(xferURI);
+
+  pTransferable = do_CreateInstance("@mozilla.org/widget/transferable;1");
+  rv = pTransferable->SetTransferData("text/x-moz-url", genericWrapper, 0);
+  return rv;
+}
+
+nsresult MakeDataObject(nsISupportsArray* transferableArray,
+                        nsRefPtr<IDataObject>& itemToDrag)
+{
+  nsresult rv;
+  PRUint32 itemCount = 0;
+
+  nsCOMPtr<nsIURI> uri;
+  rv = NS_NewURI(getter_AddRefs(uri), "http://www.mozilla.org");
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = transferableArray->Count(&itemCount);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Copied more or less exactly from nsDragService::InvokeDragSession
+  // This is what lets us play fake Drag Service for the test
+  if (itemCount > 1) {
+    nsDataObjCollection * dataObjCollection = new nsDataObjCollection();
+    if (!dataObjCollection)
+      return NS_ERROR_OUT_OF_MEMORY;
+    itemToDrag = dataObjCollection;
+    for (PRUint32 i=0; i<itemCount; ++i) {
+      nsCOMPtr<nsISupports> supports;
+      transferableArray->GetElementAt(i, getter_AddRefs(supports));
+      nsCOMPtr<nsITransferable> trans(do_QueryInterface(supports));
+      if (trans) {
+        nsRefPtr<IDataObject> dataObj;
+        rv = nsClipboard::CreateNativeDataObject(trans,
+                                                 getter_AddRefs(dataObj), uri);
+        NS_ENSURE_SUCCESS(rv, rv);
+        // Add the flavors to the collection object too
+        rv = nsClipboard::SetupNativeDataObject(trans, dataObjCollection);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        dataObjCollection->AddDataObject(dataObj);
+      }
+    }
+  } // if dragging multiple items
+  else {
+    nsCOMPtr<nsISupports> supports;
+    transferableArray->GetElementAt(0, getter_AddRefs(supports));
+    nsCOMPtr<nsITransferable> trans(do_QueryInterface(supports));
+    if (trans) {
+      rv = nsClipboard::CreateNativeDataObject(trans,
+                                               getter_AddRefs(itemToDrag),
+                                               uri);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+  } // else dragging a single object
+  return rv;
+}
+
+nsresult Do_CheckOneFile()
+{
+  nsresult rv;
+  nsCOMPtr<nsITransferable> transferable;
+  nsCOMPtr<nsISupportsArray> transferableArray;
+  nsCOMPtr<nsISupports> genericWrapper;
+  nsRefPtr<IDataObject> dataObj;
+  rv = NS_NewISupportsArray(getter_AddRefs(transferableArray));
+  if (NS_FAILED(rv)) {
+    fail("Could not create the necessary nsISupportsArray");
+    return rv;
+  }
+
+  rv = GetTransferableFile(transferable);
+  if (NS_FAILED(rv)) {
+    fail("Could not create the proper nsITransferable!");
+    return rv;
+  }
+  genericWrapper = do_QueryInterface(transferable);
+  rv = transferableArray->AppendElement(genericWrapper);
+  if (NS_FAILED(rv)) {
+    fail("Could not append element to transferable array");
+    return rv;
+  }
+
+  rv = MakeDataObject(transferableArray, dataObj);
+  if (NS_FAILED(rv)) {
+    fail("Could not create data object");
+    return rv;
+  }
+
+  FORMATETC fe;
+  SET_FORMATETC(fe, CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL);
+  if (dataObj->QueryGetData(&fe) != S_OK) {
+    fail("File data object does not support the file data type!");
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  STGMEDIUM* stg;
+  stg = (STGMEDIUM*)CoTaskMemAlloc(sizeof(STGMEDIUM));
+  if (dataObj->GetData(&fe, stg) != S_OK) {
+    fail("File data object did not provide data on request");
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  rv = CheckValidHDROP(stg);
+  if (NS_FAILED(rv)) {
+    fail("HDROP was invalid");
+    return rv;
+  }
+
+  ReleaseStgMedium(stg);
+
+  return S_OK;
+}
+
+nsresult Do_CheckOneString()
+{
+  nsresult rv;
+  nsCOMPtr<nsITransferable> transferable;
+  nsCOMPtr<nsISupportsArray> transferableArray;
+  nsCOMPtr<nsISupports> genericWrapper;
+  nsRefPtr<IDataObject> dataObj;
+  rv = NS_NewISupportsArray(getter_AddRefs(transferableArray));
+  if (NS_FAILED(rv)) {
+    fail("Could not create the necessary nsISupportsArray");
+    return rv;
+  }
+
+  rv = GetTransferableText(transferable);
+  if (NS_FAILED(rv)) {
+    fail("Could not create the proper nsITransferable!");
+    return rv;
+  }
+  genericWrapper = do_QueryInterface(transferable);
+  rv = transferableArray->AppendElement(genericWrapper);
+  if (NS_FAILED(rv)) {
+    fail("Could not append element to transferable array");
+    return rv;
+  }
+
+  rv = MakeDataObject(transferableArray, dataObj);
+  if (NS_FAILED(rv)) {
+    fail("Could not create data object");
+    return rv;
+  }
+
+  FORMATETC fe;
+  SET_FORMATETC(fe, CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL);
+  if (dataObj->QueryGetData(&fe) != S_OK) {
+    fail("String data object does not support the ASCII text data type!");
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  STGMEDIUM* stg;
+  stg = (STGMEDIUM*)CoTaskMemAlloc(sizeof(STGMEDIUM));
+  HRESULT hr;
+  if ((hr = dataObj->GetData(&fe, stg)) != S_OK) {
+    fail("String data object did not provide ASCII data on request");
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  rv = CheckValidTEXT(stg);
+  if (NS_FAILED(rv)) {
+    fail("TEXT was invalid");
+    return rv;
+  }
+
+  ReleaseStgMedium(stg);
+
+  SET_FORMATETC(fe, CF_UNICODETEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL);
+  if (dataObj->QueryGetData(&fe) != S_OK) {
+    fail("String data object does not support the wide text data type!");
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  if (dataObj->GetData(&fe, stg) != S_OK) {
+    fail("String data object did not provide wide data on request");
+    return NS_ERROR_UNEXPECTED;
+  }
+  
+  rv = CheckValidUNICODE(stg);
+  if (NS_FAILED(rv)) {
+    fail("UNICODE was invalid");
+    return rv;
+  }
+  
+  return S_OK;
+}
+
+// This function performs basic drop tests, testing a data object consisting
+// of one transferable
+nsresult Do_Test1()
+{
+  nsresult rv = NS_OK;
+  nsresult workingrv;
+
+  workingrv = Do_CheckOneFile();
+  if (NS_FAILED(workingrv)) {
+    fail("Drag object tests failed on a single file");
+    rv = NS_ERROR_UNEXPECTED;
+  } else {
+    passed("Successfully created a working file drag object!");
+  }
+
+  workingrv = Do_CheckOneString();
+  if (NS_FAILED(workingrv)) {
+    fail("Drag object tests failed on a single string");
+    rv = NS_ERROR_UNEXPECTED;
+  } else {
+    passed("Successfully created a working string drag object!");
+  }
+
+  return rv;
+}
+
+int main(int argc, char** argv)
+{
+  ScopedXPCOM xpcom("Test Windows Drag and Drop");
+
+  nsCOMPtr<nsIFile> file;
+  file = xpcom.GetProfileDirectory();
+  xferFile = file;
+
+  if (NS_SUCCEEDED(Do_Test1()))
+    passed("Basic Drag and Drop data type tests succeeded!");
+
+  return gFailCount;
+}