зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1206133 - Add popuppositioning state and popuppositioned event to improve arrow panel position handling. r=enndeakin
MozReview-Commit-ID: Dh1npORCQ6J --HG-- extra : rebase_source : 5df6076561a746791c44d249afa31009d0e1b30a
This commit is contained in:
Родитель
51b70b73b8
Коммит
ad4b55af1f
|
@ -260,12 +260,26 @@ var StarUI = {
|
|||
parent.setAttribute("open", "true");
|
||||
}
|
||||
}
|
||||
this.panel.openPopup(aAnchorElement, aPosition);
|
||||
let panel = this.panel;
|
||||
let target = panel;
|
||||
if (target.parentNode) {
|
||||
// By targeting the panel's parent and using a capturing listener, we
|
||||
// can have our listener called before others waiting for the panel to
|
||||
// be shown (which probably expect the panel to be fully initialized)
|
||||
target = target.parentNode;
|
||||
}
|
||||
target.addEventListener("popupshown", function shownListener(event) {
|
||||
if (event.target == panel) {
|
||||
target.removeEventListener("popupshown", shownListener, true);
|
||||
|
||||
gEditItemOverlay.initPanel({ node: aNode
|
||||
, hiddenRows: ["description", "location",
|
||||
"loadInSidebar", "keyword"]
|
||||
, focusedElement: "preferred" });
|
||||
, focusedElement: "preferred"});
|
||||
}
|
||||
}, true);
|
||||
|
||||
this.panel.openPopup(aAnchorElement, aPosition);
|
||||
}),
|
||||
|
||||
panelShown:
|
||||
|
|
|
@ -177,6 +177,18 @@ var gEditItemOverlay = {
|
|||
initPanel(aInfo) {
|
||||
if (typeof(aInfo) != "object" || aInfo === null)
|
||||
throw new Error("aInfo must be an object.");
|
||||
if ("node" in aInfo) {
|
||||
try {
|
||||
aInfo.node.type;
|
||||
} catch (e) {
|
||||
// If the lazy loader for |type| generates an exception, it means that
|
||||
// this bookmark could not be loaded. This sometimes happens when tests
|
||||
// create a bookmark by clicking the bookmark star, then try to cleanup
|
||||
// before the bookmark panel has finished opening. Either way, if we
|
||||
// cannot retrieve the bookmark information, we cannot open the panel.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// For sanity ensure that the implementer has uninited the panel before
|
||||
// trying to init it again, or we could end up leaking due to observers.
|
||||
|
|
|
@ -884,6 +884,7 @@ GK_ATOM(onpointerlockchange, "onpointerlockchange")
|
|||
GK_ATOM(onpointerlockerror, "onpointerlockerror")
|
||||
GK_ATOM(onpopuphidden, "onpopuphidden")
|
||||
GK_ATOM(onpopuphiding, "onpopuphiding")
|
||||
GK_ATOM(onpopuppositioned, "onpopuppositioned")
|
||||
GK_ATOM(onpopupshowing, "onpopupshowing")
|
||||
GK_ATOM(onpopupshown, "onpopupshown")
|
||||
GK_ATOM(onposter, "onposter")
|
||||
|
|
|
@ -781,6 +781,10 @@ NON_IDL_EVENT(popupshown,
|
|||
eXULPopupShown,
|
||||
EventNameType_XUL,
|
||||
eBasicEventClass)
|
||||
NON_IDL_EVENT(popuppositioned,
|
||||
eXULPopupPositioned,
|
||||
EventNameType_XUL,
|
||||
eBasicEventClass)
|
||||
NON_IDL_EVENT(popuphiding,
|
||||
eXULPopupHiding,
|
||||
EventNameType_XUL,
|
||||
|
|
|
@ -232,6 +232,7 @@ PopupBoxObject::GetPopupState(nsString& aState)
|
|||
aState.AssignLiteral("open");
|
||||
break;
|
||||
case ePopupShowing:
|
||||
case ePopupPositioning:
|
||||
case ePopupOpening:
|
||||
case ePopupVisible:
|
||||
aState.AssignLiteral("showing");
|
||||
|
|
|
@ -436,7 +436,7 @@ nsMenuPopupFrame::LayoutPopup(nsBoxLayoutState& aState, nsIFrame* aParentMenu,
|
|||
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);
|
||||
shouldPosition = (mPopupState == ePopupShowing || mPopupState == ePopupPositioning);
|
||||
if (!shouldPosition && !aSizedToPopup) {
|
||||
RemoveStateBits(NS_FRAME_FIRST_REFLOW);
|
||||
return;
|
||||
|
@ -476,7 +476,7 @@ nsMenuPopupFrame::LayoutPopup(nsBoxLayoutState& aState, nsIFrame* aParentMenu,
|
|||
|
||||
bool needCallback = false;
|
||||
if (shouldPosition) {
|
||||
SetPopupPosition(aAnchor, false, aSizedToPopup);
|
||||
SetPopupPosition(aAnchor, false, aSizedToPopup, mPopupState == ePopupPositioning);
|
||||
needCallback = true;
|
||||
}
|
||||
|
||||
|
@ -502,7 +502,7 @@ nsMenuPopupFrame::LayoutPopup(nsBoxLayoutState& aState, nsIFrame* aParentMenu,
|
|||
}
|
||||
|
||||
if (rePosition) {
|
||||
SetPopupPosition(aAnchor, false, aSizedToPopup);
|
||||
SetPopupPosition(aAnchor, false, aSizedToPopup, false);
|
||||
}
|
||||
|
||||
nsPresContext* pc = PresContext();
|
||||
|
@ -561,7 +561,7 @@ nsMenuPopupFrame::LayoutPopup(nsBoxLayoutState& aState, nsIFrame* aParentMenu,
|
|||
bool
|
||||
nsMenuPopupFrame::ReflowFinished()
|
||||
{
|
||||
SetPopupPosition(mReflowCallbackData.mAnchor, false, mReflowCallbackData.mSizedToPopup);
|
||||
SetPopupPosition(mReflowCallbackData.mAnchor, false, mReflowCallbackData.mSizedToPopup, false);
|
||||
|
||||
mReflowCallbackData.Clear();
|
||||
|
||||
|
@ -865,7 +865,7 @@ nsMenuPopupFrame::ShowPopup(bool aIsContextMenu)
|
|||
|
||||
InvalidateFrameSubtree();
|
||||
|
||||
if (mPopupState == ePopupShowing) {
|
||||
if (mPopupState == ePopupShowing || mPopupState == ePopupPositioning) {
|
||||
mPopupState = ePopupOpening;
|
||||
mIsOpenChanged = true;
|
||||
|
||||
|
@ -906,7 +906,8 @@ nsMenuPopupFrame::HidePopup(bool aDeselectMenu, nsPopupState aNewState)
|
|||
ClearPopupShownDispatcher();
|
||||
|
||||
// don't hide the popup when it isn't open
|
||||
if (mPopupState == ePopupClosed || mPopupState == ePopupShowing)
|
||||
if (mPopupState == ePopupClosed || mPopupState == ePopupShowing ||
|
||||
mPopupState == ePopupPositioning)
|
||||
return;
|
||||
|
||||
// clear the trigger content if the popup is being closed. But don't clear
|
||||
|
@ -1184,6 +1185,8 @@ nsMenuPopupFrame::FlipOrResize(nscoord& aScreenPoint, nscoord aSize,
|
|||
nscoord aOffsetForContextMenu, FlipStyle aFlip,
|
||||
bool* aFlipSide)
|
||||
{
|
||||
*aFlipSide = false;
|
||||
|
||||
// all of the coordinates used here are in app units relative to the screen
|
||||
nscoord popupSize = aSize;
|
||||
if (aScreenPoint < aScreenBegin) {
|
||||
|
@ -1284,7 +1287,7 @@ nsMenuPopupFrame::FlipOrResize(nscoord& aScreenPoint, nscoord aSize,
|
|||
}
|
||||
|
||||
nsresult
|
||||
nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame, bool aIsMove, bool aSizedToPopup)
|
||||
nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame, bool aIsMove, bool aSizedToPopup, bool aNotify)
|
||||
{
|
||||
if (!mShouldAutoPosition)
|
||||
return NS_OK;
|
||||
|
@ -1589,6 +1592,17 @@ nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame, bool aIsMove, bool aS
|
|||
SetXULBounds(state, mRect);
|
||||
}
|
||||
|
||||
// If the popup is in the positioned state or if it is shown and the position
|
||||
// or size changed, dispatch a popuppositioned event if the popup wants it.
|
||||
nsIntRect newRect(screenPoint.x, screenPoint.y, mRect.width, mRect.height);
|
||||
if (mPopupState == ePopupPositioning ||
|
||||
(mPopupState == ePopupShown && !newRect.IsEqualEdges(mUsedScreenRect))) {
|
||||
mUsedScreenRect = newRect;
|
||||
if (aNotify) {
|
||||
nsXULPopupPositionedEvent::DispatchIfNeeded(mContent, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -2270,7 +2284,7 @@ nsMenuPopupFrame::MoveTo(const CSSIntPoint& aPos, bool aUpdateAttrs)
|
|||
mScreenRect.x = aPos.x - presContext->AppUnitsToIntCSSPixels(margin.left);
|
||||
mScreenRect.y = aPos.y - presContext->AppUnitsToIntCSSPixels(margin.top);
|
||||
|
||||
SetPopupPosition(nullptr, true, false);
|
||||
SetPopupPosition(nullptr, true, false, true);
|
||||
|
||||
nsCOMPtr<nsIContent> popup = mContent;
|
||||
if (aUpdateAttrs && (popup->HasAttr(kNameSpaceID_None, nsGkAtoms::left) ||
|
||||
|
@ -2299,7 +2313,7 @@ nsMenuPopupFrame::MoveToAnchor(nsIContent* aAnchorContent,
|
|||
mPopupState = oldstate;
|
||||
|
||||
// Pass false here so that flipping and adjusting to fit on the screen happen.
|
||||
SetPopupPosition(nullptr, false, false);
|
||||
SetPopupPosition(nullptr, false, false, true);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -48,6 +48,8 @@ enum nsPopupState {
|
|||
// state from when a popup is requested to be shown to after the
|
||||
// popupshowing event has been fired.
|
||||
ePopupShowing,
|
||||
// state while a popup is waiting to be laid out and positioned
|
||||
ePopupPositioning,
|
||||
// state while a popup is open but the widget is not yet visible
|
||||
ePopupOpening,
|
||||
// state while a popup is visible and waiting for the popupshown event
|
||||
|
@ -251,8 +253,10 @@ public:
|
|||
// (or the frame for mAnchorContent if aAnchorFrame is null), anchored at a
|
||||
// rectangle, or at a specific point if a screen position is set. The popup
|
||||
// will be adjusted so that it is on screen. If aIsMove is true, then the
|
||||
// popup is being moved, and should not be flipped.
|
||||
nsresult SetPopupPosition(nsIFrame* aAnchorFrame, bool aIsMove, bool aSizedToPopup);
|
||||
// popup is being moved, and should not be flipped. If aNotify is true, then
|
||||
// a popuppositioned event is sent.
|
||||
nsresult SetPopupPosition(nsIFrame* aAnchorFrame, bool aIsMove,
|
||||
bool aSizedToPopup, bool aNotify);
|
||||
|
||||
bool HasGeneratedChildren() { return mGeneratedChildren; }
|
||||
void SetGeneratedChildren() { mGeneratedChildren = true; }
|
||||
|
@ -425,6 +429,11 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
void ShowWithPositionedEvent() {
|
||||
mPopupState = ePopupPositioning;
|
||||
mShouldAutoPosition = true;
|
||||
}
|
||||
|
||||
// nsIReflowCallback
|
||||
virtual bool ReflowFinished() override;
|
||||
virtual void ReflowCallbackCanceled() override;
|
||||
|
@ -524,6 +533,9 @@ protected:
|
|||
|
||||
RefPtr<nsXULPopupShownEvent> mPopupShownDispatcher;
|
||||
|
||||
// The popup's screen rectangle in app units.
|
||||
nsIntRect mUsedScreenRect;
|
||||
|
||||
// A popup's preferred size may be different than its actual size stored in
|
||||
// mRect in the case where the popup was resized because it was too large
|
||||
// for the screen. The preferred size mPrefSize holds the full size the popup
|
||||
|
|
|
@ -444,7 +444,7 @@ nsXULPopupManager::AdjustPopupsOnWindowChange(nsPIDOMWindowOuter* aWindow)
|
|||
}
|
||||
|
||||
for (int32_t l = list.Length() - 1; l >= 0; l--) {
|
||||
list[l]->SetPopupPosition(nullptr, true, false);
|
||||
list[l]->SetPopupPosition(nullptr, true, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -500,7 +500,7 @@ nsXULPopupManager::PopupMoved(nsIFrame* aFrame, nsIntPoint aPnt)
|
|||
// the specified screen coordinates.
|
||||
if (menuPopupFrame->IsAnchored() &&
|
||||
menuPopupFrame->PopupLevel() == ePopupLevelParent) {
|
||||
menuPopupFrame->SetPopupPosition(nullptr, true, false);
|
||||
menuPopupFrame->SetPopupPosition(nullptr, true, false, true);
|
||||
}
|
||||
else {
|
||||
CSSPoint cssPos = LayoutDeviceIntPoint::FromUnknownPoint(aPnt)
|
||||
|
@ -1038,6 +1038,24 @@ nsXULPopupManager::HidePopup(nsIContent* aPopup,
|
|||
}
|
||||
else if (foundPanel) {
|
||||
popupToHide = aPopup;
|
||||
} else {
|
||||
// When the popup is in the popuppositioning state, it will not be in the
|
||||
// mPopups list. We need another way to find it and make sure it does not
|
||||
// continue the popup showing process.
|
||||
popupFrame = do_QueryFrame(aPopup->GetPrimaryFrame());
|
||||
if (popupFrame) {
|
||||
if (popupFrame->PopupState() == ePopupPositioning) {
|
||||
// Do basically the same thing we would have done if we had found the
|
||||
// popup in the mPopups list.
|
||||
deselectMenu = aDeselectMenu;
|
||||
popupToHide = aPopup;
|
||||
type = popupFrame->PopupType();
|
||||
} else {
|
||||
// The popup is not positioning. If we were supposed to have handled
|
||||
// closing it, it should have been in mPopups or mNoHidePanels
|
||||
popupFrame = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (popupFrame) {
|
||||
|
@ -1494,7 +1512,19 @@ nsXULPopupManager::FirePopupShowingEvent(nsIContent* aPopup,
|
|||
popupFrame->ClearTriggerContent();
|
||||
}
|
||||
else {
|
||||
ShowPopupCallback(aPopup, popupFrame, aIsContextMenu, aSelectFirstItem);
|
||||
// Now check if we need to fire the popuppositioned event. If not, call
|
||||
// ShowPopupCallback directly.
|
||||
|
||||
// The popuppositioned event only fires on arrow panels for now.
|
||||
if (popup->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
|
||||
nsGkAtoms::arrow, eCaseMatters)) {
|
||||
popupFrame->ShowWithPositionedEvent();
|
||||
presShell->FrameNeedsReflow(popupFrame, nsIPresShell::eTreeChange,
|
||||
NS_FRAME_HAS_DIRTY_CHILDREN);
|
||||
}
|
||||
else {
|
||||
ShowPopupCallback(popup, popupFrame, aIsContextMenu, aSelectFirstItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1724,7 +1754,8 @@ nsXULPopupManager::MayShowPopup(nsMenuPopupFrame* aPopup)
|
|||
// if the popup is not in the open popup chain, then it must have a state that
|
||||
// is either closed, in the process of being shown, or invisible.
|
||||
NS_ASSERTION(IsPopupOpen(aPopup->GetContent()) || state == ePopupClosed ||
|
||||
state == ePopupShowing || state == ePopupInvisible,
|
||||
state == ePopupShowing || state == ePopupPositioning ||
|
||||
state == ePopupInvisible,
|
||||
"popup not in XULPopupManager open list is open");
|
||||
|
||||
// don't show popups unless they are closed or invisible
|
||||
|
@ -2705,6 +2736,61 @@ nsXULPopupHidingEvent::Run()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
nsXULPopupPositionedEvent::DispatchIfNeeded(nsIContent *aPopup,
|
||||
bool aIsContextMenu,
|
||||
bool aSelectFirstItem)
|
||||
{
|
||||
// The popuppositioned event only fires on arrow panels for now.
|
||||
if (aPopup->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
|
||||
nsGkAtoms::arrow, eCaseMatters)) {
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
new nsXULPopupPositionedEvent(aPopup, aIsContextMenu, aSelectFirstItem);
|
||||
NS_DispatchToCurrentThread(event);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsXULPopupPositionedEvent::Run()
|
||||
{
|
||||
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
|
||||
if (pm) {
|
||||
nsMenuPopupFrame* popupFrame = do_QueryFrame(mPopup->GetPrimaryFrame());
|
||||
if (popupFrame) {
|
||||
// At this point, hidePopup may have been called but it currently has no
|
||||
// way to stop this event. However, if hidePopup was called, the popup
|
||||
// will now be in the hiding or closed state. If we are in the shown or
|
||||
// positioning state instead, we can assume that we are still clear to
|
||||
// open/move the popup
|
||||
nsPopupState state = popupFrame->PopupState();
|
||||
if (state != ePopupPositioning && state != ePopupShown) {
|
||||
return NS_OK;
|
||||
}
|
||||
nsEventStatus status = nsEventStatus_eIgnore;
|
||||
WidgetMouseEvent event(true, eXULPopupPositioned, nullptr,
|
||||
WidgetMouseEvent::eReal);
|
||||
EventDispatcher::Dispatch(mPopup, popupFrame->PresContext(),
|
||||
&event, nullptr, &status);
|
||||
|
||||
// Get the popup frame and make sure it is still in the positioning
|
||||
// state. If it isn't, someone may have tried to reshow or hide it
|
||||
// during the popuppositioned event.
|
||||
// Alternately, this event may have been fired in reponse to moving the
|
||||
// popup rather than opening it. In that case, we are done.
|
||||
nsMenuPopupFrame* popupFrame = do_QueryFrame(mPopup->GetPrimaryFrame());
|
||||
if (popupFrame && popupFrame->PopupState() == ePopupPositioning) {
|
||||
pm->ShowPopupCallback(mPopup, popupFrame, mIsContextMenu, mSelectFirstItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsXULMenuCommandEvent::Run()
|
||||
{
|
||||
|
|
|
@ -237,6 +237,34 @@ private:
|
|||
bool mIsRollup;
|
||||
};
|
||||
|
||||
// this class is used for dispatching popuppositioned events asynchronously.
|
||||
class nsXULPopupPositionedEvent : public mozilla::Runnable
|
||||
{
|
||||
public:
|
||||
explicit nsXULPopupPositionedEvent(nsIContent *aPopup,
|
||||
bool aIsContextMenu,
|
||||
bool aSelectFirstItem)
|
||||
: mPopup(aPopup)
|
||||
, mIsContextMenu(aIsContextMenu)
|
||||
, mSelectFirstItem(aSelectFirstItem)
|
||||
{
|
||||
NS_ASSERTION(aPopup, "null popup supplied to nsXULPopupShowingEvent constructor");
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override;
|
||||
|
||||
// Asynchronously dispatch a popuppositioned event at aPopup if this is a
|
||||
// panel that should receieve such events. Return true if the event was sent.
|
||||
static bool DispatchIfNeeded(nsIContent *aPopup,
|
||||
bool aIsContextMenu,
|
||||
bool aSelectFirstItem);
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIContent> mPopup;
|
||||
bool mIsContextMenu;
|
||||
bool mSelectFirstItem;
|
||||
};
|
||||
|
||||
// this class is used for dispatching menu command events asynchronously.
|
||||
class nsXULMenuCommandEvent : public mozilla::Runnable
|
||||
{
|
||||
|
@ -287,6 +315,7 @@ class nsXULPopupManager final : public nsIDOMEventListener,
|
|||
public:
|
||||
friend class nsXULPopupShowingEvent;
|
||||
friend class nsXULPopupHidingEvent;
|
||||
friend class nsXULPopupPositionedEvent;
|
||||
friend class nsXULMenuCommandEvent;
|
||||
friend class TransitionEnder;
|
||||
|
||||
|
|
|
@ -88,6 +88,15 @@ function openPopup(position, callback) {
|
|||
_openPopup(position, callback);
|
||||
}
|
||||
|
||||
function waitForPopupPositioned(actionFn, callback)
|
||||
{
|
||||
panel.addEventListener("popuppositioned", function listener() {
|
||||
panel.removeEventListener("popuppositioned", listener, false);
|
||||
callback();
|
||||
}, false);
|
||||
actionFn();
|
||||
}
|
||||
|
||||
function _openPopup(position, callback) {
|
||||
// this is very ugly: the panel CSS sets the arrow's list-style-image based
|
||||
// on the 'side' attribute. If the setting of the 'side' attribute causes
|
||||
|
@ -200,14 +209,22 @@ var tests = [
|
|||
// and the anchor right - it can't fit with the panel on the left/arrow
|
||||
// on the right, so it must flip (arrow on the left, panel on the right)
|
||||
var offset = Math.floor(-anchorRight / 2);
|
||||
panel.moveToAnchor(anchor, "after_end", offset, 0);
|
||||
|
||||
waitForPopupPositioned(
|
||||
() => panel.moveToAnchor(anchor, "after_end", offset, 0),
|
||||
() => {
|
||||
isArrowPositionedOn("left", offset); // should have flipped and have the offset.
|
||||
// resize back to original and move to a zero offset - it should flip back.
|
||||
|
||||
panel.sizeTo(anchorRight - 10, 100);
|
||||
panel.moveToAnchor(anchor, "after_end", 0, 0);
|
||||
waitForPopupPositioned(
|
||||
() => panel.moveToAnchor(anchor, "after_end", 0, 0),
|
||||
() => {
|
||||
isArrowPositionedOn("right"); // should have flipped back and no offset
|
||||
next();
|
||||
});
|
||||
});
|
||||
});
|
||||
}],
|
||||
|
||||
// Do a moveToAnchor that causes the panel to flip vertically
|
||||
|
@ -219,13 +236,21 @@ var tests = [
|
|||
openPopup("start_after", function() {
|
||||
isArrowPositionedOn("bottom");
|
||||
var offset = Math.floor(-anchorBottom / 2);
|
||||
panel.moveToAnchor(anchor, "start_after", 0, offset);
|
||||
|
||||
waitForPopupPositioned(
|
||||
() => panel.moveToAnchor(anchor, "start_after", 0, offset),
|
||||
() => {
|
||||
isArrowPositionedOn("top", offset);
|
||||
panel.sizeTo(100, anchorBottom - 10);
|
||||
panel.moveToAnchor(anchor, "start_after", 0, 0);
|
||||
|
||||
waitForPopupPositioned(
|
||||
() => panel.moveToAnchor(anchor, "start_after", 0, 0),
|
||||
() => {
|
||||
isArrowPositionedOn("bottom");
|
||||
next();
|
||||
});
|
||||
});
|
||||
});
|
||||
}],
|
||||
|
||||
['veryWidePanel-after_end', 'middle', function(next) {
|
||||
|
@ -379,10 +404,8 @@ SimpleTest.waitForExplicitFinish();
|
|||
addEventListener("load", function() {
|
||||
// anchor is set by the test runner above
|
||||
panel = document.getElementById("testPanel");
|
||||
|
||||
arrow = SpecialPowers.wrap(document).getAnonymousElementByAttribute(panel, "anonid", "arrow");
|
||||
// Cancel the arrow panel slide-in transition (bug 767133) so the size and
|
||||
// position are "stable" enough to test without jumping through hoops...
|
||||
arrow.style.transition = "none";
|
||||
runTests();
|
||||
});
|
||||
|
||||
|
|
|
@ -374,19 +374,9 @@
|
|||
<body>
|
||||
<![CDATA[
|
||||
this.popupBoxObject.sizeTo(aWidth, aHeight);
|
||||
if (this.state == "open")
|
||||
this.adjustArrowPosition();
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
<method name="moveTo">
|
||||
<parameter name="aLeft"/>
|
||||
<parameter name="aTop"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
this.popupBoxObject.moveTo(aLeft, aTop);
|
||||
if (this.state == "open")
|
||||
if (this.state == "open") {
|
||||
this.adjustArrowPosition();
|
||||
}
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
@ -399,8 +389,6 @@
|
|||
<body>
|
||||
<![CDATA[
|
||||
this.popupBoxObject.moveToAnchor(aAnchorElement, aPosition, aX, aY, aAttributesOverride);
|
||||
if (this.state == "open")
|
||||
this.adjustArrowPosition();
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
@ -411,7 +399,6 @@
|
|||
|
||||
var anchor = this.anchorNode;
|
||||
if (!anchor) {
|
||||
arrow.hidden = true;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -423,8 +410,6 @@
|
|||
|
||||
this.setAttribute("arrowposition", position);
|
||||
|
||||
// if this panel has a "sliding" arrow, we may have previously set margins...
|
||||
arrowbox.style.removeProperty("transform");
|
||||
if (position.indexOf("start_") == 0 || position.indexOf("end_") == 0) {
|
||||
container.orient = "horizontal";
|
||||
arrowbox.orient = "vertical";
|
||||
|
@ -466,8 +451,6 @@
|
|||
this.setAttribute("side", "top");
|
||||
}
|
||||
}
|
||||
|
||||
arrow.hidden = false;
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
@ -475,7 +458,13 @@
|
|||
<handlers>
|
||||
<handler event="popupshowing" phase="target">
|
||||
<![CDATA[
|
||||
var arrow = document.getAnonymousElementByAttribute(this, "anonid", "arrow");
|
||||
arrow.hidden = this.anchorNode == null;
|
||||
document.getAnonymousElementByAttribute(this, "anonid", "arrowbox")
|
||||
.style.removeProperty("transform");
|
||||
|
||||
this.adjustArrowPosition();
|
||||
|
||||
if (this.getAttribute("animate") != "false") {
|
||||
this.setAttribute("animate", "open");
|
||||
}
|
||||
|
@ -518,6 +507,9 @@
|
|||
this.removeAttribute("animate");
|
||||
}
|
||||
</handler>
|
||||
<handler event="popuppositioned" phase="target">
|
||||
this.adjustArrowPosition();
|
||||
</handler>
|
||||
</handlers>
|
||||
</binding>
|
||||
|
||||
|
|
|
@ -848,13 +848,35 @@ PopupNotifications.prototype = {
|
|||
// the next reason will be that the user clicked elsewhere.
|
||||
this.nextDismissReason = TELEMETRY_STAT_DISMISSAL_CLICK_ELSEWHERE;
|
||||
|
||||
this.panel.openPopup(anchorElement, "bottomcenter topleft");
|
||||
let target = this.panel;
|
||||
if (target.parentNode) {
|
||||
// NOTIFICATION_EVENT_SHOWN should be fired for the panel before
|
||||
// anyone listening for popupshown on the panel gets run. Otherwise,
|
||||
// the panel will not be initialized when the popupshown event
|
||||
// listeners run.
|
||||
// By targeting the panel's parent and using a capturing listener, we
|
||||
// can have our listener called before others waiting for the panel to
|
||||
// be shown (which probably expect the panel to be fully initialized)
|
||||
target = target.parentNode;
|
||||
}
|
||||
if (this._popupshownListener) {
|
||||
target.removeEventListener("popupshown", this._popupshownListener, true);
|
||||
}
|
||||
this._popupshownListener = function (e) {
|
||||
target.removeEventListener("popupshown", this._popupshownListener, true);
|
||||
this._popupshownListener = null;
|
||||
|
||||
notificationsToShow.forEach(function (n) {
|
||||
this._fireCallback(n, NOTIFICATION_EVENT_SHOWN);
|
||||
}, this);
|
||||
// This notification is used by tests to know when all the processing
|
||||
// required to display the panel has happened.
|
||||
this.panel.dispatchEvent(new this.window.CustomEvent("Shown"));
|
||||
};
|
||||
this._popupshownListener = this._popupshownListener.bind(this);
|
||||
target.addEventListener("popupshown", this._popupshownListener, true);
|
||||
|
||||
this.panel.openPopup(anchorElement, "bottomcenter topleft");
|
||||
});
|
||||
},
|
||||
|
||||
|
|
|
@ -142,6 +142,7 @@ NS_EVENT_MESSAGE_FIRST_LAST(eDragDropEvent, eDragEnter, eDragLeave)
|
|||
// XUL specific events
|
||||
NS_EVENT_MESSAGE(eXULPopupShowing)
|
||||
NS_EVENT_MESSAGE(eXULPopupShown)
|
||||
NS_EVENT_MESSAGE(eXULPopupPositioned)
|
||||
NS_EVENT_MESSAGE(eXULPopupHiding)
|
||||
NS_EVENT_MESSAGE(eXULPopupHidden)
|
||||
NS_EVENT_MESSAGE(eXULBroadcast)
|
||||
|
|
Загрузка…
Ссылка в новой задаче