Add support for drag and drop client files

Currently we have two ways to upload files from the client - FileUpload
widget (browse button) and ClientFileUploader service (file drop).
Create internal Uploader interface to unify the submission calls.
Handle file drop in dialog area. Adjust and extend tests.
diff --git a/bundles/org.eclipse.rap.addons.filedialog/src/org/eclipse/swt/internal/widgets/FileUploadRunnable.java b/bundles/org.eclipse.rap.addons.filedialog/src/org/eclipse/swt/internal/widgets/FileUploadRunnable.java
index 4d7e482..583a37a 100644
--- a/bundles/org.eclipse.rap.addons.filedialog/src/org/eclipse/swt/internal/widgets/FileUploadRunnable.java
+++ b/bundles/org.eclipse.rap.addons.filedialog/src/org/eclipse/swt/internal/widgets/FileUploadRunnable.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2013 EclipseSource and others.
+ * Copyright (c) 2013, 2014 EclipseSource and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -19,8 +19,6 @@
 import org.eclipse.rap.addons.fileupload.FileUploadEvent;
 import org.eclipse.rap.addons.fileupload.FileUploadHandler;
 import org.eclipse.rap.addons.fileupload.FileUploadListener;
-import org.eclipse.rap.rwt.widgets.FileUpload;
-import org.eclipse.swt.SWT;
 import org.eclipse.swt.events.DisposeEvent;
 import org.eclipse.swt.events.DisposeListener;
 import org.eclipse.swt.widgets.Display;
@@ -31,33 +29,35 @@
   static enum State { WAITING, UPLOADING, FINISHED, FAILED }
 
   private final Display display;
-  private final FileUpload fileUpload;
   private final UploadPanel uploadPanel;
   private final ProgressCollector progressCollector;
+  private final Uploader uploader;
   private final FileUploadHandler handler;
+  private final UploadProgressListener listener;
   private final AtomicReference<State> state;
   private final Object lock;
 
