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