--- a/browser/base/content/pageinfo/pageInfo.js
+++ b/browser/base/content/pageinfo/pageInfo.js
@@ -183,20 +183,16 @@ gImageView.getCellProperties = function(
var gImageHash = { };
// localized strings (will be filled in when the document is loaded)
// this isn't all of them, these are just the ones that would otherwise have been loaded inside a loop
var gStrings = { };
var gBundle;
-const DRAGSERVICE_CONTRACTID = "@mozilla.org/widget/dragservice;1";
-const TRANSFERABLE_CONTRACTID = "@mozilla.org/widget/transferable;1";
-const ARRAY_CONTRACTID = "@mozilla.org/supports-array;1";
-const STRING_CONTRACTID = "@mozilla.org/supports-string;1";
const PERMISSION_CONTRACTID = "@mozilla.org/permissionmanager;1";
const PREFERENCES_CONTRACTID = "@mozilla.org/preferences-service;1";
const ATOM_CONTRACTID = "@mozilla.org/atom-service;1";
// a number of services I'll need later
// the cache services
const nsICacheService = Components.interfaces.nsICacheService;
const ACCESS_READ = Components.interfaces.nsICache.ACCESS_READ;
@@ -658,42 +654,26 @@ function onBeginLinkDrag(event,urlField,
var tree = event.target;
if (!("treeBoxObject" in tree))
tree = tree.parentNode;
var row = tree.treeBoxObject.getRowAt(event.clientX, event.clientY);
if (row == -1)
return;
- // Getting drag-system needed services
- var dragService = Components.classes[DRAGSERVICE_CONTRACTID].getService()
- .QueryInterface(Components.interfaces.nsIDragService);
- var transArray = Components.classes[ARRAY_CONTRACTID]
- .createInstance(Components.interfaces.nsISupportsArray);
- if (!transArray)
- return;
-
- var trans = Components.classes[TRANSFERABLE_CONTRACTID]
- .createInstance(Components.interfaces.nsITransferable);
- if (!trans)
- return;
-
// Adding URL flavor
- trans.addDataFlavor("text/x-moz-url");
var col = tree.columns[urlField];
var url = tree.view.getCellText(row, col);
col = tree.columns[descField];
var desc = tree.view.getCellText(row, col);
- var stringURL = Components.classes[STRING_CONTRACTID]
- .createInstance(Components.interfaces.nsISupportsString);
- stringURL.data = url + "\n" + desc;
- trans.setTransferData("text/x-moz-url", stringURL, stringURL.data.length * 2 );
- transArray.AppendElement(trans.QueryInterface(Components.interfaces.nsISupports));
- dragService.invokeDragSession(event.target, transArray, null, dragService.DRAGDROP_ACTION_NONE);
+ var dt = event.dataTransfer;
+ dt.setData("text/x-moz-url", url + "\n" + desc);
+ dt.setData("text/url-list", url);
+ dt.setData("text/plain", url);
}
//******** Image Stuff
function getSelectedImage(tree)
{
if (!gImageView.rowCount)
return null;
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -96,16 +96,17 @@ class nsIScriptContext;
class nsIRunnable;
template<class E> class nsCOMArray;
class nsIPref;
class nsVoidArray;
struct JSRuntime;
class nsICaseConversion;
class nsIUGenCategory;
class nsIWidget;
+class nsIDragSession;
class nsPIDOMWindow;
#ifdef MOZ_XTF
class nsIXTFService;
#endif
#ifdef IBMBIDI
class nsIBidiKeyboard;
#endif
@@ -1214,16 +1215,21 @@ public:
/**
* Hide any XUL popups associated with aDocument, including any documents
* displayed in child frames.
*/
static void HidePopupsInDocument(nsIDocument* aDocument);
/**
+ * Retrieve the current drag session, or null if no drag is currently occuring
+ */
+ static already_AddRefed<nsIDragSession> GetDragSession();
+
+ /**
* Return true if aURI is a local file URI (i.e. file://).
*/
static PRBool URIIsLocalFile(nsIURI *aURI);
/**
* If aContent is an HTML element with a DOM level 0 'name', then
* return the name. Otherwise return null.
*/
--- a/content/base/src/nsContentAreaDragDrop.cpp
+++ b/content/base/src/nsContentAreaDragDrop.cpp
@@ -40,24 +40,25 @@
// Local Includes
#include "nsContentAreaDragDrop.h"
// Helper Classes
#include "nsString.h"
// Interfaces needed to be included
+#include "nsIVariant.h"
#include "nsIDOMNSUIEvent.h"
#include "nsIDOMUIEvent.h"
#include "nsISelection.h"
#include "nsIDOMNode.h"
#include "nsIDOMNodeList.h"
#include "nsIDOMEvent.h"
#include "nsIDOMNSEvent.h"
-#include "nsIDOMMouseEvent.h"
+#include "nsIDOMDragEvent.h"
#include "nsIDOMAbstractView.h"
#include "nsPIDOMWindow.h"
#include "nsIDOMDocument.h"
#include "nsIDOMDocumentRange.h"
#include "nsIDOMRange.h"
#include "nsIDocumentEncoder.h"
#include "nsIFormControl.h"
#include "nsISelectionPrivate.h"
@@ -68,38 +69,38 @@
#include "nsIDragSession.h"
#include "nsComponentManagerUtils.h"
#include "nsXPCOM.h"
#include "nsISupportsPrimitives.h"
#include "nsServiceManagerUtils.h"
#include "nsNetUtil.h"
#include "nsIFile.h"
#include "nsIWebNavigation.h"
-#include "nsIClipboardDragDropHooks.h"
-#include "nsIClipboardDragDropHookList.h"
#include "nsIDocShell.h"
#include "nsIContent.h"
#include "nsIImageLoadingContent.h"
#include "nsINameSpaceManager.h"
#include "nsUnicharUtils.h"
#include "nsIURL.h"
#include "nsIImage.h"
#include "nsIDocument.h"
#include "nsIScriptSecurityManager.h"
+#include "nsIPrincipal.h"
#include "nsIPresShell.h"
#include "nsPresContext.h"
#include "nsIDocShellTreeItem.h"
#include "nsIFrame.h"
#include "nsRange.h"
#include "nsIWebBrowserPersist.h"
#include "nsEscape.h"
#include "nsContentUtils.h"
#include "nsIMIMEService.h"
#include "imgIRequest.h"
#include "nsContentCID.h"
+#include "nsDOMDataTransfer.h"
#include "nsISelectionController.h"
#include "nsFrameSelection.h"
#include "nsIDOMEventTarget.h"
#include "nsWidgetsCID.h"
static NS_DEFINE_CID(kHTMLConverterCID, NS_HTMLFORMATCONVERTER_CID);
// private clipboard data flavors for html copy, used by editor when pasting
@@ -109,53 +110,62 @@ static NS_DEFINE_CID(kHTMLConverterCID,
NS_IMPL_ADDREF(nsContentAreaDragDrop)
NS_IMPL_RELEASE(nsContentAreaDragDrop)
NS_INTERFACE_MAP_BEGIN(nsContentAreaDragDrop)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMDragListener)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIDOMEventListener, nsIDOMDragListener)
NS_INTERFACE_MAP_ENTRY(nsIDOMDragListener)
- NS_INTERFACE_MAP_ENTRY(nsIFlavorDataProvider)
NS_INTERFACE_MAP_ENTRY(nsIDragDropHandler)
NS_INTERFACE_MAP_END
class NS_STACK_CLASS nsTransferableFactory
{
public:
- nsTransferableFactory(nsIDOMEvent* inMouseEvent,
- nsIFlavorDataProvider *inFlavorDataProvider);
- nsresult Produce(PRBool *aDragSelection, nsITransferable** outTrans);
+ nsTransferableFactory(nsIDOMWindow* aWindow,
+ nsIContent* aTarget,
+ nsIContent* aSelectionTargetNode,
+ PRBool aIsAltKeyPressed);
+ nsresult Produce(nsDOMDataTransfer* aDataTransfer,
+ PRBool* aCanDrag,
+ PRBool* aDragSelection,
+ nsIContent** aDragNode);
private:
- nsresult ConvertStringsToTransferable(nsITransferable** outTrans);
+ void AddString(nsDOMDataTransfer* aDataTransfer,
+ const nsAString& aFlavor,
+ const nsAString& aData,
+ nsIPrincipal* aPrincipal);
+ nsresult AddStringsToDataTransfer(nsIContent* aDragNode,
+ nsDOMDataTransfer* aDataTransfer);
static nsresult GetDraggableSelectionData(nsISelection* inSelection,
- nsIDOMNode* inRealTargetNode,
- nsIDOMNode **outImageOrLinkNode,
+ nsIContent* inRealTargetNode,
+ nsIContent **outImageOrLinkNode,
PRBool* outDragSelectedText);
- static already_AddRefed<nsIDOMNode> FindParentLinkNode(nsIDOMNode* inNode);
- static void GetAnchorURL(nsIDOMNode* inNode, nsAString& outURL);
- static void GetNodeString(nsIDOMNode* inNode, nsAString & outNodeString);
+ static already_AddRefed<nsIContent> FindParentLinkNode(nsIContent* inNode);
+ static void GetAnchorURL(nsIContent* inNode, nsAString& outURL);
+ static void GetNodeString(nsIContent* inNode, nsAString & outNodeString);
static void CreateLinkText(const nsAString& inURL, const nsAString & inText,
nsAString& outLinkText);
static void GetSelectedLink(nsISelection* inSelection,
- nsIDOMNode **outLinkNode);
+ nsIContent **outLinkNode);
// if inNode is null, use the selection from the window
static nsresult SerializeNodeOrSelection(nsIDOMWindow* inWindow,
- nsIDOMNode* inNode,
+ nsIContent* inNode,
nsAString& outResultString,
nsAString& outHTMLContext,
nsAString& outHTMLInfo);
- PRBool mInstanceAlreadyUsed;
-
- nsCOMPtr<nsIDOMEvent> mMouseEvent;
- nsCOMPtr<nsIFlavorDataProvider> mFlavorDataProvider;
+ nsCOMPtr<nsIDOMWindow> mWindow;
+ nsCOMPtr<nsIContent> mTarget;
+ nsCOMPtr<nsIContent> mSelectionTargetNode;
+ PRPackedBool mIsAltKeyPressed;
nsString mUrlString;
nsString mImageSourceString;
nsString mImageDestFileName;
nsString mTitleString;
// will be filled automatically if you fill urlstring
nsString mHtmlString;
nsString mContextString;
@@ -287,101 +297,63 @@ nsContentAreaDragDrop::DragOver(nsIDOMEv
nsuiEvent->GetPreventDefault(&preventDefault);
if ( preventDefault )
return NS_OK;
// if the drag originated w/in this content area, bail
// early. This avoids loading a URL dragged from the content
// area into the very same content area (which is almost never
// the desired action).
- nsCOMPtr<nsIDragService> dragService =
- do_GetService("@mozilla.org/widget/dragservice;1");
- if (!dragService)
- return NS_ERROR_FAILURE;
- nsCOMPtr<nsIDragSession> session;
- dragService->GetCurrentSession(getter_AddRefs(session));
+ nsCOMPtr<nsIDragSession> session = nsContentUtils::GetDragSession();
+ NS_ENSURE_TRUE(session, NS_OK);
+
+ PRBool dropAllowed = PR_TRUE;
- if (session) {
- // if the client has provided an override callback, check if we
- // the drop is allowed. If it allows it, we should still protect
- // against dropping w/in the same document.
- PRBool dropAllowed = PR_TRUE;
- nsCOMPtr<nsISimpleEnumerator> enumerator;
- GetHookEnumeratorFromEvent(inEvent, getter_AddRefs(enumerator));
+ nsCOMPtr<nsIDOMDocument> sourceDoc;
+ session->GetSourceDocument(getter_AddRefs(sourceDoc));
+ nsCOMPtr<nsIDOMDocument> eventDoc;
+ GetEventDocument(inEvent, getter_AddRefs(eventDoc));
+
+ if (sourceDoc == eventDoc) { // common case
+ dropAllowed = PR_FALSE;
+ } else if (sourceDoc && eventDoc) {
+ // dig deeper
+ // XXXbz we need better ways to get from a document to the docshell!
+ nsCOMPtr<nsIDocument> sourceDocument(do_QueryInterface(sourceDoc));
+ nsCOMPtr<nsIDocument> eventDocument(do_QueryInterface(eventDoc));
+ NS_ASSERTION(sourceDocument, "Confused document object");
+ NS_ASSERTION(eventDocument, "Confused document object");
- if (enumerator) {
- PRBool hasMoreHooks = PR_FALSE;
- while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMoreHooks))
- && hasMoreHooks) {
- nsCOMPtr<nsISupports> isupp;
- if (NS_FAILED(enumerator->GetNext(getter_AddRefs(isupp)))) {
- break;
- }
+ nsPIDOMWindow* sourceWindow = sourceDocument->GetWindow();
+ nsPIDOMWindow* eventWindow = eventDocument->GetWindow();
+
+ if (sourceWindow && eventWindow) {
+ nsCOMPtr<nsIDocShellTreeItem> sourceShell =
+ do_QueryInterface(sourceWindow->GetDocShell());
+ nsCOMPtr<nsIDocShellTreeItem> eventShell =
+ do_QueryInterface(eventWindow->GetDocShell());
- nsCOMPtr<nsIClipboardDragDropHooks> override =
- do_QueryInterface(isupp);
+ if (sourceShell && eventShell) {
+ // Whew. Almost there. Get the roots that are of the same type
+ // (otherwise we'll always end up with the root docshell for the
+ // window, and drag/drop from chrom to content won't work).
+ nsCOMPtr<nsIDocShellTreeItem> sourceRoot;
+ nsCOMPtr<nsIDocShellTreeItem> eventRoot;
+ sourceShell->GetSameTypeRootTreeItem(getter_AddRefs(sourceRoot));
+ eventShell->GetSameTypeRootTreeItem(getter_AddRefs(eventRoot));
- if (override) {
-#ifdef DEBUG
- nsresult hookResult =
-#endif
- override->AllowDrop(inEvent, session, &dropAllowed);
- NS_ASSERTION(NS_SUCCEEDED(hookResult), "hook failure in AllowDrop");
-
- if (!dropAllowed) {
- break;
- }
+ if (sourceRoot && sourceRoot == eventRoot) {
+ dropAllowed = PR_FALSE;
}
}
}
-
- nsCOMPtr<nsIDOMDocument> sourceDoc;
- session->GetSourceDocument(getter_AddRefs(sourceDoc));
- nsCOMPtr<nsIDOMDocument> eventDoc;
- GetEventDocument(inEvent, getter_AddRefs(eventDoc));
-
- if (sourceDoc == eventDoc) { // common case
- dropAllowed = PR_FALSE;
- } else if (sourceDoc && eventDoc) {
- // dig deeper
- // XXXbz we need better ways to get from a document to the docshell!
- nsCOMPtr<nsIDocument> sourceDocument(do_QueryInterface(sourceDoc));
- nsCOMPtr<nsIDocument> eventDocument(do_QueryInterface(eventDoc));
- NS_ASSERTION(sourceDocument, "Confused document object");
- NS_ASSERTION(eventDocument, "Confused document object");
-
- nsPIDOMWindow* sourceWindow = sourceDocument->GetWindow();
- nsPIDOMWindow* eventWindow = eventDocument->GetWindow();
-
- if (sourceWindow && eventWindow) {
- nsCOMPtr<nsIDocShellTreeItem> sourceShell =
- do_QueryInterface(sourceWindow->GetDocShell());
- nsCOMPtr<nsIDocShellTreeItem> eventShell =
- do_QueryInterface(eventWindow->GetDocShell());
-
- if (sourceShell && eventShell) {
- // Whew. Almost there. Get the roots that are of the same type
- // (otherwise we'll always end up with the root docshell for the
- // window, and drag/drop from chrom to content won't work).
- nsCOMPtr<nsIDocShellTreeItem> sourceRoot;
- nsCOMPtr<nsIDocShellTreeItem> eventRoot;
- sourceShell->GetSameTypeRootTreeItem(getter_AddRefs(sourceRoot));
- eventShell->GetSameTypeRootTreeItem(getter_AddRefs(eventRoot));
-
- if (sourceRoot && sourceRoot == eventRoot) {
- dropAllowed = PR_FALSE;
- }
- }
- }
- }
-
- session->SetCanDrop(dropAllowed);
}
+ session->SetCanDrop(dropAllowed);
return NS_OK;
}
//
// DragExit
//
// Called when an OS drag is in process and the mouse is over a gecko
@@ -492,27 +464,18 @@ nsContentAreaDragDrop::DragDrop(nsIDOMEv
if (preventDefault) {
return NS_OK;
}
// pull the transferable out of the drag service. at the moment, we
// only care about the first item of the drag. We don't allow
// dropping multiple items into a content area.
- nsCOMPtr<nsIDragService> dragService =
- do_GetService("@mozilla.org/widget/dragservice;1");
- if (!dragService) {
- return NS_ERROR_FAILURE;
- }
-
- nsCOMPtr<nsIDragSession> session;
- dragService->GetCurrentSession(getter_AddRefs(session));
- if (!session) {
- return NS_ERROR_FAILURE;
- }
+ nsCOMPtr<nsIDragSession> session = nsContentUtils::GetDragSession();
+ NS_ENSURE_TRUE(session, NS_OK);
nsCOMPtr<nsITransferable> trans =
do_CreateInstance("@mozilla.org/widget/transferable;1");
if (!trans) {
return NS_ERROR_FAILURE;
}
// add the relevant flavors. order is important (highest fidelity to lowest)
@@ -520,45 +483,16 @@ nsContentAreaDragDrop::DragDrop(nsIDOMEv
trans->AddDataFlavor(kURLMime);
trans->AddDataFlavor(kFileMime);
trans->AddDataFlavor(kUnicodeMime);
// again, we only care about the first object
nsresult rv = session->GetData(trans, 0);
if (NS_SUCCEEDED(rv)) {
- // if the client has provided an override callback, call it. It may
- // still return that we should continue processing.
- nsCOMPtr<nsISimpleEnumerator> enumerator;
- GetHookEnumeratorFromEvent(inMouseEvent, getter_AddRefs(enumerator));
-
- if (enumerator) {
- PRBool actionCanceled = PR_TRUE;
- PRBool hasMoreHooks = PR_FALSE;
- while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMoreHooks))
- && hasMoreHooks) {
- nsCOMPtr<nsISupports> isupp;
- if (NS_FAILED(enumerator->GetNext(getter_AddRefs(isupp))))
- break;
- nsCOMPtr<nsIClipboardDragDropHooks> override =
- do_QueryInterface(isupp);
-
- if (override) {
-#ifdef DEBUG
- nsresult hookResult =
-#endif
- override->OnPasteOrDrop(inMouseEvent, trans, &actionCanceled);
- NS_ASSERTION(NS_SUCCEEDED(hookResult),
- "hook failure in OnPasteOrDrop");
- if (!actionCanceled)
- return NS_OK;
- }
- }
- }
-
nsXPIDLCString flavor;
nsCOMPtr<nsISupports> dataWrapper;
PRUint32 dataLen = 0;
rv = trans->GetAnyTransferData(getter_Copies(flavor),
getter_AddRefs(dataWrapper), &dataLen);
if (NS_SUCCEEDED(rv) && dataLen > 0) {
// get the url from one of several possible formats
nsAutoString url;
@@ -660,194 +594,64 @@ nsContentAreaDragDrop::GetEventDocument(
nsCOMPtr<nsIDOMWindow> window(do_QueryInterface(view));
if (window) {
window->GetDocument(outDocument);
}
}
}
-nsresult
-nsContentAreaDragDrop::GetHookEnumeratorFromEvent(nsIDOMEvent* inEvent,
- nsISimpleEnumerator **outEnumerator)
-{
- *outEnumerator = nsnull;
-
- nsCOMPtr<nsIDOMDocument> domdoc;
- GetEventDocument(inEvent, getter_AddRefs(domdoc));
- nsCOMPtr<nsIDocument> doc = do_QueryInterface(domdoc);
- NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
-
- nsCOMPtr<nsISupports> container = doc->GetContainer();
- nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(container);
- NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
-
- nsCOMPtr<nsIClipboardDragDropHookList> hookList = do_GetInterface(docShell);
- NS_ENSURE_TRUE(hookList, NS_ERROR_FAILURE);
- nsCOMPtr<nsISimpleEnumerator> enumerator;
- hookList->GetHookEnumerator(getter_AddRefs(enumerator));
- NS_ENSURE_TRUE(enumerator, NS_ERROR_FAILURE);
-
- *outEnumerator = enumerator;
- NS_ADDREF(*outEnumerator);
-
- return NS_OK;
-}
-
//
// DragGesture
//
-// Determine if the user has started to drag something and kick off
-// an OS-level drag if it's applicable
-//
NS_IMETHODIMP
nsContentAreaDragDrop::DragGesture(nsIDOMEvent* inMouseEvent)
{
- // first check that someone hasn't already handled this event
- PRBool preventDefault = PR_TRUE;
- nsCOMPtr<nsIDOMNSUIEvent> nsuiEvent(do_QueryInterface(inMouseEvent));
- if (nsuiEvent) {
- nsuiEvent->GetPreventDefault(&preventDefault);
- }
-
- if (preventDefault) {
- return NS_OK;
- }
-
- // if the client has provided an override callback, check if we
- // should continue
- nsCOMPtr<nsISimpleEnumerator> enumerator;
- GetHookEnumeratorFromEvent(inMouseEvent, getter_AddRefs(enumerator));
-
- if (enumerator) {
- PRBool allow = PR_TRUE;
- PRBool hasMoreHooks = PR_FALSE;
- while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMoreHooks))
- && hasMoreHooks) {
- nsCOMPtr<nsISupports> isupp;
- if (NS_FAILED(enumerator->GetNext(getter_AddRefs(isupp))))
- break;
-
- nsCOMPtr<nsIClipboardDragDropHooks> override = do_QueryInterface(isupp);
- if (override) {
-#ifdef DEBUG
- nsresult hookResult =
-#endif
- override->AllowStartDrag(inMouseEvent, &allow);
- NS_ASSERTION(NS_SUCCEEDED(hookResult),
- "hook failure in AllowStartDrag");
-
- if (!allow)
- return NS_OK;
- }
- }
- }
-
- PRBool isSelection = PR_FALSE;
- nsCOMPtr<nsITransferable> trans;
- nsTransferableFactory factory(inMouseEvent, static_cast<nsIFlavorDataProvider*>(this));
- factory.Produce(&isSelection, getter_AddRefs(trans));
-
- if (trans) {
- // if the client has provided an override callback, let them manipulate
- // the flavors or drag data
- nsCOMPtr<nsISimpleEnumerator> enumerator;
- GetHookEnumeratorFromEvent(inMouseEvent, getter_AddRefs(enumerator));
- if (enumerator) {
- PRBool hasMoreHooks = PR_FALSE;
- PRBool doContinueDrag = PR_TRUE;
- while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMoreHooks))
- && hasMoreHooks) {
- nsCOMPtr<nsISupports> isupp;
- if (NS_FAILED(enumerator->GetNext(getter_AddRefs(isupp))))
- break;
- nsCOMPtr<nsIClipboardDragDropHooks> override =
- do_QueryInterface(isupp);
+ return NS_OK;
+}
- if (override) {
-#ifdef DEBUG
- nsresult hookResult =
-#endif
- override->OnCopyOrDrag(inMouseEvent, trans, &doContinueDrag);
- NS_ASSERTION(NS_SUCCEEDED(hookResult),
- "hook failure in OnCopyOrDrag");
-
- if (!doContinueDrag) {
- return NS_OK;
- }
- }
- }
- }
-
- nsCOMPtr<nsISupportsArray> transArray =
- do_CreateInstance("@mozilla.org/supports-array;1");
- if (!transArray) {
- return NS_ERROR_FAILURE;
- }
-
- transArray->InsertElementAt(trans, 0);
-
- // kick off the drag
- nsCOMPtr<nsIDOMEventTarget> target;
- inMouseEvent->GetTarget(getter_AddRefs(target));
- nsCOMPtr<nsIDragService> dragService =
- do_GetService("@mozilla.org/widget/dragservice;1");
+nsresult
+nsContentAreaDragDrop::GetDragData(nsIDOMWindow* aWindow,
+ nsIContent* aTarget,
+ nsIContent* aSelectionTargetNode,
+ PRBool aIsAltKeyPressed,
+ nsDOMDataTransfer* aDataTransfer,
+ PRBool* aCanDrag,
+ PRBool* aDragSelection,
+ nsIContent** aDragNode)
+{
+ NS_ENSURE_TRUE(aSelectionTargetNode, NS_ERROR_INVALID_ARG);
- if (!dragService) {
- return NS_ERROR_FAILURE;
- }
-
- PRUint32 action = nsIDragService::DRAGDROP_ACTION_COPY +
- nsIDragService::DRAGDROP_ACTION_MOVE +
- nsIDragService::DRAGDROP_ACTION_LINK;
-
- nsCOMPtr<nsIDOMMouseEvent> mouseEvent(do_QueryInterface(inMouseEvent));
+ *aCanDrag = PR_TRUE;
- if (isSelection) {
- nsCOMPtr<nsIContent> targetContent(do_QueryInterface(target));
- nsIDocument* doc = targetContent->GetCurrentDoc();
- if (doc) {
- nsIPresShell* presShell = doc->GetPrimaryShell();
- if (presShell) {
- nsISelection* selection =
- presShell->GetCurrentSelection(nsISelectionController::SELECTION_NORMAL);
- return dragService->InvokeDragSessionWithSelection(selection,
- transArray,
- action,
- mouseEvent);
- }
- }
- }
-
- nsCOMPtr<nsIDOMNode> targetNode(do_QueryInterface(target));
- dragService->InvokeDragSessionWithImage(targetNode, transArray, nsnull,
- action, nsnull, 0, 0, mouseEvent);
- }
-
- return NS_OK;
+ nsTransferableFactory
+ factory(aWindow, aTarget, aSelectionTargetNode, aIsAltKeyPressed);
+ return factory.Produce(aDataTransfer, aCanDrag, aDragSelection, aDragNode);
}
NS_IMETHODIMP
nsContentAreaDragDrop::HandleEvent(nsIDOMEvent *event)
{
return NS_OK;
}
#if 0
#pragma mark -
#endif
+NS_IMPL_ISUPPORTS1(nsContentAreaDragDropDataProvider, nsIFlavorDataProvider)
+
// SaveURIToFile
// used on platforms where it's possible to drag items (e.g. images)
// into the file system
nsresult
-nsContentAreaDragDrop::SaveURIToFile(nsAString& inSourceURIString,
- nsIFile* inDestFile)
+nsContentAreaDragDropDataProvider::SaveURIToFile(nsAString& inSourceURIString,
+ nsIFile* inDestFile)
{
nsCOMPtr<nsIURI> sourceURI;
nsresult rv = NS_NewURI(getter_AddRefs(sourceURI), inSourceURIString);
if (NS_FAILED(rv)) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIURL> sourceURL = do_QueryInterface(sourceURI);
@@ -860,37 +664,37 @@ nsContentAreaDragDrop::SaveURIToFile(nsA
// we rely on the fact that the WPB is refcounted by the channel etc,
// so we don't keep a ref to it. It will die when finished.
nsCOMPtr<nsIWebBrowserPersist> persist =
do_CreateInstance("@mozilla.org/embedding/browser/nsWebBrowserPersist;1",
&rv);
NS_ENSURE_SUCCESS(rv, rv);
- return persist->SaveURI(sourceURI, nsnull, nsnull, nsnull, nsnull,
- inDestFile);
+ return persist->SaveURI(sourceURI, nsnull, nsnull, nsnull, nsnull, inDestFile);
}
// This is our nsIFlavorDataProvider callback. There are several
// assumptions here that make this work:
//
// 1. Someone put a kFilePromiseURLMime flavor into the transferable
// with the source URI of the file to save (as a string). We did
-// that above.
+// that in AddStringsToDataTransfer.
//
// 2. Someone put a kFilePromiseDirectoryMime flavor into the
// transferable with an nsILocalFile for the directory we are to
// save in. That has to be done by platform-specific code (in
-// widget), // which gets the destination directory from
-// OS-specific drag // information.
+// widget), which gets the destination directory from
+// OS-specific drag information.
//
NS_IMETHODIMP
-nsContentAreaDragDrop::GetFlavorData(nsITransferable *aTransferable,
- const char *aFlavor, nsISupports **aData,
- PRUint32 *aDataLen)
+nsContentAreaDragDropDataProvider::GetFlavorData(nsITransferable *aTransferable,
+ const char *aFlavor,
+ nsISupports **aData,
+ PRUint32 *aDataLen)
{
NS_ENSURE_ARG_POINTER(aData && aDataLen);
*aData = nsnull;
*aDataLen = 0;
nsresult rv = NS_ERROR_NOT_IMPLEMENTED;
if (strcmp(aFlavor, kFilePromiseMime) == 0) {
@@ -932,75 +736,74 @@ nsContentAreaDragDrop::GetFlavorData(nsI
return NS_ERROR_FAILURE;
nsCOMPtr<nsIFile> file;
rv = destDirectory->Clone(getter_AddRefs(file));
NS_ENSURE_SUCCESS(rv, rv);
file->Append(targetFilename);
- // now save the file
rv = SaveURIToFile(sourceURLString, file);
-
// send back an nsILocalFile
if (NS_SUCCEEDED(rv)) {
CallQueryInterface(file, aData);
*aDataLen = sizeof(nsIFile*);
}
}
return rv;
}
-nsTransferableFactory::nsTransferableFactory(nsIDOMEvent* inMouseEvent,
- nsIFlavorDataProvider *dataProvider)
- : mInstanceAlreadyUsed(PR_FALSE),
- mMouseEvent(inMouseEvent),
- mFlavorDataProvider(dataProvider)
+nsTransferableFactory::nsTransferableFactory(nsIDOMWindow* aWindow,
+ nsIContent* aTarget,
+ nsIContent* aSelectionTargetNode,
+ PRBool aIsAltKeyPressed)
+ : mWindow(aWindow),
+ mTarget(aTarget),
+ mSelectionTargetNode(aSelectionTargetNode),
+ mIsAltKeyPressed(aIsAltKeyPressed)
{
}
//
// FindParentLinkNode
//
// Finds the parent with the given link tag starting at |inNode|. If
// it gets up to the root without finding it, we stop looking and
// return null.
//
-already_AddRefed<nsIDOMNode>
-nsTransferableFactory::FindParentLinkNode(nsIDOMNode* inNode)
+already_AddRefed<nsIContent>
+nsTransferableFactory::FindParentLinkNode(nsIContent* inNode)
{
- nsCOMPtr<nsIContent> content(do_QueryInterface(inNode));
+ nsIContent* content = inNode;
if (!content) {
// That must have been the document node; nothing else to do here;
return nsnull;
}
for (; content; content = content->GetParent()) {
if (nsContentUtils::IsDraggableLink(content)) {
- nsIDOMNode* node = nsnull;
- CallQueryInterface(content, &node);
- return node;
+ NS_ADDREF(content);
+ return content;
}
}
return nsnull;
}
//
// GetAnchorURL
//
void
-nsTransferableFactory::GetAnchorURL(nsIDOMNode* inNode, nsAString& outURL)
+nsTransferableFactory::GetAnchorURL(nsIContent* inNode, nsAString& outURL)
{
nsCOMPtr<nsIURI> linkURI;
- nsCOMPtr<nsIContent> content = do_QueryInterface(inNode);
- if (!content || !content->IsLink(getter_AddRefs(linkURI))) {
+ if (!inNode || !inNode->IsLink(getter_AddRefs(linkURI))) {
// Not a link
outURL.Truncate();
return;
}
nsCAutoString spec;
linkURI->GetSpec(spec);
CopyUTF8toUTF16(spec, outURL);
@@ -1032,190 +835,166 @@ nsTransferableFactory::CreateLinkText(co
//
// GetNodeString
//
// Gets the text associated with a node
//
void
-nsTransferableFactory::GetNodeString(nsIDOMNode* inNode,
+nsTransferableFactory::GetNodeString(nsIContent* inNode,
nsAString & outNodeString)
{
+ nsCOMPtr<nsIDOMNode> node = do_QueryInterface(inNode);
+
outNodeString.Truncate();
// use a range to get the text-equivalent of the node
nsCOMPtr<nsIDOMDocument> doc;
- inNode->GetOwnerDocument(getter_AddRefs(doc));
+ node->GetOwnerDocument(getter_AddRefs(doc));
nsCOMPtr<nsIDOMDocumentRange> docRange(do_QueryInterface(doc));
if (docRange) {
nsCOMPtr<nsIDOMRange> range;
docRange->CreateRange(getter_AddRefs(range));
if (range) {
- range->SelectNode(inNode);
+ range->SelectNode(node);
range->ToString(outNodeString);
}
}
}
nsresult
-nsTransferableFactory::Produce(PRBool* aDragSelection,
- nsITransferable** outTrans)
+nsTransferableFactory::Produce(nsDOMDataTransfer* aDataTransfer,
+ PRBool* aCanDrag,
+ PRBool* aDragSelection,
+ nsIContent** aDragNode)
{
- if (mInstanceAlreadyUsed) {
- return NS_ERROR_FAILURE;
- }
+ NS_PRECONDITION(aCanDrag && aDragSelection && aDataTransfer && aDragNode,
+ "null pointer passed to Produce");
+ NS_ASSERTION(mWindow, "window not set");
+ NS_ASSERTION(mSelectionTargetNode, "selection target node should have been set");
- if (!outTrans || !mMouseEvent || !mFlavorDataProvider) {
- return NS_ERROR_FAILURE;
- }
+ *aDragNode = nsnull;
- mInstanceAlreadyUsed = PR_TRUE;
- *outTrans = nsnull;
+ nsIContent* dragNode = nsnull;
- nsCOMPtr<nsIDOMWindow> window;
- PRBool isAltKeyDown = PR_FALSE;
mIsAnchor = PR_FALSE;
- {
- nsCOMPtr<nsIDOMUIEvent> uiEvent(do_QueryInterface(mMouseEvent));
- if (!uiEvent) {
- return NS_OK;
- }
-
- // find the selection to see what we could be dragging and if
- // what we're dragging is in what is selected.
- nsCOMPtr<nsIDOMAbstractView> view;
- uiEvent->GetView(getter_AddRefs(view));
- window = do_QueryInterface(view);
- if (!window) {
- return NS_OK;
- }
- }
-
- {
- nsCOMPtr<nsIDOMMouseEvent> mouseEvent(do_QueryInterface(mMouseEvent));
- if (mouseEvent) {
- mouseEvent->GetAltKey(&isAltKeyDown);
- }
- }
-
+ // find the selection to see what we could be dragging and if
+ // what we're dragging is in what is selected.
nsCOMPtr<nsISelection> selection;
- window->GetSelection(getter_AddRefs(selection));
+ mWindow->GetSelection(getter_AddRefs(selection));
if (!selection) {
return NS_OK;
}
+ // check if the node is inside a form control. If so, dragging will be
+ // handled in editor code (nsPlaintextDataTransfer::DoDrag). Don't set
+ // aCanDrag to false however, as we still want to allow the drag.
+ nsCOMPtr<nsIContent> findFormNode = mSelectionTargetNode;
+ nsIContent* findFormParent = findFormNode->GetParent();
+ while (findFormParent) {
+ nsCOMPtr<nsIFormControl> form(do_QueryInterface(findFormParent));
+ if (form && form->GetType() != NS_FORM_OBJECT)
+ return NS_OK;
+ findFormParent = findFormParent->GetParent();
+ }
+
// if set, serialize the content under this node
- nsCOMPtr<nsIDOMNode> nodeToSerialize;
- PRBool useSelectedText = PR_FALSE;
+ nsCOMPtr<nsIContent> nodeToSerialize;
*aDragSelection = PR_FALSE;
{
PRBool haveSelectedContent = PR_FALSE;
// possible parent link node
- nsCOMPtr<nsIDOMNode> parentLink;
- nsCOMPtr<nsIDOMNode> draggedNode;
+ nsCOMPtr<nsIContent> parentLink;
+ nsCOMPtr<nsIContent> draggedNode;
{
- nsCOMPtr<nsIDOMEventTarget> target;
- mMouseEvent->GetTarget(getter_AddRefs(target));
-
// only drag form elements by using the alt key,
// otherwise buttons and select widgets are hard to use
// Note that while <object> elements implement nsIFormControl, we should
// really allow dragging them if they happen to be images.
- nsCOMPtr<nsIFormControl> form(do_QueryInterface(target));
- if (form && !isAltKeyDown && form->GetType() != NS_FORM_OBJECT) {
+ nsCOMPtr<nsIFormControl> form(do_QueryInterface(mTarget));
+ if (form && !mIsAltKeyPressed && form->GetType() != NS_FORM_OBJECT) {
+ *aCanDrag = PR_FALSE;
return NS_OK;
}
- draggedNode = do_QueryInterface(target);
+ draggedNode = mTarget;
}
nsCOMPtr<nsIDOMHTMLAreaElement> area; // client-side image map
nsCOMPtr<nsIImageLoadingContent> image;
nsCOMPtr<nsIDOMHTMLAnchorElement> link;
- {
- // Get the real target and see if it is in the selection
- nsCOMPtr<nsIDOMNode> realTargetNode;
+ nsCOMPtr<nsIContent> selectedImageOrLinkNode;
+ GetDraggableSelectionData(selection, mSelectionTargetNode,
+ getter_AddRefs(selectedImageOrLinkNode),
+ &haveSelectedContent);
- {
- nsCOMPtr<nsIDOMNSEvent> internalEvent = do_QueryInterface(mMouseEvent);
- if (internalEvent) {
- nsCOMPtr<nsIDOMEventTarget> realTarget;
- internalEvent->GetExplicitOriginalTarget(getter_AddRefs(realTarget));
- realTargetNode = do_QueryInterface(realTarget);
- }
+ // either plain text or anchor text is selected
+ if (haveSelectedContent) {
+ link = do_QueryInterface(selectedImageOrLinkNode);
+ if (link && mIsAltKeyPressed) {
+ // if alt is pressed, select the link text instead of drag the link
+ *aCanDrag = PR_FALSE;
+ return NS_OK;
}
- {
- nsCOMPtr<nsIDOMNode> selectedImageOrLinkNode;
- GetDraggableSelectionData(selection, realTargetNode,
- getter_AddRefs(selectedImageOrLinkNode),
- &haveSelectedContent);
-
- // either plain text or anchor text is selected
- if (haveSelectedContent) {
- link = do_QueryInterface(selectedImageOrLinkNode);
- if (link && isAltKeyDown) {
- return NS_OK;
- }
+ *aDragSelection = PR_TRUE;
+ } else if (selectedImageOrLinkNode) {
+ // an image is selected
+ image = do_QueryInterface(selectedImageOrLinkNode);
+ } else {
+ // nothing is selected -
+ //
+ // look for draggable elements under the mouse
+ //
+ // if the alt key is down, don't start a drag if we're in an
+ // anchor because we want to do selection.
+ parentLink = FindParentLinkNode(draggedNode);
+ if (parentLink && mIsAltKeyPressed) {
+ *aCanDrag = PR_FALSE;
+ return NS_OK;
+ }
- useSelectedText = PR_TRUE;
- *aDragSelection = PR_TRUE;
- } else if (selectedImageOrLinkNode) {
- // an image is selected
- image = do_QueryInterface(selectedImageOrLinkNode);
- } else {
- // nothing is selected -
- //
- // look for draggable elements under the mouse
- //
- // if the alt key is down, don't start a drag if we're in an
- // anchor because we want to do selection.
- parentLink = FindParentLinkNode(draggedNode);
- if (parentLink && isAltKeyDown) {
- return NS_OK;
- }
-
- area = do_QueryInterface(draggedNode);
- image = do_QueryInterface(draggedNode);
- link = do_QueryInterface(draggedNode);
- }
- }
+ area = do_QueryInterface(draggedNode);
+ image = do_QueryInterface(draggedNode);
+ link = do_QueryInterface(draggedNode);
}
{
// set for linked images, and links
- nsCOMPtr<nsIDOMNode> linkNode;
+ nsCOMPtr<nsIContent> linkNode;
if (area) {
// use the alt text (or, if missing, the href) as the title
area->GetAttribute(NS_LITERAL_STRING("alt"), mTitleString);
if (mTitleString.IsEmpty()) {
// this can be a relative link
area->GetAttribute(NS_LITERAL_STRING("href"), mTitleString);
}
// we'll generate HTML like <a href="absurl">alt text</a>
mIsAnchor = PR_TRUE;
// gives an absolute link
- GetAnchorURL(area, mUrlString);
+ GetAnchorURL(draggedNode, mUrlString);
mHtmlString.AssignLiteral("<a href=\"");
mHtmlString.Append(mUrlString);
mHtmlString.AppendLiteral("\">");
mHtmlString.Append(mTitleString);
mHtmlString.AppendLiteral("</a>");
+
+ dragNode = draggedNode;
} else if (image) {
mIsAnchor = PR_TRUE;
// grab the href as the url, use alt text as the title of the
// area if it's there. the drag data is the image tag and src
// attribute.
nsCOMPtr<nsIURI> imageURI;
image->GetCurrentURI(getter_AddRefs(imageURI));
if (imageURI) {
@@ -1305,45 +1084,47 @@ nsTransferableFactory::Produce(PRBool* a
}
if (parentLink) {
// If we are dragging around an image in an anchor, then we
// are dragging the entire anchor
linkNode = parentLink;
nodeToSerialize = linkNode;
} else {
- nodeToSerialize = draggedNode;
+ nodeToSerialize = do_QueryInterface(draggedNode);
}
+ dragNode = nodeToSerialize;
} else if (link) {
// set linkNode. The code below will handle this
- linkNode = link; // XXX test this
+ linkNode = do_QueryInterface(link); // XXX test this
GetNodeString(draggedNode, mTitleString);
} else if (parentLink) {
// parentLink will always be null if there's selected content
linkNode = parentLink;
nodeToSerialize = linkNode;
} else if (!haveSelectedContent) {
// nothing draggable
return NS_OK;
}
if (linkNode) {
mIsAnchor = PR_TRUE;
GetAnchorURL(linkNode, mUrlString);
+ dragNode = linkNode;
}
}
}
- if (nodeToSerialize || useSelectedText) {
+ if (nodeToSerialize || *aDragSelection) {
// if we have selected text, use it in preference to the node
- if (useSelectedText) {
+ if (*aDragSelection) {
nodeToSerialize = nsnull;
}
- SerializeNodeOrSelection(window, nodeToSerialize,
+ SerializeNodeOrSelection(mWindow, nodeToSerialize,
mHtmlString, mContextString, mInfoString);
nsCOMPtr<nsIFormatConverter> htmlConverter =
do_CreateInstance(kHTMLConverterCID);
NS_ENSURE_TRUE(htmlConverter, NS_ERROR_FAILURE);
nsCOMPtr<nsISupportsString> html =
do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID);
@@ -1369,173 +1150,141 @@ nsTransferableFactory::Produce(PRBool* a
if (mTitleString.IsEmpty()) {
mTitleString = mUrlString;
}
// if we haven't constructed a html version, make one now
if (mHtmlString.IsEmpty() && !mUrlString.IsEmpty())
CreateLinkText(mUrlString, mTitleString, mHtmlString);
- return ConvertStringsToTransferable(outTrans);
+ // if there is no drag node, which will be the case for a selection, just
+ // use the selection target node.
+ nsresult rv = AddStringsToDataTransfer(
+ dragNode ? dragNode : mSelectionTargetNode.get(), aDataTransfer);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_IF_ADDREF(*aDragNode = dragNode);
+ return NS_OK;
+}
+
+void
+nsTransferableFactory::AddString(nsDOMDataTransfer* aDataTransfer,
+ const nsAString& aFlavor,
+ const nsAString& aData,
+ nsIPrincipal* aPrincipal)
+{
+ nsCOMPtr<nsIWritableVariant> variant = do_CreateInstance(NS_VARIANT_CONTRACTID);
+ if (variant) {
+ variant->SetAsAString(aData);
+ aDataTransfer->SetDataWithPrincipal(aFlavor, variant, 0, aPrincipal);
+ }
}
nsresult
-nsTransferableFactory::ConvertStringsToTransferable(nsITransferable** outTrans)
+nsTransferableFactory::AddStringsToDataTransfer(nsIContent* aDragNode,
+ nsDOMDataTransfer* aDataTransfer)
{
- // now create the transferable and stuff data into it.
- nsCOMPtr<nsITransferable> trans =
- do_CreateInstance("@mozilla.org/widget/transferable;1");
- NS_ENSURE_TRUE(trans, NS_ERROR_FAILURE);
+ NS_ASSERTION(aDragNode, "adding strings for null node");
+
+ // set all of the data to have the principal of the node where the data came from
+ nsIPrincipal* principal = aDragNode->NodePrincipal();
// add a special flavor if we're an anchor to indicate that we have
// a URL in the drag data
if (!mUrlString.IsEmpty() && mIsAnchor) {
nsAutoString dragData(mUrlString);
dragData.AppendLiteral("\n");
dragData += mTitleString;
- nsCOMPtr<nsISupportsString> urlPrimitive =
- do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID);
- NS_ENSURE_TRUE(urlPrimitive, NS_ERROR_FAILURE);
-
- urlPrimitive->SetData(dragData);
- trans->SetTransferData(kURLMime, urlPrimitive,
- dragData.Length() * sizeof(PRUnichar));
-
- nsCOMPtr<nsISupportsString> urlDataPrimitive =
- do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID);
- NS_ENSURE_TRUE(urlDataPrimitive, NS_ERROR_FAILURE);
-
- urlDataPrimitive->SetData(mUrlString);
- trans->SetTransferData(kURLDataMime, urlDataPrimitive,
- mUrlString.Length() * sizeof(PRUnichar));
-
- nsCOMPtr<nsISupportsString> urlDescPrimitive =
- do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID);
- NS_ENSURE_TRUE(urlDescPrimitive, NS_ERROR_FAILURE);
-
- urlDescPrimitive->SetData(mTitleString);
- trans->SetTransferData(kURLDescriptionMime, urlDescPrimitive,
- mTitleString.Length() * sizeof(PRUnichar));
+ AddString(aDataTransfer, NS_LITERAL_STRING(kURLMime), dragData, principal);
+ AddString(aDataTransfer, NS_LITERAL_STRING(kURLDataMime), mUrlString, principal);
+ AddString(aDataTransfer, NS_LITERAL_STRING(kURLDescriptionMime), mTitleString, principal);
+ AddString(aDataTransfer, NS_LITERAL_STRING("text/uri-list"), mUrlString, principal);
}
// add a special flavor, even if we don't have html context data
- nsCOMPtr<nsISupportsString> context =
- do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID);
- NS_ENSURE_TRUE(context, NS_ERROR_FAILURE);
-
- nsAutoString contextData(mContextString);
- context->SetData(contextData);
- trans->SetTransferData(kHTMLContext, context, contextData.Length() * 2);
+ AddString(aDataTransfer, NS_LITERAL_STRING(kHTMLContext), mContextString, principal);
// add a special flavor if we have html info data
- if (!mInfoString.IsEmpty()) {
- nsCOMPtr<nsISupportsString> info =
- do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID);
- NS_ENSURE_TRUE(info, NS_ERROR_FAILURE);
-
- nsAutoString infoData(mInfoString);
- info->SetData(infoData);
- trans->SetTransferData(kHTMLInfo, info, infoData.Length() * 2);
- }
+ if (!mInfoString.IsEmpty())
+ AddString(aDataTransfer, NS_LITERAL_STRING(kHTMLInfo), mInfoString, principal);
// add the full html
- nsCOMPtr<nsISupportsString> htmlPrimitive =
- do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID);
- NS_ENSURE_TRUE(htmlPrimitive, NS_ERROR_FAILURE);
-
- htmlPrimitive->SetData(mHtmlString);
- trans->SetTransferData(kHTMLMime, htmlPrimitive,
- mHtmlString.Length() * sizeof(PRUnichar));
+ AddString(aDataTransfer, NS_LITERAL_STRING(kHTMLMime), mHtmlString, principal);
- // add the plain (unicode) text. we use the url for text/unicode
- // data if an anchor is being dragged, rather than the title text of
- // the link or the alt text for an anchor image.
- nsCOMPtr<nsISupportsString> textPrimitive =
- do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID);
- NS_ENSURE_TRUE(textPrimitive, NS_ERROR_FAILURE);
-
- textPrimitive->SetData(mIsAnchor ? mUrlString : mTitleString);
- trans->SetTransferData(kUnicodeMime, textPrimitive,
- (mIsAnchor ? mUrlString.Length() :
- mTitleString.Length()) * sizeof(PRUnichar));
+ // add the plain text. we use the url for text/plain data if an anchor is
+ // being dragged, rather than the title text of the link or the alt text for
+ // an anchor image.
+ AddString(aDataTransfer, NS_LITERAL_STRING(kTextMime),
+ mIsAnchor ? mUrlString : mTitleString, principal);
// add image data, if present. For now, all we're going to do with
// this is turn it into a native data flavor, so indicate that with
// a new flavor so as not to confuse anyone who is really registered
// for image/gif or image/jpg.
if (mImage) {
- nsCOMPtr<nsISupportsInterfacePointer> ptrPrimitive =
- do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID);
- NS_ENSURE_TRUE(ptrPrimitive, NS_ERROR_FAILURE);
+ nsCOMPtr<nsIWritableVariant> variant = do_CreateInstance(NS_VARIANT_CONTRACTID);
+ if (variant) {
+ variant->SetAsISupports(mImage);
+ aDataTransfer->SetDataWithPrincipal(NS_LITERAL_STRING(kNativeImageMime),
+ variant, 0, principal);
+ }
- ptrPrimitive->SetData(mImage);
- trans->SetTransferData(kNativeImageMime, ptrPrimitive,
- sizeof(nsISupportsInterfacePointer*));
// assume the image comes from a file, and add a file promise. We
// register ourselves as a nsIFlavorDataProvider, and will use the
// GetFlavorData callback to save the image to disk.
- trans->SetTransferData(kFilePromiseMime, mFlavorDataProvider,
- nsITransferable::kFlavorHasDataProvider);
-
- nsCOMPtr<nsISupportsString> imageUrlPrimitive =
- do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID);
- NS_ENSURE_TRUE(imageUrlPrimitive, NS_ERROR_FAILURE);
- imageUrlPrimitive->SetData(mImageSourceString);
- trans->SetTransferData(kFilePromiseURLMime, imageUrlPrimitive,
- mImageSourceString.Length() * sizeof(PRUnichar));
+ nsCOMPtr<nsIFlavorDataProvider> dataProvider =
+ new nsContentAreaDragDropDataProvider();
+ if (dataProvider) {
+ nsCOMPtr<nsIWritableVariant> variant = do_CreateInstance(NS_VARIANT_CONTRACTID);
+ if (variant) {
+ variant->SetAsISupports(dataProvider);
+ aDataTransfer->SetDataWithPrincipal(NS_LITERAL_STRING(kFilePromiseMime),
+ variant, 0, principal);
+ }
+ }
- nsCOMPtr<nsISupportsString> imageFileNamePrimitive =
- do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID);
- NS_ENSURE_TRUE(imageFileNamePrimitive, NS_ERROR_FAILURE);
-
- imageFileNamePrimitive->SetData(mImageDestFileName);
- trans->SetTransferData(kFilePromiseDestFilename, imageFileNamePrimitive,
- mImageDestFileName.Length() * sizeof(PRUnichar));
+ AddString(aDataTransfer, NS_LITERAL_STRING(kFilePromiseURLMime),
+ mImageSourceString, principal);
+ AddString(aDataTransfer, NS_LITERAL_STRING(kFilePromiseDestFilename),
+ mImageDestFileName, principal);
// if not an anchor, add the image url
if (!mIsAnchor) {
- nsCOMPtr<nsISupportsString> urlDataPrimitive =
- do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID);
- NS_ENSURE_TRUE(urlDataPrimitive, NS_ERROR_FAILURE);
-
- urlDataPrimitive->SetData(mUrlString);
- trans->SetTransferData(kURLDataMime, urlDataPrimitive,
- mUrlString.Length() * sizeof(PRUnichar));
+ AddString(aDataTransfer, NS_LITERAL_STRING(kURLDataMime), mUrlString, principal);
+ AddString(aDataTransfer, NS_LITERAL_STRING("text/uri-list"), mUrlString, principal);
}
}
- *outTrans = trans;
- NS_IF_ADDREF(*outTrans);
-
return NS_OK;
}
// note that this can return NS_OK, but a null out param (by design)
// static
nsresult
nsTransferableFactory::GetDraggableSelectionData(nsISelection* inSelection,
- nsIDOMNode* inRealTargetNode,
- nsIDOMNode **outImageOrLinkNode,
+ nsIContent* inRealTargetNode,
+ nsIContent **outImageOrLinkNode,
PRBool* outDragSelectedText)
{
NS_ENSURE_ARG(inSelection);
NS_ENSURE_ARG(inRealTargetNode);
NS_ENSURE_ARG_POINTER(outImageOrLinkNode);
*outImageOrLinkNode = nsnull;
*outDragSelectedText = PR_FALSE;
PRBool selectionContainsTarget = PR_FALSE;
PRBool isCollapsed = PR_FALSE;
inSelection->GetIsCollapsed(&isCollapsed);
if (!isCollapsed) {
- inSelection->ContainsNode(inRealTargetNode, PR_FALSE,
+ nsCOMPtr<nsIDOMNode> realTargetNode = do_QueryInterface(inRealTargetNode);
+ inSelection->ContainsNode(realTargetNode, PR_FALSE,
&selectionContainsTarget);
if (selectionContainsTarget) {
// track down the anchor node, if any, for the url
nsCOMPtr<nsIDOMNode> selectionStart;
inSelection->GetAnchorNode(getter_AddRefs(selectionStart));
nsCOMPtr<nsIDOMNode> selectionEnd;
@@ -1559,18 +1308,17 @@ nsTransferableFactory::GetDraggableSelec
if (selStartContent) {
PRInt32 childOffset =
(anchorOffset < focusOffset) ? anchorOffset : focusOffset;
nsIContent *childContent =
selStartContent->GetChildAt(childOffset);
// if we find an image, we'll fall into the node-dragging code,
// rather the the selection-dragging code
if (nsContentUtils::IsDraggableImage(childContent)) {
- CallQueryInterface(childContent, outImageOrLinkNode);
-
+ NS_ADDREF(*outImageOrLinkNode = childContent);
return NS_OK;
}
}
}
}
}
// see if the selection is a link; if so, its node will be returned
@@ -1581,30 +1329,31 @@ nsTransferableFactory::GetDraggableSelec
}
}
return NS_OK;
}
// static
void nsTransferableFactory::GetSelectedLink(nsISelection* inSelection,
- nsIDOMNode **outLinkNode)
+ nsIContent **outLinkNode)
{
*outLinkNode = nsnull;
- nsCOMPtr<nsIDOMNode> selectionStart;
- inSelection->GetAnchorNode(getter_AddRefs(selectionStart));
- nsCOMPtr<nsIDOMNode> selectionEnd;
- inSelection->GetFocusNode(getter_AddRefs(selectionEnd));
+ nsCOMPtr<nsIDOMNode> selectionStartNode;
+ inSelection->GetAnchorNode(getter_AddRefs(selectionStartNode));
+ nsCOMPtr<nsIDOMNode> selectionEndNode;
+ inSelection->GetFocusNode(getter_AddRefs(selectionEndNode));
// simple case: only one node is selected
// see if it or its parent is an anchor, then exit
- if (selectionStart == selectionEnd) {
- nsCOMPtr<nsIDOMNode> link = FindParentLinkNode(selectionStart);
+ if (selectionStartNode == selectionEndNode) {
+ nsCOMPtr<nsIContent> selectionStart = do_QueryInterface(selectionStartNode);
+ nsCOMPtr<nsIContent> link = FindParentLinkNode(selectionStart);
if (link) {
link.swap(*outLinkNode);
}
return;
}
// more complicated case: multiple nodes are selected
@@ -1622,87 +1371,89 @@ void nsTransferableFactory::GetSelectedL
nsCOMPtr<nsIDOMRange> range;
inSelection->GetRangeAt(0, getter_AddRefs(range));
if (!range) {
return;
}
nsCOMPtr<nsIDOMNode> tempNode;
range->GetStartContainer( getter_AddRefs(tempNode));
- if (tempNode != selectionStart) {
- selectionEnd = selectionStart;
- selectionStart = tempNode;
+ if (tempNode != selectionStartNode) {
+ selectionEndNode = selectionStartNode;
+ selectionStartNode = tempNode;
inSelection->GetAnchorOffset(&endOffset);
inSelection->GetFocusOffset(&startOffset);
} else {
inSelection->GetAnchorOffset(&startOffset);
inSelection->GetFocusOffset(&endOffset);
}
}
// trim leading node if the string is empty or
// the selection starts at the end of the text
nsAutoString nodeStr;
- selectionStart->GetNodeValue(nodeStr);
+ selectionStartNode->GetNodeValue(nodeStr);
if (nodeStr.IsEmpty() ||
startOffset+1 >= static_cast<PRInt32>(nodeStr.Length())) {
- nsCOMPtr<nsIDOMNode> curr = selectionStart;
+ nsCOMPtr<nsIDOMNode> curr = selectionStartNode;
nsIDOMNode* next;
while (curr) {
curr->GetNextSibling(&next);
if (next) {
- selectionStart = dont_AddRef(next);
+ selectionStartNode = dont_AddRef(next);
break;
}
curr->GetParentNode(&next);
curr = dont_AddRef(next);
}
}
// trim trailing node if the selection ends before its text begins
if (endOffset == 0) {
- nsCOMPtr<nsIDOMNode> curr = selectionEnd;
+ nsCOMPtr<nsIDOMNode> curr = selectionEndNode;
nsIDOMNode* next;
while (curr) {
curr->GetPreviousSibling(&next);
if (next){
- selectionEnd = dont_AddRef(next);
+ selectionEndNode = dont_AddRef(next);
break;
}
curr->GetParentNode(&next);
curr = dont_AddRef(next);
}
}
// see if the leading & trailing nodes are part of the
// same anchor - if so, return the anchor node
- nsCOMPtr<nsIDOMNode> link = FindParentLinkNode(selectionStart);
+ nsCOMPtr<nsIContent> selectionStart = do_QueryInterface(selectionStartNode);
+ nsCOMPtr<nsIContent> link = FindParentLinkNode(selectionStart);
if (link) {
- nsCOMPtr<nsIDOMNode> link2 = FindParentLinkNode(selectionEnd);
+ nsCOMPtr<nsIContent> selectionEnd = do_QueryInterface(selectionEndNode);
+ nsCOMPtr<nsIContent> link2 = FindParentLinkNode(selectionEnd);
if (link == link2) {
NS_IF_ADDREF(*outLinkNode = link);
}
}
return;
}
// static
nsresult
nsTransferableFactory::SerializeNodeOrSelection(nsIDOMWindow* inWindow,
- nsIDOMNode* inNode,
+ nsIContent* inNode,
nsAString& outResultString,
nsAString& outContext,
nsAString& outInfo)
{
NS_ENSURE_ARG_POINTER(inWindow);
nsresult rv;
nsCOMPtr<nsIDocumentEncoder> encoder =
@@ -1712,21 +1463,22 @@ nsTransferableFactory::SerializeNodeOrSe
nsCOMPtr<nsIDOMDocument> domDoc;
inWindow->GetDocument(getter_AddRefs(domDoc));
NS_ENSURE_TRUE(domDoc, NS_ERROR_FAILURE);
PRUint32 flags = nsIDocumentEncoder::OutputAbsoluteLinks |
nsIDocumentEncoder::OutputEncodeHTMLEntities;
nsCOMPtr<nsIDOMRange> range;
nsCOMPtr<nsISelection> selection;
- if (inNode) {
+ nsCOMPtr<nsIDOMNode> node = do_QueryInterface(inNode);
+ if (node) {
// make a range around this node
rv = NS_NewRange(getter_AddRefs(range));
NS_ENSURE_SUCCESS(rv, rv);
- rv = range->SelectNode(inNode);
+ rv = range->SelectNode(node);
NS_ENSURE_SUCCESS(rv, rv);
} else {
inWindow->GetSelection(getter_AddRefs(selection));
flags |= nsIDocumentEncoder::OutputSelectionOnly;
}
rv = encoder->Init(domDoc, NS_LITERAL_STRING(kHTMLMime), flags);
NS_ENSURE_SUCCESS(rv, rv);
--- a/content/base/src/nsContentAreaDragDrop.h
+++ b/content/base/src/nsContentAreaDragDrop.h
@@ -53,16 +53,17 @@ class nsITransferable;
class nsIImage;
class nsIPresShell;
class nsPresContext;
class nsIContent;
class nsIDocument;
class nsIURI;
class nsIFile;
class nsISimpleEnumerator;
+class nsDOMDataTransfer;
// {1f34bc80-1bc7-11d6-a384-d705dd0746fc}
#define NS_CONTENTAREADRAGDROP_CID \
{ 0x1f34bc80, 0x1bc7, 0x11d6, \
{ 0xa3, 0x84, 0xd7, 0x05, 0xdd, 0x07, 0x46, 0xfc } }
#define NS_CONTENTAREADRAGDROP_CONTRACTID "@mozilla.org:/content/content-area-dragdrop;1"
@@ -71,64 +72,98 @@ class nsISimpleEnumerator;
// class nsContentAreaDragDrop
//
// The class that listens to the chrome events handles anything
// related to drag and drop. Registers itself with the DOM with
// AddChromeListeners() and removes itself with
// RemoveChromeListeners().
//
class nsContentAreaDragDrop : public nsIDOMDragListener,
- public nsIDragDropHandler,
- public nsIFlavorDataProvider
+ public nsIDragDropHandler
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIDRAGDROPHANDLER
- NS_DECL_NSIFLAVORDATAPROVIDER
nsContentAreaDragDrop();
virtual ~nsContentAreaDragDrop();
// nsIDOMDragListener
NS_IMETHOD DragEnter(nsIDOMEvent* aMouseEvent);
NS_IMETHOD DragOver(nsIDOMEvent* aMouseEvent);
NS_IMETHOD DragExit(nsIDOMEvent* aMouseEvent);
NS_IMETHOD DragDrop(nsIDOMEvent* aMouseEvent);
NS_IMETHOD DragGesture(nsIDOMEvent* aMouseEvent);
NS_IMETHOD Drag(nsIDOMEvent* aMouseEvent);
NS_IMETHOD DragEnd(nsIDOMEvent* aMouseEvent);
NS_IMETHOD HandleEvent(nsIDOMEvent *event);
+ /**
+ * Determine what data in the content area, if any, is being dragged.
+ *
+ * aWindow - the window containing the target node
+ * aTarget - the mousedown event target that started the drag
+ * aSelectionTargetNode - the node where the drag event should be fired
+ * aIsAltKeyPressed - true if the Alt key is pressed. In some cases, this
+ * will prevent the drag from occuring. For example,
+ * holding down Alt over a link should select the text,
+ * not drag the link.
+ * aDataTransfer - the dataTransfer for the drag event.
+ * aCanDrag - [out] set to true if the drag may proceed, false to stop the
+ * drag entirely
+ * aDragSelection - [out] set to true to indicate that a selection is being
+ * dragged, rather than a specific node
+ * aDragNode - [out] the link, image or area being dragged, or null if the
+ * drag occured on another element.
+ */
+ static nsresult GetDragData(nsIDOMWindow* aWindow,
+ nsIContent* aTarget,
+ nsIContent* aSelectionTargetNode,
+ PRBool aIsAltKeyPressed,
+ nsDOMDataTransfer* aDataTransfer,
+ PRBool* aCanDrag,
+ PRBool* aDragSelection,
+ nsIContent** aDragNode);
+
private:
// Add/remove the relevant listeners
nsresult AddDragListener();
nsresult RemoveDragListener();
// utility routines
static void NormalizeSelection(nsIDOMNode* inBaseNode,
nsISelection* inSelection);
static void GetEventDocument(nsIDOMEvent* inEvent,
nsIDOMDocument** outDocument);
- static nsresult SaveURIToFile(nsAString& inSourceURIString,
- nsIFile* inDestFile);
-
- void ExtractURLFromData(const nsACString & inFlavor,
- nsISupports* inDataWrapper, PRUint32 inDataLen,
- nsAString & outURL);
- nsresult GetHookEnumeratorFromEvent(nsIDOMEvent* inEvent,
- nsISimpleEnumerator** outEnumerator);
+ static void ExtractURLFromData(const nsACString & inFlavor,
+ nsISupports* inDataWrapper, PRUint32 inDataLen,
+ nsAString & outURL);
PRPackedBool mListenerInstalled;
nsCOMPtr<nsPIDOMEventTarget> mEventTarget;
// weak ref, this is probably my owning webshell
// FIXME: we set this and never null it out. That's bad! See bug 332187.
nsIWebNavigation* mNavigator;
};
+// this is used to save images to disk lazily when the image data is asked for
+// during the drop instead of when it is added to the drag data transfer. This
+// ensures that the image data is only created when an image drop is allowed.
+class nsContentAreaDragDropDataProvider : public nsIFlavorDataProvider
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIFLAVORDATAPROVIDER
+
+ virtual ~nsContentAreaDragDropDataProvider() {}
+
+ nsresult SaveURIToFile(nsAString& inSourceURIString,
+ nsIFile* inDestFile);
+};
#endif /* nsContentAreaDragDrop_h__ */
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -149,16 +149,17 @@ static NS_DEFINE_CID(kXTFServiceCID, NS_
#include "nsIPermissionManager.h"
#include "nsIScriptObjectPrincipal.h"
#include "nsIRunnable.h"
#include "nsDOMJSUtils.h"
#include "nsGenericHTMLElement.h"
#include "nsAttrValue.h"
#include "nsReferencedElement.h"
#include "nsIUGenCategory.h"
+#include "nsIDragService.h"
#ifdef IBMBIDI
#include "nsIBidiKeyboard.h"
#endif
#include "nsCycleCollectionParticipant.h"
// for ReportToConsole
#include "nsIStringBundle.h"
@@ -412,23 +413,26 @@ nsContentUtils::InitializeEventTable() {
{ &nsGkAtoms::onclose, { NS_XUL_CLOSE, EventNameType_XUL }},
{ &nsGkAtoms::onpopupshowing, { NS_XUL_POPUP_SHOWING, EventNameType_XUL }},
{ &nsGkAtoms::onpopupshown, { NS_XUL_POPUP_SHOWN, EventNameType_XUL }},
{ &nsGkAtoms::onpopuphiding, { NS_XUL_POPUP_HIDING, EventNameType_XUL }},
{ &nsGkAtoms::onpopuphidden, { NS_XUL_POPUP_HIDDEN, EventNameType_XUL }},
{ &nsGkAtoms::oncommand, { NS_XUL_COMMAND, EventNameType_XUL }},
{ &nsGkAtoms::onbroadcast, { NS_XUL_BROADCAST, EventNameType_XUL }},
{ &nsGkAtoms::oncommandupdate, { NS_XUL_COMMAND_UPDATE, EventNameType_XUL }},
- { &nsGkAtoms::ondragenter, { NS_DRAGDROP_ENTER, EventNameType_XUL }},
- { &nsGkAtoms::ondragover, { NS_DRAGDROP_OVER_SYNTH, EventNameType_XUL }},
+ { &nsGkAtoms::ondragenter, { NS_DRAGDROP_ENTER, EventNameType_HTMLXUL }},
+ { &nsGkAtoms::ondragover, { NS_DRAGDROP_OVER_SYNTH, EventNameType_HTMLXUL }},
{ &nsGkAtoms::ondragexit, { NS_DRAGDROP_EXIT_SYNTH, EventNameType_XUL }},
- { &nsGkAtoms::ondragdrop, { NS_DRAGDROP_DROP, EventNameType_XUL }},
+ { &nsGkAtoms::ondragdrop, { NS_DRAGDROP_DRAGDROP, EventNameType_XUL }},
{ &nsGkAtoms::ondraggesture, { NS_DRAGDROP_GESTURE, EventNameType_XUL }},
- { &nsGkAtoms::ondrag, { NS_DRAGDROP_DRAG, EventNameType_XUL }},
- { &nsGkAtoms::ondragend, { NS_DRAGDROP_END, EventNameType_XUL }},
+ { &nsGkAtoms::ondrag, { NS_DRAGDROP_DRAG, EventNameType_HTMLXUL }},
+ { &nsGkAtoms::ondragend, { NS_DRAGDROP_END, EventNameType_HTMLXUL }},
+ { &nsGkAtoms::ondragstart, { NS_DRAGDROP_START, EventNameType_HTMLXUL }},
+ { &nsGkAtoms::ondragleave, { NS_DRAGDROP_LEAVE_SYNTH, EventNameType_HTMLXUL }},
+ { &nsGkAtoms::ondrop, { NS_DRAGDROP_DROP, EventNameType_HTMLXUL }},
{ &nsGkAtoms::onoverflow, { NS_SCROLLPORT_OVERFLOW, EventNameType_XUL }},
{ &nsGkAtoms::onunderflow, { NS_SCROLLPORT_UNDERFLOW, EventNameType_XUL }}
#ifdef MOZ_SVG
,{ &nsGkAtoms::onSVGLoad, { NS_SVG_LOAD, EventNameType_None }},
{ &nsGkAtoms::onSVGUnload, { NS_SVG_UNLOAD, EventNameType_None }},
{ &nsGkAtoms::onSVGAbort, { NS_SVG_ABORT, EventNameType_None }},
{ &nsGkAtoms::onSVGError, { NS_SVG_ERROR, EventNameType_None }},
{ &nsGkAtoms::onSVGResize, { NS_SVG_RESIZE, EventNameType_None }},
@@ -4269,16 +4273,28 @@ nsContentUtils::HidePopupsInDocument(nsI
nsCOMPtr<nsIDocShellTreeItem> docShellToHide = do_QueryInterface(container);
if (docShellToHide)
pm->HidePopupsInDocShell(docShellToHide);
}
#endif
}
/* static */
+already_AddRefed<nsIDragSession>
+nsContentUtils::GetDragSession()
+{
+ nsIDragSession* dragSession = nsnull;
+ nsCOMPtr<nsIDragService> dragService =
+ do_GetService("@mozilla.org/widget/dragservice;1");
+ if (dragService)
+ dragService->GetCurrentSession(&dragSession);
+ return dragSession;
+}
+
+/* static */
PRBool
nsContentUtils::URIIsLocalFile(nsIURI *aURI)
{
PRBool isFile;
nsCOMPtr<nsINetUtil> util = do_QueryInterface(sIOService);
return util && NS_SUCCEEDED(util->ProtocolHasFlags(aURI,
nsIProtocolHandler::URI_IS_LOCAL_FILE,
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -306,20 +306,24 @@ GK_ATOM(DOMNodeRemovedFromDocument, "DOM
GK_ATOM(DOMSubtreeModified, "DOMSubtreeModified")
GK_ATOM(double_, "double")
GK_ATOM(drag, "drag")
GK_ATOM(dragdrop, "dragdrop")
GK_ATOM(dragend, "dragend")
GK_ATOM(dragenter, "dragenter")
GK_ATOM(dragevent, "dragevent")
GK_ATOM(dragexit, "dragexit")
+GK_ATOM(draggable, "draggable")
GK_ATOM(draggesture, "draggesture")
GK_ATOM(dragging, "dragging")
+GK_ATOM(dragleave, "dragleave")
GK_ATOM(dragover, "dragover")
GK_ATOM(dragSession, "dragSession")
+GK_ATOM(dragstart, "dragstart")
+GK_ATOM(drop, "drop")
GK_ATOM(dropAfter, "dropAfter")
GK_ATOM(dropBefore, "dropBefore")
GK_ATOM(dropOn, "dropOn")
GK_ATOM(dt, "dt")
GK_ATOM(editable, "editable")
GK_ATOM(editing, "editing")
GK_ATOM(editor, "editor")
GK_ATOM(editorDisplayList, "EditorDisplay-List")
--- a/content/events/public/nsIPrivateDOMEvent.h
+++ b/content/events/public/nsIPrivateDOMEvent.h
@@ -78,16 +78,18 @@ nsresult
NS_NewDOMDataContainerEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, nsEvent *aEvent);
nsresult
NS_NewDOMUIEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, class nsGUIEvent *aEvent);
nsresult
NS_NewDOMMouseEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, class nsInputEvent *aEvent);
nsresult
NS_NewDOMMouseScrollEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, class nsInputEvent *aEvent);
nsresult
+NS_NewDOMDragEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, class nsDragEvent *aEvent);
+nsresult
NS_NewDOMKeyboardEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, class nsKeyEvent *aEvent);
nsresult
NS_NewDOMMutationEvent(nsIDOMEvent** aResult, nsPresContext* aPresContext, class nsMutationEvent* aEvent);
nsresult
NS_NewDOMPopupBlockedEvent(nsIDOMEvent** aResult, nsPresContext* aPresContext, class nsPopupBlockedEvent* aEvent);
nsresult
NS_NewDOMTextEvent(nsIDOMEvent** aResult, nsPresContext* aPresContext, class nsTextEvent* aEvent);
nsresult
--- a/content/events/src/Makefile.in
+++ b/content/events/src/Makefile.in
@@ -71,32 +71,34 @@ CPPSRCS = \
nsEventStateManager.cpp \
nsDOMEvent.cpp \
nsDOMDataContainerEvent.cpp \
nsDOMUIEvent.cpp \
nsDOMKeyboardEvent.cpp \
nsDOMTextEvent.cpp \
nsDOMMouseEvent.cpp \
nsDOMMouseScrollEvent.cpp \
+ nsDOMDragEvent.cpp \
nsDOMMutationEvent.cpp \
nsDOMPopupBlockedEvent.cpp \
nsDOMBeforeUnloadEvent.cpp \
nsDOMPageTransitionEvent.cpp \
nsDOMXULCommandEvent.cpp \
nsDOMCommandEvent.cpp \
nsDOMMessageEvent.cpp \
nsPrivateTextRange.cpp \
nsDOMEventGroup.cpp \
nsXMLEventsManager.cpp \
nsXMLEventsElement.cpp \
nsPLDOMEvent.cpp \
nsEventDispatcher.cpp \
nsIMEStateManager.cpp \
nsQueryContentEventHandler.cpp \
nsDOMProgressEvent.cpp \
+ nsDOMDataTransfer.cpp \
$(NULL)
# we don't want the shared lib, but we want to force the creation of a static lib.
FORCE_STATIC_LIB = 1
include $(topsrcdir)/config/rules.mk
LOCAL_INCLUDES = \
new file mode 100644
--- /dev/null
+++ b/content/events/src/nsDOMDataTransfer.cpp
@@ -0,0 +1,768 @@
+/* -*- 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 the Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Neil Deakin <enndeakin@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of 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 ***** */
+
+#include "nsDOMDataTransfer.h"
+
+#include "prlog.h"
+#include "nsAutoPtr.h"
+#include "nsString.h"
+#include "nsIServiceManager.h"
+#include "nsIVariant.h"
+#include "nsISupportsPrimitives.h"
+#include "nsDOMClassInfo.h"
+#include "nsDOMLists.h"
+#include "nsGUIEvent.h"
+#include "nsDOMError.h"
+#include "nsIDragService.h"
+#include "nsIScriptableRegion.h"
+#include "nsContentUtils.h"
+
+NS_IMPL_CYCLE_COLLECTION_2(nsDOMDataTransfer, mDragTarget, mDragImage)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMDataTransfer)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMDataTransfer)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMDataTransfer)
+ NS_INTERFACE_MAP_ENTRY(nsIDOMDataTransfer)
+ NS_INTERFACE_MAP_ENTRY(nsIDOMNSDataTransfer)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMDataTransfer)
+ NS_INTERFACE_MAP_ENTRY_DOM_CLASSINFO(DataTransfer)
+NS_INTERFACE_MAP_END
+
+// the size of the array
+const char nsDOMDataTransfer::sEffects[8][9] = {
+ "none", "copy", "move", "copyMove", "link", "copyLink", "linkMove", "all"
+};
+
+nsDOMDataTransfer::nsDOMDataTransfer()
+ : mEventType(NS_DRAGDROP_START),
+ mDropEffect(nsIDragService::DRAGDROP_ACTION_NONE),
+ mEffectAllowed(nsIDragService::DRAGDROP_ACTION_UNINITIALIZED),
+ mReadOnly(PR_FALSE),
+ mIsExternal(PR_FALSE),
+ mDragImageX(0),
+ mDragImageY(0)
+{
+}
+
+nsDOMDataTransfer::nsDOMDataTransfer(PRUint32 aEventType, PRUint32 aAction)
+ : mEventType(aEventType),
+ mDropEffect(nsIDragService::DRAGDROP_ACTION_NONE),
+ mReadOnly(PR_TRUE),
+ mIsExternal(PR_TRUE),
+ mDragImageX(0),
+ mDragImageY(0)
+{
+ mEffectAllowed = aAction &
+ (nsIDragService::DRAGDROP_ACTION_COPY |
+ nsIDragService::DRAGDROP_ACTION_LINK |
+ nsIDragService::DRAGDROP_ACTION_MOVE);
+
+ CacheExternalFormats();
+}
+
+nsDOMDataTransfer::nsDOMDataTransfer(PRUint32 aEventType,
+ const PRUint32 aEffectAllowed,
+ PRBool aIsExternal,
+ nsTArray<nsTArray<TransferItem> >& aItems,
+ nsIDOMElement* aDragImage,
+ PRUint32 aDragImageX,
+ PRUint32 aDragImageY)
+ : mEventType(aEventType),
+ mDropEffect(nsIDragService::DRAGDROP_ACTION_NONE),
+ mEffectAllowed(aEffectAllowed),
+ mReadOnly(PR_TRUE),
+ mIsExternal(aIsExternal),
+ mItems(aItems),
+ mDragImage(aDragImage),
+ mDragImageX(aDragImageX),
+ mDragImageY(aDragImageY)
+{
+ // The items are copied from aItems into mItems. There is no need to copy
+ // the actual data in the items as the data transfer will be read only. The
+ // draggesture and dragstart events are the only times when items are
+ // modifiable, but those events should have been using the first constructor
+ // above.
+ NS_ASSERTION(aEventType != NS_DRAGDROP_GESTURE &&
+ aEventType != NS_DRAGDROP_START,
+ "invalid event type for nsDOMDataTransfer constructor");
+}
+
+NS_IMETHODIMP
+nsDOMDataTransfer::GetDropEffect(nsAString& aDropEffect)
+{
+ aDropEffect.AssignASCII(sEffects[mDropEffect]);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMDataTransfer::SetDropEffect(const nsAString& aDropEffect)
+{
+ // the drop effect can only be 'none', 'copy', 'move' or 'link'.
+ for (PRUint32 e = 0; e <= nsIDragService::DRAGDROP_ACTION_LINK; e++) {
+ if (aDropEffect.EqualsASCII(sEffects[e])) {
+ // don't allow copyMove
+ if (e != (nsIDragService::DRAGDROP_ACTION_COPY |
+ nsIDragService::DRAGDROP_ACTION_MOVE))
+ mDropEffect = e;
+ break;
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMDataTransfer::GetEffectAllowed(nsAString& aEffectAllowed)
+{
+ if (mEffectAllowed == nsIDragService::DRAGDROP_ACTION_UNINITIALIZED)
+ aEffectAllowed.AssignLiteral("uninitialized");
+ else
+ aEffectAllowed.AssignASCII(sEffects[mEffectAllowed]);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMDataTransfer::SetEffectAllowed(const nsAString& aEffectAllowed)
+{
+ if (aEffectAllowed.EqualsLiteral("uninitialized")) {
+ mEffectAllowed = nsIDragService::DRAGDROP_ACTION_UNINITIALIZED;
+ return NS_OK;
+ }
+
+ PR_STATIC_ASSERT(nsIDragService::DRAGDROP_ACTION_NONE == 0);
+ PR_STATIC_ASSERT(nsIDragService::DRAGDROP_ACTION_COPY == 1);
+ PR_STATIC_ASSERT(nsIDragService::DRAGDROP_ACTION_MOVE == 2);
+ PR_STATIC_ASSERT(nsIDragService::DRAGDROP_ACTION_LINK == 4);
+
+ for (PRUint32 e = 0; e < NS_ARRAY_LENGTH(sEffects); e++) {
+ if (aEffectAllowed.EqualsASCII(sEffects[e])) {
+ mEffectAllowed = e;
+ break;
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMDataTransfer::GetDropEffectInt(PRUint32* aDropEffect)
+{
+ *aDropEffect = mDropEffect;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMDataTransfer::SetDropEffectInt(PRUint32 aDropEffect)
+{
+ mDropEffect = aDropEffect;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMDataTransfer::GetEffectAllowedInt(PRUint32* aEffectAllowed)
+{
+ *aEffectAllowed = mEffectAllowed;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMDataTransfer::SetEffectAllowedInt(PRUint32 aEffectAllowed)
+{
+ mEffectAllowed = aEffectAllowed;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMDataTransfer::GetTypes(nsIDOMDOMStringList** aTypes)
+{
+ return MozTypesAt(0, aTypes);
+}
+
+NS_IMETHODIMP
+nsDOMDataTransfer::GetData(const nsAString& aFormat, nsAString& aData)
+{
+ // return an empty string if data for the format was not found
+ aData.Truncate();
+
+ nsCOMPtr<nsIVariant> data;
+ nsresult rv = MozGetDataAt(aFormat, 0, getter_AddRefs(data));
+ if (rv == NS_ERROR_DOM_INDEX_SIZE_ERR)
+ return NS_OK;
+
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (data) {
+ nsAutoString stringdata;
+ data->GetAsAString(stringdata);
+
+ // for the URL type, parse out the first URI from the list. The URIs are
+ // separated by newlines
+ if (aFormat.EqualsLiteral("URL")) {
+ PRInt32 lastidx = 0, idx;
+ PRInt32 length = stringdata.Length();
+ while (lastidx < length) {
+ idx = stringdata.FindChar('\n', lastidx);
+ // lines beginning with # are comments
+ if (stringdata[lastidx] == '#') {
+ if (idx == -1)
+ break;
+ }
+ else {
+ if (idx == -1)
+ aData.Assign(Substring(stringdata, lastidx));
+ else
+ aData.Assign(Substring(stringdata, lastidx, idx - lastidx));
+ aData = nsContentUtils::TrimWhitespace(aData, PR_TRUE);
+ return NS_OK;
+ }
+ lastidx = idx + 1;
+ }
+ }
+ else {
+ aData = stringdata;
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMDataTransfer::SetData(const nsAString& aFormat, const nsAString& aData)
+{
+ nsCOMPtr<nsIWritableVariant> variant = do_CreateInstance(NS_VARIANT_CONTRACTID);
+ NS_ENSURE_TRUE(variant, NS_ERROR_OUT_OF_MEMORY);
+
+ variant->SetAsAString(aData);
+
+ return MozSetDataAt(aFormat, variant, 0);
+}
+
+NS_IMETHODIMP
+nsDOMDataTransfer::ClearData(const nsAString& aFormat)
+{
+ nsresult rv = MozClearDataAt(aFormat, 0);
+ return (rv == NS_ERROR_DOM_INDEX_SIZE_ERR) ? NS_OK : rv;
+}
+
+NS_IMETHODIMP
+nsDOMDataTransfer::GetMozItemCount(PRUint32* aCount)
+{
+ *aCount = mItems.Length();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMDataTransfer::MozTypesAt(PRUint32 aIndex, nsIDOMDOMStringList** aTypes)
+{
+ *aTypes = nsnull;
+
+ nsRefPtr<nsDOMStringList> types = new nsDOMStringList();
+ NS_ENSURE_TRUE(types, NS_ERROR_OUT_OF_MEMORY);
+
+ if (aIndex < mItems.Length()) {
+ // note that you can retrieve the types regardless of their principal
+ nsTArray<TransferItem>& item = mItems[aIndex];
+ for (PRUint32 i = 0; i < item.Length(); i++)
+ types->Add(item[i].mFormat);
+ }
+
+ *aTypes = types;
+ NS_ADDREF(*aTypes);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMDataTransfer::MozGetDataAt(const nsAString& aFormat,
+ PRUint32 aIndex,
+ nsIVariant** aData)
+{
+ *aData = nsnull;
+
+ if (aFormat.IsEmpty())
+ return NS_OK;
+
+ if (aIndex >= mItems.Length())
+ return NS_ERROR_DOM_INDEX_SIZE_ERR;
+
+ nsAutoString format;
+ GetRealFormat(aFormat, format);
+
+ nsTArray<TransferItem>& item = mItems[aIndex];
+
+ // allow access to any data in the drop and dragdrop events, or if the
+ // UniversalBrowserRead privilege is set, otherwise only allow access to
+ // data from the same principal.
+ nsIPrincipal* principal = nsnull;
+ if (mEventType != NS_DRAGDROP_DROP && mEventType != NS_DRAGDROP_DRAGDROP &&
+ !nsContentUtils::IsCallerTrustedForCapability("UniversalBrowserRead"))
+ principal = GetCurrentPrincipal();
+
+ PRUint32 count = item.Length();
+ for (PRUint32 i = 0; i < count; i++) {
+ TransferItem& formatitem = item[i];
+ if (formatitem.mFormat.Equals(format)) {
+ PRBool subsumes;
+ if (formatitem.mPrincipal && principal &&
+ (NS_FAILED(principal->Subsumes(formatitem.mPrincipal, &subsumes)) || !subsumes))
+ return NS_ERROR_DOM_SECURITY_ERR;
+
+ if (!formatitem.mData)
+ FillInExternalDragData(formatitem, aIndex);
+ *aData = formatitem.mData;
+ NS_IF_ADDREF(*aData);
+ return NS_OK;
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMDataTransfer::MozSetDataAt(const nsAString& aFormat,
+ nsIVariant* aData,
+ PRUint32 aIndex)
+{
+ NS_ENSURE_TRUE(aData, NS_ERROR_NULL_POINTER);
+
+ if (aFormat.IsEmpty())
+ return NS_OK;
+
+ if (mReadOnly)
+ return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
+
+ // Specifying an index less than the current length will replace an existing
+ // item. Specifying an index equal to the current length will add a new item.
+ if (aIndex > mItems.Length())
+ return NS_ERROR_DOM_INDEX_SIZE_ERR;
+
+ // don't allow non-chrome to add file data
+ // XXX perhaps this should also limit any non-string type as well
+ if ((aFormat.EqualsLiteral("application/x-moz-file-promise") ||
+ aFormat.EqualsLiteral("application/x-moz-file")) &&
+ !nsContentUtils::IsCallerChrome()) {
+ return NS_ERROR_DOM_SECURITY_ERR;
+ }
+
+ return SetDataWithPrincipal(aFormat, aData, aIndex, GetCurrentPrincipal());
+}
+
+NS_IMETHODIMP
+nsDOMDataTransfer::MozClearDataAt(const nsAString& aFormat, PRUint32 aIndex)
+{
+ if (mReadOnly)
+ return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
+
+ if (aIndex >= mItems.Length())
+ return NS_ERROR_DOM_INDEX_SIZE_ERR;
+
+ nsAutoString format;
+ GetRealFormat(aFormat, format);
+
+ nsIPrincipal* principal = GetCurrentPrincipal();
+
+ // if the format is empty, clear all formats
+ PRBool clearall = format.IsEmpty();
+
+ nsTArray<TransferItem>& item = mItems[aIndex];
+ // count backwards so that the count and index don't have to be adjusted
+ // after removing an element
+ for (PRInt32 i = item.Length() - 1; i >= 0; i--) {
+ TransferItem& formatitem = item[i];
+ if (clearall || formatitem.mFormat.Equals(format)) {
+ // don't allow removing data that has a stronger principal
+ PRBool subsumes;
+ if (formatitem.mPrincipal && principal &&
+ (NS_FAILED(principal->Subsumes(formatitem.mPrincipal, &subsumes)) || !subsumes))
+ return NS_ERROR_DOM_SECURITY_ERR;
+
+ item.RemoveElementAt(i);
+
+ // if a format was specified, break out. Otherwise, loop around until
+ // all formats have been removed
+ if (!clearall)
+ break;
+ }
+ }
+
+ // if the last format for an item is removed, remove the entire item
+ if (!item.Length())
+ mItems.RemoveElementAt(aIndex);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMDataTransfer::SetDragImage(nsIDOMElement* aImage, PRInt32 aX, PRInt32 aY)
+{
+ if (mReadOnly)
+ return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
+
+ mDragImage = aImage;
+ mDragImageX = aX;
+ mDragImageY = aY;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMDataTransfer::AddElement(nsIDOMElement* aElement)
+{
+ NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER);
+
+ if (mReadOnly)
+ return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
+
+ mDragTarget = do_QueryInterface(aElement);
+
+ return NS_OK;
+}
+
+nsresult
+nsDOMDataTransfer::Clone(PRUint32 aEventType,
+ nsIDOMDataTransfer** aNewDataTransfer)
+{
+ nsDOMDataTransfer* newDataTransfer =
+ new nsDOMDataTransfer(aEventType, mEffectAllowed, mIsExternal,
+ mItems, mDragImage, mDragImageX, mDragImageY);
+ NS_ENSURE_TRUE(newDataTransfer, NS_ERROR_OUT_OF_MEMORY);
+
+ *aNewDataTransfer = newDataTransfer;
+ NS_ADDREF(*aNewDataTransfer);
+ return NS_OK;
+}
+
+void
+nsDOMDataTransfer::GetTransferables(nsISupportsArray** aArray)
+{
+ *aArray = nsnull;
+
+ nsCOMPtr<nsISupportsArray> transArray =
+ do_CreateInstance("@mozilla.org/supports-array;1");
+ if (!transArray)
+ return;
+
+ PRBool added = PR_FALSE;
+ PRUint32 count = mItems.Length();
+ for (PRUint32 i = 0; i < count; i++) {
+
+ nsTArray<TransferItem>& item = mItems[i];
+ PRUint32 count = item.Length();
+ if (!count)
+ continue;
+
+ nsCOMPtr<nsITransferable> transferable =
+ do_CreateInstance("@mozilla.org/widget/transferable;1");
+ if (!transferable)
+ return;
+
+ for (PRUint32 f = 0; f < count; f++) {
+ TransferItem& formatitem = item[f];
+ if (!formatitem.mData) // skip empty items
+ continue;
+
+ PRUint32 length;
+ nsCOMPtr<nsISupports> convertedData;
+ if (!ConvertFromVariant(formatitem.mData, getter_AddRefs(convertedData), &length))
+ continue;
+
+ // the underlying drag code uses text/unicode, so use that instead of text/plain
+ const char* format;
+ NS_ConvertUTF16toUTF8 utf8format(formatitem.mFormat);
+ if (utf8format.EqualsLiteral("text/plain"))
+ format = kUnicodeMime;
+ else
+ format = utf8format.get();
+
+ // if a converter is set for a format, set the converter for the
+ // transferable and don't add the item
+ nsCOMPtr<nsIFormatConverter> converter = do_QueryInterface(convertedData);
+ if (converter) {
+ transferable->AddDataFlavor(format);
+ transferable->SetConverter(converter);
+ continue;
+ }
+
+ nsresult rv = transferable->SetTransferData(format, convertedData, length);
+ if (NS_FAILED(rv))
+ return;
+
+ added = PR_TRUE;
+ }
+
+ // only append the transferable if data was successfully added to it
+ if (added)
+ transArray->AppendElement(transferable);
+ }
+
+ NS_ADDREF(*aArray = transArray);
+}
+
+PRBool
+nsDOMDataTransfer::ConvertFromVariant(nsIVariant* aVariant,
+ nsISupports** aSupports,
+ PRUint32* aLength)
+{
+ *aSupports = nsnull;
+ *aLength = 0;
+
+ PRUint16 type;
+ aVariant->GetDataType(&type);
+ if (type == nsIDataType::VTYPE_INTERFACE ||
+ type == nsIDataType::VTYPE_INTERFACE_IS) {
+ if (NS_FAILED(aVariant->GetAsISupports(aSupports)))
+ return PR_FALSE;
+
+ // for flavour data providers, use kFlavorHasDataProvider (which has the
+ // value 0) as the length.
+ nsCOMPtr<nsIFlavorDataProvider> fdp = do_QueryInterface(*aSupports);
+ *aLength = fdp ? sizeof(nsISupports) : nsITransferable::kFlavorHasDataProvider;
+ }
+
+ PRUnichar* chrs;
+ nsresult rv = aVariant->GetAsWString(&chrs);
+ if (NS_FAILED(rv))
+ return PR_FALSE;
+
+ nsCOMPtr<nsISupportsString>
+ strSupports(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
+ if (!strSupports)
+ return PR_FALSE;
+
+ nsAutoString str(chrs);
+ strSupports->SetData(str);
+
+ *aSupports = strSupports;
+ NS_ADDREF(*aSupports);
+
+ // each character is two bytes
+ *aLength = str.Length() << 1;
+
+ return PR_TRUE;
+}
+
+void
+nsDOMDataTransfer::ClearAll()
+{
+ mItems.Clear();
+}
+
+nsresult
+nsDOMDataTransfer::SetDataWithPrincipal(const nsAString& aFormat,
+ nsIVariant* aData,
+ PRUint32 aIndex,
+ nsIPrincipal* aPrincipal)
+{
+ nsAutoString format;
+ GetRealFormat(aFormat, format);
+
+ // check if the item for the format already exists. In that case,
+ // just replace it.
+ TransferItem* formatitem;
+ if (aIndex < mItems.Length()) {
+ nsTArray<TransferItem>& item = mItems[aIndex];
+ PRUint32 count = item.Length();
+ for (PRUint32 i = 0; i < count; i++) {
+ TransferItem& itemformat = item[i];
+ if (itemformat.mFormat.Equals(format)) {
+ // don't allow replacing data that has a stronger principal
+ PRBool subsumes;
+ if (itemformat.mPrincipal && aPrincipal &&
+ (NS_FAILED(aPrincipal->Subsumes(itemformat.mPrincipal, &subsumes)) || !subsumes))
+ return NS_ERROR_DOM_SECURITY_ERR;
+
+ itemformat.mPrincipal = aPrincipal;
+ itemformat.mData = aData;
+ return NS_OK;
+ }
+ }
+
+ // add a new format
+ formatitem = item.AppendElement();
+ }
+ else {
+ NS_ASSERTION(aIndex == mItems.Length(), "Index out of range");
+
+ // add a new index
+ nsTArray<TransferItem>* item = mItems.AppendElement();
+ NS_ENSURE_TRUE(item, NS_ERROR_OUT_OF_MEMORY);
+
+ formatitem = item->AppendElement();
+ }
+
+ NS_ENSURE_TRUE(formatitem, NS_ERROR_OUT_OF_MEMORY);
+
+ formatitem->mFormat = format;
+ formatitem->mPrincipal = aPrincipal;
+ formatitem->mData = aData;
+
+ return NS_OK;
+}
+
+nsIPrincipal*
+nsDOMDataTransfer::GetCurrentPrincipal()
+{
+ nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
+
+ nsCOMPtr<nsIPrincipal> currentPrincipal;
+ ssm->GetSubjectPrincipal(getter_AddRefs(currentPrincipal));
+ if (!currentPrincipal)
+ ssm->GetSystemPrincipal(getter_AddRefs(currentPrincipal));
+
+ return currentPrincipal.get();
+}
+
+void
+nsDOMDataTransfer::GetRealFormat(const nsAString& aInFormat, nsAString& aOutFormat)
+{
+ // treat text/unicode as equivalent to text/plain
+ if (aInFormat.EqualsLiteral("Text") || aInFormat.EqualsLiteral("text/unicode"))
+ aOutFormat.AssignLiteral("text/plain");
+ else if (aInFormat.EqualsLiteral("URL"))
+ aOutFormat.AssignLiteral("text/uri-list");
+ else
+ aOutFormat.Assign(aInFormat);
+}
+
+void
+nsDOMDataTransfer::CacheExternalFormats()
+{
+ // Called during the constructor to cache the formats available from an
+ // external drag. The data associated with each format will be set to null.
+ // This data will instead only be retrieved in FillInExternalDragData when
+ // asked for, as it may be time consuming for the source application to
+ // generate it.
+
+ nsCOMPtr<nsIDragService> dragService =
+ do_GetService("@mozilla.org/widget/dragservice;1");
+ if (!dragService)
+ return;
+
+ nsCOMPtr<nsIDragSession> dragSession;
+ dragService->GetCurrentSession(getter_AddRefs(dragSession));
+ if (!dragSession)
+ return;
+
+ // make sure that the system principal is used for external drags
+ nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
+ nsCOMPtr<nsIPrincipal> sysPrincipal;
+ ssm->GetSystemPrincipal(getter_AddRefs(sysPrincipal));
+
+ // there isn't a way to get a list of the formats that might be available on
+ // all platforms, so just check for the types that can actually be imported
+ // XXXndeakin there are some other formats but those are platform specific.
+ const char* formats[] = { kFileMime, kHTMLMime, kURLMime, kURLDataMime, kUnicodeMime };
+
+ PRUint32 count;
+ dragSession->GetNumDropItems(&count);
+ for (PRUint32 c = 0; c < count; c++) {
+ for (PRUint32 f = 0; f < NS_ARRAY_LENGTH(formats); f++) {
+ // IsDataFlavorSupported doesn't take an index as an argument and just
+ // checks if any of the items support a particular flavor, even though
+ // the GetData method does take an index. Here, we just assume that
+ // every item being dragged has the same set of flavors.
+ PRBool supported;
+ dragSession->IsDataFlavorSupported(formats[f], &supported);
+ // if the format is supported, add an item to the array with null as
+ // the data. When retrieved, GetRealData will read the data.
+ if (supported) {
+ if (formats[f] == kUnicodeMime) {
+ SetDataWithPrincipal(NS_LITERAL_STRING("text/plain"), nsnull, c, sysPrincipal);
+ }
+ else {
+ if (formats[f] == kURLDataMime)
+ SetDataWithPrincipal(NS_LITERAL_STRING("text/uri-list"), nsnull, c, sysPrincipal);
+ SetDataWithPrincipal(NS_ConvertUTF8toUTF16(formats[f]), nsnull, c, sysPrincipal);
+ }
+ }
+ }
+ }
+}
+
+void
+nsDOMDataTransfer::FillInExternalDragData(TransferItem& aItem, PRUint32 aIndex)
+{
+ NS_PRECONDITION(mIsExternal, "Not an external drag");
+
+ if (!aItem.mData) {
+ nsCOMPtr<nsITransferable> trans =
+ do_CreateInstance("@mozilla.org/widget/transferable;1");
+ if (!trans)
+ return;
+
+ NS_ConvertUTF16toUTF8 utf8format(aItem.mFormat);
+ const char* format = utf8format.get();
+ if (strcmp(format, "text/plain") == 0)
+ format = kUnicodeMime;
+ else if (strcmp(format, "text/uri-list") == 0)
+ format = kURLDataMime;
+
+ nsCOMPtr<nsIDragService> dragService =
+ do_GetService("@mozilla.org/widget/dragservice;1");
+ if (!dragService)
+ return;
+
+ nsCOMPtr<nsIDragSession> dragSession;
+ dragService->GetCurrentSession(getter_AddRefs(dragSession));
+ if (!dragSession)
+ return;
+
+ trans->AddDataFlavor(format);
+ dragSession->GetData(trans, aIndex);
+
+ PRUint32 length = 0;
+ nsCOMPtr<nsISupports> data;
+ trans->GetTransferData(format, getter_AddRefs(data), &length);
+ if (!data)
+ return;
+
+ nsCOMPtr<nsIWritableVariant> variant = do_CreateInstance(NS_VARIANT_CONTRACTID);
+ if (!variant)
+ return;
+
+ nsCOMPtr<nsISupportsString> supportsstr = do_QueryInterface(data);
+ if (supportsstr) {
+ nsAutoString str;
+ supportsstr->GetData(str);
+ variant->SetAsAString(str);
+ }
+ else {
+ variant->SetAsISupports(data);
+ }
+ aItem.mData = variant;
+ }
+}
new file mode 100644
--- /dev/null
+++ b/content/events/src/nsDOMDataTransfer.h
@@ -0,0 +1,191 @@
+/* -*- 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 the Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Neil Deakin <enndeakin@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of 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 ***** */
+
+#ifndef nsDOMDataTransfer_h__
+#define nsDOMDataTransfer_h__
+
+#include "nsString.h"
+#include "nsTArray.h"
+#include "nsIVariant.h"
+#include "nsIPrincipal.h"
+#include "nsIDOMDataTransfer.h"
+#include "nsIDragService.h"
+#include "nsIDOMElement.h"
+#include "nsCycleCollectionParticipant.h"
+
+class nsITransferable;
+
+/**
+ * TransferItem is used to hold data for a particular format. Each piece of
+ * data has a principal set from the caller which added it. This allows a
+ * caller that wishes to retrieve the data to only be able to access the data
+ * it is allowed to, yet still allow a chrome caller to retrieve any of the
+ * data.
+ */
+struct TransferItem {
+ nsString mFormat;
+ nsCOMPtr<nsIPrincipal> mPrincipal;
+ nsCOMPtr<nsIVariant> mData;
+};
+
+class nsDOMDataTransfer : public nsIDOMDataTransfer,
+ public nsIDOMNSDataTransfer
+{
+public:
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_NSIDOMDATATRANSFER
+ NS_DECL_NSIDOMNSDATATRANSFER
+
+ NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsDOMDataTransfer, nsIDOMDataTransfer)
+
+ friend class nsDOMDragEvent;
+ friend class nsEventStateManager;
+
+protected:
+
+ // the constructors are protected so only our friends can call them
+
+ // default constructor used for the dragstart/draggesture event and
+ // synthetic events
+ nsDOMDataTransfer();
+
+ // this constructor must only be used to create a dataTransfer for a drag
+ // that was started without using a data transfer, either an external drag,
+ // that is, a drag where the source is another application, or a drag
+ // started by calling the drag service directly.
+ nsDOMDataTransfer(PRUint32 aEventType, PRUint32 aAction);
+
+ // this constructor is used only by the Clone method to copy the fields as
+ // needed to a new data transfer.
+ nsDOMDataTransfer(PRUint32 aEventType,
+ const PRUint32 aEffectAllowed,
+ PRBool aIsExternal,
+ nsTArray<nsTArray<TransferItem> >& aItems,
+ nsIDOMElement* aDragImage,
+ PRUint32 aDragImageX,
+ PRUint32 aDragImageY);
+
+ static const char sEffects[8][9];
+
+public:
+
+ void GetDragTarget(nsIDOMElement** aDragTarget)
+ {
+ *aDragTarget = mDragTarget;
+ NS_IF_ADDREF(*aDragTarget);
+ }
+
+ // a readonly dataTransfer cannot have new data added or existing data removed.
+ // Only the dropEffect and effectAllowed may be modified.
+ void SetReadOnly() { mReadOnly = PR_TRUE; }
+
+ // converts the data into an array of nsITransferable objects to be used for
+ // drag and drop or clipboard operations.
+ void GetTransferables(nsISupportsArray** transferables);
+
+ // converts the data in the variant to an nsISupportString if possible or
+ // an nsISupports or null otherwise.
+ PRBool ConvertFromVariant(nsIVariant* aVariant,
+ nsISupports** aSupports,
+ PRUint32* aLength);
+
+ // clears all of the data
+ void ClearAll();
+
+ // Similar to SetData except also specifies the principal to store.
+ // aData may be null when called from CacheExternalFormats.
+ nsresult SetDataWithPrincipal(const nsAString& aFormat,
+ nsIVariant* aData,
+ PRUint32 aIndex,
+ nsIPrincipal* aPrincipal);
+
+protected:
+
+ // returns a weak reference to the drag image
+ nsIDOMElement* GetDragImage(PRInt32* aX, PRInt32* aY)
+ {
+ *aX = mDragImageX;
+ *aY = mDragImageY;
+ return mDragImage;
+ }
+
+ // returns a weak reference to the current principal
+ nsIPrincipal* GetCurrentPrincipal();
+
+ // converts some formats used for compatibility in aInFormat into aOutFormat.
+ // Text and text/unicode become text/plain, and URL becomes text/uri-list
+ void GetRealFormat(const nsAString& aInFormat, nsAString& aOutFormat);
+
+ // caches the formats that exist in the drag service that were added by an
+ // external drag
+ void CacheExternalFormats();
+
+ // fills in the data field of aItem with the data from the drag service for
+ // a given index.
+ void FillInExternalDragData(TransferItem& aItem, PRUint32 aIndex);
+
+ // the event type this data transfer is for. This will correspond to an
+ // event->message value.
+ PRUint32 mEventType;
+
+ // the drop effect and effect allowed
+ PRUint32 mDropEffect;
+ PRUint32 mEffectAllowed;
+
+ // readonly data transfers may not be modified except the drop effect and
+ // effect allowed.
+ PRPackedBool mReadOnly;
+
+ // true for drags started without a data transfer, for example, those from
+ // another application.
+ PRPackedBool mIsExternal;
+
+ // array of items, each containing an array of format->data pairs
+ nsTArray<nsTArray<TransferItem> > mItems;
+
+ // the target of the drag. The drag and dragend events will fire at this.
+ nsCOMPtr<nsIDOMElement> mDragTarget;
+
+ // the custom drag image and coordinates within the image. If mDragImage is
+ // null, the default image is created from the drag target.
+ nsCOMPtr<nsIDOMElement> mDragImage;
+ PRUint32 mDragImageX;
+ PRUint32 mDragImageY;
+};
+
+#endif // nsDOMDataTransfer_h__
+
new file mode 100644
--- /dev/null
+++ b/content/events/src/nsDOMDragEvent.cpp
@@ -0,0 +1,246 @@
+/* -*- 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 the Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Neil Deakin <enndeakin@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of 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 ***** */
+
+#include "nsDOMDragEvent.h"
+#include "nsIServiceManager.h"
+#include "nsGUIEvent.h"
+#include "nsContentUtils.h"
+#include "nsIEventStateManager.h"
+#include "nsDOMDataTransfer.h"
+#include "nsIDragService.h"
+
+nsDOMDragEvent::nsDOMDragEvent(nsPresContext* aPresContext,
+ nsInputEvent* aEvent)
+ : nsDOMMouseEvent(aPresContext, aEvent ? aEvent :
+ new nsDragEvent(PR_FALSE, 0, nsnull))
+{
+ if (aEvent) {
+ mEventIsInternal = PR_FALSE;
+ }
+ else {
+ mEventIsInternal = PR_TRUE;
+ mEvent->time = PR_Now();
+ mEvent->refPoint.x = mEvent->refPoint.y = 0;
+ }
+}
+
+nsDOMDragEvent::~nsDOMDragEvent()
+{
+ if (mEventIsInternal) {
+ if (mEvent->eventStructType == NS_DRAG_EVENT)
+ delete static_cast<nsDragEvent*>(mEvent);
+ mEvent = nsnull;
+ }
+}
+
+NS_IMPL_ADDREF_INHERITED(nsDOMDragEvent, nsDOMMouseEvent)
+NS_IMPL_RELEASE_INHERITED(nsDOMDragEvent, nsDOMMouseEvent)
+
+NS_INTERFACE_MAP_BEGIN(nsDOMDragEvent)
+ NS_INTERFACE_MAP_ENTRY(nsIDOMDragEvent)
+ NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(DragEvent)
+NS_INTERFACE_MAP_END_INHERITING(nsDOMMouseEvent)
+
+NS_IMETHODIMP
+nsDOMDragEvent::InitDragEvent(const nsAString & aType,
+ PRBool aCanBubble,
+ PRBool aCancelable,
+ nsIDOMAbstractView* aView,
+ PRInt32 aDetail,
+ nsIDOMDataTransfer* aDataTransfer)
+{
+ nsresult rv = nsDOMUIEvent::InitUIEvent(aType, aCanBubble, aCancelable, aView, aDetail);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (mEventIsInternal && mEvent) {
+ nsDragEvent* dragEvent = static_cast<nsDragEvent*>(mEvent);
+ dragEvent->dataTransfer = aDataTransfer;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMDragEvent::InitDragEventNS(const nsAString & aNamespaceURIArg,
+ const nsAString & aType,
+ PRBool aCanBubble,
+ PRBool aCancelable,
+ nsIDOMAbstractView* aView,
+ PRInt32 aDetail,
+ nsIDOMDataTransfer* aDataTransfer)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsDOMDragEvent::GetDataTransfer(nsIDOMDataTransfer** aDataTransfer)
+{
+ *aDataTransfer = nsnull;
+
+ if (!mEvent || mEvent->eventStructType != NS_DRAG_EVENT) {
+ NS_WARNING("Tried to get dataTransfer from non-drag event!");
+ return NS_OK;
+ }
+
+ // the dataTransfer field of the event caches the DataTransfer associated
+ // with the drag. It is initialized when an attempt is made to retrieve it
+ // rather that when the event is created to avoid duplicating the data when
+ // no listener ever uses it.
+ nsDragEvent* dragEvent = static_cast<nsDragEvent*>(mEvent);
+ if (dragEvent->dataTransfer) {
+ CallQueryInterface(dragEvent->dataTransfer, aDataTransfer);
+ return NS_OK;
+ }
+
+ // for synthetic events, just use the supplied data transfer object
+ if (mEventIsInternal) {
+ NS_IF_ADDREF(*aDataTransfer = dragEvent->dataTransfer);
+ return NS_OK;
+ }
+
+ // For draggesture and dragstart events, the data transfer object is
+ // created before the event fires, so it should already be set. For other
+ // drag events, get the object from the drag session.
+ NS_ASSERTION(mEvent->message != NS_DRAGDROP_GESTURE &&
+ mEvent->message != NS_DRAGDROP_START,
+ "draggesture event created without a dataTransfer");
+
+ nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
+ NS_ENSURE_TRUE(dragSession, NS_OK); // no drag in progress
+
+ nsCOMPtr<nsIDOMDataTransfer> initialDataTransfer;
+ dragSession->GetDataTransfer(getter_AddRefs(initialDataTransfer));
+ if (!initialDataTransfer) {
+ // A dataTransfer won't exist when a drag was started by some other
+ // means, for instance calling the drag service directly, or a drag
+ // from another application. In either case, a new dataTransfer should
+ // be created that reflects the data. Pass true to the constructor for
+ // the aIsExternal argument, so that only system access is allowed.
+ PRUint32 action = 0;
+ dragSession->GetDragAction(&action);
+ initialDataTransfer =
+ new nsDOMDataTransfer(mEvent->message, action);
+ NS_ENSURE_TRUE(initialDataTransfer, NS_ERROR_OUT_OF_MEMORY);
+
+ // now set it in the drag session so we don't need to create it again
+ dragSession->SetDataTransfer(initialDataTransfer);
+ }
+
+ // each event should use a clone of the original dataTransfer.
+ nsCOMPtr<nsIDOMNSDataTransfer> initialDataTransferNS =
+ do_QueryInterface(initialDataTransfer);
+ NS_ENSURE_TRUE(initialDataTransferNS, NS_ERROR_FAILURE);
+ initialDataTransferNS->Clone(mEvent->message,
+ getter_AddRefs(dragEvent->dataTransfer));
+ NS_ENSURE_TRUE(dragEvent->dataTransfer, NS_ERROR_OUT_OF_MEMORY);
+
+ // for the dragenter and dragover events, initialize the drop effect
+ // from the drop action, which platform specific widget code sets before
+ // the event is fired based on the keyboard state.
+ if (mEvent->message == NS_DRAGDROP_ENTER ||
+ mEvent->message == NS_DRAGDROP_OVER) {
+ nsCOMPtr<nsIDOMNSDataTransfer> newDataTransfer =
+ do_QueryInterface(dragEvent->dataTransfer);
+ NS_ENSURE_TRUE(newDataTransfer, NS_ERROR_FAILURE);
+
+ PRUint32 action, effectAllowed;
+ dragSession->GetDragAction(&action);
+ newDataTransfer->GetEffectAllowedInt(&effectAllowed);
+ newDataTransfer->SetDropEffectInt(FilterDropEffect(action, effectAllowed));
+ }
+ else if (mEvent->message == NS_DRAGDROP_DROP ||
+ mEvent->message == NS_DRAGDROP_DRAGDROP ||
+ mEvent->message == NS_DRAGDROP_END) {
+ // For the drop and dragend events, set the drop effect based on the
+ // last value that the dropEffect had. This will have been set in
+ // nsEventStateManager::PostHandleEvent for the last dragenter or
+ // dragover event.
+ nsCOMPtr<nsIDOMNSDataTransfer> newDataTransfer =
+ do_QueryInterface(dragEvent->dataTransfer);
+ NS_ENSURE_TRUE(newDataTransfer, NS_ERROR_FAILURE);
+
+ PRUint32 dropEffect;
+ initialDataTransferNS->GetDropEffectInt(&dropEffect);
+ newDataTransfer->SetDropEffectInt(dropEffect);
+ }
+
+ NS_IF_ADDREF(*aDataTransfer = dragEvent->dataTransfer);
+ return NS_OK;
+}
+
+// static
+PRUint32
+nsDOMDragEvent::FilterDropEffect(PRUint32 aAction, PRUint32 aEffectAllowed)
+{
+ // It is possible for the drag action to include more than one action, but
+ // the widget code which sets the action from the keyboard state should only
+ // be including one. If multiple actions were set, we just consider them in
+ // the following order:
+ // copy, link, move
+ if (aAction & nsIDragService::DRAGDROP_ACTION_COPY)
+ aAction = nsIDragService::DRAGDROP_ACTION_COPY;
+ else if (aAction & nsIDragService::DRAGDROP_ACTION_LINK)
+ aAction = nsIDragService::DRAGDROP_ACTION_LINK;
+ else if (aAction & nsIDragService::DRAGDROP_ACTION_MOVE)
+ aAction = nsIDragService::DRAGDROP_ACTION_MOVE;
+
+ // Filter the action based on the effectAllowed. If the effectAllowed
+ // doesn't include the action, then that action cannot be done, so adjust
+ // the action to something that is allowed. For a copy, adjust to move or
+ // link. For a move, adjust to copy or link. For a link, adjust to move or
+ // link. Otherwise, use none.
+ if (aAction & aEffectAllowed ||
+ aEffectAllowed == nsIDragService::DRAGDROP_ACTION_UNINITIALIZED)
+ return aAction;
+ if (aEffectAllowed & nsIDragService::DRAGDROP_ACTION_MOVE)
+ return nsIDragService::DRAGDROP_ACTION_MOVE;
+ if (aEffectAllowed & nsIDragService::DRAGDROP_ACTION_COPY)
+ return nsIDragService::DRAGDROP_ACTION_COPY;
+ if (aEffectAllowed & nsIDragService::DRAGDROP_ACTION_LINK)
+ return nsIDragService::DRAGDROP_ACTION_LINK;
+ return nsIDragService::DRAGDROP_ACTION_NONE;
+}
+
+nsresult NS_NewDOMDragEvent(nsIDOMEvent** aInstancePtrResult,
+ nsPresContext* aPresContext,
+ nsDragEvent *aEvent)
+{
+ nsDOMDragEvent* event = new nsDOMDragEvent(aPresContext, aEvent);
+ NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
+
+ return CallQueryInterface(event, aInstancePtrResult);
+}
new file mode 100644
--- /dev/null
+++ b/content/events/src/nsDOMDragEvent.h
@@ -0,0 +1,69 @@
+/* -*- 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 the Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Neil Deakin <enndeakin@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of 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 ***** */
+
+#ifndef nsDOMDragEvent_h__
+#define nsDOMDragEvent_h__
+
+#include "nsIDOMDragEvent.h"
+#include "nsDOMMouseEvent.h"
+#include "nsIDOMDataTransfer.h"
+
+class nsIContent;
+class nsEvent;
+
+class nsDOMDragEvent : public nsIDOMDragEvent,
+ public nsDOMMouseEvent
+{
+public:
+ nsDOMDragEvent(nsPresContext* aPresContext, nsInputEvent* aEvent);
+ virtual ~nsDOMDragEvent();
+
+ NS_DECL_ISUPPORTS_INHERITED
+
+ NS_DECL_NSIDOMDRAGEVENT
+
+ NS_FORWARD_TO_NSDOMMOUSEEVENT
+
+ // filters the action to fit within the effects allowed and returns it.
+ static PRUint32 FilterDropEffect(PRUint32 aAction, PRUint32 aEffectAllowed);
+};
+
+nsresult NS_NewDOMDragEvent(nsIDOMEvent** aInstancePtrResult,
+ nsPresContext* aPresContext,
+ nsDragEvent* aEvent);
+
+#endif // nsDOMDragEvent_h__
--- a/content/events/src/nsDOMEvent.cpp
+++ b/content/events/src/nsDOMEvent.cpp
@@ -59,17 +59,17 @@
static const char* const sEventNames[] = {
"mousedown", "mouseup", "click", "dblclick", "mouseover",
"mouseout", "mousemove", "contextmenu", "keydown", "keyup", "keypress",
"focus", "blur", "load", "beforeunload", "unload", "abort", "error",
"submit", "reset", "change", "select", "input", "paint" ,"text",
"compositionstart", "compositionend", "popupshowing", "popupshown",
"popuphiding", "popuphidden", "close", "command", "broadcast", "commandupdate",
"dragenter", "dragover", "dragexit", "dragdrop", "draggesture",
- "drag", "dragend", "resize",
+ "drag", "dragend", "dragstart", "dragleave", "drop", "resize",
"scroll", "overflow", "underflow", "overflowchanged",
"DOMSubtreeModified", "DOMNodeInserted", "DOMNodeRemoved",
"DOMNodeRemovedFromDocument", "DOMNodeInsertedIntoDocument",
"DOMAttrModified", "DOMCharacterDataModified",
"DOMActivate", "DOMFocusIn", "DOMFocusOut",
"pageshow", "pagehide", "DOMMouseScroll", "offline", "online",
"copy", "cut", "paste"
#ifdef MOZ_SVG
@@ -167,16 +167,19 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
tmp->mEvent->target = nsnull;
tmp->mEvent->currentTarget = nsnull;
tmp->mEvent->originalTarget = nsnull;
switch (tmp->mEvent->eventStructType) {
case NS_MOUSE_EVENT:
case NS_MOUSE_SCROLL_EVENT:
static_cast<nsMouseEvent_base*>(tmp->mEvent)->relatedTarget = nsnull;
break;
+ case NS_DRAG_EVENT:
+ static_cast<nsDragEvent*>(tmp->mEvent)->dataTransfer = nsnull;
+ break;
case NS_XUL_COMMAND_EVENT:
static_cast<nsXULCommandEvent*>(tmp->mEvent)->sourceEvent = nsnull;
break;
case NS_MUTATION_EVENT:
static_cast<nsMutationEvent*>(tmp->mEvent)->mRelatedNode = nsnull;
break;
default:
break;
@@ -193,16 +196,20 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
cb.NoteXPCOMChild(tmp->mEvent->currentTarget);
cb.NoteXPCOMChild(tmp->mEvent->originalTarget);
switch (tmp->mEvent->eventStructType) {
case NS_MOUSE_EVENT:
case NS_MOUSE_SCROLL_EVENT:
cb.NoteXPCOMChild(
static_cast<nsMouseEvent_base*>(tmp->mEvent)->relatedTarget);
break;
+ case NS_DRAG_EVENT:
+ cb.NoteXPCOMChild(
+ static_cast<nsDragEvent*>(tmp->mEvent)->dataTransfer);
+ break;
case NS_XUL_COMMAND_EVENT:
cb.NoteXPCOMChild(
static_cast<nsXULCommandEvent*>(tmp->mEvent)->sourceEvent);
break;
case NS_MUTATION_EVENT:
cb.NoteXPCOMChild(
static_cast<nsMutationEvent*>(tmp->mEvent)->mRelatedNode);
break;
@@ -463,16 +470,37 @@ nsDOMEvent::SetEventType(const nsAString
mEvent->message = NS_MOUSE_EXIT_SYNTH;
else if (atom == nsGkAtoms::onmousemove)
mEvent->message = NS_MOUSE_MOVE;
else if (atom == nsGkAtoms::oncontextmenu)
mEvent->message = NS_CONTEXTMENU;
} else if (mEvent->eventStructType == NS_MOUSE_SCROLL_EVENT) {
if (atom == nsGkAtoms::onDOMMouseScroll)
mEvent->message = NS_MOUSE_SCROLL;
+ } else if (mEvent->eventStructType == NS_DRAG_EVENT) {
+ if (atom == nsGkAtoms::ondragstart)
+ mEvent->message = NS_DRAGDROP_START;
+ else if (atom == nsGkAtoms::ondraggesture)
+ mEvent->message = NS_DRAGDROP_GESTURE;
+ else if (atom == nsGkAtoms::ondragenter)
+ mEvent->message = NS_DRAGDROP_ENTER;
+ else if (atom == nsGkAtoms::ondragover)
+ mEvent->message = NS_DRAGDROP_OVER_SYNTH;
+ else if (atom == nsGkAtoms::ondragleave)
+ mEvent->message = NS_DRAGDROP_LEAVE_SYNTH;
+ else if (atom == nsGkAtoms::ondragexit)
+ mEvent->message = NS_DRAGDROP_EXIT;
+ else if (atom == nsGkAtoms::ondrag)
+ mEvent->message = NS_DRAGDROP_DRAG;
+ else if (atom == nsGkAtoms::ondrop)
+ mEvent->message = NS_DRAGDROP_DROP;
+ else if (atom == nsGkAtoms::ondragdrop)
+ mEvent->message = NS_DRAGDROP_DRAGDROP;
+ else if (atom == nsGkAtoms::ondragend)
+ mEvent->message = NS_DRAGDROP_END;
} else if (mEvent->eventStructType == NS_KEY_EVENT) {
if (atom == nsGkAtoms::onkeydown)
mEvent->message = NS_KEY_DOWN;
else if (atom == nsGkAtoms::onkeyup)
mEvent->message = NS_KEY_UP;
else if (atom == nsGkAtoms::onkeypress)
mEvent->message = NS_KEY_PRESS;
} else if (mEvent->eventStructType == NS_COMPOSITION_EVENT) {
@@ -755,16 +783,31 @@ NS_METHOD nsDOMEvent::DuplicatePrivateDa
mouseEvent->clickCount = oldMouseEvent->clickCount;
mouseEvent->acceptActivation = oldMouseEvent->acceptActivation;
mouseEvent->context = oldMouseEvent->context;
mouseEvent->relatedTarget = oldMouseEvent->relatedTarget;
mouseEvent->button = oldMouseEvent->button;
newEvent = mouseEvent;
break;
}
+ case NS_DRAG_EVENT:
+ {
+ nsDragEvent* oldDragEvent = static_cast<nsDragEvent*>(mEvent);
+ nsDragEvent* dragEvent =
+ new nsDragEvent(PR_FALSE, msg, nsnull);
+ NS_ENSURE_TRUE(dragEvent, NS_ERROR_OUT_OF_MEMORY);
+ isInputEvent = PR_TRUE;
+ dragEvent->dataTransfer = oldDragEvent->dataTransfer;
+ dragEvent->clickCount = oldDragEvent->clickCount;
+ dragEvent->acceptActivation = oldDragEvent->acceptActivation;
+ dragEvent->relatedTarget = oldDragEvent->relatedTarget;
+ dragEvent->button = oldDragEvent->button;
+ newEvent = dragEvent;
+ break;
+ }
case NS_MENU_EVENT:
{
newEvent = new nsMenuEvent(PR_FALSE, msg, nsnull);
NS_ENSURE_TRUE(newEvent, NS_ERROR_OUT_OF_MEMORY);
static_cast<nsMenuEvent*>(newEvent)->mCommand =
static_cast<nsMenuEvent*>(mEvent)->mCommand;
break;
}
@@ -1293,24 +1336,30 @@ const char* nsDOMEvent::GetEventName(PRU
case NS_XUL_COMMAND_UPDATE:
return sEventNames[eDOMEvents_commandupdate];
case NS_DRAGDROP_ENTER:
return sEventNames[eDOMEvents_dragenter];
case NS_DRAGDROP_OVER_SYNTH:
return sEventNames[eDOMEvents_dragover];
case NS_DRAGDROP_EXIT_SYNTH:
return sEventNames[eDOMEvents_dragexit];
- case NS_DRAGDROP_DROP:
+ case NS_DRAGDROP_DRAGDROP:
return sEventNames[eDOMEvents_dragdrop];
case NS_DRAGDROP_GESTURE:
return sEventNames[eDOMEvents_draggesture];
case NS_DRAGDROP_DRAG:
return sEventNames[eDOMEvents_drag];
case NS_DRAGDROP_END:
return sEventNames[eDOMEvents_dragend];
+ case NS_DRAGDROP_START:
+ return sEventNames[eDOMEvents_dragstart];
+ case NS_DRAGDROP_LEAVE_SYNTH:
+ return sEventNames[eDOMEvents_dragleave];
+ case NS_DRAGDROP_DROP:
+ return sEventNames[eDOMEvents_drop];
case NS_SCROLLPORT_OVERFLOW:
return sEventNames[eDOMEvents_overflow];
case NS_SCROLLPORT_UNDERFLOW:
return sEventNames[eDOMEvents_underflow];
case NS_SCROLLPORT_OVERFLOWCHANGED:
return sEventNames[eDOMEvents_overflowchanged];
case NS_MUTATION_SUBTREEMODIFIED:
return sEventNames[eDOMEvents_subtreemodified];
--- a/content/events/src/nsDOMEvent.h
+++ b/content/events/src/nsDOMEvent.h
@@ -98,16 +98,19 @@ public:
eDOMEvents_commandupdate,
eDOMEvents_dragenter,
eDOMEvents_dragover,
eDOMEvents_dragexit,
eDOMEvents_dragdrop,
eDOMEvents_draggesture,
eDOMEvents_drag,
eDOMEvents_dragend,
+ eDOMEvents_dragstart,
+ eDOMEvents_dragleave,
+ eDOMEvents_drop,
eDOMEvents_resize,
eDOMEvents_scroll,
eDOMEvents_overflow,
eDOMEvents_underflow,
eDOMEvents_overflowchanged,
eDOMEvents_subtreemodified,
eDOMEvents_nodeinserted,
eDOMEvents_noderemoved,
--- a/content/events/src/nsDOMMouseEvent.h
+++ b/content/events/src/nsDOMMouseEvent.h
@@ -60,13 +60,13 @@ public:
// Forward to base class
NS_FORWARD_TO_NSDOMUIEVENT
// Specific implementation for a mouse event.
NS_IMETHOD GetWhich(PRUint32 *aWhich);
};
-#define NS_FORWARD_TO_NSDOMMOUSEEVENT \
+#define NS_FORWARD_TO_NSDOMMOUSEEVENT \
NS_FORWARD_NSIDOMMOUSEEVENT(nsDOMMouseEvent::) \
NS_FORWARD_TO_NSDOMUIEVENT
#endif // nsDOMMouseEvent_h__
--- a/content/events/src/nsDOMUIEvent.cpp
+++ b/content/events/src/nsDOMUIEvent.cpp
@@ -117,21 +117,21 @@ NS_IMPL_RELEASE_INHERITED(nsDOMUIEvent,
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMUIEvent)
NS_INTERFACE_MAP_ENTRY(nsIDOMUIEvent)
NS_INTERFACE_MAP_ENTRY(nsIDOMNSUIEvent)
NS_INTERFACE_MAP_ENTRY(nsIPrivateCompositionEvent)
NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(UIEvent)
NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent)
nsPoint nsDOMUIEvent::GetScreenPoint() {
- if (!mEvent ||
- (mEvent->eventStructType != NS_MOUSE_EVENT &&
- mEvent->eventStructType != NS_POPUP_EVENT &&
- mEvent->eventStructType != NS_MOUSE_SCROLL_EVENT &&
- !NS_IS_DRAG_EVENT(mEvent))) {
+ if (!mEvent ||
+ (mEvent->eventStructType != NS_MOUSE_EVENT &&
+ mEvent->eventStructType != NS_POPUP_EVENT &&
+ mEvent->eventStructType != NS_MOUSE_SCROLL_EVENT &&
+ mEvent->eventStructType != NS_DRAG_EVENT)) {
return nsPoint(0, 0);
}
if (!((nsGUIEvent*)mEvent)->widget ) {
return mEvent->refPoint;
}
nsRect bounds(mEvent->refPoint, nsSize(1, 1));
@@ -142,17 +142,17 @@ nsPoint nsDOMUIEvent::GetScreenPoint() {
nsPresContext::AppUnitsToIntCSSPixels(offset.y * factor));
}
nsPoint nsDOMUIEvent::GetClientPoint() {
if (!mEvent ||
(mEvent->eventStructType != NS_MOUSE_EVENT &&
mEvent->eventStructType != NS_POPUP_EVENT &&
mEvent->eventStructType != NS_MOUSE_SCROLL_EVENT &&
- !NS_IS_DRAG_EVENT(mEvent)) ||
+ mEvent->eventStructType != NS_DRAG_EVENT) ||
!mPresContext ||
!((nsGUIEvent*)mEvent)->widget) {
return mClientPoint;
}
nsPoint pt(0, 0);
nsIPresShell* shell = mPresContext->GetPresShell();
if (!shell) {
--- a/content/events/src/nsEventDispatcher.cpp
+++ b/content/events/src/nsEventDispatcher.cpp
@@ -571,16 +571,19 @@ nsEventDispatcher::CreateEvent(nsPresCon
static_cast<nsKeyEvent*>(aEvent));
case NS_MOUSE_EVENT:
case NS_POPUP_EVENT:
return NS_NewDOMMouseEvent(aDOMEvent, aPresContext,
static_cast<nsInputEvent*>(aEvent));
case NS_MOUSE_SCROLL_EVENT:
return NS_NewDOMMouseScrollEvent(aDOMEvent, aPresContext,
static_cast<nsInputEvent*>(aEvent));
+ case NS_DRAG_EVENT:
+ return NS_NewDOMDragEvent(aDOMEvent, aPresContext,
+ static_cast<nsDragEvent*>(aEvent));
case NS_POPUPBLOCKED_EVENT:
return NS_NewDOMPopupBlockedEvent(aDOMEvent, aPresContext,
static_cast<nsPopupBlockedEvent*>
(aEvent));
case NS_TEXT_EVENT:
return NS_NewDOMTextEvent(aDOMEvent, aPresContext,
static_cast<nsTextEvent*>(aEvent));
case NS_BEFORE_PAGE_UNLOAD_EVENT:
@@ -617,16 +620,19 @@ nsEventDispatcher::CreateEvent(nsPresCon
// And if we didn't get an event, check the type argument.
if (aEventType.LowerCaseEqualsLiteral("mouseevent") ||
aEventType.LowerCaseEqualsLiteral("mouseevents") ||
aEventType.LowerCaseEqualsLiteral("popupevents"))
return NS_NewDOMMouseEvent(aDOMEvent, aPresContext, nsnull);
if (aEventType.LowerCaseEqualsLiteral("mousescrollevents"))
return NS_NewDOMMouseScrollEvent(aDOMEvent, aPresContext, nsnull);
+ if (aEventType.LowerCaseEqualsLiteral("dragevent") ||
+ aEventType.LowerCaseEqualsLiteral("dragevents"))
+ return NS_NewDOMDragEvent(aDOMEvent, aPresContext, nsnull);
if (aEventType.LowerCaseEqualsLiteral("keyboardevent") ||
aEventType.LowerCaseEqualsLiteral("keyevents"))
return NS_NewDOMKeyboardEvent(aDOMEvent, aPresContext, nsnull);
if (aEventType.LowerCaseEqualsLiteral("mutationevent") ||
aEventType.LowerCaseEqualsLiteral("mutationevents"))
return NS_NewDOMMutationEvent(aDOMEvent, aPresContext, nsnull);
if (aEventType.LowerCaseEqualsLiteral("textevent") ||
aEventType.LowerCaseEqualsLiteral("textevents"))
--- a/content/events/src/nsEventListenerManager.cpp
+++ b/content/events/src/nsEventListenerManager.cpp
@@ -257,20 +257,23 @@ static const EventDispatchData sLoadEven
{ NS_LOAD_ERROR, HANDLER(&nsIDOMLoadListener::Error) },
{ NS_BEFORE_PAGE_UNLOAD, HANDLER(&nsIDOMLoadListener::BeforeUnload) }
};
static const EventDispatchData sDragEvents[] = {
{ NS_DRAGDROP_ENTER, HANDLER(&nsIDOMDragListener::DragEnter) },
{ NS_DRAGDROP_OVER_SYNTH, HANDLER(&nsIDOMDragListener::DragOver) },
{ NS_DRAGDROP_EXIT_SYNTH, HANDLER(&nsIDOMDragListener::DragExit) },
- { NS_DRAGDROP_DROP, HANDLER(&nsIDOMDragListener::DragDrop) },
+ { NS_DRAGDROP_DRAGDROP, HANDLER(&nsIDOMDragListener::DragDrop) },
{ NS_DRAGDROP_GESTURE, HANDLER(&nsIDOMDragListener::DragGesture) },
{ NS_DRAGDROP_DRAG, HANDLER(&nsIDOMDragListener::Drag) },
- { NS_DRAGDROP_END, HANDLER(&nsIDOMDragListener::DragEnd) }
+ { NS_DRAGDROP_END, HANDLER(&nsIDOMDragListener::DragEnd) },
+ { NS_DRAGDROP_START, HANDLER(&nsIDOMDragListener::DragStart) },
+ { NS_DRAGDROP_LEAVE_SYNTH, HANDLER(&nsIDOMDragListener::DragLeave) },
+ { NS_DRAGDROP_DROP, HANDLER(&nsIDOMDragListener::Drop) }
};
static const EventDispatchData sXULEvents[] = {
{ NS_XUL_POPUP_SHOWING, HANDLER(&nsIDOMXULListener::PopupShowing) },
{ NS_XUL_POPUP_SHOWN, HANDLER(&nsIDOMXULListener::PopupShown) },
{ NS_XUL_POPUP_HIDING, HANDLER(&nsIDOMXULListener::PopupHiding) },
{ NS_XUL_POPUP_HIDDEN, HANDLER(&nsIDOMXULListener::PopupHidden) },
{ NS_XUL_CLOSE, HANDLER(&nsIDOMXULListener::Close) },
--- a/content/events/src/nsEventStateManager.cpp
+++ b/content/events/src/nsEventStateManager.cpp
@@ -55,16 +55,17 @@
#include "nsIWidget.h"
#include "nsPresContext.h"
#include "nsIPresShell.h"
#include "nsDOMEvent.h"
#include "nsGkAtoms.h"
#include "nsIEditorDocShell.h"
#include "nsIFormControl.h"
#include "nsIComboboxControlFrame.h"
+#include "nsIDOMNSHTMLElement.h"
#include "nsIDOMHTMLAnchorElement.h"
#include "nsIDOMHTMLInputElement.h"
#include "nsIDOMNSHTMLInputElement.h"
#include "nsIDOMHTMLSelectElement.h"
#include "nsIDOMHTMLTextAreaElement.h"
#include "nsIDOMHTMLAreaElement.h"
#include "nsIDOMHTMLButtonElement.h"
#include "nsIDOMHTMLObjectElement.h"
@@ -108,20 +109,21 @@
#include "nsIDOMKeyEvent.h"
#include "nsIObserverService.h"
#include "nsIDocShell.h"
#include "nsIMarkupDocumentViewer.h"
#include "nsIScrollableViewProvider.h"
#include "nsIDOMDocumentRange.h"
#include "nsIDOMDocumentEvent.h"
#include "nsIDOMMouseEvent.h"
+#include "nsIDOMDragEvent.h"
#include "nsIDOMEventTarget.h"
#include "nsIDOMDocumentView.h"
-#include "nsIDOMAbstractView.h"
#include "nsIDOMNSUIEvent.h"
+#include "nsDOMDragEvent.h"
#include "nsIDOMRange.h"
#include "nsCaret.h"
#include "nsILookAndFeel.h"
#include "nsWidgetsCID.h"
#include "nsIFrameFrame.h"
#include "nsIFrameTraversal.h"
@@ -135,16 +137,24 @@
#include "nsIProperties.h"
#include "nsISupportsPrimitives.h"
#include "nsEventDispatcher.h"
#include "nsPresShellIterator.h"
#include "nsServiceManagerUtils.h"
#include "nsITimer.h"
+#include "nsIDragService.h"
+#include "nsIDragSession.h"
+#include "nsDOMDataTransfer.h"
+#include "nsContentAreaDragDrop.h"
+#ifdef MOZ_XUL
+#include "nsITreeBoxObject.h"
+#endif
+
#ifdef XP_MACOSX
#include <Events.h>
#endif
#if defined(DEBUG_rods) || defined(DEBUG_bryner)
//#define DEBUG_DOCSHELL_FOCUS
#endif
@@ -848,17 +858,20 @@ nsEventStateManager::PreHandleEvent(nsPr
break;
#ifdef CLICK_HOLD_CONTEXT_MENUS
case NS_DRAGDROP_GESTURE:
// an external drag gesture event came in, not generated internally
// by Gecko. Make sure we get rid of the click-hold timer.
KillClickHoldTimer();
break;
#endif
+ case NS_DRAGDROP_DROP:
case NS_DRAGDROP_OVER:
+ // NS_DRAGDROP_DROP is fired before NS_DRAGDROP_DRAGDROP so send
+ // the enter/exit events before NS_DRAGDROP_DROP.
GenerateDragDropEnterExit(aPresContext, (nsGUIEvent*)aEvent);
break;
case NS_GOTFOCUS:
{
#ifdef DEBUG_smaug
printf("nsEventStateManager::PreHandleEvent, NS_GOTFOCUS \n");
#endif
// This is called when a child widget has received focus.
@@ -1966,62 +1979,321 @@ nsEventStateManager::GenerateDragGesture
if (PR_ABS(pt.x - mGestureDownPoint.x) > pixelThresholdX ||
PR_ABS(pt.y - mGestureDownPoint.y) > pixelThresholdY) {
#ifdef CLICK_HOLD_CONTEXT_MENUS
// stop the click-hold before we fire off the drag gesture, in case
// it takes a long time
KillClickHoldTimer();
#endif
- nsCOMPtr<nsIContent> targetContent = mGestureDownContent;
+ nsRefPtr<nsDOMDataTransfer> dataTransfer = new nsDOMDataTransfer();
+ if (!dataTransfer)
+ return;
+
+ PRBool isSelection = PR_FALSE;
+ nsCOMPtr<nsIContent> eventContent, targetContent;
+ mCurrentTarget->GetContentForEvent(aPresContext, aEvent,
+ getter_AddRefs(eventContent));
+ if (eventContent)
+ DetermineDragTarget(aPresContext, eventContent, dataTransfer,
+ &isSelection, getter_AddRefs(targetContent));
+
// Stop tracking the drag gesture now. This should stop us from
// reentering GenerateDragGesture inside DOM event processing.
StopTrackingDragGesture();
+ if (!targetContent)
+ return;
+
nsCOMPtr<nsIWidget> widget = mCurrentTarget->GetWindow();
// get the widget from the target frame
- nsMouseEvent gestureEvent(NS_IS_TRUSTED_EVENT(aEvent), NS_DRAGDROP_GESTURE,
- widget, nsMouseEvent::eReal);
+ nsDragEvent startEvent(NS_IS_TRUSTED_EVENT(aEvent), NS_DRAGDROP_START, widget);
+ FillInEventFromGestureDown(&startEvent);
+
+ nsDragEvent gestureEvent(NS_IS_TRUSTED_EVENT(aEvent), NS_DRAGDROP_GESTURE, widget);
FillInEventFromGestureDown(&gestureEvent);
+ startEvent.dataTransfer = gestureEvent.dataTransfer = dataTransfer;
+
// Dispatch to the DOM. By setting mCurrentTarget we are faking
// out the ESM and telling it that the current target frame is
// actually where the mouseDown occurred, otherwise it will use
// the frame the mouse is currently over which may or may not be
// the same. (Note: saari and I have decided that we don't have
// to reset |mCurrentTarget| when we're through because no one
// else is doing anything more with this event and it will get
// reset on the very next event to the correct frame).
// Hold onto old target content through the event and reset after.
nsCOMPtr<nsIContent> targetBeforeEvent = mCurrentTargetContent;
// Set the current target to the content for the mouse down
mCurrentTargetContent = targetContent;
- // Dispatch the draggesture event to the DOM
+ // Dispatch both the dragstart and draggesture events to the DOM
nsEventStatus status = nsEventStatus_eIgnore;
- nsEventDispatcher::Dispatch(targetContent, aPresContext, &gestureEvent, nsnull,
+ nsEventDispatcher::Dispatch(targetContent, aPresContext, &startEvent, nsnull,
&status);
+ nsDragEvent* event = &startEvent;
+ if (status != nsEventStatus_eConsumeNoDefault) {
+ status = nsEventStatus_eIgnore;
+ nsEventDispatcher::Dispatch(targetContent, aPresContext, &gestureEvent, nsnull,
+ &status);
+ event = &gestureEvent;
+ }
+
+ // now that the dataTransfer has been updated in the dragstart and
+ // draggesture events, make it read only so that the data doesn't
+ // change during the drag.
+ dataTransfer->SetReadOnly();
+
+ if (status != nsEventStatus_eConsumeNoDefault)
+ DoDefaultDragStart(aPresContext, event, dataTransfer,
+ targetContent, isSelection);
+
// Note that frame event handling doesn't care about NS_DRAGDROP_GESTURE,
// which is just as well since we don't really know which frame to
// send it to
// Reset mCurretTargetContent to what it was
mCurrentTargetContent = targetBeforeEvent;
}
// Now flush all pending notifications, for better responsiveness
// while dragging.
FlushPendingEvents(aPresContext);
}
} // GenerateDragGesture
+void
+nsEventStateManager::DetermineDragTarget(nsPresContext* aPresContext,
+ nsIContent* aSelectionTarget,
+ nsDOMDataTransfer* aDataTransfer,
+ PRBool* aIsSelection,
+ nsIContent** aTargetNode)
+{
+ *aTargetNode = nsnull;
+
+ nsCOMPtr<nsISupports> container = aPresContext->GetContainer();
+ nsCOMPtr<nsIDOMWindow> window = do_GetInterface(container);
+
+ // GetDragData determines if a selection, link or image in the content
+ // should be dragged, and places the data associated with the drag in the
+ // data transfer. Skip this check for chrome shells.
+ PRBool canDrag;
+ nsCOMPtr<nsIContent> dragDataNode;
+ nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(container);
+ if (dsti) {
+ PRInt32 type = -1;
+ if (NS_SUCCEEDED(dsti->GetItemType(&type)) &&
+ type != nsIDocShellTreeItem::typeChrome) {
+ // mGestureDownContent is the node where the mousedown event for the drag
+ // occured, and aSelectionTarget is the node to use when a selection is used
+ nsresult rv =
+ nsContentAreaDragDrop::GetDragData(window, mGestureDownContent,
+ aSelectionTarget, mGestureDownAlt,
+ aDataTransfer, &canDrag, aIsSelection,
+ getter_AddRefs(dragDataNode));
+ if (NS_FAILED(rv) || !canDrag)
+ return;
+ }
+ }
+
+ // if GetDragData returned a node, use that as the node being dragged.
+ // Otherwise, if a selection is being dragged, use the node within the
+ // selection that was dragged. Otherwise, just use the mousedown target.
+ nsIContent* dragContent = mGestureDownContent;
+ if (dragDataNode)
+ dragContent = dragDataNode;
+ else if (*aIsSelection)
+ dragContent = aSelectionTarget;
+
+ nsIContent* originalDragContent = dragContent;
+
+ // If a selection isn't being dragged, look for an ancestor with the
+ // draggable property set. If one is found, use that as the target of the
+ // drag instead of the node that was clicked on. If a draggable node wasn't
+ // found, just use the clicked node.
+ if (!*aIsSelection) {
+ while (dragContent) {
+ nsCOMPtr<nsIDOMNSHTMLElement> htmlElement = do_QueryInterface(dragContent);
+ if (htmlElement) {
+ PRBool draggable = PR_FALSE;
+ htmlElement->GetDraggable(&draggable);
+ if (draggable)
+ break;
+ }
+ else {
+ nsCOMPtr<nsIDOMXULElement> xulElement = do_QueryInterface(dragContent);
+ if (xulElement) {
+ // All XUL elements are draggable, so if a XUL element is
+ // encountered, stop looking for draggable nodes and just use the
+ // original clicked node instead.
+ // XXXndeakin
+ // In the future, we will want to improve this so that XUL has a
+ // better way to specify whether something is draggable than just
+ // on/off.
+ dragContent = mGestureDownContent;
+ break;
+ }
+ // otherwise, it's not an HTML or XUL element, so just keep looking
+ }
+ dragContent = dragContent->GetParent();
+ }
+ }
+
+ // if no node in the hierarchy was found to drag, but the GetDragData method
+ // returned a node, use that returned node. Otherwise, just use the original
+ // node that was clicked.
+ if (!dragContent) {
+ if (dragDataNode)
+ dragContent = originalDragContent;
+ else
+ dragContent = mGestureDownContent;
+ }
+
+ if (dragContent) {
+ // if an ancestor node was used instead, clear the drag data
+ // XXXndeakin rework this a bit. Find a way to just not call GetDragData if we don't need to.
+ if (dragContent != originalDragContent)
+ aDataTransfer->ClearAll();
+ *aTargetNode = dragContent;
+ NS_ADDREF(*aTargetNode);
+ }
+}
+
+void
+nsEventStateManager::DoDefaultDragStart(nsPresContext* aPresContext,
+ nsDragEvent* aDragEvent,
+ nsDOMDataTransfer* aDataTransfer,
+ nsIContent* aDragTarget,
+ PRBool aIsSelection)
+{
+ nsCOMPtr<nsIDragService> dragService =
+ do_GetService("@mozilla.org/widget/dragservice;1");
+ if (!dragService)
+ return;
+
+ // Default handling for the draggesture/dragstart event.
+ //
+ // First, check if a drag session already exists. This means that the drag
+ // service was called directly within a draggesture handler. In this case,
+ // don't do anything more, as it is assumed that the handler is managing
+ // drag and drop manually.
+ nsCOMPtr<nsIDragSession> dragSession;
+ dragService->GetCurrentSession(getter_AddRefs(dragSession));
+ if (dragSession)
+ return; // already a drag in progress
+
+ // No drag session is currently active, so check if a handler added
+ // any items to be dragged. If not, there isn't anything to drag.
+ PRUint32 count = 0;
+ if (aDataTransfer)
+ aDataTransfer->GetMozItemCount(&count);
+ if (!count)
+ return;
+
+ // Get the target being dragged, which may not be the same as the
+ // target of the mouse event. If one wasn't set in the
+ // aDataTransfer during the event handler, just use the original
+ // target instead.
+ nsCOMPtr<nsIDOMNode> dragTarget;
+ nsCOMPtr<nsIDOMElement> dragTargetElement;
+ aDataTransfer->GetDragTarget(getter_AddRefs(dragTargetElement));
+ dragTarget = do_QueryInterface(dragTargetElement);
+ if (!dragTarget) {
+ dragTarget = do_QueryInterface(aDragTarget);
+ if (!dragTarget)
+ return;
+ }
+
+ // check which drag effect should initially be used
+ PRUint32 effectAllowed;
+ aDataTransfer->GetEffectAllowedInt(&effectAllowed);
+
+ PRInt32 action = 0;
+ if (effectAllowed != nsIDragService::DRAGDROP_ACTION_UNINITIALIZED)
+ action = effectAllowed;
+
+ // get any custom drag image that was set
+ PRInt32 imageX, imageY;
+ nsIDOMElement* dragImage = aDataTransfer->GetDragImage(&imageX, &imageY);
+
+ // If a selection is being dragged, and no custom drag image was
+ // set, get the selection so that the drag region can be created
+ // from the selection area. If a custom image was set, it doesn't
+ // matter what the selection is since the image will be used instead.
+ nsISelection* selection = nsnull;
+ if (aIsSelection && !dragImage) {
+ nsIDocument* doc = aDragTarget->GetCurrentDoc();
+ if (doc) {
+ nsIPresShell* presShell = doc->GetPrimaryShell();
+ if (presShell) {
+ selection = presShell->GetCurrentSelection(
+ nsISelectionController::SELECTION_NORMAL);
+ }
+ }
+ }
+
+ nsCOMPtr<nsISupportsArray> transArray;
+ aDataTransfer->GetTransferables(getter_AddRefs(transArray));
+ if (!transArray)
+ return;
+
+ // XXXndeakin don't really want to create a new drag DOM event
+ // here, but we need something to pass to the InvokeDragSession
+ // methods.
+ nsCOMPtr<nsIDOMEvent> domEvent;
+ NS_NewDOMDragEvent(getter_AddRefs(domEvent), aPresContext, aDragEvent);
+
+ nsCOMPtr<nsIDOMDragEvent> domDragEvent = do_QueryInterface(domEvent);
+ // if creating a drag event failed, starting a drag session will
+ // just fail.
+ if (selection) {
+ dragService->InvokeDragSessionWithSelection(selection, transArray,
+ action, domDragEvent,
+ aDataTransfer);
+ }
+ else {
+ // if dragging within a XUL tree and no custom drag image was
+ // set, the region argument to InvokeDragSessionWithImage needs
+ // to be set to the area encompassing the selected rows of the
+ // tree to ensure that the drag feedback gets clipped to those
+ // rows. For other content, region should be null.
+ nsCOMPtr<nsIScriptableRegion> region;
+#ifdef MOZ_XUL
+ if (dragTarget && !dragImage) {
+ nsCOMPtr<nsIContent> content = do_QueryInterface(dragTarget);
+ if (content->NodeInfo()->Equals(nsGkAtoms::treechildren,
+ kNameSpaceID_XUL)) {
+ nsIDocument* doc = content->GetCurrentDoc();
+ if (doc) {
+ nsIPresShell* presShell = doc->GetPrimaryShell();
+ if (presShell) {
+ nsIFrame* frame = presShell->GetPrimaryFrameFor(content);
+ if (frame) {
+ nsITreeBoxObject* treeBoxObject;
+ CallQueryInterface(frame, &treeBoxObject);
+ treeBoxObject->GetSelectionRegion(getter_AddRefs(region));
+ }
+ }
+ }
+ }
+ }
+#endif
+
+ dragService->InvokeDragSessionWithImage(dragTarget, transArray,
+ region, action, dragImage,
+ imageX, imageY, domDragEvent,
+ aDataTransfer);
+ }
+}
+
nsresult
nsEventStateManager::GetMarkupDocumentViewer(nsIMarkupDocumentViewer** aMv)
{
*aMv = nsnull;
if(!gLastFocusedDocument) return NS_ERROR_FAILURE;
nsPIDOMWindow* ourWindow = gLastFocusedDocument->GetWindow();
@@ -2534,20 +2806,134 @@ nsEventStateManager::PostHandleEvent(nsP
break;
}
*aStatus = nsEventStatus_eConsumeNoDefault;
}
break;
+ case NS_DRAGDROP_ENTER:
+ case NS_DRAGDROP_OVER:
+ {
+ NS_ASSERTION(aEvent->eventStructType == NS_DRAG_EVENT, "Expected a drag event");
+
+ nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
+ if (!dragSession)
+ break;
+
+ // the initial dataTransfer is the one from the dragstart event that
+ // was set on the dragSession when the drag began.
+ nsCOMPtr<nsIDOMNSDataTransfer> dataTransfer;
+ nsCOMPtr<nsIDOMDataTransfer> initialDataTransfer;
+ dragSession->GetDataTransfer(getter_AddRefs(initialDataTransfer));
+
+ nsCOMPtr<nsIDOMNSDataTransfer> initialDataTransferNS =
+ do_QueryInterface(initialDataTransfer);
+
+ // cancelling a dragenter or dragover event means that a drop should be
+ // allowed, so update the dropEffect and the canDrop state to indicate
+ // that a drag is allowed. If the event isn't cancelled, a drop won't be
+ // allowed. Essentially, to allow a drop somewhere, specify the effects
+ // using the effectAllowed and dropEffect properties in a dragenter or
+ // dragover event and cancel the event. To not allow a drop somewhere,
+ // don't cancel the event or set the effectAllowed or dropEffect to
+ // "none". This way, if the event is just ignored, no drop will be
+ // allowed.
+ PRUint32 dropEffect = nsIDragService::DRAGDROP_ACTION_NONE;
+ if (nsEventStatus_eConsumeNoDefault == *aStatus) {
+ // if the event has a dataTransfer set, use it.
+ nsDragEvent *dragEvent = (nsDragEvent*)aEvent;
+ if (dragEvent->dataTransfer) {
+ // get the dataTransfer and the dropEffect that was set on it
+ dataTransfer = do_QueryInterface(dragEvent->dataTransfer);
+ dataTransfer->GetDropEffectInt(&dropEffect);
+ }
+ else {
+ // if dragEvent->dataTransfer is null, it means that no attempt was
+ // made to access the dataTransfer during the event, yet the event
+ // was cancelled. Instead, use the initial data transfer available
+ // from the drag session. The drop effect would not have been
+ // initialized (which is done in nsDOMDragEvent::GetDataTransfer),
+ // so set it from the drag action. We'll still want to filter it
+ // based on the effectAllowed below.
+ dataTransfer = initialDataTransferNS;
+
+ PRUint32 action;
+ dragSession->GetDragAction(&action);
+
+ // filter the drop effect based on the action. Use UNINITIALIZED as
+ // any effect is allowed.
+ dropEffect = nsDOMDragEvent::FilterDropEffect(action,
+ nsIDragService::DRAGDROP_ACTION_UNINITIALIZED);
+ }
+
+ // At this point, if the dataTransfer is null, it means that the
+ // drag was originally started by directly calling the drag service.
+ // Just assume that all effects are allowed.
+ PRUint32 effectAllowed = nsIDragService::DRAGDROP_ACTION_UNINITIALIZED;
+ if (dataTransfer)
+ dataTransfer->GetEffectAllowedInt(&effectAllowed);
+
+ // set the drag action based on the drop effect and effect allowed.
+ // The drop effect field on the drag transfer object specifies the
+ // desired current drop effect. However, it cannot be used if the
+ // effectAllowed state doesn't include that type of action. If the
+ // dropEffect is "none", then the action will be 'none' so a drop will
+ // not be allowed.
+ PRUint32 action = nsIDragService::DRAGDROP_ACTION_NONE;
+ if (effectAllowed == nsIDragService::DRAGDROP_ACTION_UNINITIALIZED ||
+ dropEffect & effectAllowed)
+ action = dropEffect;
+
+ if (action == nsIDragService::DRAGDROP_ACTION_NONE)
+ dropEffect = nsIDragService::DRAGDROP_ACTION_NONE;
+
+ // inform the drag session that a drop is allowed on this node.
+ dragSession->SetDragAction(action);
+ dragSession->SetCanDrop(action != nsIDragService::DRAGDROP_ACTION_NONE);
+ }
+
+ // now set the drop effect in the initial dataTransfer. This ensures
+ // that we can get the desired drop effect in the drop event.
+ if (initialDataTransferNS)
+ initialDataTransferNS->SetDropEffectInt(dropEffect);
+ }
+ break;
+
case NS_DRAGDROP_DROP:
+ {
+ // now fire the dragdrop event, for compatibility with XUL
+ if (mCurrentTarget && nsEventStatus_eConsumeNoDefault != *aStatus) {
+ nsCOMPtr<nsIContent> targetContent;
+ mCurrentTarget->GetContentForEvent(presContext, aEvent,
+ getter_AddRefs(targetContent));
+
+ nsCOMPtr<nsIWidget> widget = mCurrentTarget->GetWindow();
+ nsDragEvent event(NS_IS_TRUSTED_EVENT(aEvent), NS_DRAGDROP_DRAGDROP, widget);
+
+ nsMouseEvent* mouseEvent = static_cast<nsMouseEvent*>(aEvent);
+ event.refPoint = mouseEvent->refPoint;
+ event.isShift = mouseEvent->isShift;
+ event.isControl = mouseEvent->isControl;
+ event.isAlt = mouseEvent->isAlt;
+ event.isMeta = mouseEvent->isMeta;
+
+ nsEventStatus status = nsEventStatus_eIgnore;
+ nsCOMPtr<nsIPresShell> presShell = mPresContext->GetPresShell();
+ if (presShell) {
+ presShell->HandleEventWithTarget(&event, mCurrentTarget,
+ targetContent, &status);
+ }
+ }
+ break;
+ }
case NS_DRAGDROP_EXIT:
- // clean up after ourselves. make sure we do this _after_ the event, else we'll
- // clean up too early!
+ // make sure to fire the enter and exit_synth events after the
+ // NS_DRAGDROP_EXIT event, otherwise we'll clean up too early
GenerateDragDropEnterExit(presContext, (nsGUIEvent*)aEvent);
break;
case NS_KEY_UP:
break;
case NS_KEY_PRESS:
if (nsEventStatus_eConsumeNoDefault != *aStatus) {
@@ -3171,16 +3557,18 @@ nsEventStateManager::GenerateDragDropEnt
nsCOMPtr<nsIContent> lastContent;
nsCOMPtr<nsIContent> targetContent;
mCurrentTarget->GetContentForEvent(aPresContext, aEvent, getter_AddRefs(targetContent));
if ( mLastDragOverFrame ) {
//The frame has changed but the content may not have. Check before dispatching to content
mLastDragOverFrame->GetContentForEvent(aPresContext, aEvent, getter_AddRefs(lastContent));
+ FireDragEnterOrExit(aPresContext, aEvent, NS_DRAGDROP_LEAVE_SYNTH,
+ targetContent, lastContent, mLastDragOverFrame);
FireDragEnterOrExit(aPresContext, aEvent, NS_DRAGDROP_EXIT_SYNTH,
targetContent, lastContent, mLastDragOverFrame);
}
FireDragEnterOrExit(aPresContext, aEvent, NS_DRAGDROP_ENTER,
lastContent, targetContent, mCurrentTarget);
mLastDragOverFrame = mCurrentTarget;
@@ -3191,16 +3579,18 @@ nsEventStateManager::GenerateDragDropEnt
case NS_DRAGDROP_DROP:
case NS_DRAGDROP_EXIT:
{
//This is actually the window mouse exit event.
if ( mLastDragOverFrame ) {
nsCOMPtr<nsIContent> lastContent;
mLastDragOverFrame->GetContentForEvent(aPresContext, aEvent, getter_AddRefs(lastContent));
+ FireDragEnterOrExit(aPresContext, aEvent, NS_DRAGDROP_LEAVE_SYNTH,
+ nsnull, lastContent, mLastDragOverFrame);
FireDragEnterOrExit(aPresContext, aEvent, NS_DRAGDROP_EXIT_SYNTH,
nsnull, lastContent, mLastDragOverFrame);
mLastDragOverFrame = nsnull;
}
}
break;
}
@@ -3216,18 +3606,17 @@ void
nsEventStateManager::FireDragEnterOrExit(nsPresContext* aPresContext,
nsGUIEvent* aEvent,
PRUint32 aMsg,
nsIContent* aRelatedTarget,
nsIContent* aTargetContent,
nsWeakFrame& aTargetFrame)
{
nsEventStatus status = nsEventStatus_eIgnore;
- nsMouseEvent event(NS_IS_TRUSTED_EVENT(aEvent), aMsg,
- aEvent->widget, nsMouseEvent::eReal);
+ nsDragEvent event(NS_IS_TRUSTED_EVENT(aEvent), aMsg, aEvent->widget);
event.refPoint = aEvent->refPoint;
event.isShift = ((nsMouseEvent*)aEvent)->isShift;
event.isControl = ((nsMouseEvent*)aEvent)->isControl;
event.isAlt = ((nsMouseEvent*)aEvent)->isAlt;
event.isMeta = ((nsMouseEvent*)aEvent)->isMeta;
event.relatedTarget = aRelatedTarget;
mCurrentTargetContent = aTargetContent;
--- a/content/events/src/nsEventStateManager.h
+++ b/content/events/src/nsEventStateManager.h
@@ -55,16 +55,17 @@
class nsIScrollableView;
class nsIPresShell;
class nsIDocShell;
class nsIDocShellTreeNode;
class nsIDocShellTreeItem;
class nsIFocusController;
class imgIContainer;
+class nsDOMDataTransfer;
// mac uses click-hold context menus, a holdover from 4.x
#ifdef XP_MACOSX
#define CLICK_HOLD_CONTEXT_MENUS 1
#endif
/*
@@ -313,16 +314,48 @@ protected:
nsresult ChangeFullZoom(PRInt32 change);
// end mousewheel functions
// routines for the d&d gesture tracking state machine
void BeginTrackingDragGesture ( nsPresContext* aPresContext, nsMouseEvent* inDownEvent,
nsIFrame* inDownFrame ) ;
void StopTrackingDragGesture ( ) ;
void GenerateDragGesture ( nsPresContext* aPresContext, nsMouseEvent *aEvent ) ;
+
+ /**
+ * Determine which node the drag should be targeted at.
+ * This is either the node clicked when there is a selection, or, for HTML,
+ * the element with a draggable property set to true.
+ *
+ * aSelectionTarget - target to check for selection
+ * aDataTransfer - data transfer object that will contain the data to drag
+ * aIsSelection - [out] set to true if a selection is being dragged
+ * aTargetNode - [out] the draggable node, or null if there isn't one
+ */
+ void DetermineDragTarget(nsPresContext* aPresContext,
+ nsIContent* aSelectionTarget,
+ nsDOMDataTransfer* aDataTransfer,
+ PRBool* aIsSelection,
+ nsIContent** aTargetNode);
+
+ /*
+ * Perform the default handling for the dragstart/draggesture event and set up a
+ * drag for aDataTransfer if it contains any data.
+ *
+ * aDragEvent - the dragstart/draggesture event
+ * aDataTransfer - the data transfer that holds the data to be dragged
+ * aDragTarget - the target of the drag
+ * aIsSelection - true if a selection is being dragged
+ */
+ void DoDefaultDragStart(nsPresContext* aPresContext,
+ nsDragEvent* aDragEvent,
+ nsDOMDataTransfer* aDataTransfer,
+ nsIContent* aDragTarget,
+ PRBool aIsSelection);
+
PRBool IsTrackingDragGesture ( ) const { return mGestureDownContent != nsnull; }
/**
* Set the fields of aEvent to reflect the mouse position and modifier keys
* that were set when the user first pressed the mouse button (stored by
* BeginTrackingDragGesture). aEvent->widget must be
* mCurrentTarget->GetWindow().
*/
void FillInEventFromGestureDown(nsMouseEvent* aEvent);
--- a/content/events/test/Makefile.in
+++ b/content/events/test/Makefile.in
@@ -56,16 +56,18 @@ include $(topsrcdir)/config/rules.mk
test_bug379120.html \
test_bug391568.xhtml \
test_bug402089.html \
test_bug405632.html \
test_bug409604.html \
test_bug412567.html \
test_bug443985.html \
test_bug447736.html \
+ test_draggableprop.html \
+ test_dragstart.html \
$(NULL)
_CHROME_FILES = \
test_bug415498.xul \
bug415498-doc1.html \
bug415498-doc2.html \
$(NULL)
new file mode 100644
--- /dev/null
+++ b/content/events/test/test_draggableprop.html
@@ -0,0 +1,91 @@
+<html>
+<head>
+ <title>Tests for the draggable property on HTML elements</title>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+ <script type="application/javascript"
+ src="chrome://mochikit/content/MochiKit/packed.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+
+<span id="elem1">One</span>
+<span id="elem2" draggable="true">Two</span>
+<span id="elem3" draggable="">Three</span>
+<span id="elem4" draggable="false">Four</span>
+<span id="elem5" draggable="other">Five</span>
+
+<img id="img1" src="../happy.png">
+<img id="img2" src="../happy.png" draggable="true">
+<img id="img3" src="../happy.png" draggable="">
+<img id="img4" src="../happy.png" draggable="false">
+<img id="img5" src="../happy.png" draggable="other">
+
+<a id="a1">One</a>
+<a id="a2" draggable="true">Two</a>
+<a id="a3" draggable="">Three</a>
+<a id="a4" draggable="false">Four</a>
+<a id="a5" draggable="other">Five</a>
+
+<a id="ahref1" href="http://www.mozilla.org">One</a>
+<a id="ahref2" href="http://www.mozilla.org" draggable="true">Two</a>
+<a id="ahref3" href="http://www.mozilla.org" draggable="">Three</a>
+<a id="ahref4" href="http://www.mozilla.org" draggable="false">Four</a>
+<a id="ahref5" href="http://www.mozilla.org" draggable="other">Five</a>
+
+<script>
+function check()
+{
+ try {
+ checkElements(1, false, true, false, true);
+ checkElements(2, true, true, true, true);
+ checkElements(3, false, true, false, true);
+ checkElements(4, false, false, false, false);
+ checkElements(5, false, true, false, true);
+ }
+ catch (ex) {
+ is("script error", ex, "fail");
+ }
+}
+
+function checkElements(idx, estate, istate, astate, ahrefstate)
+{
+ checkElement("elem" + idx, estate, false);
+ checkElement("img" + idx, istate, true);
+ checkElement("a" + idx, astate, false);
+ checkElement("ahref" + idx, ahrefstate, true);
+}
+
+function checkElement(elemid, state, statedef)
+{
+ var elem = document.getElementById(elemid);
+
+ is(elem.draggable, state, elemid + "-initial");
+ elem.draggable = true;
+ is(elem.draggable, true, elemid + "-true");
+ elem.draggable = false;
+ is(elem.draggable, false, elemid + "-false");
+
+ elem.setAttribute("draggable", "true");
+ is(elem.draggable, true, elemid + "-attr-true");
+ elem.setAttribute("draggable", "false");
+ is(elem.draggable, false, elemid + "-attr-false");
+ elem.setAttribute("draggable", "other");
+ is(elem.draggable, statedef, elemid + "-attr-other");
+ elem.setAttribute("draggable", "");
+ is(elem.draggable, statedef, elemid + "-attr-empty");
+ elem.removeAttribute("draggable");
+ is(elem.draggable, statedef, elemid + "-attr-removed");
+}
+
+check();
+
+</script>
+
+</body>
+</html>
+
+
new file mode 100644
--- /dev/null
+++ b/content/events/test/test_dragstart.html
@@ -0,0 +1,526 @@
+<html>
+<head>
+ <title>Tests for the dragstart event</title>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+ <script type="application/javascript" src="/MochiKit/packed.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+
+<!--
+ This test checks the dragstart event and the DataTransfer object
+ -->
+
+<script>
+
+SimpleTest.waitForExplicitFinish();
+
+var gDragInfo;
+var gDataTransfer = null;
+
+function runTests()
+{
+ // first, create a selection and try dragging it
+ var draggable = $("draggable");
+ window.getSelection().selectAllChildren(draggable);
+ synthesizeMouse(draggable, 6, 6, { type: "mousedown" });
+ synthesizeMouse(draggable, 14, 14, { type: "mousemove" });
+
+ // the dragstart should have occured due to moving the mouse. gDataTransfer
+ // caches the dataTransfer that was used, however it should now be empty and
+ // be read only.
+ ok(gDataTransfer instanceof DataTransfer, "DataTransfer after dragstart event");
+ checkTypes(gDataTransfer, [], 0, "after dragstart event");
+
+ expectError(function() gDataTransfer.setData("text/plain", "Some Text"),
+ "NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR", "setData when read only");
+ expectError(function() gDataTransfer.clearData("text/plain"),
+ "NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR", "clearData when read only");
+ expectError(function() gDataTransfer.mozSetDataAt("text/plain", "Some Text", 0),
+ "NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR", "setDataAt when read only");
+ expectError(function() gDataTransfer.mozClearDataAt("text/plain", 0),
+ "NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR", "clearDataAt when read only");
+ expectError(function() gDataTransfer.setDragImage(draggable, 10, 10),
+ "NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR", "setDragImage when read only");
+ expectError(function() gDataTransfer.addElement(draggable),
+ "NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR", "addElement when read only");
+
+ // next, dragging links and images
+ sendMouseEventsForDrag("link");
+ sendMouseEventsForDrag("image");
+
+// disable testing input dragging for now, as it doesn't seem to be testable
+// draggable = $("input");
+// draggable.setSelectionRange(0, 4);
+// synthesizeMouse(draggable, 8, 8, { type: "mousedown" });
+// synthesizeMouse(draggable, 15, 15, { type: "mousemove" });
+// sendMouseEventsForDrag("input");
+
+ // next, check if the draggable attribute can be used to adjust the drag target
+ sendMouseEventsForDrag("dragtrue");
+ ok(gDragInfo.itemCount == 0 && gDragInfo.types.length == 0 && gDragInfo.target == $("dragtrue"), "draggable true node");
+ sendMouseEventsForDrag("spantrue");
+ ok(gDragInfo.itemCount == 0 && gDragInfo.types.length == 0 && gDragInfo.target == $("dragtrue"), "draggable true child");
+ sendMouseEventsForDrag("dragfalse");
+ ok(gDragInfo.itemCount == 0 && gDragInfo.types.length == 0 && gDragInfo.target == $("dragfalse").firstChild,
+ "draggable false node");
+ sendMouseEventsForDrag("spanfalse");
+ ok(gDragInfo.itemCount == 0 && gDragInfo.types.length == 0 && gDragInfo.target == $("spanfalse").firstChild,
+ "draggable false child");
+
+ var evt = document.createEvent("dragevent");
+ ok(evt instanceof DragEvent, "synthetic dragevent class")
+ ok(evt instanceof MouseEvent, "synthetic event inherits from MouseEvent")
+ evt.initDragEvent("dragstart", true, true, window, 1, null);
+ $("synthetic").dispatchEvent(evt);
+
+ var evt = document.createEvent("dragevents");
+ ok(evt instanceof DragEvent, "synthetic dragevents class")
+ evt.initDragEvent("dragover", true, true, window, 0, null);
+ $("synthetic2").dispatchEvent(evt);
+
+ SimpleTest.finish();
+}
+
+function sendMouseEventsForDrag(nodeid)
+{
+ gDragInfo = null;
+
+ var draggable = $(nodeid);
+ synthesizeMouse(draggable, 3, 3, { type: "mousedown" });
+ synthesizeMouse(draggable, 10, 10, { type: "mousemove" });
+}
+
+function doDragStartSelection(event)
+{
+ is(event.type, "dragstart", "dragstart event type");
+ is(event.target, $("draggable").firstChild, "dragstart event target");
+ is(event.bubbles, true, "dragstart event bubbles");
+ is(event.cancelable, true, "dragstart event cancelable");
+
+ var dt = event.dataTransfer;
+ ok(dt instanceof DataTransfer, "dataTransfer is DataTransfer");
+ gDataTransfer = dt;
+
+ var types = dt.types;
+ is(types instanceof DOMStringList, true, "initial types is a DOMStringList");
+ checkTypes(dt, ["text/_moz_htmlcontext", "text/_moz_htmlinfo", "text/html", "text/plain"], 0, "initial selection");
+
+ is(dt.getData("text/plain"), "This is a draggable bit of text.", "initial selection text/plain");
+ is(dt.getData("text/html"), "<div id=\"draggable\" ondragstart=\"doDragStartSelection(event)\">This is a <em>draggable</em> bit of text.</div>",
+ "initial selection text/html");
+
+ // text/unicode and Text are available for compatibility. They retrieve the
+ // text/plain data
+ is(dt.getData("text/unicode"), "This is a draggable bit of text.", "initial selection text/unicode");
+ is(dt.getData("Text"), "This is a draggable bit of text.", "initial selection Text");
+
+ is(dt.mozItemCount, 1, "initial selection item count");
+
+ dt.clearData("text/plain");
+ dt.clearData("text/html");
+ dt.clearData("text/_moz_htmlinfo");
+ dt.clearData("text/_moz_htmlcontext");
+
+ test_DataTransfer(dt);
+}
+
+function test_DataTransfer(dt)
+{
+ is(dt.mozItemCount, 0, "empty itemCount");
+
+ var types = dt.types;
+ is(types instanceof DOMStringList, true, "empty types is a DOMStringList");
+ checkTypes(dt, [], 0, "empty");
+ is(dt.getData("text/plain"), "", "empty data is empty");
+
+ // calling setDataAt requires an index that is 0 <= index <= dt.itemCount
+ expectError(function() dt.mozSetDataAt("text/plain", "Some Text", 1),
+ "NS_ERROR_DOM_INDEX_SIZE_ERR", "setDataAt index too high");
+
+ // because an exception occured, the data should not have been added
+ is(dt.mozItemCount, 0, "empty setDataAt index too high itemCount");
+ dt.getData("text/plain", "", "empty setDataAt index too high getData");
+
+ // if the type is '', do nothing, or return ''
+ dt.setData("", "Invalid Type");
+ is(dt.types.length, 0, "invalid type setData");
+ is(dt.getData(""), "", "invalid type getData"),
+ dt.mozSetDataAt("", "Invalid Type", 0);
+ is(dt.types.length, 0, "invalid type setDataAt");
+ is(dt.mozGetDataAt("", 0), null, "invalid type getDataAt"),
+
+ // similar with clearDataAt and getDataAt
+ expectError(function() dt.mozGetDataAt("text/plain", 1),
+ "NS_ERROR_DOM_INDEX_SIZE_ERR", "getDataAt index too high");
+ expectError(function() dt.mozClearDataAt("text/plain", 1),
+ "NS_ERROR_DOM_INDEX_SIZE_ERR", "clearDataAt index too high");
+
+ dt.setData("text/plain", "Sample Text");
+ is(dt.mozItemCount, 1, "added plaintext itemCount");
+ checkOneDataItem(dt, ["text/plain"], ["Sample Text"], 0, "added plaintext");
+
+ // after all those exceptions, the data should still be the same
+ checkOneDataItem(dt, ["text/plain"], ["Sample Text"], 0, "added plaintext after exception");
+
+ // modifying the data associated with the format should give it the new value
+ dt.setData("text/plain", "Modified Text");
+ is(dt.mozItemCount, 1, "modified plaintext itemCount");
+ checkOneDataItem(dt, ["text/plain"], ["Modified Text"], 0, "modified plaintext");
+
+ dt.setData("text/html", "<strong>Modified Text</strong>");
+ is(dt.mozItemCount, 1, "modified html itemCount");
+ checkOneDataItem(dt, ["text/plain", "text/html"],
+ ["Modified Text", "<strong>Modified Text</strong>"],
+ 0, "modified html");
+
+ // modifying data for a type that already exists should adjust it in place,
+ // not reinsert it at the beginning
+ dt.setData("text/plain", "New Text");
+ is(dt.mozItemCount, 1, "modified text again itemCount");
+ checkOneDataItem(dt, ["text/plain", "text/html"],
+ ["New Text", "<strong>Modified Text</strong>"],
+ 0, "modified text again");
+
+ var draggable = $("draggable");
+ dt.setData("application/-moz-node", draggable);
+ checkOneDataItem(dt, ["text/plain", "text/html", "application/-moz-node"],
+ ["New Text", "<strong>Modified Text</strong>", draggable],
+ 0, "added node");
+
+ dt.clearData(""); // null means clear all
+ is(dt.mozItemCount, 0, "itemCount after clearData empty string");
+ checkTypes(dt, [], 0, "empty after clearData empty string");
+
+ dt.setData("text/plain", 22);
+ dt.setData("text/html", 5.6);
+ dt.setData("text/xml", 5.6);
+ checkTypes(dt, ["text/plain", "text/html", "text/xml"], ["22", "5.6", ""], 0, "add numeric and empty data");
+
+ dt.clearData(); // no argument means clear all
+ is(dt.mozItemCount, 0, "itemCount after clearData no argument");
+ checkTypes(dt, [], 0, "empty after clearData no argument");
+
+ // check 'Text' type which should convert into text/plain
+ dt.setData("Text", "Sample Text");
+ checkOneDataItem(dt, ["text/plain"], ["Sample Text"], 0, "set Text");
+ is(dt.getData("Text"), "Sample Text", "getData Text");
+ is(dt.mozGetDataAt("Text", 0), "Sample Text", "getDataAt Text");
+ dt.setData("text/plain", "More Text");
+ checkOneDataItem(dt, ["text/plain"], ["More Text"], 0, "set text/plain after set Text");
+
+ dt.mozClearDataAt("", 0); // null means clear all
+ is(dt.mozItemCount, 0, "itemCount after clearDataAt empty string");
+ checkTypes(dt, [], 0, "empty after clearDataAt empty string");
+
+ // check text/uri-list type
+ dt.setData("text/uri-list", "http://www.mozilla.org");
+ checkURL(dt, "http://www.mozilla.org", "http://www.mozilla.org", 0, "set text/uri-list");
+
+ // check URL type which should add text/uri-list data
+ dt.setData("URL", "ftp://ftp.example.com");
+ checkURL(dt, "ftp://ftp.example.com", "ftp://ftp.example.com", 0, "set URL");
+ checkTypes(dt, ["text/uri-list"], ["ftp://ftp.example.com"], "url types");
+
+ // clearing text/uri-list data
+ dt.clearData("text/uri-list");
+ is(dt.mozItemCount, 0, "itemCount after clear url-list");
+ is(dt.getData("text/uri-list"), "", "text/uri-list after clear url-list");
+ is(dt.getData("URL"), "", "URL after clear url-list");
+
+ // check text/uri-list parsing
+ dt.setData("text/uri-list", "#http://www.mozilla.org\nhttp://www.xulplanet.com\nhttp://www.example.com");
+ checkURL(dt, "http://www.xulplanet.com",
+ "#http://www.mozilla.org\nhttp://www.xulplanet.com\nhttp://www.example.com",
+ 0, "uri-list 3 lines");
+
+ dt.setData("text/uri-list", "#http://www.mozilla.org");
+ is(dt.getData("URL"), "", "uri-list commented");
+ dt.setData("text/uri-list", "#http://www.mozilla.org\n");
+ is(dt.getData("URL"), "", "uri-list commented with newline");
+
+ // check that clearing the URL type also clears the text/uri-list type
+ dt.clearData("URL");
+ is(dt.getData("text/uri-list"), "", "clear URL");
+
+ dt.setData("text/uri-list", "#http://www.mozilla.org\n\n\n\n\n");
+ is(dt.getData("URL"), "", "uri-list with blank lines");
+ dt.setData("text/uri-list", "");
+ is(dt.getData("URL"), "", "empty uri-list");
+ dt.setData("text/uri-list", "#http://www.mozilla.org\n#Sample\nhttp://www.xulplanet.com \r\n");
+ is(dt.getData("URL"), "http://www.xulplanet.com", "uri-list mix");
+ dt.setData("text/uri-list", "\nhttp://www.mozilla.org");
+ is(dt.getData("URL"), "", "empty line to start uri-list");
+ dt.setData("text/uri-list", " http://www.mozilla.org#anchor ");
+ is(dt.getData("URL"), "http://www.mozilla.org#anchor", "uri-list with spaces and hash");
+
+ // ensure that setDataAt works the same way
+ dt.mozSetDataAt("text/uri-list", "#http://www.mozilla.org\n#Sample\nhttp://www.xulplanet.com \r\n", 0);
+ checkURL(dt, "http://www.xulplanet.com",
+ "#http://www.mozilla.org\n#Sample\nhttp://www.xulplanet.com \r\n",
+ 0, "uri-list mix setDataAt");
+
+ // now test adding multiple items to be dragged using the setDataAt method
+ dt.clearData();
+ dt.mozSetDataAt("text/plain", "First Item", 0);
+ dt.mozSetDataAt("text/plain", "Second Item", 1);
+ expectError(function() dt.mozSetDataAt("text/plain", "Some Text", 3),
+ "NS_ERROR_DOM_INDEX_SIZE_ERR", "setDataAt index too high with two items");
+ is(dt.mozItemCount, 2, "setDataAt item itemCount");
+ checkOneDataItem(dt, ["text/plain"], ["First Item"], 0, "setDataAt item at index 0");
+ checkOneDataItem(dt, ["text/plain"], ["Second Item"], 1, "setDataAt item at index 1");
+
+ dt.mozSetDataAt("text/html", "<em>First Item</em>", 0);
+ dt.mozSetDataAt("text/html", "<em>Second Item</em>", 1);
+ is(dt.mozItemCount, 2, "setDataAt two types item itemCount");
+ checkOneDataItem(dt, ["text/plain", "text/html"],
+ ["First Item", "<em>First Item</em>"], 0, "setDataAt two types item at index 0");
+ checkOneDataItem(dt, ["text/plain", "text/html"],
+ ["Second Item", "<em>Second Item</em>"], 1, "setDataAt two types item at index 1");
+
+ dt.mozSetDataAt("text/html", "<em>Changed First Item</em>", 0);
+ dt.mozSetDataAt("text/plain", "Changed Second Item", 1);
+ is(dt.mozItemCount, 2, "changed with setDataAt item itemCount");
+ checkOneDataItem(dt, ["text/plain", "text/html"],
+ ["First Item", "<em>Changed First Item</em>"], 0, "changed with setDataAt item at index 0");
+ checkOneDataItem(dt, ["text/plain", "text/html"],
+ ["Changed Second Item", "<em>Second Item</em>"], 1, "changed with setDataAt item at index 1");
+
+ dt.setData("text/html", "Changed with setData");
+ is(dt.mozItemCount, 2, "changed with setData");
+ checkOneDataItem(dt, ["text/plain", "text/html"],
+ ["First Item", "Changed with setData"], 0, "changed with setData item at index 0");
+ checkOneDataItem(dt, ["text/plain", "text/html"],
+ ["Changed Second Item", "<em>Second Item</em>"], 1, "changed with setData item at index 1");
+
+ dt.mozSetDataAt("application/-moz-node", draggable, 2);
+ is(dt.mozItemCount, 3, "setDataAt node itemCount");
+ checkOneDataItem(dt, ["application/-moz-node"], [draggable], 2, "setDataAt node item at index 2");
+
+ dt.mozClearDataAt("text/html", 1);
+ is(dt.mozItemCount, 3, "clearDataAt itemCount");
+ checkOneDataItem(dt, ["text/plain", "text/html"],
+ ["First Item", "Changed with setData"], 0, "clearDataAt item at index 0");
+ checkOneDataItem(dt, ["text/plain"], ["Changed Second Item"], 1, "clearDataAt item at index 1");
+
+ dt.mozClearDataAt("text/plain", 1);
+ is(dt.mozItemCount, 2, "clearDataAt last type itemCount");
+ checkOneDataItem(dt, ["text/plain", "text/html"],
+ ["First Item", "Changed with setData"], 0, "clearDataAt last type at index 0");
+ checkOneDataItem(dt, ["application/-moz-node"], [draggable], 1, "clearDataAt last type item at index 2");
+ expectError(function() dt.mozGetDataAt("text/plain", 2),
+ "NS_ERROR_DOM_INDEX_SIZE_ERR", "getDataAt after item removed index too high");
+
+ dt.mozSetDataAt("text/unknown", "Unknown type", 2);
+ dt.mozSetDataAt("text/unknown", "Unknown type", 1);
+ is(dt.mozItemCount, 3, "add unknown type");
+ checkOneDataItem(dt, ["application/-moz-node", "text/unknown"],
+ [draggable, "Unknown type"], 1, "add unknown type item at index 1");
+ checkOneDataItem(dt, ["text/unknown"], ["Unknown type"], 2, "add unknown type item at index 2");
+
+ dt.mozClearDataAt("", 1);
+ is(dt.mozItemCount, 2, "clearDataAt empty string");
+ checkOneDataItem(dt, ["text/plain", "text/html"],
+ ["First Item", "Changed with setData"], 0, "clearDataAt empty string item at index 0");
+ checkOneDataItem(dt, ["text/unknown"],
+ ["Unknown type"], 1, "clearDataAt empty string item at index 1");
+
+ // passing a format that doesn't exist to clearData or clearDataAt should just
+ // do nothing
+ dt.clearData("text/something");
+ dt.mozClearDataAt("text/something", 1);
+ is(dt.mozItemCount, 2, "clearData type that does not exist");
+ checkOneDataItem(dt, ["text/plain", "text/html"],
+ ["First Item", "Changed with setData"], 0, "clearData type that does not exist item at index 0");
+ checkOneDataItem(dt, ["text/unknown"],
+ ["Unknown type"], 1, "clearData type that does not exist item at index 1");
+
+ expectError(function() dt.mozClearDataAt("text/plain", 3),
+ "NS_ERROR_DOM_INDEX_SIZE_ERR", "clearData index too high with two items");
+
+ // ensure that clearData() removes all data associated with the first item
+ dt.clearData();
+ is(dt.mozItemCount, 1, "clearData no argument with multiple items itemCount");
+ checkOneDataItem(dt, ["text/unknown"],
+ ["Unknown type"], 0, "clearData no argument with multiple items item at index 1");
+
+ // remove tha remaining data
+ dt.mozClearDataAt("", 0);
+ is(dt.mozItemCount, 0, "all data cleared");
+
+ // now check the effectAllowed and dropEffect properties
+ is(dt.dropEffect, "none", "initial dropEffect");
+ is(dt.effectAllowed, "uninitialized", "initial effectAllowed");
+
+ ["copy", "none", "link", "", "other", "copyMove", "all", "uninitialized", "move"].forEach(
+ function (i) {
+ dt.dropEffect = i;
+ is(dt.dropEffect, i == "" || i == "other" || i == "copyMove" ||
+ i == "all" || i == "uninitialized" ? "link" : i,
+ "dropEffect set to " + i);
+ is(dt.effectAllowed, "uninitialized", "effectAllowed not modified by dropEffect set to " + i);
+ }
+ );
+
+ ["move", "copy", "link", "", "other", "moveCopy", "copyMove",
+ "linkMove", "copyLink", "all", "uninitialized", "none"].forEach(
+ function (i) {
+ dt.effectAllowed = i;
+ is(dt.dropEffect, "move", "dropEffect not modified by effectAllowed set to " + i);
+ is(dt.effectAllowed, i == "" || i == "other" || i == "moveCopy" ? "link" : i,
+ "effectAllowed set to " + i);
+ }
+ );
+}
+
+function doDragStartLink(event)
+{
+ var dt = event.dataTransfer;
+ checkTypes(dt, ["text/x-moz-url", "text/x-moz-url-data", "text/x-moz-url-desc", "text/uri-list",
+ "text/_moz_htmlcontext", "text/_moz_htmlinfo", "text/html", "text/plain"], 0, "initial link");
+
+ is(dt.mozItemCount, 1, "initial link item count");
+ is(dt.getData("text/uri-list"), "http://www.mozilla.org/", "link text/uri-list");
+ is(dt.getData("text/plain"), "http://www.mozilla.org/", "link text/plain");
+
+ event.preventDefault();
+}
+
+function doDragStartImage(event)
+{
+ var dataurl = $("image").src;
+
+ var dt = event.dataTransfer;
+ checkTypes(dt, ["text/x-moz-url", "text/x-moz-url-data", "text/x-moz-url-desc", "text/uri-list",
+ "text/_moz_htmlcontext", "text/_moz_htmlinfo", "text/html", "text/plain"], 0, "initial image");
+
+ is(dt.mozItemCount, 1, "initial image item count");
+ is(dt.getData("text/uri-list"), dataurl, "image text/uri-list");
+ is(dt.getData("text/plain"), dataurl, "image text/plain");
+
+ event.preventDefault();
+}
+
+function doDragStartInput(event)
+{
+ var dt = event.dataTransfer;
+ checkTypes(dt, ["text/plain"], 0, "initial input");
+
+ is(dt.mozItemCount, 1, "initial input item count");
+// is(dt.getData("text/plain"), "Text", "input text/plain");
+
+// event.preventDefault();
+}
+
+function doDragStartSynthetic(event)
+{
+ is(event.type, "dragstart", "synthetic dragstart event type");
+return;
+ var dt = event.dataTransfer;
+ ok(dt instanceof DataTransfer, "synthetic dragstart dataTransfer is DataTransfer");
+
+ checkTypes(dt, [], 0, "synthetic dragstart");
+
+ is(event.detail, 1, "synthetic dragstart detail");
+
+ dt.setData("text/plain", "Text");
+ is(dt.getData("text/plain"), "Text", "synthetic dragstart data is set after adding");
+}
+
+function doDragOverSynthetic(event)
+{
+ is(event.type, "dragover", "synthetic dragover event type");
+return;
+
+ var dt = event.dataTransfer;
+ ok(dt instanceof DataTransfer, "synthetic dragover dataTransfer is DataTransfer");
+
+ checkTypes(dt, [], 0, "synthetic dragover");
+
+ dt.setData("text/plain", "Text");
+ is(dt.getData("text/plain"), "Text", "synthetic dragover data is set after adding");
+}
+
+function onDragStartDraggable(event)
+{
+ var dt = event.dataTransfer;
+ gDragInfo = { itemCount: dt.mozItemCount, types: dt.types, target: event.originalTarget };
+}
+
+function checkOneDataItem(dt, expectedtypes, expecteddata, index, testid)
+{
+ checkTypes(dt, expectedtypes, index, testid);
+ for (var f = 0; f < expectedtypes.length; f++) {
+ if (index == 0)
+ is(dt.getData(expectedtypes[f]), expecteddata[f], testid + " getData " + expectedtypes[f]);
+ is(dt.mozGetDataAt(expectedtypes[f], index), expecteddata[f] ? expecteddata[f] : null,
+ testid + " getDataAt " + expectedtypes[f]);
+ }
+}
+
+function checkTypes(dt, expectedtypes, index, testid)
+{
+ if (index == 0) {
+ var types = dt.types;
+ is(types.length, expectedtypes.length, testid + " types length");
+ for (var f = 0; f < expectedtypes.length; f++) {
+ is(types[f], expectedtypes[f], testid + " " + types[f] + " check");
+ }
+ }
+
+ types = dt.mozTypesAt(index);
+ is(types.length, expectedtypes.length, testid + " typesAt length");
+ for (var f = 0; f < expectedtypes.length; f++) {
+ is(types[f], expectedtypes[f], testid + " " + types[f] + " at " + index + " check");
+ }
+}
+
+function checkURL(dt, url, fullurllist, index, testid)
+{
+ is(dt.getData("text/uri-list"), fullurllist, testid + " text/uri-list");
+ is(dt.getData("URL"), url, testid + " URL");
+ is(dt.mozGetDataAt("text/uri-list", 0), fullurllist, testid + " text/uri-list");
+ is(dt.mozGetDataAt("URL", 0), fullurllist, testid + " URL");
+}
+
+function expectError(fn, eid, testid)
+{
+ var error = "";
+ try {
+ fn();
+ } catch (ex) {
+ error = ex.name;
+ }
+ is(error, eid, testid + " causes exception " + eid);
+}
+
+</script>
+
+</head>
+
+<body style="height: 300px; overflow: auto;" onload="setTimeout(runTests, 0)">
+
+<div id="draggable" ondragstart="doDragStartSelection(event)">This is a <em>draggable</em> bit of text.</div>
+
+<a id="link" href="http://www.mozilla.org/" ondragstart="doDragStartLink(event)">mozilla.org</a>
+
+<img id="image" src="data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%18%00%00%00%18%02%03%00%00%00%9D%19%D5k%00%00%00%04gAMA%00%00%B1%8F%0B%FCa%05%00%00%00%0CPLTE%FF%FF%FF%FF%FF%FF%F7%DC%13%00%00%00%03%80%01X%00%00%00%01tRNS%08N%3DPT%00%00%00%01bKGD%00%88%05%1DH%00%00%00%09pHYs%00%00%0B%11%00%00%0B%11%01%7Fd_%91%00%00%00%07tIME%07%D2%05%0C%14%0C%0D%D8%3F%1FQ%00%00%00%5CIDATx%9C%7D%8E%CB%09%C0%20%10D%07r%B7%20%2F%E9wV0%15h%EA%D9%12D4%BB%C1x%CC%5C%1E%0C%CC%07%C0%9C0%9Dd7()%C0A%D3%8D%E0%B8%10%1DiCHM%D0%AC%D2d%C3M%F1%B4%E7%FF%10%0BY%AC%25%93%CD%CBF%B5%B2%C0%3Alh%CD%AE%13%DF%A5%F7%E0%03byW%09A%B4%F3%E2%00%00%00%00IEND%AEB%60%82"
+ ondragstart="doDragStartImage(event)">
+
+<input id="input" value="Text in a box" ondragstart="doDragStartInput(event)">
+
+<div style="-moz-user-select: none;" ondragstart="onDragStartDraggable(event)">
+ <div id="dragtrue" draggable="true">
+ This is a <span id="spantrue">draggable</span> area.
+ </div>
+ <div id="dragfalse" draggable="false">
+ This is a <span id="spanfalse">non-draggable</span> area.
+ </div>
+</div>
+
+<!--iframe src="http://www.mozilla.org" width="400" height="400"></iframe-->
+
+<div id="synthetic" ondragstart="doDragStartSynthetic(event)">Synthetic Event Dispatch</div>
+<div id="synthetic2" ondragover="doDragOverSynthetic(event)">Synthetic Event Dispatch</div>
+
+</body>
+</html>
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -851,16 +851,32 @@ nsGenericHTMLElement::SetSpellcheck(PRBo
{
if (aSpellcheck) {
return SetAttrHelper(nsGkAtoms::spellcheck, NS_LITERAL_STRING("true"));
}
return SetAttrHelper(nsGkAtoms::spellcheck, NS_LITERAL_STRING("false"));
}
+NS_IMETHODIMP
+nsGenericHTMLElement::GetDraggable(PRBool* aDraggable)
+{
+ *aDraggable = AttrValueIs(kNameSpaceID_None, nsGkAtoms::draggable,
+ nsGkAtoms::_true, eIgnoreCase);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGenericHTMLElement::SetDraggable(PRBool aDraggable)
+{
+ return SetAttrHelper(nsGkAtoms::draggable,
+ aDraggable ? NS_LITERAL_STRING("true") :
+ NS_LITERAL_STRING("false"));
+}
+
PRBool
nsGenericHTMLElement::InNavQuirksMode(nsIDocument* aDoc)
{
return aDoc && aDoc->GetCompatibilityMode() == eCompatibility_NavQuirks;
}
void
nsGenericHTMLElement::UpdateEditableState()
--- a/content/html/content/src/nsGenericHTMLElement.h
+++ b/content/html/content/src/nsGenericHTMLElement.h
@@ -140,25 +140,27 @@ public:
nsresult GetOffsetTop(PRInt32* aOffsetTop);
nsresult GetOffsetLeft(PRInt32* aOffsetLeft);
nsresult GetOffsetWidth(PRInt32* aOffsetWidth);
nsresult GetOffsetHeight(PRInt32* aOffsetHeight);
nsresult GetOffsetParent(nsIDOMElement** aOffsetParent);
virtual nsresult GetInnerHTML(nsAString& aInnerHTML);
virtual nsresult SetInnerHTML(const nsAString& aInnerHTML);
nsresult ScrollIntoView(PRBool aTop);
- // Declare Focus(), Blur(), GetTabIndex(), SetTabIndex(), GetSpellcheck() and
- // SetSpellcheck() such that classes that inherit interfaces with those
- // methods properly override them
+ // Declare Focus(), Blur(), GetTabIndex(), SetTabIndex(), GetSpellcheck(),
+ // SetSpellcheck(), and GetDraggable() such that classes that inherit interfaces
+ // with those methods properly override them
NS_IMETHOD Focus();
NS_IMETHOD Blur();
NS_IMETHOD GetTabIndex(PRInt32 *aTabIndex);
NS_IMETHOD SetTabIndex(PRInt32 aTabIndex);
NS_IMETHOD GetSpellcheck(PRBool* aSpellcheck);
NS_IMETHOD SetSpellcheck(PRBool aSpellcheck);
+ NS_IMETHOD GetDraggable(PRBool* aDraggable);
+ NS_IMETHOD SetDraggable(PRBool aDraggable);
nsresult GetContentEditable(nsAString &aContentEditable);
nsresult SetContentEditable(const nsAString &aContentEditable);
// Implementation for nsIContent
virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent,
PRBool aCompileEventHandlers);
virtual void UnbindFromTree(PRBool aDeep = PR_TRUE,
--- a/content/html/content/src/nsHTMLAnchorElement.cpp
+++ b/content/html/content/src/nsHTMLAnchorElement.cpp
@@ -98,16 +98,19 @@ public:
// nsILink
NS_IMETHOD GetLinkState(nsLinkState &aState);
NS_IMETHOD SetLinkState(nsLinkState aState);
NS_IMETHOD GetHrefURI(nsIURI** aURI);
NS_IMETHOD LinkAdded() { return NS_OK; }
NS_IMETHOD LinkRemoved() { return NS_OK; }
+ // override from nsGenericHTMLElement
+ NS_IMETHOD GetDraggable(PRBool* aDraggable);
+
virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent,
PRBool aCompileEventHandlers);
virtual void UnbindFromTree(PRBool aDeep = PR_TRUE,
PRBool aNullParent = PR_TRUE);
virtual void SetFocus(nsPresContext* aPresContext);
virtual PRBool IsHTMLFocusable(PRBool *aIsFocusable, PRInt32 *aTabIndex);
@@ -173,16 +176,30 @@ NS_IMPL_STRING_ATTR(nsHTMLAnchorElement,
NS_IMPL_STRING_ATTR(nsHTMLAnchorElement, Name, name)
NS_IMPL_STRING_ATTR(nsHTMLAnchorElement, Rel, rel)
NS_IMPL_STRING_ATTR(nsHTMLAnchorElement, Rev, rev)
NS_IMPL_STRING_ATTR(nsHTMLAnchorElement, Shape, shape)
NS_IMPL_INT_ATTR_DEFAULT_VALUE(nsHTMLAnchorElement, TabIndex, tabindex, 0)
NS_IMPL_STRING_ATTR(nsHTMLAnchorElement, Type, type)
NS_IMPL_STRING_ATTR(nsHTMLAnchorElement, AccessKey, accesskey)
+NS_IMETHODIMP
+nsHTMLAnchorElement::GetDraggable(PRBool* aDraggable)
+{
+ // links can be dragged as long as there is an href and the
+ // draggable attribute isn't false
+ if (HasAttr(kNameSpaceID_None, nsGkAtoms::href)) {
+ *aDraggable = !AttrValueIs(kNameSpaceID_None, nsGkAtoms::draggable,
+ nsGkAtoms::_false, eIgnoreCase);
+ return NS_OK;
+ }
+
+ // no href, so just use the same behavior as other elements
+ return nsGenericHTMLElement::GetDraggable(aDraggable);
+}
nsresult
nsHTMLAnchorElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent,
PRBool aCompileEventHandlers)
{
nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
aBindingParent,
--- a/content/html/content/src/nsHTMLImageElement.cpp
+++ b/content/html/content/src/nsHTMLImageElement.cpp
@@ -102,16 +102,19 @@ public:
NS_FORWARD_NSIDOMHTMLELEMENT(nsGenericHTMLElement::)
// nsIDOMHTMLImageElement
NS_DECL_NSIDOMHTMLIMAGEELEMENT
// nsIDOMNSHTMLImageElement
NS_DECL_NSIDOMNSHTMLIMAGEELEMENT
+ // override from nsGenericHTMLElement
+ NS_IMETHOD GetDraggable(PRBool* aDraggable);
+
// nsIJSNativeInitializer
NS_IMETHOD Initialize(nsISupports* aOwner, JSContext* aContext,
JSObject* aObj, PRUint32 argc, jsval* argv);
// nsIContent
virtual PRBool ParseAttribute(PRInt32 aNamespaceID,
nsIAtom* aAttribute,
const nsAString& aValue,
@@ -213,16 +216,25 @@ NS_IMPL_INT_ATTR(nsHTMLImageElement, Hsp
NS_IMPL_BOOL_ATTR(nsHTMLImageElement, IsMap, ismap)
NS_IMPL_URI_ATTR(nsHTMLImageElement, LongDesc, longdesc)
NS_IMPL_STRING_ATTR(nsHTMLImageElement, Lowsrc, lowsrc)
NS_IMPL_URI_ATTR(nsHTMLImageElement, Src, src)
NS_IMPL_STRING_ATTR(nsHTMLImageElement, UseMap, usemap)
NS_IMPL_INT_ATTR(nsHTMLImageElement, Vspace, vspace)
NS_IMETHODIMP
+nsHTMLImageElement::GetDraggable(PRBool* aDraggable)
+{
+ // images may be dragged unless the draggable attribute is false
+ *aDraggable = !AttrValueIs(kNameSpaceID_None, nsGkAtoms::draggable,
+ nsGkAtoms::_false, eIgnoreCase);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
nsHTMLImageElement::GetComplete(PRBool* aComplete)
{
NS_PRECONDITION(aComplete, "Null out param!");
*aComplete = PR_TRUE;
if (!mCurrentRequest) {
return NS_OK;
}
--- a/dom/public/coreEvents/nsIDOMDragListener.h
+++ b/dom/public/coreEvents/nsIDOMDragListener.h
@@ -39,21 +39,24 @@
#ifndef nsIDOMDragListener_h__
#define nsIDOMDragListener_h__
#include "nsIDOMEvent.h"
#include "nsIDOMEventListener.h"
/*
* The listener for drag events.
+ *
+ * The reason for two events for the same operation are for compatibility
+ * between the WHAT-WG drag and drop spec and existing XUL code.
*/
#define NS_IDOMDRAGLISTENER_IID \
-{ /* CD5186C4-228F-4413-AFD9-B65DAA105714 */ \
-0xcd5186c4, 0x228f, 0x4413, \
-{0xaf, 0xd9, 0xb6, 0x5d, 0xaa, 0x10, 0x57, 0x14} }
+{ /* 1A107271-1E26-419A-BCF1-0A4CF7A66B45 */ \
+0x1a107271, 0x1e26, 0x419a, \
+{0xbc, 0xf1, 0x0a, 0x4c, 0xf7, 0xa6, 0x6b, 0x45} }
class nsIDOMDragListener : public nsIDOMEventListener {
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_IDOMDRAGLISTENER_IID)
@@ -124,13 +127,18 @@ public:
/**
* The drag event is fired just before a dragover event is fired. The target
* of this event is the source node of the drag.
*
* @param aMouseEvent @see nsIDOMEvent.h
* @returns whether the event was consumed or ignored. @see nsresult
*/
NS_IMETHOD Drag(nsIDOMEvent* aMouseEvent) = 0;
+
+ // these methods are for compatibility
+ NS_IMETHOD DragStart(nsIDOMEvent* aMouseEvent) { return NS_OK; }
+ NS_IMETHOD DragLeave(nsIDOMEvent* aMouseEvent) { return NS_OK; }
+ NS_IMETHOD Drop(nsIDOMEvent* aMouseEvent) { return NS_OK; }
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsIDOMDragListener, NS_IDOMDRAGLISTENER_IID)
#endif // nsIDOMDragListener_h__
--- a/dom/public/idl/events/Makefile.in
+++ b/dom/public/idl/events/Makefile.in
@@ -62,16 +62,18 @@ SDK_XPIDLSRCS =
$(NULL)
XPIDLSRCS = \
nsIDOMNSEvent.idl \
nsIDOMDataContainerEvent.idl \
nsIDOMKeyEvent.idl \
nsIDOMMutationEvent.idl \
nsIDOMNSUIEvent.idl \
+ nsIDOMDragEvent.idl \
+ nsIDOMDataTransfer.idl \
nsIDOMPopupBlockedEvent.idl \
nsIDOMBeforeUnloadEvent.idl \
nsIDOMNSEventTarget.idl \
nsIDOMSmartCardEvent.idl \
nsIDOMPageTransitionEvent.idl \
nsIDOMCommandEvent.idl \
nsIDOMMessageEvent.idl \
$(NULL)
new file mode 100644
--- /dev/null
+++ b/dom/public/idl/events/nsIDOMDataTransfer.idl
@@ -0,0 +1,237 @@
+/* -*- 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 the Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Neil Deakin <enndeakin@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of 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 ***** */
+
+#include "domstubs.idl"
+
+interface nsIVariant;
+
+[scriptable, uuid(B5947DD0-8E86-4B9C-AA65-C86303EFCF94)]
+interface nsIDOMDataTransfer : nsISupports
+{
+ /**
+ * The actual effect that will be used, and should always be one of the
+ * possible values of effectAllowed.
+ *
+ * For dragstart, drag and dragleave events, the dropEffect is initialized
+ * to none. Any value assigned to the dropEffect will be set, but the value
+ * isn't used for anything.
+ *
+ * For the dragenter and dragover events, the dropEffect will be initialized
+ * based on what action the user is requesting. How this is determined is
+ * platform specific, but typically the user can press modifier keys to
+ * adjust which action is desired. Within an event handler for the dragenter
+ * and dragover events, the dropEffect should be modified if the action the
+ * user is requesting is not the one that is desired.
+ *
+ * For the drop and dragend events, the dropEffect will be initialized to
+ * the action that was desired, which will be the value that the dropEffect
+ * had after the last dragenter or dragover event.
+ *
+ * Possible values:
+ * copy - a copy of the source item is made at the new location
+ * move - an item is moved to a new location
+ * link - a link is established to the source at the new location
+ * none - the item may not be dropped
+ *
+ * Assigning any other value has no effect and retains the old value.
+ */
+ attribute DOMString dropEffect;
+
+ /*
+ * Specifies the effects that are allowed for this drag. You may set this in
+ * the dragstart event to set the desired effects for the source, and within
+ * the dragenter and dragover events to set the desired effects for the
+ * target. The value is not used for other events.
+ *
+ * Possible values:
+ * copy - a copy of the source item is made at the new location
+ * move - an item is moved to a new location
+ * link - a link is established to the source at the new location
+ * copyLink, copyMove, linkMove, all - combinations of the above
+ * none - the item may not be dropped
+ * uninitialized - the default value when the effect has not been set,
+ * equivalent to all.
+ *
+ * Assigning any other value has no effect and retains the old value.
+ */
+ attribute DOMString effectAllowed;
+
+ /**
+ * Holds a list of the format types of the data that is stored for the first
+ * item, in the same order the data was added. An empty list will be
+ * returned if no data was added.
+ */
+ readonly attribute nsIDOMDOMStringList types;
+
+ /**
+ * Remove the data associated with a given format. If format is empty or not
+ * specified, the data associated with all formats is removed. If data for
+ * the specified format does not exist, or the data transfer contains no
+ * data, this method will have no effect.
+ */
+ void clearData([optional] in DOMString format);
+
+ /**
+ * Set the data for a given format. If data for the format does not exist,
+ * it is added at the end, such that the last item in the types list will be
+ * the new format. If data for the format already exists, the existing data
+ * is replaced in the same position. That is, the order of the types list is
+ * not changed.
+ *
+ * @throws NS_ERROR_NULL_POINTER if the data is null
+ */
+ void setData(in DOMString format, in DOMString data);
+
+ /**
+ * Retrieves the data for a given format, or an empty string if data for
+ * that format does not exist or the data transfer contains no data.
+ */
+ DOMString getData(in DOMString format);
+
+ /**
+ * Set the image to be used for dragging if a custom one is desired. Most of
+ * the time, this would not be set, as a default image is created from the
+ * node that was dragged.
+ *
+ * If the node is an HTML img element, an HTML canvas element or a XUL image
+ * element, the image data is used. Otherwise, image should be a visible
+ * node and the drag image will be created from this. If image is null, any
+ * custom drag image is cleared and the default is used instead.
+ *
+ * The coordinates specify the offset into the image where the mouse cursor
+ * should be. To center the image for instance, use values that are half the
+ * width and height.
+ *
+ * @param image a node to use
+ * @param x the horizontal offset
+ * @param y the vertical offset
+ * @throws NO_MODIFICATION_ALLOWED_ERR if the item cannot be modified
+ */
+ void setDragImage(in nsIDOMElement image, in long x, in long y);
+
+ /*
+ * Set the drag source. Usually you would not change this, but it will
+ * affect which node the drag and dragend events are fired at. The
+ * default target is the node that was dragged.
+ *
+ * @param element drag source to use
+ * @throws NO_MODIFICATION_ALLOWED_ERR if the item cannot be modified
+ */
+ void addElement(in nsIDOMElement element);
+};
+
+[scriptable, uuid(A884E56C-1678-4978-AD20-142EE94108F5)]
+interface nsIDOMNSDataTransfer : nsISupports
+{
+ /*
+ * Integer version of dropEffect, set to one of the constants in nsIDragService.
+ */
+ [noscript] attribute unsigned long dropEffectInt;
+
+ /*
+ * Integer version of effectAllowed, set to one or a combination of the
+ * constants in nsIDragService.
+ */
+ [noscript] attribute unsigned long effectAllowedInt;
+
+ /**
+ * Creates a copy of the data transfer object
+ */
+ [noscript] nsIDOMDataTransfer clone(in PRUint32 aEventType);
+
+ /**
+ * The number of items being dragged.
+ */
+ readonly attribute unsigned long mozItemCount;
+
+ /**
+ * Holds a list of the format types of the data that is stored for an item
+ * at the specified index. If the index is not in the range from 0 to
+ * itemCount - 1, an empty string list is returned.
+ */
+ nsIDOMDOMStringList mozTypesAt(in unsigned long index);
+
+ /**
+ * Remove the data associated with the given format for an item at the
+ * specified index. The index is in the range from zero to itemCount - 1.
+ *
+ * If the last format for the item is removed, the entire item is removed,
+ * reducing the itemCount by one.
+ *
+ * If format is empty, then the data associated with all formats is removed.
+ * If the format is not found, then this method has no effect.
+ *
+ * @param format the format to remove
+ * @throws NS_ERROR_DOM_INDEX_SIZE_ERR if index is greater or equal than itemCount
+ * @throws NO_MODIFICATION_ALLOWED_ERR if the item cannot be modified
+ */
+ void mozClearDataAt(in DOMString format, in unsigned long index);
+
+ /*
+ * A data transfer may store multiple items, each at a given zero-based
+ * index. setDataAt may only be called with an index argument less than
+ * itemCount in which case an existing item is modified, or equal to
+ * itemCount in which case a new item is added, and the itemCount is
+ * incremented by one.
+ *
+ * Data should be added in order of preference, with the most specific
+ * format added first and the least specific format added last. If data of
+ * the given format already exists, it is replaced in the same position as
+ * the old data.
+ *
+ * The data should be either a string, a primitive boolean or number type
+ * (which will be converted into a string) or an nsISupports.
+ *
+ * @param format the format to add
+ * @param data the data to add
+ * @throws NS_ERROR_NULL_POINTER if the data is null
+ * @throws NS_ERROR_DOM_INDEX_SIZE_ERR if index is greater than itemCount
+ * @throws NO_MODIFICATION_ALLOWED_ERR if the item cannot be modified
+ */
+ void mozSetDataAt(in DOMString format, in nsIVariant data, in unsigned long index);
+
+ /**
+ * Retrieve the data associated with the given format for an item at the
+ * specified index, or null if it does not exist. The index should be in the
+ * range from zero to itemCount - 1.
+ *
+ * @param format the format of the data to look up
+ * @returns the data of the given format, or null if it doesn't exist.
+ * @throws NS_ERROR_DOM_INDEX_SIZE_ERR if index is greater or equal than itemCount
+ */
+ nsIVariant mozGetDataAt(in DOMString format, in unsigned long index);
+};
new file mode 100644
--- /dev/null
+++ b/dom/public/idl/events/nsIDOMDragEvent.idl
@@ -0,0 +1,63 @@
+/* -*- 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 the Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Neil Deakin <enndeakin@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of 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 ***** */
+
+#include "domstubs.idl"
+#include "nsIDOMMouseEvent.idl"
+
+interface nsIDOMAbstractView;
+interface nsIDOMDataTransfer;
+
+[scriptable, uuid(18FEEFD7-A461-4865-BCF1-4DC8A2F30584)]
+interface nsIDOMDragEvent : nsIDOMMouseEvent
+{
+ readonly attribute nsIDOMDataTransfer dataTransfer;
+
+ void initDragEvent(in DOMString typeArg,
+ in boolean canBubbleArg,
+ in boolean cancelableArg,
+ in nsIDOMAbstractView aView,
+ in PRInt32 aDetail,
+ in nsIDOMDataTransfer aDataTransfer);
+
+ void initDragEventNS(in DOMString namespaceURIArg,
+ in DOMString typeArg,
+ in boolean canBubbleArg,
+ in boolean cancelableArg,
+ in nsIDOMAbstractView aView,
+ in PRInt32 aDetail,
+ in nsIDOMDataTransfer aDataTransfer);
+};
--- a/dom/public/idl/html/nsIDOMNSHTMLElement.idl
+++ b/dom/public/idl/html/nsIDOMNSHTMLElement.idl
@@ -33,30 +33,33 @@
* 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 ***** */
#include "domstubs.idl"
-[scriptable, uuid(5D090306-86AA-43F8-A78F-7346084DA4C5)]
+[scriptable, uuid(7F142F9A-FBA7-4949-93D6-CF08A974AC51)]
interface nsIDOMNSHTMLElement : nsISupports
{
readonly attribute long offsetTop;
readonly attribute long offsetLeft;
readonly attribute long offsetWidth;
readonly attribute long offsetHeight;
readonly attribute nsIDOMElement offsetParent;
attribute DOMString innerHTML;
attribute long tabIndex;
attribute DOMString contentEditable;
+ // for WHAT-WG drag and drop
+ attribute boolean draggable;
+
void blur();
void focus();
// |top| is optional in JS, scriptability of this method is done in
// nsHTMLElementSH
void scrollIntoView(in boolean top);
attribute boolean spellcheck;
--- a/dom/public/nsDOMClassInfoID.h
+++ b/dom/public/nsDOMClassInfoID.h
@@ -79,16 +79,17 @@ enum nsDOMClassInfoID {
eDOMClassInfo_DocumentStyleSheetList_id,
// Event classes
eDOMClassInfo_Event_id,
eDOMClassInfo_MutationEvent_id,
eDOMClassInfo_UIEvent_id,
eDOMClassInfo_MouseEvent_id,
eDOMClassInfo_MouseScrollEvent_id,
+ eDOMClassInfo_DragEvent_id,
eDOMClassInfo_KeyboardEvent_id,
eDOMClassInfo_PopupBlockedEvent_id,
// HTML classes
eDOMClassInfo_HTMLDocument_id,
eDOMClassInfo_HTMLOptionsCollection_id,
eDOMClassInfo_HTMLFormControlCollection_id,
eDOMClassInfo_HTMLGenericCollection_id,
@@ -443,16 +444,18 @@ enum nsDOMClassInfoID {
#endif
eDOMClassInfo_ProgressEvent_id,
eDOMClassInfo_XMLHttpRequestUpload_id,
// DOM Traversal NodeIterator class
eDOMClassInfo_NodeIterator_id,
+ eDOMClassInfo_DataTransfer_id,
+
// This one better be the last one in this list
eDOMClassInfoIDCount
};
/**
* nsIClassInfo helper macros
*/
--- a/dom/src/base/nsDOMClassInfo.cpp
+++ b/dom/src/base/nsDOMClassInfo.cpp
@@ -230,16 +230,17 @@
#include "nsIDOMCDATASection.h"
#include "nsIDOMProcessingInstruction.h"
#include "nsIDOMNotation.h"
#include "nsIDOMNSEvent.h"
#include "nsIDOMDataContainerEvent.h"
#include "nsIDOMKeyEvent.h"
#include "nsIDOMMouseEvent.h"
#include "nsIDOMMouseScrollEvent.h"
+#include "nsIDOMDragEvent.h"
#include "nsIDOMCommandEvent.h"
#include "nsIDOMPopupBlockedEvent.h"
#include "nsIDOMBeforeUnloadEvent.h"
#include "nsIDOMMutationEvent.h"
#include "nsIDOMSmartCardEvent.h"
#include "nsIDOMXULCommandEvent.h"
#include "nsIDOMPageTransitionEvent.h"
#include "nsIDOMMessageEvent.h"
@@ -444,16 +445,19 @@
// Storage includes
#include "nsIDOMStorage.h"
#include "nsPIDOMStorage.h"
#include "nsIDOMStorageList.h"
#include "nsIDOMStorageItem.h"
#include "nsIDOMStorageEvent.h"
#include "nsIDOMToString.h"
+// Drag and drop
+#include "nsIDOMDataTransfer.h"
+
// Offline includes
#include "nsIDOMLoadStatusList.h"
#include "nsIDOMLoadStatus.h"
#include "nsIDOMLoadStatusEvent.h"
// Geolocation
#include "nsIDOMGeoGeolocation.h"
#include "nsIDOMGeoPosition.h"
@@ -648,16 +652,18 @@ static nsDOMClassInfoData sClassInfoData
NS_DEFINE_CLASSINFO_DATA(MutationEvent, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(UIEvent, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(MouseEvent, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(MouseScrollEvent, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
+ NS_DEFINE_CLASSINFO_DATA(DragEvent, nsDOMGenericSH,
+ DOM_DEFAULT_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(KeyboardEvent, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(PopupBlockedEvent, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
// Misc HTML classes
NS_DEFINE_CLASSINFO_DATA(HTMLDocument, nsHTMLDocumentSH,
DOCUMENT_SCRIPTABLE_FLAGS)
@@ -1276,16 +1282,21 @@ static nsDOMClassInfoData sClassInfoData
DOM_DEFAULT_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(XMLHttpRequestUpload, nsEventTargetSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
// DOM Traversal NodeIterator class
NS_DEFINE_CLASSINFO_DATA(NodeIterator, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
+
+ // data transfer for drag and drop
+ NS_DEFINE_CLASSINFO_DATA(DataTransfer, nsDOMGenericSH,
+ DOM_DEFAULT_SCRIPTABLE_FLAGS)
+
};
// Objects that shuld be constructable through |new Name();|
struct nsContractIDMapData
{
PRInt32 mDOMClassInfoID;
const char *mContractID;
};
@@ -2117,16 +2128,21 @@ nsDOMClassInfo::Init()
DOM_CLASSINFO_UI_EVENT_MAP_ENTRIES
DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN(MouseScrollEvent, nsIDOMMouseScrollEvent)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMMouseScrollEvent)
DOM_CLASSINFO_UI_EVENT_MAP_ENTRIES
DOM_CLASSINFO_MAP_END
+ DOM_CLASSINFO_MAP_BEGIN(DragEvent, nsIDOMDragEvent)
+ DOM_CLASSINFO_MAP_ENTRY(nsIDOMDragEvent)
+ DOM_CLASSINFO_UI_EVENT_MAP_ENTRIES
+ DOM_CLASSINFO_MAP_END
+
DOM_CLASSINFO_MAP_BEGIN(HTMLDocument, nsIDOMHTMLDocument)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMHTMLDocument)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMNSHTMLDocument)
DOM_CLASSINFO_DOCUMENT_MAP_ENTRIES
DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN(HTMLOptionsCollection, nsIDOMHTMLOptionsCollection)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMHTMLOptionsCollection)
@@ -3488,28 +3504,34 @@ nsDOMClassInfo::Init()
DOM_CLASSINFO_MAP_ENTRY(nsIDOMHTMLMediaError)
DOM_CLASSINFO_EVENT_MAP_ENTRIES
DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN(HTMLAudioElement, nsIDOMHTMLAudioElement)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMHTMLAudioElement)
DOM_CLASSINFO_GENERIC_HTML_MAP_ENTRIES
DOM_CLASSINFO_MAP_END
+
#endif
DOM_CLASSINFO_MAP_BEGIN(ProgressEvent, nsIDOMProgressEvent)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMProgressEvent)
DOM_CLASSINFO_EVENT_MAP_ENTRIES
DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN(XMLHttpRequestUpload, nsIXMLHttpRequestUpload)
DOM_CLASSINFO_MAP_ENTRY(nsIXMLHttpRequestEventTarget)
DOM_CLASSINFO_MAP_ENTRY(nsIXMLHttpRequestUpload)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
DOM_CLASSINFO_MAP_END
+ DOM_CLASSINFO_MAP_BEGIN(DataTransfer, nsIDOMDataTransfer)
+ DOM_CLASSINFO_MAP_ENTRY(nsIDOMDataTransfer)
+ DOM_CLASSINFO_MAP_ENTRY(nsIDOMNSDataTransfer)
+ DOM_CLASSINFO_MAP_END
+
#ifdef NS_DEBUG
{
PRUint32 i = NS_ARRAY_LENGTH(sClassInfoData);
if (i != eDOMClassInfoIDCount) {
NS_ERROR("The number of items in sClassInfoData doesn't match the "
"number of nsIDOMClassInfo ID's, this is bad! Fix it!");
--- a/editor/libeditor/base/nsEditorUtils.cpp
+++ b/editor/libeditor/base/nsEditorUtils.cpp
@@ -249,105 +249,16 @@ nsEditorHookUtils::GetHookEnumeratorFrom
nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(container);
nsCOMPtr<nsIClipboardDragDropHookList> hookObj = do_GetInterface(docShell);
if (!hookObj) return NS_ERROR_FAILURE;
return hookObj->GetHookEnumerator(aResult);
}
PRBool
-nsEditorHookUtils::DoAllowDragHook(nsIDOMDocument *aDoc, nsIDOMEvent *aDragEvent)
-{
- nsCOMPtr<nsISimpleEnumerator> enumerator;
- GetHookEnumeratorFromDocument(aDoc, getter_AddRefs(enumerator));
- if (!enumerator)
- return PR_TRUE;
-
- PRBool hasMoreHooks = PR_FALSE;
- while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMoreHooks)) && hasMoreHooks)
- {
- nsCOMPtr<nsISupports> isupp;
- if (NS_FAILED(enumerator->GetNext(getter_AddRefs(isupp))))
- break;
-
- nsCOMPtr<nsIClipboardDragDropHooks> override = do_QueryInterface(isupp);
- if (override)
- {
- PRBool canDrag = PR_TRUE;
- nsresult hookres = override->AllowStartDrag(aDragEvent, &canDrag);
- NS_ASSERTION(NS_SUCCEEDED(hookres), "hook failure in AllowStartDrag");
- if (!canDrag)
- return PR_FALSE;
- }
- }
-
- return PR_TRUE;
-}
-
-PRBool
-nsEditorHookUtils::DoDragHook(nsIDOMDocument *aDoc, nsIDOMEvent *aEvent,
- nsITransferable *aTrans)
-{
- nsCOMPtr<nsISimpleEnumerator> enumerator;
- GetHookEnumeratorFromDocument(aDoc, getter_AddRefs(enumerator));
- if (!enumerator)
- return PR_TRUE;
-
- PRBool hasMoreHooks = PR_FALSE;
- while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMoreHooks)) && hasMoreHooks)
- {
- nsCOMPtr<nsISupports> isupp;
- if (NS_FAILED(enumerator->GetNext(getter_AddRefs(isupp))))
- break;
-
- nsCOMPtr<nsIClipboardDragDropHooks> override = do_QueryInterface(isupp);
- if (override)
- {
- PRBool canInvokeDrag = PR_TRUE;
- nsresult hookResult = override->OnCopyOrDrag(aEvent, aTrans, &canInvokeDrag);
- NS_ASSERTION(NS_SUCCEEDED(hookResult), "hook failure in OnCopyOrDrag");
- if (!canInvokeDrag)
- return PR_FALSE;
- }
- }
-
- return PR_TRUE;
-}
-
-PRBool
-nsEditorHookUtils::DoAllowDropHook(nsIDOMDocument *aDoc, nsIDOMEvent *aEvent,
- nsIDragSession *aSession)
-{
- nsCOMPtr<nsISimpleEnumerator> enumerator;
- GetHookEnumeratorFromDocument(aDoc, getter_AddRefs(enumerator));
- if (!enumerator)
- return PR_TRUE;
-
- PRBool hasMoreHooks = PR_FALSE;
- while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMoreHooks)) && hasMoreHooks)
- {
- nsCOMPtr<nsISupports> isupp;
- if (NS_FAILED(enumerator->GetNext(getter_AddRefs(isupp))))
- break;
-
- nsCOMPtr<nsIClipboardDragDropHooks> override = do_QueryInterface(isupp);
- if (override)
- {
- PRBool allowDrop = PR_TRUE;
- nsresult hookResult = override->AllowDrop(aEvent, aSession, &allowDrop);
- NS_ASSERTION(NS_SUCCEEDED(hookResult), "hook failure in AllowDrop");
- if (!allowDrop)
- return PR_FALSE;
- }
- }
-
- return PR_TRUE;
-}
-
-PRBool
nsEditorHookUtils::DoInsertionHook(nsIDOMDocument *aDoc, nsIDOMEvent *aDropEvent,
nsITransferable *aTrans)
{
nsCOMPtr<nsISimpleEnumerator> enumerator;
GetHookEnumeratorFromDocument(aDoc, getter_AddRefs(enumerator));
if (!enumerator)
return PR_TRUE;
--- a/editor/libeditor/base/nsEditorUtils.h
+++ b/editor/libeditor/base/nsEditorUtils.h
@@ -269,21 +269,16 @@ class nsEditorUtils
class nsIDragSession;
class nsITransferable;
class nsIDOMEvent;
class nsISimpleEnumerator;
class nsEditorHookUtils
{
public:
- static PRBool DoAllowDragHook(nsIDOMDocument *aDoc, nsIDOMEvent *aEvent);
- static PRBool DoDragHook(nsIDOMDocument *aDoc, nsIDOMEvent *aEvent,
- nsITransferable *aTrans);
- static PRBool DoAllowDropHook(nsIDOMDocument *aDoc, nsIDOMEvent *aEvent,
- nsIDragSession *aSession);
static PRBool DoInsertionHook(nsIDOMDocument *aDoc, nsIDOMEvent *aEvent,
nsITransferable *aTrans);
private:
static nsresult GetHookEnumeratorFromDocument(nsIDOMDocument *aDoc,
nsISimpleEnumerator **aEnumerator);
};
#endif // nsEditorUtils_h__
--- a/editor/libeditor/html/nsHTMLDataTransfer.cpp
+++ b/editor/libeditor/html/nsHTMLDataTransfer.cpp
@@ -1492,18 +1492,16 @@ NS_IMETHODIMP nsHTMLEditor::InsertFromDr
nsCOMPtr<nsIDragSession> dragSession;
dragService->GetCurrentSession(getter_AddRefs(dragSession));
if (!dragSession) return NS_OK;
// transferable hooks here
nsCOMPtr<nsIDOMDocument> domdoc;
GetDocument(getter_AddRefs(domdoc));
- if (!nsEditorHookUtils::DoAllowDropHook(domdoc, aDropEvent, dragSession))
- return NS_OK;
// find out if we have our internal html flavor on the clipboard. We don't want to mess
// around with cfhtml if we do.
PRBool bHavePrivateHTMLFlavor = PR_FALSE;
rv = dragSession->IsDataFlavorSupported(kHTMLContext, &bHavePrivateHTMLFlavor);
if (NS_FAILED(rv)) return rv;
// Get the nsITransferable interface for getting the data from the drop
--- a/editor/libeditor/text/nsEditorEventListeners.cpp
+++ b/editor/libeditor/text/nsEditorEventListeners.cpp
@@ -565,23 +565,16 @@ nsTextEditorDragListener::DragOver(nsIDO
if (!dropParent)
return NS_ERROR_FAILURE;
if (!dropParent->IsEditable())
return NS_OK;
}
PRBool canDrop = CanDrop(aDragEvent);
- if (canDrop)
- {
- nsCOMPtr<nsIDOMDocument> domdoc;
- mEditor->GetDocument(getter_AddRefs(domdoc));
- canDrop = nsEditorHookUtils::DoAllowDropHook(domdoc, aDragEvent, dragSession);
- }
-
dragSession->SetCanDrop(canDrop);
// We need to consume the event to prevent the browser's
// default drag listeners from being fired. (Bug 199133)
aDragEvent->PreventDefault(); // consumed
if (canDrop)
--- a/editor/libeditor/text/nsPlaintextDataTransfer.cpp
+++ b/editor/libeditor/text/nsPlaintextDataTransfer.cpp
@@ -40,16 +40,17 @@
#include "nsIDOMDocument.h"
#include "nsIDocument.h"
#include "nsIContent.h"
#include "nsIFormControl.h"
#include "nsIDOMEventTarget.h"
#include "nsIDOMNSEvent.h"
#include "nsIDOMMouseEvent.h"
+#include "nsIDOMDragEvent.h"
#include "nsISelection.h"
#include "nsCRT.h"
#include "nsServiceManagerUtils.h"
#include "nsIDOMRange.h"
#include "nsIDOMNSRange.h"
#include "nsIDocumentEncoder.h"
#include "nsISupportsPrimitives.h"
@@ -158,20 +159,16 @@ NS_IMETHODIMP nsPlaintextEditor::InsertF
dragService->GetCurrentSession(getter_AddRefs(dragSession));
if (!dragSession) return NS_OK;
// Current doc is destination
nsCOMPtr<nsIDOMDocument> destdomdoc;
rv = GetDocument(getter_AddRefs(destdomdoc));
if (NS_FAILED(rv)) return rv;
- // transferable hooks
- if (!nsEditorHookUtils::DoAllowDropHook(destdomdoc, aDropEvent, dragSession))
- return NS_OK;
-
// Get the nsITransferable interface for getting the data from the drop
nsCOMPtr<nsITransferable> trans;
rv = PrepareTransferable(getter_AddRefs(trans));
if (NS_FAILED(rv)) return rv;
if (!trans) return NS_OK; // NS_ERROR_FAILURE; SHOULD WE FAIL?
PRUint32 numItems = 0;
rv = dragSession->GetNumDropItems(&numItems);
@@ -296,19 +293,16 @@ NS_IMETHODIMP nsPlaintextEditor::InsertF
PRUint32 i;
for (i = 0; i < numItems; ++i)
{
rv = dragSession->GetData(trans, i);
if (NS_FAILED(rv)) return rv;
if (!trans) return NS_OK; // NS_ERROR_FAILURE; Should we fail?
- if (!nsEditorHookUtils::DoInsertionHook(destdomdoc, aDropEvent, trans))
- return NS_OK;
-
// Beware! This may flush notifications via synchronous
// ScrollSelectionIntoView.
rv = InsertTextFromTransferable(trans, newSelectionParent, newSelectionOffset, deleteSelection);
}
return rv;
}
@@ -358,23 +352,17 @@ NS_IMETHODIMP nsPlaintextEditor::CanDrag
PRBool isTargetedCorrectly = PR_FALSE;
res = selection->ContainsNode(eventTargetDomNode, PR_FALSE, &isTargetedCorrectly);
if (NS_FAILED(res)) return res;
*aCanDrag = isTargetedCorrectly;
}
}
- if (NS_FAILED(res)) return res;
- if (!*aCanDrag) return NS_OK;
-
- nsCOMPtr<nsIDOMDocument> domdoc;
- GetDocument(getter_AddRefs(domdoc));
- *aCanDrag = nsEditorHookUtils::DoAllowDragHook(domdoc, aDragEvent);
- return NS_OK;
+ return res;
}
NS_IMETHODIMP nsPlaintextEditor::DoDrag(nsIDOMEvent *aDragEvent)
{
nsresult rv;
nsCOMPtr<nsITransferable> trans;
rv = PutDragDataInTransferable(getter_AddRefs(trans));
@@ -394,37 +382,35 @@ NS_IMETHODIMP nsPlaintextEditor::DoDrag(
/* add the transferable to the array */
rv = transferableArray->AppendElement(trans);
if (NS_FAILED(rv)) return rv;
// check our transferable hooks (if any)
nsCOMPtr<nsIDOMDocument> domdoc;
GetDocument(getter_AddRefs(domdoc));
- if (!nsEditorHookUtils::DoDragHook(domdoc, aDragEvent, trans))
- return NS_OK;
/* invoke drag */
nsCOMPtr<nsIDOMEventTarget> eventTarget;
rv = aDragEvent->GetTarget(getter_AddRefs(eventTarget));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIDOMNode> domnode = do_QueryInterface(eventTarget);
nsCOMPtr<nsIScriptableRegion> selRegion;
nsCOMPtr<nsISelection> selection;
rv = GetSelection(getter_AddRefs(selection));
if (NS_FAILED(rv)) return rv;
unsigned int flags;
// in some cases we'll want to cut rather than copy... hmmmmm...
flags = nsIDragService::DRAGDROP_ACTION_COPY + nsIDragService::DRAGDROP_ACTION_MOVE;
- nsCOMPtr<nsIDOMMouseEvent> mouseEvent(do_QueryInterface(aDragEvent));
+ nsCOMPtr<nsIDOMDragEvent> dragEvent(do_QueryInterface(aDragEvent));
rv = dragService->InvokeDragSessionWithSelection(selection, transferableArray,
- flags, mouseEvent);
+ flags, dragEvent, nsnull);
if (NS_FAILED(rv)) return rv;
aDragEvent->StopPropagation();
return rv;
}
NS_IMETHODIMP nsPlaintextEditor::Paste(PRInt32 aSelectionType)
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -621,17 +621,18 @@ nsLayoutUtils::GetDOMEventCoordinatesRel
return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
return GetEventCoordinatesRelativeTo(event, aFrame);
}
nsPoint
nsLayoutUtils::GetEventCoordinatesRelativeTo(const nsEvent* aEvent, nsIFrame* aFrame)
{
if (!aEvent || (aEvent->eventStructType != NS_MOUSE_EVENT &&
- aEvent->eventStructType != NS_MOUSE_SCROLL_EVENT))
+ aEvent->eventStructType != NS_MOUSE_SCROLL_EVENT &&
+ aEvent->eventStructType != NS_DRAG_EVENT))
return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
const nsGUIEvent* GUIEvent = static_cast<const nsGUIEvent*>(aEvent);
if (!GUIEvent->widget)
return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
// If it is, or is a descendant of, an SVG foreignobject frame,
// then we need to do extra work
@@ -664,17 +665,18 @@ nsLayoutUtils::GetEventCoordinatesRelati
}
nsPoint
nsLayoutUtils::GetEventCoordinatesForNearestView(nsEvent* aEvent,
nsIFrame* aFrame,
nsIView** aView)
{
if (!aEvent || (aEvent->eventStructType != NS_MOUSE_EVENT &&
- aEvent->eventStructType != NS_MOUSE_SCROLL_EVENT))
+ aEvent->eventStructType != NS_MOUSE_SCROLL_EVENT &&
+ aEvent->eventStructType != NS_DRAG_EVENT))
return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
nsGUIEvent* GUIEvent = static_cast<nsGUIEvent*>(aEvent);
if (!GUIEvent->widget)
return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
nsPoint viewToFrame;
nsIView* frameView;
--- a/layout/xul/base/src/tree/public/nsITreeBoxObject.idl
+++ b/layout/xul/base/src/tree/public/nsITreeBoxObject.idl
@@ -41,18 +41,19 @@
#include "nsISupports.idl"
#include "domstubs.idl"
interface nsITreeView;
interface nsITreeSelection;
interface nsITreeColumn;
interface nsITreeColumns;
+interface nsIScriptableRegion;
-[scriptable, uuid(a264f607-9d90-469e-b770-1ae7284fde05)]
+[scriptable, uuid(64BA5199-C4F4-4498-BBDC-F8E4C369086C)]
interface nsITreeBoxObject : nsISupports
{
/**
* Obtain the columns.
*/
readonly attribute nsITreeColumns columns;
/**
@@ -83,16 +84,21 @@ interface nsITreeBoxObject : nsISupports
readonly attribute long rowWidth;
/**
* Get the pixel position of the horizontal scrollbar.
*/
readonly attribute long horizontalPosition;
/**
+ * Return the region for the visible parts of the selection, in device pixels.
+ */
+ readonly attribute nsIScriptableRegion selectionRegion;
+
+ /**
* Get the index of the first visible row.
*/
long getFirstVisibleRow();
/**
* Get the index of the last visible row.
*/
long getLastVisibleRow();
--- a/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp
+++ b/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp
@@ -638,16 +638,58 @@ nsTreeBodyFrame::GetHorizontalPosition(P
NS_IMETHODIMP
nsTreeBodyFrame::GetPageLength(PRInt32 *_retval)
{
*_retval = mPageLength;
return NS_OK;
}
NS_IMETHODIMP
+nsTreeBodyFrame::GetSelectionRegion(nsIScriptableRegion **aRegion)
+{
+ *aRegion = nsnull;
+
+ nsCOMPtr<nsITreeSelection> selection;
+ mView->GetSelection(getter_AddRefs(selection));
+ NS_ENSURE_TRUE(selection, NS_OK);
+
+ nsCOMPtr<nsIScriptableRegion> region = do_CreateInstance("@mozilla.org/gfx/region;1");
+ NS_ENSURE_TRUE(region, NS_ERROR_FAILURE);
+ region->Init();
+
+ nsRefPtr<nsPresContext> presContext = PresContext();
+ nsRect rect = mRect;
+ rect.ScaleRoundOut(1.0 / presContext->AppUnitsPerCSSPixel());
+
+ nsIFrame* rootFrame = presContext->PresShell()->GetRootFrame();
+ nsPoint origin = GetOffsetTo(rootFrame);
+
+ // iterate through the visible rows and add the selected ones to the
+ // drag region
+ PRInt32 x = nsPresContext::AppUnitsToIntCSSPixels(origin.x);
+ PRInt32 y = nsPresContext::AppUnitsToIntCSSPixels(origin.y);
+ PRInt32 top = y;
+ PRInt32 end = GetLastVisibleRow();
+ PRInt32 rowHeight = nsPresContext::AppUnitsToIntCSSPixels(mRowHeight);
+ for (PRInt32 i = mTopRowIndex; i <= end; i++) {
+ PRBool isSelected;
+ selection->IsSelected(i, &isSelected);
+ if (isSelected)
+ region->UnionRect(x, y, rect.width, rowHeight);
+ y += rowHeight;
+ }
+
+ // clip to the tree boundary in case one row extends past it
+ region->IntersectRect(x, top, rect.width, rect.height);
+
+ NS_ADDREF(*aRegion = region);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
nsTreeBodyFrame::Invalidate()
{
if (mUpdateBatchNest)
return NS_OK;
nsIFrame::Invalidate(GetOverflowRect(), PR_FALSE);
return NS_OK;
@@ -2565,16 +2607,18 @@ nsTreeBodyFrame::HandleEvent(nsPresConte
do_GetService("@mozilla.org/widget/dragservice;1");
dragService->GetCurrentSession(getter_AddRefs(mSlots->mDragSession));
NS_ASSERTION(mSlots->mDragSession, "can't get drag session");
if (mSlots->mDragSession)
mSlots->mDragSession->GetDragAction(&mSlots->mDragAction);
else
mSlots->mDragAction = 0;
+ mSlots->mDropRow = -1;
+ mSlots->mDropOrient = -1;
}
else if (aEvent->message == NS_DRAGDROP_OVER) {
// The mouse is hovering over this tree. If we determine things are
// different from the last time, invalidate the drop feedback at the old
// position, query the view to see if the current location is droppable,
// and then invalidate the drop feedback at the new location if it is.
// The mouse may or may not have changed position from the last time
// we were called, so optimize out a lot of the extra notifications by
@@ -2689,35 +2733,38 @@ nsTreeBodyFrame::HandleEvent(nsPresConte
PRInt32 parentIndex;
nsresult rv = mView->GetParentIndex(mSlots->mDropRow, &parentIndex);
while (NS_SUCCEEDED(rv) && parentIndex >= 0) {
mSlots->mArray.RemoveElement(parentIndex);
rv = mView->GetParentIndex(parentIndex, &parentIndex);
}
mView->Drop(mSlots->mDropRow, mSlots->mDropOrient);
+ mSlots->mDropRow = -1;
+ mSlots->mDropOrient = -1;
+ *aEventStatus = nsEventStatus_eConsumeNoDefault; // already handled the drop
}
else if (aEvent->message == NS_DRAGDROP_EXIT) {
// this event was meant for another frame, so ignore it
if (!mSlots)
return NS_OK;
// Clear out all our tracking vars.
if (mSlots->mDropAllowed) {
mSlots->mDropAllowed = PR_FALSE;
InvalidateDropFeedback(mSlots->mDropRow, mSlots->mDropOrient);
}
else
mSlots->mDropAllowed = PR_FALSE;
- mSlots->mDropRow = -1;
- mSlots->mDropOrient = -1;
mSlots->mDragSession = nsnull;
mSlots->mScrollLines = 0;
-
+ // If a drop is occuring, the exit event will fire just before the drop
+ // event, so don't reset mDropRow or mDropOrient as these fields are used
+ // by the drop event.
if (mSlots->mTimer) {
mSlots->mTimer->Cancel();
mSlots->mTimer = nsnull;
}
if (!mSlots->mArray.IsEmpty()) {
// Close all spring loaded folders except the drop folder.
CreateTimer(nsILookAndFeel::eMetric_TreeCloseDelay,
--- a/layout/xul/base/src/tree/src/nsTreeBoxObject.cpp
+++ b/layout/xul/base/src/tree/src/nsTreeBoxObject.cpp
@@ -289,16 +289,25 @@ NS_IMETHODIMP nsTreeBoxObject::GetPageLe
{
*aPageLength = 0;
nsITreeBoxObject* body = GetTreeBody();
if (body)
return body->GetPageLength(aPageLength);
return NS_OK;
}
+NS_IMETHODIMP nsTreeBoxObject::GetSelectionRegion(nsIScriptableRegion **aRegion)
+{
+ *aRegion = nsnull;
+ nsITreeBoxObject* body = GetTreeBody();
+ if (body)
+ return body->GetSelectionRegion(aRegion);
+ return NS_OK;
+}
+
NS_IMETHODIMP
nsTreeBoxObject::EnsureRowIsVisible(PRInt32 aRow)
{
nsITreeBoxObject* body = GetTreeBody();
if (body)
return body->EnsureRowIsVisible(aRow);
return NS_OK;
}
--- a/testing/mochitest/tests/SimpleTest/EventUtils.js
+++ b/testing/mochitest/tests/SimpleTest/EventUtils.js
@@ -205,18 +205,19 @@ function synthesizeMouse(aTarget, aOffse
var utils = aWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
getInterface(Components.interfaces.nsIDOMWindowUtils);
if (utils) {
var button = aEvent.button || 0;
var clickCount = aEvent.clickCount || 1;
var modifiers = _parseModifiers(aEvent);
- var left = aTarget.boxObject.x;
- var top = aTarget.boxObject.y;
+ var rect = aTarget.getBoundingClientRect();
+ var left = rect.left;
+ var top = rect.top;
if (aEvent.type) {
utils.sendMouseEvent(aEvent.type, left + aOffsetX, top + aOffsetY, button, clickCount, modifiers);
}
else {
utils.sendMouseEvent("mousedown", left + aOffsetX, top + aOffsetY, button, clickCount, modifiers);
utils.sendMouseEvent("mouseup", left + aOffsetX, top + aOffsetY, button, clickCount, modifiers);
}
--- a/toolkit/content/nsDragAndDrop.js
+++ b/toolkit/content/nsDragAndDrop.js
@@ -32,16 +32,19 @@
# 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 *****
+// non-strings need some non-zero value used for their data length
+const kNonStringDataLength = 4;
+
/**
* nsTransferable - a wrapper for nsITransferable that simplifies
* javascript clipboard and drag&drop. for use in
* these situations you should use the nsClipboard
* and nsDragAndDrop wrappers for more convenience
**/
var nsTransferable = {
@@ -262,27 +265,25 @@ function FlavourData(aData, aLength, aFl
this.flavour = aFlavour || null;
this._XferID = "FlavourData";
}
FlavourData.prototype = {
get data ()
{
- if (this.flavour &&
- this.flavour.dataIIDKey != "nsISupportsString" )
+ if (this.flavour &&
+ this.flavour.dataIIDKey != "nsISupportsString")
return this.supports.QueryInterface(Components.interfaces[this.flavour.dataIIDKey]);
- else {
- var unicode = this.supports.QueryInterface(Components.interfaces.nsISupportsString);
- if (unicode)
- return unicode.data.substring(0, this.contentLength/2);
+
+ var supports = this.supports;
+ if (supports instanceof Components.interfaces.nsISupportsString)
+ return supports.data.substring(0, this.contentLength/2);
- return this.supports;
- }
- return "";
+ return supports;
}
}
/**
* Create a TransferData object with a single FlavourData entry. Used when
* unwrapping data of a specific flavour from the drag service.
*/
function FlavourToXfer(aData, aLength, aFlavour)
@@ -293,17 +294,17 @@ function FlavourToXfer(aData, aLength, a
var transferUtils = {
retrieveURLFromData: function (aData, flavour)
{
switch (flavour) {
case "text/unicode":
return aData.replace(/^\s+|\s+$/g, "");
case "text/x-moz-url":
- return aData.toString().split("\n")[0];
+ return ((aData instanceof Components.interfaces.nsISupportsString) ? aData.toString() : aData).split("\n")[0];
case "application/x-moz-file":
var ioService = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
var fileHandler = ioService.getProtocolHandler("file")
.QueryInterface(Components.interfaces.nsIFileProtocolHandler);
return fileHandler.getURLSpecFromFile(aData);
}
return null;
@@ -376,73 +377,43 @@ var nsDragAndDrop = {
}
catch (e)
{
return; // not a draggable item, bail!
}
if (!transferData.data) return;
transferData = transferData.data;
-
- var transArray = Components.classes["@mozilla.org/supports-array;1"]
- .createInstance(Components.interfaces.nsISupportsArray);
-
- var region = null;
- if (aEvent.originalTarget.localName == "treechildren") {
- // let's build the drag region
- var tree = aEvent.originalTarget.parentNode;
- try {
- region = Components.classes["@mozilla.org/gfx/region;1"].createInstance(Components.interfaces.nsIScriptableRegion);
- region.init();
- var obo = tree.treeBoxObject;
- var bo = obo.treeBody.boxObject;
- var sel= obo.view.selection;
-
- var rowX = bo.x;
- var rowY = bo.y;
- var rowHeight = obo.rowHeight;
- var rowWidth = bo.width;
- //add a rectangle for each visible selected row
- for (var i = obo.getFirstVisibleRow(); i <= obo.getLastVisibleRow(); i ++)
- {
- if (sel.isSelected(i))
- region.unionRect(rowX, rowY, rowWidth, rowHeight);
- rowY = rowY + rowHeight;
- }
-
- //and finally, clip the result to be sure we don't spill over...
- region.intersectRect(bo.x, bo.y, bo.width, bo.height);
- } catch(ex) {
- dump("Error while building selection region: " + ex + "\n");
- region = null;
+ var dt = aEvent.dataTransfer;
+ var count = 0;
+ do {
+ var tds = transferData._XferID == "TransferData"
+ ? transferData
+ : transferData.dataList[count]
+ for (var i = 0; i < tds.dataList.length; ++i)
+ {
+ var currData = tds.dataList[i];
+ var currFlavour = currData.flavour.contentType;
+ var value = currData.supports;
+ if (value instanceof Components.interfaces.nsISupportsString)
+ value = value.toString();
+ dt.mozSetDataAt(currFlavour, value, count);
}
+
+ count++;
}
-
- var count = 0;
- do
- {
- var trans = nsTransferable.set(transferData._XferID == "TransferData"
- ? transferData
- : transferData.dataList[count++]);
- transArray.AppendElement(trans.QueryInterface(Components.interfaces.nsISupports));
- }
while (transferData._XferID == "TransferDataSet" &&
count < transferData.dataList.length);
-
- try {
- this.mDragService.invokeDragSessionWithImage(aEvent.target, transArray,
- region, dragAction.action,
- null, 0, 0, aEvent);
- }
- catch(ex) {
- // this could be because the user pressed escape to
- // cancel the drag. even if it's not, there's not much
- // we can do, so be silent.
- }
+
+ dt.effectAllowed = "all";
+ // a drag targeted at a tree should instead use the treechildren so that
+ // the current selection is used as the drag feedback
+ dt.addElement(aEvent.originalTarget.localName == "treechildren" ?
+ aEvent.originalTarget : aEvent.target);
aEvent.stopPropagation();
},
/**
* void dragOver (DOMEvent aEvent, Object aDragDropObserver) ;
*
* called when a drag passes over this element
*
@@ -462,16 +433,17 @@ var nsDragAndDrop = {
for (var flavour in flavourSet.flavourTable)
{
if (this.mDragSession.isDataFlavorSupported(flavour))
{
aDragDropObserver.onDragOver(aEvent,
flavourSet.flavourTable[flavour],
this.mDragSession);
aEvent.stopPropagation();
+ aEvent.preventDefault();
break;
}
}
},
mDragSession: null,
/**
@@ -486,24 +458,47 @@ var nsDragAndDrop = {
* the way in which the element responds to drag events.
**/
drop: function (aEvent, aDragDropObserver)
{
if (!("onDrop" in aDragDropObserver))
return;
if (!this.checkCanDrop(aEvent, aDragDropObserver))
return;
- if (this.mDragSession.canDrop) {
- var flavourSet = aDragDropObserver.getSupportedFlavours();
- var transferData = nsTransferable.get(flavourSet, this.getDragData, true);
- // hand over to the client to respond to dropped data
- var multiple = "canHandleMultipleItems" in aDragDropObserver && aDragDropObserver.canHandleMultipleItems;
- var dropData = multiple ? transferData : transferData.first.first;
- aDragDropObserver.onDrop(aEvent, dropData, this.mDragSession);
+
+ var flavourSet = aDragDropObserver.getSupportedFlavours();
+
+ var dt = aEvent.dataTransfer;
+ var dataArray = [];
+ var count = dt.mozItemCount;
+ for (var i = 0; i < count; ++i) {
+ var types = dt.mozTypesAt(i);
+ for (var j = 0; j < types.length; ++j) {
+ var type = types[j];
+ // dataTransfer uses text/plain but older code used text/unicode, so
+ // switch this for compatibility
+ if (type == "text/plain")
+ type = "text/unicode";
+ if (type in flavourSet.flavourTable) {
+ var data = dt.mozGetDataAt(type, i);
+ if (data) {
+ var length = (typeof data == "string") ? data.length : kNonStringDataLength;
+ dataArray[i] = FlavourToXfer(data, length, flavourSet.flavourTable[type]);
+ break;
+ }
+ }
+ }
}
+
+ var transferData = new TransferDataSet(dataArray)
+
+ // hand over to the client to respond to dropped data
+ var multiple = "canHandleMultipleItems" in aDragDropObserver && aDragDropObserver.canHandleMultipleItems;
+ var dropData = multiple ? transferData : transferData.first.first;
+ aDragDropObserver.onDrop(aEvent, dropData, this.mDragSession);
aEvent.stopPropagation();
},
/**
* void dragExit (DOMEvent aEvent, Object aDragDropObserver) ;
*
* called when a drag leaves this element
*
@@ -534,41 +529,16 @@ var nsDragAndDrop = {
**/
dragEnter: function (aEvent, aDragDropObserver)
{
if (!this.checkCanDrop(aEvent, aDragDropObserver))
return;
if ("onDragEnter" in aDragDropObserver)
aDragDropObserver.onDragEnter(aEvent, this.mDragSession);
},
-
- /**
- * nsISupportsArray getDragData (Object aFlavourList)
- *
- * Creates a nsISupportsArray of all droppable items for the given
- * set of supported flavours.
- *
- * @param FlavourSet aFlavourSet
- * formatted flavour list.
- **/
- getDragData: function (aFlavourSet)
- {
- var supportsArray = Components.classes["@mozilla.org/supports-array;1"]
- .createInstance(Components.interfaces.nsISupportsArray);
-
- for (var i = 0; i < nsDragAndDrop.mDragSession.numDropItems; ++i)
- {
- var trans = nsTransferable.createTransferable();
- for (var j = 0; j < aFlavourSet.flavours.length; ++j)
- trans.addDataFlavor(aFlavourSet.flavours[j].contentType);
- nsDragAndDrop.mDragSession.getData(trans, i);
- supportsArray.AppendElement(trans);
- }
- return supportsArray;
- },
/**
* Boolean checkCanDrop (DOMEvent aEvent, Object aDragDropObserver) ;
*
* Sets the canDrop attribute for the drag session.
* returns false if there is no current drag session.
*
* @param DOMEvent aEvent
--- a/widget/public/nsGUIEvent.h
+++ b/widget/public/nsGUIEvent.h
@@ -49,16 +49,17 @@
// which conflicts with something that Windows defines somewhere.
// So, undefine it:
#ifdef WIN32
#undef ERROR
#endif
#include "nsCOMPtr.h"
#include "nsIAtom.h"
#include "nsIDOMKeyEvent.h"
+#include "nsIDOMDataTransfer.h"
#include "nsWeakPtr.h"
#include "nsIWidget.h"
#include "nsTArray.h"
#include "nsTraceRefcnt.h"
class nsIRenderingContext;
class nsIRegion;
class nsIMenuItem;
@@ -101,16 +102,17 @@ class nsHashKey;
#define NS_SVG_EVENT 30
#define NS_SVGZOOM_EVENT 31
#endif // MOZ_SVG
#define NS_XUL_COMMAND_EVENT 32
#define NS_QUERY_CONTENT_EVENT 33
#ifdef MOZ_MEDIA
#define NS_MEDIA_EVENT 34
#endif // MOZ_MEDIA
+#define NS_DRAG_EVENT 35
// These flags are sort of a mess. They're sort of shared between event
// listener flags and event flags, but only some of them. You've been
// warned!
#define NS_EVENT_FLAG_NONE 0x0000
#define NS_EVENT_FLAG_TRUSTED 0x0001
#define NS_EVENT_FLAG_BUBBLE 0x0002
#define NS_EVENT_FLAG_CAPTURE 0x0004
@@ -243,27 +245,29 @@ class nsHashKey;
#define NS_FORM_SELECTED (NS_FORM_EVENT_START + 3)
#define NS_FORM_INPUT (NS_FORM_EVENT_START + 4)
//Need separate focus/blur notifications for non-native widgets
#define NS_FOCUS_EVENT_START 1300
#define NS_FOCUS_CONTENT (NS_FOCUS_EVENT_START)
#define NS_BLUR_CONTENT (NS_FOCUS_EVENT_START + 1)
-
#define NS_DRAGDROP_EVENT_START 1400
#define NS_DRAGDROP_ENTER (NS_DRAGDROP_EVENT_START)
#define NS_DRAGDROP_OVER (NS_DRAGDROP_EVENT_START + 1)
#define NS_DRAGDROP_EXIT (NS_DRAGDROP_EVENT_START + 2)
-#define NS_DRAGDROP_DROP (NS_DRAGDROP_EVENT_START + 3)
+#define NS_DRAGDROP_DRAGDROP (NS_DRAGDROP_EVENT_START + 3)
#define NS_DRAGDROP_GESTURE (NS_DRAGDROP_EVENT_START + 4)
#define NS_DRAGDROP_DRAG (NS_DRAGDROP_EVENT_START + 5)
#define NS_DRAGDROP_END (NS_DRAGDROP_EVENT_START + 6)
+#define NS_DRAGDROP_START (NS_DRAGDROP_EVENT_START + 7)
+#define NS_DRAGDROP_DROP (NS_DRAGDROP_EVENT_START + 8)
#define NS_DRAGDROP_OVER_SYNTH (NS_DRAGDROP_EVENT_START + 1)
#define NS_DRAGDROP_EXIT_SYNTH (NS_DRAGDROP_EVENT_START + 2)
+#define NS_DRAGDROP_LEAVE_SYNTH (NS_DRAGDROP_EVENT_START + 9)
// Events for popups
#define NS_XUL_EVENT_START 1500
#define NS_XUL_POPUP_SHOWING (NS_XUL_EVENT_START)
#define NS_XUL_POPUP_SHOWN (NS_XUL_EVENT_START+1)
#define NS_XUL_POPUP_HIDING (NS_XUL_EVENT_START+2)
#define NS_XUL_POPUP_HIDDEN (NS_XUL_EVENT_START+3)
// NS_XUL_COMMAND used to be here (NS_XUL_EVENT_START+4)
@@ -673,16 +677,30 @@ public:
class nsMouseEvent : public nsMouseEvent_base
{
public:
enum buttonType { eLeftButton = 0, eMiddleButton = 1, eRightButton = 2 };
enum reasonType { eReal, eSynthesized };
enum contextType { eNormal, eContextMenuKey };
enum exitType { eChild, eTopLevel };
+protected:
+ nsMouseEvent(PRBool isTrusted, PRUint32 msg, nsIWidget *w,
+ PRUint8 structType, reasonType aReason)
+ : nsMouseEvent_base(isTrusted, msg, w, structType),
+ acceptActivation(PR_FALSE), reason(aReason), context(eNormal),
+ exit(eChild), clickCount(0)
+ {
+ if (msg == NS_MOUSE_MOVE) {
+ flags |= NS_EVENT_FLAG_CANT_CANCEL;
+ }
+ }
+
+public:
+
nsMouseEvent(PRBool isTrusted, PRUint32 msg, nsIWidget *w,
reasonType aReason, contextType aContext = eNormal)
: nsMouseEvent_base(isTrusted, msg, w, NS_MOUSE_EVENT),
acceptActivation(PR_FALSE), reason(aReason), context(aContext),
exit(eChild), clickCount(0)
{
if (msg == NS_MOUSE_MOVE) {
flags |= NS_EVENT_FLAG_CANT_CANCEL;
@@ -706,16 +724,36 @@ public:
contextType context : 4;
exitType exit;
/// The number of mouse clicks
PRUint32 clickCount;
};
/**
+ * Drag event
+ */
+
+class nsDragEvent : public nsMouseEvent
+{
+public:
+ nsDragEvent(PRBool isTrusted, PRUint32 msg, nsIWidget *w)
+ : nsMouseEvent(isTrusted, msg, w, NS_DRAG_EVENT, eReal)
+ {
+ if (msg == NS_DRAGDROP_EXIT_SYNTH ||
+ msg == NS_DRAGDROP_LEAVE_SYNTH ||
+ msg == NS_DRAGDROP_END) {
+ flags |= NS_EVENT_FLAG_CANT_CANCEL;
+ }
+ }
+
+ nsCOMPtr<nsIDOMDataTransfer> dataTransfer;
+};
+
+/**
* Accessible event
*/
class nsAccessibleEvent : public nsInputEvent
{
public:
nsAccessibleEvent(PRBool isTrusted, PRUint32 msg, nsIWidget *w)
: nsInputEvent(isTrusted, msg, w, NS_ACCESSIBLE_EVENT),
@@ -1054,20 +1092,23 @@ enum nsDragDropEventStatus {
((evnt)->eventStructType == NS_MOUSE_EVENT && \
(evnt)->message == NS_CONTEXTMENU && \
static_cast<nsMouseEvent*>((evnt))->context == nsMouseEvent::eContextMenuKey)
#define NS_IS_DRAG_EVENT(evnt) \
(((evnt)->message == NS_DRAGDROP_ENTER) || \
((evnt)->message == NS_DRAGDROP_OVER) || \
((evnt)->message == NS_DRAGDROP_EXIT) || \
- ((evnt)->message == NS_DRAGDROP_DROP) || \
+ ((evnt)->message == NS_DRAGDROP_DRAGDROP) || \
((evnt)->message == NS_DRAGDROP_GESTURE) || \
- ((evnt)->message == NS_DRAGDROP_OVER_SYNTH) || \
- ((evnt)->message == NS_DRAGDROP_EXIT_SYNTH))
+ ((evnt)->message == NS_DRAGDROP_DRAG) || \
+ ((evnt)->message == NS_DRAGDROP_END) || \
+ ((evnt)->message == NS_DRAGDROP_START) || \
+ ((evnt)->message == NS_DRAGDROP_DROP) || \
+ ((evnt)->message == NS_DRAGDROP_LEAVE_SYNTH))
#define NS_IS_KEY_EVENT(evnt) \
(((evnt)->message == NS_KEY_DOWN) || \
((evnt)->message == NS_KEY_PRESS) || \
((evnt)->message == NS_KEY_UP))
#define NS_IS_IME_EVENT(evnt) \
(((evnt)->message == NS_TEXT_TEXT) || \
--- a/widget/public/nsIDragService.idl
+++ b/widget/public/nsIDragService.idl
@@ -39,26 +39,28 @@
#include "nsISupports.idl"
#include "nsISupportsArray.idl"
#include "nsIDragSession.idl"
#include "nsIScriptableRegion.idl"
interface nsIDOMNode;
-interface nsIDOMMouseEvent;
+interface nsIDOMDragEvent;
+interface nsIDOMDataTransfer;
interface nsISelection;
-[scriptable, uuid(034c44a4-604b-44a2-9205-676d5135f359)]
+[scriptable, uuid(82B58ADA-F490-4C3D-B737-1057C4F1D052)]
interface nsIDragService : nsISupports
{
const long DRAGDROP_ACTION_NONE = 0;
const long DRAGDROP_ACTION_COPY = 1;
const long DRAGDROP_ACTION_MOVE = 2;
const long DRAGDROP_ACTION_LINK = 4;
+ const long DRAGDROP_ACTION_UNINITIALIZED = 64;
/**
* Starts a modal drag session with an array of transaferables
*
* @param aTransferables - an array of transferables to be dragged
* @param aRegion - a region containing rectangles for cursor feedback,
* in window coordinates.
* @param aActionType - specified which of copy/move/link are allowed
@@ -93,27 +95,29 @@ interface nsIDragService : nsISupports
*/
void invokeDragSessionWithImage(in nsIDOMNode aDOMNode,
in nsISupportsArray aTransferableArray,
in nsIScriptableRegion aRegion,
in unsigned long aActionType,
in nsIDOMNode aImage,
in long aImageX,
in long aImageY,
- in nsIDOMMouseEvent aDragEvent);
+ in nsIDOMDragEvent aDragEvent,
+ in nsIDOMDataTransfer aDataTransfer);
/**
* Start a modal drag session using the selection as the drag image.
* The aDragEvent must be supplied as the current screen coordinates of the
* event are needed to calculate the image location.
*/
void invokeDragSessionWithSelection(in nsISelection aSelection,
in nsISupportsArray aTransferableArray,
in unsigned long aActionType,
- in nsIDOMMouseEvent aDragEvent);
+ in nsIDOMDragEvent aDragEvent,
+ in nsIDOMDataTransfer aDataTransfer);
/**
* Returns the current Drag Session
*/
nsIDragSession getCurrentSession ( ) ;
/**
* Tells the Drag Service to start a drag session. This is called when
--- a/widget/public/nsIDragSession.idl
+++ b/widget/public/nsIDragSession.idl
@@ -46,18 +46,19 @@
#include "nsSize.h"
%}
native nsSize (nsSize);
interface nsIDOMDocument;
interface nsIDOMNode;
+interface nsIDOMDataTransfer;
-[scriptable, uuid(CBA22C53-FCCE-11d2-96D4-0060B0FB9956)]
+[scriptable, uuid(15860D52-FE2C-4DDD-AC50-9C23E24916C4)]
interface nsIDragSession : nsISupports
{
/**
* Set the current state of the drag, whether it can be dropped or not.
* usually the target "frame" sets this so the native system can render the correct feedback
*/
attribute boolean canDrop;
@@ -86,16 +87,21 @@ interface nsIDragSession : nsISupports
/**
* The dom node that was originally dragged to start the session, which will be null if the
* drag originated outside the application.
*/
readonly attribute nsIDOMNode sourceNode;
/**
+ * The data transfer object for the current drag.
+ */
+ attribute nsIDOMDataTransfer dataTransfer;
+
+ /**
* Get data from a Drag&Drop. Can be called while the drag is in process
* or after the drop has completed.
*
* @param aTransferable the transferable for the data to be put into
* @param aItemIndex which of multiple drag items, zero-based
*/
void getData ( in nsITransferable aTransferable, in unsigned long aItemIndex ) ;
--- a/widget/src/beos/nsWindow.cpp
+++ b/widget/src/beos/nsWindow.cpp
@@ -1932,17 +1932,17 @@ bool nsWindow::CallMethod(MethodInfo *in
((int32 *)info->args)[3]);
}
break;
case nsSwitchToUIThread::ONDROP :
{
NS_ASSERTION(info->nArgs == 4, "Wrong number of arguments to CallMethod");
- nsMouseEvent event(PR_TRUE, (int32) info->args[0], this, nsMouseEvent::eReal);
+ nsDragEvent event(PR_TRUE, (int32) info->args[0], this);
nsPoint point(((int32 *)info->args)[1], ((int32 *)info->args)[2]);
InitEvent (event, &point);
uint32 mod = (uint32) info->args[3];
event.isShift = mod & B_SHIFT_KEY;
event.isControl = mod & B_CONTROL_KEY;
event.isAlt = mod & B_COMMAND_KEY;
event.isMeta = mod & B_OPTION_KEY;
--- a/widget/src/cocoa/nsChildView.mm
+++ b/widget/src/cocoa/nsChildView.mm
@@ -5715,48 +5715,55 @@ static BOOL keyUpAlreadySentKeyDown = NO
// cancelled or not as there isn't actually a means to stop the drag
mDragService->FireDragEventAtSource(NS_DRAGDROP_DRAG);
dragSession->SetCanDrop(PR_FALSE);
}
else if (aMessage == NS_DRAGDROP_DROP) {
// We make the assumption that the dragOver handlers have correctly set
// the |canDrop| property of the Drag Session.
PRBool canDrop = PR_FALSE;
- if (!NS_SUCCEEDED(dragSession->GetCanDrop(&canDrop)) || !canDrop)
+ if (!NS_SUCCEEDED(dragSession->GetCanDrop(&canDrop)) || !canDrop) {
+ nsCOMPtr<nsIDOMNode> sourceNode;
+ dragSession->GetSourceNode(getter_AddRefs(sourceNode));
+ if (!sourceNode) {
+ mDragService->EndDragSession(PR_FALSE);
+ }
return NO;
+ }
}
unsigned int modifierFlags = [[NSApp currentEvent] modifierFlags];
PRUint32 action = nsIDragService::DRAGDROP_ACTION_MOVE;
// force copy = option, alias = cmd-option, default is move
if (modifierFlags & NSAlternateKeyMask) {
if (modifierFlags & NSCommandKeyMask)
action = nsIDragService::DRAGDROP_ACTION_LINK;
else
action = nsIDragService::DRAGDROP_ACTION_COPY;
}
dragSession->SetDragAction(action);
}
// set up gecko event
- nsMouseEvent geckoEvent(PR_TRUE, aMessage, nsnull, nsMouseEvent::eReal);
+ nsDragEvent geckoEvent(PR_TRUE, aMessage, nsnull);
[self convertGenericCocoaEvent:nil toGeckoEvent:&geckoEvent];
// Use our own coordinates in the gecko event.
// Convert event from gecko global coords to gecko view coords.
NSPoint localPoint = [self convertPoint:[aSender draggingLocation] fromView:nil];
geckoEvent.refPoint.x = static_cast<nscoord>(localPoint.x);
geckoEvent.refPoint.y = static_cast<nscoord>(localPoint.y);
nsAutoRetainCocoaObject kungFuDeathGrip(self);
mGeckoChild->DispatchWindowEvent(geckoEvent);
if (!mGeckoChild)
return YES;
- if (aMessage == NS_DRAGDROP_EXIT && dragSession) {
+ if ((aMessage == NS_DRAGDROP_EXIT || aMessage == NS_DRAGDROP_DROP) &&
+ dragSession) {
nsCOMPtr<nsIDOMNode> sourceNode;
dragSession->GetSourceNode(getter_AddRefs(sourceNode));
if (!sourceNode) {
// We're leaving a window while doing a drag that was
// initiated in a different app. End the drag session,
// since we're done with it for now (until the user
// drags back into mozilla).
mDragService->EndDragSession(PR_FALSE);
--- a/widget/src/cocoa/nsDragService.mm
+++ b/widget/src/cocoa/nsDragService.mm
@@ -566,13 +566,14 @@ nsDragService::EndDragSession(PRBool aDo
[mNativeDragView release];
mNativeDragView = nil;
}
if (mNativeDragEvent) {
[mNativeDragEvent release];
mNativeDragEvent = nil;
}
+ nsresult rv = nsBaseDragService::EndDragSession(aDoneDrag);
mDataItems = nsnull;
- return nsBaseDragService::EndDragSession(aDoneDrag);
+ return rv;
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
}
--- a/widget/src/gtk2/nsWindow.cpp
+++ b/widget/src/gtk2/nsWindow.cpp
@@ -2915,18 +2915,17 @@ nsWindow::OnDragMotionEvent(GtkWidget *a
// update the drag context
dragSessionGTK->TargetSetLastContext(aWidget, aDragContext, aTime);
// notify the drag service that we are starting a drag motion.
dragSessionGTK->TargetStartDragMotion();
dragService->FireDragEventAtSource(NS_DRAGDROP_DRAG);
- nsMouseEvent event(PR_TRUE, NS_DRAGDROP_OVER, innerMostWidget,
- nsMouseEvent::eReal);
+ nsDragEvent event(PR_TRUE, NS_DRAGDROP_OVER, innerMostWidget);
InitDragEvent(event);
// now that we have initialized the event update our drag status
UpdateDragStatus(event, aDragContext, dragService);
event.refPoint.x = retx;
event.refPoint.y = rety;
@@ -3024,18 +3023,17 @@ nsWindow::OnDragDropEvent(GtkWidget *aWi
// set the last window to this
mLastDragMotionWindow = innerMostWidget;
// What we do here is dispatch a new drag motion event to
// re-validate the drag target and then we do the drop. The events
// look the same except for the type.
- nsMouseEvent event(PR_TRUE, NS_DRAGDROP_OVER, innerMostWidget,
- nsMouseEvent::eReal);
+ nsDragEvent event(PR_TRUE, NS_DRAGDROP_OVER, innerMostWidget);
InitDragEvent(event);
// now that we have initialized the event update our drag status
UpdateDragStatus(event, aDragContext, dragService);
event.refPoint.x = retx;
event.refPoint.y = rety;
@@ -3096,17 +3094,17 @@ nsWindow::OnDragDataReceivedEvent(GtkWid
aSelectionData, aInfo, aTime);
}
void
nsWindow::OnDragLeave(void)
{
LOG(("nsWindow::OnDragLeave(%p)\n", this));
- nsMouseEvent event(PR_TRUE, NS_DRAGDROP_EXIT, this, nsMouseEvent::eReal);
+ nsDragEvent event(PR_TRUE, NS_DRAGDROP_EXIT, this);
nsEventStatus status;
DispatchEvent(&event, status);
nsCOMPtr<nsIDragService> dragService = do_GetService(kCDragServiceCID);
if (dragService) {
nsCOMPtr<nsIDragSession> currentDragSession;
@@ -3136,17 +3134,17 @@ nsWindow::OnDragEnter(nscoord aX, nscoor
nsCOMPtr<nsIDragService> dragService = do_GetService(kCDragServiceCID);
if (dragService) {
// Make sure that the drag service knows we're now dragging.
dragService->StartDragSession();
}
- nsMouseEvent event(PR_TRUE, NS_DRAGDROP_ENTER, this, nsMouseEvent::eReal);
+ nsDragEvent event(PR_TRUE, NS_DRAGDROP_ENTER, this);
event.refPoint.x = aX;
event.refPoint.y = aY;
nsEventStatus status;
DispatchEvent(&event, status);
}
@@ -5123,34 +5121,34 @@ theme_changed_cb (GtkSettings *settings,
nsRefPtr<nsWindow> window = data;
window->ThemeChanged();
}
//////////////////////////////////////////////////////////////////////
// These are all of our drag and drop operations
void
-nsWindow::InitDragEvent(nsMouseEvent &aEvent)
+nsWindow::InitDragEvent(nsDragEvent &aEvent)
{
// set the keyboard modifiers
gint x, y;
GdkModifierType state = (GdkModifierType)0;
gdk_window_get_pointer(NULL, &x, &y, &state);
aEvent.isShift = (state & GDK_SHIFT_MASK) ? PR_TRUE : PR_FALSE;
aEvent.isControl = (state & GDK_CONTROL_MASK) ? PR_TRUE : PR_FALSE;
aEvent.isAlt = (state & GDK_MOD1_MASK) ? PR_TRUE : PR_FALSE;
aEvent.isMeta = PR_FALSE; // GTK+ doesn't support the meta key
}
// This will update the drag action based on the information in the
// drag context. Gtk gets this from a combination of the key settings
// and what the source is offering.
void
-nsWindow::UpdateDragStatus(nsMouseEvent &aEvent,
+nsWindow::UpdateDragStatus(nsDragEvent &aEvent,
GdkDragContext *aDragContext,
nsIDragService *aDragService)
{
// default is to do nothing
int action = nsIDragService::DRAGDROP_ACTION_NONE;
// set the default just in case nothing matches below
if (aDragContext->actions & GDK_ACTION_DEFAULT)
--- a/widget/src/gtk2/nsWindow.h
+++ b/widget/src/gtk2/nsWindow.h
@@ -437,18 +437,18 @@ private:
// This bitmap tracks which pixels are transparent. We don't support
// full translucency at this time; each pixel is either fully opaque
// or fully transparent.
gchar* mTransparencyBitmap;
// all of our DND stuff
// this is the last window that had a drag event happen on it.
static nsWindow *mLastDragMotionWindow;
- void InitDragEvent (nsMouseEvent &aEvent);
- void UpdateDragStatus (nsMouseEvent &aEvent,
+ void InitDragEvent (nsDragEvent &aEvent);
+ void UpdateDragStatus (nsDragEvent &aEvent,
GdkDragContext *aDragContext,
nsIDragService *aDragService);
// this is everything we need to be able to fire motion events
// repeatedly
GtkWidget *mDragMotionWidget;
GdkDragContext *mDragMotionContext;
gint mDragMotionX;
--- a/widget/src/os2/nsWindow.cpp
+++ b/widget/src/os2/nsWindow.cpp
@@ -522,17 +522,17 @@ PRBool nsWindow::DispatchCommandEvent(PR
//-------------------------------------------------------------------------
//
// Dispatch DragDrop (target) event
//
//-------------------------------------------------------------------------
PRBool nsWindow::DispatchDragDropEvent(PRUint32 aMsg)
{
- nsMouseEvent event(PR_TRUE, aMsg, this, nsMouseEvent::eReal);
+ nsDragEvent event(PR_TRUE, aMsg, this);
InitEvent(event);
event.isShift = WinIsKeyDown(VK_SHIFT);
event.isControl = WinIsKeyDown(VK_CTRL);
event.isAlt = WinIsKeyDown(VK_ALT) || WinIsKeyDown(VK_ALTGRAF);
event.isMeta = PR_FALSE;
return DispatchWindowEvent(&event);
--- a/widget/src/photon/nsWidget.cpp
+++ b/widget/src/photon/nsWidget.cpp
@@ -1213,17 +1213,17 @@ void nsWidget::ProcessDrag( PhEvent_t *e
event->subtype = old_subtype;
// Clear the cached value
currSession->SetCanDrop(PR_FALSE);
}
void nsWidget::DispatchDragDropEvent( PhEvent_t *phevent, PRUint32 aEventType, PhPoint_t *pos ) {
nsEventStatus status;
- nsMouseEvent event(PR_TRUE, 0, nsnull, nsMouseEvent::eReal);
+ nsDragEvent event(PR_TRUE, 0, nsnull);
InitEvent( event, aEventType );
event.refPoint.x = pos->x;
event.refPoint.y = pos->y;
PhDndEvent_t *dnd = ( PhDndEvent_t * ) PhGetData( phevent );
event.isControl = ( dnd->key_mods & Pk_KM_Ctrl ) ? PR_TRUE : PR_FALSE;
--- a/widget/src/windows/nsNativeDragTarget.cpp
+++ b/widget/src/windows/nsNativeDragTarget.cpp
@@ -188,17 +188,17 @@ IsKeyDown(char key)
}
//-----------------------------------------------------
void
nsNativeDragTarget::DispatchDragDropEvent(PRUint32 aEventType, POINTL aPT)
{
nsEventStatus status;
- nsMouseEvent event(PR_TRUE, aEventType, mWindow, nsMouseEvent::eReal);
+ nsDragEvent event(PR_TRUE, aEventType, mWindow);
nsWindow * win = static_cast<nsWindow *>(mWindow);
win->InitEvent(event);
POINT cpos;
cpos.x = aPT.x;
cpos.y = aPT.y;
--- a/widget/src/xpwidgets/nsBaseDragService.cpp
+++ b/widget/src/xpwidgets/nsBaseDragService.cpp
@@ -50,20 +50,21 @@
#include "nsIInterfaceRequestorUtils.h"
#include "nsIFrame.h"
#include "nsIDocument.h"
#include "nsIContent.h"
#include "nsIPresShell.h"
#include "nsIViewManager.h"
#include "nsIScrollableView.h"
#include "nsIDOMNode.h"
-#include "nsIDOMMouseEvent.h"
+#include "nsIDOMDragEvent.h"
#include "nsISelection.h"
#include "nsISelectionPrivate.h"
#include "nsPresContext.h"
+#include "nsIDOMDataTransfer.h"
#include "nsIEventStateManager.h"
#include "nsICanvasElement.h"
#include "nsIImage.h"
#include "nsIImageLoadingContent.h"
#include "gfxIImageFrame.h"
#include "imgIContainer.h"
#include "imgIRequest.h"
#include "nsIViewObserver.h"
@@ -190,16 +191,31 @@ nsBaseDragService::GetData(nsITransferab
//-------------------------------------------------------------------------
NS_IMETHODIMP
nsBaseDragService::IsDataFlavorSupported(const char *aDataFlavor,
PRBool *_retval)
{
return NS_ERROR_FAILURE;
}
+NS_IMETHODIMP
+nsBaseDragService::GetDataTransfer(nsIDOMDataTransfer** aDataTransfer)
+{
+ *aDataTransfer = mDataTransfer;
+ NS_IF_ADDREF(*aDataTransfer);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBaseDragService::SetDataTransfer(nsIDOMDataTransfer* aDataTransfer)
+{
+ mDataTransfer = aDataTransfer;
+ return NS_OK;
+}
+
//-------------------------------------------------------------------------
NS_IMETHODIMP
nsBaseDragService::InvokeDragSession(nsIDOMNode *aDOMNode,
nsISupportsArray* aTransferableArray,
nsIScriptableRegion* aDragRgn,
PRUint32 aActionType)
{
NS_ENSURE_TRUE(aDOMNode, NS_ERROR_INVALID_ARG);
@@ -234,53 +250,60 @@ nsBaseDragService::InvokeDragSession(nsI
NS_IMETHODIMP
nsBaseDragService::InvokeDragSessionWithImage(nsIDOMNode* aDOMNode,
nsISupportsArray* aTransferableArray,
nsIScriptableRegion* aRegion,
PRUint32 aActionType,
nsIDOMNode* aImage,
PRInt32 aImageX, PRInt32 aImageY,
- nsIDOMMouseEvent* aDragEvent)
+ nsIDOMDragEvent* aDragEvent,
+ nsIDOMDataTransfer* aDataTransfer)
{
NS_ENSURE_TRUE(aDragEvent, NS_ERROR_NULL_POINTER);
+ NS_ENSURE_TRUE(aDataTransfer, NS_ERROR_NULL_POINTER);
NS_ENSURE_TRUE(mSuppressLevel == 0, NS_ERROR_FAILURE);
+ mDataTransfer = aDataTransfer;
mSelection = nsnull;
mHasImage = PR_TRUE;
mImage = aImage;
mImageX = aImageX;
mImageY = aImageY;
aDragEvent->GetScreenX(&mScreenX);
aDragEvent->GetScreenY(&mScreenY);
return InvokeDragSession(aDOMNode, aTransferableArray, aRegion, aActionType);
}
NS_IMETHODIMP
nsBaseDragService::InvokeDragSessionWithSelection(nsISelection* aSelection,
nsISupportsArray* aTransferableArray,
PRUint32 aActionType,
- nsIDOMMouseEvent* aDragEvent)
+ nsIDOMDragEvent* aDragEvent,
+ nsIDOMDataTransfer* aDataTransfer)
{
NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
NS_ENSURE_TRUE(aDragEvent, NS_ERROR_NULL_POINTER);
NS_ENSURE_TRUE(mSuppressLevel == 0, NS_ERROR_FAILURE);
+ mDataTransfer = aDataTransfer;
mSelection = aSelection;
mHasImage = PR_TRUE;
mImage = nsnull;
mImageX = 0;
mImageY = 0;
aDragEvent->GetScreenX(&mScreenX);
aDragEvent->GetScreenY(&mScreenY);
// just get the focused node from the selection
+ // XXXndeakin this should actually be the deepest node that contains both
+ // endpoints of the selection
nsCOMPtr<nsIDOMNode> node;
aSelection->GetFocusNode(getter_AddRefs(node));
return InvokeDragSession(node, aTransferableArray, nsnull, aActionType);
}
//-------------------------------------------------------------------------
NS_IMETHODIMP
@@ -324,16 +347,17 @@ nsBaseDragService::EndDragSession(PRBool
FireDragEventAtSource(NS_DRAGDROP_END);
mDoingDrag = PR_FALSE;
// release the source we've been holding on to.
mSourceDocument = nsnull;
mSourceNode = nsnull;
mSelection = nsnull;
+ mDataTransfer = nsnull;
mHasImage = PR_FALSE;
mImage = nsnull;
mImageX = 0;
mImageY = 0;
mScreenX = -1;
mScreenY = -1;
return NS_OK;
@@ -343,17 +367,17 @@ NS_IMETHODIMP
nsBaseDragService::FireDragEventAtSource(PRUint32 aMsg)
{
if (mSourceNode && !mSuppressLevel) {
nsCOMPtr<nsIDocument> doc = do_QueryInterface(mSourceDocument);
if (doc) {
nsCOMPtr<nsIPresShell> presShell = doc->GetPrimaryShell();
if (presShell) {
nsEventStatus status = nsEventStatus_eIgnore;
- nsMouseEvent event(PR_TRUE, aMsg, nsnull, nsMouseEvent::eReal);
+ nsDragEvent event(PR_TRUE, aMsg, nsnull);
nsCOMPtr<nsIContent> content = do_QueryInterface(mSourceNode);
return presShell->HandleDOMEventWithTarget(content, &event, &status);
}
}
}
return NS_OK;
--- a/widget/src/xpwidgets/nsBaseDragService.h
+++ b/widget/src/xpwidgets/nsBaseDragService.h
@@ -40,16 +40,17 @@
#include "nsIDragService.h"
#include "nsIDragSession.h"
#include "nsITransferable.h"
#include "nsISupportsArray.h"
#include "nsIDOMDocument.h"
#include "nsCOMPtr.h"
#include "nsIRenderingContext.h"
+#include "nsIDOMDataTransfer.h"
#include "gfxImageSurface.h"
// translucency level for drag images
#define DRAG_TRANSLUCENCY 0.65
class nsIDOMNode;
class nsIFrame;
@@ -126,16 +127,17 @@ protected:
// true if mImage should be used to set a drag image
PRPackedBool mHasImage;
PRUint32 mDragAction;
nsSize mTargetSize;
nsCOMPtr<nsIDOMNode> mSourceNode;
nsCOMPtr<nsIDOMDocument> mSourceDocument; // the document at the drag source. will be null
// if it came from outside the app.
+ nsCOMPtr<nsIDOMDataTransfer> mDataTransfer;
// used to determine the image to appear on the cursor while dragging
nsCOMPtr<nsIDOMNode> mImage;
// offset of cursor within the image
PRInt32 mImageX;
PRInt32 mImageY;
// set if a selection is being dragged
--- a/widget/src/xpwidgets/nsPrimitiveHelpers.cpp
+++ b/widget/src/xpwidgets/nsPrimitiveHelpers.cpp
@@ -129,16 +129,18 @@ nsPrimitiveHelpers :: CreatePrimitiveFor
//
void
nsPrimitiveHelpers :: CreateDataFromPrimitive ( const char* aFlavor, nsISupports* aPrimitive,
void** aDataBuff, PRUint32 aDataLen )
{
if ( !aDataBuff )
return;
+ *aDataBuff = nsnull;
+
if ( strcmp(aFlavor,kTextMime) == 0 ) {
nsCOMPtr<nsISupportsCString> plainText ( do_QueryInterface(aPrimitive) );
if ( plainText ) {
nsCAutoString data;
plainText->GetData ( data );
*aDataBuff = ToNewCString(data);
}
}