layout/xul/nsXULTooltipListener.cpp

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "nsXULTooltipListener.h"
     8 #include "nsIDOMMouseEvent.h"
     9 #include "nsIDOMXULDocument.h"
    10 #include "nsIDOMXULElement.h"
    11 #include "nsIDocument.h"
    12 #include "nsGkAtoms.h"
    13 #include "nsIPopupBoxObject.h"
    14 #include "nsMenuPopupFrame.h"
    15 #include "nsIServiceManager.h"
    16 #include "nsIDragService.h"
    17 #include "nsIDragSession.h"
    18 #ifdef MOZ_XUL
    19 #include "nsITreeView.h"
    20 #endif
    21 #include "nsIScriptContext.h"
    22 #include "nsPIDOMWindow.h"
    23 #ifdef MOZ_XUL
    24 #include "nsXULPopupManager.h"
    25 #endif
    26 #include "nsIRootBox.h"
    27 #include "nsIBoxObject.h"
    28 #include "mozilla/Preferences.h"
    29 #include "mozilla/LookAndFeel.h"
    30 #include "mozilla/dom/Element.h"
    31 #include "mozilla/dom/Event.h" // for nsIDOMEvent::InternalDOMEvent()
    33 using namespace mozilla;
    34 using namespace mozilla::dom;
    36 nsXULTooltipListener* nsXULTooltipListener::mInstance = nullptr;
    38 //////////////////////////////////////////////////////////////////////////
    39 //// nsISupports
    41 nsXULTooltipListener::nsXULTooltipListener()
    42   : mMouseScreenX(0)
    43   , mMouseScreenY(0)
    44   , mTooltipShownOnce(false)
    45 #ifdef MOZ_XUL
    46   , mIsSourceTree(false)
    47   , mNeedTitletip(false)
    48   , mLastTreeRow(-1)
    49 #endif
    50 {
    51   if (sTooltipListenerCount++ == 0) {
    52     // register the callback so we get notified of updates
    53     Preferences::RegisterCallback(ToolbarTipsPrefChanged,
    54                                   "browser.chrome.toolbar_tips");
    56     // Call the pref callback to initialize our state.
    57     ToolbarTipsPrefChanged("browser.chrome.toolbar_tips", nullptr);
    58   }
    59 }
    61 nsXULTooltipListener::~nsXULTooltipListener()
    62 {
    63   if (nsXULTooltipListener::mInstance == this) {
    64     ClearTooltipCache();
    65   }
    66   HideTooltip();
    68   if (--sTooltipListenerCount == 0) {
    69     // Unregister our pref observer
    70     Preferences::UnregisterCallback(ToolbarTipsPrefChanged,
    71                                     "browser.chrome.toolbar_tips");
    72   }
    73 }
    75 NS_IMPL_ISUPPORTS(nsXULTooltipListener, nsIDOMEventListener)
    77 void
    78 nsXULTooltipListener::MouseOut(nsIDOMEvent* aEvent)
    79 {
    80   // reset flag so that tooltip will display on the next MouseMove
    81   mTooltipShownOnce = false;
    83   // if the timer is running and no tooltip is shown, we
    84   // have to cancel the timer here so that it doesn't 
    85   // show the tooltip if we move the mouse out of the window
    86   nsCOMPtr<nsIContent> currentTooltip = do_QueryReferent(mCurrentTooltip);
    87   if (mTooltipTimer && !currentTooltip) {
    88     mTooltipTimer->Cancel();
    89     mTooltipTimer = nullptr;
    90     return;
    91   }
    93 #ifdef DEBUG_crap
    94   if (mNeedTitletip)
    95     return;
    96 #endif
    98 #ifdef MOZ_XUL
    99   // check to see if the mouse left the targetNode, and if so,
   100   // hide the tooltip
   101   if (currentTooltip) {
   102     // which node did the mouse leave?
   103     nsCOMPtr<nsIDOMNode> targetNode = do_QueryInterface(
   104       aEvent->InternalDOMEvent()->GetTarget());
   106     nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
   107     if (pm) {
   108       nsCOMPtr<nsIDOMNode> tooltipNode =
   109         pm->GetLastTriggerTooltipNode(currentTooltip->GetCurrentDoc());
   110       if (tooltipNode == targetNode) {
   111         // if the target node is the current tooltip target node, the mouse
   112         // left the node the tooltip appeared on, so close the tooltip.
   113         HideTooltip();
   114         // reset special tree tracking
   115         if (mIsSourceTree) {
   116           mLastTreeRow = -1;
   117           mLastTreeCol = nullptr;
   118         }
   119       }
   120     }
   121   }
   122 #endif
   123 }
   125 void
   126 nsXULTooltipListener::MouseMove(nsIDOMEvent* aEvent)
   127 {
   128   if (!sShowTooltips)
   129     return;
   131   // stash the coordinates of the event so that we can still get back to it from within the 
   132   // timer callback. On win32, we'll get a MouseMove event even when a popup goes away --
   133   // even when the mouse doesn't change position! To get around this, we make sure the
   134   // mouse has really moved before proceeding.
   135   nsCOMPtr<nsIDOMMouseEvent> mouseEvent(do_QueryInterface(aEvent));
   136   if (!mouseEvent)
   137     return;
   138   int32_t newMouseX, newMouseY;
   139   mouseEvent->GetScreenX(&newMouseX);
   140   mouseEvent->GetScreenY(&newMouseY);
   142   // filter out false win32 MouseMove event
   143   if (mMouseScreenX == newMouseX && mMouseScreenY == newMouseY)
   144     return;
   146   // filter out minor movements due to crappy optical mice and shaky hands
   147   // to prevent tooltips from hiding prematurely.
   148   nsCOMPtr<nsIContent> currentTooltip = do_QueryReferent(mCurrentTooltip);
   150   if ((currentTooltip) &&
   151       (abs(mMouseScreenX - newMouseX) <= kTooltipMouseMoveTolerance) &&
   152       (abs(mMouseScreenY - newMouseY) <= kTooltipMouseMoveTolerance))
   153     return;
   154   mMouseScreenX = newMouseX;
   155   mMouseScreenY = newMouseY;
   157   nsCOMPtr<nsIContent> sourceContent = do_QueryInterface(
   158     aEvent->InternalDOMEvent()->GetCurrentTarget());
   159   mSourceNode = do_GetWeakReference(sourceContent);
   160 #ifdef MOZ_XUL
   161   mIsSourceTree = sourceContent->Tag() == nsGkAtoms::treechildren;
   162   if (mIsSourceTree)
   163     CheckTreeBodyMove(mouseEvent);
   164 #endif
   166   // as the mouse moves, we want to make sure we reset the timer to show it, 
   167   // so that the delay is from when the mouse stops moving, not when it enters
   168   // the node.
   169   KillTooltipTimer();
   171   // If the mouse moves while the tooltip is up, hide it. If nothing is
   172   // showing and the tooltip hasn't been displayed since the mouse entered
   173   // the node, then start the timer to show the tooltip.
   174   if (!currentTooltip && !mTooltipShownOnce) {
   175     nsCOMPtr<EventTarget> eventTarget = aEvent->InternalDOMEvent()->GetTarget();
   177     // don't show tooltips attached to elements outside of a menu popup
   178     // when hovering over an element inside it. The popupsinherittooltip
   179     // attribute may be used to disable this behaviour, which is useful for
   180     // large menu hierarchies such as bookmarks.
   181     if (!sourceContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::popupsinherittooltip,
   182                                     nsGkAtoms::_true, eCaseMatters)) {
   183       nsCOMPtr<nsIContent> targetContent = do_QueryInterface(eventTarget);
   184       while (targetContent && targetContent != sourceContent) {
   185         nsIAtom* tag = targetContent->Tag();
   186         if (targetContent->GetNameSpaceID() == kNameSpaceID_XUL &&
   187             (tag == nsGkAtoms::menupopup ||
   188              tag == nsGkAtoms::panel ||
   189              tag == nsGkAtoms::tooltip)) {
   190           mSourceNode = nullptr;
   191           return;
   192         }
   194         targetContent = targetContent->GetParent();
   195       }
   196     }
   198     mTooltipTimer = do_CreateInstance("@mozilla.org/timer;1");
   199     if (mTooltipTimer) {
   200       mTargetNode = do_GetWeakReference(eventTarget);
   201       if (mTargetNode) {
   202         nsresult rv =
   203           mTooltipTimer->InitWithFuncCallback(sTooltipCallback, this,
   204             LookAndFeel::GetInt(LookAndFeel::eIntID_TooltipDelay, 500),
   205             nsITimer::TYPE_ONE_SHOT);
   206         if (NS_FAILED(rv)) {
   207           mTargetNode = nullptr;
   208           mSourceNode = nullptr;
   209         }
   210       }
   211     }
   212     return;
   213   }
   215 #ifdef MOZ_XUL
   216   if (mIsSourceTree)
   217     return;
   218 #endif
   220   HideTooltip();
   221   // set a flag so that the tooltip is only displayed once until the mouse
   222   // leaves the node
   223   mTooltipShownOnce = true;
   224 }
   226 NS_IMETHODIMP
   227 nsXULTooltipListener::HandleEvent(nsIDOMEvent* aEvent)
   228 {
   229   nsAutoString type;
   230   aEvent->GetType(type);
   231   if (type.EqualsLiteral("DOMMouseScroll") ||
   232       type.EqualsLiteral("keydown") ||
   233       type.EqualsLiteral("mousedown") ||
   234       type.EqualsLiteral("mouseup") ||
   235       type.EqualsLiteral("dragstart")) {
   236     HideTooltip();
   237     return NS_OK;
   238   }
   240   if (type.EqualsLiteral("popuphiding")) {
   241     DestroyTooltip();
   242     return NS_OK;
   243   }
   245   // Note that mousemove, mouseover and mouseout might be
   246   // fired even during dragging due to widget's bug.
   247   nsCOMPtr<nsIDragService> dragService =
   248     do_GetService("@mozilla.org/widget/dragservice;1");
   249   NS_ENSURE_TRUE(dragService, NS_OK);
   250   nsCOMPtr<nsIDragSession> dragSession;
   251   dragService->GetCurrentSession(getter_AddRefs(dragSession));
   252   if (dragSession) {
   253     return NS_OK;
   254   }
   256   // Not dragging.
   258   if (type.EqualsLiteral("mousemove")) {
   259     MouseMove(aEvent);
   260     return NS_OK;
   261   }
   263   if (type.EqualsLiteral("mouseout")) {
   264     MouseOut(aEvent);
   265     return NS_OK;
   266   }
   268   return NS_OK;
   269 }
   271 //////////////////////////////////////////////////////////////////////////
   272 //// nsXULTooltipListener
   274 // static
   275 void
   276 nsXULTooltipListener::ToolbarTipsPrefChanged(const char *aPref,
   277                                              void *aClosure)
   278 {
   279   sShowTooltips =
   280     Preferences::GetBool("browser.chrome.toolbar_tips", sShowTooltips);
   281 }
   283 //////////////////////////////////////////////////////////////////////////
   284 //// nsXULTooltipListener
   286 bool nsXULTooltipListener::sShowTooltips = false;
   287 uint32_t nsXULTooltipListener::sTooltipListenerCount = 0;
   289 nsresult
   290 nsXULTooltipListener::AddTooltipSupport(nsIContent* aNode)
   291 {
   292   if (!aNode)
   293     return NS_ERROR_NULL_POINTER;
   295   aNode->AddSystemEventListener(NS_LITERAL_STRING("mouseout"), this,
   296                                 false, false);
   297   aNode->AddSystemEventListener(NS_LITERAL_STRING("mousemove"), this,
   298                                 false, false);
   299   aNode->AddSystemEventListener(NS_LITERAL_STRING("mousedown"), this,
   300                                 false, false);
   301   aNode->AddSystemEventListener(NS_LITERAL_STRING("mouseup"), this,
   302                                 false, false);
   303   aNode->AddSystemEventListener(NS_LITERAL_STRING("dragstart"), this,
   304                                 true, false);
   306   return NS_OK;
   307 }
   309 nsresult
   310 nsXULTooltipListener::RemoveTooltipSupport(nsIContent* aNode)
   311 {
   312   if (!aNode)
   313     return NS_ERROR_NULL_POINTER;
   315   aNode->RemoveSystemEventListener(NS_LITERAL_STRING("mouseout"), this, false);
   316   aNode->RemoveSystemEventListener(NS_LITERAL_STRING("mousemove"), this, false);
   317   aNode->RemoveSystemEventListener(NS_LITERAL_STRING("mousedown"), this, false);
   318   aNode->RemoveSystemEventListener(NS_LITERAL_STRING("mouseup"), this, false);
   319   aNode->RemoveSystemEventListener(NS_LITERAL_STRING("dragstart"), this, true);
   321   return NS_OK;
   322 }
   324 #ifdef MOZ_XUL
   325 void
   326 nsXULTooltipListener::CheckTreeBodyMove(nsIDOMMouseEvent* aMouseEvent)
   327 {
   328   nsCOMPtr<nsIContent> sourceNode = do_QueryReferent(mSourceNode);
   329   if (!sourceNode)
   330     return;
   332   // get the boxObject of the documentElement of the document the tree is in
   333   nsCOMPtr<nsIBoxObject> bx;
   334   nsIDocument* doc = sourceNode->GetDocument();
   335   if (doc) {
   336     ErrorResult ignored;
   337     bx = doc->GetBoxObjectFor(doc->GetRootElement(), ignored);
   338   }
   340   nsCOMPtr<nsITreeBoxObject> obx;
   341   GetSourceTreeBoxObject(getter_AddRefs(obx));
   342   if (bx && obx) {
   343     int32_t x, y;
   344     aMouseEvent->GetScreenX(&x);
   345     aMouseEvent->GetScreenY(&y);
   347     int32_t row;
   348     nsCOMPtr<nsITreeColumn> col;
   349     nsAutoCString obj;
   351     // subtract off the documentElement's boxObject
   352     int32_t boxX, boxY;
   353     bx->GetScreenX(&boxX);
   354     bx->GetScreenY(&boxY);
   355     x -= boxX;
   356     y -= boxY;
   358     obx->GetCellAt(x, y, &row, getter_AddRefs(col), obj);
   360     // determine if we are going to need a titletip
   361     // XXX check the disabletitletips attribute on the tree content
   362     mNeedTitletip = false;
   363     if (row >= 0 && obj.EqualsLiteral("text")) {
   364       obx->IsCellCropped(row, col, &mNeedTitletip);
   365     }
   367     nsCOMPtr<nsIContent> currentTooltip = do_QueryReferent(mCurrentTooltip);
   368     if (currentTooltip && (row != mLastTreeRow || col != mLastTreeCol)) {
   369       HideTooltip();
   370     } 
   372     mLastTreeRow = row;
   373     mLastTreeCol = col;
   374   }
   375 }
   376 #endif
   378 nsresult
   379 nsXULTooltipListener::ShowTooltip()
   380 {
   381   nsCOMPtr<nsIContent> sourceNode = do_QueryReferent(mSourceNode);
   383   // get the tooltip content designated for the target node
   384   nsCOMPtr<nsIContent> tooltipNode;
   385   GetTooltipFor(sourceNode, getter_AddRefs(tooltipNode));
   386   if (!tooltipNode || sourceNode == tooltipNode)
   387     return NS_ERROR_FAILURE; // the target node doesn't need a tooltip
   389   // set the node in the document that triggered the tooltip and show it
   390   nsCOMPtr<nsIDOMXULDocument> xulDoc(do_QueryInterface(tooltipNode->GetDocument()));
   391   if (xulDoc) {
   392     // Make sure the target node is still attached to some document. 
   393     // It might have been deleted.
   394     if (sourceNode->GetDocument()) {
   395 #ifdef MOZ_XUL
   396       if (!mIsSourceTree) {
   397         mLastTreeRow = -1;
   398         mLastTreeCol = nullptr;
   399       }
   400 #endif
   402       mCurrentTooltip = do_GetWeakReference(tooltipNode);
   403       LaunchTooltip();
   404       mTargetNode = nullptr;
   406       nsCOMPtr<nsIContent> currentTooltip = do_QueryReferent(mCurrentTooltip);
   407       if (!currentTooltip)
   408         return NS_OK;
   410       // listen for popuphidden on the tooltip node, so that we can
   411       // be sure DestroyPopup is called even if someone else closes the tooltip
   412       currentTooltip->AddSystemEventListener(NS_LITERAL_STRING("popuphiding"), 
   413                                              this, false, false);
   415       // listen for mousedown, mouseup, keydown, and DOMMouseScroll events at document level
   416       nsIDocument* doc = sourceNode->GetDocument();
   417       if (doc) {
   418         // Probably, we should listen to untrusted events for hiding tooltips
   419         // on content since tooltips might disturb something of web
   420         // applications.  If we don't specify the aWantsUntrusted of
   421         // AddSystemEventListener(), the event target sets it to TRUE if the
   422         // target is in content.
   423         doc->AddSystemEventListener(NS_LITERAL_STRING("DOMMouseScroll"),
   424                                     this, true);
   425         doc->AddSystemEventListener(NS_LITERAL_STRING("mousedown"),
   426                                     this, true);
   427         doc->AddSystemEventListener(NS_LITERAL_STRING("mouseup"),
   428                                     this, true);
   429         doc->AddSystemEventListener(NS_LITERAL_STRING("keydown"),
   430                                     this, true);
   431       }
   432       mSourceNode = nullptr;
   433     }
   434   }
   436   return NS_OK;
   437 }
   439 #ifdef MOZ_XUL
   440 // XXX: "This stuff inside DEBUG_crap could be used to make tree tooltips work
   441 //       in the future."
   442 #ifdef DEBUG_crap
   443 static void
   444 GetTreeCellCoords(nsITreeBoxObject* aTreeBox, nsIContent* aSourceNode, 
   445                   int32_t aRow, nsITreeColumn* aCol, int32_t* aX, int32_t* aY)
   446 {
   447   int32_t junk;
   448   aTreeBox->GetCoordsForCellItem(aRow, aCol, EmptyCString(), aX, aY, &junk, &junk);
   449   nsCOMPtr<nsIDOMXULElement> xulEl(do_QueryInterface(aSourceNode));
   450   nsCOMPtr<nsIBoxObject> bx;
   451   xulEl->GetBoxObject(getter_AddRefs(bx));
   452   int32_t myX, myY;
   453   bx->GetX(&myX);
   454   bx->GetY(&myY);
   455   *aX += myX;
   456   *aY += myY;
   457 }
   458 #endif
   460 static void
   461 SetTitletipLabel(nsITreeBoxObject* aTreeBox, nsIContent* aTooltip,
   462                  int32_t aRow, nsITreeColumn* aCol)
   463 {
   464   nsCOMPtr<nsITreeView> view;
   465   aTreeBox->GetView(getter_AddRefs(view));
   466   if (view) {
   467     nsAutoString label;
   468 #ifdef DEBUG
   469     nsresult rv = 
   470 #endif
   471       view->GetCellText(aRow, aCol, label);
   472     NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Couldn't get the cell text!");
   473     aTooltip->SetAttr(kNameSpaceID_None, nsGkAtoms::label, label, true);
   474   }
   475 }
   476 #endif
   478 void
   479 nsXULTooltipListener::LaunchTooltip()
   480 {
   481   nsCOMPtr<nsIContent> currentTooltip = do_QueryReferent(mCurrentTooltip);
   482   if (!currentTooltip)
   483     return;
   485 #ifdef MOZ_XUL
   486   if (mIsSourceTree && mNeedTitletip) {
   487     nsCOMPtr<nsITreeBoxObject> obx;
   488     GetSourceTreeBoxObject(getter_AddRefs(obx));
   490     SetTitletipLabel(obx, currentTooltip, mLastTreeRow, mLastTreeCol);
   491     if (!(currentTooltip = do_QueryReferent(mCurrentTooltip))) {
   492       // Because of mutation events, currentTooltip can be null.
   493       return;
   494     }
   495     currentTooltip->SetAttr(kNameSpaceID_None, nsGkAtoms::titletip, NS_LITERAL_STRING("true"), true);
   496   } else {
   497     currentTooltip->UnsetAttr(kNameSpaceID_None, nsGkAtoms::titletip, true);
   498   }
   499   if (!(currentTooltip = do_QueryReferent(mCurrentTooltip))) {
   500     // Because of mutation events, currentTooltip can be null.
   501     return;
   502   }
   504   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
   505   if (pm) {
   506     nsCOMPtr<nsIContent> target = do_QueryReferent(mTargetNode);
   507     pm->ShowTooltipAtScreen(currentTooltip, target, mMouseScreenX, mMouseScreenY);
   509     // Clear the current tooltip if the popup was not opened successfully.
   510     if (!pm->IsPopupOpen(currentTooltip))
   511       mCurrentTooltip = nullptr;
   512   }
   513 #endif
   515 }
   517 nsresult
   518 nsXULTooltipListener::HideTooltip()
   519 {
   520 #ifdef MOZ_XUL
   521   nsCOMPtr<nsIContent> currentTooltip = do_QueryReferent(mCurrentTooltip);
   522   if (currentTooltip) {
   523     nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
   524     if (pm)
   525       pm->HidePopup(currentTooltip, false, false, false, false);
   526   }
   527 #endif
   529   DestroyTooltip();
   530   return NS_OK;
   531 }
   533 static void
   534 GetImmediateChild(nsIContent* aContent, nsIAtom *aTag, nsIContent** aResult) 
   535 {
   536   *aResult = nullptr;
   537   uint32_t childCount = aContent->GetChildCount();
   538   for (uint32_t i = 0; i < childCount; i++) {
   539     nsIContent *child = aContent->GetChildAt(i);
   541     if (child->Tag() == aTag) {
   542       *aResult = child;
   543       NS_ADDREF(*aResult);
   544       return;
   545     }
   546   }
   548   return;
   549 }
   551 nsresult
   552 nsXULTooltipListener::FindTooltip(nsIContent* aTarget, nsIContent** aTooltip)
   553 {
   554   if (!aTarget)
   555     return NS_ERROR_NULL_POINTER;
   557   // before we go on, make sure that target node still has a window
   558   nsIDocument *document = aTarget->GetDocument();
   559   if (!document) {
   560     NS_WARNING("Unable to retrieve the tooltip node document.");
   561     return NS_ERROR_FAILURE;
   562   }
   563   nsPIDOMWindow *window = document->GetWindow();
   564   if (!window) {
   565     return NS_OK;
   566   }
   568   bool closed;
   569   window->GetClosed(&closed);
   571   if (closed) {
   572     return NS_OK;
   573   }
   575   nsAutoString tooltipText;
   576   aTarget->GetAttr(kNameSpaceID_None, nsGkAtoms::tooltiptext, tooltipText);
   577   if (!tooltipText.IsEmpty()) {
   578     // specifying tooltiptext means we will always use the default tooltip
   579     nsIRootBox* rootBox = nsIRootBox::GetRootBox(document->GetShell());
   580     NS_ENSURE_STATE(rootBox);
   581     *aTooltip = rootBox->GetDefaultTooltip();
   582     if (*aTooltip) {
   583       NS_ADDREF(*aTooltip);
   584       (*aTooltip)->SetAttr(kNameSpaceID_None, nsGkAtoms::label, tooltipText, true);
   585     }
   586     return NS_OK;
   587   }
   589   nsAutoString tooltipId;
   590   aTarget->GetAttr(kNameSpaceID_None, nsGkAtoms::tooltip, tooltipId);
   592   // if tooltip == _child, look for first <tooltip> child
   593   if (tooltipId.EqualsLiteral("_child")) {
   594     GetImmediateChild(aTarget, nsGkAtoms::tooltip, aTooltip);
   595     return NS_OK;
   596   }
   598   if (!tooltipId.IsEmpty()) {
   599     // tooltip must be an id, use getElementById to find it
   600     nsCOMPtr<nsIContent> tooltipEl = document->GetElementById(tooltipId);
   602     if (tooltipEl) {
   603 #ifdef MOZ_XUL
   604       mNeedTitletip = false;
   605 #endif
   606       tooltipEl.forget(aTooltip);
   607       return NS_OK;
   608     }
   609   }
   611 #ifdef MOZ_XUL
   612   // titletips should just use the default tooltip
   613   if (mIsSourceTree && mNeedTitletip) {
   614     nsIRootBox* rootBox = nsIRootBox::GetRootBox(document->GetShell());
   615     NS_ENSURE_STATE(rootBox);
   616     NS_IF_ADDREF(*aTooltip = rootBox->GetDefaultTooltip());
   617   }
   618 #endif
   620   return NS_OK;
   621 }
   624 nsresult
   625 nsXULTooltipListener::GetTooltipFor(nsIContent* aTarget, nsIContent** aTooltip)
   626 {
   627   *aTooltip = nullptr;
   628   nsCOMPtr<nsIContent> tooltip;
   629   nsresult rv = FindTooltip(aTarget, getter_AddRefs(tooltip));
   630   if (NS_FAILED(rv) || !tooltip) {
   631     return rv;
   632   }
   634 #ifdef MOZ_XUL
   635   // Submenus can't be used as tooltips, see bug 288763.
   636   nsIContent* parent = tooltip->GetParent();
   637   if (parent) {
   638     nsMenuFrame* menu = do_QueryFrame(parent->GetPrimaryFrame());
   639     if (menu) {
   640       NS_WARNING("Menu cannot be used as a tooltip");
   641       return NS_ERROR_FAILURE;
   642     }
   643   }
   644 #endif
   646   tooltip.swap(*aTooltip);
   647   return rv;
   648 }
   650 nsresult
   651 nsXULTooltipListener::DestroyTooltip()
   652 {
   653   nsCOMPtr<nsIDOMEventListener> kungFuDeathGrip(this);
   654   nsCOMPtr<nsIContent> currentTooltip = do_QueryReferent(mCurrentTooltip);
   655   if (currentTooltip) {
   656     // release tooltip before removing listener to prevent our destructor from
   657     // being called recursively (bug 120863)
   658     mCurrentTooltip = nullptr;
   660     // clear out the tooltip node on the document
   661     nsCOMPtr<nsIDocument> doc = currentTooltip->GetDocument();
   662     if (doc) {
   663       // remove the mousedown and keydown listener from document
   664       doc->RemoveSystemEventListener(NS_LITERAL_STRING("DOMMouseScroll"), this,
   665                                      true);
   666       doc->RemoveSystemEventListener(NS_LITERAL_STRING("mousedown"), this,
   667                                      true);
   668       doc->RemoveSystemEventListener(NS_LITERAL_STRING("mouseup"), this, true);
   669       doc->RemoveSystemEventListener(NS_LITERAL_STRING("keydown"), this, true);
   670     }
   672     // remove the popuphidden listener from tooltip
   673     currentTooltip->RemoveSystemEventListener(NS_LITERAL_STRING("popuphiding"), this, false);
   674   }
   676   // kill any ongoing timers
   677   KillTooltipTimer();
   678   mSourceNode = nullptr;
   679 #ifdef MOZ_XUL
   680   mLastTreeCol = nullptr;
   681 #endif
   683   return NS_OK;
   684 }
   686 void
   687 nsXULTooltipListener::KillTooltipTimer()
   688 {
   689   if (mTooltipTimer) {
   690     mTooltipTimer->Cancel();
   691     mTooltipTimer = nullptr;
   692     mTargetNode = nullptr;
   693   }
   694 }
   696 void
   697 nsXULTooltipListener::sTooltipCallback(nsITimer *aTimer, void *aListener)
   698 {
   699   nsRefPtr<nsXULTooltipListener> instance = mInstance;
   700   if (instance)
   701     instance->ShowTooltip();
   702 }
   704 #ifdef MOZ_XUL
   705 nsresult
   706 nsXULTooltipListener::GetSourceTreeBoxObject(nsITreeBoxObject** aBoxObject)
   707 {
   708   *aBoxObject = nullptr;
   710   nsCOMPtr<nsIContent> sourceNode = do_QueryReferent(mSourceNode);
   711   if (mIsSourceTree && sourceNode) {
   712     nsCOMPtr<nsIDOMXULElement> xulEl(do_QueryInterface(sourceNode->GetParent()));
   713     if (xulEl) {
   714       nsCOMPtr<nsIBoxObject> bx;
   715       xulEl->GetBoxObject(getter_AddRefs(bx));
   716       nsCOMPtr<nsITreeBoxObject> obx(do_QueryInterface(bx));
   717       if (obx) {
   718         *aBoxObject = obx;
   719         NS_ADDREF(*aBoxObject);
   720         return NS_OK;
   721       }
   722     }
   723   }
   724   return NS_ERROR_FAILURE;
   725 }
   726 #endif

mercurial