diff --git a/mozilla/layout/base/nsIPresShell.h b/mozilla/layout/base/nsIPresShell.h index 3b97086a229..2db6f519d5c 100644 --- a/mozilla/layout/base/nsIPresShell.h +++ b/mozilla/layout/base/nsIPresShell.h @@ -94,12 +94,14 @@ class nsIStyleSheet; class nsCSSFrameConstructor; class nsISelection; template class nsCOMArray; +class nsWeakFrame; typedef short SelectionType; #define NS_IPRESSHELL_IID \ -{ 0xa736d2fd, 0x0191, 0x42ea, \ - { 0xb1, 0xb0, 0x80, 0x45, 0x1d, 0xfa, 0x03, 0x53 } } +{ 0x67880b18, 0xaf91, 0x431b, \ + { 0x89, 0x69, 0xfa, 0xd9, 0x2b, 0x3c, 0x33, 0x32 } } + // Constants uses for ScrollFrameIntoView() function #define NS_PRESSHELL_SCROLL_TOP 0 @@ -749,6 +751,8 @@ public: nscolor aBackgroundColor, nsIRenderingContext** aRenderedContext) = 0; + void AddWeakFrame(nsWeakFrame* aWeakFrame); + void RemoveWeakFrame(nsWeakFrame* aWeakFrame); protected: // IMPORTANT: The ownership implicit in the following member variables // has been explicitly checked. If you add any members to this class, @@ -779,7 +783,10 @@ protected: // Set to true when the accessibility service is being used to mirror // the dom/layout trees - PRPackedBool mIsAccessibilityActive; + PRPackedBool mIsAccessibilityActive; + + // A list of weak frames. This is a pointer to the last item in the list. + nsWeakFrame* mWeakFrames; }; /** diff --git a/mozilla/layout/base/nsPresShell.cpp b/mozilla/layout/base/nsPresShell.cpp index c0a9b1abfda..6456e0782e9 100644 --- a/mozilla/layout/base/nsPresShell.cpp +++ b/mozilla/layout/base/nsPresShell.cpp @@ -1603,6 +1603,32 @@ nsIPresShell::GetVerifyReflowFlags() #endif } +void +nsIPresShell::AddWeakFrame(nsWeakFrame* aWeakFrame) +{ + if (aWeakFrame->GetFrame()) { + aWeakFrame->GetFrame()->AddStateBits(NS_FRAME_EXTERNAL_REFERENCE); + } + aWeakFrame->SetPreviousWeakFrame(mWeakFrames); + mWeakFrames = aWeakFrame; +} + +void +nsIPresShell::RemoveWeakFrame(nsWeakFrame* aWeakFrame) +{ + if (mWeakFrames == aWeakFrame) { + mWeakFrames = aWeakFrame->GetPreviousWeakFrame(); + return; + } + nsWeakFrame* nextWeak = mWeakFrames; + while (nextWeak && nextWeak->GetPreviousWeakFrame() != aWeakFrame) { + nextWeak = nextWeak->GetPreviousWeakFrame(); + } + if (nextWeak) { + nextWeak->SetPreviousWeakFrame(aWeakFrame->GetPreviousWeakFrame()); + } +} + //---------------------------------------------------------------------- nsresult @@ -1931,6 +1957,11 @@ PresShell::Destroy() mFrameConstructor->WillDestroyFrameTree(); FrameManager()->Destroy(); + NS_WARN_IF_FALSE(!mWeakFrames, "Weak frames alive after destroying FrameManager"); + while (mWeakFrames) { + mWeakFrames->Clear(this); + } + // Let the style set do its cleanup. mStyleSet->Shutdown(mPresContext); @@ -3940,6 +3971,16 @@ PresShell::ClearFrameRefs(nsIFrame* aFrame) } } + nsWeakFrame* weakFrame = mWeakFrames; + while (weakFrame) { + nsWeakFrame* prev = weakFrame->GetPreviousWeakFrame(); + if (weakFrame->GetFrame() == aFrame) { + // This removes weakFrame from mWeakFrames. + weakFrame->Clear(this); + } + weakFrame = prev; + } + return NS_OK; } diff --git a/mozilla/layout/generic/nsIFrame.h b/mozilla/layout/generic/nsIFrame.h index 05f5fe57684..5e69ca18c52 100644 --- a/mozilla/layout/generic/nsIFrame.h +++ b/mozilla/layout/generic/nsIFrame.h @@ -1690,6 +1690,64 @@ private: NS_IMETHOD_(nsrefcnt) Release(void) = 0; }; +//---------------------------------------------------------------------- + +/** + * nsWeakFrame can be used to keep a reference to a nsIFrame in a safe way. + * Whenever an nsIFrame object is deleted, the nsWeakFrames pointing + * to it will be cleared. + * + * Create nsWeakFrame object when it is sure that nsIFrame object + * is alive and after some operations which may destroy the nsIFrame + * (for example any DOM modifications) use IsAlive() or GetFrame() methods to + * check whether it is safe to continue to use the nsIFrame object. + * + * @note The usage of this class should be kept to a minimum. + */ + +class nsWeakFrame { +public: + nsWeakFrame(nsIFrame* aFrame) + { + mPrev = nsnull; + mFrame = aFrame; + if (mFrame) { + nsIPresShell* shell = mFrame->GetPresContext()->GetPresShell(); + NS_WARN_IF_FALSE(shell, "Null PresShell in nsWeakFrame!"); + if (shell) { + shell->AddWeakFrame(this); + } else { + mFrame = nsnull; + } + } + } + + void Clear(nsIPresShell* aShell) { + if (aShell) { + aShell->RemoveWeakFrame(this); + } + mFrame = nsnull; + mPrev = nsnull; + } + + PRBool IsAlive() { return !!mFrame; } + + nsIFrame* GetFrame() { return mFrame; } + + nsWeakFrame* GetPreviousWeakFrame() { return mPrev; } + + void SetPreviousWeakFrame(nsWeakFrame* aPrev) { mPrev = aPrev; } + + ~nsWeakFrame() + { + Clear(mFrame ? mFrame->GetPresContext()->GetPresShell() : nsnull); + } +private: + nsWeakFrame* mPrev; + nsIFrame* mFrame; +}; + + NS_DEFINE_STATIC_IID_ACCESSOR(nsIFrame, NS_IFRAME_IID) #endif /* nsIFrame_h___ */ diff --git a/mozilla/layout/xul/base/src/nsPopupBoxObject.cpp b/mozilla/layout/xul/base/src/nsPopupBoxObject.cpp index 181e3dff608..56bbe33425b 100644 --- a/mozilla/layout/xul/base/src/nsPopupBoxObject.cpp +++ b/mozilla/layout/xul/base/src/nsPopupBoxObject.cpp @@ -110,8 +110,11 @@ nsPopupBoxObject::HidePopup() nsIPopupSetFrame *popupSet = GetPopupSetFrame(); nsIFrame *ourFrame = GetFrame(PR_FALSE); if (ourFrame && popupSet) { + nsWeakFrame weakFrame(ourFrame); popupSet->HidePopup(ourFrame); - popupSet->DestroyPopup(ourFrame, PR_TRUE); + if (weakFrame.IsAlive()) { + popupSet->DestroyPopup(ourFrame, PR_TRUE); + } } return NS_OK; diff --git a/mozilla/layout/xul/base/src/nsPopupSetFrame.cpp b/mozilla/layout/xul/base/src/nsPopupSetFrame.cpp index 7db5c588d4a..abbab3f9f5c 100644 --- a/mozilla/layout/xul/base/src/nsPopupSetFrame.cpp +++ b/mozilla/layout/xul/base/src/nsPopupSetFrame.cpp @@ -321,8 +321,9 @@ nsPopupSetFrame::ShowPopup(nsIContent* aElementContent, nsIContent* aPopupConten const nsString& aPopupType, const nsString& anAnchorAlignment, const nsString& aPopupAlignment) { + nsWeakFrame weakFrame(this); // First fire the popupshowing event. - if (!OnCreate(aXPos, aYPos, aPopupContent)) + if (!OnCreate(aXPos, aYPos, aPopupContent) || !weakFrame.IsAlive()) return NS_OK; // See if we already have an entry in our list. We must create a new one on a miss. @@ -348,6 +349,7 @@ nsPopupSetFrame::ShowPopup(nsIContent* aElementContent, nsIContent* aPopupConten entry->mPopupFrame = GetPresContext()->PresShell() ->GetPrimaryFrameFor(aPopupContent); + nsWeakFrame weakPopupFrame(entry->mPopupFrame); #ifdef DEBUG_PINK printf("X Pos: %d\n", mXPos); printf("Y Pos: %d\n", mYPos); @@ -356,18 +358,26 @@ nsPopupSetFrame::ShowPopup(nsIContent* aElementContent, nsIContent* aPopupConten // Generate the popup. entry->mCreateHandlerSucceeded = PR_TRUE; entry->mIsOpen = PR_TRUE; + // This may destroy entry->mPopupFrame MarkAsGenerated(aPopupContent); // determine if this menu is a context menu and flag it - nsIFrame* activeChild = entry->mPopupFrame; nsIMenuParent* childPopup = nsnull; - if (activeChild) - CallQueryInterface(activeChild, &childPopup); + if (weakPopupFrame.IsAlive()) + CallQueryInterface(weakPopupFrame.GetFrame(), &childPopup); if ( childPopup && aPopupType.EqualsLiteral("context") ) childPopup->SetIsContextMenu(PR_TRUE); + if (!weakFrame.IsAlive()) { + return NS_OK; + } + // Now open the popup. OpenPopup(entry, PR_TRUE); + + if (!weakFrame.IsAlive()) { + return NS_OK; + } // Now fire the popupshown event. OnCreated(aXPos, aYPos, aPopupContent); @@ -416,34 +426,37 @@ nsPopupSetFrame::DestroyPopup(nsIFrame* aPopup, PRBool aDestroyEntireChain) nsPopupFrameList* entry = mPopupList->GetEntryByFrame(aPopup); if (entry && entry->mCreateHandlerSucceeded) { // ensure the popup was created before we try to destroy it + nsWeakFrame weakFrame(this); OpenPopup(entry, PR_FALSE); - entry->mPopupType.SetLength(0); - - if (aDestroyEntireChain && entry->mElementContent && entry->mPopupType.EqualsLiteral("context")) { - // If we are a context menu, and if we are attached to a - // menupopup, then destroying us should also dismiss the parent - // menu popup. - if (entry->mElementContent->Tag() == nsXULAtoms::menupopup) { - nsIFrame* popupFrame = GetPresContext()->PresShell() - ->GetPrimaryFrameFor(entry->mElementContent); - if (popupFrame) { - nsIMenuParent *menuParent; - if (NS_SUCCEEDED(CallQueryInterface(popupFrame, &menuParent))) { - menuParent->DismissChain(); + nsCOMPtr popupContent = entry->mPopupContent; + if (weakFrame.IsAlive()) { + entry->mPopupType.SetLength(0); + + if (aDestroyEntireChain && entry->mElementContent && entry->mPopupType.EqualsLiteral("context")) { + // If we are a context menu, and if we are attached to a + // menupopup, then destroying us should also dismiss the parent + // menu popup. + if (entry->mElementContent->Tag() == nsXULAtoms::menupopup) { + nsIFrame* popupFrame = GetPresContext()->PresShell() + ->GetPrimaryFrameFor(entry->mElementContent); + if (popupFrame) { + nsIMenuParent *menuParent; + if (NS_SUCCEEDED(CallQueryInterface(popupFrame, &menuParent))) { + menuParent->DismissChain(); + } } } } + + // clear things out for next time + entry->mCreateHandlerSucceeded = PR_FALSE; + entry->mElementContent = nsnull; + entry->mXPos = entry->mYPos = 0; + entry->mLastPref.width = -1; + entry->mLastPref.height = -1; } - - // clear things out for next time - entry->mCreateHandlerSucceeded = PR_FALSE; - entry->mElementContent = nsnull; - entry->mXPos = entry->mYPos = 0; - entry->mLastPref.width = -1; - entry->mLastPref.height = -1; - // ungenerate the popup. - entry->mPopupContent->UnsetAttr(kNameSpaceID_None, nsXULAtoms::menugenerated, PR_TRUE); + popupContent->UnsetAttr(kNameSpaceID_None, nsXULAtoms::menugenerated, PR_TRUE); } return NS_OK; @@ -465,21 +478,26 @@ nsPopupSetFrame::MarkAsGenerated(nsIContent* aPopupContent) void nsPopupSetFrame::OpenPopup(nsPopupFrameList* aEntry, PRBool aActivateFlag) { + nsWeakFrame weakFrame(this); + nsIFrame* activeChild = aEntry->mPopupFrame; + nsWeakFrame weakPopupFrame(activeChild); + nsCOMPtr popupContent = aEntry->mPopupContent; + PRBool createHandlerSucceeded = aEntry->mCreateHandlerSucceeded; + nsAutoString popupType = aEntry->mPopupType; if (aActivateFlag) { ActivatePopup(aEntry, PR_TRUE); // register the rollup listeners, etc, but not if we're a tooltip - if (!aEntry->mPopupType.EqualsLiteral("tooltip")) { - nsIFrame* activeChild = aEntry->mPopupFrame; + if (!popupType.EqualsLiteral("tooltip")) { nsIMenuParent* childPopup = nsnull; - if (activeChild) + if (weakPopupFrame.IsAlive()) CallQueryInterface(activeChild, &childPopup); // Tooltips don't get keyboard navigation if (childPopup && !nsMenuDismissalListener::sInstance) { // First check and make sure this popup wants keyboard navigation - if (!aEntry->mPopupContent->AttrValueIs(kNameSpaceID_None, nsXULAtoms::ignorekeys, - nsXULAtoms::_true, eCaseMatters)) + if (!popupContent->AttrValueIs(kNameSpaceID_None, nsXULAtoms::ignorekeys, + nsXULAtoms::_true, eCaseMatters)) childPopup->InstallKeyboardNavigator(); } @@ -489,28 +507,32 @@ nsPopupSetFrame::OpenPopup(nsPopupFrameList* aEntry, PRBool aActivateFlag) } } else { - if (aEntry->mCreateHandlerSucceeded && !OnDestroy(aEntry->mPopupContent)) + if (createHandlerSucceeded && !OnDestroy(aEntry->mPopupContent)) return; // Unregister, but not if we're a tooltip - if (!aEntry->mPopupType.EqualsLiteral("tooltip") ) { + if (!popupType.EqualsLiteral("tooltip") ) { nsMenuDismissalListener::Shutdown(); } // Remove any keyboard navigators nsIMenuParent* childPopup = nsnull; - if (aEntry->mPopupFrame) - CallQueryInterface(aEntry->mPopupFrame, &childPopup); + if (weakPopupFrame.IsAlive()) + CallQueryInterface(activeChild, &childPopup); if (childPopup) childPopup->RemoveKeyboardNavigator(); + nsRefPtr presContext = GetPresContext(); + nsCOMPtr content = aEntry->mPopupContent; ActivatePopup(aEntry, PR_FALSE); - OnDestroyed(aEntry->mPopupContent); + OnDestroyed(presContext, content); } - nsBoxLayoutState state(GetPresContext()); - MarkDirtyChildren(state); // Mark ourselves dirty. + if (weakFrame.IsAlive()) { + nsBoxLayoutState state(GetPresContext()); + MarkDirtyChildren(state); // Mark ourselves dirty. + } } void @@ -524,23 +546,25 @@ nsPopupSetFrame::ActivatePopup(nsPopupFrameList* aEntry, PRBool aActivateFlag) // XXXben hook in |width| and |height| usage here? aEntry->mPopupContent->SetAttr(kNameSpaceID_None, nsXULAtoms::menutobedisplayed, NS_LITERAL_STRING("true"), PR_TRUE); else { - aEntry->mPopupContent->UnsetAttr(kNameSpaceID_None, nsXULAtoms::menuactive, PR_TRUE); - aEntry->mPopupContent->UnsetAttr(kNameSpaceID_None, nsXULAtoms::menutobedisplayed, PR_TRUE); + nsWeakFrame weakFrame(this); + nsWeakFrame weakActiveChild(aEntry->mPopupFrame); + nsCOMPtr content = aEntry->mPopupContent; + content->UnsetAttr(kNameSpaceID_None, nsXULAtoms::menuactive, PR_TRUE); + content->UnsetAttr(kNameSpaceID_None, nsXULAtoms::menutobedisplayed, PR_TRUE); // get rid of the reflows we just created. If we leave them hanging around, we // can get into trouble if a dialog with a modal event loop comes along and // processes the reflows before we get to call DestroyChain(). Processing the // reflow will cause the popup to show itself again. (bug 71219) - nsIDocument* doc = aEntry->mPopupContent->GetDocument(); + nsIDocument* doc = content->GetDocument(); if (doc) doc->FlushPendingNotifications(Flush_OnlyReflow); - + // make sure we hide the popup. We can't assume that we'll have a view // since we could be cleaning up after someone that didn't correctly // destroy the popup. - nsIFrame* activeChild = aEntry->mPopupFrame; - if (activeChild) { - nsIView* view = activeChild->GetView(); + if (weakFrame.IsAlive() && weakActiveChild.IsAlive()) { + nsIView* view = weakActiveChild.GetFrame()->GetView(); NS_ASSERTION(view, "View is gone, looks like someone forgot to roll up the popup!"); if (view) { nsIViewManager* viewManager = view->GetViewManager(); @@ -549,7 +573,7 @@ nsPopupSetFrame::ActivatePopup(nsPopupFrameList* aEntry, PRBool aActivateFlag) viewManager->ResizeView(view, r); if (aEntry->mIsOpen) { aEntry->mIsOpen = PR_FALSE; - FireDOMEventSynch(NS_LITERAL_STRING("DOMMenuInactive"), aEntry->mPopupContent); + FireDOMEventSynch(NS_LITERAL_STRING("DOMMenuInactive"), content); } } } @@ -570,6 +594,7 @@ nsPopupSetFrame::OnCreate(PRInt32 aX, PRInt32 aY, nsIContent* aPopupContent) event.refPoint.y = aY; if (aPopupContent) { + nsCOMPtr kungFuDeathGrip(aPopupContent); nsIPresShell *shell = GetPresContext()->GetPresShell(); if (shell) { nsresult rv = shell->HandleDOMEventWithTarget(aPopupContent, &event, @@ -672,14 +697,15 @@ nsPopupSetFrame::OnDestroy(nsIContent* aPopupContent) } PRBool -nsPopupSetFrame::OnDestroyed(nsIContent* aPopupContent) +nsPopupSetFrame::OnDestroyed(nsPresContext* aPresContext, + nsIContent* aPopupContent) { nsEventStatus status = nsEventStatus_eIgnore; nsMouseEvent event(PR_TRUE, NS_XUL_POPUP_HIDDEN, nsnull, nsMouseEvent::eReal); - if (aPopupContent) { - nsIPresShell *shell = GetPresContext()->GetPresShell(); + if (aPopupContent && aPresContext) { + nsIPresShell *shell = aPresContext->GetPresShell(); if (shell) { nsresult rv = shell->HandleDOMEventWithTarget(aPopupContent, &event, &status); diff --git a/mozilla/layout/xul/base/src/nsPopupSetFrame.h b/mozilla/layout/xul/base/src/nsPopupSetFrame.h index d6e917ca293..68bd16a3d29 100644 --- a/mozilla/layout/xul/base/src/nsPopupSetFrame.h +++ b/mozilla/layout/xul/base/src/nsPopupSetFrame.h @@ -117,7 +117,8 @@ public: PRBool OnCreate(PRInt32 aX, PRInt32 aY, nsIContent* aPopupContent); PRBool OnDestroy(nsIContent* aPopupContent); PRBool OnCreated(PRInt32 aX, PRInt32 aY, nsIContent* aPopupContent); - PRBool OnDestroyed(nsIContent* aPopupContent); + static PRBool OnDestroyed(nsPresContext* aPresContext, + nsIContent* aPopupContent); void ActivatePopup(nsPopupFrameList* aEntry, PRBool aActivateFlag); void OpenPopup(nsPopupFrameList* aEntry, PRBool aOpenFlag);