-  public FileUploadRunnable( FileUpload fileUpload,
-                             UploadPanel uploadPanel,
+  public FileUploadRunnable( UploadPanel uploadPanel,
                              ProgressCollector progressCollector,
+                             Uploader uploader,
                              FileUploadHandler handler )
   {
-    this.fileUpload = fileUpload;
     this.uploadPanel = uploadPanel;
     this.progressCollector = progressCollector;
+    this.uploader = uploader;
     this.handler = handler;
-    display = fileUpload.getDisplay();
-    lock = new Object();
+    display = uploadPanel.getDisplay();
     state = new AtomicReference<State>( State.WAITING );
-    setupFileUploadHandler( handler, fileUpload );
+    lock = new Object();
+    listener = new UploadProgressListener();
+    setupFileUploadHandler();
     uploadPanel.updateIcons( State.WAITING );
   }
 
   public void run() {
     asyncExec( new Runnable() {
       public void run() {
-        fileUpload.submit( handler.getUploadUrl() );
+        uploader.submit( handler.getUploadUrl() );
       }
     } );
     if( !display.isDisposed() ) {
@@ -65,17 +65,16 @@
     }
     asyncExec( new Runnable() {
       public void run() {
-        if( !fileUpload.isDisposed() && ( fileUpload.getStyle() & SWT.MULTI ) != 0 ) {
-          fileUpload.dispose();
-        }
+        uploader.dispose();
+        handler.removeUploadListener( listener );
+        handler.dispose();
       }
     } );
   }
 
-  private void setupFileUploadHandler( final FileUploadHandler handler, FileUpload fileUpload ) {
-    final UploadProgressListener listener = new UploadProgressListener();
+  private void setupFileUploadHandler() {
     handler.addUploadListener( listener );
-    fileUpload.addDisposeListener( new DisposeListener() {
+    uploadPanel.addDisposeListener( new DisposeListener() {
       public void widgetDisposed( DisposeEvent event ) {
         handler.removeUploadListener( listener );
         handler.dispose();
diff --git a/bundles/org.eclipse.rap.addons.filedialog/src/org/eclipse/swt/internal/widgets/Uploader.java b/bundles/org.eclipse.rap.addons.filedialog/src/org/eclipse/swt/internal/widgets/Uploader.java
new file mode 100644
index 0000000..072499c
--- /dev/null
+++ b/bundles/org.eclipse.rap.addons.filedialog/src/org/eclipse/swt/internal/widgets/Uploader.java
@@ -0,0 +1,19 @@
+/*******************************************************************************
+ * Copyright (c) 2014 EclipseSource and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    EclipseSource - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.swt.internal.widgets;
+
+
+public interface Uploader {
+
+  void submit( String url );
+  void dispose();
+
+}
diff --git a/bundles/org.eclipse.rap.addons.filedialog/src/org/eclipse/swt/internal/widgets/UploaderService.java b/bundles/org.eclipse.rap.addons.filedialog/src/org/eclipse/swt/internal/widgets/UploaderService.java
new file mode 100644
index 0000000..073aee7
--- /dev/null
+++ b/bundles/org.eclipse.rap.addons.filedialog/src/org/eclipse/swt/internal/widgets/UploaderService.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2014 EclipseSource and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    EclipseSource - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.swt.internal.widgets;
+
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.rap.rwt.client.ClientFile;
+import org.eclipse.rap.rwt.client.service.ClientFileUploader;
+
+
+public class UploaderService implements Uploader {
+
+  private final ClientFile[] clientFiles;
+
+  public UploaderService( ClientFile[] clientFiles ) {
+    this.clientFiles = clientFiles;
+  }
+
+  public void submit( String url ) {
+    ClientFileUploader service = RWT.getClient().getService( ClientFileUploader.class );
+    if( service != null ) {
+      service.submit( url, clientFiles );
+    }
+  }
+
+  public void dispose() {
+  }
+
+}
diff --git a/bundles/org.eclipse.rap.addons.filedialog/src/org/eclipse/swt/internal/widgets/UploaderWidget.java b/bundles/org.eclipse.rap.addons.filedialog/src/org/eclipse/swt/internal/widgets/UploaderWidget.java
new file mode 100644
index 0000000..6874fbf
--- /dev/null
+++ b/bundles/org.eclipse.rap.addons.filedialog/src/org/eclipse/swt/internal/widgets/UploaderWidget.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2014 EclipseSource and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    EclipseSource - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.swt.internal.widgets;
+
+import org.eclipse.rap.rwt.widgets.FileUpload;
+import org.eclipse.swt.SWT;
+
+
+public class UploaderWidget implements Uploader {
+
+  private final FileUpload fileUpload;
+
+  public UploaderWidget( FileUpload fileUpload ) {
+    this.fileUpload = fileUpload;
+  }
+
+  public void submit( String url ) {
+    fileUpload.submit( url );
+  }
+
+  public void dispose() {
+    if( ( fileUpload.getStyle() & SWT.MULTI ) != 0 ) {
+      fileUpload.dispose();
+    }
+  }
+
+}
diff --git a/bundles/org.eclipse.rap.addons.filedialog/src/org/eclipse/swt/widgets/FileDialog.java b/bundles/org.eclipse.rap.addons.filedialog/src/org/eclipse/swt/widgets/FileDialog.java
index ce215b3..d622ee5 100644
--- a/bundles/org.eclipse.rap.addons.filedialog/src/org/eclipse/swt/widgets/FileDialog.java
+++ b/bundles/org.eclipse.rap.addons.filedialog/src/org/eclipse/swt/widgets/FileDialog.java
@@ -24,11 +24,18 @@
 
 import org.eclipse.rap.addons.fileupload.DiskFileUploadReceiver;
 import org.eclipse.rap.addons.fileupload.FileUploadHandler;
+import org.eclipse.rap.rwt.client.ClientFile;
+import org.eclipse.rap.rwt.dnd.ClientFileTransfer;
 import org.eclipse.rap.rwt.service.ServerPushSession;
 import org.eclipse.rap.rwt.widgets.FileUpload;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.SWTException;
 import org.eclipse.swt.custom.ScrolledComposite;
+import org.eclipse.swt.dnd.DND;
+import org.eclipse.swt.dnd.DropTarget;
+import org.eclipse.swt.dnd.DropTargetAdapter;
+import org.eclipse.swt.dnd.DropTargetEvent;
+import org.eclipse.swt.dnd.Transfer;
 import org.eclipse.swt.events.ControlAdapter;
 import org.eclipse.swt.events.ControlEvent;
 import org.eclipse.swt.events.DisposeEvent;
@@ -38,6 +45,9 @@
 import org.eclipse.swt.internal.widgets.FileUploadRunnable;
 import org.eclipse.swt.internal.widgets.ProgressCollector;
 import org.eclipse.swt.internal.widgets.UploadPanel;
+import org.eclipse.swt.internal.widgets.Uploader;
+import org.eclipse.swt.internal.widgets.UploaderService;
+import org.eclipse.swt.internal.widgets.UploaderWidget;
 import org.eclipse.swt.layout.GridData;
 import org.eclipse.swt.layout.GridLayout;
 
@@ -217,6 +227,7 @@
     dialogArea.setLayout( createGridLayout( 1, 0, 5 ) );
     createUploadsArea( dialogArea );
     createProgressArea( dialogArea );
+    createDropTarget( dialogArea );
   }
 
   private void createUploadsArea( Composite parent ) {
@@ -242,6 +253,45 @@
     progressCollector.setLayoutData( createHorizontalFillData() );
   }
 
+  private void createDropTarget( Control control ) {
+    DropTarget dropTarget = new DropTarget( control, DND.DROP_MOVE | DND.DROP_COPY );
+    dropTarget.setTransfer( new Transfer[] { ClientFileTransfer.getInstance() } );
+    dropTarget.addDropListener( new DropTargetAdapter() {
+      @Override
+      public void dropAccept( DropTargetEvent event ) {
+        if( !ClientFileTransfer.getInstance().isSupportedType( event.currentDataType ) ) {
+          event.detail = DND.DROP_NONE;
+        }
+      }
+      @Override
+      public void drop( DropTargetEvent event ) {
+        handleFileDrop( ( ClientFile[] )event.data );
+      }
+    } );
+  }
+
+  private void handleFileDrop( ClientFile[] clientFiles ) {
+    placeHolder.dispose();
+    ClientFile[] files = isMulti() ? clientFiles : new ClientFile[] { clientFiles[ 0 ] };
+    UploadPanel uploadPanel = createUploadPanel( getFileNames( files ) );
+    updateScrolledComposite();
+    Uploader uploader = new UploaderService( files );
+    FileUploadHandler handler = new FileUploadHandler( new DiskFileUploadReceiver() );
+    FileUploadRunnable uploadRunnable = new FileUploadRunnable( uploadPanel,
+                                                                progressCollector,
+                                                                uploader,
+                                                                handler );
+    singleThreadExecutor.execute( uploadRunnable );
+  }
+
+  private static String[] getFileNames( ClientFile[] clientFiles ) {
+    String[] fileNames = new String[ clientFiles.length ];
+    for( int i = 0; i < fileNames.length; i++ ) {
+      fileNames[ i ] = clientFiles[ i ].getName();
+    }
+    return fileNames;
+  }
+
   private void createButtonsArea( Composite parent ) {
     Composite buttonsArea = new Composite( parent, SWT.NONE );
     buttonsArea.setLayout( createGridLayout( 4, 0, 5 ) );
@@ -292,13 +342,14 @@
 
   private void handleFileUploadSelection( FileUpload fileUpload ) {
     placeHolder.dispose();
-    FileUploadHandler handler = new FileUploadHandler( new DiskFileUploadReceiver() );
     UploadPanel uploadPanel = createUploadPanel( fileUpload.getFileNames() );
     updateScrolledComposite();
     updateButtonsArea( fileUpload );
-    FileUploadRunnable uploadRunnable = new FileUploadRunnable( fileUpload,
-                                                                uploadPanel,
+    Uploader uploader = new UploaderWidget( fileUpload );
+    FileUploadHandler handler = new FileUploadHandler( new DiskFileUploadReceiver() );
+    FileUploadRunnable uploadRunnable = new FileUploadRunnable( uploadPanel,
                                                                 progressCollector,
+                                                                uploader,
                                                                 handler );
     singleThreadExecutor.execute( uploadRunnable );
   }
diff --git a/tests/org.eclipse.rap.addons.filedialog.test/src/org/eclipse/swt/internal/widgets/FileUploadRunnable_Test.java b/tests/org.eclipse.rap.addons.filedialog.test/src/org/eclipse/swt/internal/widgets/FileUploadRunnable_Test.java
index 4c05817..e1fd523 100644
--- a/tests/org.eclipse.rap.addons.filedialog.test/src/org/eclipse/swt/internal/widgets/FileUploadRunnable_Test.java
+++ b/tests/org.eclipse.rap.addons.filedialog.test/src/org/eclipse/swt/internal/widgets/FileUploadRunnable_Test.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2013 EclipseSource and others.
+ * Copyright (c) 2013, 2014 EclipseSource and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -14,9 +14,7 @@
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -33,8 +31,6 @@
 import org.eclipse.rap.addons.fileupload.FileUploadListener;
 import org.eclipse.rap.rwt.lifecycle.PhaseId;
 import org.eclipse.rap.rwt.testfixture.Fixture;
-import org.eclipse.rap.rwt.widgets.FileUpload;
-import org.eclipse.swt.SWT;
 import org.eclipse.swt.internal.widgets.FileUploadRunnable.State;
 import org.eclipse.swt.widgets.Display;
 import org.eclipse.swt.widgets.Shell;
@@ -43,14 +39,15 @@
 import org.junit.Test;
 
 
+@SuppressWarnings( "deprecation" )
 public class FileUploadRunnable_Test {
 
   private Display display;
   private Shell shell;
   private FileUploadRunnable runnable;
-  private FileUpload fileUpload;
   private UploadPanel uploadPanel;
   private ProgressCollector progressCollector;
+  private Uploader uploader;
   private FileUploadHandler handler;
 
   @Before
@@ -59,14 +56,14 @@
     Fixture.fakePhase( PhaseId.PROCESS_ACTION );
     display = new Display();
     shell = new Shell( display );
-    fileUpload = mock( FileUpload.class );
-    when( fileUpload.getDisplay() ).thenReturn( display );
     uploadPanel = mock( UploadPanel.class );
+    when( uploadPanel.getDisplay() ).thenReturn( display );
     progressCollector = mock( ProgressCollector.class );
     DiskFileUploadReceiver diskFileUploadReceiver = mock( DiskFileUploadReceiver.class );
     when( diskFileUploadReceiver.getTargetFiles() ).thenReturn( new File[ 0 ] );
+    uploader = mock( Uploader.class );
     handler = spy( new FileUploadHandler( diskFileUploadReceiver ) );
-    runnable = new FileUploadRunnable( fileUpload, uploadPanel, progressCollector, handler );
+    runnable = new FileUploadRunnable( uploadPanel, progressCollector, uploader, handler );
   }
 
   @After
@@ -85,21 +82,21 @@
   }
 
   @Test
-  public void testFileUploadDispose_removesUploadListener() {
-    fileUpload = new FileUpload( shell, SWT.NONE );
-    runnable = new FileUploadRunnable( fileUpload, uploadPanel, progressCollector, handler );
+  public void testUploadPanelDispose_removesUploadListener() {
+    uploadPanel = new UploadPanel( shell, new String[ 0 ] );
+    runnable = new FileUploadRunnable( uploadPanel, progressCollector, uploader, handler );
 
-    fileUpload.dispose();
+    uploadPanel.dispose();
 
     verify( handler ).removeUploadListener( any( FileUploadListener.class ) );
   }
 
   @Test
-  public void testFileUploadDispose_disposesHandler() {
-    fileUpload = new FileUpload( shell, SWT.NONE );
-    runnable = new FileUploadRunnable( fileUpload, uploadPanel, progressCollector, handler );
+  public void testUploadPanelDispose_disposesHandler() {
+    uploadPanel = new UploadPanel( shell, new String[ 0 ] );
+    runnable = new FileUploadRunnable( uploadPanel, progressCollector, uploader, handler );
 
-    fileUpload.dispose();
+    uploadPanel.dispose();
 
     verify( handler ).dispose();
   }
@@ -230,34 +227,43 @@
   }
 
   @Test
-  public void testRun_callsFileUploadSubmit() {
+  public void testRun_callsUploaderSubmit() {
     sheduleFinishedEvent();
 
     runnable.run();
     runEventsLoop();
 
-    verify( fileUpload ).submit( anyString() );
+    verify( uploader ).submit( anyString() );
   }
 
   @Test
-  public void testRun_disposesMutliFileUpload() {
-    doReturn( Integer.valueOf( SWT.MULTI ) ).when( fileUpload ).getStyle();
+  public void testRun_disposesUploader() {
     sheduleFinishedEvent();
 
     runnable.run();
     runEventsLoop();
 
-    verify( fileUpload ).dispose();
+    verify( uploader ).dispose();
   }
 
   @Test
-  public void testRun_doesNotDisposeSingleFileUpload() {
+  public void testRun_disposeHandler() {
     sheduleFinishedEvent();
 
     runnable.run();
     runEventsLoop();
 
-    verify( fileUpload, never() ).dispose();
+    verify( handler ).dispose();
+  }
+
+  @Test
+  public void testRun_removesUploadListener() {
+    sheduleFinishedEvent();
+
+    runnable.run();
+    runEventsLoop();
+
+    verify( handler ).removeUploadListener( any( FileUploadListener.class ) );
   }
 
   private void sheduleFinishedEvent() {
diff --git a/tests/org.eclipse.rap.addons.filedialog.test/src/org/eclipse/swt/internal/widgets/UploaderService_Test.java b/tests/org.eclipse.rap.addons.filedialog.test/src/org/eclipse/swt/internal/widgets/UploaderService_Test.java
new file mode 100644
index 0000000..d559704
--- /dev/null
+++ b/tests/org.eclipse.rap.addons.filedialog.test/src/org/eclipse/swt/internal/widgets/UploaderService_Test.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (c) 2014 EclipseSource and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    EclipseSource - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.swt.internal.widgets;
+
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.same;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.eclipse.rap.rwt.client.Client;
+import org.eclipse.rap.rwt.client.ClientFile;
+import org.eclipse.rap.rwt.client.service.ClientFileUploader;
+import org.eclipse.rap.rwt.internal.client.ClientFileImpl;
+import org.eclipse.rap.rwt.testfixture.Fixture;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+
+@SuppressWarnings( "restriction" )
+public class UploaderService_Test {
+
+  @Before
+  public void setUp() {
+    Fixture.setUp();
+  }
+
+  @After
+  public void tearDown() {
+    Fixture.tearDown();
+  }
+
+  @Test
+  public void testSubmit_callsClientFileUploaderService() {
+    ClientFileUploader clientService = mockClientFileUploaderService();
+    ClientFile[] clientFiles = new ClientFile[] { new ClientFileImpl( "fileId", "", "", 0 ) };
+    UploaderService uploader = new UploaderService( clientFiles );
+
+    uploader.submit( "foo" );
+
+    verify( clientService ).submit( eq( "foo" ), same( clientFiles ) );
+  }
+
+  private static ClientFileUploader mockClientFileUploaderService() {
+    ClientFileUploader service = mock( ClientFileUploader.class );
+    Client client = mock( Client.class );
+    when( client.getService( ClientFileUploader.class ) ).thenReturn( service );
+    Fixture.fakeClient( client );
+    return service;
+  }
+
+}
diff --git a/tests/org.eclipse.rap.addons.filedialog.test/src/org/eclipse/swt/internal/widgets/UploaderWidget_Test.java b/tests/org.eclipse.rap.addons.filedialog.test/src/org/eclipse/swt/internal/widgets/UploaderWidget_Test.java
new file mode 100644
index 0000000..8701901
--- /dev/null
+++ b/tests/org.eclipse.rap.addons.filedialog.test/src/org/eclipse/swt/internal/widgets/UploaderWidget_Test.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (c) 2014 EclipseSource and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    EclipseSource - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.swt.internal.widgets;
+
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import org.eclipse.rap.rwt.widgets.FileUpload;
+import org.eclipse.swt.SWT;
+import org.junit.Test;
+
+
+public class UploaderWidget_Test {
+
+  @Test
+  public void testSubmit_triggersFileUploadSubmit() {
+    FileUpload fileUpload = mock( FileUpload.class );
+    UploaderWidget uploader = new UploaderWidget( fileUpload );
+
+    uploader.submit( "foo" );
+
+    verify( fileUpload ).submit( eq( "foo" ) );
+  }
+
+  @Test
+  public void testDispose_doesNotTriggersFileUploadDisposeOnSingle() {
+    FileUpload fileUpload = mock( FileUpload.class );
+    UploaderWidget uploader = new UploaderWidget( fileUpload );
+
+    uploader.dispose();
+
+    verify( fileUpload, never() ).dispose();
+  }
+
+  @Test
+  public void testDispose_triggersFileUploadDisposeOnMulti() {
+    FileUpload fileUpload = mock( FileUpload.class );
+    doReturn( Integer.valueOf( SWT.MULTI ) ).when( fileUpload ).getStyle();
+    UploaderWidget uploader = new UploaderWidget( fileUpload );
+
+    uploader.dispose();
+
+    verify( fileUpload ).dispose();
+  }
+
+}
diff --git a/tests/org.eclipse.rap.addons.filedialog.test/src/org/eclipse/swt/widgets/FileDialog_Test.java b/tests/org.eclipse.rap.addons.filedialog.test/src/org/eclipse/swt/widgets/FileDialog_Test.java
index d50b1e7..a545162 100644
--- a/tests/org.eclipse.rap.addons.filedialog.test/src/org/eclipse/swt/widgets/FileDialog_Test.java
+++ b/tests/org.eclipse.rap.addons.filedialog.test/src/org/eclipse/swt/widgets/FileDialog_Test.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2013 EclipseSource and others.
+ * Copyright (c) 2013, 2014 EclipseSource and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -25,6 +25,9 @@
 import java.io.IOException;
 import java.util.concurrent.ThreadPoolExecutor;
 
+import org.eclipse.rap.rwt.client.ClientFile;
+import org.eclipse.rap.rwt.dnd.ClientFileTransfer;
+import org.eclipse.rap.rwt.internal.client.ClientFileImpl;
 import org.eclipse.rap.rwt.internal.serverpush.ServerPushManager;
 import org.eclipse.rap.rwt.lifecycle.PhaseId;
 import org.eclipse.rap.rwt.testfixture.Fixture;
@@ -32,6 +35,10 @@
 import org.eclipse.rap.rwt.widgets.DialogUtil;
 import org.eclipse.rap.rwt.widgets.FileUpload;
 import org.eclipse.swt.SWT;
+import org.eclipse.swt.dnd.DND;
+import org.eclipse.swt.dnd.DropTarget;
+import org.eclipse.swt.dnd.FileTransfer;
+import org.eclipse.swt.internal.dnd.DNDEvent;
 import org.eclipse.swt.internal.widgets.FileUploadRunnable;
 import org.eclipse.swt.layout.GridData;
 import org.junit.After;
@@ -39,7 +46,9 @@
 import org.junit.Test;
 
 
-@SuppressWarnings( "restriction" )
+@SuppressWarnings( {
+  "restriction", "deprecation"
+} )
 public class FileDialog_Test {
 
   private Display display;
@@ -232,6 +241,38 @@
   }
 
   @Test
+  public void testFileDrop_acceptsClientFileTransfer() {
+    DNDEvent event = new DNDEvent();
+    event.dataType = ClientFileTransfer.getInstance().getSupportedTypes()[ 0 ];
+    event.detail = DND.DROP_MOVE;
+
+    getDropTarget().notifyListeners( DND.DropAccept, event );
+
+    assertEquals( DND.DROP_MOVE, event.detail );
+  }
+
+  @Test
+  public void testFileDrop_rejectsOtherTransfer() {
+    DNDEvent event = new DNDEvent();
+    event.dataType = FileTransfer.getInstance().getSupportedTypes()[ 0 ];
+    event.detail = DND.DROP_MOVE;
+
+    getDropTarget().notifyListeners( DND.DropAccept, event );
+
+    assertEquals( DND.DROP_NONE, event.detail );
+  }
+
+  @Test
+  public void testFileDrop_executesRunnable() {
+    DNDEvent event = new DNDEvent();
+    event.data = new ClientFile[] { new ClientFileImpl( "fileId", "", "", 0 ) };
+
+    getDropTarget().notifyListeners( DND.Drop, event );
+
+    verify( singleThreadExecutor ).execute( any( FileUploadRunnable.class ) );
+  }
+
+  @Test
   public void testDeleteUploadedFiles() throws IOException {
     File uploadedFile = File.createTempFile( "temp-", null );
 
@@ -240,6 +281,11 @@
     assertFalse( uploadedFile.exists() );
   }
 
+  private DropTarget getDropTarget() {
+    Composite dropControl = ( Composite )dialog.shell.getChildren()[ 0 ];
+    return ( DropTarget )dropControl.getData( DND.DROP_TARGET_KEY );
+  }
+
   private FileUpload getFileUpload() {
     Composite buttonsArea = ( Composite )dialog.shell.getChildren()[ 1 ];
     return ( FileUpload )buttonsArea.getChildren()[ 0 ];