Bug 508479 - HTML5 Drag and Drop: Drop event on elements that are not drop targets, r=enn, sr=sicking

This commit is contained in:
Olli Pettay 2009-10-29 13:11:02 +02:00
Родитель 72870cbe12
Коммит 62344a3ec7
13 изменённых файлов: 250 добавлений и 5 удалений

Просмотреть файл

@ -328,7 +328,25 @@ nsContentAreaDragDrop::DragOver(nsIDOMDragEvent* inEvent)
}
}
session->SetCanDrop(dropAllowed);
nsCOMPtr<nsIDOMNSEvent> e = do_QueryInterface(inEvent);
NS_ENSURE_STATE(e);
nsCOMPtr<nsIDOMEventTarget> target;
e->GetOriginalTarget(getter_AddRefs(target));
nsCOMPtr<nsINode> node = do_QueryInterface(target);
if (!node) {
nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(target);
if (win) {
node = do_QueryInterface(win->GetExtantDocument());
}
}
PRBool isChrome =
node ? nsContentUtils::IsChromeDoc(node->GetOwnerDoc()) : PR_FALSE;
if (isChrome) {
session->SetCanDrop(dropAllowed);
} else if (dropAllowed) {
inEvent->PreventDefault();
}
return NS_OK;
}

Просмотреть файл

@ -459,7 +459,23 @@ nsDOMEvent::PreventDefault()
{
if (!(mEvent->flags & NS_EVENT_FLAG_CANT_CANCEL)) {
mEvent->flags |= NS_EVENT_FLAG_NO_DEFAULT;
// Need to set an extra flag for drag events.
if (mEvent->eventStructType == NS_DRAG_EVENT &&
NS_IS_TRUSTED_EVENT(mEvent)) {
nsCOMPtr<nsINode> node = do_QueryInterface(mEvent->currentTarget);
if (!node) {
nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(mEvent->currentTarget);
if (win) {
node = do_QueryInterface(win->GetExtantDocument());
}
}
if (node && !nsContentUtils::IsChromeDoc(node->GetOwnerDoc())) {
mEvent->flags |= NS_EVENT_FLAG_NO_DEFAULT_CALLED_IN_CONTENT;
}
}
}
return NS_OK;
}

Просмотреть файл

