Bug 1236991 - part 1: allow forwarding label direction through nsITooltipTextProvider, r?enndeakin draft
authorGijs Kruitbosch <gijskruitbosch@gmail.com>
Tue, 12 Apr 2016 21:47:25 +0100
changeset 350941 3837d6744a109a4ce6e777da30aeff5312a98ea8
parent 350940 e7f78bd4bb2296c0130a6e9e92ec4bcf203dee84
child 350942 9a21e24e199d4820150f47ebad78a0158e6424e1
child 350954 9d65d38845b32aa36610b6ff17ad2e8f879e0208
push id15450
push usergijskruitbosch@gmail.com
push dateThu, 14 Apr 2016 14:24:15 +0000
reviewersenndeakin
bugs1236991
milestone48.0a1
Bug 1236991 - part 1: allow forwarding label direction through nsITooltipTextProvider, r?enndeakin MozReview-Commit-ID: 68LvgKvVXmX
browser/base/content/browser.js
dom/ipc/PBrowser.ipdl
dom/ipc/TabChild.cpp
dom/ipc/TabParent.cpp
dom/ipc/TabParent.h
embedding/browser/nsCTooltipTextProvider.h
embedding/browser/nsDocShellTreeOwner.cpp
embedding/browser/nsDocShellTreeOwner.h
embedding/browser/nsITooltipListener.idl
embedding/browser/nsITooltipTextProvider.idl
xpfe/appshell/nsIXULBrowserWindow.idl
--- 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();
 };