Back to index

lightning-sunbird  0.9+nobinonly
nsMenuPopupFrame.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* vim: set ts=2 sw=2 et tw=78: */
00003 /* ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is mozilla.org code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 1998
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Original Author: David W. Hyatt (hyatt@netscape.com)
00025  *   Mike Pinkerton (pinkerton@netscape.com)
00026  *   Dean Tessman <dean_tessman@hotmail.com>
00027  *   Ben Goodger <ben@netscape.com>
00028  *
00029  * Alternatively, the contents of this file may be used under the terms of
00030  * either of the GNU General Public License Version 2 or later (the "GPL"),
00031  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00032  * in which case the provisions of the GPL or the LGPL are applicable instead
00033  * of those above. If you wish to allow use of your version of this file only
00034  * under the terms of either the GPL or the LGPL, and not to allow others to
00035  * use your version of this file under the terms of the MPL, indicate your
00036  * decision by deleting the provisions above and replace them with the notice
00037  * and other provisions required by the GPL or the LGPL. If you do not delete
00038  * the provisions above, a recipient may use your version of this file under
00039  * the terms of any one of the MPL, the GPL or the LGPL.
00040  *
00041  * ***** END LICENSE BLOCK ***** */
00042 
00043 
00044 #include "nsMenuPopupFrame.h"
00045 #include "nsXULAtoms.h"
00046 #include "nsHTMLAtoms.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 "nsIViewManager.h"
00055 #include "nsWidgetsCID.h"
00056 #include "nsMenuFrame.h"
00057 #include "nsIPopupSetFrame.h"
00058 #include "nsIDOMWindowInternal.h"
00059 #include "nsIDOMScreen.h"
00060 #include "nsIScriptGlobalObject.h"
00061 #include "nsIPresShell.h"
00062 #include "nsFrameManager.h"
00063 #include "nsIDocument.h"
00064 #include "nsIDeviceContext.h"
00065 #include "nsRect.h"
00066 #include "nsIDOMXULDocument.h"
00067 #include "nsILookAndFeel.h"
00068 #include "nsIComponentManager.h"
00069 #include "nsBoxLayoutState.h"
00070 #include "nsIScrollableView.h"
00071 #include "nsIScrollableFrame.h"
00072 #include "nsGUIEvent.h"
00073 #include "nsIRootBox.h"
00074 #include "nsIDocShellTreeItem.h"
00075 #include "nsITimerInternal.h"
00076 #include "nsReadableUtils.h"
00077 #include "nsUnicharUtils.h"
00078 #include "nsCSSFrameConstructor.h"
00079 #include "nsIBoxLayout.h"
00080 #include "nsIEventQueueService.h"
00081 #include "nsIServiceManager.h"
00082 #include "nsIReflowCallback.h"
00083 #ifdef XP_WIN
00084 #include "nsISound.h"
00085 #endif
00086 
00087 static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID);
00088 
00089 const PRInt32 kMaxZ = 0x7fffffff; //XXX: Shouldn't there be a define somewhere for MaxInt for PRInt32
00090 
00091 
00092 static nsIPopupSetFrame*
00093 GetPopupSetFrame(nsPresContext* aPresContext)
00094 {
00095   nsIFrame* rootFrame =
00096     aPresContext->PresShell()->FrameManager()->GetRootFrame();
00097   if (!rootFrame)
00098     return nsnull;
00099 
00100   if (rootFrame)
00101     rootFrame = rootFrame->GetFirstChild(nsnull);
00102  
00103   nsCOMPtr<nsIRootBox> rootBox(do_QueryInterface(rootFrame));
00104   if (!rootBox)
00105     return nsnull;
00106 
00107   nsIFrame* popupSetFrame;
00108   rootBox->GetPopupSetFrame(&popupSetFrame);
00109   if (!popupSetFrame)
00110     return nsnull;
00111 
00112   nsIPopupSetFrame* popupSet = nsnull;
00113   CallQueryInterface(popupSetFrame, &popupSet);
00114   return popupSet;
00115 }
00116 
00117 
00118 // NS_NewMenuPopupFrame
00119 //
00120 // Wrapper for creating a new menu popup container
00121 //
00122 nsresult
00123 NS_NewMenuPopupFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame)
00124 {
00125   NS_PRECONDITION(aNewFrame, "null OUT ptr");
00126   if (nsnull == aNewFrame) {
00127     return NS_ERROR_NULL_POINTER;
00128   }
00129   nsMenuPopupFrame* it = new (aPresShell) nsMenuPopupFrame (aPresShell);
00130   if ( !it )
00131     return NS_ERROR_OUT_OF_MEMORY;
00132   *aNewFrame = it;
00133   return NS_OK;
00134 }
00135 
00136 NS_IMETHODIMP_(nsrefcnt) 
00137 nsMenuPopupFrame::AddRef(void)
00138 {
00139   return NS_OK;
00140 }
00141 
00142 NS_IMETHODIMP_(nsrefcnt) 
00143 nsMenuPopupFrame::Release(void)
00144 {
00145     return NS_OK;
00146 }
00147 
00148 
00149 //
00150 // QueryInterface
00151 //
00152 NS_INTERFACE_MAP_BEGIN(nsMenuPopupFrame)
00153   NS_INTERFACE_MAP_ENTRY(nsIMenuParent)
00154 NS_INTERFACE_MAP_END_INHERITING(nsBoxFrame)
00155 
00156 
00157 //
00158 // nsMenuPopupFrame ctor
00159 //
00160 nsMenuPopupFrame::nsMenuPopupFrame(nsIPresShell* aShell)
00161   :nsBoxFrame(aShell), mCurrentMenu(nsnull), mTimerMenu(nsnull), mCloseTimer(nsnull),
00162     mMenuCanOverlapOSBar(PR_FALSE), mShouldAutoPosition(PR_TRUE), mShouldRollup(PR_TRUE),
00163     mInContentShell(PR_TRUE)
00164 {
00165   SetIsContextMenu(PR_FALSE);   // we're not a context menu by default
00166 } // ctor
00167 
00168 
00169 NS_IMETHODIMP
00170 nsMenuPopupFrame::Init(nsPresContext*  aPresContext,
00171                        nsIContent*      aContent,
00172                        nsIFrame*        aParent,
00173                        nsStyleContext*  aContext,
00174                        nsIFrame*        aPrevInFlow)
00175 {
00176   nsresult rv = nsBoxFrame::Init(aPresContext, aContent, aParent, aContext, aPrevInFlow);
00177   NS_ENSURE_SUCCESS(rv, rv);
00178 
00179   // Set up a mediator which can be used for callbacks on this frame.
00180   mTimerMediator = new nsMenuPopupTimerMediator(this);
00181   if (NS_UNLIKELY(!mTimerMediator))
00182     return NS_ERROR_OUT_OF_MEMORY;
00183 
00184   // lookup if we're allowed to overlap the OS bar (menubar/taskbar) from the
00185   // look&feel object
00186   PRBool tempBool;
00187   aPresContext->LookAndFeel()->
00188     GetMetric(nsILookAndFeel::eMetric_MenusCanOverlapOSBar, tempBool);
00189   mMenuCanOverlapOSBar = tempBool;
00190   
00191   // XXX Hack
00192   mPresContext = aPresContext;
00193 
00194   rv = CreateViewForFrame(aPresContext, this, aContext, PR_TRUE);
00195   NS_ENSURE_SUCCESS(rv, rv);
00196 
00197   // Now that we've made a view, remove it and insert it at the correct
00198   // position in the view hierarchy (as the root view).  We do this so that we
00199   // can draw the menus outside the confines of the window.
00200   nsIView* ourView = GetView();
00201   nsIViewManager* viewManager = ourView->GetViewManager();
00202 
00203   // Remove the view from its old position.
00204   viewManager->RemoveChild(ourView);
00205 
00206   // Reinsert ourselves as the root view with a maximum z-index.
00207   nsIView* rootView;
00208   viewManager->GetRootView(rootView);
00209   viewManager->SetViewZIndex(ourView, PR_FALSE, kMaxZ);
00210   viewManager->InsertChild(rootView, ourView, nsnull, PR_TRUE);
00211 
00212   // XXX Hack. The menu's view should float above all other views,
00213   // so we use the nsIView::SetFloating() to tell the view manager
00214   // about that constraint.
00215   viewManager->SetViewFloating(ourView, PR_TRUE);
00216 
00217   // XXX Hack. Change our transparency to be non-transparent
00218   // until the bug related to update of transparency on show/hide
00219   // is fixed.
00220   viewManager->SetViewContentTransparency(ourView, PR_FALSE);
00221 
00222   nsCOMPtr<nsISupports> cont = aPresContext->GetContainer();
00223   nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(cont);
00224   PRInt32 type = -1;
00225   if (dsti && NS_SUCCEEDED(dsti->GetItemType(&type)) &&
00226       type == nsIDocShellTreeItem::typeChrome)
00227     mInContentShell = PR_FALSE;
00228 
00229   // Create a widget for ourselves.
00230   nsWidgetInitData widgetData;
00231   widgetData.mWindowType = eWindowType_popup;
00232   widgetData.mBorderStyle = eBorderStyle_default;
00233   widgetData.clipSiblings = PR_TRUE;
00234 
00235   PRBool isCanvas;
00236   const nsStyleBackground* bg;
00237   PRBool hasBG =
00238     nsCSSRendering::FindBackground(aPresContext, this, &bg, &isCanvas);
00239   PRBool viewHasTransparentContent = hasBG &&
00240     (bg->mBackgroundFlags & NS_STYLE_BG_COLOR_TRANSPARENT) &&
00241     !GetStyleDisplay()->mAppearance && !mInContentShell;
00242 
00243   nsIContent* parentContent = aContent->GetParent();
00244   nsIAtom *tag = nsnull;
00245   if (parentContent)
00246     tag = parentContent->Tag();
00247   widgetData.mDropShadow = !(viewHasTransparentContent || tag == nsXULAtoms::menulist);
00248   
00249   // XXX make sure we are hidden (shouldn't this be done automatically?)
00250   viewManager->SetViewVisibility(ourView, nsViewVisibility_kHide);
00251 #if defined(XP_MACOSX) 
00252   static NS_DEFINE_IID(kCPopupCID,  NS_POPUP_CID);
00253   ourView->CreateWidget(kCPopupCID, &widgetData, nsnull, PR_TRUE, PR_TRUE, 
00254                         eContentTypeUI);
00255 #else
00256   static NS_DEFINE_IID(kCChildCID,  NS_CHILD_CID);
00257   ourView->CreateWidget(kCChildCID, &widgetData, nsnull, PR_TRUE, PR_TRUE);
00258 #endif   
00259 
00260   ourView->GetWidget()->SetWindowTranslucency(viewHasTransparentContent);
00261   MoveToAttributePosition();
00262 
00263   return rv;
00264 }
00265 
00266 NS_IMETHODIMP
00267 nsMenuPopupFrame::MarkStyleChange(nsBoxLayoutState& aState)
00268 {
00269   NeedsRecalc();
00270 
00271   if (HasStyleChange())
00272     return NS_OK;
00273 
00274   // iterate through all children making them dirty
00275   MarkChildrenStyleChange();
00276 
00277   nsCOMPtr<nsIBoxLayout> layout;
00278   GetLayoutManager(getter_AddRefs(layout));
00279   if (layout)
00280     layout->BecameDirty(this, aState);
00281 
00282   nsIFrame* parent = GetParent();
00283   nsIMenuFrame* menuFrame;
00284   CallQueryInterface(parent, &menuFrame);
00285 
00286   if (menuFrame)
00287      return parent->RelayoutDirtyChild(aState, this);
00288   else {
00289     nsIPopupSetFrame* popupSet = GetPopupSetFrame(mPresContext);
00290     NS_ASSERTION(popupSet, "popup frame created without a popup set or menu");
00291     if (popupSet) {
00292       nsIFrame *frame;
00293       CallQueryInterface(popupSet, &frame);
00294       if (frame->IsBoxFrame()) {
00295         nsBoxLayoutState state(mPresContext);
00296         frame->MarkDirtyChildren(state); // Mark the popupset as dirty.
00297       }
00298       else {
00299         return frame->GetParent()->ReflowDirtyChild(aState.PresShell(), frame);
00300       }
00301     }
00302   }
00303   return NS_OK;
00304 }
00305 
00306 NS_IMETHODIMP
00307 nsMenuPopupFrame::MarkDirty(nsBoxLayoutState& aState)
00308 {
00309   NeedsRecalc();
00310 
00311   // only reflow if we aren't already dirty.
00312   if (GetStateBits() & NS_FRAME_IS_DIRTY) {      
00313 #ifdef DEBUG_COELESCED
00314     Coelesced();
00315 #endif
00316     return NS_OK;
00317   }
00318 
00319   AddStateBits(NS_FRAME_IS_DIRTY);
00320 
00321   nsCOMPtr<nsIBoxLayout> layout;
00322   GetLayoutManager(getter_AddRefs(layout));
00323   if (layout)
00324     layout->BecameDirty(this, aState);
00325 
00326   if (GetStateBits() & NS_FRAME_HAS_DIRTY_CHILDREN) {   
00327 #ifdef DEBUG_COELESCED
00328     Coelesced();
00329 #endif
00330     return NS_OK;
00331   }
00332 
00333   nsIFrame* parent = GetParent();
00334   nsIMenuFrame* menuFrame;
00335   CallQueryInterface(parent, &menuFrame);
00336 
00337   if (menuFrame)
00338      return parent->RelayoutDirtyChild(aState, this);
00339   else {
00340     nsIPopupSetFrame* popupSet = GetPopupSetFrame(mPresContext);
00341     NS_ASSERTION(popupSet, "popup frame created without a popup set or menu");
00342     if (popupSet) {
00343       nsIFrame *frame;
00344       CallQueryInterface(popupSet, &frame);
00345       if (frame->IsBoxFrame()) {
00346         nsBoxLayoutState state(mPresContext);
00347         frame->MarkDirtyChildren(state); // Mark the popupset as dirty.
00348       }
00349       else {
00350         return frame->GetParent()->ReflowDirtyChild(aState.PresShell(), frame);
00351       }
00352     }
00353   }
00354 
00355   return NS_OK;
00356 }
00357 
00358 NS_IMETHODIMP
00359 nsMenuPopupFrame::RelayoutDirtyChild(nsBoxLayoutState& aState, nsIBox* aChild)
00360 {
00361   if (aChild != nsnull) {
00362     nsCOMPtr<nsIBoxLayout> layout;
00363     GetLayoutManager(getter_AddRefs(layout));
00364     if (layout)
00365       layout->ChildBecameDirty(this, aState, aChild);
00366   }
00367 
00368   // if we are not dirty mark ourselves dirty and tell our parent we are dirty too.
00369   if (!(GetStateBits() & NS_FRAME_HAS_DIRTY_CHILDREN)) {      
00370     // Mark yourself as dirty and needing to be recalculated
00371     AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
00372     NeedsRecalc();
00373 
00374     nsIFrame* parent = GetParent();
00375     nsIMenuFrame* menuFrame;
00376     CallQueryInterface(parent, &menuFrame);
00377 
00378     if (menuFrame)
00379       return parent->RelayoutDirtyChild(aState, this);
00380     else {
00381       nsIPopupSetFrame* popupSet = GetPopupSetFrame(mPresContext);
00382       NS_ASSERTION(popupSet, "popup frame created without a popup set or menu");
00383       nsIFrame *frame = nsnull;
00384       if (popupSet)
00385         CallQueryInterface(popupSet, &frame);
00386       if (frame && frame->IsBoxFrame()) {
00387         nsBoxLayoutState state(mPresContext);
00388         frame->MarkDirtyChildren(state); // Mark the popupset as dirty.
00389       }
00390       else 
00391         return nsBox::RelayoutDirtyChild(aState, aChild);
00392     }
00393   }
00394 
00395   return NS_OK;
00396 }
00397 
00398 void
00399 nsMenuPopupFrame::GetLayoutFlags(PRUint32& aFlags)
00400 {
00401   aFlags = NS_FRAME_NO_SIZE_VIEW | NS_FRAME_NO_MOVE_VIEW | NS_FRAME_NO_VISIBILITY;
00402 }
00403 
00405 // GetViewOffset
00406 //   Retrieves the offset of the given view with the root view, in the 
00407 //   coordinate system of the root view. 
00408 void
00409 nsMenuPopupFrame::GetViewOffset(nsIView* aView, nsPoint& aPoint)
00410 {
00411   // Notes:
00412   //   1) The root view is the client area of the toplevel window that
00413   //      this popup is anchored to. 
00414   //   2) Each menupopup is a child of the root view (see 
00415   //      nsMenuPopupFrame::Init())
00416   //   3) The coordinates that we return are the total distance between 
00417   //      the top left of the start view and the origin of the root view.
00418   
00419   // Keep track of the root view so that we know to stop there
00420   nsIView* rootView;
00421   aView->GetViewManager()->GetRootView(rootView);
00422   aPoint = aView->GetOffsetTo(rootView);
00423 }
00424 
00426 // GetRootViewForPopup
00427 //   Retrieves the view for the popup widget that contains the given frame. 
00428 //   If the given frame is not contained by a popup widget, return the
00429 //   root view.  This is the root view of the pres context's
00430 //   viewmanager if aStopAtViewManagerRoot is true; otherwise it's the
00431 //   root view of the root viewmanager.
00432 void
00433 nsMenuPopupFrame::GetRootViewForPopup(nsIFrame* aStartFrame,
00434                                       PRBool    aStopAtViewManagerRoot,
00435                                       nsIView** aResult)
00436 {
00437   *aResult = nsnull;
00438 
00439   nsIView* view = aStartFrame->GetClosestView();
00440   NS_ASSERTION(view, "frame must have a closest view!");
00441   if (view) {
00442     nsIView* rootView = nsnull;
00443     if (aStopAtViewManagerRoot) {
00444       view->GetViewManager()->GetRootView(rootView);
00445     }
00446     
00447     while (view) {
00448       // Walk up the view hierachy looking for a view whose widget has a 
00449       // window type of eWindowType_popup - in other words a popup window
00450       // widget. If we find one, this is the view we want. 
00451       nsIWidget* widget = view->GetWidget();
00452       if (widget) {
00453         nsWindowType wtype;
00454         widget->GetWindowType(wtype);
00455         if (wtype == eWindowType_popup) {
00456           *aResult = view;
00457           return;
00458         }
00459       }
00460 
00461       if (aStopAtViewManagerRoot && view == rootView) {
00462         *aResult = view;
00463         return;
00464       }
00465 
00466       nsIView* temp = view->GetParent();
00467       if (!temp) {
00468         // Otherwise, we've walked all the way up to the root view and not
00469         // found a view for a popup window widget. Just return the root view.
00470         *aResult = view;
00471       }
00472       view = temp;
00473     }
00474   }
00475 }
00476 
00477 
00478 //
00479 // AdjustClientXYForNestedDocuments
00480 // 
00481 // almost certainly, the document where the mouse was clicked is not
00482 // the document that contains the popup, especially if we're viewing a page
00483 // with frames. Thus we need to make adjustments to the client coordinates to
00484 // take this into account and get them back into the relative coordinates of
00485 // this document.
00486 //
00487 void
00488 nsMenuPopupFrame::AdjustClientXYForNestedDocuments ( nsIDOMXULDocument* inPopupDoc, nsIPresShell* inPopupShell, 
00489                                                          PRInt32 inClientX, PRInt32 inClientY, 
00490                                                          PRInt32* outAdjX, PRInt32* outAdjY )
00491 {
00492   if ( !inPopupDoc || !outAdjX || !outAdjY )
00493     return;
00494 
00495   // Find the widget associated with the popup's document
00496   nsIWidget* popupDocumentWidget = nsnull;
00497   nsIViewManager* viewManager = inPopupShell->GetViewManager();
00498   if ( viewManager ) {  
00499     nsIView* rootView;
00500     viewManager->GetRootView(rootView);
00501     if ( rootView )
00502       popupDocumentWidget = rootView->GetNearestWidget(nsnull);
00503   }
00504   NS_WARN_IF_FALSE(popupDocumentWidget, "ACK, BAD WIDGET");
00505   
00506   // Find the widget associated with the target's document.
00507   // For tooltips, we check the document's tooltipNode (which is set by
00508   // nsXULTooltipListener).  For regular popups, use popupNode (set by
00509   // nsXULPopupListener).
00510 
00511   nsCOMPtr<nsIDOMNode> targetNode;
00512   nsCOMPtr<nsIDOMXULDocument2> doc = do_QueryInterface(inPopupDoc);
00513   if (mContent->Tag() == nsXULAtoms::tooltip)
00514     doc->TrustedGetTooltipNode(getter_AddRefs(targetNode));
00515   else
00516     doc->TrustedGetPopupNode(getter_AddRefs(targetNode));
00517 
00518   //NS_WARN_IF_FALSE(targetNode, "no popup/tooltip node on document!");
00519   nsCOMPtr<nsIContent> targetAsContent ( do_QueryInterface(targetNode) );
00520   nsIWidget* targetDocumentWidget = nsnull;
00521   if ( targetAsContent ) {
00522     nsCOMPtr<nsIDocument> targetDocument = targetAsContent->GetDocument();
00523     if (targetDocument) {
00524       nsIPresShell *shell = targetDocument->GetShellAt(0);
00525       if ( shell ) {
00526         // We might be inside a popup widget. If so, we need to use that widget and
00527         // not the root view's widget.
00528         nsIFrame* targetFrame;
00529         shell->GetPrimaryFrameFor(targetAsContent, &targetFrame);
00530         nsIView* parentView = nsnull;
00531         if (targetFrame) {
00532           GetRootViewForPopup(targetFrame, PR_TRUE, &parentView);
00533           if (parentView) {
00534             targetDocumentWidget = parentView->GetNearestWidget(nsnull);
00535           }
00536         }
00537         if (!targetDocumentWidget) {
00538           // We aren't inside a popup. This means we should use the root view's
00539           // widget.
00540           nsIViewManager* viewManagerTarget = shell->GetViewManager();
00541           if ( viewManagerTarget ) {
00542             nsIView* rootViewTarget;
00543             viewManagerTarget->GetRootView(rootViewTarget);
00544             if ( rootViewTarget ) {
00545               targetDocumentWidget = rootViewTarget->GetNearestWidget(nsnull);
00546             }
00547           }
00548         }
00549       }
00550     }
00551   }
00552   //NS_WARN_IF_FALSE(targetDocumentWidget, "ACK, BAD TARGET");
00553 
00554   // the offset we need is the difference between the upper left corner of the two widgets. Use
00555   // screen coordinates to find the global offset between them.
00556   nsRect popupDocTopLeft;
00557   if ( popupDocumentWidget ) {
00558     nsRect topLeftClient ( 0, 0, 10, 10 );
00559     popupDocumentWidget->WidgetToScreen ( topLeftClient, popupDocTopLeft );
00560   }
00561   nsRect targetDocTopLeft;
00562   if ( targetDocumentWidget ) {
00563     nsRect topLeftClient ( 0, 0, 10, 10 );
00564     targetDocumentWidget->WidgetToScreen ( topLeftClient, targetDocTopLeft );
00565   }
00566   nsPoint pixelOffset ( targetDocTopLeft.x - popupDocTopLeft.x, targetDocTopLeft.y - popupDocTopLeft.y );
00567 
00568   *outAdjX = inClientX + pixelOffset.x;
00569   *outAdjY = inClientY + pixelOffset.y;
00570   
00571 } // AdjustClientXYForNestedDocuments
00572 
00573 
00574 //
00575 // AdjustPositionForAnchorAlign
00576 // 
00577 // Uses the |popupanchor| and |popupalign| attributes on the popup to move the popup around and
00578 // anchor it to its parent. |outFlushWithTopBottom| will be TRUE if the popup is flush with either
00579 // the top or bottom edge of its parent, and FALSE if it is flush with the left or right edge of
00580 // the parent.
00581 // 
00582 void
00583 nsMenuPopupFrame::AdjustPositionForAnchorAlign ( PRInt32* ioXPos, PRInt32* ioYPos, const nsRect & inParentRect,
00584                                                     const nsString& aPopupAnchor, const nsString& aPopupAlign,
00585                                                     PRBool* outFlushWithTopBottom )
00586 {
00587   nsAutoString popupAnchor(aPopupAnchor);
00588   nsAutoString popupAlign(aPopupAlign);
00589 
00590   if (GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) {
00591     if (popupAnchor.EqualsLiteral("topright"))
00592       popupAnchor.AssignLiteral("topleft");
00593     else if (popupAnchor.EqualsLiteral("topleft"))
00594       popupAnchor.AssignLiteral("topright");
00595     else if (popupAnchor.EqualsLiteral("bottomleft"))
00596       popupAnchor.AssignLiteral("bottomright");
00597     else if (popupAnchor.EqualsLiteral("bottomright"))
00598       popupAnchor.AssignLiteral("bottomleft");
00599 
00600     if (popupAlign.EqualsLiteral("topright"))
00601       popupAlign.AssignLiteral("topleft");
00602     else if (popupAlign.EqualsLiteral("topleft"))
00603       popupAlign.AssignLiteral("topright");
00604     else if (popupAlign.EqualsLiteral("bottomleft"))
00605       popupAlign.AssignLiteral("bottomright");
00606     else if (popupAnchor.EqualsLiteral("bottomright"))
00607       popupAlign.AssignLiteral("bottomleft");
00608   }
00609 
00610   // Adjust position for margins at the aligned corner
00611   nsMargin margin;
00612   GetStyleMargin()->GetMargin(margin);
00613   if (popupAlign.EqualsLiteral("topleft")) {
00614     *ioXPos += margin.left;
00615     *ioYPos += margin.top;
00616   } else if (popupAlign.EqualsLiteral("topright")) {
00617     *ioXPos += margin.right;
00618     *ioYPos += margin.top;
00619   } else if (popupAlign.EqualsLiteral("bottomleft")) {
00620     *ioXPos += margin.left;
00621     *ioYPos += margin.bottom;
00622   } else if (popupAlign.EqualsLiteral("bottomright")) {
00623     *ioXPos += margin.right;
00624     *ioYPos += margin.bottom;
00625   }
00626   
00627   if (popupAnchor.EqualsLiteral("topright") && popupAlign.EqualsLiteral("topleft")) {
00628     *ioXPos += inParentRect.width;
00629   }
00630   else if (popupAnchor.EqualsLiteral("topleft") && popupAlign.EqualsLiteral("topleft")) {
00631     *outFlushWithTopBottom = PR_TRUE;
00632   }
00633   else if (popupAnchor.EqualsLiteral("topright") && popupAlign.EqualsLiteral("bottomright")) {
00634     *ioXPos -= (mRect.width - inParentRect.width);
00635     *ioYPos -= mRect.height;
00636     *outFlushWithTopBottom = PR_TRUE;
00637   }
00638   else if (popupAnchor.EqualsLiteral("bottomright") && popupAlign.EqualsLiteral("bottomleft")) {
00639     *ioXPos += inParentRect.width;
00640     *ioYPos -= (mRect.height - inParentRect.height);
00641   }
00642   else if (popupAnchor.EqualsLiteral("bottomright") && popupAlign.EqualsLiteral("topright")) {
00643     *ioXPos -= (mRect.width - inParentRect.width);
00644     *ioYPos += inParentRect.height;
00645     *outFlushWithTopBottom = PR_TRUE;
00646   }
00647   else if (popupAnchor.EqualsLiteral("topleft") && popupAlign.EqualsLiteral("topright")) {
00648     *ioXPos -= mRect.width;
00649   }
00650   else if (popupAnchor.EqualsLiteral("topleft") && popupAlign.EqualsLiteral("bottomleft")) {
00651     *ioYPos -= mRect.height;
00652     *outFlushWithTopBottom = PR_TRUE;
00653   }
00654   else if (popupAnchor.EqualsLiteral("bottomleft") && popupAlign.EqualsLiteral("bottomright")) {
00655     *ioXPos -= mRect.width;
00656     *ioYPos -= (mRect.height - inParentRect.height);
00657   }
00658   else if (popupAnchor.EqualsLiteral("bottomleft") && popupAlign.EqualsLiteral("topleft")) {
00659     *ioYPos += inParentRect.height;
00660     *outFlushWithTopBottom = PR_TRUE;
00661   }
00662   else
00663     NS_WARNING ( "Hmmm, looks like you've hit a anchor/align case we weren't setup for." );
00664 
00665 } // AdjustPositionForAnchorAlign
00666 
00667 
00668 //
00669 // IsMoreRoomOnOtherSideOfParent
00670 //
00671 // Determine if there is more room on the screen for the popup to live if it was positioned
00672 // on the flip side of the parent from the side it is flush against (ie, if it's top edge was
00673 // flush against the bottom, is there more room if its bottom edge were flush against the top)
00674 //
00675 PRBool
00676 nsMenuPopupFrame::IsMoreRoomOnOtherSideOfParent ( PRBool inFlushAboveBelow, PRInt32 inScreenViewLocX, PRInt32 inScreenViewLocY,
00677                                                      const nsRect & inScreenParentFrameRect, PRInt32 inScreenTopTwips, PRInt32 inScreenLeftTwips,
00678                                                      PRInt32 inScreenBottomTwips, PRInt32 inScreenRightTwips )
00679 {
00680   PRBool switchSides = PR_FALSE;
00681   if ( inFlushAboveBelow ) {
00682     PRInt32 availAbove = inScreenParentFrameRect.y - inScreenTopTwips;
00683     PRInt32 availBelow = inScreenBottomTwips - (inScreenParentFrameRect.y + inScreenParentFrameRect.height) ;
00684     if ( inScreenViewLocY > inScreenParentFrameRect.y )       // view is now below parent
00685       switchSides = availAbove > availBelow;
00686     else
00687       switchSides = availBelow > availAbove;
00688   }
00689   else {
00690     PRInt32 availLeft = inScreenParentFrameRect.x - inScreenLeftTwips;
00691     PRInt32 availRight = inScreenRightTwips - (inScreenParentFrameRect.x + inScreenParentFrameRect.width) ;
00692     if ( inScreenViewLocX > inScreenParentFrameRect.x )       // view is now to the right of parent
00693       switchSides = availLeft > availRight;
00694     else
00695       switchSides = availRight > availLeft;           
00696   }
00697 
00698   return switchSides;
00699   
00700 } // IsMoreRoomOnOtherSideOfParent
00701 
00702 
00703 //
00704 // MovePopupToOtherSideOfParent
00705 //
00706 // Move the popup to the other side of the parent (ie, if it the popup's top edge is flush against the
00707 // bottom of its parent, move the popup so that its bottom edge is now flush against the top of its
00708 // parent...same idea for left/right).
00709 //
00710 // NOTE: In moving the popup, it may need to change size in order to stay on the screen. This will
00711 //       have the side effect of touching |mRect|.
00712 //
00713 void
00714 nsMenuPopupFrame::MovePopupToOtherSideOfParent ( PRBool inFlushAboveBelow, PRInt32* ioXPos, PRInt32* ioYPos, 
00715                                                  PRInt32* ioScreenViewLocX, PRInt32* ioScreenViewLocY,
00716                                                  const nsRect & inScreenParentFrameRect, PRInt32 inScreenTopTwips, PRInt32 inScreenLeftTwips,
00717                                                  PRInt32 inScreenBottomTwips, PRInt32 inScreenRightTwips )
00718 {
00719   if ( inFlushAboveBelow ) {
00720     if ( *ioScreenViewLocY > inScreenParentFrameRect.y ) {     // view is currently below parent
00721       // move it above.
00722       PRInt32 shiftDistY = inScreenParentFrameRect.height + mRect.height;
00723       *ioYPos -= shiftDistY;
00724       *ioScreenViewLocY -= shiftDistY;
00725       // trim it to fit.
00726       if ( *ioScreenViewLocY < inScreenTopTwips ) {
00727         PRInt32 trimY = inScreenTopTwips - *ioScreenViewLocY;
00728         *ioYPos += trimY;
00729         *ioScreenViewLocY += trimY;
00730         mRect.height -= trimY;
00731       }
00732     }
00733     else {                                               // view is currently above parent
00734       // move it below
00735       PRInt32 shiftDistY = inScreenParentFrameRect.height + mRect.height;
00736       *ioYPos += shiftDistY;
00737       *ioScreenViewLocY += shiftDistY;
00738     }
00739   }
00740   else {
00741     if ( *ioScreenViewLocX > inScreenParentFrameRect.x ) {     // view is currently to the right of the parent
00742       // move it to the left.
00743       PRInt32 shiftDistX = inScreenParentFrameRect.width + mRect.width;
00744       *ioXPos -= shiftDistX;
00745       *ioScreenViewLocX -= shiftDistX;
00746       // trim it to fit.
00747       if ( *ioScreenViewLocX < inScreenLeftTwips ) {
00748         PRInt32 trimX = inScreenLeftTwips - *ioScreenViewLocX;
00749         *ioXPos += trimX;
00750         *ioScreenViewLocX += trimX;
00751         mRect.width -= trimX;
00752       }
00753     }
00754     else {                                               // view is currently to the right of the parent
00755       // move it to the right
00756       PRInt32 shiftDistX = inScreenParentFrameRect.width + mRect.width;
00757       *ioXPos += shiftDistX;
00758       *ioScreenViewLocX += shiftDistX;
00759     }               
00760   }
00761 
00762 } // MovePopupToOtherSideOfParent
00763 
00764 class nsASyncMenuActivation : public nsIReflowCallback
00765 {
00766 public:
00767   nsASyncMenuActivation(nsIContent* aContent)
00768     : mContent(aContent)
00769   {
00770   }
00771 
00772   NS_DECL_ISUPPORTS
00773 
00774   NS_IMETHOD ReflowFinished(nsIPresShell* aShell, PRBool* aFlushFlag) {
00775     nsAutoString shouldDisplay, menuActive;
00776     mContent->GetAttr(kNameSpaceID_None, nsXULAtoms::menuactive, menuActive);
00777     if (!menuActive.EqualsLiteral("true")) {
00778       mContent->GetAttr(kNameSpaceID_None, nsXULAtoms::menutobedisplayed,
00779                         shouldDisplay);
00780       if(shouldDisplay.EqualsLiteral("true")) {
00781         mContent->SetAttr(kNameSpaceID_None, nsXULAtoms::menuactive,
00782                           NS_LITERAL_STRING("true"), PR_TRUE);
00783         *aFlushFlag = PR_TRUE;
00784       }
00785     }
00786     return NS_OK;
00787   }
00788 
00789   nsCOMPtr<nsIContent> mContent;
00790 };
00791 
00792 NS_IMPL_ISUPPORTS1(nsASyncMenuActivation, nsIReflowCallback)
00793 
00794 nsresult 
00795 nsMenuPopupFrame::SyncViewWithFrame(nsPresContext* aPresContext,
00796                                     const nsString& aPopupAnchor,
00797                                     const nsString& aPopupAlign,
00798                                     nsIFrame* aFrame, 
00799                                     PRInt32 aXPos, PRInt32 aYPos)
00800 {
00801   NS_ENSURE_ARG(aPresContext);
00802   NS_ENSURE_ARG(aFrame);
00803 
00804   if (!mShouldAutoPosition && !mInContentShell)
00805     return NS_OK;
00806 
00807   // |containingView|
00808   //   The view that contains the frame that is invoking this popup. This is 
00809   //   the canvas view inside the scrollport view. It can have negative bounds
00810   //   if the canvas is scrolled so that part is off screen.
00811   nsIView* containingView = nsnull;
00812   nsPoint offset;
00813   nsMargin margin;
00814   containingView = aFrame->GetClosestView(&offset);
00815   if (!containingView)
00816     return NS_OK;
00817 
00818   // |view|
00819   //   The root view for the popup window widget associated with this frame,
00820   //   or, the view associated with this frame. 
00821   nsIView* view = GetView();
00822 
00823   // |parentPos|
00824   //   The distance between the containingView and the root view. This provides
00825   //   a hint as to where to position the menu relative to the window. 
00826   nsPoint parentPos;
00827   GetViewOffset(containingView, parentPos);
00828 
00829   // |parentRect|
00830   //   The dimensions of the frame invoking the popup. 
00831   nsRect parentRect = aFrame->GetRect();
00832 
00833   float p2t = aPresContext->ScaledPixelsToTwips();
00834 
00835   nsIViewManager* viewManager = containingView->GetViewManager();
00836     
00837   nsCOMPtr<nsIDeviceContext> dx;
00838   viewManager->GetDeviceContext(*getter_AddRefs(dx));
00839   float t2p = dx->AppUnitsToDevUnits();
00840 
00841   // get the document and the global script object
00842   nsIPresShell *presShell = aPresContext->PresShell();
00843   nsIDocument *document = presShell->GetDocument();
00844 
00845   PRBool sizedToPopup = (mContent->Tag() != nsXULAtoms::tooltip) &&
00846     (nsMenuFrame::IsSizedToPopup(aFrame->GetContent(), PR_FALSE));
00847 
00848   // If we stick to our parent's width, set it here before we move the
00849   // window around, because moving is done with respect to the width...
00850   if (sizedToPopup) {
00851     mRect.width = parentRect.width;
00852   }
00853 
00854   // |xpos| and |ypos| hold the x and y positions of where the popup will be moved to,
00855   // in _twips_, in the coordinate system of the _parent view_.
00856   PRInt32 xpos = 0, ypos = 0;
00857 
00858   // if we are anchored to our parent, there are certain things we don't want to do
00859   // when repositioning the view to fit on the screen, such as end up positioned over
00860   // the parent. When doing this reposition, we want to move the popup to the side with
00861   // the most room. The combination of anchor and alignment dictate if we readjst 
00862   // above/below or to the left/right.
00863   PRBool anchoredToParent = PR_FALSE;
00864   PRBool readjustAboveBelow = PR_FALSE;
00865 
00866   if ( aXPos != -1 || aYPos != -1 ) {
00867   
00868     // for this case, we've been handed a specific x/y location (in client coordinates) for
00869     // the popup. However, we may be deeply nested in a frameset, etc and so the client coordinates
00870     // need some adjusting. 
00871     nsCOMPtr<nsIDOMXULDocument> xulDoc ( do_QueryInterface(document) );
00872     PRInt32 newXPos = 0, newYPos = 0;
00873     AdjustClientXYForNestedDocuments ( xulDoc, presShell, aXPos, aYPos, &newXPos, &newYPos );
00874 
00875     xpos = NSIntPixelsToTwips(newXPos, p2t);
00876     ypos = NSIntPixelsToTwips(newYPos, p2t);
00877 
00878     // Add in the top and left margins
00879     GetStyleMargin()->GetMargin(margin);
00880     
00881     xpos += margin.left;
00882     ypos += margin.top;
00883   } 
00884   else {
00885     anchoredToParent = PR_TRUE;
00886 
00887     xpos = parentPos.x + offset.x;
00888     ypos = parentPos.y + offset.y;
00889     
00890     // move the popup according to the anchor/alignment attributes. This will also tell us
00891     // which axis the popup is flush against in case we have to move it around later.
00892     AdjustPositionForAnchorAlign ( &xpos, &ypos, parentRect, aPopupAnchor, aPopupAlign, &readjustAboveBelow );    
00893   }
00894   
00895   // Compute info about the screen dimensions. Because of multiple monitor systems,
00896   // the left or top sides of the screen may be in negative space (main monitor is on the
00897   // right, etc). We need to be sure to do the right thing.
00898   nsCOMPtr<nsIDOMWindowInternal> window(do_QueryInterface(document->GetScriptGlobalObject()));
00899   if (!window)
00900     return NS_OK;
00901 
00902   nsCOMPtr<nsIDOMScreen> screen;
00903   window->GetScreen(getter_AddRefs(screen));
00904   PRInt32 screenWidth = 0, screenHeight = 0;
00905   PRInt32 screenLeft = 0, screenTop = 0;
00906   PRInt32 screenRight = 0, screenBottom = 0;
00907   if ( mMenuCanOverlapOSBar ) {
00908     screen->GetLeft(&screenLeft);
00909     screen->GetTop(&screenTop);
00910     screen->GetWidth(&screenWidth);
00911     screen->GetHeight(&screenHeight);
00912   }
00913   else {
00914     screen->GetAvailLeft(&screenLeft);
00915     screen->GetAvailTop(&screenTop); 
00916     screen->GetAvailWidth(&screenWidth);
00917     screen->GetAvailHeight(&screenHeight);
00918   }
00919 
00920   // keep 3px margin to the right and bottom of the screen for WinXP dropshadow
00921   screenWidth -= 3;
00922   screenHeight -= 3;
00923 
00924   // for content shells, clip to the client area rather than the screen area
00925   if (mInContentShell) {
00926     nsRect screenRect(screenLeft, screenTop, screenWidth, screenHeight);
00927     nsRect rootScreenRect = presShell->GetRootFrame()->GetScreenRect();
00928     screenRect.IntersectRect(screenRect, rootScreenRect);
00929     screenLeft = screenRect.x;
00930     screenTop = screenRect.y;
00931     screenWidth = screenRect.width;
00932     screenHeight = screenRect.height;
00933   }
00934 
00935   screenRight = screenLeft + screenWidth;
00936   screenBottom = screenTop + screenHeight;
00937 
00938   PRInt32 screenTopTwips    = NSIntPixelsToTwips(screenTop, p2t);
00939   PRInt32 screenLeftTwips   = NSIntPixelsToTwips(screenLeft, p2t);
00940   PRInt32 screenWidthTwips  = NSIntPixelsToTwips(screenWidth, p2t);
00941   PRInt32 screenHeightTwips = NSIntPixelsToTwips(screenHeight, p2t);
00942   PRInt32 screenRightTwips  = NSIntPixelsToTwips(screenRight, p2t);
00943   PRInt32 screenBottomTwips = NSIntPixelsToTwips(screenBottom, p2t);
00944   
00945   // Recall that |xpos| and |ypos| are in the coordinate system of the parent view. In
00946   // order to determine the screen coordinates of where our view will end up, we
00947   // need to find the x/y position of the parent view in screen coords. That is done
00948   // by getting the widget associated with the parent view and determining the offset 
00949   // based on converting (0,0) in its coordinate space to screen coords. We then
00950   // offset that point by (|xpos|,|ypos|) to get the true screen coordinates of
00951   // the view. *whew*
00952 
00953   // |parentView|
00954   //   The root view for the window that contains the frame, for frames inside 
00955   //   menupopups this is the first view inside the popup window widget, for 
00956   //   frames inside a toplevel window, this is the root view of the toplevel
00957   //   window.
00958   nsIView* parentView = nsnull;
00959   GetRootViewForPopup(aFrame, PR_FALSE, &parentView);
00960   if (!parentView)
00961     return NS_OK;
00962 
00963   // Use containingView instead of parentView, to account for the scrollarrows
00964   // that a parent menu might have.
00965 
00966   nsPoint parentViewWidgetOffset;
00967   nsIWidget* parentViewWidget = containingView->GetNearestWidget(&parentViewWidgetOffset);
00968   nsRect localParentWidgetRect(0,0,0,0), screenParentWidgetRect;
00969   parentViewWidget->WidgetToScreen ( localParentWidgetRect, screenParentWidgetRect );
00970   PRInt32 screenViewLocX = NSIntPixelsToTwips(screenParentWidgetRect.x,p2t) +
00971     (xpos - parentPos.x) + parentViewWidgetOffset.x;
00972   PRInt32 screenViewLocY = NSIntPixelsToTwips(screenParentWidgetRect.y,p2t) +
00973     (ypos - parentPos.y) + parentViewWidgetOffset.y;
00974 
00975   if ( anchoredToParent ) {
00976     
00977     //
00978     // Popup is anchored to the parent, guarantee that it does not cover the parent. We
00979     // shouldn't do anything funky if it will already fit on the screen as is.
00980     //
00981 
00983     //
00984     //                +------------------------+          
00985     //                |           /\           |
00986     // parentPos -> - +------------------------+
00987     //              | |                        |
00988     //       offset | |                        |
00989     //              | |                        |
00990     //              | |                        | (screenViewLocX,screenViewLocY)
00991     //              - |========================|+--------------
00992     //                | parentRect           > ||
00993     //                |========================||
00994     //                |                        || Submenu 
00995     //                +------------------------+|  ( = mRect )
00996     //                |           \/           ||
00997     //                +------------------------+
00998 
00999 
01000 
01001     // compute screen coordinates of parent frame so we can play with it. Make sure we put it
01002     // into twips as everything else is as well.
01003     nsRect screenParentFrameRect ( NSTwipsToIntPixels(offset.x,t2p), NSTwipsToIntPixels(offset.y,t2p),
01004                                     parentRect.width, parentRect.height );
01005     parentViewWidget->WidgetToScreen ( screenParentFrameRect, screenParentFrameRect );
01006     screenParentFrameRect.x = NSIntPixelsToTwips(screenParentFrameRect.x, p2t);
01007     screenParentFrameRect.y = NSIntPixelsToTwips(screenParentFrameRect.y, p2t);
01008 
01009     // Don't let it spill off the screen to the top
01010     if (screenViewLocY < screenTopTwips) {
01011       PRInt32 moveDist = screenTopTwips - screenViewLocY;
01012       screenViewLocY = screenTopTwips;
01013       ypos += moveDist;
01014     }
01015     
01016     // if it doesn't fit on the screen, do our magic.
01017     if ( (screenViewLocX + mRect.width) > screenRightTwips ||
01018            screenViewLocX < screenLeftTwips ||
01019           (screenViewLocY + mRect.height) > screenBottomTwips ) {
01020       
01021       // figure out which side of the parent has the most free space so we can move/resize
01022       // the popup there. This should still work if the parent frame is partially screen.
01023       PRBool switchSides = IsMoreRoomOnOtherSideOfParent ( readjustAboveBelow, screenViewLocX, screenViewLocY,
01024                                                             screenParentFrameRect, screenTopTwips, screenLeftTwips,
01025                                                             screenBottomTwips, screenRightTwips );
01026       
01027       // move the popup to the correct side, if necessary. Note that MovePopupToOtherSideOfParent() 
01028       // can change width/height of |mRect|.
01029       if ( switchSides )
01030         MovePopupToOtherSideOfParent ( readjustAboveBelow, &xpos, &ypos, &screenViewLocX, &screenViewLocY, 
01031                                         screenParentFrameRect, screenTopTwips, screenLeftTwips,
01032                                         screenBottomTwips, screenRightTwips );
01033                                         
01034       // We are allowed to move the popup along the axis to which we're not anchored to the parent
01035       // in order to get it to not spill off the screen.
01036       if ( readjustAboveBelow ) {
01037         // move left to be on screen, but don't let it go off the screen at the left
01038         if ( (screenViewLocX + mRect.width) > screenRightTwips ) {
01039           PRInt32 moveDistX = (screenViewLocX + mRect.width) - screenRightTwips;
01040           if ( screenViewLocX - moveDistX < screenLeftTwips )
01041             moveDistX = screenViewLocX - screenLeftTwips;          
01042           screenViewLocX -= moveDistX;
01043           xpos -= moveDistX;
01044         } else if (screenViewLocX < screenLeftTwips) {
01045           // move right to be on screen, but don't let it go off the screen at the right
01046           PRInt32 moveDistX = screenLeftTwips - screenViewLocX;
01047           if ( (screenViewLocX + mRect.width + moveDistX) > screenRightTwips )
01048             moveDistX = screenRightTwips - screenViewLocX - mRect.width;
01049           screenViewLocX += moveDistX;
01050           xpos += moveDistX;
01051         }
01052       }
01053       else {
01054         // move it up to be on screen, but don't let it go off the screen at the top
01055         /*
01056          *  |
01057          *  |
01058          *  |+----  screenViewLocY
01059          *  ||
01060          *  ||  Submenu ( = mRect )
01061          * -+|
01062          *   |
01063          *   |
01064          * - - - - - - - - - - screenBottomTwips (bottom of the screen)
01065          *   |    \ 
01066          *   |     }  moveDistY
01067          *   |    / 
01068          *   +----  screenViewLocY + mRect.height
01069          */
01070 
01071         if ( (screenViewLocY + mRect.height) > screenBottomTwips ) {
01072           // XXX Bug 84121 comment 48 says the next line has to use screenHeightTwips, why not screenBottomTwips?
01073           PRInt32 moveDistY = (screenViewLocY + mRect.height) - screenHeightTwips;
01074           if ( screenViewLocY - moveDistY < screenTopTwips )
01075             moveDistY = screenViewLocY - screenTopTwips;          
01076           screenViewLocY -= moveDistY;
01077           ypos -= moveDistY; 
01078         } 
01079       }
01080       
01081       // Resize it to fit on the screen. By this point, we've given the popup as much
01082       // room as we can w/out covering the parent. If it still can't be as big
01083       // as it wants to be, well, it just has to suck up and deal. 
01084       //
01085       // ySpillage is calculated the same way as moveDistY above. see picture there.
01086 
01087       PRInt32 xSpillage = (screenViewLocX + mRect.width) - screenRightTwips;
01088       if ( xSpillage > 0 )
01089         mRect.width -= xSpillage;
01090       PRInt32 ySpillage = (screenViewLocY + mRect.height) - screenBottomTwips;
01091       if ( ySpillage > 0 )
01092         mRect.height -= ySpillage;
01093 
01094       // shrink to fit onto the screen, vertically and horizontally
01095       if(mRect.width > screenWidthTwips) 
01096           mRect.width = screenWidthTwips;    
01097       if(mRect.height > screenHeightTwips)
01098           mRect.height = screenHeightTwips;   
01099 
01100     } // if it doesn't fit on screen
01101   } // if anchored to parent
01102   else {
01103   
01104     //
01105     // Popup not anchored to anything, just make sure it's on the screen by any
01106     // means necessary
01107     //
01108 
01109     // If you decide to mess with this code in some way other than just
01110     // converting it to be just like the anchored codepath, please make sure to
01111     // not regress bug 120226, bug 172530, bug 245163.
01112 
01113     // XXXbz this is really silly.  We should be able to anchor popups to a
01114     // point or rect, not a frame, and we should be doing so with context
01115     // menus.  Furthermore, we should not be adding in pixels manually to
01116     // adjust position (in XULPopupListenerImpl::LaunchPopup comes to mind,
01117     // though ConvertPosition in the same file has some 21-px bogosity in the
01118     // y-direction too).
01119 
01120     // shrink to fit onto the screen, vertically and horizontally
01121     if(mRect.width > screenWidthTwips) 
01122         mRect.width = screenWidthTwips;    
01123     if(mRect.height > screenHeightTwips)
01124         mRect.height = screenHeightTwips;   
01125 
01126     // First, adjust the X position.  For the X position, we slide the popup
01127     // left or right as needed to get it on screen.
01128     if ( screenViewLocX < screenLeftTwips ) {
01129       PRInt32 moveDistX = screenLeftTwips - screenViewLocX;
01130       xpos += moveDistX;
01131       screenViewLocX += moveDistX;
01132     }
01133     if ( (screenViewLocX + mRect.width) > screenRightTwips )
01134       xpos -= (screenViewLocX + mRect.width) - screenRightTwips;
01135 
01136     // Now the Y position.  If the popup is up too high, slide it down so it's
01137     // on screen.
01138     if ( screenViewLocY < screenTopTwips ) {
01139       PRInt32 moveDistY = screenTopTwips - screenViewLocY;
01140       ypos += moveDistY;
01141       screenViewLocY += moveDistY;
01142     }
01143 
01144     // Now if the popup extends down too far, either resize it or flip it to be
01145     // above the anchor point and resize it to fit above, depending on where we
01146     // have more room.
01147     if ( (screenViewLocY + mRect.height) > screenBottomTwips ) {
01148       // XXXbz it'd be good to make use of IsMoreRoomOnOtherSideOfParent and
01149       // such here, but that's really focused on having a nonempty parent
01150       // rect...
01151       if (screenBottomTwips - screenViewLocY >
01152           screenViewLocY - screenTopTwips) {
01153         // More space below our desired point.  Resize to fit in this space.
01154         // Note that this is making mRect smaller; othewise we would not have
01155         // reached this code.
01156         mRect.height = screenBottomTwips - screenViewLocY;
01157       } else {
01158         // More space above our desired point.  Flip and resize to fit in this
01159         // space.
01160         if (mRect.height > screenViewLocY - screenTopTwips) {
01161           // We wouldn't fit.  Shorten before flipping.
01162           mRect.height = screenViewLocY - screenTopTwips;
01163         }
01164         ypos -= (mRect.height + margin.top + margin.bottom);
01165       }
01166     }
01167   }  
01168 
01169   viewManager->MoveViewTo(view, xpos, ypos); 
01170 
01171   // Now that we've positioned the view, sync up the frame's origin.
01172   nsPoint frameOrigin = GetPosition();
01173   nsPoint offsetToView;
01174   GetOriginToViewOffset(offsetToView, nsnull);
01175   frameOrigin -= offsetToView;
01176   nsBoxFrame::SetPosition(frameOrigin);
01177 
01178   if (sizedToPopup) {
01179       nsBoxLayoutState state(mPresContext);
01180       SetBounds(state, nsRect(mRect.x, mRect.y, parentRect.width, mRect.height));
01181   }
01182     
01183   nsAutoString shouldDisplay, menuActive;
01184   mContent->GetAttr(kNameSpaceID_None, nsXULAtoms::menuactive, menuActive);
01185   if (!menuActive.EqualsLiteral("true")) {
01186     mContent->GetAttr(kNameSpaceID_None, nsXULAtoms::menutobedisplayed, shouldDisplay);
01187     if ( shouldDisplay.EqualsLiteral("true") ) {
01188       nsCOMPtr<nsIReflowCallback> cb = new nsASyncMenuActivation(mContent);
01189       NS_ENSURE_TRUE(cb, NS_ERROR_OUT_OF_MEMORY);
01190       mPresContext->PresShell()->PostReflowCallback(cb);
01191     }
01192   }
01193 
01194   return NS_OK;
01195 }
01196 
01197 static void GetInsertionPoint(nsIPresShell* aShell, nsIFrame* aFrame, nsIFrame* aChild,
01198                               nsIFrame** aResult)
01199 {
01200   nsIContent* child = nsnull;
01201   if (aChild)
01202     child = aChild->GetContent();
01203   aShell->FrameConstructor()->GetInsertionPoint(aFrame, child, aResult);
01204 }
01205 
01206 /* virtual */ nsIMenuFrame*
01207 nsMenuPopupFrame::GetNextMenuItem(nsIMenuFrame* aStart)
01208 {
01209   nsIFrame* immediateParent = nsnull;
01210   GetInsertionPoint(mPresContext->PresShell(), this, nsnull, &immediateParent);
01211   if (!immediateParent)
01212     immediateParent = this;
01213 
01214   nsIFrame* currFrame = nsnull;
01215   nsIFrame* startFrame = nsnull;
01216   if (aStart) {
01217     aStart->QueryInterface(NS_GET_IID(nsIFrame), (void**)&currFrame); 
01218     if (currFrame) {
01219       startFrame = currFrame;
01220       currFrame = currFrame->GetNextSibling();
01221     }
01222   }
01223   else 
01224     currFrame = immediateParent->GetFirstChild(nsnull);
01225   
01226   while (currFrame) {
01227     // See if it's a menu item.
01228     if (IsValidItem(currFrame->GetContent())) {
01229       nsIMenuFrame *menuFrame;
01230       if (NS_FAILED(CallQueryInterface(currFrame, &menuFrame)))
01231         menuFrame = nsnull;
01232       return menuFrame;
01233     }
01234     currFrame = currFrame->GetNextSibling();
01235   }
01236 
01237   currFrame = immediateParent->GetFirstChild(nsnull);
01238 
01239   // Still don't have anything. Try cycling from the beginning.
01240   while (currFrame && currFrame != startFrame) {
01241     // See if it's a menu item.
01242     if (IsValidItem(currFrame->GetContent())) {
01243       nsIMenuFrame *menuFrame;
01244       if (NS_FAILED(CallQueryInterface(currFrame, &menuFrame)))
01245         menuFrame = nsnull;
01246       return menuFrame;
01247     }
01248 
01249     currFrame = currFrame->GetNextSibling();
01250   }
01251 
01252   // No luck. Just return our start value.
01253   return aStart;
01254 }
01255 
01256 /* virtual */ nsIMenuFrame*
01257 nsMenuPopupFrame::GetPreviousMenuItem(nsIMenuFrame* aStart)
01258 {
01259   nsIFrame* immediateParent = nsnull;
01260   GetInsertionPoint(mPresContext->PresShell(), this, nsnull, &immediateParent);
01261   if (!immediateParent)
01262     immediateParent = this;
01263 
01264   nsFrameList frames(immediateParent->GetFirstChild(nsnull));
01265                               
01266   nsIFrame* currFrame = nsnull;
01267   nsIFrame* startFrame = nsnull;
01268   if (aStart) {
01269     aStart->QueryInterface(NS_GET_IID(nsIFrame), (void**)&currFrame);
01270     if (currFrame) {
01271       startFrame = currFrame;
01272       currFrame = frames.GetPrevSiblingFor(currFrame);
01273     }
01274   }
01275   else currFrame = frames.LastChild();
01276 
01277   while (currFrame) {
01278     // See if it's a menu item.
01279     if (IsValidItem(currFrame->GetContent())) {
01280       nsIMenuFrame *menuFrame;
01281       if (NS_FAILED(CallQueryInterface(currFrame, &menuFrame)))
01282         menuFrame = nsnull;
01283       return menuFrame;
01284     }
01285     currFrame = frames.GetPrevSiblingFor(currFrame);
01286   }
01287 
01288   currFrame = frames.LastChild();
01289 
01290   // Still don't have anything. Try cycling from the end.
01291   while (currFrame && currFrame != startFrame) {
01292     // See if it's a menu item.
01293     if (IsValidItem(currFrame->GetContent())) {
01294       nsIMenuFrame *menuFrame;
01295       if (NS_FAILED(CallQueryInterface(currFrame, &menuFrame)))
01296         menuFrame = nsnull;
01297       return menuFrame;
01298     }
01299 
01300     currFrame = frames.GetPrevSiblingFor(currFrame);
01301   }
01302 
01303   // No luck. Just return our start value.
01304   return aStart;
01305 }
01306 
01307 /* virtual */ nsIMenuFrame*
01308 nsMenuPopupFrame::GetCurrentMenuItem()
01309 {
01310   return mCurrentMenu;
01311 }
01312 
01313 NS_IMETHODIMP nsMenuPopupFrame::ConsumeOutsideClicks(PRBool& aConsumeOutsideClicks)
01314 {
01315   /*
01316    * When this popup is open, should clicks outside of it be consumed?
01317    * Return PR_TRUE if the popup hould rollup on an outside click, 
01318    * but consume that click so it can't be used for anything else.
01319    * Return PR_FALSE to allow clicks outside the popup to activate content 
01320    * even when the popup is open.
01321    * ---------------------------------------------------------------------
01322    * 
01323    * Should clicks outside of a popup be eaten?
01324    *
01325    *       Menus     Autocomplete     Comboboxes
01326    * Mac     Eat           No              Eat
01327    * Win     No            No              Eat     
01328    * Unix    Eat           No              Eat
01329    *
01330    */
01331 
01332   aConsumeOutsideClicks = PR_TRUE;
01333 
01334   nsCOMPtr<nsIContent> parentContent = mContent->GetParent();
01335 
01336   if (parentContent) {
01337     nsIAtom *parentTag = parentContent->Tag();
01338     if (parentTag == nsXULAtoms::menulist)
01339       return NS_OK;  // Consume outside clicks for combo boxes on all platforms
01340     if (parentTag == nsXULAtoms::menu || parentTag == nsXULAtoms::popupset) {
01341 #if defined(XP_WIN) || defined(XP_OS2)
01342       // Don't consume outside clicks for menus in Windows
01343       aConsumeOutsideClicks = PR_FALSE;
01344 #endif
01345       return NS_OK;
01346     }
01347     if (parentTag == nsXULAtoms::textbox) {
01348       // Don't consume outside clicks for autocomplete widget
01349       nsAutoString typeString;
01350       parentContent->GetAttr(kNameSpaceID_None, nsHTMLAtoms::type, typeString);
01351       if (typeString.EqualsLiteral("autocomplete"))
01352         aConsumeOutsideClicks = PR_FALSE;
01353     }
01354   }
01355 
01356   return NS_OK;
01357 }
01358 
01359 static nsIScrollableView* GetScrollableViewForFrame(nsIFrame* aFrame)
01360 {
01361   nsIScrollableFrame* sf;
01362   nsresult rv = CallQueryInterface(aFrame, &sf);
01363   if (NS_FAILED(rv))
01364     return nsnull;
01365   return sf->GetScrollableView();
01366 }
01367 
01368 // XXXroc this is megalame. Fossicking around for a view of the right
01369 // type is a recipe for disaster in the long term.
01370 nsIScrollableView* nsMenuPopupFrame::GetScrollableView(nsIFrame* aStart)
01371 {
01372   if ( ! aStart )
01373     return nsnull;  
01374 
01375   nsIFrame* currFrame;
01376   nsIScrollableView* scrollableView=nsnull;
01377 
01378   // try start frame and siblings
01379   currFrame=aStart;
01380   do {
01381     scrollableView = GetScrollableViewForFrame(currFrame);
01382     if ( scrollableView )
01383       return scrollableView;
01384     currFrame = currFrame->GetNextSibling();
01385   } while ( currFrame );
01386 
01387   // try children
01388   nsIFrame* childFrame;
01389   currFrame=aStart;
01390   do {
01391     childFrame = currFrame->GetFirstChild(nsnull);
01392     scrollableView=GetScrollableView(childFrame);
01393     if ( scrollableView )
01394       return scrollableView;
01395     currFrame = currFrame->GetNextSibling();
01396   } while ( currFrame );
01397 
01398   return nsnull;
01399 }
01400 
01401 void nsMenuPopupFrame::EnsureMenuItemIsVisible(nsIMenuFrame* aMenuItem)
01402 {
01403   nsIFrame* frame=nsnull;
01404   aMenuItem->QueryInterface(NS_GET_IID(nsIFrame), (void**)&frame);
01405   if ( frame ) {
01406     nsIFrame* childFrame=nsnull;
01407     childFrame = GetFirstChild(nsnull);
01408     nsIScrollableView *scrollableView;
01409     scrollableView=GetScrollableView(childFrame);
01410     if ( scrollableView ) {
01411       nscoord scrollX, scrollY;
01412 
01413       nsRect viewRect = scrollableView->View()->GetBounds();
01414       nsRect itemRect = frame->GetRect();
01415       scrollableView->GetScrollPosition(scrollX, scrollY);
01416   
01417       // scroll down
01418       if ( itemRect.y + itemRect.height > scrollY + viewRect.height )
01419         scrollableView->ScrollTo(scrollX, itemRect.y + itemRect.height - viewRect.height, NS_SCROLL_PROPERTY_ALWAYS_BLIT);
01420       
01421       // scroll up
01422       else if ( itemRect.y < scrollY )
01423         scrollableView->ScrollTo(scrollX, itemRect.y, NS_SCROLL_PROPERTY_ALWAYS_BLIT);
01424     }
01425   }
01426 }
01427 
01428 NS_IMETHODIMP nsMenuPopupFrame::SetCurrentMenuItem(nsIMenuFrame* aMenuItem)
01429 {
01430   // When a context menu is open, the current menu is locked, and no change
01431   // to the menu is allowed.
01432   nsIMenuParent *contextMenu = GetContextMenu();
01433   if (contextMenu)
01434     return NS_OK;
01435 
01436   if (mCurrentMenu == aMenuItem)
01437     return NS_OK;
01438   
01439   // Unset the current child.
01440   if (mCurrentMenu) {
01441     PRBool isOpen = PR_FALSE;
01442     mCurrentMenu->MenuIsOpen(isOpen);
01443     mCurrentMenu->SelectMenu(PR_FALSE);
01444     // XXX bug 294183 sometimes mCurrentMenu gets cleared
01445     if (mCurrentMenu && isOpen) {
01446       // Don't close up immediately.
01447       // Kick off a close timer.
01448       KillCloseTimer(); // Ensure we don't have another stray waiting closure.
01449       PRInt32 menuDelay = 300;   // ms
01450 
01451       mPresContext->LookAndFeel()->
01452         GetMetric(nsILookAndFeel::eMetric_SubmenuDelay, menuDelay);
01453 
01454       // Kick off the timer.
01455       mCloseTimer = do_CreateInstance("@mozilla.org/timer;1");
01456       nsCOMPtr<nsITimerInternal> ti = do_QueryInterface(mCloseTimer);
01457       ti->SetIdle(PR_FALSE);
01458       mCloseTimer->InitWithCallback(mTimerMediator, menuDelay, nsITimer::TYPE_ONE_SHOT);
01459       mTimerMenu = mCurrentMenu;
01460     }
01461   }
01462 
01463   // Set the new child.
01464   if (aMenuItem) {
01465     EnsureMenuItemIsVisible(aMenuItem);
01466     aMenuItem->SelectMenu(PR_TRUE);
01467   }
01468 
01469   mCurrentMenu = aMenuItem;
01470 
01471   return NS_OK;
01472 }
01473 
01474 
01475 NS_IMETHODIMP
01476 nsMenuPopupFrame::Escape(PRBool& aHandledFlag)
01477 {
01478   mIncrementalString.Truncate();
01479 
01480   // See if we have a context menu open.
01481   nsIMenuParent* contextMenu = GetContextMenu();
01482   if (contextMenu) {
01483     // Get the context menu parent.
01484     nsIFrame* childFrame;
01485     CallQueryInterface(contextMenu, &childFrame);
01486     nsIPopupSetFrame* popupSetFrame = GetPopupSetFrame(mPresContext);
01487     if (popupSetFrame)
01488       // Destroy the popup.
01489       popupSetFrame->DestroyPopup(childFrame, PR_FALSE);
01490     aHandledFlag = PR_TRUE;
01491     return NS_OK;
01492   }
01493 
01494   if (!mCurrentMenu)
01495     return NS_OK;
01496 
01497   // See if our menu is open.
01498   PRBool isOpen = PR_FALSE;
01499   mCurrentMenu->MenuIsOpen(isOpen);
01500   if (isOpen) {
01501     // Let the child menu handle this.
01502     mCurrentMenu->Escape(aHandledFlag);
01503     if (!aHandledFlag) {
01504       // We should close up.
01505       mCurrentMenu->OpenMenu(PR_FALSE);
01506       // SelectMenu() so DOMMenuItemActive is fired for accessibility
01507       mCurrentMenu->SelectMenu(PR_TRUE);
01508       aHandledFlag = PR_TRUE;
01509     }
01510   }
01511 
01512   return NS_OK;
01513 }
01514 
01515 NS_IMETHODIMP
01516 nsMenuPopupFrame::Enter()
01517 {
01518   mIncrementalString.Truncate();
01519 
01520   // See if we have a context menu open.
01521   nsIMenuParent *contextMenu = GetContextMenu();
01522   if (contextMenu)
01523     return contextMenu->Enter();
01524 
01525   // Give it to the child.
01526   if (mCurrentMenu)
01527     mCurrentMenu->Enter();
01528 
01529   return NS_OK;
01530 }
01531 
01532 nsIMenuParent*
01533 nsMenuPopupFrame::GetContextMenu()
01534 {
01535   if (mIsContextMenu)
01536     return nsnull;
01537 
01538   return nsMenuFrame::GetContextMenu();
01539 }
01540 
01541 nsIMenuFrame*
01542 nsMenuPopupFrame::FindMenuWithShortcut(nsIDOMKeyEvent* aKeyEvent, PRBool& doAction)
01543 {
01544   PRUint32 charCode, keyCode;
01545   aKeyEvent->GetCharCode(&charCode);
01546   aKeyEvent->GetKeyCode(&keyCode);
01547 
01548   doAction = PR_FALSE;
01549 
01550   // Enumerate over our list of frames.
01551   nsIFrame* immediateParent = nsnull;
01552   GetInsertionPoint(mPresContext->PresShell(), this, nsnull, &immediateParent);
01553   if (!immediateParent)
01554     immediateParent = this;
01555 
01556   PRUint32 matchCount = 0, matchShortcutCount = 0;
01557   PRBool foundActive = PR_FALSE;
01558   PRBool isShortcut;
01559   nsIMenuFrame* frameBefore = nsnull;
01560   nsIMenuFrame* frameAfter = nsnull;
01561   nsIMenuFrame* frameShortcut = nsnull;
01562 
01563   nsIContent* parentContent = mContent->GetParent();
01564 
01565   PRBool isMenu =
01566     parentContent && parentContent->Tag() != nsXULAtoms::menulist;
01567 
01568   static DOMTimeStamp lastKeyTime = 0;
01569   DOMTimeStamp keyTime;
01570   aKeyEvent->GetTimeStamp(&keyTime);
01571 
01572   if (charCode == 0) {
01573     if (keyCode == NS_VK_BACK) {
01574       if (!isMenu && !mIncrementalString.IsEmpty()) {
01575         mIncrementalString.SetLength(mIncrementalString.Length() - 1);
01576         return nsnull;
01577       }
01578       else {
01579 #ifdef XP_WIN
01580         nsCOMPtr<nsISound> soundInterface = do_CreateInstance("@mozilla.org/sound;1");
01581         if (soundInterface)
01582           soundInterface->Beep();
01583 #endif  // #ifdef XP_WIN
01584       }
01585     }
01586     return nsnull;
01587   }
01588   else {
01589     PRUnichar uniChar = ToLowerCase(NS_STATIC_CAST(PRUnichar, charCode));
01590     if (isMenu || // Menu supports only first-letter navigation
01591         keyTime - lastKeyTime > INC_TYP_INTERVAL) // Interval too long, treat as new typing
01592       mIncrementalString = uniChar;
01593     else {
01594       mIncrementalString.Append(uniChar);
01595     }
01596   }
01597 
01598   // See bug 188199 & 192346, if all letters in incremental string are same, just try to match the first one
01599   nsAutoString incrementalString(mIncrementalString);
01600   PRUint32 charIndex = 1, stringLength = incrementalString.Length();
01601   while (charIndex < stringLength && incrementalString[charIndex] == incrementalString[charIndex - 1]) {
01602     charIndex++;
01603   }
01604   if (charIndex == stringLength) {
01605     incrementalString.Truncate(1);
01606     stringLength = 1;
01607   }
01608 
01609   lastKeyTime = keyTime;
01610 
01611   nsIFrame* currFrame;
01612   // NOTE: If you crashed here due to a bogus |immediateParent| it is 
01613   //       possible that the menu whose shortcut is being looked up has 
01614   //       been destroyed already.  One strategy would be to 
01615   //       setTimeout(<func>,0) as detailed in:
01616   //       <http://bugzilla.mozilla.org/show_bug.cgi?id=126675#c32>
01617   currFrame = immediateParent->GetFirstChild(nsnull);
01618 
01619   // We start searching from first child. This process is divided into two parts
01620   //   -- before current and after current -- by the current item
01621   while (currFrame) {
01622     nsIContent* current = currFrame->GetContent();
01623     
01624     // See if it's a menu item.
01625     if (IsValidItem(current)) {
01626       nsAutoString activeKey, textKey;
01627       // Get the shortcut attribute.
01628       current->GetAttr(kNameSpaceID_None, nsXULAtoms::accesskey, textKey);
01629       if (textKey.IsEmpty()) { // No shortcut, try first letter
01630         isShortcut = PR_FALSE;
01631         current->GetAttr(kNameSpaceID_None, nsXULAtoms::label, textKey);
01632         if (textKey.IsEmpty()) // No label, try another attribute (value)
01633           current->GetAttr(kNameSpaceID_None, nsXULAtoms::value, textKey);
01634       }
01635       else
01636         isShortcut = PR_TRUE;
01637 
01638       if (StringBeginsWith(textKey, incrementalString,
01639                            nsCaseInsensitiveStringComparator())) {
01640         // mIncrementalString is a prefix of textKey
01641         nsIMenuFrame* menuFrame;
01642         if (NS_SUCCEEDED(CallQueryInterface(currFrame, &menuFrame))) {
01643           // There is one match
01644           matchCount++;
01645           if (isShortcut) {
01646             // There is one shortcut-key match
01647             matchShortcutCount++;
01648             // Record the matched item. If there is only one matched shortcut item, do it
01649             frameShortcut = menuFrame;
01650           }
01651           if (!foundActive) {
01652             // It's a first candidate item located before/on the current item
01653             if (!frameBefore)
01654               frameBefore = menuFrame;
01655           }
01656           else {
01657             // It's a first candidate item located after the current item
01658             if (!frameAfter)
01659               frameAfter = menuFrame;
01660           }
01661         }
01662         else
01663           return nsnull;
01664       }
01665 
01666       // Get the active status
01667       current->GetAttr(kNameSpaceID_None, nsXULAtoms::menuactive, activeKey);
01668       if (activeKey.EqualsLiteral("true")) {
01669         foundActive = PR_TRUE;
01670         if (stringLength > 1) {
01671           // If there is more than one char typed, the current item has highest priority,
01672           //   otherwise the item next to current has highest priority
01673           nsIMenuFrame* menuFrame;
01674           if (NS_SUCCEEDED(CallQueryInterface(currFrame, &menuFrame)) &&
01675               menuFrame == frameBefore) {
01676             return frameBefore;
01677           }
01678         }
01679       }
01680     }
01681     currFrame = currFrame->GetNextSibling();
01682   }
01683 
01684   doAction = (isMenu && (matchCount == 1 || matchShortcutCount == 1));
01685 
01686   if (matchShortcutCount == 1) // We have one matched shortcut item
01687     return frameShortcut;
01688   if (frameAfter) // If we have matched item after the current, use it
01689     return frameAfter;
01690   else if (frameBefore) // If we haven't, use the item before the current
01691     return frameBefore;
01692 
01693   // If we don't match anything, rollback the last typing
01694   mIncrementalString.SetLength(mIncrementalString.Length() - 1);
01695 
01696   // didn't find a matching menu item
01697 #ifdef XP_WIN
01698   // behavior on Windows - this item is in a menu popup off of the
01699   // menu bar, so beep and do nothing else
01700   if (isMenu) {
01701     nsCOMPtr<nsISound> soundInterface = do_CreateInstance("@mozilla.org/sound;1");
01702     if (soundInterface)
01703       soundInterface->Beep();
01704   }
01705 #endif  // #ifdef XP_WIN
01706 
01707   return nsnull;
01708 }
01709 
01710 NS_IMETHODIMP 
01711 nsMenuPopupFrame::ShortcutNavigation(nsIDOMKeyEvent* aKeyEvent, PRBool& aHandledFlag)
01712 {
01713   // See if we have a context menu open.
01714   nsIMenuParent *contextMenu = GetContextMenu();
01715   if (contextMenu)
01716     return contextMenu->ShortcutNavigation(aKeyEvent, aHandledFlag);
01717 
01718   if (mCurrentMenu) {
01719     PRBool isOpen = PR_FALSE;
01720     mCurrentMenu->MenuIsOpen(isOpen);
01721     if (isOpen) {
01722       // No way this applies to us. Give it to our child.
01723       mCurrentMenu->ShortcutNavigation(aKeyEvent, aHandledFlag);
01724       return NS_OK;
01725     }
01726   }
01727 
01728   // This applies to us. Let's see if one of the shortcuts applies
01729   PRBool action;
01730   nsIMenuFrame* result = FindMenuWithShortcut(aKeyEvent, action);
01731   if (result) {
01732     // We got one!
01733     nsIFrame* frame = nsnull;
01734     CallQueryInterface(result, &frame);
01735     nsWeakFrame weakResult(frame);
01736     aHandledFlag = PR_TRUE;
01737     SetCurrentMenuItem(result);
01738     if (action && weakResult.IsAlive()) {
01739       result->Enter();
01740     }
01741   }
01742 
01743   return NS_OK;
01744 }
01745 
01746 NS_IMETHODIMP
01747 nsMenuPopupFrame::KeyboardNavigation(PRUint32 aKeyCode, PRBool& aHandledFlag)
01748 {
01749   // See if we have a context menu open.
01750   nsIMenuParent *contextMenu = GetContextMenu();
01751   if (contextMenu)
01752     return contextMenu->KeyboardNavigation(aKeyCode, aHandledFlag);
01753 
01754   nsNavigationDirection theDirection;
01755   NS_DIRECTION_FROM_KEY_CODE(theDirection, aKeyCode);
01756 
01757   mIncrementalString.Truncate();
01758 
01759   // This method only gets called if we're open.
01760   if (!mCurrentMenu && NS_DIRECTION_IS_INLINE(theDirection)) {
01761     // We've been opened, but we haven't had anything selected.
01762     // We can handle End, but our parent handles Start.
01763     if (theDirection == eNavigationDirection_End) {
01764       nsIMenuFrame* nextItem = GetNextMenuItem(nsnull);
01765       if (nextItem) {
01766         aHandledFlag = PR_TRUE;
01767         SetCurrentMenuItem(nextItem);
01768       }
01769     }
01770     return NS_OK;
01771   }
01772 
01773   PRBool isContainer = PR_FALSE;
01774   PRBool isOpen = PR_FALSE;
01775   PRBool isDisabled = PR_FALSE;
01776   nsWeakFrame weakFrame(this);
01777   if (mCurrentMenu) {
01778     mCurrentMenu->MenuIsContainer(isContainer);
01779     mCurrentMenu->MenuIsOpen(isOpen);
01780     mCurrentMenu->MenuIsDisabled(isDisabled);
01781 
01782     if (isOpen) {
01783       // Give our child a shot.
01784       mCurrentMenu->KeyboardNavigation(aKeyCode, aHandledFlag);
01785       NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK);
01786     }
01787     else if (theDirection == eNavigationDirection_End &&
01788              isContainer && !isDisabled) {
01789       // The menu is not yet open. Open it and select the first item.
01790       aHandledFlag = PR_TRUE;
01791       nsIFrame* frame = nsnull;
01792       CallQueryInterface(mCurrentMenu, &frame);
01793       nsWeakFrame weakCurrentFrame(frame);
01794       mCurrentMenu->OpenMenu(PR_TRUE);
01795       NS_ENSURE_TRUE(weakCurrentFrame.IsAlive(), NS_OK);
01796       mCurrentMenu->SelectFirstItem();
01797       NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK);
01798     }
01799   }
01800 
01801   if (aHandledFlag)
01802     return NS_OK; // The child menu took it for us.
01803 
01804   // For block progression, we can move in either direction
01805   if (NS_DIRECTION_IS_BLOCK(theDirection) ||
01806       NS_DIRECTION_IS_BLOCK_TO_EDGE(theDirection)) {
01807 
01808     nsIMenuFrame* nextItem;
01809     
01810     if (theDirection == eNavigationDirection_Before)
01811       nextItem = GetPreviousMenuItem(mCurrentMenu);
01812     else if (theDirection == eNavigationDirection_After)
01813       nextItem = GetNextMenuItem(mCurrentMenu);
01814     else if (theDirection == eNavigationDirection_First)
01815       nextItem = GetNextMenuItem(nsnull);
01816     else
01817       nextItem = GetPreviousMenuItem(nsnull);
01818 
01819     if (nextItem) {
01820       aHandledFlag = PR_TRUE;
01821       SetCurrentMenuItem(nextItem);
01822     }
01823   }
01824   else if (mCurrentMenu && isContainer && isOpen) {
01825     if (theDirection == eNavigationDirection_Start) {
01826       // Close it up.
01827       mCurrentMenu->OpenMenu(PR_FALSE);
01828       NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK);
01829       // SelectMenu() so DOMMenuItemActive is fired for accessibility
01830       mCurrentMenu->SelectMenu(PR_TRUE);
01831       aHandledFlag = PR_TRUE;
01832     }
01833   }
01834 
01835   return NS_OK;
01836 }
01837 
01838 NS_IMETHODIMP
01839 nsMenuPopupFrame::GetParentPopup(nsIMenuParent** aMenuParent)
01840 {
01841   *aMenuParent = nsnull;
01842   nsIFrame* frame = GetParent();
01843   if (frame) {
01844     nsIFrame* grandparent = frame->GetParent();
01845     if (grandparent ) {
01846       CallQueryInterface(grandparent, aMenuParent);
01847     }
01848   }
01849   return NS_OK;
01850 }
01851 
01852 NS_IMETHODIMP
01853 nsMenuPopupFrame::HideChain()
01854 {
01855   if (!mShouldRollup)
01856     return NS_OK;
01857 
01858   // Stop capturing rollups
01859   // (must do this during Hide, which happens before the menu item is executed,
01860   // since this reinstates normal event handling.)
01861   if (nsMenuFrame::sDismissalListener)
01862     nsMenuFrame::sDismissalListener->Unregister();
01863   
01864   nsIFrame* frame = GetParent();
01865   if (frame) {
01866     nsWeakFrame weakMenu(frame);
01867     nsIMenuFrame* menuFrame;
01868     if (NS_FAILED(CallQueryInterface(frame, &menuFrame))) {
01869       nsIPopupSetFrame* popupSetFrame = GetPopupSetFrame(mPresContext);
01870       if (popupSetFrame)
01871         // Hide the popup.
01872         popupSetFrame->HidePopup(this);
01873       return NS_OK;
01874     }
01875    
01876     menuFrame->ActivateMenu(PR_FALSE);
01877     NS_ENSURE_TRUE(weakMenu.IsAlive(), NS_OK);
01878     menuFrame->SelectMenu(PR_FALSE);
01879     NS_ENSURE_TRUE(weakMenu.IsAlive(), NS_OK);
01880 
01881     // Get the parent.
01882     nsIMenuParent *menuParent = menuFrame->GetMenuParent();
01883     if (menuParent)
01884       menuParent->HideChain();
01885   }
01886 
01887   return NS_OK;
01888 }
01889 
01890 NS_IMETHODIMP
01891 nsMenuPopupFrame::DismissChain()
01892 {
01893   if (!mShouldRollup)
01894     return NS_OK;
01895 
01896   // Stop capturing rollups
01897   if (nsMenuFrame::sDismissalListener)
01898     nsMenuFrame::sDismissalListener->Unregister();
01899   
01900   // Get our menu parent.
01901   nsIFrame* frame = GetParent();
01902   if (frame) {
01903     nsIMenuFrame *menuFrame = nsnull;
01904     CallQueryInterface(frame, &menuFrame);
01905     if (!menuFrame) {
01906       nsIPopupSetFrame* popupSetFrame = GetPopupSetFrame(mPresContext);
01907       if (popupSetFrame) {
01908         // make sure the menu is not highlighted
01909         if (mCurrentMenu) {
01910           PRBool wasOpen;
01911           mCurrentMenu->MenuIsOpen(wasOpen);
01912           if (wasOpen)
01913             mCurrentMenu->OpenMenu(PR_FALSE);
01914           mCurrentMenu->SelectMenu(PR_FALSE);
01915         }
01916         // Destroy the popup.
01917         popupSetFrame->DestroyPopup(this, PR_TRUE);
01918       }
01919       return NS_OK;
01920     }
01921   
01922     menuFrame->OpenMenu(PR_FALSE);
01923 
01924     // Get the parent.
01925     nsIMenuParent* menuParent = menuFrame->GetMenuParent();
01926     if (menuParent)
01927       menuParent->DismissChain();
01928   }
01929 
01930   return NS_OK;
01931 }
01932 
01933 NS_IMETHODIMP
01934 nsMenuPopupFrame::GetWidget(nsIWidget **aWidget)
01935 {
01936   // Get parent view
01937   nsIView * view = nsnull;
01938   // XXX should this be passing PR_FALSE or PR_TRUE for aStopAtViewManagerRoot?
01939   nsMenuPopupFrame::GetRootViewForPopup(this, PR_FALSE, &view);
01940   if (!view)
01941     return NS_OK;
01942 
01943   *aWidget = view->GetWidget();
01944   NS_IF_ADDREF(*aWidget);
01945   return NS_OK;
01946 }
01947 
01948 NS_IMETHODIMP
01949 nsMenuPopupFrame::CreateDismissalListener()
01950 {
01951   nsMenuDismissalListener *listener = new nsMenuDismissalListener();
01952   if (!listener) return NS_ERROR_OUT_OF_MEMORY;
01953   nsMenuFrame::sDismissalListener = listener;
01954   NS_ADDREF(listener);
01955   return NS_OK;
01956 }
01957 
01958 NS_IMETHODIMP
01959 nsMenuPopupFrame::InstallKeyboardNavigator()
01960 {
01961   if (mKeyboardNavigator)
01962     return NS_OK;
01963 
01964   nsCOMPtr<nsIDOMEventReceiver> target = do_QueryInterface(mContent->GetDocument());
01965   
01966   mTarget = target;
01967   mKeyboardNavigator = new nsMenuListener(this);
01968   NS_IF_ADDREF(mKeyboardNavigator);
01969 
01970   target->AddEventListener(NS_LITERAL_STRING("keypress"), (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE); 
01971   target->AddEventListener(NS_LITERAL_STRING("keydown"), (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE);  
01972   target->AddEventListener(NS_LITERAL_STRING("keyup"), (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE);   
01973   
01974   return NS_OK;
01975 }
01976 
01977 NS_IMETHODIMP
01978 nsMenuPopupFrame::RemoveKeyboardNavigator()
01979 {
01980   if (!mKeyboardNavigator)
01981     return NS_OK;
01982 
01983   mTarget->RemoveEventListener(NS_LITERAL_STRING("keypress"), (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE);
01984   mTarget->RemoveEventListener(NS_LITERAL_STRING("keydown"), (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE);
01985   mTarget->RemoveEventListener(NS_LITERAL_STRING("keyup"), (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE);
01986   
01987   NS_IF_RELEASE(mKeyboardNavigator);
01988 
01989   return NS_OK;
01990 }
01991 
01992 // helpers /////////////////////////////////////////////////////////////
01993 
01994 PRBool 
01995 nsMenuPopupFrame::IsValidItem(nsIContent* aContent)
01996 {
01997   nsIAtom *tag = aContent->Tag();
01998   
01999   PRBool skipNavigatingDisabledMenuItem;
02000   GetPresContext()->LookAndFeel()->
02001     GetMetric(nsILookAndFeel::eMetric_SkipNavigatingDisabledMenuItem,
02002               skipNavigatingDisabledMenuItem);
02003 
02004   PRBool result = (tag == nsXULAtoms::menu ||
02005                    tag == nsXULAtoms::menuitem ||
02006                    tag == nsHTMLAtoms::option);
02007   if (skipNavigatingDisabledMenuItem)
02008     result = result && !IsDisabled(aContent);
02009 
02010   return result;
02011 }
02012 
02013 PRBool 
02014 nsMenuPopupFrame::IsDisabled(nsIContent* aContent)
02015 {
02016   nsString disabled;
02017   aContent->GetAttr(kNameSpaceID_None, nsHTMLAtoms::disabled, disabled);
02018   if (disabled.EqualsLiteral("true"))
02019     return PR_TRUE;
02020   return PR_FALSE;
02021 }
02022 
02023 NS_IMETHODIMP 
02024 nsMenuPopupFrame::AttributeChanged(nsIContent* aChild,
02025                                    PRInt32 aNameSpaceID,
02026                                    nsIAtom* aAttribute,
02027                                    PRInt32 aModType)
02028 
02029 {
02030   nsresult rv = nsBoxFrame::AttributeChanged(aChild, aNameSpaceID,
02031                                              aAttribute, aModType);
02032   
02033   if (aAttribute == nsXULAtoms::left || aAttribute == nsXULAtoms::top)
02034     MoveToAttributePosition();
02035   
02036   return rv;
02037 }
02038 
02039 void 
02040 nsMenuPopupFrame::MoveToAttributePosition()
02041 {
02042   // Move the widget around when the user sets the |left| and |top| attributes. 
02043   // Note that this is not the best way to move the widget, as it results in lots
02044   // of FE notifications and is likely to be slow as molasses. Use |moveTo| on
02045   // nsIPopupBoxObject if possible. 
02046   nsAutoString left, top;
02047   mContent->GetAttr(kNameSpaceID_None, nsXULAtoms::left, left);
02048   mContent->GetAttr(kNameSpaceID_None, nsXULAtoms::top, top);
02049   PRInt32 err1, err2, xPos, yPos;
02050   xPos = left.ToInteger(&err1);
02051   yPos = top.ToInteger(&err2);
02052 
02053   if (NS_SUCCEEDED(err1) && NS_SUCCEEDED(err2)) {
02054     MoveToInternal(xPos, yPos);
02055   }
02056 }
02057 
02058 
02059 NS_IMETHODIMP 
02060 nsMenuPopupFrame::HandleEvent(nsPresContext* aPresContext, 
02061                               nsGUIEvent*     aEvent,
02062                               nsEventStatus*  aEventStatus)
02063 {
02064   return nsBoxFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
02065 }
02066 
02067 NS_IMETHODIMP
02068 nsMenuPopupFrame::Destroy(nsPresContext* aPresContext)
02069 {
02070   // Null out the pointer to this frame in the mediator wrapper so that it 
02071   // doesn't try to interact with a deallocated frame.
02072   mTimerMediator->ClearFrame();
02073 
02074   if (mCloseTimer)
02075     mCloseTimer->Cancel();
02076 
02077   RemoveKeyboardNavigator();
02078   return nsBoxFrame::Destroy(aPresContext);
02079 }
02080 
02081 NS_IMETHODIMP
02082 nsMenuPopupFrame::GetFrameForPoint(const nsPoint& aPoint,
02083                                    nsFramePaintLayer aWhichLayer,    
02084                                    nsIFrame** aFrame)
02085 {
02086   return nsBoxFrame::GetFrameForPoint(aPoint, aWhichLayer, aFrame);
02087 }
02088 
02089 
02090 //
02091 // Notify
02092 //
02093 // The item selection timer has fired, we might have to readjust the 
02094 // selected item. There are two cases here that we are trying to deal with:
02095 //   (1) diagonal movement from a parent menu to a submenu passing briefly over
02096 //       other items, and
02097 //   (2) moving out from a submenu to a parent or grandparent menu.
02098 // In both cases, |mTimerMenu| is the menu item that might have an open submenu and
02099 // |mCurrentMenu| is the item the mouse is currently over, which could be none of them.
02100 //
02101 // case (1):
02102 //  As the mouse moves from the parent item of a submenu (we'll call 'A') diagonally into the
02103 //  submenu, it probably passes through one or more sibilings (B). As the mouse passes
02104 //  through B, it becomes the current menu item and the timer is set and mTimerMenu is 
02105 //  set to A. Before the timer fires, the mouse leaves the menu containing A and B and
02106 //  enters the submenus. Now when the timer fires, |mCurrentMenu| is null (!= |mTimerMenu|)
02107 //  so we have to see if anything in A's children is selected (recall that even disabled
02108 //  items are selected, the style just doesn't show it). If that is the case, we need to
02109 //  set the selected item back to A.
02110 //
02111 // case (2);
02112 //  Item A has an open submenu, and in it there is an item (B) which also has an open
02113 //  submenu (so there are 3 menus displayed right now). The mouse then leaves B's child
02114 //  submenu and selects an item that is a sibling of A, call it C. When the mouse enters C,
02115 //  the timer is set and |mTimerMenu| is A and |mCurrentMenu| is C. As the timer fires,
02116 //  the mouse is still within C. The correct behavior is to set the current item to C
02117 //  and close up the chain parented at A.
02118 //
02119 //  This brings up the question of is the logic of case (1) enough? The answer is no,
02120 //  and is discussed in bugzilla bug 29400. Case (1) asks if A's submenu has a selected
02121 //  child, and if it does, set the selected item to A. Because B has a submenu open, it
02122 //  is selected and as a result, A is set to be the selected item even though the mouse
02123 //  rests in C -- very wrong. 
02124 //
02125 //  The solution is to use the same idea, but instead of only checking one level, 
02126 //  drill all the way down to the deepest open submenu and check if it has something 
02127 //  selected. Since the mouse is in a grandparent, it won't, and we know that we can
02128 //  safely close up A and all its children.
02129 //
02130 // The code below melds the two cases together.
02131 //
02132 nsresult
02133 nsMenuPopupFrame::Notify(nsITimer* aTimer)
02134 {
02135   // Our timer has fired. 
02136   if (aTimer == mCloseTimer.get()) {
02137     PRBool menuOpen = PR_FALSE;
02138     mTimerMenu->MenuIsOpen(menuOpen);
02139     if (menuOpen) {
02140       if (mCurrentMenu != mTimerMenu) {
02141         // Walk through all of the sub-menus of this menu item until we get to the
02142         // last sub-menu, then check if that sub-menu has an active menu item.  If it
02143         // does, then keep that menu open.  If it doesn't, close menu and its sub-menus.
02144         nsIFrame* child = mTimerMenu->GetMenuChild();
02145         nsIMenuFrame *currentMenuItem = nsnull;
02146         nsIMenuParent *menuParent;
02147         while (child && NS_SUCCEEDED(CallQueryInterface(child, &menuParent)))
02148         {
02149           // get the selected menu item for this sub-menu
02150           currentMenuItem = menuParent->GetCurrentMenuItem();
02151           // if this sub-menu has a selected menu item, does that item open a sub-menu?
02152           child = currentMenuItem ? currentMenuItem->GetMenuChild() : nsnull;
02153         } // while we're not at the last submenu
02154 
02155         if (currentMenuItem)
02156         {
02157           // the sub-menu has a selected menu item, we're dealing with case (1)
02158           SetCurrentMenuItem(mTimerMenu);
02159         }
02160         else {
02161           // Nothing selected. Either the mouse never made it to the submenu 
02162           // in case (1) or we're in a sibling of a grandparent in case (2).
02163           // Regardless, close up the open chain.
02164           mTimerMenu->OpenMenu(PR_FALSE);
02165         }
02166       } // if not the menu with an open submenu
02167     } // if menu open
02168     
02169     if (mCloseTimer) {
02170       mCloseTimer->Cancel();
02171     }
02172   }
02173   
02174   mCloseTimer = nsnull;
02175   mTimerMenu = nsnull;
02176   return NS_OK;
02177 }
02178 
02179 NS_IMETHODIMP
02180 nsMenuPopupFrame::KillCloseTimer()
02181 {
02182   if (mCloseTimer && mTimerMenu) {
02183     PRBool menuOpen = PR_FALSE;
02184     mTimerMenu->MenuIsOpen(menuOpen);
02185     if (menuOpen) {
02186       mTimerMenu->OpenMenu(PR_FALSE);
02187     }
02188     mCloseTimer->Cancel();
02189     mCloseTimer = nsnull;
02190     mTimerMenu = nsnull;
02191   }
02192   return NS_OK;
02193 }
02194 
02195 
02196 
02197 NS_IMETHODIMP
02198 nsMenuPopupFrame::KillPendingTimers ( )
02199 {
02200   return KillCloseTimer();
02201 
02202 } // KillPendingTimers
02203 
02204 void
02205 nsMenuPopupFrame::MoveTo(PRInt32 aLeft, PRInt32 aTop)
02206 {
02207   // Set the 'left' and 'top' attributes
02208   nsAutoString left, top;
02209   left.AppendInt(aLeft);
02210   top.AppendInt(aTop);
02211 
02212   nsWeakFrame weakFrame(this);
02213   mContent->SetAttr(kNameSpaceID_None, nsXULAtoms::left, left, PR_FALSE);
02214   if (!weakFrame.IsAlive()) {
02215     return;
02216   }
02217   mContent->SetAttr(kNameSpaceID_None, nsXULAtoms::top, top, PR_FALSE);
02218   if (!weakFrame.IsAlive()) {
02219     return;
02220   }
02221 
02222   MoveToInternal(aLeft, aTop);
02223 }
02224 
02225 void
02226 nsMenuPopupFrame::MoveToInternal(PRInt32 aLeft, PRInt32 aTop)
02227 {
02228   // just don't support moving popups for content shells
02229   if (mInContentShell)
02230     return;
02231 
02232   nsIView* view = GetView();
02233   NS_ASSERTION(view->GetParent(), "Must have parent!");
02234   
02235   // Retrieve screen position of parent view
02236   nsIntPoint screenPos = view->GetParent()->GetScreenPosition();
02237 
02238   // Move the widget
02239   // XXXbz don't we want screenPos to be the parent _widget_'s position, then?
02240   view->GetWidget()->Move(aLeft - screenPos.x, aTop - screenPos.y);
02241 }
02242 
02243 void 
02244 nsMenuPopupFrame::GetAutoPosition(PRBool* aShouldAutoPosition)
02245 {
02246   *aShouldAutoPosition = mShouldAutoPosition;
02247 }
02248 
02249 void
02250 nsMenuPopupFrame::SetAutoPosition(PRBool aShouldAutoPosition)
02251 {
02252   mShouldAutoPosition = aShouldAutoPosition;
02253 }
02254 
02255 void
02256 nsMenuPopupFrame::EnableRollup(PRBool aShouldRollup)
02257 {
02258   if (!aShouldRollup) {
02259     if (nsMenuFrame::sDismissalListener)
02260       nsMenuFrame::sDismissalListener->Unregister();
02261   }
02262   else
02263     CreateDismissalListener();
02264 }
02265 
02266 
02267 // nsMenuPopupTimerMediator implementation.
02268 NS_IMPL_ISUPPORTS1(nsMenuPopupTimerMediator, nsITimerCallback)
02269 
02270 
02274 nsMenuPopupTimerMediator::nsMenuPopupTimerMediator(nsMenuPopupFrame *aFrame) :
02275   mFrame(aFrame)
02276 {
02277   NS_ASSERTION(mFrame, "Must have frame");
02278 }
02279 
02280 nsMenuPopupTimerMediator::~nsMenuPopupTimerMediator()
02281 {
02282 }
02283 
02289 NS_IMETHODIMP nsMenuPopupTimerMediator::Notify(nsITimer* aTimer)
02290 {
02291   if (!mFrame)
02292     return NS_ERROR_FAILURE;
02293 
02294   return mFrame->Notify(aTimer);
02295 }
02296 
02301 void nsMenuPopupTimerMediator::ClearFrame()
02302 {
02303   mFrame = nsnull;
02304 }