From fd6335c608ab4f1ed0f42cf443b77ebca9f31afb Mon Sep 17 00:00:00 2001 From: "enndeakin%sympatico.ca" Date: Thu, 25 Jan 2007 17:59:20 +0000 Subject: [PATCH] Bug 326877, close popups when navigating pages, r+sr=bz git-svn-id: svn://10.0.0.236/trunk@218908 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/layout/base/nsDocumentViewer.cpp | 78 ++++++++++++++----- .../layout/xul/base/src/nsMenuBoxObject.cpp | 4 + .../layout/xul/base/src/nsPopupSetFrame.cpp | 48 ++++++++++++ mozilla/layout/xul/base/src/nsPopupSetFrame.h | 7 ++ 4 files changed, 116 insertions(+), 21 deletions(-) diff --git a/mozilla/layout/base/nsDocumentViewer.cpp b/mozilla/layout/base/nsDocumentViewer.cpp index 6ca74edf215..4dc395306f5 100644 --- a/mozilla/layout/base/nsDocumentViewer.cpp +++ b/mozilla/layout/base/nsDocumentViewer.cpp @@ -120,6 +120,7 @@ #include "nsPIDOMWindow.h" #include "nsJSEnvironment.h" #include "nsIFocusController.h" +#include "nsIMenuParent.h" #include "nsIScrollableView.h" #include "nsIHTMLDocument.h" @@ -370,6 +371,8 @@ private: nsresult GetPopupLinkNode(nsIDOMNode** aNode); nsresult GetPopupImageNode(nsIImageLoadingContent** aNode); + void HideViewIfPopup(nsIView* aView); + void DumpContentToPPM(const char* aFileName); void PrepareToStartLoad(void); @@ -1247,36 +1250,69 @@ DocumentViewerImpl::PageHide(PRBool aIsUnload) } mDocument->OnPageHide(!aIsUnload); - if (!aIsUnload) - return NS_OK; + if (aIsUnload) { + // if Destroy() was called during OnPageHide(), mDocument is nsnull. + NS_ENSURE_STATE(mDocument); - // if Destroy() was called during OnPageHide(), mDocument is nsnull. - NS_ENSURE_STATE(mDocument); + // First, get the window from the document... + nsPIDOMWindow *window = mDocument->GetWindow(); - // First, get the window from the document... - nsPIDOMWindow *window = mDocument->GetWindow(); + if (!window) { + // Fail if no window is available... + NS_ERROR("window not set for document!"); + return NS_ERROR_NULL_POINTER; + } - if (!window) { - // Fail if no window is available... - NS_ERROR("window not set for document!"); - return NS_ERROR_NULL_POINTER; + // Now, fire an Unload event to the document... + nsEventStatus status = nsEventStatus_eIgnore; + nsEvent event(PR_TRUE, NS_PAGE_UNLOAD); + event.flags |= NS_EVENT_FLAG_CANT_BUBBLE; + // XXX Dispatching to |window|, but using |document| as the target. + event.target = mDocument; + + // Never permit popups from the unload handler, no matter how we get + // here. + nsAutoPopupStatePusher popupStatePusher(openAbused, PR_TRUE); + + nsEventDispatcher::Dispatch(window, mPresContext, &event, nsnull, &status); } - // Now, fire an Unload event to the document... - nsEventStatus status = nsEventStatus_eIgnore; - nsEvent event(PR_TRUE, NS_PAGE_UNLOAD); - event.flags |= NS_EVENT_FLAG_CANT_BUBBLE; - // XXX Dispatching to |window|, but using |document| as the target. - event.target = mDocument; + if (mPresShell) { + // look for open menupopups and close them after the unload event, in case + // the unload event listeners open any new popups + nsIViewManager *vm = mPresShell->GetViewManager(); + if (vm) { + nsIView *rootView = nsnull; + vm->GetRootView(rootView); + if (rootView) + HideViewIfPopup(rootView); + } + } - // Never permit popups from the unload handler, no matter how we get - // here. - nsAutoPopupStatePusher popupStatePusher(openAbused, PR_TRUE); - - nsEventDispatcher::Dispatch(window, mPresContext, &event, nsnull, &status); return NS_OK; } +void +DocumentViewerImpl::HideViewIfPopup(nsIView* aView) +{ + nsIFrame* frame = NS_STATIC_CAST(nsIFrame*, aView->GetClientData()); + if (frame) { + nsIMenuParent* parent; + CallQueryInterface(frame, &parent); + if (parent) { + parent->HideChain(); + // really make sure the view is hidden + mViewManager->SetViewVisibility(aView, nsViewVisibility_kHide); + } + } + + nsIView* child = aView->GetFirstChild(); + while (child) { + HideViewIfPopup(child); + child = child->GetNextSibling(); + } +} + static void AttachContainerRecurse(nsIDocShell* aShell) { diff --git a/mozilla/layout/xul/base/src/nsMenuBoxObject.cpp b/mozilla/layout/xul/base/src/nsMenuBoxObject.cpp index 535d349fc74..a3700907857 100644 --- a/mozilla/layout/xul/base/src/nsMenuBoxObject.cpp +++ b/mozilla/layout/xul/base/src/nsMenuBoxObject.cpp @@ -44,6 +44,7 @@ #include "nsGUIEvent.h" #include "nsIDOMNSUIEvent.h" #include "nsMenuBarListener.h" +#include "nsPopupSetFrame.h" class nsMenuBoxObject : public nsIMenuBoxObject, public nsBoxObject { @@ -92,6 +93,9 @@ NS_IMETHODIMP nsMenuBoxObject::OpenMenu(PRBool aOpenFlag) if (!frame) return NS_OK; + if (!nsPopupSetFrame::MayOpenPopup(frame)) + return NS_OK; + nsIMenuFrame* menuFrame; CallQueryInterface(frame, &menuFrame); if (!menuFrame) diff --git a/mozilla/layout/xul/base/src/nsPopupSetFrame.cpp b/mozilla/layout/xul/base/src/nsPopupSetFrame.cpp index 5e0fae22926..a365cfd9e84 100644 --- a/mozilla/layout/xul/base/src/nsPopupSetFrame.cpp +++ b/mozilla/layout/xul/base/src/nsPopupSetFrame.cpp @@ -66,6 +66,12 @@ #include "nsCSSFrameConstructor.h" #include "nsGUIEvent.h" #include "nsIRootBox.h" +#include "nsIFocusController.h" +#include "nsIDocShellTreeItem.h" +#include "nsIDocShell.h" +#include "nsPIDOMWindow.h" +#include "nsIInterfaceRequestorUtils.h" +#include "nsIBaseWindow.h" #define NS_MENU_POPUP_LIST_INDEX 0 @@ -363,6 +369,9 @@ nsPopupSetFrame::ShowPopup(nsIContent* aElementContent, nsIContent* aPopupConten const nsString& aPopupType, const nsString& anAnchorAlignment, const nsString& aPopupAlignment) { + if (!MayOpenPopup(this)) + return NS_OK; + nsWeakFrame weakFrame(this); // First fire the popupshowing event. if (!OnCreate(aXPos, aYPos, aPopupContent) || !weakFrame.IsAlive()) @@ -825,3 +834,42 @@ nsPopupSetFrame::AddPopupFrame(nsIFrame* aPopup) return NS_OK; } +//static +PRBool +nsPopupSetFrame::MayOpenPopup(nsIFrame* aFrame) +{ + nsCOMPtr cont = aFrame->GetPresContext()->GetContainer(); + nsCOMPtr dsti = do_QueryInterface(cont); + if (!dsti) + return PR_FALSE; + + // chrome shells can always open popups + PRInt32 type = -1; + if (NS_SUCCEEDED(dsti->GetItemType(&type)) && type == nsIDocShellTreeItem::typeChrome) + return PR_TRUE; + + nsCOMPtr shell = do_QueryInterface(dsti); + if (!shell) + return PR_FALSE; + + nsCOMPtr win = do_GetInterface(shell); + if (!win) + return PR_FALSE; + + // only allow popups in active windows + PRBool active; + nsIFocusController* focusController = win->GetRootFocusController(); + focusController->GetActive(&active); + if (!active) + return PR_FALSE; + + nsCOMPtr baseWin = do_QueryInterface(shell); + if (!baseWin) + return PR_FALSE; + + // only allow popups in visible frames + PRBool visible; + baseWin->GetVisibility(&visible); + return visible; +} + diff --git a/mozilla/layout/xul/base/src/nsPopupSetFrame.h b/mozilla/layout/xul/base/src/nsPopupSetFrame.h index bf680cb7dcc..eb6df7e94e3 100644 --- a/mozilla/layout/xul/base/src/nsPopupSetFrame.h +++ b/mozilla/layout/xul/base/src/nsPopupSetFrame.h @@ -132,6 +132,13 @@ public: void ActivatePopup(nsPopupFrameList* aEntry, PRBool aActivateFlag); void OpenPopup(nsPopupFrameList* aEntry, PRBool aOpenFlag); + /** + * Return true if the docshell containing aFrame may open a popup. aFrame + * doesn't need to be any particular type of frame, just a frame in the + * same document. + */ + static PRBool MayOpenPopup(nsIFrame* aFrame); + #ifdef DEBUG NS_IMETHOD GetFrameName(nsAString& aResult) const {