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