|
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 "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() |
|
32 |
|
33 using namespace mozilla; |
|
34 using namespace mozilla::dom; |
|
35 |
|
36 nsXULTooltipListener* nsXULTooltipListener::mInstance = nullptr; |
|
37 |
|
38 ////////////////////////////////////////////////////////////////////////// |
|
39 //// nsISupports |
|
40 |
|
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"); |
|
55 |
|
56 // Call the pref callback to initialize our state. |
|
57 ToolbarTipsPrefChanged("browser.chrome.toolbar_tips", nullptr); |
|
58 } |
|
59 } |
|
60 |
|
61 nsXULTooltipListener::~nsXULTooltipListener() |
|
62 { |
|
63 if (nsXULTooltipListener::mInstance == this) { |
|
64 ClearTooltipCache(); |
|
65 } |
|
66 HideTooltip(); |
|
67 |
|
68 if (--sTooltipListenerCount == 0) { |
|
69 // Unregister our pref observer |
|
70 Preferences::UnregisterCallback(ToolbarTipsPrefChanged, |
|
71 "browser.chrome.toolbar_tips"); |
|
72 } |
|
73 } |
|
74 |
|
75 NS_IMPL_ISUPPORTS(nsXULTooltipListener, nsIDOMEventListener) |
|
76 |
|
77 void |
|
78 nsXULTooltipListener::MouseOut(nsIDOMEvent* aEvent) |
|
79 { |
|
80 // reset flag so that tooltip will display on the next MouseMove |
|
81 mTooltipShownOnce = false; |
|
82 |
|
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 } |
|
92 |
|
93 #ifdef DEBUG_crap |
|
94 if (mNeedTitletip) |
|
95 return; |
|
96 #endif |
|
97 |
|
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()); |
|
105 |
|
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 } |
|
124 |
|
125 void |
|
126 nsXULTooltipListener::MouseMove(nsIDOMEvent* aEvent) |
|
127 { |
|
128 if (!sShowTooltips) |
|
129 return; |
|
130 |
|
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); |
|
141 |
|
142 // filter out false win32 MouseMove event |
|
143 if (mMouseScreenX == newMouseX && mMouseScreenY == newMouseY) |
|
144 return; |
|
145 |
|
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); |
|
149 |
|
150 if ((currentTooltip) && |
|
151 (abs(mMouseScreenX - newMouseX) <= kTooltipMouseMoveTolerance) && |
|
152 (abs(mMouseScreenY - newMouseY) <= kTooltipMouseMoveTolerance)) |
|
153 return; |
|
154 mMouseScreenX = newMouseX; |
|
155 mMouseScreenY = newMouseY; |
|
156 |
|
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 |
|
165 |
|
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(); |
|
170 |
|
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(); |
|
176 |
|
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 } |
|
193 |
|
194 targetContent = targetContent->GetParent(); |
|
195 } |
|
196 } |
|
197 |
|
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 } |
|
214 |
|
215 #ifdef MOZ_XUL |
|
216 if (mIsSourceTree) |
|
217 return; |
|
218 #endif |
|
219 |
|
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 } |
|
225 |
|
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 } |
|
239 |
|
240 if (type.EqualsLiteral("popuphiding")) { |
|
241 DestroyTooltip(); |
|
242 return NS_OK; |
|
243 } |
|
244 |
|
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 } |
|
255 |
|
256 // Not dragging. |
|
257 |
|
258 if (type.EqualsLiteral("mousemove")) { |
|
259 MouseMove(aEvent); |
|
260 return NS_OK; |
|
261 } |
|
262 |
|
263 if (type.EqualsLiteral("mouseout")) { |
|
264 MouseOut(aEvent); |
|
265 return NS_OK; |
|
266 } |
|
267 |
|
268 return NS_OK; |
|
269 } |
|
270 |
|
271 ////////////////////////////////////////////////////////////////////////// |
|
272 //// nsXULTooltipListener |
|
273 |
|
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 } |
|
282 |
|
283 ////////////////////////////////////////////////////////////////////////// |
|
284 //// nsXULTooltipListener |
|
285 |
|
286 bool nsXULTooltipListener::sShowTooltips = false; |
|
287 uint32_t nsXULTooltipListener::sTooltipListenerCount = 0; |
|
288 |
|
289 nsresult |
|
290 nsXULTooltipListener::AddTooltipSupport(nsIContent* aNode) |
|
291 { |
|
292 if (!aNode) |
|
293 return NS_ERROR_NULL_POINTER; |
|
294 |
|
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); |
|
305 |
|
306 return NS_OK; |
|
307 } |
|
308 |
|
309 nsresult |
|
310 nsXULTooltipListener::RemoveTooltipSupport(nsIContent* aNode) |
|
311 { |
|
312 if (!aNode) |
|
313 return NS_ERROR_NULL_POINTER; |
|
314 |
|
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); |
|
320 |
|
321 return NS_OK; |
|
322 } |
|
323 |
|
324 #ifdef MOZ_XUL |
|
325 void |
|
326 nsXULTooltipListener::CheckTreeBodyMove(nsIDOMMouseEvent* aMouseEvent) |
|
327 { |
|
328 nsCOMPtr<nsIContent> sourceNode = do_QueryReferent(mSourceNode); |
|
329 if (!sourceNode) |
|
330 return; |
|
331 |
|
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 } |
|
339 |
|
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); |
|
346 |
|
347 int32_t row; |
|
348 nsCOMPtr<nsITreeColumn> col; |
|
349 nsAutoCString obj; |
|
350 |
|
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; |
|
357 |
|
358 obx->GetCellAt(x, y, &row, getter_AddRefs(col), obj); |
|
359 |
|
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 } |
|
366 |
|
367 nsCOMPtr<nsIContent> currentTooltip = do_QueryReferent(mCurrentTooltip); |
|
368 if (currentTooltip && (row != mLastTreeRow || col != mLastTreeCol)) { |
|
369 HideTooltip(); |
|
370 } |
|
371 |
|
372 mLastTreeRow = row; |
|
373 mLastTreeCol = col; |
|
374 } |
|
375 } |
|
376 #endif |
|
377 |
|
378 nsresult |
|
379 nsXULTooltipListener::ShowTooltip() |
|
380 { |
|
381 nsCOMPtr<nsIContent> sourceNode = do_QueryReferent(mSourceNode); |
|
382 |
|
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 |
|
388 |
|
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 |
|
401 |
|
402 mCurrentTooltip = do_GetWeakReference(tooltipNode); |
|
403 LaunchTooltip(); |
|
404 mTargetNode = nullptr; |
|
405 |
|
406 nsCOMPtr<nsIContent> currentTooltip = do_QueryReferent(mCurrentTooltip); |
|
407 if (!currentTooltip) |
|
408 return NS_OK; |
|
409 |
|
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); |
|
414 |
|
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 } |
|
435 |
|
436 return NS_OK; |
|
437 } |
|
438 |
|
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 |
|
459 |
|
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 |
|
477 |
|
478 void |
|
479 nsXULTooltipListener::LaunchTooltip() |
|
480 { |
|
481 nsCOMPtr<nsIContent> currentTooltip = do_QueryReferent(mCurrentTooltip); |
|
482 if (!currentTooltip) |
|
483 return; |
|
484 |
|
485 #ifdef MOZ_XUL |
|
486 if (mIsSourceTree && mNeedTitletip) { |
|
487 nsCOMPtr<nsITreeBoxObject> obx; |
|
488 GetSourceTreeBoxObject(getter_AddRefs(obx)); |
|
489 |
|
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 } |
|
503 |
|
504 nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); |
|
505 if (pm) { |
|
506 nsCOMPtr<nsIContent> target = do_QueryReferent(mTargetNode); |
|
507 pm->ShowTooltipAtScreen(currentTooltip, target, mMouseScreenX, mMouseScreenY); |
|
508 |
|
509 // Clear the current tooltip if the popup was not opened successfully. |
|
510 if (!pm->IsPopupOpen(currentTooltip)) |
|
511 mCurrentTooltip = nullptr; |
|
512 } |
|
513 #endif |
|
514 |
|
515 } |
|
516 |
|
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 |
|
528 |
|
529 DestroyTooltip(); |
|
530 return NS_OK; |
|
531 } |
|
532 |
|
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); |
|
540 |
|
541 if (child->Tag() == aTag) { |
|
542 *aResult = child; |
|
543 NS_ADDREF(*aResult); |
|
544 return; |
|
545 } |
|
546 } |
|
547 |
|
548 return; |
|
549 } |
|
550 |
|
551 nsresult |
|
552 nsXULTooltipListener::FindTooltip(nsIContent* aTarget, nsIContent** aTooltip) |
|
553 { |
|
554 if (!aTarget) |
|
555 return NS_ERROR_NULL_POINTER; |
|
556 |
|
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 } |
|
567 |
|
568 bool closed; |
|
569 window->GetClosed(&closed); |
|
570 |
|
571 if (closed) { |
|
572 return NS_OK; |
|
573 } |
|
574 |
|
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 } |
|
588 |
|
589 nsAutoString tooltipId; |
|
590 aTarget->GetAttr(kNameSpaceID_None, nsGkAtoms::tooltip, tooltipId); |
|
591 |
|
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 } |
|
597 |
|
598 if (!tooltipId.IsEmpty()) { |
|
599 // tooltip must be an id, use getElementById to find it |
|
600 nsCOMPtr<nsIContent> tooltipEl = document->GetElementById(tooltipId); |
|
601 |
|
602 if (tooltipEl) { |
|
603 #ifdef MOZ_XUL |
|
604 mNeedTitletip = false; |
|
605 #endif |
|
606 tooltipEl.forget(aTooltip); |
|
607 return NS_OK; |
|
608 } |
|
609 } |
|
610 |
|
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 |
|
619 |
|
620 return NS_OK; |
|
621 } |
|
622 |
|
623 |
|
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 } |
|
633 |
|
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 |
|
645 |
|
646 tooltip.swap(*aTooltip); |
|
647 return rv; |
|
648 } |
|
649 |
|
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; |
|
659 |
|
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 } |
|
671 |
|
672 // remove the popuphidden listener from tooltip |
|
673 currentTooltip->RemoveSystemEventListener(NS_LITERAL_STRING("popuphiding"), this, false); |
|
674 } |
|
675 |
|
676 // kill any ongoing timers |
|
677 KillTooltipTimer(); |
|
678 mSourceNode = nullptr; |
|
679 #ifdef MOZ_XUL |
|
680 mLastTreeCol = nullptr; |
|
681 #endif |
|
682 |
|
683 return NS_OK; |
|
684 } |
|
685 |
|
686 void |
|
687 nsXULTooltipListener::KillTooltipTimer() |
|
688 { |
|
689 if (mTooltipTimer) { |
|
690 mTooltipTimer->Cancel(); |
|
691 mTooltipTimer = nullptr; |
|
692 mTargetNode = nullptr; |
|
693 } |
|
694 } |
|
695 |
|
696 void |
|
697 nsXULTooltipListener::sTooltipCallback(nsITimer *aTimer, void *aListener) |
|
698 { |
|
699 nsRefPtr<nsXULTooltipListener> instance = mInstance; |
|
700 if (instance) |
|
701 instance->ShowTooltip(); |
|
702 } |
|
703 |
|
704 #ifdef MOZ_XUL |
|
705 nsresult |
|
706 nsXULTooltipListener::GetSourceTreeBoxObject(nsITreeBoxObject** aBoxObject) |
|
707 { |
|
708 *aBoxObject = nullptr; |
|
709 |
|
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 |