Bug 1460691 - Support tooltips in top level non-XUL windows. r?enndeakin+6102
Add an anonymous XUL tooltip node to toplevel non-XUL windows. Setup a
nsXULTooltipListener on non-XUL nsXULWindows. Make nsXULTooltipListener
always use the default tooltip in the non-XUL case.
MozReview-Commit-ID: Koe5m8PwMQM
--- a/layout/generic/nsCanvasFrame.cpp
+++ b/layout/generic/nsCanvasFrame.cpp
@@ -125,44 +125,64 @@ nsCanvasFrame::CreateAnonymousContent(ns
RefPtr<AccessibleCaretEventHub> eventHub =
PresContext()->GetPresShell()->GetAccessibleCaretEventHub();
if (eventHub) {
// AccessibleCaret will insert anonymous caret elements.
eventHub->Init();
}
// Create a popupgroup element for chrome privileged top level non-XUL
- // documents to support context menus.
+ // documents to support context menus and tooltips.
if (PresContext()->IsChrome() && PresContext()->IsRoot() &&
doc->AllowXULXBL() && !doc->IsXULDocument()) {
nsNodeInfoManager* nodeInfoManager = doc->NodeInfoManager();
RefPtr<NodeInfo> nodeInfo =
nodeInfoManager->GetNodeInfo(nsGkAtoms::popupgroup,
nullptr, kNameSpaceID_XUL,
nsINode::ELEMENT_NODE);
rv = NS_NewXULElement(getter_AddRefs(mPopupgroupContent),
nodeInfo.forget(), dom::NOT_FROM_PARSER);
NS_ENSURE_SUCCESS(rv, rv);
aElements.AppendElement(mPopupgroupContent);
+
+ nodeInfo = nodeInfoManager->GetNodeInfo(nsGkAtoms::tooltip, nullptr,
+ kNameSpaceID_XUL,
+ nsINode::ELEMENT_NODE);
+
+ rv = NS_NewXULElement(getter_AddRefs(mTooltipContent), nodeInfo.forget(),
+ dom::NOT_FROM_PARSER);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mTooltipContent->SetAttr(kNameSpaceID_None, nsGkAtoms::_default,
+ NS_LITERAL_STRING("true"), false);
+ // Set the page attribute so the XBL binding will find the text for the
+ // tooltip from the currently hovered element.
+ mTooltipContent->SetAttr(kNameSpaceID_None, nsGkAtoms::page,
+ NS_LITERAL_STRING("true"), false);
+
+ aElements.AppendElement(mTooltipContent);
}
return NS_OK;
}
void
nsCanvasFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements, uint32_t aFilter)
{
if (mCustomContentContainer) {
aElements.AppendElement(mCustomContentContainer);
}
if (mPopupgroupContent) {
aElements.AppendElement(mPopupgroupContent);
}
+ if (mTooltipContent) {
+ aElements.AppendElement(mTooltipContent);
+ }
}
void
nsCanvasFrame::DestroyFrom(nsIFrame* aDestructRoot, PostDestroyData& aPostDestroyData)
{
nsIScrollableFrame* sf =
PresContext()->GetPresShell()->GetRootScrollFrameAsScrollable();
if (sf) {
@@ -183,16 +203,19 @@ nsCanvasFrame::DestroyFrom(nsIFrame* aDe
nsCOMPtr<nsINode> clonedElement = content->GetContentNode()->CloneNode(true, rv);
content->SetContentNode(clonedElement->AsElement());
}
}
aPostDestroyData.AddAnonymousContent(mCustomContentContainer.forget());
if (mPopupgroupContent) {
aPostDestroyData.AddAnonymousContent(mPopupgroupContent.forget());
}
+ if (mTooltipContent) {
+ aPostDestroyData.AddAnonymousContent(mTooltipContent.forget());
+ }
MOZ_ASSERT(!mPopupSetFrame ||
nsLayoutUtils::IsProperAncestorFrame(this, mPopupSetFrame),
"Someone forgot to clear popup set frame");
nsContainerFrame::DestroyFrom(aDestructRoot, aPostDestroyData);
}
void
@@ -302,25 +325,25 @@ nsCanvasFrame::SetPopupSetFrame(nsPopupS
MOZ_ASSERT(!aPopupSet || !mPopupSetFrame,
"Popup set is already defined! Only 1 allowed.");
mPopupSetFrame = aPopupSet;
}
Element*
nsCanvasFrame::GetDefaultTooltip()
{
- NS_WARNING("GetDefaultTooltip not implemented");
- return nullptr;
+ return mTooltipContent;
}
void
nsCanvasFrame::SetDefaultTooltip(Element* aTooltip)
{
- NS_WARNING("SetDefaultTooltip not implemented");
- return;
+ MOZ_ASSERT(!aTooltip || aTooltip == mTooltipContent,
+ "Default tooltip should be anonymous content tooltip.");
+ mTooltipContent = aTooltip;
}
void
nsDisplayCanvasBackgroundColor::Paint(nsDisplayListBuilder* aBuilder,
gfxContext* aCtx)
{
nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame);
nsPoint offset = ToReferenceFrame();
--- a/layout/generic/nsCanvasFrame.h
+++ b/layout/generic/nsCanvasFrame.h
@@ -130,16 +130,17 @@ protected:
bool mDoPaintFocus;
bool mAddedScrollPositionListener;
nsCOMPtr<mozilla::dom::Element> mCustomContentContainer;
private:
nsPopupSetFrame* mPopupSetFrame;
nsCOMPtr<mozilla::dom::Element> mPopupgroupContent;
+ nsCOMPtr<mozilla::dom::Element> mTooltipContent;
};
/**
* Override nsDisplayBackground methods so that we pass aBGClipRect to
* PaintBackground, covering the whole overflow area.
* We can also paint an "extra background color" behind the normal
* background.
*/
--- a/layout/xul/moz.build
+++ b/layout/xul/moz.build
@@ -23,16 +23,17 @@ XPIDL_SOURCES += [
XPIDL_MODULE = 'layout_xul'
EXPORTS += [
'nsBox.h',
'nsIScrollbarMediator.h',
'nsPIBoxObject.h',
'nsPIListBoxObject.h',
'nsXULPopupManager.h',
+ 'nsXULTooltipListener.h',
]
EXPORTS.mozilla.dom += [
'BoxObject.h',
'ListBoxObject.h',
'MenuBoxObject.h',
'ScrollBoxObject.h',
]
--- a/layout/xul/nsXULTooltipListener.cpp
+++ b/layout/xul/nsXULTooltipListener.cpp
@@ -395,17 +395,17 @@ nsXULTooltipListener::ShowTooltip()
// 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
if (tooltipNode->GetComposedDoc() &&
- tooltipNode->GetComposedDoc()->IsXULDocument()) {
+ nsContentUtils::IsChromeDoc(tooltipNode->GetComposedDoc())) {
// 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;
}
@@ -573,16 +573,28 @@ nsXULTooltipListener::FindTooltip(nsICon
if (!window) {
return NS_OK;
}
if (window->Closed()) {
return NS_OK;
}
+ // non-XUL documents should just use the default tooltip
+ if (!document->IsXULDocument()) {
+ nsIPopupContainer* popupContainer =
+ nsIPopupContainer::GetPopupContainer(document->GetShell());
+ NS_ENSURE_STATE(popupContainer);
+ if (RefPtr<Element> tooltip = popupContainer->GetDefaultTooltip()) {
+ tooltip.forget(aTooltip);
+ return NS_OK;
+ }
+ return NS_ERROR_FAILURE;
+ }
+
nsAutoString tooltipText;
if (aTarget->IsElement()) {
aTarget->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::tooltiptext, tooltipText);
}
if (!tooltipText.IsEmpty()) {
// specifying tooltiptext means we will always use the default tooltip
nsIPopupContainer* popupContainer =
nsIPopupContainer::GetPopupContainer(document->GetShell());
--- a/xpfe/appshell/nsXULWindow.cpp
+++ b/xpfe/appshell/nsXULWindow.cpp
@@ -44,16 +44,17 @@
#include "nsAppShellCID.h"
#include "nsReadableUtils.h"
#include "nsStyleConsts.h"
#include "nsPresContext.h"
#include "nsContentUtils.h"
#include "nsWebShellWindow.h" // get rid of this one, too...
#include "nsGlobalWindow.h"
#include "XULDocument.h"
+#include "nsXULTooltipListener.h"
#include "prenv.h"
#include "mozilla/AutoRestore.h"
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
#include "mozilla/dom/BarProps.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/Event.h"
@@ -512,16 +513,18 @@ NS_IMETHODIMP nsXULWindow::Destroy()
parent->GetMainWidget(getter_AddRefs(parentWidget));
if (parentWidget)
parentWidget->PlaceBehind(eZPlacementTop, 0, true);
}
}
}
#endif
+ RemoveTooltipSupport();
+
mDOMWindow = nullptr;
if (mDocShell) {
nsCOMPtr<nsIBaseWindow> shellAsWin(do_QueryInterface(mDocShell));
shellAsWin->Destroy();
mDocShell = nullptr; // this can cause reentrancy of this function
}
mPrimaryContentShell = nullptr;
@@ -1121,24 +1124,69 @@ void nsXULWindow::OnChromeLoaded()
mChromeLoaded = true;
ApplyChromeFlags();
SyncAttributesToWidget();
if (mWindow) {
SizeShell();
if (mShowAfterLoad) {
SetVisibility(true);
}
+ AddTooltipSupport();
}
// At this point the window may have been closed already during Show() or
// SyncAttributesToWidget(), so nsXULWindow::Destroy may already have been
// called. Take care!
}
mPersistentAttributesMask |= PAD_POSITION | PAD_SIZE | PAD_MISC;
}
+bool
+nsXULWindow::NeedsTooltipListener()
+{
+ nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
+ if (!docShellElement || docShellElement->IsXULElement()) {
+ // Tooltips in XUL are handled by each element.
+ return false;
+ }
+ // All other non-XUL document types need a tooltip listener.
+ return true;
+}
+
+void
+nsXULWindow::AddTooltipSupport()
+{
+ if (!NeedsTooltipListener()) {
+ return;
+ }
+ nsXULTooltipListener* listener = nsXULTooltipListener::GetInstance();
+ if (!listener) {
+ return;
+ }
+
+ nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
+ MOZ_ASSERT(docShellElement);
+ listener->AddTooltipSupport(docShellElement);
+}
+
+void
+nsXULWindow::RemoveTooltipSupport()
+{
+ if (!NeedsTooltipListener()) {
+ return;
+ }
+ nsXULTooltipListener* listener = nsXULTooltipListener::GetInstance();
+ if (!listener) {
+ return;
+ }
+
+ nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
+ MOZ_ASSERT(docShellElement);
+ listener->RemoveTooltipSupport(docShellElement);
+}
+
// If aSpecWidth and/or aSpecHeight are > 0, we will use these CSS px sizes
// to fit to the screen when staggering windows; if they're negative,
// we use the window's current size instead.
bool nsXULWindow::LoadPositionFromXUL(int32_t aSpecWidth, int32_t aSpecHeight)
{
bool gotPosition = false;
// if we're the hidden window, don't try to validate our size/position. We're
--- a/xpfe/appshell/nsXULWindow.h
+++ b/xpfe/appshell/nsXULWindow.h
@@ -38,16 +38,17 @@
namespace mozilla {
namespace dom {
class Element;
} // namespace dom
} // namespace mozilla
class nsAtom;
+class nsXULTooltipListener;
// nsXULWindow
#define NS_XULWINDOW_IMPL_CID \
{ /* 8eaec2f3-ed02-4be2-8e0f-342798477298 */ \
0x8eaec2f3, \
0xed02, \
0x4be2, \
@@ -103,16 +104,20 @@ protected:
int32_t aSpecWidth, int32_t aSpecHeight);
bool LoadPositionFromXUL(int32_t aSpecWidth, int32_t aSpecHeight);
bool LoadSizeFromXUL(int32_t& aSpecWidth, int32_t& aSpecHeight);
void SetSpecifiedSize(int32_t aSpecWidth, int32_t aSpecHeight);
bool LoadMiscPersistentAttributesFromXUL();
void SyncAttributesToWidget();
NS_IMETHOD SavePersistentAttributes();
+ bool NeedsTooltipListener();
+ void AddTooltipSupport();
+ void RemoveTooltipSupport();
+
NS_IMETHOD GetWindowDOMWindow(mozIDOMWindowProxy** aDOMWindow);
mozilla::dom::Element* GetWindowDOMElement() const;
// See nsIDocShellTreeOwner for docs on next two methods
nsresult ContentShellAdded(nsIDocShellTreeItem* aContentShell,
bool aPrimary);
nsresult ContentShellRemoved(nsIDocShellTreeItem* aContentShell);
NS_IMETHOD GetPrimaryContentSize(int32_t* aWidth,