layout/xul/nsXULTooltipListener.cpp

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

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

mercurial