mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 12:51:06 +00:00
Bug 1603074 - part 1: Make synthesizePlainDragAndDrop()
synthesize drag events without DataTransfer
object r=smaug
`synthesizePlainDragAndDrop()` synthesizes drag events with `DataTransfer` object which is set to `DragEvent.dataTransfer` of `dragstart` after starting drag session explicitly. However, this causes `EventStateManager::DoDefaltDragStart()` does not initialize `nsIDragService` instance. Therefore, synthesized drag events cannot work with editor because `DragEvent::GetMozSourceNode()` returns `nullptr` due to `nsIDragSession::GetSourceNode()` returning `nullptr`. On the other hand, synthesized drag events cannot use `nsIDragService::InvodeDragSession()` normally because of hitting an assertion. https://searchfox.org/mozilla-central/rev/690e903ef689a4eca335b96bd903580394864a1c/widget/nsBaseDragService.cpp#230-233 This patch does: - mark drag events caused by synthesized mouse events as "synthesized for tests" - make `synthesizePlainDragAndDrop()` stop using `nsIDragService.startDragSession()` - make `nsBaseDragService` initialize and start session even for synthesized `dragstart` event - make `synthesizePlainDragAndDrop()` stop synthesizing drag events with `DataTransfer` object since it's normal behavior and it'll be initialized with `nsIDragService::GetDataTransfer()` - make `nsBaseDragService` store `effectAllowed` for the session only when it's synthesized session because it's required at initializing synthesized default `dropEffect` value of `dragenter`, `dragover`, `dragexit` and `drop` events' `dataTransfer` - make all tests which use `nsIDragService.startDragSession()` use new API, `nsIDragService.startDragSessionForTests()` to initialize session's `effectAllowed` value - make `EventStateManager::PostHandleEvent()` set drag end point of the test session to `eDrop` event's screen point - make `synthesizePlainDragAndDrop()` set drag end point of the session if it does not synthesize `drop` event because following `endDragSession()` use it at dispatching `dragend` event on the source element Additionally, this adds `dumpFunc` new param to `synthesizePlainDragAndDrop()` because it's really useful to investigate the reason why requesting DnD isn't performed as expected. Differential Revision: https://phabricator.services.mozilla.com/D57425 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
8d04c3f533
commit
87ca855ece
@ -8,7 +8,11 @@ function simulateItemDragAndEnd(aToDrag, aTarget) {
|
||||
Ci.nsIDragService
|
||||
);
|
||||
|
||||
ds.startDragSession();
|
||||
ds.startDragSessionForTests(
|
||||
Ci.nsIDragService.DRAGDROP_ACTION_MOVE |
|
||||
Ci.nsIDragService.DRAGDROP_ACTION_COPY |
|
||||
Ci.nsIDragService.DRAGDROP_ACTION_LINK
|
||||
);
|
||||
try {
|
||||
var [result, dataTransfer] = EventUtils.synthesizeDragOver(
|
||||
aToDrag.parentNode,
|
||||
|
@ -39,7 +39,11 @@ add_task(async function() {
|
||||
Ci.nsIDragService
|
||||
);
|
||||
|
||||
ds.startDragSession();
|
||||
ds.startDragSessionForTests(
|
||||
Ci.nsIDragService.DRAGDROP_ACTION_MOVE |
|
||||
Ci.nsIDragService.DRAGDROP_ACTION_COPY |
|
||||
Ci.nsIDragService.DRAGDROP_ACTION_LINK
|
||||
);
|
||||
try {
|
||||
var [result, dataTransfer] = EventUtils.synthesizeDragOver(
|
||||
identityBox,
|
||||
|
@ -1703,13 +1703,15 @@ nsDOMWindowUtils::GetFullZoom(float* aFullZoom) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::DispatchDOMEventViaPresShell(nsINode* aTarget, Event* aEvent,
|
||||
bool* aRetVal) {
|
||||
NS_IMETHODIMP nsDOMWindowUtils::DispatchDOMEventViaPresShellForTesting(
|
||||
nsINode* aTarget, Event* aEvent, bool* aRetVal) {
|
||||
NS_ENSURE_STATE(aEvent);
|
||||
aEvent->SetTrusted(true);
|
||||
WidgetEvent* internalEvent = aEvent->WidgetEventPtr();
|
||||
NS_ENSURE_STATE(internalEvent);
|
||||
// This API is currently used only by EventUtils.js. Thus we should always
|
||||
// set mIsSynthesizedForTests to true.
|
||||
internalEvent->mFlags.mIsSynthesizedForTests = true;
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(aTarget);
|
||||
NS_ENSURE_STATE(content);
|
||||
nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryReferent(mWindow);
|
||||
|
@ -698,9 +698,21 @@ nsresult EventStateManager::PreHandleEvent(nsPresContext* aPresContext,
|
||||
KillClickHoldTimer();
|
||||
}
|
||||
break;
|
||||
case eDragOver:
|
||||
case eDragOver: {
|
||||
WidgetDragEvent* dragEvent = aEvent->AsDragEvent();
|
||||
MOZ_ASSERT(dragEvent);
|
||||
if (dragEvent->mFlags.mIsSynthesizedForTests) {
|
||||
dragEvent->InitDropEffectForTests();
|
||||
}
|
||||
// Send the enter/exit events before eDrop.
|
||||
GenerateDragDropEnterExit(aPresContext, aEvent->AsDragEvent());
|
||||
GenerateDragDropEnterExit(aPresContext, dragEvent);
|
||||
break;
|
||||
}
|
||||
case eDrop:
|
||||
if (aEvent->mFlags.mIsSynthesizedForTests) {
|
||||
MOZ_ASSERT(aEvent->AsDragEvent());
|
||||
aEvent->AsDragEvent()->InitDropEffectForTests();
|
||||
}
|
||||
break;
|
||||
|
||||
case eKeyPress: {
|
||||
@ -1899,6 +1911,8 @@ void EventStateManager::GenerateDragGesture(nsPresContext* aPresContext,
|
||||
|
||||
// get the widget from the target frame
|
||||
WidgetDragEvent startEvent(aEvent->IsTrusted(), eDragStart, widget);
|
||||
startEvent.mFlags.mIsSynthesizedForTests =
|
||||
aEvent->mFlags.mIsSynthesizedForTests;
|
||||
FillInEventFromGestureDown(&startEvent);
|
||||
|
||||
startEvent.mDataTransfer = dataTransfer;
|
||||
@ -2076,10 +2090,14 @@ bool EventStateManager::DoDefaultDragStart(
|
||||
// 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. Make sure to return true to indicate that a drag
|
||||
// began.
|
||||
// began. However, if we're handling drag session for synthesized events,
|
||||
// we need to initialize some information of the session. Therefore, we
|
||||
// need to keep going for synthesized case.
|
||||
nsCOMPtr<nsIDragSession> dragSession;
|
||||
dragService->GetCurrentSession(getter_AddRefs(dragSession));
|
||||
if (dragSession) return true;
|
||||
if (dragSession && !dragSession->IsSynthesizedForTests()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 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.
|
||||
@ -2098,33 +2116,48 @@ bool EventStateManager::DoDefaultDragStart(
|
||||
nsCOMPtr<nsIContent> dragTarget = aDataTransfer->GetDragTarget();
|
||||
if (!dragTarget) {
|
||||
dragTarget = aDragTarget;
|
||||
if (!dragTarget) return false;
|
||||
if (!dragTarget) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// check which drag effect should initially be used. If the effect was not
|
||||
// set, just use all actions, otherwise Windows won't allow a drop.
|
||||
uint32_t action = aDataTransfer->EffectAllowedInt();
|
||||
if (action == nsIDragService::DRAGDROP_ACTION_UNINITIALIZED)
|
||||
if (action == nsIDragService::DRAGDROP_ACTION_UNINITIALIZED) {
|
||||
action = nsIDragService::DRAGDROP_ACTION_COPY |
|
||||
nsIDragService::DRAGDROP_ACTION_MOVE |
|
||||
nsIDragService::DRAGDROP_ACTION_LINK;
|
||||
}
|
||||
|
||||
// get any custom drag image that was set
|
||||
int32_t imageX, imageY;
|
||||
RefPtr<Element> dragImage = aDataTransfer->GetDragImage(&imageX, &imageY);
|
||||
|
||||
nsCOMPtr<nsIArray> transArray = aDataTransfer->GetTransferables(dragTarget);
|
||||
if (!transArray) return false;
|
||||
if (!transArray) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// After this function returns, the DataTransfer will be cleared so it appears
|
||||
// empty to content. We need to pass a DataTransfer into the Drag Session, so
|
||||
// we need to make a copy.
|
||||
RefPtr<DataTransfer> dataTransfer;
|
||||
aDataTransfer->Clone(aDragTarget, eDrop, aDataTransfer->MozUserCancelled(),
|
||||
false, getter_AddRefs(dataTransfer));
|
||||
if (!dragSession) {
|
||||
// After this function returns, the DataTransfer will be cleared so it
|
||||
// appears empty to content. We need to pass a DataTransfer into the Drag
|
||||
// Session, so we need to make a copy.
|
||||
aDataTransfer->Clone(aDragTarget, eDrop, aDataTransfer->MozUserCancelled(),
|
||||
false, getter_AddRefs(dataTransfer));
|
||||
|
||||
// Copy over the drop effect, as Clone doesn't copy it for us.
|
||||
dataTransfer->SetDropEffectInt(aDataTransfer->DropEffectInt());
|
||||
// Copy over the drop effect, as Clone doesn't copy it for us.
|
||||
dataTransfer->SetDropEffectInt(aDataTransfer->DropEffectInt());
|
||||
} else {
|
||||
MOZ_ASSERT(dragSession->IsSynthesizedForTests());
|
||||
MOZ_ASSERT(aDragEvent->mFlags.mIsSynthesizedForTests);
|
||||
// If we're initializing synthesized drag session, we should use given
|
||||
// DataTransfer as is because it'll be used with following drag events
|
||||
// in any tests, therefore it should be set to nsIDragSession.dataTransfer
|
||||
// because it and DragEvent.dataTransfer should be same instance.
|
||||
dataTransfer = aDataTransfer;
|
||||
}
|
||||
|
||||
// XXXndeakin don't really want to create a new drag DOM event
|
||||
// here, but we need something to pass to the InvokeDragSession
|
||||
@ -3613,7 +3646,13 @@ nsresult EventStateManager::PostHandleEvent(nsPresContext* aPresContext,
|
||||
uint32_t dropEffect = nsIDragService::DRAGDROP_ACTION_NONE;
|
||||
uint32_t action = nsIDragService::DRAGDROP_ACTION_NONE;
|
||||
if (nsEventStatus_eConsumeNoDefault == *aStatus) {
|
||||
// if the event has a dataTransfer set, use it.
|
||||
// If the event has initialized its mDataTransfer, use it.
|
||||
// Or the event has not been initialized its mDataTransfer, but
|
||||
// it's set before dispatch because of synthesized, but without
|
||||
// testing session (e.g., emulating drag from another app), use it
|
||||
// coming from outside.
|
||||
// XXX Perhaps, for the latter case, we need new API because we don't
|
||||
// have a chance to initialize allowed effects of the session.
|
||||
if (dragEvent->mDataTransfer) {
|
||||
// get the dataTransfer and the dropEffect that was set on it
|
||||
dataTransfer = dragEvent->mDataTransfer;
|
||||
@ -3688,6 +3727,29 @@ nsresult EventStateManager::PostHandleEvent(nsPresContext* aPresContext,
|
||||
} break;
|
||||
|
||||
case eDrop: {
|
||||
if (aEvent->mFlags.mIsSynthesizedForTests) {
|
||||
if (nsCOMPtr<nsIDragSession> dragSession =
|
||||
nsContentUtils::GetDragSession()) {
|
||||
MOZ_ASSERT(dragSession->IsSynthesizedForTests());
|
||||
RefPtr<Document> sourceDocument;
|
||||
DebugOnly<nsresult> rvIgnored =
|
||||
dragSession->GetSourceDocument(getter_AddRefs(sourceDocument));
|
||||
NS_WARNING_ASSERTION(
|
||||
NS_SUCCEEDED(rvIgnored),
|
||||
"nsIDragSession::GetSourceDocument() failed, but ignored");
|
||||
// If source document hasn't been initialized, i.e., dragstart was
|
||||
// consumed by the test, the test needs to dispatch "dragend" event
|
||||
// instead of the drag session. Therefore, it does not make sense
|
||||
// to set drag end point in such case (you hit assersion if you do
|
||||
// it).
|
||||
if (sourceDocument) {
|
||||
CSSIntPoint dropPointInScreen =
|
||||
Event::GetScreenCoords(aPresContext, aEvent, aEvent->mRefPoint);
|
||||
dragSession->SetDragEndPointForTests(dropPointInScreen.x,
|
||||
dropPointInScreen.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
sLastDragOverFrame = nullptr;
|
||||
ClearGlobalActiveContent(this);
|
||||
break;
|
||||
@ -4755,6 +4817,8 @@ void EventStateManager::GenerateDragDropEnterExit(nsPresContext* aPresContext,
|
||||
WidgetDragEvent remoteEvent(aDragEvent->IsTrusted(), eDragExit,
|
||||
aDragEvent->mWidget);
|
||||
remoteEvent.AssignDragEventData(*aDragEvent, true);
|
||||
remoteEvent.mFlags.mIsSynthesizedForTests =
|
||||
aDragEvent->mFlags.mIsSynthesizedForTests;
|
||||
nsEventStatus remoteStatus = nsEventStatus_eIgnore;
|
||||
HandleCrossProcessEvent(&remoteEvent, &remoteStatus);
|
||||
}
|
||||
@ -4815,6 +4879,8 @@ void EventStateManager::FireDragEnterOrExit(nsPresContext* aPresContext,
|
||||
nsEventStatus status = nsEventStatus_eIgnore;
|
||||
WidgetDragEvent event(aDragEvent->IsTrusted(), aMessage, aDragEvent->mWidget);
|
||||
event.AssignDragEventData(*aDragEvent, false);
|
||||
event.mFlags.mIsSynthesizedForTests =
|
||||
aDragEvent->mFlags.mIsSynthesizedForTests;
|
||||
event.mRelatedTarget = aRelatedTarget;
|
||||
mCurrentTargetContent = aTargetContent;
|
||||
|
||||
|
@ -7,5 +7,5 @@
|
||||
bubbles: true
|
||||
});
|
||||
let utils = SpecialPowers.getDOMWindowUtils(window);
|
||||
utils.dispatchDOMEventViaPresShell(document.documentElement, e);
|
||||
utils.dispatchDOMEventViaPresShellForTesting(document.documentElement, e);
|
||||
</script>
|
||||
|
@ -13,10 +13,10 @@ var gGotNotHandlingDrop = false;
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function fireEvent(target, event) {
|
||||
SpecialPowers.DOMWindowUtils.dispatchDOMEventViaPresShell(target, event);
|
||||
SpecialPowers.DOMWindowUtils.dispatchDOMEventViaPresShellForTesting(target, event);
|
||||
}
|
||||
|
||||
function fireDrop(element, shouldAllowDrop, shouldAllowOnlyChromeDrop) {
|
||||
async function fireDrop(element, shouldAllowDrop, shouldAllowOnlyChromeDrop) {
|
||||
var ds = SpecialPowers.Cc["@mozilla.org/widget/dragservice;1"].
|
||||
getService(SpecialPowers.Ci.nsIDragService);
|
||||
|
||||
@ -31,28 +31,35 @@ function fireDrop(element, shouldAllowDrop, shouldAllowOnlyChromeDrop) {
|
||||
|
||||
// need to use real mouse action
|
||||
window.addEventListener("dragstart", trapDrag, true);
|
||||
synthesizeMouse(element, 2, 2, { type: "mousedown" });
|
||||
synthesizeMouse(element, 11, 11, { type: "mousemove" });
|
||||
synthesizeMouse(element, 20, 20, { type: "mousemove" });
|
||||
await synthesizePlainDragAndDrop({
|
||||
srcElement: element,
|
||||
stepX: 9,
|
||||
stepY: 9,
|
||||
expectCancelDragStart: true,
|
||||
});
|
||||
window.removeEventListener("dragstart", trapDrag, true);
|
||||
synthesizeMouse(element, 20, 20, { type: "mouseup" });
|
||||
|
||||
ds.startDragSession();
|
||||
ds.startDragSessionForTests(
|
||||
SpecialPowers.Ci.nsIDragService.DRAGDROP_ACTION_MOVE |
|
||||
SpecialPowers.Ci.nsIDragService.DRAGDROP_ACTION_COPY |
|
||||
SpecialPowers.Ci.nsIDragService.DRAGDROP_ACTION_LINK
|
||||
); // Session for emulating dnd coming from another app.
|
||||
try {
|
||||
var event = document.createEvent("DragEvent");
|
||||
event.initDragEvent("dragover", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null, dataTransfer);
|
||||
fireEvent(element, event);
|
||||
|
||||
var event = document.createEvent("DragEvent");
|
||||
event.initDragEvent("dragover", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null, dataTransfer);
|
||||
fireEvent(element, event);
|
||||
is(ds.getCurrentSession().canDrop, shouldAllowDrop, "Unexpected .canDrop");
|
||||
is(ds.getCurrentSession().onlyChromeDrop, shouldAllowOnlyChromeDrop,
|
||||
"Unexpected .onlyChromeDrop");
|
||||
|
||||
is(ds.getCurrentSession().canDrop, shouldAllowDrop, "Unexpected .canDrop");
|
||||
is(ds.getCurrentSession().onlyChromeDrop, shouldAllowOnlyChromeDrop,
|
||||
"Unexpected .onlyChromeDrop");
|
||||
|
||||
event = document.createEvent("DragEvent");
|
||||
event.initDragEvent("drop", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null, dataTransfer);
|
||||
fireEvent(element, event);
|
||||
|
||||
ds.endDragSession(false);
|
||||
ok(!ds.getCurrentSession(), "There shouldn't be a drag session anymore!");
|
||||
event = document.createEvent("DragEvent");
|
||||
event.initDragEvent("drop", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null, dataTransfer);
|
||||
fireEvent(element, event);
|
||||
} finally {
|
||||
ds.endDragSession(false);
|
||||
ok(!ds.getCurrentSession(), "There shouldn't be a drag session anymore!");
|
||||
}
|
||||
}
|
||||
|
||||
var chromeGotEvent = false;
|
||||
@ -60,10 +67,10 @@ function chromeListener(e) {
|
||||
chromeGotEvent = true;
|
||||
}
|
||||
|
||||
function runTests()
|
||||
async function runTests()
|
||||
{
|
||||
var targetHandling = document.getElementById("handling_target");
|
||||
fireDrop(targetHandling, true, false);
|
||||
await fireDrop(targetHandling, true, false);
|
||||
|
||||
is(gGotHandlingDrop, true, "Got drop on accepting element (1)");
|
||||
is(gGotNotHandlingDrop, false, "Didn't get drop on unaccepting element (1)");
|
||||
@ -74,7 +81,7 @@ function runTests()
|
||||
|
||||
SpecialPowers.addChromeEventListener("drop", chromeListener, true, false);
|
||||
var targetNotHandling = document.getElementById("nothandling_target");
|
||||
fireDrop(targetNotHandling, true, true);
|
||||
await fireDrop(targetNotHandling, true, true);
|
||||
SpecialPowers.removeChromeEventListener("drop", chromeListener, true);
|
||||
ok(chromeGotEvent, "Chrome should have got drop event!");
|
||||
is(gGotHandlingDrop, false, "Didn't get drop on accepting element (2)");
|
||||
|
@ -31,7 +31,7 @@ function completeTest(aBox) {
|
||||
function fireEvent(target, event) {
|
||||
var win = target.ownerGlobal;
|
||||
var utils = win.windowUtils;
|
||||
utils.dispatchDOMEventViaPresShell(target, event);
|
||||
utils.dispatchDOMEventViaPresShellForTesting(target, event);
|
||||
}
|
||||
|
||||
function RunTest() {
|
||||
|
@ -30,7 +30,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=593959
|
||||
var e = document.createEvent("MouseEvent");
|
||||
e.initEvent("mousedown", false, false, window, 0, 1, 1, 1, 1,
|
||||
false, false, false, false, 0, null);
|
||||
utils.dispatchDOMEventViaPresShell(document.body, e);
|
||||
utils.dispatchDOMEventViaPresShellForTesting(document.body, e);
|
||||
|
||||
is(document.querySelector("body:active"), document.body, "body should be active!")
|
||||
|
||||
@ -41,7 +41,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=593959
|
||||
var e2 = ifrwindow.document.createEvent("MouseEvent");
|
||||
e2.initEvent("mouseup", false, false, ifrwindow, 0, 1, 1, 1, 1,
|
||||
false, false, false, false, 0, null);
|
||||
utils2.dispatchDOMEventViaPresShell(ifrwindow.document.body, e2);
|
||||
utils2.dispatchDOMEventViaPresShellForTesting(ifrwindow.document.body, e2);
|
||||
|
||||
isnot(document.querySelector("body:active"), document.body, "body shouldn't be active!")
|
||||
|
||||
|
@ -951,8 +951,8 @@ interface nsIDOMWindowUtils : nsISupports {
|
||||
readonly attribute float fullZoom;
|
||||
|
||||
/**
|
||||
* Dispatches aEvent as a trusted event via the PresShell object of the
|
||||
* window's document.
|
||||
* Dispatches aEvent as a synthesized trusted event for tests via the
|
||||
* PresShell object of the window's document.
|
||||
* The event is dispatched to aTarget, which should be an object
|
||||
* which implements nsIContent interface (#element, #text, etc).
|
||||
*
|
||||
@ -964,8 +964,8 @@ interface nsIDOMWindowUtils : nsISupports {
|
||||
* Also, aEvent should not be reused.
|
||||
*/
|
||||
[can_run_script]
|
||||
boolean dispatchDOMEventViaPresShell(in Node aTarget,
|
||||
in Event aEvent);
|
||||
boolean dispatchDOMEventViaPresShellForTesting(in Node aTarget,
|
||||
in Event aEvent);
|
||||
|
||||
/**
|
||||
* Sets WidgetEvent::mFlags::mOnlyChromeDispatch to true to ensure that
|
||||
|
@ -49,25 +49,30 @@ function sendMouseUp(el) {
|
||||
|
||||
function fireEvent(target, event) {
|
||||
var utils = SpecialPowers.getDOMWindowUtils(window);
|
||||
utils.dispatchDOMEventViaPresShell(target, event);
|
||||
utils.dispatchDOMEventViaPresShellForTesting(target, event);
|
||||
}
|
||||
|
||||
function fireDrop(element) {
|
||||
var ds = SpecialPowers.Cc["@mozilla.org/widget/dragservice;1"].
|
||||
getService(SpecialPowers.Ci.nsIDragService);
|
||||
|
||||
ds.startDragSession();
|
||||
ds.startDragSessionForTests(
|
||||
SpecialPowers.Ci.nsIDragService.DRAGDROP_ACTION_MOVE |
|
||||
SpecialPowers.Ci.nsIDragService.DRAGDROP_ACTION_COPY |
|
||||
SpecialPowers.Ci.nsIDragService.DRAGDROP_ACTION_LINK
|
||||
); // Session for getting dataTransfer object.
|
||||
try {
|
||||
var event = document.createEvent("DragEvent");
|
||||
event.initDragEvent("dragover", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null, null);
|
||||
fireEvent(element, event);
|
||||
|
||||
var event = document.createEvent("DragEvent");
|
||||
event.initDragEvent("dragover", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null, null);
|
||||
fireEvent(element, event);
|
||||
|
||||
event = document.createEvent("DragEvent");
|
||||
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!");
|
||||
event = document.createEvent("DragEvent");
|
||||
event.initDragEvent("drop", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null, null);
|
||||
fireEvent(element, event);
|
||||
} finally {
|
||||
ds.endDragSession(false);
|
||||
ok(!ds.getCurrentSession(), "There shouldn't be a drag session anymore!");
|
||||
}
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
|
@ -62,19 +62,26 @@ add_task(async function() {
|
||||
let dragService = Cc["@mozilla.org/widget/dragservice;1"].getService(
|
||||
Ci.nsIDragService
|
||||
);
|
||||
dragService.startDragSession();
|
||||
await BrowserTestUtils.synthesizeMouse(
|
||||
"#target",
|
||||
5,
|
||||
15,
|
||||
{ type: "mousemove" },
|
||||
browser
|
||||
dragService.startDragSessionForTests(
|
||||
Ci.nsIDragService.DRAGDROP_ACTION_MOVE |
|
||||
Ci.nsIDragService.DRAGDROP_ACTION_COPY |
|
||||
Ci.nsIDragService.DRAGDROP_ACTION_LINK
|
||||
);
|
||||
try {
|
||||
await BrowserTestUtils.synthesizeMouse(
|
||||
"#target",
|
||||
5,
|
||||
15,
|
||||
{ type: "mousemove" },
|
||||
browser
|
||||
);
|
||||
|
||||
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
removeEventListener("popupshown", tooltipNotExpected, true);
|
||||
dragService.endDragSession(true);
|
||||
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
} finally {
|
||||
removeEventListener("popupshown", tooltipNotExpected, true);
|
||||
dragService.endDragSession(true);
|
||||
}
|
||||
|
||||
await BrowserTestUtils.synthesizeMouse(
|
||||
"#target",
|
||||
|
@ -318,7 +318,7 @@ function sendDragEvent(aEvent, aTarget, aWindow = window) {
|
||||
}
|
||||
|
||||
var utils = _getDOMWindowUtils(aWindow);
|
||||
return utils.dispatchDOMEventViaPresShell(aTarget, event);
|
||||
return utils.dispatchDOMEventViaPresShellForTesting(aTarget, event);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2623,14 +2623,17 @@ function createDragEventObject(
|
||||
}
|
||||
|
||||
// Wrap only in plain mochitests
|
||||
let dataTransfer = _EU_maybeUnwrap(
|
||||
_EU_maybeWrap(aDataTransfer).mozCloneForEvent(aType)
|
||||
);
|
||||
let dataTransfer;
|
||||
if (aDataTransfer) {
|
||||
dataTransfer = _EU_maybeUnwrap(
|
||||
_EU_maybeWrap(aDataTransfer).mozCloneForEvent(aType)
|
||||
);
|
||||
|
||||
// Copy over the drop effect. This isn't copied over by Clone, as it uses more
|
||||
// complex logic in the actual implementation (see
|
||||
// nsContentUtils::SetDataTransferInEvent for actual impl).
|
||||
dataTransfer.dropEffect = aDataTransfer.dropEffect;
|
||||
// Copy over the drop effect. This isn't copied over by Clone, as it uses
|
||||
// more complex logic in the actual implementation (see
|
||||
// nsContentUtils::SetDataTransferInEvent for actual impl).
|
||||
dataTransfer.dropEffect = aDataTransfer.dropEffect;
|
||||
}
|
||||
|
||||
return Object.assign(
|
||||
{
|
||||
@ -2639,7 +2642,7 @@ function createDragEventObject(
|
||||
screenY: destScreenY,
|
||||
clientX: destClientX,
|
||||
clientY: destClientY,
|
||||
dataTransfer: dataTransfer,
|
||||
dataTransfer,
|
||||
_domDispatchOnly: aDragEvent._domDispatchOnly,
|
||||
},
|
||||
aDragEvent
|
||||
@ -2837,7 +2840,24 @@ function synthesizeDrop(
|
||||
_EU_Ci.nsIDragService
|
||||
);
|
||||
|
||||
ds.startDragSession();
|
||||
let dropAction;
|
||||
switch (aDropEffect) {
|
||||
case null:
|
||||
case undefined:
|
||||
case "move":
|
||||
dropAction = _EU_Ci.nsIDragService.DRAGDROP_ACTION_MOVE;
|
||||
break;
|
||||
case "copy":
|
||||
dropAction = _EU_Ci.nsIDragService.DRAGDROP_ACTION_COPY;
|
||||
break;
|
||||
case "link":
|
||||
dropAction = _EU_Ci.nsIDragService.DRAGDROP_ACTION_LINK;
|
||||
break;
|
||||
default:
|
||||
throw new Error(`${aDropEffect} is an invalid drop effect value`);
|
||||
}
|
||||
|
||||
ds.startDragSessionForTests(dropAction);
|
||||
|
||||
try {
|
||||
var [result, dataTransfer] = synthesizeDragOver(
|
||||
@ -2866,6 +2886,8 @@ function synthesizeDrop(
|
||||
* and firing events dragenter, dragover, drop, and dragend.
|
||||
* This does not modify dataTransfer and tries to emulate the plain drag and
|
||||
* drop as much as possible, compared to synthesizeDrop.
|
||||
* Note that if synthesized dragstart is canceled, this throws an exception
|
||||
* because in such case, Gecko does not start drag session.
|
||||
*
|
||||
* @param aParams
|
||||
* {
|
||||
@ -2882,6 +2904,9 @@ function synthesizeDrop(
|
||||
* defaults to the current window object
|
||||
* destWindow: The window for dispatching event on destElement,
|
||||
* defaults to the current window object
|
||||
* expectCancelDragStart: Set to true if the test cancels "dragstart"
|
||||
* logFunc: Set function which takes one argument if you need
|
||||
* to log rect of target. E.g., `console.log`.
|
||||
* }
|
||||
*/
|
||||
async function synthesizePlainDragAndDrop(aParams) {
|
||||
@ -2896,80 +2921,268 @@ async function synthesizePlainDragAndDrop(aParams) {
|
||||
finalY = srcY + stepY * 2,
|
||||
srcWindow = window,
|
||||
destWindow = window,
|
||||
expectCancelDragStart = false,
|
||||
logFunc,
|
||||
} = aParams;
|
||||
|
||||
function rectToString(aRect) {
|
||||
return `left: ${aRect.left}, top: ${aRect.top}, right: ${
|
||||
aRect.right
|
||||
}, bottom: ${aRect.bottom}`;
|
||||
}
|
||||
|
||||
if (logFunc) {
|
||||
logFunc("synthesizePlainDragAndDrop() -- START");
|
||||
logFunc(
|
||||
`srcElement.getBoundingClientRect(): ${rectToString(
|
||||
srcElement.getBoundingClientRect()
|
||||
)}`
|
||||
);
|
||||
}
|
||||
|
||||
const ds = _EU_Cc["@mozilla.org/widget/dragservice;1"].getService(
|
||||
_EU_Ci.nsIDragService
|
||||
);
|
||||
ds.startDragSession();
|
||||
|
||||
try {
|
||||
let dataTransfer = null;
|
||||
function trapDrag(aEvent) {
|
||||
dataTransfer = aEvent.dataTransfer;
|
||||
}
|
||||
srcElement.addEventListener("dragstart", trapDrag, true);
|
||||
await new Promise(r => setTimeout(r, 0));
|
||||
|
||||
synthesizeMouse(srcElement, srcX, srcY, { type: "mousedown" }, srcWindow);
|
||||
if (logFunc) {
|
||||
logFunc(`mousedown at ${srcX}, ${srcY}`);
|
||||
}
|
||||
|
||||
// Wait for the next event tick after each event dispatch, so that UI elements
|
||||
// (e.g. menu) work like the real user input.
|
||||
await new Promise(r => setTimeout(r, 0));
|
||||
let dragStartEvent;
|
||||
function onDragStart(aEvent) {
|
||||
dragStartEvent = aEvent;
|
||||
if (logFunc) {
|
||||
logFunc(`"${aEvent.type}" event is fired`);
|
||||
}
|
||||
if (!srcElement.contains(aEvent.target)) {
|
||||
// If srcX and srcY does not point in one of rects in srcElement,
|
||||
// "dragstart" target is not in srcElement. Such case must not
|
||||
// be expected by this API users so that we should throw an exception
|
||||
// for making debug easier.
|
||||
throw new Error(
|
||||
'event target of "dragstart" is not srcElement nor its descendant'
|
||||
);
|
||||
}
|
||||
}
|
||||
let dragEnterEvent;
|
||||
function onDragEnterGenerated(aEvent) {
|
||||
dragEnterEvent = aEvent;
|
||||
}
|
||||
srcWindow.addEventListener("dragstart", onDragStart, { capture: true });
|
||||
srcWindow.addEventListener("dragenter", onDragEnterGenerated, {
|
||||
capture: true,
|
||||
});
|
||||
try {
|
||||
// Wait for the next event tick after each event dispatch, so that UI
|
||||
// elements (e.g. menu) work like the real user input.
|
||||
await new Promise(r => setTimeout(r, 0));
|
||||
|
||||
srcX += stepX;
|
||||
srcY += stepY;
|
||||
synthesizeMouse(srcElement, srcX, srcY, { type: "mousemove" }, srcWindow);
|
||||
|
||||
await new Promise(r => setTimeout(r, 0));
|
||||
|
||||
srcX += stepX;
|
||||
srcY += stepY;
|
||||
synthesizeMouse(srcElement, srcX, srcY, { type: "mousemove" }, srcWindow);
|
||||
|
||||
await new Promise(r => setTimeout(r, 0));
|
||||
|
||||
srcElement.removeEventListener("dragstart", trapDrag, true);
|
||||
|
||||
await new Promise(r => setTimeout(r, 0));
|
||||
|
||||
let event;
|
||||
if (destElement) {
|
||||
// dragover and drop are only fired to a valid drop target. If the
|
||||
// destElement parameter is null, this function is being used to
|
||||
// simulate a drag'n'drop over an invalid drop target.
|
||||
event = createDragEventObject(
|
||||
"dragover",
|
||||
destElement,
|
||||
destWindow,
|
||||
dataTransfer,
|
||||
{}
|
||||
);
|
||||
sendDragEvent(event, destElement, destWindow);
|
||||
srcX += stepX;
|
||||
srcY += stepY;
|
||||
synthesizeMouse(srcElement, srcX, srcY, { type: "mousemove" }, srcWindow);
|
||||
if (logFunc) {
|
||||
logFunc(`first mousemove at ${srcX}, ${srcY}`);
|
||||
}
|
||||
|
||||
await new Promise(r => setTimeout(r, 0));
|
||||
|
||||
event = createDragEventObject(
|
||||
"drop",
|
||||
destElement,
|
||||
destWindow,
|
||||
dataTransfer,
|
||||
{}
|
||||
);
|
||||
sendDragEvent(event, destElement, destWindow);
|
||||
srcX += stepX;
|
||||
srcY += stepY;
|
||||
synthesizeMouse(srcElement, srcX, srcY, { type: "mousemove" }, srcWindow);
|
||||
if (logFunc) {
|
||||
logFunc(`second mousemove at ${srcX}, ${srcY}`);
|
||||
}
|
||||
|
||||
await new Promise(r => setTimeout(r, 0));
|
||||
|
||||
if (!dragStartEvent) {
|
||||
throw new Error('"dragstart" event is not fired');
|
||||
}
|
||||
} finally {
|
||||
srcWindow.removeEventListener("dragstart", onDragStart, {
|
||||
capture: true,
|
||||
});
|
||||
srcWindow.removeEventListener("dragenter", onDragEnterGenerated, {
|
||||
capture: true,
|
||||
});
|
||||
}
|
||||
|
||||
// dragend is fired, by definition, on the srcElement
|
||||
event = createDragEventObject(
|
||||
"dragend",
|
||||
srcElement,
|
||||
srcWindow,
|
||||
dataTransfer,
|
||||
{ clientX: finalX, clientY: finalY }
|
||||
);
|
||||
sendDragEvent(event, srcElement, srcWindow);
|
||||
let session = ds.getCurrentSession();
|
||||
if (!session) {
|
||||
if (expectCancelDragStart) {
|
||||
synthesizeMouse(srcElement, srcX, srcY, { type: "mouseup" }, srcWindow);
|
||||
return;
|
||||
}
|
||||
throw new Error("drag hasn't been started by the operation");
|
||||
} else if (expectCancelDragStart) {
|
||||
throw new Error("drag has been started by the operation");
|
||||
}
|
||||
|
||||
await new Promise(r => setTimeout(r, 0));
|
||||
if (destElement) {
|
||||
if (
|
||||
(srcElement != destElement && !dragEnterEvent) ||
|
||||
destElement != dragEnterEvent.target
|
||||
) {
|
||||
if (logFunc) {
|
||||
logFunc(
|
||||
`destElement.getBoundingClientRect(): ${rectToString(
|
||||
destElement.getBoundingClientRect()
|
||||
)}`
|
||||
);
|
||||
}
|
||||
|
||||
function onDragEnter(aEvent) {
|
||||
dragEnterEvent = aEvent;
|
||||
if (logFunc) {
|
||||
logFunc(`"${aEvent.type}" event is fired`);
|
||||
}
|
||||
if (aEvent.target != destElement) {
|
||||
throw new Error('event target of "dragenter" is not destElement');
|
||||
}
|
||||
}
|
||||
destWindow.addEventListener("dragenter", onDragEnter, {
|
||||
capture: true,
|
||||
});
|
||||
try {
|
||||
let event = createDragEventObject(
|
||||
"dragenter",
|
||||
destElement,
|
||||
destWindow,
|
||||
null,
|
||||
{}
|
||||
);
|
||||
sendDragEvent(event, destElement, destWindow);
|
||||
if (!dragEnterEvent && !destElement.disabled) {
|
||||
throw new Error('"dragenter" event is not fired');
|
||||
}
|
||||
if (dragEnterEvent && destElement.disabled) {
|
||||
throw new Error(
|
||||
'"dragenter" event should not be fired on disable element'
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
destWindow.removeEventListener("dragenter", onDragEnter, {
|
||||
capture: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let dragOverEvent;
|
||||
function onDragOver(aEvent) {
|
||||
dragOverEvent = aEvent;
|
||||
if (logFunc) {
|
||||
logFunc(`"${aEvent.type}" event is fired`);
|
||||
}
|
||||
if (aEvent.target != destElement) {
|
||||
throw new Error('event target of "dragover" is not destElement');
|
||||
}
|
||||
}
|
||||
destWindow.addEventListener("dragover", onDragOver, { capture: true });
|
||||
try {
|
||||
// dragover and drop are only fired to a valid drop target. If the
|
||||
// destElement parameter is null, this function is being used to
|
||||
// simulate a drag'n'drop over an invalid drop target.
|
||||
let event = createDragEventObject(
|
||||
"dragover",
|
||||
destElement,
|
||||
destWindow,
|
||||
null,
|
||||
{}
|
||||
);
|
||||
sendDragEvent(event, destElement, destWindow);
|
||||
if (!dragOverEvent && !destElement.disabled) {
|
||||
throw new Error('"dragover" event is not fired');
|
||||
}
|
||||
if (dragEnterEvent && destElement.disabled) {
|
||||
throw new Error(
|
||||
'"dragover" event should not be fired on disable element'
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
destWindow.removeEventListener("dragover", onDragOver, {
|
||||
capture: true,
|
||||
});
|
||||
}
|
||||
|
||||
await new Promise(r => setTimeout(r, 0));
|
||||
|
||||
let dropEvent;
|
||||
function onDrop(aEvent) {
|
||||
dropEvent = aEvent;
|
||||
if (logFunc) {
|
||||
logFunc(`"${aEvent.type}" event is fired`);
|
||||
}
|
||||
if (!destElement.contains(aEvent.target)) {
|
||||
throw new Error(
|
||||
'event target of "drop" is not destElement nor its descendant'
|
||||
);
|
||||
}
|
||||
};
|
||||
destWindow.addEventListener("drop", onDrop, { capture: true });
|
||||
try {
|
||||
let event = createDragEventObject(
|
||||
"drop",
|
||||
destElement,
|
||||
destWindow,
|
||||
null,
|
||||
{}
|
||||
);
|
||||
sendDragEvent(event, destElement, destWindow);
|
||||
if (!dropEvent && session.canDrop) {
|
||||
throw new Error('"drop" event is not fired');
|
||||
}
|
||||
} finally {
|
||||
destWindow.removeEventListener("drop", onDrop, { capture: true });
|
||||
}
|
||||
} else {
|
||||
// Since we don't synthesize drop event, we need to set drag end point
|
||||
// explicitly for "dragEnd" event which will be fired by
|
||||
// endDragSession().
|
||||
let event = createDragEventObject(
|
||||
"dragend",
|
||||
srcElement,
|
||||
srcWindow,
|
||||
null,
|
||||
{ clientX: finalX, clientY: finalY }
|
||||
);
|
||||
session.setDragEndPointForTests(event.screenX, event.screenY);
|
||||
}
|
||||
} finally {
|
||||
ds.endDragSession(true, 0);
|
||||
await new Promise(r => setTimeout(r, 0));
|
||||
|
||||
if (ds.getCurrentSession()) {
|
||||
let dragEndEvent;
|
||||
function onDragEnd(aEvent) {
|
||||
dragEndEvent = aEvent;
|
||||
if (logFunc) {
|
||||
logFunc(`"${aEvent.type}" event is fired`);
|
||||
}
|
||||
if (!srcElement.contains(aEvent.target)) {
|
||||
throw new Error(
|
||||
'event target of "dragend" is not srcElement not its descendant'
|
||||
);
|
||||
}
|
||||
}
|
||||
srcWindow.addEventListener("dragend", onDragEnd, { capture: true });
|
||||
try {
|
||||
ds.endDragSession(true, 0);
|
||||
if (!dragEndEvent) {
|
||||
// eslint-disable-next-line no-unsafe-finally
|
||||
throw new Error(
|
||||
'"dragend" event is not fired by nsIDragService.endDragSession()'
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
srcWindow.removeEventListener("dragend", onDragEnd, { capture: true });
|
||||
}
|
||||
}
|
||||
if (logFunc) {
|
||||
logFunc("synthesizePlainDragAndDrop() -- END");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -84,7 +84,7 @@ function dispatchMouseEvent(target, type) {
|
||||
e.initEvent(type, false, false, win, 0, 1, 1, 1, 1,
|
||||
false, false, false, false, 0, null);
|
||||
var utils = SpecialPowers.getDOMWindowUtils(win);
|
||||
utils.dispatchDOMEventViaPresShell(target, e);
|
||||
utils.dispatchDOMEventViaPresShellForTesting(target, e);
|
||||
ok(true, type + " sent to " + target.id);
|
||||
}
|
||||
|
||||
|
@ -195,7 +195,7 @@ async function dispatchMouseEvent(targetID, type) {
|
||||
let event = document.createEvent("MouseEvent");
|
||||
event.initEvent(type, false, false, content, 0, 1, 1, 1, 1,
|
||||
false, false, false, false, 0, null);
|
||||
content.windowUtils.dispatchDOMEventViaPresShell(element, event);
|
||||
content.windowUtils.dispatchDOMEventViaPresShellForTesting(element, event);
|
||||
/* eslint-enable no-undef */
|
||||
}
|
||||
</script>
|
||||
|
@ -69,7 +69,7 @@ async function expectLink(browser, expectedLinks, data, testid, onbody=false) {
|
||||
|
||||
function dropOnBrowserSync() {
|
||||
let dropEl = onbody ? browser.contentDocument.body : browser;
|
||||
synthesizeDrop(dropEl, dropEl, data, "", dropEl.ownerGlobal);
|
||||
synthesizeDrop(dropEl, dropEl, data, null, dropEl.ownerGlobal);
|
||||
}
|
||||
let links;
|
||||
if (browser.isRemoteBrowser) {
|
||||
|
@ -366,6 +366,14 @@ class WidgetDragEvent : public WidgetMouseEvent {
|
||||
mUserCancelled = false;
|
||||
mDefaultPreventedOnContent = aEvent.mDefaultPreventedOnContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should be called before dispatching the DOM tree if this event is
|
||||
* synthesized for tests because drop effect is initialized before
|
||||
* dispatching from widget if it's not synthesized event, but synthesized
|
||||
* events are not initialized in the path.
|
||||
*/
|
||||
void InitDropEffectForTests();
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "nsCommandParams.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIContent.h"
|
||||
#include "nsIDragSession.h"
|
||||
#include "nsPrintfCString.h"
|
||||
|
||||
#if defined(XP_WIN)
|
||||
@ -668,6 +669,53 @@ bool WidgetMouseEvent::IsMiddleClickPasteEnabled() {
|
||||
return Preferences::GetBool("middlemouse.paste", false);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* mozilla::WidgetDragEvent (MouseEvents.h)
|
||||
******************************************************************************/
|
||||
|
||||
void WidgetDragEvent::InitDropEffectForTests() {
|
||||
MOZ_ASSERT(mFlags.mIsSynthesizedForTests);
|
||||
|
||||
nsCOMPtr<nsIDragSession> session = nsContentUtils::GetDragSession();
|
||||
if (NS_WARN_IF(!session)) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t effectAllowed = session->GetEffectAllowedForTests();
|
||||
uint32_t desiredDropEffect = nsIDragService::DRAGDROP_ACTION_NONE;
|
||||
#ifdef XP_MACOSX
|
||||
if (IsAlt()) {
|
||||
desiredDropEffect = IsMeta() ? nsIDragService::DRAGDROP_ACTION_LINK
|
||||
: nsIDragService::DRAGDROP_ACTION_COPY;
|
||||
}
|
||||
#else
|
||||
// On Linux, we know user's intention from API, but we should use
|
||||
// same modifiers as Windows for tests because GNOME on Ubuntu use
|
||||
// them and that makes each test simpler.
|
||||
if (IsControl()) {
|
||||
desiredDropEffect = IsShift() ? nsIDragService::DRAGDROP_ACTION_LINK
|
||||
: nsIDragService::DRAGDROP_ACTION_COPY;
|
||||
} else if (IsShift()) {
|
||||
desiredDropEffect = nsIDragService::DRAGDROP_ACTION_MOVE;
|
||||
}
|
||||
#endif // #ifdef XP_MACOSX #else
|
||||
// First, use modifier state for preferring action which is explicitly
|
||||
// specified by the synthesizer.
|
||||
if (!(desiredDropEffect &= effectAllowed)) {
|
||||
// Otherwise, use an action which is allowed at starting the session.
|
||||
desiredDropEffect = effectAllowed;
|
||||
}
|
||||
if (desiredDropEffect & nsIDragService::DRAGDROP_ACTION_MOVE) {
|
||||
session->SetDragAction(nsIDragService::DRAGDROP_ACTION_MOVE);
|
||||
} else if (desiredDropEffect & nsIDragService::DRAGDROP_ACTION_COPY) {
|
||||
session->SetDragAction(nsIDragService::DRAGDROP_ACTION_COPY);
|
||||
} else if (desiredDropEffect & nsIDragService::DRAGDROP_ACTION_LINK) {
|
||||
session->SetDragAction(nsIDragService::DRAGDROP_ACTION_LINK);
|
||||
} else {
|
||||
session->SetDragAction(nsIDragService::DRAGDROP_ACTION_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* mozilla::WidgetWheelEvent (MouseEvents.h)
|
||||
******************************************************************************/
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "mozilla/dom/DataTransferItemList.h"
|
||||
#include "mozilla/dom/DataTransfer.h"
|
||||
#include "mozilla/dom/DocumentInlines.h"
|
||||
#include "mozilla/dom/DragEvent.h"
|
||||
#include "mozilla/dom/MouseEventBinding.h"
|
||||
#include "mozilla/dom/Selection.h"
|
||||
@ -60,12 +61,14 @@ nsBaseDragService::nsBaseDragService()
|
||||
: mCanDrop(false),
|
||||
mOnlyChromeDrop(false),
|
||||
mDoingDrag(false),
|
||||
mSessionIsSynthesizedForTests(false),
|
||||
mEndingSession(false),
|
||||
mHasImage(false),
|
||||
mUserCancelled(false),
|
||||
mDragEventDispatchedToChildProcess(false),
|
||||
mDragAction(DRAGDROP_ACTION_NONE),
|
||||
mDragActionFromChildProcess(DRAGDROP_ACTION_UNINITIALIZED),
|
||||
mEffectAllowedForTests(DRAGDROP_ACTION_UNINITIALIZED),
|
||||
mContentPolicyType(nsIContentPolicy::TYPE_OTHER),
|
||||
mSuppressLevel(0),
|
||||
mInputSource(MouseEvent_Binding::MOZ_SOURCE_MOUSE) {}
|
||||
@ -208,6 +211,33 @@ void nsBaseDragService::SetDataTransfer(DataTransfer* aDataTransfer) {
|
||||
mDataTransfer = aDataTransfer;
|
||||
}
|
||||
|
||||
bool nsBaseDragService::IsSynthesizedForTests() {
|
||||
return mSessionIsSynthesizedForTests;
|
||||
}
|
||||
|
||||
uint32_t nsBaseDragService::GetEffectAllowedForTests() {
|
||||
MOZ_ASSERT(mSessionIsSynthesizedForTests);
|
||||
return mEffectAllowedForTests;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsBaseDragService::SetDragEndPointForTests(int32_t aScreenX,
|
||||
int32_t aScreenY) {
|
||||
MOZ_ASSERT(mDoingDrag);
|
||||
MOZ_ASSERT(mSourceDocument);
|
||||
MOZ_ASSERT(mSessionIsSynthesizedForTests);
|
||||
if (!mDoingDrag || !mSourceDocument || !mSessionIsSynthesizedForTests) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
nsPresContext* presContext = mSourceDocument->GetPresContext();
|
||||
if (NS_WARN_IF(!presContext)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
SetDragEndPoint(
|
||||
LayoutDeviceIntPoint(presContext->CSSPixelsToDevPixels(aScreenX),
|
||||
presContext->CSSPixelsToDevPixels(aScreenY)));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
NS_IMETHODIMP
|
||||
nsBaseDragService::InvokeDragSession(
|
||||
@ -216,23 +246,6 @@ nsBaseDragService::InvokeDragSession(
|
||||
nsContentPolicyType aContentPolicyType = nsIContentPolicy::TYPE_OTHER) {
|
||||
AUTO_PROFILER_LABEL("nsBaseDragService::InvokeDragSession", OTHER);
|
||||
|
||||
// If you're hitting this, a test is causing the browser to attempt to enter
|
||||
// the drag-drop native nested event loop, which will put the browser in a
|
||||
// state that won't run tests properly until there's manual intervention
|
||||
// to exit the drag-drop loop (either by moving the mouse or hitting escape),
|
||||
// which can't be done from script since we're in the nested loop.
|
||||
//
|
||||
// The best way to avoid this is to catch the dragstart event on the item
|
||||
// being dragged, and then to call preventDefault() and stopPropagating() on
|
||||
// it. Alternatively, use EventUtils.synthesizeDragStart, which will do this
|
||||
// for you.
|
||||
if (XRE_IsParentProcess()) {
|
||||
MOZ_ASSERT(
|
||||
!xpc::IsInAutomation(),
|
||||
"About to start drag-drop native loop on which will prevent later "
|
||||
"tests from running properly.");
|
||||
}
|
||||
|
||||
NS_ENSURE_TRUE(aDOMNode, NS_ERROR_INVALID_ARG);
|
||||
NS_ENSURE_TRUE(mSuppressLevel == 0, NS_ERROR_FAILURE);
|
||||
|
||||
@ -250,6 +263,30 @@ nsBaseDragService::InvokeDragSession(
|
||||
// are in the wrong coord system, so turn off mouse capture.
|
||||
PresShell::ClearMouseCapture(nullptr);
|
||||
|
||||
if (mSessionIsSynthesizedForTests) {
|
||||
mDoingDrag = true;
|
||||
mDragAction = aActionType;
|
||||
mEffectAllowedForTests = aActionType;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// If you're hitting this, a test is causing the browser to attempt to enter
|
||||
// the drag-drop native nested event loop, which will put the browser in a
|
||||
// state that won't run tests properly until there's manual intervention
|
||||
// to exit the drag-drop loop (either by moving the mouse or hitting escape),
|
||||
// which can't be done from script since we're in the nested loop.
|
||||
//
|
||||
// The best way to avoid this is to catch the dragstart event on the item
|
||||
// being dragged, and then to call preventDefault() and stopPropagating() on
|
||||
// it. Alternatively, use EventUtils.synthesizeDragStart, which will do this
|
||||
// for you.
|
||||
if (XRE_IsParentProcess()) {
|
||||
MOZ_ASSERT(
|
||||
!xpc::IsInAutomation(),
|
||||
"About to start drag-drop native loop on which will prevent later "
|
||||
"tests from running properly.");
|
||||
}
|
||||
|
||||
uint32_t length = 0;
|
||||
mozilla::Unused << aTransferableArray->GetLength(&length);
|
||||
if (!length) {
|
||||
@ -299,6 +336,8 @@ nsBaseDragService::InvokeDragSessionWithImage(
|
||||
NS_ENSURE_TRUE(aDataTransfer, NS_ERROR_NULL_POINTER);
|
||||
NS_ENSURE_TRUE(mSuppressLevel == 0, NS_ERROR_FAILURE);
|
||||
|
||||
mSessionIsSynthesizedForTests =
|
||||
aDragEvent->WidgetEventPtr()->mFlags.mIsSynthesizedForTests;
|
||||
mDataTransfer = aDataTransfer;
|
||||
mSelection = nullptr;
|
||||
mHasImage = true;
|
||||
@ -347,6 +386,8 @@ nsBaseDragService::InvokeDragSessionWithRemoteImage(
|
||||
NS_ENSURE_TRUE(aDataTransfer, NS_ERROR_NULL_POINTER);
|
||||
NS_ENSURE_TRUE(mSuppressLevel == 0, NS_ERROR_FAILURE);
|
||||
|
||||
mSessionIsSynthesizedForTests =
|
||||
aDragEvent->WidgetEventPtr()->mFlags.mIsSynthesizedForTests;
|
||||
mDataTransfer = aDataTransfer;
|
||||
mSelection = nullptr;
|
||||
mHasImage = true;
|
||||
@ -375,6 +416,8 @@ nsBaseDragService::InvokeDragSessionWithSelection(
|
||||
NS_ENSURE_TRUE(aDragEvent, NS_ERROR_NULL_POINTER);
|
||||
NS_ENSURE_TRUE(mSuppressLevel == 0, NS_ERROR_FAILURE);
|
||||
|
||||
mSessionIsSynthesizedForTests =
|
||||
aDragEvent->WidgetEventPtr()->mFlags.mIsSynthesizedForTests;
|
||||
mDataTransfer = aDataTransfer;
|
||||
mSelection = aSelection;
|
||||
mHasImage = true;
|
||||
@ -426,6 +469,17 @@ nsBaseDragService::StartDragSession() {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsBaseDragService::StartDragSessionForTests(
|
||||
uint32_t aAllowedEffect) {
|
||||
if (NS_WARN_IF(NS_FAILED(StartDragSession()))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
mDragAction = aAllowedEffect;
|
||||
mEffectAllowedForTests = aAllowedEffect;
|
||||
mSessionIsSynthesizedForTests = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void nsBaseDragService::OpenDragPopup() {
|
||||
if (mDragPopup) {
|
||||
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
|
||||
@ -486,6 +540,8 @@ nsBaseDragService::EndDragSession(bool aDoneDrag, uint32_t aKeyModifiers) {
|
||||
}
|
||||
|
||||
mDoingDrag = false;
|
||||
mSessionIsSynthesizedForTests = false;
|
||||
mEffectAllowedForTests = nsIDragService::DRAGDROP_ACTION_UNINITIALIZED;
|
||||
mEndingSession = false;
|
||||
mCanDrop = false;
|
||||
|
||||
@ -542,6 +598,7 @@ nsBaseDragService::FireDragEventAtSource(EventMessage aEventMessage,
|
||||
if (presShell) {
|
||||
nsEventStatus status = nsEventStatus_eIgnore;
|
||||
WidgetDragEvent event(true, aEventMessage, nullptr);
|
||||
event.mFlags.mIsSynthesizedForTests = mSessionIsSynthesizedForTests;
|
||||
event.mInputSource = mInputSource;
|
||||
if (aEventMessage == eDragEnd) {
|
||||
event.mRefPoint = mEndDragPoint;
|
||||
|
@ -148,6 +148,7 @@ class nsBaseDragService : public nsIDragService, public nsIDragSession {
|
||||
bool mCanDrop;
|
||||
bool mOnlyChromeDrop;
|
||||
bool mDoingDrag;
|
||||
bool mSessionIsSynthesizedForTests;
|
||||
|
||||
// true if in EndDragSession
|
||||
bool mEndingSession;
|
||||
@ -161,6 +162,10 @@ class nsBaseDragService : public nsIDragService, public nsIDragSession {
|
||||
uint32_t mDragAction;
|
||||
uint32_t mDragActionFromChildProcess;
|
||||
|
||||
// mEffectAllowedForTests stores allowed effects at invoking the drag
|
||||
// for tests.
|
||||
uint32_t mEffectAllowedForTests;
|
||||
|
||||
nsCOMPtr<nsINode> mSourceNode;
|
||||
nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
|
||||
nsCOMPtr<nsIContentSecurityPolicy> mCsp;
|
||||
|
@ -138,7 +138,20 @@ interface nsIDragService : nsISupports
|
||||
* Tells the Drag Service to start a drag session. This is called when
|
||||
* an external drag occurs
|
||||
*/
|
||||
void startDragSession ( ) ;
|
||||
void startDragSession() ;
|
||||
|
||||
/**
|
||||
* Similar to startDragSession(), automated tests may want to start
|
||||
* session for emulating an external drag. At that time, this should
|
||||
* be used instead of startDragSession().
|
||||
*
|
||||
* @param aAllowedEffect Set default drag action which means allowed effects
|
||||
* in the session and every DnD events are initialized
|
||||
* with one of specified value. So, the value can be
|
||||
* DRAGDROP_ACTION_NONE, or one or more values of
|
||||
* DRAGDROP_ACTION_(MOVE|COPY|LINK).
|
||||
*/
|
||||
void startDragSessionForTests(in unsigned long aAllowedEffect);
|
||||
|
||||
/**
|
||||
* Tells the Drag Service to end a drag session. This is called when
|
||||
|
@ -105,9 +105,20 @@ interface nsIDragSession : nsISupports
|
||||
// Change the drag image, using similar arguments as
|
||||
// nsIDragService::InvokeDragSessionWithImage.
|
||||
void updateDragImage(in Node aImage, in long aImageX, in long aImageY);
|
||||
|
||||
/**
|
||||
* Returns effects allowed at starting the session for tests.
|
||||
*/
|
||||
[notxpcom, nostdcall] unsigned long getEffectAllowedForTests();
|
||||
|
||||
/**
|
||||
* Returns true if current session was started with synthesized drag start.
|
||||
*/
|
||||
[notxpcom, nostdcall] bool isSynthesizedForTests();
|
||||
|
||||
/**
|
||||
* Sets drag end point of synthesized session when the test does not dispatch
|
||||
* "drop" event.
|
||||
*/
|
||||
void setDragEndPointForTests(in long aScreenX, in long aScreenY);
|
||||
};
|
||||
|
||||
|
||||
%{ C++
|
||||
|
||||
%}
|
||||
|
Loading…
Reference in New Issue
Block a user