зеркало из https://github.com/mozilla/pjs.git
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:
Родитель
2090c7128c
Коммит
be5a15e76d
|
@ -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");
|
||||
|
|
Загрузка…
Ссылка в новой задаче