diff --git a/layout/xul/base/public/nsXULPopupManager.h b/layout/xul/base/public/nsXULPopupManager.h index 6d7155f7bb54..5045c9db46ae 100644 --- a/layout/xul/base/public/nsXULPopupManager.h +++ b/layout/xul/base/public/nsXULPopupManager.h @@ -767,6 +767,9 @@ protected: // root prescontext's root frame. nsIntPoint mCachedMousePoint; + // cached modifiers + PRInt8 mCachedModifiers; + // set to the currently active menu bar, if any nsMenuBarFrame* mActiveMenuBar; diff --git a/layout/xul/base/src/nsXULPopupManager.cpp b/layout/xul/base/src/nsXULPopupManager.cpp index 788cad55f4f9..88c2278ad6ed 100644 --- a/layout/xul/base/src/nsXULPopupManager.cpp +++ b/layout/xul/base/src/nsXULPopupManager.cpp @@ -73,6 +73,11 @@ #include "nsIObserverService.h" #include "mozilla/Services.h" +#define FLAG_ALT 0x01 +#define FLAG_CONTROL 0x02 +#define FLAG_SHIFT 0x04 +#define FLAG_META 0x08 + const nsNavigationDirection DirectionFromKeyCodeTable[2][6] = { { eNavigationDirection_Last, // NS_VK_END @@ -140,6 +145,7 @@ NS_IMPL_ISUPPORTS4(nsXULPopupManager, nsXULPopupManager::nsXULPopupManager() : mRangeOffset(0), mCachedMousePoint(0, 0), + mCachedModifiers(0), mActiveMenuBar(nsnull), mPopups(nsnull), mNoHidePanels(nsnull), @@ -462,6 +468,8 @@ nsXULPopupManager::InitTriggerEvent(nsIDOMEvent* aEvent, nsIContent* aPopup, } } + mCachedModifiers = 0; + nsCOMPtr uiEvent = do_QueryInterface(aEvent); if (uiEvent) { uiEvent->GetRangeParent(getter_AddRefs(mRangeParent)); @@ -475,6 +483,22 @@ nsXULPopupManager::InitTriggerEvent(nsIDOMEvent* aEvent, nsIContent* aPopup, nsEvent* event; event = privateEvent->GetInternalNSEvent(); if (event) { + if (event->eventStructType == NS_MOUSE_EVENT || + event->eventStructType == NS_KEY_EVENT) { + nsInputEvent* inputEvent = static_cast(event); + if (inputEvent->isAlt) { + mCachedModifiers |= FLAG_ALT; + } + if (inputEvent->isControl) { + mCachedModifiers |= FLAG_CONTROL; + } + if (inputEvent->isShift) { + mCachedModifiers |= FLAG_SHIFT; + } + if (inputEvent->isMeta) { + mCachedModifiers |= FLAG_META; + } + } nsIDocument* doc = aPopup->GetCurrentDoc(); if (doc) { nsIPresShell* presShell = doc->GetShell(); @@ -1187,10 +1211,19 @@ nsXULPopupManager::FirePopupShowingEvent(nsIContent* aPopup, } event.refPoint = mCachedMousePoint; + + event.isAlt = !!(mCachedModifiers & FLAG_ALT); + event.isControl = !!(mCachedModifiers & FLAG_CONTROL); + event.isShift = !!(mCachedModifiers & FLAG_SHIFT); + event.isMeta = !!(mCachedModifiers & FLAG_META); + nsEventDispatcher::Dispatch(popup, presContext, &event, nsnull, &status); + mCachedMousePoint = nsIntPoint(0, 0); mOpeningPopup = nsnull; + mCachedModifiers = 0; + // if a panel, blur whatever has focus so that the panel can take the focus. // This is done after the popupshowing event in case that event is cancelled. // Using noautofocus="true" will disable this behaviour, which is needed for diff --git a/toolkit/content/tests/chrome/popup_trigger.js b/toolkit/content/tests/chrome/popup_trigger.js index 97b37b8b911e..3c0e4af67831 100644 --- a/toolkit/content/tests/chrome/popup_trigger.js +++ b/toolkit/content/tests/chrome/popup_trigger.js @@ -3,6 +3,22 @@ var gTrigger = null; var gIsMenu = false; var gScreenX = -1, gScreenY = -1; var gCachedEvent = null; +var gCachedEvent2 = null; + +function cacheEvent(modifiers) +{ + var cachedEvent = null; + + var mouseFn = function(event) { + cachedEvent = event; + } + + window.addEventListener("mousedown", mouseFn, false); + synthesizeMouse(document.documentElement, 0, 0, modifiers); + window.removeEventListener("mousedown", mouseFn, false); + + return cachedEvent; +} function runTests() { @@ -15,17 +31,13 @@ function runTests() gIsMenu = gTrigger.boxObject instanceof Components.interfaces.nsIMenuBoxObject; - var mouseFn = function(event) { - gScreenX = event.screenX; - gScreenY = event.screenY; - // cache the event so that we can use it in calls to openPopup - gCachedEvent = event; - } + // a hacky way to get the screen position of the document. Cache the event + // so that we can use it in calls to openPopup. + gCachedEvent = cacheEvent({ shiftKey: true }); + gScreenX = gCachedEvent.screenX; + gScreenY = gCachedEvent.screenY; + gCachedEvent2 = cacheEvent({ altKey: true, ctrlKey: true, shiftKey: true, metaKey: true }); - // a hacky way to get the screen position of the document - window.addEventListener("mousedown", mouseFn, false); - synthesizeMouse(document.documentElement, 0, 0, { }); - window.removeEventListener("mousedown", mouseFn, false); startPopupTests(popupTests); } @@ -254,7 +266,7 @@ var popupTests = [ // can be used to override the popup's position. This test also passes an // event to openPopup to check the trigger node. testname: "open popup anchored with override", - events: [ "popupshowing thepopup", "popupshown thepopup" ], + events: [ "popupshowing thepopup 0010", "popupshown thepopup" ], test: function(testname, step) { // attribute overrides the position passed in gMenuPopup.setAttribute("position", "end_after"); @@ -403,7 +415,7 @@ var popupTests = [ }, { testname: "open context popup at screen", - events: [ "popupshowing thepopup", "popupshown thepopup" ], + events: [ "popupshowing thepopup 0010", "popupshown thepopup" ], test: function(testname, step) { gExpectedTriggerNode = gCachedEvent.target; gMenuPopup.openPopupAtScreen(gScreenX + 8, gScreenY + 16, true, gCachedEvent); @@ -558,6 +570,14 @@ var popupTests = [ checkActive(gMenuPopup, "", testname); } }, +{ + testname: "open context popup at screen with all modifiers set", + events: [ "popupshowing thepopup 1111", "popupshown thepopup" ], + autohide: "thepopup", + test: function(testname, step) { + gMenuPopup.openPopupAtScreen(gScreenX + 8, gScreenY + 16, true, gCachedEvent2); + } +}, { testname: "open popup with open property", events: [ "popupshowing thepopup", "popupshown thepopup" ], diff --git a/toolkit/content/tests/widgets/popup_shared.js b/toolkit/content/tests/widgets/popup_shared.js index d5394d72bfea..5f4d0de56e3c 100644 --- a/toolkit/content/tests/widgets/popup_shared.js +++ b/toolkit/content/tests/widgets/popup_shared.js @@ -133,6 +133,16 @@ function eventOccurred(event) matches = eventitem[0] == event.type && eventitem[1] == event.target.id; } + var modifiersMask = eventitem[2]; + if (modifiersMask) { + var m = ""; + m += event.altKey ? '1' : '0'; + m += event.ctrlKey ? '1' : '0'; + m += event.shiftKey ? '1' : '0'; + m += event.metaKey ? '1' : '0'; + is(m, modifiersMask, test.testname + " modifiers mask matches"); + } + var expectedState; switch (event.type) { case "popupshowing": expectedState = "showing"; break;