Bug 558072. Allow getting of popup position and size from popupshowing event. r=roc,a=blocking

--HG--
extra : rebase_source : 0589fbc0410548f8f7ee748d59e70721c5fcc29b
This commit is contained in:
Neil Deakin 2010-09-06 15:30:17 +12:00
Родитель 65b02549df
Коммит 08f632c3d8
11 изменённых файлов: 170 добавлений и 285 удалений

Просмотреть файл

@ -452,6 +452,11 @@ function startTest() {
contextMenu = chromeWin.document.getElementById("contentAreaContextMenu");
ok(contextMenu, "Got context menu XUL");
if (chromeWin.document.getElementById("Browser:Stop").getAttribute("disabled") != "true") {
SimpleTest.executeSoon(startTest);
return;
}
text = subwindow.document.getElementById("test-text");
link = subwindow.document.getElementById("test-link");
mailto = subwindow.document.getElementById("test-mailto");

Просмотреть файл

@ -4212,17 +4212,6 @@ nsCSSFrameConstructor::FindXULDisplayData(const nsStyleDisplay* aDisplay,
sXULDisplayData, NS_ARRAY_LENGTH(sXULDisplayData));
}
nsresult
nsCSSFrameConstructor::AddLazyChildren(nsIContent* aContent,
nsLazyFrameConstructionCallback* aCallback,
void* aArg, PRBool aIsSynch)
{
nsCOMPtr<nsIRunnable> event =
new LazyGenerateChildrenEvent(aContent, mPresShell, aCallback, aArg);
return aIsSynch ? event->Run() :
NS_DispatchToCurrentThread(event);
}
already_AddRefed<nsStyleContext>
nsCSSFrameConstructor::BeginBuildingScrollFrame(nsFrameConstructorState& aState,
nsIContent* aContent,
@ -11654,62 +11643,34 @@ nsCSSFrameConstructor::PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint)
PostRestyleEventInternal(PR_FALSE);
}
NS_IMETHODIMP
nsCSSFrameConstructor::LazyGenerateChildrenEvent::Run()
nsresult
nsCSSFrameConstructor::GenerateChildFrames(nsIFrame* aFrame)
{
mPresShell->GetDocument()->FlushPendingNotifications(Flush_Layout);
{
nsAutoScriptBlocker scriptBlocker;
BeginUpdate();
// this is hard-coded to handle only menu popup frames
nsIFrame* frame = mPresShell->GetPresContext() ?
mPresShell->GetPresContext()->GetPrimaryFrameFor(mContent) : nsnull;
if (frame && frame->GetType() == nsGkAtoms::menuPopupFrame) {
nsWeakFrame weakFrame(frame);
#ifdef MOZ_XUL
// it is possible that the frame is different than the one that requested
// the lazy generation, but as long as it's a popup frame that hasn't
// generated its children yet, that's OK.
nsMenuPopupFrame* menuPopupFrame = static_cast<nsMenuPopupFrame *>(frame);
if (menuPopupFrame->HasGeneratedChildren()) {
if (mCallback)
mCallback(mContent, frame, mArg);
return NS_OK;
}
// indicate that the children have been generated
menuPopupFrame->SetGeneratedChildren();
#endif
{
nsAutoScriptBlocker scriptBlocker;
nsCSSFrameConstructor* fc = mPresShell->FrameConstructor();
fc->BeginUpdate();
nsFrameItems childItems;
nsFrameConstructorState state(mPresShell, nsnull, nsnull, nsnull);
// We don't have a parent frame with a pending binding constructor here,
// so no need to worry about ordering of the kids' constructors with it.
// Pass null for the PendingBinding.
nsresult rv = fc->ProcessChildren(state, mContent, frame->GetStyleContext(),
frame, PR_FALSE, childItems, PR_FALSE,
nsnull);
if (NS_FAILED(rv)) {
fc->EndUpdate();
return rv;
}
frame->SetInitialChildList(nsnull, childItems);
fc->EndUpdate();
nsFrameItems childItems;
nsFrameConstructorState state(mPresShell, nsnull, nsnull, nsnull);
// We don't have a parent frame with a pending binding constructor here,
// so no need to worry about ordering of the kids' constructors with it.
// Pass null for the PendingBinding.
nsresult rv = ProcessChildren(state, aFrame->GetContent(), aFrame->GetStyleContext(),
aFrame, PR_FALSE, childItems, PR_FALSE,
nsnull);
if (NS_FAILED(rv)) {
EndUpdate();
return rv;
}
if (mCallback && weakFrame.IsAlive())
mCallback(mContent, frame, mArg);
aFrame->SetInitialChildList(nsnull, childItems);
// call XBL constructors after the frames are created
mPresShell->GetDocument()->BindingManager()->ProcessAttachedQueue();
EndUpdate();
}
// call XBL constructors after the frames are created
mPresShell->GetDocument()->BindingManager()->ProcessAttachedQueue();
return NS_OK;
}

Просмотреть файл

@ -73,9 +73,6 @@ class nsPageContentFrame;
struct PendingBinding;
class nsRefreshDriver;
typedef void (nsLazyFrameConstructionCallback)
(nsIContent* aContent, nsIFrame* aFrame, void* aArg);
class nsFrameConstructorState;
class nsFrameConstructorSaveState;
@ -242,18 +239,8 @@ public:
nsIContent* aContent2,
PRInt32 aStateMask);
// Process the children of aContent and indicate that frames should be
// created for them. This is used for lazily built content such as that
// inside popups so that it is only created when the popup is opened.
// If aIsSynch is true, this method constructs the frames synchronously.
// aCallback will be called with three arguments, the first is the value
// of aContent, the second is aContent's primary frame, and the third is
// the value of aArg.
// aCallback will always be called even if the children of aContent had
// been generated earlier.
nsresult AddLazyChildren(nsIContent* aContent,
nsLazyFrameConstructionCallback* aCallback,
void* aArg, PRBool aIsSynch = PR_FALSE);
// generate the child frames and process bindings
nsresult GenerateChildFrames(nsIFrame* aFrame);
// Should be called when a frame is going to be destroyed and
// WillDestroyFrameTree hasn't been called yet.
@ -1809,27 +1796,6 @@ public:
private:
class LazyGenerateChildrenEvent;
friend class LazyGenerateChildrenEvent;
// See comments of nsCSSFrameConstructor::AddLazyChildren()
class LazyGenerateChildrenEvent : public nsRunnable {
public:
NS_DECL_NSIRUNNABLE
LazyGenerateChildrenEvent(nsIContent *aContent,
nsIPresShell *aPresShell,
nsLazyFrameConstructionCallback* aCallback,
void* aArg)
: mContent(aContent), mPresShell(aPresShell), mCallback(aCallback), mArg(aArg)
{}
private:
nsCOMPtr<nsIContent> mContent;
nsCOMPtr<nsIPresShell> mPresShell;
nsLazyFrameConstructionCallback* mCallback;
void* mArg;
};
nsIDocument* mDocument; // Weak ref
nsIPresShell* mPresShell; // Weak ref

Просмотреть файл

@ -51,6 +51,7 @@
#include "nsCOMPtr.h"
#include "nsTArray.h"
#include "nsITimer.h"
#include "nsIReflowCallback.h"
#include "nsThreadUtils.h"
#include "nsStyleConsts.h"
@ -208,26 +209,19 @@ class nsXULPopupShowingEvent : public nsRunnable
{
public:
nsXULPopupShowingEvent(nsIContent *aPopup,
nsIContent *aMenu,
nsPopupType aPopupType,
PRBool aIsContextMenu,
PRBool aSelectFirstItem)
: mPopup(aPopup),
mMenu(aMenu),
mPopupType(aPopupType),
mIsContextMenu(aIsContextMenu),
mSelectFirstItem(aSelectFirstItem)
{
NS_ASSERTION(aPopup, "null popup supplied to nsXULPopupShowingEvent constructor");
NS_ASSERTION(aMenu, "null menu supplied to nsXULPopupShowingEvent constructor");
}
NS_IMETHOD Run();
private:
nsCOMPtr<nsIContent> mPopup;
nsCOMPtr<nsIContent> mMenu;
nsPopupType mPopupType;
PRBool mIsContextMenu;
PRBool mSelectFirstItem;
};
@ -659,22 +653,13 @@ protected:
PRBool aDeselectMenu);
/**
* Fire a popupshowing event on the popup aPopup and then open the popup.
* Fire a popupshowing event on the popup and then open the popup.
*
* The caller must keep a strong reference to aPopup.
*
* aPopup - the popup node to open
* aMenu - should be set to the parent menu if this is a popup associated
* with a menu. Otherwise, should be null.
* aPresContext - the prescontext
* aPopupType - the popup frame's PopupType
* aPopup - the popup to open
* aIsContextMenu - true for context menus
* aSelectFirstItem - true to select the first item in the menu
*/
void FirePopupShowingEvent(nsIContent* aPopup,
nsIContent* aMenu,
nsPresContext* aPresContext,
nsPopupType aPopupType,
PRBool aIsContextMenu,
PRBool aSelectFirstItem);

