Bug 606885 - Fire drag events with keyboard modifiers. r=enn

--HG--
extra : rebase_source : c6e4ac37ea2397d86368cb59b0a68aa3a9f43d79
This commit is contained in:
Stone Shih 2017-02-17 11:29:42 +08:00
parent 2ac1ba03cf
commit 229b4ba331
22 changed files with 203 additions and 73 deletions

View File

@ -9256,7 +9256,7 @@ nsGlobalWindow::EnterModalState()
nsCOMPtr<nsIDragService> ds =
do_GetService("@mozilla.org/widget/dragservice;1");
if (ds) {
ds->EndDragSession(true);
ds->EndDragSession(true, 0);
}
// Clear the capturing content if it is under topDoc.

View File

@ -176,3 +176,4 @@ skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM
[test_bug1298970.html]
[test_bug1304044.html]
[test_bug1332699.html]
[test_dnd_with_modifiers.html]

View File

@ -0,0 +1,78 @@
<!DOCTYPE html>
<html>
<meta charset="utf-8">
<title>Test dragstart, drag, dragover, drop, dragend with keyboard modifiers</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
<div id="test"></div>
<script>
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(() => {
let dragEvents = ["dragstart", "drag", "dragend"];
let dropEvents = ["dragover", "drop"];
let source = document.getElementById("source");
let target = document.getElementById("target");
dragEvents.forEach((ev, idx, array) => {
source.addEventListener(ev, (e) => {
ok(e.ctrlKey, e.type + ".ctrlKey should be true");
ok(!e.shiftKey, e.type + ".shiftKey should be false");
ok(e.altKey, e.type + ".altKey should be true");
}, {once: true});
});
dropEvents.forEach((ev, idx, array) => {
target.addEventListener(ev, (e) => {
ok(e.ctrlKey, e.type + ".ctrlKey should be true");
ok(!e.shiftKey, e.type + ".shiftKey should be false");
ok(e.altKey, e.type + ".altKey should be true");
}, {once: true});
});
source.addEventListener("dragstart", (e) => {
e.preventDefault();
}, {once: true});
source.addEventListener("dragend", (e) => {
SimpleTest.finish();
});
let selection = window.getSelection();
selection.selectAllChildren(source);
synthesizeMouse(source, 1, 1, {type: "mousedown", ctrlKey: true, altKey: true}, window);
synthesizeMouse(source, 10, 10, {type: "mousemove", ctrlKey: true, altKey: true}, window);
synthesizeMouse(source, 10, 10, {type: "mouseup", ctrlKey: true, altKey: true}, window);
let dragEvent = {
type: "drag",
ctrlKey: true,
altKey: true,
};
sendDragEvent(dragEvent, source, window);
let rect = target.getBoundingClientRect();
let dropEvent = {
ctrlKey: true,
altKey: true,
clientX: rect.left + rect.width / 2,
clientY: rect.top + rect.height / 2,
};
selection.selectAllChildren(source);
synthesizeDrop(source, target, [], "copy", window, window, dropEvent);
let dragEndEvent = {
type: "dragend",
ctrlKey: true,
altKey: true,
};
sendDragEvent(dragEndEvent, source, window);
});
</script>
<body>
<span id="source" style="font-size: 40px;">test</span>
<div id="target" contenteditable="true" width="50" height="50"></div>
</body>
</html>

View File

@ -2915,7 +2915,8 @@ ContentChild::RecvInvokeDragSession(nsTArray<IPCDataTransfer>&& aTransfers,
mozilla::ipc::IPCResult
ContentChild::RecvEndDragSession(const bool& aDoneDrag,
const bool& aUserCancelled,
const LayoutDeviceIntPoint& aDragEndPoint)
const LayoutDeviceIntPoint& aDragEndPoint,
const uint32_t& aKeyModifiers)
{
nsCOMPtr<nsIDragService> dragService =
do_GetService("@mozilla.org/widget/dragservice;1");
@ -2927,7 +2928,7 @@ ContentChild::RecvEndDragSession(const bool& aDoneDrag,
}
}
static_cast<nsBaseDragService*>(dragService.get())->SetDragEndPoint(aDragEndPoint);
dragService->EndDragSession(aDoneDrag);
dragService->EndDragSession(aDoneDrag, aKeyModifiers);
}
return IPC_OK();
}

