Bug 1833854 - Part 6: Round requested nursery before checking range when changing...
[gecko.git] / layout / xul / nsXULTooltipListener.cpp
blob0efc8fbb80bc6330fefc0cbb2592dd47b1934c5e
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 "nsIDragService.h"
16 #include "nsIDragSession.h"
17 #include "nsITreeView.h"
18 #include "nsIScriptContext.h"
19 #include "nsPIDOMWindow.h"
20 #include "nsXULPopupManager.h"
21 #include "nsIPopupContainer.h"
22 #include "nsServiceManagerUtils.h"
23 #include "nsTreeColumns.h"
24 #include "nsContentUtils.h"
25 #include "mozilla/ErrorResult.h"
26 #include "mozilla/LookAndFeel.h"
27 #include "mozilla/PresShell.h"
28 #include "mozilla/ScopeExit.h"
29 #include "mozilla/StaticPrefs_browser.h"
30 #include "mozilla/dom/Element.h"
31 #include "mozilla/dom/Event.h" // for Event
32 #include "mozilla/dom/MouseEvent.h"
33 #include "mozilla/dom/TreeColumnBinding.h"
34 #include "mozilla/dom/XULTreeElementBinding.h"
35 #include "mozilla/TextEvents.h"
37 using namespace mozilla;
38 using namespace mozilla::dom;
40 nsXULTooltipListener* nsXULTooltipListener::sInstance = nullptr;
42 //////////////////////////////////////////////////////////////////////////
43 //// nsISupports
45 nsXULTooltipListener::nsXULTooltipListener()
46 : mTooltipShownOnce(false),
47 mIsSourceTree(false),
48 mNeedTitletip(false),
49 mLastTreeRow(-1) {}
51 nsXULTooltipListener::~nsXULTooltipListener() {
52 MOZ_ASSERT(sInstance == this);
53 sInstance = nullptr;
55 HideTooltip();
58 NS_IMPL_ISUPPORTS(nsXULTooltipListener, nsIDOMEventListener)
60 void nsXULTooltipListener::MouseOut(Event* aEvent) {
61 // reset flag so that tooltip will display on the next MouseMove
62 mTooltipShownOnce = false;
63 mPreviousMouseMoveTarget = nullptr;
65 // if the timer is running and no tooltip is shown, we
66 // have to cancel the timer here so that it doesn't
67 // show the tooltip if we move the mouse out of the window
68 nsCOMPtr<nsIContent> currentTooltip = do_QueryReferent(mCurrentTooltip);
69 if (mTooltipTimer && !currentTooltip) {
70 mTooltipTimer->Cancel();
71 mTooltipTimer = nullptr;
72 return;
75 #ifdef DEBUG_crap
76 if (mNeedTitletip) return;
77 #endif
79 // check to see if the mouse left the targetNode, and if so,
80 // hide the tooltip
81 if (currentTooltip) {
82 // which node did the mouse leave?
83 EventTarget* eventTarget = aEvent->GetComposedTarget();
84 nsCOMPtr<nsINode> targetNode = nsINode::FromEventTargetOrNull(eventTarget);
85 if (targetNode && targetNode->IsContent() &&
86 !targetNode->AsContent()->GetContainingShadow()) {
87 eventTarget = aEvent->GetTarget();
90 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
91 if (pm) {
92 nsCOMPtr<nsINode> tooltipNode =
93 pm->GetLastTriggerTooltipNode(currentTooltip->GetComposedDoc());
95 // If the target node is the current tooltip target node, the mouse
96 // left the node the tooltip appeared on, so close the tooltip. However,
97 // don't do this if the mouse moved onto the tooltip in case the
98 // tooltip appears positioned near the mouse.
99 nsCOMPtr<EventTarget> relatedTarget =
100 aEvent->AsMouseEvent()->GetRelatedTarget();
101 nsIContent* relatedContent =
102 nsIContent::FromEventTargetOrNull(relatedTarget);
103 if (tooltipNode == targetNode && relatedContent != currentTooltip) {
104 HideTooltip();
105 // reset special tree tracking
106 if (mIsSourceTree) {
107 mLastTreeRow = -1;
108 mLastTreeCol = nullptr;
115 void nsXULTooltipListener::MouseMove(Event* aEvent) {
116 if (!ShowTooltips()) {
117 return;
120 // stash the coordinates of the event so that we can still get back to it from
121 // within the timer callback. On win32, we'll get a MouseMove event even when
122 // a popup goes away -- even when the mouse doesn't change position! To get
123 // around this, we make sure the mouse has really moved before proceeding.
124 MouseEvent* mouseEvent = aEvent->AsMouseEvent();
125 if (!mouseEvent) {
126 return;
128 auto newMouseScreenPoint = mouseEvent->ScreenPointLayoutDevicePix();
130 // filter out false win32 MouseMove event
131 if (mMouseScreenPoint == newMouseScreenPoint) {
132 return;
135 nsCOMPtr<nsIContent> currentTooltip = do_QueryReferent(mCurrentTooltip);
136 nsCOMPtr<EventTarget> eventTarget = aEvent->GetComposedTarget();
137 nsIContent* content = nsIContent::FromEventTargetOrNull(eventTarget);
139 bool isSameTarget = true;
140 nsCOMPtr<nsIContent> tempContent = do_QueryReferent(mPreviousMouseMoveTarget);
141 if (tempContent && tempContent != content) {
142 isSameTarget = false;
145 // filter out minor movements due to crappy optical mice and shaky hands
146 // to prevent tooltips from hiding prematurely. Do not filter out movements
147 // if we are changing targets, as they may register new tooltips.
148 if (currentTooltip && isSameTarget &&
149 abs(mMouseScreenPoint.x - newMouseScreenPoint.x) <=
150 kTooltipMouseMoveTolerance &&
151 abs(mMouseScreenPoint.y - newMouseScreenPoint.y) <=
152 kTooltipMouseMoveTolerance) {
153 return;
155 mMouseScreenPoint = newMouseScreenPoint;
156 mPreviousMouseMoveTarget = do_GetWeakReference(content);
158 nsCOMPtr<nsIContent> sourceContent =
159 do_QueryInterface(aEvent->GetCurrentTarget());
160 mSourceNode = do_GetWeakReference(sourceContent);
161 mIsSourceTree = sourceContent->IsXULElement(nsGkAtoms::treechildren);
162 if (mIsSourceTree) CheckTreeBodyMove(mouseEvent);
164 // as the mouse moves, we want to make sure we reset the timer to show it,
165 // so that the delay is from when the mouse stops moving, not when it enters
166 // the node.
167 KillTooltipTimer();
169 // Hide the current tooltip if we change target nodes. If the new target
170 // has the same tooltip, we will open it again. We cannot compare
171 // the targets' tooltips because popupshowing events can set the tooltip.
172 if (!isSameTarget) {
173 HideTooltip();
174 mTooltipShownOnce = false;
177 // If the mouse moves while the tooltip is up, hide it. If nothing is
178 // showing and the tooltip hasn't been displayed since the mouse entered
179 // the node, then start the timer to show the tooltip.
180 // If we have moved to a different target, we need to display the new tooltip,
181 // as the previous target's tooltip will have just been hidden.
182 if ((!currentTooltip && !mTooltipShownOnce) || !isSameTarget) {
183 // don't show tooltips attached to elements outside of a menu popup
184 // when hovering over an element inside it. The popupsinherittooltip
185 // attribute may be used to disable this behaviour, which is useful for
186 // large menu hierarchies such as bookmarks.
187 if (!sourceContent->IsElement() ||
188 !sourceContent->AsElement()->AttrValueIs(
189 kNameSpaceID_None, nsGkAtoms::popupsinherittooltip,
190 nsGkAtoms::_true, eCaseMatters)) {
191 for (nsIContent* targetContent =
192 nsIContent::FromEventTargetOrNull(eventTarget);
193 targetContent && targetContent != sourceContent;
194 targetContent = targetContent->GetParent()) {
195 if (targetContent->IsAnyOfXULElements(
196 nsGkAtoms::menupopup, nsGkAtoms::panel, nsGkAtoms::tooltip)) {
197 mSourceNode = nullptr;
198 return;
203 mTargetNode = do_GetWeakReference(eventTarget);
204 if (mTargetNode) {
205 nsresult rv = NS_NewTimerWithFuncCallback(
206 getter_AddRefs(mTooltipTimer), sTooltipCallback, this,
207 LookAndFeel::GetInt(LookAndFeel::IntID::TooltipDelay, 500),
208 nsITimer::TYPE_ONE_SHOT, "sTooltipCallback",
209 sourceContent->OwnerDoc()->EventTargetFor(TaskCategory::Other));
210 if (NS_FAILED(rv)) {
211 mTargetNode = nullptr;
212 mSourceNode = nullptr;
215 return;
218 if (mIsSourceTree) return;
219 // Hide the tooltip if it is currently showing.
220 if (currentTooltip) {
221 HideTooltip();
222 // set a flag so that the tooltip is only displayed once until the mouse
223 // leaves the node
224 mTooltipShownOnce = true;
228 NS_IMETHODIMP
229 nsXULTooltipListener::HandleEvent(Event* aEvent) {
230 nsAutoString type;
231 aEvent->GetType(type);
232 if (type.EqualsLiteral("wheel") || type.EqualsLiteral("mousedown") ||
233 type.EqualsLiteral("mouseup") || type.EqualsLiteral("dragstart")) {
234 HideTooltip();
235 return NS_OK;
238 if (type.EqualsLiteral("keydown")) {
239 // Hide the tooltip if a non-modifier key is pressed.
240 WidgetKeyboardEvent* keyEvent = aEvent->WidgetEventPtr()->AsKeyboardEvent();
241 if (KeyEventHidesTooltip(*keyEvent)) {
242 HideTooltip();
244 return NS_OK;
247 if (type.EqualsLiteral("popuphiding")) {
248 DestroyTooltip();
249 return NS_OK;
252 // Note that mousemove, mouseover and mouseout might be
253 // fired even during dragging due to widget's bug.
254 nsCOMPtr<nsIDragService> dragService =
255 do_GetService("@mozilla.org/widget/dragservice;1");
256 NS_ENSURE_TRUE(dragService, NS_OK);
257 nsCOMPtr<nsIDragSession> dragSession;
258 dragService->GetCurrentSession(getter_AddRefs(dragSession));
259 if (dragSession) {
260 return NS_OK;
263 // Not dragging.
265 if (type.EqualsLiteral("mousemove")) {
266 MouseMove(aEvent);
267 return NS_OK;
270 if (type.EqualsLiteral("mouseout")) {
271 MouseOut(aEvent);
272 return NS_OK;
275 return NS_OK;
278 //////////////////////////////////////////////////////////////////////////
279 //// nsXULTooltipListener
281 bool nsXULTooltipListener::ShowTooltips() {
282 return StaticPrefs::browser_chrome_toolbar_tips();
285 bool nsXULTooltipListener::KeyEventHidesTooltip(
286 const WidgetKeyboardEvent& aEvent) {
287 switch (StaticPrefs::browser_chrome_toolbar_tips_hide_on_keydown()) {
288 case 0:
289 return false;
290 case 1:
291 return true;
292 default:
293 return !aEvent.IsModifierKeyEvent();
297 void nsXULTooltipListener::AddTooltipSupport(nsIContent* aNode) {
298 MOZ_ASSERT(aNode);
299 MOZ_ASSERT(this == sInstance);
301 aNode->AddSystemEventListener(u"mouseout"_ns, this, false, false);
302 aNode->AddSystemEventListener(u"mousemove"_ns, this, false, false);
303 aNode->AddSystemEventListener(u"mousedown"_ns, this, false, false);
304 aNode->AddSystemEventListener(u"mouseup"_ns, this, false, false);
305 aNode->AddSystemEventListener(u"dragstart"_ns, this, true, false);
308 void nsXULTooltipListener::RemoveTooltipSupport(nsIContent* aNode) {
309 MOZ_ASSERT(aNode);
310 MOZ_ASSERT(this == sInstance);
312 // The last reference to us can go after some of these calls.
313 RefPtr<nsXULTooltipListener> instance = this;
315 aNode->RemoveSystemEventListener(u"mouseout"_ns, this, false);
316 aNode->RemoveSystemEventListener(u"mousemove"_ns, this, false);
317 aNode->RemoveSystemEventListener(u"mousedown"_ns, this, false);
318 aNode->RemoveSystemEventListener(u"mouseup"_ns, this, false);
319 aNode->RemoveSystemEventListener(u"dragstart"_ns, this, true);
322 void nsXULTooltipListener::CheckTreeBodyMove(MouseEvent* aMouseEvent) {
323 nsCOMPtr<nsIContent> sourceNode = do_QueryReferent(mSourceNode);
324 if (!sourceNode) return;
326 // get the documentElement of the document the tree is in
327 Document* doc = sourceNode->GetComposedDoc();
329 RefPtr<XULTreeElement> tree = GetSourceTree();
330 Element* root = doc ? doc->GetRootElement() : nullptr;
331 if (root && root->GetPrimaryFrame() && tree) {
332 CSSIntPoint pos = aMouseEvent->ScreenPoint(CallerType::System);
334 // subtract off the documentElement's position
335 // XXX Isn't this just converting to client points?
336 CSSIntRect rect = root->GetPrimaryFrame()->GetScreenRect();
337 pos -= rect.TopLeft();
339 ErrorResult rv;
340 TreeCellInfo cellInfo;
341 tree->GetCellAt(pos.x, pos.y, cellInfo, rv);
343 int32_t row = cellInfo.mRow;
344 RefPtr<nsTreeColumn> col = cellInfo.mCol;
346 // determine if we are going to need a titletip
347 // XXX check the disabletitletips attribute on the tree content
348 mNeedTitletip = false;
349 if (row >= 0 && cellInfo.mChildElt.EqualsLiteral("text")) {
350 mNeedTitletip = tree->IsCellCropped(row, col, rv);
353 nsCOMPtr<nsIContent> currentTooltip = do_QueryReferent(mCurrentTooltip);
354 if (currentTooltip && (row != mLastTreeRow || col != mLastTreeCol)) {
355 HideTooltip();
358 mLastTreeRow = row;
359 mLastTreeCol = col;
363 nsresult nsXULTooltipListener::ShowTooltip() {
364 nsCOMPtr<nsIContent> sourceNode = do_QueryReferent(mSourceNode);
366 // get the tooltip content designated for the target node
367 nsCOMPtr<nsIContent> tooltipNode;
368 GetTooltipFor(sourceNode, getter_AddRefs(tooltipNode));
369 if (!tooltipNode || sourceNode == tooltipNode)
370 return NS_ERROR_FAILURE; // the target node doesn't need a tooltip
372 // set the node in the document that triggered the tooltip and show it
373 if (tooltipNode->GetComposedDoc() &&
374 nsContentUtils::IsChromeDoc(tooltipNode->GetComposedDoc())) {
375 // Make sure the target node is still attached to some document.
376 // It might have been deleted.
377 if (sourceNode->IsInComposedDoc()) {
378 if (!mIsSourceTree) {
379 mLastTreeRow = -1;
380 mLastTreeCol = nullptr;
383 mCurrentTooltip = do_GetWeakReference(tooltipNode);
384 LaunchTooltip();
385 mTargetNode = nullptr;
387 nsCOMPtr<nsIContent> currentTooltip = do_QueryReferent(mCurrentTooltip);
388 if (!currentTooltip) return NS_OK;
390 // listen for popuphidden on the tooltip node, so that we can
391 // be sure DestroyPopup is called even if someone else closes the tooltip
392 currentTooltip->AddSystemEventListener(u"popuphiding"_ns, this, false,
393 false);
395 // listen for mousedown, mouseup, keydown, and mouse events at
396 // document level
397 if (Document* doc = sourceNode->GetComposedDoc()) {
398 // Probably, we should listen to untrusted events for hiding tooltips
399 // on content since tooltips might disturb something of web
400 // applications. If we don't specify the aWantsUntrusted of
401 // AddSystemEventListener(), the event target sets it to TRUE if the
402 // target is in content.
403 doc->AddSystemEventListener(u"wheel"_ns, this, true);
404 doc->AddSystemEventListener(u"mousedown"_ns, this, true);
405 doc->AddSystemEventListener(u"mouseup"_ns, this, true);
406 doc->AddSystemEventListener(u"keydown"_ns, this, true);
408 mSourceNode = nullptr;
412 return NS_OK;
415 static void SetTitletipLabel(XULTreeElement* aTree, Element* aTooltip,
416 int32_t aRow, nsTreeColumn* aCol) {
417 nsCOMPtr<nsITreeView> view = aTree->GetView();
418 if (view) {
419 nsAutoString label;
420 #ifdef DEBUG
421 nsresult rv =
422 #endif
423 view->GetCellText(aRow, aCol, label);
424 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get the cell text!");
425 aTooltip->SetAttr(kNameSpaceID_None, nsGkAtoms::label, label, true);
429 void nsXULTooltipListener::LaunchTooltip() {
430 RefPtr<Element> currentTooltip = do_QueryReferent(mCurrentTooltip);
431 if (!currentTooltip) {
432 return;
435 if (mIsSourceTree && mNeedTitletip) {
436 RefPtr<XULTreeElement> tree = GetSourceTree();
438 SetTitletipLabel(tree, currentTooltip, mLastTreeRow, mLastTreeCol);
439 if (!(currentTooltip = do_QueryReferent(mCurrentTooltip))) {
440 // Because of mutation events, currentTooltip can be null.
441 return;
443 currentTooltip->SetAttr(kNameSpaceID_None, nsGkAtoms::titletip, u"true"_ns,
444 true);
445 } else {
446 currentTooltip->UnsetAttr(kNameSpaceID_None, nsGkAtoms::titletip, true);
449 if (!(currentTooltip = do_QueryReferent(mCurrentTooltip))) {
450 // Because of mutation events, currentTooltip can be null.
451 return;
454 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
455 if (!pm) {
456 return;
459 auto cleanup = MakeScopeExit([&] {
460 // Clear the current tooltip if the popup was not opened successfully.
461 if (!pm->IsPopupOpen(currentTooltip)) {
462 mCurrentTooltip = nullptr;
466 RefPtr<Element> target = do_QueryReferent(mTargetNode);
467 if (!target) {
468 return;
471 pm->ShowTooltipAtScreen(currentTooltip, target, mMouseScreenPoint);
474 nsresult nsXULTooltipListener::HideTooltip() {
475 if (nsCOMPtr<Element> currentTooltip = do_QueryReferent(mCurrentTooltip)) {
476 if (nsXULPopupManager* pm = nsXULPopupManager::GetInstance()) {
477 pm->HidePopup(currentTooltip, {});
481 DestroyTooltip();
482 return NS_OK;
485 static void GetImmediateChild(nsIContent* aContent, nsAtom* aTag,
486 nsIContent** aResult) {
487 *aResult = nullptr;
488 for (nsCOMPtr<nsIContent> childContent = aContent->GetFirstChild();
489 childContent; childContent = childContent->GetNextSibling()) {
490 if (childContent->IsXULElement(aTag)) {
491 childContent.forget(aResult);
492 return;
497 nsresult nsXULTooltipListener::FindTooltip(nsIContent* aTarget,
498 nsIContent** aTooltip) {
499 if (!aTarget) return NS_ERROR_NULL_POINTER;
501 // before we go on, make sure that target node still has a window
502 Document* document = aTarget->GetComposedDoc();
503 if (!document) {
504 NS_WARNING("Unable to retrieve the tooltip node document.");
505 return NS_ERROR_FAILURE;
507 nsPIDOMWindowOuter* window = document->GetWindow();
508 if (!window) {
509 return NS_OK;
512 if (window->Closed()) {
513 return NS_OK;
516 // non-XUL elements should just use the default tooltip
517 if (!aTarget->IsXULElement()) {
518 nsIPopupContainer* popupContainer =
519 nsIPopupContainer::GetPopupContainer(document->GetPresShell());
520 NS_ENSURE_STATE(popupContainer);
521 if (RefPtr<Element> tooltip = popupContainer->GetDefaultTooltip()) {
522 tooltip.forget(aTooltip);
523 return NS_OK;
525 return NS_ERROR_FAILURE;
528 // On Windows, the OS shows the tooltip, so we don't want Gecko to do it
529 #ifdef XP_WIN
530 if (nsIFrame* f = aTarget->GetPrimaryFrame()) {
531 if (f->StyleDisplay()->GetWindowButtonType()) {
532 return NS_OK;
535 #endif
537 nsAutoString tooltipText;
538 aTarget->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::tooltiptext,
539 tooltipText);
541 if (!tooltipText.IsEmpty()) {
542 // specifying tooltiptext means we will always use the default tooltip
543 nsIPopupContainer* popupContainer =
544 nsIPopupContainer::GetPopupContainer(document->GetPresShell());
545 NS_ENSURE_STATE(popupContainer);
546 if (RefPtr<Element> tooltip = popupContainer->GetDefaultTooltip()) {
547 tooltip->SetAttr(kNameSpaceID_None, nsGkAtoms::label, tooltipText, true);
548 tooltip.forget(aTooltip);
550 return NS_OK;
553 nsAutoString tooltipId;
554 aTarget->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::tooltip,
555 tooltipId);
557 // if tooltip == _child, look for first <tooltip> child
558 if (tooltipId.EqualsLiteral("_child")) {
559 GetImmediateChild(aTarget, nsGkAtoms::tooltip, aTooltip);
560 return NS_OK;
563 if (!tooltipId.IsEmpty()) {
564 DocumentOrShadowRoot* documentOrShadowRoot =
565 aTarget->GetUncomposedDocOrConnectedShadowRoot();
566 // tooltip must be an id, use getElementById to find it
567 if (documentOrShadowRoot) {
568 nsCOMPtr<nsIContent> tooltipEl =
569 documentOrShadowRoot->GetElementById(tooltipId);
571 if (tooltipEl) {
572 mNeedTitletip = false;
573 tooltipEl.forget(aTooltip);
574 return NS_OK;
579 // titletips should just use the default tooltip
580 if (mIsSourceTree && mNeedTitletip) {
581 nsIPopupContainer* popupContainer =
582 nsIPopupContainer::GetPopupContainer(document->GetPresShell());
583 NS_ENSURE_STATE(popupContainer);
584 NS_IF_ADDREF(*aTooltip = popupContainer->GetDefaultTooltip());
587 return NS_OK;
590 nsresult nsXULTooltipListener::GetTooltipFor(nsIContent* aTarget,
591 nsIContent** aTooltip) {
592 *aTooltip = nullptr;
593 nsCOMPtr<nsIContent> tooltip;
594 nsresult rv = FindTooltip(aTarget, getter_AddRefs(tooltip));
595 if (NS_FAILED(rv) || !tooltip) {
596 return rv;
599 // Submenus can't be used as tooltips, see bug 288763.
600 if (nsIContent* parent = tooltip->GetParent()) {
601 if (auto* button = XULButtonElement::FromNode(parent)) {
602 if (button->IsMenu()) {
603 NS_WARNING("Menu cannot be used as a tooltip");
604 return NS_ERROR_FAILURE;
609 tooltip.swap(*aTooltip);
610 return rv;
613 nsresult nsXULTooltipListener::DestroyTooltip() {
614 nsCOMPtr<nsIDOMEventListener> kungFuDeathGrip(this);
615 nsCOMPtr<nsIContent> currentTooltip = do_QueryReferent(mCurrentTooltip);
616 if (currentTooltip) {
617 // release tooltip before removing listener to prevent our destructor from
618 // being called recursively (bug 120863)
619 mCurrentTooltip = nullptr;
621 // clear out the tooltip node on the document
622 if (nsCOMPtr<Document> doc = currentTooltip->GetComposedDoc()) {
623 // remove the mousedown and keydown listener from document
624 doc->RemoveSystemEventListener(u"wheel"_ns, this, true);
625 doc->RemoveSystemEventListener(u"mousedown"_ns, this, true);
626 doc->RemoveSystemEventListener(u"mouseup"_ns, this, true);
627 doc->RemoveSystemEventListener(u"keydown"_ns, this, true);
630 // remove the popuphidden listener from tooltip
631 currentTooltip->RemoveSystemEventListener(u"popuphiding"_ns, this, false);
634 // kill any ongoing timers
635 KillTooltipTimer();
636 mSourceNode = nullptr;
637 mLastTreeCol = nullptr;
639 return NS_OK;
642 void nsXULTooltipListener::KillTooltipTimer() {
643 if (mTooltipTimer) {
644 mTooltipTimer->Cancel();
645 mTooltipTimer = nullptr;
646 mTargetNode = nullptr;
650 void nsXULTooltipListener::sTooltipCallback(nsITimer* aTimer, void* aListener) {
651 RefPtr<nsXULTooltipListener> instance = sInstance;
652 if (instance) instance->ShowTooltip();
655 XULTreeElement* nsXULTooltipListener::GetSourceTree() {
656 nsCOMPtr<nsIContent> sourceNode = do_QueryReferent(mSourceNode);
657 if (mIsSourceTree && sourceNode) {
658 RefPtr<XULTreeElement> xulEl =
659 XULTreeElement::FromNodeOrNull(sourceNode->GetParent());
660 return xulEl;
663 return nullptr;