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