View File

@ -442,7 +442,8 @@ public:
virtual mozilla::ipc::IPCResult RecvEndDragSession(const bool& aDoneDrag,
const bool& aUserCancelled,
const mozilla::LayoutDeviceIntPoint& aEndDragPoint) override;
const mozilla::LayoutDeviceIntPoint& aEndDragPoint,
const uint32_t& aKeyModifiers) override;
virtual mozilla::ipc::IPCResult
RecvPush(const nsCString& aScope,

View File

@ -493,7 +493,8 @@ child:
async InvokeDragSession(IPCDataTransfer[] transfers, uint32_t action);
async EndDragSession(bool aDoneDrag, bool aUserCancelled,
LayoutDeviceIntPoint aDragEndPoint);
LayoutDeviceIntPoint aDragEndPoint,
uint32_t aKeyModifiers);
async DomainSetChanged(uint32_t aSetType, uint32_t aChangeType, OptionalURIParams aDomain);

View File

@ -1760,7 +1760,7 @@ TabChild::RecvRealDragEvent(const WidgetDragEvent& aEvent,
if (dragService) {
// This will dispatch 'drag' event at the source if the
// drag transaction started in this process.
dragService->FireDragEventAtSource(eDrag);
dragService->FireDragEventAtSource(eDrag, aEvent.mModifiers);
}
}

View File

@ -3079,7 +3079,8 @@ TabParent::RecvInvokeDragSession(nsTArray<IPCDataTransfer>&& aTransfers,
if (!shell) {
if (Manager()->IsContentParent()) {
Unused << Manager()->AsContentParent()->SendEndDragSession(true, true,
LayoutDeviceIntPoint());
LayoutDeviceIntPoint(),
0);
}
return IPC_OK();
}

View File

@ -2131,7 +2131,7 @@ function synthesizeDrop(aSrcElement, aDestElement, aDragData, aDropEffect, aWind
return synthesizeDropAfterDragOver(result, dataTransfer, aDestElement,
aDestWindow, aDragEvent);
} finally {
ds.endDragSession(true);
ds.endDragSession(true, _parseModifiers(aDragEvent));
}
}

View File

@ -5680,7 +5680,8 @@ GetIntegerDeltaForEvent(NSEvent* aEvent)
if (aMessage == eDragOver) {
// fire the drag event at the source. Just ignore whether it was
// cancelled or not as there isn't actually a means to stop the drag
mDragService->FireDragEventAtSource(eDrag);
mDragService->FireDragEventAtSource(
eDrag, nsCocoaUtils::ModifiersForEvent([NSApp currentEvent]));
dragSession->SetCanDrop(false);
} else if (aMessage == eDrop) {
// We make the assumption that the dragOver handlers have correctly set
@ -5692,7 +5693,8 @@ GetIntegerDeltaForEvent(NSEvent* aEvent)
nsCOMPtr<nsIDOMNode> sourceNode;
dragSession->GetSourceNode(getter_AddRefs(sourceNode));
if (!sourceNode) {
mDragService->EndDragSession(false);
mDragService->EndDragSession(
false, nsCocoaUtils::ModifiersForEvent([NSApp currentEvent]));
}
return NSDragOperationNone;
}
@ -5753,7 +5755,8 @@ GetIntegerDeltaForEvent(NSEvent* aEvent)
// 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(false);
mDragService->EndDragSession(
false, nsCocoaUtils::ModifiersForEvent([NSApp currentEvent]));
}
}
default:
@ -5870,7 +5873,8 @@ GetIntegerDeltaForEvent(NSEvent* aEvent)
dataTransfer->SetDropEffectInt(nsIDragService::DRAGDROP_ACTION_NONE);
}
mDragService->EndDragSession(true);
mDragService->EndDragSession(
true, nsCocoaUtils::ModifiersForEvent(currentEvent));
NS_RELEASE(mDragService);
}

View File

