michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsXULTooltipListener.h" michael@0: michael@0: #include "nsIDOMMouseEvent.h" michael@0: #include "nsIDOMXULDocument.h" michael@0: #include "nsIDOMXULElement.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsIPopupBoxObject.h" michael@0: #include "nsMenuPopupFrame.h" michael@0: #include "nsIServiceManager.h" michael@0: #include "nsIDragService.h" michael@0: #include "nsIDragSession.h" michael@0: #ifdef MOZ_XUL michael@0: #include "nsITreeView.h" michael@0: #endif michael@0: #include "nsIScriptContext.h" michael@0: #include "nsPIDOMWindow.h" michael@0: #ifdef MOZ_XUL michael@0: #include "nsXULPopupManager.h" michael@0: #endif michael@0: #include "nsIRootBox.h" michael@0: #include "nsIBoxObject.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/LookAndFeel.h" michael@0: #include "mozilla/dom/Element.h" michael@0: #include "mozilla/dom/Event.h" // for nsIDOMEvent::InternalDOMEvent() michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: michael@0: nsXULTooltipListener* nsXULTooltipListener::mInstance = nullptr; michael@0: michael@0: ////////////////////////////////////////////////////////////////////////// michael@0: //// nsISupports michael@0: michael@0: nsXULTooltipListener::nsXULTooltipListener() michael@0: : mMouseScreenX(0) michael@0: , mMouseScreenY(0) michael@0: , mTooltipShownOnce(false) michael@0: #ifdef MOZ_XUL michael@0: , mIsSourceTree(false) michael@0: , mNeedTitletip(false) michael@0: , mLastTreeRow(-1) michael@0: #endif michael@0: { michael@0: if (sTooltipListenerCount++ == 0) { michael@0: // register the callback so we get notified of updates michael@0: Preferences::RegisterCallback(ToolbarTipsPrefChanged, michael@0: "browser.chrome.toolbar_tips"); michael@0: michael@0: // Call the pref callback to initialize our state. michael@0: ToolbarTipsPrefChanged("browser.chrome.toolbar_tips", nullptr); michael@0: } michael@0: } michael@0: michael@0: nsXULTooltipListener::~nsXULTooltipListener() michael@0: { michael@0: if (nsXULTooltipListener::mInstance == this) { michael@0: ClearTooltipCache(); michael@0: } michael@0: HideTooltip(); michael@0: michael@0: if (--sTooltipListenerCount == 0) { michael@0: // Unregister our pref observer michael@0: Preferences::UnregisterCallback(ToolbarTipsPrefChanged, michael@0: "browser.chrome.toolbar_tips"); michael@0: } michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsXULTooltipListener, nsIDOMEventListener) michael@0: michael@0: void michael@0: nsXULTooltipListener::MouseOut(nsIDOMEvent* aEvent) michael@0: { michael@0: // reset flag so that tooltip will display on the next MouseMove michael@0: mTooltipShownOnce = false; michael@0: michael@0: // if the timer is running and no tooltip is shown, we michael@0: // have to cancel the timer here so that it doesn't michael@0: // show the tooltip if we move the mouse out of the window michael@0: nsCOMPtr currentTooltip = do_QueryReferent(mCurrentTooltip); michael@0: if (mTooltipTimer && !currentTooltip) { michael@0: mTooltipTimer->Cancel(); michael@0: mTooltipTimer = nullptr; michael@0: return; michael@0: } michael@0: michael@0: #ifdef DEBUG_crap michael@0: if (mNeedTitletip) michael@0: return; michael@0: #endif michael@0: michael@0: #ifdef MOZ_XUL michael@0: // check to see if the mouse left the targetNode, and if so, michael@0: // hide the tooltip michael@0: if (currentTooltip) { michael@0: // which node did the mouse leave? michael@0: nsCOMPtr targetNode = do_QueryInterface( michael@0: aEvent->InternalDOMEvent()->GetTarget()); michael@0: michael@0: nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); michael@0: if (pm) { michael@0: nsCOMPtr tooltipNode = michael@0: pm->GetLastTriggerTooltipNode(currentTooltip->GetCurrentDoc()); michael@0: if (tooltipNode == targetNode) { michael@0: // if the target node is the current tooltip target node, the mouse michael@0: // left the node the tooltip appeared on, so close the tooltip. michael@0: HideTooltip(); michael@0: // reset special tree tracking michael@0: if (mIsSourceTree) { michael@0: mLastTreeRow = -1; michael@0: mLastTreeCol = nullptr; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: void michael@0: nsXULTooltipListener::MouseMove(nsIDOMEvent* aEvent) michael@0: { michael@0: if (!sShowTooltips) michael@0: return; michael@0: michael@0: // stash the coordinates of the event so that we can still get back to it from within the michael@0: // timer callback. On win32, we'll get a MouseMove event even when a popup goes away -- michael@0: // even when the mouse doesn't change position! To get around this, we make sure the michael@0: // mouse has really moved before proceeding. michael@0: nsCOMPtr mouseEvent(do_QueryInterface(aEvent)); michael@0: if (!mouseEvent) michael@0: return; michael@0: int32_t newMouseX, newMouseY; michael@0: mouseEvent->GetScreenX(&newMouseX); michael@0: mouseEvent->GetScreenY(&newMouseY); michael@0: michael@0: // filter out false win32 MouseMove event michael@0: if (mMouseScreenX == newMouseX && mMouseScreenY == newMouseY) michael@0: return; michael@0: michael@0: // filter out minor movements due to crappy optical mice and shaky hands michael@0: // to prevent tooltips from hiding prematurely. michael@0: nsCOMPtr currentTooltip = do_QueryReferent(mCurrentTooltip); michael@0: michael@0: if ((currentTooltip) && michael@0: (abs(mMouseScreenX - newMouseX) <= kTooltipMouseMoveTolerance) && michael@0: (abs(mMouseScreenY - newMouseY) <= kTooltipMouseMoveTolerance)) michael@0: return; michael@0: mMouseScreenX = newMouseX; michael@0: mMouseScreenY = newMouseY; michael@0: michael@0: nsCOMPtr sourceContent = do_QueryInterface( michael@0: aEvent->InternalDOMEvent()->GetCurrentTarget()); michael@0: mSourceNode = do_GetWeakReference(sourceContent); michael@0: #ifdef MOZ_XUL michael@0: mIsSourceTree = sourceContent->Tag() == nsGkAtoms::treechildren; michael@0: if (mIsSourceTree) michael@0: CheckTreeBodyMove(mouseEvent); michael@0: #endif michael@0: michael@0: // as the mouse moves, we want to make sure we reset the timer to show it, michael@0: // so that the delay is from when the mouse stops moving, not when it enters michael@0: // the node. michael@0: KillTooltipTimer(); michael@0: michael@0: // If the mouse moves while the tooltip is up, hide it. If nothing is michael@0: // showing and the tooltip hasn't been displayed since the mouse entered michael@0: // the node, then start the timer to show the tooltip. michael@0: if (!currentTooltip && !mTooltipShownOnce) { michael@0: nsCOMPtr eventTarget = aEvent->InternalDOMEvent()->GetTarget(); michael@0: michael@0: // don't show tooltips attached to elements outside of a menu popup michael@0: // when hovering over an element inside it. The popupsinherittooltip michael@0: // attribute may be used to disable this behaviour, which is useful for michael@0: // large menu hierarchies such as bookmarks. michael@0: if (!sourceContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::popupsinherittooltip, michael@0: nsGkAtoms::_true, eCaseMatters)) { michael@0: nsCOMPtr targetContent = do_QueryInterface(eventTarget); michael@0: while (targetContent && targetContent != sourceContent) { michael@0: nsIAtom* tag = targetContent->Tag(); michael@0: if (targetContent->GetNameSpaceID() == kNameSpaceID_XUL && michael@0: (tag == nsGkAtoms::menupopup || michael@0: tag == nsGkAtoms::panel || michael@0: tag == nsGkAtoms::tooltip)) { michael@0: mSourceNode = nullptr; michael@0: return; michael@0: } michael@0: michael@0: targetContent = targetContent->GetParent(); michael@0: } michael@0: } michael@0: michael@0: mTooltipTimer = do_CreateInstance("@mozilla.org/timer;1"); michael@0: if (mTooltipTimer) { michael@0: mTargetNode = do_GetWeakReference(eventTarget); michael@0: if (mTargetNode) { michael@0: nsresult rv = michael@0: mTooltipTimer->InitWithFuncCallback(sTooltipCallback, this, michael@0: LookAndFeel::GetInt(LookAndFeel::eIntID_TooltipDelay, 500), michael@0: nsITimer::TYPE_ONE_SHOT); michael@0: if (NS_FAILED(rv)) { michael@0: mTargetNode = nullptr; michael@0: mSourceNode = nullptr; michael@0: } michael@0: } michael@0: } michael@0: return; michael@0: } michael@0: michael@0: #ifdef MOZ_XUL michael@0: if (mIsSourceTree) michael@0: return; michael@0: #endif michael@0: michael@0: HideTooltip(); michael@0: // set a flag so that the tooltip is only displayed once until the mouse michael@0: // leaves the node michael@0: mTooltipShownOnce = true; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTooltipListener::HandleEvent(nsIDOMEvent* aEvent) michael@0: { michael@0: nsAutoString type; michael@0: aEvent->GetType(type); michael@0: if (type.EqualsLiteral("DOMMouseScroll") || michael@0: type.EqualsLiteral("keydown") || michael@0: type.EqualsLiteral("mousedown") || michael@0: type.EqualsLiteral("mouseup") || michael@0: type.EqualsLiteral("dragstart")) { michael@0: HideTooltip(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (type.EqualsLiteral("popuphiding")) { michael@0: DestroyTooltip(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Note that mousemove, mouseover and mouseout might be michael@0: // fired even during dragging due to widget's bug. michael@0: nsCOMPtr dragService = michael@0: do_GetService("@mozilla.org/widget/dragservice;1"); michael@0: NS_ENSURE_TRUE(dragService, NS_OK); michael@0: nsCOMPtr dragSession; michael@0: dragService->GetCurrentSession(getter_AddRefs(dragSession)); michael@0: if (dragSession) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Not dragging. michael@0: michael@0: if (type.EqualsLiteral("mousemove")) { michael@0: MouseMove(aEvent); michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (type.EqualsLiteral("mouseout")) { michael@0: MouseOut(aEvent); michael@0: return NS_OK; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: ////////////////////////////////////////////////////////////////////////// michael@0: //// nsXULTooltipListener michael@0: michael@0: // static michael@0: void michael@0: nsXULTooltipListener::ToolbarTipsPrefChanged(const char *aPref, michael@0: void *aClosure) michael@0: { michael@0: sShowTooltips = michael@0: Preferences::GetBool("browser.chrome.toolbar_tips", sShowTooltips); michael@0: } michael@0: michael@0: ////////////////////////////////////////////////////////////////////////// michael@0: //// nsXULTooltipListener michael@0: michael@0: bool nsXULTooltipListener::sShowTooltips = false; michael@0: uint32_t nsXULTooltipListener::sTooltipListenerCount = 0; michael@0: michael@0: nsresult michael@0: nsXULTooltipListener::AddTooltipSupport(nsIContent* aNode) michael@0: { michael@0: if (!aNode) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: aNode->AddSystemEventListener(NS_LITERAL_STRING("mouseout"), this, michael@0: false, false); michael@0: aNode->AddSystemEventListener(NS_LITERAL_STRING("mousemove"), this, michael@0: false, false); michael@0: aNode->AddSystemEventListener(NS_LITERAL_STRING("mousedown"), this, michael@0: false, false); michael@0: aNode->AddSystemEventListener(NS_LITERAL_STRING("mouseup"), this, michael@0: false, false); michael@0: aNode->AddSystemEventListener(NS_LITERAL_STRING("dragstart"), this, michael@0: true, false); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsXULTooltipListener::RemoveTooltipSupport(nsIContent* aNode) michael@0: { michael@0: if (!aNode) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: aNode->RemoveSystemEventListener(NS_LITERAL_STRING("mouseout"), this, false); michael@0: aNode->RemoveSystemEventListener(NS_LITERAL_STRING("mousemove"), this, false); michael@0: aNode->RemoveSystemEventListener(NS_LITERAL_STRING("mousedown"), this, false); michael@0: aNode->RemoveSystemEventListener(NS_LITERAL_STRING("mouseup"), this, false); michael@0: aNode->RemoveSystemEventListener(NS_LITERAL_STRING("dragstart"), this, true); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: #ifdef MOZ_XUL michael@0: void michael@0: nsXULTooltipListener::CheckTreeBodyMove(nsIDOMMouseEvent* aMouseEvent) michael@0: { michael@0: nsCOMPtr sourceNode = do_QueryReferent(mSourceNode); michael@0: if (!sourceNode) michael@0: return; michael@0: michael@0: // get the boxObject of the documentElement of the document the tree is in michael@0: nsCOMPtr bx; michael@0: nsIDocument* doc = sourceNode->GetDocument(); michael@0: if (doc) { michael@0: ErrorResult ignored; michael@0: bx = doc->GetBoxObjectFor(doc->GetRootElement(), ignored); michael@0: } michael@0: michael@0: nsCOMPtr obx; michael@0: GetSourceTreeBoxObject(getter_AddRefs(obx)); michael@0: if (bx && obx) { michael@0: int32_t x, y; michael@0: aMouseEvent->GetScreenX(&x); michael@0: aMouseEvent->GetScreenY(&y); michael@0: michael@0: int32_t row; michael@0: nsCOMPtr col; michael@0: nsAutoCString obj; michael@0: michael@0: // subtract off the documentElement's boxObject michael@0: int32_t boxX, boxY; michael@0: bx->GetScreenX(&boxX); michael@0: bx->GetScreenY(&boxY); michael@0: x -= boxX; michael@0: y -= boxY; michael@0: michael@0: obx->GetCellAt(x, y, &row, getter_AddRefs(col), obj); michael@0: michael@0: // determine if we are going to need a titletip michael@0: // XXX check the disabletitletips attribute on the tree content michael@0: mNeedTitletip = false; michael@0: if (row >= 0 && obj.EqualsLiteral("text")) { michael@0: obx->IsCellCropped(row, col, &mNeedTitletip); michael@0: } michael@0: michael@0: nsCOMPtr currentTooltip = do_QueryReferent(mCurrentTooltip); michael@0: if (currentTooltip && (row != mLastTreeRow || col != mLastTreeCol)) { michael@0: HideTooltip(); michael@0: } michael@0: michael@0: mLastTreeRow = row; michael@0: mLastTreeCol = col; michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: nsresult michael@0: nsXULTooltipListener::ShowTooltip() michael@0: { michael@0: nsCOMPtr sourceNode = do_QueryReferent(mSourceNode); michael@0: michael@0: // get the tooltip content designated for the target node michael@0: nsCOMPtr tooltipNode; michael@0: GetTooltipFor(sourceNode, getter_AddRefs(tooltipNode)); michael@0: if (!tooltipNode || sourceNode == tooltipNode) michael@0: return NS_ERROR_FAILURE; // the target node doesn't need a tooltip michael@0: michael@0: // set the node in the document that triggered the tooltip and show it michael@0: nsCOMPtr xulDoc(do_QueryInterface(tooltipNode->GetDocument())); michael@0: if (xulDoc) { michael@0: // Make sure the target node is still attached to some document. michael@0: // It might have been deleted. michael@0: if (sourceNode->GetDocument()) { michael@0: #ifdef MOZ_XUL michael@0: if (!mIsSourceTree) { michael@0: mLastTreeRow = -1; michael@0: mLastTreeCol = nullptr; michael@0: } michael@0: #endif michael@0: michael@0: mCurrentTooltip = do_GetWeakReference(tooltipNode); michael@0: LaunchTooltip(); michael@0: mTargetNode = nullptr; michael@0: michael@0: nsCOMPtr currentTooltip = do_QueryReferent(mCurrentTooltip); michael@0: if (!currentTooltip) michael@0: return NS_OK; michael@0: michael@0: // listen for popuphidden on the tooltip node, so that we can michael@0: // be sure DestroyPopup is called even if someone else closes the tooltip michael@0: currentTooltip->AddSystemEventListener(NS_LITERAL_STRING("popuphiding"), michael@0: this, false, false); michael@0: michael@0: // listen for mousedown, mouseup, keydown, and DOMMouseScroll events at document level michael@0: nsIDocument* doc = sourceNode->GetDocument(); michael@0: if (doc) { michael@0: // Probably, we should listen to untrusted events for hiding tooltips michael@0: // on content since tooltips might disturb something of web michael@0: // applications. If we don't specify the aWantsUntrusted of michael@0: // AddSystemEventListener(), the event target sets it to TRUE if the michael@0: // target is in content. michael@0: doc->AddSystemEventListener(NS_LITERAL_STRING("DOMMouseScroll"), michael@0: this, true); michael@0: doc->AddSystemEventListener(NS_LITERAL_STRING("mousedown"), michael@0: this, true); michael@0: doc->AddSystemEventListener(NS_LITERAL_STRING("mouseup"), michael@0: this, true); michael@0: doc->AddSystemEventListener(NS_LITERAL_STRING("keydown"), michael@0: this, true); michael@0: } michael@0: mSourceNode = nullptr; michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: #ifdef MOZ_XUL michael@0: // XXX: "This stuff inside DEBUG_crap could be used to make tree tooltips work michael@0: // in the future." michael@0: #ifdef DEBUG_crap michael@0: static void michael@0: GetTreeCellCoords(nsITreeBoxObject* aTreeBox, nsIContent* aSourceNode, michael@0: int32_t aRow, nsITreeColumn* aCol, int32_t* aX, int32_t* aY) michael@0: { michael@0: int32_t junk; michael@0: aTreeBox->GetCoordsForCellItem(aRow, aCol, EmptyCString(), aX, aY, &junk, &junk); michael@0: nsCOMPtr xulEl(do_QueryInterface(aSourceNode)); michael@0: nsCOMPtr bx; michael@0: xulEl->GetBoxObject(getter_AddRefs(bx)); michael@0: int32_t myX, myY; michael@0: bx->GetX(&myX); michael@0: bx->GetY(&myY); michael@0: *aX += myX; michael@0: *aY += myY; michael@0: } michael@0: #endif michael@0: michael@0: static void michael@0: SetTitletipLabel(nsITreeBoxObject* aTreeBox, nsIContent* aTooltip, michael@0: int32_t aRow, nsITreeColumn* aCol) michael@0: { michael@0: nsCOMPtr view; michael@0: aTreeBox->GetView(getter_AddRefs(view)); michael@0: if (view) { michael@0: nsAutoString label; michael@0: #ifdef DEBUG michael@0: nsresult rv = michael@0: #endif michael@0: view->GetCellText(aRow, aCol, label); michael@0: NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Couldn't get the cell text!"); michael@0: aTooltip->SetAttr(kNameSpaceID_None, nsGkAtoms::label, label, true); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: void michael@0: nsXULTooltipListener::LaunchTooltip() michael@0: { michael@0: nsCOMPtr currentTooltip = do_QueryReferent(mCurrentTooltip); michael@0: if (!currentTooltip) michael@0: return; michael@0: michael@0: #ifdef MOZ_XUL michael@0: if (mIsSourceTree && mNeedTitletip) { michael@0: nsCOMPtr obx; michael@0: GetSourceTreeBoxObject(getter_AddRefs(obx)); michael@0: michael@0: SetTitletipLabel(obx, currentTooltip, mLastTreeRow, mLastTreeCol); michael@0: if (!(currentTooltip = do_QueryReferent(mCurrentTooltip))) { michael@0: // Because of mutation events, currentTooltip can be null. michael@0: return; michael@0: } michael@0: currentTooltip->SetAttr(kNameSpaceID_None, nsGkAtoms::titletip, NS_LITERAL_STRING("true"), true); michael@0: } else { michael@0: currentTooltip->UnsetAttr(kNameSpaceID_None, nsGkAtoms::titletip, true); michael@0: } michael@0: if (!(currentTooltip = do_QueryReferent(mCurrentTooltip))) { michael@0: // Because of mutation events, currentTooltip can be null. michael@0: return; michael@0: } michael@0: michael@0: nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); michael@0: if (pm) { michael@0: nsCOMPtr target = do_QueryReferent(mTargetNode); michael@0: pm->ShowTooltipAtScreen(currentTooltip, target, mMouseScreenX, mMouseScreenY); michael@0: michael@0: // Clear the current tooltip if the popup was not opened successfully. michael@0: if (!pm->IsPopupOpen(currentTooltip)) michael@0: mCurrentTooltip = nullptr; michael@0: } michael@0: #endif michael@0: michael@0: } michael@0: michael@0: nsresult michael@0: nsXULTooltipListener::HideTooltip() michael@0: { michael@0: #ifdef MOZ_XUL michael@0: nsCOMPtr currentTooltip = do_QueryReferent(mCurrentTooltip); michael@0: if (currentTooltip) { michael@0: nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); michael@0: if (pm) michael@0: pm->HidePopup(currentTooltip, false, false, false, false); michael@0: } michael@0: #endif michael@0: michael@0: DestroyTooltip(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: static void michael@0: GetImmediateChild(nsIContent* aContent, nsIAtom *aTag, nsIContent** aResult) michael@0: { michael@0: *aResult = nullptr; michael@0: uint32_t childCount = aContent->GetChildCount(); michael@0: for (uint32_t i = 0; i < childCount; i++) { michael@0: nsIContent *child = aContent->GetChildAt(i); michael@0: michael@0: if (child->Tag() == aTag) { michael@0: *aResult = child; michael@0: NS_ADDREF(*aResult); michael@0: return; michael@0: } michael@0: } michael@0: michael@0: return; michael@0: } michael@0: michael@0: nsresult michael@0: nsXULTooltipListener::FindTooltip(nsIContent* aTarget, nsIContent** aTooltip) michael@0: { michael@0: if (!aTarget) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: // before we go on, make sure that target node still has a window michael@0: nsIDocument *document = aTarget->GetDocument(); michael@0: if (!document) { michael@0: NS_WARNING("Unable to retrieve the tooltip node document."); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: nsPIDOMWindow *window = document->GetWindow(); michael@0: if (!window) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool closed; michael@0: window->GetClosed(&closed); michael@0: michael@0: if (closed) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsAutoString tooltipText; michael@0: aTarget->GetAttr(kNameSpaceID_None, nsGkAtoms::tooltiptext, tooltipText); michael@0: if (!tooltipText.IsEmpty()) { michael@0: // specifying tooltiptext means we will always use the default tooltip michael@0: nsIRootBox* rootBox = nsIRootBox::GetRootBox(document->GetShell()); michael@0: NS_ENSURE_STATE(rootBox); michael@0: *aTooltip = rootBox->GetDefaultTooltip(); michael@0: if (*aTooltip) { michael@0: NS_ADDREF(*aTooltip); michael@0: (*aTooltip)->SetAttr(kNameSpaceID_None, nsGkAtoms::label, tooltipText, true); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsAutoString tooltipId; michael@0: aTarget->GetAttr(kNameSpaceID_None, nsGkAtoms::tooltip, tooltipId); michael@0: michael@0: // if tooltip == _child, look for first child michael@0: if (tooltipId.EqualsLiteral("_child")) { michael@0: GetImmediateChild(aTarget, nsGkAtoms::tooltip, aTooltip); michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (!tooltipId.IsEmpty()) { michael@0: // tooltip must be an id, use getElementById to find it michael@0: nsCOMPtr tooltipEl = document->GetElementById(tooltipId); michael@0: michael@0: if (tooltipEl) { michael@0: #ifdef MOZ_XUL michael@0: mNeedTitletip = false; michael@0: #endif michael@0: tooltipEl.forget(aTooltip); michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: #ifdef MOZ_XUL michael@0: // titletips should just use the default tooltip michael@0: if (mIsSourceTree && mNeedTitletip) { michael@0: nsIRootBox* rootBox = nsIRootBox::GetRootBox(document->GetShell()); michael@0: NS_ENSURE_STATE(rootBox); michael@0: NS_IF_ADDREF(*aTooltip = rootBox->GetDefaultTooltip()); michael@0: } michael@0: #endif michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: nsXULTooltipListener::GetTooltipFor(nsIContent* aTarget, nsIContent** aTooltip) michael@0: { michael@0: *aTooltip = nullptr; michael@0: nsCOMPtr tooltip; michael@0: nsresult rv = FindTooltip(aTarget, getter_AddRefs(tooltip)); michael@0: if (NS_FAILED(rv) || !tooltip) { michael@0: return rv; michael@0: } michael@0: michael@0: #ifdef MOZ_XUL michael@0: // Submenus can't be used as tooltips, see bug 288763. michael@0: nsIContent* parent = tooltip->GetParent(); michael@0: if (parent) { michael@0: nsMenuFrame* menu = do_QueryFrame(parent->GetPrimaryFrame()); michael@0: if (menu) { michael@0: NS_WARNING("Menu cannot be used as a tooltip"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: tooltip.swap(*aTooltip); michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsXULTooltipListener::DestroyTooltip() michael@0: { michael@0: nsCOMPtr kungFuDeathGrip(this); michael@0: nsCOMPtr currentTooltip = do_QueryReferent(mCurrentTooltip); michael@0: if (currentTooltip) { michael@0: // release tooltip before removing listener to prevent our destructor from michael@0: // being called recursively (bug 120863) michael@0: mCurrentTooltip = nullptr; michael@0: michael@0: // clear out the tooltip node on the document michael@0: nsCOMPtr doc = currentTooltip->GetDocument(); michael@0: if (doc) { michael@0: // remove the mousedown and keydown listener from document michael@0: doc->RemoveSystemEventListener(NS_LITERAL_STRING("DOMMouseScroll"), this, michael@0: true); michael@0: doc->RemoveSystemEventListener(NS_LITERAL_STRING("mousedown"), this, michael@0: true); michael@0: doc->RemoveSystemEventListener(NS_LITERAL_STRING("mouseup"), this, true); michael@0: doc->RemoveSystemEventListener(NS_LITERAL_STRING("keydown"), this, true); michael@0: } michael@0: michael@0: // remove the popuphidden listener from tooltip michael@0: currentTooltip->RemoveSystemEventListener(NS_LITERAL_STRING("popuphiding"), this, false); michael@0: } michael@0: michael@0: // kill any ongoing timers michael@0: KillTooltipTimer(); michael@0: mSourceNode = nullptr; michael@0: #ifdef MOZ_XUL michael@0: mLastTreeCol = nullptr; michael@0: #endif michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsXULTooltipListener::KillTooltipTimer() michael@0: { michael@0: if (mTooltipTimer) { michael@0: mTooltipTimer->Cancel(); michael@0: mTooltipTimer = nullptr; michael@0: mTargetNode = nullptr; michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsXULTooltipListener::sTooltipCallback(nsITimer *aTimer, void *aListener) michael@0: { michael@0: nsRefPtr instance = mInstance; michael@0: if (instance) michael@0: instance->ShowTooltip(); michael@0: } michael@0: michael@0: #ifdef MOZ_XUL michael@0: nsresult michael@0: nsXULTooltipListener::GetSourceTreeBoxObject(nsITreeBoxObject** aBoxObject) michael@0: { michael@0: *aBoxObject = nullptr; michael@0: michael@0: nsCOMPtr sourceNode = do_QueryReferent(mSourceNode); michael@0: if (mIsSourceTree && sourceNode) { michael@0: nsCOMPtr xulEl(do_QueryInterface(sourceNode->GetParent())); michael@0: if (xulEl) { michael@0: nsCOMPtr bx; michael@0: xulEl->GetBoxObject(getter_AddRefs(bx)); michael@0: nsCOMPtr obx(do_QueryInterface(bx)); michael@0: if (obx) { michael@0: *aBoxObject = obx; michael@0: NS_ADDREF(*aBoxObject); michael@0: return NS_OK; michael@0: } michael@0: } michael@0: } michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: #endif