1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/xul/nsXULTooltipListener.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,726 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsXULTooltipListener.h" 1.10 + 1.11 +#include "nsIDOMMouseEvent.h" 1.12 +#include "nsIDOMXULDocument.h" 1.13 +#include "nsIDOMXULElement.h" 1.14 +#include "nsIDocument.h" 1.15 +#include "nsGkAtoms.h" 1.16 +#include "nsIPopupBoxObject.h" 1.17 +#include "nsMenuPopupFrame.h" 1.18 +#include "nsIServiceManager.h" 1.19 +#include "nsIDragService.h" 1.20 +#include "nsIDragSession.h" 1.21 +#ifdef MOZ_XUL 1.22 +#include "nsITreeView.h" 1.23 +#endif 1.24 +#include "nsIScriptContext.h" 1.25 +#include "nsPIDOMWindow.h" 1.26 +#ifdef MOZ_XUL 1.27 +#include "nsXULPopupManager.h" 1.28 +#endif 1.29 +#include "nsIRootBox.h" 1.30 +#include "nsIBoxObject.h" 1.31 +#include "mozilla/Preferences.h" 1.32 +#include "mozilla/LookAndFeel.h" 1.33 +#include "mozilla/dom/Element.h" 1.34 +#include "mozilla/dom/Event.h" // for nsIDOMEvent::InternalDOMEvent() 1.35 + 1.36 +using namespace mozilla; 1.37 +using namespace mozilla::dom; 1.38 + 1.39 +nsXULTooltipListener* nsXULTooltipListener::mInstance = nullptr; 1.40 + 1.41 +////////////////////////////////////////////////////////////////////////// 1.42 +//// nsISupports 1.43 + 1.44 +nsXULTooltipListener::nsXULTooltipListener() 1.45 + : mMouseScreenX(0) 1.46 + , mMouseScreenY(0) 1.47 + , mTooltipShownOnce(false) 1.48 +#ifdef MOZ_XUL 1.49 + , mIsSourceTree(false) 1.50 + , mNeedTitletip(false) 1.51 + , mLastTreeRow(-1) 1.52 +#endif 1.53 +{ 1.54 + if (sTooltipListenerCount++ == 0) { 1.55 + // register the callback so we get notified of updates 1.56 + Preferences::RegisterCallback(ToolbarTipsPrefChanged, 1.57 + "browser.chrome.toolbar_tips"); 1.58 + 1.59 + // Call the pref callback to initialize our state. 1.60 + ToolbarTipsPrefChanged("browser.chrome.toolbar_tips", nullptr); 1.61 + } 1.62 +} 1.63 + 1.64 +nsXULTooltipListener::~nsXULTooltipListener() 1.65 +{ 1.66 + if (nsXULTooltipListener::mInstance == this) { 1.67 + ClearTooltipCache(); 1.68 + } 1.69 + HideTooltip(); 1.70 + 1.71 + if (--sTooltipListenerCount == 0) { 1.72 + // Unregister our pref observer 1.73 + Preferences::UnregisterCallback(ToolbarTipsPrefChanged, 1.74 + "browser.chrome.toolbar_tips"); 1.75 + } 1.76 +} 1.77 + 1.78 +NS_IMPL_ISUPPORTS(nsXULTooltipListener, nsIDOMEventListener) 1.79 + 1.80 +void 1.81 +nsXULTooltipListener::MouseOut(nsIDOMEvent* aEvent) 1.82 +{ 1.83 + // reset flag so that tooltip will display on the next MouseMove 1.84 + mTooltipShownOnce = false; 1.85 + 1.86 + // if the timer is running and no tooltip is shown, we 1.87 + // have to cancel the timer here so that it doesn't 1.88 + // show the tooltip if we move the mouse out of the window 1.89 + nsCOMPtr<nsIContent> currentTooltip = do_QueryReferent(mCurrentTooltip); 1.90 + if (mTooltipTimer && !currentTooltip) { 1.91 + mTooltipTimer->Cancel(); 1.92 + mTooltipTimer = nullptr; 1.93 + return; 1.94 + } 1.95 + 1.96 +#ifdef DEBUG_crap 1.97 + if (mNeedTitletip) 1.98 + return; 1.99 +#endif 1.100 + 1.101 +#ifdef MOZ_XUL 1.102 + // check to see if the mouse left the targetNode, and if so, 1.103 + // hide the tooltip 1.104 + if (currentTooltip) { 1.105 + // which node did the mouse leave? 1.106 + nsCOMPtr<nsIDOMNode> targetNode = do_QueryInterface( 1.107 + aEvent->InternalDOMEvent()->GetTarget()); 1.108 + 1.109 + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); 1.110 + if (pm) { 1.111 + nsCOMPtr<nsIDOMNode> tooltipNode = 1.112 + pm->GetLastTriggerTooltipNode(currentTooltip->GetCurrentDoc()); 1.113 + if (tooltipNode == targetNode) { 1.114 + // if the target node is the current tooltip target node, the mouse 1.115 + // left the node the tooltip appeared on, so close the tooltip. 1.116 + HideTooltip(); 1.117 + // reset special tree tracking 1.118 + if (mIsSourceTree) { 1.119 + mLastTreeRow = -1; 1.120 + mLastTreeCol = nullptr; 1.121 + } 1.122 + } 1.123 + } 1.124 + } 1.125 +#endif 1.126 +} 1.127 + 1.128 +void 1.129 +nsXULTooltipListener::MouseMove(nsIDOMEvent* aEvent) 1.130 +{ 1.131 + if (!sShowTooltips) 1.132 + return; 1.133 + 1.134 + // stash the coordinates of the event so that we can still get back to it from within the 1.135 + // timer callback. On win32, we'll get a MouseMove event even when a popup goes away -- 1.136 + // even when the mouse doesn't change position! To get around this, we make sure the 1.137 + // mouse has really moved before proceeding. 1.138 + nsCOMPtr<nsIDOMMouseEvent> mouseEvent(do_QueryInterface(aEvent)); 1.139 + if (!mouseEvent) 1.140 + return; 1.141 + int32_t newMouseX, newMouseY; 1.142 + mouseEvent->GetScreenX(&newMouseX); 1.143 + mouseEvent->GetScreenY(&newMouseY); 1.144 + 1.145 + // filter out false win32 MouseMove event 1.146 + if (mMouseScreenX == newMouseX && mMouseScreenY == newMouseY) 1.147 + return; 1.148 + 1.149 + // filter out minor movements due to crappy optical mice and shaky hands 1.150 + // to prevent tooltips from hiding prematurely. 1.151 + nsCOMPtr<nsIContent> currentTooltip = do_QueryReferent(mCurrentTooltip); 1.152 + 1.153 + if ((currentTooltip) && 1.154 + (abs(mMouseScreenX - newMouseX) <= kTooltipMouseMoveTolerance) && 1.155 + (abs(mMouseScreenY - newMouseY) <= kTooltipMouseMoveTolerance)) 1.156 + return; 1.157 + mMouseScreenX = newMouseX; 1.158 + mMouseScreenY = newMouseY; 1.159 + 1.160 + nsCOMPtr<nsIContent> sourceContent = do_QueryInterface( 1.161 + aEvent->InternalDOMEvent()->GetCurrentTarget()); 1.162 + mSourceNode = do_GetWeakReference(sourceContent); 1.163 +#ifdef MOZ_XUL 1.164 + mIsSourceTree = sourceContent->Tag() == nsGkAtoms::treechildren; 1.165 + if (mIsSourceTree) 1.166 + CheckTreeBodyMove(mouseEvent); 1.167 +#endif 1.168 + 1.169 + // as the mouse moves, we want to make sure we reset the timer to show it, 1.170 + // so that the delay is from when the mouse stops moving, not when it enters 1.171 + // the node. 1.172 + KillTooltipTimer(); 1.173 + 1.174 + // If the mouse moves while the tooltip is up, hide it. If nothing is 1.175 + // showing and the tooltip hasn't been displayed since the mouse entered 1.176 + // the node, then start the timer to show the tooltip. 1.177 + if (!currentTooltip && !mTooltipShownOnce) { 1.178 + nsCOMPtr<EventTarget> eventTarget = aEvent->InternalDOMEvent()->GetTarget(); 1.179 + 1.180 + // don't show tooltips attached to elements outside of a menu popup 1.181 + // when hovering over an element inside it. The popupsinherittooltip 1.182 + // attribute may be used to disable this behaviour, which is useful for 1.183 + // large menu hierarchies such as bookmarks. 1.184 + if (!sourceContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::popupsinherittooltip, 1.185 + nsGkAtoms::_true, eCaseMatters)) { 1.186 + nsCOMPtr<nsIContent> targetContent = do_QueryInterface(eventTarget); 1.187 + while (targetContent && targetContent != sourceContent) { 1.188 + nsIAtom* tag = targetContent->Tag(); 1.189 + if (targetContent->GetNameSpaceID() == kNameSpaceID_XUL && 1.190 + (tag == nsGkAtoms::menupopup || 1.191 + tag == nsGkAtoms::panel || 1.192 + tag == nsGkAtoms::tooltip)) { 1.193 + mSourceNode = nullptr; 1.194 + return; 1.195 + } 1.196 + 1.197 + targetContent = targetContent->GetParent(); 1.198 + } 1.199 + } 1.200 + 1.201 + mTooltipTimer = do_CreateInstance("@mozilla.org/timer;1"); 1.202 + if (mTooltipTimer) { 1.203 + mTargetNode = do_GetWeakReference(eventTarget); 1.204 + if (mTargetNode) { 1.205 + nsresult rv = 1.206 + mTooltipTimer->InitWithFuncCallback(sTooltipCallback, this, 1.207 + LookAndFeel::GetInt(LookAndFeel::eIntID_TooltipDelay, 500), 1.208 + nsITimer::TYPE_ONE_SHOT); 1.209 + if (NS_FAILED(rv)) { 1.210 + mTargetNode = nullptr; 1.211 + mSourceNode = nullptr; 1.212 + } 1.213 + } 1.214 + } 1.215 + return; 1.216 + } 1.217 + 1.218 +#ifdef MOZ_XUL 1.219 + if (mIsSourceTree) 1.220 + return; 1.221 +#endif 1.222 + 1.223 + HideTooltip(); 1.224 + // set a flag so that the tooltip is only displayed once until the mouse 1.225 + // leaves the node 1.226 + mTooltipShownOnce = true; 1.227 +} 1.228 + 1.229 +NS_IMETHODIMP 1.230 +nsXULTooltipListener::HandleEvent(nsIDOMEvent* aEvent) 1.231 +{ 1.232 + nsAutoString type; 1.233 + aEvent->GetType(type); 1.234 + if (type.EqualsLiteral("DOMMouseScroll") || 1.235 + type.EqualsLiteral("keydown") || 1.236 + type.EqualsLiteral("mousedown") || 1.237 + type.EqualsLiteral("mouseup") || 1.238 + type.EqualsLiteral("dragstart")) { 1.239 + HideTooltip(); 1.240 + return NS_OK; 1.241 + } 1.242 + 1.243 + if (type.EqualsLiteral("popuphiding")) { 1.244 + DestroyTooltip(); 1.245 + return NS_OK; 1.246 + } 1.247 + 1.248 + // Note that mousemove, mouseover and mouseout might be 1.249 + // fired even during dragging due to widget's bug. 1.250 + nsCOMPtr<nsIDragService> dragService = 1.251 + do_GetService("@mozilla.org/widget/dragservice;1"); 1.252 + NS_ENSURE_TRUE(dragService, NS_OK); 1.253 + nsCOMPtr<nsIDragSession> dragSession; 1.254 + dragService->GetCurrentSession(getter_AddRefs(dragSession)); 1.255 + if (dragSession) { 1.256 + return NS_OK; 1.257 + } 1.258 + 1.259 + // Not dragging. 1.260 + 1.261 + if (type.EqualsLiteral("mousemove")) { 1.262 + MouseMove(aEvent); 1.263 + return NS_OK; 1.264 + } 1.265 + 1.266 + if (type.EqualsLiteral("mouseout")) { 1.267 + MouseOut(aEvent); 1.268 + return NS_OK; 1.269 + } 1.270 + 1.271 + return NS_OK; 1.272 +} 1.273 + 1.274 +////////////////////////////////////////////////////////////////////////// 1.275 +//// nsXULTooltipListener 1.276 + 1.277 +// static 1.278 +void 1.279 +nsXULTooltipListener::ToolbarTipsPrefChanged(const char *aPref, 1.280 + void *aClosure) 1.281 +{ 1.282 + sShowTooltips = 1.283 + Preferences::GetBool("browser.chrome.toolbar_tips", sShowTooltips); 1.284 +} 1.285 + 1.286 +////////////////////////////////////////////////////////////////////////// 1.287 +//// nsXULTooltipListener 1.288 + 1.289 +bool nsXULTooltipListener::sShowTooltips = false; 1.290 +uint32_t nsXULTooltipListener::sTooltipListenerCount = 0; 1.291 + 1.292 +nsresult 1.293 +nsXULTooltipListener::AddTooltipSupport(nsIContent* aNode) 1.294 +{ 1.295 + if (!aNode) 1.296 + return NS_ERROR_NULL_POINTER; 1.297 + 1.298 + aNode->AddSystemEventListener(NS_LITERAL_STRING("mouseout"), this, 1.299 + false, false); 1.300 + aNode->AddSystemEventListener(NS_LITERAL_STRING("mousemove"), this, 1.301 + false, false); 1.302 + aNode->AddSystemEventListener(NS_LITERAL_STRING("mousedown"), this, 1.303 + false, false); 1.304 + aNode->AddSystemEventListener(NS_LITERAL_STRING("mouseup"), this, 1.305 + false, false); 1.306 + aNode->AddSystemEventListener(NS_LITERAL_STRING("dragstart"), this, 1.307 + true, false); 1.308 + 1.309 + return NS_OK; 1.310 +} 1.311 + 1.312 +nsresult 1.313 +nsXULTooltipListener::RemoveTooltipSupport(nsIContent* aNode) 1.314 +{ 1.315 + if (!aNode) 1.316 + return NS_ERROR_NULL_POINTER; 1.317 + 1.318 + aNode->RemoveSystemEventListener(NS_LITERAL_STRING("mouseout"), this, false); 1.319 + aNode->RemoveSystemEventListener(NS_LITERAL_STRING("mousemove"), this, false); 1.320 + aNode->RemoveSystemEventListener(NS_LITERAL_STRING("mousedown"), this, false); 1.321 + aNode->RemoveSystemEventListener(NS_LITERAL_STRING("mouseup"), this, false); 1.322 + aNode->RemoveSystemEventListener(NS_LITERAL_STRING("dragstart"), this, true); 1.323 + 1.324 + return NS_OK; 1.325 +} 1.326 + 1.327 +#ifdef MOZ_XUL 1.328 +void 1.329 +nsXULTooltipListener::CheckTreeBodyMove(nsIDOMMouseEvent* aMouseEvent) 1.330 +{ 1.331 + nsCOMPtr<nsIContent> sourceNode = do_QueryReferent(mSourceNode); 1.332 + if (!sourceNode) 1.333 + return; 1.334 + 1.335 + // get the boxObject of the documentElement of the document the tree is in 1.336 + nsCOMPtr<nsIBoxObject> bx; 1.337 + nsIDocument* doc = sourceNode->GetDocument(); 1.338 + if (doc) { 1.339 + ErrorResult ignored; 1.340 + bx = doc->GetBoxObjectFor(doc->GetRootElement(), ignored); 1.341 + } 1.342 + 1.343 + nsCOMPtr<nsITreeBoxObject> obx; 1.344 + GetSourceTreeBoxObject(getter_AddRefs(obx)); 1.345 + if (bx && obx) { 1.346 + int32_t x, y; 1.347 + aMouseEvent->GetScreenX(&x); 1.348 + aMouseEvent->GetScreenY(&y); 1.349 + 1.350 + int32_t row; 1.351 + nsCOMPtr<nsITreeColumn> col; 1.352 + nsAutoCString obj; 1.353 + 1.354 + // subtract off the documentElement's boxObject 1.355 + int32_t boxX, boxY; 1.356 + bx->GetScreenX(&boxX); 1.357 + bx->GetScreenY(&boxY); 1.358 + x -= boxX; 1.359 + y -= boxY; 1.360 + 1.361 + obx->GetCellAt(x, y, &row, getter_AddRefs(col), obj); 1.362 + 1.363 + // determine if we are going to need a titletip 1.364 + // XXX check the disabletitletips attribute on the tree content 1.365 + mNeedTitletip = false; 1.366 + if (row >= 0 && obj.EqualsLiteral("text")) { 1.367 + obx->IsCellCropped(row, col, &mNeedTitletip); 1.368 + } 1.369 + 1.370 + nsCOMPtr<nsIContent> currentTooltip = do_QueryReferent(mCurrentTooltip); 1.371 + if (currentTooltip && (row != mLastTreeRow || col != mLastTreeCol)) { 1.372 + HideTooltip(); 1.373 + } 1.374 + 1.375 + mLastTreeRow = row; 1.376 + mLastTreeCol = col; 1.377 + } 1.378 +} 1.379 +#endif 1.380 + 1.381 +nsresult 1.382 +nsXULTooltipListener::ShowTooltip() 1.383 +{ 1.384 + nsCOMPtr<nsIContent> sourceNode = do_QueryReferent(mSourceNode); 1.385 + 1.386 + // get the tooltip content designated for the target node 1.387 + nsCOMPtr<nsIContent> tooltipNode; 1.388 + GetTooltipFor(sourceNode, getter_AddRefs(tooltipNode)); 1.389 + if (!tooltipNode || sourceNode == tooltipNode) 1.390 + return NS_ERROR_FAILURE; // the target node doesn't need a tooltip 1.391 + 1.392 + // set the node in the document that triggered the tooltip and show it 1.393 + nsCOMPtr<nsIDOMXULDocument> xulDoc(do_QueryInterface(tooltipNode->GetDocument())); 1.394 + if (xulDoc) { 1.395 + // Make sure the target node is still attached to some document. 1.396 + // It might have been deleted. 1.397 + if (sourceNode->GetDocument()) { 1.398 +#ifdef MOZ_XUL 1.399 + if (!mIsSourceTree) { 1.400 + mLastTreeRow = -1; 1.401 + mLastTreeCol = nullptr; 1.402 + } 1.403 +#endif 1.404 + 1.405 + mCurrentTooltip = do_GetWeakReference(tooltipNode); 1.406 + LaunchTooltip(); 1.407 + mTargetNode = nullptr; 1.408 + 1.409 + nsCOMPtr<nsIContent> currentTooltip = do_QueryReferent(mCurrentTooltip); 1.410 + if (!currentTooltip) 1.411 + return NS_OK; 1.412 + 1.413 + // listen for popuphidden on the tooltip node, so that we can 1.414 + // be sure DestroyPopup is called even if someone else closes the tooltip 1.415 + currentTooltip->AddSystemEventListener(NS_LITERAL_STRING("popuphiding"), 1.416 + this, false, false); 1.417 + 1.418 + // listen for mousedown, mouseup, keydown, and DOMMouseScroll events at document level 1.419 + nsIDocument* doc = sourceNode->GetDocument(); 1.420 + if (doc) { 1.421 + // Probably, we should listen to untrusted events for hiding tooltips 1.422 + // on content since tooltips might disturb something of web 1.423 + // applications. If we don't specify the aWantsUntrusted of 1.424 + // AddSystemEventListener(), the event target sets it to TRUE if the 1.425 + // target is in content. 1.426 + doc->AddSystemEventListener(NS_LITERAL_STRING("DOMMouseScroll"), 1.427 + this, true); 1.428 + doc->AddSystemEventListener(NS_LITERAL_STRING("mousedown"), 1.429 + this, true); 1.430 + doc->AddSystemEventListener(NS_LITERAL_STRING("mouseup"), 1.431 + this, true); 1.432 + doc->AddSystemEventListener(NS_LITERAL_STRING("keydown"), 1.433 + this, true); 1.434 + } 1.435 + mSourceNode = nullptr; 1.436 + } 1.437 + } 1.438 + 1.439 + return NS_OK; 1.440 +} 1.441 + 1.442 +#ifdef MOZ_XUL 1.443 +// XXX: "This stuff inside DEBUG_crap could be used to make tree tooltips work 1.444 +// in the future." 1.445 +#ifdef DEBUG_crap 1.446 +static void 1.447 +GetTreeCellCoords(nsITreeBoxObject* aTreeBox, nsIContent* aSourceNode, 1.448 + int32_t aRow, nsITreeColumn* aCol, int32_t* aX, int32_t* aY) 1.449 +{ 1.450 + int32_t junk; 1.451 + aTreeBox->GetCoordsForCellItem(aRow, aCol, EmptyCString(), aX, aY, &junk, &junk); 1.452 + nsCOMPtr<nsIDOMXULElement> xulEl(do_QueryInterface(aSourceNode)); 1.453 + nsCOMPtr<nsIBoxObject> bx; 1.454 + xulEl->GetBoxObject(getter_AddRefs(bx)); 1.455 + int32_t myX, myY; 1.456 + bx->GetX(&myX); 1.457 + bx->GetY(&myY); 1.458 + *aX += myX; 1.459 + *aY += myY; 1.460 +} 1.461 +#endif 1.462 + 1.463 +static void 1.464 +SetTitletipLabel(nsITreeBoxObject* aTreeBox, nsIContent* aTooltip, 1.465 + int32_t aRow, nsITreeColumn* aCol) 1.466 +{ 1.467 + nsCOMPtr<nsITreeView> view; 1.468 + aTreeBox->GetView(getter_AddRefs(view)); 1.469 + if (view) { 1.470 + nsAutoString label; 1.471 +#ifdef DEBUG 1.472 + nsresult rv = 1.473 +#endif 1.474 + view->GetCellText(aRow, aCol, label); 1.475 + NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Couldn't get the cell text!"); 1.476 + aTooltip->SetAttr(kNameSpaceID_None, nsGkAtoms::label, label, true); 1.477 + } 1.478 +} 1.479 +#endif 1.480 + 1.481 +void 1.482 +nsXULTooltipListener::LaunchTooltip() 1.483 +{ 1.484 + nsCOMPtr<nsIContent> currentTooltip = do_QueryReferent(mCurrentTooltip); 1.485 + if (!currentTooltip) 1.486 + return; 1.487 + 1.488 +#ifdef MOZ_XUL 1.489 + if (mIsSourceTree && mNeedTitletip) { 1.490 + nsCOMPtr<nsITreeBoxObject> obx; 1.491 + GetSourceTreeBoxObject(getter_AddRefs(obx)); 1.492 + 1.493 + SetTitletipLabel(obx, currentTooltip, mLastTreeRow, mLastTreeCol); 1.494 + if (!(currentTooltip = do_QueryReferent(mCurrentTooltip))) { 1.495 + // Because of mutation events, currentTooltip can be null. 1.496 + return; 1.497 + } 1.498 + currentTooltip->SetAttr(kNameSpaceID_None, nsGkAtoms::titletip, NS_LITERAL_STRING("true"), true); 1.499 + } else { 1.500 + currentTooltip->UnsetAttr(kNameSpaceID_None, nsGkAtoms::titletip, true); 1.501 + } 1.502 + if (!(currentTooltip = do_QueryReferent(mCurrentTooltip))) { 1.503 + // Because of mutation events, currentTooltip can be null. 1.504 + return; 1.505 + } 1.506 + 1.507 + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); 1.508 + if (pm) { 1.509 + nsCOMPtr<nsIContent> target = do_QueryReferent(mTargetNode); 1.510 + pm->ShowTooltipAtScreen(currentTooltip, target, mMouseScreenX, mMouseScreenY); 1.511 + 1.512 + // Clear the current tooltip if the popup was not opened successfully. 1.513 + if (!pm->IsPopupOpen(currentTooltip)) 1.514 + mCurrentTooltip = nullptr; 1.515 + } 1.516 +#endif 1.517 + 1.518 +} 1.519 + 1.520 +nsresult 1.521 +nsXULTooltipListener::HideTooltip() 1.522 +{ 1.523 +#ifdef MOZ_XUL 1.524 + nsCOMPtr<nsIContent> currentTooltip = do_QueryReferent(mCurrentTooltip); 1.525 + if (currentTooltip) { 1.526 + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); 1.527 + if (pm) 1.528 + pm->HidePopup(currentTooltip, false, false, false, false); 1.529 + } 1.530 +#endif 1.531 + 1.532 + DestroyTooltip(); 1.533 + return NS_OK; 1.534 +} 1.535 + 1.536 +static void 1.537 +GetImmediateChild(nsIContent* aContent, nsIAtom *aTag, nsIContent** aResult) 1.538 +{ 1.539 + *aResult = nullptr; 1.540 + uint32_t childCount = aContent->GetChildCount(); 1.541 + for (uint32_t i = 0; i < childCount; i++) { 1.542 + nsIContent *child = aContent->GetChildAt(i); 1.543 + 1.544 + if (child->Tag() == aTag) { 1.545 + *aResult = child; 1.546 + NS_ADDREF(*aResult); 1.547 + return; 1.548 + } 1.549 + } 1.550 + 1.551 + return; 1.552 +} 1.553 + 1.554 +nsresult 1.555 +nsXULTooltipListener::FindTooltip(nsIContent* aTarget, nsIContent** aTooltip) 1.556 +{ 1.557 + if (!aTarget) 1.558 + return NS_ERROR_NULL_POINTER; 1.559 + 1.560 + // before we go on, make sure that target node still has a window 1.561 + nsIDocument *document = aTarget->GetDocument(); 1.562 + if (!document) { 1.563 + NS_WARNING("Unable to retrieve the tooltip node document."); 1.564 + return NS_ERROR_FAILURE; 1.565 + } 1.566 + nsPIDOMWindow *window = document->GetWindow(); 1.567 + if (!window) { 1.568 + return NS_OK; 1.569 + } 1.570 + 1.571 + bool closed; 1.572 + window->GetClosed(&closed); 1.573 + 1.574 + if (closed) { 1.575 + return NS_OK; 1.576 + } 1.577 + 1.578 + nsAutoString tooltipText; 1.579 + aTarget->GetAttr(kNameSpaceID_None, nsGkAtoms::tooltiptext, tooltipText); 1.580 + if (!tooltipText.IsEmpty()) { 1.581 + // specifying tooltiptext means we will always use the default tooltip 1.582 + nsIRootBox* rootBox = nsIRootBox::GetRootBox(document->GetShell()); 1.583 + NS_ENSURE_STATE(rootBox); 1.584 + *aTooltip = rootBox->GetDefaultTooltip(); 1.585 + if (*aTooltip) { 1.586 + NS_ADDREF(*aTooltip); 1.587 + (*aTooltip)->SetAttr(kNameSpaceID_None, nsGkAtoms::label, tooltipText, true); 1.588 + } 1.589 + return NS_OK; 1.590 + } 1.591 + 1.592 + nsAutoString tooltipId; 1.593 + aTarget->GetAttr(kNameSpaceID_None, nsGkAtoms::tooltip, tooltipId); 1.594 + 1.595 + // if tooltip == _child, look for first <tooltip> child 1.596 + if (tooltipId.EqualsLiteral("_child")) { 1.597 + GetImmediateChild(aTarget, nsGkAtoms::tooltip, aTooltip); 1.598 + return NS_OK; 1.599 + } 1.600 + 1.601 + if (!tooltipId.IsEmpty()) { 1.602 + // tooltip must be an id, use getElementById to find it 1.603 + nsCOMPtr<nsIContent> tooltipEl = document->GetElementById(tooltipId); 1.604 + 1.605 + if (tooltipEl) { 1.606 +#ifdef MOZ_XUL 1.607 + mNeedTitletip = false; 1.608 +#endif 1.609 + tooltipEl.forget(aTooltip); 1.610 + return NS_OK; 1.611 + } 1.612 + } 1.613 + 1.614 +#ifdef MOZ_XUL 1.615 + // titletips should just use the default tooltip 1.616 + if (mIsSourceTree && mNeedTitletip) { 1.617 + nsIRootBox* rootBox = nsIRootBox::GetRootBox(document->GetShell()); 1.618 + NS_ENSURE_STATE(rootBox); 1.619 + NS_IF_ADDREF(*aTooltip = rootBox->GetDefaultTooltip()); 1.620 + } 1.621 +#endif 1.622 + 1.623 + return NS_OK; 1.624 +} 1.625 + 1.626 + 1.627 +nsresult 1.628 +nsXULTooltipListener::GetTooltipFor(nsIContent* aTarget, nsIContent** aTooltip) 1.629 +{ 1.630 + *aTooltip = nullptr; 1.631 + nsCOMPtr<nsIContent> tooltip; 1.632 + nsresult rv = FindTooltip(aTarget, getter_AddRefs(tooltip)); 1.633 + if (NS_FAILED(rv) || !tooltip) { 1.634 + return rv; 1.635 + } 1.636 + 1.637 +#ifdef MOZ_XUL 1.638 + // Submenus can't be used as tooltips, see bug 288763. 1.639 + nsIContent* parent = tooltip->GetParent(); 1.640 + if (parent) { 1.641 + nsMenuFrame* menu = do_QueryFrame(parent->GetPrimaryFrame()); 1.642 + if (menu) { 1.643 + NS_WARNING("Menu cannot be used as a tooltip"); 1.644 + return NS_ERROR_FAILURE; 1.645 + } 1.646 + } 1.647 +#endif 1.648 + 1.649 + tooltip.swap(*aTooltip); 1.650 + return rv; 1.651 +} 1.652 + 1.653 +nsresult 1.654 +nsXULTooltipListener::DestroyTooltip() 1.655 +{ 1.656 + nsCOMPtr<nsIDOMEventListener> kungFuDeathGrip(this); 1.657 + nsCOMPtr<nsIContent> currentTooltip = do_QueryReferent(mCurrentTooltip); 1.658 + if (currentTooltip) { 1.659 + // release tooltip before removing listener to prevent our destructor from 1.660 + // being called recursively (bug 120863) 1.661 + mCurrentTooltip = nullptr; 1.662 + 1.663 + // clear out the tooltip node on the document 1.664 + nsCOMPtr<nsIDocument> doc = currentTooltip->GetDocument(); 1.665 + if (doc) { 1.666 + // remove the mousedown and keydown listener from document 1.667 + doc->RemoveSystemEventListener(NS_LITERAL_STRING("DOMMouseScroll"), this, 1.668 + true); 1.669 + doc->RemoveSystemEventListener(NS_LITERAL_STRING("mousedown"), this, 1.670 + true); 1.671 + doc->RemoveSystemEventListener(NS_LITERAL_STRING("mouseup"), this, true); 1.672 + doc->RemoveSystemEventListener(NS_LITERAL_STRING("keydown"), this, true); 1.673 + } 1.674 + 1.675 + // remove the popuphidden listener from tooltip 1.676 + currentTooltip->RemoveSystemEventListener(NS_LITERAL_STRING("popuphiding"), this, false); 1.677 + } 1.678 + 1.679 + // kill any ongoing timers 1.680 + KillTooltipTimer(); 1.681 + mSourceNode = nullptr; 1.682 +#ifdef MOZ_XUL 1.683 + mLastTreeCol = nullptr; 1.684 +#endif 1.685 + 1.686 + return NS_OK; 1.687 +} 1.688 + 1.689 +void 1.690 +nsXULTooltipListener::KillTooltipTimer() 1.691 +{ 1.692 + if (mTooltipTimer) { 1.693 + mTooltipTimer->Cancel(); 1.694 + mTooltipTimer = nullptr; 1.695 + mTargetNode = nullptr; 1.696 + } 1.697 +} 1.698 + 1.699 +void 1.700 +nsXULTooltipListener::sTooltipCallback(nsITimer *aTimer, void *aListener) 1.701 +{ 1.702 + nsRefPtr<nsXULTooltipListener> instance = mInstance; 1.703 + if (instance) 1.704 + instance->ShowTooltip(); 1.705 +} 1.706 + 1.707 +#ifdef MOZ_XUL 1.708 +nsresult 1.709 +nsXULTooltipListener::GetSourceTreeBoxObject(nsITreeBoxObject** aBoxObject) 1.710 +{ 1.711 + *aBoxObject = nullptr; 1.712 + 1.713 + nsCOMPtr<nsIContent> sourceNode = do_QueryReferent(mSourceNode); 1.714 + if (mIsSourceTree && sourceNode) { 1.715 + nsCOMPtr<nsIDOMXULElement> xulEl(do_QueryInterface(sourceNode->GetParent())); 1.716 + if (xulEl) { 1.717 + nsCOMPtr<nsIBoxObject> bx; 1.718 + xulEl->GetBoxObject(getter_AddRefs(bx)); 1.719 + nsCOMPtr<nsITreeBoxObject> obx(do_QueryInterface(bx)); 1.720 + if (obx) { 1.721 + *aBoxObject = obx; 1.722 + NS_ADDREF(*aBoxObject); 1.723 + return NS_OK; 1.724 + } 1.725 + } 1.726 + } 1.727 + return NS_ERROR_FAILURE; 1.728 +} 1.729 +#endif