Skip to content

Commit

Permalink
Bug 1130400. Part 2. Use a reflow callback to set the position of xul…
Browse files Browse the repository at this point in the history
… popups. r=enndeakin

During reflow our frame, or any ancestors, may not yet be placed at their final position. So calculating the screen position of our popup during our reflow could produce incorrect results.

Instead post a reflow callback to do it after reflow is finished.
  • Loading branch information
rmottola committed Jun 22, 2019
1 parent 2de244c commit aa21170
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 1 deletion.
30 changes: 30 additions & 0 deletions layout/xul/nsMenuPopupFrame.cpp
Expand Up @@ -458,8 +458,11 @@ nsMenuPopupFrame::LayoutPopup(nsBoxLayoutState& aState, nsIFrame* aParentMenu,
mPrefSize = prefSize;
}

bool needCallback = false;

if (shouldPosition) {
SetPopupPosition(aAnchor, false, aSizedToPopup);
needCallback = true;
}

nsRect bounds(GetRect());
Expand All @@ -477,6 +480,7 @@ nsMenuPopupFrame::LayoutPopup(nsBoxLayoutState& aState, nsIFrame* aParentMenu,
mPrefSize = newsize;
if (isOpen) {
SetPopupPosition(aAnchor, false, aSizedToPopup);
needCallback = true;
}
}
}
Expand Down Expand Up @@ -528,6 +532,27 @@ nsMenuPopupFrame::LayoutPopup(nsBoxLayoutState& aState, nsIFrame* aParentMenu,
nsCOMPtr<nsIRunnable> event = new nsXULPopupShownEvent(GetContent(), pc);
NS_DispatchToCurrentThread(event);
}

if (needCallback && !mReflowCallbackData.mPosted) {
pc->PresShell()->PostReflowCallback(this);
mReflowCallbackData.MarkPosted(aAnchor, aSizedToPopup);
}
}

bool
nsMenuPopupFrame::ReflowFinished()
{
SetPopupPosition(mReflowCallbackData.mAnchor, false, mReflowCallbackData.mSizedToPopup);

mReflowCallbackData.Clear();

return false;
}

void
nsMenuPopupFrame::ReflowCallbackCanceled()
{
mReflowCallbackData.Clear();
}

nsIContent*
Expand Down Expand Up @@ -1943,6 +1968,11 @@ nsMenuPopupFrame::MoveToAttributePosition()
void
nsMenuPopupFrame::DestroyFrom(nsIFrame* aDestructRoot)
{
if (mReflowCallbackData.mPosted) {
PresContext()->PresShell()->CancelReflowCallback(this);
mReflowCallbackData.Clear();
}

nsMenuFrame* menu = do_QueryFrame(GetParent());
if (menu) {
// clear the open attribute on the parent menu
Expand Down
29 changes: 28 additions & 1 deletion layout/xul/nsMenuPopupFrame.h
Expand Up @@ -152,7 +152,8 @@ class nsXULPopupShownEvent : public nsRunnable, public nsIDOMEventListener
nsRefPtr<nsPresContext> mPresContext;
};

class nsMenuPopupFrame final : public nsBoxFrame, public nsMenuParent
class nsMenuPopupFrame final : public nsBoxFrame, public nsMenuParent,
public nsIReflowCallback
{
public:
NS_DECL_QUERYFRAME_TARGET(nsMenuPopupFrame)
Expand Down Expand Up @@ -399,6 +400,10 @@ class nsMenuPopupFrame final : public nsBoxFrame, public nsMenuParent
return false;
}

// nsIReflowCallback
virtual bool ReflowFinished() MOZ_OVERRIDE;
virtual void ReflowCallbackCanceled() MOZ_OVERRIDE;

protected:

// returns the popup's level.
Expand Down Expand Up @@ -526,6 +531,28 @@ class nsMenuPopupFrame final : public nsBoxFrame, public nsMenuParent
uint8_t mConsumeRollupEvent;
FlipType mFlip; // Whether to flip

struct ReflowCallbackData {
ReflowCallbackData() :
mPosted(false),
mAnchor(nullptr),
mSizedToPopup(false)
{}
void MarkPosted(nsIFrame* aAnchor, bool aSizedToPopup) {
mPosted = true;
mAnchor = aAnchor;
mSizedToPopup = aSizedToPopup;
}
void Clear() {
mPosted = false;
mAnchor = nullptr;
mSizedToPopup = false;
}
bool mPosted;
nsIFrame* mAnchor;
bool mSizedToPopup;
};
ReflowCallbackData mReflowCallbackData;

bool mIsOpenChanged; // true if the open state changed since the last layout
bool mIsContextMenu; // true for context menus
// true if we need to offset the popup to ensure it's not under the mouse
Expand Down

0 comments on commit aa21170

Please sign in to comment.