Wed, 31 Dec 2014 06:55:50 +0100
Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2
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