Просмотреть файл

@ -185,14 +185,11 @@ public:
// nsMenuFrame methods
nsresult DestroyPopupFrames(nsPresContext* aPresContext);
virtual PRBool IsOnMenuBar() { return mMenuParent && mMenuParent->IsMenuBar(); }
virtual PRBool IsOnActiveMenuBar() { return IsOnMenuBar() && mMenuParent->IsActive(); }
virtual PRBool IsOpen();
virtual PRBool IsMenu();
PRBool IsDisabled();
PRBool IsGenerated();
void ToggleMenuState();
// indiciate that the menu's popup has just been opened, so that the menu

Просмотреть файл

@ -40,7 +40,6 @@
*
* ***** END LICENSE BLOCK ***** */
#include "nsMenuPopupFrame.h"
#include "nsGkAtoms.h"
#include "nsIContent.h"
@ -406,11 +405,27 @@ nsMenuPopupFrame::IsLeaf() const
void
nsMenuPopupFrame::LayoutPopup(nsBoxLayoutState& aState, nsIFrame* aParentMenu, PRBool aSizedToPopup)
{
// if the popup is not open, only do layout if the menu is sized to the popup
PRBool isOpen = IsOpen();
if (!mGeneratedChildren || (!isOpen && !aSizedToPopup))
if (!mGeneratedChildren)
return;
PRBool shouldPosition = PR_TRUE;
PRBool isOpen = IsOpen();
if (!isOpen) {
// if the popup is not open, only do layout while showing or if the menu
// is sized to the popup
shouldPosition = (mPopupState == ePopupShowing);
if (!shouldPosition && !aSizedToPopup)
return;
}
// if the popup has just been opened, make sure the scrolled window is at 0,0
if (mIsOpenChanged) {
nsIScrollableFrame *scrollframe = do_QueryFrame(GetChildBox());
if (scrollframe) {
scrollframe->ScrollTo(nsPoint(0,0), nsIScrollableFrame::INSTANT);
}
}
// get the preferred, minimum and maximum size. If the menu is sized to the
// popup, then the popup's width is the menu's width.
nsSize prefSize = GetPrefSize(aState);
@ -429,7 +444,7 @@ nsMenuPopupFrame::LayoutPopup(nsBoxLayoutState& aState, nsIFrame* aParentMenu, P
mPrefSize = prefSize;
}
if (isOpen) {
if (shouldPosition) {
SetPopupPosition(aParentMenu, PR_FALSE);
}
@ -452,50 +467,36 @@ nsMenuPopupFrame::LayoutPopup(nsBoxLayoutState& aState, nsIFrame* aParentMenu, P
}
}
if (isOpen) {
AdjustView();
}
}
void
nsMenuPopupFrame::AdjustView()
{
// if the popup has just opened, make sure the scrolled window is at 0,0
if (mIsOpenChanged) {
nsIBox* child = GetChildBox();
nsIScrollableFrame *scrollframe = do_QueryFrame(child);
if (scrollframe)
scrollframe->ScrollTo(nsPoint(0,0), nsIScrollableFrame::INSTANT);
}
nsIView* view = GetView();
nsIViewManager* viewManager = view->GetViewManager();
nsRect rect = GetRect();
rect.x = rect.y = 0;
// Increase the popup's view size to account for any titlebar or borders.
// XXXndeakin this should really be accounted for earlier in
// SetPopupPosition so that this extra size is accounted for when flipping
// or resizing the popup due to it being too large, but that can be a
// followup bug.
nsPresContext* pc = PresContext();
if (mPopupType == ePopupTypePanel && view) {
nsIWidget* widget = view->GetWidget();
if (widget) {
nsIntSize popupSize = nsIntSize(pc->AppUnitsToDevPixels(rect.width),
pc->AppUnitsToDevPixels(rect.height));
popupSize = widget->ClientToWindowSize(popupSize);
rect.width = pc->DevPixelsToAppUnits(popupSize.width);
rect.height = pc->DevPixelsToAppUnits(popupSize.height);
if (isOpen) {
nsIView* view = GetView();
nsIViewManager* viewManager = view->GetViewManager();
nsRect rect = GetRect();
rect.x = rect.y = 0;
// Increase the popup's view size to account for any titlebar or borders.
// XXXndeakin this should really be accounted for earlier in
// SetPopupPosition so that this extra size is accounted for when flipping
// or resizing the popup due to it being too large, but that can be a
// followup bug.
if (mPopupType == ePopupTypePanel && view) {
nsIWidget* widget = view->GetWidget();
if (widget) {
nsIntSize popupSize = nsIntSize(pc->AppUnitsToDevPixels(rect.width),
pc->AppUnitsToDevPixels(rect.height));
popupSize = widget->ClientToWindowSize(popupSize);
rect.width = pc->DevPixelsToAppUnits(popupSize.width);
rect.height = pc->DevPixelsToAppUnits(popupSize.height);
}
}
viewManager->ResizeView(view, rect);
viewManager->SetViewVisibility(view, nsViewVisibility_kShow);
mPopupState = ePopupOpenAndVisible;
nsContainerFrame::SyncFrameViewProperties(pc, this, nsnull, view, 0);
}
viewManager->ResizeView(view, rect);
viewManager->SetViewVisibility(view, nsViewVisibility_kShow);
mPopupState = ePopupOpenAndVisible;
nsContainerFrame::SyncFrameViewProperties(pc, this, nsnull, view, 0);
// fire popupshown event when the state has changed
// finally, if the popup just opened, send a popupshown event
if (mIsOpenChanged) {
mIsOpenChanged = PR_FALSE;
nsCOMPtr<nsIRunnable> event = new nsXULPopupShownEvent(GetContent(), pc);
@ -693,44 +694,10 @@ nsMenuPopupFrame::InitializePopupWithAnchorAlign(nsIContent* aAnchorContent,
}
void
LazyGeneratePopupDone(nsIContent* aPopup, nsIFrame* aFrame, void* aArg)
{
// be safe and check the frame type
if (aFrame->GetType() == nsGkAtoms::menuPopupFrame) {
nsWeakFrame weakFrame(aFrame);
nsMenuPopupFrame* popupFrame = static_cast<nsMenuPopupFrame*>(aFrame);
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
if (pm && popupFrame->IsMenu()) {
nsCOMPtr<nsIContent> popup = aPopup;
pm->UpdateMenuItems(popup);
if (!weakFrame.IsAlive())
return;
PRBool selectFirstItem = (PRBool)NS_PTR_TO_INT32(aArg);
if (selectFirstItem) {
nsMenuFrame* next = pm->GetNextMenuItem(popupFrame, nsnull, PR_TRUE);
popupFrame->SetCurrentMenuItem(next);
}
}
if (weakFrame.IsAlive()) {
popupFrame->PresContext()->PresShell()->
FrameNeedsReflow(popupFrame, nsIPresShell::eTreeChange,
NS_FRAME_HAS_DIRTY_CHILDREN);
}
}
}
PRBool
nsMenuPopupFrame::ShowPopup(PRBool aIsContextMenu, PRBool aSelectFirstItem)
{
mIsContextMenu = aIsContextMenu;
PRBool hasChildren = PR_FALSE;
if (mPopupState == ePopupShowing) {
mPopupState = ePopupOpen;
mIsOpenChanged = PR_TRUE;
@ -740,21 +707,14 @@ nsMenuPopupFrame::ShowPopup(PRBool aIsContextMenu, PRBool aSelectFirstItem)
nsWeakFrame weakFrame(this);
menuFrame->PopupOpened();
if (!weakFrame.IsAlive())
return PR_FALSE;
return;
}
// the frames for the child menus have not been created yet, so tell the
// frame constructor to build them
if (mFrames.IsEmpty() && !mGeneratedChildren) {
PresContext()->PresShell()->FrameConstructor()->
AddLazyChildren(mContent, LazyGeneratePopupDone, NS_INT32_TO_PTR(aSelectFirstItem));
}
else {
hasChildren = PR_TRUE;
PresContext()->PresShell()->
FrameNeedsReflow(this, nsIPresShell::eTreeChange,
NS_FRAME_HAS_DIRTY_CHILDREN);
}
// do we need an actual reflow here?
// is SetPopupPosition all that is needed?
PresContext()->PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange,
NS_FRAME_HAS_DIRTY_CHILDREN);
if (mPopupType == ePopupTypeMenu) {
nsCOMPtr<nsISound> sound(do_CreateInstance("@mozilla.org/sound;1"));
if (sound)
@ -763,7 +723,6 @@ nsMenuPopupFrame::ShowPopup(PRBool aIsContextMenu, PRBool aSelectFirstItem)
}
mShouldAutoPosition = PR_TRUE;
return hasChildren;
}
void
@ -1713,10 +1672,12 @@ nsMenuPopupFrame::AttributeChanged(PRInt32 aNameSpaceID,
if (aAttribute == nsGkAtoms::menugenerated &&
mFrames.IsEmpty() && !mGeneratedChildren) {
EnsureWidget();
PresContext()->PresShell()->FrameConstructor()->
AddLazyChildren(mContent, LazyGeneratePopupDone, nsnull, PR_TRUE);
// indicate that the children have been generated and then generate them
mGeneratedChildren = PR_TRUE;
PresContext()->PresShell()->FrameConstructor()->GenerateChildFrames(this);
}
return rv;
}

Просмотреть файл

@ -65,9 +65,8 @@ class nsIWidget;
// ePopupShowing - during the period when the popupshowing event fires
// ePopupOpen - between the popupshowing event and being visible. Creation
// of the child frames, layout and reflow occurs in this state.
// ePopupOpenAndVisible - layout is done and AdjustView is called to make
// the popup's widget visible. The popup is now
// visible and the popupshown event fires.
// ePopupOpenAndVisible - layout is done and the popup's view and widget are
// made visible. The popupshown event fires.
// When closing a popup:
// ePopupHidden - during the period when the popuphiding event fires and
// the popup is removed.
@ -203,9 +202,6 @@ public:
// layout, position and display the popup as needed
void LayoutPopup(nsBoxLayoutState& aState, nsIFrame* aParentMenu, PRBool aSizedToPopup);
// AdjustView is called by LayoutPopup to position and show the popup's view.
void AdjustView();
nsIView* GetRootViewForPopup(nsIFrame* aStartFrame);
// set the position of the popup either relative to the anchor aAnchorFrame
@ -269,7 +265,7 @@ public:
PRInt32 aXPos, PRInt32 aYPos);
// indicate that the popup should be opened
PRBool ShowPopup(PRBool aIsContextMenu, PRBool aSelectFirstItem);
void ShowPopup(PRBool aIsContextMenu, PRBool aSelectFirstItem);
// indicate that the popup should be hidden. The new state should either be
// ePopupClosed or ePopupInvisible.
void HidePopup(PRBool aDeselectMenu, nsPopupState aNewState);

Просмотреть файл

@ -364,7 +364,7 @@ nsXULPopupManager::GetFrameOfTypeForContent(nsIContent* aContent,
if (document) {
nsCOMPtr<nsIPresShell> presShell = document->GetShell();
if (presShell)
presShell->FlushPendingNotifications(Flush_Frames);
presShell->FlushPendingNotifications(Flush_Layout);
}
}
@ -540,21 +540,19 @@ nsXULPopupManager::ShowMenu(nsIContent *aMenu,
else
position.AssignLiteral("end_before");
// there is no trigger event for menus
InitTriggerEvent(nsnull, nsnull, nsnull);
popupFrame->InitializePopup(aMenu, nsnull, position, 0, 0, PR_TRUE);
if (aAsynchronous) {
// there is no trigger event for menus
InitTriggerEvent(nsnull, nsnull, nsnull);
nsCOMPtr<nsIRunnable> event =
new nsXULPopupShowingEvent(popupFrame->GetContent(), aMenu, popupFrame->PopupType(),
new nsXULPopupShowingEvent(popupFrame->GetContent(),
parentIsContextMenu, aSelectFirstItem);
NS_DispatchToCurrentThread(event);
}
else {
nsCOMPtr<nsIContent> popupContent = popupFrame->GetContent();
FirePopupShowingEvent(popupContent, aMenu,
popupFrame->PresContext(), popupFrame->PopupType(),
parentIsContextMenu, aSelectFirstItem);
FirePopupShowingEvent(popupContent, parentIsContextMenu, aSelectFirstItem);
}
}
@ -578,8 +576,7 @@ nsXULPopupManager::ShowPopup(nsIContent* aPopup,
popupFrame->InitializePopup(aAnchorContent, triggerContent, aPosition,
aXPos, aYPos, aAttributesOverride);
FirePopupShowingEvent(aPopup, nsnull, popupFrame->PresContext(),
popupFrame->PopupType(), aIsContextMenu, aSelectFirstItem);
FirePopupShowingEvent(aPopup, aIsContextMenu, aSelectFirstItem);
}
void
@ -596,9 +593,7 @@ nsXULPopupManager::ShowPopupAtScreen(nsIContent* aPopup,
InitTriggerEvent(aTriggerEvent, aPopup, getter_AddRefs(triggerContent));
popupFrame->InitializePopupAtScreen(triggerContent, aXPos, aYPos, aIsContextMenu);
FirePopupShowingEvent(aPopup, nsnull, popupFrame->PresContext(),
popupFrame->PopupType(), aIsContextMenu, PR_FALSE);
FirePopupShowingEvent(aPopup, aIsContextMenu, PR_FALSE);
}
void
@ -617,9 +612,7 @@ nsXULPopupManager::ShowPopupWithAnchorAlign(nsIContent* aPopup,
popupFrame->InitializePopupWithAnchorAlign(aAnchorContent, aAnchor,
aAlign, aXPos, aYPos);
FirePopupShowingEvent(aPopup, nsnull, popupFrame->PresContext(),
popupFrame->PopupType(), aIsContextMenu, PR_FALSE);
FirePopupShowingEvent(aPopup, aIsContextMenu, PR_FALSE);
}
static void
@ -686,7 +679,7 @@ nsXULPopupManager::ShowPopupCallback(nsIContent* aPopup,
// use a weak frame as the popup will set an open attribute if it is a menu
nsWeakFrame weakFrame(aPopupFrame);
PRBool hasChildren = aPopupFrame->ShowPopup(aIsContextMenu, aSelectFirstItem);
aPopupFrame->ShowPopup(aIsContextMenu, aSelectFirstItem);
ENSURE_TRUE(weakFrame.IsAlive());
// popups normally hide when an outside click occurs. Panels may use
@ -706,16 +699,14 @@ nsXULPopupManager::ShowPopupCallback(nsIContent* aPopup,
SetCaptureState(oldmenu);
}
if (hasChildren) {
if (aSelectFirstItem) {
nsMenuFrame* next = GetNextMenuItem(aPopupFrame, nsnull, PR_TRUE);
aPopupFrame->SetCurrentMenuItem(next);
}
if (ismenu)
UpdateMenuItems(aPopup);
if (aSelectFirstItem) {
nsMenuFrame* next = GetNextMenuItem(aPopupFrame, nsnull, PR_TRUE);
aPopupFrame->SetCurrentMenuItem(next);
}
if (ismenu)
UpdateMenuItems(aPopup);
// Caret visibility may have been affected, ensure that
// the caret isn't now drawn when it shouldn't be.
CheckCaretDrawingState();
@ -1081,13 +1072,33 @@ nsXULPopupManager::ExecuteMenu(nsIContent* aMenu, nsXULMenuCommandEvent* aEvent)
void
nsXULPopupManager::FirePopupShowingEvent(nsIContent* aPopup,
nsIContent* aMenu,
nsPresContext* aPresContext,
nsPopupType aPopupType,
PRBool aIsContextMenu,
PRBool aSelectFirstItem)
{
nsCOMPtr<nsIPresShell> presShell = aPresContext->PresShell();
nsCOMPtr<nsIContent> popup = aPopup; // keep a strong reference to the popup
nsIFrame* frame = aPopup->GetPrimaryFrame();
if (!frame || frame->GetType() != nsGkAtoms::menuPopupFrame)
return;
nsMenuPopupFrame* popupFrame = static_cast<nsMenuPopupFrame *>(frame);
nsPresContext *presContext = popupFrame->PresContext();
nsCOMPtr<nsIPresShell> presShell = presContext->PresShell();
nsPopupType popupType = popupFrame->PopupType();
// generate the child frames if they have not already been generated
if (!popupFrame->HasGeneratedChildren()) {
popupFrame->SetGeneratedChildren();
presShell->FrameConstructor()->GenerateChildFrames(popupFrame);
}
// get the frame again
frame = aPopup->GetPrimaryFrame();
if (!frame)
return;
presShell->FrameNeedsReflow(frame, nsIPresShell::eTreeChange,
NS_FRAME_HAS_DIRTY_CHILDREN);
// cache the popup so that document.popupNode can retrieve the trigger node
// during the popupshowing event. It will be cleared below after the event
@ -1109,7 +1120,7 @@ nsXULPopupManager::FirePopupShowingEvent(nsIContent* aPopup,
}
event.refPoint = mCachedMousePoint;
nsEventDispatcher::Dispatch(aPopup, aPresContext, &event, nsnull, &status);
nsEventDispatcher::Dispatch(popup, presContext, &event, nsnull, &status);
mCachedMousePoint = nsIntPoint(0, 0);
mOpeningPopup = nsnull;
@ -1117,12 +1128,12 @@ nsXULPopupManager::FirePopupShowingEvent(nsIContent* aPopup,
// This is done after the popupshowing event in case that event is cancelled.
// Using noautofocus="true" will disable this behaviour, which is needed for
// the autocomplete widget as it manages focus itself.
if (aPopupType == ePopupTypePanel &&
!aPopup->AttrValueIs(kNameSpaceID_None, nsGkAtoms::noautofocus,
if (popupType == ePopupTypePanel &&
!popup->AttrValueIs(kNameSpaceID_None, nsGkAtoms::noautofocus,
nsGkAtoms::_true, eCaseMatters)) {
nsIFocusManager* fm = nsFocusManager::GetFocusManager();
if (fm) {
nsIDocument* doc = aPopup->GetCurrentDoc();
nsIDocument* doc = popup->GetCurrentDoc();
// Only remove the focus if the currently focused item is ouside the
// popup. It isn't a big deal if the current focus is in a child popup
@ -1133,27 +1144,18 @@ nsXULPopupManager::FirePopupShowingEvent(nsIContent* aPopup,
fm->GetFocusedElement(getter_AddRefs(currentFocusElement));
nsCOMPtr<nsIContent> currentFocus = do_QueryInterface(currentFocusElement);
if (doc && currentFocus &&
!nsContentUtils::ContentIsCrossDocDescendantOf(currentFocus, aPopup)) {
!nsContentUtils::ContentIsCrossDocDescendantOf(currentFocus, popup)) {
fm->ClearFocus(doc->GetWindow());
}
}
}
// it is common to append content to the menu during the popupshowing event.
// Flush the notifications so that the frames are up to date before showing
// the popup, otherwise the new frames will reflow after the popup appears,
// causing the popup to flicker. Frame code always calls this asynchronously,
// so this should be safe.
nsIDocument *document = aPopup->GetCurrentDoc();
if (document)
document->FlushPendingNotifications(Flush_Layout);
// clear these as they are no longer valid
mRangeParent = nsnull;
mRangeOffset = 0;
// get the frame again in case it went away
nsIFrame* frame = aPopup->GetPrimaryFrame();
frame = aPopup->GetPrimaryFrame();
if (frame && frame->GetType() == nsGkAtoms::menuPopupFrame) {
nsMenuPopupFrame* popupFrame = static_cast<nsMenuPopupFrame *>(frame);
@ -2160,27 +2162,12 @@ nsXULPopupManager::KeyPress(nsIDOMEvent* aKeyEvent)
return NS_OK; // I am consuming event
}
static nsPresContext*
GetPresContextFor(nsIContent* aContent)
{
nsIDocument *document = aContent->GetCurrentDoc();
if (document) {
nsIPresShell* presShell = document->GetShell();
if (presShell)
return presShell->GetPresContext();
}
return nsnull;
}
NS_IMETHODIMP
nsXULPopupShowingEvent::Run()
{
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
nsPresContext* context = GetPresContextFor(mPopup);
if (pm && context) {
pm->FirePopupShowingEvent(mPopup, mMenu, context, mPopupType,
mIsContextMenu, mSelectFirstItem);
if (pm) {
pm->FirePopupShowingEvent(mPopup, mIsContextMenu, mSelectFirstItem);
}
return NS_OK;
@ -2190,10 +2177,17 @@ NS_IMETHODIMP
nsXULPopupHidingEvent::Run()
{
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
nsPresContext* context = GetPresContextFor(mPopup);
if (pm && context) {
pm->FirePopupHidingEvent(mPopup, mNextPopup, mLastPopup,
context, mPopupType, mDeselectMenu);
nsIDocument *document = mPopup->GetCurrentDoc();
if (pm && document) {
nsIPresShell* presShell = document->GetShell();
if (presShell) {
nsPresContext* context = presShell->GetPresContext();
if (context) {
pm->FirePopupHidingEvent(mPopup, mNextPopup, mLastPopup,
context, mPopupType, mDeselectMenu);
}
}
}
return NS_OK;

Просмотреть файл

@ -73,6 +73,12 @@ function popupShowingEventOccurred(event)
// should be one higher than its index within its parent.
is(event.rangeParent, trigger.parentNode, testname + "rangeParent");
is(event.rangeOffset, Array.indexOf(trigger.parentNode.childNodes, trigger) + 1, testname + "rangeOffset");
var popuprect = event.target.getBoundingClientRect();
is(Math.round(popuprect.left), Math.round(rect.left + 4), "popup left");
is(Math.round(popuprect.top), Math.round(rect.top + 5), "popup top");
ok(popuprect.width > 0, "popup width");
ok(popuprect.height > 0, "popup height");
}
</script>

Просмотреть файл

@ -36,6 +36,7 @@ function runTests()
setScale($("frame").contentWindow, 2);
var anchor = $("frame").contentDocument.getElementById("two");
anchor.getBoundingClientRect(); // flush to update display after scale change
$("popup").openPopup(anchor, "after_start");
}

Просмотреть файл

@ -10,7 +10,13 @@
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<menupopup id="popup" hidden="true" onpopupshown="ok(true, 'popupshown'); this.hidePopup()"
onpopuphidden="runTests($('popupinbutton'));">
onpopuphidden="$('popup-hideonshow').openPopup(null, 'after_start')">
<menuitem id="i1" label="One"/>
<menuitem id="i2" label="Two"/>
</menupopup>
<menupopup id="popup-hideonshow" onpopupshowing="hidePopupWhileShowing(this)"
onpopupshown="ok(false, 'popupshown when hidden')">
<menuitem id="i1" label="One"/>
<menuitem id="i2" label="Two"/>
</menupopup>
@ -34,6 +40,13 @@ function runTests(popup)
popup.openPopup(null, "after_start");
}
function hidePopupWhileShowing(popup)
{
popup.hidden = true;
is(popup.state, 'closed', 'popupshowing hidden');
SimpleTest.executeSoon(function () runTests($('popupinbutton')));
}
function checkEndTest(event)
{
var button = $("button");