Back to index

lightning-sunbird  0.9+nobinonly
nsXULPopupListener.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  *   Dean Tessman <dean_tessman@hotmail.com>
00025  *   Pierre Phaneuf <pp@ludusdesign.com>
00026  *   Robert O'Callahan <roc+moz@cs.cmu.edu>
00027  *
00028  * Alternatively, the contents of this file may be used under the terms of
00029  * either of the GNU General Public License Version 2 or later (the "GPL"),
00030  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00031  * in which case the provisions of the GPL or the LGPL are applicable instead
00032  * of those above. If you wish to allow use of your version of this file only
00033  * under the terms of either the GPL or the LGPL, and not to allow others to
00034  * use your version of this file under the terms of the MPL, indicate your
00035  * decision by deleting the provisions above and replace them with the notice
00036  * and other provisions required by the GPL or the LGPL. If you do not delete
00037  * the provisions above, a recipient may use your version of this file under
00038  * the terms of any one of the MPL, the GPL or the LGPL.
00039  *
00040  * ***** END LICENSE BLOCK ***** */
00041 
00042 /*
00043   This file provides the implementation for xul popup listener which
00044   tracks xul popups and context menus
00045  */
00046 
00047 #include "nsCOMPtr.h"
00048 #include "nsXULAtoms.h"
00049 #include "nsIDOMElement.h"
00050 #include "nsIDOMXULElement.h"
00051 #include "nsIDOMNodeList.h"
00052 #include "nsIDOMDocument.h"
00053 #include "nsIDOMDocumentXBL.h"
00054 #include "nsIXULPopupListener.h"
00055 #include "nsIDOMMouseListener.h"
00056 #include "nsIDOMContextMenuListener.h"
00057 #include "nsContentCID.h"
00058 #include "nsContentUtils.h"
00059 
00060 #include "nsIScriptGlobalObject.h"
00061 #include "nsIScriptContext.h"
00062 #include "nsIDOMWindowInternal.h"
00063 #include "nsIDOMXULDocument.h"
00064 #include "nsIDocument.h"
00065 #include "nsIContent.h"
00066 #include "nsIDOMMouseEvent.h"
00067 #include "nsIDOMNSUIEvent.h"
00068 #include "nsIDOMEventTarget.h"
00069 #include "nsIDOMNSEvent.h"
00070 #include "nsServiceManagerUtils.h"
00071 #include "nsIPrincipal.h"
00072 #include "nsIScriptSecurityManager.h"
00073 
00074 #include "nsIBoxObject.h"
00075 #include "nsIPopupBoxObject.h"
00076 
00077 // for event firing in context menus
00078 #include "nsPresContext.h"
00079 #include "nsIPresShell.h"
00080 #include "nsIEventStateManager.h"
00081 #include "nsIFocusController.h"
00082 #include "nsPIDOMWindow.h"
00083 
00084 #include "nsIFrame.h"
00085 
00086 // on win32 and os/2, context menus come up on mouse up. On other platforms,
00087 // they appear on mouse down. Certain bits of code care about this difference.
00088 #if !defined(XP_WIN) && !defined(XP_OS2)
00089 #define NS_CONTEXT_MENU_IS_MOUSEUP 1
00090 #endif
00091 
00092 
00094 // PopupListenerImpl
00095 //
00096 //   This is the popup listener implementation for popup menus and context menus.
00097 //
00098 class XULPopupListenerImpl : public nsIXULPopupListener,
00099                              public nsIDOMMouseListener,
00100                              public nsIDOMContextMenuListener
00101 {
00102 public:
00103     XULPopupListenerImpl(void);
00104     virtual ~XULPopupListenerImpl(void);
00105 
00106 public:
00107     // nsISupports
00108     NS_DECL_ISUPPORTS
00109 
00110     // nsIXULPopupListener
00111     NS_IMETHOD Init(nsIDOMElement* aElement, const XULPopupType& popupType);
00112 
00113     // nsIDOMMouseListener
00114     NS_IMETHOD MouseDown(nsIDOMEvent* aMouseEvent);
00115     NS_IMETHOD MouseUp(nsIDOMEvent* aMouseEvent) { return NS_OK; };
00116     NS_IMETHOD MouseClick(nsIDOMEvent* aMouseEvent) { return NS_OK; };
00117     NS_IMETHOD MouseDblClick(nsIDOMEvent* aMouseEvent) { return NS_OK; };
00118     NS_IMETHOD MouseOver(nsIDOMEvent* aMouseEvent) { return NS_OK; };
00119     NS_IMETHOD MouseOut(nsIDOMEvent* aMouseEvent) { return NS_OK; };
00120 
00121     // nsIDOMContextMenuListener
00122     NS_IMETHOD ContextMenu(nsIDOMEvent* aContextMenuEvent);
00123 
00124     // nsIDOMEventListener
00125     NS_IMETHOD HandleEvent(nsIDOMEvent* anEvent) { return NS_OK; };
00126 
00127 protected:
00128 
00129     virtual nsresult LaunchPopup(nsIDOMEvent* anEvent);
00130     virtual nsresult LaunchPopup(PRInt32 aClientX, PRInt32 aClientY) ;
00131 
00132 private:
00133 
00134     nsresult PreLaunchPopup(nsIDOMEvent* aMouseEvent);
00135     nsresult FireFocusOnTargetContent(nsIDOMNode* aTargetNode);
00136 
00137     // |mElement| is the node to which this listener is attached.
00138     nsIDOMElement* mElement;               // Weak ref. The element will go away first.
00139 
00140     // The popup that is getting shown on top of mElement.
00141     nsCOMPtr<nsIPopupBoxObject> mPopup;
00142 
00143     // The type of the popup
00144     XULPopupType popupType;
00145     
00146 };
00147 
00149       
00150 XULPopupListenerImpl::XULPopupListenerImpl(void)
00151   : mElement(nsnull)
00152 {
00153 }
00154 
00155 XULPopupListenerImpl::~XULPopupListenerImpl(void)
00156 {
00157   if (mPopup) {
00158     mPopup->HidePopup();
00159   }
00160   
00161 #ifdef DEBUG_REFS
00162     --gInstanceCount;
00163     fprintf(stdout, "%d - RDF: XULPopupListenerImpl\n", gInstanceCount);
00164 #endif
00165 }
00166 
00167 NS_IMPL_ADDREF(XULPopupListenerImpl)
00168 NS_IMPL_RELEASE(XULPopupListenerImpl)
00169 
00170 
00171 NS_INTERFACE_MAP_BEGIN(XULPopupListenerImpl)
00172   NS_INTERFACE_MAP_ENTRY(nsIXULPopupListener)
00173   NS_INTERFACE_MAP_ENTRY(nsIDOMMouseListener)
00174   NS_INTERFACE_MAP_ENTRY(nsIDOMContextMenuListener)
00175   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIDOMEventListener, nsIDOMMouseListener)
00176   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXULPopupListener)
00177 NS_INTERFACE_MAP_END
00178 
00179 NS_IMETHODIMP
00180 XULPopupListenerImpl::Init(nsIDOMElement* aElement, const XULPopupType& popup)
00181 {
00182   mElement = aElement; // Weak reference. Don't addref it.
00183   popupType = popup;
00184   return NS_OK;
00185 }
00186 
00188 // nsIDOMMouseListener
00189 
00190 nsresult
00191 XULPopupListenerImpl::MouseDown(nsIDOMEvent* aMouseEvent)
00192 {
00193   if(popupType != eXULPopupType_context)
00194     return PreLaunchPopup(aMouseEvent);
00195   else
00196     return NS_OK;
00197 }
00198 
00199 nsresult
00200 XULPopupListenerImpl::ContextMenu(nsIDOMEvent* aMouseEvent)
00201 {
00202   if(popupType == eXULPopupType_context)
00203     return PreLaunchPopup(aMouseEvent);
00204   else 
00205     return NS_OK;
00206 }
00207 
00208 nsresult
00209 XULPopupListenerImpl::PreLaunchPopup(nsIDOMEvent* aMouseEvent)
00210 {
00211   PRUint16 button;
00212 
00213   nsCOMPtr<nsIDOMMouseEvent> mouseEvent;
00214   mouseEvent = do_QueryInterface(aMouseEvent);
00215   if (!mouseEvent) {
00216     //non-ui event passed in.  bad things.
00217     return NS_OK;
00218   }
00219 
00220   // check if someone has attempted to prevent this action.
00221   nsCOMPtr<nsIDOMNSUIEvent> nsUIEvent;
00222   nsUIEvent = do_QueryInterface(mouseEvent);
00223   if (!nsUIEvent) {
00224     return NS_OK;
00225   }
00226 
00227   // Get the node that was clicked on.
00228   nsCOMPtr<nsIDOMEventTarget> target;
00229   mouseEvent->GetTarget(getter_AddRefs(target));
00230   nsCOMPtr<nsIDOMNode> targetNode = do_QueryInterface(target);
00231 
00232   PRBool preventDefault;
00233   nsUIEvent->GetPreventDefault(&preventDefault);
00234   if (preventDefault && targetNode && popupType == eXULPopupType_context) {
00235     // Someone called preventDefault on a context menu.
00236     // Let's make sure they are allowed to do so.
00237     PRBool eventEnabled =
00238       nsContentUtils::GetBoolPref("dom.event.contextmenu.enabled", PR_TRUE);
00239     if (!eventEnabled) {
00240       // The user wants his contextmenus.  Let's make sure that this is a website
00241       // and not chrome since there could be places in chrome which don't want
00242       // contextmenus.
00243       nsCOMPtr<nsIDocument> doc;
00244       nsCOMPtr<nsIPrincipal> prin;
00245       nsContentUtils::GetDocumentAndPrincipal(targetNode,
00246                                               getter_AddRefs(doc),
00247                                               getter_AddRefs(prin));
00248       if (prin) {
00249         nsCOMPtr<nsIPrincipal> system;
00250         nsContentUtils::GetSecurityManager()->GetSystemPrincipal(getter_AddRefs(system));
00251         if (prin != system) {
00252           // This isn't chrome.  Cancel the preventDefault() and
00253           // let the event go forth.
00254           preventDefault = PR_FALSE;
00255         }
00256       }
00257     }
00258   }
00259 
00260   if (preventDefault) {
00261     // someone called preventDefault. bail.
00262     return NS_OK;
00263   }
00264 
00265   // This is a gross hack to deal with a recursive popup situation happening in AIM code. 
00266   // See http://bugzilla.mozilla.org/show_bug.cgi?id=96920.
00267   // If a menu item child was clicked on that leads to a popup needing
00268   // to show, we know (guaranteed) that we're dealing with a menu or
00269   // submenu of an already-showing popup.  We don't need to do anything at all.
00270   if (popupType == eXULPopupType_popup) {
00271     nsCOMPtr<nsIContent> targetContent = do_QueryInterface(target);
00272 
00273     if (targetContent) {
00274       nsIAtom *tag = targetContent->Tag();
00275       if (tag == nsXULAtoms::menu || tag == nsXULAtoms::menuitem)
00276         return NS_OK;
00277     }
00278   }
00279 
00280   // Get the document with the popup.
00281   nsCOMPtr<nsIContent> content = do_QueryInterface(mElement);
00282 
00283   // Turn the document into a XUL document so we can use SetPopupNode
00284   nsCOMPtr<nsIDOMXULDocument2> xulDocument = do_QueryInterface(content->GetDocument());
00285   if (!xulDocument) {
00286     NS_ERROR("Popup attached to an element that isn't in XUL!");
00287     return NS_ERROR_FAILURE;
00288   }
00289 
00290   // Store clicked-on node in xul document for context menus and menu popups.
00291   // CLEAR THE POPUP EVENT BEFORE THIS FUNCTION EXITS
00292   xulDocument->SetPopupNode( targetNode );
00293   xulDocument->SetTrustedPopupEvent( aMouseEvent );
00294 
00295   switch (popupType) {
00296     case eXULPopupType_popup:
00297       // Check for left mouse button down
00298       mouseEvent->GetButton(&button);
00299       if (button == 0) {
00300         // Time to launch a popup menu.
00301         LaunchPopup(aMouseEvent);
00302         aMouseEvent->StopPropagation();
00303         aMouseEvent->PreventDefault();
00304       }
00305       break;
00306     case eXULPopupType_context:
00307 
00308     // Time to launch a context menu
00309 #ifndef NS_CONTEXT_MENU_IS_MOUSEUP
00310     // If the context menu launches on mousedown,
00311     // we have to fire focus on the content we clicked on
00312     FireFocusOnTargetContent(targetNode);
00313 #endif
00314     LaunchPopup(aMouseEvent);
00315     aMouseEvent->StopPropagation();
00316     aMouseEvent->PreventDefault();
00317     break;
00318   }
00319   xulDocument->SetTrustedPopupEvent(nsnull);
00320   return NS_OK;
00321 }
00322 
00323 nsresult
00324 XULPopupListenerImpl::FireFocusOnTargetContent(nsIDOMNode* aTargetNode)
00325 {
00326   nsresult rv;
00327   nsCOMPtr<nsIDOMDocument> domDoc;
00328   rv = aTargetNode->GetOwnerDocument(getter_AddRefs(domDoc));
00329   if(NS_SUCCEEDED(rv) && domDoc)
00330   {
00331     nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
00332 
00333     // Get nsIDOMElement for targetNode
00334     nsIPresShell *shell = doc->GetShellAt(0);
00335     if (!shell)
00336       return NS_ERROR_FAILURE;
00337 
00338     // strong reference to keep this from going away between events
00339     nsCOMPtr<nsPresContext> context = shell->GetPresContext();
00340  
00341     nsCOMPtr<nsIContent> content = do_QueryInterface(aTargetNode);
00342     nsIFrame* targetFrame;
00343     shell->GetPrimaryFrameFor(content, &targetFrame);
00344     if (!targetFrame) return NS_ERROR_FAILURE;
00345       
00346     PRBool suppressBlur = PR_FALSE;
00347     const nsStyleUserInterface* ui = targetFrame->GetStyleUserInterface();
00348     suppressBlur = (ui->mUserFocus == NS_STYLE_USER_FOCUS_IGNORE);
00349 
00350     nsCOMPtr<nsIDOMElement> element;
00351     nsCOMPtr<nsIContent> newFocus = do_QueryInterface(content);
00352 
00353     nsIFrame* currFrame = targetFrame;
00354     // Look for the nearest enclosing focusable frame.
00355     while (currFrame) {
00356         if (currFrame->IsFocusable()) {
00357           newFocus = currFrame->GetContent();
00358           nsCOMPtr<nsIDOMElement> domElement(do_QueryInterface(newFocus));
00359           if (domElement) {
00360             element = domElement;
00361             break;
00362           }
00363         }
00364         currFrame = currFrame->GetParent();
00365     } 
00366     nsCOMPtr<nsIContent> focusableContent = do_QueryInterface(element);
00367     nsIEventStateManager *esm = context->EventStateManager();
00368 
00369     if (focusableContent) {
00370       // Lock to scroll by SetFocus. See bug 309075.
00371       nsCOMPtr<nsIFocusController> focusController = nsnull;
00372       PRBool isAlreadySuppressed = PR_FALSE;
00373       nsCOMPtr<nsPIDOMWindow> ourWindow =
00374         do_QueryInterface(doc->GetScriptGlobalObject());
00375       if (ourWindow) {
00376         focusController = ourWindow->GetRootFocusController();
00377         if (focusController) {
00378           focusController->GetSuppressFocusScroll(&isAlreadySuppressed);
00379           if (!isAlreadySuppressed)
00380             focusController->SetSuppressFocusScroll(PR_TRUE);
00381         }
00382       }
00383 
00384       focusableContent->SetFocus(context);
00385 
00386       // Unlock scroll if it's needed.
00387       if (focusController && !isAlreadySuppressed)
00388         focusController->SetSuppressFocusScroll(PR_FALSE);
00389     } else if (!suppressBlur)
00390       esm->SetContentState(nsnull, NS_EVENT_STATE_FOCUS);
00391 
00392     esm->SetContentState(focusableContent, NS_EVENT_STATE_ACTIVE);
00393   }
00394   return rv;
00395 }
00396 
00397 //
00398 // LaunchPopup
00399 //
00400 nsresult
00401 XULPopupListenerImpl::LaunchPopup ( nsIDOMEvent* anEvent )
00402 {
00403   // Retrieve our x and y position.
00404   nsCOMPtr<nsIDOMMouseEvent> mouseEvent ( do_QueryInterface(anEvent) );
00405   if (!mouseEvent) {
00406     //non-ui event passed in.  bad things.
00407     return NS_OK;
00408   }
00409 
00410   PRInt32 xPos, yPos;
00411   mouseEvent->GetClientX(&xPos); 
00412   mouseEvent->GetClientY(&yPos); 
00413 
00414   return LaunchPopup(xPos, yPos);
00415 }
00416 
00417 
00418 static void
00419 GetImmediateChild(nsIContent* aContent, nsIAtom *aTag, nsIContent** aResult) 
00420 {
00421   *aResult = nsnull;
00422   PRInt32 childCount = aContent->GetChildCount();
00423   for (PRInt32 i = 0; i < childCount; i++) {
00424     nsIContent *child = aContent->GetChildAt(i);
00425     if (child->Tag() == aTag) {
00426       *aResult = child;
00427       NS_ADDREF(*aResult);
00428       return;
00429     }
00430   }
00431 
00432   return;
00433 }
00434 
00435 static void ConvertPosition(nsIDOMElement* aPopupElt, nsString& aAnchor, nsString& aAlign, PRInt32& aY)
00436 {
00437   nsAutoString position;
00438   aPopupElt->GetAttribute(NS_LITERAL_STRING("position"), position);
00439   if (position.IsEmpty())
00440     return;
00441 
00442   if (position.EqualsLiteral("before_start")) {
00443     aAnchor.AssignLiteral("topleft");
00444     aAlign.AssignLiteral("bottomleft");
00445   }
00446   else if (position.EqualsLiteral("before_end")) {
00447     aAnchor.AssignLiteral("topright");
00448     aAlign.AssignLiteral("bottomright");
00449   }
00450   else if (position.EqualsLiteral("after_start")) {
00451     aAnchor.AssignLiteral("bottomleft");
00452     aAlign.AssignLiteral("topleft");
00453   }
00454   else if (position.EqualsLiteral("after_end")) {
00455     aAnchor.AssignLiteral("bottomright");
00456     aAlign.AssignLiteral("topright");
00457   }
00458   else if (position.EqualsLiteral("start_before")) {
00459     aAnchor.AssignLiteral("topleft");
00460     aAlign.AssignLiteral("topright");
00461   }
00462   else if (position.EqualsLiteral("start_after")) {
00463     aAnchor.AssignLiteral("bottomleft");
00464     aAlign.AssignLiteral("bottomright");
00465   }
00466   else if (position.EqualsLiteral("end_before")) {
00467     aAnchor.AssignLiteral("topright");
00468     aAlign.AssignLiteral("topleft");
00469   }
00470   else if (position.EqualsLiteral("end_after")) {
00471     aAnchor.AssignLiteral("bottomright");
00472     aAlign.AssignLiteral("bottomleft");
00473   }
00474   else if (position.EqualsLiteral("overlap")) {
00475     aAnchor.AssignLiteral("topleft");
00476     aAlign.AssignLiteral("topleft");
00477   }
00478   else if (position.EqualsLiteral("after_pointer"))
00479     aY += 21;
00480 }
00481 
00482 //
00483 // LaunchPopup
00484 //
00485 // Given the element on which the event was triggered and the mouse locations in
00486 // Client and widget coordinates, popup a new window showing the appropriate 
00487 // content.
00488 //
00489 // This looks for an attribute on |aElement| of the appropriate popup type 
00490 // (popup, context) and uses that attribute's value as an ID for
00491 // the popup content in the document.
00492 //
00493 nsresult
00494 XULPopupListenerImpl::LaunchPopup(PRInt32 aClientX, PRInt32 aClientY)
00495 {
00496   nsresult rv = NS_OK;
00497 
00498   nsAutoString type(NS_LITERAL_STRING("popup"));
00499   if ( popupType == eXULPopupType_context ) {
00500     type.AssignLiteral("context");
00501     
00502     // position the menu two pixels down and to the right from the current
00503     // mouse position. This makes it easier to dismiss the menu by just
00504     // clicking.
00505     aClientX += 2;
00506     aClientY += 2;
00507   }
00508 
00509   nsAutoString identifier;
00510   mElement->GetAttribute(type, identifier);
00511 
00512   if (identifier.IsEmpty()) {
00513     if (type.EqualsLiteral("popup"))
00514       mElement->GetAttribute(NS_LITERAL_STRING("menu"), identifier);
00515     else if (type.EqualsLiteral("context"))
00516       mElement->GetAttribute(NS_LITERAL_STRING("contextmenu"), identifier);
00517     if (identifier.IsEmpty())
00518       return rv;
00519   }
00520 
00521   // Try to find the popup content and the document.
00522   nsCOMPtr<nsIContent> content = do_QueryInterface(mElement);
00523   nsCOMPtr<nsIDocument> document = content->GetDocument();
00524 
00525   // Turn the document into a DOM document so we can use getElementById
00526   nsCOMPtr<nsIDOMDocument> domDocument = do_QueryInterface(document);
00527   if (!domDocument) {
00528     NS_ERROR("Popup attached to an element that isn't in XUL!");
00529     return NS_ERROR_FAILURE;
00530   }
00531 
00532   // Handle the _child case for popups and context menus
00533   nsCOMPtr<nsIDOMElement> popupContent;
00534 
00535   if (identifier.EqualsLiteral("_child")) {
00536     nsCOMPtr<nsIContent> popup;
00537 
00538     GetImmediateChild(content, nsXULAtoms::menupopup, getter_AddRefs(popup));
00539     if (popup)
00540       popupContent = do_QueryInterface(popup);
00541     else {
00542       nsCOMPtr<nsIDOMDocumentXBL> nsDoc(do_QueryInterface(domDocument));
00543       nsCOMPtr<nsIDOMNodeList> list;
00544       nsDoc->GetAnonymousNodes(mElement, getter_AddRefs(list));
00545       if (list) {
00546         PRUint32 ctr,listLength;
00547         nsCOMPtr<nsIDOMNode> node;
00548         list->GetLength(&listLength);
00549         for (ctr = 0; ctr < listLength; ctr++) {
00550           list->Item(ctr, getter_AddRefs(node));
00551           nsCOMPtr<nsIContent> childContent(do_QueryInterface(node));
00552 
00553           nsINodeInfo *ni = childContent->GetNodeInfo();
00554 
00555           if (ni && ni->Equals(nsXULAtoms::menupopup, kNameSpaceID_XUL)) {
00556             popupContent = do_QueryInterface(childContent);
00557             break;
00558           }
00559         }
00560       }
00561     }
00562   }
00563   else if (NS_FAILED(rv = domDocument->GetElementById(identifier,
00564                                               getter_AddRefs(popupContent)))) {
00565     // Use getElementById to obtain the popup content and gracefully fail if 
00566     // we didn't find any popup content in the document. 
00567     NS_ERROR("GetElementById had some kind of spasm.");
00568     return rv;
00569   }
00570 
00571   if (!popupContent || mElement == popupContent)
00572     return NS_OK;
00573 
00574   // We have some popup content. Obtain our window.
00575   nsCOMPtr<nsIDOMWindowInternal> domWindow =
00576     do_QueryInterface(document->GetScriptGlobalObject());
00577 
00578   if (domWindow) {
00579     // Find out if we're anchored.
00580     nsAutoString anchorAlignment;
00581     popupContent->GetAttribute(NS_LITERAL_STRING("popupanchor"), anchorAlignment);
00582 
00583     nsAutoString popupAlignment;
00584     popupContent->GetAttribute(NS_LITERAL_STRING("popupalign"), popupAlignment);
00585 
00586     PRInt32 xPos = aClientX, yPos = aClientY;
00587 
00588     ConvertPosition(popupContent, anchorAlignment, popupAlignment, yPos);
00589     if (!anchorAlignment.IsEmpty() && !popupAlignment.IsEmpty())
00590       xPos = yPos = -1;
00591 
00592     nsCOMPtr<nsIBoxObject> popupBox;
00593     nsCOMPtr<nsIDOMXULElement> xulPopupElt(do_QueryInterface(popupContent));
00594     xulPopupElt->GetBoxObject(getter_AddRefs(popupBox));
00595     nsCOMPtr<nsIPopupBoxObject> popupBoxObject(do_QueryInterface(popupBox));
00596     if (popupBoxObject) {
00597       mPopup = popupBoxObject;
00598       popupBoxObject->ShowPopup(mElement, popupContent, xPos, yPos, 
00599                                 type.get(), anchorAlignment.get(), 
00600                                 popupAlignment.get());
00601     }
00602   }
00603 
00604   return NS_OK;
00605 }
00606 
00608 nsresult
00609 NS_NewXULPopupListener(nsIXULPopupListener** pop)
00610 {
00611     XULPopupListenerImpl* popup = new XULPopupListenerImpl();
00612     if (!popup)
00613       return NS_ERROR_OUT_OF_MEMORY;
00614     
00615     NS_ADDREF(popup);
00616     *pop = popup;
00617     return NS_OK;
00618 }