@ -27,7 +27,7 @@ public:
nsIScriptableRegion* aRegion,
uint32_t aActionType);
// nsIDragService
NS_IMETHOD EndDragSession(bool aDoneDrag);
NS_IMETHOD EndDragSession(bool aDoneDrag, uint32_t aKeyModifiers);
NS_IMETHOD UpdateDragImage(nsIDOMNode* aImage, int32_t aImageX, int32_t aImageY);
// nsIDragSession

View File

@ -712,7 +712,7 @@ nsDragService::DragMovedWithView(NSDraggingSession* aSession, NSPoint aPoint)
}
NS_IMETHODIMP
nsDragService::EndDragSession(bool aDoneDrag)
nsDragService::EndDragSession(bool aDoneDrag, uint32_t aKeyModifiers)
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
@ -727,7 +727,7 @@ nsDragService::EndDragSession(bool aDoneDrag)
mUserCancelled = gUserCancelledDrag;
nsresult rv = nsBaseDragService::EndDragSession(aDoneDrag);
nsresult rv = nsBaseDragService::EndDragSession(aDoneDrag, aKeyModifiers);
mDataItems = nullptr;
return rv;

View File

@ -37,6 +37,7 @@
#include "nsViewManager.h"
#include "nsIFrame.h"
#include "nsGtkUtils.h"
#include "nsGtkKeyUtils.h"
#include "mozilla/gfx/2D.h"
#include "gfxPlatform.h"
#include "nsScreenGtk.h"
@ -522,7 +523,7 @@ nsDragService::StartDragSession()
}
NS_IMETHODIMP
nsDragService::EndDragSession(bool aDoneDrag)
nsDragService::EndDragSession(bool aDoneDrag, uint32_t aKeyModifiers)
{
MOZ_LOG(sDragLm, LogLevel::Debug, ("nsDragService::EndDragSession %d",
aDoneDrag));
@ -549,7 +550,7 @@ nsDragService::EndDragSession(bool aDoneDrag)
// We're done with the drag context.
mTargetDragContextForRemote = nullptr;
return nsBaseDragService::EndDragSession(aDoneDrag);
return nsBaseDragService::EndDragSession(aDoneDrag, aKeyModifiers);
}
// nsIDragSession
@ -1920,7 +1921,7 @@ nsDragService::RunScheduledTask()
// The 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 this app).
EndDragSession(false);
EndDragSession(false, GetCurrentModifiers());
}
}
@ -1942,7 +1943,7 @@ nsDragService::RunScheduledTask()
if (task == eDragTaskLeave || task == eDragTaskSourceEnd) {
if (task == eDragTaskSourceEnd) {
// Dispatch drag end events.
EndDragSession(true);
EndDragSession(true, GetCurrentModifiers());
}
// Nothing more to do
@ -2013,7 +2014,7 @@ nsDragService::RunScheduledTask()
mTargetWindow = nullptr;
// Make sure to end the drag session. If this drag started in a
// different app, we won't get a drag_end signal to end it from.
EndDragSession(true);
EndDragSession(true, GetCurrentModifiers());
}
// We're done with the drag context.
@ -2085,7 +2086,7 @@ nsDragService::DispatchMotionEvents()
{
mCanDrop = false;
FireDragEventAtSource(eDrag);
FireDragEventAtSource(eDrag, GetCurrentModifiers());
mTargetWindow->DispatchDragEvent(eDragOver, mTargetWindowPoint,
mTargetTime);
@ -2107,3 +2108,9 @@ nsDragService::DispatchDropEvent()
return mCanDrop;
}
/* static */ uint32_t
nsDragService::GetCurrentModifiers()
{
return mozilla::widget::KeymapWrapper::ComputeCurrentKeyModifiers();
}

View File

@ -69,7 +69,7 @@ public:
uint32_t aActionType,
nsContentPolicyType aContentPolicyType) override;
NS_IMETHOD StartDragSession() override;
NS_IMETHOD EndDragSession(bool aDoneDrag) override;
NS_IMETHOD EndDragSession(bool aDoneDrag, uint32_t aKeyModifiers) override;
// nsIDragSession
NS_IMETHOD SetCanDrop (bool aCanDrop) override;
@ -220,6 +220,7 @@ private:
void DispatchMotionEvents();
void ReplyToDragMotion(GdkDragContext* aDragContext);
gboolean DispatchDropEvent();
static uint32_t GetCurrentModifiers();
};
#endif // nsDragService_h__

