no bug - Import translations from android-l10n r=release a=l10n CLOSED TREE
[gecko.git] / layout / xul / nsXULTooltipListener.cpp
blob77dfbb7a30a44a72210c1cf66ad10d49b7debfe6
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsXULTooltipListener.h"
9 #include "XULButtonElement.h"
10 #include "XULTreeElement.h"
11 #include "nsXULElement.h"
12 #include "mozilla/dom/Document.h"
13 #include "nsGkAtoms.h"
14 #include "nsMenuPopupFrame.h"
15 #include "nsIContentInlines.h"
16 #include "nsIDragService.h"
17 #include "nsIDragSession.h"
18 #include "nsITreeView.h"
19 #include "nsIScriptContext.h"
20 #include "nsPIDOMWindow.h"
21 #include "nsXULPopupManager.h"
22 #include "nsIPopupContainer.h"
23 #include "nsServiceManagerUtils.h"
24 #include "nsTreeColumns.h"
25 #include "nsContentUtils.h"
26 #include "mozilla/ErrorResult.h"
27 #include "mozilla/LookAndFeel.h"
28 #include "mozilla/PresShell.h"
29 #include "mozilla/ScopeExit.h"
30 #include "mozilla/StaticPrefs_browser.h"
31 #include "mozilla/dom/Element.h"
32 #include "mozilla/dom/Event.h" // for Event
33 #include "mozilla/dom/MouseEvent.h"
34 #include "mozilla/dom/TreeColumnBinding.h"
35 #include "mozilla/dom/XULTreeElementBinding.h"
36 #include "mozilla/TextEvents.h"
38 using namespace mozilla;
39 using namespace mozilla::dom;
41 nsXULTooltipListener* nsXULTooltipListener::sInstance = nullptr;
43 //////////////////////////////////////////////////////////////////////////
44 //// nsISupports
46 nsXULTooltipListener::nsXULTooltipListener()
47 : mTooltipShownOnce(false),
48 mIsSourceTree(false),
49 mNeedTitletip(false),
50 mLastTreeRow(-1) {}
52 nsXULTooltipListener::~nsXULTooltipListener() {
53 MOZ_ASSERT(sInstance == this);
54 sInstance = nullptr;
56 HideTooltip();
59 NS_IMPL_ISUPPORTS(nsXULTooltipListener, nsIDOMEventListener)
61 void nsXULTooltipListener::MouseOut(Event* aEvent) {
62 // reset flag so that tooltip will display on the next MouseMove
63 mTooltipShownOnce = false;
64 mPreviousMouseMoveTarget = nullptr;
66 // if the timer is running and no tooltip is shown, we
67 // have to cancel the timer here so that it doesn't
68 // show the tooltip if we move the mouse out of the window
69 nsCOMPtr<nsIContent> currentTooltip = do_QueryReferent(mCurrentTooltip);
70 if (mTooltipTimer && !currentTooltip) {
71 mTooltipTimer->Cancel();
72 mTooltipTimer = nullptr;
73 return;
76 #ifdef DEBUG_crap
77 if (mNeedTitletip) return;
78 #endif
80 // check to see if the mouse left the targetNode, and if so,
81 // hide the tooltip
82 if (currentTooltip) {
83 nsCOMPtr<nsINode> targetNode =
84 nsINode::FromEventTargetOrNull(aEvent->GetOriginalTarget());
85 if (nsXULPopupManager* pm = nsXULPopupManager::GetInstance()) {
86 nsCOMPtr<nsINode> tooltipNode =
87 pm->GetLastTriggerTooltipNode(currentTooltip->GetComposedDoc());
89 // If the target node is the current tooltip target node, the mouse
90 // left the node the tooltip appeared on, so close the tooltip. However,
91 // don't do this if the mouse moved onto the tooltip in case the
92 // tooltip appears positioned near the mouse.
93 nsCOMPtr<EventTarget> relatedTarget =
94 aEvent->AsMouseEvent()->GetRelatedTarget();
95 auto* relatedContent = nsIContent::FromEventTargetOrNull(relatedTarget);
96 if (tooltipNode == targetNode && relatedContent != currentTooltip) {
97 HideTooltip();
98 // reset special tree tracking
99 if (mIsSourceTree) {
100 mLastTreeRow = -1;
101 mLastTreeCol = nullptr;
108 void nsXULTooltipListener::MouseMove(Event* aEvent) {
109 if (!ShowTooltips()) {
110 return;
113 // stash the coordinates of the event so that we can still get back to it from
114 // within the timer callback. On win32, we'll get a MouseMove event even when
115 // a popup goes away -- even when the mouse doesn't change position! To get
116 // around this, we make sure the mouse has really moved before proceeding.
117 MouseEvent* mouseEvent = aEvent->AsMouseEvent();
118 if (!mouseEvent) {
119 return;
121 auto newMouseScreenPoint = mouseEvent->ScreenPointLayoutDevicePix();
123 // filter out false win32 MouseMove event
124 if (mMouseScreenPoint == newMouseScreenPoint) {
125 return;
128 nsCOMPtr<nsIContent> currentTooltip = do_QueryReferent(mCurrentTooltip);
129 auto* const mouseMoveTarget =
130 nsIContent::FromEventTargetOrNull(aEvent->GetOriginalTarget());
132 bool isSameTarget = true;
133 nsCOMPtr<nsIContent> tempContent = do_QueryReferent(mPreviousMouseMoveTarget);
134 if (tempContent && tempContent != mouseMoveTarget) {
135 isSameTarget = false;
138 // filter out minor movements due to crappy optical mice and shaky hands
139 // to prevent tooltips from hiding prematurely. Do not filter out movements
140 // if we are changing targets, as they may register new tooltips.
141 if (currentTooltip && isSameTarget &&
142 abs(mMouseScreenPoint.x - newMouseScreenPoint.x) <=
143 kTooltipMouseMoveTolerance &&
144 abs(mMouseScreenPoint.y - newMouseScreenPoint.y) <=
145 kTooltipMouseMoveTolerance) {
146 return;
148 mMouseScreenPoint = newMouseScreenPoint;
149 mPreviousMouseMoveTarget = do_GetWeakReference(mouseMoveTarget);
151 auto* const sourceContent =
152 nsIContent::FromEventTargetOrNull(aEvent->GetCurrentTarget());
153 mSourceNode = do_GetWeakReference(sourceContent);
154 mIsSourceTree = sourceContent->IsXULElement(nsGkAtoms::treechildren);
155 if (mIsSourceTree) {
156 CheckTreeBodyMove(mouseEvent);
159 // as the mouse moves, we want to make sure we reset the timer to show it,
160 // so that the delay is from when the mouse stops moving, not when it enters
161 // the node.
162 KillTooltipTimer();
164 // Hide the current tooltip if we change target nodes. If the new target
165 // has the same tooltip, we will open it again. We cannot compare
166 // the targets' tooltips because popupshowing events can set the tooltip.
167 if (!isSameTarget) {
168 HideTooltip();
169 mTooltipShownOnce = false;
172 // If the mouse moves while the tooltip is up, hide it. If nothing is
173 // showing and the tooltip hasn't been displayed since the mouse entered
174 // the node, then start the timer to show the tooltip.
175 // If we have moved to a different target, we need to display the new tooltip,
176 // as the previous target's tooltip will have just been hidden.
177 if ((!currentTooltip && !mTooltipShownOnce) || !isSameTarget) {
178 // don't show tooltips attached to elements outside of a menu popup
179 // when hovering over an element inside it. The popupsinherittooltip
180 // attribute may be used to disable this behaviour, which is useful for
181 // large menu hierarchies such as bookmarks.
182 const bool allowTooltipCrossingPopup =
183 !sourceContent->GetParent() ||
184 (sourceContent->IsElement() &&
185 sourceContent->AsElement()->AttrValueIs(
186 kNameSpaceID_None, nsGkAtoms::popupsinherittooltip,
187 nsGkAtoms::_true, eCaseMatters));
188 if (!allowTooltipCrossingPopup) {
189 for (auto* targetContent = mouseMoveTarget;
190 targetContent && targetContent != sourceContent;
191 targetContent = targetContent->GetFlattenedTreeParent()) {
192 if (targetContent->IsAnyOfXULElements(
193 nsGkAtoms::menupopup, nsGkAtoms::panel, nsGkAtoms::tooltip)) {
194 mSourceNode = nullptr;
195 return;
200 mTargetNode = do_GetWeakReference(mouseMoveTarget);
201 if (mTargetNode) {
202 nsresult rv = NS_NewTimerWithFuncCallback(
203 getter_AddRefs(mTooltipTimer), sTooltipCallback, this,
204 StaticPrefs::ui_tooltip_delay_ms(), nsITimer::TYPE_ONE_SHOT,
205 "sTooltipCallback", GetMainThreadSerialEventTarget());
206 if (NS_FAILED(rv)) {
207 mTargetNode = nullptr;
208 mSourceNode = nullptr;
211 return;
214 if (mIsSourceTree) {
215 return;
217 // Hide the tooltip if it is currently showing.
218 if (currentTooltip) {
219 HideTooltip();
220 // set a flag so that the tooltip is only displayed once until the mouse
221 // leaves the node
222 mTooltipShownOnce = true;
226 NS_IMETHODIMP
227 nsXULTooltipListener::HandleEvent(Event* aEvent) {
228 nsAutoString type;
229 aEvent->GetType(type);
230 if (type.EqualsLiteral("wheel") || type.EqualsLiteral("mousedown") ||
231 type.EqualsLiteral("mouseup") || type.EqualsLiteral("dragstart")) {
232 HideTooltip();
233 return NS_OK;
236 if (type.EqualsLiteral("keydown")) {
237 // Hide the tooltip if a non-modifier key is pressed.
238 WidgetKeyboardEvent* keyEvent = aEvent->WidgetEventPtr()->AsKeyboardEvent();
239 if (KeyEventHidesTooltip(*keyEvent)) {
240 HideTooltip();
242 return NS_OK;
245 if (type.EqualsLiteral("popuphiding")) {
246 DestroyTooltip();
247 return NS_OK;
250 // Note that mousemove, mouseover and mouseout might be
251 // fired even during dragging due to widget's bug.
252 nsCOMPtr<nsIDragService> dragService =
253 do_GetService("@mozilla.org/widget/dragservice;1");
254 NS_ENSURE_TRUE(dragService, NS_OK);
255 nsCOMPtr<nsIDragSession> dragSession;
256 dragService->GetCurrentSession(getter_AddRefs(dragSession));
257 if (dragSession) {
258 return NS_OK;
261 // Not dragging.
263 if (type.EqualsLiteral("mousemove")) {
264 MouseMove(aEvent);
265 return NS_OK;
268 if (type.EqualsLiteral("mouseout")) {
269 MouseOut(aEvent);
270 return NS_OK;
273 return NS_OK;
276 //////////////////////////////////////////////////////////////////////////
277 //// nsXULTooltipListener
279 bool nsXULTooltipListener::ShowTooltips() {
280 return StaticPrefs::browser_chrome_toolbar_tips();
283 bool nsXULTooltipListener::KeyEventHidesTooltip(
284 const WidgetKeyboardEvent& aEvent) {
285 switch (StaticPrefs::browser_chrome_toolbar_tips_hide_on_keydown()) {
286 case 0:
287 return false;
288 case 1:
289 return true;
290 default:
291 return !aEvent.IsModifierKeyEvent();
295 void nsXULTooltipListener::AddTooltipSupport(nsIContent* aNode) {
296 MOZ_ASSERT(aNode);
297 MOZ_ASSERT(this == sInstance);
299 aNode->AddSystemEventListener(u"mouseout"_ns, this, false, false);
300 aNode->AddSystemEventListener(u"mousemove"_ns, this, false, false);
301 aNode->AddSystemEventListener(u"mousedown"_ns, this, false, false);
302 aNode->AddSystemEventListener(u"mouseup"_ns, this, false, false);
303 aNode->AddSystemEventListener(u"dragstart"_ns, this, true, false);
306 void nsXULTooltipListener::RemoveTooltipSupport(nsIContent* aNode) {
307 MOZ_ASSERT(aNode);
308 MOZ_ASSERT(this == sInstance);
310 // The last reference to us can go after some of these calls.
311 RefPtr<nsXULTooltipListener> instance = this;
313 aNode->RemoveSystemEventListener(u"mouseout"_ns, this, false);
314 aNode->RemoveSystemEventListener(u"mousemove"_ns, this, false);
315 aNode->RemoveSystemEventListener(u"mousedown"_ns, this, false);
316 aNode->RemoveSystemEventListener(u"mouseup"_ns, this, false);
317 aNode->RemoveSystemEventListener(u"dragstart"_ns, this, true);
320 void nsXULTooltipListener::CheckTreeBodyMove(MouseEvent* aMouseEvent) {
321 nsCOMPtr<nsIContent> sourceNode = do_QueryReferent(mSourceNode);
322 if (!sourceNode) return;
324 // get the documentElement of the document the tree is in
325 Document* doc = sourceNode->GetComposedDoc();
327 RefPtr<XULTreeElement> tree = GetSourceTree();
328 Element* root = doc ? doc->GetRootElement() : nullptr;
329 if (root && root->GetPrimaryFrame() && tree) {
330 CSSIntPoint pos = aMouseEvent->ScreenPoint(CallerType::System);
332 // subtract off the documentElement's position
333 // XXX Isn't this just converting to client points?
334 CSSIntRect rect = root->GetPrimaryFrame()->GetScreenRect();
335 pos -= rect.TopLeft();
337 ErrorResult rv;
338 TreeCellInfo cellInfo;
339 tree->GetCellAt(pos.x, pos.y, cellInfo, rv);
341 int32_t row = cellInfo.mRow;
342 RefPtr<nsTreeColumn> col = cellInfo.mCol;
344 // determine if we are going to need a titletip
345 // XXX check the disabletitletips attribute on the tree content
346 mNeedTitletip = false;
347 if (row >= 0 && cellInfo.mChildElt.EqualsLiteral("text")) {
348 mNeedTitletip = tree->IsCellCropped(row, col, rv);
351 nsCOMPtr<nsIContent> currentTooltip = do_QueryReferent(mCurrentTooltip);
352 if (currentTooltip && (row != mLastTreeRow || col != mLastTreeCol)) {
353 HideTooltip();
356 mLastTreeRow = row;
357 mLastTreeCol = col;
361 nsresult nsXULTooltipListener::ShowTooltip() {
362 nsCOMPtr<nsIContent> sourceNode = do_QueryReferent(mSourceNode);
364 // get the tooltip content designated for the target node
365 nsCOMPtr<nsIContent> tooltipNode;
366 GetTooltipFor(sourceNode, getter_AddRefs(tooltipNode));
367 if (!tooltipNode || sourceNode == tooltipNode) {
368 return NS_ERROR_FAILURE; // the target node doesn't need a tooltip
371 // set the node in the document that triggered the tooltip and show it
372 // Make sure the document still has focus.
373 auto* doc = tooltipNode->GetComposedDoc();
374 if (!doc || !nsContentUtils::IsChromeDoc(doc) ||
375 doc->IsTopLevelWindowInactive()) {
376 return NS_OK;
379 // Make sure the target node is still attached to some document.
380 // It might have been deleted.
381 if (!sourceNode->IsInComposedDoc()) {
382 return NS_OK;
385 if (!mIsSourceTree) {
386 mLastTreeRow = -1;
387 mLastTreeCol = nullptr;
390 mCurrentTooltip = do_GetWeakReference(tooltipNode);
391 LaunchTooltip();
392 mTargetNode = nullptr;
394 nsCOMPtr<nsIContent> currentTooltip = do_QueryReferent(mCurrentTooltip);
395 if (!currentTooltip) {
396 return NS_OK;
399 // Listen for popuphidden on the tooltip node so that we can be sure
400 // DestroyPopup is called even if someone else closes the tooltip.
401 currentTooltip->AddSystemEventListener(u"popuphiding"_ns, this, false, false);
403 // Listen for mousedown, mouseup, keydown, and mouse events at document level.
404 if (Document* doc = sourceNode->GetComposedDoc()) {
405 // Probably, we should listen to untrusted events for hiding tooltips on
406 // content since tooltips might disturb something of web applications. If we
407 // don't specify the aWantsUntrusted of AddSystemEventListener(), the event
408 // target sets it to TRUE if the target is in content.
409 doc->AddSystemEventListener(u"wheel"_ns, this, true);
410 doc->AddSystemEventListener(u"mousedown"_ns, this, true);
411 doc->AddSystemEventListener(u"mouseup"_ns, this, true);
412 doc->AddSystemEventListener(u"keydown"_ns, this, true);
414 mSourceNode = nullptr;
416 return NS_OK;
419 static void SetTitletipLabel(XULTreeElement* aTree, Element* aTooltip,
420 int32_t aRow, nsTreeColumn* aCol) {
421 nsCOMPtr<nsITreeView> view = aTree->GetView();
422 if (view) {
423 nsAutoString label;
424 #ifdef DEBUG
425 nsresult rv =
426 #endif
427 view->GetCellText(aRow, aCol, label);
428 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get the cell text!");
429 aTooltip->SetAttr(kNameSpaceID_None, nsGkAtoms::label, label, true);
433 void nsXULTooltipListener::LaunchTooltip() {
434 RefPtr<Element> currentTooltip = do_QueryReferent(mCurrentTooltip);
435 if (!currentTooltip) {
436 return;
439 if (mIsSourceTree && mNeedTitletip) {
440 RefPtr<XULTreeElement> tree = GetSourceTree();
442 SetTitletipLabel(tree, currentTooltip, mLastTreeRow, mLastTreeCol);
443 if (!(currentTooltip = do_QueryReferent(mCurrentTooltip))) {
444 // Because of mutation events, currentTooltip can be null.
445 return;
447 currentTooltip->SetAttr(kNameSpaceID_None, nsGkAtoms::titletip, u"true"_ns,
448 true);
449 } else {
450 currentTooltip->UnsetAttr(kNameSpaceID_None, nsGkAtoms::titletip, true);
453 if (!(currentTooltip = do_QueryReferent(mCurrentTooltip))) {
454 // Because of mutation events, currentTooltip can be null.
455 return;
458 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
459 if (!pm) {
460 return;
463 auto cleanup = MakeScopeExit([&] {
464 // Clear the current tooltip if the popup was not opened successfully.
465 if (!pm->IsPopupOpen(currentTooltip)) {
466 mCurrentTooltip = nullptr;
470 RefPtr<Element> target = do_QueryReferent(mTargetNode);
471 if (!target) {
472 return;
475 pm->ShowTooltipAtScreen(currentTooltip, target, mMouseScreenPoint);
478 nsresult nsXULTooltipListener::HideTooltip() {
479 if (nsCOMPtr<Element> currentTooltip = do_QueryReferent(mCurrentTooltip)) {
480 if (nsXULPopupManager* pm = nsXULPopupManager::GetInstance()) {
481 pm->HidePopup(currentTooltip, {});
485 DestroyTooltip();
486 return NS_OK;
489 static void GetImmediateChild(nsIContent* aContent, nsAtom* aTag,
490 nsIContent** aResult) {
491 *aResult = nullptr;
492 for (nsCOMPtr<nsIContent> childContent = aContent->GetFirstChild();
493 childContent; childContent = childContent->GetNextSibling()) {
494 if (childContent->IsXULElement(aTag)) {
495 childContent.forget(aResult);
496 return;
501 nsresult nsXULTooltipListener::FindTooltip(nsIContent* aTarget,
502 nsIContent** aTooltip) {
503 if (!aTarget) {
504 return NS_ERROR_NULL_POINTER;
507 // before we go on, make sure that target node still has a window
508 Document* document = aTarget->GetComposedDoc();
509 if (!document) {
510 NS_WARNING("Unable to retrieve the tooltip node document.");
511 return NS_ERROR_FAILURE;
513 nsPIDOMWindowOuter* window = document->GetWindow();
514 if (!window) {
515 return NS_OK;
518 if (window->Closed()) {
519 return NS_OK;
522 // non-XUL elements should just use the default tooltip
523 if (!aTarget->IsXULElement()) {
524 nsIPopupContainer* popupContainer =
525 nsIPopupContainer::GetPopupContainer(document->GetPresShell());
526 NS_ENSURE_STATE(popupContainer);
527 if (RefPtr<Element> tooltip = popupContainer->GetDefaultTooltip()) {
528 tooltip.forget(aTooltip);
529 return NS_OK;
531 return NS_ERROR_FAILURE;
534 // On Windows, the OS shows the tooltip, so we don't want Gecko to do it
535 #ifdef XP_WIN
536 if (nsIFrame* f = aTarget->GetPrimaryFrame()) {
537 if (f->StyleDisplay()->GetWindowButtonType()) {
538 return NS_OK;
541 #endif
543 nsAutoString tooltipText;
544 aTarget->AsElement()->GetAttr(nsGkAtoms::tooltiptext, tooltipText);
546 if (!tooltipText.IsEmpty()) {
547 // specifying tooltiptext means we will always use the default tooltip
548 nsIPopupContainer* popupContainer =
549 nsIPopupContainer::GetPopupContainer(document->GetPresShell());
550 NS_ENSURE_STATE(popupContainer);
551 if (RefPtr<Element> tooltip = popupContainer->GetDefaultTooltip()) {
552 tooltip->SetAttr(kNameSpaceID_None, nsGkAtoms::label, tooltipText, true);
553 tooltip.forget(aTooltip);
555 return NS_OK;
558 nsAutoString tooltipId;
559 aTarget->AsElement()->GetAttr(nsGkAtoms::tooltip, tooltipId);
561 // if tooltip == _child, look for first <tooltip> child
562 if (tooltipId.EqualsLiteral("_child")) {
563 GetImmediateChild(aTarget, nsGkAtoms::tooltip, aTooltip);
564 return NS_OK;
567 if (!tooltipId.IsEmpty()) {
568 DocumentOrShadowRoot* documentOrShadowRoot =
569 aTarget->GetUncomposedDocOrConnectedShadowRoot();
570 // tooltip must be an id, use getElementById to find it
571 if (documentOrShadowRoot) {
572 nsCOMPtr<nsIContent> tooltipEl =
573 documentOrShadowRoot->GetElementById(tooltipId);
575 if (tooltipEl) {
576 mNeedTitletip = false;
577 tooltipEl.forget(aTooltip);
578 return NS_OK;
583 // titletips should just use the default tooltip
584 if (mIsSourceTree && mNeedTitletip) {
585 nsIPopupContainer* popupContainer =
586 nsIPopupContainer::GetPopupContainer(document->GetPresShell());
587 NS_ENSURE_STATE(popupContainer);
588 NS_IF_ADDREF(*aTooltip = popupContainer->GetDefaultTooltip());
591 return NS_OK;
594 nsresult nsXULTooltipListener::GetTooltipFor(nsIContent* aTarget,
595 nsIContent** aTooltip) {
596 *aTooltip = nullptr;
597 nsCOMPtr<nsIContent> tooltip;
598 nsresult rv = FindTooltip(aTarget, getter_AddRefs(tooltip));
599 if (NS_FAILED(rv) || !tooltip) {
600 return rv;
603 // Submenus can't be used as tooltips, see bug 288763.
604 if (nsIContent* parent = tooltip->GetParent()) {
605 if (auto* button = XULButtonElement::FromNode(parent)) {
606 if (button->IsMenu()) {
607 NS_WARNING("Menu cannot be used as a tooltip");
608 return NS_ERROR_FAILURE;
613 tooltip.swap(*aTooltip);
614 return rv;
617 nsresult nsXULTooltipListener::DestroyTooltip() {
618 nsCOMPtr<nsIDOMEventListener> kungFuDeathGrip(this);
619 nsCOMPtr<nsIContent> currentTooltip = do_QueryReferent(mCurrentTooltip);
620 if (currentTooltip) {
621 // release tooltip before removing listener to prevent our destructor from
622 // being called recursively (bug 120863)
623 mCurrentTooltip = nullptr;
625 // clear out the tooltip node on the document
626 if (nsCOMPtr<Document> doc = currentTooltip->GetComposedDoc()) {
627 // remove the mousedown and keydown listener from document
628 doc->RemoveSystemEventListener(u"wheel"_ns, this, true);
629 doc->RemoveSystemEventListener(u"mousedown"_ns, this, true);
630 doc->RemoveSystemEventListener(u"mouseup"_ns, this, true);
631 doc->RemoveSystemEventListener(u"keydown"_ns, this, true);
634 // remove the popuphidden listener from tooltip
635 currentTooltip->RemoveSystemEventListener(u"popuphiding"_ns, this, false);
638 // kill any ongoing timers
639 KillTooltipTimer();
640 mSourceNode = nullptr;
641 mLastTreeCol = nullptr;
643 return NS_OK;
646 void nsXULTooltipListener::KillTooltipTimer() {
647 if (mTooltipTimer) {
648 mTooltipTimer->Cancel();
649 mTooltipTimer = nullptr;
650 mTargetNode = nullptr;
654 void nsXULTooltipListener::sTooltipCallback(nsITimer* aTimer, void* aListener) {
655 RefPtr<nsXULTooltipListener> instance = sInstance;
656 if (instance) instance->ShowTooltip();
659 XULTreeElement* nsXULTooltipListener::GetSourceTree() {
660 nsCOMPtr<nsIContent> sourceNode = do_QueryReferent(mSourceNode);
661 if (mIsSourceTree && sourceNode) {
662 RefPtr<XULTreeElement> xulEl =
663 XULTreeElement::FromNodeOrNull(sourceNode->GetParent());
664 return xulEl;
667 return nullptr;