Back to index

lightning-sunbird  0.9+nobinonly
nsXULTooltipListener.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  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "nsXULTooltipListener.h"
00039 
00040 #include "nsIDOMMouseEvent.h"
00041 #include "nsIDOMEventTarget.h"
00042 #include "nsIDOMDocument.h"
00043 #include "nsIDOMXULDocument.h"
00044 #include "nsIDOMXULElement.h"
00045 #include "nsIDocument.h"
00046 #include "nsXULAtoms.h"
00047 #include "nsIPresShell.h"
00048 #include "nsIFrame.h"
00049 #include "nsIPopupBoxObject.h"
00050 #include "nsIServiceManager.h"
00051 #ifdef MOZ_XUL
00052 #include "nsIDOMNSDocument.h"
00053 #include "nsITreeView.h"
00054 #endif
00055 #include "nsGUIEvent.h"
00056 #include "nsIPrivateDOMEvent.h"
00057 #include "nsPresContext.h"
00058 #include "nsIScriptContext.h"
00059 #include "nsIScriptGlobalObject.h"
00060 #include "nsIDOMWindowInternal.h"
00061 #include "nsContentUtils.h"
00062 
00065 
00066 nsXULTooltipListener::nsXULTooltipListener()
00067   : mSourceNode(nsnull)
00068   , mTargetNode(nsnull)
00069   , mCurrentTooltip(nsnull)
00070   , mMouseClientX(0)
00071   , mMouseClientY(0)
00072 #ifdef MOZ_XUL
00073   , mIsSourceTree(PR_FALSE)
00074   , mNeedTitletip(PR_FALSE)
00075   , mLastTreeRow(-1)
00076 #endif
00077 {
00078   if (sTooltipListenerCount++ == 0) {
00079     // register the callback so we get notified of updates
00080     nsContentUtils::RegisterPrefCallback("browser.chrome.toolbar_tips",
00081                                          ToolbarTipsPrefChanged, nsnull);
00082 
00083     // Call the pref callback to initialize our state.
00084     ToolbarTipsPrefChanged("browser.chrome.toolbar_tips", nsnull);
00085   }
00086 }
00087 
00088 nsXULTooltipListener::~nsXULTooltipListener()
00089 {
00090   HideTooltip();
00091 
00092   if (--sTooltipListenerCount == 0) {
00093     // Unregister our pref observer
00094     nsContentUtils::UnregisterPrefCallback("browser.chrome.toolbar_tips",
00095                                            ToolbarTipsPrefChanged, nsnull);
00096   }
00097 }
00098 
00099 NS_IMPL_ADDREF(nsXULTooltipListener)
00100 NS_IMPL_RELEASE(nsXULTooltipListener)
00101 
00102 NS_INTERFACE_MAP_BEGIN(nsXULTooltipListener)
00103   NS_INTERFACE_MAP_ENTRY(nsIDOMMouseListener)
00104   NS_INTERFACE_MAP_ENTRY(nsIDOMMouseMotionListener)
00105   NS_INTERFACE_MAP_ENTRY(nsIDOMKeyListener)
00106   NS_INTERFACE_MAP_ENTRY(nsIDOMXULListener)
00107   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIDOMEventListener, nsIDOMMouseListener)
00108   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMMouseMotionListener)
00109 NS_INTERFACE_MAP_END
00110 
00113 
00114 NS_IMETHODIMP
00115 nsXULTooltipListener::MouseDown(nsIDOMEvent* aMouseEvent)
00116 {
00117   HideTooltip();
00118 
00119   return NS_OK;
00120 }
00121 
00122 NS_IMETHODIMP
00123 nsXULTooltipListener::MouseUp(nsIDOMEvent* aMouseEvent)
00124 {
00125   HideTooltip();
00126 
00127   return NS_OK;
00128 }
00129 
00130 NS_IMETHODIMP
00131 nsXULTooltipListener::MouseOut(nsIDOMEvent* aMouseEvent)
00132 {
00133   // if the timer is running and no tooltip is shown, we
00134   // have to cancel the timer here so that it doesn't 
00135   // show the tooltip if we move the mouse out of the window
00136   if (mTooltipTimer && !mCurrentTooltip) {
00137     mTooltipTimer->Cancel();
00138     mTooltipTimer = nsnull;
00139     return NS_OK;
00140   }
00141 
00142 #ifdef DEBUG_crap
00143   if (mNeedTitletip)
00144     return NS_OK;
00145 #endif
00146 
00147   // check to see if the mouse left the targetNode, and if so,
00148   // hide the tooltip
00149   if (mCurrentTooltip) {
00150     // which node did the mouse leave?
00151     nsCOMPtr<nsIDOMEventTarget> eventTarget;
00152     aMouseEvent->GetTarget(getter_AddRefs(eventTarget));
00153     nsCOMPtr<nsIDOMNode> targetNode(do_QueryInterface(eventTarget));
00154 
00155     // which node is our tooltip on?
00156     nsCOMPtr<nsIDOMXULDocument2> xulDoc(do_QueryInterface(mCurrentTooltip->GetDocument()));
00157     if (!xulDoc)     // remotely possible someone could have 
00158       return NS_OK;  // removed tooltip from dom while it was open
00159     nsCOMPtr<nsIDOMNode> tooltipNode;
00160     xulDoc->TrustedGetTooltipNode (getter_AddRefs(tooltipNode));
00161 
00162     // if they're the same, the mouse left the node the tooltip appeared on,
00163     // close the tooltip.
00164     if (tooltipNode == targetNode) {
00165       HideTooltip();
00166 #ifdef MOZ_XUL
00167       // reset special tree tracking
00168       if (mIsSourceTree) {
00169         mLastTreeRow = -1;
00170         mLastTreeCol = nsnull;
00171       }
00172 #endif
00173     }
00174   }
00175 
00176   return NS_OK;
00177 }
00178 
00181 
00182 NS_IMETHODIMP
00183 nsXULTooltipListener::MouseMove(nsIDOMEvent* aMouseEvent)
00184 {
00185   if (!sShowTooltips)
00186     return NS_OK;
00187 
00188   // stash the coordinates of the event so that we can still get back to it from within the 
00189   // timer callback. On win32, we'll get a MouseMove event even when a popup goes away --
00190   // even when the mouse doesn't change position! To get around this, we make sure the
00191   // mouse has really moved before proceeding.
00192   nsCOMPtr<nsIDOMMouseEvent> mouseEvent(do_QueryInterface(aMouseEvent));
00193   PRInt32 newMouseX, newMouseY;
00194   mouseEvent->GetClientX(&newMouseX);
00195   mouseEvent->GetClientY(&newMouseY);
00196   if (mMouseClientX == newMouseX && mMouseClientY == newMouseY)
00197     return NS_OK;
00198   mMouseClientX = newMouseX;
00199   mMouseClientY = newMouseY;
00200 
00201 #ifdef MOZ_XUL
00202   if (mIsSourceTree)
00203     CheckTreeBodyMove(mouseEvent);
00204 #endif
00205 
00206   // as the mouse moves, we want to make sure we reset the timer to show it, 
00207   // so that the delay is from when the mouse stops moving, not when it enters
00208   // the node.
00209   KillTooltipTimer();
00210     
00211   // If the mouse moves while the tooltip is up, don't do anything. We make it
00212   // go away only if it times out or leaves the target node. If nothing is
00213   // showing, though, we have to do the work.
00214   if (!mCurrentTooltip) {
00215     mTooltipTimer = do_CreateInstance("@mozilla.org/timer;1");
00216     if (mTooltipTimer) {
00217       nsCOMPtr<nsIDOMEventTarget> eventTarget;
00218       aMouseEvent->GetTarget(getter_AddRefs(eventTarget));
00219       if (eventTarget) {
00220         nsCOMPtr<nsIContent> targetContent(do_QueryInterface(eventTarget));
00221         mTargetNode = targetContent;
00222       }
00223       if (mTargetNode) {
00224         nsresult rv = mTooltipTimer->InitWithFuncCallback(sTooltipCallback, this, 
00225                                                           kTooltipShowTime, nsITimer::TYPE_ONE_SHOT);
00226         if (NS_FAILED(rv))
00227           mTargetNode = nsnull;
00228       }
00229     }
00230   }
00231 
00232   return NS_OK;
00233 }
00234 
00237 
00238 NS_IMETHODIMP
00239 nsXULTooltipListener::KeyDown(nsIDOMEvent* aKeyEvent)
00240 {
00241   HideTooltip();
00242   return NS_OK;
00243 }
00244 
00247 
00248 
00249 NS_IMETHODIMP
00250 nsXULTooltipListener::HandleEvent(nsIDOMEvent* aEvent)
00251 {
00252   nsAutoString type;
00253   aEvent->GetType(type);
00254   if (type.EqualsLiteral("DOMMouseScroll"))
00255     HideTooltip();
00256   return NS_OK;
00257 }
00258 
00261 
00262 // static
00263 int
00264 nsXULTooltipListener::ToolbarTipsPrefChanged(const char *aPref,
00265                                              void *aClosure)
00266 {
00267   sShowTooltips = nsContentUtils::GetBoolPref("browser.chrome.toolbar_tips",
00268                                               sShowTooltips);
00269 
00270   return 0;
00271 }
00272 
00273 NS_IMETHODIMP
00274 nsXULTooltipListener::PopupHiding(nsIDOMEvent* aEvent)
00275 {
00276   DestroyTooltip();
00277   return NS_OK;
00278 }
00279 
00282 
00283 PRBool nsXULTooltipListener::sShowTooltips = PR_FALSE;
00284 PRUint32 nsXULTooltipListener::sTooltipListenerCount = 0;
00285 
00286 // XXX: This could all be done in the ctor.
00287 nsresult
00288 nsXULTooltipListener::Init(nsIContent* aSourceNode, nsIRootBox* aRootBox)
00289 {
00290   mRootBox = aRootBox;
00291   mSourceNode = aSourceNode;
00292   AddTooltipSupport(aSourceNode);
00293   
00294 #ifdef MOZ_XUL
00295   // if the target is an treechildren, we may have some special
00296   // case handling to do
00297   mIsSourceTree = mSourceNode->Tag() == nsXULAtoms::treechildren;
00298 #endif
00299 
00300   return NS_OK;
00301 }
00302 
00303 nsresult
00304 nsXULTooltipListener::AddTooltipSupport(nsIContent* aNode)
00305 {
00306   if (!aNode)
00307     return NS_ERROR_NULL_POINTER;
00308 
00309   nsCOMPtr<nsIDOMEventTarget> evtTarget(do_QueryInterface(aNode));
00310   evtTarget->AddEventListener(NS_LITERAL_STRING("mouseout"), (nsIDOMMouseListener*)this, PR_FALSE);
00311   evtTarget->AddEventListener(NS_LITERAL_STRING("mousemove"), (nsIDOMMouseListener*)this, PR_FALSE);
00312   
00313   return NS_OK;
00314 }
00315 
00316 nsresult
00317 nsXULTooltipListener::RemoveTooltipSupport(nsIContent* aNode)
00318 {
00319   if (!aNode)
00320     return NS_ERROR_NULL_POINTER;
00321 
00322   nsCOMPtr<nsIDOMEventTarget> evtTarget(do_QueryInterface(aNode));
00323   evtTarget->RemoveEventListener(NS_LITERAL_STRING("mouseout"), (nsIDOMMouseListener*)this, PR_FALSE);
00324   evtTarget->RemoveEventListener(NS_LITERAL_STRING("mousemove"), (nsIDOMMouseListener*)this, PR_FALSE);
00325 
00326   return NS_OK;
00327 }
00328 
00329 #ifdef MOZ_XUL
00330 void
00331 nsXULTooltipListener::CheckTreeBodyMove(nsIDOMMouseEvent* aMouseEvent)
00332 {
00333   if (!mSourceNode)
00334     return;
00335 
00336   // get the boxObject of the documentElement of the document the tree is in
00337   nsCOMPtr<nsIBoxObject> bx;
00338   nsCOMPtr<nsIDOMDocument> doc(do_QueryInterface(mSourceNode->GetDocument()));
00339   if (doc) {
00340     nsCOMPtr<nsIDOMNSDocument> nsDoc(do_QueryInterface(doc));
00341     nsCOMPtr<nsIDOMElement> docElement;
00342     doc->GetDocumentElement(getter_AddRefs(docElement));
00343     if (nsDoc && docElement) {
00344       nsDoc->GetBoxObjectFor(docElement, getter_AddRefs(bx));
00345     }
00346   }
00347 
00348   nsCOMPtr<nsITreeBoxObject> obx;
00349   GetSourceTreeBoxObject(getter_AddRefs(obx));
00350   if (bx && obx) {
00351     PRInt32 x, y;
00352     aMouseEvent->GetScreenX(&x);
00353     aMouseEvent->GetScreenY(&y);
00354 
00355     PRInt32 row;
00356     nsCOMPtr<nsITreeColumn> col;
00357     nsCAutoString obj;
00358 
00359     // subtract off the documentElement's boxObject
00360     PRInt32 boxX, boxY;
00361     bx->GetScreenX(&boxX);
00362     bx->GetScreenY(&boxY);
00363     x -= boxX;
00364     y -= boxY;
00365 
00366     obx->GetCellAt(x, y, &row, getter_AddRefs(col), obj);
00367 
00368     // determine if we are going to need a titletip
00369     // XXX check the disabletitletips attribute on the tree content
00370     mNeedTitletip = PR_FALSE;
00371     if (row >= 0 && obj.EqualsLiteral("text")) {
00372       PRBool isCropped;
00373       obx->IsCellCropped(row, col, &isCropped);
00374       mNeedTitletip = isCropped;
00375     }
00376 
00377     if (mCurrentTooltip && (row != mLastTreeRow || col != mLastTreeCol)) {
00378       HideTooltip();
00379     } 
00380 
00381     mLastTreeRow = row;
00382     mLastTreeCol = col;
00383   }
00384 }
00385 #endif
00386 
00387 nsresult
00388 nsXULTooltipListener::ShowTooltip()
00389 {
00390   // get the tooltip content designated for the target node 
00391   GetTooltipFor(mSourceNode, getter_AddRefs(mCurrentTooltip));
00392   if (!mCurrentTooltip || mSourceNode == mCurrentTooltip)
00393     return NS_ERROR_FAILURE; // the target node doesn't need a tooltip
00394 
00395   // set the node in the document that triggered the tooltip and show it
00396   nsCOMPtr<nsIDOMXULDocument> xulDoc(do_QueryInterface(mCurrentTooltip->GetDocument()));
00397   if (xulDoc) {
00398     // Make sure the target node is still attached to some document. 
00399     // It might have been deleted.
00400     if (mSourceNode->GetDocument()) {
00401 #ifdef MOZ_XUL
00402       if (!mIsSourceTree) {
00403         mLastTreeRow = -1;
00404         mLastTreeCol = nsnull;
00405       }
00406 #endif
00407 
00408       nsCOMPtr<nsIDOMNode> targetNode(do_QueryInterface(mTargetNode));
00409       xulDoc->SetTooltipNode(targetNode);
00410       LaunchTooltip(mSourceNode, mMouseClientX, mMouseClientY);
00411       mTargetNode = nsnull;
00412 
00413       // at this point, |mCurrentTooltip| holds the content node of
00414       // the tooltip. If there is an attribute on the popup telling us
00415       // not to create the auto-hide timer, don't.
00416       nsCOMPtr<nsIDOMElement> tooltipEl(do_QueryInterface(mCurrentTooltip));
00417       if (!tooltipEl)
00418         return NS_ERROR_FAILURE;
00419       nsAutoString noAutoHide;
00420       tooltipEl->GetAttribute(NS_LITERAL_STRING("noautohide"), noAutoHide);
00421       if (!noAutoHide.EqualsLiteral("true"))
00422         CreateAutoHideTimer();
00423 
00424       // listen for popuphidden on the tooltip node, so that we can
00425       // be sure DestroyPopup is called even if someone else closes the tooltip
00426       nsCOMPtr<nsIDOMEventTarget> evtTarget(do_QueryInterface(mCurrentTooltip));
00427       evtTarget->AddEventListener(NS_LITERAL_STRING("popuphiding"), 
00428                                   (nsIDOMMouseListener*)this, PR_FALSE);
00429 
00430       // listen for mousedown, mouseup, keydown, and DOMMouseScroll events at document level
00431       nsIDocument* doc = mSourceNode->GetDocument();
00432       if (doc) {
00433         evtTarget = do_QueryInterface(doc);
00434         evtTarget->AddEventListener(NS_LITERAL_STRING("DOMMouseScroll"), 
00435                                     (nsIDOMMouseListener*)this, PR_TRUE);
00436         evtTarget->AddEventListener(NS_LITERAL_STRING("mousedown"), 
00437                                     (nsIDOMMouseListener*)this, PR_TRUE);
00438         evtTarget->AddEventListener(NS_LITERAL_STRING("mouseup"), 
00439                                     (nsIDOMMouseListener*)this, PR_TRUE);                                    
00440         evtTarget->AddEventListener(NS_LITERAL_STRING("keydown"), 
00441                                     (nsIDOMMouseListener*)this, PR_TRUE);
00442       }
00443     }
00444   }
00445 
00446   return NS_OK;
00447 }
00448 
00449 #ifdef MOZ_XUL
00450 // XXX: "This stuff inside DEBUG_crap could be used to make tree tooltips work
00451 //       in the future."
00452 #ifdef DEBUG_crap
00453 static void
00454 GetTreeCellCoords(nsITreeBoxObject* aTreeBox, nsIContent* aSourceNode, 
00455                   PRInt32 aRow, nsITreeColumn* aCol, PRInt32* aX, PRInt32* aY)
00456 {
00457   PRInt32 junk;
00458   aTreeBox->GetCoordsForCellItem(aRow, aCol, EmptyCString(), aX, aY, &junk, &junk);
00459   nsCOMPtr<nsIDOMXULElement> xulEl(do_QueryInterface(aSourceNode));
00460   nsCOMPtr<nsIBoxObject> bx;
00461   xulEl->GetBoxObject(getter_AddRefs(bx));
00462   PRInt32 myX, myY;
00463   bx->GetX(&myX);
00464   bx->GetY(&myY);
00465   *aX += myX;
00466   *aY += myY;
00467 }
00468 #endif
00469 
00470 static void
00471 SetTitletipLabel(nsITreeBoxObject* aTreeBox, nsIContent* aTooltip,
00472                  PRInt32 aRow, nsITreeColumn* aCol)
00473 {
00474   nsCOMPtr<nsITreeView> view;
00475   aTreeBox->GetView(getter_AddRefs(view));
00476 
00477   nsAutoString label;
00478   view->GetCellText(aRow, aCol, label);
00479   
00480   aTooltip->SetAttr(nsnull, nsXULAtoms::label, label, PR_TRUE);
00481 }
00482 #endif
00483 
00484 nsresult
00485 nsXULTooltipListener::LaunchTooltip(nsIContent* aTarget, PRInt32 aX, PRInt32 aY)
00486 {
00487   if (!mCurrentTooltip)
00488     return NS_OK;
00489 
00490   nsCOMPtr<nsIBoxObject> popupBox;
00491   nsCOMPtr<nsIDOMXULElement> xulTooltipEl(do_QueryInterface(mCurrentTooltip));
00492   if (!xulTooltipEl) {
00493     NS_ERROR("tooltip isn't a XUL element!");
00494     return NS_ERROR_FAILURE;
00495   }
00496 
00497   xulTooltipEl->GetBoxObject(getter_AddRefs(popupBox));
00498   nsCOMPtr<nsIPopupBoxObject> popupBoxObject(do_QueryInterface(popupBox));
00499   if (popupBoxObject) {
00500     PRInt32 x = aX;
00501     PRInt32 y = aY;
00502 #ifdef MOZ_XUL
00503     if (mNeedTitletip) {
00504       nsCOMPtr<nsITreeBoxObject> obx;
00505       GetSourceTreeBoxObject(getter_AddRefs(obx));
00506 #ifdef DEBUG_crap
00507       GetTreeCellCoords(obx, mSourceNode,
00508                         mLastTreeRow, mLastTreeCol, &x, &y);
00509 #endif
00510 
00511       SetTitletipLabel(obx, mCurrentTooltip, mLastTreeRow, mLastTreeCol);
00512       mCurrentTooltip->SetAttr(nsnull, nsXULAtoms::titletip, NS_LITERAL_STRING("true"), PR_TRUE);
00513     } else
00514       mCurrentTooltip->UnsetAttr(nsnull, nsXULAtoms::titletip, PR_TRUE);
00515 #endif
00516 
00517     nsCOMPtr<nsIDOMElement> targetEl(do_QueryInterface(aTarget));
00518     popupBoxObject->ShowPopup(targetEl, xulTooltipEl, x, y,
00519                               NS_LITERAL_STRING("tooltip").get(),
00520                               NS_LITERAL_STRING("none").get(),
00521                               NS_LITERAL_STRING("topleft").get());
00522   }
00523 
00524   return NS_OK;
00525 }
00526 
00527 nsresult
00528 nsXULTooltipListener::HideTooltip()
00529 {
00530   if (mCurrentTooltip) {
00531     // hide the popup through its box object
00532     nsCOMPtr<nsIDOMXULElement> tooltipEl(do_QueryInterface(mCurrentTooltip));
00533     nsCOMPtr<nsIBoxObject> boxObject;
00534     if (tooltipEl)
00535       tooltipEl->GetBoxObject(getter_AddRefs(boxObject));
00536     nsCOMPtr<nsIPopupBoxObject> popupObject(do_QueryInterface(boxObject));
00537     if (popupObject)
00538       popupObject->HidePopup();
00539   }
00540 
00541   DestroyTooltip();
00542   return NS_OK;
00543 }
00544 
00545 static void
00546 GetImmediateChild(nsIContent* aContent, nsIAtom *aTag, nsIContent** aResult) 
00547 {
00548   *aResult = nsnull;
00549   PRUint32 childCount = aContent->GetChildCount();
00550   for (PRUint32 i = 0; i < childCount; i++) {
00551     nsIContent *child = aContent->GetChildAt(i);
00552 
00553     if (child->Tag() == aTag) {
00554       *aResult = child;
00555       NS_ADDREF(*aResult);
00556       return;
00557     }
00558   }
00559 
00560   return;
00561 }
00562 
00563 nsresult
00564 nsXULTooltipListener::GetTooltipFor(nsIContent* aTarget, nsIContent** aTooltip)
00565 {
00566   if (!aTarget)
00567     return NS_ERROR_NULL_POINTER;
00568 
00569   nsCOMPtr<nsIDOMElement> targetEl(do_QueryInterface(aTarget));
00570   if (!targetEl)
00571     return NS_ERROR_FAILURE; // could be a text node or something
00572 
00573   // before we go on, make sure that target node still has a window
00574   nsCOMPtr<nsIDocument> document = aTarget->GetDocument();
00575   if (!document) {
00576     NS_ERROR("Unable to retrieve the tooltip node document.");
00577     return NS_ERROR_FAILURE;
00578   }
00579   nsIScriptGlobalObject *global = document->GetScriptGlobalObject();
00580   if (global) {
00581     nsIScriptContext *context = global->GetContext();
00582 
00583     if (context) {
00584       nsCOMPtr<nsIDOMWindowInternal> domWindow = do_QueryInterface(global);
00585       if (!domWindow)
00586         return NS_ERROR_FAILURE;
00587       else {
00588         PRBool needTooltip;
00589         targetEl->HasAttribute(NS_LITERAL_STRING("tooltiptext"), &needTooltip);
00590         if (needTooltip) {
00591           // specifying tooltiptext means we will always use the default tooltip
00592            mRootBox->GetDefaultTooltip(aTooltip);
00593            NS_IF_ADDREF(*aTooltip);
00594            return NS_OK;
00595         } else {
00596           nsAutoString tooltipId;
00597           targetEl->GetAttribute(NS_LITERAL_STRING("tooltip"), tooltipId);
00598 
00599           // if tooltip == _child, look for first <tooltip> child
00600           if (tooltipId.EqualsLiteral("_child")) {
00601             GetImmediateChild(aTarget, nsXULAtoms::tooltip, aTooltip);
00602             return NS_OK;
00603           } else {
00604             if (!tooltipId.IsEmpty()) {
00605               // tooltip must be an id, use getElementById to find it
00606               nsCOMPtr<nsIDOMDocument> domDocument =
00607                 do_QueryInterface(document);
00608               if (!domDocument) {
00609                 return NS_ERROR_FAILURE;
00610               }
00611 
00612               nsCOMPtr<nsIDOMElement> tooltipEl;
00613               domDocument->GetElementById(tooltipId,
00614                                           getter_AddRefs(tooltipEl));
00615 
00616               if (tooltipEl) {
00617 #ifdef MOZ_XUL
00618                 mNeedTitletip = PR_FALSE;
00619 #endif
00620 
00621                 nsCOMPtr<nsIContent> tooltipContent(do_QueryInterface(tooltipEl));
00622                 *aTooltip = tooltipContent;
00623                 NS_IF_ADDREF(*aTooltip);
00624 
00625                 return NS_OK;
00626               }
00627             }
00628           }
00629         }
00630 
00631 #ifdef MOZ_XUL
00632         // titletips should just use the default tooltip
00633         if (mIsSourceTree && mNeedTitletip) {
00634           mRootBox->GetDefaultTooltip(aTooltip);
00635           NS_IF_ADDREF(*aTooltip);
00636           return NS_OK;
00637         }
00638 #endif
00639       }
00640     }
00641   }
00642 
00643   return NS_OK;
00644 }
00645 
00646 nsresult
00647 nsXULTooltipListener::DestroyTooltip()
00648 {
00649   nsCOMPtr<nsIDOMMouseListener> kungFuDeathGrip(this);
00650   if (mCurrentTooltip) {
00651     // clear out the tooltip node on the document
00652     nsCOMPtr<nsIDocument> doc = mCurrentTooltip->GetDocument();
00653     if (doc) {
00654       nsCOMPtr<nsIDOMXULDocument> xulDoc(do_QueryInterface(doc));
00655       if (xulDoc)
00656         xulDoc->SetTooltipNode(nsnull);
00657 
00658       // remove the mousedown and keydown listener from document
00659       nsCOMPtr<nsIDOMEventTarget> evtTarget(do_QueryInterface(doc));
00660       evtTarget->RemoveEventListener(NS_LITERAL_STRING("DOMMouseScroll"), (nsIDOMMouseListener*)this, PR_TRUE);
00661       evtTarget->RemoveEventListener(NS_LITERAL_STRING("mousedown"), (nsIDOMMouseListener*)this, PR_TRUE);
00662       evtTarget->RemoveEventListener(NS_LITERAL_STRING("mouseup"), (nsIDOMMouseListener*)this, PR_TRUE);
00663       evtTarget->RemoveEventListener(NS_LITERAL_STRING("keydown"), (nsIDOMMouseListener*)this, PR_TRUE);
00664     }
00665 
00666     // remove the popuphidden listener from tooltip
00667     nsCOMPtr<nsIDOMEventTarget> evtTarget(do_QueryInterface(mCurrentTooltip));
00668 
00669     // release tooltip before removing listener to prevent our destructor from
00670     // being called recursively (bug 120863)
00671     mCurrentTooltip = nsnull;
00672 
00673     evtTarget->RemoveEventListener(NS_LITERAL_STRING("popuphiding"), (nsIDOMMouseListener*)this, PR_FALSE);
00674   }
00675   
00676   // kill any ongoing timers
00677   KillTooltipTimer();
00678   if (mAutoHideTimer) {
00679     mAutoHideTimer->Cancel();
00680     mAutoHideTimer = nsnull;
00681   }
00682 
00683   return NS_OK;
00684 }
00685 
00686 void
00687 nsXULTooltipListener::KillTooltipTimer()
00688 {
00689   if (mTooltipTimer) {
00690     mTooltipTimer->Cancel();
00691     mTooltipTimer = nsnull;
00692     mTargetNode = nsnull;
00693   }
00694 }
00695 
00696 void
00697 nsXULTooltipListener::CreateAutoHideTimer()
00698 {
00699   if (mAutoHideTimer) {
00700     mAutoHideTimer->Cancel();
00701     mAutoHideTimer = nsnull;
00702   }
00703   
00704   mAutoHideTimer = do_CreateInstance("@mozilla.org/timer;1");
00705   if ( mAutoHideTimer )
00706     mAutoHideTimer->InitWithFuncCallback(sAutoHideCallback, this, kTooltipAutoHideTime, 
00707                                          nsITimer::TYPE_ONE_SHOT);
00708 }
00709 
00710 void
00711 nsXULTooltipListener::sTooltipCallback(nsITimer *aTimer, void *aListener)
00712 {
00713   nsXULTooltipListener* self = NS_STATIC_CAST(nsXULTooltipListener*, aListener);
00714   if (self)
00715     self->ShowTooltip();
00716 }
00717 
00718 void
00719 nsXULTooltipListener::sAutoHideCallback(nsITimer *aTimer, void* aListener)
00720 {
00721   nsXULTooltipListener* self = NS_STATIC_CAST(nsXULTooltipListener*, aListener);
00722   if (self) {
00723     nsCOMPtr<nsIDOMMouseListener> kungFuDeathGrip(self);
00724     self->HideTooltip();
00725   }
00726 }
00727 
00728 #ifdef MOZ_XUL
00729 nsresult
00730 nsXULTooltipListener::GetSourceTreeBoxObject(nsITreeBoxObject** aBoxObject)
00731 {
00732   *aBoxObject = nsnull;
00733 
00734   if (mIsSourceTree && mSourceNode) {
00735     nsCOMPtr<nsIDOMXULElement> xulEl(do_QueryInterface(mSourceNode->GetParent()));
00736     if (xulEl) {
00737       nsCOMPtr<nsIBoxObject> bx;
00738       xulEl->GetBoxObject(getter_AddRefs(bx));
00739       nsCOMPtr<nsITreeBoxObject> obx(do_QueryInterface(bx));
00740       if (obx) {
00741         *aBoxObject = obx;
00742         NS_ADDREF(*aBoxObject);
00743         return NS_OK;
00744       }
00745     }
00746   }
00747   return NS_ERROR_FAILURE;
00748 }
00749 #endif