Back to index

lightning-sunbird  0.9+nobinonly
nsPopupSetFrame.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Original Author: David W. Hyatt (hyatt@netscape.com)
00024  *   Pierre Phaneuf <pp@ludusdesign.com>
00025  *   Dean Tessman <dean_tessman@hotmail.com>
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 #include "nsXULAtoms.h"
00042 #include "nsHTMLAtoms.h"
00043 #include "nsPopupSetFrame.h"
00044 #include "nsIMenuParent.h"
00045 #include "nsMenuFrame.h"
00046 #include "nsBoxFrame.h"
00047 #include "nsIContent.h"
00048 #include "prtypes.h"
00049 #include "nsIAtom.h"
00050 #include "nsPresContext.h"
00051 #include "nsStyleContext.h"
00052 #include "nsCSSRendering.h"
00053 #include "nsINameSpaceManager.h"
00054 #include "nsLayoutAtoms.h"
00055 #include "nsMenuPopupFrame.h"
00056 #include "nsMenuBarFrame.h"
00057 #include "nsIView.h"
00058 #include "nsIWidget.h"
00059 #include "nsIDocument.h"
00060 #include "nsIDOMNSDocument.h"
00061 #include "nsIDOMDocument.h"
00062 #include "nsIDOMXULDocument.h"
00063 #include "nsIDOMElement.h"
00064 #include "nsISupportsArray.h"
00065 #include "nsIDOMText.h"
00066 #include "nsBoxLayoutState.h"
00067 #include "nsIScrollableFrame.h"
00068 #include "nsCSSFrameConstructor.h"
00069 #include "nsGUIEvent.h"
00070 #include "nsIRootBox.h"
00071 #include "nsIFocusController.h"
00072 #include "nsIDocShellTreeItem.h"
00073 #include "nsIDocShell.h"
00074 #include "nsPIDOMWindow.h"
00075 #include "nsIInterfaceRequestorUtils.h"
00076 #include "nsIBaseWindow.h"
00077 
00078 #define NS_MENU_POPUP_LIST_INDEX   0
00079 
00080 nsPopupFrameList::nsPopupFrameList(nsIContent* aPopupContent, nsPopupFrameList* aNext)
00081 :mNextPopup(aNext), 
00082  mPopupFrame(nsnull),
00083  mPopupContent(aPopupContent),
00084  mElementContent(nsnull), 
00085  mCreateHandlerSucceeded(PR_FALSE),
00086  mIsOpen(PR_FALSE),
00087  mLastPref(-1,-1)
00088 {
00089 }
00090 
00091 nsPopupFrameList* nsPopupFrameList::GetEntry(nsIContent* aPopupContent) {
00092   if (aPopupContent == mPopupContent)
00093     return this;
00094 
00095   if (mNextPopup)
00096     return mNextPopup->GetEntry(aPopupContent);
00097 
00098   return nsnull;
00099 }
00100 
00101 nsPopupFrameList* nsPopupFrameList::GetEntryByFrame(nsIFrame* aPopupFrame) {
00102   if (aPopupFrame == mPopupFrame)
00103     return this;
00104 
00105   if (mNextPopup)
00106     return mNextPopup->GetEntryByFrame(aPopupFrame);
00107 
00108   return nsnull;
00109 }
00110 
00111 //
00112 // NS_NewPopupSetFrame
00113 //
00114 // Wrapper for creating a new menu popup container
00115 //
00116 nsresult
00117 NS_NewPopupSetFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame)
00118 {
00119   NS_PRECONDITION(aNewFrame, "null OUT ptr");
00120   if (nsnull == aNewFrame) {
00121     return NS_ERROR_NULL_POINTER;
00122   }
00123   nsPopupSetFrame* it = new (aPresShell) nsPopupSetFrame (aPresShell);
00124   if ( !it )
00125     return NS_ERROR_OUT_OF_MEMORY;
00126   *aNewFrame = it;
00127   return NS_OK;
00128 }
00129 
00130 NS_IMETHODIMP_(nsrefcnt) 
00131 nsPopupSetFrame::AddRef(void)
00132 {
00133   return NS_OK;
00134 }
00135 
00136 NS_IMETHODIMP_(nsrefcnt) 
00137 nsPopupSetFrame::Release(void)
00138 {
00139     return NS_OK;
00140 }
00141 
00142 //
00143 // QueryInterface
00144 //
00145 NS_INTERFACE_MAP_BEGIN(nsPopupSetFrame)
00146   NS_INTERFACE_MAP_ENTRY(nsIPopupSetFrame)
00147 NS_INTERFACE_MAP_END_INHERITING(nsBoxFrame)
00148 
00149 
00150 //
00151 // nsPopupSetFrame cntr
00152 //
00153 nsPopupSetFrame::nsPopupSetFrame(nsIPresShell* aShell):nsBoxFrame(aShell),
00154 mPresContext(nsnull)
00155 {
00156 
00157 } // cntr
00158 
00159 NS_IMETHODIMP
00160 nsPopupSetFrame::Init(nsPresContext*  aPresContext,
00161                      nsIContent*      aContent,
00162                      nsIFrame*        aParent,
00163                      nsStyleContext*  aContext,
00164                      nsIFrame*        aPrevInFlow)
00165 {
00166   mPresContext = aPresContext; // Don't addref it.  Our lifetime is shorter.
00167   nsresult  rv = nsBoxFrame::Init(aPresContext, aContent, aParent, aContext, aPrevInFlow);
00168 
00169   nsIRootBox *rootBox;
00170   nsresult res = CallQueryInterface(aParent->GetParent(), &rootBox);
00171   NS_ASSERTION(NS_SUCCEEDED(res), "grandparent should be root box");
00172   if (NS_SUCCEEDED(res)) {
00173     rootBox->SetPopupSetFrame(this);
00174   }
00175 
00176   return rv;
00177 }
00178 
00179 NS_IMETHODIMP
00180 nsPopupSetFrame::Destroy(nsPresContext* aPresContext)
00181 {
00182   // Remove our frame list.
00183   if (mPopupList) {
00184     // Try to hide any active popups
00185     if (nsMenuFrame::sDismissalListener) {
00186       nsIMenuParent *menuParent = nsMenuFrame::sDismissalListener->GetCurrentMenuParent();
00187       nsIFrame* frame;
00188       CallQueryInterface(menuParent, &frame);
00189       // Rollup popups, but only if they're ours
00190       if (frame && mPopupList->GetEntryByFrame(frame)) {
00191         nsMenuFrame::sDismissalListener->Rollup();
00192       }
00193     }
00194 
00195     // Actually remove each popup from the list as we go. This
00196     // keeps things consistent so reentering won't crash us
00197     while (mPopupList) {
00198       if (mPopupList->mPopupFrame) {
00199         mPopupList->mPopupFrame->Destroy(aPresContext);
00200       }
00201 
00202       nsPopupFrameList* temp = mPopupList;
00203       mPopupList = mPopupList->mNextPopup;
00204       delete temp;
00205     }
00206   }
00207 
00208   nsIRootBox *rootBox;
00209   nsresult res = CallQueryInterface(mParent->GetParent(), &rootBox);
00210   NS_ASSERTION(NS_SUCCEEDED(res), "grandparent should be root box");
00211   if (NS_SUCCEEDED(res)) {
00212     rootBox->SetPopupSetFrame(nsnull);
00213   }
00214 
00215   return nsBoxFrame::Destroy(aPresContext);
00216 }
00217 
00218 NS_IMETHODIMP
00219 nsPopupSetFrame::DoLayout(nsBoxLayoutState& aState)
00220 {
00221   // lay us out
00222   nsresult rv = nsBoxFrame::DoLayout(aState);
00223 
00224   // lay out all of our currently open popups.
00225   nsPopupFrameList* currEntry = mPopupList;
00226   while (currEntry) {
00227     nsIFrame* popupChild = currEntry->mPopupFrame;
00228     if (popupChild) {
00229       NS_ASSERTION(popupChild->IsBoxFrame(), "popupChild is not box!!");
00230 
00231       // then get its preferred size
00232       nsSize prefSize(0,0);
00233       nsSize minSize(0,0);
00234       nsSize maxSize(0,0);
00235 
00236       popupChild->GetPrefSize(aState, prefSize);
00237       popupChild->GetMinSize(aState, minSize);
00238       popupChild->GetMaxSize(aState, maxSize);
00239 
00240       BoundsCheck(minSize, prefSize, maxSize);
00241 
00242       // if the pref size changed then set bounds to be the pref size
00243       // and sync the view. Also set new pref size.
00244      // if (currEntry->mLastPref != prefSize) {
00245         popupChild->SetBounds(aState, nsRect(0,0,prefSize.width, prefSize.height));
00246         RepositionPopup(currEntry, aState);
00247         currEntry->mLastPref = prefSize;
00248      // }
00249 
00250       // is the new size too small? Make sure we handle scrollbars correctly
00251       nsIBox* child;
00252       popupChild->GetChildBox(&child);
00253 
00254       nsRect bounds(popupChild->GetRect());
00255 
00256       nsCOMPtr<nsIScrollableFrame> scrollframe = do_QueryInterface(child);
00257       if (scrollframe &&
00258           scrollframe->GetScrollbarStyles().mVertical == NS_STYLE_OVERFLOW_AUTO) {
00259         // if our pref height
00260         if (bounds.height < prefSize.height) {
00261           // layout the child
00262           popupChild->Layout(aState);
00263 
00264           nsMargin scrollbars = scrollframe->GetActualScrollbarSizes();
00265           if (bounds.width < prefSize.width + scrollbars.left + scrollbars.right)
00266           {
00267             bounds.width += scrollbars.left + scrollbars.right;
00268             //printf("Width=%d\n",width);
00269             popupChild->SetBounds(aState, bounds);
00270           }
00271         }
00272       }
00273     
00274       // layout the child
00275       popupChild->Layout(aState);
00276 
00277       // only size popup if open
00278       if (currEntry->mCreateHandlerSucceeded) {
00279         nsIView* view = popupChild->GetView();
00280         nsIViewManager* viewManager = view->GetViewManager();
00281         nsRect r(0, 0, bounds.width, bounds.height);
00282         viewManager->ResizeView(view, r);
00283         viewManager->SetViewVisibility(view, nsViewVisibility_kShow);
00284       }
00285     }
00286 
00287     currEntry = currEntry->mNextPopup;
00288   }
00289 
00290   SyncLayout(aState);
00291 
00292   return rv;
00293 }
00294 
00295 
00296 #ifdef DEBUG_LAYOUT
00297 NS_IMETHODIMP
00298 nsPopupSetFrame::SetDebug(nsBoxLayoutState& aState, PRBool aDebug)
00299 {
00300   // see if our state matches the given debug state
00301   PRBool debugSet = mState & NS_STATE_CURRENTLY_IN_DEBUG;
00302   PRBool debugChanged = (!aDebug && debugSet) || (aDebug && !debugSet);
00303 
00304   // if it doesn't then tell each child below us the new debug state
00305   if (debugChanged)
00306   {
00307     // XXXdwh fix later.  nobody uses this anymore anyway.
00308   }
00309 
00310   return NS_OK;
00311 }
00312 
00313 nsresult
00314 nsPopupSetFrame::SetDebug(nsBoxLayoutState& aState, nsIFrame* aList, PRBool aDebug)
00315 {
00316       if (!aList)
00317           return NS_OK;
00318 
00319       while (aList) {
00320         if (aList->IsBoxFrame())
00321           aList->SetDebug(aState, aDebug);
00322 
00323         aList = aList->GetNextSibling();
00324       }
00325 
00326       return NS_OK;
00327 }
00328 #endif
00329 
00330 
00331 void
00332 nsPopupSetFrame::RepositionPopup(nsPopupFrameList* aEntry, nsBoxLayoutState& aState)
00333 {
00334   // Sync up the view.
00335   if (aEntry && aEntry->mElementContent) {
00336     nsIFrame* frameToSyncTo = nsnull;
00337     nsPresContext* presContext = aState.PresContext();
00338     presContext->PresShell()->GetPrimaryFrameFor(aEntry->mElementContent,
00339                                                  &frameToSyncTo );
00340     ((nsMenuPopupFrame*)(aEntry->mPopupFrame))->SyncViewWithFrame(presContext, 
00341           aEntry->mPopupAnchor, aEntry->mPopupAlign, frameToSyncTo, aEntry->mXPos, aEntry->mYPos);
00342   }
00343 }
00344 
00345 NS_IMETHODIMP
00346 nsPopupSetFrame::ShowPopup(nsIContent* aElementContent, nsIContent* aPopupContent, 
00347                            PRInt32 aXPos, PRInt32 aYPos, 
00348                            const nsString& aPopupType, const nsString& anAnchorAlignment,
00349                            const nsString& aPopupAlignment)
00350 {
00351   NS_ASSERTION(aElementContent != aPopupContent, "self referential popup");
00352 
00353   if (!MayOpenPopup(this))
00354     return NS_OK;
00355 
00356   nsWeakFrame weakFrame(this);
00357   // First fire the popupshowing event.
00358   if (!OnCreate(aXPos, aYPos, aPopupContent) || !weakFrame.IsAlive())
00359     return NS_OK;
00360         
00361   // See if we already have an entry in our list.  We must create a new one on a miss.
00362   nsPopupFrameList* entry = nsnull;
00363   if (mPopupList)
00364     entry = mPopupList->GetEntry(aPopupContent);
00365   if (!entry) {
00366     entry = new nsPopupFrameList(aPopupContent, mPopupList);
00367     if (!entry)
00368       return NS_ERROR_OUT_OF_MEMORY;
00369     mPopupList = entry;
00370   }
00371 
00372   // Cache the element content we're supposed to sync to
00373   entry->mPopupType = aPopupType;
00374   entry->mElementContent = aElementContent;
00375   entry->mPopupAlign = aPopupAlignment;
00376   entry->mPopupAnchor = anAnchorAlignment;
00377   entry->mXPos = aXPos;
00378   entry->mYPos = aYPos;
00379 
00380   // If a frame exists already, go ahead and use it.
00381   mPresContext->PresShell()->GetPrimaryFrameFor(aPopupContent,
00382                                                 &entry->mPopupFrame);
00383 
00384 #ifdef DEBUG_PINK
00385   printf("X Pos: %d\n", mXPos);
00386   printf("Y Pos: %d\n", mYPos);
00387 #endif
00388 
00389   // Generate the popup.
00390   entry->mCreateHandlerSucceeded = PR_TRUE;
00391   entry->mIsOpen = PR_TRUE;
00392   // This may destroy or change entry->mPopupFrame or remove the entry from
00393   // mPopupList. |this| may also get deleted.
00394   MarkAsGenerated(aPopupContent);
00395 
00396   if (!weakFrame.IsAlive()) {
00397     return NS_OK;
00398   }
00399 
00400   nsPopupFrameList* newEntry =
00401     mPopupList ? mPopupList->GetEntry(aPopupContent) : nsnull;
00402   if (!newEntry || newEntry != entry) {
00403     NS_WARNING("The popup entry for aPopupContent has changed!");
00404     return NS_OK;
00405   }
00406 
00407   // determine if this menu is a context menu and flag it
00408   nsIMenuParent* childPopup = nsnull;
00409   if (entry->mPopupFrame)
00410     CallQueryInterface(entry->mPopupFrame, &childPopup);
00411   if ( childPopup && aPopupType.EqualsLiteral("context") )
00412     childPopup->SetIsContextMenu(PR_TRUE);
00413 
00414   // Now open the popup.
00415   OpenPopup(entry, PR_TRUE);
00416 
00417   if (!weakFrame.IsAlive()) {
00418     return NS_OK;
00419   }
00420 
00421   // Now fire the popupshown event.
00422   OnCreated(aXPos, aYPos, aPopupContent);
00423 
00424   return NS_OK;
00425 }
00426 
00427 NS_IMETHODIMP
00428 nsPopupSetFrame::HidePopup(nsIFrame* aPopup)
00429 {
00430   if (!mPopupList)
00431     return NS_OK; // No active popups
00432 
00433   nsPopupFrameList* entry = mPopupList->GetEntryByFrame(aPopup);
00434   if (!entry)
00435     return NS_OK;
00436 
00437   if (entry->mCreateHandlerSucceeded)
00438     ActivatePopup(entry, PR_FALSE);
00439 
00440   if (entry->mElementContent && entry->mPopupType.EqualsLiteral("context")) {
00441     // If we are a context menu, and if we are attached to a
00442     // menupopup, then hiding us should also hide the parent menu
00443     // popup.
00444     if (entry->mElementContent->Tag() == nsXULAtoms::menupopup) {
00445       nsIFrame* popupFrame = nsnull;
00446       mPresContext->PresShell()->GetPrimaryFrameFor(entry->mElementContent,
00447                                                     &popupFrame);
00448       if (popupFrame) {
00449         nsIMenuParent *menuParent;
00450         if (NS_SUCCEEDED(CallQueryInterface(popupFrame, &menuParent))) {
00451           menuParent->HideChain();
00452         }
00453       }
00454     }
00455   }
00456 
00457   return NS_OK;
00458 }
00459 
00460 NS_IMETHODIMP
00461 nsPopupSetFrame::DestroyPopup(nsIFrame* aPopup, PRBool aDestroyEntireChain)
00462 {
00463   if (!mPopupList)
00464     return NS_OK; // No active popups
00465 
00466   nsPopupFrameList* entry = mPopupList->GetEntryByFrame(aPopup);
00467 
00468   if (entry && entry->mCreateHandlerSucceeded) {    // ensure the popup was created before we try to destroy it
00469     nsWeakFrame weakFrame(this);
00470     OpenPopup(entry, PR_FALSE);
00471     nsCOMPtr<nsIContent> popupContent = entry->mPopupContent;
00472     if (weakFrame.IsAlive()) {
00473       if (aDestroyEntireChain && entry->mElementContent && entry->mPopupType.EqualsLiteral("context")) {
00474         // If we are a context menu, and if we are attached to a
00475         // menupopup, then destroying us should also dismiss the parent
00476         // menu popup.
00477         if (entry->mElementContent->Tag() == nsXULAtoms::menupopup) {
00478           nsIFrame* popupFrame = nsnull;
00479           mPresContext->PresShell()->GetPrimaryFrameFor(entry->mElementContent,
00480                                                         &popupFrame);
00481           if (popupFrame) {
00482             nsIMenuParent *menuParent;
00483             if (NS_SUCCEEDED(CallQueryInterface(popupFrame, &menuParent))) {
00484               menuParent->DismissChain();
00485             }
00486           }
00487         }
00488       }
00489   
00490       // clear things out for next time
00491       entry->mPopupType.Truncate();
00492       entry->mCreateHandlerSucceeded = PR_FALSE;
00493       entry->mElementContent = nsnull;
00494       entry->mXPos = entry->mYPos = 0;
00495       entry->mLastPref.width = -1;
00496       entry->mLastPref.height = -1;
00497     }
00498 
00499     // ungenerate the popup.
00500     popupContent->UnsetAttr(kNameSpaceID_None, nsXULAtoms::menugenerated, PR_TRUE);
00501   }
00502 
00503   return NS_OK;
00504 }
00505 
00506 void
00507 nsPopupSetFrame::MarkAsGenerated(nsIContent* aPopupContent)
00508 {
00509   // Set our attribute, but only if we aren't already generated.
00510   // Retrieve the menugenerated attribute.
00511   nsAutoString value;
00512   aPopupContent->GetAttr(kNameSpaceID_None, nsXULAtoms::menugenerated, 
00513                          value);
00514   if (!value.EqualsLiteral("true")) {
00515     // Generate this element.
00516     aPopupContent->SetAttr(kNameSpaceID_None, nsXULAtoms::menugenerated, NS_LITERAL_STRING("true"),
00517                            PR_TRUE);
00518   }
00519 }
00520 
00521 void
00522 nsPopupSetFrame::OpenPopup(nsPopupFrameList* aEntry, PRBool aActivateFlag)
00523 {
00524   nsWeakFrame weakFrame(this);
00525   nsIFrame* activeChild = aEntry->mPopupFrame;
00526   nsWeakFrame weakPopupFrame(activeChild);
00527   nsRefPtr<nsPresContext> presContext = GetPresContext();
00528   nsCOMPtr<nsIContent> popupContent = aEntry->mPopupContent;
00529   PRBool createHandlerSucceeded = aEntry->mCreateHandlerSucceeded;
00530   nsAutoString popupType = aEntry->mPopupType;
00531   if (aActivateFlag) {
00532     ActivatePopup(aEntry, PR_TRUE);
00533 
00534     // register the rollup listeners, etc, but not if we're a tooltip
00535     if (!popupType.EqualsLiteral("tooltip")) {
00536       nsIFrame* activeChild = aEntry->mPopupFrame;
00537       nsIMenuParent* childPopup = nsnull;
00538       if (weakPopupFrame.IsAlive())
00539         CallQueryInterface(activeChild, &childPopup);
00540 
00541       // Tooltips don't get keyboard navigation
00542       if (childPopup && !nsMenuFrame::sDismissalListener) {
00543         // First check and make sure this popup wants keyboard navigation
00544         nsAutoString property;    
00545         popupContent->GetAttr(kNameSpaceID_None, nsXULAtoms::ignorekeys, property);
00546         if (!property.EqualsLiteral("true"))
00547           childPopup->InstallKeyboardNavigator();
00548       }
00549 
00550       UpdateDismissalListener(childPopup);
00551     }
00552   }
00553   else {
00554     if (createHandlerSucceeded && !OnDestroy(popupContent))
00555       return;
00556 
00557     // Unregister, but not if we're a tooltip
00558     if (!popupType.EqualsLiteral("tooltip")) {
00559       if (nsMenuFrame::sDismissalListener)
00560         nsMenuFrame::sDismissalListener->Unregister();
00561     }
00562     
00563     // Remove any keyboard navigators
00564     nsIMenuParent* childPopup = nsnull;
00565     if (weakPopupFrame.IsAlive())
00566       CallQueryInterface(activeChild, &childPopup);
00567     if (childPopup)
00568       childPopup->RemoveKeyboardNavigator();
00569 
00570     if (weakPopupFrame.IsAlive())
00571       ActivatePopup(aEntry, PR_FALSE);
00572 
00573     OnDestroyed(presContext, popupContent);
00574   }
00575 
00576   if (weakFrame.IsAlive()) {
00577     nsBoxLayoutState state(mPresContext);
00578     MarkDirtyChildren(state); // Mark ourselves dirty.
00579   }
00580 }
00581 
00582 void
00583 nsPopupSetFrame::ActivatePopup(nsPopupFrameList* aEntry, PRBool aActivateFlag)
00584 {
00585   if (aEntry->mPopupContent) {
00586     // When we sync the popup view with the frame, we'll show the popup if |menutobedisplayed|
00587     // is set by setting the |menuactive| attribute. This used to trip css into showing the menu
00588     // but now we do it ourselves. 
00589     if (aActivateFlag)
00590       // XXXben hook in |width| and |height| usage here? 
00591       aEntry->mPopupContent->SetAttr(kNameSpaceID_None, nsXULAtoms::menutobedisplayed, NS_LITERAL_STRING("true"), PR_TRUE);
00592     else {
00593       nsWeakFrame weakFrame(this);
00594       nsWeakFrame weakActiveChild(aEntry->mPopupFrame);
00595       nsCOMPtr<nsIContent> content = aEntry->mPopupContent;
00596       content->UnsetAttr(kNameSpaceID_None, nsXULAtoms::menuactive, PR_TRUE);
00597       content->UnsetAttr(kNameSpaceID_None, nsXULAtoms::menutobedisplayed, PR_TRUE);
00598 
00599       // get rid of the reflows we just created. If we leave them hanging around, we
00600       // can get into trouble if a dialog with a modal event loop comes along and
00601       // processes the reflows before we get to call DestroyChain(). Processing the
00602       // reflow will cause the popup to show itself again. (bug 71219)
00603       nsIDocument* doc = content->GetDocument();
00604       if (doc)
00605         doc->FlushPendingNotifications(Flush_OnlyReflow);
00606          
00607       // make sure we hide the popup. We can't assume that we'll have a view
00608       // since we could be cleaning up after someone that didn't correctly 
00609       // destroy the popup.
00610       nsIFrame* activeChild = aEntry->mPopupFrame;
00611       if (weakFrame.IsAlive() && weakActiveChild.IsAlive()) {
00612         nsIView* view = weakActiveChild.GetFrame()->GetView();
00613         NS_ASSERTION(view, "View is gone, looks like someone forgot to roll up the popup!");
00614         if (view) {
00615           nsIViewManager* viewManager = view->GetViewManager();
00616           viewManager->SetViewVisibility(view, nsViewVisibility_kHide);
00617           nsRect r(0, 0, 0, 0);
00618           viewManager->ResizeView(view, r);
00619           if (aEntry->mIsOpen) {
00620             aEntry->mIsOpen = PR_FALSE;
00621             FireDOMEventSynch(NS_LITERAL_STRING("DOMMenuInactive"), content);
00622           }
00623         }
00624       }
00625     }
00626   }
00627 }
00628 
00629 PRBool
00630 nsPopupSetFrame::OnCreate(PRInt32 aX, PRInt32 aY, nsIContent* aPopupContent)
00631 {
00632   nsEventStatus status = nsEventStatus_eIgnore;
00633   nsMouseEvent event(PR_TRUE, NS_XUL_POPUP_SHOWING, nsnull,
00634                      nsMouseEvent::eReal);
00635   event.point.x = aX;
00636   event.point.y = aY;
00637 
00638   if (aPopupContent) {
00639     nsCOMPtr<nsIContent> kungFuDeathGrip(aPopupContent);
00640     nsCOMPtr<nsIPresShell> shell = mPresContext->GetPresShell();
00641     if (shell) {
00642       nsresult rv = shell->HandleDOMEventWithTarget(aPopupContent, &event,
00643                                                     &status);
00644       if ( NS_FAILED(rv) || status == nsEventStatus_eConsumeNoDefault )
00645         return PR_FALSE;
00646     }
00647 
00648     // The menu is going to show, and the create handler has executed.
00649     // We should now walk all of our menu item children, checking to see if any
00650     // of them has a command attribute.  If so, then several attributes must
00651     // potentially be updated.
00652  
00653     nsCOMPtr<nsIDOMDocument> domDoc(do_QueryInterface(aPopupContent->GetDocument()));
00654 
00655     PRUint32 count = aPopupContent->GetChildCount();
00656     for (PRUint32 i = 0; i < count; i++) {
00657       nsCOMPtr<nsIContent> grandChild = aPopupContent->GetChildAt(i);
00658 
00659       if (grandChild->Tag() == nsXULAtoms::menuitem) {
00660         // See if we have a command attribute.
00661         nsAutoString command;
00662         grandChild->GetAttr(kNameSpaceID_None, nsXULAtoms::command, command);
00663         if (!command.IsEmpty()) {
00664           // We do! Look it up in our document
00665           nsCOMPtr<nsIDOMElement> commandElt;
00666           domDoc->GetElementById(command, getter_AddRefs(commandElt));
00667           nsCOMPtr<nsIContent> commandContent(do_QueryInterface(commandElt));
00668           if ( commandContent ) {
00669             nsAutoString commandDisabled, menuDisabled;
00670             commandContent->GetAttr(kNameSpaceID_None, nsHTMLAtoms::disabled, commandDisabled);
00671             grandChild->GetAttr(kNameSpaceID_None, nsHTMLAtoms::disabled, menuDisabled);
00672             if (!commandDisabled.Equals(menuDisabled)) {
00673               // The menu's disabled state needs to be updated to match the command.
00674               if (commandDisabled.IsEmpty()) 
00675                 grandChild->UnsetAttr(kNameSpaceID_None, nsHTMLAtoms::disabled, PR_TRUE);
00676               else grandChild->SetAttr(kNameSpaceID_None, nsHTMLAtoms::disabled, commandDisabled, PR_TRUE);
00677             }
00678 
00679             nsAutoString commandValue, menuValue;
00680             commandContent->GetAttr(kNameSpaceID_None, nsXULAtoms::label, commandValue);
00681             grandChild->GetAttr(kNameSpaceID_None, nsXULAtoms::label, menuValue);
00682             if (!commandValue.Equals(menuValue)) {
00683               // The menu's value state needs to be updated to match the command.
00684               // Note that (unlike the disabled state) if the command has *no* value, we
00685               // assume the menu is supplying its own.
00686               if (!commandValue.IsEmpty()) 
00687                 grandChild->SetAttr(kNameSpaceID_None, nsXULAtoms::label, commandValue, PR_TRUE);
00688             }
00689 
00690             // The menu's accesskey needs to be updated to match the command.
00691             // If the command has no accesskey, assume the menu is supplying its own.
00692             commandContent->GetAttr(kNameSpaceID_None, nsHTMLAtoms::accesskey, commandValue);
00693             grandChild->GetAttr(kNameSpaceID_None, nsHTMLAtoms::accesskey, menuValue);
00694             if (!commandValue.Equals(menuValue)) {
00695               if (!commandValue.IsEmpty()) 
00696                 grandChild->SetAttr(kNameSpaceID_None, nsHTMLAtoms::accesskey, commandValue, PR_TRUE);
00697             }
00698           }
00699         }
00700       }
00701     }
00702   }
00703 
00704   return PR_TRUE;
00705 }
00706 
00707 PRBool
00708 nsPopupSetFrame::OnCreated(PRInt32 aX, PRInt32 aY, nsIContent* aPopupContent)
00709 {
00710   nsEventStatus status = nsEventStatus_eIgnore;
00711   nsMouseEvent event(PR_TRUE, NS_XUL_POPUP_SHOWN, nsnull,
00712                      nsMouseEvent::eReal);
00713   event.point.x = aX;
00714   event.point.y = aY;
00715 
00716   if (aPopupContent) {
00717     nsCOMPtr<nsIPresShell> shell = mPresContext->GetPresShell();
00718     if (shell) {
00719       nsresult rv = shell->HandleDOMEventWithTarget(aPopupContent, &event,
00720                                                     &status);
00721       if ( NS_FAILED(rv) || status == nsEventStatus_eConsumeNoDefault )
00722         return PR_FALSE;
00723     }
00724   }
00725 
00726   return PR_TRUE;
00727 }
00728 
00729 PRBool
00730 nsPopupSetFrame::OnDestroy(nsIContent* aPopupContent)
00731 {
00732   nsEventStatus status = nsEventStatus_eIgnore;
00733   nsMouseEvent event(PR_TRUE, NS_XUL_POPUP_HIDING, nsnull,
00734                      nsMouseEvent::eReal);
00735 
00736   if (aPopupContent) {
00737     nsCOMPtr<nsIPresShell> shell = mPresContext->GetPresShell();
00738     if (shell) {
00739       nsresult rv = shell->HandleDOMEventWithTarget(aPopupContent, &event,
00740                                                     &status);
00741       if ( NS_FAILED(rv) || status == nsEventStatus_eConsumeNoDefault )
00742         return PR_FALSE;
00743     }
00744   }
00745   return PR_TRUE;
00746 }
00747 
00748 PRBool
00749 nsPopupSetFrame::OnDestroyed(nsPresContext* aPresContext,
00750                              nsIContent* aPopupContent)
00751 {
00752   nsEventStatus status = nsEventStatus_eIgnore;
00753   nsMouseEvent event(PR_TRUE, NS_XUL_POPUP_HIDDEN, nsnull,
00754                      nsMouseEvent::eReal);
00755 
00756   if (aPopupContent && aPresContext) {
00757     nsCOMPtr<nsIPresShell> shell = aPresContext->GetPresShell();
00758     if (shell) {
00759       nsresult rv = shell->HandleDOMEventWithTarget(aPopupContent, &event,
00760                                                     &status);
00761       if ( NS_FAILED(rv) || status == nsEventStatus_eConsumeNoDefault )
00762         return PR_FALSE;
00763     }
00764   }
00765   return PR_TRUE;
00766 }
00767 
00768 void
00769 nsPopupSetFrame::UpdateDismissalListener(nsIMenuParent* aMenuParent)
00770 {
00771   if (!nsMenuFrame::sDismissalListener) {
00772     if (!aMenuParent)
00773        return;
00774     // Create the listener and attach it to the outermost window.
00775     aMenuParent->CreateDismissalListener();
00776   }
00777   
00778   // Make sure the menu dismissal listener knows what the current
00779   // innermost menu popup frame is.
00780   nsMenuFrame::sDismissalListener->SetCurrentMenuParent(aMenuParent);
00781 }
00782 
00783 NS_IMETHODIMP
00784 nsPopupSetFrame::RemovePopupFrame(nsIFrame* aPopup)
00785 {
00786   // This was called by the Destroy() method of the popup, so all we have to do is
00787   // get the popup out of our list, so we don't reflow it later.
00788   nsPopupFrameList* currEntry = mPopupList;
00789   nsPopupFrameList* temp = nsnull;
00790   while (currEntry) {
00791     if (currEntry->mPopupFrame == aPopup) {
00792       // Remove this entry.
00793       if (temp)
00794         temp->mNextPopup = currEntry->mNextPopup;
00795       else
00796         mPopupList = currEntry->mNextPopup;
00797       
00798       // Destroy the frame.
00799       currEntry->mPopupFrame->Destroy(mPresContext);
00800 
00801       // Delete the entry.
00802       currEntry->mNextPopup = nsnull;
00803       delete currEntry;
00804 
00805       // Break out of the loop.
00806       break;
00807     }
00808 
00809     temp = currEntry;
00810     currEntry = currEntry->mNextPopup;
00811   }
00812 
00813   return NS_OK;
00814 }
00815 
00816 NS_IMETHODIMP
00817 nsPopupSetFrame::AddPopupFrame(nsIFrame* aPopup)
00818 {
00819   // The entry should already exist, but might not (if someone decided to make their
00820   // popup visible straightaway, e.g., the autocomplete widget).
00821 
00822   // First look for an entry by content.
00823   nsIContent* content = aPopup->GetContent();
00824   nsPopupFrameList* entry = nsnull;
00825   if (mPopupList)
00826     entry = mPopupList->GetEntry(content);
00827   if (!entry) {
00828     entry = new nsPopupFrameList(content, mPopupList);
00829     if (!entry)
00830       return NS_ERROR_OUT_OF_MEMORY;
00831     mPopupList = entry;
00832   }
00833   
00834   // Set the frame connection.
00835   entry->mPopupFrame = aPopup;
00836   
00837   // Now return.  The remaining entry values will be filled in if/when showPopup is
00838   // called for this popup.
00839   return NS_OK;
00840 }
00841 
00842 //static
00843 PRBool
00844 nsPopupSetFrame::MayOpenPopup(nsIFrame* aFrame)
00845 {
00846   nsCOMPtr<nsISupports> cont = aFrame->GetPresContext()->GetContainer();
00847   nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(cont);
00848   if (!dsti)
00849     return PR_FALSE;
00850 
00851   // chrome shells can always open popups
00852   PRInt32 type = -1;
00853   if (NS_SUCCEEDED(dsti->GetItemType(&type)) && type == nsIDocShellTreeItem::typeChrome)
00854     return PR_TRUE;
00855 
00856   nsCOMPtr<nsIDocShell> shell = do_QueryInterface(dsti);
00857   if (!shell)
00858     return PR_FALSE;
00859 
00860   nsCOMPtr<nsPIDOMWindow> win = do_GetInterface(shell);
00861   if (!win)
00862     return PR_FALSE;
00863 
00864   // only allow popups in active windows
00865   PRBool active;
00866   nsIFocusController* focusController = win->GetRootFocusController();
00867   focusController->GetActive(&active);
00868   if (!active)
00869     return PR_FALSE;
00870 
00871   nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(shell);
00872   if (!baseWin)
00873     return PR_FALSE;
00874 
00875   // only allow popups in visible frames
00876   PRBool visible;
00877   baseWin->GetVisibility(&visible);
00878   return visible;
00879 }
00880