View File

@ -606,44 +606,59 @@ KeymapWrapper::AreModifiersActive(Modifiers aModifiers,
return true;
}
/* static */ uint32_t
KeymapWrapper::ComputeCurrentKeyModifiers()
{
return ComputeKeyModifiers(GetCurrentModifierState());
}
/* static */ uint32_t
KeymapWrapper::ComputeKeyModifiers(guint aModifierState)
{
KeymapWrapper* keymapWrapper = GetInstance();
uint32_t keyModifiers = 0;
// DOM Meta key should be TRUE only on Mac. We need to discuss this
// issue later.
if (keymapWrapper->AreModifiersActive(SHIFT, aModifierState)) {
keyModifiers |= MODIFIER_SHIFT;
}
if (keymapWrapper->AreModifiersActive(CTRL, aModifierState)) {
keyModifiers |= MODIFIER_CONTROL;
}
if (keymapWrapper->AreModifiersActive(ALT, aModifierState)) {
keyModifiers |= MODIFIER_ALT;
}
if (keymapWrapper->AreModifiersActive(META, aModifierState)) {
keyModifiers |= MODIFIER_META;
}
if (keymapWrapper->AreModifiersActive(SUPER, aModifierState) ||
keymapWrapper->AreModifiersActive(HYPER, aModifierState)) {
keyModifiers |= MODIFIER_OS;
}
if (keymapWrapper->AreModifiersActive(LEVEL3, aModifierState) ||
keymapWrapper->AreModifiersActive(LEVEL5, aModifierState)) {
keyModifiers |= MODIFIER_ALTGRAPH;
}
if (keymapWrapper->AreModifiersActive(CAPS_LOCK, aModifierState)) {
keyModifiers |= MODIFIER_CAPSLOCK;
}
if (keymapWrapper->AreModifiersActive(NUM_LOCK, aModifierState)) {
keyModifiers |= MODIFIER_NUMLOCK;
}
if (keymapWrapper->AreModifiersActive(SCROLL_LOCK, aModifierState)) {
keyModifiers |= MODIFIER_SCROLLLOCK;
}
return keyModifiers;
}
/* static */ void
KeymapWrapper::InitInputEvent(WidgetInputEvent& aInputEvent,
guint aModifierState)
{
KeymapWrapper* keymapWrapper = GetInstance();
aInputEvent.mModifiers = 0;
// DOM Meta key should be TRUE only on Mac. We need to discuss this
// issue later.
if (keymapWrapper->AreModifiersActive(SHIFT, aModifierState)) {
aInputEvent.mModifiers |= MODIFIER_SHIFT;
}
if (keymapWrapper->AreModifiersActive(CTRL, aModifierState)) {
aInputEvent.mModifiers |= MODIFIER_CONTROL;
}
if (keymapWrapper->AreModifiersActive(ALT, aModifierState)) {
aInputEvent.mModifiers |= MODIFIER_ALT;
}
if (keymapWrapper->AreModifiersActive(META, aModifierState)) {
aInputEvent.mModifiers |= MODIFIER_META;
}
if (keymapWrapper->AreModifiersActive(SUPER, aModifierState) ||
keymapWrapper->AreModifiersActive(HYPER, aModifierState)) {
aInputEvent.mModifiers |= MODIFIER_OS;
}
if (keymapWrapper->AreModifiersActive(LEVEL3, aModifierState) ||
keymapWrapper->AreModifiersActive(LEVEL5, aModifierState)) {
aInputEvent.mModifiers |= MODIFIER_ALTGRAPH;
}
if (keymapWrapper->AreModifiersActive(CAPS_LOCK, aModifierState)) {
aInputEvent.mModifiers |= MODIFIER_CAPSLOCK;
}
if (keymapWrapper->AreModifiersActive(NUM_LOCK, aModifierState)) {
aInputEvent.mModifiers |= MODIFIER_NUMLOCK;
}
if (keymapWrapper->AreModifiersActive(SCROLL_LOCK, aModifierState)) {
aInputEvent.mModifiers |= MODIFIER_SCROLLLOCK;
}
aInputEvent.mModifiers = ComputeKeyModifiers(aModifierState);
MOZ_LOG(gKeymapWrapperLog, LogLevel::Debug,
("%p InitInputEvent, aModifierState=0x%08X, "

View File

@ -106,6 +106,18 @@ public:
static bool AreModifiersActive(Modifiers aModifiers,
guint aModifierState);
/**
* Utility function to compute current keyboard modifiers for
* WidgetInputEvent
*/
static uint32_t ComputeCurrentKeyModifiers();
/**
* Utility function to covert platform modifier state to keyboard modifiers
* of WidgetInputEvent
*/
static uint32_t ComputeKeyModifiers(guint aModifierState);
/**
* InitInputEvent() initializes the aInputEvent with aModifierState.
*/

View File

@ -3337,9 +3337,7 @@ nsWindow::DispatchDragEvent(EventMessage aMsg, const LayoutDeviceIntPoint& aRefP
{
WidgetDragEvent event(true, aMsg, this);
if (aMsg == eDragOver) {
InitDragEvent(event);
}
InitDragEvent(event);
event.mRefPoint = aRefPoint;
event.AssignEventTime(GetWidgetEventTime(aTime));

View File

@ -380,14 +380,14 @@ nsBaseDragService::TakeChildProcessDragAction()
//-------------------------------------------------------------------------
NS_IMETHODIMP
nsBaseDragService::EndDragSession(bool aDoneDrag)
nsBaseDragService::EndDragSession(bool aDoneDrag, uint32_t aKeyModifiers)
{
if (!mDoingDrag) {
return NS_ERROR_FAILURE;
}
if (aDoneDrag && !mSuppressLevel) {
FireDragEventAtSource(eDragEnd);
FireDragEventAtSource(eDragEnd, aKeyModifiers);
}
if (mDragPopup) {
@ -400,7 +400,8 @@ nsBaseDragService::EndDragSession(bool aDoneDrag)
for (uint32_t i = 0; i < mChildProcesses.Length(); ++i) {
mozilla::Unused << mChildProcesses[i]->SendEndDragSession(aDoneDrag,
mUserCancelled,
mEndDragPoint);
mEndDragPoint,
aKeyModifiers);
}
mChildProcesses.Clear();
@ -458,7 +459,8 @@ nsBaseDragService::DiscardInternalTransferData()
}
NS_IMETHODIMP
nsBaseDragService::FireDragEventAtSource(EventMessage aEventMessage)
nsBaseDragService::FireDragEventAtSource(EventMessage aEventMessage,
uint32_t aKeyModifiers)
{
if (mSourceNode && !mSuppressLevel) {
nsCOMPtr<nsIDocument> doc = do_QueryInterface(mSourceDocument);
@ -472,7 +474,7 @@ nsBaseDragService::FireDragEventAtSource(EventMessage aEventMessage)
event.mRefPoint = mEndDragPoint;
event.mUserCancelled = mUserCancelled;
}
event.mModifiers = aKeyModifiers;
// Send the drag event to APZ, which needs to know about them to be
// able to accurately detect the end of a drag gesture.
if (nsPresContext* presContext = presShell->GetPresContext()) {
@ -781,7 +783,7 @@ nsBaseDragService::ConvertToUnscaledDevPixels(nsPresContext* aPresContext,
NS_IMETHODIMP
nsBaseDragService::Suppress()
{
EndDragSession(false);
EndDragSession(false, 0);
++mSuppressLevel;
return NS_OK;
}

View File

@ -124,12 +124,14 @@ interface nsIDragService : nsISupports
* If aDoneDrag is true, the drag has finished, otherwise the drag has
* just left the window.
*/
void endDragSession ( in boolean aDoneDrag ) ;
void endDragSession(in boolean aDoneDrag,
[optional] in unsigned long aKeyModifiers);
/**
* Fire a drag event at the source of the drag
*/
[noscript] void fireDragEventAtSource(in EventMessage aEventMessage);
[noscript] void fireDragEventAtSource(in EventMessage aEventMessage,
in unsigned long aKeyModifiers);
/**
* Increase/decrease dragging suppress level by one.

View File

@ -38,6 +38,7 @@
#include "nsRect.h"
#include "nsMathUtils.h"
#include "WinUtils.h"
#include "KeyboardLayout.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/DataSurfaceHelpers.h"
#include "mozilla/gfx/Tools.h"
@ -369,7 +370,8 @@ nsDragService::StartInvokingDragSession(IDataObject * aDataObj,
// until bug 1224754 is fixed.
SetDragEndPoint(LayoutDeviceIntPoint(NSToIntRound(cssPos.x),
NSToIntRound(cssPos.y)));
EndDragSession(true);
ModifierKeyState modifierKeyState;
EndDragSession(true, modifierKeyState.GetModifiers());
mDoingDrag = false;
@ -625,7 +627,7 @@ nsDragService::IsCollectionObject(IDataObject* inDataObj)
// w/out crashing when we're still holding onto their data
//
NS_IMETHODIMP
nsDragService::EndDragSession(bool aDoneDrag)
nsDragService::EndDragSession(bool aDoneDrag, uint32_t aKeyModifiers)
{
// Bug 100180: If we've got mouse events captured, make sure we release it -
// that way, if we happen to call EndDragSession before diving into a nested
@ -634,7 +636,7 @@ nsDragService::EndDragSession(bool aDoneDrag)
::ReleaseCapture();
}
nsBaseDragService::EndDragSession(aDoneDrag);
nsBaseDragService::EndDragSession(aDoneDrag, aKeyModifiers);
NS_IF_RELEASE(mDataObject);
return NS_OK;

View File

@ -32,7 +32,7 @@ public:
NS_IMETHOD GetData(nsITransferable * aTransferable, uint32_t anItem);
NS_IMETHOD GetNumDropItems(uint32_t * aNumItems);
NS_IMETHOD IsDataFlavorSupported(const char *aDataFlavor, bool *_retval);
NS_IMETHOD EndDragSession(bool aDoneDrag);
NS_IMETHOD EndDragSession(bool aDoneDrag, uint32_t aKeyModifiers);
NS_IMETHOD UpdateDragImage(nsIDOMNode* aImage, int32_t aImageX, int32_t aImageY);
// native impl.

View File

@ -351,7 +351,8 @@ nsNativeDragTarget::DragOver(DWORD grfKeyState,
GetDropTargetHelper()->DragOver(&pt, *pdwEffect);
}
mDragService->FireDragEventAtSource(eDrag);
ModifierKeyState modifierKeyState;
mDragService->FireDragEventAtSource(eDrag, modifierKeyState.GetModifiers());
// Now process the native drag state and then dispatch the event
ProcessDrag(eDragOver, grfKeyState, ptl, pdwEffect);
@ -387,7 +388,8 @@ nsNativeDragTarget::DragLeave()
// 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(false);
ModifierKeyState modifierKeyState;
mDragService->EndDragSession(false, modifierKeyState.GetModifiers());
}
}
@ -410,7 +412,8 @@ nsNativeDragTarget::DragCancel()
GetDropTargetHelper()->DragLeave();
}
if (mDragService) {
mDragService->EndDragSession(false);
ModifierKeyState modifierKeyState;
mDragService->EndDragSession(false, modifierKeyState.GetModifiers());
}
this->Release(); // matching the AddRef in DragEnter
mTookOwnRef = false;
@ -471,7 +474,8 @@ nsNativeDragTarget::Drop(LPDATAOBJECT pData,
cpos.x = GET_X_LPARAM(pos);
cpos.y = GET_Y_LPARAM(pos);
winDragService->SetDragEndPoint(nsIntPoint(cpos.x, cpos.y));
serv->EndDragSession(true);
ModifierKeyState modifierKeyState;
serv->EndDragSession(true, modifierKeyState.GetModifiers());
// release the ref that was taken in DragEnter
NS_ASSERTION(mTookOwnRef, "want to release own ref, but not taken!");