@ -46,6 +46,8 @@
#include "nsMutationEvent.h"
#include NEW_H
#include "nsFixedSizeAllocator.h"
#include "nsINode.h"
#include "nsPIDOMWindow.h"
#define NS_TARGET_CHAIN_FORCE_CONTENT_DISPATCH (1 << 0)
#define NS_TARGET_CHAIN_WANTS_WILL_HANDLE_EVENT (1 << 1)
@ -444,6 +446,28 @@ nsEventDispatcher::Dispatch(nsISupports* aTarget,
NS_ERROR_ILLEGAL_VALUE);
NS_ASSERTION(!aTargets || !aEvent->message, "Wrong parameters!");
if (aEvent->flags & NS_EVENT_FLAG_ONLY_CHROME_DISPATCH) {
nsCOMPtr<nsINode> node = do_QueryInterface(aTarget);
if (!node) {
nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(aTarget);
if (win) {
node = do_QueryInterface(win->GetExtantDocument());
}
}
NS_ENSURE_STATE(node);
nsIDocument* doc = node->GetOwnerDoc();
if (!nsContentUtils::IsChromeDoc(doc)) {
nsPIDOMWindow* win = doc ? doc->GetInnerWindow() : nsnull;
// If we can't dispatch the event to chrome, do nothing.
NS_ENSURE_TRUE(win && win->GetChromeEventHandler(), NS_OK);
// Set the target to be the original dispatch target,
aEvent->target = aTarget;
// but use chrome event handler for event target chain.
aTarget = win->GetChromeEventHandler();
}
}
nsCOMPtr<nsPIDOMEventTarget> target = do_QueryInterface(aTarget);
#ifdef DEBUG
if (aDOMEvent) {

Просмотреть файл

@ -3042,6 +3042,13 @@ nsEventStateManager::PostHandleEvent(nsPresContext* aPresContext,
if (!dragSession)
break;
// Reset the flag.
dragSession->SetOnlyChromeDrop(PR_FALSE);
if (mPresContext) {
EnsureDocument(mPresContext);
}
PRBool isChromeDoc = nsContentUtils::IsChromeDoc(mDocument);
// the initial dataTransfer is the one from the dragstart event that
// was set on the dragSession when the drag began.
nsCOMPtr<nsIDOMNSDataTransfer> dataTransfer;
@ -3117,6 +3124,16 @@ nsEventStateManager::PostHandleEvent(nsPresContext* aPresContext,
// inform the drag session that a drop is allowed on this node.
dragSession->SetDragAction(action);
dragSession->SetCanDrop(action != nsIDragService::DRAGDROP_ACTION_NONE);
// For now, do this only for dragover.
//XXXsmaug dragenter needs some more work.
if (aEvent->message == NS_DRAGDROP_OVER && !isChromeDoc) {
// Someone has called preventDefault(), check whether is was content.
dragSession->SetOnlyChromeDrop(
!(aEvent->flags & NS_EVENT_FLAG_NO_DEFAULT_CALLED_IN_CONTENT));
}
} else if (aEvent->message == NS_DRAGDROP_OVER && !isChromeDoc) {
dragSession->SetCanDrop(PR_FALSE);
}
// now set the drop effect in the initial dataTransfer. This ensures

Просмотреть файл

@ -80,6 +80,7 @@ _TEST_FILES = \
test_bug489671.html \
test_bug493251.html \
test_bug502818.html \
test_bug508479.html \
test_bug517851.html \
$(NULL)

Просмотреть файл

@ -0,0 +1,81 @@
<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>
<script>
var gGotHandlingDrop = false;
var gGotNotHandlingDrop = false;
SimpleTest.waitForExplicitFinish();
function fireEvent(target, event) {
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
var utils =
window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
getInterface(Components.interfaces.nsIDOMWindowUtils);
utils.dispatchDOMEventViaPresShell(target, event, true);
}
function fireDrop(element, dragData, effectAllowed) {
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
var ds = Components.classes["@mozilla.org/widget/dragservice;1"].
getService(Components.interfaces.nsIDragService);
ds.startDragSession();
var event = document.createEvent("DragEvents");
event.initDragEvent("dragover", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null, null);
fireEvent(element, event);
event = document.createEvent("DragEvents");
event.initDragEvent("drop", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null, null);
fireEvent(element, event);
ds.endDragSession(false);
ok(!ds.getCurrentSession(), "There shouldn't be a drag session anymore!");
}
function runTests()
{
var targetHandling = document.getElementById("handling_target");
fireDrop(targetHandling, [{"test/plain": "Hello!"}]);
is(gGotHandlingDrop, true, "Got drop on accepting element (1)");
is(gGotNotHandlingDrop, false, "Didn't get drop on unaccepting element (1)");
// reset
gGotHandlingDrop = false;
gGotNotHandlingDrop = false;
var targetNotHandling = document.getElementById("nothandling_target");
fireDrop(targetNotHandling, [{"test/plain": "Hello!"}]);
is(gGotHandlingDrop, false, "Didn't get drop on accepting element (2)");
is(gGotNotHandlingDrop, false, "Didn't get drop on unaccepting element (2)");
SimpleTest.finish();
}
</script>
<body onload="window.setTimeout(runTests, 0);">
<img style="width: 100px; height: 100px;"
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"
id="handling_target"
ondragenter="event.preventDefault()"
ondragover="event.preventDefault()"
ondrop="gGotHandlingDrop = true;">
<img style="width: 100px; height: 100px;"
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"
id="nothandling_target"
ondrop="gGotNotHandlingDrop = true;">
</body>
</html>

Просмотреть файл

@ -40,7 +40,7 @@
#include "nsDOMClassInfo.h"
#include "nsDOMError.h"
#include "nsIDOMNSEvent.h"
#include "nsIPrivateDOMEvent.h"
#include "nsDOMWindowUtils.h"
#include "nsGlobalWindow.h"
#include "nsIDocument.h"
@ -920,3 +920,32 @@ nsDOMWindowUtils::GetCOWForObject()
cc->SetReturnValueWasSet(PR_TRUE);
return NS_OK;
}
NS_IMETHODIMP
nsDOMWindowUtils::DispatchDOMEventViaPresShell(nsIDOMNode* aTarget,
nsIDOMEvent* aEvent,
PRBool aTrusted,
PRBool* aRetVal)
{
if (!nsContentUtils::IsCallerTrustedForRead()) {
return NS_ERROR_DOM_SECURITY_ERR;
}
nsPresContext* presContext = GetPresContext();
NS_ENSURE_STATE(presContext);
nsCOMPtr<nsIPresShell> shell = presContext->GetPresShell();
NS_ENSURE_STATE(shell);
nsCOMPtr<nsIPrivateDOMEvent> event = do_QueryInterface(aEvent);
NS_ENSURE_STATE(event);
event->SetTrusted(aTrusted);
nsEvent* internalEvent = event->GetInternalNSEvent();
NS_ENSURE_STATE(internalEvent);
nsCOMPtr<nsIContent> content = do_QueryInterface(aTarget);
NS_ENSURE_STATE(content);
nsEventStatus status = nsEventStatus_eIgnore;
shell->HandleEventWithTarget(internalEvent, nsnull, content,
&status);
*aRetVal = (status != nsEventStatus_eConsumeNoDefault);
return NS_OK;
}

Просмотреть файл

@ -45,10 +45,12 @@
* getInterface on a DOMWindow.
*/
interface nsIDOMNode;
interface nsIDOMElement;
interface nsIDOMHTMLCanvasElement;
interface nsIDOMEvent;
[scriptable, uuid(4171ea1a-3752-4bc3-8c66-1b2936ecde7a)]
[scriptable, uuid(4775e623-d596-4364-8637-0968a5ce5e3d)]
interface nsIDOMWindowUtils : nsISupports {
/**
@ -403,4 +405,20 @@ interface nsIDOMWindowUtils : nsISupports {
* Get the number of screen pixels per CSS pixel.
*/
readonly attribute float screenPixelsPerCSSPixel;
/**
* Dispatches aEvent via the nsIPresShell object of the window's document.
* The event is dispatched to aTarget, which should be an object
* which implements nsIContent interface (#element, #text, etc).
*
* Cannot be accessed from unprivileged context (not
* content-accessible) Will throw a DOM security error if called
* without UniversalXPConnect privileges.
*
* @note Event handlers won't get aEvent as parameter, but a similar event.
* Also, aEvent should not be reused.
*/
boolean dispatchDOMEventViaPresShell(in nsIDOMNode aTarget,
in nsIDOMEvent aEvent,
in boolean aTrusted);
};

Просмотреть файл

@ -6488,6 +6488,17 @@ PresShell::HandleEventInternal(nsEvent* aEvent, nsIView *aView,
case NS_KEY_DOWN:
case NS_KEY_UP:
isHandlingUserInput = PR_TRUE;
break;
case NS_DRAGDROP_DROP:
nsCOMPtr<nsIDragSession> session = nsContentUtils::GetDragSession();
if (session) {
PRBool onlyChromeDrop = PR_FALSE;
session->GetOnlyChromeDrop(&onlyChromeDrop);
if (onlyChromeDrop) {
aEvent->flags |= NS_EVENT_FLAG_ONLY_CHROME_DISPATCH;
}
}
break;
}
}

