Bug 1236991 - part 1: allow forwarding label direction through nsITooltipTextProvider, r?enndeakin
MozReview-Commit-ID: 68LvgKvVXmX
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -4110,26 +4110,27 @@ var XULBrowserWindow = {
if (gURLBar && gURLBar._mayTrimURLs /* corresponds to browser.urlbar.trimURLs */)
url = trimURL(url);
this.overLink = url;
LinkTargetDisplay.update();
},
- showTooltip: function (x, y, tooltip) {
+ showTooltip: function (x, y, tooltip, direction) {
if (Cc["@mozilla.org/widget/dragservice;1"].getService(Ci.nsIDragService).
getCurrentSession()) {
return;
}
// The x,y coordinates are relative to the <browser> element using
// the chrome zoom level.
let elt = document.getElementById("remoteBrowserTooltip");
elt.label = tooltip;
+ elt.style.direction = direction;
let anchor = gBrowser.selectedBrowser;
elt.openPopupAtScreen(anchor.boxObject.screenX + x, anchor.boxObject.screenY + y, false, null);
},
hideTooltip: function () {
let elt = document.getElementById("remoteBrowserTooltip");
elt.hidePopup();
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -369,17 +369,17 @@ parent:
* Nowadays this is mainly used for link locations on hover.
*/
async SetStatus(uint32_t type, nsString status);
/**
* Show/hide a tooltip when the mouse hovers over an element in the content
* document.
*/
- async ShowTooltip(uint32_t x, uint32_t y, nsString tooltip);
+ async ShowTooltip(uint32_t x, uint32_t y, nsString tooltip, nsString direction);
async HideTooltip();
/**
* Create an asynchronous color picker on the parent side,
* but don't open it yet.
*/
async PColorPicker(nsString title, nsString initialColor);
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -2889,20 +2889,22 @@ TabChild::CompositorUpdated(const Textur
ClientLayerManager* clm = lm->AsClientLayerManager();
mTextureFactoryIdentifier = aNewIdentifier;
clm->UpdateTextureFactoryIdentifier(aNewIdentifier);
FrameLayerBuilder::InvalidateAllLayers(clm);
}
NS_IMETHODIMP
-TabChild::OnShowTooltip(int32_t aXCoords, int32_t aYCoords, const char16_t *aTipText)
+TabChild::OnShowTooltip(int32_t aXCoords, int32_t aYCoords, const char16_t *aTipText,
+ const char16_t *aTipDir)
{
nsString str(aTipText);
- SendShowTooltip(aXCoords, aYCoords, str);
+ nsString dir(aTipDir);
+ SendShowTooltip(aXCoords, aYCoords, str, dir);
return NS_OK;
}
NS_IMETHODIMP
TabChild::OnHideTooltip()
{
SendHideTooltip();
return NS_OK;
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -1742,24 +1742,25 @@ TabParent::RecvSetStatus(const uint32_t&
case nsIWebBrowserChrome::STATUS_LINK:
xulBrowserWindow->SetOverLink(aStatus, nullptr);
break;
}
return true;
}
bool
-TabParent::RecvShowTooltip(const uint32_t& aX, const uint32_t& aY, const nsString& aTooltip)
+TabParent::RecvShowTooltip(const uint32_t& aX, const uint32_t& aY, const nsString& aTooltip,
+ const nsString& aDirection)
{
nsCOMPtr<nsIXULBrowserWindow> xulBrowserWindow = GetXULBrowserWindow();
if (!xulBrowserWindow) {
return true;
}
- xulBrowserWindow->ShowTooltip(aX, aY, aTooltip);
+ xulBrowserWindow->ShowTooltip(aX, aY, aTooltip, aDirection);
return true;
}
bool
TabParent::RecvHideTooltip()
{
nsCOMPtr<nsIXULBrowserWindow> xulBrowserWindow = GetXULBrowserWindow();
if (!xulBrowserWindow) {
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -276,17 +276,18 @@ public:
virtual bool RecvSetStatus(const uint32_t& aType,
const nsString& aStatus) override;
virtual bool RecvIsParentWindowMainWidgetVisible(bool* aIsVisible) override;
virtual bool RecvShowTooltip(const uint32_t& aX,
const uint32_t& aY,
- const nsString& aTooltip) override;
+ const nsString& aTooltip,
+ const nsString& aDirection) override;
virtual bool RecvHideTooltip() override;
virtual bool RecvGetDPI(float* aValue) override;
virtual bool RecvGetDefaultScale(double* aValue) override;
virtual bool RecvGetMaxTouchPoints(uint32_t* aTouchPoints) override;
--- a/embedding/browser/nsCTooltipTextProvider.h
+++ b/embedding/browser/nsCTooltipTextProvider.h
@@ -6,10 +6,12 @@
#ifndef NSCTOOLTIPTEXTPROVIDER_H
#define NSCTOOLTIPTEXTPROVIDER_H
#include "nsITooltipTextProvider.h"
#define NS_TOOLTIPTEXTPROVIDER_CONTRACTID \
"@mozilla.org/embedcomp/tooltiptextprovider;1"
+#define NS_DEFAULTTOOLTIPTEXTPROVIDER_CONTRACTID \
+ "@mozilla.org/embedcomp/default-tooltiptextprovider;1"
#endif
--- a/embedding/browser/nsDocShellTreeOwner.cpp
+++ b/embedding/browser/nsDocShellTreeOwner.cpp
@@ -1059,223 +1059,31 @@ nsDocShellTreeOwner::GetOwnerRequestor()
if (mWebBrowserChromeWeak) {
req = do_QueryReferent(mWebBrowserChromeWeak);
} else if (mOwnerRequestor) {
req = mOwnerRequestor;
}
return req.forget();
}
-class DefaultTooltipTextProvider final : public nsITooltipTextProvider
-{
-public:
- DefaultTooltipTextProvider();
-
- NS_DECL_ISUPPORTS
- NS_DECL_NSITOOLTIPTEXTPROVIDER
-
-protected:
- ~DefaultTooltipTextProvider() {}
-
- nsCOMPtr<nsIAtom> mTag_dialogHeader;
-};
-
-NS_IMPL_ISUPPORTS(DefaultTooltipTextProvider, nsITooltipTextProvider)
-
-DefaultTooltipTextProvider::DefaultTooltipTextProvider()
-{
- // There are certain element types which we don't want to use
- // as tool tip text.
- mTag_dialogHeader = NS_Atomize("dialogheader");
-}
-
-// A helper routine that determines whether we're still interested in SVG
-// titles. We need to stop at the SVG root element that has a document node
-// parent.
-static bool
-UseSVGTitle(nsIDOMElement* aCurrElement)
-{
- nsCOMPtr<dom::Element> element(do_QueryInterface(aCurrElement));
- if (!element || !element->IsSVGElement() || !element->GetParentNode()) {
- return false;
- }
-
- return element->GetParentNode()->NodeType() != nsIDOMNode::DOCUMENT_NODE;
-}
-
-NS_IMETHODIMP
-DefaultTooltipTextProvider::GetNodeText(nsIDOMNode* aNode, char16_t** aText,
- bool* aResult)
-{
- NS_ENSURE_ARG_POINTER(aNode);
- NS_ENSURE_ARG_POINTER(aText);
-
- nsString outText;
-
- nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
-
- bool lookingForSVGTitle = true;
- bool found = false;
- nsCOMPtr<nsIDOMNode> current(aNode);
-
- // If the element implement the constraint validation API and has no title,
- // show the validation message, if any.
- nsCOMPtr<nsIConstraintValidation> cvElement = do_QueryInterface(current);
- if (cvElement) {
- nsCOMPtr<nsIContent> content = do_QueryInterface(cvElement);
- nsCOMPtr<nsIAtom> titleAtom = NS_Atomize("title");
-
- nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(content);
- bool formHasNoValidate = false;
- mozilla::dom::Element* form = formControl->GetFormElement();
- if (form) {
- nsCOMPtr<nsIAtom> noValidateAtom = NS_Atomize("novalidate");
- formHasNoValidate = form->HasAttr(kNameSpaceID_None, noValidateAtom);
- }
-
- if (!content->HasAttr(kNameSpaceID_None, titleAtom) && !formHasNoValidate) {
- cvElement->GetValidationMessage(outText);
- found = !outText.IsEmpty();
- }
- }
-
- while (!found && current) {
- nsCOMPtr<nsIDOMElement> currElement(do_QueryInterface(current));
- if (currElement) {
- nsCOMPtr<nsIContent> content(do_QueryInterface(currElement));
- if (content) {
- if (!content->IsAnyOfXULElements(nsGkAtoms::dialog,
- mTag_dialogHeader,
- nsGkAtoms::window)) {
- // first try the normal title attribute...
- if (!content->IsSVGElement()) {
- // If the element is an <input type="file"> without a title,
- // we should show the current file selection.
- if (content->IsHTMLElement(nsGkAtoms::input) &&
- content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
- NS_LITERAL_STRING("file"), eIgnoreCase) &&
- !content->HasAttr(kNameSpaceID_None, nsGkAtoms::title)) {
- found = true;
- nsCOMPtr<nsIDOMFileList> fileList;
- nsCOMPtr<nsIDOMHTMLInputElement> currInputElement(do_QueryInterface(currElement));
- nsresult rv = currInputElement->GetFiles(getter_AddRefs(fileList));
- NS_ENSURE_SUCCESS(rv, rv);
- if (!fileList) {
- return NS_ERROR_FAILURE;
- }
-
- nsCOMPtr<nsIStringBundleService> bundleService =
- mozilla::services::GetStringBundleService();
- if (!bundleService) {
- return NS_ERROR_FAILURE;
- }
-
- nsCOMPtr<nsIStringBundle> bundle;
- rv = bundleService->CreateBundle("chrome://global/locale/layout/HtmlForm.properties",
- getter_AddRefs(bundle));
- NS_ENSURE_SUCCESS(rv, rv);
- uint32_t listLength = 0;
- rv = fileList->GetLength(&listLength);
- NS_ENSURE_SUCCESS(rv, rv);
- if (listLength == 0) {
- if (content->HasAttr(kNameSpaceID_None, nsGkAtoms::multiple)) {
- rv = bundle->GetStringFromName(MOZ_UTF16("NoFilesSelected"),
- getter_Copies(outText));
- } else {
- rv = bundle->GetStringFromName(MOZ_UTF16("NoFileSelected"),
- getter_Copies(outText));
- }
- NS_ENSURE_SUCCESS(rv, rv);
- } else {
- FileList* fl = static_cast<FileList*>(fileList.get());
- fl->Item(0)->GetName(outText);
-
- // For UX and performance (jank) reasons we cap the number of
- // files that we list in the tooltip to 20 plus a "and xxx more"
- // line, or to 21 if exactly 21 files were picked.
- const uint32_t TRUNCATED_FILE_COUNT = 20;
- uint32_t count = std::min(listLength, TRUNCATED_FILE_COUNT);
- for (uint32_t i = 1; i < count; ++i) {
- nsString fileName;
- fl->Item(i)->GetName(fileName);
- outText.Append(NS_LITERAL_STRING("\n"));
- outText.Append(fileName);
- }
- }
- } else if (NS_SUCCEEDED(currElement->GetAttribute(NS_LITERAL_STRING("title"), outText)) &&
- outText.Length()) {
- found = true;
- }
- }
- if (!found) {
- // ...ok, that didn't work, try it in the XLink namespace
- NS_NAMED_LITERAL_STRING(xlinkNS, "http://www.w3.org/1999/xlink");
- nsCOMPtr<mozilla::dom::Link> linkContent(
- do_QueryInterface(currElement));
- if (linkContent) {
- nsCOMPtr<nsIURI> uri(linkContent->GetURIExternal());
- if (uri) {
- currElement->GetAttributeNS(
- xlinkNS, NS_LITERAL_STRING("title"), outText);
- if (outText.Length()) {
- found = true;
- }
- }
- } else {
- if (lookingForSVGTitle) {
- lookingForSVGTitle = UseSVGTitle(currElement);
- }
- if (lookingForSVGTitle) {
- nsINodeList* childNodes = content->ChildNodes();
- uint32_t childNodeCount = childNodes->Length();
- for (uint32_t i = 0; i < childNodeCount; i++) {
- nsIContent* child = childNodes->Item(i);
- if (child->IsSVGElement(nsGkAtoms::title)) {
- static_cast<dom::SVGTitleElement*>(child)->GetTextContent(outText);
- if (outText.Length()) {
- found = true;
- }
- break;
- }
- }
- }
- }
- }
- }
- }
- }
-
- // not found here, walk up to the parent and keep trying
- if (!found) {
- nsCOMPtr<nsIDOMNode> temp(current);
- temp->GetParentNode(getter_AddRefs(current));
- }
- }
-
- *aResult = found;
- *aText = (found) ? ToNewUnicode(outText) : nullptr;
-
- return NS_OK;
-}
-
NS_IMPL_ISUPPORTS(ChromeTooltipListener, nsIDOMEventListener)
ChromeTooltipListener::ChromeTooltipListener(nsWebBrowser* aInBrowser,
nsIWebBrowserChrome* aInChrome)
: mWebBrowser(aInBrowser)
, mWebBrowserChrome(aInChrome)
, mTooltipListenerInstalled(false)
, mMouseClientX(0)
, mMouseClientY(0)
, mShowingTooltip(false)
, mTooltipShownOnce(false)
{
mTooltipTextProvider = do_GetService(NS_TOOLTIPTEXTPROVIDER_CONTRACTID);
if (!mTooltipTextProvider) {
- mTooltipTextProvider = new DefaultTooltipTextProvider();
+ mTooltipTextProvider = do_GetService(NS_DEFAULTTOOLTIPTEXTPROVIDER_CONTRACTID);
}
}
ChromeTooltipListener::~ChromeTooltipListener()
{
}
// Hook up things to the chrome like context menus and tooltips, if the chrome
@@ -1455,26 +1263,28 @@ ChromeTooltipListener::MouseMove(nsIDOME
}
return NS_OK;
}
// Tell the registered chrome that they should show the tooltip.
NS_IMETHODIMP
ChromeTooltipListener::ShowTooltip(int32_t aInXCoords, int32_t aInYCoords,
- const nsAString& aInTipText)
+ const nsAString& aInTipText,
+ const nsAString& aTipDir)
{
nsresult rv = NS_OK;
// do the work to call the client
nsCOMPtr<nsITooltipListener> tooltipListener(
do_QueryInterface(mWebBrowserChrome));
if (tooltipListener) {
rv = tooltipListener->OnShowTooltip(aInXCoords, aInYCoords,
- PromiseFlatString(aInTipText).get());
+ PromiseFlatString(aInTipText).get(),
+ PromiseFlatString(aTipDir).get());
if (NS_SUCCEEDED(rv)) {
mShowingTooltip = true;
}
}
return rv;
}
@@ -1553,35 +1363,38 @@ ChromeTooltipListener::sTooltipCallback(
self->mPossibleTooltipNode = nullptr;
return;
}
// if there is text associated with the node, show the tip and fire
// off a timer to auto-hide it.
nsXPIDLString tooltipText;
+ nsXPIDLString directionText;
if (self->mTooltipTextProvider) {
bool textFound = false;
self->mTooltipTextProvider->GetNodeText(
- self->mPossibleTooltipNode, getter_Copies(tooltipText), &textFound);
+ self->mPossibleTooltipNode, getter_Copies(tooltipText),
+ getter_Copies(directionText), &textFound);
if (textFound) {
nsString tipText(tooltipText);
+ nsString dirText(directionText);
LayoutDeviceIntPoint screenDot = widget->WidgetToScreenOffset();
double scaleFactor = 1.0;
if (shell->GetPresContext()) {
nsDeviceContext* dc = shell->GetPresContext()->DeviceContext();
scaleFactor = double(nsPresContext::AppUnitsPerCSSPixel()) /
dc->AppUnitsPerDevPixelAtUnitFullZoom();
}
// ShowTooltip expects widget-relative position.
self->ShowTooltip(self->mMouseScreenX - screenDot.x / scaleFactor,
self->mMouseScreenY - screenDot.y / scaleFactor,
- tipText);
+ tipText, dirText);
}
}
// release tooltip target if there is one, NO MATTER WHAT
self->mPossibleTooltipNode = nullptr;
}
}
--- a/embedding/browser/nsDocShellTreeOwner.h
+++ b/embedding/browser/nsDocShellTreeOwner.h
@@ -166,17 +166,18 @@ private:
kTooltipAutoHideTime = 5000, // ms
kTooltipMouseMoveTolerance = 7 // pixel tolerance for mousemove event
};
NS_IMETHOD AddTooltipListener();
NS_IMETHOD RemoveTooltipListener();
NS_IMETHOD ShowTooltip(int32_t aInXCoords, int32_t aInYCoords,
- const nsAString& aInTipText);
+ const nsAString& aInTipText,
+ const nsAString& aDirText);
NS_IMETHOD HideTooltip();
nsWebBrowser* mWebBrowser;
nsCOMPtr<mozilla::dom::EventTarget> mEventTarget;
nsCOMPtr<nsITooltipTextProvider> mTooltipTextProvider;
// This must be a strong ref in order to make sure we can hide the tooltip if
// the window goes away while we're displaying one. If we don't hold a strong
--- a/embedding/browser/nsITooltipListener.idl
+++ b/embedding/browser/nsITooltipListener.idl
@@ -20,23 +20,25 @@ interface nsITooltipListener : nsISuppor
/**
* Called when a tooltip should be displayed.
*
* @param aXCoords The tooltip left edge X coordinate.
* @param aYCoords The tooltip top edge Y coordinate.
* @param aTipText The text to display in the tooltip, typically obtained
* from the TITLE attribute of the node (or containing parent)
* over which the pointer has been positioned.
+ * @param aTipDir The direction (ltr or rtl) in which to display the text
*
* @note
* Coordinates are specified in pixels, relative to the top-left
* corner of the browser area.
*
* @return <code>NS_OK</code> if the tooltip was displayed.
*/
- void onShowTooltip(in long aXCoords, in long aYCoords, in wstring aTipText);
+ void onShowTooltip(in long aXCoords, in long aYCoords, in wstring aTipText,
+ in wstring aTipDir);
/**
* Called when the tooltip should be hidden, either because the pointer
* has moved or the tooltip has timed out.
*/
void onHideTooltip();
};
--- a/embedding/browser/nsITooltipTextProvider.idl
+++ b/embedding/browser/nsITooltipTextProvider.idl
@@ -27,17 +27,18 @@ interface nsIDOMNode;
* @see nsIDOMNode
*/
[scriptable, uuid(b128a1e6-44f3-4331-8fbe-5af360ff21ee)]
interface nsITooltipTextProvider : nsISupports
{
/**
* Called to obtain the tooltip text for a node.
*
- * @arg aNode The node to obtain the text from.
- * @arg aText The tooltip text.
+ * @arg aNode The node to obtain the text from.
+ * @arg aText The tooltip text.
+ * @arg aDirection The text direction (ltr or rtl) to use
*
* @return <CODE>PR_TRUE</CODE> if tooltip text is associated
* with the node and was returned in the aText argument;
* <CODE>PR_FALSE</CODE> otherwise.
*/
- boolean getNodeText(in nsIDOMNode aNode, out wstring aText);
+ boolean getNodeText(in nsIDOMNode aNode, out wstring aText, out wstring aDirection);
};
--- a/xpfe/appshell/nsIXULBrowserWindow.idl
+++ b/xpfe/appshell/nsIXULBrowserWindow.idl
@@ -60,12 +60,12 @@ interface nsIXULBrowserWindow : nsISuppo
* The referrer of the load.
*/
bool shouldLoadURI(in nsIDocShell aDocShell,
in nsIURI aURI,
in nsIURI aReferrer);
/**
* Show/hide a tooltip (when the user mouses over a link, say).
*/
- void showTooltip(in long x, in long y, in AString tooltip);
+ void showTooltip(in long x, in long y, in AString tooltip, in AString direction);
void hideTooltip();
};