From 2872d3cc6bad509fc77640b3eaff78c9d18da740 Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Fri, 16 Dec 2011 15:17:48 +0900 Subject: [PATCH] Bug 706743 tooltip listener should ignore mousemove and mouseout events during drag r=enndeakin --- layout/xul/base/src/nsXULTooltipListener.cpp | 38 ++++- layout/xul/test/Makefile.in | 1 + layout/xul/test/browser_bug706743.js | 150 +++++++++++++++++++ 3 files changed, 183 insertions(+), 6 deletions(-) create mode 100644 layout/xul/test/browser_bug706743.js diff --git a/layout/xul/base/src/nsXULTooltipListener.cpp b/layout/xul/base/src/nsXULTooltipListener.cpp index fb2d779100f2..7f131d0f801e 100644 --- a/layout/xul/base/src/nsXULTooltipListener.cpp +++ b/layout/xul/base/src/nsXULTooltipListener.cpp @@ -48,6 +48,8 @@ #include "nsIPopupBoxObject.h" #include "nsMenuPopupFrame.h" #include "nsIServiceManager.h" +#include "nsIDragService.h" +#include "nsIDragSession.h" #ifdef MOZ_XUL #include "nsITreeView.h" #endif @@ -267,14 +269,38 @@ nsXULTooltipListener::HandleEvent(nsIDOMEvent* aEvent) type.EqualsLiteral("keydown") || type.EqualsLiteral("mousedown") || type.EqualsLiteral("mouseup") || - type.EqualsLiteral("dragstart")) + type.EqualsLiteral("dragstart")) { HideTooltip(); - else if (type.EqualsLiteral("mousemove")) - MouseMove(aEvent); - else if (type.EqualsLiteral("mouseout")) - MouseOut(aEvent); - else if (type.EqualsLiteral("popuphiding")) + return NS_OK; + } + + if (type.EqualsLiteral("popuphiding")) { DestroyTooltip(); + return NS_OK; + } + + // Note that mousemove, mouseover and mouseout might be + // fired even during dragging due to widget's bug. + nsCOMPtr dragService = + do_GetService("@mozilla.org/widget/dragservice;1"); + NS_ENSURE_TRUE(dragService, NS_OK); + nsCOMPtr dragSession; + dragService->GetCurrentSession(getter_AddRefs(dragSession)); + if (dragSession) { + return NS_OK; + } + + // Not dragging. + + if (type.EqualsLiteral("mousemove")) { + MouseMove(aEvent); + return NS_OK; + } + + if (type.EqualsLiteral("mouseout")) { + MouseOut(aEvent); + return NS_OK; + } return NS_OK; } diff --git a/layout/xul/test/Makefile.in b/layout/xul/test/Makefile.in index 5f26ffd15e78..3af54b40bb59 100644 --- a/layout/xul/test/Makefile.in +++ b/layout/xul/test/Makefile.in @@ -67,6 +67,7 @@ libs:: $(_CHROME_FILES) ifneq (mobile,$(MOZ_BUILD_APP)) _BROWSER_FILES = \ browser_bug703210.js \ + browser_bug706743.js \ $(NULL) libs:: $(_BROWSER_FILES) diff --git a/layout/xul/test/browser_bug706743.js b/layout/xul/test/browser_bug706743.js new file mode 100644 index 000000000000..ba449ab22a90 --- /dev/null +++ b/layout/xul/test/browser_bug706743.js @@ -0,0 +1,150 @@ +function test() { + waitForExplicitFinish(); + gBrowser.selectedTab = gBrowser.addTab(); + + let target; + let doc; + let win; + let callbackOnPopupShown; + let callbackOnPopupHidden; + let dragService = Components.classes["@mozilla.org/widget/dragservice;1"]. + getService(Components.interfaces.nsIDragService); + + let onPopupHidden = function (aEvent) + { + if (aEvent.originalTarget.localName != "tooltip") { + return; + } + if (callbackOnPopupHidden) { + setTimeout(callbackOnPopupHidden, 0); + } + } + + let onPopupShown = function (aEvent) + { + if (aEvent.originalTarget.localName != "tooltip") { + return; + } + if (callbackOnPopupShown) { + setTimeout(callbackOnPopupShown, 0); + } + } + + let finishTest = function () + { + document.removeEventListener("popupshown", onPopupShown, true); + document.removeEventListener("popuphidden", onPopupHidden, true); + + gBrowser.removeCurrentTab(); + finish(); + } + + let testHideTooltipByMouseDown = function () + { + callbackOnPopupShown = function () { + callbackOnPopupShown = null; + ok(true, "tooltip is shown before testing mousedown"); + + // hide tooltip by mousemove to outside. + callbackOnPopupHidden = function () { + callbackOnPopupHidden = null; + ok(true, "The tooltip is hidden by mousedown"); + + EventUtils.synthesizeMouse(target, 5, 15, { type: "mouseup" }, win); + EventUtils.synthesizeMouse(target, -5, 15, { type: "mousemove" }, win); + + setTimeout(finishTest, 0); + } + EventUtils.synthesizeMouse(target, 5, 15, { type: "mousedown" }, win); + } + EventUtils.synthesizeMouse(target, 5, 15, { type: "mousemove" }, win); + } + + let testShowTooltipAgain = function () + { + // If tooltip listener used a flag for managing D&D state, we would need + // to test if the tooltip is shown after drag. + callbackOnPopupShown = function () { + callbackOnPopupShown = null; + ok(true, "tooltip is shown after drag"); + + // hide tooltip by mousemove to outside. + callbackOnPopupHidden = function () { + callbackOnPopupHidden = null; + ok(true, "The tooltip is hidden again"); + + setTimeout(testHideTooltipByMouseDown, 0); + } + EventUtils.synthesizeMouse(target, -5, 15, { type: "mousemove" }, win); + } + EventUtils.synthesizeMouse(target, 5, 15, { type: "mousemove" }, win); + } + + let testDuringDnD = function () + { + // mousemove into the target and start drag by emulation via nsIDragService. + // Note that on some platforms, we cannot actually start the drag by + // synthesized events. E.g., Windows waits an actual mousemove event after + // dragstart. + callbackOnPopupShown = function () { + callbackOnPopupShown = null; + ok(false, "tooltip is shown during drag"); + } + dragService.startDragSession(); + // Emulate a buggy mousemove event. widget might dispatch mousemove event + // during drag. + EventUtils.synthesizeMouse(target, 5, 15, { type: "mousemove" }, win); + setTimeout(function () { + callbackOnPopupShown = null; + ok(true, "tooltip isn't shown during drag"); + dragService.endDragSession(true); + EventUtils.synthesizeMouse(target, -5, -5, { type: "mousemove" }, win); + + setTimeout(testShowTooltipAgain, 0); + }, 600); + } + + let onLoad = function (aEvent) + { + doc = gBrowser.contentDocument; + win = gBrowser.contentWindow; + target = doc.getElementById("target"); + + document.addEventListener("popupshown", onPopupShown, true); + document.addEventListener("popuphidden", onPopupHidden, true); + + EventUtils.synthesizeMouse(target, -5, -5, { type: "mousemove" }, win); + + // show tooltip by mousemove into target. + callbackOnPopupShown = function () + { + callbackOnPopupShown = null; + ok(true, "The tooltip is shown"); + + // hide tooltip by mousemove to outside. + callbackOnPopupHidden = function () { + callbackOnPopupHidden = null; + ok(true, "The tooltip is hidden"); + + setTimeout(testDuringDnD, 0); + } + EventUtils.synthesizeMouse(target, -5, 15, { type: "mousemove" }, win); + } + EventUtils.synthesizeMouse(target, 5, 15, { type: "mousemove" }, win); + } + + gBrowser.selectedBrowser.addEventListener("load", + function () { + gBrowser.selectedBrowser. + removeEventListener("load", arguments.callee, true); + setTimeout(onLoad, 0); + }, true); + + content.location = "data:text/html," + + "here is an anchor element"; +} + + +