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