Просмотреть файл

@ -132,6 +132,12 @@ class nsHashKey;
// events.
#define NS_EVENT_FLAG_SYNTHETIC_TEST_EVENT 0x1000
// Use this flag if the event should be dispatched only to chrome.
#define NS_EVENT_FLAG_ONLY_CHROME_DISPATCH 0x2000
// A flag for drag&drop handling.
#define NS_EVENT_FLAG_NO_DEFAULT_CALLED_IN_CONTENT 0x4000
#define NS_PRIV_EVENT_UNTRUSTED_PERMITTED 0x8000
#define NS_EVENT_CAPTURE_MASK (~(NS_EVENT_FLAG_BUBBLE | NS_EVENT_FLAG_NO_CONTENT_DISPATCH))

Просмотреть файл

@ -53,7 +53,7 @@ interface nsIDOMDocument;
interface nsIDOMNode;
interface nsIDOMDataTransfer;
[scriptable, uuid(15860D52-FE2C-4DDD-AC50-9C23E24916C4)]
[scriptable, uuid(fde41f6a-c710-46f8-a0a8-1ff76ca4ff57)]
interface nsIDragSession : nsISupports
{
/**
@ -61,6 +61,11 @@ interface nsIDragSession : nsISupports
* usually the target "frame" sets this so the native system can render the correct feedback
*/
attribute boolean canDrop;
/**
* Indicates if the drop event should be dispatched only to chrome.
*/
attribute boolean onlyChromeDrop;
/**
* Sets the action (copy, move, link, et.c) for the current drag

Просмотреть файл

@ -76,7 +76,8 @@
#define DRAGIMAGES_PREF "nglayout.enable_drag_images"
nsBaseDragService::nsBaseDragService()
: mCanDrop(PR_FALSE), mDoingDrag(PR_FALSE), mHasImage(PR_FALSE), mUserCancelled(PR_FALSE),
: mCanDrop(PR_FALSE), mOnlyChromeDrop(PR_FALSE), mDoingDrag(PR_FALSE),
mHasImage(PR_FALSE), mUserCancelled(PR_FALSE),
mDragAction(DRAGDROP_ACTION_NONE), mTargetSize(0,0),
mImageX(0), mImageY(0), mScreenX(-1), mScreenY(-1), mSuppressLevel(0)
{
@ -103,6 +104,21 @@ nsBaseDragService::GetCanDrop(PRBool * aCanDrop)
*aCanDrop = mCanDrop;
return NS_OK;
}
//---------------------------------------------------------
NS_IMETHODIMP
nsBaseDragService::SetOnlyChromeDrop(PRBool aOnlyChrome)
{
mOnlyChromeDrop = aOnlyChrome;
return NS_OK;
}
//---------------------------------------------------------
NS_IMETHODIMP
nsBaseDragService::GetOnlyChromeDrop(PRBool* aOnlyChrome)
{
*aOnlyChrome = mOnlyChromeDrop;
return NS_OK;
}
//---------------------------------------------------------
NS_IMETHODIMP
@ -323,6 +339,8 @@ nsBaseDragService::StartDragSession()
return NS_ERROR_FAILURE;
}
mDoingDrag = PR_TRUE;
// By default dispatch drop also to content.
mOnlyChromeDrop = PR_FALSE;
return NS_OK;
}

Просмотреть файл

@ -128,6 +128,7 @@ protected:
PRInt32* aScreenX, PRInt32* aScreenY);
PRPackedBool mCanDrop;
PRPackedBool mOnlyChromeDrop;
PRPackedBool mDoingDrag;
// true if mImage should be used to set a drag image
PRPackedBool mHasImage;