зеркало из https://github.com/mozilla/pjs.git
Bug 355375 - Make context menuitems blink when chosen. r=enn
--HG-- extra : rebase_source : 87b93bda596b4af4c1adf294ecc91510047ca0f1
This commit is contained in:
Родитель
1c3a9a9faf
Коммит
0967efa34e
|
@ -271,7 +271,7 @@ public:
|
|||
PRBool aAlt,
|
||||
PRBool aMeta,
|
||||
PRBool aUserInput,
|
||||
CloseMenuMode aCloseMenuMode)
|
||||
PRBool aFlipChecked)
|
||||
: mMenu(aMenu),
|
||||
mIsTrusted(aIsTrusted),
|
||||
mShift(aShift),
|
||||
|
@ -279,13 +279,16 @@ public:
|
|||
mAlt(aAlt),
|
||||
mMeta(aMeta),
|
||||
mUserInput(aUserInput),
|
||||
mCloseMenuMode(aCloseMenuMode)
|
||||
mFlipChecked(aFlipChecked),
|
||||
mCloseMenuMode(CloseMenuMode_Auto)
|
||||
{
|
||||
NS_ASSERTION(aMenu, "null menu supplied to nsXULMenuCommandEvent constructor");
|
||||
}
|
||||
|
||||
NS_IMETHOD Run();
|
||||
|
||||
void SetCloseMenuMode(CloseMenuMode aCloseMenuMode) { mCloseMenuMode = aCloseMenuMode; }
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIContent> mMenu;
|
||||
PRBool mIsTrusted;
|
||||
|
@ -294,6 +297,7 @@ private:
|
|||
PRBool mAlt;
|
||||
PRBool mMeta;
|
||||
PRBool mUserInput;
|
||||
PRBool mFlipChecked;
|
||||
CloseMenuMode mCloseMenuMode;
|
||||
};
|
||||
|
||||
|
@ -480,10 +484,10 @@ public:
|
|||
* Execute a menu command from the triggering event aEvent.
|
||||
*
|
||||
* aMenu - a menuitem to execute
|
||||
* aEvent - the mouse event which triggered the menu to be executed,
|
||||
* may be null
|
||||
* aEvent - an nsXULMenuCommandEvent that contains all the info from the mouse
|
||||
* event which triggered the menu to be executed, may not be null
|
||||
*/
|
||||
void ExecuteMenu(nsIContent* aMenu, nsEvent* aEvent);
|
||||
void ExecuteMenu(nsIContent* aMenu, nsXULMenuCommandEvent* aEvent);
|
||||
|
||||
/**
|
||||
* Return true if the popup for the supplied content node is open.
|
||||
|
|
|
@ -91,6 +91,9 @@ public:
|
|||
|
||||
virtual nsIAtom* GetType() const { return nsGkAtoms::menuBarFrame; }
|
||||
|
||||
virtual void LockMenuUntilClosed(PRBool aLock) {}
|
||||
virtual PRBool IsMenuLocked() { return PR_FALSE; }
|
||||
|
||||
// Non-interface helpers
|
||||
|
||||
void
|
||||
|
|
|
@ -80,6 +80,8 @@
|
|||
#include "nsDisplayList.h"
|
||||
#include "nsIReflowCallback.h"
|
||||
#include "nsISound.h"
|
||||
#include "nsEventStateManager.h"
|
||||
#include "nsIDOMXULMenuListElement.h"
|
||||
|
||||
#define NS_MENU_POPUP_LIST_INDEX 0
|
||||
|
||||
|
@ -97,6 +99,7 @@ nsString *nsMenuFrame::gControlText = nsnull;
|
|||
nsString *nsMenuFrame::gMetaText = nsnull;
|
||||
nsString *nsMenuFrame::gAltText = nsnull;
|
||||
nsString *nsMenuFrame::gModifierSeparator = nsnull;
|
||||
const PRInt32 kBlinkDelay = 67; // milliseconds
|
||||
|
||||
// this class is used for dispatching menu activation events asynchronously.
|
||||
class nsMenuActivateEvent : public nsRunnable
|
||||
|
@ -191,7 +194,8 @@ nsMenuFrame::nsMenuFrame(nsIPresShell* aShell, nsStyleContext* aContext):
|
|||
mChecked(PR_FALSE),
|
||||
mType(eMenuType_Normal),
|
||||
mMenuParent(nsnull),
|
||||
mPopupFrame(nsnull)
|
||||
mPopupFrame(nsnull),
|
||||
mBlinkState(0)
|
||||
{
|
||||
|
||||
} // cntr
|
||||
|
@ -376,6 +380,8 @@ nsMenuFrame::DestroyFrom(nsIFrame* aDestructRoot)
|
|||
mOpenTimer->Cancel();
|
||||
}
|
||||
|
||||
StopBlinking();
|
||||
|
||||
// Null out the pointer to this frame in the mediator wrapper so that it
|
||||
// doesn't try to interact with a deallocated frame.
|
||||
mTimerMediator->ClearFrame();
|
||||
|
@ -417,7 +423,8 @@ nsMenuFrame::HandleEvent(nsPresContext* aPresContext,
|
|||
nsEventStatus* aEventStatus)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aEventStatus);
|
||||
if (nsEventStatus_eConsumeNoDefault == *aEventStatus) {
|
||||
if (nsEventStatus_eConsumeNoDefault == *aEventStatus ||
|
||||
(mMenuParent && mMenuParent->IsMenuLocked())) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -897,6 +904,31 @@ nsMenuFrame::Notify(nsITimer* aTimer)
|
|||
}
|
||||
}
|
||||
}
|
||||
} else if (aTimer == mBlinkTimer) {
|
||||
switch (mBlinkState++) {
|
||||
case 0:
|
||||
NS_ASSERTION(false, "Blink timer fired while not blinking");
|
||||
StopBlinking();
|
||||
break;
|
||||
case 1:
|
||||
{
|
||||
// Turn the highlight back on and wait for a while before closing the menu.
|
||||
nsWeakFrame weakFrame(this);
|
||||
mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::menuactive,
|
||||
NS_LITERAL_STRING("true"), PR_TRUE);
|
||||
if (weakFrame.IsAlive()) {
|
||||
aTimer->InitWithCallback(mTimerMediator, kBlinkDelay, nsITimer::TYPE_ONE_SHOT);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (mMenuParent) {
|
||||
mMenuParent->LockMenuUntilClosed(PR_FALSE);
|
||||
}
|
||||
PassMenuCommandEventToPopupManager();
|
||||
StopBlinking();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -1154,31 +1186,123 @@ nsMenuFrame::BuildAcceleratorText()
|
|||
void
|
||||
nsMenuFrame::Execute(nsGUIEvent *aEvent)
|
||||
{
|
||||
nsWeakFrame weakFrame(this);
|
||||
// flip "checked" state if we're a checkbox menu, or an un-checked radio menu
|
||||
PRBool needToFlipChecked = PR_FALSE;
|
||||
if (mType == eMenuType_Checkbox || (mType == eMenuType_Radio && !mChecked)) {
|
||||
if (!mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::autocheck,
|
||||
nsGkAtoms::_false, eCaseMatters)) {
|
||||
if (mChecked) {
|
||||
mContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::checked,
|
||||
PR_TRUE);
|
||||
ENSURE_TRUE(weakFrame.IsAlive());
|
||||
}
|
||||
else {
|
||||
mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::checked, NS_LITERAL_STRING("true"),
|
||||
PR_TRUE);
|
||||
ENSURE_TRUE(weakFrame.IsAlive());
|
||||
}
|
||||
}
|
||||
needToFlipChecked = !mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::autocheck,
|
||||
nsGkAtoms::_false, eCaseMatters);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISound> sound(do_CreateInstance("@mozilla.org/sound;1"));
|
||||
if (sound)
|
||||
sound->PlayEventSound(nsISound::EVENT_MENU_EXECUTE);
|
||||
|
||||
StartBlinking(aEvent, needToFlipChecked);
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsMenuFrame::ShouldBlink()
|
||||
{
|
||||
PRInt32 shouldBlink = 0;
|
||||
nsCOMPtr<nsILookAndFeel> lookAndFeel(do_GetService(kLookAndFeelCID));
|
||||
if (lookAndFeel) {
|
||||
lookAndFeel->GetMetric(nsILookAndFeel::eMetric_ChosenMenuItemsShouldBlink, shouldBlink);
|
||||
}
|
||||
if (!shouldBlink)
|
||||
return PR_FALSE;
|
||||
|
||||
// Don't blink in editable menulists.
|
||||
if (mMenuParent && mMenuParent->IsMenu()) {
|
||||
nsMenuPopupFrame* popupFrame = static_cast<nsMenuPopupFrame*>(mMenuParent);
|
||||
nsIFrame* parentMenu = popupFrame->GetParent();
|
||||
if (parentMenu) {
|
||||
nsCOMPtr<nsIDOMXULMenuListElement> menulist = do_QueryInterface(parentMenu->GetContent());
|
||||
if (menulist) {
|
||||
PRBool isEditable = PR_FALSE;
|
||||
menulist->GetEditable(&isEditable);
|
||||
return !isEditable;
|
||||
}
|
||||
}
|
||||
}
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
nsMenuFrame::StartBlinking(nsGUIEvent *aEvent, PRBool aFlipChecked)
|
||||
{
|
||||
StopBlinking();
|
||||
CreateMenuCommandEvent(aEvent, aFlipChecked);
|
||||
|
||||
if (!ShouldBlink()) {
|
||||
PassMenuCommandEventToPopupManager();
|
||||
return;
|
||||
}
|
||||
|
||||
// Blink off.
|
||||
nsWeakFrame weakFrame(this);
|
||||
mContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::menuactive, PR_TRUE);
|
||||
if (!weakFrame.IsAlive())
|
||||
return;
|
||||
|
||||
if (mMenuParent) {
|
||||
// Make this menu ignore events from now on.
|
||||
mMenuParent->LockMenuUntilClosed(PR_TRUE);
|
||||
}
|
||||
|
||||
// Set up a timer to blink back on.
|
||||
mBlinkTimer = do_CreateInstance("@mozilla.org/timer;1");
|
||||
mBlinkTimer->InitWithCallback(mTimerMediator, kBlinkDelay, nsITimer::TYPE_ONE_SHOT);
|
||||
mBlinkState = 1;
|
||||
}
|
||||
|
||||
void
|
||||
nsMenuFrame::StopBlinking()
|
||||
{
|
||||
mBlinkState = 0;
|
||||
if (mBlinkTimer) {
|
||||
mBlinkTimer->Cancel();
|
||||
mBlinkTimer = nsnull;
|
||||
}
|
||||
mDelayedMenuCommandEvent = nsnull;
|
||||
}
|
||||
|
||||
void
|
||||
nsMenuFrame::CreateMenuCommandEvent(nsGUIEvent *aEvent, PRBool aFlipChecked)
|
||||
{
|
||||
// Create a trusted event if the triggering event was trusted, or if
|
||||
// we're called from chrome code (since at least one of our caller
|
||||
// passes in a null event).
|
||||
PRBool isTrusted = aEvent ? NS_IS_TRUSTED_EVENT(aEvent) :
|
||||
nsContentUtils::IsCallerChrome();
|
||||
|
||||
PRBool shift = PR_FALSE, control = PR_FALSE, alt = PR_FALSE, meta = PR_FALSE;
|
||||
if (aEvent && (aEvent->eventStructType == NS_MOUSE_EVENT ||
|
||||
aEvent->eventStructType == NS_KEY_EVENT ||
|
||||
aEvent->eventStructType == NS_ACCESSIBLE_EVENT)) {
|
||||
shift = static_cast<nsInputEvent *>(aEvent)->isShift;
|
||||
control = static_cast<nsInputEvent *>(aEvent)->isControl;
|
||||
alt = static_cast<nsInputEvent *>(aEvent)->isAlt;
|
||||
meta = static_cast<nsInputEvent *>(aEvent)->isMeta;
|
||||
}
|
||||
|
||||
// Because the command event is firing asynchronously, a flag is needed to
|
||||
// indicate whether user input is being handled. This ensures that a popup
|
||||
// window won't get blocked.
|
||||
PRBool userinput = nsEventStateManager::IsHandlingUserInput();
|
||||
|
||||
mDelayedMenuCommandEvent =
|
||||
new nsXULMenuCommandEvent(mContent, isTrusted, shift, control, alt, meta,
|
||||
userinput, aFlipChecked);
|
||||
}
|
||||
|
||||
void
|
||||
nsMenuFrame::PassMenuCommandEventToPopupManager()
|
||||
{
|
||||
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
|
||||
if (pm && mMenuParent)
|
||||
pm->ExecuteMenu(mContent, aEvent);
|
||||
if (pm && mMenuParent && mDelayedMenuCommandEvent) {
|
||||
pm->ExecuteMenu(mContent, mDelayedMenuCommandEvent);
|
||||
}
|
||||
mDelayedMenuCommandEvent = nsnull;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
|
@ -253,6 +253,12 @@ protected:
|
|||
|
||||
PRBool SizeToPopup(nsBoxLayoutState& aState, nsSize& aSize);
|
||||
|
||||
PRBool ShouldBlink();
|
||||
void StartBlinking(nsGUIEvent *aEvent, PRBool aFlipChecked);
|
||||
void StopBlinking();
|
||||
void CreateMenuCommandEvent(nsGUIEvent *aEvent, PRBool aFlipChecked);
|
||||
void PassMenuCommandEventToPopupManager();
|
||||
|
||||
protected:
|
||||
#ifdef DEBUG_LAYOUT
|
||||
nsresult SetDebug(nsBoxLayoutState& aState, nsIFrame* aList, PRBool aDebug);
|
||||
|
@ -272,6 +278,10 @@ protected:
|
|||
nsRefPtr<nsMenuTimerMediator> mTimerMediator;
|
||||
|
||||
nsCOMPtr<nsITimer> mOpenTimer;
|
||||
nsCOMPtr<nsITimer> mBlinkTimer;
|
||||
|
||||
PRUint8 mBlinkState; // 0: not blinking, 1: off, 2: on
|
||||
nsRefPtr<nsXULMenuCommandEvent> mDelayedMenuCommandEvent;
|
||||
|
||||
nsString mGroupName;
|
||||
|
||||
|
|
|
@ -85,6 +85,14 @@ public:
|
|||
// cleared. This should return true if the menu should be deselected
|
||||
// by the caller.
|
||||
virtual PRBool MenuClosed() = 0;
|
||||
|
||||
// Lock this menu and its parents until they're closed or unlocked.
|
||||
// A menu being "locked" means that all events inside it that would change the
|
||||
// selected menu item should be ignored.
|
||||
// This is used when closing the popup is delayed because of a blink or fade
|
||||
// animation.
|
||||
virtual void LockMenuUntilClosed(PRBool aLock) = 0;
|
||||
virtual PRBool IsMenuLocked() = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -121,6 +121,7 @@ nsMenuPopupFrame::nsMenuPopupFrame(nsIPresShell* aShell, nsStyleContext* aContex
|
|||
mShouldAutoPosition(PR_TRUE),
|
||||
mConsumeRollupEvent(nsIPopupBoxObject::ROLLUP_DEFAULT),
|
||||
mInContentShell(PR_TRUE),
|
||||
mIsMenuLocked(PR_FALSE),
|
||||
mHFlip(PR_FALSE),
|
||||
mVFlip(PR_FALSE)
|
||||
{
|
||||
|
@ -690,6 +691,8 @@ nsMenuPopupFrame::HidePopup(PRBool aDeselectMenu, nsPopupState aNewState)
|
|||
|
||||
mIncrementalString.Truncate();
|
||||
|
||||
LockMenuUntilClosed(PR_FALSE);
|
||||
|
||||
mIsOpenChanged = PR_FALSE;
|
||||
mCurrentMenu = nsnull; // make sure no current menu is set
|
||||
mHFlip = mVFlip = PR_FALSE;
|
||||
|
@ -1504,6 +1507,21 @@ nsMenuPopupFrame::FindMenuWithShortcut(nsIDOMKeyEvent* aKeyEvent, PRBool& doActi
|
|||
return nsnull;
|
||||
}
|
||||
|
||||
void
|
||||
nsMenuPopupFrame::LockMenuUntilClosed(PRBool aLock)
|
||||
{
|
||||
mIsMenuLocked = aLock;
|
||||
|
||||
// Lock / unlock the parent, too.
|
||||
nsIFrame* parent = GetParent();
|
||||
if (parent && parent->GetType() == nsGkAtoms::menuFrame) {
|
||||
nsMenuParent* parentParent = static_cast<nsMenuFrame*>(parent)->GetMenuParent();
|
||||
if (parentParent) {
|
||||
parentParent->LockMenuUntilClosed(aLock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMenuPopupFrame::GetWidget(nsIWidget **aWidget)
|
||||
{
|
||||
|
|
|
@ -158,6 +158,9 @@ public:
|
|||
|
||||
virtual PRBool MenuClosed() { return PR_TRUE; }
|
||||
|
||||
virtual void LockMenuUntilClosed(PRBool aLock);
|
||||
virtual PRBool IsMenuLocked() { return mIsMenuLocked; }
|
||||
|
||||
NS_IMETHOD GetWidget(nsIWidget **aWidget);
|
||||
|
||||
// The dismissal listener gets created and attached to the window.
|
||||
|
@ -395,6 +398,7 @@ protected:
|
|||
PRPackedBool mShouldAutoPosition; // Should SetPopupPosition be allowed to auto position popup?
|
||||
PRPackedBool mConsumeRollupEvent; // Should the rollup event be consumed?
|
||||
PRPackedBool mInContentShell; // True if the popup is in a content shell
|
||||
PRPackedBool mIsMenuLocked; // Should events inside this menu be ignored?
|
||||
|
||||
// the flip modes that were used when the popup was opened
|
||||
PRPackedBool mHFlip;
|
||||
|
|
|
@ -931,7 +931,7 @@ nsXULPopupManager::HidePopupsInDocShell(nsIDocShellTreeItem* aDocShellToHide)
|
|||
}
|
||||
|
||||
void
|
||||
nsXULPopupManager::ExecuteMenu(nsIContent* aMenu, nsEvent* aEvent)
|
||||
nsXULPopupManager::ExecuteMenu(nsIContent* aMenu, nsXULMenuCommandEvent* aEvent)
|
||||
{
|
||||
CloseMenuMode cmm = CloseMenuMode_Auto;
|
||||
|
||||
|
@ -974,30 +974,8 @@ nsXULPopupManager::ExecuteMenu(nsIContent* aMenu, nsEvent* aEvent)
|
|||
HidePopupsInList(popupsToHide, cmm == CloseMenuMode_Auto);
|
||||
}
|
||||
|
||||
// Create a trusted event if the triggering event was trusted, or if
|
||||
// we're called from chrome code (since at least one of our caller
|
||||
// passes in a null event).
|
||||
PRBool isTrusted = aEvent ? NS_IS_TRUSTED_EVENT(aEvent) :
|
||||
nsContentUtils::IsCallerChrome();
|
||||
|
||||
PRBool shift = PR_FALSE, control = PR_FALSE, alt = PR_FALSE, meta = PR_FALSE;
|
||||
if (aEvent && (aEvent->eventStructType == NS_MOUSE_EVENT ||
|
||||
aEvent->eventStructType == NS_KEY_EVENT ||
|
||||
aEvent->eventStructType == NS_ACCESSIBLE_EVENT)) {
|
||||
shift = static_cast<nsInputEvent *>(aEvent)->isShift;
|
||||
control = static_cast<nsInputEvent *>(aEvent)->isControl;
|
||||
alt = static_cast<nsInputEvent *>(aEvent)->isAlt;
|
||||
meta = static_cast<nsInputEvent *>(aEvent)->isMeta;
|
||||
}
|
||||
|
||||
// Because the command event is firing asynchronously, a flag is needed to
|
||||
// indicate whether user input is being handled. This ensures that a popup
|
||||
// window won't get blocked.
|
||||
PRBool userinput = nsEventStateManager::IsHandlingUserInput();
|
||||
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
new nsXULMenuCommandEvent(aMenu, isTrusted, shift, control,
|
||||
alt, meta, userinput, cmm);
|
||||
aEvent->SetCloseMenuMode(cmm);
|
||||
nsCOMPtr<nsIRunnable> event = aEvent;
|
||||
NS_DispatchToCurrentThread(event);
|
||||
}
|
||||
|
||||
|
@ -1898,12 +1876,13 @@ nsXULPopupManager::KeyUp(nsIDOMEvent* aKeyEvent)
|
|||
nsresult
|
||||
nsXULPopupManager::KeyDown(nsIDOMEvent* aKeyEvent)
|
||||
{
|
||||
nsMenuChainItem* item = GetTopVisibleMenu();
|
||||
if (item && item->Frame()->IsMenuLocked())
|
||||
return NS_OK;
|
||||
|
||||
// don't do anything if a menu isn't open or a menubar isn't active
|
||||
if (!mActiveMenuBar) {
|
||||
nsMenuChainItem* item = GetTopVisibleMenu();
|
||||
if (!item || item->PopupType() != ePopupTypeMenu)
|
||||
return NS_OK;
|
||||
}
|
||||
if (!mActiveMenuBar && (!item || item->PopupType() != ePopupTypeMenu))
|
||||
return NS_OK;
|
||||
|
||||
PRInt32 menuAccessKey = -1;
|
||||
|
||||
|
@ -1954,6 +1933,10 @@ nsXULPopupManager::KeyPress(nsIDOMEvent* aKeyEvent)
|
|||
// When a menu is open, the prevent default flag on a keypress is always set, so
|
||||
// that no one else uses the key event.
|
||||
|
||||
nsMenuChainItem* item = GetTopVisibleMenu();
|
||||
if (item && item->Frame()->IsMenuLocked())
|
||||
return NS_OK;
|
||||
|
||||
//handlers shouldn't be triggered by non-trusted events.
|
||||
nsCOMPtr<nsIDOMNSEvent> domNSEvent = do_QueryInterface(aKeyEvent);
|
||||
PRBool trustedEvent = PR_FALSE;
|
||||
|
@ -1970,7 +1953,6 @@ nsXULPopupManager::KeyPress(nsIDOMEvent* aKeyEvent)
|
|||
keyEvent->GetKeyCode(&theChar);
|
||||
|
||||
// Escape should close panels, but the other keys should have no effect.
|
||||
nsMenuChainItem* item = GetTopVisibleMenu();
|
||||
if (item && item->PopupType() != ePopupTypeMenu) {
|
||||
if (theChar == NS_VK_ESCAPE) {
|
||||
HidePopup(item->Content(), PR_FALSE, PR_FALSE, PR_FALSE);
|
||||
|
@ -2094,7 +2076,17 @@ nsXULMenuCommandEvent::Run()
|
|||
|
||||
nsCOMPtr<nsIContent> popup;
|
||||
nsMenuFrame* menuFrame = pm->GetMenuFrameForContent(mMenu);
|
||||
if (menuFrame) {
|
||||
nsWeakFrame weakFrame(menuFrame);
|
||||
if (menuFrame && mFlipChecked) {
|
||||
if (menuFrame->IsChecked()) {
|
||||
mMenu->UnsetAttr(kNameSpaceID_None, nsGkAtoms::checked, PR_TRUE);
|
||||
} else {
|
||||
mMenu->SetAttr(kNameSpaceID_None, nsGkAtoms::checked,
|
||||
NS_LITERAL_STRING("true"), PR_TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
if (menuFrame && weakFrame.IsAlive()) {
|
||||
// Find the popup that the menu is inside. Below, this popup will
|
||||
// need to be hidden.
|
||||
nsIFrame* popupFrame = menuFrame->GetParent();
|
||||
|
|
|
@ -56,6 +56,7 @@ _TEST_FILES = test_bug360220.xul \
|
|||
test_closemenu_attribute.xul \
|
||||
test_colorpicker_popup.xul \
|
||||
test_deck.xul \
|
||||
test_menuitem_blink.xul \
|
||||
test_menulist_keynav.xul \
|
||||
test_menulist_null_value.xul \
|
||||
test_popup_coords.xul \
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<?xml-stylesheet href="/tests/SimpleTest/test.css" type="text/css"?>
|
||||
|
||||
<window title="Blinking Context Menu Item Tests"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<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>
|
||||
|
||||
<menulist id="menulist">
|
||||
<menupopup id="menupopup">
|
||||
<menuitem label="Menu Item" id="menuitem"/>
|
||||
</menupopup>
|
||||
</menulist>
|
||||
<script class="testbody" type="application/javascript">
|
||||
<![CDATA[
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SimpleTest.waitForFocus(startTest);
|
||||
|
||||
function startTest() {
|
||||
if (!/Mac/.test(navigator.platform)) {
|
||||
ok(true, "Nothing to test on non-Mac.");
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
// Destroy frame while removing the _moz-menuactive attribute.
|
||||
test_crash("REMOVAL", test2);
|
||||
}
|
||||
|
||||
function test2() {
|
||||
// Destroy frame while adding the _moz-menuactive attribute.
|
||||
test_crash("ADDITION", test3);
|
||||
}
|
||||
|
||||
function test3() {
|
||||
// Don't mess with the frame, just test whether we've blinked.
|
||||
test_crash("", SimpleTest.finish);
|
||||
}
|
||||
|
||||
function test_crash(when, andThen) {
|
||||
var menupopup = document.getElementById("menupopup");
|
||||
var menuitem = document.getElementById("menuitem");
|
||||
var attrChanges = { "REMOVAL": 0, "ADDITION": 0 };
|
||||
var storedEvent = null;
|
||||
menupopup.addEventListener("popupshown", function () {
|
||||
menupopup.removeEventListener("popupshown", arguments.callee, false);
|
||||
menuitem.addEventListener("mouseup", function (e) {
|
||||
menuitem.removeEventListener("mouseup", arguments.callee, true);
|
||||
menuitem.addEventListener("DOMAttrModified", function (e) {
|
||||
if (e.attrName == "_moz-menuactive") {
|
||||
if (!attrChanges[e.attrChange])
|
||||
attrChanges[e.attrChange] = 1;
|
||||
else
|
||||
attrChanges[e.attrChange]++;
|
||||
storedEvent = e;
|
||||
if (e.attrChange == e[when]) {
|
||||
menuitem.hidden = true;
|
||||
menuitem.getBoundingClientRect();
|
||||
ok(true, "Didn't crash on _moz-menuactive " + when.toLowerCase() + " during blinking")
|
||||
menuitem.hidden = false;
|
||||
menuitem.removeEventListener("DOMAttrModified", arguments.callee, false);
|
||||
SimpleTest.executeSoon(function () {
|
||||
menupopup.hidePopup();
|
||||
});
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
}, true);
|
||||
menupopup.addEventListener("popuphidden", function() {
|
||||
menupopup.removeEventListener("popuphidden", arguments.callee, false);
|
||||
if (!when) {
|
||||
// Test whether we've blinked at all.
|
||||
var shouldBlink = navigator.platform.match(/Mac/);
|
||||
var expectedNumRemoval = shouldBlink ? 2 : 1;
|
||||
var expectedNumAddition = shouldBlink ? 1 : 0;
|
||||
ok(storedEvent, "got DOMAttrModified events after clicking menuitem")
|
||||
is(attrChanges[storedEvent.REMOVAL], expectedNumRemoval, "blinking unset attributes correctly");
|
||||
is(attrChanges[storedEvent.ADDITION], expectedNumAddition, "blinking set attributes correctly");
|
||||
}
|
||||
SimpleTest.executeSoon(andThen);
|
||||
}, false);
|
||||
synthesizeMouse(menuitem, 10, 5, { type : "mousemove" });
|
||||
synthesizeMouse(menuitem, 10, 5, { type : "mousemove" });
|
||||
synthesizeMouse(menuitem, 10, 5, { type : "mousedown" });
|
||||
SimpleTest.executeSoon(function () {
|
||||
synthesizeMouse(menuitem, 10, 5, { type : "mouseup" });
|
||||
});
|
||||
}, false);
|
||||
document.getElementById("menulist").open = true;
|
||||
}
|
||||
|
||||
]]>
|
||||
</script>
|
||||
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<p id="display">
|
||||
</p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
|
||||
</window>
|
|
@ -225,6 +225,7 @@ public:
|
|||
eMetric_TreeScrollDelay, // delay for scrolling the tree
|
||||
eMetric_TreeScrollLinesMax, // the maximum number of lines to be scrolled at ones
|
||||
eMetric_TabFocusModel, // What type of tab-order to use
|
||||
eMetric_ChosenMenuItemsShouldBlink, // Should menu items blink when they're chosen?
|
||||
|
||||
/*
|
||||
* A Boolean value to determine whether the Windows default theme is
|
||||
|
|
|
@ -467,6 +467,9 @@ NS_IMETHODIMP nsLookAndFeel::GetMetric(const nsMetricID aID, PRInt32 & aMetric)
|
|||
aMetric = [[NSUserDefaults standardUserDefaults] boolForKey:@"AppleScrollerPagingBehavior"];
|
||||
}
|
||||
break;
|
||||
case eMetric_ChosenMenuItemsShouldBlink:
|
||||
aMetric = 1;
|
||||
break;
|
||||
case eMetric_IMERawInputUnderlineStyle:
|
||||
case eMetric_IMEConvertedTextUnderlineStyle:
|
||||
case eMetric_IMESelectedRawTextUnderlineStyle:
|
||||
|
|
Загрузка…
Ссылка в новой задаче